mirror of
https://github.com/adtools/clib2.git
synced 2025-12-08 14:59:05 +00:00
Compare commits
65 Commits
master
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| 14c53db07c | |||
| c56fb088ef | |||
| fe363c5403 | |||
| 9f1a9ae92e | |||
| d9b2c0f38f | |||
| 165232d694 | |||
| 7a4966e670 | |||
| dfa3c412d9 | |||
| f49877920e | |||
| 2704ff880f | |||
| 8378274572 | |||
| 0e637e9663 | |||
| a1f85a9c7f | |||
| 76d5b5874e | |||
| 51f88950eb | |||
| f9d1222bd7 | |||
| d37909e409 | |||
| f5631d8bda | |||
| 9a9ae7d6fd | |||
| 64ab8643b5 | |||
| f7fd63acb4 | |||
| 8bd7403ae3 | |||
| 5efacb1a2b | |||
| 7207c96a6f | |||
| 21bc996705 | |||
| 438fd5bbd2 | |||
| 101846e423 | |||
| fba7f7da9b | |||
| 115099698a | |||
| 4f3d0c981c | |||
| 96af3a1165 | |||
| 6cabff4bbb | |||
| 71708e84ce | |||
| 3d70b18c23 | |||
| 842acf2eaa | |||
| b9463f442b | |||
| 7ceeb4f8ed | |||
| fc8051f724 | |||
| 64ba9b5389 | |||
| 1286b86f07 | |||
| 8101b43fc5 | |||
| efaffd9182 | |||
| 6197531c90 | |||
| b94937ff38 | |||
| 2cb54d48a9 | |||
| a7389454bb | |||
| 45d118101a | |||
| e4a703000a | |||
| 8a4a75e721 | |||
| 4cb621d24d | |||
| ff5826c54e | |||
| b7ce13cbf8 | |||
| e71249a15b | |||
| 19323ec218 | |||
| cc8b81e7cc | |||
| a7efdabefc | |||
| 57774795af | |||
| 9d99542299 | |||
| e02089e28a | |||
| 2bcfec3e60 | |||
| 4a01746be2 | |||
| 914ef57844 | |||
| 089ae11181 | |||
| ae13cd77fc | |||
| 3f21e90fca |
@ -122,7 +122,7 @@ WARNINGS = \
|
||||
# -Wconversion -Wshadow
|
||||
|
||||
INCLUDES = -Iinclude -I. -Inetinclude
|
||||
#OPTIONS = -fno-builtin -fno-common -DDEBUG
|
||||
#OPTIONS = -fno-builtin -fno-common
|
||||
OPTIONS = -fno-builtin -fno-common -DNDEBUG
|
||||
#OPTIONS = -fno-builtin -fno-common -DNDEBUG -D__THREAD_SAFE
|
||||
#OPTIONS = -fno-builtin -fno-common -D__MEM_DEBUG
|
||||
@ -261,6 +261,7 @@ C_LIB = \
|
||||
stdio_iobhookentry.o \
|
||||
stdio_lock.o \
|
||||
stdio_locksemaphorename.o \
|
||||
stdio_long_path.o \
|
||||
stdio_nostdio.o \
|
||||
stdio_openiob.o \
|
||||
stdio_parent_of_fh.o \
|
||||
@ -449,7 +450,6 @@ C_LIB = \
|
||||
time_numbertostring.o \
|
||||
time_strftime.o \
|
||||
time_time.o \
|
||||
time_tzset.o \
|
||||
time_weekday.o \
|
||||
uio_readv.o \
|
||||
uio_writev.o \
|
||||
@ -492,13 +492,13 @@ C_LIB = \
|
||||
UNIX_LIB = \
|
||||
unix.lib_rev.o \
|
||||
dirent_closedir.o \
|
||||
dirent_rewinddir.o \
|
||||
dirent_opendir.o \
|
||||
dirent_readdir.o \
|
||||
dirent_rewinddir.o \
|
||||
fcntl_creat.o \
|
||||
fcntl_fcntl.o \
|
||||
fcntl_open.o \
|
||||
fcntl_get_default_file.o \
|
||||
fcntl_open.o \
|
||||
getopt_getopt_long.o \
|
||||
mount_convertinfo.o \
|
||||
mount_statfs.o \
|
||||
@ -506,18 +506,19 @@ UNIX_LIB = \
|
||||
resource_setrlimit.o \
|
||||
stat_chmod.o \
|
||||
stat_fstat.o \
|
||||
stat_lstat.o \
|
||||
stat_lock.o \
|
||||
stat_lstat.o \
|
||||
stat_mkdir.o \
|
||||
stat_rmdir.o \
|
||||
stat_stat.o \
|
||||
stdio_ctermid.o \
|
||||
stdio_fdhookentry.o \
|
||||
stdio_fflush.o \
|
||||
stdio_file_init.o \
|
||||
stdio_fopen.o \
|
||||
stdio_init_exit.o \
|
||||
stdio_file_init.o \
|
||||
stdio_locksemaphorename.o \
|
||||
stdio_long_path.o \
|
||||
stdio_openiob.o \
|
||||
stdio_popen.o \
|
||||
stdio_record_locking.o \
|
||||
@ -527,17 +528,28 @@ UNIX_LIB = \
|
||||
stdlib_alloca_cleanup.o \
|
||||
stdlib_alloca_trap.o \
|
||||
stdlib_arg.o \
|
||||
stdlib_calloc.o \
|
||||
stdlib_decay_unused_slabs.o \
|
||||
stdlib_expand_wildcard.o \
|
||||
stdlib_expand_wildcard_check.o \
|
||||
stdlib_free.o \
|
||||
stdlib_free_unused_slabs.o \
|
||||
stdlib_getmemstats.o \
|
||||
stdlib_get_slab_allocations.o \
|
||||
stdlib_get_slab_stats.o \
|
||||
stdlib_get_slab_usage.o \
|
||||
stdlib_main.o \
|
||||
stdlib_main_stub.o \
|
||||
stdlib_malloc.o \
|
||||
stdlib_mkdtemp.o \
|
||||
stdlib_mkstemp.o \
|
||||
stdlib_mktemp.o \
|
||||
stdlib_malloc.o \
|
||||
stdlib_realloc.o \
|
||||
stdlib_red_black.o \
|
||||
stdlib_resetmemstats.o \
|
||||
stdlib_slab.o \
|
||||
stdlib_slab_max_size.o \
|
||||
stdlib_slab_purge_threshold.o \
|
||||
stdlib_system.o \
|
||||
systeminfo_sysinfo.o \
|
||||
termios_cfgetispeed.o \
|
||||
@ -941,6 +953,7 @@ AMIGA_LIB = \
|
||||
amiga_hotkey.o \
|
||||
amiga_invertstring.o \
|
||||
amiga_newlist.o \
|
||||
amiga_pools.o \
|
||||
amiga_rangerand.o \
|
||||
amiga_remtof.o \
|
||||
amiga_rexxvars.o \
|
||||
@ -1156,6 +1169,34 @@ $(LIBC_OBJS)/stdlib_red_black.o : stdlib_red_black.c stdlib_memory.h include/std
|
||||
|
||||
##############################################################################
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_alloca.o : stdlib_alloca.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_calloc.o : stdlib_calloc.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_decay_unused_slabs.o : stdlib_decay_unused_slabs.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_free.o : stdlib_free.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_free_unused_slabs.o : stdlib_free_unused_slabs.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_get_slab_allocations.o : stdlib_get_slab_allocations.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_get_slab_stats.o : stdlib_get_slab_stats.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_get_slab_usage.o : stdlib_get_slab_usage.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_malloc.o : stdlib_malloc.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_realloc.o : stdlib_realloc.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_red_black.o : stdlib_red_black.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_slab.o : stdlib_slab.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
$(LIBUNIX_OBJS)/stdlib_slab_purge_threshold.o : stdlib_slab_purge_threshold.c stdlib_memory.h include/stdlib.h
|
||||
|
||||
##############################################################################
|
||||
|
||||
# The -fbaserel32 option requires the CPU type to be 68020, too.
|
||||
ifneq (,$(findstring fbaserel32,$(CODE_FLAGS)))
|
||||
LOCAL_CODE_FLAGS := $(CODE_FLAGS) $(CODE_TYPE)
|
||||
|
||||
@ -97,7 +97,11 @@ include libm.gmk
|
||||
include libnet.gmk
|
||||
include libdebug.gmk
|
||||
include libamiga.gmk
|
||||
include libprofile.gmk
|
||||
|
||||
# Olaf (2019-08-22): Please note that "profile_profil.o" can no longer
|
||||
# be built, presumably for lack of header files needed
|
||||
# to build it properly.
|
||||
#include libprofile.gmk
|
||||
|
||||
all-targets: \
|
||||
lib/crt0.o \
|
||||
@ -164,7 +168,9 @@ cvs-tag:
|
||||
|
||||
# General build rules for all object files and the individual libraries
|
||||
|
||||
lib/crtbegin.o : CFLAGS += -fno-aggressive-loop-optimizations
|
||||
#NO_AGGRESSIVE_LOOP_OPTIMIZATIONS := -fno-aggressive-loop-optimizations
|
||||
|
||||
lib/crtbegin.o : CFLAGS += $(NO_AGGRESSIVE_LOOP_OPTIMIZATIONS)
|
||||
lib/crtbegin.o : crtbegin.c
|
||||
@$(COMPILE)
|
||||
|
||||
@ -179,7 +185,7 @@ lib/small-data/%.o : AFLAGS += $(SMALLDATA)
|
||||
lib/small-data/%.o : %.S
|
||||
@$(ASSEMBLE)
|
||||
|
||||
lib/small-data/crtbegin.o : CFLAGS += $(SMALLDATA) -fno-aggressive-loop-optimizations
|
||||
lib/small-data/crtbegin.o : CFLAGS += $(SMALLDATA) $(NO_AGGRESSIVE_LOOP_OPTIMIZATIONS)
|
||||
lib/small-data/crtbegin.o : crtbegin.c
|
||||
@$(COMPILE)
|
||||
|
||||
@ -191,7 +197,7 @@ lib/soft-float/%.o : AFLAGS += $(SOFTFLOAT)
|
||||
lib/soft-float/%.o : %.S
|
||||
@$(ASSEMBLE)
|
||||
|
||||
lib/soft-float/crtbegin.o : CFLAGS += $(SOFTFLOAT) -fno-aggressive-loop-optimizations
|
||||
lib/soft-float/crtbegin.o : CFLAGS += $(SOFTFLOAT) $(NO_AGGRESSIVE_LOOP_OPTIMIZATIONS)
|
||||
lib/soft-float/crtbegin.o : crtbegin.c
|
||||
@$(COMPILE)
|
||||
|
||||
@ -203,7 +209,7 @@ lib/baserel/%.o : AFLAGS += $(BASEREL)
|
||||
lib/baserel/%.o : %.S
|
||||
@$(ASSEMBLE)
|
||||
|
||||
lib/baserel/crtbegin.o : CFLAGS += $(BASEREL) -fno-aggressive-loop-optimizations
|
||||
lib/baserel/crtbegin.o : CFLAGS += $(BASEREL) $(NO_AGGRESSIVE_LOOP_OPTIMIZATIONS)
|
||||
lib/baserel/crtbegin.o : crtbegin.c
|
||||
@$(COMPILE)
|
||||
|
||||
@ -215,7 +221,7 @@ lib.threadsafe/%.o : AFLAGS += $(LARGEDATA) $(THREADSAFE)
|
||||
lib.threadsafe/%.o : %.S
|
||||
@$(ASSEMBLE)
|
||||
|
||||
lib.threadsafe/crtbegin.o : CFLAGS += $(THREADSAFE) $(LARGEDATA) -fno-aggressive-loop-optimizations
|
||||
lib.threadsafe/crtbegin.o : CFLAGS += $(THREADSAFE) $(LARGEDATA) $(NO_AGGRESSIVE_LOOP_OPTIMIZATIONS)
|
||||
lib.threadsafe/crtbegin.o : crtbegin.c
|
||||
@$(COMPILE)
|
||||
|
||||
@ -227,7 +233,7 @@ lib.threadsafe/small-data/%.o : AFLAGS += $(SMALLDATA) $(THREADSAFE)
|
||||
lib.threadsafe/small-data/%.o : %.S
|
||||
@$(ASSEMBLE)
|
||||
|
||||
lib.threadsafe/small-data/crtbegin.o : CFLAGS += $(THREADSAFE) $(SMALLDATA) -fno-aggressive-loop-optimizations
|
||||
lib.threadsafe/small-data/crtbegin.o : CFLAGS += $(THREADSAFE) $(SMALLDATA) $(NO_AGGRESSIVE_LOOP_OPTIMIZATIONS)
|
||||
lib.threadsafe/small-data/crtbegin.o : crtbegin.c
|
||||
@$(COMPILE)
|
||||
|
||||
@ -239,7 +245,7 @@ lib.threadsafe/soft-float/%.o : AFLAGS += $(SOFTFLOAT) $(THREADSAFE)
|
||||
lib.threadsafe/soft-float/%.o : %.S
|
||||
@$(ASSEMBLE)
|
||||
|
||||
lib.threadsafe/soft-float/crtbegin.o : CFLAGS += $(THREADSAFE) $(SOFTFLOAT) -fno-aggressive-loop-optimizations
|
||||
lib.threadsafe/soft-float/crtbegin.o : CFLAGS += $(THREADSAFE) $(SOFTFLOAT) $(NO_AGGRESSIVE_LOOP_OPTIMIZATIONS)
|
||||
lib.threadsafe/soft-float/crtbegin.o : crtbegin.c
|
||||
@$(COMPILE)
|
||||
|
||||
@ -251,7 +257,7 @@ lib.threadsafe/baserel/%.o : AFLAGS += $(BASEREL) $(THREADSAFE)
|
||||
lib.threadsafe/baserel/%.o : %.S
|
||||
@$(ASSEMBLE)
|
||||
|
||||
lib.threadsafe/baserel/crtbegin.o : CFLAGS += $(THREADSAFE) $(BASEREL) -fno-aggressive-loop-optimizations
|
||||
lib.threadsafe/baserel/crtbegin.o : CFLAGS += $(THREADSAFE) $(BASEREL) $(NO_AGGRESSIVE_LOOP_OPTIMIZATIONS)
|
||||
lib.threadsafe/baserel/crtbegin.o : crtbegin.c
|
||||
@$(COMPILE)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#define VERSION 1
|
||||
#define REVISION 215
|
||||
#define DATE "26.6.2017"
|
||||
#define VERS "amiga.lib 1.215"
|
||||
#define VSTRING "amiga.lib 1.215 (26.6.2017)\r\n"
|
||||
#define VERSTAG "\0$VER: amiga.lib 1.215 (26.6.2017)"
|
||||
#define REVISION 216
|
||||
#define DATE "10.7.2025"
|
||||
#define VERS "amiga.lib 1.216"
|
||||
#define VSTRING "amiga.lib 1.216 (10.7.2025)\r\n"
|
||||
#define VERSTAG "\0$VER: amiga.lib 1.216 (10.7.2025)"
|
||||
|
||||
@ -1 +1 @@
|
||||
215
|
||||
216
|
||||
|
||||
562
library/amiga_pools.c
Normal file
562
library/amiga_pools.c
Normal file
@ -0,0 +1,562 @@
|
||||
/*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2021 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Neither the name of Olaf Barthel nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <exec/memory.h>
|
||||
#include <exec/alerts.h>
|
||||
#include <exec/lists.h>
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#include <proto/exec.h>
|
||||
#include <clib/alib_protos.h>
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* This signature identifies a large allocation on the list
|
||||
* which contains both puddles and such large memory allocations.
|
||||
*/
|
||||
#define IS_LARGE_ALLOCATION 0
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* A private memory pool, as used by this implementation of the
|
||||
* pool memory management functions.
|
||||
*/
|
||||
struct MemoryPool
|
||||
{
|
||||
struct MinList mp_PuddleMinList; /* Both puddles and large allocations
|
||||
* are stored in this list. The puddles
|
||||
* are stored near the head of the list
|
||||
* and the large allocations are stored
|
||||
* near the tail of the list.
|
||||
*/
|
||||
|
||||
ULONG mp_MemoryFlags; /* Memory attributes for allocation,
|
||||
* which may also include MEMF_CLEAR.
|
||||
*/
|
||||
ULONG mp_PuddleSize; /* Largest allocation which will fit
|
||||
* into a single puddle.
|
||||
*/
|
||||
ULONG mp_PuddleSizeThreshold; /* Allocations which exceed this size
|
||||
* will be allocated separately and not
|
||||
* make use of the puddles.
|
||||
*/
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* The management data structure associated with a separate large memory
|
||||
* allocation. This is distinct from the puddles.
|
||||
*/
|
||||
struct LargeAllocation
|
||||
{
|
||||
struct MinNode la_MinNode;
|
||||
|
||||
ULONG la_Signature; /* Must be set to IS_LARGE_ALLOCATION */
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* This combines a plain Node, a MemHeader and a LargeAllocation. They
|
||||
* all overlap to some degree. The "puddle" is really a MemHeader from
|
||||
* which the individual memory allocations are made.
|
||||
*
|
||||
* For reference, here is how the individual data structures look like.
|
||||
* The 'struct MinNode' is used by the 'struct LargeAllocation' and you
|
||||
* can see that the LargeAllocation.la_Signature overlaps the
|
||||
* Node.ln_Type and Node.ln_Pri fields. The 'struct MemHeader' begins
|
||||
* with a 'struct Node' and is initialized so that the Node.ln_Type and
|
||||
* Node.ln_Pri are never zero. This is why LargeAllocation.la_Signature
|
||||
* being zero is key to identifying large allocations which along with
|
||||
* the MemHeaders share the same storage list.
|
||||
*
|
||||
* struct MinNode {
|
||||
* struct MinNode * mln_Succ;
|
||||
* struct MinNode * mln_Pred;
|
||||
* };
|
||||
*
|
||||
* struct LargeAllocation {
|
||||
* struct MinNode la_MinNode;
|
||||
* ULONG la_Signature;
|
||||
* };
|
||||
*
|
||||
* struct Node {
|
||||
* struct Node * ln_Succ;
|
||||
* struct Node * ln_Pred;
|
||||
* UBYTE ln_Type;
|
||||
* BYTE ln_Pri;
|
||||
* char * ln_Name;
|
||||
* };
|
||||
*
|
||||
* struct MemHeader {
|
||||
* struct Node mh_Node;
|
||||
* UWORD mh_Attributes;
|
||||
* struct MemChunk * mh_First;
|
||||
* APTR mh_Lower;
|
||||
* APTR mh_Upper;
|
||||
* ULONG mh_Free;
|
||||
* };
|
||||
*/
|
||||
union PuddleUnion
|
||||
{
|
||||
struct Node pu_Node;
|
||||
struct MemHeader pu_MemHeader;
|
||||
struct LargeAllocation pu_LargeAllocation;
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Our local Kickstart 1.x compatible implementation of FreeVec(). */
|
||||
static VOID free_vec(APTR allocation)
|
||||
{
|
||||
if (allocation != NULL)
|
||||
{
|
||||
ULONG * mem = allocation;
|
||||
|
||||
FreeMem(&mem[-1], mem[-1]);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Our local Kickstart 1.x compatible implementation of AllocVec(). */
|
||||
static APTR alloc_vec(ULONG size, ULONG flags)
|
||||
{
|
||||
ULONG * mem = NULL;
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
/* Note: no overflow testing is being performed! */
|
||||
size += sizeof(*mem);
|
||||
|
||||
mem = AllocMem(size, flags);
|
||||
if (mem != NULL)
|
||||
(*mem++) = size;
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
VOID LibDeletePool(APTR pool)
|
||||
{
|
||||
/* If possible, use the operating system implementation
|
||||
* instead of this reimplementation.
|
||||
*/
|
||||
#if ! defined(__amigaos4__)
|
||||
if (SysBase->LibNode.lib_Version >= 39)
|
||||
{
|
||||
DeletePool(pool);
|
||||
return;
|
||||
}
|
||||
#endif /* ! defined(__amigaos4__) */
|
||||
|
||||
if (pool != NULL)
|
||||
{
|
||||
struct MemoryPool * mp = pool;
|
||||
struct MinNode * mln_next;
|
||||
struct MinNode * mln;
|
||||
|
||||
for (mln = mp->mp_PuddleMinList.mlh_Head ;
|
||||
mln->mln_Succ != NULL ;
|
||||
mln = mln_next)
|
||||
{
|
||||
mln_next = mln->mln_Succ;
|
||||
|
||||
free_vec(mln);
|
||||
}
|
||||
|
||||
FreeMem(mp, sizeof(*mp));
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Note: puddle size should not be 0 or every single memory allocation
|
||||
* will result in an AllocVec() call.
|
||||
*/
|
||||
APTR LibCreatePool(
|
||||
ULONG memory_flags,
|
||||
ULONG puddle_size,
|
||||
ULONG threshold_size)
|
||||
{
|
||||
struct MemoryPool * mp = NULL;
|
||||
APTR result = NULL;
|
||||
|
||||
/* If possible, use the operating system implementation
|
||||
* instead of this reimplementation.
|
||||
*/
|
||||
#if ! defined(__amigaos4__)
|
||||
if (SysBase->LibNode.lib_Version >= 39)
|
||||
{
|
||||
return CreatePool(memory_flags, puddle_size, threshold_size);
|
||||
}
|
||||
#endif /* ! defined(__amigaos4__) */
|
||||
|
||||
/* The threshold size must be less than or equal
|
||||
* to the puddle size.
|
||||
*/
|
||||
if (threshold_size > puddle_size)
|
||||
goto out;
|
||||
|
||||
/* Round up the puddle size to the size of a
|
||||
* memory block, as managed by Allocate().
|
||||
*
|
||||
* Note: no overflow checking is performed!
|
||||
*/
|
||||
puddle_size = (puddle_size + MEM_BLOCKMASK) & ~MEM_BLOCKMASK;
|
||||
|
||||
mp = AllocMem(sizeof(*mp), MEMF_ANY);
|
||||
if (mp == NULL)
|
||||
goto out;
|
||||
|
||||
NewList((struct List *)&mp->mp_PuddleMinList);
|
||||
|
||||
mp->mp_MemoryFlags = memory_flags;
|
||||
mp->mp_PuddleSize = puddle_size;
|
||||
mp->mp_PuddleSizeThreshold = threshold_size;
|
||||
|
||||
result = mp;
|
||||
mp = NULL;
|
||||
|
||||
out:
|
||||
|
||||
if (mp != NULL)
|
||||
LibDeletePool(mp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Creates a new puddle from which memory allocations up to a certain size
|
||||
* will be made. Returns the new puddle or NULL for failure.
|
||||
*
|
||||
* Note: No overflow testing is performed when allocating the puddle.
|
||||
*/
|
||||
static union PuddleUnion * create_new_puddle(struct MemoryPool * mp)
|
||||
{
|
||||
ULONG memory_flags = mp->mp_MemoryFlags;
|
||||
ULONG puddle_size = mp->mp_PuddleSize;
|
||||
union PuddleUnion * result = NULL;
|
||||
union PuddleUnion * pu;
|
||||
struct MemHeader * mh;
|
||||
struct MemChunk * mc;
|
||||
|
||||
/* The extra sizeof(APTR) is needed for aligning the
|
||||
* allocatable memory chunks within the memory header.
|
||||
*
|
||||
* Note: no overflow checking is performed!
|
||||
*
|
||||
* Also note that the original memory allocation flags are
|
||||
* being used, which may include MEMF_CLEAR. This means that
|
||||
* if MEMF_CLEAR is set, the initial allocation will be
|
||||
* cleared and then the individual puddle allocations made
|
||||
* through LibAllocPooled() will be cleared, too.
|
||||
*/
|
||||
pu = alloc_vec(sizeof(pu->pu_MemHeader) + sizeof(LONG) + puddle_size, memory_flags);
|
||||
if (pu == NULL)
|
||||
goto out;
|
||||
|
||||
mh = &pu->pu_MemHeader;
|
||||
|
||||
/* The first allocatable memory chunk follows the memory header
|
||||
* and must be aligned to a 64 bit address. This happens to be
|
||||
* the smallest allocatable memory unit (MEM_BLOCKSIZE == 8).
|
||||
*/
|
||||
mc = (struct MemChunk *)(((ULONG)&mh[1] + sizeof(LONG)) & ~MEM_BLOCKMASK);
|
||||
|
||||
mc->mc_Next = NULL;
|
||||
mc->mc_Bytes = puddle_size;
|
||||
|
||||
/* Both ln_Type and ln_Pri must be non-zero! This allows them to
|
||||
* be identified as small puddles as compared to the large puddles
|
||||
* which have the la_Signature member set to IS_LARGE_ALLOCATION.
|
||||
* The 'struct Node' which introduces the 'struct MemHeader'
|
||||
* overlaps the 'struct LargeAllocation' in the Node.ln_Type/ln_Pri
|
||||
* fields. This is why the test for la_Signature == IS_LARGE_ALLOCATION
|
||||
* works.
|
||||
*/
|
||||
mh->mh_Node.ln_Type = NT_MEMORY;
|
||||
mh->mh_Node.ln_Pri = mh->mh_Node.ln_Type;
|
||||
|
||||
mh->mh_Node.ln_Name = (char *)&"\0Pool"[1]; /* The memory name must be an odd address. */
|
||||
mh->mh_Attributes = memory_flags;
|
||||
mh->mh_First = mc;
|
||||
mh->mh_Lower = mc;
|
||||
mh->mh_Upper = &((BYTE *)mc)[puddle_size];
|
||||
mh->mh_Free = puddle_size;
|
||||
|
||||
/* Puddles are always added at the head of the list. */
|
||||
AddHead((struct List *)&mp->mp_PuddleMinList, &pu->pu_Node);
|
||||
|
||||
result = pu;
|
||||
|
||||
out:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Note: No overflow testing is performed when making a large allocation. */
|
||||
APTR LibAllocPooled(APTR pool, ULONG mem_size)
|
||||
{
|
||||
struct MemoryPool * mp;
|
||||
union PuddleUnion * pu;
|
||||
APTR result = NULL;
|
||||
|
||||
/* If possible, use the operating system implementation
|
||||
* instead of this reimplementation.
|
||||
*/
|
||||
#if ! defined(__amigaos4__)
|
||||
if (SysBase->LibNode.lib_Version >= 39)
|
||||
{
|
||||
return AllocPooled(pool, mem_size);
|
||||
}
|
||||
#endif /* ! defined(__amigaos4__) */
|
||||
|
||||
/* No pool or no memory to allocate? */
|
||||
if (pool == NULL || mem_size == 0)
|
||||
goto out;
|
||||
|
||||
mp = pool;
|
||||
|
||||
/* Requested allocation size is still within the threshold limit? */
|
||||
if (mem_size <= mp->mp_PuddleSizeThreshold)
|
||||
{
|
||||
/* Search for the first puddle from which memory may be
|
||||
* allocated. If no such puddle exists, it will have
|
||||
* to be created. Instead of puddles we might end up
|
||||
* finding a large memory allocation instead. This is
|
||||
* also an indication that a new puddle will have to
|
||||
* be created.
|
||||
*/
|
||||
pu = (union PuddleUnion *)mp->mp_PuddleMinList.mlh_Head;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
/* We just reached the end of the puddle list or this
|
||||
* is a large allocation? We can't use an existing puddle to
|
||||
* allocate memory, so we have to make a new one.
|
||||
*/
|
||||
if (pu->pu_Node.ln_Succ == NULL || pu->pu_LargeAllocation.la_Signature == IS_LARGE_ALLOCATION)
|
||||
{
|
||||
/* We need to create another puddle, which is added to
|
||||
* the head of the list. Then the search for allocatable
|
||||
* memory in the puddle list will resume there.
|
||||
*/
|
||||
pu = create_new_puddle(mp);
|
||||
if (pu == NULL)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try to allocate memory from this puddle.*/
|
||||
result = Allocate(&pu->pu_MemHeader, mem_size);
|
||||
if (result != NULL)
|
||||
{
|
||||
/* If the allocation needs to be zeroed, clear
|
||||
* the entire allocated memory which Allocate()
|
||||
* returned. It's been rounded up to be a
|
||||
* multiple of MEM_BLOCKSIZE.
|
||||
*
|
||||
* Note that the puddle will have been allocated
|
||||
* with MEMF_CLEAR already, which does not render
|
||||
* the following memset() call redundant, but
|
||||
* making the initial puddle allocation use
|
||||
* MEMF_CLEAR is definitely redundant.
|
||||
*/
|
||||
if ((mp->mp_MemoryFlags & MEMF_CLEAR) != 0)
|
||||
memset(result, 0, (mem_size + MEM_BLOCKMASK) & ~MEM_BLOCKMASK);
|
||||
|
||||
/* And we're good. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Try the next puddle on the list. */
|
||||
pu = (union PuddleUnion *)pu->pu_Node.ln_Succ;
|
||||
}
|
||||
}
|
||||
/* No, we need to allocate this memory chunk separately. */
|
||||
else
|
||||
{
|
||||
struct LargeAllocation * la;
|
||||
|
||||
/* Note: no overflow checking is performed! */
|
||||
pu = alloc_vec(sizeof(pu->pu_LargeAllocation) + mem_size, mp->mp_MemoryFlags);
|
||||
if (pu == NULL)
|
||||
goto out;
|
||||
|
||||
/* This identifies it as a separate large allocation. */
|
||||
pu->pu_LargeAllocation.la_Signature = IS_LARGE_ALLOCATION;
|
||||
|
||||
/* The separate large allocations are stored near the end
|
||||
* of the list. The puddles are always stored at the beginning
|
||||
* of the list.
|
||||
*/
|
||||
AddTail((struct List *)&mp->mp_PuddleMinList, &pu->pu_Node);
|
||||
|
||||
la = &pu->pu_LargeAllocation;
|
||||
|
||||
result = &la[1];
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Note: The size of the allocation to free must match
|
||||
* exactly or memory will remain unfreed. Furthermore,
|
||||
* the wrong allocation size may lead to general
|
||||
* memory corruption.
|
||||
*/
|
||||
VOID LibFreePooled(APTR pool, APTR memory, ULONG size)
|
||||
{
|
||||
/* If possible, use the operating system implementation
|
||||
* instead of this reimplementation.
|
||||
*/
|
||||
#if ! defined(__amigaos4__)
|
||||
if (SysBase->LibNode.lib_Version >= 39)
|
||||
{
|
||||
FreePooled(pool, memory, size);
|
||||
return;
|
||||
}
|
||||
#endif /* ! defined(__amigaos4__) */
|
||||
|
||||
if (pool != NULL && memory != NULL)
|
||||
{
|
||||
struct MemoryPool * mp = pool;
|
||||
|
||||
/* This allocation was made from a puddle? */
|
||||
if (size <= mp->mp_PuddleSizeThreshold)
|
||||
{
|
||||
union PuddleUnion * pu_deallocation = NULL;
|
||||
union PuddleUnion * pu;
|
||||
|
||||
/* Try to find the puddle which contains this
|
||||
* allocation.
|
||||
*/
|
||||
for (pu = (union PuddleUnion *)mp->mp_PuddleMinList.mlh_Head ;
|
||||
pu->pu_Node.ln_Succ != NULL ;
|
||||
pu = (union PuddleUnion *)pu->pu_Node.ln_Succ)
|
||||
{
|
||||
/* Did we reach the end of the puddle list? Then
|
||||
* the given memory address and/or size may be
|
||||
* invalid...
|
||||
*/
|
||||
if (pu->pu_LargeAllocation.la_Signature == IS_LARGE_ALLOCATION)
|
||||
break;
|
||||
|
||||
/* Is this the memory header which contains the
|
||||
* memory allocation?
|
||||
*/
|
||||
if (pu->pu_MemHeader.mh_Lower <= memory && memory < pu->pu_MemHeader.mh_Upper)
|
||||
{
|
||||
/* Free the allocation and remember where it
|
||||
* came from. We will make use of this below to
|
||||
* free now unused puddles.
|
||||
*/
|
||||
Deallocate(&pu->pu_MemHeader, memory, size);
|
||||
|
||||
pu_deallocation = pu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Did we succeed in releasing this memory allocation? */
|
||||
if (pu_deallocation != NULL)
|
||||
{
|
||||
APTR upper;
|
||||
|
||||
/* The more frequently memory is freed from a
|
||||
* specific puddle, the easier it should become
|
||||
* to find it on the list again. We try to move it
|
||||
* closer to the beginning of the list unless it
|
||||
* is already at the head of the list.
|
||||
*/
|
||||
if (mp->mp_PuddleMinList.mlh_Head != (struct MinNode *)pu)
|
||||
{
|
||||
/* This puddle precedes the one from which we
|
||||
* just released an allocation.
|
||||
*/
|
||||
struct Node * pred = pu->pu_Node.ln_Pred;
|
||||
struct Node * before;
|
||||
|
||||
/* Put this puddle at the head of the list? */
|
||||
if (pred == (struct Node *)mp->mp_PuddleMinList.mlh_Head)
|
||||
before = NULL;
|
||||
/* No, insert it in front of its predecessor. */
|
||||
else
|
||||
before = pred->ln_Pred;
|
||||
|
||||
Remove(&pu->pu_Node);
|
||||
Insert((struct List *)&mp->mp_PuddleMinList, &pu->pu_Node, before);
|
||||
}
|
||||
|
||||
/* Has this puddle been emptied? */
|
||||
upper = &((BYTE *)pu->pu_MemHeader.mh_Lower)[pu->pu_MemHeader.mh_Free];
|
||||
if (upper == pu->pu_MemHeader.mh_Upper)
|
||||
{
|
||||
/* Then we may remove and free it now. */
|
||||
Remove(&pu->pu_Node);
|
||||
|
||||
free_vec(pu);
|
||||
}
|
||||
}
|
||||
/* Something's wrong :-( */
|
||||
else
|
||||
{
|
||||
Alert(AN_BadFreeAddr);
|
||||
}
|
||||
}
|
||||
/* No, this is a large allocation which must be freed as it is. */
|
||||
else
|
||||
{
|
||||
struct LargeAllocation * la = &((struct LargeAllocation *)memory)[-1];
|
||||
|
||||
Remove((struct Node *)&la->la_MinNode);
|
||||
|
||||
free_vec(la);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
#define VERSION 1
|
||||
#define REVISION 215
|
||||
#define DATE "26.6.2017"
|
||||
#define VERS "c.lib 1.215"
|
||||
#define VSTRING "c.lib 1.215 (26.6.2017)\r\n"
|
||||
#define VERSTAG "\0$VER: c.lib 1.215 (26.6.2017)"
|
||||
#define REVISION 217
|
||||
#define DATE "10.7.2025"
|
||||
#define VERS "c.lib 1.217"
|
||||
#define VSTRING "c.lib 1.217 (10.7.2025)\r\n"
|
||||
#define VERSTAG "\0$VER: c.lib 1.217 (10.7.2025)"
|
||||
|
||||
@ -1 +1 @@
|
||||
215
|
||||
217
|
||||
|
||||
139
library/changes
139
library/changes
@ -1,6 +1,139 @@
|
||||
c.lib 1.217 (10.7.2025)
|
||||
|
||||
- Added support for handling path names that exceed 255 characters in
|
||||
the AmigaOS 2.x/3.x build. This feature works by replacing calls to the
|
||||
CreateDir(), DeleteFile(), Lock(), MakeLink(), Open(), Rename(),
|
||||
SetComment(), SetFileDate(), SetOwner() and SetProtection() functions
|
||||
with a call to a front-end which breaks down long path names into
|
||||
the individual path components. This is a compile-time option which
|
||||
is enabled through the "library/stdio_long_path.h" header file.
|
||||
|
||||
Note that this feature is by default disabled for AmigaOS 4 because
|
||||
its dos.library already transparently performs the long path
|
||||
processing.
|
||||
|
||||
- The raise() function no longer directly calls abort() because that
|
||||
function in turn would have invoked raise(SIGABRT), triggering an
|
||||
infinite loop. Instead, raise() now invokes the __abort() function
|
||||
which abort() would have called via raise(SIGABRT).
|
||||
|
||||
- fgets() no longer leverages the __getc() macro, which in the
|
||||
original implementation added excessive overhead for even small
|
||||
read operations. Instead, fgets() now focuses on copying data
|
||||
stored in the FILE read buffer, transferring the buffered data
|
||||
in bulk, if possible. The same kind of changes are present in
|
||||
the gets() function.
|
||||
|
||||
- fputs() and puts() no longer leverage the __putc() macro. As with
|
||||
the fgets() implementation, the focus is now on enabling bulk
|
||||
data transfer, eliminating the __putc() macro overhead. For line
|
||||
buffered output, both fputs() and puts() will check if the text
|
||||
ends with a line feed and, if so, will output everything up to
|
||||
and including the line feed. If enabled, Ctrl+C checking will
|
||||
occur for every single line written.
|
||||
|
||||
- fread() and fwrite() no longer leverage the __getc() and
|
||||
__putc() macros. Their focus is on enabling bulk data transfer
|
||||
instead.
|
||||
|
||||
- The setvbuf() function now correctly sets the buffer size to the
|
||||
value of BUFSIZ if a buffer size of 0 is specified. Previously,
|
||||
setvbuf() would have disabled buffering for the FILE instead (this
|
||||
was a bug).
|
||||
|
||||
Failure to allocate a buffer of the specified non-zero size is
|
||||
now caught and results in the setvbuf() function to indicate
|
||||
failure, setting the errno variable to ENOBUFS.
|
||||
|
||||
- The alloca() library function (which is a built-in function for
|
||||
gcc, for example) now calls abort() rather than returning NULL.
|
||||
|
||||
- Changed the numeric overflow check which the calloc() function
|
||||
uses, simplifying it.
|
||||
|
||||
- The memory management debugging code in stdlib_free.c is no longer
|
||||
reusing the allocation size value to indicate that an allocation
|
||||
should never be freed. This indication is now stored in a separate
|
||||
flags field.
|
||||
|
||||
- The memory debugging code in malloc() now checks if the combined
|
||||
size of the management data structures and front/back padding
|
||||
stay within the bounds of a 32 bit integer.
|
||||
|
||||
The list of memory nodes to be freed is now initialized first.
|
||||
Previously, the list would be left uninitialized if the attempt to
|
||||
allocate memory for memory arbitration semaphore had failed.
|
||||
|
||||
- The realloc() function now obtains the original size of the
|
||||
allocation from a dedicated field of the data structure which
|
||||
precedes the allocation, whose purpose is to track the
|
||||
allocation until it is freed. Previously, obtaining the relevant
|
||||
allocation size information was encapsulated by a "clever"
|
||||
macro.
|
||||
|
||||
With memory debugging enabled, the stored allocation size no
|
||||
longer says how much memory, padding included, is being used.
|
||||
Instead, the requested allocation size is stored unchanged, with
|
||||
the total allocation size derived from this number. The idea is
|
||||
to make it harder to mistake the original allocation size for
|
||||
the padded one and vice versa.
|
||||
|
||||
- The use of the optional slab allocator now has to be deliberately
|
||||
enabled in the "library/stdlib_memory.h" header file.
|
||||
|
||||
- The minimum size of an allocation managed by the optional slab
|
||||
allocator is now 16 bytes instead of 8 because the management
|
||||
data structure for each allocation now includes a flags field.
|
||||
|
||||
- The minimum size of a slab is now 4096 bytes.
|
||||
|
||||
- The optional slab allocator now checks for integer overflows when
|
||||
processing allocation requests, taking into account the size of
|
||||
the actual allocation, including padding. The task of figuring
|
||||
out how much padding is needed now rests in a function which
|
||||
calculates the total overhead.
|
||||
|
||||
- The usleep() function now correctly returns 0 and the function
|
||||
prototype reflects this, too. Thanks go to capehill who raised
|
||||
this issue.
|
||||
|
||||
- The "struct dirent" which is used by the readdir() function now
|
||||
features a "d_type" member which indicates the kind of directory
|
||||
entry the respective entry represents. For the time being,
|
||||
the type can be one of DT_DIR (directory, or hard link to a
|
||||
directory), DT_REG (file, or hard link to a file) or DT_LINK (soft
|
||||
link). How do you know if the "d_type" member is available?
|
||||
Check if the _DIRENT_HAVE_D_TYPE macro is defined. If it is, then
|
||||
you can make use of the "d_type" member.
|
||||
|
||||
- Implemented the fixes for the tgamma() and tgammaf() functions
|
||||
which were prompted by sodero. Thank you very much!
|
||||
|
||||
- The sqrt() function now returns a plain NAN if the function
|
||||
parameter is < 0. Thanks go to sodero for providing the fix.
|
||||
|
||||
- The definition of "locale_t" is now based upon an incomplete
|
||||
structure, with locale_t being a pointer to it. This change was
|
||||
was provided by sacredbanana. Thank you very much!
|
||||
|
||||
|
||||
amiga.lib 1.216 (10.7.2025)
|
||||
|
||||
- Added a 'C' implementation of the pools.lib functions which were
|
||||
merged with the Kickstart/Workbench 3.0 era amiga.lib. This
|
||||
implementation provides the LibDeletePool(), LibCreatePool(),
|
||||
LibAllocPooled() and LibFreePooled() functions which had not
|
||||
been available before in clib2.
|
||||
|
||||
Please note that these functions are largely obsolete and only
|
||||
helpful for code which must run on Kickstart 2.04. The current
|
||||
implementation performs no error checking for integer overflows,
|
||||
just like the original pools.lib implementation.
|
||||
|
||||
|
||||
c.lib 1.216 (xxx)
|
||||
|
||||
- Add some wchar and multbyte-string related functions to allow gcc
|
||||
- Add some wchar and multibyte-string related functions to allow gcc
|
||||
building a libstdc++ library with wide char support. For now, the
|
||||
functions are mostly stub ones only. They can be implemented on
|
||||
demand.
|
||||
@ -177,7 +310,7 @@ c.lib 1.206 (24.4.2015)
|
||||
|
||||
- Removed the remains of all the stack extension and stack overflow/underflow
|
||||
checking code. It never actually worked. The bit that does work is the stack
|
||||
usage measurement code, plus the bit that sets up the the custom stack
|
||||
usage measurement code, plus the bit that sets up the custom stack
|
||||
according to local setting or by calling a query function.
|
||||
|
||||
|
||||
@ -1355,7 +1488,7 @@ c.lib 1.187 (29.1.2005)
|
||||
adding/subtracting the local time zone.
|
||||
|
||||
- Changed the algorithm that calculates the number of days that have passed
|
||||
so far as used by the the __convert_time() function and the conversion
|
||||
so far as used by the __convert_time() function and the conversion
|
||||
code in strftime().
|
||||
|
||||
- Also changed the algorithm used by strftime() to produce the week numbers
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: dirent_readdir.c,v 1.10 2006-09-25 14:51:15 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2025 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -79,9 +77,11 @@ readdir(DIR * directory_pointer)
|
||||
|
||||
dh->dh_Position++;
|
||||
|
||||
dh->dh_DirectoryEntry.d_ino = 0;
|
||||
strcpy(dh->dh_DirectoryEntry.d_name,".");
|
||||
|
||||
dh->dh_DirectoryEntry.d_ino = 0;
|
||||
dh->dh_DirectoryEntry.d_type = DT_DIR;
|
||||
|
||||
result = &dh->dh_DirectoryEntry;
|
||||
}
|
||||
else
|
||||
@ -115,8 +115,9 @@ readdir(DIR * directory_pointer)
|
||||
assert( sizeof(dh->dh_DirectoryEntry.d_name) >= sizeof(fib->fib_FileName) );
|
||||
|
||||
strcpy(dh->dh_DirectoryEntry.d_name,fib->fib_FileName);
|
||||
|
||||
|
||||
dh->dh_DirectoryEntry.d_ino = fib->fib_DiskKey;
|
||||
dh->dh_DirectoryEntry.d_type = DT_DIR;
|
||||
|
||||
result = &dh->dh_DirectoryEntry;
|
||||
}
|
||||
@ -147,10 +148,11 @@ readdir(DIR * directory_pointer)
|
||||
|
||||
dh->dh_Position++;
|
||||
|
||||
dh->dh_DirectoryEntry.d_ino = dh->dh_FileInfo.fib_DiskKey;
|
||||
|
||||
strcpy(dh->dh_DirectoryEntry.d_name,".");
|
||||
|
||||
dh->dh_DirectoryEntry.d_ino = dh->dh_FileInfo.fib_DiskKey;
|
||||
dh->dh_DirectoryEntry.d_type = DT_DIR;
|
||||
|
||||
result = &dh->dh_DirectoryEntry;
|
||||
}
|
||||
else if (dh->dh_Position == 1)
|
||||
@ -176,10 +178,11 @@ readdir(DIR * directory_pointer)
|
||||
|
||||
SHOWMSG("returning ..");
|
||||
|
||||
dh->dh_DirectoryEntry.d_ino = fib->fib_DiskKey;
|
||||
|
||||
strcpy(dh->dh_DirectoryEntry.d_name,"..");
|
||||
|
||||
dh->dh_DirectoryEntry.d_ino = fib->fib_DiskKey;
|
||||
dh->dh_DirectoryEntry.d_type = DT_DIR;
|
||||
|
||||
result = &dh->dh_DirectoryEntry;
|
||||
}
|
||||
}
|
||||
@ -192,12 +195,23 @@ readdir(DIR * directory_pointer)
|
||||
|
||||
if(ExNext(dh->dh_DirLock,&dh->dh_FileInfo))
|
||||
{
|
||||
dh->dh_DirectoryEntry.d_ino = dh->dh_FileInfo.fib_DiskKey;
|
||||
|
||||
int type;
|
||||
|
||||
assert( sizeof(dh->dh_DirectoryEntry.d_name) >= sizeof(dh->dh_FileInfo.fib_FileName) );
|
||||
|
||||
strcpy(dh->dh_DirectoryEntry.d_name,dh->dh_FileInfo.fib_FileName);
|
||||
|
||||
dh->dh_DirectoryEntry.d_ino = dh->dh_FileInfo.fib_DiskKey;
|
||||
|
||||
if (dh->dh_FileInfo.fib_DirEntryType == ST_SOFTLINK)
|
||||
type = DT_LNK;
|
||||
else if (dh->dh_FileInfo.fib_DirEntryType < 0)
|
||||
type = DT_REG;
|
||||
else
|
||||
type = DT_DIR;
|
||||
|
||||
dh->dh_DirectoryEntry.d_type = type;
|
||||
|
||||
result = &dh->dh_DirectoryEntry;
|
||||
}
|
||||
else
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: dirent.h,v 1.7 2006-01-08 12:06:14 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2025 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -71,12 +69,41 @@ typedef long DIR;
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* The POSIX requirements for the 'struct dirent' cover little more than
|
||||
* the 'd_ino' and 'd_name' fields. Because it is useful, the 'd_type'
|
||||
* field identifies the type of object in question. This was added with
|
||||
* clib2 1.217 (10.7.2025), and you can find out if the 'd_type' field
|
||||
* is available in the 'struct dirent' by checking if the
|
||||
* _DIRENT_HAVE_D_TYPE macro is defined.
|
||||
*/
|
||||
struct dirent
|
||||
{
|
||||
ino_t d_ino;
|
||||
char d_name[NAME_MAX+1];
|
||||
int d_type;
|
||||
};
|
||||
|
||||
#define _DIRENT_HAVE_D_TYPE
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Note that the directory entry type is not a POSIX feature, but it will
|
||||
* make life easier for porting code which expects the 'd_type' structure
|
||||
* member in the 'struct dirent'.
|
||||
*
|
||||
* The following types are not all supported on the Amiga. For the time being,
|
||||
* DT_DIR (directory), DT_REG (regular file) and DT_LNK (soft link) should make
|
||||
* sense.
|
||||
*/
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_FIFO 1
|
||||
#define DT_CHR 2
|
||||
#define DT_DIR 4
|
||||
#define DT_BLK 6
|
||||
#define DT_REG 8
|
||||
#define DT_LNK 10
|
||||
#define DT_SOCK 12
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
extern DIR * opendir(const char * path_name);
|
||||
|
||||
@ -166,6 +166,8 @@ extern int errno;
|
||||
|
||||
#define EILSEQ 85 /* Encoding error detected */
|
||||
|
||||
#define ENOTSUP 86 /* Not supported */
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: locale.h,v 1.5 2006-01-08 12:06:14 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2025 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -59,7 +57,10 @@ extern "C" {
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
typedef void *locale_t;
|
||||
/* Forward declaration */
|
||||
struct __locale_t;
|
||||
|
||||
typedef struct __locale_t *locale_t;
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
|
||||
@ -369,7 +369,7 @@ void __get_slab_allocations(__slab_allocation_callback callback);
|
||||
* You supply a function which will be called for each line of the JSON
|
||||
* data produced. You can store this data in a file, or in the clipboard,
|
||||
* for later use. Your function must return 0 if it wants to be called
|
||||
* again, or return -1 if it wants to stop (e.g. if an error occured
|
||||
* again, or return -1 if it wants to stop (e.g. if an error occurred
|
||||
* when writing the JSON data to disk). The same "user_data" pointer which
|
||||
* you pass to __get_slab_stats() will be passed to your callback function.
|
||||
*
|
||||
|
||||
@ -117,7 +117,7 @@ extern int readlink(const char * path_name, char * buffer, int buffer_size);
|
||||
extern int chdir(const char * path_name);
|
||||
extern int lockf(int file_descriptor, int function, off_t size);
|
||||
extern unsigned int sleep(unsigned int seconds);
|
||||
extern void usleep(unsigned long microseconds);
|
||||
extern int usleep(unsigned long microseconds);
|
||||
extern int getopt(int argc, char * const argv[], const char *opts);
|
||||
extern pid_t getpid(void);
|
||||
extern char *realpath(const char *file_name, char *resolved_name);
|
||||
|
||||
@ -46,6 +46,7 @@ AMIGA_LIB = \
|
||||
amiga_hotkey.o \
|
||||
amiga_invertstring.o \
|
||||
amiga_newlist.o \
|
||||
amiga_pools.o \
|
||||
amiga_rangerand.o \
|
||||
amiga_remtof.o \
|
||||
amiga_rexxvars.o \
|
||||
|
||||
@ -19,6 +19,10 @@ LIBS += \
|
||||
|
||||
##############################################################################
|
||||
|
||||
# Olaf (2019-08-22): Please note that "profile_profil.o" can no longer
|
||||
# be built, presumably for lack of header files needed
|
||||
# to build it properly.
|
||||
|
||||
C_LIB := \
|
||||
c.lib_rev.o \
|
||||
ctype_isalnum.o \
|
||||
@ -66,7 +70,6 @@ C_LIB := \
|
||||
mount_convertinfo.o \
|
||||
mount_fstatfs.o \
|
||||
mount_statfs.o \
|
||||
profile_profil.o \
|
||||
signal_checkabort.o \
|
||||
signal_data.o \
|
||||
signal_kill.o \
|
||||
|
||||
@ -22,13 +22,13 @@ LIBS += \
|
||||
UNIX_LIB := \
|
||||
unix.lib_rev.o \
|
||||
dirent_closedir.o \
|
||||
dirent_rewinddir.o \
|
||||
dirent_opendir.o \
|
||||
dirent_readdir.o \
|
||||
dirent_rewinddir.o \
|
||||
fcntl_creat.o \
|
||||
fcntl_fcntl.o \
|
||||
fcntl_open.o \
|
||||
fcntl_get_default_file.o \
|
||||
fcntl_open.o \
|
||||
getopt_getopt_long.o \
|
||||
mount_convertinfo.o \
|
||||
mount_statfs.o \
|
||||
@ -36,16 +36,16 @@ UNIX_LIB := \
|
||||
resource_setrlimit.o \
|
||||
stat_chmod.o \
|
||||
stat_fstat.o \
|
||||
stat_lstat.o \
|
||||
stat_lock.o \
|
||||
stat_lstat.o \
|
||||
stat_mkdir.o \
|
||||
stat_rmdir.o \
|
||||
stat_stat.o \
|
||||
stdio_ctermid.o \
|
||||
stdio_fdhookentry.o \
|
||||
stdio_fflush.o \
|
||||
stdio_fopen.o \
|
||||
stdio_file_init.o \
|
||||
stdio_fopen.o \
|
||||
stdio_init_exit.o \
|
||||
stdio_locksemaphorename.o \
|
||||
stdio_openiob.o \
|
||||
@ -60,15 +60,17 @@ UNIX_LIB := \
|
||||
stdlib_dlopen.o \
|
||||
stdlib_expand_wildcard.o \
|
||||
stdlib_expand_wildcard_check.o \
|
||||
stdlib_free.o \
|
||||
stdlib_getmemstats.o \
|
||||
stdlib_main.o \
|
||||
stdlib_main_stub.o \
|
||||
stdlib_malloc.o \
|
||||
stdlib_mkdtemp.o \
|
||||
stdlib_mkstemp.o \
|
||||
stdlib_mktemp.o \
|
||||
stdlib_malloc.o \
|
||||
stdlib_realloc.o \
|
||||
stdlib_resetmemstats.o \
|
||||
stdlib_slab.o \
|
||||
stdlib_system.o \
|
||||
systeminfo_sysinfo.o \
|
||||
termios_cfgetispeed.o \
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: math_sqrt.c,v 1.9 2006-09-22 07:54:24 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2025 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -251,7 +249,8 @@ sqrt(double x)
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 0;
|
||||
result = NAN;
|
||||
|
||||
__set_errno(EDOM);
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: math_tgamma.c,v 1.3 2006-01-08 12:04:24 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2025 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -54,13 +52,11 @@ double
|
||||
tgamma(double x)
|
||||
{
|
||||
int gamma_sign;
|
||||
double y;
|
||||
double y;
|
||||
|
||||
y = __lgamma(x,&gamma_sign);
|
||||
if (gamma_sign < 0)
|
||||
y = -y;
|
||||
y = __lgamma(x, &gamma_sign);
|
||||
|
||||
return y;
|
||||
return gamma_sign * exp(y);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
/*
|
||||
* $Id: math_tgammaf.c,v 1.3 2006-01-08 12:04:24 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
@ -56,11 +54,9 @@ tgammaf(float x)
|
||||
int gamma_sign;
|
||||
float y;
|
||||
|
||||
y = __lgammaf(x,&gamma_sign);
|
||||
if (gamma_sign < 0)
|
||||
y = -y;
|
||||
y = __lgammaf(x, &gamma_sign);
|
||||
|
||||
return y;
|
||||
return gamma_sign * expf(y);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
@ -71,7 +71,7 @@ raise(int sig)
|
||||
assert( SIGABRT <= sig && sig <= SIGTERM );
|
||||
|
||||
/* This has to be a well-known and supported signal. */
|
||||
if(sig < SIGABRT || sig > SIGTERM)
|
||||
if (sig < SIGABRT || sig > SIGTERM)
|
||||
{
|
||||
SHOWMSG("unknown signal number");
|
||||
|
||||
@ -80,48 +80,49 @@ raise(int sig)
|
||||
}
|
||||
|
||||
/* Can we deliver the signal? */
|
||||
if(FLAG_IS_CLEAR(__signals_blocked, (1 << sig)) &&
|
||||
FLAG_IS_CLEAR(local_signals_blocked, (1 << sig)))
|
||||
if (FLAG_IS_CLEAR(__signals_blocked, (1 << sig)) &&
|
||||
FLAG_IS_CLEAR(local_signals_blocked, (1 << sig)))
|
||||
{
|
||||
signal_handler_t handler;
|
||||
|
||||
/* Which handler is installed for this signal? */
|
||||
handler = __signal_handler_table[sig - SIGABRT];
|
||||
|
||||
/* Should we ignore this signal? */
|
||||
if(handler != SIG_IGN)
|
||||
/* Should we handle this signal rather than ignoring it? */
|
||||
if (handler != SIG_IGN)
|
||||
{
|
||||
/* Block delivery of this signal to prevent recursion. */
|
||||
SET_FLAG(local_signals_blocked,(1 << sig));
|
||||
SET_FLAG(local_signals_blocked, (1 << sig));
|
||||
|
||||
/* The default behaviour is to drop into abort(), or do
|
||||
something very much like it. */
|
||||
if(handler == SIG_DFL)
|
||||
* something very much like it.
|
||||
*/
|
||||
if (handler == SIG_DFL)
|
||||
{
|
||||
SHOWMSG("this is the default handler");
|
||||
|
||||
if(sig == SIGINT)
|
||||
if (sig == SIGINT)
|
||||
{
|
||||
char break_string[80];
|
||||
|
||||
/* Turn off ^C checking for good. */
|
||||
__check_abort_enabled = FALSE;
|
||||
|
||||
Fault(ERROR_BREAK,NULL,break_string,(LONG)sizeof(break_string));
|
||||
Fault(ERROR_BREAK, NULL, break_string, (LONG)sizeof(break_string));
|
||||
|
||||
__print_termination_message(break_string);
|
||||
|
||||
SHOWMSG("bye, bye...");
|
||||
}
|
||||
|
||||
/* Drop straight into abort(), which might call signal()
|
||||
again but is otherwise guaranteed to eventually
|
||||
land us in _exit(). */
|
||||
abort();
|
||||
/* Drop straight into __abort(), which will
|
||||
eventually land us in _exit(). Note that
|
||||
abort() calls raise(SIGABRT). */
|
||||
__abort();
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
SHOWMSG("calling the handler");
|
||||
SHOWMSG("calling the signal handler");
|
||||
|
||||
(*handler)(sig);
|
||||
|
||||
@ -129,7 +130,7 @@ raise(int sig)
|
||||
}
|
||||
|
||||
/* Unblock signal delivery again. */
|
||||
CLEAR_FLAG(local_signals_blocked,(1 << sig));
|
||||
CLEAR_FLAG(local_signals_blocked, (1 << sig));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: stdio_fgets.c,v 1.6 2006-01-08 12:04:24 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2019 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -46,6 +44,7 @@
|
||||
char *
|
||||
fgets(char *s,int n,FILE *stream)
|
||||
{
|
||||
struct iob * file = (struct iob *)stream;
|
||||
char * result = s;
|
||||
int c;
|
||||
|
||||
@ -57,14 +56,14 @@ fgets(char *s,int n,FILE *stream)
|
||||
|
||||
assert( s != NULL && stream != NULL );
|
||||
|
||||
if(__check_abort_enabled)
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
flockfile(stream);
|
||||
|
||||
#if defined(CHECK_FOR_NULL_POINTERS)
|
||||
{
|
||||
if(s == NULL || stream == NULL)
|
||||
if (s == NULL || stream == NULL)
|
||||
{
|
||||
SHOWMSG("invalid parameters");
|
||||
|
||||
@ -75,7 +74,7 @@ fgets(char *s,int n,FILE *stream)
|
||||
}
|
||||
#endif /* CHECK_FOR_NULL_POINTERS */
|
||||
|
||||
if(n <= 0)
|
||||
if (n <= 0)
|
||||
{
|
||||
SHOWMSG("no work to be done");
|
||||
|
||||
@ -86,7 +85,7 @@ fgets(char *s,int n,FILE *stream)
|
||||
/* Take care of the checks and data structure changes that
|
||||
* need to be handled only once for this stream.
|
||||
*/
|
||||
if(__fgetc_check(stream) < 0)
|
||||
if (__fgetc_check(stream) < 0)
|
||||
{
|
||||
result = NULL;
|
||||
goto out;
|
||||
@ -98,12 +97,83 @@ fgets(char *s,int n,FILE *stream)
|
||||
/* One off for the terminating '\0'. */
|
||||
n--;
|
||||
|
||||
while(n-- > 0)
|
||||
assert( 0 <= file->iob_BufferReadBytes );
|
||||
assert( file->iob_BufferReadBytes <= file->iob_BufferSize );
|
||||
assert( file->iob_BufferPosition <= file->iob_BufferSize );
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
c = __getc(stream);
|
||||
if(c == EOF)
|
||||
/* If there is data in the buffer, try to copy it directly
|
||||
* into the string buffer. If there is a line feed in the
|
||||
* buffer, too, try to conclude the read operation.
|
||||
*/
|
||||
if (file->iob_BufferPosition < file->iob_BufferReadBytes)
|
||||
{
|
||||
if(ferror(stream))
|
||||
const unsigned char * buffer = &file->iob_Buffer[file->iob_BufferPosition];
|
||||
size_t num_bytes_in_buffer;
|
||||
const unsigned char * lf;
|
||||
|
||||
/* Copy only as much data as will fit into the string buffer. */
|
||||
assert( file->iob_BufferReadBytes >= file->iob_BufferPosition );
|
||||
|
||||
num_bytes_in_buffer = file->iob_BufferReadBytes - file->iob_BufferPosition;
|
||||
if (num_bytes_in_buffer > (size_t)n)
|
||||
num_bytes_in_buffer = n;
|
||||
|
||||
/* Try to find a line feed character which could conclude
|
||||
* the read operation if the remaining buffer data, including
|
||||
* the line feed character, fit into the string buffer.
|
||||
*/
|
||||
lf = (unsigned char *)memchr(buffer, '\n', num_bytes_in_buffer);
|
||||
if(lf != NULL)
|
||||
{
|
||||
size_t num_characters_in_line = lf + 1 - buffer;
|
||||
|
||||
/* Give the user a chance to abort what could otherwise
|
||||
* become an uninterrupted series of copying operations.
|
||||
*/
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
assert( num_characters_in_line <= num_bytes_in_buffer );
|
||||
|
||||
/* Copy the remainder of the read buffer into the
|
||||
* string buffer, including the terminating line
|
||||
* feed character.
|
||||
*/
|
||||
memmove(s, buffer, num_characters_in_line);
|
||||
s += num_characters_in_line;
|
||||
|
||||
assert( file->iob_BufferPosition + num_characters_in_line <= file->iob_BufferSize );
|
||||
|
||||
file->iob_BufferPosition += num_characters_in_line;
|
||||
|
||||
/* And that concludes the line read operation. */
|
||||
break;
|
||||
}
|
||||
|
||||
memmove(s, buffer, num_bytes_in_buffer);
|
||||
s += num_bytes_in_buffer;
|
||||
|
||||
assert( file->iob_BufferPosition + num_bytes_in_buffer <= file->iob_BufferSize );
|
||||
|
||||
file->iob_BufferPosition += num_bytes_in_buffer;
|
||||
|
||||
/* Stop if the string buffer has been filled. */
|
||||
assert( n >= num_bytes_in_buffer );
|
||||
|
||||
n -= num_bytes_in_buffer;
|
||||
if (n == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the next buffered character; this will refill the read
|
||||
* buffer, if necessary.
|
||||
*/
|
||||
c = __getc(stream);
|
||||
if (c == EOF)
|
||||
{
|
||||
if (ferror(stream))
|
||||
{
|
||||
/* Just to be on the safe side. */
|
||||
(*s) = '\0';
|
||||
@ -113,8 +183,9 @@ fgets(char *s,int n,FILE *stream)
|
||||
}
|
||||
|
||||
/* Make sure that we return NULL if we really
|
||||
didn't read anything at all */
|
||||
if(s == result)
|
||||
* didn't read anything at all.
|
||||
*/
|
||||
if (s == result)
|
||||
result = NULL;
|
||||
|
||||
break;
|
||||
@ -122,8 +193,11 @@ fgets(char *s,int n,FILE *stream)
|
||||
|
||||
(*s++) = c;
|
||||
|
||||
if(c == '\n')
|
||||
if (c == '\n')
|
||||
break;
|
||||
|
||||
assert( n > 0 );
|
||||
n--;
|
||||
}
|
||||
|
||||
(*s) = '\0';
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: stdio_fputs.c,v 1.7 2006-01-08 12:04:24 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2019 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -43,10 +41,12 @@
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Both fputs() and puts() share this function. */
|
||||
int
|
||||
fputs(const char *s, FILE *stream)
|
||||
__fputs(const char *s, int line_feed, FILE *stream)
|
||||
{
|
||||
struct iob * file = (struct iob *)stream;
|
||||
size_t total_size;
|
||||
int result = EOF;
|
||||
int buffer_mode;
|
||||
int c;
|
||||
@ -58,12 +58,12 @@ fputs(const char *s, FILE *stream)
|
||||
|
||||
assert( s != NULL && stream != NULL );
|
||||
|
||||
if(__check_abort_enabled)
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
#if defined(CHECK_FOR_NULL_POINTERS)
|
||||
{
|
||||
if(s == NULL || stream == NULL)
|
||||
if (s == NULL || stream == NULL)
|
||||
{
|
||||
__set_errno(EFAULT);
|
||||
goto out;
|
||||
@ -77,37 +77,261 @@ fputs(const char *s, FILE *stream)
|
||||
assert( FLAG_IS_SET(file->iob_Flags,IOBF_IN_USE) );
|
||||
assert( file->iob_BufferSize > 0 );
|
||||
|
||||
buffer_mode = (file->iob_Flags & IOBF_BUFFER_MODE);
|
||||
if(buffer_mode == IOBF_BUFFER_MODE_NONE)
|
||||
buffer_mode = IOBF_BUFFER_MODE_LINE;
|
||||
|
||||
if(__fputc_check(stream) < 0)
|
||||
if (__fputc_check(stream) < 0)
|
||||
goto out;
|
||||
|
||||
while((c = (*s++)) != '\0')
|
||||
assert( 0 <= file->iob_BufferWriteBytes );
|
||||
assert( file->iob_BufferWriteBytes <= file->iob_BufferSize );
|
||||
assert( file->iob_BufferPosition <= file->iob_BufferSize );
|
||||
|
||||
/* We perform line buffering to improve readability of the
|
||||
* buffered text if buffering was disabled and the output
|
||||
* goes to an interactive stream.
|
||||
*/
|
||||
buffer_mode = (file->iob_Flags & IOBF_BUFFER_MODE);
|
||||
if (buffer_mode == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
if(__putc(c,stream,buffer_mode) == EOF)
|
||||
goto out;
|
||||
struct fd * fd = __fd[file->iob_Descriptor];
|
||||
|
||||
__fd_lock(fd);
|
||||
|
||||
if (FLAG_IS_SET(fd->fd_Flags, FDF_IS_INTERACTIVE))
|
||||
buffer_mode = IOBF_BUFFER_MODE_LINE;
|
||||
|
||||
__fd_unlock(fd);
|
||||
}
|
||||
|
||||
total_size = strlen(s);
|
||||
if (total_size > 0)
|
||||
{
|
||||
if (buffer_mode == IOBF_BUFFER_MODE_LINE)
|
||||
{
|
||||
while (total_size > 0)
|
||||
{
|
||||
/* Is there still room in the write buffer to store
|
||||
* more of the string?
|
||||
*/
|
||||
if (file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
{
|
||||
unsigned char * buffer = &file->iob_Buffer[file->iob_BufferWriteBytes];
|
||||
size_t num_buffer_bytes;
|
||||
const char * lf;
|
||||
|
||||
/* Give the user a chance to abort what could otherwise
|
||||
* become an uninterrupted series of copying operations.
|
||||
*/
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
/* Store only as many characters as will fit into the write buffer. */
|
||||
assert( file->iob_BufferSize >= file->iob_BufferWriteBytes );
|
||||
|
||||
num_buffer_bytes = file->iob_BufferSize - file->iob_BufferWriteBytes;
|
||||
if (total_size < num_buffer_bytes)
|
||||
num_buffer_bytes = total_size;
|
||||
|
||||
/* Try to find a line feed in the string. If there is one,
|
||||
* reduce the number of characters to write to the sequence
|
||||
* which ends with the line feed character.
|
||||
*/
|
||||
lf = memchr(s, '\n', num_buffer_bytes);
|
||||
if (lf != NULL)
|
||||
{
|
||||
assert( (size_t)(lf + 1 - s) <= num_buffer_bytes );
|
||||
|
||||
num_buffer_bytes = lf + 1 - s;
|
||||
}
|
||||
|
||||
assert( num_buffer_bytes > 0 );
|
||||
|
||||
memmove(buffer, s, num_buffer_bytes);
|
||||
s += num_buffer_bytes;
|
||||
|
||||
assert( file->iob_BufferWriteBytes + num_buffer_bytes <= file->iob_BufferSize );
|
||||
|
||||
file->iob_BufferWriteBytes += num_buffer_bytes;
|
||||
|
||||
/* Write the buffer to disk if it's full or contains a line feed. */
|
||||
if ((lf != NULL || __iob_write_buffer_is_full(file)) && __flush_iob_write_buffer(file) < 0)
|
||||
{
|
||||
/* Abort with error. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Stop as soon as no further data needs to be written. */
|
||||
assert( total_size >= num_buffer_bytes );
|
||||
|
||||
total_size -= num_buffer_bytes;
|
||||
if (total_size == 0)
|
||||
break;
|
||||
|
||||
/* If there is again room in the output buffer,
|
||||
* repeat this optimization.
|
||||
*/
|
||||
if (file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
continue;
|
||||
}
|
||||
|
||||
c = (*s++);
|
||||
|
||||
if (__putc_line_buffered(c, (FILE *)file) == EOF)
|
||||
goto out;
|
||||
|
||||
total_size--;
|
||||
}
|
||||
}
|
||||
else if (buffer_mode == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
ssize_t num_bytes_written;
|
||||
|
||||
/* We bypass the buffer entirely. */
|
||||
num_bytes_written = write(file->iob_Descriptor, s, total_size);
|
||||
if (num_bytes_written == -1)
|
||||
{
|
||||
SET_FLAG(file->iob_Flags, IOBF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (total_size > 0)
|
||||
{
|
||||
/* If there is more data to be written than the write buffer will hold
|
||||
* and the write buffer is empty anyway, then we'll bypass the write
|
||||
* buffer entirely.
|
||||
*/
|
||||
if (file->iob_BufferWriteBytes == 0 && total_size >= (size_t)file->iob_BufferSize)
|
||||
{
|
||||
ssize_t num_bytes_written;
|
||||
|
||||
/* We bypass the buffer entirely. */
|
||||
num_bytes_written = write(file->iob_Descriptor, s, total_size);
|
||||
if (num_bytes_written == -1)
|
||||
{
|
||||
SET_FLAG(file->iob_Flags, IOBF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Is there still room in the write buffer to store
|
||||
* more of the string?
|
||||
*/
|
||||
if (file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
{
|
||||
unsigned char * buffer = &file->iob_Buffer[file->iob_BufferWriteBytes];
|
||||
size_t num_buffer_bytes;
|
||||
|
||||
/* Give the user a chance to abort what could otherwise
|
||||
* become an uninterrupted series of copying operations.
|
||||
*/
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
/* Store only as many characters as will fit into the write buffer. */
|
||||
assert( file->iob_BufferSize >= file->iob_BufferWriteBytes );
|
||||
|
||||
num_buffer_bytes = file->iob_BufferSize - file->iob_BufferWriteBytes;
|
||||
if (total_size < num_buffer_bytes)
|
||||
num_buffer_bytes = total_size;
|
||||
|
||||
assert( num_buffer_bytes > 0 );
|
||||
|
||||
memmove(buffer, s, num_buffer_bytes);
|
||||
s += num_buffer_bytes;
|
||||
|
||||
assert( file->iob_BufferWriteBytes + num_buffer_bytes <= file->iob_BufferSize );
|
||||
|
||||
file->iob_BufferWriteBytes += num_buffer_bytes;
|
||||
|
||||
/* Write a full buffer to disk. */
|
||||
if (__iob_write_buffer_is_full(file) && __flush_iob_write_buffer(file) < 0)
|
||||
{
|
||||
/* Abort with error. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Stop as soon as no further data needs to be written. */
|
||||
assert( total_size >= num_buffer_bytes );
|
||||
|
||||
total_size -= num_buffer_bytes;
|
||||
if (total_size == 0)
|
||||
break;
|
||||
|
||||
/* If there is again room in the output buffer,
|
||||
* try this optimization again.
|
||||
*/
|
||||
if (file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
continue;
|
||||
}
|
||||
|
||||
c = (*s++);
|
||||
|
||||
if (__putc_fully_buffered(c, (FILE *)file) == EOF)
|
||||
goto out;
|
||||
|
||||
total_size--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (line_feed != 0 && __putc(line_feed, stream, buffer_mode) == EOF)
|
||||
goto out;
|
||||
|
||||
result = OK;
|
||||
|
||||
out:
|
||||
|
||||
/* Note: if buffering is disabled for this stream, then we still
|
||||
may have buffered data around, queued to be printed right now.
|
||||
This is intended to improve performance as it takes more effort
|
||||
to write a single character to a file than to write a bunch. */
|
||||
if(result == 0 && (file->iob_Flags & IOBF_BUFFER_MODE) == IOBF_BUFFER_MODE_NONE)
|
||||
* may have buffered data around, queued to be printed right now.
|
||||
* This is intended to improve performance as it takes more effort
|
||||
* to write a single character to a file than to write a bunch.
|
||||
*/
|
||||
if (result == 0 && (file->iob_Flags & IOBF_BUFFER_MODE) == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
if(__iob_write_buffer_is_valid(file) && __flush_iob_write_buffer(file) < 0)
|
||||
if (__iob_write_buffer_is_valid(file) && __flush_iob_write_buffer(file) < 0)
|
||||
{
|
||||
SHOWMSG("couldn't flush the write buffer");
|
||||
result = EOF;
|
||||
}
|
||||
}
|
||||
|
||||
funlockfile(stream);
|
||||
if (stream != NULL)
|
||||
funlockfile(stream);
|
||||
|
||||
RETURN(result);
|
||||
return(result);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
int
|
||||
fputs(const char *s, FILE *stream)
|
||||
{
|
||||
int result;
|
||||
|
||||
ENTER();
|
||||
|
||||
SHOWSTRING(s);
|
||||
SHOWPOINTER(stream);
|
||||
|
||||
assert( s != NULL && stream != NULL );
|
||||
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
#if defined(CHECK_FOR_NULL_POINTERS)
|
||||
{
|
||||
if (s == NULL || stream == NULL)
|
||||
{
|
||||
__set_errno(EFAULT);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif /* CHECK_FOR_NULL_POINTERS */
|
||||
|
||||
result = __fputs(s, 0, stream);
|
||||
|
||||
RETURN(result);
|
||||
return(result);
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: stdio_fread.c,v 1.7 2006-01-08 12:04:24 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2019 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -59,14 +57,14 @@ fread(void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
assert( ptr != NULL && stream != NULL );
|
||||
assert( (int)element_size >= 0 && (int)count >= 0 );
|
||||
|
||||
if(__check_abort_enabled)
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
flockfile(stream);
|
||||
|
||||
#if defined(CHECK_FOR_NULL_POINTERS)
|
||||
{
|
||||
if(ptr == NULL || stream == NULL)
|
||||
if (ptr == NULL || stream == NULL)
|
||||
{
|
||||
SHOWMSG("invalid parameters");
|
||||
|
||||
@ -80,7 +78,7 @@ fread(void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
assert( FLAG_IS_SET(file->iob_Flags,IOBF_IN_USE) );
|
||||
assert( file->iob_BufferSize > 0 );
|
||||
|
||||
if(FLAG_IS_CLEAR(file->iob_Flags,IOBF_IN_USE))
|
||||
if (FLAG_IS_CLEAR(file->iob_Flags, IOBF_IN_USE))
|
||||
{
|
||||
SHOWMSG("this file is not even in use");
|
||||
|
||||
@ -91,7 +89,7 @@ fread(void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(FLAG_IS_CLEAR(file->iob_Flags,IOBF_READ))
|
||||
if (FLAG_IS_CLEAR(file->iob_Flags, IOBF_READ))
|
||||
{
|
||||
SHOWMSG("this file is not read-enabled");
|
||||
|
||||
@ -102,29 +100,114 @@ fread(void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(element_size > 0 && count > 0)
|
||||
/* So that we can tell error and 'end of file' conditions apart. */
|
||||
clearerr(stream);
|
||||
|
||||
if (element_size > 0 && count > 0)
|
||||
{
|
||||
size_t total_bytes_read = 0;
|
||||
size_t total_size;
|
||||
unsigned char * data = ptr;
|
||||
ssize_t num_bytes_read;
|
||||
int c;
|
||||
|
||||
if(__fgetc_check((FILE *)file) < 0)
|
||||
if (__fgetc_check((FILE *)file) < 0)
|
||||
goto out;
|
||||
|
||||
/* Check for overflow. */
|
||||
total_size = element_size * count;
|
||||
if (element_size != (total_size / count))
|
||||
goto out;
|
||||
|
||||
SHOWVALUE(total_size);
|
||||
|
||||
while(total_size-- > 0)
|
||||
/* No buffering enabled? */
|
||||
if ((file->iob_Flags & IOBF_BUFFER_MODE) == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
c = __getc(file);
|
||||
if(c == EOF)
|
||||
break;
|
||||
/* We bypass the buffer entirely. */
|
||||
num_bytes_read = read(file->iob_Descriptor, data, total_size);
|
||||
if (num_bytes_read == -1)
|
||||
{
|
||||
SET_FLAG(file->iob_Flags,IOBF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
(*data++) = c;
|
||||
if (num_bytes_read == 0)
|
||||
SET_FLAG(file->iob_Flags, IOBF_EOF_REACHED);
|
||||
|
||||
total_bytes_read++;
|
||||
total_bytes_read = num_bytes_read;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (total_size > 0)
|
||||
{
|
||||
/* If the read buffer is empty and there is more data to be
|
||||
* read, we'll bypass the buffer entirely. Note that we try
|
||||
* to read a complete full buffer worth's of data, not just
|
||||
* a few bytes.
|
||||
*/
|
||||
if (file->iob_BufferReadBytes == 0 && total_size >= (size_t)file->iob_BufferSize)
|
||||
{
|
||||
/* We bypass the buffer entirely. */
|
||||
num_bytes_read = read(file->iob_Descriptor, data, total_size);
|
||||
if (num_bytes_read == -1)
|
||||
{
|
||||
SET_FLAG(file->iob_Flags, IOBF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (num_bytes_read == 0)
|
||||
SET_FLAG(file->iob_Flags, IOBF_EOF_REACHED);
|
||||
|
||||
total_bytes_read += num_bytes_read;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If there is still data in the read buffer, try to copy it
|
||||
* directly into the output buffer.
|
||||
*/
|
||||
if (file->iob_BufferPosition < file->iob_BufferReadBytes)
|
||||
{
|
||||
const unsigned char * buffer = &file->iob_Buffer[file->iob_BufferPosition];
|
||||
size_t num_bytes_in_buffer;
|
||||
|
||||
/* Copy as much data as will fit. */
|
||||
assert( file->iob_BufferReadBytes >= file->iob_BufferPosition );
|
||||
|
||||
num_bytes_in_buffer = file->iob_BufferReadBytes - file->iob_BufferPosition;
|
||||
if (total_size < num_bytes_in_buffer)
|
||||
num_bytes_in_buffer = total_size;
|
||||
|
||||
assert( num_bytes_in_buffer > 0 );
|
||||
|
||||
memmove(data, buffer, num_bytes_in_buffer);
|
||||
data += num_bytes_in_buffer;
|
||||
|
||||
assert( file->iob_BufferPosition + num_bytes_in_buffer <= file->iob_BufferSize );
|
||||
|
||||
file->iob_BufferPosition += num_bytes_in_buffer;
|
||||
|
||||
total_bytes_read += num_bytes_in_buffer;
|
||||
|
||||
/* Stop if the string buffer has been filled. */
|
||||
assert( total_size >= num_bytes_in_buffer );
|
||||
|
||||
/* Are we finished yet? */
|
||||
total_size -= num_bytes_in_buffer;
|
||||
if (total_size == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the next byte and/or refill the read buffer. */
|
||||
c = __getc(file);
|
||||
if (c == EOF)
|
||||
break;
|
||||
|
||||
(*data++) = c;
|
||||
|
||||
total_size--;
|
||||
total_bytes_read++;
|
||||
}
|
||||
}
|
||||
|
||||
SHOWVALUE(total_bytes_read);
|
||||
@ -137,9 +220,6 @@ fread(void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
SHOWVALUE(count);
|
||||
|
||||
SHOWMSG("either element size or count is zero");
|
||||
|
||||
/* Don't let this appear like an EOF or error. */
|
||||
clearerr((FILE *)file);
|
||||
}
|
||||
|
||||
D(("total number of elements read = %ld",result));
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: stdio_fwrite.c,v 1.12 2010-10-20 13:12:58 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2019 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -59,14 +57,14 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
assert( ptr != NULL && stream != NULL );
|
||||
assert( (int)element_size >= 0 && (int)count >= 0 );
|
||||
|
||||
if(__check_abort_enabled)
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
flockfile(stream);
|
||||
|
||||
#if defined(CHECK_FOR_NULL_POINTERS)
|
||||
{
|
||||
if(ptr == NULL || stream == NULL)
|
||||
if (ptr == NULL || stream == NULL)
|
||||
{
|
||||
SHOWMSG("invalid parameters");
|
||||
|
||||
@ -78,9 +76,8 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
|
||||
assert( __is_valid_iob(file) );
|
||||
assert( FLAG_IS_SET(file->iob_Flags,IOBF_IN_USE) );
|
||||
assert( file->iob_BufferSize > 0 );
|
||||
|
||||
if(FLAG_IS_CLEAR(file->iob_Flags,IOBF_IN_USE))
|
||||
if (FLAG_IS_CLEAR(file->iob_Flags,IOBF_IN_USE))
|
||||
{
|
||||
SHOWMSG("this file is not even in use");
|
||||
|
||||
@ -91,7 +88,7 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(FLAG_IS_CLEAR(file->iob_Flags,IOBF_WRITE))
|
||||
if (FLAG_IS_CLEAR(file->iob_Flags,IOBF_WRITE))
|
||||
{
|
||||
SHOWMSG("this stream is not write-enabled");
|
||||
|
||||
@ -102,69 +99,242 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(element_size > 0 && count > 0)
|
||||
clearerr((FILE *)file);
|
||||
|
||||
if (element_size > 0 && count > 0)
|
||||
{
|
||||
const unsigned char * data = (unsigned char *)ptr;
|
||||
const unsigned char * s = (unsigned char *)ptr;
|
||||
unsigned char c;
|
||||
int buffer_mode;
|
||||
size_t total_bytes_written = 0;
|
||||
size_t total_size;
|
||||
|
||||
/* Check for overflow. */
|
||||
total_size = element_size * count;
|
||||
|
||||
if(__fputc_check((FILE *)file) < 0)
|
||||
if (element_size != (total_size / count))
|
||||
goto out;
|
||||
|
||||
if (__fputc_check((FILE *)file) < 0)
|
||||
goto out;
|
||||
|
||||
/* If this is an unbuffered interactive stream, we will switch
|
||||
* to line buffered mode in order to improve readability of
|
||||
* the output.
|
||||
*/
|
||||
buffer_mode = (file->iob_Flags & IOBF_BUFFER_MODE);
|
||||
if(buffer_mode == IOBF_BUFFER_MODE_NONE)
|
||||
if (buffer_mode == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
struct fd * fd = __fd[file->iob_Descriptor];
|
||||
|
||||
__fd_lock(fd);
|
||||
|
||||
if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE))
|
||||
if (FLAG_IS_SET(fd->fd_Flags, FDF_IS_INTERACTIVE))
|
||||
buffer_mode = IOBF_BUFFER_MODE_LINE;
|
||||
|
||||
__fd_unlock(fd);
|
||||
}
|
||||
|
||||
if(buffer_mode == IOBF_BUFFER_MODE_LINE)
|
||||
if (buffer_mode == IOBF_BUFFER_MODE_LINE)
|
||||
{
|
||||
while(total_size-- > 0)
|
||||
assert( file->iob_BufferSize > 0 );
|
||||
|
||||
while (total_size > 0)
|
||||
{
|
||||
c = (*data++);
|
||||
/* Is there still room in the write buffer to store
|
||||
* more of the data?
|
||||
*/
|
||||
if (file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
{
|
||||
unsigned char * buffer = &file->iob_Buffer[file->iob_BufferWriteBytes];
|
||||
size_t num_buffer_bytes;
|
||||
const unsigned char * lf;
|
||||
|
||||
if(__putc_line_buffered(c,(FILE *)file) == EOF)
|
||||
goto out;
|
||||
/* Give the user a chance to abort what could otherwise
|
||||
* become an uninterrupted series of copying operations.
|
||||
*/
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
/* Store only as many characters as will fit into the write buffer. */
|
||||
assert( file->iob_BufferSize >= file->iob_BufferWriteBytes );
|
||||
|
||||
num_buffer_bytes = file->iob_BufferSize - file->iob_BufferWriteBytes;
|
||||
if (total_size < num_buffer_bytes)
|
||||
num_buffer_bytes = total_size;
|
||||
|
||||
/* Try to find a line feed in the string. If there is one,
|
||||
* reduce the number of characters to write to the sequence
|
||||
* which ends with the line feed character.
|
||||
*/
|
||||
lf = (unsigned char *)memchr(s, '\n', num_buffer_bytes);
|
||||
if (lf != NULL)
|
||||
{
|
||||
assert( (size_t)(lf + 1 - s) <= num_buffer_bytes );
|
||||
|
||||
num_buffer_bytes = lf + 1 - s;
|
||||
}
|
||||
|
||||
assert( num_buffer_bytes > 0 );
|
||||
|
||||
memmove(buffer, s, num_buffer_bytes);
|
||||
s += num_buffer_bytes;
|
||||
|
||||
assert( file->iob_BufferWriteBytes + num_buffer_bytes <= file->iob_BufferSize );
|
||||
|
||||
file->iob_BufferWriteBytes += num_buffer_bytes;
|
||||
|
||||
/* Write the buffer to disk if the buffer is full
|
||||
* or contains a line feed.
|
||||
*/
|
||||
if ((lf != NULL || __iob_write_buffer_is_full(file)) && __flush_iob_write_buffer(file) < 0)
|
||||
{
|
||||
/* Abort with error. */
|
||||
break;
|
||||
}
|
||||
|
||||
total_bytes_written += num_buffer_bytes;
|
||||
|
||||
/* Stop as soon as no further data needs to be written. */
|
||||
assert( total_size >= num_buffer_bytes );
|
||||
|
||||
total_size -= num_buffer_bytes;
|
||||
if (total_size == 0)
|
||||
break;
|
||||
|
||||
/* If there is again room in the output buffer,
|
||||
* repeat this optimization.
|
||||
*/
|
||||
if (file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
continue;
|
||||
}
|
||||
|
||||
c = (*s++);
|
||||
|
||||
if (__putc_line_buffered(c, (FILE *)file) == EOF)
|
||||
break;
|
||||
|
||||
total_size--;
|
||||
total_bytes_written++;
|
||||
}
|
||||
}
|
||||
else if (buffer_mode == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
ssize_t num_bytes_written;
|
||||
|
||||
/* We bypass the buffer entirely. */
|
||||
num_bytes_written = write(file->iob_Descriptor, s, total_size);
|
||||
if (num_bytes_written == -1)
|
||||
{
|
||||
SET_FLAG(file->iob_Flags, IOBF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
total_bytes_written = (size_t)num_bytes_written;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(total_size-- > 0)
|
||||
assert( file->iob_BufferSize > 0 );
|
||||
|
||||
while (total_size > 0)
|
||||
{
|
||||
c = (*data++);
|
||||
/* If there is more data to be written than the write buffer will hold
|
||||
* and the write buffer is empty anyway, then we'll bypass the write
|
||||
* buffer entirely. Note that we try to store a complete full buffer
|
||||
* worth's of data, not just a few bytes.
|
||||
*/
|
||||
if (file->iob_BufferWriteBytes == 0 && total_size >= (size_t)file->iob_BufferSize)
|
||||
{
|
||||
ssize_t num_bytes_written;
|
||||
|
||||
if(__putc_fully_buffered(c,(FILE *)file) == EOF)
|
||||
goto out;
|
||||
/* We bypass the buffer entirely. */
|
||||
num_bytes_written = write(file->iob_Descriptor, s, total_size);
|
||||
if (num_bytes_written == -1)
|
||||
{
|
||||
SET_FLAG(file->iob_Flags, IOBF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
total_bytes_written += num_bytes_written;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Is there still room in the write buffer to store
|
||||
* more of the data?
|
||||
*/
|
||||
if (file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
{
|
||||
unsigned char * buffer = &file->iob_Buffer[file->iob_BufferWriteBytes];
|
||||
size_t num_buffer_bytes;
|
||||
|
||||
/* Give the user a chance to abort what could otherwise
|
||||
* become an uninterrupted series of copying operations.
|
||||
*/
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
/* Store only as many bytes as will fit into the write buffer. */
|
||||
assert( file->iob_BufferSize >= file->iob_BufferWriteBytes );
|
||||
|
||||
num_buffer_bytes = file->iob_BufferSize - file->iob_BufferWriteBytes;
|
||||
if (total_size < num_buffer_bytes)
|
||||
num_buffer_bytes = total_size;
|
||||
|
||||
assert( num_buffer_bytes > 0 );
|
||||
|
||||
memmove(buffer, s, num_buffer_bytes);
|
||||
s += num_buffer_bytes;
|
||||
|
||||
assert( file->iob_BufferWriteBytes + num_buffer_bytes <= file->iob_BufferSize );
|
||||
|
||||
file->iob_BufferWriteBytes += num_buffer_bytes;
|
||||
|
||||
/* Write a full buffer to disk. */
|
||||
if (__iob_write_buffer_is_full(file) && __flush_iob_write_buffer(file) < 0)
|
||||
{
|
||||
/* Abort with error. */
|
||||
break;
|
||||
}
|
||||
|
||||
total_bytes_written += num_buffer_bytes;
|
||||
|
||||
/* Stop as soon as no further data needs to be written. */
|
||||
assert( total_size >= num_buffer_bytes );
|
||||
|
||||
total_size -= num_buffer_bytes;
|
||||
if (total_size == 0)
|
||||
break;
|
||||
|
||||
/* If there is again room in the output buffer,
|
||||
* try this optimization again.
|
||||
*/
|
||||
if (file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
continue;
|
||||
}
|
||||
|
||||
c = (*s++);
|
||||
|
||||
if (__putc_fully_buffered(c, (FILE *)file) == EOF)
|
||||
break;
|
||||
|
||||
total_size--;
|
||||
total_bytes_written++;
|
||||
}
|
||||
}
|
||||
|
||||
if((file->iob_Flags & IOBF_BUFFER_MODE) == IOBF_BUFFER_MODE_NONE)
|
||||
if ((file->iob_Flags & IOBF_BUFFER_MODE) == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
if(__iob_write_buffer_is_valid(file) && __flush_iob_write_buffer(file) < 0)
|
||||
goto out;
|
||||
if (__iob_write_buffer_is_valid(file))
|
||||
__flush_iob_write_buffer(file);
|
||||
}
|
||||
|
||||
result = total_bytes_written / element_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Don't let this appear like an EOF or error. */
|
||||
clearerr((FILE *)file);
|
||||
SHOWVALUE(element_size);
|
||||
SHOWVALUE(count);
|
||||
|
||||
SHOWMSG("either element size or count is zero");
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: stdio_gets.c,v 1.6 2006-01-08 12:04:24 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2019 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -46,6 +44,8 @@
|
||||
char *
|
||||
gets(char *s)
|
||||
{
|
||||
FILE * stream = stdin;
|
||||
struct iob * file = (struct iob *)stream;
|
||||
char * result = s;
|
||||
int c;
|
||||
|
||||
@ -53,16 +53,16 @@ gets(char *s)
|
||||
|
||||
SHOWPOINTER(s);
|
||||
|
||||
assert( s != NULL && stdin != NULL );
|
||||
assert( s != NULL );
|
||||
|
||||
if(__check_abort_enabled)
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
flockfile(stdin);
|
||||
flockfile(stream);
|
||||
|
||||
#if defined(CHECK_FOR_NULL_POINTERS)
|
||||
{
|
||||
if(s == NULL || stdin == NULL)
|
||||
if (s == NULL)
|
||||
{
|
||||
SHOWMSG("invalid parameters");
|
||||
|
||||
@ -77,21 +77,84 @@ gets(char *s)
|
||||
/* Take care of the checks and data structure changes that
|
||||
* need to be handled only once for this stream.
|
||||
*/
|
||||
if(__fgetc_check(stdin) < 0)
|
||||
if (__fgetc_check(stream) < 0)
|
||||
{
|
||||
result = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* So that we can tell error and 'end of file' conditions apart. */
|
||||
clearerr(stdin);
|
||||
clearerr(stream);
|
||||
|
||||
while(TRUE)
|
||||
assert( 0 <= file->iob_BufferReadBytes );
|
||||
assert( file->iob_BufferReadBytes <= file->iob_BufferSize );
|
||||
assert( file->iob_BufferPosition <= file->iob_BufferSize );
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
c = __getc(stdin);
|
||||
if(c == EOF)
|
||||
/* If there is data in the buffer, try to copy it directly
|
||||
* into the string buffer. If there is a line feed in the
|
||||
* buffer, too, try to conclude the read operation.
|
||||
*/
|
||||
if (file->iob_BufferPosition < file->iob_BufferReadBytes)
|
||||
{
|
||||
if(ferror(stdin))
|
||||
size_t num_bytes_in_buffer = file->iob_BufferReadBytes - file->iob_BufferPosition;
|
||||
const unsigned char * buffer = &file->iob_Buffer[file->iob_BufferPosition];
|
||||
const unsigned char * lf;
|
||||
|
||||
assert( file->iob_BufferReadBytes >= file->iob_BufferPosition );
|
||||
|
||||
/* Try to find a line feed character which could conclude
|
||||
* the read operation if the remaining buffer data, including
|
||||
* the line feed character, fit into the string buffer.
|
||||
*/
|
||||
lf = (unsigned char *)memchr(buffer, '\n', num_bytes_in_buffer);
|
||||
if (lf != NULL)
|
||||
{
|
||||
size_t num_characters_in_line = lf + 1 - buffer;
|
||||
|
||||
assert( num_characters_in_line <= num_bytes_in_buffer );
|
||||
|
||||
/* Give the user a chance to abort what could otherwise
|
||||
* become an uninterrupted series of copying operations.
|
||||
*/
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
/* Copy the remainder of the read buffer into the
|
||||
* string buffer, omitting the terminating line
|
||||
* feed character. This is the difference between
|
||||
* gets() and fgets(): fgets() will keep the
|
||||
* terminating line feed character, but gets()
|
||||
* will drop it.
|
||||
*/
|
||||
assert( num_characters_in_line > 0 );
|
||||
|
||||
memmove(s, buffer, num_characters_in_line - 1);
|
||||
s += num_characters_in_line - 1;
|
||||
|
||||
assert( file->iob_BufferPosition + num_characters_in_line <= file->iob_BufferSize );
|
||||
|
||||
file->iob_BufferPosition += num_characters_in_line;
|
||||
|
||||
/* And that concludes the line read operation. */
|
||||
break;
|
||||
}
|
||||
|
||||
assert( num_bytes_in_buffer > 0 );
|
||||
|
||||
memmove(s, buffer, num_bytes_in_buffer);
|
||||
s += num_bytes_in_buffer;
|
||||
|
||||
assert( file->iob_BufferPosition + num_bytes_in_buffer <= file->iob_BufferSize );
|
||||
|
||||
file->iob_BufferPosition += num_bytes_in_buffer;
|
||||
}
|
||||
|
||||
c = __getc(stream);
|
||||
if (c == EOF)
|
||||
{
|
||||
if (ferror(stream))
|
||||
{
|
||||
/* Just to be on the safe side. */
|
||||
(*s) = '\0';
|
||||
@ -101,14 +164,15 @@ gets(char *s)
|
||||
}
|
||||
|
||||
/* Make sure that we return NULL if we really
|
||||
didn't read anything at all */
|
||||
if(s == result)
|
||||
* didn't read anything at all.
|
||||
*/
|
||||
if (s == result)
|
||||
result = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(c == '\n')
|
||||
if (c == '\n')
|
||||
break;
|
||||
|
||||
(*s++) = c;
|
||||
@ -120,7 +184,7 @@ gets(char *s)
|
||||
|
||||
out:
|
||||
|
||||
funlockfile(stdin);
|
||||
funlockfile(stream);
|
||||
|
||||
RETURN(result);
|
||||
return(result);
|
||||
|
||||
581
library/stdio_long_path.c
Normal file
581
library/stdio_long_path.c
Normal file
@ -0,0 +1,581 @@
|
||||
/*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2023 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Neither the name of Olaf Barthel nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __amigaos4__
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* We don't want the replacement functions to become active when buliding
|
||||
* this file.
|
||||
*/
|
||||
#define __DISABLE_LONG_PATHS
|
||||
|
||||
#ifndef _STDIO_LONG_PATH_H
|
||||
#include "stdio_long_path.h"
|
||||
#endif /* _STDIO_LONG_PATH_H */
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#include <proto/exec.h>
|
||||
#include <proto/dos.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Split an AmigaDOS path name into a sequence of the individual path
|
||||
* pieces. A path name may be empty, it may include a device name,
|
||||
* a series of directory names or even "/" references to the
|
||||
* parent directory and a file, directory or link name.
|
||||
*
|
||||
* split_path() is modeled on the SplitName() function in that it
|
||||
* will copy each path piece into the supplied buffer, returning -1
|
||||
* if the last piece has been copied. If -2 is returned, it means
|
||||
* that the piece buffer is too short for the path piece to be
|
||||
* copied.
|
||||
*/
|
||||
static LONG
|
||||
split_path(CONST TEXT * path, TEXT * piece, LONG old_position, LONG size)
|
||||
{
|
||||
LONG length_copied = 0;
|
||||
TEXT c;
|
||||
|
||||
/* The piece has to be NUL-terminated, which reduces the
|
||||
* usable size of the piece buffer.
|
||||
*/
|
||||
size--;
|
||||
|
||||
/* Is the piece buffer too short? */
|
||||
if (size <= 0)
|
||||
return -2;
|
||||
|
||||
/* Is this not an empty string, which stands for the
|
||||
* current directory?
|
||||
*/
|
||||
if (path[0] != '\0')
|
||||
{
|
||||
/* Adjust the starting position for scanning the path name,
|
||||
* which simplifies the loop below.
|
||||
*/
|
||||
path += old_position;
|
||||
|
||||
while (size-- >= 0)
|
||||
{
|
||||
c = path[length_copied];
|
||||
|
||||
/* Was this the end of the path name? */
|
||||
if (c == '\0')
|
||||
break;
|
||||
|
||||
(*piece++) = c;
|
||||
length_copied++;
|
||||
|
||||
/* Is this a device name separator, which may appear
|
||||
* exactly once in a path name? Then we will have to
|
||||
* keep it.
|
||||
*/
|
||||
if (c == ':' && old_position == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* Is there a '/' at the end of this path piece? */
|
||||
else if (c == '/')
|
||||
{
|
||||
/* Is this in fact a single '/' which represents a
|
||||
* reference to the parent directory? Then we need
|
||||
* to keep it, otherwise it will have to be removed.
|
||||
*/
|
||||
if (length_copied > 1)
|
||||
{
|
||||
/* Make sure that the trailing '/' will
|
||||
* be removed from the piece.
|
||||
*/
|
||||
piece--;
|
||||
size++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is the piece buffer too short? */
|
||||
if (size < 0)
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Make sure that the piece is NUL-terminated. */
|
||||
(*piece) = '\0';
|
||||
|
||||
/* Is the separator character at the end of the path name,
|
||||
* or is this an empty string (with path_length == 0)?
|
||||
*/
|
||||
if (path[length_copied] == '\0')
|
||||
return -1; /* This is the last piece of the path name. */
|
||||
|
||||
/* Continue path processing at this position. */
|
||||
return old_position + length_copied;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* If a path name exceeds 255 characters, break down the path into individual
|
||||
* pieces, traveling to the end of the path and invoke a callback function
|
||||
* with the current directory set and the name of the file/directory/link
|
||||
* provided along with additional parameters needed to carry out a file
|
||||
* system operation.
|
||||
*
|
||||
* For path names shorter than 256 characters, no extra work will be done
|
||||
* and the callback function will be invoked directly.
|
||||
*/
|
||||
|
||||
typedef LONG (*callback_f)(CONST TEXT * name, LONG arg1, LONG arg2);
|
||||
|
||||
static void
|
||||
long_path_workaround(
|
||||
CONST TEXT * path_name, LONG arg1, LONG arg2,
|
||||
LONG * result_ptr,
|
||||
callback_f callback)
|
||||
{
|
||||
TEXT piece[256];
|
||||
LONG path_name_len;
|
||||
LONG len;
|
||||
CONST TEXT * colon;
|
||||
BPTR reference_dir = (BPTR)NULL;
|
||||
BPTR old_current_dir = (BPTR)NULL;
|
||||
LONG position = 0;
|
||||
BPTR lock;
|
||||
|
||||
path_name_len = strlen(path_name);
|
||||
|
||||
/* Only go through with the extra effort to resolve the path
|
||||
* if this job requires a file system and not a handler (such
|
||||
* as "NIL:" or "CON:") and if the path name exceeds 255
|
||||
* characters.
|
||||
*/
|
||||
if (path_name_len > 255 && IsFileSystem(path_name))
|
||||
{
|
||||
/* Does the path begin with a device name, or a reference
|
||||
* to the root directory of a volume?
|
||||
*/
|
||||
colon = strchr(path_name, ':');
|
||||
if (colon != NULL)
|
||||
{
|
||||
/* We need to copy the name of the device, which must
|
||||
* not exceed 255 characters, including the colon
|
||||
* character. Volume and device names should never
|
||||
* exceed 30 characters, but accidents may still
|
||||
* happen...
|
||||
*/
|
||||
len = (LONG)(colon - path_name) + 1;
|
||||
if (len > 255)
|
||||
{
|
||||
SetIoErr(ERROR_LINE_TOO_LONG);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Obtain a shared lock on the device or root
|
||||
* directory. We are using Lock() because it
|
||||
* implies that the handler is a file system
|
||||
* and not, for example, "NIL:" or "CON:".
|
||||
*/
|
||||
CopyMem(path_name, piece, len);
|
||||
piece[len] = '\0';
|
||||
|
||||
reference_dir = Lock(piece, SHARED_LOCK);
|
||||
if (reference_dir == (BPTR)NULL)
|
||||
goto out;
|
||||
|
||||
/* We took care of the device or root directory name. */
|
||||
path_name += len;
|
||||
}
|
||||
|
||||
/* Process the path name, one piece at a time, relative
|
||||
* to a Lock on its parent directory. This repeats
|
||||
* until the last piece of the path has been found.
|
||||
* Each loop iteration will reuse the Lock of the
|
||||
* previous iteration as the parent directory.
|
||||
*/
|
||||
while (TRUE)
|
||||
{
|
||||
/* Obtain the next piece of the path. */
|
||||
position = split_path(path_name, piece, position, sizeof(piece));
|
||||
|
||||
/* Is the piece too long? */
|
||||
if (position == -2)
|
||||
{
|
||||
SetIoErr(ERROR_LINE_TOO_LONG);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Access the next piece of the path relative to a
|
||||
* specific directory? Otherwise, we use the current
|
||||
* directory of the current Process. Because the current
|
||||
* directory may be an exclusive lock we cannot
|
||||
* conveniently fall back onto DupLock() or
|
||||
* Lock("", SHARED_LOCK) instead.
|
||||
*/
|
||||
if (reference_dir != (BPTR)NULL)
|
||||
old_current_dir = CurrentDir(reference_dir);
|
||||
|
||||
/* Unless this is the final piece of the path
|
||||
* (split_path() will return -1), we will need to call
|
||||
* Lock() and make use of shared locks.
|
||||
*/
|
||||
if (position != -1)
|
||||
{
|
||||
lock = Lock(piece, SHARED_LOCK);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock = (BPTR)NULL;
|
||||
|
||||
/* Time to do what we came here for. */
|
||||
(*result_ptr) = (*callback)(piece, arg1, arg2);
|
||||
}
|
||||
|
||||
if (reference_dir != (BPTR)NULL)
|
||||
CurrentDir(old_current_dir);
|
||||
|
||||
/* Stop as soon as we reach the end of the path or
|
||||
* if the Lock could not be obtained.
|
||||
*/
|
||||
if (position == -1 || lock == (BPTR)NULL)
|
||||
break;
|
||||
|
||||
/* Look at the next piece of the remaining path.
|
||||
* Free the current reference directory Lock and
|
||||
* replace it with the Lock obtained above.
|
||||
*/
|
||||
UnLock(reference_dir);
|
||||
reference_dir = lock;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(*result_ptr) = (*callback)(path_name, arg1, arg2);
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
UnLock(reference_dir);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static LONG
|
||||
lock_callback(CONST TEXT * name, LONG type)
|
||||
{
|
||||
return Lock(name, type);
|
||||
}
|
||||
|
||||
BPTR
|
||||
__new_lock(CONST TEXT * name, LONG type )
|
||||
{
|
||||
BPTR result = (BPTR)NULL;
|
||||
|
||||
long_path_workaround(name, type, 0, (LONG *)&result, (callback_f)lock_callback);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static LONG
|
||||
set_owner_callback(CONST TEXT * name, LONG owner_info)
|
||||
{
|
||||
return SetOwner(name, owner_info);
|
||||
}
|
||||
|
||||
LONG
|
||||
__new_set_owner(CONST TEXT * name, LONG owner_info)
|
||||
{
|
||||
LONG success = FALSE;
|
||||
|
||||
long_path_workaround(name, owner_info, 0, &success, (callback_f)set_owner_callback);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static LONG
|
||||
set_comment_callback(CONST TEXT * name, LONG comment)
|
||||
{
|
||||
return SetComment(name, (STRPTR)comment);
|
||||
}
|
||||
|
||||
LONG
|
||||
__new_set_comment(CONST TEXT * name, CONST TEXT * comment)
|
||||
{
|
||||
LONG success = FALSE;
|
||||
|
||||
long_path_workaround(name, (LONG)comment, 0, &success, (callback_f)set_comment_callback);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static LONG
|
||||
set_protect_callback(CONST TEXT * name, LONG protect)
|
||||
{
|
||||
return SetProtection(name, protect);
|
||||
}
|
||||
|
||||
LONG
|
||||
__new_set_protection(CONST TEXT * name, LONG protect)
|
||||
{
|
||||
LONG success = FALSE;
|
||||
|
||||
long_path_workaround(name, protect, 0, &success, (callback_f)set_protect_callback);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static LONG
|
||||
set_file_date_callback(CONST TEXT * name, LONG date)
|
||||
{
|
||||
return SetFileDate(name, (struct DateStamp *)date);
|
||||
}
|
||||
|
||||
LONG
|
||||
__new_set_file_date(CONST TEXT * name, CONST struct DateStamp * date)
|
||||
{
|
||||
LONG success = FALSE;
|
||||
|
||||
long_path_workaround(name, (LONG)date, 0, &success, (callback_f)set_file_date_callback);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static LONG
|
||||
open_callback(CONST TEXT * name, LONG mode)
|
||||
{
|
||||
return Open(name, mode);
|
||||
}
|
||||
|
||||
BPTR
|
||||
__new_open(CONST TEXT * name, LONG mode)
|
||||
{
|
||||
BPTR file = (BPTR)NULL;
|
||||
|
||||
long_path_workaround(name, mode, 0, (LONG *)&file, (callback_f)open_callback);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static LONG
|
||||
delete_file_callback(CONST TEXT * name)
|
||||
{
|
||||
return DeleteFile(name);
|
||||
}
|
||||
|
||||
LONG
|
||||
__new_delete_file(CONST TEXT * name)
|
||||
{
|
||||
LONG successful = FALSE;
|
||||
|
||||
long_path_workaround(name, 0, 0, &successful, (callback_f)delete_file_callback);
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static LONG
|
||||
create_dir_callback(CONST TEXT * name)
|
||||
{
|
||||
return CreateDir(name);
|
||||
}
|
||||
|
||||
BPTR
|
||||
__new_create_dir(CONST TEXT * name)
|
||||
{
|
||||
BPTR lock = (BPTR)NULL;
|
||||
|
||||
long_path_workaround(name, 0, 0, (LONG *)&lock, (callback_f)create_dir_callback);
|
||||
|
||||
return lock;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static LONG
|
||||
make_link_callback(CONST TEXT * name, LONG dest, LONG soft)
|
||||
{
|
||||
return MakeLink(name, dest, soft);
|
||||
}
|
||||
|
||||
LONG
|
||||
__new_make_link(CONST TEXT * name, LONG dest, LONG soft)
|
||||
{
|
||||
LONG success = FALSE;
|
||||
|
||||
long_path_workaround(name, dest, soft, (LONG *)&success, (callback_f)make_link_callback);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
struct rename_arg
|
||||
{
|
||||
BPTR dir_lock;
|
||||
TEXT name[256+4];
|
||||
};
|
||||
|
||||
static struct rename_arg *
|
||||
create_rename_arg(void)
|
||||
{
|
||||
struct rename_arg * arg;
|
||||
|
||||
arg = AllocMem(sizeof(*arg), MEMF_PUBLIC);
|
||||
if (arg != NULL)
|
||||
arg->dir_lock = (BPTR)NULL;
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void
|
||||
delete_rename_arg(struct rename_arg * arg)
|
||||
{
|
||||
if (arg != NULL)
|
||||
{
|
||||
UnLock(arg->dir_lock);
|
||||
FreeMem(arg, sizeof(*arg));
|
||||
}
|
||||
}
|
||||
|
||||
static LONG
|
||||
rename_callback(CONST TEXT * name, struct rename_arg * arg)
|
||||
{
|
||||
BOOL successful = FALSE;
|
||||
LONG name_len;
|
||||
BPTR old_lock;
|
||||
|
||||
old_lock = CurrentDir((BPTR)NULL);
|
||||
CurrentDir(old_lock);
|
||||
|
||||
if (old_lock != (BPTR)NULL)
|
||||
{
|
||||
arg->dir_lock = DupLock(old_lock);
|
||||
if (arg->dir_lock != (BPTR)NULL)
|
||||
successful = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
successful = TRUE;
|
||||
}
|
||||
|
||||
if (successful)
|
||||
{
|
||||
name_len = strlen(name);
|
||||
if (name_len > 255)
|
||||
name_len = 255;
|
||||
|
||||
arg->name[0] = name_len;
|
||||
CopyMem(name, &arg->name[1], name_len);
|
||||
arg->name[1+name_len] = '\0';
|
||||
}
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
LONG
|
||||
__new_rename(CONST TEXT * oldName, CONST TEXT * newName)
|
||||
{
|
||||
struct rename_arg * from = NULL;
|
||||
struct rename_arg * to = NULL;
|
||||
struct MsgPort * filesys_port;
|
||||
LONG successful = FALSE;
|
||||
|
||||
if (IsFileSystem(oldName) && IsFileSystem(newName) && (strlen(oldName) > 255 || strlen(newName) > 255))
|
||||
{
|
||||
from = create_rename_arg();
|
||||
if (from == NULL)
|
||||
goto out;
|
||||
|
||||
to = create_rename_arg();
|
||||
if (to == NULL)
|
||||
goto out;
|
||||
|
||||
long_path_workaround(oldName, (LONG)from, 0, (LONG *)&successful, (callback_f)rename_callback);
|
||||
if (successful == FALSE)
|
||||
goto out;
|
||||
|
||||
long_path_workaround(newName, (LONG)to, 0, (LONG *)&successful, (callback_f)rename_callback);
|
||||
if (successful == FALSE)
|
||||
goto out;
|
||||
|
||||
if (from->dir_lock == (BPTR)NULL && to->dir_lock == (BPTR)NULL)
|
||||
{
|
||||
filesys_port = GetFileSysTask();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (from->dir_lock == (BPTR)NULL || to->dir_lock == (BPTR)NULL ||
|
||||
SameLock(from->dir_lock, to->dir_lock) == LOCK_DIFFERENT)
|
||||
{
|
||||
SetIoErr(ERROR_RENAME_ACROSS_DEVICES);
|
||||
|
||||
successful = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
filesys_port = ((struct FileLock *)BADDR(from->dir_lock))->fl_Task;
|
||||
}
|
||||
|
||||
successful = DoPkt4(filesys_port, ACTION_RENAME_OBJECT,
|
||||
from->dir_lock, MKBADDR(from->name),
|
||||
to->dir_lock, MKBADDR(to->name));
|
||||
}
|
||||
else
|
||||
{
|
||||
successful = Rename(oldName, newName);
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
delete_rename_arg(from);
|
||||
delete_rename_arg(to);
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#endif /* __amigaos4__ */
|
||||
168
library/stdio_long_path.h
Normal file
168
library/stdio_long_path.h
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2023 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Neither the name of Olaf Barthel nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _STDIO_LONG_PATH_H
|
||||
#define _STDIO_LONG_PATH_H
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* This option enables support for long path names under Kickstart 2.04
|
||||
* and beyond. Because this workaround is not needed for AmigaOS4, it will
|
||||
* be ignored if you try to enable it.
|
||||
*
|
||||
* Caution: Enable this feature only here in the "stdio_headers.h" file,
|
||||
* never as a compiler option. This is to make sure that all the
|
||||
* source code which makes use of this feature is rebuilt when
|
||||
* you change this file.
|
||||
*/
|
||||
/*#define __USE_LONG_PATHS*/
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#ifndef __amigaos4__
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#ifndef PROTO_DOS_H
|
||||
#include <proto/dos.h>
|
||||
#endif /* PROTO_DOS_H */
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
extern BPTR __new_lock(CONST TEXT * name, LONG type );
|
||||
extern LONG __new_set_owner(CONST TEXT * name, LONG owner_info);
|
||||
extern LONG __new_set_comment(CONST TEXT * name, CONST TEXT * comment);
|
||||
extern LONG __new_set_protection(CONST TEXT * name, LONG protect);
|
||||
extern LONG __new_set_file_date(CONST TEXT * name, CONST struct DateStamp * date);
|
||||
extern BPTR __new_open(CONST TEXT * name, LONG mode);
|
||||
extern LONG __new_delete_file(CONST TEXT * name);
|
||||
extern BPTR __new_create_dir(CONST TEXT * name);
|
||||
extern LONG __new_make_link(CONST TEXT * name, LONG dest, LONG soft);
|
||||
extern LONG __new_rename(CONST TEXT * oldName, CONST TEXT * newName);
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* We don't want the replacement functions to become active when building
|
||||
* the "stdio_long_path.c" file.
|
||||
*/
|
||||
#if ! defined(__DISABLE_LONG_PATHS) && defined(__USE_LONG_PATHS)
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(Lock)
|
||||
#undef Lock
|
||||
#endif /* Lock */
|
||||
|
||||
#define Lock(name, type) __new_lock((name), (type))
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(SetOwner)
|
||||
#undef SetOwner
|
||||
#endif /* SetOwner */
|
||||
|
||||
#define SetOwner(name, owner_info) __new_set_owner((name), (owner_info))
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(SetComment)
|
||||
#undef SetComment
|
||||
#endif /* SetComment */
|
||||
|
||||
#define SetComment(name, comment) __new_set_comment((name), (comment))
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(SetProtection)
|
||||
#undef SetProtection
|
||||
#endif /* SetProtection */
|
||||
|
||||
#define SetProtection(name, protect) __new_set_protection((name), (protect))
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(SetFileDate)
|
||||
#undef SetFileDate
|
||||
#endif /* SetFileDate */
|
||||
|
||||
#define SetFileDate(name, date) __new_set_file_date((name), (date))
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(Open)
|
||||
#undef Open
|
||||
#endif /* Open */
|
||||
|
||||
#define Open(name, mode) __new_open((name), (mode))
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(DeleteFile)
|
||||
#undef DeleteFile
|
||||
#endif /* DeleteFile */
|
||||
|
||||
#define DeleteFile(name) __new_delete_file(name)
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(CreateDir)
|
||||
#undef CreateDir
|
||||
#endif /* CreateDir */
|
||||
|
||||
#define CreateDir(name) __new_create_dir(name)
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(MakeLink)
|
||||
#undef MakeLink
|
||||
#endif /* MakeLink */
|
||||
|
||||
#define MakeLink(name, dest, soft) __new_make_link((name), (dest), (soft))
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(Rename)
|
||||
#undef Rename
|
||||
#endif /* Rename */
|
||||
|
||||
#define Rename(oldName, newName) __new_rename((oldName), (newName))
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#endif /* !__DISABLE_LONG_PATHS && __USE_LONG_PATHS */
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#endif /* __amigaos4__ */
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#endif /* _STDIO_LONG_PATH_H */
|
||||
@ -162,6 +162,11 @@ extern int __fputc(int c,FILE *stream,int buffer_mode);
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* stdio_fputs.c */
|
||||
extern int __fputs(const char *s, int line_feed, FILE *stream);
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* stdio_sscanf_hook_entry.c */
|
||||
extern int __sscanf_hook_entry(struct iob *string,struct file_action_message *fam);
|
||||
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: stdio_puts.c,v 1.8 2006-01-08 12:04:25 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2019 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -46,10 +44,7 @@
|
||||
int
|
||||
puts(const char *s)
|
||||
{
|
||||
struct iob * file = (struct iob *)stdout;
|
||||
int result = EOF;
|
||||
int buffer_mode;
|
||||
int c;
|
||||
|
||||
ENTER();
|
||||
|
||||
@ -57,11 +52,6 @@ puts(const char *s)
|
||||
|
||||
assert( s != NULL );
|
||||
|
||||
if(__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
flockfile(stdout);
|
||||
|
||||
#if defined(CHECK_FOR_NULL_POINTERS)
|
||||
{
|
||||
if(s == NULL)
|
||||
@ -72,45 +62,13 @@ puts(const char *s)
|
||||
}
|
||||
#endif /* CHECK_FOR_NULL_POINTERS */
|
||||
|
||||
assert( __is_valid_iob(file) );
|
||||
assert( FLAG_IS_SET(file->iob_Flags,IOBF_IN_USE) );
|
||||
assert( file->iob_BufferSize > 0 );
|
||||
|
||||
buffer_mode = (file->iob_Flags & IOBF_BUFFER_MODE);
|
||||
if(buffer_mode == IOBF_BUFFER_MODE_NONE)
|
||||
buffer_mode = IOBF_BUFFER_MODE_LINE;
|
||||
|
||||
if(__fputc_check(stdout) < 0)
|
||||
goto out;
|
||||
|
||||
while((c = (*s++)) != '\0')
|
||||
{
|
||||
if(__putc(c,stdout,buffer_mode) == EOF)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(__putc('\n',stdout,buffer_mode) == EOF)
|
||||
if (__fputs(s, '\n', stdout) == EOF)
|
||||
goto out;
|
||||
|
||||
result = OK;
|
||||
|
||||
out:
|
||||
|
||||
/* Note: if buffering is disabled for this stream, then we still
|
||||
may have buffered data around, queued to be printed right now.
|
||||
This is intended to improve performance as it takes more effort
|
||||
to write a single character to a file than to write a bunch. */
|
||||
if(result == 0 && (file->iob_Flags & IOBF_BUFFER_MODE) == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
if(__iob_write_buffer_is_valid(file) && __flush_iob_write_buffer(file) < 0)
|
||||
{
|
||||
SHOWMSG("couldn't flush the write buffer");
|
||||
result = EOF;
|
||||
}
|
||||
}
|
||||
|
||||
funlockfile(stdout);
|
||||
|
||||
RETURN(result);
|
||||
return(result);
|
||||
}
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: stdio_setvbuf.c,v 1.11 2008-09-04 12:07:58 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2019 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -114,20 +112,28 @@ setvbuf(FILE *stream,char *buf,int bufmode,size_t size)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* A buffer size of 0 bytes defaults to unbuffered operation. */
|
||||
/* A buffer size of 0 bytes will cause the default buffer size to be used. */
|
||||
if(size == 0)
|
||||
bufmode = IOBF_BUFFER_MODE_NONE;
|
||||
|
||||
/* If a certain buffer size is requested but no buffer was provided,
|
||||
allocate some memory for it. */
|
||||
if(size > 0 && buf == NULL)
|
||||
{
|
||||
/* Allocate a little more memory than necessary. */
|
||||
new_buffer = malloc(size + (__cache_line_size-1));
|
||||
if(new_buffer == NULL)
|
||||
size = BUFSIZ;
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
if(bufmode != IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
/* If a certain buffer size is requested but no buffer was provided,
|
||||
allocate some memory for it. */
|
||||
if(size > 0 && buf == NULL)
|
||||
{
|
||||
__set_errno(ENOBUFS);
|
||||
goto out;
|
||||
assert( size <= ((size + (__cache_line_size-1)) & ~(__cache_line_size-1)) );
|
||||
|
||||
/* Allocate a little more memory than necessary. */
|
||||
new_buffer = malloc(size + (__cache_line_size-1));
|
||||
if(new_buffer == NULL)
|
||||
{
|
||||
__set_errno(ENOBUFS);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -46,15 +46,8 @@
|
||||
/****************************************************************************/
|
||||
|
||||
void
|
||||
abort(void)
|
||||
__abort(void)
|
||||
{
|
||||
/* Try to call the signal handler that might be in charge of
|
||||
handling cleanup operations, etc. */
|
||||
raise(SIGABRT);
|
||||
|
||||
/* If the signal handler returns it means that we still have
|
||||
to terminate the program. */
|
||||
|
||||
__check_abort_enabled = FALSE;
|
||||
|
||||
__print_termination_message(NULL);
|
||||
@ -63,3 +56,17 @@ abort(void)
|
||||
does not trigger the exit trap. */
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void
|
||||
abort(void)
|
||||
{
|
||||
/* Try to call the signal handler that might be in charge of
|
||||
handling cleanup operations, etc. */
|
||||
raise(SIGABRT);
|
||||
|
||||
/* If the signal handler returns it means that we still have
|
||||
to terminate the program. */
|
||||
__abort();
|
||||
}
|
||||
|
||||
@ -69,11 +69,36 @@ struct MemoryContextNode
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* The default behaviour for alloca() failing is to
|
||||
* terminate the program rather than let it stumble
|
||||
* into a NULL-pointer reference.
|
||||
*/
|
||||
static void
|
||||
default_alloca_trap(void)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
CLIB_CONSTRUCTOR(alloca_init)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
__alloca_trap = default_alloca_trap;
|
||||
|
||||
NewList((struct List *)&alloca_memory_list);
|
||||
|
||||
LEAVE();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
CLIB_DESTRUCTOR(alloca_exit)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
/* Clean this up, too, just to be safe. */
|
||||
/* This list should remain empty. */
|
||||
NewList((struct List *)&alloca_memory_list);
|
||||
|
||||
LEAVE();
|
||||
@ -83,18 +108,18 @@ CLIB_DESTRUCTOR(alloca_exit)
|
||||
|
||||
/* Cleans up after all alloca() allocations that have been made so far. */
|
||||
static void
|
||||
alloca_cleanup(const char * file,int line)
|
||||
alloca_cleanup(const char * file, int line)
|
||||
{
|
||||
void * stack_pointer = __get_sp();
|
||||
|
||||
__memory_lock();
|
||||
|
||||
/* Initialize this if it hasn't been taken care of yet. */
|
||||
if(alloca_memory_list.mlh_Head == NULL)
|
||||
if (alloca_memory_list.mlh_Head == NULL)
|
||||
NewList((struct List *)&alloca_memory_list);
|
||||
|
||||
/* Is this worth cleaning up? */
|
||||
if(NOT IsMinListEmpty(&alloca_memory_list))
|
||||
if (NOT IsListEmpty((struct List *)&alloca_memory_list))
|
||||
{
|
||||
struct MemoryContextNode * mcn_prev;
|
||||
struct MemoryContextNode * mcn;
|
||||
@ -106,21 +131,24 @@ alloca_cleanup(const char * file,int line)
|
||||
we move up from the end to the top of the list, the closer we get
|
||||
to the allocations made in the context of a stack frame near to
|
||||
where were currently are. */
|
||||
for(mcn = (struct MemoryContextNode *)alloca_memory_list.mlh_TailPred ;
|
||||
mcn->mcn_MinNode.mln_Pred != NULL && mcn->mcn_StackPointer < stack_pointer ;
|
||||
mcn = mcn_prev)
|
||||
for (mcn = (struct MemoryContextNode *)alloca_memory_list.mlh_TailPred ;
|
||||
mcn->mcn_MinNode.mln_Pred != NULL && mcn->mcn_StackPointer < stack_pointer ;
|
||||
mcn = mcn_prev)
|
||||
{
|
||||
mcn_prev = (struct MemoryContextNode *)mcn->mcn_MinNode.mln_Pred;
|
||||
|
||||
Remove((struct Node *)mcn);
|
||||
|
||||
__free_memory(mcn->mcn_Memory,TRUE,file,line);
|
||||
__free_memory(mcn,FALSE,file,line);
|
||||
/* Note: force the memory to be freed because it
|
||||
has gone out of scope. */
|
||||
__free_memory(mcn->mcn_Memory, TRUE, file, line);
|
||||
|
||||
__free_memory(mcn, FALSE, file, line);
|
||||
}
|
||||
|
||||
/* Drop the cleanup callback if there's nothing to be cleaned
|
||||
up any more. */
|
||||
if(IsMinListEmpty(&alloca_memory_list))
|
||||
if (IsListEmpty((struct List *)&alloca_memory_list))
|
||||
__alloca_cleanup = NULL;
|
||||
}
|
||||
|
||||
@ -130,7 +158,7 @@ alloca_cleanup(const char * file,int line)
|
||||
/****************************************************************************/
|
||||
|
||||
__static void *
|
||||
__alloca(size_t size,const char * file,int line)
|
||||
__alloca(size_t size, const char * file, int line)
|
||||
{
|
||||
void * stack_pointer = __get_sp();
|
||||
struct MemoryContextNode * mcn;
|
||||
@ -138,27 +166,23 @@ __alloca(size_t size,const char * file,int line)
|
||||
|
||||
__memory_lock();
|
||||
|
||||
/* Initialize this if it hasn't been taken care of yet. */
|
||||
if(alloca_memory_list.mlh_Head == NULL)
|
||||
NewList((struct List *)&alloca_memory_list);
|
||||
|
||||
__alloca_cleanup = alloca_cleanup;
|
||||
(*__alloca_cleanup)(file,line);
|
||||
(*__alloca_cleanup)(file, line);
|
||||
|
||||
mcn = __allocate_memory(sizeof(*mcn),FALSE,file,line);
|
||||
if(mcn == NULL)
|
||||
mcn = __allocate_memory(sizeof(*mcn), FALSE, file, line);
|
||||
if (mcn == NULL)
|
||||
{
|
||||
SHOWMSG("not enough memory");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Allocate memory which cannot be run through realloc() or free(). */
|
||||
mcn->mcn_Memory = __allocate_memory(size,TRUE,file,line);
|
||||
if(mcn->mcn_Memory == NULL)
|
||||
mcn->mcn_Memory = __allocate_memory(size, TRUE, file, line);
|
||||
if (mcn->mcn_Memory == NULL)
|
||||
{
|
||||
SHOWMSG("not enough memory");
|
||||
|
||||
__free(mcn,file,line);
|
||||
__free(mcn, file, line);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -166,7 +190,7 @@ __alloca(size_t size,const char * file,int line)
|
||||
|
||||
assert( alloca_memory_list.mlh_Head != NULL );
|
||||
|
||||
AddTail((struct List *)&alloca_memory_list,(struct Node *)mcn);
|
||||
AddTail((struct List *)&alloca_memory_list, (struct Node *)mcn);
|
||||
|
||||
result = mcn->mcn_Memory;
|
||||
|
||||
@ -176,10 +200,14 @@ __alloca(size_t size,const char * file,int line)
|
||||
|
||||
/* If we are about to return NULL and a trap function is
|
||||
provided, call it rather than returning NULL. */
|
||||
if(result == NULL && __alloca_trap != NULL)
|
||||
(*__alloca_trap)();
|
||||
if (result == NULL && __alloca_trap != NULL)
|
||||
{
|
||||
__alloca_cleanup = NULL;
|
||||
|
||||
return(result);
|
||||
(*__alloca_trap)();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
@ -191,5 +219,5 @@ alloca(size_t size)
|
||||
|
||||
result = __alloca(size,NULL,0);
|
||||
|
||||
return(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
47
library/stdlib_calloc.c
Normal file → Executable file
47
library/stdlib_calloc.c
Normal file → Executable file
@ -48,48 +48,51 @@
|
||||
/****************************************************************************/
|
||||
|
||||
__static void *
|
||||
__calloc(size_t num_elements,size_t element_size,const char * file,int line)
|
||||
__calloc(size_t num_elements, size_t element_size, const char * file UNUSED, int line UNUSED)
|
||||
{
|
||||
void * result = NULL;
|
||||
size_t total_size;
|
||||
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
/*__check_memory_allocations(file,line);*/
|
||||
__check_memory_allocations(file, line);
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
|
||||
/* This might overflow. */
|
||||
total_size = num_elements * element_size;
|
||||
|
||||
/* No arithmetic overflow? */
|
||||
if(total_size >= num_elements)
|
||||
{
|
||||
result = __malloc(total_size,file,line);
|
||||
if(result != NULL)
|
||||
memset(result,0,total_size);
|
||||
else
|
||||
SHOWMSG("memory allocation failure");
|
||||
}
|
||||
/* Multiplying the number and size of elements overflows
|
||||
* the size_t range.
|
||||
*/
|
||||
else
|
||||
/* Check for overflow. */
|
||||
total_size = element_size * num_elements;
|
||||
if (num_elements > 0 && element_size > 0 && element_size != (total_size / num_elements))
|
||||
{
|
||||
D(("calloc(num_elements=%ld, element_size=%ld) overflow"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
return(result);
|
||||
/* Note: malloc(0) may allocate memory and will also
|
||||
* initialize its contents to zero.
|
||||
*/
|
||||
result = __malloc(total_size, file, line);
|
||||
if (result == NULL)
|
||||
{
|
||||
SHOWMSG("memory allocation failure");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (total_size > 0)
|
||||
memset(result, 0, total_size);
|
||||
|
||||
out:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void *
|
||||
calloc(size_t num_elements,size_t element_size)
|
||||
calloc(size_t num_elements, size_t element_size)
|
||||
{
|
||||
void * result;
|
||||
|
||||
result = __calloc(num_elements,element_size,NULL,0);
|
||||
result = __calloc(num_elements, element_size, NULL, 0);
|
||||
|
||||
return(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -59,18 +59,18 @@ get_hex_char(int n)
|
||||
{
|
||||
char result;
|
||||
|
||||
if(0 <= n && n <= 9)
|
||||
if (0 <= n && n <= 9)
|
||||
result = n + '0';
|
||||
else
|
||||
result = n + 'A' - 10;
|
||||
|
||||
return(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
STATIC VOID
|
||||
int_to_hex(unsigned long v,char * buffer,int min_digits)
|
||||
int_to_hex(unsigned long v, char * buffer, int min_digits)
|
||||
{
|
||||
int i,j,c;
|
||||
int i, j, c;
|
||||
|
||||
i = 0;
|
||||
|
||||
@ -81,12 +81,12 @@ int_to_hex(unsigned long v,char * buffer,int min_digits)
|
||||
|
||||
buffer[i++] = get_hex_char(c);
|
||||
}
|
||||
while(v > 0);
|
||||
while (v > 0);
|
||||
|
||||
while(i < min_digits)
|
||||
while (i < min_digits)
|
||||
buffer[i++] = '0';
|
||||
|
||||
for(j = 0 ; j < i / 2 ; j++)
|
||||
for (j = 0 ; j < i / 2 ; j++)
|
||||
{
|
||||
c = buffer[i - 1 - j];
|
||||
buffer[i - 1 - j] = buffer[j];
|
||||
@ -97,47 +97,47 @@ int_to_hex(unsigned long v,char * buffer,int min_digits)
|
||||
}
|
||||
|
||||
STATIC VOID
|
||||
dump_memory(unsigned char * m,int size,int ignore)
|
||||
dump_memory(const unsigned char * m, int size, int ignore)
|
||||
{
|
||||
const int mod = 20;
|
||||
int position,i,c;
|
||||
int position, i, c;
|
||||
char buffer[120];
|
||||
char hex[10];
|
||||
|
||||
buffer[0] = '\0';
|
||||
|
||||
for(i = 0 ; i < size ; i++)
|
||||
for (i = 0 ; i < size ; i++)
|
||||
{
|
||||
position = i % mod;
|
||||
if(position == 0)
|
||||
if (position == 0)
|
||||
{
|
||||
if(buffer[0] != '\0')
|
||||
if (buffer[0] != '\0')
|
||||
{
|
||||
int len = sizeof(buffer)-1;
|
||||
|
||||
while(len > 0 && buffer[len-1] == ' ')
|
||||
while (len > 0 && buffer[len-1] == ' ')
|
||||
len--;
|
||||
|
||||
buffer[len] = '\0';
|
||||
|
||||
kprintf("[%s] %s\n",__program_name,buffer);
|
||||
kprintf("[%s] %s\n", __program_name, buffer);
|
||||
}
|
||||
|
||||
memset(buffer,' ',sizeof(buffer)-1);
|
||||
memset(buffer, ' ', sizeof(buffer)-1);
|
||||
|
||||
int_to_hex((unsigned long)&m[i],hex,8);
|
||||
int_to_hex((unsigned long)&m[i], hex, 8);
|
||||
|
||||
memmove(buffer,hex,8);
|
||||
memmove(buffer, hex, 8);
|
||||
hex[9] = ':';
|
||||
}
|
||||
|
||||
if(m[i] != ignore)
|
||||
if (m[i] != ignore)
|
||||
{
|
||||
buffer[10 + 2 * position + 0] = get_hex_char(m[i] >> 4);
|
||||
buffer[10 + 2 * position + 1] = get_hex_char(m[i] & 15);
|
||||
|
||||
c = m[i];
|
||||
if(c < ' ' || (c >= 127 && c <= 160))
|
||||
if (c < ' ' || (127 <= c && c < 160))
|
||||
c = '.';
|
||||
|
||||
buffer[10 + 2 * mod + 1 + position] = c;
|
||||
@ -149,111 +149,111 @@ dump_memory(unsigned char * m,int size,int ignore)
|
||||
}
|
||||
}
|
||||
|
||||
if(buffer[0] != '\0')
|
||||
if (buffer[0] != '\0')
|
||||
{
|
||||
int len = sizeof(buffer)-1;
|
||||
|
||||
while(len > 0 && buffer[len-1] == ' ')
|
||||
while (len > 0 && buffer[len-1] == ' ')
|
||||
len--;
|
||||
|
||||
buffer[len] = '\0';
|
||||
|
||||
kprintf("[%s] %s\n",__program_name,buffer);
|
||||
kprintf("[%s] %s\n", __program_name, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
STATIC VOID
|
||||
check_memory_node(struct MemoryNode * mn,const char * file,int line)
|
||||
check_memory_node(struct MemoryNode * mn, const char * file, int line)
|
||||
{
|
||||
ULONG size = GET_MN_SIZE(mn);
|
||||
unsigned char * head = (unsigned char *)(mn + 1);
|
||||
unsigned char * body = head + MALLOC_HEAD_SIZE;
|
||||
unsigned char * tail = body + size;
|
||||
ULONG size = mn->mn_OriginalSize;
|
||||
const unsigned char * head = (unsigned char *)&mn[1];
|
||||
const unsigned char * body = &head[MALLOC_HEAD_SIZE];
|
||||
const unsigned char * tail = &body[size];
|
||||
int max_head_damage = 0;
|
||||
int max_tail_damage = 0;
|
||||
int max_body_damage = 0;
|
||||
int i;
|
||||
|
||||
for(i = 1 ; i <= MALLOC_HEAD_SIZE ; i++)
|
||||
for (i = 1 ; i <= MALLOC_HEAD_SIZE ; i++)
|
||||
{
|
||||
if(head[MALLOC_HEAD_SIZE - i] != MALLOC_HEAD_FILL)
|
||||
if (head[MALLOC_HEAD_SIZE - i] != MALLOC_HEAD_FILL)
|
||||
max_head_damage = i;
|
||||
}
|
||||
|
||||
if(max_head_damage > 0)
|
||||
if (max_head_damage > 0)
|
||||
{
|
||||
kprintf("[%s] ",__program_name);
|
||||
kprintf("[%s] ", __program_name);
|
||||
|
||||
if(file != NULL)
|
||||
kprintf("%s:%ld:",file,line);
|
||||
if (file != NULL)
|
||||
kprintf("%s:%ld:", file, line);
|
||||
|
||||
kprintf("At least %ld bytes were damaged in front of allocation 0x%08lx..0x%08lx, size = %ld",
|
||||
max_head_damage,
|
||||
body,body + size - 1,size);
|
||||
body, body + size - 1, size);
|
||||
|
||||
if(mn->mn_File != NULL)
|
||||
kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line);
|
||||
if (mn->mn_File != NULL)
|
||||
kprintf(", allocated at %s:%ld", mn->mn_File, mn->mn_Line);
|
||||
|
||||
kprintf("\n");
|
||||
|
||||
dump_memory(head,MALLOC_HEAD_SIZE,MALLOC_HEAD_FILL);
|
||||
dump_memory(head, MALLOC_HEAD_SIZE, MALLOC_HEAD_FILL);
|
||||
}
|
||||
|
||||
for(i = 0 ; i < MALLOC_TAIL_SIZE ; i++)
|
||||
for (i = 0 ; i < MALLOC_TAIL_SIZE ; i++)
|
||||
{
|
||||
if(tail[i] != MALLOC_TAIL_FILL)
|
||||
if (tail[i] != MALLOC_TAIL_FILL)
|
||||
max_tail_damage = i+1;
|
||||
}
|
||||
|
||||
if(max_tail_damage > 0)
|
||||
if (max_tail_damage > 0)
|
||||
{
|
||||
kprintf("[%s] ",__program_name);
|
||||
kprintf("[%s] ", __program_name);
|
||||
|
||||
if(file != NULL)
|
||||
kprintf("%s:%ld:",file,line);
|
||||
if (file != NULL)
|
||||
kprintf("%s:%ld:", file, line);
|
||||
|
||||
kprintf("At least %ld bytes were damaged behind allocation 0x%08lx..0x%08lx, size = %ld (with damage = %ld)",
|
||||
max_tail_damage,
|
||||
body,body + size - 1,
|
||||
size,size+max_tail_damage);
|
||||
body, body + size - 1,
|
||||
size, size + max_tail_damage);
|
||||
|
||||
if(mn->mn_File != NULL)
|
||||
kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line);
|
||||
if (mn->mn_File != NULL)
|
||||
kprintf(", allocated at %s:%ld", mn->mn_File, mn->mn_Line);
|
||||
|
||||
kprintf("\n");
|
||||
|
||||
dump_memory(tail,MALLOC_TAIL_SIZE,MALLOC_TAIL_FILL);
|
||||
dump_memory(tail, MALLOC_TAIL_SIZE, MALLOC_TAIL_FILL);
|
||||
}
|
||||
|
||||
if(mn->mn_AlreadyFree)
|
||||
if (mn->mn_AlreadyFree)
|
||||
{
|
||||
ULONG j;
|
||||
|
||||
for(j = 0 ; j < size ; j++)
|
||||
for (j = 0 ; j < size ; j++)
|
||||
{
|
||||
if(body[j] != MALLOC_FREE_FILL)
|
||||
if (body[j] != MALLOC_FREE_FILL)
|
||||
max_body_damage = j+1;
|
||||
}
|
||||
|
||||
if(max_body_damage > 0)
|
||||
if (max_body_damage > 0)
|
||||
{
|
||||
kprintf("[%s] ",__program_name);
|
||||
kprintf("[%s] ", __program_name);
|
||||
|
||||
if(file != NULL)
|
||||
kprintf("%s:%ld:",file,line);
|
||||
if (file != NULL)
|
||||
kprintf("%s:%ld:", file, line);
|
||||
|
||||
kprintf("At least %ld bytes were damaged in freed allocation 0x%08lx..0x%08lx, size = %ld",
|
||||
max_body_damage,
|
||||
body,body + size - 1,size);
|
||||
body, body + size - 1, size);
|
||||
|
||||
if(mn->mn_File != NULL)
|
||||
kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line);
|
||||
if (mn->mn_File != NULL)
|
||||
kprintf(", allocated at %s:%ld", mn->mn_File, mn->mn_Line);
|
||||
|
||||
kprintf("\n");
|
||||
|
||||
dump_memory(body,size,MALLOC_FREE_FILL);
|
||||
dump_memory(body, size, MALLOC_FREE_FILL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -261,17 +261,17 @@ check_memory_node(struct MemoryNode * mn,const char * file,int line)
|
||||
/****************************************************************************/
|
||||
|
||||
void
|
||||
__check_memory_allocations(const char * file,int line)
|
||||
__check_memory_allocations(const char * file, int line)
|
||||
{
|
||||
struct MemoryNode * mn;
|
||||
|
||||
__memory_lock();
|
||||
|
||||
for(mn = (struct MemoryNode *)__memory_list.mlh_Head ;
|
||||
mn->mn_MinNode.mln_Succ != NULL ;
|
||||
mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ)
|
||||
for (mn = (struct MemoryNode *)__memory_list.mlh_Head ;
|
||||
mn->mn_MinNode.mln_Succ != NULL ;
|
||||
mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ)
|
||||
{
|
||||
check_memory_node(mn,file,line);
|
||||
check_memory_node(mn, file, line);
|
||||
}
|
||||
|
||||
__memory_unlock();
|
||||
@ -290,7 +290,7 @@ __find_memory_node(void * address)
|
||||
|
||||
#if defined(__USE_MEM_TREES)
|
||||
{
|
||||
result = __red_black_tree_find(&__memory_tree,address);
|
||||
result = __red_black_tree_find(&__memory_tree, address);
|
||||
}
|
||||
#else
|
||||
{
|
||||
@ -298,11 +298,11 @@ __find_memory_node(void * address)
|
||||
|
||||
result = NULL;
|
||||
|
||||
for(mn = (struct MemoryNode *)__memory_list.mlh_Head ;
|
||||
mn->mn_MinNode.mln_Succ != NULL ;
|
||||
mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ)
|
||||
for (mn = (struct MemoryNode *)__memory_list.mlh_Head ;
|
||||
mn->mn_MinNode.mln_Succ != NULL ;
|
||||
mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ)
|
||||
{
|
||||
if(address == mn->mn_Allocation)
|
||||
if (address == mn->mn_Allocation)
|
||||
{
|
||||
result = mn;
|
||||
break;
|
||||
@ -331,7 +331,7 @@ __find_memory_node(void * address)
|
||||
|
||||
result = &((struct MemoryNode *)address)[-1];
|
||||
|
||||
return(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
@ -349,96 +349,84 @@ remove_and_free_memory_node(struct MemoryNode * mn)
|
||||
|
||||
__memory_lock();
|
||||
|
||||
allocation_size = mn->mn_AllocationSize;
|
||||
|
||||
#if defined(__MEM_DEBUG)
|
||||
{
|
||||
Remove((struct Node *)mn);
|
||||
|
||||
#if defined(__USE_MEM_TREES)
|
||||
{
|
||||
__red_black_tree_remove(&__memory_tree,mn);
|
||||
__red_black_tree_remove(&__memory_tree, mn);
|
||||
}
|
||||
#endif /* __USE_MEM_TREES */
|
||||
|
||||
allocation_size = sizeof(*mn) + MALLOC_HEAD_SIZE + GET_MN_SIZE(mn) + MALLOC_TAIL_SIZE;
|
||||
|
||||
assert( allocation_size == mn->mn_AllocationSize );
|
||||
|
||||
memset(mn,MALLOC_FREE_FILL,allocation_size);
|
||||
}
|
||||
#else
|
||||
{
|
||||
allocation_size = sizeof(*mn) + GET_MN_SIZE(mn);
|
||||
memset(mn, MALLOC_FREE_FILL, allocation_size);
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
|
||||
#if defined(__USE_SLAB_ALLOCATOR)
|
||||
{
|
||||
/* Are we using the slab allocator? */
|
||||
if(__slab_data.sd_InUse)
|
||||
if (__slab_data.sd_InUse)
|
||||
{
|
||||
__slab_free(mn,allocation_size);
|
||||
/* No need to remove the memory node because it was never
|
||||
* added or has already been removed if the memory debugging
|
||||
* option is in effect.
|
||||
*/
|
||||
__slab_free(mn, allocation_size);
|
||||
}
|
||||
/* Are we using the memory pool? */
|
||||
else if (__memory_pool != NULL)
|
||||
{
|
||||
/* No need to remove the memory node because it was never
|
||||
* added or has already been removed if the memory debugging
|
||||
* option is in effect.
|
||||
*/
|
||||
PROFILE_OFF();
|
||||
FreePooled(__memory_pool, mn, allocation_size);
|
||||
PROFILE_ON();
|
||||
}
|
||||
/* So we have to do this the hard way... */
|
||||
else
|
||||
{
|
||||
if(__memory_pool != NULL)
|
||||
#if NOT defined(__MEM_DEBUG)
|
||||
{
|
||||
PROFILE_OFF();
|
||||
FreePooled(__memory_pool,mn,allocation_size);
|
||||
PROFILE_ON();
|
||||
Remove((struct Node *)mn);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(__MEM_DEBUG)
|
||||
{
|
||||
PROFILE_OFF();
|
||||
FreeMem(mn,allocation_size);
|
||||
PROFILE_ON();
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct MinNode * mln = (struct MinNode *)mn;
|
||||
#endif /* __MEM_DEBUG */
|
||||
|
||||
mln--;
|
||||
|
||||
Remove((struct Node *)mln);
|
||||
|
||||
PROFILE_OFF();
|
||||
FreeMem(mln,sizeof(*mln) + allocation_size);
|
||||
PROFILE_ON();
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
}
|
||||
PROFILE_OFF();
|
||||
FreeMem(mn, allocation_size);
|
||||
PROFILE_ON();
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
if(__memory_pool != NULL)
|
||||
if (__memory_pool != NULL)
|
||||
{
|
||||
/* No need to remove the memory node because it was never
|
||||
* added or has already been removed if the memory debugging
|
||||
* option is in effect.
|
||||
*/
|
||||
PROFILE_OFF();
|
||||
FreePooled(__memory_pool,mn,allocation_size);
|
||||
FreePooled(__memory_pool, mn, allocation_size);
|
||||
PROFILE_ON();
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(__MEM_DEBUG)
|
||||
#if NOT defined(__MEM_DEBUG)
|
||||
{
|
||||
PROFILE_OFF();
|
||||
FreeMem(mn,allocation_size);
|
||||
PROFILE_ON();
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct MinNode * mln = (struct MinNode *)mn;
|
||||
|
||||
mln--;
|
||||
|
||||
Remove((struct Node *)mln);
|
||||
|
||||
PROFILE_OFF();
|
||||
FreeMem(mln,sizeof(*mln) + allocation_size);
|
||||
PROFILE_ON();
|
||||
Remove((struct Node *)mn);
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
|
||||
/* No need to remove the memory node because the memory
|
||||
* debugging option is in effect.
|
||||
*/
|
||||
PROFILE_OFF();
|
||||
FreeMem(mn, allocation_size);
|
||||
PROFILE_ON();
|
||||
}
|
||||
}
|
||||
#endif /* __USE_SLAB_ALLOCATOR */
|
||||
@ -452,37 +440,35 @@ remove_and_free_memory_node(struct MemoryNode * mn)
|
||||
/****************************************************************************/
|
||||
|
||||
void
|
||||
__free_memory_node(struct MemoryNode * mn,const char * UNUSED file,int UNUSED line)
|
||||
__free_memory_node(struct MemoryNode * mn, const char * UNUSED file, int UNUSED line)
|
||||
{
|
||||
assert(mn != NULL);
|
||||
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
ULONG size = GET_MN_SIZE(mn);
|
||||
check_memory_node(mn, file, line);
|
||||
|
||||
check_memory_node(mn,file,line);
|
||||
|
||||
if(NOT mn->mn_AlreadyFree)
|
||||
if (NOT mn->mn_AlreadyFree)
|
||||
{
|
||||
#ifdef __MEM_DEBUG_LOG
|
||||
{
|
||||
kprintf("[%s] - %10ld 0x%08lx [",__program_name,size,mn->mn_Allocation);
|
||||
kprintf("[%s] - %10ld 0x%08lx [",__program_name, mn->mn_OriginalSize, mn->mn_Allocation);
|
||||
|
||||
if(mn->mn_File != NULL)
|
||||
kprintf("allocated at %s:%ld, ",mn->mn_File,mn->mn_Line);
|
||||
if (mn->mn_File != NULL)
|
||||
kprintf("allocated at %s:%ld, ", mn->mn_File, mn->mn_Line);
|
||||
|
||||
kprintf("freed at %s:%ld]\n",file,line);
|
||||
kprintf("freed at %s:%ld]\n", file, line);
|
||||
}
|
||||
#endif /* __MEM_DEBUG_LOG */
|
||||
|
||||
if(__never_free)
|
||||
if (__never_free)
|
||||
{
|
||||
mn->mn_AlreadyFree = TRUE;
|
||||
|
||||
mn->mn_FreeFile = (char *)file;
|
||||
mn->mn_FreeFile = (char *)file;
|
||||
mn->mn_FreeLine = line;
|
||||
|
||||
memset(mn->mn_Allocation,MALLOC_FREE_FILL,size);
|
||||
memset(&mn[1], MALLOC_FREE_FILL, MALLOC_HEAD_SIZE + mn->mn_OriginalSize + MALLOC_TAIL_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -493,19 +479,19 @@ __free_memory_node(struct MemoryNode * mn,const char * UNUSED file,int UNUSED li
|
||||
{
|
||||
#ifdef __MEM_DEBUG_LOG
|
||||
{
|
||||
kprintf("[%s] - %10ld 0x%08lx [",__program_name,size,mn->mn_Allocation);
|
||||
kprintf("[%s] - %10ld 0x%08lx [", __program_name, mn->mn_AllocationSize, mn);
|
||||
|
||||
kprintf("FAILED]\n");
|
||||
}
|
||||
#endif /* __MEM_DEBUG_LOG */
|
||||
|
||||
kprintf("[%s] %s:%ld:Allocation at address 0x%08lx, size %ld",
|
||||
__program_name,file,line,mn->mn_Allocation,size);
|
||||
__program_name, file, line, mn->mn_Allocation, mn->mn_OriginalSize);
|
||||
|
||||
if(mn->mn_File != NULL)
|
||||
kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line);
|
||||
if (mn->mn_File != NULL)
|
||||
kprintf(", allocated at %s:%ld", mn->mn_File, mn->mn_Line);
|
||||
|
||||
kprintf(", has already been freed at %s:%ld.\n",mn->mn_FreeFile,mn->mn_FreeLine);
|
||||
kprintf(", has already been freed at %s:%ld.\n", mn->mn_FreeFile, mn->mn_FreeLine);
|
||||
}
|
||||
}
|
||||
#else
|
||||
@ -518,7 +504,7 @@ __free_memory_node(struct MemoryNode * mn,const char * UNUSED file,int UNUSED li
|
||||
/****************************************************************************/
|
||||
|
||||
VOID
|
||||
__free_memory(void * ptr,BOOL force,const char * file,int line)
|
||||
__free_memory(void * ptr, BOOL force, const char * file, int line)
|
||||
{
|
||||
struct MemoryNode * mn;
|
||||
|
||||
@ -529,7 +515,7 @@ __free_memory(void * ptr,BOOL force,const char * file,int line)
|
||||
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
/*if((rand() % 16) == 0)
|
||||
/*if ((rand() % 16) == 0)
|
||||
__check_memory_allocations(file,line);*/
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
@ -538,34 +524,37 @@ __free_memory(void * ptr,BOOL force,const char * file,int line)
|
||||
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
if(mn != NULL)
|
||||
if (mn != NULL)
|
||||
{
|
||||
if(force || FLAG_IS_CLEAR(mn->mn_Size, MN_SIZE_NEVERFREE))
|
||||
__free_memory_node(mn,file,line);
|
||||
if (force || FLAG_IS_CLEAR(mn->mn_Flags, MNF_NEVER_FREE))
|
||||
__free_memory_node(mn, file, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __MEM_DEBUG_LOG
|
||||
{
|
||||
kprintf("[%s] - %10ld 0x%08lx [",__program_name,0,ptr);
|
||||
kprintf("[%s] - %10ld 0x%08lx [", __program_name, 0, ptr);
|
||||
|
||||
kprintf("FAILED]\n");
|
||||
}
|
||||
#endif /* __MEM_DEBUG_LOG */
|
||||
|
||||
kprintf("[%s] %s:%ld:Address for free(0x%08lx) not known.\n",__program_name,file,line,ptr);
|
||||
kprintf("[%s] %s:%ld:Address for free(0x%08lx) not known.\n", __program_name, file, line, ptr);
|
||||
|
||||
D(("memory allocation at 0x%08lx could not be freed",ptr));
|
||||
D(("memory allocation at 0x%08lx could not be freed", ptr));
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
assert( mn != NULL );
|
||||
|
||||
SHOWVALUE(mn->mn_Size);
|
||||
if (FLAG_IS_SET(mn->mn_Flags, MNF_NEVER_FREE))
|
||||
D(("mn->mn_AllocationSize=%ld (0x%08lx), not to be freed", mn->mn_AllocationSize, mn->mn_AllocationSize));
|
||||
else
|
||||
D(("mn->mn_AllocationSize=%ld (0x%08lx)", mn->mn_AllocationSize, mn->mn_AllocationSize));
|
||||
|
||||
if(mn != NULL && (force || FLAG_IS_CLEAR(mn->mn_Size, MN_SIZE_NEVERFREE)))
|
||||
__free_memory_node(mn,file,line);
|
||||
if (mn != NULL && (force || FLAG_IS_CLEAR(mn->mn_Flags, MNF_NEVER_FREE)))
|
||||
__free_memory_node(mn, file, line);
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
}
|
||||
@ -578,13 +567,13 @@ __free(void * ptr,const char * file,int line)
|
||||
__memory_lock();
|
||||
|
||||
/* Try to get rid of now unused memory. */
|
||||
if(__alloca_cleanup != NULL)
|
||||
(*__alloca_cleanup)(file,line);
|
||||
if (__alloca_cleanup != NULL)
|
||||
(*__alloca_cleanup)(file, line);
|
||||
|
||||
__memory_unlock();
|
||||
|
||||
if(ptr != NULL)
|
||||
__free_memory(ptr,FALSE,file,line);
|
||||
if (ptr != NULL)
|
||||
__free_memory(ptr, FALSE, file, line);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
@ -592,5 +581,5 @@ __free(void * ptr,const char * file,int line)
|
||||
void
|
||||
free(void * ptr)
|
||||
{
|
||||
__free(ptr,NULL,0);
|
||||
__free(ptr, NULL, 0);
|
||||
}
|
||||
|
||||
26
library/stdlib_get_slab_usage.c
Normal file → Executable file
26
library/stdlib_get_slab_usage.c
Normal file → Executable file
@ -44,14 +44,14 @@
|
||||
void
|
||||
__get_slab_usage(__slab_usage_callback callback)
|
||||
{
|
||||
if(__slab_data.sd_InUse)
|
||||
if (__slab_data.sd_InUse)
|
||||
{
|
||||
struct __slab_usage_information sui;
|
||||
const struct SlabNode * sn;
|
||||
BOOL stop;
|
||||
int i;
|
||||
|
||||
memset(&sui,0,sizeof(sui));
|
||||
memset(&sui, 0, sizeof(sui));
|
||||
|
||||
__memory_lock();
|
||||
|
||||
@ -59,11 +59,11 @@ __get_slab_usage(__slab_usage_callback callback)
|
||||
sui.sui_num_single_allocations = __slab_data.sd_NumSingleAllocations;
|
||||
sui.sui_total_single_allocation_size = __slab_data.sd_TotalSingleAllocationSize;
|
||||
|
||||
for(i = 0 ; i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; i++)
|
||||
for (i = 0 ; i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; i++)
|
||||
{
|
||||
for(sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ;
|
||||
sn->sn_MinNode.mln_Succ != NULL ;
|
||||
sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ)
|
||||
for (sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ;
|
||||
sn->sn_MinNode.mln_Succ != NULL ;
|
||||
sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ)
|
||||
{
|
||||
if (sn->sn_UseCount == 0)
|
||||
sui.sui_num_empty_slabs++;
|
||||
@ -76,13 +76,15 @@ __get_slab_usage(__slab_usage_callback callback)
|
||||
}
|
||||
}
|
||||
|
||||
if(sui.sui_num_slabs > 0)
|
||||
if (sui.sui_num_slabs > 0)
|
||||
{
|
||||
for(i = 0, stop = FALSE ; NOT stop && i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; i++)
|
||||
for (i = 0, stop = FALSE ;
|
||||
stop == FALSE && i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ;
|
||||
i++)
|
||||
{
|
||||
for(sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ;
|
||||
sn->sn_MinNode.mln_Succ != NULL ;
|
||||
sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ)
|
||||
for (sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ;
|
||||
sn->sn_MinNode.mln_Succ != NULL ;
|
||||
sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ)
|
||||
{
|
||||
sui.sui_chunk_size = sn->sn_ChunkSize;
|
||||
sui.sui_num_chunks = sn->sn_Count;
|
||||
@ -91,7 +93,7 @@ __get_slab_usage(__slab_usage_callback callback)
|
||||
|
||||
sui.sui_slab_index++;
|
||||
|
||||
if((*callback)(&sui) != 0)
|
||||
if ((*callback)(&sui) != 0)
|
||||
{
|
||||
stop = TRUE;
|
||||
break;
|
||||
|
||||
@ -189,6 +189,10 @@ extern BOOL NOCOMMON __lib_startup;
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
extern int __addition_overflows(ULONG x, ULONG y);
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
extern void NOCOMMON (*__alloca_trap)(void);
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
@ -58,6 +58,7 @@
|
||||
|
||||
unsigned long NOCOMMON __maximum_memory_allocated;
|
||||
unsigned long NOCOMMON __current_memory_allocated;
|
||||
|
||||
unsigned long NOCOMMON __maximum_num_memory_chunks_allocated;
|
||||
unsigned long NOCOMMON __current_num_memory_chunks_allocated;
|
||||
|
||||
@ -74,36 +75,59 @@ struct MinList NOCOMMON __memory_list;
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void *
|
||||
__allocate_memory(size_t size,BOOL never_free,const char *debug_file_name UNUSED,int debug_line_number UNUSED)
|
||||
/* Check if the sum of two unsigned 32-bit integers will be larger than what
|
||||
* an unsigned 32-bit integer can hold and return the overflow. This
|
||||
* algorithm comes from Henry S. Warren's book "Hacker's delight".
|
||||
*/
|
||||
int
|
||||
__addition_overflows(ULONG x, ULONG y)
|
||||
{
|
||||
struct MemoryNode * mn;
|
||||
ULONG z;
|
||||
|
||||
assert( sizeof(x) == 4 );
|
||||
assert( sizeof(y) == 4 );
|
||||
|
||||
z = (x & y) | ((x | y) & ~(x + y));
|
||||
|
||||
return ((LONG)z) < 0;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void *
|
||||
__allocate_memory(
|
||||
size_t size,
|
||||
BOOL never_free,
|
||||
const char * debug_file_name UNUSED,
|
||||
int debug_line_number UNUSED)
|
||||
{
|
||||
struct MemoryNode * mn UNUSED;
|
||||
size_t allocation_size;
|
||||
void * result = NULL;
|
||||
size_t original_size UNUSED;
|
||||
|
||||
#if defined(UNIX_PATH_SEMANTICS)
|
||||
size_t original_size;
|
||||
|
||||
{
|
||||
original_size = size;
|
||||
|
||||
/* The libunix.a flavour accepts zero length memory allocations
|
||||
and quietly turns them into a pointer sized allocations. */
|
||||
if(size == 0)
|
||||
size = sizeof(char *);
|
||||
/* The libunix.a flavour of malloc() accepts zero-length
|
||||
memory allocations and quietly turns these into
|
||||
pointer-sized allocations. */
|
||||
if (size == 0)
|
||||
size = sizeof(BYTE *);
|
||||
}
|
||||
#endif /* UNIX_PATH_SEMANTICS */
|
||||
|
||||
__memory_lock();
|
||||
|
||||
/* Zero length allocations are by default rejected. */
|
||||
if(size == 0)
|
||||
if (size == 0)
|
||||
{
|
||||
__set_errno(EINVAL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(__free_memory_threshold > 0 && AvailMem(MEMF_ANY|MEMF_LARGEST) < __free_memory_threshold)
|
||||
if (__free_memory_threshold > 0 && AvailMem(MEMF_ANY|MEMF_LARGEST) < __free_memory_threshold)
|
||||
{
|
||||
SHOWMSG("not enough free memory available to safely proceed with allocation");
|
||||
goto out;
|
||||
@ -115,172 +139,140 @@ __allocate_memory(size_t size,BOOL never_free,const char *debug_file_name UNUSED
|
||||
assert( MALLOC_TAIL_SIZE > 0 && (MALLOC_TAIL_SIZE % 4) == 0 );
|
||||
assert( (sizeof(*mn) % 4) == 0 );
|
||||
|
||||
if (__addition_overflows(sizeof(*mn) + MALLOC_HEAD_SIZE + MALLOC_TAIL_SIZE, size))
|
||||
{
|
||||
SHOWMSG("integer overflow");
|
||||
|
||||
__set_errno(ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
allocation_size = sizeof(*mn) + MALLOC_HEAD_SIZE + size + MALLOC_TAIL_SIZE;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* Round up allocation to a multiple of 32 bits. */
|
||||
if((size & 3) != 0)
|
||||
size += 4 - (size & 3);
|
||||
if (__addition_overflows(sizeof(*mn), size))
|
||||
{
|
||||
SHOWMSG("integer overflow");
|
||||
|
||||
__set_errno(ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
allocation_size = sizeof(*mn) + size;
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
|
||||
/* Integer overflow has occured? */
|
||||
if(size == 0 || allocation_size < size)
|
||||
/* Round up allocation to a multiple of 8 bytes. */
|
||||
if ((allocation_size % MEM_BLOCKSIZE) > 0)
|
||||
{
|
||||
__set_errno(ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
size_t padding;
|
||||
|
||||
/* We reuse the MemoryNode.mn_Size field to mark
|
||||
* allocations are not suitable for use with
|
||||
* free() and realloc(). This limits allocation
|
||||
* sizes to a little less than 2 GBytes.
|
||||
*/
|
||||
if(allocation_size & MN_SIZE_NEVERFREE)
|
||||
{
|
||||
__set_errno(ENOMEM);
|
||||
goto out;
|
||||
padding = MEM_BLOCKSIZE - (allocation_size % MEM_BLOCKSIZE);
|
||||
|
||||
if (__addition_overflows(padding, allocation_size))
|
||||
{
|
||||
SHOWMSG("integer overflow");
|
||||
|
||||
__set_errno(ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
allocation_size += padding;
|
||||
}
|
||||
|
||||
#if defined(__USE_SLAB_ALLOCATOR)
|
||||
{
|
||||
/* Are we using the slab allocator? */
|
||||
if(__slab_data.sd_InUse)
|
||||
if (__slab_data.sd_InUse)
|
||||
{
|
||||
mn = __slab_allocate(allocation_size);
|
||||
|
||||
SHOWPOINTER(mn);
|
||||
|
||||
assert( (((ULONG)mn) & MEM_BLOCKMASK) == 0 );
|
||||
assert( (((ULONG)&mn[1]) & MEM_BLOCKMASK) == 0 );
|
||||
}
|
||||
/* Are we using the memory pool? */
|
||||
else if (__memory_pool != NULL)
|
||||
{
|
||||
PROFILE_OFF();
|
||||
mn = AllocPooled(__memory_pool, allocation_size);
|
||||
PROFILE_ON();
|
||||
}
|
||||
/* Then we'll have to do it the hard way... */
|
||||
else
|
||||
{
|
||||
if (__memory_pool != NULL)
|
||||
{
|
||||
PROFILE_OFF();
|
||||
mn = AllocPooled(__memory_pool,allocation_size);
|
||||
PROFILE_ON();
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
PROFILE_OFF();
|
||||
mn = AllocMem(allocation_size,MEMF_ANY);
|
||||
PROFILE_ON();
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct MinNode * mln;
|
||||
|
||||
PROFILE_OFF();
|
||||
mln = AllocMem(sizeof(*mln) + allocation_size,MEMF_ANY);
|
||||
PROFILE_ON();
|
||||
|
||||
if(mln != NULL)
|
||||
{
|
||||
AddTail((struct List *)&__memory_list,(struct Node *)mln);
|
||||
|
||||
mn = (struct MemoryNode *)&mln[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
mn = NULL;
|
||||
}
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
}
|
||||
PROFILE_OFF();
|
||||
mn = AllocMem(allocation_size, MEMF_ANY);
|
||||
PROFILE_ON();
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
if(__memory_pool != NULL)
|
||||
if (__memory_pool != NULL)
|
||||
{
|
||||
PROFILE_OFF();
|
||||
mn = AllocPooled(__memory_pool,allocation_size);
|
||||
mn = AllocPooled(__memory_pool, allocation_size);
|
||||
PROFILE_ON();
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
PROFILE_OFF();
|
||||
mn = AllocMem(allocation_size,MEMF_ANY);
|
||||
PROFILE_ON();
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct MinNode * mln;
|
||||
|
||||
PROFILE_OFF();
|
||||
mln = AllocMem(sizeof(*mln) + allocation_size,MEMF_ANY);
|
||||
PROFILE_ON();
|
||||
|
||||
if(mln != NULL)
|
||||
{
|
||||
AddTail((struct List *)&__memory_list,(struct Node *)mln);
|
||||
|
||||
mn = (struct MemoryNode *)&mln[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
mn = NULL;
|
||||
}
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
PROFILE_OFF();
|
||||
mn = AllocMem(allocation_size, MEMF_ANY);
|
||||
PROFILE_ON();
|
||||
}
|
||||
}
|
||||
#endif /* __USE_SLAB_ALLOCATOR */
|
||||
|
||||
if(mn == NULL)
|
||||
if (mn == NULL)
|
||||
{
|
||||
SHOWMSG("not enough memory");
|
||||
goto out;
|
||||
}
|
||||
|
||||
mn->mn_Size = size;
|
||||
|
||||
if(never_free)
|
||||
SET_FLAG(mn->mn_Size, MN_SIZE_NEVERFREE);
|
||||
mn->mn_AllocationSize = allocation_size;
|
||||
mn->mn_Flags = never_free ? MNF_NEVER_FREE : 0;
|
||||
|
||||
__current_memory_allocated += allocation_size;
|
||||
if(__maximum_memory_allocated < __current_memory_allocated)
|
||||
if (__maximum_memory_allocated < __current_memory_allocated)
|
||||
__maximum_memory_allocated = __current_memory_allocated;
|
||||
|
||||
__current_num_memory_chunks_allocated++;
|
||||
if(__maximum_num_memory_chunks_allocated < __current_num_memory_chunks_allocated)
|
||||
if (__maximum_num_memory_chunks_allocated < __current_num_memory_chunks_allocated)
|
||||
__maximum_num_memory_chunks_allocated = __current_num_memory_chunks_allocated;
|
||||
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
char * head = (char *)(mn + 1);
|
||||
char * body = head + MALLOC_HEAD_SIZE;
|
||||
char * tail = body + size;
|
||||
|
||||
AddTail((struct List *)&__memory_list,(struct Node *)mn);
|
||||
BYTE * head = (BYTE *)&mn[1];
|
||||
BYTE * body = &head[MALLOC_HEAD_SIZE];
|
||||
BYTE * tail = &body[size];
|
||||
|
||||
mn->mn_AlreadyFree = FALSE;
|
||||
mn->mn_Allocation = body;
|
||||
mn->mn_AllocationSize = allocation_size;
|
||||
mn->mn_OriginalSize = size;
|
||||
mn->mn_File = (char *)debug_file_name;
|
||||
mn->mn_Line = debug_line_number;
|
||||
mn->mn_FreeFile = NULL;
|
||||
mn->mn_FreeLine = 0;
|
||||
|
||||
memset(head,MALLOC_HEAD_FILL,MALLOC_HEAD_SIZE);
|
||||
memset(body,MALLOC_NEW_FILL,size);
|
||||
memset(tail,MALLOC_TAIL_FILL,MALLOC_TAIL_SIZE);
|
||||
memset(head, MALLOC_HEAD_FILL, MALLOC_HEAD_SIZE);
|
||||
memset(body, MALLOC_NEW_FILL, size);
|
||||
memset(tail, MALLOC_TAIL_FILL, MALLOC_TAIL_SIZE);
|
||||
|
||||
#ifdef __MEM_DEBUG_LOG
|
||||
{
|
||||
kprintf("[%s] + %10ld 0x%08lx [",__program_name,size,body);
|
||||
kprintf("[%s] + %10ld 0x%08lx [", __program_name, size, body);
|
||||
|
||||
kprintf("allocated at %s:%ld]\n",debug_file_name,debug_line_number);
|
||||
kprintf("allocated at %s:%ld]\n", debug_file_name, debug_line_number);
|
||||
}
|
||||
#endif /* __MEM_DEBUG_LOG */
|
||||
|
||||
AddTail((struct List *)&__memory_list,(struct Node *)mn);
|
||||
|
||||
#ifdef __USE_MEM_TREES
|
||||
{
|
||||
__red_black_tree_insert(&__memory_tree,mn);
|
||||
__red_black_tree_insert(&__memory_tree, mn);
|
||||
}
|
||||
#endif /* __USE_MEM_TREES */
|
||||
|
||||
@ -288,6 +280,22 @@ __allocate_memory(size_t size,BOOL never_free,const char *debug_file_name UNUSED
|
||||
}
|
||||
#else
|
||||
{
|
||||
#if defined(__USE_SLAB_ALLOCATOR)
|
||||
{
|
||||
/* If we are using neither the slab allocator nor
|
||||
* the memory pool, then the allocation will have
|
||||
* to be freed later, the hard way.
|
||||
*/
|
||||
if (__slab_data.sd_InUse == FALSE && __memory_pool == NULL)
|
||||
AddTail((struct List *)&__memory_list, (struct Node *)mn);
|
||||
}
|
||||
#else
|
||||
{
|
||||
if (__memory_pool == NULL)
|
||||
AddTail((struct List *)&__memory_list, (struct Node *)mn);
|
||||
}
|
||||
#endif /* __USE_SLAB_ALLOCATOR */
|
||||
|
||||
result = &mn[1];
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
@ -295,57 +303,57 @@ __allocate_memory(size_t size,BOOL never_free,const char *debug_file_name UNUSED
|
||||
#if defined(UNIX_PATH_SEMANTICS)
|
||||
{
|
||||
/* Set the zero length allocation contents to NULL. */
|
||||
if(original_size == 0)
|
||||
*(char **)result = NULL;
|
||||
if (original_size == 0 && size >= sizeof(BYTE *))
|
||||
*(BYTE **)result = NULL;
|
||||
}
|
||||
#endif /* UNIX_PATH_SEMANTICS */
|
||||
|
||||
assert( (((ULONG)result) & 3) == 0 );
|
||||
assert( (((ULONG)result) & MEM_BLOCKMASK) == 0 );
|
||||
|
||||
out:
|
||||
|
||||
#ifdef __MEM_DEBUG_LOG
|
||||
{
|
||||
if(result == NULL)
|
||||
if (result == NULL)
|
||||
{
|
||||
kprintf("[%s] + %10ld 0x%08lx [",__program_name,size,NULL);
|
||||
kprintf("[%s] + %10ld 0x%08lx [", __program_name, size, NULL);
|
||||
|
||||
kprintf("FAILED: allocated at %s:%ld]\n",debug_file_name,debug_line_number);
|
||||
kprintf("FAILED: allocated at %s:%ld]\n", debug_file_name, debug_line_number);
|
||||
}
|
||||
}
|
||||
#endif /* __MEM_DEBUG_LOG */
|
||||
|
||||
__memory_unlock();
|
||||
|
||||
return(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
__static void *
|
||||
__malloc(size_t size,const char * file,int line)
|
||||
__malloc(size_t size, const char * file, int line)
|
||||
{
|
||||
void * result = NULL;
|
||||
|
||||
__memory_lock();
|
||||
|
||||
/* Try to get rid of now unused memory. */
|
||||
if(__alloca_cleanup != NULL)
|
||||
(*__alloca_cleanup)(file,line);
|
||||
if (__alloca_cleanup != NULL)
|
||||
(*__alloca_cleanup)(file, line);
|
||||
|
||||
__memory_unlock();
|
||||
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
/*if((rand() % 16) == 0)
|
||||
/*if ((rand() % 16) == 0)
|
||||
__check_memory_allocations(file,line);*/
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
|
||||
/* Allocate memory which can be put through realloc() and free(). */
|
||||
result = __allocate_memory(size,FALSE,file,line);
|
||||
result = __allocate_memory(size, FALSE, file, line);
|
||||
|
||||
return(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
@ -355,7 +363,7 @@ malloc(size_t size)
|
||||
{
|
||||
void * result;
|
||||
|
||||
result = __malloc(size,NULL,0);
|
||||
result = __malloc(size, NULL, 0);
|
||||
|
||||
return(result);
|
||||
}
|
||||
@ -375,7 +383,7 @@ __memory_lock(void)
|
||||
{
|
||||
PROFILE_OFF();
|
||||
|
||||
if(memory_semaphore != NULL)
|
||||
if (memory_semaphore != NULL)
|
||||
ObtainSemaphore(memory_semaphore);
|
||||
|
||||
PROFILE_ON();
|
||||
@ -388,7 +396,7 @@ __memory_unlock(void)
|
||||
{
|
||||
PROFILE_OFF();
|
||||
|
||||
if(memory_semaphore != NULL)
|
||||
if (memory_semaphore != NULL)
|
||||
ReleaseSemaphore(memory_semaphore);
|
||||
|
||||
PROFILE_ON();
|
||||
@ -404,30 +412,34 @@ STDLIB_DESTRUCTOR(stdlib_memory_exit)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
/* Make sure that freeing any memory does not also
|
||||
* trigger the alloca cleanup operations. Otherwise,
|
||||
* the data structures used by alloca() to track
|
||||
* the scope in which allocated memory remains
|
||||
* valid and should not be freed just yet may be
|
||||
* freed, corrupting them.
|
||||
*/
|
||||
__alloca_cleanup = NULL;
|
||||
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
kprintf("[%s] %ld bytes still allocated upon exit, maximum of %ld bytes allocated at a time.\n",
|
||||
__program_name,__current_memory_allocated,__maximum_memory_allocated);
|
||||
__program_name, __current_memory_allocated, __maximum_memory_allocated);
|
||||
|
||||
kprintf("[%s] %ld chunks of memory still allocated upon exit, maximum of %ld chunks allocated at a time.\n",
|
||||
__program_name,__current_num_memory_chunks_allocated,__maximum_num_memory_chunks_allocated);
|
||||
__program_name, __current_num_memory_chunks_allocated, __maximum_num_memory_chunks_allocated);
|
||||
|
||||
__check_memory_allocations(__FILE__,__LINE__);
|
||||
__check_memory_allocations(__FILE__, __LINE__);
|
||||
|
||||
/* Make sure that those memory nodes which were
|
||||
* intended not to be freed will get freed this
|
||||
* time around.
|
||||
*/
|
||||
__never_free = FALSE;
|
||||
|
||||
if(__memory_list.mlh_Head != NULL)
|
||||
{
|
||||
while(NOT IsMinListEmpty(&__memory_list))
|
||||
{
|
||||
((struct MemoryNode *)__memory_list.mlh_Head)->mn_AlreadyFree = FALSE;
|
||||
|
||||
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head,__FILE__,__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__USE_MEM_TREES)
|
||||
{
|
||||
/* This must remain empty. */
|
||||
__initialize_red_black_tree(&__memory_tree);
|
||||
}
|
||||
#endif /* __USE_MEM_TREES */
|
||||
@ -439,39 +451,36 @@ STDLIB_DESTRUCTOR(stdlib_memory_exit)
|
||||
/* Is the slab memory allocator enabled? */
|
||||
if (__slab_data.sd_InUse)
|
||||
{
|
||||
/* Just release the memory. */
|
||||
__slab_exit();
|
||||
}
|
||||
else
|
||||
/* Is the memory pool in use? */
|
||||
else if (__memory_pool != NULL)
|
||||
{
|
||||
if (__memory_pool != NULL)
|
||||
/* Just release the memory. */
|
||||
DeletePool(__memory_pool);
|
||||
__memory_pool = NULL;
|
||||
}
|
||||
/* Do we have to release every single allocation? */
|
||||
else if (__memory_list.mlh_Head != NULL)
|
||||
{
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
NewList((struct List *)&__memory_list);
|
||||
|
||||
DeletePool(__memory_pool);
|
||||
__memory_pool = NULL;
|
||||
while (NOT IsMinListEmpty(&__memory_list))
|
||||
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head, __FILE__, __LINE__);
|
||||
}
|
||||
else if (__memory_list.mlh_Head != NULL)
|
||||
#else
|
||||
{
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
while(NOT IsMinListEmpty(&__memory_list))
|
||||
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head,__FILE__,__LINE__);
|
||||
}
|
||||
#else
|
||||
{
|
||||
while(NOT IsMinListEmpty(&__memory_list))
|
||||
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head,NULL,0);
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
while (NOT IsMinListEmpty(&__memory_list))
|
||||
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head, NULL, 0);
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
if (__memory_pool != NULL)
|
||||
{
|
||||
NewList((struct List *)&__memory_list);
|
||||
|
||||
DeletePool(__memory_pool);
|
||||
__memory_pool = NULL;
|
||||
}
|
||||
@ -479,19 +488,22 @@ STDLIB_DESTRUCTOR(stdlib_memory_exit)
|
||||
{
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
while(NOT IsMinListEmpty(&__memory_list))
|
||||
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head,__FILE__,__LINE__);
|
||||
while (NOT IsMinListEmpty(&__memory_list))
|
||||
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head, __FILE__, __LINE__);
|
||||
}
|
||||
#else
|
||||
{
|
||||
while(NOT IsMinListEmpty(&__memory_list))
|
||||
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head,NULL,0);
|
||||
while (NOT IsMinListEmpty(&__memory_list))
|
||||
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head, NULL, 0);
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
}
|
||||
}
|
||||
#endif /* __USE_SLAB_ALLOCATOR */
|
||||
|
||||
/* The list of memory allocations must remain empty. */
|
||||
NewList((struct List *)&__memory_list);
|
||||
|
||||
#if defined(__THREAD_SAFE)
|
||||
{
|
||||
__delete_semaphore(memory_semaphore);
|
||||
@ -510,13 +522,7 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
|
||||
|
||||
ENTER();
|
||||
|
||||
#if defined(__THREAD_SAFE)
|
||||
{
|
||||
memory_semaphore = __create_semaphore();
|
||||
if(memory_semaphore == NULL)
|
||||
goto out;
|
||||
}
|
||||
#endif /* __THREAD_SAFE */
|
||||
NewList((struct List *)&__memory_list);
|
||||
|
||||
#if defined(__USE_MEM_TREES) && defined(__MEM_DEBUG)
|
||||
{
|
||||
@ -524,27 +530,38 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
|
||||
}
|
||||
#endif /* __USE_MEM_TREES && __MEM_DEBUG */
|
||||
|
||||
NewList((struct List *)&__memory_list);
|
||||
#if defined(__THREAD_SAFE)
|
||||
{
|
||||
memory_semaphore = __create_semaphore();
|
||||
if (memory_semaphore == NULL)
|
||||
{
|
||||
SHOWMSG("could not create memory semaphore");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif /* __THREAD_SAFE */
|
||||
|
||||
#if defined(__USE_SLAB_ALLOCATOR)
|
||||
#ifdef __USE_SLAB_ALLOCATOR
|
||||
{
|
||||
/* ZZZ this is just for the purpose of testing */
|
||||
#if DEBUG
|
||||
{
|
||||
TEXT slab_size_var[20];
|
||||
|
||||
if(GetVar("SLAB_SIZE", slab_size_var, sizeof(slab_size_var), 0) > 0)
|
||||
if (GetVar("SLAB_SIZE", slab_size_var, sizeof(slab_size_var), 0) > 0)
|
||||
{
|
||||
LONG value;
|
||||
LONG value = 0;
|
||||
|
||||
if(StrToLong(slab_size_var,&value) > 0 && value > 0)
|
||||
if (StrToLong(slab_size_var, &value) > 0 && value > 0)
|
||||
__slab_max_size = (size_t)value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SHOWVALUE(__slab_max_size);
|
||||
|
||||
/* Enable the slab memory allocator? */
|
||||
if(__slab_max_size > 0)
|
||||
if (__slab_max_size > 0)
|
||||
{
|
||||
__slab_init(__slab_max_size);
|
||||
}
|
||||
@ -552,15 +569,27 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
|
||||
{
|
||||
#if defined(__amigaos4__)
|
||||
{
|
||||
__memory_pool = CreatePool(MEMF_PRIVATE,(ULONG)__default_pool_size,(ULONG)__default_puddle_size);
|
||||
__memory_pool = CreatePool(MEMF_PRIVATE, (ULONG)__default_pool_size, (ULONG)__default_puddle_size);
|
||||
if (__memory_pool == NULL)
|
||||
{
|
||||
SHOWMSG("could not create memory pool");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* There is no support for memory pools in the operating system
|
||||
* prior to Kickstart 3.0 (V39).
|
||||
*/
|
||||
if(((struct Library *)SysBase)->lib_Version >= 39)
|
||||
__memory_pool = CreatePool(MEMF_ANY,(ULONG)__default_pool_size,(ULONG)__default_puddle_size);
|
||||
if (((struct Library *)SysBase)->lib_Version >= 39)
|
||||
{
|
||||
__memory_pool = CreatePool(MEMF_ANY, (ULONG)__default_pool_size, (ULONG)__default_puddle_size);
|
||||
if (__memory_pool == NULL)
|
||||
{
|
||||
SHOWMSG("could not create memory pool");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* __amigaos4__ */
|
||||
}
|
||||
@ -569,19 +598,31 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
|
||||
{
|
||||
#if defined(__amigaos4__)
|
||||
{
|
||||
__memory_pool = CreatePool(MEMF_PRIVATE,(ULONG)__default_pool_size,(ULONG)__default_puddle_size);
|
||||
__memory_pool = CreatePool(MEMF_PRIVATE, (ULONG)__default_pool_size, (ULONG)__default_puddle_size);
|
||||
if (__memory_pool == NULL)
|
||||
{
|
||||
SHOWMSG("could not create memory pool");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* There is no support for memory pools in the operating system
|
||||
* prior to Kickstart 3.0 (V39).
|
||||
*/
|
||||
if(((struct Library *)SysBase)->lib_Version >= 39)
|
||||
__memory_pool = CreatePool(MEMF_ANY,(ULONG)__default_pool_size,(ULONG)__default_puddle_size);
|
||||
if (((struct Library *)SysBase)->lib_Version >= 39)
|
||||
{
|
||||
__memory_pool = CreatePool(MEMF_ANY, (ULONG)__default_pool_size, (ULONG)__default_puddle_size);
|
||||
if (__memory_pool == NULL)
|
||||
{
|
||||
SHOWMSG("could not create memory pool");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* __amigaos4__ */
|
||||
}
|
||||
#endif /* __USE_SLAB_ALLOCATOR) */
|
||||
#endif /* __USE_SLAB_ALLOCATOR */
|
||||
|
||||
success = TRUE;
|
||||
|
||||
@ -590,7 +631,7 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
|
||||
SHOWVALUE(success);
|
||||
LEAVE();
|
||||
|
||||
if(success)
|
||||
if (success)
|
||||
CONSTRUCTOR_SUCCEED();
|
||||
else
|
||||
CONSTRUCTOR_FAIL();
|
||||
|
||||
36
library/stdlib_memory.h
Normal file → Executable file
36
library/stdlib_memory.h
Normal file → Executable file
@ -63,7 +63,7 @@
|
||||
/*
|
||||
* Uncomment this to enable the slab allocator.
|
||||
*/
|
||||
#define __USE_SLAB_ALLOCATOR
|
||||
/*#define __USE_SLAB_ALLOCATOR*/
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
@ -150,27 +150,26 @@ extern char * __getcwd(char * buffer,size_t buffer_size,const char *file,int lin
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* If this flag is set in mn_Size, then this memory allocation
|
||||
/* If this flag is set in mn_Flags, then this memory allocation
|
||||
* cannot be released with free() or used with realloc(). This
|
||||
* flag is set by alloca().
|
||||
*/
|
||||
#define MN_SIZE_NEVERFREE (0x80000000UL)
|
||||
#define MNF_NEVER_FREE (1UL << 0)
|
||||
|
||||
/* This obtains the allocation size from a memory node, ignoring
|
||||
* the "never free" flag altogether.
|
||||
/* Memory allocations are remembered by this tracking data structure.
|
||||
* Its size is always a multiple of 8 bytes, which provides memory
|
||||
* address alignment to a 64-bit boundary.
|
||||
*/
|
||||
#define GET_MN_SIZE(mn) ((mn)->mn_Size & ~MN_SIZE_NEVERFREE)
|
||||
|
||||
struct MemoryNode
|
||||
{
|
||||
#ifdef __MEM_DEBUG
|
||||
struct MinNode mn_MinNode;
|
||||
|
||||
UBYTE mn_AlreadyFree;
|
||||
UBYTE mn_Pad0[3];
|
||||
ULONG mn_AllocationSize;
|
||||
ULONG mn_Flags;
|
||||
|
||||
#ifdef __MEM_DEBUG
|
||||
void * mn_Allocation;
|
||||
size_t mn_AllocationSize;
|
||||
size_t mn_OriginalSize;
|
||||
|
||||
char * mn_FreeFile;
|
||||
int mn_FreeLine;
|
||||
@ -178,17 +177,18 @@ struct MemoryNode
|
||||
char * mn_File;
|
||||
int mn_Line;
|
||||
|
||||
UBYTE mn_AlreadyFree;
|
||||
UBYTE mn_Pad1[7];
|
||||
#ifdef __USE_MEM_TREES
|
||||
struct MemoryNode * mn_Left;
|
||||
struct MemoryNode * mn_Right;
|
||||
struct MemoryNode * mn_Parent;
|
||||
|
||||
UBYTE mn_IsRed;
|
||||
UBYTE mn_Pad1[3];
|
||||
UBYTE mn_Pad2[3];
|
||||
#endif /* __USE_MEM_TREES */
|
||||
|
||||
#endif /* __MEM_DEBUG */
|
||||
|
||||
ULONG mn_Size;
|
||||
};
|
||||
|
||||
#ifdef __USE_MEM_TREES
|
||||
@ -247,12 +247,14 @@ struct SlabNode
|
||||
};
|
||||
|
||||
/* Memory allocations which are not part of a slab are
|
||||
* tracked using this data structure.
|
||||
* tracked using this data structure. This data structure
|
||||
* is supposed to be a multiple of 8 bytes in size.
|
||||
*/
|
||||
struct SlabSingleAllocation
|
||||
{
|
||||
struct MinNode ssa_MinNode;
|
||||
ULONG ssa_Size;
|
||||
ULONG ssa_Pad;
|
||||
};
|
||||
|
||||
/* This is the global bookkeeping information for managing
|
||||
@ -264,8 +266,8 @@ struct SlabData
|
||||
* which are a power of 2 bytes in size, e.g. 8, 16, 32,
|
||||
* 64, 128 bytes. Hence, sd_Slabs[3] keeps track of the slabs
|
||||
* which are 8 bytes in size, sd_Slabs[4] is for 16 byte
|
||||
* chunks, etc. The minimum chunk size is 8, which is why
|
||||
* lists 0..2 are not used. Currently, there is an upper limit
|
||||
* chunks, etc. The minimum chunk size is 16, which is why
|
||||
* lists 0..3 are not used. Currently, there is an upper limit
|
||||
* of 2^17 bytes per chunk, but you should not be using slab
|
||||
* chunks much larger than 4096 bytes.
|
||||
*/
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
/****************************************************************************/
|
||||
|
||||
__static void *
|
||||
__realloc(void *ptr,size_t size,const char * file,int line)
|
||||
__realloc(void *ptr, size_t size, const char * file, int line)
|
||||
{
|
||||
void * result = NULL;
|
||||
BOOL locked = FALSE;
|
||||
@ -62,23 +62,24 @@ __realloc(void *ptr,size_t size,const char * file,int line)
|
||||
|
||||
assert( (int)size >= 0 );
|
||||
|
||||
if(ptr == NULL)
|
||||
if (ptr == NULL)
|
||||
{
|
||||
D(("calling malloc(%ld)",size));
|
||||
D(("calling malloc(%ld)", size));
|
||||
|
||||
result = __malloc(size,file,line);
|
||||
result = __malloc(size, file, line);
|
||||
}
|
||||
#ifndef UNIX_PATH_SEMANTICS
|
||||
else if (size == 0)
|
||||
{
|
||||
D(("calling free(0x%08lx)",ptr));
|
||||
|
||||
__free(ptr,file,line);
|
||||
__free(ptr, file, line);
|
||||
}
|
||||
#endif /* UNIX_PATH_SEMANTICS */
|
||||
else
|
||||
{
|
||||
size_t old_size;
|
||||
size_t new_allocation_size UNUSED;
|
||||
size_t old_allocation_size;
|
||||
struct MemoryNode * mn;
|
||||
BOOL reallocate;
|
||||
|
||||
@ -92,16 +93,14 @@ __realloc(void *ptr,size_t size,const char * file,int line)
|
||||
|
||||
#ifdef __MEM_DEBUG
|
||||
{
|
||||
/* If we managed to find the memory allocation,
|
||||
reallocate it. */
|
||||
if(mn == NULL)
|
||||
/* Quit if we failed to find the memory allocation. */
|
||||
if (mn == NULL)
|
||||
{
|
||||
SHOWMSG("allocation not found");
|
||||
|
||||
kprintf("[%s] %s:%ld:Address for realloc(0x%08lx,%ld) not known.\n",__program_name,file,line,ptr,size);
|
||||
kprintf("[%s] %s:%ld:Address for realloc(0x%08lx,%ld) not known.\n",
|
||||
__program_name, file, line, ptr, size);
|
||||
|
||||
/* Apparently, the address did not qualify for
|
||||
reallocation. */
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -111,70 +110,114 @@ __realloc(void *ptr,size_t size,const char * file,int line)
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
|
||||
if(mn == NULL || FLAG_IS_SET(mn->mn_Size, MN_SIZE_NEVERFREE))
|
||||
if (mn == NULL || FLAG_IS_SET(mn->mn_Flags, MNF_NEVER_FREE))
|
||||
{
|
||||
SHOWMSG("cannot free this chunk");
|
||||
goto out;
|
||||
}
|
||||
|
||||
old_size = GET_MN_SIZE(mn);
|
||||
assert( FLAG_IS_CLEAR(mn->mn_Flags, MNF_NEVER_FREE) );
|
||||
|
||||
/* Don't do anything unless the size of the allocation
|
||||
has really changed. */
|
||||
old_allocation_size = mn->mn_AllocationSize;
|
||||
|
||||
/* If the memory debug option is enabled, just check if
|
||||
* requested allocation size has changed.
|
||||
*/
|
||||
#if defined(__MEM_DEBUG)
|
||||
{
|
||||
reallocate = (old_size != size);
|
||||
reallocate = (mn->mn_OriginalSize != size);
|
||||
|
||||
new_allocation_size = (sizeof(*mn) + MALLOC_HEAD_SIZE + size + MALLOC_TAIL_SIZE + MEM_BLOCKMASK) & ~MEM_BLOCKMASK;
|
||||
}
|
||||
#else
|
||||
{
|
||||
if(size > old_size)
|
||||
/* The actual size of the allocation is affected by the
|
||||
granularity and minimum allocation size used by the
|
||||
operating system. */
|
||||
new_allocation_size = (sizeof(*mn) + size + MEM_BLOCKMASK) & ~MEM_BLOCKMASK;
|
||||
|
||||
if (new_allocation_size > old_allocation_size)
|
||||
{
|
||||
/* Allocation size should grow. */
|
||||
reallocate = TRUE;
|
||||
}
|
||||
else
|
||||
else if (new_allocation_size < old_allocation_size)
|
||||
{
|
||||
/* Optimization: If the block size shrinks by less than half the
|
||||
original allocation size, do not reallocate the
|
||||
block and do not copy over the contents of the old
|
||||
allocation. We also take into account that the
|
||||
actual size of the allocation is affected by a
|
||||
certain operating system imposed granularity. */
|
||||
reallocate = (size < old_size && size <= old_size / 2);
|
||||
block. */
|
||||
reallocate = (size <= old_allocation_size / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
reallocate = FALSE;
|
||||
}
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
|
||||
if(reallocate)
|
||||
if (reallocate)
|
||||
{
|
||||
void * new_ptr;
|
||||
|
||||
D(("realloc() size has changed; old=%ld, new=%ld",old_size,size));
|
||||
D(("realloc() allocation size has changed; old=%ld, new=%ld", old_allocation_size, new_allocation_size));
|
||||
|
||||
/* We allocate the new memory chunk before we
|
||||
attempt to replace the old. */
|
||||
new_ptr = __malloc(size,file,line);
|
||||
if(new_ptr == NULL)
|
||||
attempt to replace the old one. */
|
||||
new_ptr = __malloc(size, file, line);
|
||||
if (new_ptr == NULL)
|
||||
{
|
||||
SHOWMSG("could not reallocate memory");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Copy the contents of the old allocation to the new buffer. */
|
||||
if(size > old_size)
|
||||
size = old_size;
|
||||
/* With memory debugging enabled, the size of the allocation made
|
||||
* will use the requested and not the rounded size of the
|
||||
* allocation, which can be shorter. We need to deal with this.
|
||||
*/
|
||||
#if defined(__MEM_DEBUG)
|
||||
{
|
||||
struct MemoryNode * new_mn;
|
||||
|
||||
memmove(new_ptr,ptr,size);
|
||||
new_mn = __find_memory_node(new_ptr);
|
||||
if (new_mn == NULL)
|
||||
{
|
||||
free(new_ptr);
|
||||
|
||||
SHOWMSG("Could not find memory node for new allocation");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Make sure that if the new allocation size is smaller than
|
||||
* the old allocation, we only copy as much data as will fit
|
||||
* into the new allocation.
|
||||
*/
|
||||
if (size > new_mn->mn_OriginalSize)
|
||||
size = new_mn->mn_OriginalSize;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* We assume that the total size of the allocation will
|
||||
* include padding. The requested size does not include
|
||||
* the memory node, of course, which is why it is added
|
||||
* here.
|
||||
*/
|
||||
if (size + sizeof(*mn) > old_allocation_size)
|
||||
size = old_allocation_size - sizeof(*mn);
|
||||
}
|
||||
#endif /* __MEM_DEBUG */
|
||||
|
||||
memmove(new_ptr, ptr, size);
|
||||
|
||||
/* Free the old allocation. Since we already know which memory
|
||||
node is associated with it, we don't call __free() here. */
|
||||
__free_memory_node(mn,file,line);
|
||||
__free_memory_node(mn, file, line);
|
||||
|
||||
result = new_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
D(("size didn't actually change that much (%ld -> %ld); returning memory block as is.",old_size,size));
|
||||
D(("size didn't actually change that much (%ld -> %ld); returning memory block as is.",
|
||||
old_allocation_size, new_allocation_size));
|
||||
|
||||
/* No change in size. */
|
||||
result = ptr;
|
||||
@ -183,14 +226,14 @@ __realloc(void *ptr,size_t size,const char * file,int line)
|
||||
|
||||
out:
|
||||
|
||||
if(locked)
|
||||
if (locked)
|
||||
__memory_unlock();
|
||||
|
||||
if(result == NULL)
|
||||
if (result == NULL)
|
||||
SHOWMSG("ouch! realloc failed");
|
||||
|
||||
RETURN(result);
|
||||
return(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
1144
library/stdlib_slab.c
Normal file → Executable file
1144
library/stdlib_slab.c
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
@ -41,7 +41,7 @@
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static const char * error_table[EILSEQ - EPERM + 1] =
|
||||
static const char * error_table[ENOTSUP - EPERM + 1] =
|
||||
{
|
||||
"Operation not permitted",
|
||||
"No such file or directory",
|
||||
@ -127,7 +127,8 @@ static const char * error_table[EILSEQ - EPERM + 1] =
|
||||
"Identifier removed",
|
||||
"No message of the desired type.",
|
||||
"Value too large to be stored in data type.",
|
||||
"Encoding error detected"
|
||||
"Encoding error detected",
|
||||
"Not supported"
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
@ -139,7 +140,7 @@ strerror_r(int number,char * buffer,size_t buffer_size)
|
||||
const char * str;
|
||||
size_t len;
|
||||
|
||||
if(number < EPERM || number > EILSEQ)
|
||||
if(number < EPERM || number > ENOTSUP)
|
||||
{
|
||||
__set_errno(EINVAL);
|
||||
goto out;
|
||||
|
||||
151
library/time_mktime.c
Normal file → Executable file
151
library/time_mktime.c
Normal file → Executable file
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: time_mktime.c,v 1.10 2006-01-08 12:04:27 obarthel Exp $
|
||||
* $Id: time_mktime.c,v 1.11 2015-06-26 11:22:00 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
@ -52,9 +52,10 @@ mktime(struct tm *tm)
|
||||
{
|
||||
DECLARE_UTILITYBASE();
|
||||
struct ClockData clock_data;
|
||||
ULONG seconds, delta;
|
||||
ULONG seconds;
|
||||
time_t result = (time_t)-1;
|
||||
int max_month_days;
|
||||
LONG combined_seconds;
|
||||
int month, year;
|
||||
|
||||
ENTER();
|
||||
|
||||
@ -73,116 +74,63 @@ mktime(struct tm *tm)
|
||||
}
|
||||
#endif /* CHECK_FOR_NULL_POINTERS */
|
||||
|
||||
/* The month must be valid. */
|
||||
if(tm->tm_mon < 0 || tm->tm_mon > 11)
|
||||
/* Normalize the year and month. */
|
||||
year = tm->tm_year + 1900;
|
||||
month = tm->tm_mon + 1;
|
||||
|
||||
if(month < 0 || month > 12)
|
||||
{
|
||||
SHOWVALUE(tm->tm_mon);
|
||||
SHOWMSG("invalid month");
|
||||
goto out;
|
||||
int y;
|
||||
|
||||
y = month / 12;
|
||||
|
||||
month -= y * 12;
|
||||
year += y;
|
||||
}
|
||||
|
||||
/* The day of the month must be valid. */
|
||||
if(tm->tm_mday < 1 || tm->tm_mday > 31)
|
||||
if(month < 1)
|
||||
{
|
||||
SHOWVALUE(tm->tm_mday);
|
||||
SHOWMSG("invalid day of month");
|
||||
goto out;
|
||||
month += 12;
|
||||
year -= 1;
|
||||
}
|
||||
|
||||
/* The year must be valid. */
|
||||
if(tm->tm_year < 78)
|
||||
/* The year must be valid. Amiga time begins with January 1st, 1978. */
|
||||
if(year < 1978)
|
||||
{
|
||||
SHOWVALUE(tm->tm_year);
|
||||
SHOWVALUE(year);
|
||||
SHOWMSG("invalid year");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Is this the month of February? */
|
||||
if(tm->tm_mon == 1)
|
||||
{
|
||||
int year;
|
||||
|
||||
/* We need to have the full year number for the
|
||||
leap year calculation below. */
|
||||
year = tm->tm_year + 1900;
|
||||
|
||||
/* Now for the famous leap year calculation rules... In
|
||||
the given year, how many days are there in the month
|
||||
of February? */
|
||||
if((year % 4) != 0)
|
||||
max_month_days = 28;
|
||||
else if ((year % 400) == 0)
|
||||
max_month_days = 29;
|
||||
else if ((year % 100) == 0)
|
||||
max_month_days = 28;
|
||||
else
|
||||
max_month_days = 29;
|
||||
}
|
||||
else
|
||||
{
|
||||
static const char days_per_month[12] =
|
||||
{
|
||||
31, 0,31,
|
||||
30,31,30,
|
||||
31,31,30,
|
||||
31,30,31
|
||||
};
|
||||
|
||||
max_month_days = days_per_month[tm->tm_mon];
|
||||
}
|
||||
|
||||
/* The day of the month must be valid. */
|
||||
if(tm->tm_mday < 0 || tm->tm_mday > max_month_days)
|
||||
{
|
||||
SHOWVALUE(tm->tm_mday);
|
||||
SHOWMSG("invalid day of month");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The hour must be valid. */
|
||||
if(tm->tm_hour < 0 || tm->tm_hour > 23)
|
||||
{
|
||||
SHOWVALUE(tm->tm_hour);
|
||||
SHOWMSG("invalid hour");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The minute must be valid. */
|
||||
if(tm->tm_min < 0 || tm->tm_min > 59)
|
||||
{
|
||||
SHOWVALUE(tm->tm_min);
|
||||
SHOWMSG("invalid minute");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Note: the number of seconds can be larger than 59
|
||||
in order to account for leap seconds. */
|
||||
if(tm->tm_sec < 0 || tm->tm_sec > 60)
|
||||
{
|
||||
SHOWVALUE(tm->tm_sec);
|
||||
SHOWMSG("invalid seconds");
|
||||
goto out;
|
||||
}
|
||||
|
||||
clock_data.sec = (tm->tm_sec > 59) ? 59 : tm->tm_sec;
|
||||
clock_data.min = tm->tm_min;
|
||||
clock_data.hour = tm->tm_hour;
|
||||
clock_data.mday = tm->tm_mday;
|
||||
clock_data.month = tm->tm_mon + 1;
|
||||
clock_data.year = tm->tm_year + 1900;
|
||||
|
||||
seconds = Date2Amiga(&clock_data) + (tm->tm_sec - 59);
|
||||
|
||||
/* The AmigaOS "epoch" starts with January 1st, 1978, which was
|
||||
a Sunday. */
|
||||
tm->tm_wday = (seconds / (24 * 60 * 60)) % 7;
|
||||
|
||||
/* Convert the first day of the month in the given year
|
||||
into the corresponding number of seconds. */
|
||||
memset(&clock_data, 0, sizeof(clock_data));
|
||||
|
||||
clock_data.mday = 1;
|
||||
clock_data.month = 1;
|
||||
clock_data.month = month;
|
||||
clock_data.year = year;
|
||||
|
||||
delta = Date2Amiga(&clock_data);
|
||||
seconds = Date2Amiga(&clock_data);
|
||||
|
||||
tm->tm_yday = (seconds - delta) / (24 * 60 * 60);
|
||||
/* Put the combined number of seconds involved together,
|
||||
covering the seconds/minutes/hours of the day as well
|
||||
as the number of days of the month. This will be added
|
||||
to the number of seconds for the date. */
|
||||
combined_seconds = tm->tm_sec + 60 * (tm->tm_min + 60 * (tm->tm_hour + 24 * (tm->tm_mday-1)));
|
||||
|
||||
/* If the combined number of seconds is negative, adding it
|
||||
* to the number of seconds for the date should not produce
|
||||
* a negative value.
|
||||
*/
|
||||
if(combined_seconds < 0 && seconds < (ULONG)(-combined_seconds))
|
||||
{
|
||||
SHOWVALUE(seconds);
|
||||
SHOWVALUE(combined_seconds);
|
||||
SHOWMSG("invalid combined number of seconds");
|
||||
goto out;
|
||||
}
|
||||
|
||||
seconds += combined_seconds;
|
||||
|
||||
__locale_lock();
|
||||
|
||||
@ -193,10 +141,13 @@ mktime(struct tm *tm)
|
||||
|
||||
__locale_unlock();
|
||||
|
||||
/* Finally, adjust for the difference between the Unix and the
|
||||
/* Adjust for the difference between the Unix and the
|
||||
AmigaOS epochs, which differ by 8 years. */
|
||||
result = seconds + UNIX_TIME_OFFSET;
|
||||
|
||||
/* Finally, normalize the provided time and date information. */
|
||||
localtime_r(&result, tm);
|
||||
|
||||
out:
|
||||
|
||||
RETURN(result);
|
||||
|
||||
@ -58,9 +58,19 @@ readlink(const char * path_name, char * buffer, int buffer_size)
|
||||
struct name_translation_info path_name_nti;
|
||||
struct name_translation_info buffer_nti;
|
||||
#endif /* UNIX_PATH_SEMANTICS */
|
||||
|
||||
D_S(struct bcpl_name, bname);
|
||||
const size_t name_size = sizeof(bname->name);
|
||||
|
||||
BPTR lock = ZERO;
|
||||
int result = ERROR;
|
||||
int target_length = -1;
|
||||
struct DevProc *dvp = NULL;
|
||||
char * new_name = NULL;
|
||||
char * path_part;
|
||||
size_t name_len;
|
||||
size_t new_name_size;
|
||||
LONG readlink_result;
|
||||
LONG error;
|
||||
|
||||
ENTER();
|
||||
|
||||
@ -70,12 +80,12 @@ readlink(const char * path_name, char * buffer, int buffer_size)
|
||||
|
||||
assert( path_name != NULL && buffer != NULL );
|
||||
|
||||
if(__check_abort_enabled)
|
||||
if (__check_abort_enabled)
|
||||
__check_abort();
|
||||
|
||||
#if defined(CHECK_FOR_NULL_POINTERS)
|
||||
{
|
||||
if(path_name == NULL || buffer == NULL)
|
||||
if (path_name == NULL || buffer == NULL)
|
||||
{
|
||||
SHOWSTRING("invalid parameters");
|
||||
|
||||
@ -87,9 +97,9 @@ readlink(const char * path_name, char * buffer, int buffer_size)
|
||||
|
||||
#if defined(UNIX_PATH_SEMANTICS)
|
||||
{
|
||||
if(__unix_path_semantics)
|
||||
if (__unix_path_semantics)
|
||||
{
|
||||
if(path_name[0] == '\0')
|
||||
if (path_name[0] == '\0')
|
||||
{
|
||||
SHOWMSG("no name given");
|
||||
|
||||
@ -97,53 +107,144 @@ readlink(const char * path_name, char * buffer, int buffer_size)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(__translate_unix_to_amiga_path_name(&path_name,&path_name_nti) != 0)
|
||||
if (__translate_unix_to_amiga_path_name(&path_name, &path_name_nti) != 0)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif /* UNIX_PATH_SEMANTICS */
|
||||
|
||||
D(("trying to get a lock on '%s'",path_name));
|
||||
name_len = strlen(path_name);
|
||||
if (name_len >= name_size)
|
||||
{
|
||||
SHOWMSG("name too long");
|
||||
|
||||
SetIoErr(ERROR_LINE_TOO_LONG);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Convert the name into a BCPL string. */
|
||||
bname->name[0] = name_len;
|
||||
memcpy(&bname->name[1], path_name, name_len);
|
||||
|
||||
/* Get a handle on the device, volume or assignment name in the path. */
|
||||
dvp = GetDeviceProc((STRPTR)path_name, dvp);
|
||||
if (dvp == NULL)
|
||||
{
|
||||
SHOWMSG("dvp == NULL");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try to obtain a lock on the object. It should be a link and not
|
||||
* a file or directory.
|
||||
*/
|
||||
lock = DoPkt(dvp->dvp_Port, ACTION_LOCATE_OBJECT, dvp->dvp_Lock, MKBADDR(bname), SHARED_LOCK, 0, 0);
|
||||
if (lock != ZERO)
|
||||
{
|
||||
SHOWMSG("lock != ZERO");
|
||||
|
||||
SetIoErr(ERROR_OBJECT_WRONG_TYPE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = IoErr();
|
||||
|
||||
if (error != ERROR_IS_SOFT_LINK)
|
||||
{
|
||||
SHOWMSG("not a soft link");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We only need the leading path name. */
|
||||
name_len = ((BYTE *)PathPart(path_name)) - (BYTE *)path_name;
|
||||
|
||||
path_part = malloc(name_len+1);
|
||||
if (path_part == NULL)
|
||||
{
|
||||
SHOWMSG("path_part too long");
|
||||
|
||||
SetIoErr(ERROR_NO_FREE_STORE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(path_part, path_name, name_len);
|
||||
path_part[name_len] = '\0';
|
||||
|
||||
PROFILE_OFF();
|
||||
lock = __lock((STRPTR)path_name,SHARED_LOCK,&target_length,buffer,(size_t)buffer_size);
|
||||
lock = Lock((STRPTR)path_part, SHARED_LOCK);
|
||||
PROFILE_ON();
|
||||
|
||||
if(lock != ZERO)
|
||||
new_name_size = name_size+1;
|
||||
|
||||
/* Provide as much buffer space as possible. */
|
||||
if (buffer_size > 0 && (size_t)buffer_size > new_name_size)
|
||||
new_name_size = buffer_size;
|
||||
|
||||
/* For soft link resolution we need a temporary buffer to
|
||||
let the file system store the resolved path name in. */
|
||||
new_name = malloc(new_name_size);
|
||||
if (new_name == NULL)
|
||||
{
|
||||
__set_errno(EINVAL);
|
||||
goto out;
|
||||
}
|
||||
else if (target_length <= 0) /* No a soft-link. */
|
||||
{
|
||||
__set_errno(__translate_io_error_to_errno(IoErr()));
|
||||
SHOWMSG("new_name too long");
|
||||
|
||||
SetIoErr(ERROR_NO_FREE_STORE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if defined(UNIX_PATH_SEMANTICS)
|
||||
/* Now ask the file system to resolve the entire path. */
|
||||
readlink_result = ReadLink(dvp->dvp_Port, lock, (STRPTR)FilePart(path_name), (STRPTR)new_name, (LONG)new_name_size);
|
||||
if (readlink_result < 0)
|
||||
{
|
||||
if(__unix_path_semantics)
|
||||
SHOWMSG("ReadLink");
|
||||
|
||||
/* This will return either -1 (resolution error) or -2
|
||||
(buffer too small). We regard both as trouble. */
|
||||
SetIoErr(ERROR_INVALID_COMPONENT_NAME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
assert( result > 0 );
|
||||
|
||||
/* If the caller supplied a buffer, copy as much of the name
|
||||
as possible into it. */
|
||||
if (buffer != NULL && buffer_size > 0)
|
||||
{
|
||||
strlcpy(buffer, new_name, buffer_size);
|
||||
|
||||
#if defined(UNIX_PATH_SEMANTICS)
|
||||
{
|
||||
if(__translate_amiga_to_unix_path_name((char const **)&buffer,&buffer_nti) != 0)
|
||||
goto out;
|
||||
if (__unix_path_semantics)
|
||||
{
|
||||
if (__translate_amiga_to_unix_path_name((char const **)&buffer, &buffer_nti) != 0)
|
||||
goto out;
|
||||
|
||||
__restore_path_name((char const **)&buffer,&buffer_nti);
|
||||
__restore_path_name((char const **)&buffer,&buffer_nti);
|
||||
|
||||
strcpy(buffer,buffer_nti.substitute);
|
||||
strcpy(buffer, buffer_nti.substitute);
|
||||
}
|
||||
}
|
||||
#endif /* UNIX_PATH_SEMANTICS */
|
||||
|
||||
result = strlen(buffer);
|
||||
|
||||
SHOWSTRING(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
#endif /* UNIX_PATH_SEMANTICS */
|
||||
|
||||
result = strlen(buffer);
|
||||
|
||||
SHOWSTRING(buffer);
|
||||
|
||||
out:
|
||||
|
||||
PROFILE_OFF();
|
||||
|
||||
if (dvp != NULL)
|
||||
FreeDeviceProc(dvp);
|
||||
|
||||
UnLock(lock);
|
||||
|
||||
PROFILE_ON();
|
||||
|
||||
RETURN(result);
|
||||
return(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void
|
||||
int
|
||||
usleep(unsigned long microseconds)
|
||||
{
|
||||
ENTER();
|
||||
@ -51,4 +51,6 @@ usleep(unsigned long microseconds)
|
||||
__time_delay(0,microseconds);
|
||||
|
||||
LEAVE();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -334,7 +334,7 @@ __wildcard_expand_init(void)
|
||||
}
|
||||
else if (rc != OK)
|
||||
{
|
||||
/* Some error occured. */
|
||||
/* Some error occurred. */
|
||||
error = EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user