1
0
mirror of https://github.com/adtools/clib2.git synced 2025-12-08 14:59:05 +00:00

65 Commits

Author SHA1 Message Date
14c53db07c Typo corrections and small changes to the implementation documentation 2025-07-17 11:49:12 +02:00
c56fb088ef The new "d_type" field of the "struct dirent" is available if the _DIRENT_HAVE_D_TYPE macro is defined 2025-07-17 11:48:29 +02:00
fe363c5403 The dependencies for the stdio_long_path.c file need more work 2025-07-14 13:47:52 +02:00
9f1a9ae92e The readlink() function now uses DOS packet I/O instead of trusting Lock() to do the right thing 2025-07-14 13:33:02 +02:00
d9b2c0f38f Added the header file dependency for the long path handling which makes it build on demand 2025-07-14 13:31:14 +02:00
165232d694 The long path handling code works much better if you actually check it in 2025-07-14 13:29:06 +02:00
7a4966e670 Documentation now covers the most recent handful of changes
Details matter: the locale_t definition uses an incomplete structure definition, not an anonymous structure.
2025-07-13 10:59:51 +02:00
dfa3c412d9 The definition of 'locale_t' is now based upon a pointer to an "anonymous" structure
Thanks go to sacredbanana for the fix.
2025-07-12 12:30:59 +02:00
f49877920e sqrt() now returns NAN, rather than 0, if the argument is < 0
Thanks go to sodero for providing the fix.
2025-07-12 12:26:34 +02:00
2704ff880f Implemented the fixes for the tgamma() and tgammaf() functions
Thanks go to sodero for providing the fixes.
2025-07-12 12:25:42 +02:00
8378274572 Added support for the "d_type" member of the "dirent" structure.
If the '_DIRENT_HAVE_D_TYPE' macro is defined after "#include <dirent.h>" has been used successfully, it means that the "dirent" structure features the "d_type" member. The directory entry type values were adapted from 4.4BSD-Lite2, but not all of these are used.
2025-07-12 12:17:12 +02:00
0e637e9663 usleep() now correctly returns 0, with the function prototype to match
This addresses issue #4  by· capehill opened on Jul 30, 2017
2025-07-12 10:15:59 +02:00
a1f85a9c7f First preparations for a future clib2 release 2025-07-10 14:21:30 +02:00
76d5b5874e Very minor documentation cleanup 2025-07-10 13:17:30 +02:00
51f88950eb Copied the changes which introduced ENOTSUP to <errno.h> and the related strerror_r() function. 2025-07-08 14:27:43 +02:00
f9d1222bd7 Fix for wrong address alignment rounding
The original V39/V40 amiga.lib puddle creation code did not round up, it rounded off.
2023-10-11 09:15:28 +02:00
d37909e409 Documented why bypassing the read/write buffer is only attempted if there is at least a full buffer worth of data to be read or written. 2023-09-14 09:52:21 +02:00
f5631d8bda The inner loop of the read operation already fills the buffer as far as it can go. No need to drop into "continue;". 2023-09-11 17:36:32 +02:00
9a9ae7d6fd The SIGABRT handler can no longer invoke abort() recursively. 2023-09-10 11:45:38 +02:00
64ab8643b5 The size of a slab entry is now calculated inside a single local function, rather than calculated differently in different parts of stdlib_slab.c. This change made it easier to simplify the implementation. 2023-09-10 11:17:00 +02:00
f7fd63acb4 Indentation fix. 2023-09-10 11:15:25 +02:00
8bd7403ae3 The 'struct MemoryNode' is now so larger than the smallest usable allocation size is now 16 bytes. 2023-09-10 11:15:15 +02:00
5efacb1a2b Fix for unnecessary compiler warnings. 2023-09-10 11:14:32 +02:00
7207c96a6f Made the slab allocator work with libunix.a again. 2023-09-09 18:36:09 +02:00
21bc996705 Fixed to make use of the aligned slab address when verifying that the slab node is present in the slab. The requested slab size is now properly rounded up to a power of 2 and will always be limited to a valid range. 2023-09-09 18:35:31 +02:00
438fd5bbd2 Fixed the MemoryNode padding, which was short of a 32 bit word. 2023-09-09 18:33:38 +02:00
101846e423 If the memory management system constructor fails, it now prints some diagnostic debug output. 2023-09-09 18:32:50 +02:00
fba7f7da9b Small type corrections. 2023-09-09 12:55:13 +02:00
115099698a Added an assertion to verify that the first free chunk on the slab's list is really always available. 2023-09-09 12:55:01 +02:00
4f3d0c981c If the memory debug option is active, realloc() can no longer damage the trailing guard area. 2023-09-09 12:54:16 +02:00
96af3a1165 Reformatted, so that it might be a bit more readable. 2023-09-08 17:20:51 +02:00
6cabff4bbb Added the missing semicolon; 2023-09-08 17:20:16 +02:00
71708e84ce The memory debugging code now places the the damage detection areas which bracket the allocation area so that the originally requested allocation size is used, not the rounded-up size. Debug output in memory node checking no longer counts the non-breaking space character as unprintable. 2023-09-08 16:11:01 +02:00
3d70b18c23 Simplified how the slab allocator, the memory pool or AllocMem() are being used. Added robust integer overflow detection. Allocation sizes are now padded to a multiple of MEM_BLOCKSIZE. Repaired how the plain AllocMem() allocations are managed. alloca() cleanup is disabled during the stdlib_memory_exit destructor's work. The stdlib_memory_init constructor is more robust and now insists that exec.library V39+ uses memory pools. 2023-09-08 16:08:45 +02:00
842acf2eaa Updated to properly use the MemoryNode changes. 2023-09-08 16:05:29 +02:00
b9463f442b Added notes on calloc() being safe to use under certain circumstances if the number of elements or the element size happens to be 0. In effect, malloc() will decide what is going to happen. 2023-09-08 16:04:53 +02:00
7ceeb4f8ed If alloca() would end up returning a NULL pointer, it now invokes the default alloca trap function, which calls abort(). You can override this, if you need to. 2023-09-08 16:03:47 +02:00
fc8051f724 Modifed the MemoryNode so that its size will always be a multiple of MEM_BLOCKSIZE. The SlabSingleAllocation is now always a multiple of MEM_BLOCKSIZE, too. Tracking whether a MemoryNode is not supposed to be freed is now accomplished through dedicated flag instead of repurposing the allocation size field. 2023-09-08 16:02:37 +02:00
64ba9b5389 Added the __addition_overflows() function prototype, which is used by code which checks for unsigned 32 bit integer sums exceeding the range of an unsigned 32 bit integer. 2023-09-08 16:00:38 +02:00
1286b86f07 Added more robust integer overflow detection. Slab memory is now allocated so that its address starts on a MEM_BLOCKSIZE boundary. 2023-09-08 15:59:19 +02:00
8101b43fc5 Tpyo correction 2023-09-08 15:52:06 +02:00
efaffd9182 Typo corection 2023-09-08 15:51:13 +02:00
6197531c90 Added integer overflow checking for the element_size and count parameters. 2023-09-06 13:28:48 +02:00
b94937ff38 Fixed the bug which broke both fgets() and gets(): copying data from the FILE buffer failed to bump the destination string pointer. Also added an abort check in order to avoid turning the memcpy() operation into an uninterruptable sequence. 2023-09-06 13:27:45 +02:00
2cb54d48a9 Added the common function which both fputs() and puts() share now. 2023-09-06 13:24:45 +02:00
a7389454bb fputs() and puts() now share the same common code. Added abort checks in order to avoid turning the memcpy() operations into an uninterruptable sequence. 2023-09-06 13:24:15 +02:00
45d118101a Added assertion test for the buffer alignment padding size. 2023-09-06 13:22:07 +02:00
e4a703000a Reactivated memory debugging features. Added more robust integer overflow checking. 2023-09-06 13:21:13 +02:00
8a4a75e721 Optionally, the slab allocator can be built to deliver 64-bit aligned allocations by default. Also, the slab allocator is no longer enabled at build time. 2023-09-06 13:20:10 +02:00
4cb621d24d Added documentation on side-effects caused by using MEMF_CLEAR 2022-03-30 09:33:25 +02:00
ff5826c54e Small changes to the code documentation
The memory pools store puddles near head of the allocation list and the large allocations near the tail of the list.

Some hints added to point to side-effects which might apply to memory allocation operations.
2021-12-23 13:08:08 +01:00
b7ce13cbf8 Changed the internal data structures for slab allocations to yield 64-bit-aligned memory allocations. This can be tuned with the __MEM_ALLOCATIONS_64_BIT_ALIGNED preprocessor symbol. 2021-09-27 11:03:08 +02:00
e71249a15b Removed the profile_profile.o file from the library to be be built. Added a comment explaining why this was necessary. 2021-09-27 11:00:54 +02:00
19323ec218 The aggressive loop optimization option can be disabled/enabled as needed through a variable. 2021-09-27 11:00:20 +02:00
cc8b81e7cc Added a 'C' implementation for the memory pools functions
Data structures and behaviour should follow the original (well, "original and bug-fixed") implementation.
2021-06-22 15:15:29 +02:00
a7efdabefc Rewritten to handle date ranges better which lie outside the expected time of day, day of month and month.
The 'struct tm' is now properly updated to reflect the time and date information which comes out of the number of seconds calculated.

A bug reported by Andreas Falkenhahn is resolved which had the effect of distorting the number of seconds calculated.
2020-07-06 12:01:35 +02:00
57774795af Typo correction. 2020-02-27 12:15:30 +01:00
9d99542299 Fix for indentation error. 2020-02-27 12:15:17 +01:00
e02089e28a Better handling of unbuffered mode for interactive streams
If the stream is unbuffered and turns out to be an interactive stream (e.g. stderr) then output will be made to follow the rules of line buffering to yield better readability of the output. Similar code exists with fputs(), for example.
2019-08-24 14:05:09 +02:00
2bcfec3e60 Optimizations for better write performance.
fwrite() now tries to fill the write buffer as far is possible and will only resort to using the __putc() macros when necessary. This should improve write performance by quite a bit.

If the write buffer happens to be empty and the number of bytes to write is at least as large as the write buffer, then fwrite() will directly call write(). This should improve write performance, too.

If the file is in unbuffered mode, fwrite() now always calls write(), bypassing the write buffer altogether.
2019-08-24 14:03:34 +02:00
4a01746be2 Optimizations for better write performance.
fputs() now works almost exactly like fwrite(), except that it's dealing with a NUL-terminated string.
2019-08-24 14:00:03 +02:00
914ef57844 Changed how size=0 plays out
Setting the buffer size to 0 had the effect of switching the buffering mode. This is a bad idea since it wasn't actually called for: this is what the 'bufmode' parameter is intended for. What happens now is that the default buffer size is used (BUFSIZ), which sort of follows what the BSD libc did: a zero buffer size delayed the allocation of a buffer until the first read/write access occured.
2019-08-24 13:58:04 +02:00
089ae11181 Improvements for better read performance.
gets() now tries to copy as much data from the read buffer as possible, and will fall back onto using the __getc() macro only if necessary. This should improve performance on long lines, or crash faster if the read buffer happens to be too short. This is probably wasted on gets(), but you never know...
2019-08-24 13:55:31 +02:00
ae13cd77fc Optimizations for better read performance.
If the buffer mode is set to "no buffering" then fread() will always bypass the buffer and call read() instead.

If there is enough data waiting to be read from the buffer, fread() will now copy it directly, refilling the buffer as needed.

If the read buffer happens to be empty, buffering is enabled for the stream, and the number of bytes to read is at least as large as the buffer size,  then fread() will directly call read(), which should improve performance significantly.
2019-08-24 13:53:48 +02:00
3f21e90fca Optimizations for better read performance
fgets() now copies as much data from the read buffer as possible, falling back onto the __getc() macro only as a last resort. This should help greatly when reading long lines since the overhead of calling __getc() goes away.
2019-08-24 13:50:41 +02:00
46 changed files with 3758 additions and 1364 deletions

View File

@ -122,7 +122,7 @@ WARNINGS = \
# -Wconversion -Wshadow # -Wconversion -Wshadow
INCLUDES = -Iinclude -I. -Inetinclude 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
#OPTIONS = -fno-builtin -fno-common -DNDEBUG -D__THREAD_SAFE #OPTIONS = -fno-builtin -fno-common -DNDEBUG -D__THREAD_SAFE
#OPTIONS = -fno-builtin -fno-common -D__MEM_DEBUG #OPTIONS = -fno-builtin -fno-common -D__MEM_DEBUG
@ -261,6 +261,7 @@ C_LIB = \
stdio_iobhookentry.o \ stdio_iobhookentry.o \
stdio_lock.o \ stdio_lock.o \
stdio_locksemaphorename.o \ stdio_locksemaphorename.o \
stdio_long_path.o \
stdio_nostdio.o \ stdio_nostdio.o \
stdio_openiob.o \ stdio_openiob.o \
stdio_parent_of_fh.o \ stdio_parent_of_fh.o \
@ -449,7 +450,6 @@ C_LIB = \
time_numbertostring.o \ time_numbertostring.o \
time_strftime.o \ time_strftime.o \
time_time.o \ time_time.o \
time_tzset.o \
time_weekday.o \ time_weekday.o \
uio_readv.o \ uio_readv.o \
uio_writev.o \ uio_writev.o \
@ -492,13 +492,13 @@ C_LIB = \
UNIX_LIB = \ UNIX_LIB = \
unix.lib_rev.o \ unix.lib_rev.o \
dirent_closedir.o \ dirent_closedir.o \
dirent_rewinddir.o \
dirent_opendir.o \ dirent_opendir.o \
dirent_readdir.o \ dirent_readdir.o \
dirent_rewinddir.o \
fcntl_creat.o \ fcntl_creat.o \
fcntl_fcntl.o \ fcntl_fcntl.o \
fcntl_open.o \
fcntl_get_default_file.o \ fcntl_get_default_file.o \
fcntl_open.o \
getopt_getopt_long.o \ getopt_getopt_long.o \
mount_convertinfo.o \ mount_convertinfo.o \
mount_statfs.o \ mount_statfs.o \
@ -506,18 +506,19 @@ UNIX_LIB = \
resource_setrlimit.o \ resource_setrlimit.o \
stat_chmod.o \ stat_chmod.o \
stat_fstat.o \ stat_fstat.o \
stat_lstat.o \
stat_lock.o \ stat_lock.o \
stat_lstat.o \
stat_mkdir.o \ stat_mkdir.o \
stat_rmdir.o \ stat_rmdir.o \
stat_stat.o \ stat_stat.o \
stdio_ctermid.o \ stdio_ctermid.o \
stdio_fdhookentry.o \ stdio_fdhookentry.o \
stdio_fflush.o \ stdio_fflush.o \
stdio_file_init.o \
stdio_fopen.o \ stdio_fopen.o \
stdio_init_exit.o \ stdio_init_exit.o \
stdio_file_init.o \
stdio_locksemaphorename.o \ stdio_locksemaphorename.o \
stdio_long_path.o \
stdio_openiob.o \ stdio_openiob.o \
stdio_popen.o \ stdio_popen.o \
stdio_record_locking.o \ stdio_record_locking.o \
@ -527,17 +528,28 @@ UNIX_LIB = \
stdlib_alloca_cleanup.o \ stdlib_alloca_cleanup.o \
stdlib_alloca_trap.o \ stdlib_alloca_trap.o \
stdlib_arg.o \ stdlib_arg.o \
stdlib_calloc.o \
stdlib_decay_unused_slabs.o \
stdlib_expand_wildcard.o \ stdlib_expand_wildcard.o \
stdlib_expand_wildcard_check.o \ stdlib_expand_wildcard_check.o \
stdlib_free.o \
stdlib_free_unused_slabs.o \
stdlib_getmemstats.o \ stdlib_getmemstats.o \
stdlib_get_slab_allocations.o \
stdlib_get_slab_stats.o \
stdlib_get_slab_usage.o \
stdlib_main.o \ stdlib_main.o \
stdlib_main_stub.o \ stdlib_main_stub.o \
stdlib_malloc.o \
stdlib_mkdtemp.o \ stdlib_mkdtemp.o \
stdlib_mkstemp.o \ stdlib_mkstemp.o \
stdlib_mktemp.o \ stdlib_mktemp.o \
stdlib_malloc.o \
stdlib_realloc.o \ stdlib_realloc.o \
stdlib_red_black.o \
stdlib_resetmemstats.o \ stdlib_resetmemstats.o \
stdlib_slab.o \
stdlib_slab_max_size.o \
stdlib_slab_purge_threshold.o \
stdlib_system.o \ stdlib_system.o \
systeminfo_sysinfo.o \ systeminfo_sysinfo.o \
termios_cfgetispeed.o \ termios_cfgetispeed.o \
@ -941,6 +953,7 @@ AMIGA_LIB = \
amiga_hotkey.o \ amiga_hotkey.o \
amiga_invertstring.o \ amiga_invertstring.o \
amiga_newlist.o \ amiga_newlist.o \
amiga_pools.o \
amiga_rangerand.o \ amiga_rangerand.o \
amiga_remtof.o \ amiga_remtof.o \
amiga_rexxvars.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. # The -fbaserel32 option requires the CPU type to be 68020, too.
ifneq (,$(findstring fbaserel32,$(CODE_FLAGS))) ifneq (,$(findstring fbaserel32,$(CODE_FLAGS)))
LOCAL_CODE_FLAGS := $(CODE_FLAGS) $(CODE_TYPE) LOCAL_CODE_FLAGS := $(CODE_FLAGS) $(CODE_TYPE)

View File

@ -97,7 +97,11 @@ include libm.gmk
include libnet.gmk include libnet.gmk
include libdebug.gmk include libdebug.gmk
include libamiga.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: \ all-targets: \
lib/crt0.o \ lib/crt0.o \
@ -164,7 +168,9 @@ cvs-tag:
# General build rules for all object files and the individual libraries # 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 lib/crtbegin.o : crtbegin.c
@$(COMPILE) @$(COMPILE)
@ -179,7 +185,7 @@ lib/small-data/%.o : AFLAGS += $(SMALLDATA)
lib/small-data/%.o : %.S lib/small-data/%.o : %.S
@$(ASSEMBLE) @$(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 lib/small-data/crtbegin.o : crtbegin.c
@$(COMPILE) @$(COMPILE)
@ -191,7 +197,7 @@ lib/soft-float/%.o : AFLAGS += $(SOFTFLOAT)
lib/soft-float/%.o : %.S lib/soft-float/%.o : %.S
@$(ASSEMBLE) @$(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 lib/soft-float/crtbegin.o : crtbegin.c
@$(COMPILE) @$(COMPILE)
@ -203,7 +209,7 @@ lib/baserel/%.o : AFLAGS += $(BASEREL)
lib/baserel/%.o : %.S lib/baserel/%.o : %.S
@$(ASSEMBLE) @$(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 lib/baserel/crtbegin.o : crtbegin.c
@$(COMPILE) @$(COMPILE)
@ -215,7 +221,7 @@ lib.threadsafe/%.o : AFLAGS += $(LARGEDATA) $(THREADSAFE)
lib.threadsafe/%.o : %.S lib.threadsafe/%.o : %.S
@$(ASSEMBLE) @$(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 lib.threadsafe/crtbegin.o : crtbegin.c
@$(COMPILE) @$(COMPILE)
@ -227,7 +233,7 @@ lib.threadsafe/small-data/%.o : AFLAGS += $(SMALLDATA) $(THREADSAFE)
lib.threadsafe/small-data/%.o : %.S lib.threadsafe/small-data/%.o : %.S
@$(ASSEMBLE) @$(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 lib.threadsafe/small-data/crtbegin.o : crtbegin.c
@$(COMPILE) @$(COMPILE)
@ -239,7 +245,7 @@ lib.threadsafe/soft-float/%.o : AFLAGS += $(SOFTFLOAT) $(THREADSAFE)
lib.threadsafe/soft-float/%.o : %.S lib.threadsafe/soft-float/%.o : %.S
@$(ASSEMBLE) @$(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 lib.threadsafe/soft-float/crtbegin.o : crtbegin.c
@$(COMPILE) @$(COMPILE)
@ -251,7 +257,7 @@ lib.threadsafe/baserel/%.o : AFLAGS += $(BASEREL) $(THREADSAFE)
lib.threadsafe/baserel/%.o : %.S lib.threadsafe/baserel/%.o : %.S
@$(ASSEMBLE) @$(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 lib.threadsafe/baserel/crtbegin.o : crtbegin.c
@$(COMPILE) @$(COMPILE)

View File

@ -1,6 +1,6 @@
#define VERSION 1 #define VERSION 1
#define REVISION 215 #define REVISION 216
#define DATE "26.6.2017" #define DATE "10.7.2025"
#define VERS "amiga.lib 1.215" #define VERS "amiga.lib 1.216"
#define VSTRING "amiga.lib 1.215 (26.6.2017)\r\n" #define VSTRING "amiga.lib 1.216 (10.7.2025)\r\n"
#define VERSTAG "\0$VER: amiga.lib 1.215 (26.6.2017)" #define VERSTAG "\0$VER: amiga.lib 1.216 (10.7.2025)"

View File

@ -1 +1 @@
215 216

562
library/amiga_pools.c Normal file
View 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);
}
}
}

View File

@ -1,6 +1,6 @@
#define VERSION 1 #define VERSION 1
#define REVISION 215 #define REVISION 217
#define DATE "26.6.2017" #define DATE "10.7.2025"
#define VERS "c.lib 1.215" #define VERS "c.lib 1.217"
#define VSTRING "c.lib 1.215 (26.6.2017)\r\n" #define VSTRING "c.lib 1.217 (10.7.2025)\r\n"
#define VERSTAG "\0$VER: c.lib 1.215 (26.6.2017)" #define VERSTAG "\0$VER: c.lib 1.217 (10.7.2025)"

View File

@ -1 +1 @@
215 217

View File

@ -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) 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 building a libstdc++ library with wide char support. For now, the
functions are mostly stub ones only. They can be implemented on functions are mostly stub ones only. They can be implemented on
demand. demand.
@ -177,7 +310,7 @@ c.lib 1.206 (24.4.2015)
- Removed the remains of all the stack extension and stack overflow/underflow - 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 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. 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. adding/subtracting the local time zone.
- Changed the algorithm that calculates the number of days that have passed - 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(). code in strftime().
- Also changed the algorithm used by strftime() to produce the week numbers - Also changed the algorithm used by strftime() to produce the week numbers

View File

@ -1,10 +1,8 @@
/* /*
* $Id: dirent_readdir.c,v 1.10 2006-09-25 14:51:15 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -79,9 +77,11 @@ readdir(DIR * directory_pointer)
dh->dh_Position++; dh->dh_Position++;
dh->dh_DirectoryEntry.d_ino = 0;
strcpy(dh->dh_DirectoryEntry.d_name,"."); strcpy(dh->dh_DirectoryEntry.d_name,".");
dh->dh_DirectoryEntry.d_ino = 0;
dh->dh_DirectoryEntry.d_type = DT_DIR;
result = &dh->dh_DirectoryEntry; result = &dh->dh_DirectoryEntry;
} }
else else
@ -115,8 +115,9 @@ readdir(DIR * directory_pointer)
assert( sizeof(dh->dh_DirectoryEntry.d_name) >= sizeof(fib->fib_FileName) ); assert( sizeof(dh->dh_DirectoryEntry.d_name) >= sizeof(fib->fib_FileName) );
strcpy(dh->dh_DirectoryEntry.d_name,fib->fib_FileName); strcpy(dh->dh_DirectoryEntry.d_name,fib->fib_FileName);
dh->dh_DirectoryEntry.d_ino = fib->fib_DiskKey; dh->dh_DirectoryEntry.d_ino = fib->fib_DiskKey;
dh->dh_DirectoryEntry.d_type = DT_DIR;
result = &dh->dh_DirectoryEntry; result = &dh->dh_DirectoryEntry;
} }
@ -147,10 +148,11 @@ readdir(DIR * directory_pointer)
dh->dh_Position++; dh->dh_Position++;
dh->dh_DirectoryEntry.d_ino = dh->dh_FileInfo.fib_DiskKey;
strcpy(dh->dh_DirectoryEntry.d_name,"."); 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; result = &dh->dh_DirectoryEntry;
} }
else if (dh->dh_Position == 1) else if (dh->dh_Position == 1)
@ -176,10 +178,11 @@ readdir(DIR * directory_pointer)
SHOWMSG("returning .."); SHOWMSG("returning ..");
dh->dh_DirectoryEntry.d_ino = fib->fib_DiskKey;
strcpy(dh->dh_DirectoryEntry.d_name,".."); 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; result = &dh->dh_DirectoryEntry;
} }
} }
@ -192,12 +195,23 @@ readdir(DIR * directory_pointer)
if(ExNext(dh->dh_DirLock,&dh->dh_FileInfo)) 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) ); assert( sizeof(dh->dh_DirectoryEntry.d_name) >= sizeof(dh->dh_FileInfo.fib_FileName) );
strcpy(dh->dh_DirectoryEntry.d_name,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; result = &dh->dh_DirectoryEntry;
} }
else else

View File

@ -1,10 +1,8 @@
/* /*
* $Id: dirent.h,v 1.7 2006-01-08 12:06:14 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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 struct dirent
{ {
ino_t d_ino; ino_t d_ino;
char d_name[NAME_MAX+1]; 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); extern DIR * opendir(const char * path_name);

View File

@ -166,6 +166,8 @@ extern int errno;
#define EILSEQ 85 /* Encoding error detected */ #define EILSEQ 85 /* Encoding error detected */
#define ENOTSUP 86 /* Not supported */
/****************************************************************************/ /****************************************************************************/
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -1,10 +1,8 @@
/* /*
* $Id: locale.h,v 1.5 2006-01-08 12:06:14 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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;
/****************************************************************************/ /****************************************************************************/

View File

@ -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 * 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, * 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 * 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 * 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. * you pass to __get_slab_stats() will be passed to your callback function.
* *

View File

@ -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 chdir(const char * path_name);
extern int lockf(int file_descriptor, int function, off_t size); extern int lockf(int file_descriptor, int function, off_t size);
extern unsigned int sleep(unsigned int seconds); 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 int getopt(int argc, char * const argv[], const char *opts);
extern pid_t getpid(void); extern pid_t getpid(void);
extern char *realpath(const char *file_name, char *resolved_name); extern char *realpath(const char *file_name, char *resolved_name);

View File

@ -46,6 +46,7 @@ AMIGA_LIB = \
amiga_hotkey.o \ amiga_hotkey.o \
amiga_invertstring.o \ amiga_invertstring.o \
amiga_newlist.o \ amiga_newlist.o \
amiga_pools.o \
amiga_rangerand.o \ amiga_rangerand.o \
amiga_remtof.o \ amiga_remtof.o \
amiga_rexxvars.o \ amiga_rexxvars.o \

View File

@ -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 := \
c.lib_rev.o \ c.lib_rev.o \
ctype_isalnum.o \ ctype_isalnum.o \
@ -66,7 +70,6 @@ C_LIB := \
mount_convertinfo.o \ mount_convertinfo.o \
mount_fstatfs.o \ mount_fstatfs.o \
mount_statfs.o \ mount_statfs.o \
profile_profil.o \
signal_checkabort.o \ signal_checkabort.o \
signal_data.o \ signal_data.o \
signal_kill.o \ signal_kill.o \

View File

@ -22,13 +22,13 @@ LIBS += \
UNIX_LIB := \ UNIX_LIB := \
unix.lib_rev.o \ unix.lib_rev.o \
dirent_closedir.o \ dirent_closedir.o \
dirent_rewinddir.o \
dirent_opendir.o \ dirent_opendir.o \
dirent_readdir.o \ dirent_readdir.o \
dirent_rewinddir.o \
fcntl_creat.o \ fcntl_creat.o \
fcntl_fcntl.o \ fcntl_fcntl.o \
fcntl_open.o \
fcntl_get_default_file.o \ fcntl_get_default_file.o \
fcntl_open.o \
getopt_getopt_long.o \ getopt_getopt_long.o \
mount_convertinfo.o \ mount_convertinfo.o \
mount_statfs.o \ mount_statfs.o \
@ -36,16 +36,16 @@ UNIX_LIB := \
resource_setrlimit.o \ resource_setrlimit.o \
stat_chmod.o \ stat_chmod.o \
stat_fstat.o \ stat_fstat.o \
stat_lstat.o \
stat_lock.o \ stat_lock.o \
stat_lstat.o \
stat_mkdir.o \ stat_mkdir.o \
stat_rmdir.o \ stat_rmdir.o \
stat_stat.o \ stat_stat.o \
stdio_ctermid.o \ stdio_ctermid.o \
stdio_fdhookentry.o \ stdio_fdhookentry.o \
stdio_fflush.o \ stdio_fflush.o \
stdio_fopen.o \
stdio_file_init.o \ stdio_file_init.o \
stdio_fopen.o \
stdio_init_exit.o \ stdio_init_exit.o \
stdio_locksemaphorename.o \ stdio_locksemaphorename.o \
stdio_openiob.o \ stdio_openiob.o \
@ -60,15 +60,17 @@ UNIX_LIB := \
stdlib_dlopen.o \ stdlib_dlopen.o \
stdlib_expand_wildcard.o \ stdlib_expand_wildcard.o \
stdlib_expand_wildcard_check.o \ stdlib_expand_wildcard_check.o \
stdlib_free.o \
stdlib_getmemstats.o \ stdlib_getmemstats.o \
stdlib_main.o \ stdlib_main.o \
stdlib_main_stub.o \ stdlib_main_stub.o \
stdlib_malloc.o \
stdlib_mkdtemp.o \ stdlib_mkdtemp.o \
stdlib_mkstemp.o \ stdlib_mkstemp.o \
stdlib_mktemp.o \ stdlib_mktemp.o \
stdlib_malloc.o \
stdlib_realloc.o \ stdlib_realloc.o \
stdlib_resetmemstats.o \ stdlib_resetmemstats.o \
stdlib_slab.o \
stdlib_system.o \ stdlib_system.o \
systeminfo_sysinfo.o \ systeminfo_sysinfo.o \
termios_cfgetispeed.o \ termios_cfgetispeed.o \

View File

@ -1,10 +1,8 @@
/* /*
* $Id: math_sqrt.c,v 1.9 2006-09-22 07:54:24 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -251,7 +249,8 @@ sqrt(double x)
} }
else else
{ {
result = 0; result = NAN;
__set_errno(EDOM); __set_errno(EDOM);
} }

View File

@ -1,10 +1,8 @@
/* /*
* $Id: math_tgamma.c,v 1.3 2006-01-08 12:04:24 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -54,13 +52,11 @@ double
tgamma(double x) tgamma(double x)
{ {
int gamma_sign; int gamma_sign;
double y; double y;
y = __lgamma(x,&gamma_sign); y = __lgamma(x, &gamma_sign);
if (gamma_sign < 0)
y = -y;
return y; return gamma_sign * exp(y);
} }
/****************************************************************************/ /****************************************************************************/

View File

@ -1,6 +1,4 @@
/* /*
* $Id: math_tgammaf.c,v 1.3 2006-01-08 12:04:24 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * Portable ISO 'C' (1994) runtime library for the Amiga computer
@ -56,11 +54,9 @@ tgammaf(float x)
int gamma_sign; int gamma_sign;
float y; float y;
y = __lgammaf(x,&gamma_sign); y = __lgammaf(x, &gamma_sign);
if (gamma_sign < 0)
y = -y;
return y; return gamma_sign * expf(y);
} }
/****************************************************************************/ /****************************************************************************/

View File

@ -71,7 +71,7 @@ raise(int sig)
assert( SIGABRT <= sig && sig <= SIGTERM ); assert( SIGABRT <= sig && sig <= SIGTERM );
/* This has to be a well-known and supported signal. */ /* This has to be a well-known and supported signal. */
if(sig < SIGABRT || sig > SIGTERM) if (sig < SIGABRT || sig > SIGTERM)
{ {
SHOWMSG("unknown signal number"); SHOWMSG("unknown signal number");
@ -80,48 +80,49 @@ raise(int sig)
} }
/* Can we deliver the signal? */ /* Can we deliver the signal? */
if(FLAG_IS_CLEAR(__signals_blocked, (1 << sig)) && if (FLAG_IS_CLEAR(__signals_blocked, (1 << sig)) &&
FLAG_IS_CLEAR(local_signals_blocked, (1 << sig))) FLAG_IS_CLEAR(local_signals_blocked, (1 << sig)))
{ {
signal_handler_t handler; signal_handler_t handler;
/* Which handler is installed for this signal? */ /* Which handler is installed for this signal? */
handler = __signal_handler_table[sig - SIGABRT]; handler = __signal_handler_table[sig - SIGABRT];
/* Should we ignore this signal? */ /* Should we handle this signal rather than ignoring it? */
if(handler != SIG_IGN) if (handler != SIG_IGN)
{ {
/* Block delivery of this signal to prevent recursion. */ /* 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 /* The default behaviour is to drop into abort(), or do
something very much like it. */ * something very much like it.
if(handler == SIG_DFL) */
if (handler == SIG_DFL)
{ {
SHOWMSG("this is the default handler"); SHOWMSG("this is the default handler");
if(sig == SIGINT) if (sig == SIGINT)
{ {
char break_string[80]; char break_string[80];
/* Turn off ^C checking for good. */ /* Turn off ^C checking for good. */
__check_abort_enabled = FALSE; __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); __print_termination_message(break_string);
SHOWMSG("bye, bye..."); SHOWMSG("bye, bye...");
} }
/* Drop straight into abort(), which might call signal() /* Drop straight into __abort(), which will
again but is otherwise guaranteed to eventually eventually land us in _exit(). Note that
land us in _exit(). */ abort() calls raise(SIGABRT). */
abort(); __abort();
} }
else else
{ {
SHOWMSG("calling the handler"); SHOWMSG("calling the signal handler");
(*handler)(sig); (*handler)(sig);
@ -129,7 +130,7 @@ raise(int sig)
} }
/* Unblock signal delivery again. */ /* Unblock signal delivery again. */
CLEAR_FLAG(local_signals_blocked,(1 << sig)); CLEAR_FLAG(local_signals_blocked, (1 << sig));
} }
} }
else else

View File

@ -1,10 +1,8 @@
/* /*
* $Id: stdio_fgets.c,v 1.6 2006-01-08 12:04:24 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -46,6 +44,7 @@
char * char *
fgets(char *s,int n,FILE *stream) fgets(char *s,int n,FILE *stream)
{ {
struct iob * file = (struct iob *)stream;
char * result = s; char * result = s;
int c; int c;
@ -57,14 +56,14 @@ fgets(char *s,int n,FILE *stream)
assert( s != NULL && stream != NULL ); assert( s != NULL && stream != NULL );
if(__check_abort_enabled) if (__check_abort_enabled)
__check_abort(); __check_abort();
flockfile(stream); flockfile(stream);
#if defined(CHECK_FOR_NULL_POINTERS) #if defined(CHECK_FOR_NULL_POINTERS)
{ {
if(s == NULL || stream == NULL) if (s == NULL || stream == NULL)
{ {
SHOWMSG("invalid parameters"); SHOWMSG("invalid parameters");
@ -75,7 +74,7 @@ fgets(char *s,int n,FILE *stream)
} }
#endif /* CHECK_FOR_NULL_POINTERS */ #endif /* CHECK_FOR_NULL_POINTERS */
if(n <= 0) if (n <= 0)
{ {
SHOWMSG("no work to be done"); 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 /* Take care of the checks and data structure changes that
* need to be handled only once for this stream. * need to be handled only once for this stream.
*/ */
if(__fgetc_check(stream) < 0) if (__fgetc_check(stream) < 0)
{ {
result = NULL; result = NULL;
goto out; goto out;
@ -98,12 +97,83 @@ fgets(char *s,int n,FILE *stream)
/* One off for the terminating '\0'. */ /* One off for the terminating '\0'. */
n--; 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 there is data in the buffer, try to copy it directly
if(c == EOF) * 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. */ /* Just to be on the safe side. */
(*s) = '\0'; (*s) = '\0';
@ -113,8 +183,9 @@ fgets(char *s,int n,FILE *stream)
} }
/* Make sure that we return NULL if we really /* Make sure that we return NULL if we really
didn't read anything at all */ * didn't read anything at all.
if(s == result) */
if (s == result)
result = NULL; result = NULL;
break; break;
@ -122,8 +193,11 @@ fgets(char *s,int n,FILE *stream)
(*s++) = c; (*s++) = c;
if(c == '\n') if (c == '\n')
break; break;
assert( n > 0 );
n--;
} }
(*s) = '\0'; (*s) = '\0';

View File

@ -1,10 +1,8 @@
/* /*
* $Id: stdio_fputs.c,v 1.7 2006-01-08 12:04:24 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -43,10 +41,12 @@
/****************************************************************************/ /****************************************************************************/
/* Both fputs() and puts() share this function. */
int int
fputs(const char *s, FILE *stream) __fputs(const char *s, int line_feed, FILE *stream)
{ {
struct iob * file = (struct iob *)stream; struct iob * file = (struct iob *)stream;
size_t total_size;
int result = EOF; int result = EOF;
int buffer_mode; int buffer_mode;
int c; int c;
@ -58,12 +58,12 @@ fputs(const char *s, FILE *stream)
assert( s != NULL && stream != NULL ); assert( s != NULL && stream != NULL );
if(__check_abort_enabled) if (__check_abort_enabled)
__check_abort(); __check_abort();
#if defined(CHECK_FOR_NULL_POINTERS) #if defined(CHECK_FOR_NULL_POINTERS)
{ {
if(s == NULL || stream == NULL) if (s == NULL || stream == NULL)
{ {
__set_errno(EFAULT); __set_errno(EFAULT);
goto out; goto out;
@ -77,37 +77,261 @@ fputs(const char *s, FILE *stream)
assert( FLAG_IS_SET(file->iob_Flags,IOBF_IN_USE) ); assert( FLAG_IS_SET(file->iob_Flags,IOBF_IN_USE) );
assert( file->iob_BufferSize > 0 ); assert( file->iob_BufferSize > 0 );
buffer_mode = (file->iob_Flags & IOBF_BUFFER_MODE); if (__fputc_check(stream) < 0)
if(buffer_mode == IOBF_BUFFER_MODE_NONE)
buffer_mode = IOBF_BUFFER_MODE_LINE;
if(__fputc_check(stream) < 0)
goto out; 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) struct fd * fd = __fd[file->iob_Descriptor];
goto out;
__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; result = OK;
out: out:
/* Note: if buffering is disabled for this stream, then we still /* Note: if buffering is disabled for this stream, then we still
may have buffered data around, queued to be printed right now. * may have buffered data around, queued to be printed right now.
This is intended to improve performance as it takes more effort * This is intended to improve performance as it takes more effort
to write a single character to a file than to write a bunch. */ * 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 (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"); SHOWMSG("couldn't flush the write buffer");
result = EOF; 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);
return(result); return(result);

View File

@ -1,10 +1,8 @@
/* /*
* $Id: stdio_fread.c,v 1.7 2006-01-08 12:04:24 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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( ptr != NULL && stream != NULL );
assert( (int)element_size >= 0 && (int)count >= 0 ); assert( (int)element_size >= 0 && (int)count >= 0 );
if(__check_abort_enabled) if (__check_abort_enabled)
__check_abort(); __check_abort();
flockfile(stream); flockfile(stream);
#if defined(CHECK_FOR_NULL_POINTERS) #if defined(CHECK_FOR_NULL_POINTERS)
{ {
if(ptr == NULL || stream == NULL) if (ptr == NULL || stream == NULL)
{ {
SHOWMSG("invalid parameters"); 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( FLAG_IS_SET(file->iob_Flags,IOBF_IN_USE) );
assert( file->iob_BufferSize > 0 ); 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"); 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; 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"); 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; 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_bytes_read = 0;
size_t total_size; size_t total_size;
unsigned char * data = ptr; unsigned char * data = ptr;
ssize_t num_bytes_read;
int c; int c;
if(__fgetc_check((FILE *)file) < 0) if (__fgetc_check((FILE *)file) < 0)
goto out; goto out;
/* Check for overflow. */
total_size = element_size * count; total_size = element_size * count;
if (element_size != (total_size / count))
goto out;
SHOWVALUE(total_size); SHOWVALUE(total_size);
while(total_size-- > 0) /* No buffering enabled? */
if ((file->iob_Flags & IOBF_BUFFER_MODE) == IOBF_BUFFER_MODE_NONE)
{ {
c = __getc(file); /* We bypass the buffer entirely. */
if(c == EOF) num_bytes_read = read(file->iob_Descriptor, data, total_size);
break; 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); SHOWVALUE(total_bytes_read);
@ -137,9 +220,6 @@ fread(void *ptr,size_t element_size,size_t count,FILE *stream)
SHOWVALUE(count); SHOWVALUE(count);
SHOWMSG("either element size or count is zero"); 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)); D(("total number of elements read = %ld",result));

View File

@ -1,10 +1,8 @@
/* /*
* $Id: stdio_fwrite.c,v 1.12 2010-10-20 13:12:58 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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( ptr != NULL && stream != NULL );
assert( (int)element_size >= 0 && (int)count >= 0 ); assert( (int)element_size >= 0 && (int)count >= 0 );
if(__check_abort_enabled) if (__check_abort_enabled)
__check_abort(); __check_abort();
flockfile(stream); flockfile(stream);
#if defined(CHECK_FOR_NULL_POINTERS) #if defined(CHECK_FOR_NULL_POINTERS)
{ {
if(ptr == NULL || stream == NULL) if (ptr == NULL || stream == NULL)
{ {
SHOWMSG("invalid parameters"); 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( __is_valid_iob(file) );
assert( FLAG_IS_SET(file->iob_Flags,IOBF_IN_USE) ); 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"); 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; 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"); 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; 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; unsigned char c;
int buffer_mode; int buffer_mode;
size_t total_bytes_written = 0; size_t total_bytes_written = 0;
size_t total_size; size_t total_size;
/* Check for overflow. */
total_size = element_size * count; total_size = element_size * count;
if (element_size != (total_size / count))
if(__fputc_check((FILE *)file) < 0)
goto out; 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); 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]; struct fd * fd = __fd[file->iob_Descriptor];
__fd_lock(fd); __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; buffer_mode = IOBF_BUFFER_MODE_LINE;
__fd_unlock(fd); __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) /* Give the user a chance to abort what could otherwise
goto out; * 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++; 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 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) /* We bypass the buffer entirely. */
goto out; 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++; 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) if (__iob_write_buffer_is_valid(file))
goto out; __flush_iob_write_buffer(file);
} }
result = total_bytes_written / element_size; result = total_bytes_written / element_size;
} }
else else
{ {
/* Don't let this appear like an EOF or error. */ SHOWVALUE(element_size);
clearerr((FILE *)file); SHOWVALUE(count);
SHOWMSG("either element size or count is zero");
} }
out: out:

View File

@ -1,10 +1,8 @@
/* /*
* $Id: stdio_gets.c,v 1.6 2006-01-08 12:04:24 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -46,6 +44,8 @@
char * char *
gets(char *s) gets(char *s)
{ {
FILE * stream = stdin;
struct iob * file = (struct iob *)stream;
char * result = s; char * result = s;
int c; int c;
@ -53,16 +53,16 @@ gets(char *s)
SHOWPOINTER(s); SHOWPOINTER(s);
assert( s != NULL && stdin != NULL ); assert( s != NULL );
if(__check_abort_enabled) if (__check_abort_enabled)
__check_abort(); __check_abort();
flockfile(stdin); flockfile(stream);
#if defined(CHECK_FOR_NULL_POINTERS) #if defined(CHECK_FOR_NULL_POINTERS)
{ {
if(s == NULL || stdin == NULL) if (s == NULL)
{ {
SHOWMSG("invalid parameters"); SHOWMSG("invalid parameters");
@ -77,21 +77,84 @@ gets(char *s)
/* Take care of the checks and data structure changes that /* Take care of the checks and data structure changes that
* need to be handled only once for this stream. * need to be handled only once for this stream.
*/ */
if(__fgetc_check(stdin) < 0) if (__fgetc_check(stream) < 0)
{ {
result = NULL; result = NULL;
goto out; goto out;
} }
/* So that we can tell error and 'end of file' conditions apart. */ /* 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 there is data in the buffer, try to copy it directly
if(c == EOF) * 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. */ /* Just to be on the safe side. */
(*s) = '\0'; (*s) = '\0';
@ -101,14 +164,15 @@ gets(char *s)
} }
/* Make sure that we return NULL if we really /* Make sure that we return NULL if we really
didn't read anything at all */ * didn't read anything at all.
if(s == result) */
if (s == result)
result = NULL; result = NULL;
break; break;
} }
if(c == '\n') if (c == '\n')
break; break;
(*s++) = c; (*s++) = c;
@ -120,7 +184,7 @@ gets(char *s)
out: out:
funlockfile(stdin); funlockfile(stream);
RETURN(result); RETURN(result);
return(result); return(result);

581
library/stdio_long_path.c Normal file
View 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
View 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 */

View File

@ -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 */ /* stdio_sscanf_hook_entry.c */
extern int __sscanf_hook_entry(struct iob *string,struct file_action_message *fam); extern int __sscanf_hook_entry(struct iob *string,struct file_action_message *fam);

View File

@ -1,10 +1,8 @@
/* /*
* $Id: stdio_puts.c,v 1.8 2006-01-08 12:04:25 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -46,10 +44,7 @@
int int
puts(const char *s) puts(const char *s)
{ {
struct iob * file = (struct iob *)stdout;
int result = EOF; int result = EOF;
int buffer_mode;
int c;
ENTER(); ENTER();
@ -57,11 +52,6 @@ puts(const char *s)
assert( s != NULL ); assert( s != NULL );
if(__check_abort_enabled)
__check_abort();
flockfile(stdout);
#if defined(CHECK_FOR_NULL_POINTERS) #if defined(CHECK_FOR_NULL_POINTERS)
{ {
if(s == NULL) if(s == NULL)
@ -72,45 +62,13 @@ puts(const char *s)
} }
#endif /* CHECK_FOR_NULL_POINTERS */ #endif /* CHECK_FOR_NULL_POINTERS */
assert( __is_valid_iob(file) ); if (__fputs(s, '\n', stdout) == EOF)
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)
goto out; goto out;
result = OK; result = OK;
out: 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);
return(result); return(result);
} }

View File

@ -1,10 +1,8 @@
/* /*
* $Id: stdio_setvbuf.c,v 1.11 2008-09-04 12:07:58 obarthel Exp $
*
* :ts=4 * :ts=4
* *
* Portable ISO 'C' (1994) runtime library for the Amiga computer * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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; 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) 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. */ size = BUFSIZ;
new_buffer = malloc(size + (__cache_line_size-1)); buf = NULL;
if(new_buffer == 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); assert( size <= ((size + (__cache_line_size-1)) & ~(__cache_line_size-1)) );
goto out;
/* Allocate a little more memory than necessary. */
new_buffer = malloc(size + (__cache_line_size-1));
if(new_buffer == NULL)
{
__set_errno(ENOBUFS);
goto out;
}
} }
} }

View File

@ -46,15 +46,8 @@
/****************************************************************************/ /****************************************************************************/
void 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; __check_abort_enabled = FALSE;
__print_termination_message(NULL); __print_termination_message(NULL);
@ -63,3 +56,17 @@ abort(void)
does not trigger the exit trap. */ does not trigger the exit trap. */
_exit(EXIT_FAILURE); _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();
}

View File

@ -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) CLIB_DESTRUCTOR(alloca_exit)
{ {
ENTER(); ENTER();
/* Clean this up, too, just to be safe. */ /* This list should remain empty. */
NewList((struct List *)&alloca_memory_list); NewList((struct List *)&alloca_memory_list);
LEAVE(); LEAVE();
@ -83,18 +108,18 @@ CLIB_DESTRUCTOR(alloca_exit)
/* Cleans up after all alloca() allocations that have been made so far. */ /* Cleans up after all alloca() allocations that have been made so far. */
static void static void
alloca_cleanup(const char * file,int line) alloca_cleanup(const char * file, int line)
{ {
void * stack_pointer = __get_sp(); void * stack_pointer = __get_sp();
__memory_lock(); __memory_lock();
/* Initialize this if it hasn't been taken care of yet. */ /* 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); NewList((struct List *)&alloca_memory_list);
/* Is this worth cleaning up? */ /* 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_prev;
struct MemoryContextNode * mcn; 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 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 to the allocations made in the context of a stack frame near to
where were currently are. */ where were currently are. */
for(mcn = (struct MemoryContextNode *)alloca_memory_list.mlh_TailPred ; for (mcn = (struct MemoryContextNode *)alloca_memory_list.mlh_TailPred ;
mcn->mcn_MinNode.mln_Pred != NULL && mcn->mcn_StackPointer < stack_pointer ; mcn->mcn_MinNode.mln_Pred != NULL && mcn->mcn_StackPointer < stack_pointer ;
mcn = mcn_prev) mcn = mcn_prev)
{ {
mcn_prev = (struct MemoryContextNode *)mcn->mcn_MinNode.mln_Pred; mcn_prev = (struct MemoryContextNode *)mcn->mcn_MinNode.mln_Pred;
Remove((struct Node *)mcn); Remove((struct Node *)mcn);
__free_memory(mcn->mcn_Memory,TRUE,file,line); /* Note: force the memory to be freed because it
__free_memory(mcn,FALSE,file,line); 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 /* Drop the cleanup callback if there's nothing to be cleaned
up any more. */ up any more. */
if(IsMinListEmpty(&alloca_memory_list)) if (IsListEmpty((struct List *)&alloca_memory_list))
__alloca_cleanup = NULL; __alloca_cleanup = NULL;
} }
@ -130,7 +158,7 @@ alloca_cleanup(const char * file,int line)
/****************************************************************************/ /****************************************************************************/
__static void * __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(); void * stack_pointer = __get_sp();
struct MemoryContextNode * mcn; struct MemoryContextNode * mcn;
@ -138,27 +166,23 @@ __alloca(size_t size,const char * file,int line)
__memory_lock(); __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 = alloca_cleanup;
(*__alloca_cleanup)(file,line); (*__alloca_cleanup)(file, line);
mcn = __allocate_memory(sizeof(*mcn),FALSE,file,line); mcn = __allocate_memory(sizeof(*mcn), FALSE, file, line);
if(mcn == NULL) if (mcn == NULL)
{ {
SHOWMSG("not enough memory"); SHOWMSG("not enough memory");
goto out; goto out;
} }
/* Allocate memory which cannot be run through realloc() or free(). */ /* Allocate memory which cannot be run through realloc() or free(). */
mcn->mcn_Memory = __allocate_memory(size,TRUE,file,line); mcn->mcn_Memory = __allocate_memory(size, TRUE, file, line);
if(mcn->mcn_Memory == NULL) if (mcn->mcn_Memory == NULL)
{ {
SHOWMSG("not enough memory"); SHOWMSG("not enough memory");
__free(mcn,file,line); __free(mcn, file, line);
goto out; goto out;
} }
@ -166,7 +190,7 @@ __alloca(size_t size,const char * file,int line)
assert( alloca_memory_list.mlh_Head != NULL ); 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; 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 /* If we are about to return NULL and a trap function is
provided, call it rather than returning NULL. */ provided, call it rather than returning NULL. */
if(result == NULL && __alloca_trap != NULL) if (result == NULL && __alloca_trap != NULL)
(*__alloca_trap)(); {
__alloca_cleanup = NULL;
return(result); (*__alloca_trap)();
}
return result;
} }
/****************************************************************************/ /****************************************************************************/
@ -191,5 +219,5 @@ alloca(size_t size)
result = __alloca(size,NULL,0); result = __alloca(size,NULL,0);
return(result); return result;
} }

47
library/stdlib_calloc.c Normal file → Executable file
View File

@ -48,48 +48,51 @@
/****************************************************************************/ /****************************************************************************/
__static void * __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; void * result = NULL;
size_t total_size; size_t total_size;
#ifdef __MEM_DEBUG #ifdef __MEM_DEBUG
{ {
/*__check_memory_allocations(file,line);*/ __check_memory_allocations(file, line);
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
/* This might overflow. */ /* Check for overflow. */
total_size = num_elements * element_size; total_size = element_size * num_elements;
if (num_elements > 0 && element_size > 0 && element_size != (total_size / num_elements))
/* 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
{ {
D(("calloc(num_elements=%ld, element_size=%ld) overflow")); 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 * void *
calloc(size_t num_elements,size_t element_size) calloc(size_t num_elements, size_t element_size)
{ {
void * result; void * result;
result = __calloc(num_elements,element_size,NULL,0); result = __calloc(num_elements, element_size, NULL, 0);
return(result); return result;
} }

View File

@ -59,18 +59,18 @@ get_hex_char(int n)
{ {
char result; char result;
if(0 <= n && n <= 9) if (0 <= n && n <= 9)
result = n + '0'; result = n + '0';
else else
result = n + 'A' - 10; result = n + 'A' - 10;
return(result); return result;
} }
STATIC VOID 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; i = 0;
@ -81,12 +81,12 @@ int_to_hex(unsigned long v,char * buffer,int min_digits)
buffer[i++] = get_hex_char(c); buffer[i++] = get_hex_char(c);
} }
while(v > 0); while (v > 0);
while(i < min_digits) while (i < min_digits)
buffer[i++] = '0'; buffer[i++] = '0';
for(j = 0 ; j < i / 2 ; j++) for (j = 0 ; j < i / 2 ; j++)
{ {
c = buffer[i - 1 - j]; c = buffer[i - 1 - j];
buffer[i - 1 - j] = buffer[j]; buffer[i - 1 - j] = buffer[j];
@ -97,47 +97,47 @@ int_to_hex(unsigned long v,char * buffer,int min_digits)
} }
STATIC VOID 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; const int mod = 20;
int position,i,c; int position, i, c;
char buffer[120]; char buffer[120];
char hex[10]; char hex[10];
buffer[0] = '\0'; buffer[0] = '\0';
for(i = 0 ; i < size ; i++) for (i = 0 ; i < size ; i++)
{ {
position = i % mod; position = i % mod;
if(position == 0) if (position == 0)
{ {
if(buffer[0] != '\0') if (buffer[0] != '\0')
{ {
int len = sizeof(buffer)-1; int len = sizeof(buffer)-1;
while(len > 0 && buffer[len-1] == ' ') while (len > 0 && buffer[len-1] == ' ')
len--; len--;
buffer[len] = '\0'; 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] = ':'; 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 + 0] = get_hex_char(m[i] >> 4);
buffer[10 + 2 * position + 1] = get_hex_char(m[i] & 15); buffer[10 + 2 * position + 1] = get_hex_char(m[i] & 15);
c = m[i]; c = m[i];
if(c < ' ' || (c >= 127 && c <= 160)) if (c < ' ' || (127 <= c && c < 160))
c = '.'; c = '.';
buffer[10 + 2 * mod + 1 + position] = 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; int len = sizeof(buffer)-1;
while(len > 0 && buffer[len-1] == ' ') while (len > 0 && buffer[len-1] == ' ')
len--; len--;
buffer[len] = '\0'; buffer[len] = '\0';
kprintf("[%s] %s\n",__program_name,buffer); kprintf("[%s] %s\n", __program_name, buffer);
} }
} }
/****************************************************************************/ /****************************************************************************/
STATIC VOID 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); ULONG size = mn->mn_OriginalSize;
unsigned char * head = (unsigned char *)(mn + 1); const unsigned char * head = (unsigned char *)&mn[1];
unsigned char * body = head + MALLOC_HEAD_SIZE; const unsigned char * body = &head[MALLOC_HEAD_SIZE];
unsigned char * tail = body + size; const unsigned char * tail = &body[size];
int max_head_damage = 0; int max_head_damage = 0;
int max_tail_damage = 0; int max_tail_damage = 0;
int max_body_damage = 0; int max_body_damage = 0;
int i; 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; 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) if (file != NULL)
kprintf("%s:%ld:",file,line); kprintf("%s:%ld:", file, line);
kprintf("At least %ld bytes were damaged in front of allocation 0x%08lx..0x%08lx, size = %ld", kprintf("At least %ld bytes were damaged in front of allocation 0x%08lx..0x%08lx, size = %ld",
max_head_damage, max_head_damage,
body,body + size - 1,size); body, body + size - 1, size);
if(mn->mn_File != NULL) if (mn->mn_File != NULL)
kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line); kprintf(", allocated at %s:%ld", mn->mn_File, mn->mn_Line);
kprintf("\n"); 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; 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) if (file != NULL)
kprintf("%s:%ld:",file,line); kprintf("%s:%ld:", file, line);
kprintf("At least %ld bytes were damaged behind allocation 0x%08lx..0x%08lx, size = %ld (with damage = %ld)", kprintf("At least %ld bytes were damaged behind allocation 0x%08lx..0x%08lx, size = %ld (with damage = %ld)",
max_tail_damage, max_tail_damage,
body,body + size - 1, body, body + size - 1,
size,size+max_tail_damage); size, size + max_tail_damage);
if(mn->mn_File != NULL) if (mn->mn_File != NULL)
kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line); kprintf(", allocated at %s:%ld", mn->mn_File, mn->mn_Line);
kprintf("\n"); 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; 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; 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) if (file != NULL)
kprintf("%s:%ld:",file,line); kprintf("%s:%ld:", file, line);
kprintf("At least %ld bytes were damaged in freed allocation 0x%08lx..0x%08lx, size = %ld", kprintf("At least %ld bytes were damaged in freed allocation 0x%08lx..0x%08lx, size = %ld",
max_body_damage, max_body_damage,
body,body + size - 1,size); body, body + size - 1, size);
if(mn->mn_File != NULL) if (mn->mn_File != NULL)
kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line); kprintf(", allocated at %s:%ld", mn->mn_File, mn->mn_Line);
kprintf("\n"); 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 void
__check_memory_allocations(const char * file,int line) __check_memory_allocations(const char * file, int line)
{ {
struct MemoryNode * mn; struct MemoryNode * mn;
__memory_lock(); __memory_lock();
for(mn = (struct MemoryNode *)__memory_list.mlh_Head ; for (mn = (struct MemoryNode *)__memory_list.mlh_Head ;
mn->mn_MinNode.mln_Succ != NULL ; mn->mn_MinNode.mln_Succ != NULL ;
mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ) mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ)
{ {
check_memory_node(mn,file,line); check_memory_node(mn, file, line);
} }
__memory_unlock(); __memory_unlock();
@ -290,7 +290,7 @@ __find_memory_node(void * address)
#if defined(__USE_MEM_TREES) #if defined(__USE_MEM_TREES)
{ {
result = __red_black_tree_find(&__memory_tree,address); result = __red_black_tree_find(&__memory_tree, address);
} }
#else #else
{ {
@ -298,11 +298,11 @@ __find_memory_node(void * address)
result = NULL; result = NULL;
for(mn = (struct MemoryNode *)__memory_list.mlh_Head ; for (mn = (struct MemoryNode *)__memory_list.mlh_Head ;
mn->mn_MinNode.mln_Succ != NULL ; mn->mn_MinNode.mln_Succ != NULL ;
mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ) mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ)
{ {
if(address == mn->mn_Allocation) if (address == mn->mn_Allocation)
{ {
result = mn; result = mn;
break; break;
@ -331,7 +331,7 @@ __find_memory_node(void * address)
result = &((struct MemoryNode *)address)[-1]; result = &((struct MemoryNode *)address)[-1];
return(result); return result;
} }
/****************************************************************************/ /****************************************************************************/
@ -349,96 +349,84 @@ remove_and_free_memory_node(struct MemoryNode * mn)
__memory_lock(); __memory_lock();
allocation_size = mn->mn_AllocationSize;
#if defined(__MEM_DEBUG) #if defined(__MEM_DEBUG)
{ {
Remove((struct Node *)mn); Remove((struct Node *)mn);
#if defined(__USE_MEM_TREES) #if defined(__USE_MEM_TREES)
{ {
__red_black_tree_remove(&__memory_tree,mn); __red_black_tree_remove(&__memory_tree, mn);
} }
#endif /* __USE_MEM_TREES */ #endif /* __USE_MEM_TREES */
allocation_size = sizeof(*mn) + MALLOC_HEAD_SIZE + GET_MN_SIZE(mn) + MALLOC_TAIL_SIZE; memset(mn, MALLOC_FREE_FILL, allocation_size);
assert( allocation_size == mn->mn_AllocationSize );
memset(mn,MALLOC_FREE_FILL,allocation_size);
}
#else
{
allocation_size = sizeof(*mn) + GET_MN_SIZE(mn);
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
#if defined(__USE_SLAB_ALLOCATOR) #if defined(__USE_SLAB_ALLOCATOR)
{ {
/* Are we using the 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 else
{ {
if(__memory_pool != NULL) #if NOT defined(__MEM_DEBUG)
{ {
PROFILE_OFF(); Remove((struct Node *)mn);
FreePooled(__memory_pool,mn,allocation_size);
PROFILE_ON();
} }
else #endif /* __MEM_DEBUG */
{
#if defined(__MEM_DEBUG)
{
PROFILE_OFF();
FreeMem(mn,allocation_size);
PROFILE_ON();
}
#else
{
struct MinNode * mln = (struct MinNode *)mn;
mln--; PROFILE_OFF();
FreeMem(mn, allocation_size);
Remove((struct Node *)mln); PROFILE_ON();
PROFILE_OFF();
FreeMem(mln,sizeof(*mln) + allocation_size);
PROFILE_ON();
}
#endif /* __MEM_DEBUG */
}
} }
} }
#else #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(); PROFILE_OFF();
FreePooled(__memory_pool,mn,allocation_size); FreePooled(__memory_pool, mn, allocation_size);
PROFILE_ON(); PROFILE_ON();
} }
else else
{ {
#if defined(__MEM_DEBUG) #if NOT defined(__MEM_DEBUG)
{ {
PROFILE_OFF(); Remove((struct Node *)mn);
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();
} }
#endif /* __MEM_DEBUG */ #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 */ #endif /* __USE_SLAB_ALLOCATOR */
@ -452,37 +440,35 @@ remove_and_free_memory_node(struct MemoryNode * mn)
/****************************************************************************/ /****************************************************************************/
void 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); assert(mn != NULL);
#ifdef __MEM_DEBUG #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 #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) if (mn->mn_File != NULL)
kprintf("allocated at %s:%ld, ",mn->mn_File,mn->mn_Line); 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 */ #endif /* __MEM_DEBUG_LOG */
if(__never_free) if (__never_free)
{ {
mn->mn_AlreadyFree = TRUE; mn->mn_AlreadyFree = TRUE;
mn->mn_FreeFile = (char *)file; mn->mn_FreeFile = (char *)file;
mn->mn_FreeLine = line; 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 else
{ {
@ -493,19 +479,19 @@ __free_memory_node(struct MemoryNode * mn,const char * UNUSED file,int UNUSED li
{ {
#ifdef __MEM_DEBUG_LOG #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"); kprintf("FAILED]\n");
} }
#endif /* __MEM_DEBUG_LOG */ #endif /* __MEM_DEBUG_LOG */
kprintf("[%s] %s:%ld:Allocation at address 0x%08lx, size %ld", 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) if (mn->mn_File != NULL)
kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line); 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 #else
@ -518,7 +504,7 @@ __free_memory_node(struct MemoryNode * mn,const char * UNUSED file,int UNUSED li
/****************************************************************************/ /****************************************************************************/
VOID 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; struct MemoryNode * mn;
@ -529,7 +515,7 @@ __free_memory(void * ptr,BOOL force,const char * file,int line)
#ifdef __MEM_DEBUG #ifdef __MEM_DEBUG
{ {
/*if((rand() % 16) == 0) /*if ((rand() % 16) == 0)
__check_memory_allocations(file,line);*/ __check_memory_allocations(file,line);*/
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
@ -538,34 +524,37 @@ __free_memory(void * ptr,BOOL force,const char * file,int line)
#ifdef __MEM_DEBUG #ifdef __MEM_DEBUG
{ {
if(mn != NULL) if (mn != NULL)
{ {
if(force || FLAG_IS_CLEAR(mn->mn_Size, MN_SIZE_NEVERFREE)) if (force || FLAG_IS_CLEAR(mn->mn_Flags, MNF_NEVER_FREE))
__free_memory_node(mn,file,line); __free_memory_node(mn, file, line);
} }
else else
{ {
#ifdef __MEM_DEBUG_LOG #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"); kprintf("FAILED]\n");
} }
#endif /* __MEM_DEBUG_LOG */ #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 #else
{ {
assert( mn != NULL ); 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))) if (mn != NULL && (force || FLAG_IS_CLEAR(mn->mn_Flags, MNF_NEVER_FREE)))
__free_memory_node(mn,file,line); __free_memory_node(mn, file, line);
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
} }
@ -578,13 +567,13 @@ __free(void * ptr,const char * file,int line)
__memory_lock(); __memory_lock();
/* Try to get rid of now unused memory. */ /* Try to get rid of now unused memory. */
if(__alloca_cleanup != NULL) if (__alloca_cleanup != NULL)
(*__alloca_cleanup)(file,line); (*__alloca_cleanup)(file, line);
__memory_unlock(); __memory_unlock();
if(ptr != NULL) if (ptr != NULL)
__free_memory(ptr,FALSE,file,line); __free_memory(ptr, FALSE, file, line);
} }
/****************************************************************************/ /****************************************************************************/
@ -592,5 +581,5 @@ __free(void * ptr,const char * file,int line)
void void
free(void * ptr) free(void * ptr)
{ {
__free(ptr,NULL,0); __free(ptr, NULL, 0);
} }

26
library/stdlib_get_slab_usage.c Normal file → Executable file
View File

@ -44,14 +44,14 @@
void void
__get_slab_usage(__slab_usage_callback callback) __get_slab_usage(__slab_usage_callback callback)
{ {
if(__slab_data.sd_InUse) if (__slab_data.sd_InUse)
{ {
struct __slab_usage_information sui; struct __slab_usage_information sui;
const struct SlabNode * sn; const struct SlabNode * sn;
BOOL stop; BOOL stop;
int i; int i;
memset(&sui,0,sizeof(sui)); memset(&sui, 0, sizeof(sui));
__memory_lock(); __memory_lock();
@ -59,11 +59,11 @@ __get_slab_usage(__slab_usage_callback callback)
sui.sui_num_single_allocations = __slab_data.sd_NumSingleAllocations; sui.sui_num_single_allocations = __slab_data.sd_NumSingleAllocations;
sui.sui_total_single_allocation_size = __slab_data.sd_TotalSingleAllocationSize; 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 ; for (sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ;
sn->sn_MinNode.mln_Succ != NULL ; sn->sn_MinNode.mln_Succ != NULL ;
sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ) sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ)
{ {
if (sn->sn_UseCount == 0) if (sn->sn_UseCount == 0)
sui.sui_num_empty_slabs++; 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 ; for (sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ;
sn->sn_MinNode.mln_Succ != NULL ; sn->sn_MinNode.mln_Succ != NULL ;
sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ) sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ)
{ {
sui.sui_chunk_size = sn->sn_ChunkSize; sui.sui_chunk_size = sn->sn_ChunkSize;
sui.sui_num_chunks = sn->sn_Count; sui.sui_num_chunks = sn->sn_Count;
@ -91,7 +93,7 @@ __get_slab_usage(__slab_usage_callback callback)
sui.sui_slab_index++; sui.sui_slab_index++;
if((*callback)(&sui) != 0) if ((*callback)(&sui) != 0)
{ {
stop = TRUE; stop = TRUE;
break; break;

View File

@ -189,6 +189,10 @@ extern BOOL NOCOMMON __lib_startup;
/****************************************************************************/ /****************************************************************************/
extern int __addition_overflows(ULONG x, ULONG y);
/****************************************************************************/
extern void NOCOMMON (*__alloca_trap)(void); extern void NOCOMMON (*__alloca_trap)(void);
/****************************************************************************/ /****************************************************************************/

View File

@ -58,6 +58,7 @@
unsigned long NOCOMMON __maximum_memory_allocated; unsigned long NOCOMMON __maximum_memory_allocated;
unsigned long NOCOMMON __current_memory_allocated; unsigned long NOCOMMON __current_memory_allocated;
unsigned long NOCOMMON __maximum_num_memory_chunks_allocated; unsigned long NOCOMMON __maximum_num_memory_chunks_allocated;
unsigned long NOCOMMON __current_num_memory_chunks_allocated; unsigned long NOCOMMON __current_num_memory_chunks_allocated;
@ -74,36 +75,59 @@ struct MinList NOCOMMON __memory_list;
/****************************************************************************/ /****************************************************************************/
void * /* Check if the sum of two unsigned 32-bit integers will be larger than what
__allocate_memory(size_t size,BOOL never_free,const char *debug_file_name UNUSED,int debug_line_number UNUSED) * 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; size_t allocation_size;
void * result = NULL; void * result = NULL;
size_t original_size UNUSED;
#if defined(UNIX_PATH_SEMANTICS) #if defined(UNIX_PATH_SEMANTICS)
size_t original_size;
{ {
original_size = size; original_size = size;
/* The libunix.a flavour accepts zero length memory allocations /* The libunix.a flavour of malloc() accepts zero-length
and quietly turns them into a pointer sized allocations. */ memory allocations and quietly turns these into
if(size == 0) pointer-sized allocations. */
size = sizeof(char *); if (size == 0)
size = sizeof(BYTE *);
} }
#endif /* UNIX_PATH_SEMANTICS */ #endif /* UNIX_PATH_SEMANTICS */
__memory_lock(); __memory_lock();
/* Zero length allocations are by default rejected. */ /* Zero length allocations are by default rejected. */
if(size == 0) if (size == 0)
{ {
__set_errno(EINVAL); __set_errno(EINVAL);
goto out; 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"); SHOWMSG("not enough free memory available to safely proceed with allocation");
goto out; 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( MALLOC_TAIL_SIZE > 0 && (MALLOC_TAIL_SIZE % 4) == 0 );
assert( (sizeof(*mn) % 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; allocation_size = sizeof(*mn) + MALLOC_HEAD_SIZE + size + MALLOC_TAIL_SIZE;
} }
#else #else
{ {
/* Round up allocation to a multiple of 32 bits. */ if (__addition_overflows(sizeof(*mn), size))
if((size & 3) != 0) {
size += 4 - (size & 3); SHOWMSG("integer overflow");
__set_errno(ENOMEM);
goto out;
}
allocation_size = sizeof(*mn) + size; allocation_size = sizeof(*mn) + size;
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
/* Integer overflow has occured? */ /* Round up allocation to a multiple of 8 bytes. */
if(size == 0 || allocation_size < size) if ((allocation_size % MEM_BLOCKSIZE) > 0)
{ {
__set_errno(ENOMEM); size_t padding;
goto out;
}
/* We reuse the MemoryNode.mn_Size field to mark padding = MEM_BLOCKSIZE - (allocation_size % MEM_BLOCKSIZE);
* allocations are not suitable for use with
* free() and realloc(). This limits allocation if (__addition_overflows(padding, allocation_size))
* sizes to a little less than 2 GBytes. {
*/ SHOWMSG("integer overflow");
if(allocation_size & MN_SIZE_NEVERFREE)
{ __set_errno(ENOMEM);
__set_errno(ENOMEM); goto out;
goto out; }
allocation_size += padding;
} }
#if defined(__USE_SLAB_ALLOCATOR) #if defined(__USE_SLAB_ALLOCATOR)
{ {
/* Are we using the slab allocator? */ /* Are we using the slab allocator? */
if(__slab_data.sd_InUse) if (__slab_data.sd_InUse)
{ {
mn = __slab_allocate(allocation_size); 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 else
{ {
if (__memory_pool != NULL) PROFILE_OFF();
{ mn = AllocMem(allocation_size, MEMF_ANY);
PROFILE_OFF(); PROFILE_ON();
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 */
}
} }
} }
#else #else
{ {
if(__memory_pool != NULL) if (__memory_pool != NULL)
{ {
PROFILE_OFF(); PROFILE_OFF();
mn = AllocPooled(__memory_pool,allocation_size); mn = AllocPooled(__memory_pool, allocation_size);
PROFILE_ON(); PROFILE_ON();
} }
else else
{ {
#ifdef __MEM_DEBUG PROFILE_OFF();
{ mn = AllocMem(allocation_size, MEMF_ANY);
PROFILE_OFF(); PROFILE_ON();
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 */
} }
} }
#endif /* __USE_SLAB_ALLOCATOR */ #endif /* __USE_SLAB_ALLOCATOR */
if(mn == NULL) if (mn == NULL)
{ {
SHOWMSG("not enough memory"); SHOWMSG("not enough memory");
goto out; goto out;
} }
mn->mn_Size = size; mn->mn_AllocationSize = allocation_size;
mn->mn_Flags = never_free ? MNF_NEVER_FREE : 0;
if(never_free)
SET_FLAG(mn->mn_Size, MN_SIZE_NEVERFREE);
__current_memory_allocated += allocation_size; __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; __maximum_memory_allocated = __current_memory_allocated;
__current_num_memory_chunks_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; __maximum_num_memory_chunks_allocated = __current_num_memory_chunks_allocated;
#ifdef __MEM_DEBUG #ifdef __MEM_DEBUG
{ {
char * head = (char *)(mn + 1); BYTE * head = (BYTE *)&mn[1];
char * body = head + MALLOC_HEAD_SIZE; BYTE * body = &head[MALLOC_HEAD_SIZE];
char * tail = body + size; BYTE * tail = &body[size];
AddTail((struct List *)&__memory_list,(struct Node *)mn);
mn->mn_AlreadyFree = FALSE; mn->mn_AlreadyFree = FALSE;
mn->mn_Allocation = body; mn->mn_Allocation = body;
mn->mn_AllocationSize = allocation_size; mn->mn_OriginalSize = size;
mn->mn_File = (char *)debug_file_name; mn->mn_File = (char *)debug_file_name;
mn->mn_Line = debug_line_number; mn->mn_Line = debug_line_number;
mn->mn_FreeFile = NULL; mn->mn_FreeFile = NULL;
mn->mn_FreeLine = 0; mn->mn_FreeLine = 0;
memset(head,MALLOC_HEAD_FILL,MALLOC_HEAD_SIZE); memset(head, MALLOC_HEAD_FILL, MALLOC_HEAD_SIZE);
memset(body,MALLOC_NEW_FILL,size); memset(body, MALLOC_NEW_FILL, size);
memset(tail,MALLOC_TAIL_FILL,MALLOC_TAIL_SIZE); memset(tail, MALLOC_TAIL_FILL, MALLOC_TAIL_SIZE);
#ifdef __MEM_DEBUG_LOG #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 */ #endif /* __MEM_DEBUG_LOG */
AddTail((struct List *)&__memory_list,(struct Node *)mn);
#ifdef __USE_MEM_TREES #ifdef __USE_MEM_TREES
{ {
__red_black_tree_insert(&__memory_tree,mn); __red_black_tree_insert(&__memory_tree, mn);
} }
#endif /* __USE_MEM_TREES */ #endif /* __USE_MEM_TREES */
@ -288,6 +280,22 @@ __allocate_memory(size_t size,BOOL never_free,const char *debug_file_name UNUSED
} }
#else #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]; result = &mn[1];
} }
#endif /* __MEM_DEBUG */ #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) #if defined(UNIX_PATH_SEMANTICS)
{ {
/* Set the zero length allocation contents to NULL. */ /* Set the zero length allocation contents to NULL. */
if(original_size == 0) if (original_size == 0 && size >= sizeof(BYTE *))
*(char **)result = NULL; *(BYTE **)result = NULL;
} }
#endif /* UNIX_PATH_SEMANTICS */ #endif /* UNIX_PATH_SEMANTICS */
assert( (((ULONG)result) & 3) == 0 ); assert( (((ULONG)result) & MEM_BLOCKMASK) == 0 );
out: out:
#ifdef __MEM_DEBUG_LOG #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 */ #endif /* __MEM_DEBUG_LOG */
__memory_unlock(); __memory_unlock();
return(result); return result;
} }
/****************************************************************************/ /****************************************************************************/
__static void * __static void *
__malloc(size_t size,const char * file,int line) __malloc(size_t size, const char * file, int line)
{ {
void * result = NULL; void * result = NULL;
__memory_lock(); __memory_lock();
/* Try to get rid of now unused memory. */ /* Try to get rid of now unused memory. */
if(__alloca_cleanup != NULL) if (__alloca_cleanup != NULL)
(*__alloca_cleanup)(file,line); (*__alloca_cleanup)(file, line);
__memory_unlock(); __memory_unlock();
#ifdef __MEM_DEBUG #ifdef __MEM_DEBUG
{ {
/*if((rand() % 16) == 0) /*if ((rand() % 16) == 0)
__check_memory_allocations(file,line);*/ __check_memory_allocations(file,line);*/
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
/* Allocate memory which can be put through realloc() and free(). */ /* 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; void * result;
result = __malloc(size,NULL,0); result = __malloc(size, NULL, 0);
return(result); return(result);
} }
@ -375,7 +383,7 @@ __memory_lock(void)
{ {
PROFILE_OFF(); PROFILE_OFF();
if(memory_semaphore != NULL) if (memory_semaphore != NULL)
ObtainSemaphore(memory_semaphore); ObtainSemaphore(memory_semaphore);
PROFILE_ON(); PROFILE_ON();
@ -388,7 +396,7 @@ __memory_unlock(void)
{ {
PROFILE_OFF(); PROFILE_OFF();
if(memory_semaphore != NULL) if (memory_semaphore != NULL)
ReleaseSemaphore(memory_semaphore); ReleaseSemaphore(memory_semaphore);
PROFILE_ON(); PROFILE_ON();
@ -404,30 +412,34 @@ STDLIB_DESTRUCTOR(stdlib_memory_exit)
{ {
ENTER(); 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 #ifdef __MEM_DEBUG
{ {
kprintf("[%s] %ld bytes still allocated upon exit, maximum of %ld bytes allocated at a time.\n", 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", 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; __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) #if defined(__USE_MEM_TREES)
{ {
/* This must remain empty. */
__initialize_red_black_tree(&__memory_tree); __initialize_red_black_tree(&__memory_tree);
} }
#endif /* __USE_MEM_TREES */ #endif /* __USE_MEM_TREES */
@ -439,39 +451,36 @@ STDLIB_DESTRUCTOR(stdlib_memory_exit)
/* Is the slab memory allocator enabled? */ /* Is the slab memory allocator enabled? */
if (__slab_data.sd_InUse) if (__slab_data.sd_InUse)
{ {
/* Just release the memory. */
__slab_exit(); __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); while (NOT IsMinListEmpty(&__memory_list))
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head, __FILE__, __LINE__);
DeletePool(__memory_pool);
__memory_pool = NULL;
} }
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, NULL, 0);
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 */
} }
#endif /* __MEM_DEBUG */
} }
} }
#else #else
{ {
if (__memory_pool != NULL) if (__memory_pool != NULL)
{ {
NewList((struct List *)&__memory_list);
DeletePool(__memory_pool); DeletePool(__memory_pool);
__memory_pool = NULL; __memory_pool = NULL;
} }
@ -479,19 +488,22 @@ STDLIB_DESTRUCTOR(stdlib_memory_exit)
{ {
#ifdef __MEM_DEBUG #ifdef __MEM_DEBUG
{ {
while(NOT IsMinListEmpty(&__memory_list)) while (NOT IsMinListEmpty(&__memory_list))
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head,__FILE__,__LINE__); __free_memory_node((struct MemoryNode *)__memory_list.mlh_Head, __FILE__, __LINE__);
} }
#else #else
{ {
while(NOT IsMinListEmpty(&__memory_list)) while (NOT IsMinListEmpty(&__memory_list))
__free_memory_node((struct MemoryNode *)__memory_list.mlh_Head,NULL,0); __free_memory_node((struct MemoryNode *)__memory_list.mlh_Head, NULL, 0);
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
} }
} }
#endif /* __USE_SLAB_ALLOCATOR */ #endif /* __USE_SLAB_ALLOCATOR */
/* The list of memory allocations must remain empty. */
NewList((struct List *)&__memory_list);
#if defined(__THREAD_SAFE) #if defined(__THREAD_SAFE)
{ {
__delete_semaphore(memory_semaphore); __delete_semaphore(memory_semaphore);
@ -510,13 +522,7 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
ENTER(); ENTER();
#if defined(__THREAD_SAFE) NewList((struct List *)&__memory_list);
{
memory_semaphore = __create_semaphore();
if(memory_semaphore == NULL)
goto out;
}
#endif /* __THREAD_SAFE */
#if defined(__USE_MEM_TREES) && defined(__MEM_DEBUG) #if defined(__USE_MEM_TREES) && defined(__MEM_DEBUG)
{ {
@ -524,27 +530,38 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
} }
#endif /* __USE_MEM_TREES && __MEM_DEBUG */ #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 */ /* ZZZ this is just for the purpose of testing */
#if DEBUG #if DEBUG
{ {
TEXT slab_size_var[20]; 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; __slab_max_size = (size_t)value;
} }
} }
#endif #endif
SHOWVALUE(__slab_max_size);
/* Enable the slab memory allocator? */ /* Enable the slab memory allocator? */
if(__slab_max_size > 0) if (__slab_max_size > 0)
{ {
__slab_init(__slab_max_size); __slab_init(__slab_max_size);
} }
@ -552,15 +569,27 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
{ {
#if defined(__amigaos4__) #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 #else
{ {
/* There is no support for memory pools in the operating system /* There is no support for memory pools in the operating system
* prior to Kickstart 3.0 (V39). * prior to Kickstart 3.0 (V39).
*/ */
if(((struct Library *)SysBase)->lib_Version >= 39) if (((struct Library *)SysBase)->lib_Version >= 39)
__memory_pool = CreatePool(MEMF_ANY,(ULONG)__default_pool_size,(ULONG)__default_puddle_size); {
__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 /* __amigaos4__ */
} }
@ -569,19 +598,31 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
{ {
#if defined(__amigaos4__) #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 #else
{ {
/* There is no support for memory pools in the operating system /* There is no support for memory pools in the operating system
* prior to Kickstart 3.0 (V39). * prior to Kickstart 3.0 (V39).
*/ */
if(((struct Library *)SysBase)->lib_Version >= 39) if (((struct Library *)SysBase)->lib_Version >= 39)
__memory_pool = CreatePool(MEMF_ANY,(ULONG)__default_pool_size,(ULONG)__default_puddle_size); {
__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 /* __amigaos4__ */
} }
#endif /* __USE_SLAB_ALLOCATOR) */ #endif /* __USE_SLAB_ALLOCATOR */
success = TRUE; success = TRUE;
@ -590,7 +631,7 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
SHOWVALUE(success); SHOWVALUE(success);
LEAVE(); LEAVE();
if(success) if (success)
CONSTRUCTOR_SUCCEED(); CONSTRUCTOR_SUCCEED();
else else
CONSTRUCTOR_FAIL(); CONSTRUCTOR_FAIL();

36
library/stdlib_memory.h Normal file → Executable file
View File

@ -63,7 +63,7 @@
/* /*
* Uncomment this to enable the slab allocator. * 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 * cannot be released with free() or used with realloc(). This
* flag is set by alloca(). * 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 /* Memory allocations are remembered by this tracking data structure.
* the "never free" flag altogether. * 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 struct MemoryNode
{ {
#ifdef __MEM_DEBUG
struct MinNode mn_MinNode; struct MinNode mn_MinNode;
UBYTE mn_AlreadyFree; ULONG mn_AllocationSize;
UBYTE mn_Pad0[3]; ULONG mn_Flags;
#ifdef __MEM_DEBUG
void * mn_Allocation; void * mn_Allocation;
size_t mn_AllocationSize; size_t mn_OriginalSize;
char * mn_FreeFile; char * mn_FreeFile;
int mn_FreeLine; int mn_FreeLine;
@ -178,17 +177,18 @@ struct MemoryNode
char * mn_File; char * mn_File;
int mn_Line; int mn_Line;
UBYTE mn_AlreadyFree;
UBYTE mn_Pad1[7];
#ifdef __USE_MEM_TREES #ifdef __USE_MEM_TREES
struct MemoryNode * mn_Left; struct MemoryNode * mn_Left;
struct MemoryNode * mn_Right; struct MemoryNode * mn_Right;
struct MemoryNode * mn_Parent; struct MemoryNode * mn_Parent;
UBYTE mn_IsRed; UBYTE mn_IsRed;
UBYTE mn_Pad1[3]; UBYTE mn_Pad2[3];
#endif /* __USE_MEM_TREES */ #endif /* __USE_MEM_TREES */
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
ULONG mn_Size;
}; };
#ifdef __USE_MEM_TREES #ifdef __USE_MEM_TREES
@ -247,12 +247,14 @@ struct SlabNode
}; };
/* Memory allocations which are not part of a slab are /* 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 SlabSingleAllocation
{ {
struct MinNode ssa_MinNode; struct MinNode ssa_MinNode;
ULONG ssa_Size; ULONG ssa_Size;
ULONG ssa_Pad;
}; };
/* This is the global bookkeeping information for managing /* 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, * 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 * 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 * which are 8 bytes in size, sd_Slabs[4] is for 16 byte
* chunks, etc. The minimum chunk size is 8, which is why * chunks, etc. The minimum chunk size is 16, which is why
* lists 0..2 are not used. Currently, there is an upper limit * 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 * of 2^17 bytes per chunk, but you should not be using slab
* chunks much larger than 4096 bytes. * chunks much larger than 4096 bytes.
*/ */

View File

@ -50,7 +50,7 @@
/****************************************************************************/ /****************************************************************************/
__static void * __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; void * result = NULL;
BOOL locked = FALSE; BOOL locked = FALSE;
@ -62,23 +62,24 @@ __realloc(void *ptr,size_t size,const char * file,int line)
assert( (int)size >= 0 ); 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 #ifndef UNIX_PATH_SEMANTICS
else if (size == 0) else if (size == 0)
{ {
D(("calling free(0x%08lx)",ptr)); D(("calling free(0x%08lx)",ptr));
__free(ptr,file,line); __free(ptr, file, line);
} }
#endif /* UNIX_PATH_SEMANTICS */ #endif /* UNIX_PATH_SEMANTICS */
else else
{ {
size_t old_size; size_t new_allocation_size UNUSED;
size_t old_allocation_size;
struct MemoryNode * mn; struct MemoryNode * mn;
BOOL reallocate; BOOL reallocate;
@ -92,16 +93,14 @@ __realloc(void *ptr,size_t size,const char * file,int line)
#ifdef __MEM_DEBUG #ifdef __MEM_DEBUG
{ {
/* If we managed to find the memory allocation, /* Quit if we failed to find the memory allocation. */
reallocate it. */ if (mn == NULL)
if(mn == NULL)
{ {
SHOWMSG("allocation not found"); 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; goto out;
} }
} }
@ -111,70 +110,114 @@ __realloc(void *ptr,size_t size,const char * file,int line)
} }
#endif /* __MEM_DEBUG */ #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"); SHOWMSG("cannot free this chunk");
goto out; 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 old_allocation_size = mn->mn_AllocationSize;
has really changed. */
/* If the memory debug option is enabled, just check if
* requested allocation size has changed.
*/
#if defined(__MEM_DEBUG) #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 #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. */ /* Allocation size should grow. */
reallocate = TRUE; reallocate = TRUE;
} }
else else if (new_allocation_size < old_allocation_size)
{ {
/* Optimization: If the block size shrinks by less than half the /* Optimization: If the block size shrinks by less than half the
original allocation size, do not reallocate the original allocation size, do not reallocate the
block and do not copy over the contents of the old block. */
allocation. We also take into account that the reallocate = (size <= old_allocation_size / 2);
actual size of the allocation is affected by a }
certain operating system imposed granularity. */ else
reallocate = (size < old_size && size <= old_size / 2); {
reallocate = FALSE;
} }
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
if(reallocate) if (reallocate)
{ {
void * new_ptr; 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 /* We allocate the new memory chunk before we
attempt to replace the old. */ attempt to replace the old one. */
new_ptr = __malloc(size,file,line); new_ptr = __malloc(size, file, line);
if(new_ptr == NULL) if (new_ptr == NULL)
{ {
SHOWMSG("could not reallocate memory"); SHOWMSG("could not reallocate memory");
goto out; goto out;
} }
/* Copy the contents of the old allocation to the new buffer. */ /* With memory debugging enabled, the size of the allocation made
if(size > old_size) * will use the requested and not the rounded size of the
size = old_size; * 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 /* Free the old allocation. Since we already know which memory
node is associated with it, we don't call __free() here. */ 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; result = new_ptr;
} }
else 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. */ /* No change in size. */
result = ptr; result = ptr;
@ -183,14 +226,14 @@ __realloc(void *ptr,size_t size,const char * file,int line)
out: out:
if(locked) if (locked)
__memory_unlock(); __memory_unlock();
if(result == NULL) if (result == NULL)
SHOWMSG("ouch! realloc failed"); SHOWMSG("ouch! realloc failed");
RETURN(result); RETURN(result);
return(result); return result;
} }
/****************************************************************************/ /****************************************************************************/

1144
library/stdlib_slab.c Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,7 @@
/****************************************************************************/ /****************************************************************************/
static const char * error_table[EILSEQ - EPERM + 1] = static const char * error_table[ENOTSUP - EPERM + 1] =
{ {
"Operation not permitted", "Operation not permitted",
"No such file or directory", "No such file or directory",
@ -127,7 +127,8 @@ static const char * error_table[EILSEQ - EPERM + 1] =
"Identifier removed", "Identifier removed",
"No message of the desired type.", "No message of the desired type.",
"Value too large to be stored in data 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; const char * str;
size_t len; size_t len;
if(number < EPERM || number > EILSEQ) if(number < EPERM || number > ENOTSUP)
{ {
__set_errno(EINVAL); __set_errno(EINVAL);
goto out; goto out;

151
library/time_mktime.c Normal file → Executable file
View 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 * :ts=4
* *
@ -52,9 +52,10 @@ mktime(struct tm *tm)
{ {
DECLARE_UTILITYBASE(); DECLARE_UTILITYBASE();
struct ClockData clock_data; struct ClockData clock_data;
ULONG seconds, delta; ULONG seconds;
time_t result = (time_t)-1; time_t result = (time_t)-1;
int max_month_days; LONG combined_seconds;
int month, year;
ENTER(); ENTER();
@ -73,116 +74,63 @@ mktime(struct tm *tm)
} }
#endif /* CHECK_FOR_NULL_POINTERS */ #endif /* CHECK_FOR_NULL_POINTERS */
/* The month must be valid. */ /* Normalize the year and month. */
if(tm->tm_mon < 0 || tm->tm_mon > 11) year = tm->tm_year + 1900;
month = tm->tm_mon + 1;
if(month < 0 || month > 12)
{ {
SHOWVALUE(tm->tm_mon); int y;
SHOWMSG("invalid month");
goto out; y = month / 12;
month -= y * 12;
year += y;
} }
/* The day of the month must be valid. */ if(month < 1)
if(tm->tm_mday < 1 || tm->tm_mday > 31)
{ {
SHOWVALUE(tm->tm_mday); month += 12;
SHOWMSG("invalid day of month"); year -= 1;
goto out;
} }
/* The year must be valid. */ /* The year must be valid. Amiga time begins with January 1st, 1978. */
if(tm->tm_year < 78) if(year < 1978)
{ {
SHOWVALUE(tm->tm_year); SHOWVALUE(year);
SHOWMSG("invalid year"); SHOWMSG("invalid year");
goto out; goto out;
} }
/* Is this the month of February? */ /* Convert the first day of the month in the given year
if(tm->tm_mon == 1) into the corresponding number of seconds. */
{ memset(&clock_data, 0, sizeof(clock_data));
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;
clock_data.mday = 1; 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(); __locale_lock();
@ -193,10 +141,13 @@ mktime(struct tm *tm)
__locale_unlock(); __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. */ AmigaOS epochs, which differ by 8 years. */
result = seconds + UNIX_TIME_OFFSET; result = seconds + UNIX_TIME_OFFSET;
/* Finally, normalize the provided time and date information. */
localtime_r(&result, tm);
out: out:
RETURN(result); RETURN(result);

View File

@ -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 path_name_nti;
struct name_translation_info buffer_nti; struct name_translation_info buffer_nti;
#endif /* UNIX_PATH_SEMANTICS */ #endif /* UNIX_PATH_SEMANTICS */
D_S(struct bcpl_name, bname);
const size_t name_size = sizeof(bname->name);
BPTR lock = ZERO; BPTR lock = ZERO;
int result = ERROR; 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(); ENTER();
@ -70,12 +80,12 @@ readlink(const char * path_name, char * buffer, int buffer_size)
assert( path_name != NULL && buffer != NULL ); assert( path_name != NULL && buffer != NULL );
if(__check_abort_enabled) if (__check_abort_enabled)
__check_abort(); __check_abort();
#if defined(CHECK_FOR_NULL_POINTERS) #if defined(CHECK_FOR_NULL_POINTERS)
{ {
if(path_name == NULL || buffer == NULL) if (path_name == NULL || buffer == NULL)
{ {
SHOWSTRING("invalid parameters"); SHOWSTRING("invalid parameters");
@ -87,9 +97,9 @@ readlink(const char * path_name, char * buffer, int buffer_size)
#if defined(UNIX_PATH_SEMANTICS) #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"); SHOWMSG("no name given");
@ -97,53 +107,144 @@ readlink(const char * path_name, char * buffer, int buffer_size)
goto out; 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; goto out;
} }
} }
#endif /* UNIX_PATH_SEMANTICS */ #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(); 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(); 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); SHOWMSG("new_name too long");
goto out;
} SetIoErr(ERROR_NO_FREE_STORE);
else if (target_length <= 0) /* No a soft-link. */
{
__set_errno(__translate_io_error_to_errno(IoErr()));
goto out; 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) if (__unix_path_semantics)
goto out; {
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: out:
PROFILE_OFF(); PROFILE_OFF();
if (dvp != NULL)
FreeDeviceProc(dvp);
UnLock(lock); UnLock(lock);
PROFILE_ON(); PROFILE_ON();
RETURN(result); RETURN(result);
return(result);
return result;
} }

View File

@ -41,7 +41,7 @@
/****************************************************************************/ /****************************************************************************/
void int
usleep(unsigned long microseconds) usleep(unsigned long microseconds)
{ {
ENTER(); ENTER();
@ -51,4 +51,6 @@ usleep(unsigned long microseconds)
__time_delay(0,microseconds); __time_delay(0,microseconds);
LEAVE(); LEAVE();
return 0;
} }

View File

@ -334,7 +334,7 @@ __wildcard_expand_init(void)
} }
else if (rc != OK) else if (rc != OK)
{ {
/* Some error occured. */ /* Some error occurred. */
error = EIO; error = EIO;
break; break;
} }