From 95a35cd0bfbdf7d1e68ef20db3557009b090b145 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 23 Aug 2020 17:21:08 -0700 Subject: [PATCH] [libc] Bring libc in-tree Moving libc from its separate repo into this one, minor resulting build fixes, and a hacky way to add -I for libc headers in builds. --- modules.yaml | 178 + scripts/templates/target.default.j2 | 5 + scripts/templates/target.host.j2 | 1 + scripts/templates/target.user.j2 | 4 +- src/kernel/console.h | 3 +- src/kernel/memory_bootstrap.cpp | 1 - src/kernel/page_manager.cpp | 2 - src/kernel/scheduler.cpp | 2 + src/libraries/kutil/include/kutil/avl_tree.h | 3 +- src/libraries/kutil/include/kutil/vector.h | 3 +- src/libraries/libc/arch/x86_64/_Exit.s | 5 + src/libraries/libc/ctype/isalnum.c | 13 + src/libraries/libc/ctype/isalpha.c | 13 + src/libraries/libc/ctype/isblank.c | 13 + src/libraries/libc/ctype/iscntrl.c | 13 + src/libraries/libc/ctype/isdigit.c | 13 + src/libraries/libc/ctype/isgraph.c | 13 + src/libraries/libc/ctype/islower.c | 13 + src/libraries/libc/ctype/isprint.c | 14 + src/libraries/libc/ctype/ispunct.c | 13 + src/libraries/libc/ctype/isspace.c | 13 + src/libraries/libc/ctype/isupper.c | 13 + src/libraries/libc/ctype/isxdigit.c | 15 + src/libraries/libc/ctype/tolower.c | 13 + src/libraries/libc/ctype/toupper.c | 13 + src/libraries/libc/include/assert.h | 29 + src/libraries/libc/include/ctype.h | 96 + src/libraries/libc/include/errno.h | 15 + src/libraries/libc/include/float.h | 71 + src/libraries/libc/include/inttypes.h | 250 + src/libraries/libc/include/iso646.h | 18 + src/libraries/libc/include/j6libc/aux.h | 57 + src/libraries/libc/include/j6libc/config.h | 336 + src/libraries/libc/include/j6libc/cpp.h | 9 + src/libraries/libc/include/j6libc/glue.h | 67 + src/libraries/libc/include/j6libc/int.h | 347 + .../libc/include/j6libc/int_widths.h | 57 + .../libc/include/j6libc/max_align_t.h | 14 + src/libraries/libc/include/j6libc/null.h | 6 + .../libc/include/j6libc/sig_atomic_t.h | 14 + src/libraries/libc/include/j6libc/size_t.h | 6 + src/libraries/libc/include/j6libc/wchar_t.h | 6 + src/libraries/libc/include/j6libc/wctype_t.h | 6 + src/libraries/libc/include/j6libc/wint_t.h | 5 + src/libraries/libc/include/limits.h | 36 + src/libraries/libc/include/locale.h | 96 + src/libraries/libc/include/signal.h | 86 + src/libraries/libc/include/stdalign.h | 12 + src/libraries/libc/include/stdarg.h | 15 + src/libraries/libc/include/stdbool.h | 14 + src/libraries/libc/include/stddef.h | 22 + src/libraries/libc/include/stdint.h | 151 + src/libraries/libc/include/stdio.h | 779 ++ src/libraries/libc/include/stdlib.h | 232 + src/libraries/libc/include/stdnoreturn.h | 8 + src/libraries/libc/include/string.h | 178 + src/libraries/libc/include/time.h | 105 + src/libraries/libc/include/wchar.h | 91 + src/libraries/libc/include/wctype.h | 138 + src/libraries/libc/inttypes/imaxabs.c | 12 + src/libraries/libc/inttypes/imaxdiv.c | 15 + src/libraries/libc/inttypes/strtoimax.c | 27 + src/libraries/libc/inttypes/strtoumax.c | 20 + src/libraries/libc/j6libc/Readme.txt | 10 + src/libraries/libc/j6libc/allocpages.c | 16 + src/libraries/libc/j6libc/assert.c | 19 + src/libraries/libc/j6libc/atomax.c | 25 + src/libraries/libc/j6libc/close.c | 16 + src/libraries/libc/j6libc/closeall.c | 21 + src/libraries/libc/j6libc/digits.c | 12 + src/libraries/libc/j6libc/errno.c | 14 + src/libraries/libc/j6libc/filemode.c | 54 + src/libraries/libc/j6libc/fillbuffer.c | 19 + src/libraries/libc/j6libc/flushbuffer.c | 19 + src/libraries/libc/j6libc/is_leap.c | 14 + src/libraries/libc/j6libc/load_lc_collate.c | 44 + src/libraries/libc/j6libc/load_lc_ctype.c | 74 + src/libraries/libc/j6libc/load_lc_messages.c | 60 + src/libraries/libc/j6libc/load_lc_monetary.c | 90 + src/libraries/libc/j6libc/load_lc_numeric.c | 56 + src/libraries/libc/j6libc/load_lc_time.c | 79 + src/libraries/libc/j6libc/load_lines.c | 48 + src/libraries/libc/j6libc/open.c | 17 + src/libraries/libc/j6libc/prepread.c | 34 + src/libraries/libc/j6libc/prepwrite.c | 25 + src/libraries/libc/j6libc/print.c | 624 ++ src/libraries/libc/j6libc/rename.c | 18 + src/libraries/libc/j6libc/sbrk.c | 4 + src/libraries/libc/j6libc/scan.c | 595 ++ src/libraries/libc/j6libc/seed.c | 7 + src/libraries/libc/j6libc/seek.c | 18 + src/libraries/libc/j6libc/stdinit.c | 412 ++ src/libraries/libc/j6libc/strtox_main.c | 42 + src/libraries/libc/j6libc/strtox_prelim.c | 50 + src/libraries/libc/locale/localeconv.c | 12 + src/libraries/libc/locale/setlocale.c | 242 + src/libraries/libc/signal/raise.c | 63 + src/libraries/libc/signal/signal.c | 58 + src/libraries/libc/stdio/clearerr.c | 12 + src/libraries/libc/stdio/fclose.c | 67 + src/libraries/libc/stdio/feof.c | 12 + src/libraries/libc/stdio/ferror.c | 12 + src/libraries/libc/stdio/fflush.c | 36 + src/libraries/libc/stdio/fgetc.c | 21 + src/libraries/libc/stdio/fgetpos.c | 15 + src/libraries/libc/stdio/fgets.c | 42 + src/libraries/libc/stdio/fopen.c | 70 + src/libraries/libc/stdio/fprintf.c | 18 + src/libraries/libc/stdio/fputc.c | 30 + src/libraries/libc/stdio/fputs.c | 42 + src/libraries/libc/stdio/fread.c | 36 + src/libraries/libc/stdio/freopen.c | 70 + src/libraries/libc/stdio/fscanf.c | 18 + src/libraries/libc/stdio/fseek.c | 25 + src/libraries/libc/stdio/fsetpos.c | 26 + src/libraries/libc/stdio/ftell.c | 38 + src/libraries/libc/stdio/fwrite.c | 76 + src/libraries/libc/stdio/getc.c | 12 + src/libraries/libc/stdio/getchar.c | 12 + src/libraries/libc/stdio/perror.c | 27 + src/libraries/libc/stdio/printf.c | 18 + src/libraries/libc/stdio/putc.c | 12 + src/libraries/libc/stdio/putchar.c | 12 + src/libraries/libc/stdio/puts.c | 39 + src/libraries/libc/stdio/remove.c | 29 + src/libraries/libc/stdio/rename.c | 27 + src/libraries/libc/stdio/rewind.c | 13 + src/libraries/libc/stdio/scanf.c | 15 + src/libraries/libc/stdio/setbuf.c | 19 + src/libraries/libc/stdio/setvbuf.c | 64 + src/libraries/libc/stdio/snprintf.c | 18 + src/libraries/libc/stdio/sprintf.c | 19 + src/libraries/libc/stdio/sscanf.c | 18 + src/libraries/libc/stdio/tmpfile.c | 22 + src/libraries/libc/stdio/tmpnam.c | 23 + src/libraries/libc/stdio/ungetc.c | 16 + src/libraries/libc/stdio/vfprintf.c | 44 + src/libraries/libc/stdio/vfscanf.c | 82 + src/libraries/libc/stdio/vprintf.c | 13 + src/libraries/libc/stdio/vscanf.c | 13 + src/libraries/libc/stdio/vsnprintf.c | 50 + src/libraries/libc/stdio/vsprintf.c | 14 + src/libraries/libc/stdio/vsscanf.c | 80 + src/libraries/libc/stdlib/_Exit.c | 17 + src/libraries/libc/stdlib/abort.c | 14 + src/libraries/libc/stdlib/abs.c | 12 + src/libraries/libc/stdlib/atexit.c | 23 + src/libraries/libc/stdlib/atoi.c | 12 + src/libraries/libc/stdlib/atol.c | 12 + src/libraries/libc/stdlib/atoll.c | 12 + src/libraries/libc/stdlib/bsearch.c | 33 + src/libraries/libc/stdlib/div.c | 16 + src/libraries/libc/stdlib/exit.c | 28 + src/libraries/libc/stdlib/getenv.c | 27 + src/libraries/libc/stdlib/labs.c | 12 + src/libraries/libc/stdlib/ldiv.c | 16 + src/libraries/libc/stdlib/llabs.c | 12 + src/libraries/libc/stdlib/lldiv.c | 16 + src/libraries/libc/stdlib/malloc.c | 6270 +++++++++++++++++ src/libraries/libc/stdlib/qsort.c | 124 + src/libraries/libc/stdlib/rand.c | 13 + src/libraries/libc/stdlib/srand.c | 12 + src/libraries/libc/stdlib/strtol.c | 27 + src/libraries/libc/stdlib/strtoll.c | 27 + src/libraries/libc/stdlib/strtoul.c | 20 + src/libraries/libc/stdlib/strtoull.c | 20 + src/libraries/libc/stdlib/system.c | 34 + src/libraries/libc/string/memchr.c | 21 + src/libraries/libc/string/memcmp.c | 23 + src/libraries/libc/string/memcpy.c | 18 + src/libraries/libc/string/memmove.c | 30 + src/libraries/libc/string/memset.c | 17 + src/libraries/libc/string/strcat.c | 18 + src/libraries/libc/string/strchr.c | 19 + src/libraries/libc/string/strcmp.c | 17 + src/libraries/libc/string/strcoll.c | 23 + src/libraries/libc/string/strcpy.c | 14 + src/libraries/libc/string/strcspn.c | 26 + src/libraries/libc/string/strerror.c | 21 + src/libraries/libc/string/strlen.c | 17 + src/libraries/libc/string/strncat.c | 25 + src/libraries/libc/string/strncmp.c | 25 + src/libraries/libc/string/strncpy.c | 26 + src/libraries/libc/string/strpbrk.c | 26 + src/libraries/libc/string/strrchr.c | 21 + src/libraries/libc/string/strspn.c | 31 + src/libraries/libc/string/strstr.c | 29 + src/libraries/libc/string/strtok.c | 69 + src/libraries/libc/string/strxfrm.c | 23 + src/libraries/libc/tests/_PDCLIB_iotest.h | 169 + src/libraries/libc/tests/_PDCLIB_test.h | 101 + src/libraries/libc/tests/main.c | 32 + src/libraries/libc/tests/printf_testcases.h | 444 ++ src/libraries/libc/tests/scanf_testcases.h | 96 + src/libraries/libc/tests/test_assert.c | 50 + src/libraries/libc/tests/test_ctype.c | 186 + src/libraries/libc/tests/test_internal.c | 724 ++ src/libraries/libc/tests/test_inttypes.c | 194 + src/libraries/libc/tests/test_locale.c | 7 + src/libraries/libc/tests/test_signal.c | 49 + src/libraries/libc/tests/test_stdarg.c | 67 + src/libraries/libc/tests/test_stdio.c | 649 ++ src/libraries/libc/tests/test_stdlib.c | 515 ++ src/libraries/libc/tests/test_string.c | 386 + src/libraries/libc/tests/test_time.c | 1167 +++ src/libraries/libc/tests/testfile.txt | Bin 0 -> 532 bytes src/libraries/libc/time/asctime.c | 12 + src/libraries/libc/time/clock.c | 12 + src/libraries/libc/time/ctime.c | 12 + src/libraries/libc/time/difftime.c | 56 + src/libraries/libc/time/gmtime.c | 12 + src/libraries/libc/time/localtime.c | 12 + src/libraries/libc/time/mktime.c | 12 + src/libraries/libc/time/strftime.c | 582 ++ src/libraries/libc/time/time.c | 13 + src/libraries/libc/time/timespec_get.c | 12 + 216 files changed, 21033 insertions(+), 12 deletions(-) create mode 100644 src/libraries/libc/arch/x86_64/_Exit.s create mode 100644 src/libraries/libc/ctype/isalnum.c create mode 100644 src/libraries/libc/ctype/isalpha.c create mode 100644 src/libraries/libc/ctype/isblank.c create mode 100644 src/libraries/libc/ctype/iscntrl.c create mode 100644 src/libraries/libc/ctype/isdigit.c create mode 100644 src/libraries/libc/ctype/isgraph.c create mode 100644 src/libraries/libc/ctype/islower.c create mode 100644 src/libraries/libc/ctype/isprint.c create mode 100644 src/libraries/libc/ctype/ispunct.c create mode 100644 src/libraries/libc/ctype/isspace.c create mode 100644 src/libraries/libc/ctype/isupper.c create mode 100644 src/libraries/libc/ctype/isxdigit.c create mode 100644 src/libraries/libc/ctype/tolower.c create mode 100644 src/libraries/libc/ctype/toupper.c create mode 100644 src/libraries/libc/include/assert.h create mode 100644 src/libraries/libc/include/ctype.h create mode 100644 src/libraries/libc/include/errno.h create mode 100644 src/libraries/libc/include/float.h create mode 100644 src/libraries/libc/include/inttypes.h create mode 100644 src/libraries/libc/include/iso646.h create mode 100644 src/libraries/libc/include/j6libc/aux.h create mode 100644 src/libraries/libc/include/j6libc/config.h create mode 100644 src/libraries/libc/include/j6libc/cpp.h create mode 100644 src/libraries/libc/include/j6libc/glue.h create mode 100644 src/libraries/libc/include/j6libc/int.h create mode 100644 src/libraries/libc/include/j6libc/int_widths.h create mode 100644 src/libraries/libc/include/j6libc/max_align_t.h create mode 100644 src/libraries/libc/include/j6libc/null.h create mode 100644 src/libraries/libc/include/j6libc/sig_atomic_t.h create mode 100644 src/libraries/libc/include/j6libc/size_t.h create mode 100644 src/libraries/libc/include/j6libc/wchar_t.h create mode 100644 src/libraries/libc/include/j6libc/wctype_t.h create mode 100644 src/libraries/libc/include/j6libc/wint_t.h create mode 100644 src/libraries/libc/include/limits.h create mode 100644 src/libraries/libc/include/locale.h create mode 100644 src/libraries/libc/include/signal.h create mode 100644 src/libraries/libc/include/stdalign.h create mode 100644 src/libraries/libc/include/stdarg.h create mode 100644 src/libraries/libc/include/stdbool.h create mode 100644 src/libraries/libc/include/stddef.h create mode 100644 src/libraries/libc/include/stdint.h create mode 100644 src/libraries/libc/include/stdio.h create mode 100644 src/libraries/libc/include/stdlib.h create mode 100644 src/libraries/libc/include/stdnoreturn.h create mode 100644 src/libraries/libc/include/string.h create mode 100644 src/libraries/libc/include/time.h create mode 100644 src/libraries/libc/include/wchar.h create mode 100644 src/libraries/libc/include/wctype.h create mode 100644 src/libraries/libc/inttypes/imaxabs.c create mode 100644 src/libraries/libc/inttypes/imaxdiv.c create mode 100644 src/libraries/libc/inttypes/strtoimax.c create mode 100644 src/libraries/libc/inttypes/strtoumax.c create mode 100644 src/libraries/libc/j6libc/Readme.txt create mode 100644 src/libraries/libc/j6libc/allocpages.c create mode 100644 src/libraries/libc/j6libc/assert.c create mode 100644 src/libraries/libc/j6libc/atomax.c create mode 100644 src/libraries/libc/j6libc/close.c create mode 100644 src/libraries/libc/j6libc/closeall.c create mode 100644 src/libraries/libc/j6libc/digits.c create mode 100644 src/libraries/libc/j6libc/errno.c create mode 100644 src/libraries/libc/j6libc/filemode.c create mode 100644 src/libraries/libc/j6libc/fillbuffer.c create mode 100644 src/libraries/libc/j6libc/flushbuffer.c create mode 100644 src/libraries/libc/j6libc/is_leap.c create mode 100644 src/libraries/libc/j6libc/load_lc_collate.c create mode 100644 src/libraries/libc/j6libc/load_lc_ctype.c create mode 100644 src/libraries/libc/j6libc/load_lc_messages.c create mode 100644 src/libraries/libc/j6libc/load_lc_monetary.c create mode 100644 src/libraries/libc/j6libc/load_lc_numeric.c create mode 100644 src/libraries/libc/j6libc/load_lc_time.c create mode 100644 src/libraries/libc/j6libc/load_lines.c create mode 100644 src/libraries/libc/j6libc/open.c create mode 100644 src/libraries/libc/j6libc/prepread.c create mode 100644 src/libraries/libc/j6libc/prepwrite.c create mode 100644 src/libraries/libc/j6libc/print.c create mode 100644 src/libraries/libc/j6libc/rename.c create mode 100644 src/libraries/libc/j6libc/sbrk.c create mode 100644 src/libraries/libc/j6libc/scan.c create mode 100644 src/libraries/libc/j6libc/seed.c create mode 100644 src/libraries/libc/j6libc/seek.c create mode 100644 src/libraries/libc/j6libc/stdinit.c create mode 100644 src/libraries/libc/j6libc/strtox_main.c create mode 100644 src/libraries/libc/j6libc/strtox_prelim.c create mode 100644 src/libraries/libc/locale/localeconv.c create mode 100644 src/libraries/libc/locale/setlocale.c create mode 100644 src/libraries/libc/signal/raise.c create mode 100644 src/libraries/libc/signal/signal.c create mode 100644 src/libraries/libc/stdio/clearerr.c create mode 100644 src/libraries/libc/stdio/fclose.c create mode 100644 src/libraries/libc/stdio/feof.c create mode 100644 src/libraries/libc/stdio/ferror.c create mode 100644 src/libraries/libc/stdio/fflush.c create mode 100644 src/libraries/libc/stdio/fgetc.c create mode 100644 src/libraries/libc/stdio/fgetpos.c create mode 100644 src/libraries/libc/stdio/fgets.c create mode 100644 src/libraries/libc/stdio/fopen.c create mode 100644 src/libraries/libc/stdio/fprintf.c create mode 100644 src/libraries/libc/stdio/fputc.c create mode 100644 src/libraries/libc/stdio/fputs.c create mode 100644 src/libraries/libc/stdio/fread.c create mode 100644 src/libraries/libc/stdio/freopen.c create mode 100644 src/libraries/libc/stdio/fscanf.c create mode 100644 src/libraries/libc/stdio/fseek.c create mode 100644 src/libraries/libc/stdio/fsetpos.c create mode 100644 src/libraries/libc/stdio/ftell.c create mode 100644 src/libraries/libc/stdio/fwrite.c create mode 100644 src/libraries/libc/stdio/getc.c create mode 100644 src/libraries/libc/stdio/getchar.c create mode 100644 src/libraries/libc/stdio/perror.c create mode 100644 src/libraries/libc/stdio/printf.c create mode 100644 src/libraries/libc/stdio/putc.c create mode 100644 src/libraries/libc/stdio/putchar.c create mode 100644 src/libraries/libc/stdio/puts.c create mode 100644 src/libraries/libc/stdio/remove.c create mode 100644 src/libraries/libc/stdio/rename.c create mode 100644 src/libraries/libc/stdio/rewind.c create mode 100644 src/libraries/libc/stdio/scanf.c create mode 100644 src/libraries/libc/stdio/setbuf.c create mode 100644 src/libraries/libc/stdio/setvbuf.c create mode 100644 src/libraries/libc/stdio/snprintf.c create mode 100644 src/libraries/libc/stdio/sprintf.c create mode 100644 src/libraries/libc/stdio/sscanf.c create mode 100644 src/libraries/libc/stdio/tmpfile.c create mode 100644 src/libraries/libc/stdio/tmpnam.c create mode 100644 src/libraries/libc/stdio/ungetc.c create mode 100644 src/libraries/libc/stdio/vfprintf.c create mode 100644 src/libraries/libc/stdio/vfscanf.c create mode 100644 src/libraries/libc/stdio/vprintf.c create mode 100644 src/libraries/libc/stdio/vscanf.c create mode 100644 src/libraries/libc/stdio/vsnprintf.c create mode 100644 src/libraries/libc/stdio/vsprintf.c create mode 100644 src/libraries/libc/stdio/vsscanf.c create mode 100644 src/libraries/libc/stdlib/_Exit.c create mode 100644 src/libraries/libc/stdlib/abort.c create mode 100644 src/libraries/libc/stdlib/abs.c create mode 100644 src/libraries/libc/stdlib/atexit.c create mode 100644 src/libraries/libc/stdlib/atoi.c create mode 100644 src/libraries/libc/stdlib/atol.c create mode 100644 src/libraries/libc/stdlib/atoll.c create mode 100644 src/libraries/libc/stdlib/bsearch.c create mode 100644 src/libraries/libc/stdlib/div.c create mode 100644 src/libraries/libc/stdlib/exit.c create mode 100644 src/libraries/libc/stdlib/getenv.c create mode 100644 src/libraries/libc/stdlib/labs.c create mode 100644 src/libraries/libc/stdlib/ldiv.c create mode 100644 src/libraries/libc/stdlib/llabs.c create mode 100644 src/libraries/libc/stdlib/lldiv.c create mode 100644 src/libraries/libc/stdlib/malloc.c create mode 100644 src/libraries/libc/stdlib/qsort.c create mode 100644 src/libraries/libc/stdlib/rand.c create mode 100644 src/libraries/libc/stdlib/srand.c create mode 100644 src/libraries/libc/stdlib/strtol.c create mode 100644 src/libraries/libc/stdlib/strtoll.c create mode 100644 src/libraries/libc/stdlib/strtoul.c create mode 100644 src/libraries/libc/stdlib/strtoull.c create mode 100644 src/libraries/libc/stdlib/system.c create mode 100644 src/libraries/libc/string/memchr.c create mode 100644 src/libraries/libc/string/memcmp.c create mode 100644 src/libraries/libc/string/memcpy.c create mode 100644 src/libraries/libc/string/memmove.c create mode 100644 src/libraries/libc/string/memset.c create mode 100644 src/libraries/libc/string/strcat.c create mode 100644 src/libraries/libc/string/strchr.c create mode 100644 src/libraries/libc/string/strcmp.c create mode 100644 src/libraries/libc/string/strcoll.c create mode 100644 src/libraries/libc/string/strcpy.c create mode 100644 src/libraries/libc/string/strcspn.c create mode 100644 src/libraries/libc/string/strerror.c create mode 100644 src/libraries/libc/string/strlen.c create mode 100644 src/libraries/libc/string/strncat.c create mode 100644 src/libraries/libc/string/strncmp.c create mode 100644 src/libraries/libc/string/strncpy.c create mode 100644 src/libraries/libc/string/strpbrk.c create mode 100644 src/libraries/libc/string/strrchr.c create mode 100644 src/libraries/libc/string/strspn.c create mode 100644 src/libraries/libc/string/strstr.c create mode 100644 src/libraries/libc/string/strtok.c create mode 100644 src/libraries/libc/string/strxfrm.c create mode 100644 src/libraries/libc/tests/_PDCLIB_iotest.h create mode 100644 src/libraries/libc/tests/_PDCLIB_test.h create mode 100644 src/libraries/libc/tests/main.c create mode 100644 src/libraries/libc/tests/printf_testcases.h create mode 100644 src/libraries/libc/tests/scanf_testcases.h create mode 100644 src/libraries/libc/tests/test_assert.c create mode 100644 src/libraries/libc/tests/test_ctype.c create mode 100644 src/libraries/libc/tests/test_internal.c create mode 100644 src/libraries/libc/tests/test_inttypes.c create mode 100644 src/libraries/libc/tests/test_locale.c create mode 100644 src/libraries/libc/tests/test_signal.c create mode 100644 src/libraries/libc/tests/test_stdarg.c create mode 100644 src/libraries/libc/tests/test_stdio.c create mode 100644 src/libraries/libc/tests/test_stdlib.c create mode 100644 src/libraries/libc/tests/test_string.c create mode 100644 src/libraries/libc/tests/test_time.c create mode 100644 src/libraries/libc/tests/testfile.txt create mode 100644 src/libraries/libc/time/asctime.c create mode 100644 src/libraries/libc/time/clock.c create mode 100644 src/libraries/libc/time/ctime.c create mode 100644 src/libraries/libc/time/difftime.c create mode 100644 src/libraries/libc/time/gmtime.c create mode 100644 src/libraries/libc/time/localtime.c create mode 100644 src/libraries/libc/time/mktime.c create mode 100644 src/libraries/libc/time/strftime.c create mode 100644 src/libraries/libc/time/time.c create mode 100644 src/libraries/libc/time/timespec_get.c diff --git a/modules.yaml b/modules.yaml index 228c379..009afcc 100644 --- a/modules.yaml +++ b/modules.yaml @@ -77,6 +77,8 @@ modules: kind: exe target: user output: nulldrv + deps: + - libc source: - src/drivers/nulldrv/main.cpp - src/drivers/nulldrv/main.s @@ -115,6 +117,182 @@ modules: - src/libraries/kutil/printf.c - src/libraries/kutil/vm_space.cpp + + libc: + kind: lib + output: libc.a + includes: + - src/libraries/libc/include + target: user + defines: + - DISABLE_SSE + - LACKS_UNISTD_H + - LACKS_FCNTL_H + - LACKS_SYS_PARAM_H + - LACKS_SYS_MMAN_H + - LACKS_SCHED_H + - LACKS_STRINGS_H + - HAVE_MMAP=0 + #- LACKS_STRING_H + #- LACKS_ERRNO_H + #- LACKS_STDLIB_H + #- LACKS_TIME_H + source: + - src/libraries/libc/arch/x86_64/_Exit.s + - src/libraries/libc/ctype/isalnum.c + - src/libraries/libc/ctype/isalpha.c + - src/libraries/libc/ctype/isblank.c + - src/libraries/libc/ctype/iscntrl.c + - src/libraries/libc/ctype/isdigit.c + - src/libraries/libc/ctype/isgraph.c + - src/libraries/libc/ctype/islower.c + - src/libraries/libc/ctype/isprint.c + - src/libraries/libc/ctype/ispunct.c + - src/libraries/libc/ctype/isspace.c + - src/libraries/libc/ctype/isupper.c + - src/libraries/libc/ctype/isxdigit.c + - src/libraries/libc/ctype/tolower.c + - src/libraries/libc/ctype/toupper.c + - src/libraries/libc/inttypes/imaxabs.c + - src/libraries/libc/inttypes/imaxdiv.c + - src/libraries/libc/inttypes/strtoimax.c + - src/libraries/libc/inttypes/strtoumax.c + - src/libraries/libc/locale/localeconv.c + - src/libraries/libc/locale/setlocale.c + - src/libraries/libc/j6libc/assert.c + - src/libraries/libc/j6libc/errno.c + - src/libraries/libc/j6libc/allocpages.c + - src/libraries/libc/j6libc/atomax.c + - src/libraries/libc/j6libc/closeall.c + - src/libraries/libc/j6libc/close.c + - src/libraries/libc/j6libc/digits.c + - src/libraries/libc/j6libc/filemode.c + - src/libraries/libc/j6libc/fillbuffer.c + - src/libraries/libc/j6libc/flushbuffer.c + - src/libraries/libc/j6libc/is_leap.c + - src/libraries/libc/j6libc/load_lc_collate.c + - src/libraries/libc/j6libc/load_lc_ctype.c + - src/libraries/libc/j6libc/load_lc_messages.c + - src/libraries/libc/j6libc/load_lc_monetary.c + - src/libraries/libc/j6libc/load_lc_numeric.c + - src/libraries/libc/j6libc/load_lc_time.c + - src/libraries/libc/j6libc/load_lines.c + - src/libraries/libc/j6libc/open.c + - src/libraries/libc/j6libc/prepread.c + - src/libraries/libc/j6libc/prepwrite.c + - src/libraries/libc/j6libc/print.c + - src/libraries/libc/j6libc/rename.c + - src/libraries/libc/j6libc/scan.c + - src/libraries/libc/j6libc/seed.c + - src/libraries/libc/j6libc/seek.c + - src/libraries/libc/j6libc/stdinit.c + - src/libraries/libc/j6libc/strtox_main.c + - src/libraries/libc/j6libc/strtox_prelim.c + - src/libraries/libc/j6libc/sbrk.c + - src/libraries/libc/signal/raise.c + - src/libraries/libc/signal/signal.c + - src/libraries/libc/stdio/clearerr.c + - src/libraries/libc/stdio/fclose.c + - src/libraries/libc/stdio/feof.c + - src/libraries/libc/stdio/ferror.c + - src/libraries/libc/stdio/fflush.c + - src/libraries/libc/stdio/fgetc.c + - src/libraries/libc/stdio/fgetpos.c + - src/libraries/libc/stdio/fgets.c + - src/libraries/libc/stdio/fopen.c + - src/libraries/libc/stdio/fprintf.c + - src/libraries/libc/stdio/fputc.c + - src/libraries/libc/stdio/fputs.c + - src/libraries/libc/stdio/fread.c + - src/libraries/libc/stdio/freopen.c + - src/libraries/libc/stdio/fscanf.c + - src/libraries/libc/stdio/fseek.c + - src/libraries/libc/stdio/fsetpos.c + - src/libraries/libc/stdio/ftell.c + - src/libraries/libc/stdio/fwrite.c + - src/libraries/libc/stdio/getc.c + - src/libraries/libc/stdio/getchar.c + - src/libraries/libc/stdio/perror.c + - src/libraries/libc/stdio/printf.c + - src/libraries/libc/stdio/putc.c + - src/libraries/libc/stdio/putchar.c + - src/libraries/libc/stdio/puts.c + - src/libraries/libc/stdio/remove.c + - src/libraries/libc/stdio/rename.c + - src/libraries/libc/stdio/rewind.c + - src/libraries/libc/stdio/scanf.c + - src/libraries/libc/stdio/setbuf.c + - src/libraries/libc/stdio/setvbuf.c + - src/libraries/libc/stdio/snprintf.c + - src/libraries/libc/stdio/sprintf.c + - src/libraries/libc/stdio/sscanf.c + - src/libraries/libc/stdio/tmpfile.c + - src/libraries/libc/stdio/tmpnam.c + - src/libraries/libc/stdio/ungetc.c + - src/libraries/libc/stdio/vfprintf.c + - src/libraries/libc/stdio/vfscanf.c + - src/libraries/libc/stdio/vprintf.c + - src/libraries/libc/stdio/vscanf.c + - src/libraries/libc/stdio/vsnprintf.c + - src/libraries/libc/stdio/vsprintf.c + - src/libraries/libc/stdio/vsscanf.c + - src/libraries/libc/stdlib/abort.c + - src/libraries/libc/stdlib/abs.c + - src/libraries/libc/stdlib/atexit.c + - src/libraries/libc/stdlib/atoi.c + - src/libraries/libc/stdlib/atol.c + - src/libraries/libc/stdlib/atoll.c + - src/libraries/libc/stdlib/bsearch.c + - src/libraries/libc/stdlib/div.c + - src/libraries/libc/stdlib/exit.c + - src/libraries/libc/stdlib/_Exit.c + - src/libraries/libc/stdlib/getenv.c + - src/libraries/libc/stdlib/labs.c + - src/libraries/libc/stdlib/ldiv.c + - src/libraries/libc/stdlib/llabs.c + - src/libraries/libc/stdlib/lldiv.c + - src/libraries/libc/stdlib/malloc.c + - src/libraries/libc/stdlib/qsort.c + - src/libraries/libc/stdlib/rand.c + - src/libraries/libc/stdlib/srand.c + - src/libraries/libc/stdlib/strtol.c + - src/libraries/libc/stdlib/strtoll.c + - src/libraries/libc/stdlib/strtoul.c + - src/libraries/libc/stdlib/strtoull.c + - src/libraries/libc/stdlib/system.c + - src/libraries/libc/string/memchr.c + - src/libraries/libc/string/memcmp.c + - src/libraries/libc/string/memcpy.c + - src/libraries/libc/string/memmove.c + - src/libraries/libc/string/memset.c + - src/libraries/libc/string/strcat.c + - src/libraries/libc/string/strchr.c + - src/libraries/libc/string/strcmp.c + - src/libraries/libc/string/strcoll.c + - src/libraries/libc/string/strcpy.c + - src/libraries/libc/string/strcspn.c + - src/libraries/libc/string/strerror.c + - src/libraries/libc/string/strlen.c + - src/libraries/libc/string/strncat.c + - src/libraries/libc/string/strncmp.c + - src/libraries/libc/string/strncpy.c + - src/libraries/libc/string/strpbrk.c + - src/libraries/libc/string/strrchr.c + - src/libraries/libc/string/strspn.c + - src/libraries/libc/string/strstr.c + - src/libraries/libc/string/strtok.c + - src/libraries/libc/string/strxfrm.c + - src/libraries/libc/time/asctime.c + - src/libraries/libc/time/clock.c + - src/libraries/libc/time/ctime.c + - src/libraries/libc/time/difftime.c + - src/libraries/libc/time/gmtime.c + - src/libraries/libc/time/localtime.c + - src/libraries/libc/time/mktime.c + - src/libraries/libc/time/strftime.c + - src/libraries/libc/time/time.c + - src/libraries/libc/time/timespec_get.c + makerd: kind: exe target: native diff --git a/scripts/templates/target.default.j2 b/scripts/templates/target.default.j2 index 6c4e891..62e6597 100644 --- a/scripts/templates/target.default.j2 +++ b/scripts/templates/target.default.j2 @@ -22,5 +22,10 @@ build ${builddir}/cpp.defs : dump_cpp_defs | {{ buildfile }} build ${builddir}/c.run : dump_c_run | {{ buildfile }} build ${builddir}/cpp.run : dump_cpp_run | {{ buildfile }} +default ${builddir}/c.defs +default ${builddir}/cpp.defs +default ${builddir}/c.run +default ${builddir}/cpp.run + # vim: ft=ninja et ts=4 sts=4 sw=4 diff --git a/scripts/templates/target.host.j2 b/scripts/templates/target.host.j2 index bca8886..c8a6db4 100644 --- a/scripts/templates/target.host.j2 +++ b/scripts/templates/target.host.j2 @@ -24,6 +24,7 @@ ccflags = $ccflags $ -D__ELF__ $ -D__JSIX__ $ -isystem${srcroot}/sysroot/include $ + -isystem${srcroot}/src/libraries/libc/include $ --sysroot="${srcroot}/sysroot" cxxflags = $cxxflags $ diff --git a/scripts/templates/target.user.j2 b/scripts/templates/target.user.j2 index eb0e3d7..28ca47e 100644 --- a/scripts/templates/target.user.j2 +++ b/scripts/templates/target.user.j2 @@ -23,6 +23,7 @@ ccflags = $ccflags $ -D__ELF__ $ -D__JSIX__ $ -isystem${srcroot}/sysroot/include $ + -isystem${srcroot}/src/libraries/libc/include $ --sysroot="${srcroot}/sysroot" cxxflags = $cxxflags $ @@ -38,9 +39,6 @@ ldflags = $ldflags $ --sysroot="${srcroot}/sysroot" $ -L "${srcroot}/sysroot/lib" $ -libs = $libs $ - -lc - {% endblock %} # vim: ft=ninja et ts=4 sts=4 sw=4 diff --git a/src/kernel/console.h b/src/kernel/console.h index 7fc0538..213bd0d 100644 --- a/src/kernel/console.h +++ b/src/kernel/console.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include @@ -61,7 +60,7 @@ void console::put_hex(T x, int width, char pad) int len = 1; for (int i = chars - 1; i >= 0; --i) { uint8_t nibble = (x >> (i*4)) & 0xf; - if (nibble) len = std::max(i + 1, len); + if (nibble) len = len > i + 1 ? len : i + 1; message[chars - i - 1] = digits[nibble]; } message[chars] = 0; diff --git a/src/kernel/memory_bootstrap.cpp b/src/kernel/memory_bootstrap.cpp index 3216b7d..0fec758 100644 --- a/src/kernel/memory_bootstrap.cpp +++ b/src/kernel/memory_bootstrap.cpp @@ -1,4 +1,3 @@ -#include #include #include "kernel_args.h" diff --git a/src/kernel/page_manager.cpp b/src/kernel/page_manager.cpp index 68163ba..9a62103 100644 --- a/src/kernel/page_manager.cpp +++ b/src/kernel/page_manager.cpp @@ -1,5 +1,3 @@ -#include - #include "kutil/assert.h" #include "kutil/vm_space.h" #include "console.h" diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp index 06f6a11..921bfbd 100644 --- a/src/kernel/scheduler.cpp +++ b/src/kernel/scheduler.cpp @@ -1,3 +1,5 @@ +#include + #include "apic.h" #include "clock.h" #include "console.h" diff --git a/src/libraries/kutil/include/kutil/avl_tree.h b/src/libraries/kutil/include/kutil/avl_tree.h index 39d95d0..71264dc 100644 --- a/src/libraries/kutil/include/kutil/avl_tree.h +++ b/src/libraries/kutil/include/kutil/avl_tree.h @@ -2,7 +2,6 @@ /// \file avl_tree.h /// Templated container class for an AVL tree -#include #include #include "kutil/assert.h" #include "kutil/slab_allocated.h" @@ -72,7 +71,7 @@ private: { int left = height(m_left); int right = height(m_right); - m_height = std::max(left, right) + 1; + m_height = left > right ? left : right; return left - right; } diff --git a/src/libraries/kutil/include/kutil/vector.h b/src/libraries/kutil/include/kutil/vector.h index 77a4a98..1841280 100644 --- a/src/libraries/kutil/include/kutil/vector.h +++ b/src/libraries/kutil/include/kutil/vector.h @@ -2,7 +2,6 @@ /// \file vector.h /// Definition of a simple dynamic vector collection for use in kernel space -#include #include #include "kutil/memory.h" @@ -175,7 +174,7 @@ public: void set_capacity(size_t capacity) { T *new_array = reinterpret_cast(kalloc(capacity * sizeof(T))); - size_t size = std::min(capacity, m_size); + size_t size = capacity > m_size ? m_size : capacity; kutil::memcpy(new_array, m_elements, size * sizeof(T)); diff --git a/src/libraries/libc/arch/x86_64/_Exit.s b/src/libraries/libc/arch/x86_64/_Exit.s new file mode 100644 index 0000000..da96a5c --- /dev/null +++ b/src/libraries/libc/arch/x86_64/_Exit.s @@ -0,0 +1,5 @@ +global _PDCLIB_Exit +_PDCLIB_Exit: + ; arg should already be in rdi + mov rax, 0x11 ; Exit syscall + syscall diff --git a/src/libraries/libc/ctype/isalnum.c b/src/libraries/libc/ctype/isalnum.c new file mode 100644 index 0000000..6836bcd --- /dev/null +++ b/src/libraries/libc/ctype/isalnum.c @@ -0,0 +1,13 @@ +/* isalnum( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int isalnum( int c ) +{ + return ( isdigit( c ) || isalpha( c ) ); +} diff --git a/src/libraries/libc/ctype/isalpha.c b/src/libraries/libc/ctype/isalpha.c new file mode 100644 index 0000000..7ece916 --- /dev/null +++ b/src/libraries/libc/ctype/isalpha.c @@ -0,0 +1,13 @@ +/* isalpha( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int isalpha( int c ) +{ + return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_ALPHA ); +} diff --git a/src/libraries/libc/ctype/isblank.c b/src/libraries/libc/ctype/isblank.c new file mode 100644 index 0000000..0d10fbf --- /dev/null +++ b/src/libraries/libc/ctype/isblank.c @@ -0,0 +1,13 @@ +/* isblank( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int isblank( int c ) +{ + return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_BLANK ); +} diff --git a/src/libraries/libc/ctype/iscntrl.c b/src/libraries/libc/ctype/iscntrl.c new file mode 100644 index 0000000..5b4652c --- /dev/null +++ b/src/libraries/libc/ctype/iscntrl.c @@ -0,0 +1,13 @@ +/* iscntrl( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int iscntrl( int c ) +{ + return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_CNTRL ); +} diff --git a/src/libraries/libc/ctype/isdigit.c b/src/libraries/libc/ctype/isdigit.c new file mode 100644 index 0000000..e3147c8 --- /dev/null +++ b/src/libraries/libc/ctype/isdigit.c @@ -0,0 +1,13 @@ +/* isdigit( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int isdigit( int c ) +{ + return ( c >= _PDCLIB_lc_ctype.digits_low && c <= _PDCLIB_lc_ctype.digits_high ); +} diff --git a/src/libraries/libc/ctype/isgraph.c b/src/libraries/libc/ctype/isgraph.c new file mode 100644 index 0000000..838e771 --- /dev/null +++ b/src/libraries/libc/ctype/isgraph.c @@ -0,0 +1,13 @@ +/* isgraph( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int isgraph( int c ) +{ + return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_GRAPH ); +} diff --git a/src/libraries/libc/ctype/islower.c b/src/libraries/libc/ctype/islower.c new file mode 100644 index 0000000..e7a3ef5 --- /dev/null +++ b/src/libraries/libc/ctype/islower.c @@ -0,0 +1,13 @@ +/* islower( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int islower( int c ) +{ + return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_LOWER ); +} diff --git a/src/libraries/libc/ctype/isprint.c b/src/libraries/libc/ctype/isprint.c new file mode 100644 index 0000000..efa3c14 --- /dev/null +++ b/src/libraries/libc/ctype/isprint.c @@ -0,0 +1,14 @@ +/* isprint( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int isprint( int c ) +{ + /* FIXME: Space as of current locale charset, not source charset. */ + return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_GRAPH ) || ( c == ' ' ); +} diff --git a/src/libraries/libc/ctype/ispunct.c b/src/libraries/libc/ctype/ispunct.c new file mode 100644 index 0000000..e14eda0 --- /dev/null +++ b/src/libraries/libc/ctype/ispunct.c @@ -0,0 +1,13 @@ +/* ispunct( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int ispunct( int c ) +{ + return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_PUNCT ); +} diff --git a/src/libraries/libc/ctype/isspace.c b/src/libraries/libc/ctype/isspace.c new file mode 100644 index 0000000..dc8abba --- /dev/null +++ b/src/libraries/libc/ctype/isspace.c @@ -0,0 +1,13 @@ +/* isspace( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int isspace( int c ) +{ + return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_SPACE ); +} diff --git a/src/libraries/libc/ctype/isupper.c b/src/libraries/libc/ctype/isupper.c new file mode 100644 index 0000000..6aa01f2 --- /dev/null +++ b/src/libraries/libc/ctype/isupper.c @@ -0,0 +1,13 @@ +/* isupper( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int isupper( int c ) +{ + return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_UPPER ); +} diff --git a/src/libraries/libc/ctype/isxdigit.c b/src/libraries/libc/ctype/isxdigit.c new file mode 100644 index 0000000..70d05c9 --- /dev/null +++ b/src/libraries/libc/ctype/isxdigit.c @@ -0,0 +1,15 @@ +/* isxdigit( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int isxdigit( int c ) +{ + return ( isdigit( c ) + || ( c >= _PDCLIB_lc_ctype.Xdigits_low && c <= _PDCLIB_lc_ctype.Xdigits_high ) + || ( c >= _PDCLIB_lc_ctype.xdigits_low && c <= _PDCLIB_lc_ctype.xdigits_high ) ); +} diff --git a/src/libraries/libc/ctype/tolower.c b/src/libraries/libc/ctype/tolower.c new file mode 100644 index 0000000..f43652a --- /dev/null +++ b/src/libraries/libc/ctype/tolower.c @@ -0,0 +1,13 @@ +/* tolower( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int tolower( int c ) +{ + return _PDCLIB_lc_ctype.entry[c].lower; +} diff --git a/src/libraries/libc/ctype/toupper.c b/src/libraries/libc/ctype/toupper.c new file mode 100644 index 0000000..b48d534 --- /dev/null +++ b/src/libraries/libc/ctype/toupper.c @@ -0,0 +1,13 @@ +/* toupper( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int toupper( int c ) +{ + return _PDCLIB_lc_ctype.entry[c].upper; +} diff --git a/src/libraries/libc/include/assert.h b/src/libraries/libc/include/assert.h new file mode 100644 index 0000000..22eac10 --- /dev/null +++ b/src/libraries/libc/include/assert.h @@ -0,0 +1,29 @@ +#pragma once +/* Diagnostics + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/aux.h" +#include "j6libc/cpp.h" + +CPP_CHECK_BEGIN + +void _PDCLIB_assert( const char * const, const char * const, const char * const ); + +/* If NDEBUG is set, assert() is a null operation. */ +#undef assert + +#ifdef NDEBUG +#define assert( ignore ) ( (void) 0 ) +#else +#define assert( expression ) ( ( expression ) ? (void) 0 \ + : _PDCLIB_assert( "Assertion failed: " #expression \ + ", function ", __func__, \ + ", file " __FILE__ \ + ", line " _PDCLIB_symbol2string( __LINE__ ) \ + "." _PDCLIB_endl ) ) +#endif + +CPP_CHECK_END diff --git a/src/libraries/libc/include/ctype.h b/src/libraries/libc/include/ctype.h new file mode 100644 index 0000000..5f68dd7 --- /dev/null +++ b/src/libraries/libc/include/ctype.h @@ -0,0 +1,96 @@ +#pragma once +/* Character handling + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/int.h" +#include "j6libc/cpp.h" + +CPP_CHECK_BEGIN + +/* Character classification functions */ + +/* Note that there is a difference between "whitespace" (any printing, non- + graph character, like horizontal and vertical tab), and "blank" (the literal + ' ' space character). + + There will be masking macros for each of these later on, but right now I + focus on the functions only. +*/ + +/* Returns isalpha( c ) || isdigit( c ) */ +int isalnum( int c ); + +/* Returns isupper( c ) || islower( c ) in the "C" locale. + In any other locale, also returns true for a locale-specific set of + alphabetic characters which are neither control characters, digits, + punctation, or whitespace. +*/ +int isalpha( int c ); + +/* Returns true if the character isspace() and used for seperating words within + a line of text. In the "C" locale, only ' ' and '\t' are considered blanks. +*/ +int isblank( int c ); + +/* Returns true if the character is a control character. */ +int iscntrl( int c ); + +/* Returns true if the character is a decimal digit. Locale-independent. */ +int isdigit( int c ); + +/* Returns true for every printing character except space (' '). + NOTE: This definition differs from that of iswgraph() in , + which considers any iswspace() character, not only ' '. +*/ +int isgraph( int c ); + +/* Returns true for lowercase letters in the "C" locale. + In any other locale, also returns true for a locale-specific set of + characters which are neither control characters, digits, punctation, or + space (' '). In a locale other than the "C" locale, a character might test + true for both islower() and isupper(). +*/ +int islower( int c ); + +/* Returns true for every printing character including space (' '). */ +int isprint( int c ); + +/* Returns true for a locale-specific set of punctuation charcters; these + may not be whitespace or alphanumeric. In the "C" locale, returns true + for every printing character that is not whitespace or alphanumeric. +*/ +int ispunct( int c ); + +/* Returns true for every standard whitespace character (' ', '\f', '\n', '\r', + '\t', '\v') in the "C" locale. In any other locale, also returns true for a + locale-specific set of characters for which isalnum() is false. +*/ +int isspace( int c ); + +/* Returns true for uppercase letters in the "C" locale. + In any other locale, also returns true for a locale-specific set of + characters which are neither control characters, digits, punctation, or + space (' '). In a locale other than the "C" locale, a character might test + true for both islower() and isupper(). +*/ +int isupper( int c ); + +/* Returns true for any hexadecimal-digit character. Locale-independent. */ +int isxdigit( int c ); + +/* Character case mapping functions */ + +/* Converts an uppercase letter to a corresponding lowercase letter. Input that + is not an uppercase letter remains unchanged. +*/ +int tolower( int c ); + +/* Converts a lowercase letter to a corresponding uppercase letter. Input that + is not a lowercase letter remains unchanged. +*/ +int toupper( int c ); + +CPP_CHECK_END diff --git a/src/libraries/libc/include/errno.h b/src/libraries/libc/include/errno.h new file mode 100644 index 0000000..b2d5423 --- /dev/null +++ b/src/libraries/libc/include/errno.h @@ -0,0 +1,15 @@ +#pragma once +/* Errors + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/int.h" + +#define errno (*_PDCLIB_errno_func()) + +#define ERANGE _PDCLIB_ERANGE +#define EDOM _PDCLIB_EDOM +#define ENOMEM _PDCLIB_ENOMEM +#define EINVAL _PDCLIB_EINVAL diff --git a/src/libraries/libc/include/float.h b/src/libraries/libc/include/float.h new file mode 100644 index 0000000..70f72c0 --- /dev/null +++ b/src/libraries/libc/include/float.h @@ -0,0 +1,71 @@ +#pragma once +/* Characteristics of floating types + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/config.h" + +#define FLT_ROUNDS _PDCLIB_FLT_ROUNDS +#define FLT_EVAL_METHOD _PDCLIB_FLT_EVAL_METHOD +#define DECIMAL_DIG _PDCLIB_DECIMAL_DIG + + /* Radix of exponent representation */ +#define FLT_RADIX __FLT_RADIX__ + /* Number of base-FLT_RADIX digits in the significand of a float */ +#define FLT_MANT_DIG __FLT_MANT_DIG__ + /* Number of decimal digits of precision in a float */ +#define FLT_DIG __FLT_DIG__ + /* Difference between 1.0 and the minimum float greater than 1.0 */ +#define FLT_EPSILON __FLT_EPSILON__ + /* Minimum int x such that FLT_RADIX**(x-1) is a normalised float */ +#define FLT_MIN_EXP __FLT_MIN_EXP__ + /* Minimum normalised float */ +#define FLT_MIN __FLT_MIN__ + /* Minimum int x such that 10**x is a normalised float */ +#define FLT_MIN_10_EXP __FLT_MIN_10_EXP__ + /* Maximum int x such that FLT_RADIX**(x-1) is a representable float */ +#define FLT_MAX_EXP __FLT_MAX_EXP__ + /* Maximum float */ +#define FLT_MAX __FLT_MAX__ + /* Maximum int x such that 10**x is a representable float */ +#define FLT_MAX_10_EXP __FLT_MAX_10_EXP__ + + /* Number of base-FLT_RADIX digits in the significand of a double */ +#define DBL_MANT_DIG __DBL_MANT_DIG__ + /* Number of decimal digits of precision in a double */ +#define DBL_DIG __DBL_DIG__ + /* Difference between 1.0 and the minimum double greater than 1.0 */ +#define DBL_EPSILON __DBL_EPSILON__ + /* Minimum int x such that FLT_RADIX**(x-1) is a normalised double */ +#define DBL_MIN_EXP __DBL_MIN_EXP__ + /* Minimum normalised double */ +#define DBL_MIN __DBL_MIN__ + /* Minimum int x such that 10**x is a normalised double */ +#define DBL_MIN_10_EXP __DBL_MIN_10_EXP__ + /* Maximum int x such that FLT_RADIX**(x-1) is a representable double */ +#define DBL_MAX_EXP __DBL_MAX_EXP__ + /* Maximum double */ +#define DBL_MAX __DBL_MAX__ + /* Maximum int x such that 10**x is a representable double */ +#define DBL_MAX_10_EXP __DBL_MAX_10_EXP__ + + /* Number of base-FLT_RADIX digits in the significand of a long double */ +#define LDBL_MANT_DIG __LDBL_MANT_DIG__ + /* Number of decimal digits of precision in a long double */ +#define LDBL_DIG __LDBL_DIG__ + /* Difference between 1.0 and the minimum long double greater than 1.0 */ +#define LDBL_EPSILON __LDBL_EPSILON__ + /* Minimum int x such that FLT_RADIX**(x-1) is a normalised long double */ +#define LDBL_MIN_EXP __LDBL_MIN_EXP__ + /* Minimum normalised long double */ +#define LDBL_MIN __LDBL_MIN__ + /* Minimum int x such that 10**x is a normalised long double */ +#define LDBL_MIN_10_EXP __LDBL_MIN_10_EXP__ + /* Maximum int x such that FLT_RADIX**(x-1) is a representable long double */ +#define LDBL_MAX_EXP __LDBL_MAX_EXP__ + /* Maximum long double */ +#define LDBL_MAX __LDBL_MAX__ + /* Maximum int x such that 10**x is a representable long double */ +#define LDBL_MAX_10_EXP __LDBL_MAX_10_EXP__ diff --git a/src/libraries/libc/include/inttypes.h b/src/libraries/libc/include/inttypes.h new file mode 100644 index 0000000..f2ad82c --- /dev/null +++ b/src/libraries/libc/include/inttypes.h @@ -0,0 +1,250 @@ +#pragma once +/* Format conversion of integer types + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/cpp.h" +#include + +CPP_CHECK_BEGIN + +typedef struct _PDCLIB_imaxdiv_t imaxdiv_t; + +#define PRId8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, d ) ) +#define PRId16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, d ) ) +#define PRId32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, d ) ) +#define PRId64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, d ) ) + +#define PRIdLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, d ) ) +#define PRIdLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, d ) ) +#define PRIdLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, d ) ) +#define PRIdLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, d ) ) + +#define PRIdFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, d ) ) +#define PRIdFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, d ) ) +#define PRIdFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, d ) ) +#define PRIdFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, d ) ) + +#define PRIdMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, d ) ) +#define PRIdPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, d ) ) + +#define PRIi8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, i ) ) +#define PRIi16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, i ) ) +#define PRIi32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, i ) ) +#define PRIi64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, i ) ) + +#define PRIiLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, i ) ) +#define PRIiLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, i ) ) +#define PRIiLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, i ) ) +#define PRIiLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, i ) ) + +#define PRIiFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, i ) ) +#define PRIiFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, i ) ) +#define PRIiFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, i ) ) +#define PRIiFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, i ) ) + +#define PRIiMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, i ) ) +#define PRIiPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, i ) ) + +#define PRIo8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, o ) ) +#define PRIo16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, o ) ) +#define PRIo32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, o ) ) +#define PRIo64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, o ) ) + +#define PRIoLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, o ) ) +#define PRIoLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, o ) ) +#define PRIoLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, o ) ) +#define PRIoLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, o ) ) + +#define PRIoFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, o ) ) +#define PRIoFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, o ) ) +#define PRIoFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, o ) ) +#define PRIoFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, o ) ) + +#define PRIoMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, o ) ) +#define PRIoPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, o ) ) + +#define PRIu8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, u ) ) +#define PRIu16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, u ) ) +#define PRIu32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, u ) ) +#define PRIu64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, u ) ) + +#define PRIuLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, u ) ) +#define PRIuLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, u ) ) +#define PRIuLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, u ) ) +#define PRIuLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, u ) ) + +#define PRIuFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, u ) ) +#define PRIuFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, u ) ) +#define PRIuFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, u ) ) +#define PRIuFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, u ) ) + +#define PRIuMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, u ) ) +#define PRIuPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, u ) ) + +#define PRIx8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, x ) ) +#define PRIx16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, x ) ) +#define PRIx32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, x ) ) +#define PRIx64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, x ) ) + +#define PRIxLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, x ) ) +#define PRIxLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, x ) ) +#define PRIxLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, x ) ) +#define PRIxLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, x ) ) + +#define PRIxFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, x ) ) +#define PRIxFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, x ) ) +#define PRIxFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, x ) ) +#define PRIxFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, x ) ) + +#define PRIxMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, x ) ) +#define PRIxPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, x ) ) + +#define PRIX8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, X ) ) +#define PRIX16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, X ) ) +#define PRIX32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, X ) ) +#define PRIX64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, X ) ) + +#define PRIXLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, X ) ) +#define PRIXLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, X ) ) +#define PRIXLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, X ) ) +#define PRIXLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, X ) ) + +#define PRIXFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, X ) ) +#define PRIXFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, X ) ) +#define PRIXFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, X ) ) +#define PRIXFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, X ) ) + +#define PRIXMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, X ) ) +#define PRIXPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, X ) ) + +#define SCNd8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, d ) ) +#define SCNd16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, d ) ) +#define SCNd32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, d ) ) +#define SCNd64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, d ) ) + +#define SCNdLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, d ) ) +#define SCNdLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, d ) ) +#define SCNdLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, d ) ) +#define SCNdLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, d ) ) + +#define SCNdFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, d ) ) +#define SCNdFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, d ) ) +#define SCNdFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, d ) ) +#define SCNdFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, d ) ) + +#define SCNdMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, d ) ) +#define SCNdPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, d ) ) + +#define SCNi8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, i ) ) +#define SCNi16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, i ) ) +#define SCNi32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, i ) ) +#define SCNi64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, i ) ) + +#define SCNiLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, i ) ) +#define SCNiLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, i ) ) +#define SCNiLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, i ) ) +#define SCNiLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, i ) ) + +#define SCNiFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, i ) ) +#define SCNiFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, i ) ) +#define SCNiFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, i ) ) +#define SCNiFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, i ) ) + +#define SCNiMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, i ) ) +#define SCNiPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, i ) ) + +#define SCNo8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, o ) ) +#define SCNo16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, o ) ) +#define SCNo32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, o ) ) +#define SCNo64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, o ) ) + +#define SCNoLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, o ) ) +#define SCNoLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, o ) ) +#define SCNoLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, o ) ) +#define SCNoLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, o ) ) + +#define SCNoFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, o ) ) +#define SCNoFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, o ) ) +#define SCNoFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, o ) ) +#define SCNoFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, o ) ) + +#define SCNoMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, o ) ) +#define SCNoPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, o ) ) + +#define SCNu8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, u ) ) +#define SCNu16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, u ) ) +#define SCNu32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, u ) ) +#define SCNu64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, u ) ) + +#define SCNuLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, u ) ) +#define SCNuLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, u ) ) +#define SCNuLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, u ) ) +#define SCNuLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, u ) ) + +#define SCNuFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, u ) ) +#define SCNuFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, u ) ) +#define SCNuFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, u ) ) +#define SCNuFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, u ) ) + +#define SCNuMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, u ) ) +#define SCNuPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, u ) ) + +#define SCNx8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, x ) ) +#define SCNx16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, x ) ) +#define SCNx32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, x ) ) +#define SCNx64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, x ) ) + +#define SCNxLEAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_8_CONV, x ) ) +#define SCNxLEAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_16_CONV, x ) ) +#define SCNxLEAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_32_CONV, x ) ) +#define SCNxLEAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_64_CONV, x ) ) + +#define SCNxFAST8 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST8_CONV, x ) ) +#define SCNxFAST16 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST16_CONV, x ) ) +#define SCNxFAST32 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST32_CONV, x ) ) +#define SCNxFAST64 _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_FAST64_CONV, x ) ) + +#define SCNxMAX _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_MAX_CONV, x ) ) +#define SCNxPTR _PDCLIB_symbol2string( _PDCLIB_concat( _PDCLIB_PTR_CONV, x ) ) + +/* 7.8.2 Functions for greatest-width integer types */ + +/* Calculate the absolute value of j */ +intmax_t imaxabs( intmax_t j ); + +/* Return quotient (quot) and remainder (rem) of an integer division in the + imaxdiv_t struct. +*/ +imaxdiv_t imaxdiv( intmax_t numer, intmax_t denom ); + +/* Seperate the character array nptr into three parts: A (possibly empty) + sequence of whitespace characters, a character representation of an integer + to the given base, and trailing invalid characters (including the terminating + null character). If base is 0, assume it to be 10, unless the integer + representation starts with 0x / 0X (setting base to 16) or 0 (setting base to + 8). If given, base can be anything from 0 to 36, using the 26 letters of the + base alphabet (both lowercase and uppercase) as digits 10 through 35. + The integer representation is then converted into the return type of the + function. It can start with a '+' or '-' sign. If the sign is '-', the result + of the conversion is negated. + If the conversion is successful, the converted value is returned. If endptr + is not a NULL pointer, a pointer to the first trailing invalid character is + returned in *endptr. + If no conversion could be performed, zero is returned (and nptr in *endptr, + if endptr is not a NULL pointer). If the converted value does not fit into + the return type, the functions return INTMAX_MIN, INTMAX_MAX, or UINTMAX_MAX, + respectively, depending on the sign of the integer representation and the + return type, and errno is set to ERANGE. +*/ +/* This function is equivalent to strtol() / strtoul() in , but on + the potentially larger type. +*/ +intmax_t strtoimax( const char * restrict nptr, char * * restrict endptr, int base ); +uintmax_t strtoumax( const char * restrict nptr, char * * restrict endptr, int base ); + +/* TODO: wcstoimax(), wcstoumax() */ + +CPP_CHECK_END diff --git a/src/libraries/libc/include/iso646.h b/src/libraries/libc/include/iso646.h new file mode 100644 index 0000000..8564902 --- /dev/null +++ b/src/libraries/libc/include/iso646.h @@ -0,0 +1,18 @@ +#pragma once +/* Alternative spellings + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#define and && +#define and_eq &= +#define bitand & +#define bitor | +#define compl ~ +#define not ! +#define not_eq != +#define or || +#define or_eq |= +#define xor ^ +#define xor_eq ^= diff --git a/src/libraries/libc/include/j6libc/aux.h b/src/libraries/libc/include/j6libc/aux.h new file mode 100644 index 0000000..7d876b8 --- /dev/null +++ b/src/libraries/libc/include/j6libc/aux.h @@ -0,0 +1,57 @@ +#pragma once +/* Auxiliary PDCLib code + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* -------------------------------------------------------------------------- */ +/* You should not have to edit anything in this file; if you DO have to, it */ +/* would be considered a bug / missing feature: notify the author(s). */ +/* -------------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* Standard Version */ +/* -------------------------------------------------------------------------- */ + +/* Many a compiler gets this wrong, so you might have to hardcode it instead. */ + +#if __STDC__ != 1 +#error Compiler does not define _ _STDC_ _ to 1 (not standard-compliant)! +#endif + +#ifdef __cplusplus +#define restrict __restrict__ +#endif + +#ifndef __STDC_HOSTED__ +#error Compiler does not define _ _STDC_HOSTED_ _ (not standard-compliant)! +#elif __STDC_HOSTED__ == 0 +#define _PDCLIB_HOSTED 0 +#elif __STDC_HOSTED__ == 1 +#define _PDCLIB_HOSTED 1 +#else +#error Compiler does not define _ _STDC_HOSTED_ _ to 0 or 1 (not standard-compliant)! +#endif + +/* -------------------------------------------------------------------------- */ +/* Helper macros: */ +/* _PDCLIB_cc( x, y ) concatenates two preprocessor tokens without extending */ +/* _PDCLIB_concat( x, y ) concatenates two preprocessor tokens with extending */ +/* _PDCLIB_static_assert( e, m ) does a compile-time assertion of expression */ +/* e, with m as the failure message. */ +/* _PDCLIB_TYPE_SIGNED( type ) resolves to true if type is signed. */ +/* _PDCLIB_TWOS_COMPLEMENT( type ) resolves to true if two's complement is */ +/* used for type. */ +/* -------------------------------------------------------------------------- */ + +#define _PDCLIB_cc( x, y ) x ## y +#define _PDCLIB_concat( x, y ) _PDCLIB_cc( x, y ) + +#define _PDCLIB_static_assert( e, m ) enum { _PDCLIB_concat( _PDCLIB_assert_, __LINE__ ) = 1 / ( !!(e) ) } + +#define _PDCLIB_TYPE_SIGNED( type ) (((type) -1) < 0) +#define _PDCLIB_TWOS_COMPLEMENT( type ) ((type) ~ (type) 0 < 0 ) + +#define _PDCLIB_symbol2value( x ) #x +#define _PDCLIB_symbol2string( x ) _PDCLIB_symbol2value( x ) diff --git a/src/libraries/libc/include/j6libc/config.h b/src/libraries/libc/include/j6libc/config.h new file mode 100644 index 0000000..3913389 --- /dev/null +++ b/src/libraries/libc/include/j6libc/config.h @@ -0,0 +1,336 @@ +#pragma once +/* Internal PDCLib configuration + (Generic Template) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +/* -------------------------------------------------------------------------- */ +/* Misc */ +/* -------------------------------------------------------------------------- */ + +/* The character (sequence) your platform uses as newline. */ +#define _PDCLIB_endl "\n" + +/* exit() can signal success to the host environment by the value of zero or */ +/* the constant EXIT_SUCCESS. Failure is signaled by EXIT_FAILURE. Note that */ +/* any other return value is "implementation-defined", i.e. your environment */ +/* is not required to handle it gracefully. Set your definitions here. */ +#define _PDCLIB_SUCCESS 0 +#define _PDCLIB_FAILURE -1 + +/* qsort() in requires a function that swaps two memory areas. */ +/* Below is a naive implementation that can be improved significantly for */ +/* specific platforms, e.g. by swapping int instead of char. */ +#define _PDCLIB_memswp( i, j, size ) char tmp; do { tmp = *i; *i++ = *j; *j++ = tmp; } while ( --size ); + +/* -------------------------------------------------------------------------- */ +/* Integers */ +/* -------------------------------------------------------------------------- */ +/* Assuming 8-bit char, two's-complement architecture here. 'short' being */ +/* 16 bit, 'int' being either 16, 32 or 64 bit, 'long' being either 32 or 64 */ +/* bit (but 64 bit only if 'int' is 32 bit), and 'long long' being 64 bit if */ +/* 'long' is not, 64 or 128 bit otherwise. */ +/* Author is quite willing to support other systems but would like to hear of */ +/* interest in such support and details on the to-be-supported architecture */ +/* first, before going to lengths about it. */ +/* -------------------------------------------------------------------------- */ + +/* Set to 0 if your 'char' type is unsigned. */ +#ifdef __CHAR_UNSIGNED__ +#define _PDCLIB_CHAR_SIGNED 0 +#else +#define _PDCLIB_CHAR_SIGNED 1 +#endif + +/* Width of the integer types short, int, long, and long long, in bytes. */ +/* SHRT == 2, INT >= SHRT, LONG >= INT >= 4, LLONG >= LONG - check your */ +/* compiler manuals. */ +#define _PDCLIB_SHRT_BYTES 2 +#define _PDCLIB_INT_BYTES 4 +#ifdef __LP64__ +#define _PDCLIB_LONG_BYTES 8 +#else +#define _PDCLIB_LONG_BYTES 4 +#endif +#define _PDCLIB_LLONG_BYTES 8 + +/* defines the div() function family that allows taking quotient */ +/* and remainder of an integer division in one operation. Many platforms */ +/* support this in hardware / opcode, and the standard permits ordering of */ +/* the return structure in any way to fit the hardware. That is why those */ +/* structs can be configured here. */ + +struct _PDCLIB_div_t +{ + int quot; + int rem; +}; + +struct _PDCLIB_ldiv_t +{ + long int quot; + long int rem; +}; + +struct _PDCLIB_lldiv_t +{ + long long int quot; + long long int rem; +}; + +/* defines imaxdiv(), which is equivalent to the div() function */ +/* family (see further above) with intmax_t as basis. */ + +struct _PDCLIB_imaxdiv_t +{ + intmax_t quot; + intmax_t rem; +}; + +/* -------------------------------------------------------------------------- */ +/* Time types */ +/* -------------------------------------------------------------------------- */ + +/* See for a couple of comments on these types and their semantics. */ + +#define _PDCLIB_time long + +#define _PDCLIB_clock long +#define _PDCLIB_CLOCKS_PER_SEC 1000000 + +#define _PDCLIB_TIME_UTC 1 + +/* -------------------------------------------------------------------------- */ +/* Floating Point */ +/* -------------------------------------------------------------------------- */ + +/* Whether the implementation rounds toward zero (0), to nearest (1), toward + positive infinity (2), or toward negative infinity (3). (-1) signifies + indeterminable rounding, any other value implementation-specific rounding. +*/ +#define _PDCLIB_FLT_ROUNDS -1 + +/* Whether the implementation uses exact-width precision (0), promotes float + to double (1), or promotes float and double to long double (2). (-1) + signifies indeterminable behaviour, any other value implementation-specific + behaviour. +*/ +#define _PDCLIB_FLT_EVAL_METHOD -1 + +/* "Number of the decimal digits (n), such that any floating-point number in the + widest supported floating type with p(max) radix (b) digits can be rounded to + a floating-point number with (n) decimal digits and back again without change + to the value p(max) log(10)b if (b) is a power of 10, [1 + p(max) log(10)b] + otherwise." + 64bit IEC 60559 double format (53bit mantissa) is DECIMAL_DIG 17. + 80bit IEC 60559 double-extended format (64bit mantissa) is DECIMAL_DIG 21. +*/ +#define _PDCLIB_DECIMAL_DIG 17 + +/* -------------------------------------------------------------------------- */ +/* Platform-dependent macros defined by the standard headers. */ +/* -------------------------------------------------------------------------- */ + +/* The offsetof macro + Contract: Expand to an integer constant expression of type size_t, which + represents the offset in bytes to the structure member from the beginning + of the structure. If the specified member is a bitfield, behaviour is + undefined. + There is no standard-compliant way to do this. + This implementation casts an integer zero to 'pointer to type', and then + takes the address of member. This is undefined behaviour but should work on + most compilers. +*/ +#define _PDCLIB_offsetof( type, member ) ( (size_t) &( ( (type *) 0 )->member ) ) + +/* Variable Length Parameter List Handling () + The macros defined by are highly dependent on the calling + conventions used, and you probably have to replace them with builtins of + your compiler. +*/ + +#if defined( __i386 ) + +/* The following generic implementation works only for pure + stack-based architectures, and only if arguments are aligned to pointer + type. Credits to Michael Moody, who contributed this to the Public Domain. +*/ + +/* Internal helper macro. va_round is not part of . */ +#define _PDCLIB_va_round( type ) ( (sizeof(type) + sizeof(void *) - 1) & ~(sizeof(void *) - 1) ) + +typedef char * _PDCLIB_va_list; +#define _PDCLIB_va_arg( ap, type ) ( (ap) += (_PDCLIB_va_round(type)), ( *(type*) ( (ap) - (_PDCLIB_va_round(type)) ) ) ) +#define _PDCLIB_va_copy( dest, src ) ( (dest) = (src), (void)0 ) +#define _PDCLIB_va_end( ap ) ( (ap) = (void *)0, (void)0 ) +#define _PDCLIB_va_start( ap, parmN ) ( (ap) = (char *) &parmN + ( _PDCLIB_va_round(parmN) ), (void)0 ) + +#elif defined( __x86_64 ) || defined( __arm__ ) + +/* No way to cover x86_64 or arm with a generic implementation, as it uses + register-based parameter passing. Using compiler builtins here. +*/ +typedef __builtin_va_list _PDCLIB_va_list; +#define _PDCLIB_va_arg( ap, type ) ( __builtin_va_arg( ap, type ) ) +#define _PDCLIB_va_copy( dest, src ) ( __builtin_va_copy( dest, src ) ) +#define _PDCLIB_va_end( ap ) ( __builtin_va_end( ap ) ) +#define _PDCLIB_va_start( ap, parmN ) ( __builtin_va_start( ap, parmN ) ) + +#else + +#error Please create your own config.h. Using the existing one as-is will not work. + +#endif + +/* -------------------------------------------------------------------------- */ +/* OS "glue", part 1 */ +/* These are values and data type definitions that you would have to adapt to */ +/* the capabilities and requirements of your OS. */ +/* The actual *functions* of the OS interface are declared in glue.h. */ +/* -------------------------------------------------------------------------- */ + +/* Memory management -------------------------------------------------------- */ + +/* Set this to the page size of your OS. If your OS does not support paging, set + to an appropriate value. (Too small, and malloc() will call the kernel too + often. Too large, and you will waste memory.) +*/ +#define _PDCLIB_PAGESIZE 4096 + +/* Set this to the minimum memory node size. Any malloc() for a smaller size + will be satisfied by a malloc() of this size instead (to avoid excessive + fragmentation). +*/ +#define _PDCLIB_MINALLOC 8 + +/* I/O ---------------------------------------------------------------------- */ + +/* The type of the file descriptor returned by _PDCLIB_open(). */ +typedef int _PDCLIB_fd_t; + +/* The value (of type _PDCLIB_fd_t) returned by _PDCLIB_open() if the operation + failed. +*/ +#define _PDCLIB_NOHANDLE ( (_PDCLIB_fd_t) -1 ) + +/* The default size for file buffers. Must be at least 256. */ +#define _PDCLIB_BUFSIZ 1024 + +/* The minimum number of files the implementation can open simultaneously. Must + be at least 8. Depends largely on how the bookkeeping is done by fopen() / + freopen() / fclose(). The example implementation limits the number of open + files only by available memory. +*/ +#define _PDCLIB_FOPEN_MAX 8 + +/* Length of the longest filename the implementation guarantees to support. */ +#define _PDCLIB_FILENAME_MAX 128 + +/* Maximum length of filenames generated by tmpnam(). (See tmpfile.c.) */ +#define _PDCLIB_L_tmpnam 46 + +/* Number of distinct file names that can be generated by tmpnam(). */ +#define _PDCLIB_TMP_MAX 50 + +/* The values of SEEK_SET, SEEK_CUR and SEEK_END, used by fseek(). + Since at least one platform (POSIX) uses the same symbols for its own "seek" + function, we use whatever the host defines (if it does define them). +*/ +#define _PDCLIB_SEEK_SET 0 +#define _PDCLIB_SEEK_CUR 1 +#define _PDCLIB_SEEK_END 2 + +/* The number of characters that can be buffered with ungetc(). The standard + guarantees only one (1); anything larger would make applications relying on + this capability dependent on implementation-defined behaviour (not good). +*/ +#define _PDCLIB_UNGETCBUFSIZE 1 + +/* errno -------------------------------------------------------------------- */ + +/* These are the values that _PDCLIB_errno can be set to by the library. + + By keeping PDCLib's errno in the _PDCLIB_* namespace, the library is capable + to "translate" between errno values used by the hosting operating system and + those used and passed out by the library. + + Example: In the example platform, the remove() function uses the unlink() + system call as backend. Linux sets its errno to EISDIR if you try to unlink() + a directory, but POSIX demands EPERM. Within the remove() function, you can + catch the 'errno == EISDIR', and set '_PDCLIB_errno = _PDCLIB_EPERM'. Anyone + using PDCLib's will "see" EPERM instead of EISDIR (the _PDCLIB_* + prefix removed by mechanics). + + If you do not want that kind of translation, you might want to "match" the + values used by PDCLib with those used by the host OS, as to avoid confusion. + + The standard only defines three distinct errno values: ERANGE, EDOM, and + EILSEQ. The standard leaves it up to "the implementation" whether there are + any more beyond those three. There is some controversy as to whether errno is + such a good idea at all, so you might want to come up with a different error + reporting facility for your platform. Since errno values beyond the three + defined by the standard are not portable anyway (unless you look at POSIX), + having your own error reporting facility would not hurt anybody either. +*/ +#define _PDCLIB_ERANGE 1 +#define _PDCLIB_EDOM 2 +#define _PDCLIB_EILSEQ 3 +#define _PDCLIB_ENOMEM 12 +#define _PDCLIB_EINVAL 22 + +/* The following is not strictly "configuration", but there is no better place + to explain it than here. + + PDCLib strives to be as generic as possible, so by default it does NOT define + any values beyond the three standard ones above, even where it would have + been prudent and convenient to do so. Any errno "caught" from the host OS, + and some internal error conditions as well, are all lumped together into the + value of '_PDCLIB_ERROR'. + + '_PDCLIB_ERROR' is STRICLY meant as a PLACEHOLDER only. + + You should NEVER ship an adaption of PDCLib still using that particular + value. You should NEVER write code that *tests* for that value. Indeed it is + not even conforming, since errno values should be defined as beginning with + an uppercase 'E', and there is no mechanics in to unmask that + particular value (for exactly that reason). + + There also is no error message available for this value through either the + strerror() or perror() functions. It is being reported as "unknown" error. + + The idea is that you scan the source of PDCLib for occurrences of this macro + and replace _PDCLIB_ERROR with whatever additional errno value you came up + with for your platform. + + If you cannot find it within you to do that, tell your clients to check for + an errno value larger than zero. That, at least, would be standard compliant + (and fully portable). +*/ +#define _PDCLIB_ERROR 4 + +/* The maximum value that errno can be set to. This is used to set the size */ +/* of the array in struct _PDCLIB_lc_text_t holding error messages for the */ +/* strerror() and perror() functions. (If you change this value because you */ +/* are using additional errno values, you *HAVE* to provide appropriate error */ +/* messages for *ALL* locales.) */ +/* Default is 4 (0, ERANGE, EDOM, EILSEQ). */ +#define _PDCLIB_ERRNO_MAX 4 + +/* locale data -------------------------------------------------------------- */ + +/* The default path where PDCLib should look for its locale data. */ +/* Must end with the appropriate separator character. */ +#define _PDCLIB_LOCALE_PATH "/usr/share/pdclib/i18n" + +/* The name of the environment variable that can be used to override that */ +/* path setting. */ +#define _PDCLIB_LOCALE_PATH_ENV PDCLIB_I18N + +#ifdef __CYGWIN__ +typedef unsigned int wint_t; +#endif + diff --git a/src/libraries/libc/include/j6libc/cpp.h b/src/libraries/libc/include/j6libc/cpp.h new file mode 100644 index 0000000..b14a5ef --- /dev/null +++ b/src/libraries/libc/include/j6libc/cpp.h @@ -0,0 +1,9 @@ +#pragma once + +#ifdef __cplusplus +#define CPP_CHECK_BEGIN extern "C" { +#define CPP_CHECK_END } +#else +#define CPP_CHECK_BEGIN +#define CPP_CHECK_END +#endif diff --git a/src/libraries/libc/include/j6libc/glue.h b/src/libraries/libc/include/j6libc/glue.h new file mode 100644 index 0000000..7c817cb --- /dev/null +++ b/src/libraries/libc/include/j6libc/glue.h @@ -0,0 +1,67 @@ +#pragma once +/* OS glue functions declaration + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/int.h" + +/* -------------------------------------------------------------------------- */ +/* OS "glue", part 2 */ +/* These are the functions you will have to touch, as they are where PDCLib */ +/* interfaces with the operating system. */ +/* They operate on data types partially defined by config.h. */ +/* -------------------------------------------------------------------------- */ + +/* stdlib.h */ + +/* A system call that terminates the calling process, returning a given status + to the environment. +*/ +_Noreturn void _PDCLIB_Exit( int status ); + +/* A system call that adds n pages of memory to the process heap (if n is + positive), or releases n pages from the process heap (if n is negative). + Return a (void *) pointing to the *former* end-of-heap if successful, NULL + otherwise. +*/ +void * _PDCLIB_allocpages( int n ); + + +/* stdio.h */ + +/* A system call that opens a file identified by name in a given mode. Return + a file descriptor uniquely identifying that file. + (The mode is the return value of the _PDCLIB_filemode() function.) +*/ +_PDCLIB_fd_t _PDCLIB_open( const char * const filename, unsigned int mode ); + +/* A system call that writes a stream's buffer. + Returns 0 on success, EOF on write error. + Sets stream error flags and errno appropriately on error. +*/ +int _PDCLIB_flushbuffer( struct _PDCLIB_file_t * stream ); + +/* A system call that fills a stream's buffer. + Returns 0 on success, EOF on read error / EOF. + Sets stream EOF / error flags and errno appropriately on error. +*/ +int _PDCLIB_fillbuffer( struct _PDCLIB_file_t * stream ); + +/* A system call that repositions within a file. Returns new offset on success, + -1 / errno on error. +*/ +int64_t _PDCLIB_seek( struct _PDCLIB_file_t * stream, int64_t offset, int whence ); + +/* A system call that closes a file identified by given file descriptor. Return + zero on success, non-zero otherwise. +*/ +int _PDCLIB_close( _PDCLIB_fd_t fd ); + +/* A system call that renames a file from given old name to given new name. + Return zero on success, non-zero otherwise. In case of failure, the file + must still be accessible by old name. Any handling of open files etc. is + done by standard rename() already. +*/ +int _PDCLIB_rename( const char * old, const char * new ); diff --git a/src/libraries/libc/include/j6libc/int.h b/src/libraries/libc/include/j6libc/int.h new file mode 100644 index 0000000..fb04780 --- /dev/null +++ b/src/libraries/libc/include/j6libc/int.h @@ -0,0 +1,347 @@ +#pragma once +/* PDCLib internal integer logic + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* -------------------------------------------------------------------------- */ +/* You should not have to edit anything in this file; if you DO have to, it */ +/* would be considered a bug / missing feature: notify the author(s). */ +/* -------------------------------------------------------------------------- */ + +#include + +#include "j6libc/config.h" +#include "j6libc/cpp.h" +#include "j6libc/aux.h" +#include "j6libc/int_widths.h" +#include "j6libc/size_t.h" +#include "j6libc/wchar_t.h" + +CPP_CHECK_BEGIN + +/* -------------------------------------------------------------------------- */ +/* Various internals */ +/* -------------------------------------------------------------------------- */ + +/* Flags for representing mode (see fopen()). Note these must fit the same + status field as the _IO?BF flags in and the internal flags below. +*/ +#define _PDCLIB_FREAD 8u +#define _PDCLIB_FWRITE 16u +#define _PDCLIB_FAPPEND 32u +#define _PDCLIB_FRW 64u +#define _PDCLIB_FBIN 128u + +/* Internal flags, made to fit the same status field as the flags above. */ +/* -------------------------------------------------------------------------- */ +/* free() the buffer memory on closing (false for user-supplied buffer) */ +#define _PDCLIB_FREEBUFFER 512u +/* stream has encountered error / EOF */ +#define _PDCLIB_ERRORFLAG 1024u +#define _PDCLIB_EOFFLAG 2048u +/* stream is wide-oriented */ +#define _PDCLIB_WIDESTREAM 4096u +/* stream is byte-oriented */ +#define _PDCLIB_BYTESTREAM 8192u +/* file associated with stream should be remove()d on closing (tmpfile()) */ +#define _PDCLIB_DELONCLOSE 16384u +/* stream handle should not be free()d on close (stdin, stdout, stderr) */ +#define _PDCLIB_STATIC 32768u + +/* Position / status structure for getpos() / fsetpos(). */ +struct _PDCLIB_fpos_t +{ + uint64_t offset; /* File position offset */ + int status; /* Multibyte parsing state (unused, reserved) */ +}; + +/* FILE structure */ +struct _PDCLIB_file_t +{ + _PDCLIB_fd_t handle; /* OS file handle */ + char * buffer; /* Pointer to buffer memory */ + size_t bufsize; /* Size of buffer */ + size_t bufidx; /* Index of current position in buffer */ + size_t bufend; /* Index of last pre-read character in buffer */ + struct _PDCLIB_fpos_t pos; /* Offset and multibyte parsing state */ + size_t ungetidx; /* Number of ungetc()'ed characters */ + unsigned char * ungetbuf; /* ungetc() buffer */ + unsigned int status; /* Status flags; see above */ + /* multibyte parsing status to be added later */ + char * filename; /* Name the current stream has been opened with */ + struct _PDCLIB_file_t * next; /* Pointer to next struct (internal) */ +}; + +/* -------------------------------------------------------------------------- */ +/* Various internals */ +/* -------------------------------------------------------------------------- */ + +typedef _PDCLIB_time _PDCLIB_time_t; +typedef _PDCLIB_clock _PDCLIB_clock_t; + +/* -------------------------------------------------------------------------- */ +/* Internal data types */ +/* -------------------------------------------------------------------------- */ + +/* Structure required by both atexit() and exit() for handling atexit functions */ +struct _PDCLIB_exitfunc_t +{ + struct _PDCLIB_exitfunc_t * next; + void (*func)( void ); +}; + +/* Structures required by malloc(), realloc(), and free(). */ +struct _PDCLIB_headnode_t +{ + struct _PDCLIB_memnode_t * first; + struct _PDCLIB_memnode_t * last; +}; + +struct _PDCLIB_memnode_t +{ + size_t size; + struct _PDCLIB_memnode_t * next; +}; + +/* Status structure required by _PDCLIB_print(). */ +struct _PDCLIB_status_t +{ + int base; /* base to which the value shall be converted */ + int_fast32_t flags; /* flags and length modifiers */ + size_t n; /* print: maximum characters to be written */ + /* scan: number matched conversion specifiers */ + size_t i; /* number of characters read/written */ + size_t current;/* chars read/written in the CURRENT conversion */ + char * s; /* *sprintf(): target buffer */ + /* *sscanf(): source string */ + size_t width; /* specified field width */ + int prec; /* specified field precision */ + struct _PDCLIB_file_t * stream; /* *fprintf() / *fscanf() stream */ + _PDCLIB_va_list arg; /* argument stack */ +}; + +/* -------------------------------------------------------------------------- */ +/* Declaration of helper functions (implemented in functions/_PDCLIB). */ +/* -------------------------------------------------------------------------- */ + +/* This is the main function called by atoi(), atol() and atoll(). */ +intmax_t _PDCLIB_atomax( const char * s ); + +/* Two helper functions used by strtol(), strtoul() and long long variants. */ +const char * _PDCLIB_strtox_prelim( const char * p, char * sign, int * base ); +uintmax_t _PDCLIB_strtox_main( const char ** p, unsigned int base, uintmax_t error, uintmax_t limval, int limdigit, char * sign ); + +/* Digits arrays used by various integer conversion functions */ +extern const char _PDCLIB_digits[]; +extern const char _PDCLIB_Xdigits[]; + +/* The worker for all printf() type of functions. The pointer spec should point + to the introducing '%' of a conversion specifier. The status structure is to + be that of the current printf() function, of which the members n, s, stream + and arg will be preserved; i will be updated; and all others will be trashed + by the function. + Returns a pointer to the first character not parsed as conversion specifier. +*/ +const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status ); + +/* The worker for all scanf() type of functions. The pointer spec should point + to the introducing '%' of a conversion specifier. The status structure is to + be that of the current scanf() function, of which the member stream will be + preserved; n, i, and s will be updated; and all others will be trashed by + the function. + Returns a pointer to the first character not parsed as conversion specifier, + or NULL in case of error. + FIXME: Should distinguish between matching and input error +*/ +const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ); + +/* Parsing any fopen() style filemode string into a number of flags. */ +unsigned int _PDCLIB_filemode( const char * mode ); + +/* Sanity checking and preparing of read buffer, should be called first thing + by any stdio read-data function. + Returns 0 on success, EOF on error. + On error, EOF / error flags and errno are set appropriately. +*/ +int _PDCLIB_prepread( struct _PDCLIB_file_t * stream ); + +/* Sanity checking, should be called first thing by any stdio write-data + function. + Returns 0 on success, EOF on error. + On error, error flags and errno are set appropriately. +*/ +int _PDCLIB_prepwrite( struct _PDCLIB_file_t * stream ); + +/* Closing all streams on program exit */ +void _PDCLIB_closeall( void ); + +/* Check if a given year is a leap year. Parameter is offset to 1900. */ +int _PDCLIB_is_leap( int year_offset ); + +/* Read a specified number of lines from a file stream; return a pointer to + allocated memory holding the lines (newlines replaced with zero terminators) + or NULL in case of error. +*/ +char * _PDCLIB_load_lines( struct _PDCLIB_file_t * fh, size_t lines ); + +/* -------------------------------------------------------------------------- */ +/* errno */ +/* -------------------------------------------------------------------------- */ + +/* If PDCLib would call its error number "errno" directly, there would be no way + to catch its value from underlying system calls that also use it (i.e., POSIX + operating systems). That is why we use an internal name, providing a means to + access it through . +*/ +extern int _PDCLIB_errno; + +/* A mechanism for delayed evaluation. (Not sure if this is really necessary, so + no detailed documentation on the "why".) +*/ +int * _PDCLIB_errno_func( void ); + +/* -------------------------------------------------------------------------- */ +/* support */ +/* -------------------------------------------------------------------------- */ + +#define _PDCLIB_LC_ALL 0 +#define _PDCLIB_LC_COLLATE 1 +#define _PDCLIB_LC_CTYPE 2 +#define _PDCLIB_LC_MONETARY 3 +#define _PDCLIB_LC_NUMERIC 4 +#define _PDCLIB_LC_TIME 5 +#define _PDCLIB_LC_MESSAGES 6 +#define _PDCLIB_LC_COUNT 7 + +#define _PDCLIB_CTYPE_ALPHA 1 +#define _PDCLIB_CTYPE_BLANK 2 +#define _PDCLIB_CTYPE_CNTRL 4 +#define _PDCLIB_CTYPE_GRAPH 8 +#define _PDCLIB_CTYPE_PUNCT 16 +#define _PDCLIB_CTYPE_SPACE 32 +#define _PDCLIB_CTYPE_LOWER 64 +#define _PDCLIB_CTYPE_UPPER 128 + +#define _PDCLIB_CHARSET_SIZE ( 1 << __CHAR_BIT__ ) + +struct _PDCLIB_lc_lconv_numeric_t +{ + char * decimal_point; + char * thousands_sep; + char * grouping; +}; + +struct _PDCLIB_lc_lconv_monetary_t +{ + char * mon_decimal_point; + char * mon_thousands_sep; + char * mon_grouping; + char * positive_sign; + char * negative_sign; + char * currency_symbol; + char * int_curr_symbol; + char frac_digits; + char p_cs_precedes; + char n_cs_precedes; + char p_sep_by_space; + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; + char int_frac_digits; + char int_p_cs_precedes; + char int_n_cs_precedes; + char int_p_sep_by_space; + char int_n_sep_by_space; + char int_p_sign_posn; + char int_n_sign_posn; +}; + +struct _PDCLIB_lc_numeric_monetary_t +{ + struct lconv * lconv; + int numeric_alloced; + int monetary_alloced; +}; + +extern struct _PDCLIB_lc_numeric_monetary_t _PDCLIB_lc_numeric_monetary; + +struct _PDCLIB_lc_collate_t +{ + int alloced; + /* 1..3 code points */ + /* 1..8, 18 collation elements of 3 16-bit integers */ +}; + +extern struct _PDCLIB_lc_collate_t _PDCLIB_lc_collate; + +struct _PDCLIB_lc_ctype_entry_t +{ + uint16_t flags; + unsigned char upper; + unsigned char lower; +}; + +struct _PDCLIB_lc_ctype_t +{ + int alloced; + int digits_low; + int digits_high; + int Xdigits_low; + int Xdigits_high; + int xdigits_low; + int xdigits_high; + struct _PDCLIB_lc_ctype_entry_t * entry; +}; + +extern struct _PDCLIB_lc_ctype_t _PDCLIB_lc_ctype; + +struct _PDCLIB_lc_messages_t +{ + int alloced; + char * errno_texts[_PDCLIB_ERRNO_MAX]; /* strerror() / perror() */ +}; + +extern struct _PDCLIB_lc_messages_t _PDCLIB_lc_messages; + +struct _PDCLIB_lc_time_t +{ + int alloced; + char * month_name_abbr[12]; /* month names, abbreviated */ + char * month_name_full[12]; /* month names, full */ + char * day_name_abbr[7]; /* weekday names, abbreviated */ + char * day_name_full[7]; /* weekday names, full */ + char * date_time_format; /* date / time format for strftime( "%c" ) */ + char * time_format_12h; /* 12-hour time format for strftime( "%r" ) */ + char * date_format; /* date format for strftime( "%x" ) */ + char * time_format; /* time format for strftime( "%X" ) */ + char * am_pm[2]; /* AM / PM designation */ +}; + +extern struct _PDCLIB_lc_time_t _PDCLIB_lc_time; + +struct _PDCLIB_lc_lconv_numeric_t * _PDCLIB_load_lc_numeric( const char * path, const char * locale ); +struct _PDCLIB_lc_lconv_monetary_t * _PDCLIB_load_lc_monetary( const char * path, const char * locale ); +struct _PDCLIB_lc_collate_t * _PDCLIB_load_lc_collate( const char * path, const char * locale ); +struct _PDCLIB_lc_ctype_t * _PDCLIB_load_lc_ctype( const char * path, const char * locale ); +struct _PDCLIB_lc_time_t * _PDCLIB_load_lc_time( const char * path, const char * locale ); +struct _PDCLIB_lc_messages_t * _PDCLIB_load_lc_messages( const char * path, const char * locale ); + +/* -------------------------------------------------------------------------- */ +/* Sanity checks */ +/* -------------------------------------------------------------------------- */ + +_PDCLIB_static_assert( sizeof( short ) == _PDCLIB_SHRT_BYTES, "Compiler disagrees on _PDCLIB_SHRT_BYTES." ); +_PDCLIB_static_assert( sizeof( int ) == _PDCLIB_INT_BYTES, "Compiler disagrees on _PDCLIB_INT_BYTES." ); +_PDCLIB_static_assert( sizeof( long ) == _PDCLIB_LONG_BYTES, "Compiler disagrees on _PDCLIB_LONG_BYTES." ); +_PDCLIB_static_assert( sizeof( long long ) == _PDCLIB_LLONG_BYTES, "Compiler disagrees on _PDCLIB_LLONG_BYTES." ); + +_PDCLIB_static_assert( ( (char)-1 < 0 ) == _PDCLIB_CHAR_SIGNED, "Compiler disagrees on _PDCLIB_CHAR_SIGNED." ); + +_PDCLIB_static_assert( sizeof( sizeof( int ) ) == sizeof( size_t ), "Compiler disagrees on size_t." ); +_PDCLIB_static_assert( sizeof( wchar_t ) == sizeof( L'x' ), "Compiler disagrees on _PDCLIB_wchar." ); +_PDCLIB_static_assert( sizeof( void * ) == sizeof( intptr_t ), "Compiler disagrees on intptr." ); +_PDCLIB_static_assert( sizeof( &_PDCLIB_digits[1] - &_PDCLIB_digits[0] ) == sizeof( ptrdiff_t ), "Compiler disagrees on ptrdiff_t." ); + +CPP_CHECK_END diff --git a/src/libraries/libc/include/j6libc/int_widths.h b/src/libraries/libc/include/j6libc/int_widths.h new file mode 100644 index 0000000..9889c4d --- /dev/null +++ b/src/libraries/libc/include/j6libc/int_widths.h @@ -0,0 +1,57 @@ +#pragma once +/* Type definitions: fixed-width integral types */ + +/* 7.18.1.1 Exact-width integer types. */ + +typedef __INT8_TYPE__ int8_t; +typedef __INT16_TYPE__ int16_t; +typedef __INT32_TYPE__ int32_t; +typedef __INT64_TYPE__ int64_t; + +typedef __UINT8_TYPE__ uint8_t; +typedef __UINT16_TYPE__ uint16_t; +typedef __UINT32_TYPE__ uint32_t; +typedef __UINT64_TYPE__ uint64_t; + +/* 7.18.1.2 Minimum-width integer types */ + +/* You are allowed to add more types here, e.g. int_least24_t. For the standard + types, int_leastN_t is equivalent to the corresponding exact type intN_t by + definition. +*/ + +typedef __INT_LEAST8_TYPE__ int_least8_t; +typedef __INT_LEAST16_TYPE__ int_least16_t; +typedef __INT_LEAST32_TYPE__ int_least32_t; +typedef __INT_LEAST64_TYPE__ int_least64_t; + +typedef __UINT_LEAST8_TYPE__ uint_least8_t; +typedef __UINT_LEAST16_TYPE__ uint_least16_t; +typedef __UINT_LEAST32_TYPE__ uint_least32_t; +typedef __UINT_LEAST64_TYPE__ uint_least64_t; + +/* 7.18.1.3 Fastest minimum-width integer types */ + +/* You are allowed to add more types here, e.g. int_fast24_t. */ + +typedef __INT_FAST8_TYPE__ int_fast8_t; +typedef __INT_FAST16_TYPE__ int_fast16_t; +typedef __INT_FAST32_TYPE__ int_fast32_t; +typedef __INT_FAST64_TYPE__ int_fast64_t; + +typedef __UINT_FAST8_TYPE__ uint_fast8_t; +typedef __UINT_FAST16_TYPE__ uint_fast16_t; +typedef __UINT_FAST32_TYPE__ uint_fast32_t; +typedef __UINT_FAST64_TYPE__ uint_fast64_t; + +/* 7.18.1.4 Integer types capable of holding object pointers */ + +typedef __INTPTR_TYPE__ intptr_t; +typedef __UINTPTR_TYPE__ uintptr_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; + +/* 7.18.1.5 Greatest-width integer types */ + +typedef __INTMAX_TYPE__ intmax_t; +typedef __UINTMAX_TYPE__ uintmax_t; + diff --git a/src/libraries/libc/include/j6libc/max_align_t.h b/src/libraries/libc/include/j6libc/max_align_t.h new file mode 100644 index 0000000..c847953 --- /dev/null +++ b/src/libraries/libc/include/j6libc/max_align_t.h @@ -0,0 +1,14 @@ +#pragma once + +#if __has_include("__stddef_max_align_t.h") +#include "__stddef_max_align_t.h" +#elif __BIGGEST_ALIGNMENT__ == __SIZEOF_LONG_DOUBLE__ +typedef long double max_align_t; +#elif __BIGGEST_ALIGNMENT__ == __SIZEOF_DOUBLE__ +typedef double max_align_t; +#elif __BIGGEST_ALIGNMENT__ == __SIZEOF_LONG_LONG__ +typedef long long max_align_t; +#else +#error Can't figure out size of max_align_t! +#endif + diff --git a/src/libraries/libc/include/j6libc/null.h b/src/libraries/libc/include/j6libc/null.h new file mode 100644 index 0000000..d1d53d9 --- /dev/null +++ b/src/libraries/libc/include/j6libc/null.h @@ -0,0 +1,6 @@ +#pragma once +/* Constant definition: NULL */ + +#ifndef NULL +#define NULL 0 +#endif diff --git a/src/libraries/libc/include/j6libc/sig_atomic_t.h b/src/libraries/libc/include/j6libc/sig_atomic_t.h new file mode 100644 index 0000000..4bce55c --- /dev/null +++ b/src/libraries/libc/include/j6libc/sig_atomic_t.h @@ -0,0 +1,14 @@ +#pragma once +/* Type definition: sig_atomic_t */ + +#if __is_identifier(sig_atomic_t) +#if __SIG_ATOMIC_WIDTH__ == 16 +typedef int16_t sig_atomic_t; +#elif __SIG_ATOMIC_WIDTH__ == 32 +typedef int32_t sig_atomic_t; +#elif __SIG_ATOMIC_WIDTH__ == 64 +typedef int64_t sig_atomic_t; +#else +#error "Unknown size of sig_atomic_t" __SIG_ATOMIC_WIDTH__ +#endif +#endif diff --git a/src/libraries/libc/include/j6libc/size_t.h b/src/libraries/libc/include/j6libc/size_t.h new file mode 100644 index 0000000..d698f18 --- /dev/null +++ b/src/libraries/libc/include/j6libc/size_t.h @@ -0,0 +1,6 @@ +#pragma once +/* Type definition: size_t */ + +#if __is_identifier(size_t) +typedef __SIZE_TYPE__ size_t; +#endif diff --git a/src/libraries/libc/include/j6libc/wchar_t.h b/src/libraries/libc/include/j6libc/wchar_t.h new file mode 100644 index 0000000..5637bbb --- /dev/null +++ b/src/libraries/libc/include/j6libc/wchar_t.h @@ -0,0 +1,6 @@ +#pragma once +/* Type definition: wchar_t */ + +#if __is_identifier(wchar_t) +typedef __WCHAR_TYPE__ wchar_t; +#endif diff --git a/src/libraries/libc/include/j6libc/wctype_t.h b/src/libraries/libc/include/j6libc/wctype_t.h new file mode 100644 index 0000000..c9c8dd9 --- /dev/null +++ b/src/libraries/libc/include/j6libc/wctype_t.h @@ -0,0 +1,6 @@ +#pragma once +/* Type definitions: wctype_t wctrans_t */ + +typedef uint32_t wctrans_t; +typedef uint32_t wctype_t ; + diff --git a/src/libraries/libc/include/j6libc/wint_t.h b/src/libraries/libc/include/j6libc/wint_t.h new file mode 100644 index 0000000..86b3aa9 --- /dev/null +++ b/src/libraries/libc/include/j6libc/wint_t.h @@ -0,0 +1,5 @@ +#pragma once +/* Type definition: wint_t */ + +typedef __WINT_TYPE__ wint_t; + diff --git a/src/libraries/libc/include/limits.h b/src/libraries/libc/include/limits.h new file mode 100644 index 0000000..5c02621 --- /dev/null +++ b/src/libraries/libc/include/limits.h @@ -0,0 +1,36 @@ +#pragma once +/* Sizes of integer types + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/int.h" + +/* TODO: Defined to 1 as multibyte characters are not supported yet. */ +#define MB_LEN_MAX 1 + +#define SCHAR_MAX __SCHAR_MAX__ +#define SCHAR_MIN ((-SCHAR_MAX) - 1) +#define UCHAR_MAX (((unsigned char)SCHAR_MAX << 1) | 1) + +#define CHAR_BIT __CHAR_BIT__ +#define CHAR_MAX SCHAR_MAX +#define CHAR_MIN SCHAR_MIN + +#define SHRT_MAX __SHRT_MAX__ +#define SHRT_MIN ((-SHRT_MAX) - 1) +#define USHRT_MAX (((unsigned short)SHRT_MAX << 1) | 1) + +#define INT_MAX __INT_MAX__ +#define INT_MIN ((-INT_MAX) - 1) +#define UINT_MAX (((unsigned int)INT_MAX << 1) | 1) + +#define LONG_MAX __LONG_MAX__ +#define LONG_MIN ((-LONG_MAX) - 1) +#define ULONG_MAX (((unsigned long)LONG_MAX << 1) | 1) + +#define LLONG_MAX __LONG_LONG_MAX__ +#define LLONG_MIN ((-LLONG_MAX) - 1) +#define ULLONG_MAX (((unsigned long long)LLONG_MAX << 1) | 1) + diff --git a/src/libraries/libc/include/locale.h b/src/libraries/libc/include/locale.h new file mode 100644 index 0000000..e93f21e --- /dev/null +++ b/src/libraries/libc/include/locale.h @@ -0,0 +1,96 @@ +#pragma once +/* Localization + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/cpp.h" +#include "j6libc/int.h" +#include "j6libc/null.h" + +CPP_CHECK_BEGIN + +/* The structure returned by localeconv(). + + The values for *_sep_by_space: + 0 - no space + 1 - if symbol and sign are adjacent, a space seperates them from the value; + otherwise a space seperates the symbol from the value + 2 - if symbol and sign are adjacent, a space seperates them; otherwise a + space seperates the sign from the value + + The values for *_sign_posn: + 0 - Parentheses surround value and symbol + 1 - sign precedes value and symbol + 2 - sign succeeds value and symbol + 3 - sign immediately precedes symbol + 4 - sign immediately succeeds symbol +*/ +struct lconv +{ + char * decimal_point; /* decimal point character */ /* LC_NUMERIC */ + char * thousands_sep; /* character for seperating groups of digits */ /* LC_NUMERIC */ + char * grouping; /* string indicating the size of digit groups */ /* LC_NUMERIC */ + char * mon_decimal_point; /* decimal point for monetary quantities */ /* LC_MONETARY */ + char * mon_thousands_sep; /* thousands_sep for monetary quantities */ /* LC_MONETARY */ + char * mon_grouping; /* grouping for monetary quantities */ /* LC_MONETARY */ + char * positive_sign; /* string indicating nonnegative mty. qty. */ /* LC_MONETARY */ + char * negative_sign; /* string indicating negative mty. qty. */ /* LC_MONETARY */ + char * currency_symbol; /* local currency symbol (e.g. '$') */ /* LC_MONETARY */ + char * int_curr_symbol; /* international currency symbol (e.g. "USD" */ /* LC_MONETARY */ + char frac_digits; /* fractional digits in local monetary qty. */ /* LC_MONETARY */ + char p_cs_precedes; /* if currency_symbol precedes positive qty. */ /* LC_MONETARY */ + char n_cs_precedes; /* if currency_symbol precedes negative qty. */ /* LC_MONETARY */ + char p_sep_by_space; /* if it is seperated by space from pos. qty. */ /* LC_MONETARY */ + char n_sep_by_space; /* if it is seperated by space from neg. qty. */ /* LC_MONETARY */ + char p_sign_posn; /* positioning of positive_sign for mon. qty. */ /* LC_MONETARY */ + char n_sign_posn; /* positioning of negative_sign for mon. qty. */ /* LC_MONETARY */ + char int_frac_digits; /* Same as above, for international format */ /* LC_MONETARY */ + char int_p_cs_precedes; /* Same as above, for international format */ /* LC_MONETARY */ + char int_n_cs_precedes; /* Same as above, for international format */ /* LC_MONETARY */ + char int_p_sep_by_space; /* Same as above, for international format */ /* LC_MONETARY */ + char int_n_sep_by_space; /* Same as above, for international format */ /* LC_MONETARY */ + char int_p_sign_posn; /* Same as above, for international format */ /* LC_MONETARY */ + char int_n_sign_posn; /* Same as above, for international format */ /* LC_MONETARY */ +}; + +/* First arguments to setlocale(). + NOTE: If you add to / modify these, look at functions/locale/setlocale.c + and keep things in sync. +*/ +/* Entire locale */ +#define LC_ALL _PDCLIB_LC_ALL +/* Collation (strcoll(), strxfrm()) */ +#define LC_COLLATE _PDCLIB_LC_COLLATE +/* Character types (, ) */ +#define LC_CTYPE _PDCLIB_LC_CTYPE +/* Monetary formatting (as returned by localeconv) */ +#define LC_MONETARY _PDCLIB_LC_MONETARY +/* Decimal-point character (for printf() / scanf() functions), string + conversions, nonmonetary formatting as returned by localeconv +*/ +#define LC_NUMERIC _PDCLIB_LC_NUMERIC +/* Time formats (strftime(), wcsftime()) */ +#define LC_TIME _PDCLIB_LC_TIME +/* Messages (not specified but allowed by C99, and specified by POSIX) + (used by perror() / strerror()) +*/ +#define LC_MESSAGES _PDCLIB_LC_MESSAGES + +/* The category parameter can be any of the LC_* macros to specify if the call + to setlocale() shall affect the entire locale or only a portion thereof. + The category locale specifies which locale should be switched to, with "C" + being the minimal default locale, and "" being the locale-specific native + environment. A NULL pointer makes setlocale() return the *current* setting. + Otherwise, returns a pointer to a string associated with the specified + category for the new locale. +*/ +char * setlocale( int category, const char * locale ); + +/* Returns a struct lconv initialized to the values appropriate for the current + locale setting. +*/ +struct lconv * localeconv( void ); + +CPP_CHECK_END diff --git a/src/libraries/libc/include/signal.h b/src/libraries/libc/include/signal.h new file mode 100644 index 0000000..273184a --- /dev/null +++ b/src/libraries/libc/include/signal.h @@ -0,0 +1,86 @@ +#pragma once +/* Signal handling + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/config.h" +#include "j6libc/cpp.h" +#include "j6libc/sig_atomic_t.h" + +CPP_CHECK_BEGIN + +/* Signals ------------------------------------------------------------------ */ + +/* A word on signals, to the people using PDCLib in their OS projects. + + The definitions of the C standard leave about everything that *could* be + useful to be "implementation defined". Without additional, non-standard + arrangements, it is not possible to turn them into a useful tool. + + This example implementation chose to "not generate any of these signals, + except as a result of explicit calls to the raise function", which is + allowed by the standard but of course does nothing for the usefulness of + . + + A useful signal handling would: + 1) make signal() a system call that registers the signal handler with the OS + 2) make raise() a system call triggering an OS signal to the running process + 3) make provisions that further signals of the same type are blocked until + the signal handler returns (optional for SIGILL) +*/ + +/* These are the values used by Linux. */ + +/* Abnormal termination / abort() */ +#define SIGABRT 6 +/* Arithmetic exception / division by zero / overflow */ +#define SIGFPE 8 +/* Illegal instruction */ +#define SIGILL 4 +/* Interactive attention signal */ +#define SIGINT 2 +/* Invalid memory access */ +#define SIGSEGV 11 +/* Termination request */ +#define SIGTERM 15 + +typedef void (*sighandler_t)(int); + +/* The following should be defined to pointer values that could NEVER point to + a valid signal handler function. (They are used as special arguments to + signal().) Again, these are the values used by Linux. +*/ +#define SIG_DFL (sighandler_t)0 +#define SIG_ERR (sighandler_t)-1 +#define SIG_IGN (sighandler_t)1 + +/* Installs a signal handler "func" for the given signal. + A signal handler is a function that takes an integer as argument (the signal + number) and returns void. + + Note that a signal handler can do very little else than: + 1) assign a value to a static object of type "volatile sig_atomic_t", + 2) call signal() with the value of sig equal to the signal received, + 3) call _Exit(), + 4) call abort(). + Virtually everything else is undefind. + + The signal() function returns the previous installed signal handler, which + at program start may be SIG_DFL or SIG_ILL. (This implementation uses + SIG_DFL for all handlers.) If the request cannot be honored, SIG_ERR is + returned and errno is set to an unspecified positive value. +*/ +sighandler_t signal( int sig, sighandler_t func ); + +/* Raises the given signal (executing the registered signal handler with the + given signal number as parameter). + This implementation does not prevent further signals of the same time from + occuring, but executes signal( sig, SIG_DFL ) before entering the signal + handler (i.e., a second signal before the signal handler re-registers itself + or SIG_IGN will end the program). + Returns zero if successful, nonzero otherwise. */ +int raise( int sig ); + +CPP_CHECK_END diff --git a/src/libraries/libc/include/stdalign.h b/src/libraries/libc/include/stdalign.h new file mode 100644 index 0000000..4b30dbc --- /dev/null +++ b/src/libraries/libc/include/stdalign.h @@ -0,0 +1,12 @@ +#pragma once +/* Alignment + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#define alignas _Alignas +#define alignof _Alignof + +#define __alignas_is_defined 1 +#define __alignof_is_defined 1 diff --git a/src/libraries/libc/include/stdarg.h b/src/libraries/libc/include/stdarg.h new file mode 100644 index 0000000..92b6ecf --- /dev/null +++ b/src/libraries/libc/include/stdarg.h @@ -0,0 +1,15 @@ +#pragma once +/* Variable arguments + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/config.h" + +typedef _PDCLIB_va_list va_list; + +#define va_arg( ap, type ) _PDCLIB_va_arg( ap, type ) +#define va_copy( dest, src ) _PDCLIB_va_copy( dest, src ) +#define va_end( ap ) _PDCLIB_va_end( ap ) +#define va_start( ap, parmN ) _PDCLIB_va_start( ap, parmN ) diff --git a/src/libraries/libc/include/stdbool.h b/src/libraries/libc/include/stdbool.h new file mode 100644 index 0000000..b741dd2 --- /dev/null +++ b/src/libraries/libc/include/stdbool.h @@ -0,0 +1,14 @@ +#pragma once +/* Boolean type and values + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#ifndef __cplusplus +#define bool _Bool +#define true 1 +#define false 0 +#endif + +#define __bool_true_false_are_defined 1 diff --git a/src/libraries/libc/include/stddef.h b/src/libraries/libc/include/stddef.h new file mode 100644 index 0000000..10ec849 --- /dev/null +++ b/src/libraries/libc/include/stddef.h @@ -0,0 +1,22 @@ +#pragma once +/* Common definitions + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/int.h" +#include "j6libc/null.h" +#include "j6libc/max_align_t.h" +#include "j6libc/size_t.h" +#include "j6libc/wchar_t.h" + +typedef __PTRDIFF_TYPE__ ptrdiff_t; + +#if ! __has_include("__stddef_max_align_t.h") +typedef long double max_align_t; +#endif + +#ifndef offsetof +#define offsetof( type, member ) _PDCLIB_offsetof( type, member ) +#endif diff --git a/src/libraries/libc/include/stdint.h b/src/libraries/libc/include/stdint.h new file mode 100644 index 0000000..298208b --- /dev/null +++ b/src/libraries/libc/include/stdint.h @@ -0,0 +1,151 @@ +#pragma once +/* Integer types + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* 7.18.2 Limits of specified-width integer types */ + +#include +#include + +#ifdef __cplusplus +#ifndef __STDC_LIMIT_MACROS +#define _PDCLIB_NO_LIMIT_MACROS +#endif +#endif + +#ifndef _PDCLIB_NO_LIMIT_MACROS + +/* 7.18.2.1 Limits of exact-width integer types */ + +#define INT8_MAX __INT8_MAX__ +#define INT8_MIN ((-INT8_MAX) - 1) +#define UINT8_MAX __UINT8_MAX__ + +#define INT16_MAX __INT16_MAX__ +#define INT16_MIN ((-INT16_MAX) - 1) +#define UINT16_MAX __UINT16_MAX__ + +#define INT32_MAX __INT32_MAX__ +#define INT32_MIN ((-INT32_MAX) - 1) +#define UINT32_MAX __UINT32_MAX__ + +#define INT64_MAX __INT64_MAX__ +#define INT64_MIN ((-INT64_MAX) - 1) +#define UINT64_MAX __UINT64_MAX__ + +/* 7.18.2.2 Limits of minimum-width integer types */ + +/* For the standard widths, least and exact types are equivalent. + You are allowed to add more types here, e.g. int_least24_t. +*/ + +#define INT_LEAST8_MAX __INT_LEAST8_MAX__ +#define INT_LEAST8_MIN ((-INT_LEAST8_MAX) - 1) +#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__ + +#define INT_LEAST16_MAX __INT_LEAST16_MAX__ +#define INT_LEAST16_MIN ((-INT_LEAST16_MAX) - 1) +#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__ + +#define INT_LEAST32_MAX __INT_LEAST32_MAX__ +#define INT_LEAST32_MIN ((-INT_LEAST32_MAX) - 1) +#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__ + +#define INT_LEAST64_MAX __INT_LEAST64_MAX__ +#define INT_LEAST64_MIN ((-INT_LEAST64_MAX) - 1) +#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__ + +/* 7.18.2.3 Limits of fastest minimum-width integer types */ + +#define INT_FAST8_MAX __INT_FAST8_MAX__ +#define INT_FAST8_MIN ((-INT_FAST8_MAX) - 1) +#define UINT_FAST8_MAX __UINT_FAST8_MAX__ + +#define INT_FAST16_MAX __INT_FAST16_MAX__ +#define INT_FAST16_MIN ((-INT_FAST16_MAX) - 1) +#define UINT_FAST16_MAX __UINT_FAST16_MAX__ + +#define INT_FAST32_MAX __INT_FAST32_MAX__ +#define INT_FAST32_MIN ((-INT_FAST32_MAX) - 1) +#define UINT_FAST32_MAX __UINT_FAST32_MAX__ + +#define INT_FAST64_MAX __INT_FAST64_MAX__ +#define INT_FAST64_MIN ((-INT_FAST64_MAX) - 1) +#define UINT_FAST64_MAX __UINT_FAST64_MAX__ + +/* 7.18.2.4 Limits of integer types capable of holding object pointers */ + +#define INTPTR_MAX __INTPTR_MAX__ +#define INTPTR_MIN ((-INTPTR_MAX) - 1) +#define UINTPTR_MAX __UINTPTR_MAX__ + +/* 7.18.2.5 Limits of greatest-width integer types */ + +#define INTMAX_MAX __INTMAX_MAX__ +#define INTMAX_MIN ((-INTMAX_MAX) - 1) +#define UINTMAX_MAX __UINTMAX_MAX__ + +/* 7.18.3 Limits of other integer types */ + +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define PTRDIFF_MIN ((-PTRDIFF_MAX) - 1) + +#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__ +#define SIG_ATOMIC_MIN ((-SIG_ATOMIC_MAX) - 1) + +#define SIZE_MAX __SIZE_MAX__ + +#define WCHAR_MAX __WCHAR_MAX__ +#define WCHAR_MIN ((-WCHAR_MAX) - 1) + +#define WINT_MAX __WINT_MAX__ +#define WINT_MIN ((-WINT_MAX) - 1) + +#endif + +/* 7.18.4 Macros for integer constants */ + +#ifdef __cplusplus +#ifndef __STDC_CONSTANT_MACROS +#define _PDCLIB_NO_CONSTANT_MACROS +#endif +#endif + +#ifndef _PDCLIB_NO_CONSTANT_MACROS + +/* 7.18.4.1 Macros for minimum-width integer constants */ + +/* As the minimum-width types - for the required widths of 8, 16, 32, and 64 + bits - are expressed in terms of the exact-width types, the mechanism for + these macros is to append the literal of that exact-width type to the macro + parameter. + This is considered a hack, as the author is not sure his understanding of + the requirements of this macro is correct. Any input appreciated. +*/ + +/* Expand to an integer constant of specified value and type int_leastN_t */ + +#define INT8_C( value ) _PDCLIB_concat( value, __INT8_C_SUFFIX__ ) +#define INT16_C( value ) _PDCLIB_concat( value, __INT16_C_SUFFIX__ ) +#define INT32_C( value ) _PDCLIB_concat( value, __INT32_C_SUFFIX__ ) +#define INT64_C( value ) _PDCLIB_concat( value, __INT64_C_SUFFIX__ ) + +/* Expand to an integer constant of specified value and type uint_leastN_t */ + +#define UINT8_C( value ) _PDCLIB_concat( value, __UINT8_C_SUFFIX__ ) +#define UINT16_C( value ) _PDCLIB_concat( value, __UINT16_C_SUFFIX__ ) +#define UINT32_C( value ) _PDCLIB_concat( value, __UINT32_C_SUFFIX__ ) +#define UINT64_C( value ) _PDCLIB_concat( value, __UINT64_C_SUFFIX__ ) + +/* 7.18.4.2 Macros for greatest-width integer constants */ + +/* Expand to an integer constant of specified value and type intmax_t */ +#define INTMAX_C( value ) _PDCLIB_concat( value, __INTMAX_C_SUFFIX__ ) + +/* Expand to an integer constant of specified value and type uintmax_t */ +#define UINTMAX_C( value ) _PDCLIB_concat( value, __UINTMAX_C_SUFFIX__ ) + +#endif diff --git a/src/libraries/libc/include/stdio.h b/src/libraries/libc/include/stdio.h new file mode 100644 index 0000000..e1d24bd --- /dev/null +++ b/src/libraries/libc/include/stdio.h @@ -0,0 +1,779 @@ +#pragma once +/* Input/output + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/cpp.h" +#include "j6libc/int.h" +#include "j6libc/null.h" +#include "j6libc/size_t.h" + +CPP_CHECK_BEGIN + +/* See setvbuf(), third argument */ +#define _IOFBF 1 +#define _IOLBF 2 +#define _IONBF 4 + +/* The following are platform-dependant, and defined in config.h. */ +typedef struct _PDCLIB_fpos_t fpos_t; +typedef struct _PDCLIB_file_t FILE; +#define EOF -1 +#define BUFSIZ _PDCLIB_BUFSIZ +#define FOPEN_MAX _PDCLIB_FOPEN_MAX +#define FILENAME_MAX _PDCLIB_FILENAME_MAX +#define L_tmpnam _PDCLIB_L_tmpnam +#define TMP_MAX _PDCLIB_TMP_MAX + +/* See fseek(), third argument */ +#define SEEK_CUR _PDCLIB_SEEK_CUR +#define SEEK_END _PDCLIB_SEEK_END +#define SEEK_SET _PDCLIB_SEEK_SET + +extern FILE * stdin; +extern FILE * stdout; +extern FILE * stderr; + +/* Operations on files */ + +/* Remove the given file. + Returns zero if successful, non-zero otherwise. + This implementation does detect if a file of that name is currently open, + and fails the remove in this case. This does not detect two distinct names + that merely result in the same file (e.g. "/home/user/foo" vs. "~/foo"). +*/ +int remove( const char * filename ); + +/* Rename the given old file to the given new name. + Returns zero if successful, non-zero otherwise. + This implementation does detect if the old filename corresponds to an open + file, and fails the rename in this case. + If there already is a file with the new filename, behaviour is defined by + the glue code (see functions/_PDCLIB/rename.c). +*/ +int rename( const char * src, const char * dst ); + +/* Open a temporary file with mode "wb+", i.e. binary-update. Remove the file + automatically if it is closed or the program exits normally (by returning + from main() or calling exit()). + Returns a pointer to a FILE handle for this file. + This implementation does not remove temporary files if the process aborts + abnormally (e.g. abort()). +*/ +FILE * tmpfile( void ); + +/* Generate a file name that is not equal to any existing filename AT THE TIME + OF GENERATION. Generate a different name each time it is called. + Returns a pointer to an internal static buffer containing the filename if s + is a NULL pointer. (This is not thread-safe!) + Returns s if it is not a NULL pointer (s is then assumed to point to an array + of at least L_tmpnam characters). + Returns NULL if unable to generate a suitable name (because all possible + names already exist, or the function has been called TMP_MAX times already). + Note that this implementation cannot guarantee a file of the name generated + is not generated between the call to this function and a subsequent fopen(). +*/ +char * tmpnam( char * s ); + +/* File access functions */ + +/* Close the file associated with the given stream (after flushing its buffers). + Returns zero if successful, EOF if any errors occur. +*/ +int fclose( FILE * stream ); + +/* Flush the buffers of the given output stream. If the stream is an input + stream, or an update stream with the last operation being an input operation, + behaviour is undefined. + If stream is a NULL pointer, perform the buffer flushing for all applicable + streams. + Returns zero if successful, EOF if a write error occurs. + Sets the error indicator of the stream if a write error occurs. +*/ +int fflush( FILE * stream ); + +/* Open the file with the given filename in the given mode, and return a stream + handle for it in which error and end-of-file indicator are cleared. Defined + values for mode are: + + READ MODES + text files binary files + without update "r" "rb" + with update "r+" "rb+" or "r+b" + + Opening in read mode fails if no file with the given filename exists, or if + cannot be read. + + WRITE MODES + text files binary files + without update "w" "wb" + with update "w+" "wb+" or "w+b" + + With write modes, if a file with the given filename already exists, it is + truncated to zero length. + + APPEND MODES + text files binary files + without update "a" "ab" + with update "a+" "ab+" or "a+b" + + With update modes, if a file with the given filename already exists, it is + not truncated to zero length, but all writes are forced to end-of-file (this + regardless to fseek() calls). Note that binary files opened in append mode + might have their end-of-file padded with '\0' characters. + + Update modes mean that both input and output functions can be performed on + the stream, but output must be terminated with a call to either fflush(), + fseek(), fsetpos(), or rewind() before input is performed, and input must + be terminated with a call to either fseek(), fsetpos(), or rewind() before + output is performed, unless input encountered end-of-file. + + If a text file is opened with update mode, the implementation is at liberty + to open a binary stream instead. This implementation honors the exact mode + given. + + The stream is fully buffered if and only if it can be determined not to + refer to an interactive device. + + If the mode string begins with but is longer than one of the above sequences + the implementation is at liberty to ignore the additional characters, or do + implementation-defined things. This implementation only accepts the exact + modes above. + + Returns a pointer to the stream handle if successfull, NULL otherwise. +*/ +FILE * fopen( const char * restrict filename, const char * restrict mode ); + +/* Close any file currently associated with the given stream. Open the file + identified by the given filename with the given mode (equivalent to fopen()), + and associate it with the given stream. If filename is a NULL pointer, + attempt to change the mode of the given stream. + This implementation allows any mode changes on "real" files, and associating + of the standard streams with files. It does *not* support mode changes on + standard streams. + (Primary use of this function is to redirect stdin, stdout, and stderr.) +*/ +FILE * freopen( const char * restrict filename, const char * restrict mode, FILE * restrict stream ); + +/* If buf is a NULL pointer, call setvbuf( stream, NULL, _IONBF, BUFSIZ ). + If buf is not a NULL pointer, call setvbuf( stream, buf, _IOFBF, BUFSIZ ). +*/ +void setbuf( FILE * restrict stream, char * restrict buf ); + +/* Set the given stream to the given buffering mode. If buf is not a NULL + pointer, use buf as file buffer (of given size). If buf is a NULL pointer, + use a buffer of given size allocated internally. _IONBF causes unbuffered + behaviour, _IOLBF causes line-buffered behaviour, _IOFBF causes fully + buffered behaviour. Calling this function is only valid right after a file is + opened, and before any other operation (except for any unsuccessful calls to + setvbuf()) has been performed. + Returns zero if successful, nonzero otherwise. +*/ +int setvbuf( FILE * restrict stream, char * restrict buf, int mode, size_t size ); + +/* Formatted input/output functions */ + +/* + Write output to the given stream, as defined by the given format string and + 0..n subsequent arguments (the argument stack). + + The format string is written to the given stream verbatim, except for any + conversion specifiers included, which start with the letter '%' and are + documented below. If the given conversion specifiers require more arguments + from the argument stack than provided, behaviour is undefined. Additional + arguments not required by conversion specifiers are evaluated but otherwise + ignored. + + (The standard specifies the format string is allowed to contain multibyte + character sequences as long as it starts and ends in initial shift state, + but this is not yet supported by this implementation, which interprets the + format string as sequence of char.) + TODO: Add multibyte support to printf() functions. + + A conversion specifier consists of: + - Zero or more flags (one of the characters "-+ #0"). + - Optional minimum field width as decimal integer. Default is padding to the + left, using spaces. Note that 0 is taken as a flag, not the beginning of a + field width. Note also that a small field width will not result in the + truncation of a value. + - Optional precision (given as ".#" with # being a decimal integer), + specifying: + - the min. number of digits to appear (diouxX), + - the max. number of digits after the decimal point (aAeEfF), + - the max. number of significant digits (gG), + - the max. number of bytes to be written (s). + - behaviour with other conversion specifiers is undefined. + - Optional length modifier specifying the size of the argument (one of "hh", + "ll", or one of the characters "hljztL"). + - Conversion specifier character specifying the type of conversion to be + applied (and the type of the next argument from the argument stack). One + of the characters "diouxXfFeEgGaAcspn%". + + Minimum field width and/or precision may be given as asterisk ('*') instead + of a decimal integer. In this case, the next argument from the argument + stack is assumed to be an int value specifying the width / precision. A + negative field width is interpreted as flag '-' followed by a positive field + width. A negative precision is interpreted as if no precision was given. + + FLAGS + - Left-justify the conversion result within its field width. + + Prefix a '+' on positive signed conversion results. Prefix a '-' on + floating conversions resulting in negative zero, or negative values + rounding to zero. + space Prefix a space on positive signed conversion results, or if a signed + conversion results in no characters. If both '+' and ' ' are given, + ' ' is ignored. + # Use an "alternative form" for + - 'o' conversion, increasing precision until the first digit of the + result is a zero; + - 'x' or 'X' conversion, prefixing "0x" or "0X" to nonzero results; + - "aAeEfF" conversions, always printing a decimal point even if no + digits are following; + - 'g' or 'G' conversions, always printing a decimal point even if no + digits are following, and not removing trailing zeroes. + - behaviour for other conversions is unspecified. + 0 Use leading zeroes instead of spaces for field width padding. If both + '-' and '0' are given, '0' is ignored. If a precision is specified for + any of the "diouxX" conversions, '0' is ignored. Behaviour is only + defined for "diouxXaAeEfFgG". + + LENGTH MODIFIERS + hh For "diouxX" conversions, the argument from the argument stack is + assumed to be of char width. (It will have been subject to integer + promotion but will be converted back.) For 'n' conversions, the argument + is assumed to be a pointer to signed char. + h For "diouxX" conversions, the argument from the argument stack is + assumed to be of short int width. (It will have been subject to integer + promotion but will be converted back.) For 'n' conversions, the argument + is assumed to be a pointer to short int. + l For "diouxX" conversions, the argument from the argument stack is + assumed to be of long int width. For 'n' conversions, the argument is + assumed to be a pointer to short int. For 'c' conversions, the argument + is assumed to be a wint_t. For 's' conversions, the argument is assumed + to be a pointer to wchar_t. No effect on "aAeEfFgG" conversions. + ll For "diouxX" conversions, the argument from the argument stack is + assumed to be of long long int width. For 'n' conversions, the argument + is assumed to be a pointer to long long int. + j For "diouxX" conversions, the argument from the argument stack is + assumed to be of intmax_t width. For 'n' conversions, the argument is + assumed to be a pointer to intmax_t. + z For "diouxX" conversions, the argument from the argument stack is + assumed to be of size_t width. For 'n' conversions, the argument is + assumed to be a pointer to size_t. + t For "diouxX" conversions, the argument from the argument stack is + assumed to be of ptrdiff_t width. For 'n' conversions, the argument is + assumed to be a pointer to ptrdiff_t. + L For "aAeEfFgG" conversions, the argument from the argument stack is + assumed to be a long double. + Length modifiers appearing for any conversions not mentioned above will have + undefined behaviour. + If a length modifier appears with any conversion specifier other than as + specified above, the behavior is undefined. + + CONVERSION SPECIFIERS + d,i The argument from the argument stack is assumed to be of type int, and + is converted to a signed decimal value with a minimum number of digits + as specified by the precision (default 1), padded with leading zeroes. + A zero value converted with precision zero yields no output. + o The argument from the argument stack is assumed to be of type unsigned + int, and is converted to an unsigned octal value, other behaviour being + as above. + u The argument from the argument stack is assumed to be of type unsigned + int, and converted to an unsigned decimal value, other behaviour being + as above. + x,X The argument from the argument stack is assumed to be of type unsigned + int, and converted to an unsigned hexadecimal value, using lowercase + "abcdef" for 'x' and uppercase "ABCDEF" for 'X' conversion, other + behaviour being as above. + f,F The argument from the argument stack is assumed to be of type double, + and converted to a decimal floating point in decimal-point notation, + with the number of digits after the decimal point as specified by the + precision (default 6) and the value being rounded appropriately. If + precision is zero (and the '#' flag is not given), no decimal point is + printed. At least one digit is always printed before the decimal point. + For 'f' conversions, an infinity value is printed as either [-]inf or + [-]infinity (, depending on the configuration of this implementation. A + NaN value is printed as [-]nan. For 'F' conversions uppercase characters + are used for these special values. The flags '-', '+' and ' ' apply as + usual to these special values, '#' and '0' have no effect. + e,E The argument from the argument stack is assumed to be of type double, + and converted to a decimal floating point in normalized exponential + notation ([?]d.ddd edd). "Normalized" means one nonzero digit before + the decimal point, unless the value is zero. The number of digits after + the decimal point is specified by the precision (default 6), the value + being rounded appropriately. If precision is zero (and the '#' flag is + not given), no decimal point is printed. The exponent has at least two + digits, and not more than necessary to represent the exponent. If the + value is zero, the exponent is zero. The 'e' written to indicate the + exponend is uppercase for 'E' conversions. + Infinity or NaN values are represented as for 'f' and 'F' conversions, + respectively. + g,G The argument from the argument stack is assumed to be of type double, + and converted according to either 'f' or 'e' format for 'g' conversions, + or 'F' or 'E' format for 'G' conversions, respectively, with the actual + conversion chosen depending on the value. 'e' / 'E' conversion is chosen + if the resulting exponent is < -4 or >= the precision (default 1). + Trailing zeroes are removed (unless the '#' flag is given). A decimal + point appears only if followed by a digit. + Infinity or NaN values are represented as for 'f' and 'F' conversions, + respectively. + a,A The argument from the argument stack is assumed to be of type double, + and converted to a floating point hexadecimal notation ([?]0xh.hhhh pd) + with one hexadecimal digit (being nonzero if the value is normalized, + and otherwise unspecified) before the decimal point, and the number of + digits after the decimal point being specified by the precision. If no + precision is given, the default is to print as many digits as nevessary + to give an exact representation of the value (if FLT_RADIX is a power of + 2). If no precision is given and FLT_RADIX is not a power of 2, the + default is to print as many digits to distinguish values of type double + (possibly omitting trailing zeroes). (A precision p is sufficient to + distinguish values of the source type if 16^p-1 > b^n where b is + FLT_RADIX and n is the number of digits in the significand (to base b) + of the source type. A smaller p might suffice depending on the + implementation's scheme for determining the digit to the left of the + decimal point.) The error has the correct sign for the current rounding + direction. + Unless the '#' flag is given, no decimal-point is given for zero + precision. + The 'a' conversion uses lowercase "abcdef", "0x" and 'p', the 'A' + conversion uppercase "ABCDEF", "0X" and 'P'. + The exponent always has at least one digit, and not more than necessary + to represent the decimal exponent of 2. If the value is zero, the + exponent is zero. + Infinity or NaN values are represented as for 'f' and 'F' conversions, + respectively. + Binary implementations are at liberty to chose the hexadecimal digit to + the left of the decimal point so that subsequent digits align to nibble + boundaries. + c The argument from the argument stack is assumed to be of type int, and + converted to a character after the value has been cast to unsigned char. + If the 'l' length modifier is given, the argument is assumed to be of + type wint_t, and converted as by a "%ls" conversion with no precision + and a pointer to a two-element wchar_t array, with the first element + being the wint_t argument and the second a '\0' wide character. + s The argument from the argument stack is assumed to be a char array (i.e. + pointer to char). Characters from that array are printed until a zero + byte is encountered or as many bytes as specified by a given precision + have been written. + If the l length modifier is given, the argument from the argument stack + is assumed to be a wchar_t array (i.e. pointer to wchar_t). Wide + characters from that array are converted to multibyte characters as by + calls to wcrtomb() (using a mbstate_t object initialized to zero prior + to the first conversion), up to and including the terminating null wide + character. The resulting multibyte character sequence is then printed up + to but not including the terminating null character. If a precision is + given, it specifies the maximum number of bytes to be written (including + shift sequences). If the given precision would require access to a wide + character one past the end of the array, the array shall contain a '\0' + wide character. In no case is a partial multibyte character written. + Redundant shift sequences may result if the multibyte characters have a + state-dependent encoding. + TODO: Clarify these statements regarding %ls. + p The argument from the argument stack is assumed to be a void pointer, + and converted to a sequence of printing characters in an implementation- + defined manner. + This implementation casts the pointer to type intptr_t, and prints the + value as if a %#x conversion specifier was given. + n The argument from the argument stack is assumed to be a pointer to a + signed integer, into which the number of characters written so far by + this call to fprintf is stored. The behaviour, should any flags, field + widths, or precisions be given is undefined. + % A verbatim '%' character is written. No argument is taken from the + argument stack. + + Returns the number of characters written if successful, a negative value + otherwise. +*/ +int fprintf( FILE * restrict stream, const char * restrict format, ... ); + +/* TODO: fscanf() documentation */ +/* + Read input from a given stream, as defined by the given format string, and + store converted input in the objects pointed to by 0..n subsequent arguments + (the argument stack). + + The format string contains a sequence of directives that are expected to + match the input. If such a directive fails to match, the function returns + (matching error). It also returns if an input error occurs (input error). + + Directives can be: + - one or more whitespaces, matching any number of whitespaces in the input; + - printing characters, matching the input verbatim; + - conversion specifications, which convert an input sequence into a value as + defined by the individual specifier, and store that value in a memory + location pointed to by the next pointer on the argument stack. Details are + documented below. If there is an insufficient number of pointers on the + argument stack, behaviour is undefined. Additional arguments not required + by any conversion specifications are evaluated, but otherwise ignored. + + (The standard specifies the format string is allowed to contain multibyte + character sequences as long as it starts and ends in initial shift state, + but this is not yet supported by this implementation, which interprets the + format string as sequence of char.) + TODO: Add multibyte support to scanf() functions. + + A conversion specifier consists of: + - Optional assignment-suppressing character ('*') that makes the conversion + read input as usual, but does not assign the conversion result. + - Optional maximum field width as decimal integer. + - Optional length modifier specifying the size of the argument (one of "hh", + "ll", or one of the characters "hljztL"). + - Conversion specifier character specifying the type of conversion to be + applied (and the type of the next argument from the argument stack). One + of the characters "diouxXaAeEfFgGcs[pn%". + + LENGTH MODIFIERS + hh For "diouxXn" conversions, the next pointer from the argument stack is + assumed to point to a variable of of char width. + h For "diouxXn" conversions, the next pointer from the argument stack is + assumed to point to a variable of short int width. + l For "diouxXn" conversions, the next pointer from the argument stack is + assumed to point to a variable of long int width. + For "aAeEfFgG" conversions, it is assumed to point to a variable of type + double. + For "cs[" conversions, it is assumed to point to a variable of type + wchar_t. + ll For "diouxXn" conversions, the next pointer from the argument stack is + assumed to point to a variable of long long int width. + j For "diouxXn" conversions, the next pointer from the argument stack is + assumed to point to a variable of intmax_t width. + z For "diouxXn" conversions, the next pointer from the argument stack is + assumed to point to a variable of size_t width. + t For "diouxXn" conversions, the next pointer from the argument stack is + assumed to point to a variable of ptrdiff_t width. + L For "aAeEfFgG" conversions, the next pointer from the argument stack is + assumed to point to a variable of type long double. + Length modifiers appearing for any conversions not mentioned above will have + undefined behaviour. + If a length modifier appears with any conversion specifier other than as + specified above, the behavior is undefined. + + CONVERSION SPECIFIERS + d Matches an (optionally signed) decimal integer of the format expected + by strtol() with base 10. The next pointer from the argument stack is + assumed to point to a signed integer. + i Matches an (optionally signed) integer of the format expected by + strtol() with base 0. The next pointer from the argument stack is + assumed to point to a signed integer. + o Matches an (optionally signed) octal integer of the format expected by + strtoul() with base 8. The next pointer from the argument stack is + assumed to point to an unsigned integer. + u Matches an (optionally signed) decimal integer of the format expected + by strtoul() with base 10. The next pointer from the argument stack is + assumed to point to an unsigned integer. + x Matches an (optionally signed) hexadecimal integer of the format + expected by strtoul() with base 16. The next pointer from the argument + stack is assumed to point to an unsigned integer. + aefg Matches an (optionally signed) floating point number, infinity, or not- + a-number-value of the format expected by strtod(). The next pointer + from the argument stack is assumed to point to a float. + c Matches a number of characters as specified by the field width (default + 1). The next pointer from the argument stack is assumed to point to a + character array large enough to hold that many characters. + If the 'l' length modifier is given, the input is assumed to match a + sequence of multibyte characters (starting in the initial shift state), + which will be converted to a wide character sequence as by successive + calls to mbrtowc() with a mbstate_t object initialized to zero prior to + the first conversion. The next pointer from the argument stack is + assumed to point to a wchar_t array large enough to hold that many + characters. + In either case, note that no '\0' character is added to terminate the + sequence. + s Matches a sequence of non-white-space characters. The next pointer from + the argument stack is assumed to point to a character array large + enough to hold the sequence including terminating '\0' character. + If the 'l' length modifier is given, the input is assumed to match a + sequence of multibyte characters (starting in the initial shift state), + which will be converted to a wide character sequence as by a call to + mbrtowc() with a mbstate_t object initialized to zero prior to the + first conversion. The next pointer from the argument stack is assumed + to point to a wchar_t array large enough to hold the sequence including + terminating '\0' character. + [ Matches a nonempty sequence consisting of any of those characters + specified between itself and a corresponding closing bracket (']'). + If the first character in the list is a circumflex ('^'), this matches + a nonempty sequence consisting of any characters NOT specified. If the + closing bracket appears as the first character in the scanset ("[]" or + "[^]", it is assumed to belong to the scanset, which then ends with the + NEXT closing bracket. + If there is a '-' character in the scanset which is not the first after + the opening bracket (or the circumflex, see above) or the last in the + scanset, behaviour is implementation-defined. This implementation + handles this character like any other. + + The extend of the input field is determined byte-by-byte for the above + conversions ('c', 's', '['), with no special provisions being made for + multibyte characters. The resulting field is nevertheless a multibyte + sequence begining in intial shift state. + + p Matches a sequence of characters as produced by the printf() "%p" + conversion. The next pointer from the argument stack is assumed to + point to a void pointer, which will be filled with the same location + as the pointer used in the printf() statement. Note that behaviour is + undefined if the input value is not the result of an earlier printf() + call. + n Does not read input. The next pointer from the argument stack is + assumed to point to a signed integer, into which the number of + characters read from input so far by this call to fscanf() is stored. + This does not affect the return value of fscanf(). The behaviour, + should an assignment-supressing character of field width be given, + is undefined. + This can be used to test the success of literal matches and suppressed + assignments. + % Matches a single, verbatim '%' character. + + A, E, F, G and X are valid, and equivalent to their lowercase counterparts. + + All conversions except [, c, or n imply that whitespace characters from the + input stream are consumed until a non-whitespace character is encountered. + Such whitespaces do not count against a maximum field width. + + Conversions push at most one character back into the input stream. That + implies that some character sequences converted by the strtol() and strtod() + function families are not converted identically by the scnaf() function + family. + + Returns the number of input items successfully assigned. This can be zero if + an early mismatch occurs. Returns EOF if an input failure occurs before the + first conversion. +*/ +int fscanf( FILE * restrict stream, const char * restrict format, ... ); + +/* Equivalent to fprintf( stdout, format, ... ). */ +int printf( const char * restrict format, ... ); + +/* Equivalent to fscanf( stdin, format, ... ). */ +int scanf( const char * restrict format, ... ); + +/* Equivalent to fprintf( stdout, format, ... ), except that the result is + written into the buffer pointed to by s, instead of stdout, and that any + characters beyond the (n-1)th are discarded. The (n)th character is + replaced by a '\0' character in this case. + Returns the number of characters that would have been written (not counting + the terminating '\0' character) if n had been sufficiently large, if + successful, and a negative number if an encoding error ocurred. +*/ +int snprintf( char * restrict s, size_t n, const char * restrict format, ... ); + +/* Equivalent to fprintf( stdout, format, ... ), except that the result is + written into the buffer pointed to by s, instead of stdout. +*/ +int sprintf( char * restrict s, const char * restrict format, ... ); + +/* Equivalent to fscanf( stdin, format, ... ), except that the input is read + from the buffer pointed to by s, instead of stdin. +*/ +int sscanf( const char * restrict s, const char * restrict format, ... ); + +/* Equivalent to fprintf( stream, format, ... ), except that the argument stack + is passed as va_list parameter. Note that va_list is not declared by + . +*/ +int vfprintf( FILE * restrict stream, const char * restrict format, _PDCLIB_va_list arg ); + +/* Equivalent to fscanf( stream, format, ... ), except that the argument stack + is passed as va_list parameter. Note that va_list is not declared by + . +*/ +int vfscanf( FILE * restrict stream, const char * restrict format, _PDCLIB_va_list arg ); + +/* Equivalent to fprintf( stdout, format, ... ), except that the argument stack + is passed as va_list parameter. Note that va_list is not declared by + . +*/ +int vprintf( const char * restrict format, _PDCLIB_va_list arg ); + +/* Equivalent to fscanf( stdin, format, ... ), except that the argument stack + is passed as va_list parameter. Note that va_list is not declared by + . +*/ +int vscanf( const char * restrict format, _PDCLIB_va_list arg ); + +/* Equivalent to snprintf( s, n, format, ... ), except that the argument stack + is passed as va_list parameter. Note that va_list is not declared by + . + */ +int vsnprintf( char * restrict s, size_t n, const char * restrict format, _PDCLIB_va_list arg ); + +/* Equivalent to fprintf( stdout, format, ... ), except that the argument stack + is passed as va_list parameter, and the result is written to the buffer + pointed to by s, instead of stdout. Note that va_list is not declared by + . +*/ +int vsprintf( char * restrict s, const char * restrict format, _PDCLIB_va_list arg ); + +/* Equivalent to fscanf( stdin, format, ... ), except that the argument stack + is passed as va_list parameter, and the input is read from the buffer + pointed to by s, instead of stdin. Note that va_list is not declared by + . +*/ +int vsscanf( const char * restrict s, const char * restrict format, _PDCLIB_va_list arg ); + +/* Character input/output functions */ + +/* Retrieve the next character from given stream. + Returns the character, EOF otherwise. + If end-of-file is reached, the EOF indicator of the stream is set. + If a read error occurs, the error indicator of the stream is set. +*/ +int fgetc( FILE * stream ); + +/* Read at most n-1 characters from given stream into the array s, stopping at + \n or EOF. Terminate the read string with \n. If EOF is encountered before + any characters are read, leave the contents of s unchanged. + Returns s if successful, NULL otherwise. + If a read error occurs, the error indicator of the stream is set. In this + case, the contents of s are indeterminate. +*/ +char * fgets( char * restrict s, int n, FILE * restrict stream ); + +/* Write the value c (cast to unsigned char) to the given stream. + Returns c if successful, EOF otherwise. + If a write error occurs, sets the error indicator of the stream is set. +*/ +int fputc( int c, FILE * stream ); + +/* Write the string s (not including the terminating \0) to the given stream. + Returns a value >=0 if successful, EOF otherwise. + This implementation does set the error indicator of the stream if a write + error occurs. +*/ +int fputs( const char * restrict s, FILE * restrict stream ); + +/* Equivalent to fgetc( stream ), but may be overloaded by a macro that + evaluates its parameter more than once. +*/ +int getc( FILE * stream ); + +/* Equivalent to fgetc( stdin ). */ +int getchar( void ); + +/* Equivalent to fputc( c, stream ), but may be overloaded by a macro that + evaluates its parameter more than once. +*/ +int putc( int c, FILE * stream ); + +/* Equivalent to fputc( c, stdout ), but may be overloaded by a macro that + evaluates its parameter more than once. +*/ +int putchar( int c ); + +/* Write the string s (not including the terminating \0) to stdout, and append + a newline to the output. Returns a value >= 0 when successful, EOF if a + write error occurred. +*/ +int puts( const char * s ); + +/* Push the value c (cast to unsigned char) back onto the given (input) stream. + A character pushed back in this way will be delivered by subsequent read + operations (and skipped by subsequent file positioning operations) as if it + has not been read. The external representation of the stream is unaffected + by this pushback (it is a buffer operation). One character of pushback is + guaranteed, further pushbacks may fail. EOF as value for c does not change + the input stream and results in failure of the function. + For text files, the file position indicator is indeterminate until all + pushed-back characters are read. For binary files, the file position + indicator is decremented by each successful call of ungetc(). If the file + position indicator for a binary file was zero before the call of ungetc(), + behaviour is undefined. (Older versions of the library allowed such a call.) + Returns the pushed-back character if successful, EOF if it fails. +*/ +int ungetc( int c, FILE * stream ); + +/* Direct input/output functions */ + +/* Read up to nmemb elements of given size from given stream into the buffer + pointed to by ptr. Returns the number of elements successfully read, which + may be less than nmemb if a read error or EOF is encountered. If a read + error is encountered, the value of the file position indicator is + indeterminate. If a partial element is read, its value is indeterminate. + If size or nmemb are zero, the function does nothing and returns zero. +*/ +size_t fread( void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream ); + +/* Write up to nmemb elements of given size from buffer pointed to by ptr to + the given stream. Returns the number of elements successfully written, which + will be less than nmemb only if a write error is encountered. If a write + error is encountered, the value of the file position indicator is + indeterminate. If size or nmemb are zero, the function does nothing and + returns zero. +*/ +size_t fwrite( const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream ); + +/* File positioning functions */ + +/* Store the current position indicator (and, where appropriate, the current + mbstate_t status object) for the given stream into the given pos object. The + actual contents of the object are unspecified, but it can be used as second + parameter to fsetpos() to reposition the stream to the exact position and + parse state at the time fgetpos() was called. + Returns zero if successful, nonzero otherwise. + TODO: Implementation-defined errno setting for fgetpos(). +*/ +int fgetpos( FILE * restrict stream, fpos_t * restrict pos ); + +/* Set the position indicator for the given stream to the given offset from: + - the beginning of the file if whence is SEEK_SET, + - the current value of the position indicator if whence is SEEK_CUR, + - end-of-file if whence is SEEK_END. + On text streams, non-zero offsets are only allowed with SEEK_SET, and must + have been returned by ftell() for the same file. + Any characters buffered by ungetc() are dropped, the end-of-file indicator + for the stream is cleared. If the given stream is an update stream, the next + operation after a successful fseek() may be either input or output. + Returns zero if successful, nonzero otherwise. If a read/write error occurs, + the error indicator for the given stream is set. +*/ +int fseek( FILE * stream, long int offset, int whence ); + +/* Set the position indicator (and, where appropriate the mbstate_t status + object) for the given stream to the given pos object (created by an earlier + call to fgetpos() on the same file). + Any characters buffered by ungetc() are dropped, the end-of-file indicator + for the stream is cleared. If the given stream is an update stream, the next + operation after a successful fsetpos() may be either input or output. + Returns zero if successful, nonzero otherwise. If a read/write error occurs, + the error indicator for the given stream is set. + TODO: Implementation-defined errno setting for fsetpos(). +*/ +int fsetpos( FILE * stream, const fpos_t * pos ); + +/* Return the current offset of the given stream from the beginning of the + associated file. For text streams, the exact value returned is unspecified + (and may not be equal to the number of characters), but may be used in + subsequent calls to fseek(). + Returns -1L if unsuccessful. + TODO: Implementation-defined errno setting for ftell(). +*/ +long int ftell( FILE * stream ); + +/* Equivalent to (void)fseek( stream, 0L, SEEK_SET ), except that the error + indicator for the stream is also cleared. +*/ +void rewind( FILE * stream ); + +/* Error-handling functions */ + +/* Clear the end-of-file and error indicators for the given stream. */ +void clearerr( FILE * stream ); + +/* Return zero if the end-of-file indicator for the given stream is not set, + nonzero otherwise. +*/ +int feof( FILE * stream ); + +/* Return zero if the error indicator for the given stream is not set, nonzero + otherwise. +*/ +int ferror( FILE * stream ); + +/* If s is neither a NULL pointer nor an empty string, print the string to + stderr (with appended colon (':') and a space) first. In any case, print an + error message depending on the current value of errno (being the same as if + strerror( errno ) had been called). +*/ +void perror( const char * s ); + +CPP_CHECK_END diff --git a/src/libraries/libc/include/stdlib.h b/src/libraries/libc/include/stdlib.h new file mode 100644 index 0000000..d32dfb4 --- /dev/null +++ b/src/libraries/libc/include/stdlib.h @@ -0,0 +1,232 @@ +#pragma once +/* General utilities + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/cpp.h" +#include "j6libc/int.h" +#include "j6libc/null.h" +#include "j6libc/size_t.h" +#include "j6libc/wchar_t.h" + +CPP_CHECK_BEGIN + +/* Numeric conversion functions */ + +/* TODO: atof(), strtof(), strtod(), strtold() */ + +double atof( const char * nptr ); +double strtod( const char * restrict nptr, char * * restrict endptr ); +float strtof( const char * restrict nptr, char * * restrict endptr ); +long double strtold( const char * restrict nptr, char * * restrict endptr ); + +/* Seperate the character array nptr into three parts: A (possibly empty) + sequence of whitespace characters, a character representation of an integer + to the given base, and trailing invalid characters (including the terminating + null character). If base is 0, assume it to be 10, unless the integer + representation starts with 0x / 0X (setting base to 16) or 0 (setting base to + 8). If given, base can be anything from 0 to 36, using the 26 letters of the + base alphabet (both lowercase and uppercase) as digits 10 through 35. + The integer representation is then converted into the return type of the + function. It can start with a '+' or '-' sign. If the sign is '-', the result + of the conversion is negated. + If the conversion is successful, the converted value is returned. If endptr + is not a NULL pointer, a pointer to the first trailing invalid character is + returned in *endptr. + If no conversion could be performed, zero is returned (and nptr in *endptr, + if endptr is not a NULL pointer). If the converted value does not fit into + the return type, the functions return LONG_MIN, LONG_MAX, ULONG_MAX, + LLONG_MIN, LLONG_MAX, or ULLONG_MAX respectively, depending on the sign of + the integer representation and the return type, and errno is set to ERANGE. +*/ +/* There is strtoimax() and strtoumax() in operating on intmax_t / + uintmax_t, if the long long versions do not suit your needs. +*/ +long int strtol( const char * restrict nptr, char * * restrict endptr, int base ); +long long int strtoll( const char * restrict nptr, char * * restrict endptr, int base ); +unsigned long int strtoul( const char * restrict nptr, char * * restrict endptr, int base ); +unsigned long long int strtoull( const char * restrict nptr, char * * restrict endptr, int base ); + +/* These functions are the equivalent of (int)strtol( nptr, NULL, 10 ), + strtol( nptr, NULL, 10 ) and strtoll(nptr, NULL, 10 ) respectively, with the + exception that they do not have to handle overflow situations in any defined + way. + (PDCLib does not simply forward these to their strtox() equivalents, but + provides a simpler atox() function that saves a couple of tests and simply + continues with the conversion in case of overflow.) +*/ +int atoi( const char * nptr ); +long int atol( const char * nptr ); +long long int atoll( const char * nptr ); + +/* Pseudo-random sequence generation functions */ + +extern unsigned long int _PDCLIB_seed; + +#define RAND_MAX 32767 + +/* Returns the next number in a pseudo-random sequence, which is between 0 and + RAND_MAX. + (PDCLib uses the implementation suggested by the standard document, which is + next = next * 1103515245 + 12345; return (unsigned int)(next/65536) % 32768;) +*/ +int rand( void ); + +/* Initialize a new pseudo-random sequence with the starting seed. Same seeds + result in the same pseudo-random sequence. The default seed is 1. +*/ +void srand( unsigned int seed ); + +/* Memory management functions */ + +/* Allocate a chunk of heap memory of given size. If request could not be + satisfied, return NULL. Otherwise, return a pointer to the allocated + memory. Memory contents are undefined. +*/ +void * malloc( size_t size ); + +/* Allocate a chunk of heap memory that is large enough to hold nmemb elements + of the given size, and zero-initialize that memory. If request could not be + satisfied, return NULL. Otherwise, return a pointer to the allocated + memory. +*/ +void * calloc( size_t nmemb, size_t size ); + +/* De-allocate a chunk of heap memory previously allocated using malloc(), + calloc(), or realloc(), and pointed to by ptr. If ptr does not match a + pointer previously returned by the mentioned allocation functions, or + free() has already been called for this ptr, behaviour is undefined. +*/ +void free( void * ptr ); + +/* Resize a chunk of memory previously allocated with malloc() and pointed to + by ptr to the given size (which might be larger or smaller than the original + size). Returns a pointer to the reallocated memory, or NULL if the request + could not be satisfied. Note that the resizing might include a memcpy() + from the original location to a different one, so the return value might or + might not equal ptr. If size is larger than the original size, the value of + memory beyond the original size is undefined. If ptr is NULL, realloc() + behaves like malloc(). +*/ +void * realloc( void * ptr, size_t size ); + +/* Communication with the environment */ + +/* These two can be passed to exit() or _Exit() as status values, to signal + successful and unsuccessful program termination, respectively. EXIT_SUCCESS + can be replaced by 0. How successful or unsuccessful program termination are + signaled to the environment, and what happens if exit() or _Exit() are being + called with a value that is neither of the three, is defined by the hosting + OS and its glue function. +*/ +#define EXIT_SUCCESS _PDCLIB_SUCCESS +#define EXIT_FAILURE _PDCLIB_FAILURE + +/* Initiate abnormal process termination, unless programm catches SIGABRT and + does not return from the signal handler. + This implementantion flushes all streams, closes all files, and removes any + temporary files before exiting with EXIT_FAILURE. + abort() does not return. +*/ +void abort( void ); + +/* Register a function that will be called on exit(), or when main() returns. + At least 32 functions can be registered this way, and will be called in + reverse order of registration (last-in, first-out). + Returns zero if registration is successfull, nonzero if it failed. +*/ +int atexit( void (*func)( void ) ); + +/* Normal process termination. Functions registered by atexit() (see above) are + called, streams flushed, files closed and temporary files removed before the + program is terminated with the given status. (See comment for EXIT_SUCCESS + and EXIT_FAILURE above.) + exit() does not return. +*/ +void exit( int status ); + +/* Normal process termination. Functions registered by atexit() (see above) are + NOT CALLED. This implementation DOES flush streams, close files and removes + temporary files before the program is teminated with the given status. (See + comment for EXIT_SUCCESS and EXIT_FAILURE above.) + _Exit() does not return. +*/ +void _Exit( int status ); + +/* Search an environment-provided key-value map for the given key name, and + return a pointer to the associated value string (or NULL if key name cannot + be found). The value string pointed to might be overwritten by a subsequent + call to getenv(). The library never calls getenv() itself. + Details on the provided keys and how to set / change them are determined by + the hosting OS and its glue function. +*/ +char * getenv( const char * name ); + +/* If string is a NULL pointer, system() returns nonzero if a command processor + is available, and zero otherwise. If string is not a NULL pointer, it is + passed to the command processor. If system() returns, it does so with a + value that is determined by the hosting OS and its glue function. +*/ +int system( const char * string ); + +/* Searching and sorting */ + +/* Do a binary search for a given key in the array with a given base pointer, + which consists of nmemb elements that are of the given size each. To compare + the given key with an element from the array, the given function compar is + called (with key as first parameter and a pointer to the array member as + second parameter); the function should return a value less than, equal to, + or greater than 0 if the key is considered to be less than, equal to, or + greater than the array element, respectively. + The function returns a pointer to the first matching element found, or NULL + if no match is found. +*/ +void * bsearch( const void * key, const void * base, size_t nmemb, size_t size, int (*compar)( const void *, const void * ) ); + +/* Do a quicksort on an array with a given base pointer, which consists of + nmemb elements that are of the given size each. To compare two elements from + the array, the given function compar is called, which should return a value + less than, equal to, or greater than 0 if the first argument is considered + to be less than, equal to, or greater than the second argument, respectively. + If two elements are compared equal, their order in the sorted array is not + specified. +*/ +void qsort( void * base, size_t nmemb, size_t size, int (*compar)( const void *, const void * ) ); + +/* Integer arithmetic functions */ + +/* Return the absolute value of the argument. Note that on machines using two- + complement's notation (most modern CPUs), the largest negative value cannot + be represented as positive value. In this case, behaviour is unspecified. +*/ +int abs( int j ); +long int labs( long int j ); +long long int llabs( long long int j ); + +/* These structures each have a member quot and a member rem, of type int (for + div_t), long int (for ldiv_t) and long long it (for lldiv_t) respectively. + The order of the members is platform-defined to allow the div() functions + below to be implemented efficiently. +*/ +typedef struct _PDCLIB_div_t div_t; +typedef struct _PDCLIB_ldiv_t ldiv_t; +typedef struct _PDCLIB_lldiv_t lldiv_t; + +/* Return quotient (quot) and remainder (rem) of an integer division in one of + the structs above. +*/ +div_t div( int numer, int denom ); +ldiv_t ldiv( long int numer, long int denom ); +lldiv_t lldiv( long long int numer, long long int denom ); + +/* TODO: Multibyte / wide character conversion functions */ +/* TODO: Macro MB_CUR_MAX */ +int mblen( const char * s, size_t n ); +int mbtowc( wchar_t * restrict pwc, const char * restrict s, size_t n ); +int wctomb( char * s, wchar_t wc ); +size_t mbstowcs( wchar_t * restrict pwcs, const char * restrict s, size_t n ); +size_t wcstombs( char * restrict s, const wchar_t * restrict pwcs, size_t n ); + +CPP_CHECK_END diff --git a/src/libraries/libc/include/stdnoreturn.h b/src/libraries/libc/include/stdnoreturn.h new file mode 100644 index 0000000..c18059b --- /dev/null +++ b/src/libraries/libc/include/stdnoreturn.h @@ -0,0 +1,8 @@ +#pragma once +/* _Noreturn + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#define noreturn _Noreturn diff --git a/src/libraries/libc/include/string.h b/src/libraries/libc/include/string.h new file mode 100644 index 0000000..ac7dd4a --- /dev/null +++ b/src/libraries/libc/include/string.h @@ -0,0 +1,178 @@ +#pragma once +/* String handling + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/cpp.h" +#include "j6libc/int.h" +#include "j6libc/null.h" +#include "j6libc/size_t.h" + +CPP_CHECK_BEGIN + +/* String function conventions */ + +/* + In any of the following functions taking a size_t n to specify the length of + an array or size of a memory region, n may be 0, but the pointer arguments to + the call shall still be valid unless otherwise stated. +*/ + +/* Copying functions */ + +/* Copy a number of n characters from the memory area pointed to by s2 to the + area pointed to by s1. If the two areas overlap, behaviour is undefined. + Returns the value of s1. +*/ +void * memcpy( void * restrict s1, const void * restrict s2, size_t n ); + +/* Copy a number of n characters from the memory area pointed to by s2 to the + area pointed to by s1. The two areas may overlap. + Returns the value of s1. +*/ +void * memmove( void * restrict s1, const void * restrict s2, size_t n ); + +/* Copy the character array s2 (including terminating '\0' byte) into the + character array s1. + Returns the value of s1. +*/ +char * strcpy( char * restrict s1, const char * restrict s2 ); + +/* Copy a maximum of n characters from the character array s2 into the character + array s1. If s2 is shorter than n characters, '\0' bytes will be appended to + the copy in s1 until n characters have been written. If s2 is longer than n + characters, NO terminating '\0' will be written to s1. If the arrays overlap, + behaviour is undefined. + Returns the value of s1. +*/ +char * strncpy( char * restrict s1, const char * restrict s2, size_t n ); + +/* Concatenation functions */ + +/* Append the contents of the character array s2 (including terminating '\0') to + the character array s1 (first character of s2 overwriting the '\0' of s1). If + the arrays overlap, behaviour is undefined. + Returns the value of s1. +*/ +char * strcat( char * restrict s1, const char * restrict s2 ); + +/* Append a maximum of n characters from the character array s1 to the character + array s1 (first character of s2 overwriting the '\0' of s1). A terminating + '\0' is ALWAYS appended, even if the full n characters have already been + written. If the arrays overlap, behaviour is undefined. + Returns the value of s1. +*/ +char * strncat( char * restrict s1, const char * restrict s2, size_t n ); + +/* Comparison functions */ + +/* Compare the first n characters of the memory areas pointed to by s1 and s2. + Returns 0 if s1 == s2, a negative number if s1 < s2, and a positive number if + s1 > s2. +*/ +int memcmp( const void * s1, const void * s2, size_t n ); + +/* Compare the character arrays s1 and s2. + Returns 0 if s1 == s2, a negative number if s1 < s2, and a positive number if + s1 > s2. +*/ +int strcmp( const char * s1, const char * s2 ); + +/* Compare the character arrays s1 and s2, interpreted as specified by the + LC_COLLATE category of the current locale. + Returns 0 if s1 == s2, a negative number if s1 < s2, and a positive number if + s1 > s2. + TODO: Currently a dummy wrapper for strcmp() as PDCLib does not yet support + locales. +*/ +int strcoll( const char * s1, const char * s2 ); + +/* Compare no more than the first n characters of the character arrays s1 and + s2. + Returns 0 if s1 == s2, a negative number if s1 < s2, and a positive number if + s1 > s2. +*/ +int strncmp( const char * s1, const char * s2, size_t n ); + +/* Transform the character array s2 as appropriate for the LC_COLLATE setting of + the current locale. If length of resulting string is less than n, store it in + the character array pointed to by s1. Return the length of the resulting + string. +*/ +size_t strxfrm( char * restrict s1, const char * restrict s2, size_t n ); + +/* Search functions */ + +/* Search the first n characters in the memory area pointed to by s for the + character c (interpreted as unsigned char). + Returns a pointer to the first instance found, or NULL. +*/ +void * memchr( const void * s, int c, size_t n ); + +/* Search the character array s (including terminating '\0') for the character c + (interpreted as char). + Returns a pointer to the first instance found, or NULL. +*/ +char * strchr( const char * s, int c ); + +/* Determine the length of the initial substring of character array s1 which + consists only of characters not from the character array s2. + Returns the length of that substring. +*/ +size_t strcspn( const char * s1, const char * s2 ); + +/* Search the character array s1 for any character from the character array s2. + Returns a pointer to the first occurrence, or NULL. +*/ +char * strpbrk( const char * s1, const char * s2 ); + +/* Search the character array s (including terminating '\0') for the character c + (interpreted as char). + Returns a pointer to the last instance found, or NULL. +*/ +char * strrchr( const char * s, int c ); + +/* Determine the length of the initial substring of character array s1 which + consists only of characters from the character array s2. + Returns the length of that substring. +*/ +size_t strspn( const char * s1, const char * s2 ); + +/* Search the character array s1 for the substring in character array s2. + Returns a pointer to that sbstring, or NULL. If s2 is of length zero, + returns s1. +*/ +char * strstr( const char * s1, const char * s2 ); + +/* In a series of subsequent calls, parse a C string into tokens. + On the first call to strtok(), the first argument is a pointer to the to-be- + parsed C string. On subsequent calls, the first argument is NULL unless you + want to start parsing a new string. s2 holds an array of seperator characters + which can differ from call to call. Leading seperators are skipped, the first + trailing seperator overwritten with '\0'. + Returns a pointer to the next token. + WARNING: This function uses static storage, and as such is not reentrant. +*/ +char * strtok( char * restrict s1, const char * restrict s2 ); + +/* Miscellaneous functions */ + +/* Write the character c (interpreted as unsigned char) to the first n + characters of the memory area pointed to by s. + Returns s. +*/ +void * memset( void * s, int c, size_t n ); + +/* Map an error number to a (locale-specific) error message string. Error + numbers are typically errno values, but any number is mapped to a message. + TODO: PDCLib does not yet support locales. +*/ +char * strerror( int errnum ); + +/* Returns the length of the string s (excluding terminating '\0'). +*/ +size_t strlen( const char * s ); + +CPP_CHECK_END diff --git a/src/libraries/libc/include/time.h b/src/libraries/libc/include/time.h new file mode 100644 index 0000000..9680a64 --- /dev/null +++ b/src/libraries/libc/include/time.h @@ -0,0 +1,105 @@ +#pragma once +/* Date and time + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/cpp.h" +#include "j6libc/int.h" +#include "j6libc/null.h" +#include "j6libc/size_t.h" + +CPP_CHECK_BEGIN + +/* These are defined to be "real types capable of representing types", with + "range and precision of times representable in [them being] implementation- + defined". + As part of struct timespec (see below), time_t is further defined as "a + linear count of seconds", with potentially different semantics from a + "normal" time_t. + For sake of simplicity, we used just that (common) definition of "seconds + since epoch" as integer. +*/ +typedef _PDCLIB_time_t time_t; +typedef _PDCLIB_clock_t clock_t; + +#define CLOCKS_PER_SEC _PDCLIB_CLOCKS_PER_SEC +#define TIME_UTC _PDCLIB_TIME_UTC + +struct timespec +{ + time_t tv_sec; + long tv_nsec; +}; + +struct tm +{ + int tm_sec; /* 0-60 */ + int tm_min; /* 0-59 */ + int tm_hour; /* 0-23 */ + int tm_mday; /* 1-31 */ + int tm_mon; /* 0-11 */ + int tm_year; /* years since 1900 */ + int tm_wday; /* 0-6 */ + int tm_yday; /* 0-365 */ + int tm_isdst; /* >0 DST, 0 no DST, <0 information unavailable */ +}; + +/* Returns the number of "clocks" in processor time since the invocation + of the program. Divide by CLOCKS_PER_SEC to get the value in seconds. + Returns -1 if the value cannot be represented in the return type or is + not available. +*/ +clock_t clock( void ); + +/* Returns the difference between two calendar times in seconds. */ +double difftime( time_t time1, time_t time0 ); + +/* Normalizes the values in the broken-down time pointed to by timeptr. + Returns the calender time specified by the broken-down time. +*/ +time_t mktime( struct tm * timeptr ); + +/* Returns the current calender time. If timer is not a NULL pointer, stores + the current calender time at that address as well. +*/ +time_t time( time_t * timer ); + +/* Sets the interval pointed to by ts to the current calender time, based + on the specified base. + Returns base, if successful, otherwise zero. +*/ +int timespec_get( struct timespec * ts, int base ); + +/* Converts the broken-down time pointed to by timeptr into a string in the + form "Sun Sep 16 01:03:52 1973\n\0". +*/ +char * asctime( const struct tm * timeptr ); + +/* Equivalent to asctime( localtime( timer ) ). */ +char * ctime( const time_t * timer ); + +/* Converts the calender time pointed to by timer into a broken-down time + expressed as UTC. + Returns a pointer to the broken-down time, or a NULL pointer if it + cannot be represented. +*/ +struct tm * gmtime( const time_t * timer ); + +/* Converts the calender time pointed to by timer into a broken-down time + expressed as local time. + Returns a pointer to the broken-down time, or a NULL pointer if if + cannot be represented. +*/ +struct tm * localtime( const time_t * timer ); + +/* Writes the broken-down time pointed to by timeptr into the character + array pointed to by s. The string pointed to by format controls the + exact output. No more than maxsize charactrs will be written. + Returns the number of characters written (excluding the terminating + null character), or zero on failure. +*/ +size_t strftime( char * restrict s, size_t maxsize, const char * restrict format, const struct tm * restrict timeptr ); + +CPP_CHECK_END diff --git a/src/libraries/libc/include/wchar.h b/src/libraries/libc/include/wchar.h new file mode 100644 index 0000000..8d2d57e --- /dev/null +++ b/src/libraries/libc/include/wchar.h @@ -0,0 +1,91 @@ +#pragma once +/* Wide character functions +*/ + +#include "j6libc/cpp.h" +#include "j6libc/int.h" +#include "j6libc/null.h" +#include "j6libc/size_t.h" +#include "j6libc/wchar_t.h" +#include "j6libc/wctype_t.h" +#include "j6libc/wint_t.h" + +#include +#include + +CPP_CHECK_BEGIN + +typedef unsigned long mbstate_t; + +wint_t btowc(int); +int fwprintf(FILE *, const wchar_t *, ...); +int fwscanf(FILE *, const wchar_t *, ...); +int iswalnum(wint_t); +int iswalpha(wint_t); +int iswcntrl(wint_t); +int iswdigit(wint_t); +int iswgraph(wint_t); +int iswlower(wint_t); +int iswprint(wint_t); +int iswpunct(wint_t); +int iswspace(wint_t); +int iswupper(wint_t); +int iswxdigit(wint_t); +int iswctype(wint_t, wctype_t); +wint_t fgetwc(FILE *); +wchar_t *fgetws(wchar_t *, int, FILE *); +wint_t fputwc(wchar_t, FILE *); +int fputws(const wchar_t *, FILE *); +int fwide(FILE *, int); +wint_t getwc(FILE *); +wint_t getwchar(void); +int mbsinit(const mbstate_t *); +size_t mbrlen(const char *, size_t, mbstate_t *); +size_t mbrtowc(wchar_t *, const char *, size_t, mbstate_t *); +size_t mbsrtowcs(wchar_t *, const char **, size_t, mbstate_t *); +wint_t putwc(wchar_t, FILE *); +wint_t putwchar(wchar_t); +int swprintf(wchar_t *, size_t, const wchar_t *, ...); +int swscanf(const wchar_t *, const wchar_t *, ...); +wint_t towlower(wint_t); +wint_t towupper(wint_t); +wint_t ungetwc(wint_t, FILE *); +int vfwprintf(FILE *, const wchar_t *, va_list); +int vwprintf(const wchar_t *, va_list); +int vswprintf(wchar_t *, size_t, const wchar_t *, va_list); +size_t wcrtomb(char *, wchar_t, mbstate_t *); +wchar_t *wcscat(wchar_t *, const wchar_t *); +wchar_t *wcschr(const wchar_t *, wchar_t); +int wcscmp(const wchar_t *, const wchar_t *); +int wcscoll(const wchar_t *, const wchar_t *); +wchar_t *wcscpy(wchar_t *, const wchar_t *); +size_t wcscspn(const wchar_t *, const wchar_t *); +size_t wcsftime(wchar_t *, size_t, const wchar_t *, const struct tm *); +size_t wcslen(const wchar_t *); +wchar_t *wcsncat(wchar_t *, const wchar_t *, size_t); +int wcsncmp(const wchar_t *, const wchar_t *, size_t); +wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t); +wchar_t *wcspbrk(const wchar_t *, const wchar_t *); +wchar_t *wcsrchr(const wchar_t *, wchar_t); +size_t wcsrtombs(char *, const wchar_t **, size_t, mbstate_t *); +size_t wcsspn(const wchar_t *, const wchar_t *); +wchar_t *wcsstr(const wchar_t *, const wchar_t *); +double wcstod(const wchar_t *, wchar_t **); +wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **); +long int wcstol(const wchar_t *, wchar_t **, int); +unsigned long int wcstoul(const wchar_t *, wchar_t **, int); +wchar_t *wcswcs(const wchar_t *, const wchar_t *); +int wcswidth(const wchar_t *, size_t); +size_t wcsxfrm(wchar_t *, const wchar_t *, size_t); +int wctob(wint_t); +wctype_t wctype(const char *); +int wcwidth(wchar_t); +wchar_t *wmemchr(const wchar_t *, wchar_t, size_t); +int wmemcmp(const wchar_t *, const wchar_t *, size_t); +wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t); +wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t); +wchar_t *wmemset(wchar_t *, wchar_t, size_t); +int wprintf(const wchar_t *, ...); +int wscanf(const wchar_t *, ...); + +CPP_CHECK_END diff --git a/src/libraries/libc/include/wctype.h b/src/libraries/libc/include/wctype.h new file mode 100644 index 0000000..89364fc --- /dev/null +++ b/src/libraries/libc/include/wctype.h @@ -0,0 +1,138 @@ +#pragma once +/* Wide character classification and mapping utilities + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/cpp.h" +#include "j6libc/int.h" +#include "j6libc/wint_t.h" + +CPP_CHECK_BEGIN + +// wctrans_t +// wctype_t + +#ifndef _PDCLIB_WEOF_DEFINED +#define _PDCLIB_WEOF_DEFINED _PDCLIB_WEOF_DEFINED +#define WEOF (wint_t)-1 +#endif + +/* Wide character classification functions */ + +/* Returns iswalpha( wc ) || iswdigit( wc ) */ +int iswalnum( wint_t wc ); + +/* Returns true for wide characters for which either isupper( wc ) or + islower( wc ) is true, as well as a set of locale-specific wide + characters which are neither control characters, digits, punctuation, + or whitespace. +*/ +int iswalpha( wint_t wc ); + +/* Returns true if the character iswspace() and used for separating words + within a line of text. In the "C" locale, only L' ' and L'\t' are + considered blanks. +*/ +int iswblank( wint_t wc ); + +/* Returns true if the wide character is a control character. */ +int iswcntrl( wint_t wc ); + +/* Returns true if the wide character is a decimal digit. Locale- + independent. */ +int iswdigit( wint_t wc ); + +/* Returns iswprint( wc ) && ! iswspace( wc ). + NOTE: This definition differs from that of isgraph() in , + which considers only ' ', not all isspace() characters. +*/ +int iswgraph( wint_t wc ); + +/* Returns true for lowerspace wide characters, as well as a set of + locale-specific wide characters which are neither control charcters, + digits, punctuation, or whitespace. +*/ +int iswlower( wint_t wc ); + +/* Returns true for every printing wide character. */ +int iswprint( wint_t wc ); + +/* Returns true for a locale-specific set of punctuation characters that + are neither whitespace nor alphanumeric. +*/ +int iswpunct( wint_t wc ); + +/* Returns true for a locale-specific set of whitespace characters that + are neither alphanumeric, graphic, or punctuation. +*/ +int iswspace( wint_t wc ); + +/* Returns true for upperspace wide characters, as well as a set of + locale-specific wide characters which are neither control charcters, + digits, punctuation, or whitespace. +*/ +int iswupper( wint_t wc ); + +/* Returns true if the wide character is a hexadecimal digit. Locale- + independent. */ +int iswxdigit( wint_t wc ); + +/* Extensible wide character classification functions */ + +/* Returns true if the wide character wc has the property described by + desc (which was retrieved by a previous call to wctype() without + changing the LC_CTYPE locale setting between the two calls). +*/ +int iswctype( wint_t wc, wctype_t desc ); + +/* Returns a description object for a named character property, to be + used as parameter to the iswctype() function. Supported property + names are: + "alnum" -- alphanumeric, as per iswalnum() + "alpha" -- alphabetic, as per iswalpha() + "blank" -- blank, as per iswblank() + "cntrl" -- control, as per iswcntrl() + "digit" -- decimal digit, as per iswdigit() + "graph" -- graphic, as per iswgraph() + "lower" -- lowercase, as per iswlower() + "print" -- printing, as per iswprint() + "punct" -- punctuation, as per iswprint() + "space" -- whitespace, as per iswspace() + "upper" -- uppercase, as per iswupper() + "xdigit" -- hexadecimal digit, as per iswxdigit() + For unsupported properties, the function returns zero. +*/ +wctype_t wctype( const char * property ); + +/* Wide character case mapping utilities */ + +/* Converts an uppercase letter to a corresponding lowercase letter. Input for + which no corresponding lowercase letter exists remains unchanged. +*/ +wint_t towlower( wint_t wc ); + +/* Converts a lowercase letter to a corresponding uppercase letter. Input for + which no corresponding uppercase letter exists remains unchanged. +*/ +wint_t towupper( wint_t wc ); + +/* Extensible wide character case mapping utilities */ + +/* Converts the wide character wc according to the transition described + by desc (which was retrieved by a previous call to wctrans() without + changing the LC_CTYPE locale setting between the two calls). +*/ +wint_t towctrans( wint_t wc, wctrans_t desc ); + +/* Returns a description object for a named character transformation, to + be used as parameter to the towctrans() function. Supported transformation + properties are: + "tolower" -- lowercase mapping, as per towlower() + "toupper" -- uppercase mapping, as per towupper() + For unsupported properties, the function returns zero. +*/ +wctrans_t wctrans( const char * property ); + +CPP_CHECK_END diff --git a/src/libraries/libc/inttypes/imaxabs.c b/src/libraries/libc/inttypes/imaxabs.c new file mode 100644 index 0000000..5d5232b --- /dev/null +++ b/src/libraries/libc/inttypes/imaxabs.c @@ -0,0 +1,12 @@ +/* imaxabs( intmax_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +intmax_t imaxabs( intmax_t j ) +{ + return ( j >= 0 ) ? j : -j; +} diff --git a/src/libraries/libc/inttypes/imaxdiv.c b/src/libraries/libc/inttypes/imaxdiv.c new file mode 100644 index 0000000..d70c0c1 --- /dev/null +++ b/src/libraries/libc/inttypes/imaxdiv.c @@ -0,0 +1,15 @@ +/* lldiv( long long int, long long int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +imaxdiv_t imaxdiv( intmax_t numer, intmax_t denom ) +{ + imaxdiv_t rc; + rc.quot = numer / denom; + rc.rem = numer % denom; + return rc; +} diff --git a/src/libraries/libc/inttypes/strtoimax.c b/src/libraries/libc/inttypes/strtoimax.c new file mode 100644 index 0000000..a8b871d --- /dev/null +++ b/src/libraries/libc/inttypes/strtoimax.c @@ -0,0 +1,27 @@ +/* strtoimax( const char *, char * *, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +intmax_t strtoimax( const char * restrict nptr, char ** restrict endptr, int base ) +{ + intmax_t rc; + char sign = '+'; + const char * p = _PDCLIB_strtox_prelim( nptr, &sign, &base ); + if ( base < 2 || base > 36 ) return 0; + if ( sign == '+' ) + { + rc = (intmax_t)_PDCLIB_strtox_main( &p, (unsigned)base, (uintmax_t)INTMAX_MAX, (uintmax_t)( INTMAX_MAX / base ), (int)( INTMAX_MAX % base ), &sign ); + } + else + { + rc = (intmax_t)_PDCLIB_strtox_main( &p, (unsigned)base, (uintmax_t)INTMAX_MIN, (uintmax_t)( INTMAX_MIN / -base ), (int)( -( INTMAX_MIN % base ) ), &sign ); + } + if ( endptr != NULL ) *endptr = ( p != NULL ) ? (char *) p : (char *) nptr; + return ( sign == '+' ) ? rc : -rc; +} diff --git a/src/libraries/libc/inttypes/strtoumax.c b/src/libraries/libc/inttypes/strtoumax.c new file mode 100644 index 0000000..38be1c3 --- /dev/null +++ b/src/libraries/libc/inttypes/strtoumax.c @@ -0,0 +1,20 @@ +/* strtoumax( const char *, char * *, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +uintmax_t strtoumax( const char * restrict nptr, char ** restrict endptr, int base ) +{ + uintmax_t rc; + char sign = '+'; + const char * p = _PDCLIB_strtox_prelim( nptr, &sign, &base ); + if ( base < 2 || base > 36 ) return 0; + rc = _PDCLIB_strtox_main( &p, (unsigned)base, (uintmax_t)UINTMAX_MAX, (uintmax_t)( UINTMAX_MAX / base ), (int)( UINTMAX_MAX % base ), &sign ); + if ( endptr != NULL ) *endptr = ( p != NULL ) ? (char *) p : (char *) nptr; + return ( sign == '+' ) ? rc : -rc; +} diff --git a/src/libraries/libc/j6libc/Readme.txt b/src/libraries/libc/j6libc/Readme.txt new file mode 100644 index 0000000..13ad05c --- /dev/null +++ b/src/libraries/libc/j6libc/Readme.txt @@ -0,0 +1,10 @@ +This directory holds various "internals" of PDCLib: + +- definitions of helper functions not specified by the standard (hidden in the + _PDCLIB_* namespace); + +- definitions of data objects, both internal (like _PDCLIB_digits) and specified by + the standard (_PDCLIB_errno); + +- test drivers for functionality that does not have its own implementation + file to put the test driver in (stdarg). diff --git a/src/libraries/libc/j6libc/allocpages.c b/src/libraries/libc/j6libc/allocpages.c new file mode 100644 index 0000000..f638ba3 --- /dev/null +++ b/src/libraries/libc/j6libc/allocpages.c @@ -0,0 +1,16 @@ +/* _PDCLIB_allocpages( int const ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* This is an example implementation of _PDCLIB_allocpages() fit for use with + POSIX kernels. +*/ + +#include "j6libc/glue.h" + +void * _PDCLIB_allocpages( int const n ) +{ + return 0; +} diff --git a/src/libraries/libc/j6libc/assert.c b/src/libraries/libc/j6libc/assert.c new file mode 100644 index 0000000..8c751ac --- /dev/null +++ b/src/libraries/libc/j6libc/assert.c @@ -0,0 +1,19 @@ +/* _PDCLIB_assert( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +#include "j6libc/aux.h" + +void _PDCLIB_assert( const char * const message1, const char * const function, const char * const message2 ) +{ + fputs( message1, stderr ); + fputs( function, stderr ); + fputs( message2, stderr ); + abort(); +} diff --git a/src/libraries/libc/j6libc/atomax.c b/src/libraries/libc/j6libc/atomax.c new file mode 100644 index 0000000..2a7e949 --- /dev/null +++ b/src/libraries/libc/j6libc/atomax.c @@ -0,0 +1,25 @@ +/* _PDCLIB_atomax( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +intmax_t _PDCLIB_atomax( const char * s ) +{ + intmax_t rc = 0; + char sign = '+'; + const char * x; + /* TODO: In other than "C" locale, additional patterns may be defined */ + while ( isspace( *s ) ) ++s; + if ( *s == '+' ) ++s; + else if ( *s == '-' ) sign = *(s++); + /* TODO: Earlier version was missing tolower() but was not caught by tests */ + while ( ( x = memchr( _PDCLIB_digits, tolower(*(s++)), 10 ) ) != NULL ) + { + rc = rc * 10 + ( x - _PDCLIB_digits ); + } + return ( sign == '+' ) ? rc : -rc; +} diff --git a/src/libraries/libc/j6libc/close.c b/src/libraries/libc/j6libc/close.c new file mode 100644 index 0000000..362bae4 --- /dev/null +++ b/src/libraries/libc/j6libc/close.c @@ -0,0 +1,16 @@ +/* _PDCLIB_close( _PDCLIB_fd_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* This is an example implementation of _PDCLIB_close() fit for use with POSIX + kernels. +*/ + +#include "j6libc/glue.h" + +int _PDCLIB_close( int fd ) +{ + return -1; +} diff --git a/src/libraries/libc/j6libc/closeall.c b/src/libraries/libc/j6libc/closeall.c new file mode 100644 index 0000000..7031a2e --- /dev/null +++ b/src/libraries/libc/j6libc/closeall.c @@ -0,0 +1,21 @@ +/* _PDCLIB_closeall( void ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +extern struct _PDCLIB_file_t * _PDCLIB_filelist; + +void _PDCLIB_closeall( void ) +{ + struct _PDCLIB_file_t * stream = _PDCLIB_filelist; + struct _PDCLIB_file_t * next; + while ( stream != NULL ) + { + next = stream->next; + fclose( stream ); + stream = next; + } +} diff --git a/src/libraries/libc/j6libc/digits.c b/src/libraries/libc/j6libc/digits.c new file mode 100644 index 0000000..86d4ce9 --- /dev/null +++ b/src/libraries/libc/j6libc/digits.c @@ -0,0 +1,12 @@ +/* _PDCLIB_digits + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/int.h" + +const char _PDCLIB_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +/* For _PDCLIB/print.c only; obsolete with ctype.h */ +const char _PDCLIB_Xdigits[] = "0123456789ABCDEF"; diff --git a/src/libraries/libc/j6libc/errno.c b/src/libraries/libc/j6libc/errno.c new file mode 100644 index 0000000..26e8605 --- /dev/null +++ b/src/libraries/libc/j6libc/errno.c @@ -0,0 +1,14 @@ +/* _PDCLIB_errno + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/int.h" + +int _PDCLIB_errno = 0; + +int * _PDCLIB_errno_func() +{ + return &_PDCLIB_errno; +} diff --git a/src/libraries/libc/j6libc/filemode.c b/src/libraries/libc/j6libc/filemode.c new file mode 100644 index 0000000..0249a14 --- /dev/null +++ b/src/libraries/libc/j6libc/filemode.c @@ -0,0 +1,54 @@ +/* _PDCLIB_filemode( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +/* Helper function that parses the C-style mode string passed to fopen() into + the PDCLib flags FREAD, FWRITE, FAPPEND, FRW (read-write) and FBIN (binary + mode). +*/ +unsigned int _PDCLIB_filemode( const char * const mode ) +{ + unsigned rc = 0; + size_t i; + switch ( mode[0] ) + { + case 'r': + rc |= _PDCLIB_FREAD; + break; + case 'w': + rc |= _PDCLIB_FWRITE; + break; + case 'a': + rc |= _PDCLIB_FAPPEND | _PDCLIB_FWRITE; + break; + default: + /* Other than read, write, or append - invalid */ + return 0; + } + for ( i = 1; i < 4; ++i ) + { + switch ( mode[i] ) + { + case '+': + if ( rc & _PDCLIB_FRW ) return 0; /* Duplicates are invalid */ + rc |= _PDCLIB_FRW; + break; + case 'b': + if ( rc & _PDCLIB_FBIN ) return 0; /* Duplicates are invalid */ + rc |= _PDCLIB_FBIN; + break; + case '\0': + /* End of mode */ + return rc; + default: + /* Other than read/write or binary - invalid. */ + return 0; + } + } + /* Longer than three chars - invalid. */ + return 0; +} diff --git a/src/libraries/libc/j6libc/fillbuffer.c b/src/libraries/libc/j6libc/fillbuffer.c new file mode 100644 index 0000000..5176e5f --- /dev/null +++ b/src/libraries/libc/j6libc/fillbuffer.c @@ -0,0 +1,19 @@ +/* _PDCLIB_fillbuffer( struct _PDCLIB_file_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* This is an example implementation of _PDCLIB_fillbuffer() fit for + use with POSIX kernels. +*/ + +#include +#include "j6libc/glue.h" + +int _PDCLIB_fillbuffer( struct _PDCLIB_file_t * stream ) +{ + _PDCLIB_errno = _PDCLIB_ERROR; + stream->status |= _PDCLIB_ERRORFLAG; + return EOF; +} diff --git a/src/libraries/libc/j6libc/flushbuffer.c b/src/libraries/libc/j6libc/flushbuffer.c new file mode 100644 index 0000000..0ef7b07 --- /dev/null +++ b/src/libraries/libc/j6libc/flushbuffer.c @@ -0,0 +1,19 @@ +/* _PDCLIB_flushbuffer( struct _PDCLIB_file_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* This is an example implementation of _PDCLIB_flushbuffer() fit for + use with POSIX kernels. +*/ + +#include +#include "j6libc/glue.h" + +int _PDCLIB_flushbuffer( struct _PDCLIB_file_t * stream ) +{ + _PDCLIB_errno = _PDCLIB_ERROR; + stream->status |= _PDCLIB_ERRORFLAG; + return EOF; +} diff --git a/src/libraries/libc/j6libc/is_leap.c b/src/libraries/libc/j6libc/is_leap.c new file mode 100644 index 0000000..97edd5a --- /dev/null +++ b/src/libraries/libc/j6libc/is_leap.c @@ -0,0 +1,14 @@ +/* _PDCLIB_is_leap( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include "j6libc/int.h" + +int _PDCLIB_is_leap( int year_offset ) +{ + /* year given as offset from 1900, matching tm.tm_year in */ + long long year = year_offset + 1900ll; + return ( ( year % 4 ) == 0 && ( ( year % 25 ) != 0 || ( year % 400 ) == 0 ) ); +} diff --git a/src/libraries/libc/j6libc/load_lc_collate.c b/src/libraries/libc/j6libc/load_lc_collate.c new file mode 100644 index 0000000..b4cc362 --- /dev/null +++ b/src/libraries/libc/j6libc/load_lc_collate.c @@ -0,0 +1,44 @@ +/* _PDCLIB_load_lc_collate( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include + +#include "j6libc/int.h" + +struct _PDCLIB_lc_collate_t * _PDCLIB_load_lc_collate( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_collate_t * rc = NULL; + const char * extension = "_collate.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_collate_t ) ) ) != NULL ) + { + /* TODO: Collation data */ + + rc->alloced = 1; + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} diff --git a/src/libraries/libc/j6libc/load_lc_ctype.c b/src/libraries/libc/j6libc/load_lc_ctype.c new file mode 100644 index 0000000..6abeb85 --- /dev/null +++ b/src/libraries/libc/j6libc/load_lc_ctype.c @@ -0,0 +1,74 @@ +/* _PDCLIB_load_lc_ctype( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "j6libc/int.h" + +struct _PDCLIB_lc_ctype_t * _PDCLIB_load_lc_ctype( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_ctype_t * rc = NULL; + const char * extension = "_ctype.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_ctype_t ) ) ) != NULL ) + { + struct _PDCLIB_lc_ctype_entry_t * entry; + + if ( ( entry = malloc( sizeof( struct _PDCLIB_lc_ctype_entry_t ) * _PDCLIB_CHARSET_SIZE + 1 ) ) != NULL ) + { + rc->entry = entry + 1; + rc->entry[ -1 ].flags = rc->entry[ -1 ].upper = rc->entry[ -1 ].lower = 0; + + if ( fscanf( fh, "%x %x %x %x %x %x", &rc->digits_low, &_PDCLIB_lc_ctype.digits_high, &_PDCLIB_lc_ctype.Xdigits_low, &_PDCLIB_lc_ctype.Xdigits_high, &_PDCLIB_lc_ctype.xdigits_low, &_PDCLIB_lc_ctype.xdigits_high ) == 6 ) + { + size_t i; + + for ( i = 0; i < _PDCLIB_CHARSET_SIZE; ++i ) + { + if ( fscanf( fh, "%hx %hhx %hhx", &rc->entry[ i ].flags, &rc->entry[ i ].upper, &rc->entry[ i ].lower ) != 3 ) + { + fclose( fh ); + free( file ); + free( rc->entry - 1 ); + free( rc ); + return NULL; + } + } + } + + rc->alloced = 1; + } + else + { + free( rc ); + } + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} diff --git a/src/libraries/libc/j6libc/load_lc_messages.c b/src/libraries/libc/j6libc/load_lc_messages.c new file mode 100644 index 0000000..b429745 --- /dev/null +++ b/src/libraries/libc/j6libc/load_lc_messages.c @@ -0,0 +1,60 @@ +/* _PDCLIB_load_lc_messages( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include + +#include "j6libc/int.h" + +struct _PDCLIB_lc_messages_t * _PDCLIB_load_lc_messages( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_messages_t * rc = NULL; + const char * extension = "_messages.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_messages_t ) ) ) != NULL ) + { + char * data = _PDCLIB_load_lines( fh, _PDCLIB_ERRNO_MAX ); + + if ( data != NULL ) + { + size_t i; + + for ( i = 0; i < _PDCLIB_ERRNO_MAX; ++i ) + { + rc->errno_texts[ i ] = data; + data += strlen( data ) + 1; + } + + rc->alloced = 1; + } + else + { + free( rc ); + rc = NULL; + } + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} diff --git a/src/libraries/libc/j6libc/load_lc_monetary.c b/src/libraries/libc/j6libc/load_lc_monetary.c new file mode 100644 index 0000000..e0a7d74 --- /dev/null +++ b/src/libraries/libc/j6libc/load_lc_monetary.c @@ -0,0 +1,90 @@ +/* _PDCLIB_load_lc_monetary( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include +#include + +#include "j6libc/int.h" + +struct _PDCLIB_lc_lconv_monetary_t * _PDCLIB_load_lc_monetary( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_lconv_monetary_t * rc = NULL; + const char * extension = "_monetary.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_lconv_monetary_t ) ) ) != NULL ) + { + char buffer[ 14 ]; + char * data = _PDCLIB_load_lines( fh, 7 ); + + if ( data != NULL ) + { + if ( fread( buffer, 1, 14, fh ) == 14 ) + { + rc->mon_decimal_point = data; + data += strlen( data ) + 1; + rc->mon_thousands_sep = data; + data += strlen( data ) + 1; + rc->mon_grouping = data; + data += strlen( data ) + 1; + rc->positive_sign = data; + data += strlen( data ) + 1; + rc->negative_sign = data; + data += strlen( data ) + 1; + rc->currency_symbol = data; + data += strlen( data ) + 1; + rc->int_curr_symbol = data; + + rc->frac_digits = buffer[ 0 ]; + rc->p_cs_precedes = buffer[ 1 ]; + rc->n_cs_precedes = buffer[ 2 ]; + rc->p_sep_by_space = buffer[ 3 ]; + rc->n_sep_by_space = buffer[ 4 ]; + rc->p_sign_posn = buffer[ 5 ]; + rc->n_sign_posn = buffer[ 6 ]; + rc->int_frac_digits = buffer[ 7 ]; + rc->int_p_cs_precedes = buffer[ 8 ]; + rc->int_n_cs_precedes = buffer[ 9 ]; + rc->int_p_sep_by_space = buffer[ 10 ]; + rc->int_n_sep_by_space = buffer[ 11 ]; + rc->int_p_sign_posn = buffer[ 12 ]; + rc->int_n_sign_posn= buffer[ 13 ]; + } + else + { + free( data ); + free( rc ); + rc = NULL; + } + } + else + { + free( rc ); + rc = NULL; + } + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} diff --git a/src/libraries/libc/j6libc/load_lc_numeric.c b/src/libraries/libc/j6libc/load_lc_numeric.c new file mode 100644 index 0000000..5c5f671 --- /dev/null +++ b/src/libraries/libc/j6libc/load_lc_numeric.c @@ -0,0 +1,56 @@ +/* _PDCLIB_load_lc_numeric( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include + +#include "j6libc/int.h" + +struct _PDCLIB_lc_lconv_numeric_t * _PDCLIB_load_lc_numeric( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_lconv_numeric_t * rc = NULL; + const char * extension = "_numeric.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_lconv_numeric_t ) ) ) != NULL ) + { + char * data = _PDCLIB_load_lines( fh, 3 ); + + if ( data != NULL ) + { + rc->decimal_point = data; + data += strlen( data ) + 1; + rc->thousands_sep = data; + data += strlen( data ) + 1; + rc->grouping = data; + } + else + { + free( rc ); + rc = NULL; + } + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} diff --git a/src/libraries/libc/j6libc/load_lc_time.c b/src/libraries/libc/j6libc/load_lc_time.c new file mode 100644 index 0000000..aa17ab2 --- /dev/null +++ b/src/libraries/libc/j6libc/load_lc_time.c @@ -0,0 +1,79 @@ +/* _PDCLIB_load_lc_time( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include +#include + +#include "j6libc/int.h" + +struct _PDCLIB_lc_time_t * _PDCLIB_load_lc_time( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_time_t * rc = NULL; + const char * extension = "_time.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_time_t ) ) ) != NULL ) + { + char * data = _PDCLIB_load_lines( fh, 44 ); + + if ( data != NULL ) + { + size_t i; + + for ( i = 0; i < 12; ++i ) + { + rc->month_name_abbr[ i ] = data; + data += strlen( data ) + 1; + } + + for ( i = 0; i < 12; ++i ) + { + rc->month_name_full[ i ] = data; + data += strlen( data ) + 1; + } + + for ( i = 0; i < 7; ++i ) + { + rc->day_name_abbr[ i ] = data; + data += strlen( data ) + 1; + } + + for ( i = 0; i < 7; ++i ) + { + rc->day_name_full[ i ] = data; + data += strlen( data ) + 1; + } + + rc->alloced = 1; + } + else + { + free( rc ); + rc = NULL; + } + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} diff --git a/src/libraries/libc/j6libc/load_lines.c b/src/libraries/libc/j6libc/load_lines.c new file mode 100644 index 0000000..b61a283 --- /dev/null +++ b/src/libraries/libc/j6libc/load_lines.c @@ -0,0 +1,48 @@ +/* _PDCLIB_load_lines( FILE *, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +char * _PDCLIB_load_lines( FILE * fh, size_t lines ) +{ + size_t required = 0; + long pos = ftell( fh ); + char * rc = NULL; + int c; + + /* Count the number of characters */ + while ( lines && ( c = fgetc( fh ) ) != EOF ) + { + if ( c == '\n' ) + { + --lines; + } + + ++required; + } + + if ( ! feof( fh ) ) + { + if ( ( rc = malloc( required ) ) != NULL ) + { + size_t i; + + fseek( fh, pos, SEEK_SET ); + fread( rc, 1, required, fh ); + + for ( i = 0; i < required; ++i ) + { + if ( rc[ i ] == '\n' ) + { + rc[ i ] = '\0'; + } + } + } + } + + return rc; +} diff --git a/src/libraries/libc/j6libc/open.c b/src/libraries/libc/j6libc/open.c new file mode 100644 index 0000000..c6a2804 --- /dev/null +++ b/src/libraries/libc/j6libc/open.c @@ -0,0 +1,17 @@ +/* _PDCLIB_open( const char * const, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* This is an example implementation of _PDCLIB_open() fit for use with POSIX + kernels. +*/ + +#include "j6libc/glue.h" + +int _PDCLIB_open( const char * const filename, unsigned int mode ) +{ + _PDCLIB_errno = _PDCLIB_ERROR; + return -1; +} diff --git a/src/libraries/libc/j6libc/prepread.c b/src/libraries/libc/j6libc/prepread.c new file mode 100644 index 0000000..c60b34c --- /dev/null +++ b/src/libraries/libc/j6libc/prepread.c @@ -0,0 +1,34 @@ +/* _PDCLIB_prepread( struct _PDCLIB_file_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +#include "j6libc/glue.h" + +int _PDCLIB_prepread( struct _PDCLIB_file_t * stream ) +{ + if ( ( stream->bufidx > stream->bufend ) || + ( stream->status & ( _PDCLIB_FWRITE | _PDCLIB_FAPPEND | _PDCLIB_ERRORFLAG | _PDCLIB_WIDESTREAM | _PDCLIB_EOFFLAG ) ) || + ! ( stream->status & ( _PDCLIB_FREAD | _PDCLIB_FRW ) ) ) + { + /* Function called on illegal (e.g. output) stream. + See comments on implementation-defined errno values in + . + */ + _PDCLIB_errno = _PDCLIB_ERROR; + stream->status |= _PDCLIB_ERRORFLAG; + return EOF; + } + stream->status |= _PDCLIB_FREAD | _PDCLIB_BYTESTREAM; + if ( ( stream->bufidx == stream->bufend ) && ( stream->ungetidx == 0 ) ) + { + return _PDCLIB_fillbuffer( stream ); + } + else + { + return 0; + } +} diff --git a/src/libraries/libc/j6libc/prepwrite.c b/src/libraries/libc/j6libc/prepwrite.c new file mode 100644 index 0000000..3d9e4b4 --- /dev/null +++ b/src/libraries/libc/j6libc/prepwrite.c @@ -0,0 +1,25 @@ +/* _PDCLIB_prepwrite( struct _PDCLIB_file_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int _PDCLIB_prepwrite( struct _PDCLIB_file_t * stream ) +{ + if ( ( stream->bufidx < stream->bufend ) || ( stream->ungetidx > 0 ) || + ( stream->status & ( _PDCLIB_FREAD | _PDCLIB_ERRORFLAG | _PDCLIB_WIDESTREAM | _PDCLIB_EOFFLAG ) ) || + ! ( stream->status & ( _PDCLIB_FWRITE | _PDCLIB_FAPPEND | _PDCLIB_FRW ) ) ) + { + /* Function called on illegal (e.g. input) stream. + See the comments on implementation-defined errno values in + . + */ + _PDCLIB_errno = _PDCLIB_ERROR; + stream->status |= _PDCLIB_ERRORFLAG; + return EOF; + } + stream->status |= _PDCLIB_FWRITE | _PDCLIB_BYTESTREAM; + return 0; +} diff --git a/src/libraries/libc/j6libc/print.c b/src/libraries/libc/j6libc/print.c new file mode 100644 index 0000000..0057a0d --- /dev/null +++ b/src/libraries/libc/j6libc/print.c @@ -0,0 +1,624 @@ +/* _PDCLIB_print( const char *, struct _PDCLIB_status_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include +#include +#include + +/* Using an integer's bits as flags for both the conversion flags and length + modifiers. +*/ +/* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the + width flags) into a combined field. +*/ +#define E_minus (1<<0) +#define E_plus (1<<1) +#define E_alt (1<<2) +#define E_space (1<<3) +#define E_zero (1<<4) +#define E_done (1<<5) + +#define E_char (1<<6) +#define E_short (1<<7) +#define E_long (1<<8) +#define E_llong (1<<9) +#define E_intmax (1<<10) +#define E_size (1<<11) +#define E_ptrdiff (1<<12) +#define E_pointer (1<<13) + +#define E_ldouble (1<<14) + +#define E_lower (1<<15) +#define E_unsigned (1<<16) + +/* This macro delivers a given character to either a memory buffer or a stream, + depending on the contents of 'status' (struct _PDCLIB_status_t). + x - the character to be delivered + i - pointer to number of characters already delivered in this call + n - pointer to maximum number of characters to be delivered in this call + s - the buffer into which the character shall be delivered +*/ +#define PUT( x ) \ +do { \ + int character = x; \ + if ( status->i < status->n ) { \ + if ( status->stream != NULL ) \ + putc( character, status->stream ); \ + else \ + status->s[status->i] = character; \ + } \ + ++(status->i); \ +} while ( 0 ) + + +static void intformat( intmax_t value, struct _PDCLIB_status_t * status ) +{ + /* At worst, we need two prefix characters (hex prefix). */ + char preface[3] = "\0"; + size_t preidx = 0; + if ( status->prec < 0 ) + { + status->prec = 1; + } + if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) && ( value != 0 ) ) + { + /* Octal / hexadecimal prefix for "%#" conversions */ + preface[ preidx++ ] = '0'; + if ( status->base == 16 ) + { + preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X'; + } + } + if ( value < 0 ) + { + /* Negative sign for negative values - at all times. */ + preface[ preidx++ ] = '-'; + } + else if ( ! ( status->flags & E_unsigned ) ) + { + /* plus sign / extra space are only for unsigned conversions */ + if ( status->flags & E_plus ) + { + preface[ preidx++ ] = '+'; + } + else if ( status->flags & E_space ) + { + preface[ preidx++ ] = ' '; + } + } + { + /* At this point, status->current has the number of digits queued up. + Determine if we have a precision requirement to pad those. + */ + size_t prec_pads = ( (size_t)status->prec > status->current ) ? ( (size_t)status->prec - status->current ) : 0; + if ( ! ( status->flags & ( E_minus | E_zero ) ) ) + { + /* Space padding is only done if no zero padding or left alignment + is requested. Calculate the number of characters that WILL be + printed, including any prefixes determined above. + */ + /* The number of characters to be printed, plus prefixes if any. */ + /* This line contained probably the most stupid, time-wasting bug + I've ever perpetrated. Greetings to Samface, DevL, and all + sceners at Breakpoint 2006. + */ + size_t characters = preidx + ( ( status->current > (size_t)status->prec ) ? status->current : (size_t)status->prec ); + if ( status->width > characters ) + { + size_t i; + for ( i = 0; i < status->width - characters; ++i ) + { + PUT( ' ' ); + ++(status->current); + } + } + } + /* Now we did the padding, do the prefixes (if any). */ + preidx = 0; + while ( preface[ preidx ] != '\0' ) + { + PUT( preface[ preidx++ ] ); + ++(status->current); + } + /* Do the precision padding if necessary. */ + while ( prec_pads-- > 0 ) + { + PUT( '0' ); + ++(status->current); + } + if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) ) + { + /* If field is not left aligned, and zero padding is requested, do + so. + */ + while ( status->current < status->width ) + { + PUT( '0' ); + ++(status->current); + } + } + } +} + + +static void int2base( intmax_t value, struct _PDCLIB_status_t * status ) +{ + /* This function recursively converts a given integer value to a character + stream. The conversion is done under the control of a given status struct + and written either to a character string or a stream, depending on that + same status struct. The status struct also keeps the function from exceeding + snprintf() limits, and enables any necessary padding / prefixing of the + output once the number of characters to be printed is known, which happens + at the lowermost recursion level. + */ + + /* Special case: zero value, zero precision -- no output (but padding) */ + if ( status->current == 0 && value == 0 && status->prec == 0 ) + { + intformat( value, status ); + } + else + { + /* Registering the character being printed at the end of the function here + already so it will be taken into account when the deepestmost recursion + does the prefix / padding stuff. + */ + ++(status->current); + if ( ( value / status->base ) != 0 ) + { + /* More digits to be done - recurse deeper */ + int2base( value / status->base, status ); + } + else + { + /* We reached the last digit, the deepest point of our recursion, and + only now know how long the number to be printed actually is. Now we + have to do the sign, prefix, width, and precision padding stuff + before printing the numbers while we resurface from the recursion. + */ + intformat( value, status ); + } + + /* Recursion tail - print the current digit. */ + int digit = value % status->base; + if ( digit < 0 ) + { + digit *= -1; + } + if ( status->flags & E_lower ) + { + /* Lowercase letters. Same array used for strto...(). */ + PUT( _PDCLIB_digits[ digit ] ); + } + else + { + /* Uppercase letters. Array only used here, only 0-F. */ + PUT( _PDCLIB_Xdigits[ digit ] ); + } + } +} + +static void uint2base( uintmax_t value, struct _PDCLIB_status_t * status ) +{ + /* This function recursively converts a given integer value to a character + stream. The conversion is done under the control of a given status struct + and written either to a character string or a stream, depending on that + same status struct. The status struct also keeps the function from exceeding + snprintf() limits, and enables any necessary padding / prefixing of the + output once the number of characters to be printed is known, which happens + at the lowermost recursion level. + */ + + /* Special case: zero value, zero precision -- no output (but padding) */ + if ( status->current == 0 && value == 0 && status->prec == 0 ) + { + intformat( value, status ); + } + else + { + /* Registering the character being printed at the end of the function here + already so it will be taken into account when the deepestmost recursion + does the prefix / padding stuff. + */ + ++(status->current); + if ( ( value / status->base ) != 0 ) + { + /* More digits to be done - recurse deeper */ + uint2base( value / status->base, status ); + } + else + { + /* We reached the last digit, the deepest point of our recursion, and + only now know how long the number to be printed actually is. Now we + have to do the sign, prefix, width, and precision padding stuff + before printing the numbers while we resurface from the recursion. + */ + intformat( value, status ); + } + + /* Recursion tail - print the current digit. */ + int digit = value % status->base; + if ( digit < 0 ) + { + digit *= -1; + } + if ( status->flags & E_lower ) + { + /* Lowercase letters. Same array used for strto...(). */ + PUT( _PDCLIB_digits[ digit ] ); + } + else + { + /* Uppercase letters. Array only used here, only 0-F. */ + PUT( _PDCLIB_Xdigits[ digit ] ); + } + } +} + + +static void stringformat( const char * s, struct _PDCLIB_status_t * status ) +{ + if ( status->flags & E_char ) + { + status->prec = 1; + } + else + { + if ( status->prec < 0 ) + { + status->prec = strlen( s ); + } + else + { + int i; + for ( i = 0; i < status->prec; ++i ) + { + if ( s[i] == 0 ) + { + status->prec = i; + break; + } + } + } + } + if ( ! ( status->flags & E_minus ) && ( status->width > (size_t)status->prec ) ) + { + while ( status->current < ( status->width - status->prec ) ) + { + PUT( ' ' ); + ++(status->current); + } + } + while ( status->prec > 0 ) + { + PUT( *(s++) ); + --(status->prec); + ++(status->current); + } + if ( status->flags & E_minus ) + { + while ( status->width > status->current ) + { + PUT( ' ' ); + ++(status->current); + } + } +} + + +const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status ) +{ + const char * orig_spec = spec; + if ( *(++spec) == '%' ) + { + /* %% -> print single '%' */ + PUT( *spec ); + return ++spec; + } + /* Initializing status structure */ + status->flags = 0; + status->base = 0; + status->current = 0; + status->width = 0; + status->prec = EOF; + + /* First come 0..n flags */ + do + { + switch ( *spec ) + { + case '-': + /* left-aligned output */ + status->flags |= E_minus; + ++spec; + break; + case '+': + /* positive numbers prefixed with '+' */ + status->flags |= E_plus; + ++spec; + break; + case '#': + /* alternative format (leading 0x for hex, 0 for octal) */ + status->flags |= E_alt; + ++spec; + break; + case ' ': + /* positive numbers prefixed with ' ' */ + status->flags |= E_space; + ++spec; + break; + case '0': + /* right-aligned padding done with '0' instead of ' ' */ + status->flags |= E_zero; + ++spec; + break; + default: + /* not a flag, exit flag parsing */ + status->flags |= E_done; + break; + } + } while ( ! ( status->flags & E_done ) ); + + /* Optional field width */ + if ( *spec == '*' ) + { + /* Retrieve width value from argument stack */ + int width = va_arg( status->arg, int ); + if ( width < 0 ) + { + status->flags |= E_minus; + status->width = abs( width ); + } + else + { + status->width = width; + } + ++spec; + } + else + { + /* If a width is given, strtol() will return its value. If not given, + strtol() will return zero. In both cases, endptr will point to the + rest of the conversion specifier - just what we need. + */ + status->width = (int)strtol( spec, (char**)&spec, 10 ); + } + + /* Optional precision */ + if ( *spec == '.' ) + { + ++spec; + if ( *spec == '*' ) + { + /* Retrieve precision value from argument stack. A negative value + is as if no precision is given - as precision is initalized to + EOF (negative), there is no need for testing for negative here. + */ + status->prec = va_arg( status->arg, int ); + ++spec; + } + else + { + char * endptr; + status->prec = (int)strtol( spec, &endptr, 10 ); + if ( spec == endptr ) + { + /* Decimal point but no number - equals zero */ + status->prec = 0; + } + spec = endptr; + } + /* Having a precision cancels out any zero flag. */ + status->flags &= ~E_zero; + } + + /* Optional length modifier + We step one character ahead in any case, and step back only if we find + there has been no length modifier (or step ahead another character if it + has been "hh" or "ll"). + */ + switch ( *(spec++) ) + { + case 'h': + if ( *spec == 'h' ) + { + /* hh -> char */ + status->flags |= E_char; + ++spec; + } + else + { + /* h -> short */ + status->flags |= E_short; + } + break; + case 'l': + if ( *spec == 'l' ) + { + /* ll -> long long */ + status->flags |= E_llong; + ++spec; + } + else + { + /* k -> long */ + status->flags |= E_long; + } + break; + case 'j': + /* j -> intmax_t, which might or might not be long long */ + status->flags |= E_intmax; + break; + case 'z': + /* z -> size_t, which might or might not be unsigned int */ + status->flags |= E_size; + break; + case 't': + /* t -> ptrdiff_t, which might or might not be long */ + status->flags |= E_ptrdiff; + break; + case 'L': + /* L -> long double */ + status->flags |= E_ldouble; + break; + default: + --spec; + break; + } + + /* Conversion specifier */ + switch ( *spec ) + { + case 'd': + /* FALLTHROUGH */ + case 'i': + status->base = 10; + break; + case 'o': + status->base = 8; + status->flags |= E_unsigned; + break; + case 'u': + status->base = 10; + status->flags |= E_unsigned; + break; + case 'x': + status->base = 16; + status->flags |= ( E_lower | E_unsigned ); + break; + case 'X': + status->base = 16; + status->flags |= E_unsigned; + break; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + break; + case 'a': + case 'A': + break; + case 'c': + /* TODO: wide chars. */ + { + char c[1]; + c[0] = (char)va_arg( status->arg, int ); + status->flags |= E_char; + stringformat( c, status ); + return ++spec; + } + case 's': + /* TODO: wide chars. */ + stringformat( va_arg( status->arg, char * ), status ); + return ++spec; + case 'p': + status->base = 16; + status->flags |= ( E_lower | E_unsigned | E_alt | E_pointer ); + break; + case 'n': + { + int * val = va_arg( status->arg, int * ); + *val = status->i; + return ++spec; + } + default: + /* No conversion specifier. Bad conversion. */ + return orig_spec; + } + + /* Do the actual output based on our findings */ + if ( status->base != 0 ) + { + /* Integer conversions */ + /* TODO: Check for invalid flag combinations. */ + if ( status->flags & E_unsigned ) + { + uintmax_t value; + switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size | E_pointer | E_intmax ) ) + { + case E_char: + value = (uintmax_t)(unsigned char)va_arg( status->arg, int ); + break; + case E_short: + value = (uintmax_t)(unsigned short)va_arg( status->arg, int ); + break; + case 0: + value = (uintmax_t)va_arg( status->arg, unsigned int ); + break; + case E_long: + value = (uintmax_t)va_arg( status->arg, unsigned long ); + break; + case E_llong: + value = (uintmax_t)va_arg( status->arg, unsigned long long ); + break; + case E_size: + value = (uintmax_t)va_arg( status->arg, size_t ); + break; + case E_pointer: + value = (uintmax_t)(uintptr_t)va_arg( status->arg, void * ); + break; + case E_intmax: + value = va_arg( status->arg, uintmax_t ); + break; + default: + puts( "UNSUPPORTED PRINTF FLAG COMBINATION" ); + return NULL; + } + uint2base(value, status); + } + else + { + intmax_t value; + switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) ) + { + case E_char: + value = (intmax_t)(char)va_arg( status->arg, int ); + break; + case E_short: + value = (intmax_t)(short)va_arg( status->arg, int ); + break; + case 0: + value = (intmax_t)va_arg( status->arg, int ); + break; + case E_long: + value = (intmax_t)va_arg( status->arg, long ); + break; + case E_llong: + value = (intmax_t)va_arg( status->arg, long long ); + break; + case E_ptrdiff: + value = (intmax_t)va_arg( status->arg, ptrdiff_t ); + break; + case E_intmax: + value = va_arg( status->arg, intmax_t ); + break; + default: + puts( "UNSUPPORTED PRINTF FLAG COMBINATION" ); + return NULL; + } + int2base(value, status); + } + if ( status->flags & E_minus ) + { + while ( status->current < status->width ) + { + PUT( ' ' ); + ++(status->current); + } + } + if ( status->i >= status->n && status->n > 0 ) + { + status->s[status->n - 1] = '\0'; + } + } + return ++spec; +} diff --git a/src/libraries/libc/j6libc/rename.c b/src/libraries/libc/j6libc/rename.c new file mode 100644 index 0000000..dfafd2c --- /dev/null +++ b/src/libraries/libc/j6libc/rename.c @@ -0,0 +1,18 @@ +/* _PDCLIB_rename( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* This is an example implementation of _PDCLIB_rename() fit for use with + POSIX kernels. + */ + +#include +#include "j6libc/glue.h" + +int _PDCLIB_rename( const char * old, const char * new ) +{ + _PDCLIB_errno = _PDCLIB_ERROR; + return EOF; +} diff --git a/src/libraries/libc/j6libc/sbrk.c b/src/libraries/libc/j6libc/sbrk.c new file mode 100644 index 0000000..17a914b --- /dev/null +++ b/src/libraries/libc/j6libc/sbrk.c @@ -0,0 +1,4 @@ +#include +void *sbrk(intptr_t) __attribute__ ((weak)); + +void *sbrk(intptr_t i) { return 0; } diff --git a/src/libraries/libc/j6libc/scan.c b/src/libraries/libc/j6libc/scan.c new file mode 100644 index 0000000..7fbd4df --- /dev/null +++ b/src/libraries/libc/j6libc/scan.c @@ -0,0 +1,595 @@ +/* _PDCLIB_scan( const char *, struct _PDCLIB_status_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Using an integer's bits as flags for both the conversion flags and length + modifiers. +*/ +#define E_suppressed 1<<0 +#define E_char 1<<6 +#define E_short 1<<7 +#define E_long 1<<8 +#define E_llong 1<<9 +#define E_intmax 1<<10 +#define E_size 1<<11 +#define E_ptrdiff 1<<12 +#define E_pointer 1<<13 +#define E_ldouble 1<<14 +#define E_unsigned 1<<16 + + +/* Helper function to get a character from the string or stream, whatever is + used for input. When reading from a string, returns EOF on end-of-string + so that handling of the return value can be uniform for both streams and + strings. +*/ +static int GET( struct _PDCLIB_status_t * status ) +{ + int rc = EOF; + if ( status->stream != NULL ) + { + rc = getc( status->stream ); + } + else + { + rc = ( *status->s == '\0' ) ? EOF : (unsigned char)*((status->s)++); + } + if ( rc != EOF ) + { + ++(status->i); + ++(status->current); + } + return rc; +} + + +/* Helper function to put a read character back into the string or stream, + whatever is used for input. +*/ +static void UNGET( int c, struct _PDCLIB_status_t * status ) +{ + if ( status->stream != NULL ) + { + ungetc( c, status->stream ); /* TODO: Error? */ + } + else + { + --(status->s); + } + --(status->i); + --(status->current); +} + + +/* Helper function to check if a character is part of a given scanset */ +static int IN_SCANSET( const char * scanlist, const char * end_scanlist, int rc ) +{ + /* SOLAR */ + int previous = -1; + while ( scanlist != end_scanlist ) + { + if ( ( *scanlist == '-' ) && ( previous != -1 ) ) + { + /* possible scangroup ("a-z") */ + if ( ++scanlist == end_scanlist ) + { + /* '-' at end of scanlist does not describe a scangroup */ + return rc == '-'; + } + while ( ++previous <= (unsigned char)*scanlist ) + { + if ( previous == rc ) + { + return 1; + } + } + previous = -1; + } + else + { + /* not a scangroup, check verbatim */ + if ( rc == (unsigned char)*scanlist ) + { + return 1; + } + previous = (unsigned char)(*scanlist++); + } + } + return 0; +} + + +const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) +{ + /* generic input character */ + int rc; + const char * prev_spec; + const char * orig_spec = spec; + int value_parsed; + if ( *(++spec) == '%' ) + { + /* %% -> match single '%' */ + rc = GET( status ); + switch ( rc ) + { + case EOF: + /* input error */ + if ( status->n == 0 ) + { + status->n = -1; + } + return NULL; + case '%': + return ++spec; + default: + UNGET( rc, status ); + break; + } + } + /* Initializing status structure */ + status->flags = 0; + status->base = -1; + status->current = 0; + status->width = 0; + status->prec = 0; + + /* '*' suppresses assigning parsed value to variable */ + if ( *spec == '*' ) + { + status->flags |= E_suppressed; + ++spec; + } + + /* If a width is given, strtol() will return its value. If not given, + strtol() will return zero. In both cases, endptr will point to the + rest of the conversion specifier - just what we need. + */ + prev_spec = spec; + status->width = (int)strtol( spec, (char**)&spec, 10 ); + if ( spec == prev_spec ) + { + status->width = SIZE_MAX; + } + + /* Optional length modifier + We step one character ahead in any case, and step back only if we find + there has been no length modifier (or step ahead another character if it + has been "hh" or "ll"). + */ + switch ( *(spec++) ) + { + case 'h': + if ( *spec == 'h' ) + { + /* hh -> char */ + status->flags |= E_char; + ++spec; + } + else + { + /* h -> short */ + status->flags |= E_short; + } + break; + case 'l': + if ( *spec == 'l' ) + { + /* ll -> long long */ + status->flags |= E_llong; + ++spec; + } + else + { + /* l -> long */ + status->flags |= E_long; + } + break; + case 'j': + /* j -> intmax_t, which might or might not be long long */ + status->flags |= E_intmax; + break; + case 'z': + /* z -> size_t, which might or might not be unsigned int */ + status->flags |= E_size; + break; + case 't': + /* t -> ptrdiff_t, which might or might not be long */ + status->flags |= E_ptrdiff; + break; + case 'L': + /* L -> long double */ + status->flags |= E_ldouble; + break; + default: + --spec; + break; + } + + /* Conversion specifier */ + + /* whether valid input had been parsed */ + value_parsed = 0; + + switch ( *spec ) + { + case 'd': + status->base = 10; + break; + case 'i': + status->base = 0; + break; + case 'o': + status->base = 8; + status->flags |= E_unsigned; + break; + case 'u': + status->base = 10; + status->flags |= E_unsigned; + break; + case 'x': + status->base = 16; + status->flags |= E_unsigned; + break; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + break; + case 'c': + { + char * c = va_arg( status->arg, char * ); + /* for %c, default width is one */ + if ( status->width == SIZE_MAX ) + { + status->width = 1; + } + /* reading until width reached or input exhausted */ + while ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + *(c++) = rc; + value_parsed = 1; + } + /* width or input exhausted */ + if ( value_parsed ) + { + ++status->n; + return ++spec; + } + else + { + /* input error, no character read */ + if ( status->n == 0 ) + { + status->n = -1; + } + return NULL; + } + } + case 's': + { + char * c = va_arg( status->arg, char * ); + while ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( isspace( rc ) ) + { + UNGET( rc, status ); + if ( value_parsed ) + { + /* matching sequence terminated by whitespace */ + *c = '\0'; + ++status->n; + return ++spec; + } + else + { + /* matching error */ + return NULL; + } + } + else + { + /* match */ + value_parsed = 1; + *(c++) = rc; + } + } + /* width or input exhausted */ + if ( value_parsed ) + { + *c = '\0'; + ++status->n; + return ++spec; + } + else + { + /* input error, no character read */ + if ( status->n == 0 ) + { + status->n = -1; + } + return NULL; + } + } + case '[': + { + const char * endspec = spec; + int negative_scanlist = 0; + char * c; + if ( *(++endspec) == '^' ) + { + negative_scanlist = 1; + ++endspec; + } + spec = endspec; + do + { + /* TODO: This can run beyond a malformed format string */ + ++endspec; + } while ( *endspec != ']' ); + /* read according to scanlist, equiv. to %s above */ + c = va_arg( status->arg, char * ); + while ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( negative_scanlist ) + { + if ( IN_SCANSET( spec, endspec, rc ) ) + { + UNGET( rc, status ); + break; + } + } + else + { + if ( ! IN_SCANSET( spec, endspec, rc ) ) + { + UNGET( rc, status ); + break; + } + } + value_parsed = 1; + *(c++) = rc; + } + if ( value_parsed ) + { + *c = '\0'; + ++status->n; + return ++endspec; + } + else + { + if ( rc == EOF ) + { + status->n = -1; + } + return NULL; + } + } + case 'p': + status->base = 16; + status->flags |= E_pointer; + break; + case 'n': + { + int * val = va_arg( status->arg, int * ); + *val = status->i; + return ++spec; + } + default: + /* No conversion specifier. Bad conversion. */ + return orig_spec; + } + + if ( status->base != -1 ) + { + /* integer conversion */ + uintmax_t value = 0; /* absolute value read */ + int prefix_parsed = 0; + int sign = 0; + while ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( isspace( rc ) ) + { + if ( sign ) + { + /* matching sequence terminated by whitespace */ + UNGET( rc, status ); + break; + } + else + { + /* leading whitespace not counted against width */ + status->current--; + } + } + else if ( ! sign ) + { + /* no sign parsed yet */ + switch ( rc ) + { + case '-': + sign = -1; + break; + case '+': + sign = 1; + break; + default: + /* not a sign; put back character */ + sign = 1; + UNGET( rc, status ); + break; + } + } + else if ( ! prefix_parsed ) + { + /* no prefix (0x... for hex, 0... for octal) parsed yet */ + prefix_parsed = 1; + if ( rc != '0' ) + { + /* not a prefix; if base not yet set, set to decimal */ + if ( status->base == 0 ) + { + status->base = 10; + } + UNGET( rc, status ); + } + else + { + /* starts with zero, so it might be a prefix. */ + /* check what follows next (might be 0x...) */ + if ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( tolower( rc ) == 'x' ) + { + /* 0x... would be prefix for hex base... */ + if ( ( status->base == 0 ) || + ( status->base == 16 ) ) + { + status->base = 16; + } + else + { + /* ...unless already set to other value */ + UNGET( rc, status ); + value_parsed = 1; + } + } + else + { + /* 0... but not 0x.... would be octal prefix */ + UNGET( rc, status ); + if ( status->base == 0 ) + { + status->base = 8; + } + /* in any case we have read a zero */ + value_parsed = 1; + } + } + else + { + /* failed to read beyond the initial zero */ + value_parsed = 1; + break; + } + } + } + else + { + char * digitptr = memchr( _PDCLIB_digits, tolower( rc ), status->base ); + if ( digitptr == NULL ) + { + /* end of input item */ + UNGET( rc, status ); + break; + } + value *= status->base; + value += digitptr - _PDCLIB_digits; + value_parsed = 1; + } + } + /* width or input exhausted, or non-matching character */ + if ( ! value_parsed ) + { + /* out of input before anything could be parsed - input error */ + /* FIXME: if first character does not match, value_parsed is not set - but it is NOT an input error */ + if ( ( status->n == 0 ) && ( rc == EOF ) ) + { + status->n = -1; + } + return NULL; + } + /* convert value to target type and assign to parameter */ + if ( ! ( status->flags & E_suppressed ) ) + { + switch ( status->flags & ( E_char | E_short | E_long | E_llong | + E_intmax | E_size | E_ptrdiff | E_pointer | + E_unsigned ) ) + { + case E_char: + *( va_arg( status->arg, char * ) ) = (char)( value * sign ); + break; + case E_char | E_unsigned: + *( va_arg( status->arg, unsigned char * ) ) = (unsigned char)( value * sign ); + break; + + case E_short: + *( va_arg( status->arg, short * ) ) = (short)( value * sign ); + break; + case E_short | E_unsigned: + *( va_arg( status->arg, unsigned short * ) ) = (unsigned short)( value * sign ); + break; + + case 0: + *( va_arg( status->arg, int * ) ) = (int)( value * sign ); + break; + case E_unsigned: + *( va_arg( status->arg, unsigned int * ) ) = (unsigned int)( value * sign ); + break; + + case E_long: + *( va_arg( status->arg, long * ) ) = (long)( value * sign ); + break; + case E_long | E_unsigned: + *( va_arg( status->arg, unsigned long * ) ) = (unsigned long)( value * sign ); + break; + + case E_llong: + *( va_arg( status->arg, long long * ) ) = (long long)( value * sign ); + break; + case E_llong | E_unsigned: + *( va_arg( status->arg, unsigned long long * ) ) = (unsigned long long)( value * sign ); + break; + + case E_intmax: + *( va_arg( status->arg, intmax_t * ) ) = (intmax_t)( value * sign ); + break; + case E_intmax | E_unsigned: + *( va_arg( status->arg, uintmax_t * ) ) = (uintmax_t)( value * sign ); + break; + + case E_size: + /* E_size always implies unsigned */ + *( va_arg( status->arg, size_t * ) ) = (size_t)( value * sign ); + break; + + case E_ptrdiff: + /* E_ptrdiff always implies signed */ + *( va_arg( status->arg, ptrdiff_t * ) ) = (ptrdiff_t)( value * sign ); + break; + + case E_pointer: + /* E_pointer always implies unsigned */ + *( uintptr_t* )( va_arg( status->arg, void * ) ) = (uintptr_t)( value * sign ); + break; + + default: + puts( "UNSUPPORTED SCANF FLAG COMBINATION" ); + return NULL; /* behaviour unspecified */ + } + ++(status->n); + } + return ++spec; + } + /* TODO: Floats. */ + return NULL; +} diff --git a/src/libraries/libc/j6libc/seed.c b/src/libraries/libc/j6libc/seed.c new file mode 100644 index 0000000..e1df6f9 --- /dev/null +++ b/src/libraries/libc/j6libc/seed.c @@ -0,0 +1,7 @@ +/* _PDCLIB_seed + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +unsigned long int _PDCLIB_seed = 1; diff --git a/src/libraries/libc/j6libc/seek.c b/src/libraries/libc/j6libc/seek.c new file mode 100644 index 0000000..70834fc --- /dev/null +++ b/src/libraries/libc/j6libc/seek.c @@ -0,0 +1,18 @@ +/* int64_t _PDCLIB_seek( FILE *, int64_t, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* This is an example implementation of _PDCLIB_seek() fit for use with POSIX + kernels. + */ + +#include +#include "j6libc/glue.h" + +int64_t _PDCLIB_seek( struct _PDCLIB_file_t * stream, int64_t offset, int whence ) +{ + _PDCLIB_errno = _PDCLIB_ERROR; + return EOF; +} diff --git a/src/libraries/libc/j6libc/stdinit.c b/src/libraries/libc/j6libc/stdinit.c new file mode 100644 index 0000000..e2b408d --- /dev/null +++ b/src/libraries/libc/j6libc/stdinit.c @@ -0,0 +1,412 @@ +/* _PDCLIB_stdinit + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* This is an example initialization of stdin, stdout and stderr to the integer + file descriptors 0, 1, and 2, respectively. This applies for a great variety + of operating systems, including POSIX compliant ones. +*/ + +#include +#include +#include + +/* In a POSIX system, stdin / stdout / stderr are equivalent to the (int) file + descriptors 0, 1, and 2 respectively. +*/ +/* TODO: This is proof-of-concept, requires finetuning. */ +static char _PDCLIB_sin_buffer[BUFSIZ]; +static char _PDCLIB_sout_buffer[BUFSIZ]; +static char _PDCLIB_serr_buffer[BUFSIZ]; + +static unsigned char _PDCLIB_sin_ungetbuf[_PDCLIB_UNGETCBUFSIZE]; +static unsigned char _PDCLIB_sout_ungetbuf[_PDCLIB_UNGETCBUFSIZE]; +static unsigned char _PDCLIB_serr_ungetbuf[_PDCLIB_UNGETCBUFSIZE]; + +static struct _PDCLIB_file_t _PDCLIB_serr = { 2, _PDCLIB_serr_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_serr_ungetbuf, _IONBF | _PDCLIB_FWRITE | _PDCLIB_STATIC, NULL, NULL }; +static struct _PDCLIB_file_t _PDCLIB_sout = { 1, _PDCLIB_sout_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_sout_ungetbuf, _IOLBF | _PDCLIB_FWRITE | _PDCLIB_STATIC, NULL, &_PDCLIB_serr }; +static struct _PDCLIB_file_t _PDCLIB_sin = { 0, _PDCLIB_sin_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_sin_ungetbuf, _IOLBF | _PDCLIB_FREAD | _PDCLIB_STATIC, NULL, &_PDCLIB_sout }; + +struct _PDCLIB_file_t * stdin = &_PDCLIB_sin; +struct _PDCLIB_file_t * stdout = &_PDCLIB_sout; +struct _PDCLIB_file_t * stderr = &_PDCLIB_serr; + +/* FIXME: This approach is a possible attack vector. */ +struct _PDCLIB_file_t * _PDCLIB_filelist = &_PDCLIB_sin; + +/* "C" locale - defaulting to ASCII-7. + 1 kByte (+ 4 byte) of data. + Each line: flags, lowercase, uppercase, collation. +*/ +static struct _PDCLIB_lc_ctype_entry_t _ctype_entries[ _PDCLIB_CHARSET_SIZE + 1 ] = { + { /* EOF */ 0, 0, 0 }, + { /* NUL */ _PDCLIB_CTYPE_CNTRL, 0x00, 0x00 }, + { /* SOH */ _PDCLIB_CTYPE_CNTRL, 0x01, 0x01 }, + { /* STX */ _PDCLIB_CTYPE_CNTRL, 0x02, 0x02 }, + { /* ETX */ _PDCLIB_CTYPE_CNTRL, 0x03, 0x03 }, + { /* EOT */ _PDCLIB_CTYPE_CNTRL, 0x04, 0x04 }, + { /* ENQ */ _PDCLIB_CTYPE_CNTRL, 0x05, 0x05 }, + { /* ACK */ _PDCLIB_CTYPE_CNTRL, 0x06, 0x06 }, + { /* BEL */ _PDCLIB_CTYPE_CNTRL, 0x07, 0x07 }, + { /* BS */ _PDCLIB_CTYPE_CNTRL, 0x08, 0x08 }, + { /* HT */ _PDCLIB_CTYPE_CNTRL | _PDCLIB_CTYPE_BLANK | _PDCLIB_CTYPE_SPACE, 0x09, 0x09 }, + { /* LF */ _PDCLIB_CTYPE_CNTRL | _PDCLIB_CTYPE_SPACE, 0x0A, 0x0A }, + { /* VT */ _PDCLIB_CTYPE_CNTRL | _PDCLIB_CTYPE_SPACE, 0x0B, 0x0B }, + { /* FF */ _PDCLIB_CTYPE_CNTRL | _PDCLIB_CTYPE_SPACE, 0x0C, 0x0C }, + { /* CR */ _PDCLIB_CTYPE_CNTRL | _PDCLIB_CTYPE_SPACE, 0x0D, 0x0D }, + { /* SO */ _PDCLIB_CTYPE_CNTRL, 0x0E, 0x0E }, + { /* SI */ _PDCLIB_CTYPE_CNTRL, 0x0F, 0x0F }, + { /* DLE */ _PDCLIB_CTYPE_CNTRL, 0x10, 0x10 }, + { /* DC1 */ _PDCLIB_CTYPE_CNTRL, 0x11, 0x11 }, + { /* DC2 */ _PDCLIB_CTYPE_CNTRL, 0x12, 0x12 }, + { /* DC3 */ _PDCLIB_CTYPE_CNTRL, 0x13, 0x13 }, + { /* DC4 */ _PDCLIB_CTYPE_CNTRL, 0x14, 0x14 }, + { /* NAK */ _PDCLIB_CTYPE_CNTRL, 0x15, 0x15 }, + { /* SYN */ _PDCLIB_CTYPE_CNTRL, 0x16, 0x16 }, + { /* ETB */ _PDCLIB_CTYPE_CNTRL, 0x17, 0x17 }, + { /* CAN */ _PDCLIB_CTYPE_CNTRL, 0x18, 0x18 }, + { /* EM */ _PDCLIB_CTYPE_CNTRL, 0x19, 0x19 }, + { /* SUB */ _PDCLIB_CTYPE_CNTRL, 0x1A, 0x1A }, + { /* ESC */ _PDCLIB_CTYPE_CNTRL, 0x1B, 0x1B }, + { /* FS */ _PDCLIB_CTYPE_CNTRL, 0x1C, 0x1C }, + { /* GS */ _PDCLIB_CTYPE_CNTRL, 0x1D, 0x1D }, + { /* RS */ _PDCLIB_CTYPE_CNTRL, 0x1E, 0x1E }, + { /* US */ _PDCLIB_CTYPE_CNTRL, 0x1F, 0x1F }, + { /* SP */ _PDCLIB_CTYPE_BLANK | _PDCLIB_CTYPE_SPACE, 0x20, 0x20 }, + { /* '!' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x21, 0x21 }, + { /* '"' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x22, 0x22 }, + { /* '#' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x23, 0x23 }, + { /* '$' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x24, 0x24 }, + { /* '%' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x25, 0x25 }, + { /* '&' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x26, 0x26 }, + { /* ''' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x27, 0x27 }, + { /* '(' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x28, 0x28 }, + { /* ')' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x29, 0x29 }, + { /* '*' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x2A, 0x2A }, + { /* '+' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x2B, 0x2B }, + { /* ',' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x2C, 0x2C }, + { /* '-' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x2D, 0x2D }, + { /* '.' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x2E, 0x2E }, + { /* '/' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x2F, 0x2F }, + { /* '0' */ _PDCLIB_CTYPE_GRAPH, 0x30, 0x30 }, + { /* '1' */ _PDCLIB_CTYPE_GRAPH, 0x31, 0x31 }, + { /* '2' */ _PDCLIB_CTYPE_GRAPH, 0x32, 0x32 }, + { /* '3' */ _PDCLIB_CTYPE_GRAPH, 0x33, 0x33 }, + { /* '4' */ _PDCLIB_CTYPE_GRAPH, 0x34, 0x34 }, + { /* '5' */ _PDCLIB_CTYPE_GRAPH, 0x35, 0x35 }, + { /* '6' */ _PDCLIB_CTYPE_GRAPH, 0x36, 0x36 }, + { /* '7' */ _PDCLIB_CTYPE_GRAPH, 0x37, 0x37 }, + { /* '8' */ _PDCLIB_CTYPE_GRAPH, 0x38, 0x38 }, + { /* '9' */ _PDCLIB_CTYPE_GRAPH, 0x39, 0x39 }, + { /* ':' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x3A, 0x3A }, + { /* ';' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x3B, 0x3B }, + { /* '<' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x3C, 0x3C }, + { /* '=' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x3D, 0x3D }, + { /* '>' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x3E, 0x3E }, + { /* '?' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x3F, 0x3F }, + { /* '@' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x40, 0x40 }, + { /* 'A' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x41, 0x61 }, + { /* 'B' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x42, 0x62 }, + { /* 'C' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x43, 0x63 }, + { /* 'D' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x44, 0x64 }, + { /* 'E' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x45, 0x65 }, + { /* 'F' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x46, 0x66 }, + { /* 'G' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x47, 0x67 }, + { /* 'H' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x48, 0x68 }, + { /* 'I' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x49, 0x69 }, + { /* 'J' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x4A, 0x6A }, + { /* 'K' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x4B, 0x6B }, + { /* 'L' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x4C, 0x6C }, + { /* 'M' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x4D, 0x6D }, + { /* 'N' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x4E, 0x6E }, + { /* 'O' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x4F, 0x6F }, + { /* 'P' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x50, 0x70 }, + { /* 'Q' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x51, 0x71 }, + { /* 'R' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x52, 0x72 }, + { /* 'S' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x53, 0x73 }, + { /* 'T' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x54, 0x74 }, + { /* 'U' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x55, 0x75 }, + { /* 'V' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x56, 0x76 }, + { /* 'W' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x57, 0x77 }, + { /* 'X' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x58, 0x78 }, + { /* 'Y' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x59, 0x79 }, + { /* 'Z' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x5A, 0x7A }, + { /* '[' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x5B, 0x5B }, + { /* '\' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x5C, 0x5C }, + { /* ']' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x5D, 0x5D }, + { /* '^' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x5E, 0x5E }, + { /* '_' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x5F, 0x5F }, + { /* '`' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x60, 0x60 }, + { /* 'a' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x41, 0x61 }, + { /* 'b' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x42, 0x62 }, + { /* 'c' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x43, 0x63 }, + { /* 'd' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x44, 0x64 }, + { /* 'e' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x45, 0x65 }, + { /* 'f' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x46, 0x66 }, + { /* 'g' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x47, 0x67 }, + { /* 'h' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x48, 0x68 }, + { /* 'i' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x49, 0x69 }, + { /* 'j' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x4A, 0x6A }, + { /* 'k' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x4B, 0x6B }, + { /* 'l' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x4C, 0x6C }, + { /* 'm' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x4D, 0x6D }, + { /* 'n' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x4E, 0x6E }, + { /* 'o' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x4F, 0x6F }, + { /* 'p' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x50, 0x70 }, + { /* 'q' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x51, 0x71 }, + { /* 'r' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x52, 0x72 }, + { /* 's' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x53, 0x73 }, + { /* 't' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x54, 0x74 }, + { /* 'u' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x55, 0x75 }, + { /* 'v' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x56, 0x76 }, + { /* 'w' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x57, 0x77 }, + { /* 'x' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x58, 0x78 }, + { /* 'y' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x59, 0x79 }, + { /* 'z' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x5A, 0x7A }, + { /* '{' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x7B, 0x7B }, + { /* '|' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x7C, 0x7C }, + { /* '}' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x7D, 0x7D }, + { /* '~' */ _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_PUNCT, 0x7E, 0x7E }, + { /* DEL */ _PDCLIB_CTYPE_CNTRL, 0x7F, 0x7F }, + { 0x00, 0x80, 0x80 }, + { 0x00, 0x81, 0x81 }, + { 0x00, 0x82, 0x82 }, + { 0x00, 0x83, 0x83 }, + { 0x00, 0x84, 0x84 }, + { 0x00, 0x85, 0x85 }, + { 0x00, 0x86, 0x86 }, + { 0x00, 0x87, 0x87 }, + { 0x00, 0x88, 0x88 }, + { 0x00, 0x89, 0x89 }, + { 0x00, 0x8A, 0x8A }, + { 0x00, 0x8B, 0x8B }, + { 0x00, 0x8C, 0x8C }, + { 0x00, 0x8D, 0x8D }, + { 0x00, 0x8E, 0x8E }, + { 0x00, 0x8F, 0x8F }, + { 0x00, 0x90, 0x90 }, + { 0x00, 0x91, 0x91 }, + { 0x00, 0x92, 0x92 }, + { 0x00, 0x93, 0x93 }, + { 0x00, 0x94, 0x94 }, + { 0x00, 0x95, 0x95 }, + { 0x00, 0x96, 0x96 }, + { 0x00, 0x97, 0x97 }, + { 0x00, 0x98, 0x98 }, + { 0x00, 0x99, 0x99 }, + { 0x00, 0x9A, 0x9A }, + { 0x00, 0x9B, 0x9B }, + { 0x00, 0x9C, 0x9C }, + { 0x00, 0x9D, 0x9D }, + { 0x00, 0x9E, 0x9E }, + { 0x00, 0x9F, 0x9F }, + { 0x00, 0xA0, 0xA0 }, + { 0x00, 0xA1, 0xA1 }, + { 0x00, 0xA2, 0xA2 }, + { 0x00, 0xA3, 0xA3 }, + { 0x00, 0xA4, 0xA4 }, + { 0x00, 0xA5, 0xA5 }, + { 0x00, 0xA6, 0xA6 }, + { 0x00, 0xA7, 0xA7 }, + { 0x00, 0xA8, 0xA8 }, + { 0x00, 0xA9, 0xA9 }, + { 0x00, 0xAA, 0xAA }, + { 0x00, 0xAB, 0xAB }, + { 0x00, 0xAC, 0xAC }, + { 0x00, 0xAD, 0xAD }, + { 0x00, 0xAE, 0xAE }, + { 0x00, 0xAF, 0xAF }, + { 0x00, 0xB0, 0xB0 }, + { 0x00, 0xB1, 0xB1 }, + { 0x00, 0xB2, 0xB2 }, + { 0x00, 0xB3, 0xB3 }, + { 0x00, 0xB4, 0xB4 }, + { 0x00, 0xB5, 0xB5 }, + { 0x00, 0xB6, 0xB6 }, + { 0x00, 0xB7, 0xB7 }, + { 0x00, 0xB8, 0xB8 }, + { 0x00, 0xB9, 0xB9 }, + { 0x00, 0xBA, 0xBA }, + { 0x00, 0xBB, 0xBB }, + { 0x00, 0xBC, 0xBC }, + { 0x00, 0xBD, 0xBD }, + { 0x00, 0xBE, 0xBE }, + { 0x00, 0xBF, 0xBF }, + { 0x00, 0xC0, 0xC0 }, + { 0x00, 0xC1, 0xC1 }, + { 0x00, 0xC2, 0xC2 }, + { 0x00, 0xC3, 0xC3 }, + { 0x00, 0xC4, 0xC4 }, + { 0x00, 0xC5, 0xC5 }, + { 0x00, 0xC6, 0xC6 }, + { 0x00, 0xC7, 0xC7 }, + { 0x00, 0xC8, 0xC8 }, + { 0x00, 0xC9, 0xC9 }, + { 0x00, 0xCA, 0xCA }, + { 0x00, 0xCB, 0xCB }, + { 0x00, 0xCC, 0xCC }, + { 0x00, 0xCD, 0xCD }, + { 0x00, 0xCE, 0xCE }, + { 0x00, 0xCF, 0xCF }, + { 0x00, 0xD0, 0xD0 }, + { 0x00, 0xD1, 0xD1 }, + { 0x00, 0xD2, 0xD2 }, + { 0x00, 0xD3, 0xD3 }, + { 0x00, 0xD4, 0xD4 }, + { 0x00, 0xD5, 0xD5 }, + { 0x00, 0xD6, 0xD6 }, + { 0x00, 0xD7, 0xD7 }, + { 0x00, 0xD8, 0xD8 }, + { 0x00, 0xD9, 0xD9 }, + { 0x00, 0xDA, 0xDA }, + { 0x00, 0xDB, 0xDB }, + { 0x00, 0xDC, 0xDC }, + { 0x00, 0xDD, 0xDD }, + { 0x00, 0xDE, 0xDE }, + { 0x00, 0xDF, 0xDF }, + { 0x00, 0xE0, 0xE0 }, + { 0x00, 0xE1, 0xE1 }, + { 0x00, 0xE2, 0xE2 }, + { 0x00, 0xE3, 0xE3 }, + { 0x00, 0xE4, 0xE4 }, + { 0x00, 0xE5, 0xE5 }, + { 0x00, 0xE6, 0xE6 }, + { 0x00, 0xE7, 0xE7 }, + { 0x00, 0xE8, 0xE8 }, + { 0x00, 0xE9, 0xE9 }, + { 0x00, 0xEA, 0xEA }, + { 0x00, 0xEB, 0xEB }, + { 0x00, 0xEC, 0xEC }, + { 0x00, 0xED, 0xED }, + { 0x00, 0xEE, 0xEE }, + { 0x00, 0xEF, 0xEF }, + { 0x00, 0xF0, 0xF0 }, + { 0x00, 0xF1, 0xF1 }, + { 0x00, 0xF2, 0xF2 }, + { 0x00, 0xF3, 0xF3 }, + { 0x00, 0xF4, 0xF4 }, + { 0x00, 0xF5, 0xF5 }, + { 0x00, 0xF6, 0xF6 }, + { 0x00, 0xF7, 0xF7 }, + { 0x00, 0xF8, 0xF8 }, + { 0x00, 0xF9, 0xF9 }, + { 0x00, 0xFA, 0xFA }, + { 0x00, 0xFB, 0xFB }, + { 0x00, 0xFC, 0xFC }, + { 0x00, 0xFD, 0xFD }, + { 0x00, 0xFE, 0xFE }, + { 0x00, 0xFF, 0xFF } +}; + +struct _PDCLIB_lc_ctype_t _PDCLIB_lc_ctype = { 0, 0x30, 0x39, 0x41, 0x46, 0x61, 0x66, &_ctype_entries[1] }; + +struct _PDCLIB_lc_collate_t _PDCLIB_lc_collate = { 0 }; + +struct lconv _PDCLIB_lconv = { + /* decimal_point */ (char *)".", + /* thousands_sep */ (char *)"", + /* grouping */ (char *)"", + /* mon_decimal_point */ (char *)"", + /* mon_thousands_sep */ (char *)"", + /* mon_grouping */ (char *)"", + /* positive_sign */ (char *)"", + /* negative_sign */ (char *)"", + /* currency_symbol */ (char *)"", + /* int_curr_symbol */ (char *)"", + /* frac_digits */ CHAR_MAX, + /* p_cs_precedes */ CHAR_MAX, + /* n_cs_precedes */ CHAR_MAX, + /* p_sep_by_space */ CHAR_MAX, + /* n_sep_by_space */ CHAR_MAX, + /* p_sign_posn */ CHAR_MAX, + /* n_sign_posn */ CHAR_MAX, + /* int_frac_digits */ CHAR_MAX, + /* int_p_cs_precedes */ CHAR_MAX, + /* int_n_cs_precedes */ CHAR_MAX, + /* int_p_sep_by_space */ CHAR_MAX, + /* int_n_sep_by_space */ CHAR_MAX, + /* int_p_sign_posn */ CHAR_MAX, + /* int_n_sign_posn */ CHAR_MAX +}; + +struct _PDCLIB_lc_numeric_monetary_t _PDCLIB_lc_numeric_monetary = { + &_PDCLIB_lconv, + 0, /* numeric_allocated */ + 0 /* monetary_allocated */ +}; + +struct _PDCLIB_lc_messages_t _PDCLIB_lc_messages = { + 0, + /* _PDCLIB_errno_texts */ + { + /* no error */ (char *)"", + /* ERANGE */ (char *)"ERANGE (Range error)", + /* EDOM */ (char *)"EDOM (Domain error)", + /* EILSEQ */ (char *)"EILSEQ (Illegal sequence)" + } +}; + +struct _PDCLIB_lc_time_t _PDCLIB_lc_time = { + 0, + /* _PDCLIB_month_name_abbr */ + { + (char *)"Jan", + (char *)"Feb", + (char *)"Mar", + (char *)"Apr", + (char *)"May", + (char *)"Jun", + (char *)"Jul", + (char *)"Aug", + (char *)"Sep", + (char *)"Oct", + (char *)"Now", + (char *)"Dec" + }, + /* _PDCLIB_month_name_full */ + { + (char *)"January", + (char *)"February", + (char *)"March", + (char *)"April", + (char *)"May", + (char *)"June", + (char *)"July", + (char *)"August", + (char *)"September", + (char *)"October", + (char *)"November", + (char *)"December" + }, + /* _PDCLIB_day_name_abbr */ + { + (char *)"Sun", + (char *)"Mon", + (char *)"Tue", + (char *)"Wed", + (char *)"Thu", + (char *)"Fri", + (char *)"Sat" + }, + /* _PDCLIB_day_name_full */ + { + (char *)"Sunday", + (char *)"Monday", + (char *)"Tuesday", + (char *)"Wednesday", + (char *)"Thursday", + (char *)"Friday", + (char *)"Saturday" + }, + /* date / time format */ (char *)"%a %b %e %T %Y", + /* 12h time format */ (char *)"%I:%M:%S %p", + /* date format */ (char *)"%m/%d/%y", + /* time format */ (char *)"%T", + /* AM / PM designation */ + { + (char *)"AM", + (char *)"PM" + } +}; diff --git a/src/libraries/libc/j6libc/strtox_main.c b/src/libraries/libc/j6libc/strtox_main.c new file mode 100644 index 0000000..0d039ae --- /dev/null +++ b/src/libraries/libc/j6libc/strtox_main.c @@ -0,0 +1,42 @@ +/* _PDCLIB_strtox_main( const char * *, int, uintmax_t, uintmax_t, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include + +uintmax_t _PDCLIB_strtox_main( const char ** p, unsigned int base, uintmax_t error, uintmax_t limval, int limdigit, char * sign ) +{ + uintmax_t rc = 0; + int digit = -1; + const char * x; + while ( ( x = memchr( _PDCLIB_digits, tolower(**p), base ) ) != NULL ) + { + digit = x - _PDCLIB_digits; + if ( ( rc < limval ) || ( ( rc == limval ) && ( digit <= limdigit ) ) ) + { + rc = rc * base + (unsigned)digit; + ++(*p); + } + else + { + errno = ERANGE; + /* TODO: Only if endptr != NULL - but do we really want *another* parameter? */ + /* TODO: Earlier version was missing tolower() here but was not caught by tests */ + while ( memchr( _PDCLIB_digits, tolower(**p), base ) != NULL ) ++(*p); + /* TODO: This is ugly, but keeps caller from negating the error value */ + *sign = '+'; + return error; + } + } + if ( digit == -1 ) + { + *p = NULL; + return 0; + } + return rc; +} diff --git a/src/libraries/libc/j6libc/strtox_prelim.c b/src/libraries/libc/j6libc/strtox_prelim.c new file mode 100644 index 0000000..935632c --- /dev/null +++ b/src/libraries/libc/j6libc/strtox_prelim.c @@ -0,0 +1,50 @@ +/* _PDCLIB_strtox_prelim( const char *, char *, int * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +const char * _PDCLIB_strtox_prelim( const char * p, char * sign, int * base ) +{ + /* skipping leading whitespace */ + while ( isspace( *p ) ) ++p; + /* determining / skipping sign */ + if ( *p != '+' && *p != '-' ) *sign = '+'; + else *sign = *(p++); + /* determining base */ + if ( *p == '0' ) + { + ++p; + if ( ( *base == 0 || *base == 16 ) && ( *p == 'x' || *p == 'X' ) ) + { + *base = 16; + ++p; + /* catching a border case here: "0x" followed by a non-digit should + be parsed as the unprefixed zero. + We have to "rewind" the parsing; having the base set to 16 if it + was zero previously does not hurt, as the result is zero anyway. + */ + if ( memchr( _PDCLIB_digits, tolower(*p), *base ) == NULL ) + { + p -= 2; + } + } + else if ( *base == 0 ) + { + *base = 8; + } + else + { + --p; + } + } + else if ( ! *base ) + { + *base = 10; + } + return ( ( *base >= 2 ) && ( *base <= 36 ) ) ? p : NULL; +} diff --git a/src/libraries/libc/locale/localeconv.c b/src/libraries/libc/locale/localeconv.c new file mode 100644 index 0000000..d610c6b --- /dev/null +++ b/src/libraries/libc/locale/localeconv.c @@ -0,0 +1,12 @@ +/* localeconv( void ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +struct lconv * localeconv( void ) +{ + return _PDCLIB_lc_numeric_monetary.lconv; +} diff --git a/src/libraries/libc/locale/setlocale.c b/src/libraries/libc/locale/setlocale.c new file mode 100644 index 0000000..f31aa44 --- /dev/null +++ b/src/libraries/libc/locale/setlocale.c @@ -0,0 +1,242 @@ +/* setlocale( int, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +#if 0 +static const char * _PDCLIB_LC_category_name[ _PDCLIB_LC_COUNT ] = { NULL, "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LC_MESSAGES" }; + +static const char * _PDCLIB_default_locale( int category ) +{ + const char * s; + + if ( ( s = getenv( "LC_ALL" ) ) == NULL ) + { + if ( category == LC_ALL || ( s = getenv( _PDCLIB_LC_category_name[ category ] ) ) == NULL ) + { + if ( ( s = getenv( "LANG" ) ) == NULL ) + { + s = "C"; + } + } + } + + return s; +} +#endif + +char * setlocale( int category, const char * locale ) +{ + /* All below is very much work-in-progress, so we do a dumb-dummy + return here. + */ + if ( locale == NULL || ! strcmp( locale, "C" ) ) + { + return (char *)"C"; + } + else + { + return NULL; + } + +#if 0 + /* Path to locale data files - _PDCLIB_LOCALE_PATH unless overruled + by the environment variable whose name is defined by preprocessor + symbol _PDCLIB_LOCALE_PATH_ENV (defaulting to PDCLIB_I18N). + Both of these definitions are set in config.h. + */ + const char * path = _PDCLIB_LOCALE_PATH; + + struct _PDCLIB_lc_lconv_numeric_t * numeric = NULL; + struct _PDCLIB_lc_lconv_monetary_t * monetary = NULL; + struct _PDCLIB_lc_collate_t * collate = NULL; + struct _PDCLIB_lc_ctype_t * ctype = NULL; + struct _PDCLIB_lc_messages_t * messages = NULL; + struct _PDCLIB_lc_time_t * time = NULL; + + char * rc = (char *)locale; + + if ( category < 0 || category >= _PDCLIB_LC_COUNT ) + { + /* Bad category */ + return NULL; + } + + if ( locale == NULL ) + { + /* NULL - Return current locale settings */ + /* TODO */ + } + + if ( strlen( locale ) == 0 ) + { + /* "" - Use default locale */ + locale = _PDCLIB_default_locale( category ); + } + + if ( getenv( _PDCLIB_symbol2string( _PDCLIB_LOCALE_PATH_ENV ) ) != NULL ) + { + path = getenv( _PDCLIB_symbol2string( _PDCLIB_LOCALE_PATH_ENV ) ); + } + + /* We have to do this in two runs. As we might be facing LC_ALL, we + need to be certain all the loads are successful before we start + to overwrite the current locale settings, because there is no way + this function could report a _partial_ success. + */ + + /* Run One -- get all the data for the new locale setting */ + if ( category == LC_COLLATE || category == LC_ALL ) + { + if ( ! ( collate = _PDCLIB_load_lc_collate( path, locale ) ) ) + { + rc = NULL; + } + } + + if ( category == LC_CTYPE || category == LC_ALL ) + { + if ( ! ( ctype = _PDCLIB_load_lc_ctype( path, locale ) ) ) + { + rc = NULL; + } + } + + if ( category == LC_MONETARY || category == LC_ALL ) + { + if ( ! ( monetary = _PDCLIB_load_lc_monetary( path, locale ) ) ) + { + rc = NULL; + } + } + + if ( category == LC_NUMERIC || category == LC_ALL ) + { + if ( ! ( numeric = _PDCLIB_load_lc_numeric( path, locale ) ) ) + { + rc = NULL; + } + } + + if ( category == LC_TIME || category == LC_ALL ) + { + if ( ! ( time = _PDCLIB_load_lc_time( path, locale ) ) ) + { + rc = NULL; + } + } + + if ( category == LC_MESSAGES || category == LC_ALL ) + { + if ( ! ( messages = _PDCLIB_load_lc_messages( path, locale ) ) ) + { + rc = NULL; + } + } + + /* Run Two -- continue or release resources */ + if ( rc != NULL ) + { + if ( category == LC_COLLATE || category == LC_ALL ) + { + if ( _PDCLIB_lc_collate.alloced ) + { + /* free resources */ + } + + _PDCLIB_lc_collate = *collate; + free( collate ); + } + + if ( category == LC_CTYPE || category == LC_ALL ) + { + if ( _PDCLIB_lc_ctype.alloced ) + { + free( _PDCLIB_lc_ctype.entry - 1 ); + } + + _PDCLIB_lc_ctype = *ctype; + free( ctype ); + } + + if ( category == LC_MONETARY || category == LC_ALL ) + { + if ( _PDCLIB_lc_numeric_monetary.monetary_alloced ) + { + free( _PDCLIB_lc_numeric_monetary.lconv->mon_decimal_point ); + } + + _PDCLIB_lc_numeric_monetary.lconv->mon_decimal_point = monetary->mon_decimal_point; + _PDCLIB_lc_numeric_monetary.lconv->mon_thousands_sep = monetary->mon_thousands_sep; + _PDCLIB_lc_numeric_monetary.lconv->mon_grouping = monetary->mon_grouping; + _PDCLIB_lc_numeric_monetary.lconv->positive_sign = monetary->positive_sign; + _PDCLIB_lc_numeric_monetary.lconv->negative_sign = monetary->negative_sign; + _PDCLIB_lc_numeric_monetary.lconv->currency_symbol = monetary->currency_symbol; + _PDCLIB_lc_numeric_monetary.lconv->int_curr_symbol = monetary->int_curr_symbol; + _PDCLIB_lc_numeric_monetary.lconv->frac_digits = monetary->frac_digits; + _PDCLIB_lc_numeric_monetary.lconv->p_cs_precedes = monetary->p_cs_precedes; + _PDCLIB_lc_numeric_monetary.lconv->n_cs_precedes = monetary->n_cs_precedes; + _PDCLIB_lc_numeric_monetary.lconv->p_sep_by_space = monetary->p_sep_by_space; + _PDCLIB_lc_numeric_monetary.lconv->n_sep_by_space = monetary->n_sep_by_space; + _PDCLIB_lc_numeric_monetary.lconv->p_sign_posn = monetary->p_sign_posn; + _PDCLIB_lc_numeric_monetary.lconv->n_sign_posn = monetary->n_sign_posn; + _PDCLIB_lc_numeric_monetary.lconv->int_frac_digits = monetary->int_frac_digits; + _PDCLIB_lc_numeric_monetary.lconv->int_p_cs_precedes = monetary->int_p_cs_precedes; + _PDCLIB_lc_numeric_monetary.lconv->int_n_cs_precedes = monetary->int_n_cs_precedes; + _PDCLIB_lc_numeric_monetary.lconv->int_p_sep_by_space = monetary->int_p_sep_by_space; + _PDCLIB_lc_numeric_monetary.lconv->int_n_sep_by_space = monetary->int_n_sep_by_space; + _PDCLIB_lc_numeric_monetary.lconv->int_p_sign_posn = monetary->int_p_sign_posn; + _PDCLIB_lc_numeric_monetary.lconv->int_n_sign_posn = monetary->int_n_sign_posn; + + _PDCLIB_lc_numeric_monetary.monetary_alloced = 1; + + free( monetary ); + } + + if ( category == LC_NUMERIC || category == LC_ALL ) + { + if ( _PDCLIB_lc_numeric_monetary.numeric_alloced ) + { + free( _PDCLIB_lc_numeric_monetary.lconv->decimal_point ); + } + + _PDCLIB_lc_numeric_monetary.lconv->decimal_point = numeric->decimal_point; + _PDCLIB_lc_numeric_monetary.lconv->thousands_sep = numeric->thousands_sep; + _PDCLIB_lc_numeric_monetary.lconv->grouping = numeric->grouping; + + _PDCLIB_lc_numeric_monetary.numeric_alloced = 1; + + free( numeric ); + } + + if ( category == LC_TIME || category == LC_ALL ) + { + if ( _PDCLIB_lc_time.alloced ) + { + free( _PDCLIB_lc_time.month_name_abbr[ 0 ] ); + } + + _PDCLIB_lc_time = *time; + free( time ); + } + + if ( category == LC_MESSAGES || category == LC_ALL ) + { + if ( _PDCLIB_lc_messages.alloced ) + { + free( _PDCLIB_lc_messages.errno_texts[ 0 ] ); + } + + _PDCLIB_lc_messages = *messages; + free( messages ); + } + } + + return NULL; +#endif +} diff --git a/src/libraries/libc/signal/raise.c b/src/libraries/libc/signal/raise.c new file mode 100644 index 0000000..679bb5f --- /dev/null +++ b/src/libraries/libc/signal/raise.c @@ -0,0 +1,63 @@ +/* raise( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +extern void (*_PDCLIB_sigabrt)( int ); +extern void (*_PDCLIB_sigfpe)( int ); +extern void (*_PDCLIB_sigill)( int ); +extern void (*_PDCLIB_sigint)( int ); +extern void (*_PDCLIB_sigsegv)( int ); +extern void (*_PDCLIB_sigterm)( int ); + +int raise( int sig ) +{ + void (*sighandler)( int ) = NULL; + const char * message; + switch ( sig ) + { + case SIGABRT: + sighandler = _PDCLIB_sigabrt; + message = "Abnormal termination (SIGABRT)"; + break; + case SIGFPE: + sighandler = _PDCLIB_sigfpe; + message = "Arithmetic exception (SIGFPE)"; + break; + case SIGILL: + sighandler = _PDCLIB_sigill; + message = "Illegal instruction (SIGILL)"; + break; + case SIGINT: + sighandler = _PDCLIB_sigint; + message = "Interactive attention signal (SIGINT)"; + break; + case SIGSEGV: + sighandler = _PDCLIB_sigsegv; + message = "Invalid memory access (SIGSEGV)"; + break; + case SIGTERM: + sighandler = _PDCLIB_sigterm; + message = "Termination request (SIGTERM)"; + break; + default: + fprintf( stderr, "Unknown signal #%d\n", sig ); + _Exit( EXIT_FAILURE ); + } + if ( sighandler == SIG_DFL ) + { + fputs( message, stderr ); + _Exit( EXIT_FAILURE ); + } + else if ( sighandler != SIG_IGN ) + { + sighandler = signal( sig, SIG_DFL ); + sighandler( sig ); + } + return 0; +} diff --git a/src/libraries/libc/signal/signal.c b/src/libraries/libc/signal/signal.c new file mode 100644 index 0000000..2358f87 --- /dev/null +++ b/src/libraries/libc/signal/signal.c @@ -0,0 +1,58 @@ +/* signal( int, void (*)( int ) ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +void (*_PDCLIB_sigabrt)( int ) = SIG_DFL; +void (*_PDCLIB_sigfpe)( int ) = SIG_DFL; +void (*_PDCLIB_sigill)( int ) = SIG_DFL; +void (*_PDCLIB_sigint)( int ) = SIG_DFL; +void (*_PDCLIB_sigsegv)( int ) = SIG_DFL; +void (*_PDCLIB_sigterm)( int ) = SIG_DFL; + +void (*signal( int sig, void (*func)( int ) ) )( int ) +{ + void (*oldhandler)( int ); + if ( sig <= 0 || func == SIG_ERR ) + { + return SIG_ERR; + } + switch ( sig ) + { + case SIGABRT: + oldhandler = _PDCLIB_sigabrt; + _PDCLIB_sigabrt = func; + break; + case SIGFPE: + oldhandler = _PDCLIB_sigfpe; + _PDCLIB_sigfpe = func; + break; + case SIGILL: + oldhandler = _PDCLIB_sigill; + _PDCLIB_sigill = func; + break; + case SIGINT: + oldhandler = _PDCLIB_sigint; + _PDCLIB_sigint = func; + break; + case SIGSEGV: + oldhandler = _PDCLIB_sigsegv; + _PDCLIB_sigsegv = func; + break; + case SIGTERM: + oldhandler = _PDCLIB_sigterm; + _PDCLIB_sigterm = func; + break; + default: + /* The standard calls for an unspecified "positive value". You + will probably want to define a specific value for this. + */ + _PDCLIB_errno = 1; + return SIG_ERR; + } + return oldhandler; +} diff --git a/src/libraries/libc/stdio/clearerr.c b/src/libraries/libc/stdio/clearerr.c new file mode 100644 index 0000000..0dbf9c1 --- /dev/null +++ b/src/libraries/libc/stdio/clearerr.c @@ -0,0 +1,12 @@ +/* clearerr( FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +void clearerr( struct _PDCLIB_file_t * stream ) +{ + stream->status &= ~( _PDCLIB_ERRORFLAG | _PDCLIB_EOFFLAG ); +} diff --git a/src/libraries/libc/stdio/fclose.c b/src/libraries/libc/stdio/fclose.c new file mode 100644 index 0000000..c321f35 --- /dev/null +++ b/src/libraries/libc/stdio/fclose.c @@ -0,0 +1,67 @@ +/* fclose( FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include "j6libc/glue.h" + +extern struct _PDCLIB_file_t * _PDCLIB_filelist; + +int fclose( struct _PDCLIB_file_t * stream ) +{ + struct _PDCLIB_file_t * current = _PDCLIB_filelist; + struct _PDCLIB_file_t * previous = NULL; + /* Checking that the FILE handle is actually one we had opened before. */ + while ( current != NULL ) + { + if ( stream == current ) + { + /* Flush buffer */ + if ( stream->status & _PDCLIB_FWRITE ) + { + if ( _PDCLIB_flushbuffer( stream ) == EOF ) + { + /* Flush failed, errno already set */ + return EOF; + } + } + /* Close handle */ + _PDCLIB_close( stream->handle ); + /* Remove stream from list */ + if ( previous != NULL ) + { + previous->next = stream->next; + } + else + { + _PDCLIB_filelist = stream->next; + } + /* Delete tmpfile() */ + if ( stream->status & _PDCLIB_DELONCLOSE ) + { + remove( stream->filename ); + } + /* Free user buffer (SetVBuf allocated) */ + if ( stream->status & _PDCLIB_FREEBUFFER ) + { + free( stream->buffer ); + } + /* Free stream */ + if ( ! ( stream->status & _PDCLIB_STATIC ) ) + { + free( stream ); + } + return 0; + } + previous = current; + current = current->next; + } + /* See the comments on implementation-defined errno values in + . + */ + _PDCLIB_errno = _PDCLIB_ERROR; + return -1; +} diff --git a/src/libraries/libc/stdio/feof.c b/src/libraries/libc/stdio/feof.c new file mode 100644 index 0000000..56c1115 --- /dev/null +++ b/src/libraries/libc/stdio/feof.c @@ -0,0 +1,12 @@ +/* feof( FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int feof( struct _PDCLIB_file_t * stream ) +{ + return stream->status & _PDCLIB_EOFFLAG; +} diff --git a/src/libraries/libc/stdio/ferror.c b/src/libraries/libc/stdio/ferror.c new file mode 100644 index 0000000..06baded --- /dev/null +++ b/src/libraries/libc/stdio/ferror.c @@ -0,0 +1,12 @@ +/* ferror( FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int ferror( struct _PDCLIB_file_t * stream ) +{ + return stream->status & _PDCLIB_ERRORFLAG; +} diff --git a/src/libraries/libc/stdio/fflush.c b/src/libraries/libc/stdio/fflush.c new file mode 100644 index 0000000..48d8f32 --- /dev/null +++ b/src/libraries/libc/stdio/fflush.c @@ -0,0 +1,36 @@ +/* fflush( FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +extern struct _PDCLIB_file_t * _PDCLIB_filelist; + +int fflush( struct _PDCLIB_file_t * stream ) +{ + if ( stream == NULL ) + { + int rc = 0; + stream = _PDCLIB_filelist; + /* TODO: Check what happens when fflush( NULL ) encounters write errors, in other libs */ + while ( stream != NULL ) + { + if ( stream->status & _PDCLIB_FWRITE ) + { + if ( _PDCLIB_flushbuffer( stream ) == EOF ) + { + rc = EOF; + } + } + stream = stream->next; + } + return rc; + } + else + { + return _PDCLIB_flushbuffer( stream ); + } +} diff --git a/src/libraries/libc/stdio/fgetc.c b/src/libraries/libc/stdio/fgetc.c new file mode 100644 index 0000000..b71b449 --- /dev/null +++ b/src/libraries/libc/stdio/fgetc.c @@ -0,0 +1,21 @@ +/* fgetc( FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +int fgetc( struct _PDCLIB_file_t * stream ) +{ + if ( _PDCLIB_prepread( stream ) == EOF ) + { + return EOF; + } + if ( stream->ungetidx > 0 ) + { + return (unsigned char)stream->ungetbuf[ --(stream->ungetidx) ]; + } + return (unsigned char)stream->buffer[stream->bufidx++]; +} diff --git a/src/libraries/libc/stdio/fgetpos.c b/src/libraries/libc/stdio/fgetpos.c new file mode 100644 index 0000000..0076489 --- /dev/null +++ b/src/libraries/libc/stdio/fgetpos.c @@ -0,0 +1,15 @@ +/* fgetpos( FILE * , fpos_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int fgetpos( struct _PDCLIB_file_t * restrict stream, struct _PDCLIB_fpos_t * restrict pos ) +{ + pos->offset = stream->pos.offset + stream->bufidx - stream->ungetidx; + pos->status = stream->pos.status; + /* TODO: Add mbstate. */ + return 0; +} diff --git a/src/libraries/libc/stdio/fgets.c b/src/libraries/libc/stdio/fgets.c new file mode 100644 index 0000000..410a6a5 --- /dev/null +++ b/src/libraries/libc/stdio/fgets.c @@ -0,0 +1,42 @@ +/* fgets( char *, int, FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +char * fgets( char * restrict s, int size, struct _PDCLIB_file_t * restrict stream ) +{ + char * dest = s; + if ( size == 0 ) + { + return NULL; + } + if ( size == 1 ) + { + *s = '\0'; + return s; + } + if ( _PDCLIB_prepread( stream ) == EOF ) + { + return NULL; + } + while ( ( ( *dest++ = stream->buffer[stream->bufidx++] ) != '\n' ) && --size > 0 ) + { + if ( stream->bufidx == stream->bufend ) + { + if ( _PDCLIB_fillbuffer( stream ) == EOF ) + { + /* In case of error / EOF before a character is read, this + will lead to a \0 be written anyway. Since the results + are "indeterminate" by definition, this does not hurt. + */ + break; + } + } + } + *dest = '\0'; + return ( dest == s ) ? NULL : s; +} diff --git a/src/libraries/libc/stdio/fopen.c b/src/libraries/libc/stdio/fopen.c new file mode 100644 index 0000000..b3dd147 --- /dev/null +++ b/src/libraries/libc/stdio/fopen.c @@ -0,0 +1,70 @@ +/* fopen( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include "j6libc/glue.h" + +#include + +extern struct _PDCLIB_file_t * _PDCLIB_filelist; + +struct _PDCLIB_file_t * fopen( const char * restrict filename, const char * restrict mode ) +{ + struct _PDCLIB_file_t * rc; + size_t filename_len; + if ( mode == NULL || filename == NULL || filename[0] == '\0' ) + { + /* Mode or filename invalid */ + return NULL; + } + /* To reduce the number of malloc calls, all data fields are concatenated: + * the FILE structure itself, + * ungetc buffer, + * filename buffer, + * data buffer. + Data buffer comes last because it might change in size ( setvbuf() ). + */ + filename_len = strlen( filename ) + 1; + if ( ( rc = calloc( 1, sizeof( struct _PDCLIB_file_t ) + _PDCLIB_UNGETCBUFSIZE + filename_len + BUFSIZ ) ) == NULL ) + { + /* no memory */ + return NULL; + } + if ( ( rc->status = _PDCLIB_filemode( mode ) ) == 0 ) + { + /* invalid mode */ + free( rc ); + return NULL; + } + rc->handle = _PDCLIB_open( filename, rc->status ); + if ( rc->handle == _PDCLIB_NOHANDLE ) + { + /* OS open() failed */ + free( rc ); + return NULL; + } + /* Setting pointers into the memory block allocated above */ + rc->ungetbuf = (unsigned char *)rc + sizeof( struct _PDCLIB_file_t ); + rc->filename = (char *)rc->ungetbuf + _PDCLIB_UNGETCBUFSIZE; + rc->buffer = rc->filename + filename_len; + /* Copying filename to FILE structure */ + strcpy( rc->filename, filename ); + /* Initializing the rest of the structure */ + rc->bufsize = BUFSIZ; + rc->bufidx = 0; + rc->ungetidx = 0; + /* Setting buffer to _IOLBF because "when opened, a stream is fully + buffered if and only if it can be determined not to refer to an + interactive device." + */ + rc->status |= _IOLBF; + /* TODO: Setting mbstate */ + /* Adding to list of open files */ + rc->next = _PDCLIB_filelist; + _PDCLIB_filelist = rc; + return rc; +} diff --git a/src/libraries/libc/stdio/fprintf.c b/src/libraries/libc/stdio/fprintf.c new file mode 100644 index 0000000..562ce70 --- /dev/null +++ b/src/libraries/libc/stdio/fprintf.c @@ -0,0 +1,18 @@ +/* fprintf( FILE *, const char *, ... ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int fprintf( struct _PDCLIB_file_t * restrict stream, const char * restrict format, ... ) +{ + int rc; + va_list ap; + va_start( ap, format ); + rc = vfprintf( stream, format, ap ); + va_end( ap ); + return rc; +} diff --git a/src/libraries/libc/stdio/fputc.c b/src/libraries/libc/stdio/fputc.c new file mode 100644 index 0000000..8a55f84 --- /dev/null +++ b/src/libraries/libc/stdio/fputc.c @@ -0,0 +1,30 @@ +/* fputc( int, FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +/* Write the value c (cast to unsigned char) to the given stream. + Returns c if successful, EOF otherwise. + If a write error occurs, the error indicator of the stream is set. +*/ +int fputc( int c, struct _PDCLIB_file_t * stream ) +{ + if ( _PDCLIB_prepwrite( stream ) == EOF ) + { + return EOF; + } + stream->buffer[stream->bufidx++] = (char)c; + if ( ( stream->bufidx == stream->bufsize ) /* _IOFBF */ + || ( ( stream->status & _IOLBF ) && ( (char)c == '\n' ) ) /* _IOLBF */ + || ( stream->status & _IONBF ) /* _IONBF */ + ) + { + /* buffer filled, unbuffered stream, or end-of-line. */ + return ( _PDCLIB_flushbuffer( stream ) == 0 ) ? c : EOF; + } + return c; +} diff --git a/src/libraries/libc/stdio/fputs.c b/src/libraries/libc/stdio/fputs.c new file mode 100644 index 0000000..f52d61d --- /dev/null +++ b/src/libraries/libc/stdio/fputs.c @@ -0,0 +1,42 @@ +/* fputs( const char *, FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +int fputs( const char * restrict s, struct _PDCLIB_file_t * restrict stream ) +{ + if ( _PDCLIB_prepwrite( stream ) == EOF ) + { + return EOF; + } + while ( *s != '\0' ) + { + /* Unbuffered and line buffered streams get flushed when fputs() does + write the terminating end-of-line. All streams get flushed if the + buffer runs full. + */ + stream->buffer[ stream->bufidx++ ] = *s; + if ( ( stream->bufidx == stream->bufsize ) || + ( ( stream->status & _IOLBF ) && *s == '\n' ) + ) + { + if ( _PDCLIB_flushbuffer( stream ) == EOF ) + { + return EOF; + } + } + ++s; + } + if ( stream->status & _IONBF ) + { + if ( _PDCLIB_flushbuffer( stream ) == EOF ) + { + return EOF; + } + } + return 0; +} diff --git a/src/libraries/libc/stdio/fread.c b/src/libraries/libc/stdio/fread.c new file mode 100644 index 0000000..923c8fa --- /dev/null +++ b/src/libraries/libc/stdio/fread.c @@ -0,0 +1,36 @@ +/* fwrite( void *, size_t, size_t, FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include "j6libc/glue.h" + +size_t fread( void * restrict ptr, size_t size, size_t nmemb, struct _PDCLIB_file_t * restrict stream ) +{ + char * dest = (char *)ptr; + size_t nmemb_i; + if ( _PDCLIB_prepread( stream ) == EOF ) + { + return 0; + } + for ( nmemb_i = 0; nmemb_i < nmemb; ++nmemb_i ) + { + size_t size_i; + for ( size_i = 0; size_i < size; ++size_i ) + { + if ( stream->bufidx == stream->bufend ) + { + if ( _PDCLIB_fillbuffer( stream ) == EOF ) + { + /* Could not read requested data */ + return nmemb_i; + } + } + dest[ nmemb_i * size + size_i ] = stream->buffer[ stream->bufidx++ ]; + } + } + return nmemb_i; +} diff --git a/src/libraries/libc/stdio/freopen.c b/src/libraries/libc/stdio/freopen.c new file mode 100644 index 0000000..331b60c --- /dev/null +++ b/src/libraries/libc/stdio/freopen.c @@ -0,0 +1,70 @@ +/* freopen( const char *, const char *, FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include "j6libc/glue.h" + +struct _PDCLIB_file_t * freopen( const char * restrict filename, const char * restrict mode, struct _PDCLIB_file_t * restrict stream ) +{ + unsigned int status = stream->status & ( _IONBF | _IOLBF | _IOFBF | _PDCLIB_FREEBUFFER | _PDCLIB_DELONCLOSE ); + /* TODO: This function can change wide orientation of a stream */ + if ( stream->status & _PDCLIB_FWRITE ) + { + _PDCLIB_flushbuffer( stream ); + } + if ( ( filename == NULL ) && ( stream->filename == NULL ) ) + { + /* TODO: Special handling for mode changes on std-streams */ + return NULL; + } + _PDCLIB_close( stream->handle ); + /* TODO: It is not nice to do this on a stream we just closed. + It does not matter with the current implementation of clearerr(), + but it might start to matter if someone replaced that implementation. + */ + clearerr( stream ); + /* The new filename might not fit the old buffer */ + if ( filename == NULL ) + { + /* Use previous filename */ + filename = stream->filename; + } + else if ( ( stream->filename != NULL ) && ( strlen( stream->filename ) >= strlen( filename ) ) ) + { + /* Copy new filename into existing buffer */ + strcpy( stream->filename, filename ); + } + else + { + /* Allocate new buffer */ + if ( ( stream->filename = (char *)malloc( strlen( filename ) ) ) == NULL ) + { + return NULL; + } + strcpy( stream->filename, filename ); + } + if ( ( mode == NULL ) || ( filename[0] == '\0' ) ) + { + return NULL; + } + if ( ( stream->status = _PDCLIB_filemode( mode ) ) == 0 ) + { + return NULL; + } + /* Re-add the flags we saved above */ + stream->status |= status; + stream->bufidx = 0; + stream->bufend = 0; + stream->ungetidx = 0; + /* TODO: Setting mbstate */ + if ( ( stream->handle = _PDCLIB_open( filename, stream->status ) ) == _PDCLIB_NOHANDLE ) + { + return NULL; + } + return stream; +} diff --git a/src/libraries/libc/stdio/fscanf.c b/src/libraries/libc/stdio/fscanf.c new file mode 100644 index 0000000..15115b5 --- /dev/null +++ b/src/libraries/libc/stdio/fscanf.c @@ -0,0 +1,18 @@ +/* fscanf( FILE *, const char *, ... ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int fscanf( FILE * restrict stream, const char * restrict format, ... ) +{ + int rc; + va_list ap; + va_start( ap, format ); + rc = vfscanf( stream, format, ap ); + va_end( ap ); + return rc; +} diff --git a/src/libraries/libc/stdio/fseek.c b/src/libraries/libc/stdio/fseek.c new file mode 100644 index 0000000..19a8187 --- /dev/null +++ b/src/libraries/libc/stdio/fseek.c @@ -0,0 +1,25 @@ +/* fseek( FILE *, long, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +int fseek( struct _PDCLIB_file_t * stream, long offset, int whence ) +{ + if ( stream->status & _PDCLIB_FWRITE ) + { + if ( _PDCLIB_flushbuffer( stream ) == EOF ) + { + return EOF; + } + } + stream->status &= ~ _PDCLIB_EOFFLAG; + if ( stream->status & _PDCLIB_FRW ) + { + stream->status &= ~ ( _PDCLIB_FREAD | _PDCLIB_FWRITE ); + } + return ( _PDCLIB_seek( stream, offset, whence ) != EOF ) ? 0 : EOF; +} diff --git a/src/libraries/libc/stdio/fsetpos.c b/src/libraries/libc/stdio/fsetpos.c new file mode 100644 index 0000000..b0bdeef --- /dev/null +++ b/src/libraries/libc/stdio/fsetpos.c @@ -0,0 +1,26 @@ +/* fsetpos( FILE *, const fpos_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +int fsetpos( struct _PDCLIB_file_t * stream, const struct _PDCLIB_fpos_t * pos ) +{ + if ( stream->status & _PDCLIB_FWRITE ) + { + if ( _PDCLIB_flushbuffer( stream ) == EOF ) + { + return EOF; + } + } + if ( _PDCLIB_seek( stream, pos->offset, SEEK_SET ) == EOF ) + { + return EOF; + } + stream->pos.status = pos->status; + /* TODO: Add mbstate. */ + return 0; +} diff --git a/src/libraries/libc/stdio/ftell.c b/src/libraries/libc/stdio/ftell.c new file mode 100644 index 0000000..72a3fbc --- /dev/null +++ b/src/libraries/libc/stdio/ftell.c @@ -0,0 +1,38 @@ +/* ftell( FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +long int ftell( struct _PDCLIB_file_t * stream ) +{ + /* ftell() must take into account: + - the actual *physical* offset of the file, i.e. the offset as recognized + by the operating system (and stored in stream->pos.offset); and + - any buffers held by PDCLib, which + - in case of unwritten buffers, count in *addition* to the offset; or + - in case of unprocessed pre-read buffers, count in *substraction* to + the offset. (Remember to count ungetidx into this number.) + Conveniently, the calculation ( ( bufend - bufidx ) + ungetidx ) results + in just the right number in both cases: + - in case of unwritten buffers, ( ( 0 - unwritten ) + 0 ) + i.e. unwritten bytes as negative number + - in case of unprocessed pre-read, ( ( preread - processed ) + unget ) + i.e. unprocessed bytes as positive number. + That is how the somewhat obscure return-value calculation works. + */ + /* If offset is too large for return type, report error instead of wrong + offset value. + */ + /* TODO: Check what happens when ungetc() is called on a stream at offset 0 */ + if ( ( stream->pos.offset - stream->bufend ) > ( LONG_MAX - ( stream->bufidx - stream->ungetidx ) ) ) + { + /* integer overflow */ + _PDCLIB_errno = _PDCLIB_ERANGE; + return -1; + } + return (long int)( stream->pos.offset - ( ( (int)stream->bufend - (int)stream->bufidx ) + stream->ungetidx ) ); +} diff --git a/src/libraries/libc/stdio/fwrite.c b/src/libraries/libc/stdio/fwrite.c new file mode 100644 index 0000000..360baf1 --- /dev/null +++ b/src/libraries/libc/stdio/fwrite.c @@ -0,0 +1,76 @@ +/* fwrite( const void *, size_t, size_t, FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include "j6libc/glue.h" + +size_t fwrite( const void * restrict ptr, size_t size, size_t nmemb, struct _PDCLIB_file_t * restrict stream ) +{ + size_t offset = 0; + /* TODO: lineend */ + /* int lineend = 0; */ + size_t nmemb_i; + if ( _PDCLIB_prepwrite( stream ) == EOF ) + { + return 0; + } + for ( nmemb_i = 0; nmemb_i < nmemb; ++nmemb_i ) + { + size_t size_i; + for ( size_i = 0; size_i < size; ++size_i ) + { + if ( ( stream->buffer[ stream->bufidx++ ] = ((char*)ptr)[ nmemb_i * size + size_i ] ) == '\n' ) + { + /* Remember last newline, in case we have to do a partial line-buffered flush */ + offset = stream->bufidx; + /* lineend = true; */ + } + if ( stream->bufidx == stream->bufsize ) + { + if ( _PDCLIB_flushbuffer( stream ) == EOF ) + { + /* Returning number of objects completely buffered */ + return nmemb_i; + } + /* lineend = false; */ + } + } + } + /* Fully-buffered streams are OK. Non-buffered streams must be flushed, + line-buffered streams only if there's a newline in the buffer. + */ + switch ( stream->status & ( _IONBF | _IOLBF ) ) + { + case _IONBF: + if ( _PDCLIB_flushbuffer( stream ) == EOF ) + { + /* We are in a pinch here. We have an error, which requires a + return value < nmemb. On the other hand, all objects have + been written to buffer, which means all the caller had to + do was removing the error cause, and re-flush the stream... + Catch 22. We'll return a value one short, to indicate the + error, and can't really do anything about the inconsistency. + */ + return nmemb_i - 1; + } + break; + case _IOLBF: + { + size_t bufidx = stream->bufidx; + stream->bufidx = offset; + if ( _PDCLIB_flushbuffer( stream ) == EOF ) + { + /* See comment above. */ + stream->bufidx = bufidx; + return nmemb_i - 1; + } + stream->bufidx = bufidx - offset; + memmove( stream->buffer, stream->buffer + offset, stream->bufidx ); + } + } + return nmemb_i; +} diff --git a/src/libraries/libc/stdio/getc.c b/src/libraries/libc/stdio/getc.c new file mode 100644 index 0000000..c32609d --- /dev/null +++ b/src/libraries/libc/stdio/getc.c @@ -0,0 +1,12 @@ +/* getc( FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int getc( struct _PDCLIB_file_t * stream ) +{ + return fgetc( stream ); +} diff --git a/src/libraries/libc/stdio/getchar.c b/src/libraries/libc/stdio/getchar.c new file mode 100644 index 0000000..5cbcb94 --- /dev/null +++ b/src/libraries/libc/stdio/getchar.c @@ -0,0 +1,12 @@ +/* getchar( void ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int getchar( void ) +{ + return fgetc( stdin ); +} diff --git a/src/libraries/libc/stdio/perror.c b/src/libraries/libc/stdio/perror.c new file mode 100644 index 0000000..9ea8273 --- /dev/null +++ b/src/libraries/libc/stdio/perror.c @@ -0,0 +1,27 @@ +/* perror( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +/* TODO: Doing this via a static array is not the way to do it. */ +void perror( const char * s ) +{ + if ( ( s != NULL ) && ( s[0] != '\n' ) ) + { + fprintf( stderr, "%s: ", s ); + } + if ( errno >= _PDCLIB_ERRNO_MAX ) + { + fprintf( stderr, "Unknown error\n" ); + } + else + { + fprintf( stderr, "%s\n", _PDCLIB_lc_messages.errno_texts[errno] ); + } + return; +} diff --git a/src/libraries/libc/stdio/printf.c b/src/libraries/libc/stdio/printf.c new file mode 100644 index 0000000..18d7deb --- /dev/null +++ b/src/libraries/libc/stdio/printf.c @@ -0,0 +1,18 @@ +/* printf( const char *, ... ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int printf( const char * restrict format, ... ) +{ + int rc; + va_list ap; + va_start( ap, format ); + rc = vfprintf( stdout, format, ap ); + va_end( ap ); + return rc; +} diff --git a/src/libraries/libc/stdio/putc.c b/src/libraries/libc/stdio/putc.c new file mode 100644 index 0000000..db4931d --- /dev/null +++ b/src/libraries/libc/stdio/putc.c @@ -0,0 +1,12 @@ +/* putc( int, FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int putc( int c, struct _PDCLIB_file_t * stream ) +{ + return fputc( c, stream ); +} diff --git a/src/libraries/libc/stdio/putchar.c b/src/libraries/libc/stdio/putchar.c new file mode 100644 index 0000000..01a6fbd --- /dev/null +++ b/src/libraries/libc/stdio/putchar.c @@ -0,0 +1,12 @@ +/* putchar( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int putchar( int c ) +{ + return fputc( c, stdout ); +} diff --git a/src/libraries/libc/stdio/puts.c b/src/libraries/libc/stdio/puts.c new file mode 100644 index 0000000..6923723 --- /dev/null +++ b/src/libraries/libc/stdio/puts.c @@ -0,0 +1,39 @@ +/* puts( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +extern char * _PDCLIB_eol; + +int puts( const char * s ) +{ + if ( _PDCLIB_prepwrite( stdout ) == EOF ) + { + return EOF; + } + while ( *s != '\0' ) + { + stdout->buffer[ stdout->bufidx++ ] = *s++; + if ( stdout->bufidx == stdout->bufsize ) + { + if ( _PDCLIB_flushbuffer( stdout ) == EOF ) + { + return EOF; + } + } + } + stdout->buffer[ stdout->bufidx++ ] = '\n'; + if ( ( stdout->bufidx == stdout->bufsize ) || + ( stdout->status & ( _IOLBF | _IONBF ) ) ) + { + return _PDCLIB_flushbuffer( stdout ); + } + else + { + return 0; + } +} diff --git a/src/libraries/libc/stdio/remove.c b/src/libraries/libc/stdio/remove.c new file mode 100644 index 0000000..73f1287 --- /dev/null +++ b/src/libraries/libc/stdio/remove.c @@ -0,0 +1,29 @@ +/* remove( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* This is an example implementation of remove() fit for use with POSIX kernels. +*/ + +#include +#include + +extern struct _PDCLIB_file_t * _PDCLIB_filelist; + +int remove( const char * pathname ) +{ + struct _PDCLIB_file_t * current = _PDCLIB_filelist; + while ( current != NULL ) + { + if ( ( current->filename != NULL ) && ( strcmp( current->filename, pathname ) == 0 ) ) + { + return EOF; + } + current = current->next; + } + + _PDCLIB_errno = _PDCLIB_ERROR; + return -1; +} diff --git a/src/libraries/libc/stdio/rename.c b/src/libraries/libc/stdio/rename.c new file mode 100644 index 0000000..cf2f372 --- /dev/null +++ b/src/libraries/libc/stdio/rename.c @@ -0,0 +1,27 @@ +/* rename( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +#include + +extern struct _PDCLIB_file_t * _PDCLIB_filelist; + +int rename( const char * old, const char * new ) +{ + struct _PDCLIB_file_t * current = _PDCLIB_filelist; + while ( current != NULL ) + { + if ( ( current->filename != NULL ) && ( strcmp( current->filename, old ) == 0 ) ) + { + /* File of that name currently open. Do not rename. */ + return EOF; + } + current = current->next; + } + return _PDCLIB_rename( old, new ); +} diff --git a/src/libraries/libc/stdio/rewind.c b/src/libraries/libc/stdio/rewind.c new file mode 100644 index 0000000..246f68e --- /dev/null +++ b/src/libraries/libc/stdio/rewind.c @@ -0,0 +1,13 @@ +/* rewind( FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +void rewind( struct _PDCLIB_file_t * stream ) +{ + stream->status &= ~ _PDCLIB_ERRORFLAG; + fseek( stream, 0L, SEEK_SET ); +} diff --git a/src/libraries/libc/stdio/scanf.c b/src/libraries/libc/stdio/scanf.c new file mode 100644 index 0000000..c0176dd --- /dev/null +++ b/src/libraries/libc/stdio/scanf.c @@ -0,0 +1,15 @@ +/* scanf( const char *, ... ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int scanf( const char * restrict format, ... ) +{ + va_list ap; + va_start( ap, format ); + return vfscanf( stdin, format, ap ); +} diff --git a/src/libraries/libc/stdio/setbuf.c b/src/libraries/libc/stdio/setbuf.c new file mode 100644 index 0000000..dfca6b3 --- /dev/null +++ b/src/libraries/libc/stdio/setbuf.c @@ -0,0 +1,19 @@ +/* setbuf( FILE *, char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +void setbuf( struct _PDCLIB_file_t * restrict stream, char * restrict buf ) +{ + if ( buf == NULL ) + { + setvbuf( stream, buf, _IONBF, BUFSIZ ); + } + else + { + setvbuf( stream, buf, _IOFBF, BUFSIZ ); + } +} diff --git a/src/libraries/libc/stdio/setvbuf.c b/src/libraries/libc/stdio/setvbuf.c new file mode 100644 index 0000000..3219538 --- /dev/null +++ b/src/libraries/libc/stdio/setvbuf.c @@ -0,0 +1,64 @@ +/* setvbuf( FILE *, char *, int, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +int setvbuf( struct _PDCLIB_file_t * restrict stream, char * restrict buf, int mode, size_t size ) +{ + switch ( mode ) + { + case _IONBF: + /* When unbuffered I/O is requested, we keep the buffer anyway, as + we don't want to e.g. flush the stream for every character of a + stream being printed. + */ + break; + case _IOFBF: + case _IOLBF: + if ( size > INT_MAX || size == 0 ) + { + /* PDCLib only supports buffers up to INT_MAX in size. A size + of zero doesn't make sense. + */ + return -1; + } + if ( buf == NULL ) + { + /* User requested buffer size, but leaves it to library to + allocate the buffer. + */ + /* If current buffer is big enough for requested size, but not + over twice as big (and wasting memory space), we use the + current buffer (i.e., do nothing), to save the malloc() / + free() overhead. + */ + if ( ( stream->bufsize < size ) || ( stream->bufsize > ( size << 1 ) ) ) + { + /* Buffer too small, or much too large - allocate. */ + if ( ( buf = (char *) malloc( size ) ) == NULL ) + { + /* Out of memory error. */ + return -1; + } + /* This buffer must be free()d on fclose() */ + stream->status |= _PDCLIB_FREEBUFFER; + } + } + stream->buffer = buf; + stream->bufsize = size; + break; + default: + /* If mode is something else than _IOFBF, _IOLBF or _IONBF -> exit */ + return -1; + } + /* Deleting current buffer mode */ + stream->status &= ~( _IOFBF | _IOLBF | _IONBF ); + /* Set user-defined mode */ + stream->status |= mode; + return 0; +} diff --git a/src/libraries/libc/stdio/snprintf.c b/src/libraries/libc/stdio/snprintf.c new file mode 100644 index 0000000..ee05173 --- /dev/null +++ b/src/libraries/libc/stdio/snprintf.c @@ -0,0 +1,18 @@ +/* snprintf( char *, size_t, const char *, ... ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int snprintf( char * restrict s, size_t n, const char * restrict format, ...) +{ + int rc; + va_list ap; + va_start( ap, format ); + rc = vsnprintf( s, n, format, ap ); + va_end( ap ); + return rc; +} diff --git a/src/libraries/libc/stdio/sprintf.c b/src/libraries/libc/stdio/sprintf.c new file mode 100644 index 0000000..8c32b1b --- /dev/null +++ b/src/libraries/libc/stdio/sprintf.c @@ -0,0 +1,19 @@ +/* sprintf( char *, const char *, ... ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +int sprintf( char * restrict s, const char * restrict format, ...) +{ + int rc; + va_list ap; + va_start( ap, format ); + rc = vsnprintf( s, SIZE_MAX, format, ap ); /* TODO: replace with non-checking call */ + va_end( ap ); + return rc; +} diff --git a/src/libraries/libc/stdio/sscanf.c b/src/libraries/libc/stdio/sscanf.c new file mode 100644 index 0000000..e3b517b --- /dev/null +++ b/src/libraries/libc/stdio/sscanf.c @@ -0,0 +1,18 @@ +/* sscanf( const char *, const char *, ... ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int sscanf( const char * restrict s, const char * restrict format, ... ) +{ + int rc; + va_list ap; + va_start( ap, format ); + rc = vsscanf( s, format, ap ); + va_end( ap ); + return rc; +} diff --git a/src/libraries/libc/stdio/tmpfile.c b/src/libraries/libc/stdio/tmpfile.c new file mode 100644 index 0000000..f00a175 --- /dev/null +++ b/src/libraries/libc/stdio/tmpfile.c @@ -0,0 +1,22 @@ +/* tmpfile( void ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +#include +#include +#include + +extern struct _PDCLIB_file_t * _PDCLIB_filelist; + +/* This is an example implementation of tmpfile() fit for use with POSIX + kernels. +*/ +struct _PDCLIB_file_t * tmpfile( void ) +{ + return NULL; +} diff --git a/src/libraries/libc/stdio/tmpnam.c b/src/libraries/libc/stdio/tmpnam.c new file mode 100644 index 0000000..a275293 --- /dev/null +++ b/src/libraries/libc/stdio/tmpnam.c @@ -0,0 +1,23 @@ +/* tmpnam( char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include "j6libc/glue.h" + +#include + +char * tmpnam( char * s ) +{ + static char filename[ L_tmpnam ]; + FILE * file = tmpfile(); + if ( s == NULL ) + { + s = filename; + } + strcpy( s, file->filename ); + fclose( file ); + return s; +} diff --git a/src/libraries/libc/stdio/ungetc.c b/src/libraries/libc/stdio/ungetc.c new file mode 100644 index 0000000..c62f2ca --- /dev/null +++ b/src/libraries/libc/stdio/ungetc.c @@ -0,0 +1,16 @@ +/* ungetc( int, FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int ungetc( int c, struct _PDCLIB_file_t * stream ) +{ + if ( c == EOF || stream->ungetidx == _PDCLIB_UNGETCBUFSIZE ) + { + return -1; + } + return stream->ungetbuf[stream->ungetidx++] = (unsigned char) c; +} diff --git a/src/libraries/libc/stdio/vfprintf.c b/src/libraries/libc/stdio/vfprintf.c new file mode 100644 index 0000000..1d68dfe --- /dev/null +++ b/src/libraries/libc/stdio/vfprintf.c @@ -0,0 +1,44 @@ +/* vfprintf( FILE *, const char *, va_list ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +int vfprintf( struct _PDCLIB_file_t * restrict stream, const char * restrict format, va_list arg ) +{ + /* TODO: This function should interpret format as multibyte characters. */ + struct _PDCLIB_status_t status; + status.base = 0; + status.flags = 0; + status.n = SIZE_MAX; + status.i = 0; + status.current = 0; + status.s = NULL; + status.width = 0; + status.prec = EOF; + status.stream = stream; + va_copy( status.arg, arg ); + + while ( *format != '\0' ) + { + const char * rc; + if ( ( *format != '%' ) || ( ( rc = _PDCLIB_print( format, &status ) ) == format ) ) + { + /* No conversion specifier, print verbatim */ + putc( *(format++), stream ); + status.i++; + } + else + { + /* Continue parsing after conversion specifier */ + format = rc; + } + } + va_end( status.arg ); + return status.i; +} + diff --git a/src/libraries/libc/stdio/vfscanf.c b/src/libraries/libc/stdio/vfscanf.c new file mode 100644 index 0000000..dcf498c --- /dev/null +++ b/src/libraries/libc/stdio/vfscanf.c @@ -0,0 +1,82 @@ +/* vfscanf( FILE *, const char *, va_list ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +int vfscanf( FILE * restrict stream, const char * restrict format, va_list arg ) +{ + /* TODO: This function should interpret format as multibyte characters. */ + struct _PDCLIB_status_t status; + status.base = 0; + status.flags = 0; + status.n = 0; + status.i = 0; + status.current = 0; + status.s = NULL; + status.width = 0; + status.prec = EOF; + status.stream = stream; + va_copy( status.arg, arg ); + + while ( *format != '\0' ) + { + const char * rc; + if ( ( *format != '%' ) || ( ( rc = _PDCLIB_scan( format, &status ) ) == format ) ) + { + int c; + /* No conversion specifier, match verbatim */ + if ( isspace( *format ) ) + { + /* Whitespace char in format string: Skip all whitespaces */ + /* No whitespaces in input does not result in matching error */ + while ( isspace( c = getc( stream ) ) ) + { + ++status.i; + } + if ( ! feof( stream ) ) + { + ungetc( c, stream ); + } + } + else + { + /* Non-whitespace char in format string: Match verbatim */ + if ( ( ( c = getc( stream ) ) != *format ) || feof( stream ) ) + { + /* Matching error */ + if ( ! feof( stream ) && ! ferror( stream ) ) + { + ungetc( c, stream ); + } + else if ( status.n == 0 ) + { + return EOF; + } + return status.n; + } + else + { + ++status.i; + } + } + ++format; + } + else + { + /* NULL return code indicates matching error */ + if ( rc == NULL ) + { + break; + } + /* Continue parsing after conversion specifier */ + format = rc; + } + } + va_end( status.arg ); + return status.n; +} diff --git a/src/libraries/libc/stdio/vprintf.c b/src/libraries/libc/stdio/vprintf.c new file mode 100644 index 0000000..395bec3 --- /dev/null +++ b/src/libraries/libc/stdio/vprintf.c @@ -0,0 +1,13 @@ +/* vprintf( const char *, va_list ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int vprintf( const char * restrict format, _PDCLIB_va_list arg ) +{ + return vfprintf( stdout, format, arg ); +} diff --git a/src/libraries/libc/stdio/vscanf.c b/src/libraries/libc/stdio/vscanf.c new file mode 100644 index 0000000..cfdb8fe --- /dev/null +++ b/src/libraries/libc/stdio/vscanf.c @@ -0,0 +1,13 @@ +/* vscanf( const char *, va_list ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int vscanf( const char * restrict format, _PDCLIB_va_list arg ) +{ + return vfscanf( stdin, format, arg ); +} diff --git a/src/libraries/libc/stdio/vsnprintf.c b/src/libraries/libc/stdio/vsnprintf.c new file mode 100644 index 0000000..e62da66 --- /dev/null +++ b/src/libraries/libc/stdio/vsnprintf.c @@ -0,0 +1,50 @@ +/* vsnprintf( char *, size_t, const char *, va_list ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int vsnprintf( char * restrict s, size_t n, const char * restrict format, _PDCLIB_va_list arg ) +{ + /* TODO: This function should interpret format as multibyte characters. */ + struct _PDCLIB_status_t status; + status.base = 0; + status.flags = 0; + status.n = n; + status.i = 0; + status.current = 0; + status.s = s; + status.width = 0; + status.prec = EOF; + status.stream = NULL; + va_copy( status.arg, arg ); + + while ( *format != '\0' ) + { + const char * rc; + if ( ( *format != '%' ) || ( ( rc = _PDCLIB_print( format, &status ) ) == format ) ) + { + /* No conversion specifier, print verbatim */ + if ( status.i < n ) + { + s[ status.i ] = *format; + } + status.i++; + format++; + } + else + { + /* Continue parsing after conversion specifier */ + format = rc; + } + } + if ( status.i < n ) + { + s[ status.i ] = '\0'; + } + va_end( status.arg ); + return status.i; +} diff --git a/src/libraries/libc/stdio/vsprintf.c b/src/libraries/libc/stdio/vsprintf.c new file mode 100644 index 0000000..57fba2e --- /dev/null +++ b/src/libraries/libc/stdio/vsprintf.c @@ -0,0 +1,14 @@ +/* vsprintf( char *, const char *, va_list ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +int vsprintf( char * restrict s, const char * restrict format, va_list arg ) +{ + return vsnprintf( s, SIZE_MAX, format, arg ); /* TODO: Replace with a non-checking call */ +} diff --git a/src/libraries/libc/stdio/vsscanf.c b/src/libraries/libc/stdio/vsscanf.c new file mode 100644 index 0000000..472ad0f --- /dev/null +++ b/src/libraries/libc/stdio/vsscanf.c @@ -0,0 +1,80 @@ +/* vsscanf( const char *, const char *, va_list ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +int vsscanf( const char * restrict s, const char * restrict format, va_list arg ) +{ + /* TODO: This function should interpret format as multibyte characters. */ + struct _PDCLIB_status_t status; + status.base = 0; + status.flags = 0; + status.n = 0; + status.i = 0; + status.current = 0; + status.s = (char *) s; + status.width = 0; + status.prec = EOF; + status.stream = NULL; + va_copy( status.arg, arg ); + + while ( *format != '\0' ) + { + const char * rc; + if ( ( *format != '%' ) || ( ( rc = _PDCLIB_scan( format, &status ) ) == format ) ) + { + /* No conversion specifier, match verbatim */ + if ( isspace( *format ) ) + { + /* Whitespace char in format string: Skip all whitespaces */ + /* No whitespaces in input do not result in matching error */ + while ( isspace( *status.s ) ) + { + ++status.s; + ++status.i; + } + } + else + { + /* Non-whitespace char in format string: Match verbatim */ + if ( *status.s != *format ) + { + if ( *status.s == '\0' && status.n == 0 ) + { + /* Early input error */ + return EOF; + } + /* Matching error */ + return status.n; + } + else + { + ++status.s; + ++status.i; + } + } + ++format; + } + else + { + /* NULL return code indicates error */ + if ( rc == NULL ) + { + if ( ( *status.s == '\n' ) && ( status.n == 0 ) ) + { + status.n = EOF; + } + break; + } + /* Continue parsing after conversion specifier */ + format = rc; + } + } + va_end( status.arg ); + return status.n; +} diff --git a/src/libraries/libc/stdlib/_Exit.c b/src/libraries/libc/stdlib/_Exit.c new file mode 100644 index 0000000..2112dab --- /dev/null +++ b/src/libraries/libc/stdlib/_Exit.c @@ -0,0 +1,17 @@ +/* _Exit( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include "j6libc/glue.h" + +void _Exit( int status ) +{ + /* TODO: Flush and close open streams. Remove tmpfile() files. Make this + called on process termination automatically. + */ + _PDCLIB_Exit( status ); +} diff --git a/src/libraries/libc/stdlib/abort.c b/src/libraries/libc/stdlib/abort.c new file mode 100644 index 0000000..beebd2f --- /dev/null +++ b/src/libraries/libc/stdlib/abort.c @@ -0,0 +1,14 @@ +/* abort( void ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +void abort( void ) +{ + raise( SIGABRT ); + exit( EXIT_FAILURE ); +} diff --git a/src/libraries/libc/stdlib/abs.c b/src/libraries/libc/stdlib/abs.c new file mode 100644 index 0000000..6ffdf36 --- /dev/null +++ b/src/libraries/libc/stdlib/abs.c @@ -0,0 +1,12 @@ +/* abs( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int abs( int j ) +{ + return ( j >= 0 ) ? j : -j; +} diff --git a/src/libraries/libc/stdlib/atexit.c b/src/libraries/libc/stdlib/atexit.c new file mode 100644 index 0000000..49d1301 --- /dev/null +++ b/src/libraries/libc/stdlib/atexit.c @@ -0,0 +1,23 @@ +/* atexit( void (*)( void ) ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +extern void (*_PDCLIB_exitstack[])( void ); +extern size_t _PDCLIB_exitptr; + +int atexit( void (*func)( void ) ) +{ + if ( _PDCLIB_exitptr == 0 ) + { + return -1; + } + else + { + _PDCLIB_exitstack[ --_PDCLIB_exitptr ] = func; + return 0; + } +} diff --git a/src/libraries/libc/stdlib/atoi.c b/src/libraries/libc/stdlib/atoi.c new file mode 100644 index 0000000..a3f71ac --- /dev/null +++ b/src/libraries/libc/stdlib/atoi.c @@ -0,0 +1,12 @@ +/* atoi( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int atoi( const char * s ) +{ + return (int) _PDCLIB_atomax( s ); +} diff --git a/src/libraries/libc/stdlib/atol.c b/src/libraries/libc/stdlib/atol.c new file mode 100644 index 0000000..7fb8e2e --- /dev/null +++ b/src/libraries/libc/stdlib/atol.c @@ -0,0 +1,12 @@ +/* atol( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +long int atol( const char * s ) +{ + return (long int) _PDCLIB_atomax( s ); +} diff --git a/src/libraries/libc/stdlib/atoll.c b/src/libraries/libc/stdlib/atoll.c new file mode 100644 index 0000000..874f966 --- /dev/null +++ b/src/libraries/libc/stdlib/atoll.c @@ -0,0 +1,12 @@ +/* atoll( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +long long int atoll( const char * s ) +{ + return (long long int) _PDCLIB_atomax( s ); +} diff --git a/src/libraries/libc/stdlib/bsearch.c b/src/libraries/libc/stdlib/bsearch.c new file mode 100644 index 0000000..daf66a2 --- /dev/null +++ b/src/libraries/libc/stdlib/bsearch.c @@ -0,0 +1,33 @@ +/* bsearch( const void *, const void *, size_t, size_t, int(*)( const void *, const void * ) ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +void * bsearch( const void * key, const void * base, size_t nmemb, size_t size, int (*compar)( const void *, const void * ) ) +{ + const void * pivot; + int rc; + size_t corr; + while ( nmemb ) + { + /* algorithm needs -1 correction if remaining elements are an even number. */ + corr = nmemb % 2; + nmemb /= 2; + pivot = (const char *)base + (nmemb * size); + rc = compar( key, pivot ); + if ( rc > 0 ) + { + base = (const char *)pivot + size; + /* applying correction */ + nmemb -= ( 1 - corr ); + } + else if ( rc == 0 ) + { + return (void *)pivot; + } + } + return NULL; +} diff --git a/src/libraries/libc/stdlib/div.c b/src/libraries/libc/stdlib/div.c new file mode 100644 index 0000000..508dc75 --- /dev/null +++ b/src/libraries/libc/stdlib/div.c @@ -0,0 +1,16 @@ +/* div( int, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +div_t div( int numer, int denom ) +{ + div_t rc; + rc.quot = numer / denom; + rc.rem = numer % denom; + /* TODO: pre-C99 compilers might require modulus corrections */ + return rc; +} diff --git a/src/libraries/libc/stdlib/exit.c b/src/libraries/libc/stdlib/exit.c new file mode 100644 index 0000000..fe8a50c --- /dev/null +++ b/src/libraries/libc/stdlib/exit.c @@ -0,0 +1,28 @@ +/* exit( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +/* TODO - "except that a function is called after any previously registered + functions that had already been called at the time it was registered. +*/ + +/* TODO: 32 is guaranteed. This should be dynamic but ATM gives problems + with my malloc. +*/ +#define NUMBER_OF_SLOTS 40 + +void (*_PDCLIB_exitstack[ NUMBER_OF_SLOTS ])( void ) = { _PDCLIB_closeall }; +size_t _PDCLIB_exitptr = NUMBER_OF_SLOTS; + +void exit( int status ) +{ + while ( _PDCLIB_exitptr < NUMBER_OF_SLOTS ) + { + _PDCLIB_exitstack[ _PDCLIB_exitptr++ ](); + } + _Exit( status ); +} diff --git a/src/libraries/libc/stdlib/getenv.c b/src/libraries/libc/stdlib/getenv.c new file mode 100644 index 0000000..c0471d6 --- /dev/null +++ b/src/libraries/libc/stdlib/getenv.c @@ -0,0 +1,27 @@ +/* getenv( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +char **environ __attribute__ ((weak)); + +extern char **environ; + +char * getenv( const char * name ) +{ + size_t len = strlen( name ); + size_t index = 0; + while ( environ[ index ] != NULL ) + { + if ( strncmp( environ[ index ], name, len ) == 0 ) + { + return environ[ index ] + len + 1; + } + index++; + } + return NULL; +} diff --git a/src/libraries/libc/stdlib/labs.c b/src/libraries/libc/stdlib/labs.c new file mode 100644 index 0000000..151ed3a --- /dev/null +++ b/src/libraries/libc/stdlib/labs.c @@ -0,0 +1,12 @@ +/* labs( long int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +long int labs( long int j ) +{ + return ( j >= 0 ) ? j : -j; +} diff --git a/src/libraries/libc/stdlib/ldiv.c b/src/libraries/libc/stdlib/ldiv.c new file mode 100644 index 0000000..86c9c0e --- /dev/null +++ b/src/libraries/libc/stdlib/ldiv.c @@ -0,0 +1,16 @@ +/* ldiv( long int, long int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +ldiv_t ldiv( long int numer, long int denom ) +{ + ldiv_t rc; + rc.quot = numer / denom; + rc.rem = numer % denom; + /* TODO: pre-C99 compilers might require modulus corrections */ + return rc; +} diff --git a/src/libraries/libc/stdlib/llabs.c b/src/libraries/libc/stdlib/llabs.c new file mode 100644 index 0000000..4231e91 --- /dev/null +++ b/src/libraries/libc/stdlib/llabs.c @@ -0,0 +1,12 @@ +/* llabs( long int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +long long int llabs( long long int j ) +{ + return ( j >= 0 ) ? j : -j; +} diff --git a/src/libraries/libc/stdlib/lldiv.c b/src/libraries/libc/stdlib/lldiv.c new file mode 100644 index 0000000..dd1c2b5 --- /dev/null +++ b/src/libraries/libc/stdlib/lldiv.c @@ -0,0 +1,16 @@ +/* lldiv( long long int, long long int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +lldiv_t lldiv( long long int numer, long long int denom ) +{ + lldiv_t rc; + rc.quot = numer / denom; + rc.rem = numer % denom; + /* TODO: pre-C99 compilers might require modulus corrections */ + return rc; +} diff --git a/src/libraries/libc/stdlib/malloc.c b/src/libraries/libc/stdlib/malloc.c new file mode 100644 index 0000000..8307634 --- /dev/null +++ b/src/libraries/libc/stdlib/malloc.c @@ -0,0 +1,6270 @@ +/* + This is a version (aka dlmalloc) of malloc/free/realloc written by + Doug Lea and released to the public domain, as explained at + http://creativecommons.org/publicdomain/zero/1.0/ Send questions, + comments, complaints, performance data, etc to dl@cs.oswego.edu + +* Version 2.8.5 Sun May 22 10:26:02 2011 Doug Lea (dl at gee) + + Note: There may be an updated version of this malloc obtainable at + ftp://gee.cs.oswego.edu/pub/misc/malloc.c + Check before installing! + +* Quickstart + + This library is all in one file to simplify the most common usage: + ftp it, compile it (-O3), and link it into another program. All of + the compile-time options default to reasonable values for use on + most platforms. You might later want to step through various + compile-time and dynamic tuning options. + + For convenience, an include file for code using this malloc is at: + ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.5.h + You don't really need this .h file unless you call functions not + defined in your system include files. The .h file contains only the + excerpts from this file needed for using this malloc on ANSI C/C++ + systems, so long as you haven't changed compile-time options about + naming and tuning parameters. If you do, then you can create your + own malloc.h that does include all settings by cutting at the point + indicated below. Note that you may already by default be using a C + library containing a malloc that is based on some version of this + malloc (for example in linux). You might still want to use the one + in this file to customize settings or to avoid overheads associated + with library versions. + +* Vital statistics: + + Supported pointer/size_t representation: 4 or 8 bytes + size_t MUST be an unsigned type of the same width as + pointers. (If you are using an ancient system that declares + size_t as a signed type, or need it to be a different width + than pointers, you can use a previous release of this malloc + (e.g. 2.7.2) supporting these.) + + Alignment: 8 bytes (default) + This suffices for nearly all current machines and C compilers. + However, you can define MALLOC_ALIGNMENT to be wider than this + if necessary (up to 128bytes), at the expense of using more space. + + Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) + 8 or 16 bytes (if 8byte sizes) + Each malloced chunk has a hidden word of overhead holding size + and status information, and additional cross-check word + if FOOTERS is defined. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) + 8-byte ptrs: 32 bytes (including overhead) + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + The maximum overhead wastage (i.e., number of extra bytes + allocated than were requested in malloc) is less than or equal + to the minimum size, except for requests >= mmap_threshold that + are serviced via mmap(), where the worst case wastage is about + 32 bytes plus the remainder from a system page (the minimal + mmap unit); typically 4096 or 8192 bytes. + + Security: static-safe; optionally more or less + The "security" of malloc refers to the ability of malicious + code to accentuate the effects of errors (for example, freeing + space that is not currently malloc'ed or overwriting past the + ends of chunks) in code that calls malloc. This malloc + guarantees not to modify any memory locations below the base of + heap, i.e., static variables, even in the presence of usage + errors. The routines additionally detect most improper frees + and reallocs. All this holds as long as the static bookkeeping + for malloc itself is not corrupted by some other means. This + is only one aspect of security -- these checks do not, and + cannot, detect all possible programming errors. + + If FOOTERS is defined nonzero, then each allocated chunk + carries an additional check word to verify that it was malloced + from its space. These check words are the same within each + execution of a program using malloc, but differ across + executions, so externally crafted fake chunks cannot be + freed. This improves security by rejecting frees/reallocs that + could corrupt heap memory, in addition to the checks preventing + writes to statics that are always on. This may further improve + security at the expense of time and space overhead. (Note that + FOOTERS may also be worth using with MSPACES.) + + By default detected errors cause the program to abort (calling + "abort()"). You can override this to instead proceed past + errors by defining PROCEED_ON_ERROR. In this case, a bad free + has no effect, and a malloc that encounters a bad address + caused by user overwrites will ignore the bad address by + dropping pointers and indices to all known memory. This may + be appropriate for programs that should continue if at all + possible in the face of programming errors, although they may + run out of memory because dropped memory is never reclaimed. + + If you don't like either of these options, you can define + CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything + else. And if if you are sure that your program using malloc has + no errors or vulnerabilities, you can define INSECURE to 1, + which might (or might not) provide a small performance improvement. + + It is also possible to limit the maximum total allocatable + space, using malloc_set_footprint_limit. This is not + designed as a security feature in itself (calls to set limits + are not screened or privileged), but may be useful as one + aspect of a secure implementation. + + Thread-safety: NOT thread-safe unless USE_LOCKS defined non-zero + When USE_LOCKS is defined, each public call to malloc, free, + etc is surrounded with a lock. By default, this uses a plain + pthread mutex, win32 critical section, or a spin-lock if if + available for the platform and not disabled by setting + USE_SPIN_LOCKS=0. However, if USE_RECURSIVE_LOCKS is defined, + recursive versions are used instead (which are not required for + base functionality but may be needed in layered extensions). + Using a global lock is not especially fast, and can be a major + bottleneck. It is designed only to provide minimal protection + in concurrent environments, and to provide a basis for + extensions. If you are using malloc in a concurrent program, + consider instead using nedmalloc + (http://www.nedprod.com/programs/portable/nedmalloc/) or + ptmalloc (See http://www.malloc.de), which are derived from + versions of this malloc. + + System requirements: Any combination of MORECORE and/or MMAP/MUNMAP + This malloc can use unix sbrk or any emulation (invoked using + the CALL_MORECORE macro) and/or mmap/munmap or any emulation + (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system + memory. On most unix systems, it tends to work best if both + MORECORE and MMAP are enabled. On Win32, it uses emulations + based on VirtualAlloc. It also uses common C library functions + like memset. + + Compliance: I believe it is compliant with the Single Unix Specification + (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably + others as well. + +* Overview of algorithms + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and + tunable. Consistent balance across these factors results in a good + general-purpose allocator for malloc-intensive programs. + + In most ways, this malloc is a best-fit allocator. Generally, it + chooses the best-fitting existing chunk for a request, with ties + broken in approximately least-recently-used order. (This strategy + normally maintains low fragmentation.) However, for requests less + than 256bytes, it deviates from best-fit when there is not an + exactly fitting available chunk by preferring to use space adjacent + to that used for the previous small request, as well as by breaking + ties in approximately most-recently-used order. (These enhance + locality of series of small allocations.) And for very large requests + (>= 256Kb by default), it relies on system memory mapping + facilities, if supported. (This helps avoid carrying around and + possibly fragmenting memory used only for large chunks.) + + All operations (except malloc_stats and mallinfo) have execution + times that are bounded by a constant factor of the number of bits in + a size_t, not counting any clearing in calloc or copying in realloc, + or actions surrounding MORECORE and MMAP that have times + proportional to the number of non-contiguous regions returned by + system allocation routines, which is often just 1. In real-time + applications, you can optionally suppress segment traversals using + NO_SEGMENT_TRAVERSAL, which assures bounded execution even when + system allocators return non-contiguous spaces, at the typical + expense of carrying around more memory and increased fragmentation. + + The implementation is not very modular and seriously overuses + macros. Perhaps someday all C compilers will do as good a job + inlining modular code as can now be done by brute-force expansion, + but now, enough of them seem not to. + + Some compilers issue a lot of warnings about code that is + dead/unreachable only on some platforms, and also about intentional + uses of negation on unsigned types. All known cases of each can be + ignored. + + For a longer but out of date high-level description, see + http://gee.cs.oswego.edu/dl/html/malloc.html + +* MSPACES + If MSPACES is defined, then in addition to malloc, free, etc., + this file also defines mspace_malloc, mspace_free, etc. These + are versions of malloc routines that take an "mspace" argument + obtained using create_mspace, to control all internal bookkeeping. + If ONLY_MSPACES is defined, only these versions are compiled. + So if you would like to use this allocator for only some allocations, + and your system malloc for others, you can compile with + ONLY_MSPACES and then do something like... + static mspace mymspace = create_mspace(0,0); // for example + #define mymalloc(bytes) mspace_malloc(mymspace, bytes) + + (Note: If you only need one instance of an mspace, you can instead + use "USE_DL_PREFIX" to relabel the global malloc.) + + You can similarly create thread-local allocators by storing + mspaces as thread-locals. For example: + static __thread mspace tlms = 0; + void* tlmalloc(size_t bytes) { + if (tlms == 0) tlms = create_mspace(0, 0); + return mspace_malloc(tlms, bytes); + } + void tlfree(void* mem) { mspace_free(tlms, mem); } + + Unless FOOTERS is defined, each mspace is completely independent. + You cannot allocate from one and free to another (although + conformance is only weakly checked, so usage errors are not always + caught). If FOOTERS is defined, then each chunk carries around a tag + indicating its originating mspace, and frees are directed to their + originating spaces. Normally, this requires use of locks. + + ------------------------- Compile-time options --------------------------- + +Be careful in setting #define values for numerical constants of type +size_t. On some systems, literal values are not automatically extended +to size_t precision unless they are explicitly casted. You can also +use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. + +WIN32 default: defined if _WIN32 defined + Defining WIN32 sets up defaults for MS environment and compilers. + Otherwise defaults are for unix. Beware that there seem to be some + cases where this malloc might not be a pure drop-in replacement for + Win32 malloc: Random-looking failures from Win32 GDI API's (eg; + SetDIBits()) may be due to bugs in some video driver implementations + when pixel buffers are malloc()ed, and the region spans more than + one VirtualAlloc()ed region. Because dlmalloc uses a small (64Kb) + default granularity, pixel buffers may straddle virtual allocation + regions more often than when using the Microsoft allocator. You can + avoid this by using VirtualAlloc() and VirtualFree() for all pixel + buffers rather than using malloc(). If this is not possible, + recompile this malloc with a larger DEFAULT_GRANULARITY. Note: + in cases where MSC and gcc (cygwin) are known to differ on WIN32, + conditions use _MSC_VER to distinguish them. + +DLMALLOC_EXPORT default: extern + Defines how public APIs are declared. If you want to export via a + Windows DLL, you might define this as + #define DLMALLOC_EXPORT extern __declspace(dllexport) + If you want a POSIX ELF shared object, you might use + #define DLMALLOC_EXPORT extern __attribute__((visibility("default"))) + +MALLOC_ALIGNMENT default: (size_t)8 + Controls the minimum alignment for malloc'ed chunks. It must be a + power of two and at least 8, even on machines for which smaller + alignments would suffice. It may be defined as larger than this + though. Note however that code and data structures are optimized for + the case of 8-byte alignment. + +MSPACES default: 0 (false) + If true, compile in support for independent allocation spaces. + This is only supported if HAVE_MMAP is true. + +ONLY_MSPACES default: 0 (false) + If true, only compile in mspace versions, not regular versions. + +USE_LOCKS default: 0 (false) + Causes each call to each public routine to be surrounded with + pthread or WIN32 mutex lock/unlock. (If set true, this can be + overridden on a per-mspace basis for mspace versions.) If set to a + non-zero value other than 1, locks are used, but their + implementation is left out, so lock functions must be supplied manually, + as described below. + +USE_SPIN_LOCKS default: 1 iff USE_LOCKS and spin locks available + If true, uses custom spin locks for locking. This is currently + supported only gcc >= 4.1, older gccs on x86 platforms, and recent + MS compilers. Otherwise, posix locks or win32 critical sections are + used. + +USE_RECURSIVE_LOCKS default: not defined + If defined nonzero, uses recursive (aka reentrant) locks, otherwise + uses plain mutexes. This is not required for malloc proper, but may + be needed for layered allocators such as nedmalloc. + +FOOTERS default: 0 + If true, provide extra checking and dispatching by placing + information in the footers of allocated chunks. This adds + space and time overhead. + +INSECURE default: 0 + If true, omit checks for usage errors and heap space overwrites. + +USE_DL_PREFIX default: NOT defined + Causes compiler to prefix all public routines with the string 'dl'. + This can be useful when you only want to use this malloc in one part + of a program, using your regular system malloc elsewhere. + +MALLOC_INSPECT_ALL default: NOT defined + If defined, compiles malloc_inspect_all and mspace_inspect_all, that + perform traversal of all heap space. Unless access to these + functions is otherwise restricted, you probably do not want to + include them in secure implementations. + +ABORT default: defined as abort() + Defines how to abort on failed checks. On most systems, a failed + check cannot die with an "assert" or even print an informative + message, because the underlying print routines in turn call malloc, + which will fail again. Generally, the best policy is to simply call + abort(). It's not very useful to do more than this because many + errors due to overwriting will show up as address faults (null, odd + addresses etc) rather than malloc-triggered checks, so will also + abort. Also, most compilers know that abort() does not return, so + can better optimize code conditionally calling it. + +PROCEED_ON_ERROR default: defined as 0 (false) + Controls whether detected bad addresses cause them to bypassed + rather than aborting. If set, detected bad arguments to free and + realloc are ignored. And all bookkeeping information is zeroed out + upon a detected overwrite of freed heap space, thus losing the + ability to ever return it from malloc again, but enabling the + application to proceed. If PROCEED_ON_ERROR is defined, the + static variable malloc_corruption_error_count is compiled in + and can be examined to see if errors have occurred. This option + generates slower code than the default abort policy. + +DEBUG default: NOT defined + The DEBUG setting is mainly intended for people trying to modify + this code or diagnose problems when porting to new platforms. + However, it may also be able to better isolate user errors than just + using runtime checks. The assertions in the check routines spell + out in more detail the assumptions and invariants underlying the + algorithms. The checking is fairly extensive, and will slow down + execution noticeably. Calling malloc_stats or mallinfo with DEBUG + set will attempt to check every non-mmapped allocated and free chunk + in the course of computing the summaries. + +ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) + Debugging assertion failures can be nearly impossible if your + version of the assert macro causes malloc to be called, which will + lead to a cascade of further failures, blowing the runtime stack. + ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), + which will usually make debugging easier. + +MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 + The action to take before "return 0" when malloc fails to be able to + return memory because there is none available. + +HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES + True if this system supports sbrk or an emulation of it. + +MORECORE default: sbrk + The name of the sbrk-style system routine to call to obtain more + memory. See below for guidance on writing custom MORECORE + functions. The type of the argument to sbrk/MORECORE varies across + systems. It cannot be size_t, because it supports negative + arguments, so it is normally the signed type of the same width as + size_t (sometimes declared as "intptr_t"). It doesn't much matter + though. Internally, we only call it with arguments less than half + the max value of a size_t, which should work across all reasonable + possibilities, although sometimes generating compiler warnings. + +MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE + If true, take advantage of fact that consecutive calls to MORECORE + with positive arguments always return contiguous increasing + addresses. This is true of unix sbrk. It does not hurt too much to + set it true anyway, since malloc copes with non-contiguities. + Setting it false when definitely non-contiguous saves time + and possibly wasted space it would take to discover this though. + +MORECORE_CANNOT_TRIM default: NOT defined + True if MORECORE cannot release space back to the system when given + negative arguments. This is generally necessary only if you are + using a hand-crafted MORECORE function that cannot handle negative + arguments. + +NO_SEGMENT_TRAVERSAL default: 0 + If non-zero, suppresses traversals of memory segments + returned by either MORECORE or CALL_MMAP. This disables + merging of segments that are contiguous, and selectively + releasing them to the OS if unused, but bounds execution times. + +HAVE_MMAP default: 1 (true) + True if this system supports mmap or an emulation of it. If so, and + HAVE_MORECORE is not true, MMAP is used for all system + allocation. If set and HAVE_MORECORE is true as well, MMAP is + primarily used to directly allocate very large blocks. It is also + used as a backup strategy in cases where MORECORE fails to provide + space from system. Note: A single call to MUNMAP is assumed to be + able to unmap memory that may have be allocated using multiple calls + to MMAP, so long as they are adjacent. + +HAVE_MREMAP default: 1 on linux, else 0 + If true realloc() uses mremap() to re-allocate large blocks and + extend or shrink allocation spaces. + +MMAP_CLEARS default: 1 except on WINCE. + True if mmap clears memory so calloc doesn't need to. This is true + for standard unix mmap using /dev/zero and on WIN32 except for WINCE. + +USE_BUILTIN_FFS default: 0 (i.e., not used) + Causes malloc to use the builtin ffs() function to compute indices. + Some compilers may recognize and intrinsify ffs to be faster than the + supplied C version. Also, the case of x86 using gcc is special-cased + to an asm instruction, so is already as fast as it can be, and so + this setting has no effect. Similarly for Win32 under recent MS compilers. + (On most x86s, the asm version is only slightly faster than the C version.) + +malloc_getpagesize default: derive from system includes, or 4096. + The system page size. To the extent possible, this malloc manages + memory from the system in page-size units. This may be (and + usually is) a function rather than a constant. This is ignored + if WIN32, where page size is determined using getSystemInfo during + initialization. + +USE_DEV_RANDOM default: 0 (i.e., not used) + Causes malloc to use /dev/random to initialize secure magic seed for + stamping footers. Otherwise, the current time is used. + +NO_MALLINFO default: 0 + If defined, don't compile "mallinfo". This can be a simple way + of dealing with mismatches between system declarations and + those in this file. + +MALLINFO_FIELD_TYPE default: size_t + The type of the fields in the mallinfo struct. This was originally + defined as "int" in SVID etc, but is more usefully defined as + size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set + +NO_MALLOC_STATS default: 0 + If defined, don't compile "malloc_stats". This avoids calls to + fprintf and bringing in stdio dependencies you might not want. + +REALLOC_ZERO_BYTES_FREES default: not defined + This should be set if a call to realloc with zero bytes should + be the same as a call to free. Some people think it should. Otherwise, + since this malloc returns a unique pointer for malloc(0), so does + realloc(p, 0). + +LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H +LACKS_STRINGS_H, LACKS_STRING_H, LACKS_ERRNO_H LACKS_STDLIB_H LACKS_SCHED_H +LACKS_TIME_H default: NOT defined unless on WIN32 + Define these if your system does not have these header files. + You might need to manually insert some of the declarations they provide. + +DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, + system_info.dwAllocationGranularity in WIN32, + otherwise 64K. + Also settable using mallopt(M_GRANULARITY, x) + The unit for allocating and deallocating memory from the system. On + most systems with contiguous MORECORE, there is no reason to + make this more than a page. However, systems with MMAP tend to + either require or encourage larger granularities. You can increase + this value to prevent system allocation functions to be called so + often, especially if they are slow. The value must be at least one + page and must be a power of two. Setting to 0 causes initialization + to either page size or win32 region size. (Note: In previous + versions of malloc, the equivalent of this option was called + "TOP_PAD") + +DEFAULT_TRIM_THRESHOLD default: 2MB + Also settable using mallopt(M_TRIM_THRESHOLD, x) + The maximum amount of unused top-most memory to keep before + releasing via malloc_trim in free(). Automatic trimming is mainly + useful in long-lived programs using contiguous MORECORE. Because + trimming via sbrk can be slow on some systems, and can sometimes be + wasteful (in cases where programs immediately afterward allocate + more large chunks) the value should be high enough so that your + overall system performance would improve by releasing this much + memory. As a rough guide, you might set to a value close to the + average size of a process (program) running on your system. + Releasing this much memory would allow such a process to run in + memory. Generally, it is worth tuning trim thresholds when a + program undergoes phases where several large chunks are allocated + and released in ways that can reuse each other's storage, perhaps + mixed with phases where there are no such chunks at all. The trim + value must be greater than page size to have any useful effect. To + disable trimming completely, you can set to MAX_SIZE_T. Note that the trick + some people use of mallocing a huge space and then freeing it at + program startup, in an attempt to reserve system memory, doesn't + have the intended effect under automatic trimming, since that memory + will immediately be returned to the system. + +DEFAULT_MMAP_THRESHOLD default: 256K + Also settable using mallopt(M_MMAP_THRESHOLD, x) + The request size threshold for using MMAP to directly service a + request. Requests of at least this size that cannot be allocated + using already-existing space will be serviced via mmap. (If enough + normal freed space already exists it is used instead.) Using mmap + segregates relatively large chunks of memory so that they can be + individually obtained and released from the host system. A request + serviced through mmap is never reused by any other request (at least + not directly; the system may just so happen to remap successive + requests to the same locations). Segregating space in this way has + the benefits that: Mmapped space can always be individually released + back to the system, which helps keep the system level memory demands + of a long-lived program low. Also, mapped memory doesn't become + `locked' between other chunks, as can happen with normally allocated + chunks, which means that even trimming via malloc_trim would not + release them. However, it has the disadvantage that the space + cannot be reclaimed, consolidated, and then used to service later + requests, as happens with normal chunks. The advantages of mmap + nearly always outweigh disadvantages for "large" chunks, but the + value of "large" may vary across systems. The default is an + empirically derived value that works well in most systems. You can + disable mmap by setting to MAX_SIZE_T. + +MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP + The number of consolidated frees between checks to release + unused segments when freeing. When using non-contiguous segments, + especially with multiple mspaces, checking only for topmost space + doesn't always suffice to trigger trimming. To compensate for this, + free() will, with a period of MAX_RELEASE_CHECK_RATE (or the + current number of segments, if greater) try to release unused + segments to the OS when freeing chunks that result in + consolidation. The best value for this parameter is a compromise + between slowing down frees with relatively costly checks that + rarely trigger versus holding on to unused memory. To effectively + disable, set to MAX_SIZE_T. This may lead to a very slight speed + improvement at the expense of carrying around more memory. +*/ + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-align" + +/* Version identifier to allow people to support multiple versions */ +#ifndef DLMALLOC_VERSION +#define DLMALLOC_VERSION 20805 +#endif /* DLMALLOC_VERSION */ + +#ifndef DLMALLOC_EXPORT +#define DLMALLOC_EXPORT extern +#endif + +#ifndef WIN32 +#ifdef _WIN32 +#define WIN32 1 +#endif /* _WIN32 */ +#ifdef _WIN32_WCE +#define LACKS_FCNTL_H +#define WIN32 1 +#endif /* _WIN32_WCE */ +#endif /* WIN32 */ +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#define HAVE_MMAP 1 +#define HAVE_MORECORE 0 +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H +#define LACKS_SYS_MMAN_H +#define LACKS_STRING_H +#define LACKS_STRINGS_H +#define LACKS_ERRNO_H +#define LACKS_SCHED_H +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef MMAP_CLEARS +#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ +#define MMAP_CLEARS 0 +#else +#define MMAP_CLEARS 1 +#endif /* _WIN32_WCE */ +#endif /*MMAP_CLEARS */ +#endif /* WIN32 */ + +#if defined(DARWIN) || defined(_DARWIN) +/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ +#ifndef HAVE_MORECORE +#define HAVE_MORECORE 0 +#define HAVE_MMAP 1 +/* OSX allocators provide 16 byte alignment */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)16U) +#endif +#endif /* HAVE_MORECORE */ +#endif /* DARWIN */ + +#include /* For size_t */ +#include /* For uintptr_t */ + +/* The maximum possible size_t value has all bits set */ +#define MAX_SIZE_T (~(size_t)0) + +#ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ +#if ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ + (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) +#define USE_LOCKS 1 +#else +#define USE_LOCKS 0 +#endif +#endif /* USE_LOCKS */ + +#if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ +#if ((defined(__GNUC__) && \ + ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ + defined(__i386__) || defined(__x86_64__))) || \ + (defined(_MSC_VER) && _MSC_VER>=1310)) +#ifndef USE_SPIN_LOCKS +#define USE_SPIN_LOCKS 1 +#endif /* USE_SPIN_LOCKS */ +#elif USE_SPIN_LOCKS +#error "USE_SPIN_LOCKS defined without implementation" +#endif /* ... locks available... */ +#elif !defined(USE_SPIN_LOCKS) +#define USE_SPIN_LOCKS 0 +#endif /* USE_LOCKS */ + +#ifndef ONLY_MSPACES +#define ONLY_MSPACES 0 +#endif /* ONLY_MSPACES */ +#ifndef MSPACES +#if ONLY_MSPACES +#define MSPACES 1 +#else /* ONLY_MSPACES */ +#define MSPACES 0 +#endif /* ONLY_MSPACES */ +#endif /* MSPACES */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)8U) +#endif /* MALLOC_ALIGNMENT */ +#ifndef FOOTERS +#define FOOTERS 0 +#endif /* FOOTERS */ +#ifndef ABORT +#define ABORT abort() +#endif /* ABORT */ +#ifndef ABORT_ON_ASSERT_FAILURE +#define ABORT_ON_ASSERT_FAILURE 1 +#endif /* ABORT_ON_ASSERT_FAILURE */ +#ifndef PROCEED_ON_ERROR +#define PROCEED_ON_ERROR 0 +#endif /* PROCEED_ON_ERROR */ + +#ifndef INSECURE +#define INSECURE 0 +#endif /* INSECURE */ +#ifndef MALLOC_INSPECT_ALL +#define MALLOC_INSPECT_ALL 0 +#endif /* MALLOC_INSPECT_ALL */ +#ifndef HAVE_MMAP +#define HAVE_MMAP 1 +#endif /* HAVE_MMAP */ +#ifndef MMAP_CLEARS +#define MMAP_CLEARS 1 +#endif /* MMAP_CLEARS */ +#ifndef HAVE_MREMAP +#ifdef linux +#define HAVE_MREMAP 1 +#define _GNU_SOURCE /* Turns on mremap() definition */ +#else /* linux */ +#define HAVE_MREMAP 0 +#endif /* linux */ +#endif /* HAVE_MREMAP */ +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION errno = ENOMEM; +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef HAVE_MORECORE +#if ONLY_MSPACES +#define HAVE_MORECORE 0 +#else /* ONLY_MSPACES */ +#define HAVE_MORECORE 1 +#endif /* ONLY_MSPACES */ +#endif /* HAVE_MORECORE */ +#if !HAVE_MORECORE +#define MORECORE_CONTIGUOUS 0 +#else /* !HAVE_MORECORE */ +#define MORECORE_DEFAULT sbrk +#ifndef MORECORE_CONTIGUOUS +#define MORECORE_CONTIGUOUS 1 +#endif /* MORECORE_CONTIGUOUS */ +#endif /* HAVE_MORECORE */ +#ifndef DEFAULT_GRANULARITY +#if (MORECORE_CONTIGUOUS || defined(WIN32)) +#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ +#else /* MORECORE_CONTIGUOUS */ +#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) +#endif /* MORECORE_CONTIGUOUS */ +#endif /* DEFAULT_GRANULARITY */ +#ifndef DEFAULT_TRIM_THRESHOLD +#ifndef MORECORE_CANNOT_TRIM +#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) +#else /* MORECORE_CANNOT_TRIM */ +#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T +#endif /* MORECORE_CANNOT_TRIM */ +#endif /* DEFAULT_TRIM_THRESHOLD */ +#ifndef DEFAULT_MMAP_THRESHOLD +#if HAVE_MMAP +#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) +#else /* HAVE_MMAP */ +#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* DEFAULT_MMAP_THRESHOLD */ +#ifndef MAX_RELEASE_CHECK_RATE +#if HAVE_MMAP +#define MAX_RELEASE_CHECK_RATE 4095 +#else +#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* MAX_RELEASE_CHECK_RATE */ +#ifndef USE_BUILTIN_FFS +#define USE_BUILTIN_FFS 0 +#endif /* USE_BUILTIN_FFS */ +#ifndef USE_DEV_RANDOM +#define USE_DEV_RANDOM 0 +#endif /* USE_DEV_RANDOM */ +#ifndef NO_MALLINFO +#define NO_MALLINFO 0 +#endif /* NO_MALLINFO */ +#ifndef MALLINFO_FIELD_TYPE +#define MALLINFO_FIELD_TYPE size_t +#endif /* MALLINFO_FIELD_TYPE */ +#ifndef NO_MALLOC_STATS +#define NO_MALLOC_STATS 0 +#endif /* NO_MALLOC_STATS */ +#ifndef NO_SEGMENT_TRAVERSAL +#define NO_SEGMENT_TRAVERSAL 0 +#endif /* NO_SEGMENT_TRAVERSAL */ + +/* + mallopt tuning options. SVID/XPG defines four standard parameter + numbers for mallopt, normally defined in malloc.h. None of these + are used in this malloc, so setting them has no effect. But this + malloc does support the following options. +*/ + +#define M_TRIM_THRESHOLD (-1) +#define M_GRANULARITY (-2) +#define M_MMAP_THRESHOLD (-3) + +/* ------------------------ Mallinfo declarations ------------------------ */ + +#if !NO_MALLINFO +/* + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing usage properties and + statistics. It should work on any system that has a + /usr/include/malloc.h defining struct mallinfo. The main + declaration needed is the mallinfo struct that is returned (by-copy) + by mallinfo(). The malloinfo struct contains a bunch of fields that + are not even meaningful in this version of malloc. These fields are + are instead filled by mallinfo() with other numbers that might be of + interest. + + HAVE_USR_INCLUDE_MALLOC_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else a compliant version is + declared below. These must be precisely the same for mallinfo() to + work. The original SVID version of this struct, defined on most + systems with mallinfo, declares all fields as ints. But some others + define as unsigned long. If your system defines the fields using a + type of different width than listed here, you MUST #include your + system version and #define HAVE_USR_INCLUDE_MALLOC_H. +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + +#ifdef HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else /* HAVE_USR_INCLUDE_MALLOC_H */ +#ifndef STRUCT_MALLINFO_DECLARED +/* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is defined */ +#define _STRUCT_MALLINFO +#define STRUCT_MALLINFO_DECLARED 1 +struct mallinfo { + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ +}; +#endif /* STRUCT_MALLINFO_DECLARED */ +#endif /* HAVE_USR_INCLUDE_MALLOC_H */ +#endif /* NO_MALLINFO */ + +/* + Try to persuade compilers to inline. The most critical functions for + inlining are defined as macros, so these aren't used for them. +*/ + +#ifndef FORCEINLINE + #if defined(__GNUC__) +#define FORCEINLINE __inline __attribute__ ((always_inline)) + #elif defined(_MSC_VER) + #define FORCEINLINE __forceinline + #endif +#endif +#ifndef NOINLINE + #if defined(__GNUC__) + #define NOINLINE __attribute__ ((noinline)) + #elif defined(_MSC_VER) + #define NOINLINE __declspec(noinline) + #else + #define NOINLINE + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#ifndef FORCEINLINE + #define FORCEINLINE inline +#endif +#endif /* __cplusplus */ +#ifndef FORCEINLINE + #define FORCEINLINE +#endif + +#if !ONLY_MSPACES + +/* ------------------- Declarations of public routines ------------------- */ + +#ifndef USE_DL_PREFIX +#define dlcalloc calloc +#define dlfree free +#define dlmalloc malloc +#define dlmemalign memalign +#define dlposix_memalign posix_memalign +#define dlrealloc realloc +#define dlrealloc_in_place realloc_in_place +#define dlvalloc valloc +#define dlpvalloc pvalloc +#define dlmallinfo mallinfo +#define dlmallopt mallopt +#define dlmalloc_trim malloc_trim +#define dlmalloc_stats malloc_stats +#define dlmalloc_usable_size malloc_usable_size +#define dlmalloc_footprint malloc_footprint +#define dlmalloc_max_footprint malloc_max_footprint +#define dlmalloc_footprint_limit malloc_footprint_limit +#define dlmalloc_set_footprint_limit malloc_set_footprint_limit +#define dlmalloc_inspect_all malloc_inspect_all +#define dlindependent_calloc independent_calloc +#define dlindependent_comalloc independent_comalloc +#define dlbulk_free bulk_free +#endif /* USE_DL_PREFIX */ + +/* + malloc(size_t n) + Returns a pointer to a newly allocated chunk of at least n bytes, or + null if no space is available, in which case errno is set to ENOMEM + on ANSI C systems. + + If n is zero, malloc returns a minimum-sized chunk. (The minimum + size is 16 bytes on most 32bit systems, and 32 bytes on 64bit + systems.) Note that size_t is an unsigned type, so calls with + arguments that would be negative if signed are interpreted as + requests for huge amounts of space, which will often fail. The + maximum supported value of n differs across systems, but is in all + cases less than the maximum representable value of a size_t. +*/ +DLMALLOC_EXPORT void* dlmalloc(size_t); + +/* + free(void* p) + Releases the chunk of memory pointed to by p, that had been previously + allocated using malloc or a related routine such as realloc. + It has no effect if p is null. If p was not malloced or already + freed, free(p) will by default cause the current program to abort. +*/ +DLMALLOC_EXPORT void dlfree(void*); + +/* + calloc(size_t n_elements, size_t element_size); + Returns a pointer to n_elements * element_size bytes, with all locations + set to zero. +*/ +DLMALLOC_EXPORT void* dlcalloc(size_t, size_t); + +/* + realloc(void* p, size_t n) + Returns a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. + + The returned pointer may or may not be the same as p. The algorithm + prefers extending p in most cases when possible, otherwise it + employs the equivalent of a malloc-copy-free sequence. + + If p is null, realloc is equivalent to malloc. + + If space is not available, realloc returns null, errno is set (if on + ANSI) and p is NOT freed. + + if n is for fewer bytes than already held by p, the newly unused + space is lopped off and freed if possible. realloc with a size + argument of zero (re)allocates a minimum-sized chunk. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is not supported. +*/ +DLMALLOC_EXPORT void* dlrealloc(void*, size_t); + +/* + realloc_in_place(void* p, size_t n) + Resizes the space allocated for p to size n, only if this can be + done without moving p (i.e., only if there is adjacent space + available if n is greater than p's current allocated size, or n is + less than or equal to p's size). This may be used instead of plain + realloc if an alternative allocation strategy is needed upon failure + to expand space; for example, reallocation of a buffer that must be + memory-aligned or cleared. You can use realloc_in_place to trigger + these alternatives only when needed. + + Returns p if successful; otherwise null. +*/ +DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t); + +/* + memalign(size_t alignment, size_t n); + Returns a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument. + + The alignment argument should be a power of two. If the argument is + not a power of two, the nearest greater power is used. + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. +*/ +DLMALLOC_EXPORT void* dlmemalign(size_t, size_t); + +/* + int posix_memalign(void** pp, size_t alignment, size_t n); + Allocates a chunk of n bytes, aligned in accord with the alignment + argument. Differs from memalign only in that it (1) assigns the + allocated memory to *pp rather than returning it, (2) fails and + returns EINVAL if the alignment is not a power of two (3) fails and + returns ENOMEM if memory cannot be allocated. +*/ +DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t); + +/* + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system. If the pagesize is unknown, 4096 is used. +*/ +DLMALLOC_EXPORT void* dlvalloc(size_t); + +/* + mallopt(int parameter_number, int parameter_value) + Sets tunable parameters The format is to provide a + (parameter-number, parameter-value) pair. mallopt then sets the + corresponding parameter to the argument value if it can (i.e., so + long as the value is meaningful), and returns 1 if successful else + 0. To workaround the fact that mallopt is specified to use int, + not size_t parameters, the value -1 is specially treated as the + maximum unsigned size_t value. + + SVID/XPG/ANSI defines four standard param numbers for mallopt, + normally defined in malloc.h. None of these are use in this malloc, + so setting them has no effect. But this malloc also supports other + options in mallopt. See below for details. Briefly, supported + parameters are as follows (listed defaults are for "typical" + configurations). + + Symbol param # default allowed param values + M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) + M_GRANULARITY -2 page size any power of 2 >= page size + M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) +*/ +DLMALLOC_EXPORT int dlmallopt(int, int); + +/* + malloc_footprint(); + Returns the number of bytes obtained from the system. The total + number of bytes allocated by malloc, realloc etc., is less than this + value. Unlike mallinfo, this function returns only a precomputed + result, so can be called frequently to monitor memory consumption. + Even if locks are otherwise defined, this function does not use them, + so results might not be up to date. +*/ +DLMALLOC_EXPORT size_t dlmalloc_footprint(void); + +/* + malloc_max_footprint(); + Returns the maximum number of bytes obtained from the system. This + value will be greater than current footprint if deallocated space + has been reclaimed by the system. The peak number of bytes allocated + by malloc, realloc etc., is less than this value. Unlike mallinfo, + this function returns only a precomputed result, so can be called + frequently to monitor memory consumption. Even if locks are + otherwise defined, this function does not use them, so results might + not be up to date. +*/ +DLMALLOC_EXPORT size_t dlmalloc_max_footprint(void); + +/* + malloc_footprint_limit(); + Returns the number of bytes that the heap is allowed to obtain from + the system, returning the last value returned by + malloc_set_footprint_limit, or the maximum size_t value if + never set. The returned value reflects a permission. There is no + guarantee that this number of bytes can actually be obtained from + the system. +*/ +DLMALLOC_EXPORT size_t dlmalloc_footprint_limit(void); + +/* + malloc_set_footprint_limit(); + Sets the maximum number of bytes to obtain from the system, causing + failure returns from malloc and related functions upon attempts to + exceed this value. The argument value may be subject to page + rounding to an enforceable limit; this actual value is returned. + Using an argument of the maximum possible size_t effectively + disables checks. If the argument is less than or equal to the + current malloc_footprint, then all future allocations that require + additional system memory will fail. However, invocation cannot + retroactively deallocate existing used memory. +*/ +DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); + +#if MALLOC_INSPECT_ALL +/* + malloc_inspect_all(void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg); + Traverses the heap and calls the given handler for each managed + region, skipping all bytes that are (or may be) used for bookkeeping + purposes. Traversal does not include include chunks that have been + directly memory mapped. Each reported region begins at the start + address, and continues up to but not including the end address. The + first used_bytes of the region contain allocated data. If + used_bytes is zero, the region is unallocated. The handler is + invoked with the given callback argument. If locks are defined, they + are held during the entire traversal. It is a bad idea to invoke + other malloc functions from within the handler. + + For example, to count the number of in-use chunks with size greater + than 1000, you could write: + static int count = 0; + void count_chunks(void* start, void* end, size_t used, void* arg) { + if (used >= 1000) ++count; + } + then: + malloc_inspect_all(count_chunks, NULL); + + malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. +*/ +DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), + void* arg); + +#endif /* MALLOC_INSPECT_ALL */ + +#if !NO_MALLINFO +/* + mallinfo() + Returns (by copy) a struct containing various summary statistics: + + arena: current total non-mmapped bytes allocated from system + ordblks: the number of free chunks + smblks: always zero. + hblks: current number of mmapped regions + hblkhd: total bytes held in mmapped regions + usmblks: the maximum total allocated space. This will be greater + than current total if trimming has occurred. + fsmblks: always zero + uordblks: current total allocated space (normal or mmapped) + fordblks: total free space + keepcost: the maximum number of bytes that could ideally be released + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) + + Because these fields are ints, but internal bookkeeping may + be kept as longs, the reported values may wrap around zero and + thus be inaccurate. +*/ +DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); +#endif /* NO_MALLINFO */ + +/* + independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); + + independent_calloc is similar to calloc, but instead of returning a + single cleared space, it returns an array of pointers to n_elements + independent elements that can hold contents of size elem_size, each + of which starts out cleared, and can be independently freed, + realloc'ed etc. The elements are guaranteed to be adjacently + allocated (this is not guaranteed to occur with multiple callocs or + mallocs), which may also improve cache locality in some + applications. + + The "chunks" argument is optional (i.e., may be null, which is + probably the most typical usage). If it is null, the returned array + is itself dynamically allocated and should also be freed when it is + no longer needed. Otherwise, the chunks array must be of at least + n_elements in length. It is filled in with the pointers to the + chunks. + + In either case, independent_calloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and "chunks" + is null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be freed when it is no longer needed. This can be + done all at once using bulk_free. + + independent_calloc simplifies and speeds up implementations of many + kinds of pools. It may also be useful when constructing large data + structures that initially have a fixed number of fixed-sized nodes, + but the number is not known at compile time, and some of the nodes + may later need to be freed. For example: + + struct Node { int item; struct Node* next; }; + + struct Node* build_list() { + struct Node** pool; + int n = read_number_of_nodes_needed(); + if (n <= 0) return 0; + pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); + if (pool == 0) die(); + // organize into a linked list... + struct Node* first = pool[0]; + for (i = 0; i < n-1; ++i) + pool[i]->next = pool[i+1]; + free(pool); // Can now free the array (or not, if it is needed later) + return first; + } +*/ +DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); + +/* + independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); + + independent_comalloc allocates, all at once, a set of n_elements + chunks with sizes indicated in the "sizes" array. It returns + an array of pointers to these elements, each of which can be + independently freed, realloc'ed etc. The elements are guaranteed to + be adjacently allocated (this is not guaranteed to occur with + multiple callocs or mallocs), which may also improve cache locality + in some applications. + + The "chunks" argument is optional (i.e., may be null). If it is null + the returned array is itself dynamically allocated and should also + be freed when it is no longer needed. Otherwise, the chunks array + must be of at least n_elements in length. It is filled in with the + pointers to the chunks. + + In either case, independent_comalloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and chunks is + null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be freed when it is no longer needed. This can be + done all at once using bulk_free. + + independent_comallac differs from independent_calloc in that each + element may have a different size, and also that it does not + automatically clear elements. + + independent_comalloc can be used to speed up allocation in cases + where several structs or objects must always be allocated at the + same time. For example: + + struct Head { ... } + struct Foot { ... } + + void send_message(char* msg) { + int msglen = strlen(msg); + size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; + void* chunks[3]; + if (independent_comalloc(3, sizes, chunks) == 0) + die(); + struct Head* head = (struct Head*)(chunks[0]); + char* body = (char*)(chunks[1]); + struct Foot* foot = (struct Foot*)(chunks[2]); + // ... + } + + In general though, independent_comalloc is worth using only for + larger values of n_elements. For small values, you probably won't + detect enough difference from series of malloc calls to bother. + + Overuse of independent_comalloc can increase overall memory usage, + since it cannot reuse existing noncontiguous small chunks that + might be available for some of the elements. +*/ +DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**); + +/* + bulk_free(void* array[], size_t n_elements) + Frees and clears (sets to null) each non-null pointer in the given + array. This is likely to be faster than freeing them one-by-one. + If footers are used, pointers that have been allocated in different + mspaces are not freed or cleared, and the count of all such pointers + is returned. For large arrays of pointers with poor locality, it + may be worthwhile to sort this array before calling bulk_free. +*/ +DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements); + +/* + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + */ +DLMALLOC_EXPORT void* dlpvalloc(size_t); + +/* + malloc_trim(size_t pad); + + If possible, gives memory back to the system (via negative arguments + to sbrk) if there is unused memory at the `high' end of the malloc + pool or in unused MMAP segments. You can call this after freeing + large blocks of memory to potentially reduce the system-level memory + requirements of a program. However, it cannot guarantee to reduce + memory. Under some allocation patterns, some large free blocks of + memory will be locked between two used chunks, so they cannot be + given back to the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, only + the minimum amount of memory to maintain internal data structures + will be left. Non-zero arguments can be supplied to maintain enough + trailing space to service future expected allocations without having + to re-obtain memory from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. +*/ +DLMALLOC_EXPORT int dlmalloc_trim(size_t); + +/* + malloc_stats(); + Prints on stderr the amount of space obtained from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), and the current + number of bytes allocated via malloc (or realloc, etc) but not yet + freed. Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead. Because it includes + alignment wastage as being in use, this figure may be greater than + zero even when no user-level chunks are allocated. + + The reported current and maximum system memory can be inaccurate if + a program makes other calls to system memory allocation functions + (normally sbrk) outside of malloc. + + malloc_stats prints only the most commonly interesting statistics. + More information can be obtained by calling mallinfo. +*/ +DLMALLOC_EXPORT void dlmalloc_stats(void); + +#endif /* ONLY_MSPACES */ + +/* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); +*/ +size_t dlmalloc_usable_size(void*); + +#if MSPACES + +/* + mspace is an opaque type representing an independent + region of space that supports mspace_malloc, etc. +*/ +typedef void* mspace; + +/* + create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). +*/ +DLMALLOC_EXPORT mspace create_mspace(size_t capacity, int locked); + +/* + destroy_mspace destroys the given space, and attempts to return all + of its memory back to the system, returning the total number of + bytes freed. After destruction, the results of access to all memory + used by the space become undefined. +*/ +DLMALLOC_EXPORT size_t destroy_mspace(mspace msp); + +/* + create_mspace_with_base uses the memory supplied as the initial base + of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this + space is used for bookkeeping, so the capacity must be at least this + large. (Otherwise 0 is returned.) When this initial space is + exhausted, additional memory will be obtained from the system. + Destroying this space will deallocate all additionally allocated + space (if possible) but not the initial base. +*/ +DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked); + +/* + mspace_track_large_chunks controls whether requests for large chunks + are allocated in their own untracked mmapped regions, separate from + others in this mspace. By default large chunks are not tracked, + which reduces fragmentation. However, such chunks are not + necessarily released to the system upon destroy_mspace. Enabling + tracking by setting to true may increase fragmentation, but avoids + leakage when relying on destroy_mspace to release all memory + allocated using this space. The function returns the previous + setting. +*/ +DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable); + + +/* + mspace_malloc behaves as malloc, but operates within + the given space. +*/ +DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes); + +/* + mspace_free behaves as free, but operates within + the given space. + + If compiled with FOOTERS==1, mspace_free is not actually needed. + free may be called instead of mspace_free because freed chunks from + any space are handled by their originating spaces. +*/ +DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem); + +/* + mspace_realloc behaves as realloc, but operates within + the given space. + + If compiled with FOOTERS==1, mspace_realloc is not actually + needed. realloc may be called instead of mspace_realloc because + realloced chunks from any space are handled by their originating + spaces. +*/ +DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize); + +/* + mspace_calloc behaves as calloc, but operates within + the given space. +*/ +DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); + +/* + mspace_memalign behaves as memalign, but operates within + the given space. +*/ +DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); + +/* + mspace_independent_calloc behaves as independent_calloc, but + operates within the given space. +*/ +DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); + +/* + mspace_independent_comalloc behaves as independent_comalloc, but + operates within the given space. +*/ +DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); + +/* + mspace_footprint() returns the number of bytes obtained from the + system for this space. +*/ +DLMALLOC_EXPORT size_t mspace_footprint(mspace msp); + +/* + mspace_max_footprint() returns the peak number of bytes obtained from the + system for this space. +*/ +DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp); + + +#if !NO_MALLINFO +/* + mspace_mallinfo behaves as mallinfo, but reports properties of + the given space. +*/ +DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp); +#endif /* NO_MALLINFO */ + +/* + malloc_usable_size(void* p) behaves the same as malloc_usable_size; +*/ +DLMALLOC_EXPORT size_t mspace_usable_size(void* mem); + +/* + mspace_malloc_stats behaves as malloc_stats, but reports + properties of the given space. +*/ +DLMALLOC_EXPORT void mspace_malloc_stats(mspace msp); + +/* + mspace_trim behaves as malloc_trim, but + operates within the given space. +*/ +DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad); + +/* + An alias for mallopt. +*/ +DLMALLOC_EXPORT int mspace_mallopt(int, int); + +#endif /* MSPACES */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +/* + ======================================================================== + To make a fully customizable malloc.h header file, cut everything + above this line, put into file malloc.h, edit to suit, and #include it + on the next line, as well as in programs that use this malloc. + ======================================================================== +*/ + +/* #include "malloc.h" */ + +/*------------------------------ internal #includes ---------------------- */ + +#ifdef _MSC_VER +#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ +#endif /* _MSC_VER */ + +#if !NO_MALLOC_STATS +#include /* for printing in malloc_stats */ +#endif /* NO_MALLOC_STATS */ + +#ifndef LACKS_ERRNO_H +#include /* for MALLOC_FAILURE_ACTION */ +#endif /* LACKS_ERRNO_H */ + +#ifdef DEBUG +#if ABORT_ON_ASSERT_FAILURE +#undef assert +#define assert(x) if(!(x)) ABORT +#else /* ABORT_ON_ASSERT_FAILURE */ +#include +#endif /* ABORT_ON_ASSERT_FAILURE */ +#else /* DEBUG */ +#ifndef assert +#define assert(x) +#endif +#define DEBUG 0 +#endif /* DEBUG */ + +#if !defined(WIN32) && !defined(LACKS_TIME_H) +#include /* for magic initialization */ +#endif /* WIN32 */ + +#ifndef LACKS_STDLIB_H +#include /* for abort() */ +#endif /* LACKS_STDLIB_H */ + +#ifndef LACKS_STRING_H +#include /* for memset etc */ +#endif /* LACKS_STRING_H */ + +#if USE_BUILTIN_FFS +#ifndef LACKS_STRINGS_H +#include /* for ffs */ +#endif /* LACKS_STRINGS_H */ +#endif /* USE_BUILTIN_FFS */ + +#if HAVE_MMAP +#ifndef LACKS_SYS_MMAN_H +/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ +#if (defined(linux) && !defined(__USE_GNU)) +#define __USE_GNU 1 +#include /* for mmap */ +#undef __USE_GNU +#else +#include /* for mmap */ +#endif /* linux */ +#endif /* LACKS_SYS_MMAN_H */ +#ifndef LACKS_FCNTL_H +#include +#endif /* LACKS_FCNTL_H */ +#endif /* HAVE_MMAP */ + +#ifndef LACKS_UNISTD_H +#include /* for sbrk, sysconf */ +#else /* LACKS_UNISTD_H */ +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +extern void* sbrk(ptrdiff_t); +#endif /* FreeBSD etc */ +#endif /* LACKS_UNISTD_H */ + +/* Declarations for locking */ +#if USE_LOCKS +#ifndef WIN32 +#if defined (__SVR4) && defined (__sun) /* solaris */ +#include +#elif !defined(LACKS_SCHED_H) +#include +#endif /* solaris or LACKS_SCHED_H */ +#if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || !USE_SPIN_LOCKS +#include +#endif /* USE_RECURSIVE_LOCKS ... */ +#elif defined(_MSC_VER) +#ifndef _M_AMD64 +/* These are already defined on AMD64 builds */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); +LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _M_AMD64 */ +#pragma intrinsic (_InterlockedCompareExchange) +#pragma intrinsic (_InterlockedExchange) +#define interlockedcompareexchange _InterlockedCompareExchange +#define interlockedexchange _InterlockedExchange +#elif defined(WIN32) && defined(__GNUC__) +#define interlockedcompareexchange(a, b, c) __sync_val_compare_and_swap(a, c, b) +#define interlockedexchange __sync_lock_test_and_set +#endif /* Win32 */ +#endif /* USE_LOCKS */ + +/* Declarations for bit scanning on win32 */ +#if defined(_MSC_VER) && _MSC_VER>=1300 +#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +unsigned char _BitScanForward(unsigned long *index, unsigned long mask); +unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#define BitScanForward _BitScanForward +#define BitScanReverse _BitScanReverse +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) +#endif /* BitScanForward */ +#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ + +#ifndef WIN32 +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef WIN32 /* use supplied emulation of getpagesize */ +# define malloc_getpagesize getpagesize() +# else +# ifndef LACKS_SYS_PARAM_H +# include +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else /* just guess */ +# define malloc_getpagesize ((size_t)4096U) +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif +#endif + +/* ------------------- size_t and alignment properties -------------------- */ + +/* The byte and bit size of a size_t */ +#define SIZE_T_SIZE (sizeof(size_t)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) + +/* Some constants coerced to size_t */ +/* Annoying but necessary to avoid errors on some platforms */ +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define SIZE_T_FOUR ((size_t)4) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) +#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) + +/* The bit mask value corresponding to MALLOC_ALIGNMENT */ +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +/* True if address a has acceptable alignment */ +#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) + +/* the number of bytes to offset an address to align it */ +#define align_offset(A)\ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) + +/* -------------------------- MMAP preliminaries ------------------------- */ + +/* + If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and + checks to fail so compiler optimizer can delete code rather than + using so many "#if"s. +*/ + + +/* MORECORE and MMAP must return MFAIL on failure */ +#define MFAIL ((void*)(MAX_SIZE_T)) +#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ + +#if HAVE_MMAP + +#ifndef WIN32 +#define MUNMAP_DEFAULT(a, s) munmap((a), (s)) +#define MMAP_PROT (PROT_READ|PROT_WRITE) +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif /* MAP_ANON */ +#ifdef MAP_ANONYMOUS +#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) +#define MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) +#else /* MAP_ANONYMOUS */ +/* + Nearly all versions of mmap support MAP_ANONYMOUS, so the following + is unlikely to be needed, but is supplied just in case. +*/ +#define MMAP_FLAGS (MAP_PRIVATE) +static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ +#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ + (dev_zero_fd = open("/dev/zero", O_RDWR), \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) +#endif /* MAP_ANONYMOUS */ + +#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) + +#else /* WIN32 */ + +/* Win32 MMAP via VirtualAlloc */ +static FORCEINLINE void* win32mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; +} + +/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ +static FORCEINLINE void* win32direct_mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; +} + +/* This function supports releasing coalesed segments */ +static FORCEINLINE int win32munmap(void* ptr, size_t size) { + MEMORY_BASIC_INFORMATION minfo; + char* cptr = (char*)ptr; + while (size) { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + return 0; +} + +#define MMAP_DEFAULT(s) win32mmap(s) +#define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) +#define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) +#endif /* WIN32 */ +#endif /* HAVE_MMAP */ + +#if HAVE_MREMAP +#ifndef WIN32 +#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) +#endif /* WIN32 */ +#endif /* HAVE_MREMAP */ + +/** + * Define CALL_MORECORE + */ +#if HAVE_MORECORE + #ifdef MORECORE + #define CALL_MORECORE(S) MORECORE(S) + #else /* MORECORE */ + #define CALL_MORECORE(S) MORECORE_DEFAULT(S) + #endif /* MORECORE */ +#else /* HAVE_MORECORE */ + #define CALL_MORECORE(S) MFAIL +#endif /* HAVE_MORECORE */ + +/** + * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP + */ +#if HAVE_MMAP + #define USE_MMAP_BIT (SIZE_T_ONE) + + #ifdef MMAP + #define CALL_MMAP(s) MMAP(s) + #else /* MMAP */ + #define CALL_MMAP(s) MMAP_DEFAULT(s) + #endif /* MMAP */ + #ifdef MUNMAP + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) + #else /* MUNMAP */ + #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) + #endif /* MUNMAP */ + #ifdef DIRECT_MMAP + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #else /* DIRECT_MMAP */ + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) + #endif /* DIRECT_MMAP */ +#else /* HAVE_MMAP */ + #define USE_MMAP_BIT (SIZE_T_ZERO) + + #define MMAP(s) MFAIL + #define MUNMAP(a, s) (-1) + #define DIRECT_MMAP(s) MFAIL + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #define CALL_MMAP(s) MMAP(s) + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) +#endif /* HAVE_MMAP */ + +/** + * Define CALL_MREMAP + */ +#if HAVE_MMAP && HAVE_MREMAP + #ifdef MREMAP + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) + #else /* MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) + #endif /* MREMAP */ +#else /* HAVE_MMAP && HAVE_MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL +#endif /* HAVE_MMAP && HAVE_MREMAP */ + +/* mstate bit set if continguous morecore disabled or failed */ +#define USE_NONCONTIGUOUS_BIT (4U) + +/* segment bit set in create_mspace_with_base */ +#define EXTERN_BIT (8U) + + +/* --------------------------- Lock preliminaries ------------------------ */ + +/* + When locks are defined, there is one global lock, plus + one per-mspace lock. + + The global lock_ensures that mparams.magic and other unique + mparams values are initialized only once. It also protects + sequences of calls to MORECORE. In many cases sys_alloc requires + two calls, that should not be interleaved with calls by other + threads. This does not protect against direct calls to MORECORE + by other threads not using this lock, so there is still code to + cope the best we can on interference. + + Per-mspace locks surround calls to malloc, free, etc. + By default, locks are simple non-reentrant mutexes. + + Because lock-protected regions generally have bounded times, it is + OK to use the supplied simple spinlocks. Spinlocks are likely to + improve performance for lightly contended applications, but worsen + performance under heavy contention. + + If USE_LOCKS is > 1, the definitions of lock routines here are + bypassed, in which case you will need to define the type MLOCK_T, + and at least INITIAL_LOCK, DESTROY_LOCK, ACQUIRE_LOCK, RELEASE_LOCK + and TRY_LOCK. You must also declare a + static MLOCK_T malloc_global_mutex = { initialization values };. + +*/ + +#if !USE_LOCKS +#define USE_LOCK_BIT (0U) +#define INITIAL_LOCK(l) (0) +#define DESTROY_LOCK(l) (0) +#define ACQUIRE_MALLOC_GLOBAL_LOCK() +#define RELEASE_MALLOC_GLOBAL_LOCK() + +#else +#if USE_LOCKS > 1 +/* ----------------------- User-defined locks ------------------------ */ +/* Define your own lock implementation here */ +/* #define INITIAL_LOCK(lk) ... */ +/* #define DESTROY_LOCK(lk) ... */ +/* #define ACQUIRE_LOCK(lk) ... */ +/* #define RELEASE_LOCK(lk) ... */ +/* #define TRY_LOCK(lk) ... */ +/* static MLOCK_T malloc_global_mutex = ... */ + +#elif USE_SPIN_LOCKS + +/* First, define CAS_LOCK and CLEAR_LOCK on ints */ +/* Note CAS_LOCK defined to return 0 on success */ + +#if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) +#define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) +#define CLEAR_LOCK(sl) __sync_lock_release(sl) + +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) +/* Custom spin locks for older gcc on x86 */ +static FORCEINLINE int x86_cas_lock(int *sl) { + int ret; + int val = 1; + int cmp = 0; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (val), "m" (*(sl)), "0"(cmp) + : "memory", "cc"); + return ret; +} + +static FORCEINLINE void x86_clear_lock(int* sl) { + assert(*sl != 0); + int prev = 0; + int ret; + __asm__ __volatile__ ("lock; xchgl %0, %1" + : "=r" (ret) + : "m" (*(sl)), "0"(prev) + : "memory"); +} + +#define CAS_LOCK(sl) x86_cas_lock(sl) +#define CLEAR_LOCK(sl) x86_clear_lock(sl) + +#else /* Win32 MSC */ +#define CAS_LOCK(sl) interlockedexchange(sl, 1) +#define CLEAR_LOCK(sl) interlockedexchange (sl, 0) + +#endif /* ... gcc spins locks ... */ + +/* How to yield for a spin lock */ +#define SPINS_PER_YIELD 63 +#if defined(_MSC_VER) +#define SLEEP_EX_DURATION 50 /* delay for yield/sleep */ +#define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE) +#elif defined (__SVR4) && defined (__sun) /* solaris */ +#define SPIN_LOCK_YIELD thr_yield(); +#elif !defined(LACKS_SCHED_H) +#define SPIN_LOCK_YIELD sched_yield(); +#else +#define SPIN_LOCK_YIELD +#endif /* ... yield ... */ + +#if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 +/* Plain spin locks use single word (embedded in malloc_states) */ +static int spin_acquire_lock(int *sl) { + int spins = 0; + while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) { + if ((++spins & SPINS_PER_YIELD) == 0) { + SPIN_LOCK_YIELD; + } + } + return 0; +} + +#define MLOCK_T int +#define TRY_LOCK(sl) !CAS_LOCK(sl) +#define RELEASE_LOCK(sl) CLEAR_LOCK(sl) +#define ACQUIRE_LOCK(sl) (CAS_LOCK(sl)? spin_acquire_lock(sl) : 0) +#define INITIAL_LOCK(sl) (*sl = 0) +#define DESTROY_LOCK(sl) (0) +static MLOCK_T malloc_global_mutex = 0; + +#else /* USE_RECURSIVE_LOCKS */ +/* types for lock owners */ +#ifdef WIN32 +#define THREAD_ID_T DWORD +#define CURRENT_THREAD GetCurrentThreadId() +#define EQ_OWNER(X,Y) ((X) == (Y)) +#else +/* + Note: the following assume that pthread_t is a type that can be + initialized to (casted) zero. If this is not the case, you will need to + somehow redefine these or not use spin locks. +*/ +#define THREAD_ID_T pthread_t +#define CURRENT_THREAD pthread_self() +#define EQ_OWNER(X,Y) pthread_equal(X, Y) +#endif + +struct malloc_recursive_lock { + int sl; + unsigned int c; + THREAD_ID_T threadid; +}; + +#define MLOCK_T struct malloc_recursive_lock +static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0}; + +static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) { + assert(lk->sl != 0); + if (--lk->c == 0) { + CLEAR_LOCK(&lk->sl); + } +} + +static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; + int spins = 0; + for (;;) { + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; + lk->c = 1; + return 0; + } + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; + return 0; + } + if ((++spins & SPINS_PER_YIELD) == 0) { + SPIN_LOCK_YIELD; + } + } +} + +static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; + lk->c = 1; + return 1; + } + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; + return 1; + } + return 0; +} + +#define RELEASE_LOCK(lk) recursive_release_lock(lk) +#define TRY_LOCK(lk) recursive_try_lock(lk) +#define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) +#define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) +#define DESTROY_LOCK(lk) (0) +#endif /* USE_RECURSIVE_LOCKS */ + +#elif defined(WIN32) /* Win32 critical sections */ +#define MLOCK_T CRITICAL_SECTION +#define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) +#define RELEASE_LOCK(lk) LeaveCriticalSection(lk) +#define TRY_LOCK(lk) TryEnterCriticalSection(lk) +#define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000)) +#define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) +#define NEED_GLOBAL_LOCK_INIT + +static MLOCK_T malloc_global_mutex; +static volatile long malloc_global_mutex_status; + +/* Use spin loop to initialize global lock */ +static void init_malloc_global_mutex() { + for (;;) { + long stat = malloc_global_mutex_status; + if (stat > 0) + return; + /* transition to < 0 while initializing, then to > 0) */ + if (stat == 0 && + interlockedcompareexchange(&malloc_global_mutex_status, -1, 0) == 0) { + InitializeCriticalSection(&malloc_global_mutex); + interlockedexchange(&malloc_global_mutex_status,1); + return; + } + SleepEx(0, FALSE); + } +} + +#else /* pthreads-based locks */ +#define MLOCK_T pthread_mutex_t +#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) +#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) +#define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) +#define INITIAL_LOCK(lk) pthread_init_lock(lk) +#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) + +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) +/* Cope with old-style linux recursive lock initialization by adding */ +/* skipped internal declaration from pthread.h */ +extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, + int __kind)); +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) +#endif /* USE_RECURSIVE_LOCKS ... */ + +static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int pthread_init_lock (MLOCK_T *lk) { + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr)) return 1; +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; +#endif + if (pthread_mutex_init(lk, &attr)) return 1; + if (pthread_mutexattr_destroy(&attr)) return 1; + return 0; +} + +#endif /* ... lock types ... */ + +/* Common code for all lock types */ +#define USE_LOCK_BIT (2U) + +#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK +#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); +#endif + +#ifndef RELEASE_MALLOC_GLOBAL_LOCK +#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); +#endif + +#endif /* USE_LOCKS */ + +/* ----------------------- Chunk representations ------------------------ */ + +/* + (The following includes lightly edited explanations by Colin Plumb.) + + The malloc_chunk declaration below is misleading (but accurate and + necessary). It declares a "view" into memory allowing access to + necessary fields at known offsets from a given base. + + Chunks of memory are maintained using a `boundary tag' method as + originally described by Knuth. (See the paper by Paul Wilson + ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such + techniques.) Sizes of free chunks are stored both in the front of + each chunk and at the end. This makes consolidating fragmented + chunks into bigger chunks fast. The head fields also hold bits + representing whether chunks are free or in use. + + Here are some pictures to make it clearer. They are "exploded" to + show that the state of a chunk can be thought of as extending from + the high 31 bits of the head field of its header through the + prev_foot and PINUSE_BIT bit of the following chunk header. + + A chunk that's in use looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk (if P = 0) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 1| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +- -+ + | | + +- -+ + | : + +- size - sizeof(size_t) available payload bytes -+ + : | + chunk-> +- -+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| + | Size of next chunk (may or may not be in use) | +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + And if it's free, it looks like this: + + chunk-> +- -+ + | User payload (must be in use, or we would have merged!) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 0| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Prev pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- size - sizeof(struct chunk) unused bytes -+ + : | + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| + | Size of next chunk (must be in use, or we would have merged)| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- User payload -+ + : | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |0| + +-+ + Note that since we always merge adjacent free chunks, the chunks + adjacent to a free chunk must be in use. + + Given a pointer to a chunk (which can be derived trivially from the + payload pointer) we can, in O(1) time, find out whether the adjacent + chunks are free, and if so, unlink them from the lists that they + are on and merge them with the current chunk. + + Chunks always begin on even word boundaries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus at least double-word aligned. + + The P (PINUSE_BIT) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + The very first chunk allocated always has this bit set, preventing + access to non-existent (or non-owned) memory. If pinuse is set for + any given chunk, then you CANNOT determine the size of the + previous chunk, and might even get a memory addressing fault when + trying to do so. + + The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of + the chunk size redundantly records whether the current chunk is + inuse (unless the chunk is mmapped). This redundancy enables usage + checks within free and realloc, and reduces indirection when freeing + and consolidating chunks. + + Each freshly allocated chunk must have both cinuse and pinuse set. + That is, each allocated chunk borders either a previously allocated + and still in-use chunk, or the base of its memory arena. This is + ensured by making all allocations from the `lowest' part of any + found chunk. Further, no free chunk physically borders another one, + so each free chunk is known to be preceded and followed by either + inuse chunks or the ends of memory. + + Note that the `foot' of the current chunk is actually represented + as the prev_foot of the NEXT chunk. This makes it easier to + deal with alignments etc but can be very confusing when trying + to extend or adapt this code. + + The exceptions to all this are + + 1. The special chunk `top' is the top-most available chunk (i.e., + the one bordering the end of available memory). It is treated + specially. Top is never included in any bin, is used only if + no other chunk is available, and is released back to the + system if it is very large (see M_TRIM_THRESHOLD). In effect, + the top chunk is treated as larger (and thus less well + fitting) than any other available chunk. The top chunk + doesn't update its trailing size field since there is no next + contiguous chunk that would have to index off it. However, + space is still allocated for it (TOP_FOOT_SIZE) to enable + separation or merging when space is extended. + + 3. Chunks allocated via mmap, have both cinuse and pinuse bits + cleared in their head fields. Because they are allocated + one-by-one, each must carry its own prev_foot field, which is + also used to hold the offset this chunk has within its mmapped + region, which is needed to preserve alignment. Each mmapped + chunk is trailed by the first two fields of a fake next-chunk + for sake of usage checks. + +*/ + +struct malloc_chunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk* mchunkptr; +typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#define MCHUNK_SIZE (sizeof(mchunk)) + +#if FOOTERS +#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +#else /* FOOTERS */ +#define CHUNK_OVERHEAD (SIZE_T_SIZE) +#endif /* FOOTERS */ + +/* MMapped chunks need a second word of overhead ... */ +#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +/* ... and additional padding for fake next-chunk at foot */ +#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) + +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* conversion from malloc headers to user pointers, and back */ +#define chunk2mem(p) ((void*)((uintptr_t)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) +/* chunk associated with aligned address A */ +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +/* Bounds on request (not chunk) sizes. */ +#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + +/* pad request bytes into a usable size */ +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* pad request, checking for minimum (but not maximum) */ +#define request2size(req) \ + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + + +/* ------------------ Operations on head and foot fields ----------------- */ + +/* + The head field of a chunk is or'ed with PINUSE_BIT when previous + adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in + use, unless mmapped, in which case both bits are cleared. + + FLAG4_BIT is not used by this malloc, but might be useful in extensions. +*/ + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define FLAG4_BIT (SIZE_T_FOUR) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) +#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) + +/* Head value for fenceposts */ +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + +/* extraction of fields from head words */ +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define flag4inuse(p) ((p)->head & FLAG4_BIT) +#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) +#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) + +#define chunksize(p) ((p)->head & ~(FLAG_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) +#define set_flag4(p) ((p)->head |= FLAG4_BIT) +#define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) + +/* Treat space at ptr +/- offset as a chunk */ +#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) + +/* Ptr to next or previous physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) +#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) + +/* extract next chunk's pinuse bit */ +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +/* Get/set size at footer */ +#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) + +/* Set size, pinuse bit, and foot */ +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + +/* Set size, pinuse bit, foot, and clear next pinuse */ +#define set_free_with_pinuse(p, s, n)\ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +/* Get the internal overhead associated with chunk p */ +#define overhead_for(p)\ + (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + +/* Return true if malloced space is not necessarily cleared */ +#if MMAP_CLEARS +#define calloc_must_clear(p) (!is_mmapped(p)) +#else /* MMAP_CLEARS */ +#define calloc_must_clear(p) (1) +#endif /* MMAP_CLEARS */ + +/* ---------------------- Overlaid data structures ----------------------- */ + +/* + When chunks are not in use, they are treated as nodes of either + lists or trees. + + "Small" chunks are stored in circular doubly-linked lists, and look + like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Larger chunks are kept in a form of bitwise digital trees (aka + tries) keyed on chunksizes. Because malloc_tree_chunks are only for + free chunks greater than 256 bytes, their size doesn't impose any + constraints on user chunk sizes. Each node looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to left child (child[0]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to right child (child[1]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to parent | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | bin index of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Each tree holding treenodes is a tree of unique chunk sizes. Chunks + of the same size are arranged in a circularly-linked list, with only + the oldest chunk (the next to be used, in our FIFO ordering) + actually in the tree. (Tree members are distinguished by a non-null + parent pointer.) If a chunk with the same size an an existing node + is inserted, it is linked off the existing node using pointers that + work in the same way as fd/bk pointers of small chunks. + + Each tree contains a power of 2 sized range of chunk sizes (the + smallest is 0x100 <= x < 0x180), which is is divided in half at each + tree level, with the chunks in the smaller half of the range (0x100 + <= x < 0x140 for the top nose) in the left subtree and the larger + half (0x140 <= x < 0x180) in the right subtree. This is, of course, + done by inspecting individual bits. + + Using these rules, each node's left subtree contains all smaller + sizes than its right subtree. However, the node at the root of each + subtree has no particular ordering relationship to either. (The + dividing line between the subtree sizes is based on trie relation.) + If we remove the last chunk of a given size from the interior of the + tree, we need to replace it with a leaf node. The tree ordering + rules permit a node to be replaced by any leaf below it. + + The smallest chunk in a tree (a common operation in a best-fit + allocator) can be found by walking a path to the leftmost leaf in + the tree. Unlike a usual binary tree, where we follow left child + pointers until we reach a null, here we follow the right child + pointer any time the left one is null, until we reach a leaf with + both child pointers null. The smallest chunk in the tree will be + somewhere along that path. + + The worst case number of steps to add, find, or remove a node is + bounded by the number of bits differentiating chunks within + bins. Under current bin calculations, this ranges from 6 up to 21 + (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case + is of course much better. +*/ + +struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ + size_t prev_foot; + size_t head; + struct malloc_tree_chunk* fd; + struct malloc_tree_chunk* bk; + + struct malloc_tree_chunk* child[2]; + struct malloc_tree_chunk* parent; + bindex_t index; +}; + +typedef struct malloc_tree_chunk tchunk; +typedef struct malloc_tree_chunk* tchunkptr; +typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ + +/* A little helper macro for trees */ +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + +/* ----------------------------- Segments -------------------------------- */ + +/* + Each malloc space may include non-contiguous segments, held in a + list headed by an embedded malloc_segment record representing the + top-most space. Segments also include flags holding properties of + the space. Large chunks that are directly allocated by mmap are not + included in this list. They are instead independently created and + destroyed without otherwise keeping track of them. + + Segment management mainly comes into play for spaces allocated by + MMAP. Any call to MMAP might or might not return memory that is + adjacent to an existing segment. MORECORE normally contiguously + extends the current space, so this space is almost always adjacent, + which is simpler and faster to deal with. (This is why MORECORE is + used preferentially to MMAP when both are available -- see + sys_alloc.) When allocating using MMAP, we don't use any of the + hinting mechanisms (inconsistently) supported in various + implementations of unix mmap, or distinguish reserving from + committing memory. Instead, we just ask for space, and exploit + contiguity when we get it. It is probably possible to do + better than this on some systems, but no general scheme seems + to be significantly better. + + Management entails a simpler variant of the consolidation scheme + used for chunks to reduce fragmentation -- new adjacent memory is + normally prepended or appended to an existing segment. However, + there are limitations compared to chunk consolidation that mostly + reflect the fact that segment processing is relatively infrequent + (occurring only when getting memory from system) and that we + don't expect to have huge numbers of segments: + + * Segments are not indexed, so traversal requires linear scans. (It + would be possible to index these, but is not worth the extra + overhead and complexity for most programs on most platforms.) + * New segments are only appended to old ones when holding top-most + memory; if they cannot be prepended to others, they are held in + different segments. + + Except for the top-most segment of an mstate, each segment record + is kept at the tail of its segment. Segments are added by pushing + segment records onto the list headed by &mstate.seg for the + containing mstate. + + Segment flags control allocation/merge/deallocation policies: + * If EXTERN_BIT set, then we did not allocate this segment, + and so should not try to deallocate or merge with others. + (This currently holds only for the initial segment passed + into create_mspace_with_base.) + * If USE_MMAP_BIT set, the segment may be merged with + other surrounding mmapped segments and trimmed/de-allocated + using munmap. + * If neither bit is set, then the segment was obtained using + MORECORE so can be merged with surrounding MORECORE'd segments + and deallocated/trimmed using MORECORE with negative arguments. +*/ + +struct malloc_segment { + char* base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment* next; /* ptr to next segment */ + flag_t sflags; /* mmap and extern flag */ +}; + +#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) +#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) + +typedef struct malloc_segment msegment; +typedef struct malloc_segment* msegmentptr; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* + A malloc_state holds all of the bookkeeping for a space. + The main fields are: + + Top + The topmost chunk of the currently active segment. Its size is + cached in topsize. The actual size of topmost space is + topsize+TOP_FOOT_SIZE, which includes space reserved for adding + fenceposts and segment records if necessary when getting more + space from the system. The size at which to autotrim top is + cached from mparams in trim_check, except that it is disabled if + an autotrim fails. + + Designated victim (dv) + This is the preferred chunk for servicing small requests that + don't have exact fits. It is normally the chunk split off most + recently to service another small request. Its size is cached in + dvsize. The link fields of this chunk are not maintained since it + is not kept in a bin. + + SmallBins + An array of bin headers for free chunks. These bins hold chunks + with sizes less than MIN_LARGE_SIZE bytes. Each bin contains + chunks of all the same size, spaced 8 bytes apart. To simplify + use in double-linked lists, each bin header acts as a malloc_chunk + pointing to the real first node, if it exists (else pointing to + itself). This avoids special-casing for headers. But to avoid + waste, we allocate only the fd/bk pointers of bins, and then use + repositioning tricks to treat these as the fields of a chunk. + + TreeBins + Treebins are pointers to the roots of trees holding a range of + sizes. There are 2 equally spaced treebins for each power of two + from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything + larger. + + Bin maps + There is one bit map for small bins ("smallmap") and one for + treebins ("treemap). Each bin sets its bit when non-empty, and + clears the bit when empty. Bit operations are then used to avoid + bin-by-bin searching -- nearly all "search" is done without ever + looking at bins that won't be selected. The bit maps + conservatively use 32 bits per map word, even if on 64bit system. + For a good description of some of the bit-based techniques used + here, see Henry S. Warren Jr's book "Hacker's Delight" (and + supplement at http://hackersdelight.org/). Many of these are + intended to reduce the branchiness of paths through malloc etc, as + well as to reduce the number of memory locations read or written. + + Segments + A list of segments headed by an embedded malloc_segment record + representing the initial space. + + Address check support + The least_addr field is the least address ever obtained from + MORECORE or MMAP. Attempted frees and reallocs of any address less + than this are trapped (unless INSECURE is defined). + + Magic tag + A cross-check field that should always hold same value as mparams.magic. + + Max allowed footprint + The maximum allowed bytes to allocate from system (zero means no limit) + + Flags + Bits recording whether to use MMAP, locks, or contiguous MORECORE + + Statistics + Each space keeps track of current and maximum system memory + obtained via MORECORE or MMAP. + + Trim support + Fields holding the amount of unused topmost memory that should trigger + trimming, and a counter to force periodic scanning to release unused + non-topmost segments. + + Locking + If USE_LOCKS is defined, the "mutex" lock is acquired and released + around every public call using this mspace. + + Extension support + A void* pointer and a size_t field that can be used to help implement + extensions to this malloc. +*/ + +/* Bin types, widths and sizes */ +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + +struct malloc_state { + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + char* least_addr; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t release_checks; + size_t magic; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + size_t footprint; + size_t max_footprint; + size_t footprint_limit; /* zero means no limit */ + flag_t mflags; +#if USE_LOCKS + MLOCK_T mutex; /* locate lock among fields that rarely change */ +#endif /* USE_LOCKS */ + msegment seg; + void* extp; /* Unused but available for extensions */ + size_t exts; +}; + +typedef struct malloc_state* mstate; + +/* ------------- Global malloc_state and malloc_params ------------------- */ + +/* + malloc_params holds global properties, including those that can be + dynamically set using mallopt. There is a single instance, mparams, + initialized in init_mparams. Note that the non-zeroness of "magic" + also serves as an initialization flag. +*/ + +struct malloc_params { + size_t magic; + size_t page_size; + size_t granularity; + size_t mmap_threshold; + size_t trim_threshold; + flag_t default_mflags; +}; + +static struct malloc_params mparams; + +/* Ensure mparams initialized */ +#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) + +#if !ONLY_MSPACES + +/* The global malloc_state used for all non-"mspace" calls */ +static struct malloc_state _gm_; +#define gm (&_gm_) +#define is_global(M) ((M) == &_gm_) + +#endif /* !ONLY_MSPACES */ + +#define is_initialized(M) ((M)->top != 0) + +/* -------------------------- system alloc setup ------------------------- */ + +/* Operations on mflags */ + +#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) +#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) +#if USE_LOCKS +#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) +#else +#define disable_lock(M) +#endif + +#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) +#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) +#if HAVE_MMAP +#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) +#else +#define disable_mmap(M) +#endif + +#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) +#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) + +#define set_lock(M,L)\ + ((M)->mflags = (L)?\ + ((M)->mflags | USE_LOCK_BIT) :\ + ((M)->mflags & ~USE_LOCK_BIT)) + +/* page-align a size */ +#define page_align(S)\ + (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) + +/* granularity-align a size */ +#define granularity_align(S)\ + (((S) + (mparams.granularity - SIZE_T_ONE))\ + & ~(mparams.granularity - SIZE_T_ONE)) + + +/* For mmap, use granularity alignment on windows, else page-align */ +#ifdef WIN32 +#define mmap_align(S) granularity_align(S) +#else +#define mmap_align(S) page_align(S) +#endif + +/* For sys_alloc, enough padding to ensure can malloc request on success */ +#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) + +#define is_page_aligned(S)\ + (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) +#define is_granularity_aligned(S)\ + (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) + +/* True if segment S holds address A */ +#define segment_holds(S, A)\ + ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) + +/* Return segment holding given address */ +static msegmentptr segment_holding(mstate m, char* addr) { + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* Return true if segment contains a segment link */ +static int has_segment_link(mstate m, msegmentptr ss) { + msegmentptr sp = &m->seg; + for (;;) { + if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } +} + +#ifndef MORECORE_CANNOT_TRIM +#define should_trim(M,s) ((s) > (M)->trim_check) +#else /* MORECORE_CANNOT_TRIM */ +#define should_trim(M,s) (0) +#endif /* MORECORE_CANNOT_TRIM */ + +/* + TOP_FOOT_SIZE is padding at the end of a segment, including space + that may be needed to place segment records and fenceposts when new + noncontiguous segments are added. +*/ +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + + +/* ------------------------------- Hooks -------------------------------- */ + +/* + PREACTION should be defined to return 0 on success, and nonzero on + failure. If you are not using locking, you can redefine these to do + anything you like. +*/ + +#if USE_LOCKS +#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) +#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } +#else /* USE_LOCKS */ + +#ifndef PREACTION +#define PREACTION(M) (0) +#endif /* PREACTION */ + +#ifndef POSTACTION +#define POSTACTION(M) +#endif /* POSTACTION */ + +#endif /* USE_LOCKS */ + +/* + CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. + USAGE_ERROR_ACTION is triggered on detected bad frees and + reallocs. The argument p is an address that might have triggered the + fault. It is ignored by the two predefined actions, but might be + useful in custom actions that try to help diagnose errors. +*/ + +#if PROCEED_ON_ERROR + +/* A count of the number of corruption errors causing resets */ +int malloc_corruption_error_count; + +/* default corruption action */ +static void reset_on_error(mstate m); + +#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) +#define USAGE_ERROR_ACTION(m, p) + +#else /* PROCEED_ON_ERROR */ + +#ifndef CORRUPTION_ERROR_ACTION +#define CORRUPTION_ERROR_ACTION(m) ABORT +#endif /* CORRUPTION_ERROR_ACTION */ + +#ifndef USAGE_ERROR_ACTION +#define USAGE_ERROR_ACTION(m,p) ABORT +#endif /* USAGE_ERROR_ACTION */ + +#endif /* PROCEED_ON_ERROR */ + + +/* -------------------------- Debugging setup ---------------------------- */ + +#if ! DEBUG + +#define check_free_chunk(M,P) +#define check_inuse_chunk(M,P) +#define check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) +#define check_malloc_state(M) +#define check_top_chunk(M,P) + +#else /* DEBUG */ +#define check_free_chunk(M,P) do_check_free_chunk(M,P) +#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) +#define check_top_chunk(M,P) do_check_top_chunk(M,P) +#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) +#define check_malloc_state(M) do_check_malloc_state(M) + +static void do_check_any_chunk(mstate m, mchunkptr p); +static void do_check_top_chunk(mstate m, mchunkptr p); +static void do_check_mmapped_chunk(mstate m, mchunkptr p); +static void do_check_inuse_chunk(mstate m, mchunkptr p); +static void do_check_free_chunk(mstate m, mchunkptr p); +static void do_check_malloced_chunk(mstate m, void* mem, size_t s); +static void do_check_tree(mstate m, tchunkptr t); +static void do_check_treebin(mstate m, bindex_t i); +static void do_check_smallbin(mstate m, bindex_t i); +static void do_check_malloc_state(mstate m); +static int bin_find(mstate m, mchunkptr x); +static size_t traverse_and_check(mstate m); +#endif /* DEBUG */ + +/* ---------------------------- Indexing Bins ---------------------------- */ + +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + +/* addressing by index. See above about smallbin repositioning */ +#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) + +/* assign tree index for size S to variable I. Use x86 asm if possible */ +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_tree_index(S, I)\ +{\ + unsigned int X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = (unsigned) sizeof(X)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = _bit_scan_reverse (X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + _BitScanReverse((DWORD *) &K, (DWORD) X);\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#else /* GNUC */ +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int Y = (unsigned int)X;\ + unsigned int N = ((Y - 0x100) >> 16) & 8;\ + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ + N += K;\ + N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ + K = 14 - N + ((Y <<= K) >> 15);\ + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ + }\ +} +#endif /* GNUC */ + +/* Bit representing maximum resolved size in a treebin at i */ +#define bit_for_tree_index(i) \ + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + +/* Shift placing maximum resolved bit in a treebin at i as sign bit */ +#define leftshift_for_tree_index(i) \ + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + +/* The size of the smallest chunk held in bin with index i */ +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + + +/* ------------------------ Operations on bin maps ----------------------- */ + +/* bit corresponding to given index */ +#define idx2bit(i) ((binmap_t)(1) << (i)) + +/* Mark/Clear bits with given index */ +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + +/* isolate the least set bit of a bitmap */ +#define least_bit(x) ((x) & -(x)) + +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | -(x<<1)) + +/* mask with all bits to left of or equal to least bit of x on */ +#define same_or_left_bits(x) ((x) | -(x)) + +/* index corresponding to given bit. Use x86 asm if possible */ + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = __builtin_ctz(X); \ + I = (bindex_t)J;\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = _bit_scan_forward (X); \ + I = (bindex_t)J;\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + _BitScanForward((DWORD *) &J, X);\ + I = (bindex_t)J;\ +} + +#elif USE_BUILTIN_FFS +#define compute_bit2idx(X, I) I = ffs(X)-1 + +#else +#define compute_bit2idx(X, I)\ +{\ + unsigned int Y = X - 1;\ + unsigned int K = Y >> (16-4) & 16;\ + unsigned int N = K; Y >>= K;\ + N += K = Y >> (8-3) & 8; Y >>= K;\ + N += K = Y >> (4-2) & 4; Y >>= K;\ + N += K = Y >> (2-1) & 2; Y >>= K;\ + N += K = Y >> (1-0) & 1; Y >>= K;\ + I = (bindex_t)(N + Y);\ +} +#endif /* GNUC */ + + +/* ----------------------- Runtime Check Support ------------------------- */ + +/* + For security, the main invariant is that malloc/free/etc never + writes to a static address other than malloc_state, unless static + malloc_state itself has been corrupted, which cannot occur via + malloc (because of these checks). In essence this means that we + believe all pointers, sizes, maps etc held in malloc_state, but + check all of those linked or offsetted from other embedded data + structures. These checks are interspersed with main code in a way + that tends to minimize their run-time cost. + + When FOOTERS is defined, in addition to range checking, we also + verify footer fields of inuse chunks, which can be used guarantee + that the mstate controlling malloc/free is intact. This is a + streamlined version of the approach described by William Robertson + et al in "Run-time Detection of Heap-based Overflows" LISA'03 + http://www.usenix.org/events/lisa03/tech/robertson.html The footer + of an inuse chunk holds the xor of its mstate and a random seed, + that is checked upon calls to free() and realloc(). This is + (probabalistically) unguessable from outside the program, but can be + computed by any code successfully malloc'ing any chunk, so does not + itself provide protection against code that has already broken + security through some other means. Unlike Robertson et al, we + always dynamically check addresses of all offset chunks (previous, + next, etc). This turns out to be cheaper than relying on hashes. +*/ + +#if !INSECURE +/* Check if address a is at least as high as any from MORECORE or MMAP */ +#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) +/* Check if address of next chunk n is higher than base chunk p */ +#define ok_next(p, n) ((char*)(p) < (char*)(n)) +/* Check if p has inuse status */ +#define ok_inuse(p) is_inuse(p) +/* Check if p has its pinuse bit on */ +#define ok_pinuse(p) pinuse(p) + +#else /* !INSECURE */ +#define ok_address(M, a) (1) +#define ok_next(b, n) (1) +#define ok_inuse(p) (1) +#define ok_pinuse(p) (1) +#endif /* !INSECURE */ + +#if (FOOTERS && !INSECURE) +/* Check if (alleged) mstate m has expected magic field */ +#define ok_magic(M) ((M)->magic == mparams.magic) +#else /* (FOOTERS && !INSECURE) */ +#define ok_magic(M) (1) +#endif /* (FOOTERS && !INSECURE) */ + +/* In gcc, use __builtin_expect to minimize impact of checks */ +#if !INSECURE +#if defined(__GNUC__) && __GNUC__ >= 3 +#define RTCHECK(e) __builtin_expect(e, 1) +#else /* GNUC */ +#define RTCHECK(e) (e) +#endif /* GNUC */ +#else /* !INSECURE */ +#define RTCHECK(e) (1) +#endif /* !INSECURE */ + +/* macros to set up inuse chunks with or without footers */ + +#if !FOOTERS + +#define mark_inuse_foot(M,p,s) + +/* Macros for setting head/foot of non-mmapped chunks */ + +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + +#else /* FOOTERS */ + +/* Set foot of inuse chunk to be xor of mstate and seed */ +#define mark_inuse_foot(M,p,s)\ + (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) + +#define get_mstate_for(p)\ + ((mstate)(((mchunkptr)((char*)(p) +\ + (chunksize(p))))->prev_foot ^ mparams.magic)) + +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M,p,s)) + +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ + mark_inuse_foot(M,p,s)) + +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + mark_inuse_foot(M, p, s)) + +#endif /* !FOOTERS */ + +/* ---------------------------- setting mparams -------------------------- */ + +/* Initialize mparams */ +static int init_mparams(void) { +#ifdef NEED_GLOBAL_LOCK_INIT + if (malloc_global_mutex_status <= 0) + init_malloc_global_mutex(); +#endif + + ACQUIRE_MALLOC_GLOBAL_LOCK(); + if (mparams.magic == 0) { + size_t magic; + size_t psize; + size_t gsize; + +#ifndef WIN32 + psize = malloc_getpagesize; + gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); +#else /* WIN32 */ + { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + psize = system_info.dwPageSize; + gsize = ((DEFAULT_GRANULARITY != 0)? + DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); + } +#endif /* WIN32 */ + + /* Sanity-check configuration: + size_t must be unsigned and as wide as pointer type. + ints must be at least 4 bytes. + alignment must be at least 8. + Alignment, min chunk size, and page size must all be powers of 2. + */ + if ((sizeof(size_t) != sizeof(char*)) || + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((gsize & (gsize-SIZE_T_ONE)) != 0) || + ((psize & (psize-SIZE_T_ONE)) != 0)) + ABORT; + + mparams.granularity = gsize; + mparams.page_size = psize; + mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; + mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; +#if MORECORE_CONTIGUOUS + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; +#else /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; +#endif /* MORECORE_CONTIGUOUS */ + +#if !ONLY_MSPACES + /* Set up lock for main malloc area */ + gm->mflags = mparams.default_mflags; + (void)INITIAL_LOCK(&gm->mutex); +#endif + + { +#if USE_DEV_RANDOM + int fd; + unsigned char buf[sizeof(size_t)]; + /* Try to use /dev/urandom, else fall back on using time */ + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && + read(fd, buf, sizeof(buf)) == sizeof(buf)) { + magic = *((size_t *) buf); + close(fd); + } + else +#endif /* USE_DEV_RANDOM */ +#ifdef WIN32 + magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); +#elif defined(LACKS_TIME_H) + magic = (size_t)&magic ^ (size_t)0x55555555U; +#else + magic = (size_t)(time(0) ^ (size_t)0x55555555U); +#endif + magic |= (size_t)8U; /* ensure nonzero */ + magic &= ~(size_t)7U; /* improve chances of fault for bad values */ + /* Until memory modes commonly available, use volatile-write */ + (*(volatile size_t *)(&(mparams.magic))) = magic; + } + } + + RELEASE_MALLOC_GLOBAL_LOCK(); + return 1; +} + +/* support for mallopt */ +static int change_mparam(int param_number, int value) { + size_t val; + ensure_initialization(); + val = (value == -1)? MAX_SIZE_T : (size_t)value; + switch(param_number) { + case M_TRIM_THRESHOLD: + mparams.trim_threshold = val; + return 1; + case M_GRANULARITY: + if (val >= mparams.page_size && ((val & (val-1)) == 0)) { + mparams.granularity = val; + return 1; + } + else + return 0; + case M_MMAP_THRESHOLD: + mparams.mmap_threshold = val; + return 1; + default: + return 0; + } +} + +#if DEBUG +/* ------------------------- Debugging Support --------------------------- */ + +/* Check properties of any chunk, whether free, inuse, mmapped etc */ +static void do_check_any_chunk(mstate m, mchunkptr p) { + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); +} + +/* Check properties of top chunk */ +static void do_check_top_chunk(mstate m, mchunkptr p) { + msegmentptr sp = segment_holding(m, (char*)p); + size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ + assert(sp != 0); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(sz == m->topsize); + assert(sz > 0); + assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); + assert(pinuse(p)); + assert(!pinuse(chunk_plus_offset(p, sz))); +} + +/* Check properties of (inuse) mmapped chunks */ +static void do_check_mmapped_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); + assert(is_mmapped(p)); + assert(use_mmap(m)); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(!is_small(sz)); + assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); + assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); + assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); +} + +/* Check properties of inuse chunks */ +static void do_check_inuse_chunk(mstate m, mchunkptr p) { + do_check_any_chunk(m, p); + assert(is_inuse(p)); + assert(next_pinuse(p)); + /* If not pinuse and not mmapped, previous chunk has OK offset */ + assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); + if (is_mmapped(p)) + do_check_mmapped_chunk(m, p); +} + +/* Check properties of free chunks */ +static void do_check_free_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + mchunkptr next = chunk_plus_offset(p, sz); + do_check_any_chunk(m, p); + assert(!is_inuse(p)); + assert(!next_pinuse(p)); + assert (!is_mmapped(p)); + if (p != m->dv && p != m->top) { + if (sz >= MIN_CHUNK_SIZE) { + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(is_aligned(chunk2mem(p))); + assert(next->prev_foot == sz); + assert(pinuse(p)); + assert (next == m->top || is_inuse(next)); + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_T_SIZE */ + assert(sz == SIZE_T_SIZE); + } +} + +/* Check properties of malloced chunks at the point they are malloced */ +static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t sz = p->head & ~INUSE_BITS; + do_check_inuse_chunk(m, p); + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(sz >= MIN_CHUNK_SIZE); + assert(sz >= s); + /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ + assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); + } +} + +/* Check a tree and its subtrees. */ +static void do_check_tree(mstate m, tchunkptr t) { + tchunkptr head = 0; + tchunkptr u = t; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; + compute_tree_index(tsize, idx); + assert(tindex == idx); + assert(tsize >= MIN_LARGE_SIZE); + assert(tsize >= minsize_for_tree_index(idx)); + assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); + + do { /* traverse through chain of same-sized nodes */ + do_check_any_chunk(m, ((mchunkptr)u)); + assert(u->index == tindex); + assert(chunksize(u) == tsize); + assert(!is_inuse(u)); + assert(!next_pinuse(u)); + assert(u->fd->bk == u); + assert(u->bk->fd == u); + if (u->parent == 0) { + assert(u->child[0] == 0); + assert(u->child[1] == 0); + } + else { + assert(head == 0); /* only one node on chain has parent */ + head = u; + assert(u->parent != u); + assert (u->parent->child[0] == u || + u->parent->child[1] == u || + *((tbinptr*)(u->parent)) == u); + if (u->child[0] != 0) { + assert(u->child[0]->parent == u); + assert(u->child[0] != u); + do_check_tree(m, u->child[0]); + } + if (u->child[1] != 0) { + assert(u->child[1]->parent == u); + assert(u->child[1] != u); + do_check_tree(m, u->child[1]); + } + if (u->child[0] != 0 && u->child[1] != 0) { + assert(chunksize(u->child[0]) < chunksize(u->child[1])); + } + } + u = u->fd; + } while (u != t); + assert(head != 0); +} + +/* Check all the chunks in a treebin. */ +static void do_check_treebin(mstate m, bindex_t i) { + tbinptr* tb = treebin_at(m, i); + tchunkptr t = *tb; + int empty = (m->treemap & (1U << i)) == 0; + if (t == 0) + assert(empty); + if (!empty) + do_check_tree(m, t); +} + +/* Check all the chunks in a smallbin. */ +static void do_check_smallbin(mstate m, bindex_t i) { + sbinptr b = smallbin_at(m, i); + mchunkptr p = b->bk; + unsigned int empty = (m->smallmap & (1U << i)) == 0; + if (p == b) + assert(empty); + if (!empty) { + for (; p != b; p = p->bk) { + size_t size = chunksize(p); + mchunkptr q; + /* each chunk claims to be free */ + do_check_free_chunk(m, p); + /* chunk belongs in bin */ + assert(small_index(size) == i); + assert(p->bk == b || chunksize(p->bk) == chunksize(p)); + /* chunk is followed by an inuse chunk */ + q = next_chunk(p); + if (q->head != FENCEPOST_HEAD) + do_check_inuse_chunk(m, q); + } + } +} + +/* Find x in a bin. Used in other check functions. */ +static int bin_find(mstate m, mchunkptr x) { + size_t size = chunksize(x); + if (is_small(size)) { + bindex_t sidx = small_index(size); + sbinptr b = smallbin_at(m, sidx); + if (smallmap_is_marked(m, sidx)) { + mchunkptr p = b; + do { + if (p == x) + return 1; + } while ((p = p->fd) != b); + } + } + else { + bindex_t tidx; + compute_tree_index(size, tidx); + if (treemap_is_marked(m, tidx)) { + tchunkptr t = *treebin_at(m, tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); + while (t != 0 && chunksize(t) != size) { + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + sizebits <<= 1; + } + if (t != 0) { + tchunkptr u = t; + do { + if (u == (tchunkptr)x) + return 1; + } while ((u = u->fd) != t); + } + } + } + return 0; +} + +/* Traverse each chunk and check it; return total */ +static size_t traverse_and_check(mstate m) { + size_t sum = 0; + if (is_initialized(m)) { + msegmentptr s = &m->seg; + sum += m->topsize + TOP_FOOT_SIZE; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + mchunkptr lastq = 0; + assert(pinuse(q)); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + sum += chunksize(q); + if (is_inuse(q)) { + assert(!bin_find(m, q)); + do_check_inuse_chunk(m, q); + } + else { + assert(q == m->dv || bin_find(m, q)); + assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ + do_check_free_chunk(m, q); + } + lastq = q; + q = next_chunk(q); + } + s = s->next; + } + } + return sum; +} + + +/* Check all properties of malloc_state. */ +static void do_check_malloc_state(mstate m) { + bindex_t i; + size_t total; + /* check bins */ + for (i = 0; i < NSMALLBINS; ++i) + do_check_smallbin(m, i); + for (i = 0; i < NTREEBINS; ++i) + do_check_treebin(m, i); + + if (m->dvsize != 0) { /* check dv chunk */ + do_check_any_chunk(m, m->dv); + assert(m->dvsize == chunksize(m->dv)); + assert(m->dvsize >= MIN_CHUNK_SIZE); + assert(bin_find(m, m->dv) == 0); + } + + if (m->top != 0) { /* check top chunk */ + do_check_top_chunk(m, m->top); + /*assert(m->topsize == chunksize(m->top)); redundant */ + assert(m->topsize > 0); + assert(bin_find(m, m->top) == 0); + } + + total = traverse_and_check(m); + assert(total <= m->footprint); + assert(m->footprint <= m->max_footprint); +} +#endif /* DEBUG */ + +/* ----------------------------- statistics ------------------------------ */ + +#if !NO_MALLINFO +static struct mallinfo internal_mallinfo(mstate m) { + struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ensure_initialization(); + if (!PREACTION(m)) { + check_malloc_state(m); + if (is_initialized(m)) { + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; + msegmentptr s = &m->seg; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + sum += sz; + if (!is_inuse(q)) { + mfree += sz; + ++nfree; + } + q = next_chunk(q); + } + s = s->next; + } + + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; + nm.uordblks = m->footprint - mfree; + nm.fordblks = mfree; + nm.keepcost = m->topsize; + } + + POSTACTION(m); + } + return nm; +} +#endif /* !NO_MALLINFO */ + +#if !NO_MALLOC_STATS +static void internal_malloc_stats(mstate m) { + ensure_initialization(); + if (!PREACTION(m)) { + size_t maxfp = 0; + size_t fp = 0; + size_t used = 0; + check_malloc_state(m); + if (is_initialized(m)) { + msegmentptr s = &m->seg; + maxfp = m->max_footprint; + fp = m->footprint; + used = fp - (m->topsize + TOP_FOOT_SIZE); + + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!is_inuse(q)) + used -= chunksize(q); + q = next_chunk(q); + } + s = s->next; + } + } + POSTACTION(m); /* drop lock */ + fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); + fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); + fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); + } +} +#endif /* NO_MALLOC_STATS */ + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* + Various forms of linking and unlinking are defined as macros. Even + the ones for trees, which are very long but have very short typical + paths. This is ugly but reduces reliance on inlining support of + compilers. +*/ + +/* Link a free chunk into a smallbin */ +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + assert(S >= MIN_CHUNK_SIZE);\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, B->fd)))\ + F = B->fd;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} + +/* Unlink a chunk from a smallbin */ +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (RTCHECK(F == smallbin_at(M,I) || (ok_address(M, F) && F->bk == P))) { \ + if (B == F) {\ + clear_smallmap(M, I);\ + }\ + else if (RTCHECK(B == smallbin_at(M,I) ||\ + (ok_address(M, B) && B->fd == P))) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Unlink the first chunk from a smallbin */ +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (B == F) {\ + clear_smallmap(M, I);\ + }\ + else if (RTCHECK(ok_address(M, F) && F->bk == P)) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + assert(is_small(DVS));\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +#define insert_large_chunk(M, X, S) {\ + tbinptr* H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + }\ + else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0)\ + T = *C;\ + else if (RTCHECK(ok_address(M, C))) {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + else {\ + tchunkptr F = T->fd;\ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + }\ + }\ +} + +/* + Unlink steps: + + 1. If x is a chained node, unlink it from its same-sized fd/bk links + and choose its bk node as its replacement. + 2. If x was the last node of its size, but not a leaf node, it must + be replaced with a leaf node (not merely one with an open left or + right), to make sure that lefts and rights of descendents + correspond properly to bit masks. We use the rightmost descendent + of x. We could use any other leaf, but this is easy to locate and + tends to counteract removal of leftmosts elsewhere, and so keeps + paths shorter than minimally guaranteed. This doesn't loop much + because on average a node in a tree is near the bottom. + 3. If x is the base of a chain (i.e., has parent links) relink + x's parent and children to x's replacement (or null if none). +*/ + +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) {\ + F->bk = R;\ + R->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + tchunkptr* RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr* CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ + }\ + if (RTCHECK(ok_address(M, RP)))\ + *RP = 0;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + }\ + if (XP != 0) {\ + tbinptr* H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + }\ + else if (RTCHECK(ok_address(M, XP))) {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + if (R != 0) {\ + if (RTCHECK(ok_address(M, R))) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + if (RTCHECK(ok_address(M, C0))) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + if ((C1 = X->child[1]) != 0) {\ + if (RTCHECK(ok_address(M, C1))) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ +} + +/* Relays to large vs small bin operations */ + +#define insert_chunk(M, P, S)\ + if (is_small(S)) insert_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) unlink_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + + +/* Relays to internal calls to malloc/free from realloc, memalign etc */ + +#if ONLY_MSPACES +#define internal_malloc(m, b) mspace_malloc(m, b) +#define internal_free(m, mem) mspace_free(m,mem); +#else /* ONLY_MSPACES */ +#if MSPACES +#define internal_malloc(m, b)\ + ((m == gm)? dlmalloc(b) : mspace_malloc(m, b)) +#define internal_free(m, mem)\ + if (m == gm) dlfree(mem); else mspace_free(m,mem); +#else /* MSPACES */ +#define internal_malloc(m, b) dlmalloc(b) +#define internal_free(m, mem) dlfree(mem) +#endif /* MSPACES */ +#endif /* ONLY_MSPACES */ + +/* ----------------------- Direct-mmapping chunks ----------------------- */ + +/* + Directly mmapped chunks are set up with an offset to the start of + the mmapped region stored in the prev_foot field of the chunk. This + allows reconstruction of the required argument to MUNMAP when freed, + and also allows adjustment of the returned chunk to meet alignment + requirements (especially in memalign). +*/ + +/* Malloc using mmap */ +static void* mmap_alloc(mstate m, size_t nb) { + size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (m->footprint_limit != 0) { + size_t fp = m->footprint + mmsize; + if (fp <= m->footprint || fp > m->footprint_limit) + return 0; + } + if (mmsize > nb) { /* Check for wrap around 0 */ + char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); + if (mm != CMFAIL) { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - MMAP_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->prev_foot = offset; + p->head = psize; + mark_inuse_foot(m, p, psize); + chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + + if (m->least_addr == 0 || mm < m->least_addr) + m->least_addr = mm; + if ((m->footprint += mmsize) > m->max_footprint) + m->max_footprint = m->footprint; + assert(is_aligned(chunk2mem(p))); + check_mmapped_chunk(m, p); + return chunk2mem(p); + } + } + return 0; +} + +/* Realloc using mmap */ +static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb, int flags) { + size_t oldsize = chunksize(oldp); + if (is_small(nb)) /* Can't shrink mmap regions below small size */ + return 0; + /* Keep old chunk if big enough but not too big */ + if (oldsize >= nb + SIZE_T_SIZE && + (oldsize - nb) <= (mparams.granularity << 1)) + return oldp; + else { + size_t offset = oldp->prev_foot; + size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; + size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + char* cp = (char*)CALL_MREMAP((char*)oldp - offset, + oldmmsize, newmmsize, flags); + if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - MMAP_FOOT_PAD; + newp->head = psize; + mark_inuse_foot(m, newp, psize); + chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + + if (cp < m->least_addr) + m->least_addr = cp; + if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) + m->max_footprint = m->footprint; + check_mmapped_chunk(m, newp); + return newp; + } + } + return 0; +} + + +/* -------------------------- mspace management -------------------------- */ + +/* Initialize top chunk and its size */ +static void init_top(mstate m, mchunkptr p, size_t psize) { + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char*)p + offset); + psize -= offset; + + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; + m->trim_check = mparams.trim_threshold; /* reset on each update */ +} + +/* Initialize bins for a new mstate that is otherwise zeroed out */ +static void init_bins(mstate m) { + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; ++i) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} + +#if PROCEED_ON_ERROR + +/* default corruption action */ +static void reset_on_error(mstate m) { + int i; + ++malloc_corruption_error_count; + /* Reinitialize fields to forget about all memory */ + m->smallmap = m->treemap = 0; + m->dvsize = m->topsize = 0; + m->seg.base = 0; + m->seg.size = 0; + m->seg.next = 0; + m->top = m->dv = 0; + for (i = 0; i < NTREEBINS; ++i) + *treebin_at(m, i) = 0; + init_bins(m); +} +#endif /* PROCEED_ON_ERROR */ + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +static void* prepend_alloc(mstate m, char* newbase, char* oldbase, + size_t nb) { + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (char*)oldfirst - (char*)p; + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + assert((char*)oldfirst > (char*)q); + assert(pinuse(oldfirst)); + assert(qsize >= MIN_CHUNK_SIZE); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + check_top_chunk(m, q); + } + else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; + m->dv = q; + set_size_and_pinuse_of_free_chunk(q, dsize); + } + else { + if (!is_inuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize); + check_free_chunk(m, q); + } + + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); +} + +/* Add a segment to hold a new noncontiguous region */ +static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { + /* Determine locations and sizes of segment, fenceposts, old top */ + char* old_top = (char*)m->top; + msegmentptr oldsp = segment_holding(m, old_top); + char* old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char* asp = rawsp + offset; + char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + int nfences = 0; + + /* reset top to new space */ + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + + /* Set up segment record */ + assert(is_aligned(ss)); + set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); + *ss = m->seg; /* Push current record */ + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmapped; + m->seg.next = ss; + + /* Insert trailing fenceposts */ + for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); + p->head = FENCEPOST_HEAD; + ++nfences; + if ((char*)(&(nextp->head)) < old_end) + p = nextp; + else + break; + } + assert(nfences >= 2); + + /* Insert the rest of old top into a bin as an ordinary free chunk */ + if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; + size_t psize = csp - old_top; + mchunkptr tn = chunk_plus_offset(q, psize); + set_free_with_pinuse(q, psize, tn); + insert_chunk(m, q, psize); + } + + check_top_chunk(m, m->top); +} + +/* -------------------------- System allocation -------------------------- */ + +/* Get memory from system using MORECORE or MMAP */ +static void* sys_alloc(mstate m, size_t nb) { + char* tbase = CMFAIL; + size_t tsize = 0; + flag_t mmap_flag = 0; + size_t asize; /* allocation size */ + + ensure_initialization(); + + /* Directly map large chunks, but only if already initialized */ + if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { + void* mem = mmap_alloc(m, nb); + if (mem != 0) + return mem; + } + + asize = granularity_align(nb + SYS_ALLOC_PADDING); + if (asize <= nb) + return 0; /* wraparound */ + if (m->footprint_limit != 0) { + size_t fp = m->footprint + asize; + if (fp <= m->footprint || fp > m->footprint_limit) + return 0; + } + + /* + Try getting memory in any of three ways (in most-preferred to + least-preferred order): + 1. A call to MORECORE that can normally contiguously extend memory. + (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or + or main space is mmapped or a previous contiguous call failed) + 2. A call to MMAP new space (disabled if not HAVE_MMAP). + Note that under the default settings, if MORECORE is unable to + fulfill a request, and HAVE_MMAP is true, then mmap is + used as a noncontiguous system allocator. This is a useful backup + strategy for systems with holes in address spaces -- in this case + sbrk cannot contiguously expand the heap, but mmap may be able to + find space. + 3. A call to MORECORE that cannot usually contiguously extend memory. + (disabled if not HAVE_MORECORE) + + In all cases, we need to request enough bytes from system to ensure + we can malloc nb bytes upon success, so pad with enough space for + top_foot, plus alignment-pad to make sure we don't lose bytes if + not on boundary, and round this up to a granularity unit. + */ + + if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { + char* br = CMFAIL; + msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); + ACQUIRE_MALLOC_GLOBAL_LOCK(); + + if (ss == 0) { /* First time through or recovery */ + char* base = (char*)CALL_MORECORE(0); + if (base != CMFAIL) { + size_t fp; + /* Adjust to end on a page boundary */ + if (!is_page_aligned(base)) + asize += (page_align((size_t)base) - (size_t)base); + fp = m->footprint + asize; /* recheck limits */ + if (asize > nb && asize < HALF_MAX_SIZE_T && + (m->footprint_limit == 0 || + (fp > m->footprint && fp <= m->footprint_limit)) && + (br = (char*)(CALL_MORECORE(asize))) == base) { + tbase = base; + tsize = asize; + } + } + } + else { + /* Subtract out existing available top space from MORECORE request. */ + asize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); + /* Use mem here only if it did continuously extend old space */ + if (asize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) { + tbase = br; + tsize = asize; + } + } + + if (tbase == CMFAIL) { /* Cope with partial failure */ + if (br != CMFAIL) { /* Try to use/extend the space we did get */ + if (asize < HALF_MAX_SIZE_T && + asize < nb + SYS_ALLOC_PADDING) { + size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - asize); + if (esize < HALF_MAX_SIZE_T) { + char* end = (char*)CALL_MORECORE(esize); + if (end != CMFAIL) + asize += esize; + else { /* Can't use; try to release */ + (void) CALL_MORECORE(-asize); + br = CMFAIL; + } + } + } + } + if (br != CMFAIL) { /* Use the space we did get */ + tbase = br; + tsize = asize; + } + else + disable_contiguous(m); /* Don't try contiguous path in the future */ + } + + RELEASE_MALLOC_GLOBAL_LOCK(); + } + + if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ + char* mp = (char*)(CALL_MMAP(asize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = asize; + mmap_flag = USE_MMAP_BIT; + } + } + + if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ + if (asize < HALF_MAX_SIZE_T) { + char* br = CMFAIL; + char* end = CMFAIL; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + br = (char*)(CALL_MORECORE(asize)); + end = (char*)(CALL_MORECORE(0)); + RELEASE_MALLOC_GLOBAL_LOCK(); + if (br != CMFAIL && end != CMFAIL && br < end) { + size_t ssize = end - br; + if (ssize > nb + TOP_FOOT_SIZE) { + tbase = br; + tsize = ssize; + } + } + } + } + + if (tbase != CMFAIL) { + + if ((m->footprint += tsize) > m->max_footprint) + m->max_footprint = m->footprint; + + if (!is_initialized(m)) { /* first-time initialization */ + if (m->least_addr == 0 || tbase < m->least_addr) + m->least_addr = tbase; + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmap_flag; + m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; + init_bins(m); +#if !ONLY_MSPACES + if (is_global(m)) + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + else +#endif + { + /* Offset top by embedded malloc_state */ + mchunkptr mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); + } + } + + else { + /* Try to merge with an existing segment */ + msegmentptr sp = &m->seg; + /* Only consider most recent segment if traversal suppressed */ + while (sp != 0 && tbase != sp->base + sp->size) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag && + segment_holds(sp, m->top)) { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); + } + else { + if (tbase < m->least_addr) + m->least_addr = tbase; + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag) { + char* oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } + else + add_segment(m, tbase, tsize, mmap_flag); + } + } + + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; + mchunkptr p = m->top; + mchunkptr r = m->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + check_top_chunk(m, m->top); + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); + } + } + + MALLOC_FAILURE_ACTION; + return 0; +} + +/* ----------------------- system deallocation -------------------------- */ + +/* Unmap and unlink any mmapped segments that don't contain used chunks */ +static size_t release_unused_segments(mstate m) { + size_t released = 0; + size_t nsegs = 0; + msegmentptr pred = &m->seg; + msegmentptr sp = pred->next; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + msegmentptr next = sp->next; + ++nsegs; + if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { + mchunkptr p = align_as_chunk(base); + size_t psize = chunksize(p); + /* Can unmap if first chunk holds entire segment and not pinned */ + if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + assert(segment_holds(sp, (char*)sp)); + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; + m->footprint -= size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } + else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } + } + } + if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ + break; + pred = sp; + sp = next; + } + /* Reset check counter */ + m->release_checks = ((nsegs > MAX_RELEASE_CHECK_RATE)? + nsegs : MAX_RELEASE_CHECK_RATE); + return released; +} + +static int sys_trim(mstate m, size_t pad) { + size_t released = 0; + ensure_initialization(); + if (pad < MAX_REQUEST && is_initialized(m)) { + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ + size_t unit = mparams.granularity; + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - + SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char*)m->top); + + if (!is_extern_segment(sp)) { + if (is_mmapped_segment(sp)) { + if (HAVE_MMAP && + sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-variable" + size_t newsize = sp->size - extra; +#pragma clang diagnostic pop + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || + (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + { + /* Make sure end of memory is where we last set it. */ + char* old_br = (char*)(CALL_MORECORE(0)); + if (old_br == sp->base + sp->size) { + char* rel_br = (char*)(CALL_MORECORE(-extra)); + char* new_br = (char*)(CALL_MORECORE(0)); + if (rel_br != CMFAIL && new_br < old_br) + released = old_br - new_br; + } + } + RELEASE_MALLOC_GLOBAL_LOCK(); + } + } + + if (released != 0) { + sp->size -= released; + m->footprint -= released; + init_top(m, m->top, m->topsize - released); + check_top_chunk(m, m->top); + } + } + + /* Unmap any unused mmapped segments */ + if (HAVE_MMAP) + released += release_unused_segments(m); + + /* On failure, disable autotrim to avoid repeated failed future calls */ + if (released == 0 && m->topsize > m->trim_check) + m->trim_check = MAX_SIZE_T; + } + + return (released != 0)? 1 : 0; +} + +/* Consolidate and bin a chunk. Differs from exported versions + of free mainly in that the chunk need not be marked as inuse. +*/ +static void dispose_chunk(mstate m, mchunkptr p, size_t psize) { + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + mchunkptr prev; + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + m->footprint -= psize; + return; + } + prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */ + if (p != m->dv) { + unlink_chunk(m, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + m->dvsize = psize; + set_free_with_pinuse(p, psize, next); + return; + } + } + else { + CORRUPTION_ERROR_ACTION(m); + return; + } + } + if (RTCHECK(ok_address(m, next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == m->top) { + size_t tsize = m->topsize += psize; + m->top = p; + p->head = tsize | PINUSE_BIT; + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + return; + } + else if (next == m->dv) { + size_t dsize = m->dvsize += psize; + m->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + return; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(m, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == m->dv) { + m->dvsize = psize; + return; + } + } + } + else { + set_free_with_pinuse(p, psize, next); + } + insert_chunk(m, p, psize); + } + else { + CORRUPTION_ERROR_ACTION(m); + } +} + +/* ---------------------------- malloc --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +static void* tmalloc_large(mstate m, size_t nb) { + tchunkptr v = 0; + size_t rsize = -nb; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) { + t = rst; /* set t to least subtree holding sizes > nb */ + break; + } + sizebits <<= 1; + } + } + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) { + bindex_t i; + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + t = *treebin_at(m, i); + } + } + + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + + /* If dv is a better fit, return 0 so malloc will use it */ + if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { + if (RTCHECK(ok_address(m, v))) { /* split */ + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + } + CORRUPTION_ERROR_ACTION(m); + } + return 0; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +static void* tmalloc_small(mstate m, size_t nb) { + tchunkptr t, v; + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); + compute_bit2idx(leastbit, i); + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + if (RTCHECK(ok_address(m, v))) { + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); + } + return chunk2mem(v); + } + } + + CORRUPTION_ERROR_ACTION(m); + return 0; +} + +#if !ONLY_MSPACES + +void* dlmalloc(size_t bytes) { + /* + Basic algorithm: + If a small request (< 256 bytes minus per-chunk overhead): + 1. If one exists, use a remainderless chunk in associated smallbin. + (Remainderless means that there are too few excess bytes to + represent as a chunk.) + 2. If it is big enough, use the dv chunk, which is normally the + chunk adjacent to the one used for the most recent small request. + 3. If one exists, split the smallest available chunk in a bin, + saving remainder in dv. + 4. If it is big enough, use the top chunk. + 5. If available, get memory from system and use it + Otherwise, for a large request: + 1. Find the smallest available binned chunk that fits, and use it + if it is better fitting than dv chunk, splitting if necessary. + 2. If better fitting than any binned chunk, use the dv chunk. + 3. If it is big enough, use the top chunk. + 4. If request size >= mmap threshold, try to directly mmap this chunk. + 5. If available, get memory from system and use it + + The ugly goto's here ensure that postaction occurs along all paths. + */ + +#if USE_LOCKS + ensure_initialization(); /* initialize in sys_alloc if not using locks */ +#endif + + if (!PREACTION(gm)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = gm->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(gm, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(gm, b, p, idx); + set_inuse_and_pinuse(gm, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb > gm->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(gm, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(gm, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(gm, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(gm, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + + if (nb <= gm->dvsize) { + size_t rsize = gm->dvsize - nb; + mchunkptr p = gm->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = gm->dv = chunk_plus_offset(p, nb); + gm->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + } + else { /* exhaust dv */ + size_t dvs = gm->dvsize; + gm->dvsize = 0; + gm->dv = 0; + set_inuse_and_pinuse(gm, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; + mchunkptr p = gm->top; + mchunkptr r = gm->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + mem = chunk2mem(p); + check_top_chunk(gm, gm->top); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + mem = sys_alloc(gm, nb); + + postaction: + POSTACTION(gm); + return mem; + } + + return 0; +} + +/* ---------------------------- free --------------------------- */ + +void dlfree(void* mem) { + /* + Consolidate freed chunks with preceeding or succeeding bordering + free chunks, if they exist, and then place in a bin. Intermixed + with special cases for top, dv, mmapped chunks, and usage errors. + */ + + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } +#else /* FOOTERS */ +#define fm gm +#endif /* FOOTERS */ + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +#if !FOOTERS +#undef fm +#endif /* FOOTERS */ +} + +void* dlcalloc(size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = dlmalloc(req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +#endif /* !ONLY_MSPACES */ + +/* ------------ Internal support for realloc, memalign, etc -------------- */ + +/* Try to realloc; only in-place unless can_move true */ +static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb, + int can_move) { + mchunkptr newp = 0; + size_t oldsize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, oldsize); + if (RTCHECK(ok_address(m, p) && ok_inuse(p) && + ok_next(p, next) && ok_pinuse(next))) { + if (is_mmapped(p)) { + newp = mmap_resize(m, p, nb, can_move); + } + else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ + mchunkptr r = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, r, rsize); + dispose_chunk(m, r, rsize); + } + newp = p; + } + else if (next == m->top) { /* extend into top */ + if (oldsize + m->topsize > nb) { + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = p; + } + } + else if (next == m->dv) { /* extend into dv */ + size_t dvs = m->dvsize; + if (oldsize + dvs >= nb) { + size_t dsize = oldsize + dvs - nb; + if (dsize >= MIN_CHUNK_SIZE) { + mchunkptr r = chunk_plus_offset(p, nb); + mchunkptr n = chunk_plus_offset(r, dsize); + set_inuse(m, p, nb); + set_size_and_pinuse_of_free_chunk(r, dsize); + clear_pinuse(n); + m->dvsize = dsize; + m->dv = r; + } + else { /* exhaust dv */ + size_t newsize = oldsize + dvs; + set_inuse(m, p, newsize); + m->dvsize = 0; + m->dv = 0; + } + newp = p; + } + } + else if (!cinuse(next)) { /* extend into next free chunk */ + size_t nextsize = chunksize(next); + if (oldsize + nextsize >= nb) { + size_t rsize = oldsize + nextsize - nb; + unlink_chunk(m, next, nextsize); + if (rsize < MIN_CHUNK_SIZE) { + size_t newsize = oldsize + nextsize; + set_inuse(m, p, newsize); + } + else { + mchunkptr r = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, r, rsize); + dispose_chunk(m, r, rsize); + } + newp = p; + } + } + } + else { + USAGE_ERROR_ACTION(m, oldmem); + } + return newp; +} + +static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { + void* mem = 0; + if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ + alignment = MIN_CHUNK_SIZE; + if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ + size_t a = MALLOC_ALIGNMENT << 1; + while (a < alignment) a <<= 1; + alignment = a; + } + if (bytes >= MAX_REQUEST - alignment) { + if (m != 0) { /* Test isn't needed but avoids compiler warning */ + MALLOC_FAILURE_ACTION; + } + } + else { + size_t nb = request2size(bytes); + size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; + mem = internal_malloc(m, req); + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (PREACTION(m)) + return 0; + if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ + /* + Find an aligned spot inside chunk. Since we need to give + back leading space in a chunk of at least MIN_CHUNK_SIZE, if + the first calculation places us at a spot with less than + MIN_CHUNK_SIZE leader, we can move to the next aligned spot. + We've allocated enough total room so that this is always + possible. + */ + char* br = (char*)mem2chunk((size_t)(((size_t)((char*)mem + alignment - + SIZE_T_ONE)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? + br : br+alignment; + mchunkptr newp = (mchunkptr)pos; + size_t leadsize = pos - (char*)(p); + size_t newsize = chunksize(p) - leadsize; + + if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ + newp->prev_foot = p->prev_foot + leadsize; + newp->head = newsize; + } + else { /* Otherwise, give back leader, use the rest */ + set_inuse(m, newp, newsize); + set_inuse(m, p, leadsize); + dispose_chunk(m, p, leadsize); + } + p = newp; + } + + /* Give back spare room at the end */ + if (!is_mmapped(p)) { + size_t size = chunksize(p); + if (size > nb + MIN_CHUNK_SIZE) { + size_t remainder_size = size - nb; + mchunkptr remainder = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, remainder, remainder_size); + dispose_chunk(m, remainder, remainder_size); + } + } + + mem = chunk2mem(p); + assert (chunksize(p) >= nb); + assert(((size_t)mem & (alignment - 1)) == 0); + check_inuse_chunk(m, p); + POSTACTION(m); + } + } + return mem; +} + +/* + Common support for independent_X routines, handling + all of the combinations that can result. + The opts arg has: + bit 0 set if all elements are same size (using sizes[0]) + bit 1 set if elements should be zeroed +*/ +static void** ialloc(mstate m, + size_t n_elements, + size_t* sizes, + int opts, + void* chunks[]) { + + size_t element_size; /* chunksize of each element, if all same */ + size_t contents_size; /* total size of elements */ + size_t array_size; /* request size of pointer array */ + void* mem; /* malloced aggregate space */ + mchunkptr p; /* corresponding chunk */ + size_t remainder_size; /* remaining bytes while splitting */ + void** marray; /* either "chunks" or malloced ptr array */ + mchunkptr array_chunk; /* chunk for malloced ptr array */ + flag_t was_enabled; /* to disable mmap */ + size_t size; + size_t i; + + ensure_initialization(); + /* compute array length, if needed */ + if (chunks != 0) { + if (n_elements == 0) + return chunks; /* nothing to do */ + marray = chunks; + array_size = 0; + } + else { + /* if empty req, must still return chunk representing empty array */ + if (n_elements == 0) + return (void**)internal_malloc(m, 0); + marray = 0; + array_size = request2size(n_elements * (sizeof(void*))); + } + + /* compute total element size */ + if (opts & 0x1) { /* all-same-size */ + element_size = request2size(*sizes); + contents_size = n_elements * element_size; + } + else { /* add up all the sizes */ + element_size = 0; + contents_size = 0; + for (i = 0; i != n_elements; ++i) + contents_size += request2size(sizes[i]); + } + + size = contents_size + array_size; + + /* + Allocate the aggregate chunk. First disable direct-mmapping so + malloc won't use it, since we would not be able to later + free/realloc space internal to a segregated mmap region. + */ + was_enabled = use_mmap(m); + disable_mmap(m); + mem = internal_malloc(m, size - CHUNK_OVERHEAD); + if (was_enabled) + enable_mmap(m); + if (mem == 0) + return 0; + + if (PREACTION(m)) return 0; + p = mem2chunk(mem); + remainder_size = chunksize(p); + + assert(!is_mmapped(p)); + + if (opts & 0x2) { /* optionally clear the elements */ + memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); + } + + /* If not provided, allocate the pointer array as final part of chunk */ + if (marray == 0) { + size_t array_chunk_size; + array_chunk = chunk_plus_offset(p, contents_size); + array_chunk_size = remainder_size - contents_size; + marray = (void**) (chunk2mem(array_chunk)); + set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); + remainder_size = contents_size; + } + + /* split out elements */ + for (i = 0; ; ++i) { + marray[i] = chunk2mem(p); + if (i != n_elements-1) { + if (element_size != 0) + size = element_size; + else + size = request2size(sizes[i]); + remainder_size -= size; + set_size_and_pinuse_of_inuse_chunk(m, p, size); + p = chunk_plus_offset(p, size); + } + else { /* the final element absorbs any overallocation slop */ + set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); + break; + } + } + +#if DEBUG + if (marray != chunks) { + /* final element must have exactly exhausted chunk */ + if (element_size != 0) { + assert(remainder_size == element_size); + } + else { + assert(remainder_size == request2size(sizes[i])); + } + check_inuse_chunk(m, mem2chunk(marray)); + } + for (i = 0; i != n_elements; ++i) + check_inuse_chunk(m, mem2chunk(marray[i])); + +#endif /* DEBUG */ + + POSTACTION(m); + return marray; +} + +/* Try to free all pointers in the given array. + Note: this could be made faster, by delaying consolidation, + at the price of disabling some user integrity checks, We + still optimize some consolidations by combining adjacent + chunks before freeing, which will occur often if allocated + with ialloc or the array is sorted. +*/ +static size_t internal_bulk_free(mstate m, void* array[], size_t nelem) { + size_t unfreed = 0; + if (!PREACTION(m)) { + void** a; + void** fence = &(array[nelem]); + for (a = array; a != fence; ++a) { + void* mem = *a; + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t psize = chunksize(p); +#if FOOTERS + if (get_mstate_for(p) != m) { + ++unfreed; + continue; + } +#endif + check_inuse_chunk(m, p); + *a = 0; + if (RTCHECK(ok_address(m, p) && ok_inuse(p))) { + void ** b = a + 1; /* try to merge with next chunk */ + mchunkptr next = next_chunk(p); + if (b != fence && *b == chunk2mem(next)) { + size_t newsize = chunksize(next) + psize; + set_inuse(m, p, newsize); + *b = chunk2mem(p); + } + else + dispose_chunk(m, p, psize); + } + else { + CORRUPTION_ERROR_ACTION(m); + break; + } + } + } + if (should_trim(m, m->topsize)) + sys_trim(m, 0); + POSTACTION(m); + } + return unfreed; +} + +/* Traversal */ +#if MALLOC_INSPECT_ALL +static void internal_inspect_all(mstate m, + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + if (is_initialized(m)) { + mchunkptr top = m->top; + msegmentptr s; + for (s = &m->seg; s != 0; s = s->next) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && q->head != FENCEPOST_HEAD) { + mchunkptr next = next_chunk(q); + size_t sz = chunksize(q); + size_t used; + void* start; + if (is_inuse(q)) { + used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ + start = chunk2mem(q); + } + else { + used = 0; + if (is_small(sz)) { /* offset by possible bookkeeping */ + start = (void*)((char*)q + sizeof(malloc_chunk)); + } + else { + start = (void*)((char*)q + sizeof(malloc_tree_chunk)); + } + } + if (start < (void*)next) /* skip if all space is bookkeeping */ + handler(start, next, used, arg); + if (q == top) + break; + q = next; + } + } + } +} +#endif /* MALLOC_INSPECT_ALL */ + +/* ------------------ Exported realloc, memalign, etc -------------------- */ + +#if !ONLY_MSPACES + +void* dlrealloc(void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem == 0) { + mem = dlmalloc(bytes); + } + else if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } +#ifdef REALLOC_ZERO_BYTES_FREES + else if (bytes == 0) { + dlfree(oldmem); + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); + POSTACTION(m); + if (newp != 0) { + check_inuse_chunk(m, newp); + mem = chunk2mem(newp); + } + else { + mem = internal_malloc(m, bytes); + if (mem != 0) { + size_t oc = chunksize(oldp) - overhead_for(oldp); + memcpy(mem, oldmem, (oc < bytes)? oc : bytes); + internal_free(m, oldmem); + } + } + } + } + return mem; +} + +void* dlrealloc_in_place(void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem != 0) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); + POSTACTION(m); + if (newp == oldp) { + check_inuse_chunk(m, newp); + mem = oldmem; + } + } + } + } + return mem; +} + +void* dlmemalign(size_t alignment, size_t bytes) { + if (alignment <= MALLOC_ALIGNMENT) { + return dlmalloc(bytes); + } + return internal_memalign(gm, alignment, bytes); +} + +int dlposix_memalign(void** pp, size_t alignment, size_t bytes) { + void* mem = 0; + if (alignment == MALLOC_ALIGNMENT) + mem = dlmalloc(bytes); + else { + size_t d = alignment / sizeof(void*); + size_t r = alignment % sizeof(void*); + if (r != 0 || d == 0 || (d & (d-SIZE_T_ONE)) != 0) + return EINVAL; + else if (bytes >= MAX_REQUEST - alignment) { + if (alignment < MIN_CHUNK_SIZE) + alignment = MIN_CHUNK_SIZE; + mem = internal_memalign(gm, alignment, bytes); + } + } + if (mem == 0) + return ENOMEM; + else { + *pp = mem; + return 0; + } +} + +void* dlvalloc(size_t bytes) { + size_t pagesz; + ensure_initialization(); + pagesz = mparams.page_size; + return dlmemalign(pagesz, bytes); +} + +void* dlpvalloc(size_t bytes) { + size_t pagesz; + ensure_initialization(); + pagesz = mparams.page_size; + return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); +} + +void** dlindependent_calloc(size_t n_elements, size_t elem_size, + void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + return ialloc(gm, n_elements, &sz, 3, chunks); +} + +void** dlindependent_comalloc(size_t n_elements, size_t sizes[], + void* chunks[]) { + return ialloc(gm, n_elements, sizes, 0, chunks); +} + +size_t dlbulk_free(void* array[], size_t nelem) { + return internal_bulk_free(gm, array, nelem); +} + +#if MALLOC_INSPECT_ALL +void dlmalloc_inspect_all(void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + ensure_initialization(); + if (!PREACTION(gm)) { + internal_inspect_all(gm, handler, arg); + POSTACTION(gm); + } +} +#endif /* MALLOC_INSPECT_ALL */ + +int dlmalloc_trim(size_t pad) { + int result = 0; + ensure_initialization(); + if (!PREACTION(gm)) { + result = sys_trim(gm, pad); + POSTACTION(gm); + } + return result; +} + +size_t dlmalloc_footprint(void) { + return gm->footprint; +} + +size_t dlmalloc_max_footprint(void) { + return gm->max_footprint; +} + +size_t dlmalloc_footprint_limit(void) { + size_t maf = gm->footprint_limit; + return maf == 0 ? MAX_SIZE_T : maf; +} + +size_t dlmalloc_set_footprint_limit(size_t bytes) { + size_t result; /* invert sense of 0 */ + if (bytes == 0) + result = granularity_align(1); /* Use minimal size */ + if (bytes == MAX_SIZE_T) + result = 0; /* disable */ + else + result = granularity_align(bytes); + return gm->footprint_limit = result; +} + +#if !NO_MALLINFO +struct mallinfo dlmallinfo(void) { + return internal_mallinfo(gm); +} +#endif /* NO_MALLINFO */ + +#if !NO_MALLOC_STATS +void dlmalloc_stats() { + internal_malloc_stats(gm); +} +#endif /* NO_MALLOC_STATS */ + +int dlmallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +size_t dlmalloc_usable_size(void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +#endif /* !ONLY_MSPACES */ + +/* ----------------------------- user mspaces ---------------------------- */ + +#if MSPACES + +static mstate init_user_mstate(char* tbase, size_t tsize) { + size_t msize = pad_request(sizeof(struct malloc_state)); + mchunkptr mn; + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + memset(m, 0, msize); + (void)INITIAL_LOCK(&m->mutex); + msp->head = (msize|INUSE_BITS); + m->seg.base = m->least_addr = tbase; + m->seg.size = m->footprint = m->max_footprint = tsize; + m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; + m->mflags = mparams.default_mflags; + m->extp = 0; + m->exts = 0; + disable_contiguous(m); + init_bins(m); + mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); + check_top_chunk(m, m->top); + return m; +} + +mspace create_mspace(size_t capacity, int locked) { + mstate m = 0; + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); + if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + size_t rs = ((capacity == 0)? mparams.granularity : + (capacity + TOP_FOOT_SIZE + msize)); + size_t tsize = granularity_align(rs); + char* tbase = (char*)(CALL_MMAP(tsize)); + if (tbase != CMFAIL) { + m = init_user_mstate(tbase, tsize); + m->seg.sflags = USE_MMAP_BIT; + set_lock(m, locked); + } + } + return (mspace)m; +} + +mspace create_mspace_with_base(void* base, size_t capacity, int locked) { + mstate m = 0; + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); + if (capacity > msize + TOP_FOOT_SIZE && + capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + m = init_user_mstate((char*)base, capacity); + m->seg.sflags = EXTERN_BIT; + set_lock(m, locked); + } + return (mspace)m; +} + +int mspace_track_large_chunks(mspace msp, int enable) { + int ret = 0; + mstate ms = (mstate)msp; + if (!PREACTION(ms)) { + if (!use_mmap(ms)) + ret = 1; + if (!enable) + enable_mmap(ms); + else + disable_mmap(ms); + POSTACTION(ms); + } + return ret; +} + +size_t destroy_mspace(mspace msp) { + size_t freed = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + msegmentptr sp = &ms->seg; + (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */ + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + flag_t flag = sp->sflags; + sp = sp->next; + if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && + CALL_MUNMAP(base, size) == 0) + freed += size; + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return freed; +} + +/* + mspace versions of routines are near-clones of the global + versions. This is not so nice but better than the alternatives. +*/ + +void* mspace_malloc(mspace msp, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (!PREACTION(ms)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = ms->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb > ms->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(ms, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(ms, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + + if (nb <= ms->dvsize) { + size_t rsize = ms->dvsize - nb; + mchunkptr p = ms->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + } + else { /* exhaust dv */ + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; + mchunkptr p = ms->top; + mchunkptr r = ms->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mem = chunk2mem(p); + check_top_chunk(ms, ms->top); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + mem = sys_alloc(ms, nb); + + postaction: + POSTACTION(ms); + return mem; + } + + return 0; +} + +void mspace_free(mspace msp, void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + msp = msp; /* placate people compiling -Wunused */ +#else /* FOOTERS */ + mstate fm = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +} + +void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = internal_malloc(ms, req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem == 0) { + mem = mspace_malloc(msp, bytes); + } + else if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } +#ifdef REALLOC_ZERO_BYTES_FREES + else if (bytes == 0) { + mspace_free(msp, oldmem); + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = (mstate)msp; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); + POSTACTION(m); + if (newp != 0) { + check_inuse_chunk(m, newp); + mem = chunk2mem(newp); + } + else { + mem = mspace_malloc(m, bytes); + if (mem != 0) { + size_t oc = chunksize(oldp) - overhead_for(oldp); + memcpy(mem, oldmem, (oc < bytes)? oc : bytes); + mspace_free(m, oldmem); + } + } + } + } + return mem; +} + +void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem != 0) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = (mstate)msp; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + msp = msp; /* placate people compiling -Wunused */ + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); + POSTACTION(m); + if (newp == oldp) { + check_inuse_chunk(m, newp); + mem = oldmem; + } + } + } + } + return mem; +} + +void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (alignment <= MALLOC_ALIGNMENT) + return mspace_malloc(msp, bytes); + return internal_memalign(ms, alignment, bytes); +} + +void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, &sz, 3, chunks); +} + +void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, sizes, 0, chunks); +} + +size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem) { + return internal_bulk_free((mstate)msp, array, nelem); +} + +#if MALLOC_INSPECT_ALL +void mspace_inspect_all(mspace msp, + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + internal_inspect_all(ms, handler, arg); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} +#endif /* MALLOC_INSPECT_ALL */ + +int mspace_trim(mspace msp, size_t pad) { + int result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + result = sys_trim(ms, pad); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +#if !NO_MALLOC_STATS +void mspace_malloc_stats(mspace msp) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + internal_malloc_stats(ms); + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} +#endif /* NO_MALLOC_STATS */ + +size_t mspace_footprint(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->footprint; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +size_t mspace_max_footprint(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->max_footprint; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +size_t mspace_footprint_limit(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + size_t maf = ms->footprint_limit; + result = (maf == 0) ? MAX_SIZE_T : maf; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +size_t mspace_set_footprint_limit(mspace msp, size_t bytes) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (bytes == 0) + result = granularity_align(1); /* Use minimal size */ + if (bytes == MAX_SIZE_T) + result = 0; /* disable */ + else + result = granularity_align(bytes); + ms->footprint_limit = result; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +#if !NO_MALLINFO +struct mallinfo mspace_mallinfo(mspace msp) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + } + return internal_mallinfo(ms); +} +#endif /* NO_MALLINFO */ + +size_t mspace_usable_size(void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +int mspace_mallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* MSPACES */ + +#pragma clang diagnostic pop // ignored -Wcast-align + +/* -------------------- Alternative MORECORE functions ------------------- */ + +/* + Guidelines for creating a custom version of MORECORE: + + * For best performance, MORECORE should allocate in multiples of pagesize. + * MORECORE may allocate more memory than requested. (Or even less, + but this will usually result in a malloc failure.) + * MORECORE must not allocate memory when given argument zero, but + instead return one past the end address of memory from previous + nonzero call. + * For best performance, consecutive calls to MORECORE with positive + arguments should return increasing addresses, indicating that + space has been contiguously extended. + * Even though consecutive calls to MORECORE need not return contiguous + addresses, it must be OK for malloc'ed chunks to span multiple + regions in those cases where they do happen to be contiguous. + * MORECORE need not handle negative arguments -- it may instead + just return MFAIL when given negative arguments. + Negative arguments are always multiples of pagesize. MORECORE + must not misinterpret negative args as large positive unsigned + args. You can suppress all such calls from even occurring by defining + MORECORE_CANNOT_TRIM, + + As an example alternative MORECORE, here is a custom allocator + kindly contributed for pre-OSX macOS. It uses virtually but not + necessarily physically contiguous non-paged memory (locked in, + present and won't get swapped out). You can use it by uncommenting + this section, adding some #includes, and setting up the appropriate + defines above: + + #define MORECORE osMoreCore + + There is also a shutdown routine that should somehow be called for + cleanup upon program exit. + + #define MAX_POOL_ENTRIES 100 + #define MINIMUM_MORECORE_SIZE (64 * 1024U) + static int next_os_pool; + void *our_os_pools[MAX_POOL_ENTRIES]; + + void *osMoreCore(int size) + { + void *ptr = 0; + static void *sbrk_top = 0; + + if (size > 0) + { + if (size < MINIMUM_MORECORE_SIZE) + size = MINIMUM_MORECORE_SIZE; + if (CurrentExecutionLevel() == kTaskLevel) + ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); + if (ptr == 0) + { + return (void *) MFAIL; + } + // save ptrs so they can be freed during cleanup + our_os_pools[next_os_pool] = ptr; + next_os_pool++; + ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); + sbrk_top = (char *) ptr + size; + return ptr; + } + else if (size < 0) + { + // we don't currently support shrink behavior + return (void *) MFAIL; + } + else + { + return sbrk_top; + } + } + + // cleanup any allocated memory pools + // called as last thing before shutting down driver + + void osCleanupMem(void) + { + void **ptr; + + for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) + if (*ptr) + { + PoolDeallocate(*ptr); + *ptr = 0; + } + } + +*/ + + +/* ----------------------------------------------------------------------- +History: + v2.8.5 Sun May 22 10:26:02 2011 Doug Lea (dl at gee) + * Always perform unlink checks unless INSECURE + * Add posix_memalign. + * Improve realloc to expand in more cases; expose realloc_in_place. + Thanks to Peter Buhr for the suggestion. + * Add footprint_limit, inspect_all, bulk_free. Thanks + to Barry Hayes and others for the suggestions. + * Internal refactorings to avoid calls while holding locks + * Use non-reentrant locks by default. Thanks to Roland McGrath + for the suggestion. + * Small fixes to mspace_destroy, reset_on_error. + * Various configuration extensions/changes. Thanks + to all who contributed these. + + V2.8.4a Thu Apr 28 14:39:43 2011 (dl at gee.cs.oswego.edu) + * Update Creative Commons URL + + V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) + * Use zeros instead of prev foot for is_mmapped + * Add mspace_track_large_chunks; thanks to Jean Brouwers + * Fix set_inuse in internal_realloc; thanks to Jean Brouwers + * Fix insufficient sys_alloc padding when using 16byte alignment + * Fix bad error check in mspace_footprint + * Adaptations for ptmalloc; thanks to Wolfram Gloger. + * Reentrant spin locks; thanks to Earl Chew and others + * Win32 improvements; thanks to Niall Douglas and Earl Chew + * Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options + * Extension hook in malloc_state + * Various small adjustments to reduce warnings on some compilers + * Various configuration extensions/changes for more platforms. Thanks + to all who contributed these. + + V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) + * Add max_footprint functions + * Ensure all appropriate literals are size_t + * Fix conditional compilation problem for some #define settings + * Avoid concatenating segments with the one provided + in create_mspace_with_base + * Rename some variables to avoid compiler shadowing warnings + * Use explicit lock initialization. + * Better handling of sbrk interference. + * Simplify and fix segment insertion, trimming and mspace_destroy + * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x + * Thanks especially to Dennis Flanagan for help on these. + + V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) + * Fix memalign brace error. + + V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) + * Fix improper #endif nesting in C++ + * Add explicit casts needed for C++ + + V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) + * Use trees for large bins + * Support mspaces + * Use segments to unify sbrk-based and mmap-based system allocation, + removing need for emulation on most platforms without sbrk. + * Default safety checks + * Optional footer checks. Thanks to William Robertson for the idea. + * Internal code refactoring + * Incorporate suggestions and platform-specific changes. + Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, + Aaron Bachmann, Emery Berger, and others. + * Speed up non-fastbin processing enough to remove fastbins. + * Remove useless cfree() to avoid conflicts with other apps. + * Remove internal memcpy, memset. Compilers handle builtins better. + * Remove some options that no one ever used and rename others. + + V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) + * Fix malloc_state bitmap array misdeclaration + + V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) + * Allow tuning of FIRST_SORTED_BIN_SIZE + * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. + * Better detection and support for non-contiguousness of MORECORE. + Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger + * Bypass most of malloc if no frees. Thanks To Emery Berger. + * Fix freeing of old top non-contiguous chunk im sysmalloc. + * Raised default trim and map thresholds to 256K. + * Fix mmap-related #defines. Thanks to Lubos Lunak. + * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. + * Branch-free bin calculation + * Default trim and mmap thresholds now 256K. + + V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) + * Introduce independent_comalloc and independent_calloc. + Thanks to Michael Pachos for motivation and help. + * Make optional .h file available + * Allow > 2GB requests on 32bit systems. + * new WIN32 sbrk, mmap, munmap, lock code from . + Thanks also to Andreas Mueller , + and Anonymous. + * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for + helping test this.) + * memalign: check alignment arg + * realloc: don't try to shift chunks backwards, since this + leads to more fragmentation in some programs and doesn't + seem to help in any others. + * Collect all cases in malloc requiring system memory into sysmalloc + * Use mmap as backup to sbrk + * Place all internal state in malloc_state + * Introduce fastbins (although similar to 2.5.1) + * Many minor tunings and cosmetic improvements + * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK + * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS + Thanks to Tony E. Bennett and others. + * Include errno.h to support default failure action. + + V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) + * return null for negative arguments + * Added Several WIN32 cleanups from Martin C. Fong + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop + * Always call 'fREe()' rather than 'free()' + + V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) + * Fixed ordering problem with boundary-stamping + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshhold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occuring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ + diff --git a/src/libraries/libc/stdlib/qsort.c b/src/libraries/libc/stdlib/qsort.c new file mode 100644 index 0000000..c908407 --- /dev/null +++ b/src/libraries/libc/stdlib/qsort.c @@ -0,0 +1,124 @@ +/* qsort( void *, size_t, size_t, int(*)( const void *, const void * ) ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +/* This implementation is taken from Paul Edward's PDPCLIB. + + Original code is credited to Raymond Gardner, Englewood CO. + Minor mods are credited to Paul Edwards. + Some reformatting and simplification done by Martin Baute. + All code is still Public Domain. +*/ + +/* Wrapper for _PDCLIB_memswp protects against multiple argument evaluation. */ +static inline void memswp( char * i, char * j, size_t size ) +{ + _PDCLIB_memswp( i, j, size ); +} + +/* For small sets, insertion sort is faster than quicksort. + T is the threshold below which insertion sort will be used. + Must be 3 or larger. +*/ +#define T 7 + +/* Macros for handling the QSort stack */ +#define PREPARE_STACK char * stack[STACKSIZE]; char * * stackptr = stack +#define PUSH( base, limit ) stackptr[0] = base; stackptr[1] = limit; stackptr += 2 +#define POP( base, limit ) stackptr -= 2; base = stackptr[0]; limit = stackptr[1] +/* TODO: Stack usage is log2( nmemb ) (minus what T shaves off the worst case). + Worst-case nmemb is platform dependent and should probably be + configured through config.h. +*/ +#define STACKSIZE 64 + +void qsort( void * base, size_t nmemb, size_t size, int (*compar)( const void *, const void * ) ) +{ + char * i; + char * j; + size_t thresh = T * size; + char * base_ = (char *)base; + char * limit = base_ + nmemb * size; + PREPARE_STACK; + + for ( ;; ) + { + if ( (size_t)( limit - base_ ) > thresh ) /* QSort for more than T elements. */ + { + /* We work from second to last - first will be pivot element. */ + i = base_ + size; + j = limit - size; + /* We swap first with middle element, then sort that with second + and last element so that eventually first element is the median + of the three - avoiding pathological pivots. + TODO: Instead of middle element, chose one randomly. + */ + memswp( ( ( ( (size_t)( limit - base_ ) ) / size ) / 2 ) * size + base_, base_, size ); + if ( compar( i, j ) > 0 ) memswp( i, j, size ); + if ( compar( base_, j ) > 0 ) memswp( base_, j, size ); + if ( compar( i, base_ ) > 0 ) memswp( i, base_, size ); + /* Now we have the median for pivot element, entering main Quicksort. */ + for ( ;; ) + { + do + { + /* move i right until *i >= pivot */ + i += size; + } while ( compar( i, base_ ) < 0 ); + do + { + /* move j left until *j <= pivot */ + j -= size; + } while ( compar( j, base_ ) > 0 ); + if ( i > j ) + { + /* break loop if pointers crossed */ + break; + } + /* else swap elements, keep scanning */ + memswp( i, j, size ); + } + /* move pivot into correct place */ + memswp( base_, j, size ); + /* larger subfile base / limit to stack, sort smaller */ + if ( j - base_ > limit - i ) + { + /* left is larger */ + PUSH( base_, j ); + base_ = i; + } + else + { + /* right is larger */ + PUSH( i, limit ); + limit = j; + } + } + else /* insertion sort for less than T elements */ + { + for ( j = base_, i = j + size; i < limit; j = i, i += size ) + { + for ( ; compar( j, j + size ) > 0; j -= size ) + { + memswp( j, j + size, size ); + if ( j == base_ ) + { + break; + } + } + } + if ( stackptr != stack ) /* if any entries on stack */ + { + POP( base_, limit ); + } + else /* else stack empty, done */ + { + break; + } + } + } +} diff --git a/src/libraries/libc/stdlib/rand.c b/src/libraries/libc/stdlib/rand.c new file mode 100644 index 0000000..165e026 --- /dev/null +++ b/src/libraries/libc/stdlib/rand.c @@ -0,0 +1,13 @@ +/* rand( void ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int rand( void ) +{ + _PDCLIB_seed = _PDCLIB_seed * 1103515245 + 12345; + return (int)( _PDCLIB_seed / 65536 ) % 32768; +} diff --git a/src/libraries/libc/stdlib/srand.c b/src/libraries/libc/stdlib/srand.c new file mode 100644 index 0000000..6dcf28c --- /dev/null +++ b/src/libraries/libc/stdlib/srand.c @@ -0,0 +1,12 @@ +/* srand( unsigned int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +void srand( unsigned int seed ) +{ + _PDCLIB_seed = seed; +} diff --git a/src/libraries/libc/stdlib/strtol.c b/src/libraries/libc/stdlib/strtol.c new file mode 100644 index 0000000..162a1e1 --- /dev/null +++ b/src/libraries/libc/stdlib/strtol.c @@ -0,0 +1,27 @@ +/* strtol( const char *, char * *, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +long int strtol( const char * s, char ** endptr, int base ) +{ + long int rc; + char sign = '+'; + const char * p = _PDCLIB_strtox_prelim( s, &sign, &base ); + if ( base < 2 || base > 36 ) return 0; + if ( sign == '+' ) + { + rc = (long int)_PDCLIB_strtox_main( &p, (unsigned)base, (uintmax_t)LONG_MAX, (uintmax_t)( LONG_MAX / base ), (int)( LONG_MAX % base ), &sign ); + } + else + { + rc = (long int)_PDCLIB_strtox_main( &p, (unsigned)base, (uintmax_t)LONG_MIN, (uintmax_t)( LONG_MIN / -base ), (int)( -( LONG_MIN % base ) ), &sign ); + } + if ( endptr != NULL ) *endptr = ( p != NULL ) ? (char *) p : (char *) s; + return ( sign == '+' ) ? rc : -rc; +} diff --git a/src/libraries/libc/stdlib/strtoll.c b/src/libraries/libc/stdlib/strtoll.c new file mode 100644 index 0000000..64ae4c8 --- /dev/null +++ b/src/libraries/libc/stdlib/strtoll.c @@ -0,0 +1,27 @@ +/* strtoll( const char *, char * *, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +long long int strtoll( const char * s, char ** endptr, int base ) +{ + long long int rc; + char sign = '+'; + const char * p = _PDCLIB_strtox_prelim( s, &sign, &base ); + if ( base < 2 || base > 36 ) return 0; + if ( sign == '+' ) + { + rc = (long long int)_PDCLIB_strtox_main( &p, (unsigned)base, (uintmax_t)LLONG_MAX, (uintmax_t)( LLONG_MAX / base ), (int)( LLONG_MAX % base ), &sign ); + } + else + { + rc = (long long int)_PDCLIB_strtox_main( &p, (unsigned)base, (uintmax_t)LLONG_MIN, (uintmax_t)( LLONG_MIN / -base ), (int)( -( LLONG_MIN % base ) ), &sign ); + } + if ( endptr != NULL ) *endptr = ( p != NULL ) ? (char *) p : (char *) s; + return ( sign == '+' ) ? rc : -rc; +} diff --git a/src/libraries/libc/stdlib/strtoul.c b/src/libraries/libc/stdlib/strtoul.c new file mode 100644 index 0000000..73f887d --- /dev/null +++ b/src/libraries/libc/stdlib/strtoul.c @@ -0,0 +1,20 @@ +/* strtoul( const char *, char * *, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +unsigned long int strtoul( const char * s, char ** endptr, int base ) +{ + unsigned long int rc; + char sign = '+'; + const char * p = _PDCLIB_strtox_prelim( s, &sign, &base ); + if ( base < 2 || base > 36 ) return 0; + rc = (unsigned long int)_PDCLIB_strtox_main( &p, (unsigned)base, (uintmax_t)ULONG_MAX, (uintmax_t)( ULONG_MAX / base ), (int)( ULONG_MAX % base ), &sign ); + if ( endptr != NULL ) *endptr = ( p != NULL ) ? (char *) p : (char *) s; + return ( sign == '+' ) ? rc : -rc; +} diff --git a/src/libraries/libc/stdlib/strtoull.c b/src/libraries/libc/stdlib/strtoull.c new file mode 100644 index 0000000..258f886 --- /dev/null +++ b/src/libraries/libc/stdlib/strtoull.c @@ -0,0 +1,20 @@ +/* strtoull( const char *, char * *, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +unsigned long long int strtoull( const char * s, char ** endptr, int base ) +{ + unsigned long long int rc; + char sign = '+'; + const char * p = _PDCLIB_strtox_prelim( s, &sign, &base ); + if ( base < 2 || base > 36 ) return 0; + rc = _PDCLIB_strtox_main( &p, (unsigned)base, (uintmax_t)ULLONG_MAX, (uintmax_t)( ULLONG_MAX / base ), (int)( ULLONG_MAX % base ), &sign ); + if ( endptr != NULL ) *endptr = ( p != NULL ) ? (char *) p : (char *) s; + return ( sign == '+' ) ? rc : -rc; +} diff --git a/src/libraries/libc/stdlib/system.c b/src/libraries/libc/stdlib/system.c new file mode 100644 index 0000000..8102c47 --- /dev/null +++ b/src/libraries/libc/stdlib/system.c @@ -0,0 +1,34 @@ +/* system( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +/* This is an example implementation of system() fit for use with POSIX kernels. +*/ + +extern int fork( void ); +extern int execve( const char * filename, char * const argv[], char * const envp[] ); +extern int wait( int * status ); + +int system( const char * string ) +{ + const char * argv[] = { "sh", "-c", NULL, NULL }; + argv[2] = string; + if ( string != NULL ) + { + int pid = fork(); + if ( pid == 0 ) + { + execve( "/bin/sh", (char * * const)argv, NULL ); + } + else if ( pid > 0 ) + { + while( wait( NULL ) != pid ); + } + } + return -1; +} + diff --git a/src/libraries/libc/string/memchr.c b/src/libraries/libc/string/memchr.c new file mode 100644 index 0000000..efb84fb --- /dev/null +++ b/src/libraries/libc/string/memchr.c @@ -0,0 +1,21 @@ +/* memchr( const void *, int, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +void * memchr( const void * s, int c, size_t n ) +{ + const unsigned char * p = (const unsigned char *) s; + while ( n-- ) + { + if ( *p == (unsigned char) c ) + { + return (void *) p; + } + ++p; + } + return NULL; +} diff --git a/src/libraries/libc/string/memcmp.c b/src/libraries/libc/string/memcmp.c new file mode 100644 index 0000000..4076140 --- /dev/null +++ b/src/libraries/libc/string/memcmp.c @@ -0,0 +1,23 @@ +/* memcmp( const void *, const void *, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int memcmp( const void * s1, const void * s2, size_t n ) +{ + const unsigned char * p1 = (const unsigned char *) s1; + const unsigned char * p2 = (const unsigned char *) s2; + while ( n-- ) + { + if ( *p1 != *p2 ) + { + return *p1 - *p2; + } + ++p1; + ++p2; + } + return 0; +} diff --git a/src/libraries/libc/string/memcpy.c b/src/libraries/libc/string/memcpy.c new file mode 100644 index 0000000..981d3c6 --- /dev/null +++ b/src/libraries/libc/string/memcpy.c @@ -0,0 +1,18 @@ +/* memcpy( void *, const void *, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +void * memcpy( void * restrict s1, const void * restrict s2, size_t n ) +{ + char * dest = (char *) s1; + const char * src = (const char *) s2; + while ( n-- ) + { + *dest++ = *src++; + } + return s1; +} diff --git a/src/libraries/libc/string/memmove.c b/src/libraries/libc/string/memmove.c new file mode 100644 index 0000000..ffe1aa8 --- /dev/null +++ b/src/libraries/libc/string/memmove.c @@ -0,0 +1,30 @@ +/* memmove( void *, const void *, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +void * memmove( void * s1, const void * s2, size_t n ) +{ + char * dest = (char *) s1; + const char * src = (const char *) s2; + if ( dest <= src ) + { + while ( n-- ) + { + *dest++ = *src++; + } + } + else + { + src += n; + dest += n; + while ( n-- ) + { + *--dest = *--src; + } + } + return s1; +} diff --git a/src/libraries/libc/string/memset.c b/src/libraries/libc/string/memset.c new file mode 100644 index 0000000..9a03985 --- /dev/null +++ b/src/libraries/libc/string/memset.c @@ -0,0 +1,17 @@ +/* memset( void *, int, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +void * memset( void * s, int c, size_t n ) +{ + unsigned char * p = (unsigned char *) s; + while ( n-- ) + { + *p++ = (unsigned char) c; + } + return s; +} diff --git a/src/libraries/libc/string/strcat.c b/src/libraries/libc/string/strcat.c new file mode 100644 index 0000000..b5b3c30 --- /dev/null +++ b/src/libraries/libc/string/strcat.c @@ -0,0 +1,18 @@ +/* strcat( char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * strcat( char * restrict s1, const char * restrict s2 ) +{ + char * rc = s1; + if ( *s1 ) + { + while ( *++s1 ); + } + while ( (*s1++ = *s2++) ); + return rc; +} diff --git a/src/libraries/libc/string/strchr.c b/src/libraries/libc/string/strchr.c new file mode 100644 index 0000000..115d970 --- /dev/null +++ b/src/libraries/libc/string/strchr.c @@ -0,0 +1,19 @@ +/* strchr( const char *, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * strchr( const char * s, int c ) +{ + do + { + if ( *s == (char) c ) + { + return (char *) s; + } + } while ( *s++ ); + return NULL; +} diff --git a/src/libraries/libc/string/strcmp.c b/src/libraries/libc/string/strcmp.c new file mode 100644 index 0000000..bd7b3fc --- /dev/null +++ b/src/libraries/libc/string/strcmp.c @@ -0,0 +1,17 @@ +/* strcmp( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int strcmp( const char * s1, const char * s2 ) +{ + while ( ( *s1 ) && ( *s1 == *s2 ) ) + { + ++s1; + ++s2; + } + return ( *(unsigned char *)s1 - *(unsigned char *)s2 ); +} diff --git a/src/libraries/libc/string/strcoll.c b/src/libraries/libc/string/strcoll.c new file mode 100644 index 0000000..2b8ec4f --- /dev/null +++ b/src/libraries/libc/string/strcoll.c @@ -0,0 +1,23 @@ +/* strcoll( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +int strcoll( const char * s1, const char * s2 ) +{ + return strcmp( s1, s2 ); + + /* FIXME: This code became invalid when we started doing *real* locales... */ + /* + while ( ( *s1 ) && ( _PDCLIB_lc_ctype[(unsigned char)*s1].collation == _PDCLIB_lc_ctype[(unsigned char)*s2].collation ) ) + { + ++s1; + ++s2; + } + return ( _PDCLIB_lc_ctype[(unsigned char)*s1].collation == _PDCLIB_lc_ctype[(unsigned char)*s2].collation ); + */ +} diff --git a/src/libraries/libc/string/strcpy.c b/src/libraries/libc/string/strcpy.c new file mode 100644 index 0000000..40428fb --- /dev/null +++ b/src/libraries/libc/string/strcpy.c @@ -0,0 +1,14 @@ +/* strcpy( char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * strcpy( char * restrict s1, const char * restrict s2 ) +{ + char * rc = s1; + while ( ( *s1++ = *s2++ ) ); + return rc; +} diff --git a/src/libraries/libc/string/strcspn.c b/src/libraries/libc/string/strcspn.c new file mode 100644 index 0000000..2f067da --- /dev/null +++ b/src/libraries/libc/string/strcspn.c @@ -0,0 +1,26 @@ +/* strcspn( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +size_t strcspn( const char * s1, const char * s2 ) +{ + size_t len = 0; + const char * p; + while ( s1[len] ) + { + p = s2; + while ( *p ) + { + if ( s1[len] == *p++ ) + { + return len; + } + } + ++len; + } + return len; +} diff --git a/src/libraries/libc/string/strerror.c b/src/libraries/libc/string/strerror.c new file mode 100644 index 0000000..2c4a617 --- /dev/null +++ b/src/libraries/libc/string/strerror.c @@ -0,0 +1,21 @@ +/* strerror( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +/* TODO: Doing this via a static array is not the way to do it. */ +char * strerror( int errnum ) +{ + if ( errnum >= _PDCLIB_ERRNO_MAX ) + { + return (char *)"Unknown error"; + } + else + { + return _PDCLIB_lc_messages.errno_texts[errnum]; + } +} diff --git a/src/libraries/libc/string/strlen.c b/src/libraries/libc/string/strlen.c new file mode 100644 index 0000000..f21877b --- /dev/null +++ b/src/libraries/libc/string/strlen.c @@ -0,0 +1,17 @@ +/* strlen( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +size_t strlen( const char * s ) +{ + size_t rc = 0; + while ( s[rc] ) + { + ++rc; + } + return rc; +} diff --git a/src/libraries/libc/string/strncat.c b/src/libraries/libc/string/strncat.c new file mode 100644 index 0000000..5c7c3d5 --- /dev/null +++ b/src/libraries/libc/string/strncat.c @@ -0,0 +1,25 @@ +/* strncat( char *, const char *, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * strncat( char * restrict s1, const char * restrict s2, size_t n ) +{ + char * rc = s1; + while ( *s1 ) + { + ++s1; + } + while ( n && ( *s1++ = *s2++ ) ) + { + --n; + } + if ( n == 0 ) + { + *s1 = '\0'; + } + return rc; +} diff --git a/src/libraries/libc/string/strncmp.c b/src/libraries/libc/string/strncmp.c new file mode 100644 index 0000000..99fe338 --- /dev/null +++ b/src/libraries/libc/string/strncmp.c @@ -0,0 +1,25 @@ +/* strncmp( const char *, const char *, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int strncmp( const char * s1, const char * s2, size_t n ) +{ + while ( n && *s1 && ( *s1 == *s2 ) ) + { + ++s1; + ++s2; + --n; + } + if ( n == 0 ) + { + return 0; + } + else + { + return ( *(unsigned char *)s1 - *(unsigned char *)s2 ); + } +} diff --git a/src/libraries/libc/string/strncpy.c b/src/libraries/libc/string/strncpy.c new file mode 100644 index 0000000..8b6350c --- /dev/null +++ b/src/libraries/libc/string/strncpy.c @@ -0,0 +1,26 @@ +/* strncpy( char *, const char *, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * strncpy( char * restrict s1, const char * restrict s2, size_t n ) +{ + char * rc = s1; + while ( n && ( *s1++ = *s2++ ) ) + { + /* Cannot do "n--" in the conditional as size_t is unsigned and we have + to check it again for >0 in the next loop below, so we must not risk + underflow. + */ + --n; + } + /* Checking against 1 as we missed the last --n in the loop above. */ + while ( n-- > 1 ) + { + *s1++ = '\0'; + } + return rc; +} diff --git a/src/libraries/libc/string/strpbrk.c b/src/libraries/libc/string/strpbrk.c new file mode 100644 index 0000000..853f398 --- /dev/null +++ b/src/libraries/libc/string/strpbrk.c @@ -0,0 +1,26 @@ +/* strpbrk( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * strpbrk( const char * s1, const char * s2 ) +{ + const char * p1 = s1; + const char * p2; + while ( *p1 ) + { + p2 = s2; + while ( *p2 ) + { + if ( *p1 == *p2++ ) + { + return (char *) p1; + } + } + ++p1; + } + return NULL; +} diff --git a/src/libraries/libc/string/strrchr.c b/src/libraries/libc/string/strrchr.c new file mode 100644 index 0000000..5bc5183 --- /dev/null +++ b/src/libraries/libc/string/strrchr.c @@ -0,0 +1,21 @@ +/* strrchr( const char *, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * strrchr( const char * s, int c ) +{ + size_t i = 0; + while ( s[i++] ); + do + { + if ( s[--i] == (char) c ) + { + return (char *) s + i; + } + } while ( i ); + return NULL; +} diff --git a/src/libraries/libc/string/strspn.c b/src/libraries/libc/string/strspn.c new file mode 100644 index 0000000..37a2eb2 --- /dev/null +++ b/src/libraries/libc/string/strspn.c @@ -0,0 +1,31 @@ +/* strspn( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +size_t strspn( const char * s1, const char * s2 ) +{ + size_t len = 0; + const char * p; + while ( s1[ len ] ) + { + p = s2; + while ( *p ) + { + if ( s1[len] == *p ) + { + break; + } + ++p; + } + if ( ! *p ) + { + return len; + } + ++len; + } + return len; +} diff --git a/src/libraries/libc/string/strstr.c b/src/libraries/libc/string/strstr.c new file mode 100644 index 0000000..78f0e5f --- /dev/null +++ b/src/libraries/libc/string/strstr.c @@ -0,0 +1,29 @@ +/* strstr( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * strstr( const char * s1, const char * s2 ) +{ + const char * p1 = s1; + const char * p2; + while ( *s1 ) + { + p2 = s2; + while ( *p2 && ( *p1 == *p2 ) ) + { + ++p1; + ++p2; + } + if ( ! *p2 ) + { + return (char *) s1; + } + ++s1; + p1 = s1; + } + return NULL; +} diff --git a/src/libraries/libc/string/strtok.c b/src/libraries/libc/string/strtok.c new file mode 100644 index 0000000..d8864c4 --- /dev/null +++ b/src/libraries/libc/string/strtok.c @@ -0,0 +1,69 @@ +/* strtok( char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * strtok( char * restrict s1, const char * restrict s2 ) +{ + static char * tmp = NULL; + const char * p = s2; + + if ( s1 != NULL ) + { + /* new string */ + tmp = s1; + } + else + { + /* old string continued */ + if ( tmp == NULL ) + { + /* No old string, no new string, nothing to do */ + return NULL; + } + s1 = tmp; + } + + /* skipping leading s2 characters */ + while ( *p && *s1 ) + { + if ( *s1 == *p ) + { + /* found seperator; skip and start over */ + ++s1; + p = s2; + continue; + } + ++p; + } + + if ( ! *s1 ) + { + /* no more to parse */ + return ( tmp = NULL ); + } + + /* skipping non-s2 characters */ + tmp = s1; + while ( *tmp ) + { + p = s2; + while ( *p ) + { + if ( *tmp == *p++ ) + { + /* found seperator; overwrite with '\0', position tmp, return */ + *tmp++ = '\0'; + return s1; + } + } + ++tmp; + } + + /* parsed to end of string */ + tmp = NULL; + return s1; +} diff --git a/src/libraries/libc/string/strxfrm.c b/src/libraries/libc/string/strxfrm.c new file mode 100644 index 0000000..83821f1 --- /dev/null +++ b/src/libraries/libc/string/strxfrm.c @@ -0,0 +1,23 @@ +/* strxfrm( char *, const char *, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +size_t strxfrm( char * restrict s1, const char * restrict s2, size_t n ) +{ + size_t len = strlen( s2 ); + if ( len < n ) + { + /* Cannot use strncpy() here as the filling of s1 with '\0' is not part + of the spec. + */ + /* FIXME: The code below became invalid when we started doing *real* locales... */ + /*while ( n-- && ( *s1++ = _PDCLIB_lc_collate[(unsigned char)*s2++].collation ) );*/ + while ( n-- && ( *s1++ = (unsigned char)*s2++ ) ); + } + return len; +} diff --git a/src/libraries/libc/tests/_PDCLIB_iotest.h b/src/libraries/libc/tests/_PDCLIB_iotest.h new file mode 100644 index 0000000..3c04980 --- /dev/null +++ b/src/libraries/libc/tests/_PDCLIB_iotest.h @@ -0,0 +1,169 @@ +/* PDCLib testing suite <_PDCLIB_test.h> + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* -------------------------------------------------------------------------- */ +/* Helper macros for printf() / scanf() tests */ +/* -------------------------------------------------------------------------- */ +/* Tucked away in a seperate header because these are ugly, complex, and not */ +/* needed in 95% of all test cases. */ +/* -------------------------------------------------------------------------- */ +/* ...scanf() tests */ +#if defined( _PDCLIB_FILEIO ) + #define PREPARE_SOURCE( input_string ) \ + rewind( source ); \ + fwrite( input_string, 1, sizeof( input_string ), source ); \ + rewind( source ); +#elif defined( _PDCLIB_STRINGIO ) + #define PREPARE_SOURCE( input_string ) \ + memcpy( source, input_string, sizeof( input_string ) ); +#endif + +/* Virtually everything in the printf() / scanf() test drivers is heavily + depending on the platform, i.e. the width of the integer values. To do + proper domain tests, we need the limits of the integers (largest and + smallest value), which we can get from . But we also need the + string representations of these numbers, to the various bases, which of + course vary depending on how the platform defines 'int' and 'long'. +*/ + +#define sym2v( x ) #x +#define sym2s( x ) sym2v( x ) + +#if __SIZEOF_INT__ == 2 + +#define UINT_DIG 5 +#define INT_DIG 5 +#define INT_DIG_LESS1 "4" +#define INT_DIG_PLUS1 "6" +#define INT_DIG_PLUS2 "7" +#define INT_HEXDIG "FFF" +#define INT_hexdig "fff" +#define INT_OCTDIG "177777" +#define INT_MAX_DEZ_STR "32767" +#define INT_MIN_DEZ_STR "32768" +#define UINT_MAX_DEZ_STR "65535" +#define INT_MAX_OCT_STR +#define INT_MIN_OCT_STR +#define UINT_MAX_OCT_STR +#define INT_MAX_HEX_STR +#define INT_MIN_HEX_STR +#define UINT_MAX_HEX_STR + +#elif __SIZEOF_INT__ == 4 + +#define UINT_DIG 10 +#define INT_DIG 10 +#define INT_DIG_LESS1 "9" +#define INT_DIG_PLUS1 "11" +#define INT_DIG_PLUS2 "12" +#define INT_HEXDIG "FFFFFFF" +#define INT_hexdig "fffffff" +#define INT_OCTDIG "37777777777" +#define INT_MAX_DEZ_STR "2147483647" +#define INT_MIN_DEZ_STR "2147483648" +#define UINT_MAX_DEZ_STR "4294967295" +#define INT_MAX_OCT_STR +#define INT_MIN_OCT_STR +#define UINT_MAX_OCT_STR +#define INT_MAX_HEX_STR +#define INT_MIN_HEX_STR +#define UINT_MAX_HEX_STR + +#elif __SIZEOF_INT__ == 8 + +#define UINT_DIG 20 +#define INT_DIG 19 +#define INT_DIG_LESS1 "18" +#define INT_DIG_PLUS1 "20" +#define INT_DIG_PLUS2 "21" +#define INT_HEXDIG "FFFFFFFFFFFFFFF" +#define INT_hexdig "fffffffffffffff" +#define INT_OCTDIG "1777777777777777777777" +#define INT_MAX_DEZ_STR "9223372036854775807" +#define INT_MIN_DEZ_STR "9223372036854775808" +#define UINT_MAX_DEZ_STR "18446744073709551615" +#define INT_MAX_OCT_STR +#define INT_MIN_OCT_STR +#define UINT_MAX_OCT_STR +#define INT_MAX_HEX_STR +#define INT_MIN_HEX_STR +#define UINT_MAX_HEX_STR + +#else + +#error Unsupported width of 'int' (neither 16, 32, nor 64 bit). + +#endif + + +#if __SIZEOF_LONG__ == 4 + +#define ULONG_DIG 10 +#define LONG_DIG 10 +#define LONG_MAX_DEZ_STR "2147483647" +#define LONG_MIN_DEZ_STR "2147483648" +#define ULONG_MAX_DEZ_STR "4294967295" +#define LONG_MAX_OCT_STR +#define LONG_MIN_OCT_STR +#define ULONG_MAX_OCT_STR +#define LONG_MAX_HEX_STR +#define LONG_MIN_HEX_STR +#define ULONG_MAX_HEX_STR + +#elif __SIZEOF_LONG__ == 8 + +#define ULONG_DIG 20 +#define LONG_DIG 19 +#define LONG_MAX_DEZ_STR "9223372036854775807" +#define LONG_MIN_DEZ_STR "9223372036854775808" +#define ULONG_MAX_DEZ_STR "18446744073709551615" +#define LONG_MAX_OCT_STR +#define LONG_MIN_OCT_STR +#define ULONG_MAX_OCT_STR +#define LONG_MAX_HEX_STR +#define LONG_MIN_HEX_STR +#define ULONG_MAX_HEX_STR + +#else + +#error Unsupported width of 'long' (neither 32 nor 64 bit). + +#endif + + +#if __SIZEOF_LONG_LONG__ == 8 + +#define ULLONG_DIG 20 +#define LLONG_DIG 19 +#define LLONG_MAX_DEZ_STR "9223372036854775807" +#define LLONG_MIN_DEZ_STR "9223372036854775808" +#define ULLONG_MAX_DEZ_STR "18446744073709551615" +#define LLONG_MAX_OCT_STR +#define LLONG_MIN_OCT_STR +#define ULLONG_MAX_OCT_STR +#define LLONG_MAX_HEX_STR +#define LLONG_MIN_HEX_STR +#define ULLONG_MAX_HEX_STR + +#elif __SIZEOF_LONG_LONG__ == 16 + +#define ULLONG_DIG 38 +#define LLONG_DIG 38 +#define LLONG_MAX_DEZ_STR "170141183460469231731687303715884105727" +#define LLONG_MIN_DEZ_STR "170141183460469231731687303715884105728" +#define ULLONG_MAX_DEZ_STR "340282366920938463463374607431768211455" +#define LLONG_MAX_OCT_STR +#define LLONG_MIN_OCT_STR +#define ULLONG_MAX_OCT_STR +#define LLONG_MAX_HEX_STR +#define LLONG_MIN_HEX_STR +#define ULLONG_MAX_HEX_STR + +#else + +#error Unsupported width of 'long long' (neither 64 nor 128 bit). + +#endif diff --git a/src/libraries/libc/tests/_PDCLIB_test.h b/src/libraries/libc/tests/_PDCLIB_test.h new file mode 100644 index 0000000..8e5dcd0 --- /dev/null +++ b/src/libraries/libc/tests/_PDCLIB_test.h @@ -0,0 +1,101 @@ +/* PDCLib testing suite <_PDCLIB_test.h> + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* -------------------------------------------------------------------------- */ +/* Helper macros for test drivers */ +/* -------------------------------------------------------------------------- */ + +#define fprintf j6libc_fprintf +#define stderr j6libc_stderr +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef stderr +#undef fprintf + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-library-redeclaration" +struct _IO_FILE; +extern struct _IO_FILE *stderr; +extern int fprintf(struct _IO_FILE *, const char *, ...); +#pragma clang diagnostic pop + +/* Host system sys/types.h */ +#include + +/* Some strings used for and testing. */ +static const char abcde[] = "abcde"; +static const char abcdx[] = "abcdx"; +static const char teststring[] = "1234567890\nABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n"; + +/* Temporary file names */ +static const char testfile[]="testing/testfile"; +static const char testfile1[]="testing/testfile1"; +static const char testfile2[]="testing/testfile2"; + +/* Host system fork(), waitpid(), and _exit() */ +pid_t fork(void); +pid_t waitpid(pid_t, int *, int); +void _exit(int); + +#define START_SUITE(name) \ + int run_suite_ ##name (void) { \ + int TEST_RESULTS = 0; + +#define END_SUITE \ + return TEST_RESULTS; \ + } + +#define DECLARE_SUITE(name) extern int run_suite_ ##name (void) +#define RUN_TEST(name) TEST_RESULTS += test__ ##name(); + +#define START_TEST(name) \ + int test__ ##name (void) { \ + int TEST_RESULTS = 0; + +#define END_TEST \ + return TEST_RESULTS; \ + } + +/* TESTCASE() - generic test */ +#define TESTCASE( x ) \ + do { \ + pid_t pid = fork(); \ + if ( !pid ) _exit((x) ? 0 : 0xFF); \ + else { \ + int __rc = 0; \ + waitpid(pid, &__rc, 0); \ + if ( __rc & 0xff00 ) { \ + TEST_RESULTS += 1; \ + fprintf( stderr, "FAILED: " __FILE__ ":%s, line %d - %s\n", __func__, __LINE__, #x ); \ + } \ + } \ + } while(0) + +/* TESTCASE_REQUIRE() - must-pass test; return early otherwise */ +#define TESTCASE_REQUIRE( x ) \ + do { \ + pid_t pid = fork(); \ + if ( !pid ) _exit((x) ? 0 : 0xFF); \ + else { \ + int __rc = 0; \ + waitpid(pid, &__rc, 0); \ + if ( __rc & 0xff00 ) { \ + TEST_RESULTS += 1; \ + fprintf( stderr, "FAILED: " __FILE__ ":%s, line %d - %s\n", __func__, __LINE__, #x ); \ + return TEST_RESULTS; \ + } \ + } \ + } while(0) diff --git a/src/libraries/libc/tests/main.c b/src/libraries/libc/tests/main.c new file mode 100644 index 0000000..3e0c481 --- /dev/null +++ b/src/libraries/libc/tests/main.c @@ -0,0 +1,32 @@ +#include "_PDCLIB_test.h" + +DECLARE_SUITE(internal); + +DECLARE_SUITE(assert); +DECLARE_SUITE(ctype); +DECLARE_SUITE(inttypes); +DECLARE_SUITE(locale); +DECLARE_SUITE(stdarg); +DECLARE_SUITE(stdio); +DECLARE_SUITE(stdlib); +DECLARE_SUITE(string); +DECLARE_SUITE(time); + +int main(int argc, const char **argv) +{ + int result = 0; + + result += run_suite_internal(); + + result += run_suite_assert(); + result += run_suite_ctype(); + result += run_suite_inttypes(); + result += run_suite_locale(); + result += run_suite_stdarg(); + result += run_suite_stdio(); + result += run_suite_stdlib(); + result += run_suite_string(); + result += run_suite_time(); + + return result; +} diff --git a/src/libraries/libc/tests/printf_testcases.h b/src/libraries/libc/tests/printf_testcases.h new file mode 100644 index 0000000..45bcf53 --- /dev/null +++ b/src/libraries/libc/tests/printf_testcases.h @@ -0,0 +1,444 @@ +#define PRINTF_TEST( expected_rc, expected_string, ... ) do { \ + TEST_RESULTS += DO_TESTPRINTF(IMPLFILE, __FILE__, __LINE__, expected_rc, expected_string, __VA_ARGS__); \ + } while (0); + + + { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat" +#if CHAR_MIN == -128 + assert(CHAR_MIN == -128); + PRINTF_TEST( 4, "-128", "%hhd", CHAR_MIN ); + assert(CHAR_MAX == 127); + PRINTF_TEST( 3, "127", "%hhd", CHAR_MAX ); +#else + assert(CHAR_MIN == 0); + PRINTF_TEST( 1, "0", "%hhu", CHAR_MIN ); + assert(CHAR_MAX == 255); + PRINTF_TEST( 3, "255", "%hhu", CHAR_MAX ); +#endif + PRINTF_TEST( 1, "0", "%hhd", 0 ); + assert(SHRT_MIN == -32768); + PRINTF_TEST( 6, "-32768", "%hd", SHRT_MIN ); + assert(SHRT_MAX == 32767); + PRINTF_TEST( 5, "32767", "%hd", SHRT_MAX ); + PRINTF_TEST( 1, "0", "%hd", 0 ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%d", INT_MIN ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%d", INT_MAX ); + PRINTF_TEST( 1, "0", "%d", 0 ); + PRINTF_TEST( LONG_DIG + 1, "-" LONG_MIN_DEZ_STR, "%ld", LONG_MIN ); + PRINTF_TEST( LONG_DIG, LONG_MAX_DEZ_STR, "%ld", LONG_MAX ); + PRINTF_TEST( 1, "0", "%ld", 0l ); + PRINTF_TEST( LLONG_DIG + 1, "-" LLONG_MIN_DEZ_STR, "%lld", LLONG_MIN ); + PRINTF_TEST( LLONG_DIG, LLONG_MAX_DEZ_STR, "%lld", LLONG_MAX ); + PRINTF_TEST( 1, "0", "%lld", 0ll ); + PRINTF_TEST( 3, "255", "%hhu", UCHAR_MAX ); + PRINTF_TEST( 3, "255", "%hhu", (unsigned char)-1 ); + PRINTF_TEST( 5, "65535", "%hu", USHRT_MAX ); + PRINTF_TEST( 5, "65535", "%hu", (unsigned short)-1 ); + PRINTF_TEST( UINT_DIG, UINT_MAX_DEZ_STR, "%u", UINT_MAX ); + PRINTF_TEST( UINT_DIG, UINT_MAX_DEZ_STR, "%u", -1u ); + PRINTF_TEST( ULONG_DIG, ULONG_MAX_DEZ_STR, "%lu", ULONG_MAX ); + PRINTF_TEST( ULONG_DIG, ULONG_MAX_DEZ_STR, "%lu", -1ul ); + PRINTF_TEST( ULLONG_DIG, ULLONG_MAX_DEZ_STR, "%llu", ULLONG_MAX ); + PRINTF_TEST( ULLONG_DIG, ULLONG_MAX_DEZ_STR, "%llu", -1ull ); + PRINTF_TEST( (int)strlen( INT_HEXDIG ) + 1, "F" INT_HEXDIG, "%X", UINT_MAX ); + PRINTF_TEST( (int)strlen( INT_HEXDIG ) + 3, "0XF" INT_HEXDIG, "%#X", -1u ); + PRINTF_TEST( (int)strlen( INT_HEXDIG ) + 1, "f" INT_hexdig, "%x", UINT_MAX ); + PRINTF_TEST( (int)strlen( INT_HEXDIG ) + 3, "0xf" INT_hexdig, "%#x", -1u ); + PRINTF_TEST( (int)strlen( INT_OCTDIG ), INT_OCTDIG, "%o", UINT_MAX ); + PRINTF_TEST( (int)strlen( INT_OCTDIG ) + 1, "0" INT_OCTDIG, "%#o", -1u ); +#if 0 + /* TODO: This test case is broken, doesn't test what it was intended to. */ + PRINTF_TEST( 5, "%.0#o", "%.0#o", 0 ); +#endif + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%+d", INT_MIN ); + PRINTF_TEST( INT_DIG + 1, "+" INT_MAX_DEZ_STR, "%+d", INT_MAX ); + PRINTF_TEST( 2, "+0", "%+d", 0 ); + PRINTF_TEST( UINT_DIG, UINT_MAX_DEZ_STR, "%+u", UINT_MAX ); + PRINTF_TEST( UINT_DIG, UINT_MAX_DEZ_STR, "%+u", -1u ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "% d", INT_MIN ); + PRINTF_TEST( INT_DIG + 1, " " INT_MAX_DEZ_STR, "% d", INT_MAX ); + PRINTF_TEST( 2, " 0", "% d", 0 ); + PRINTF_TEST( UINT_DIG, UINT_MAX_DEZ_STR, "% u", UINT_MAX ); + PRINTF_TEST( UINT_DIG, UINT_MAX_DEZ_STR, "% u", -1u ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%" INT_DIG_LESS1 "d", INT_MIN ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%" INT_DIG_LESS1 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%" sym2s(INT_DIG) "d", INT_MIN ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%" sym2s(INT_DIG) "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%" INT_DIG_PLUS1 "d", INT_MIN ); + PRINTF_TEST( INT_DIG + 1, " " INT_MAX_DEZ_STR, "%" INT_DIG_PLUS1 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 2, " -" INT_MIN_DEZ_STR, "%" INT_DIG_PLUS2 "d", INT_MIN ); + PRINTF_TEST( INT_DIG + 2, " " INT_MAX_DEZ_STR, "%" INT_DIG_PLUS2 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%-" INT_DIG_LESS1 "d", INT_MIN ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%-" INT_DIG_LESS1 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%-" sym2s(INT_DIG) "d", INT_MIN ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%-" sym2s(INT_DIG) "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%-" INT_DIG_PLUS1 "d", INT_MIN ); + PRINTF_TEST( INT_DIG + 1, INT_MAX_DEZ_STR " ", "%-" INT_DIG_PLUS1 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 2, "-" INT_MIN_DEZ_STR " ", "%-" INT_DIG_PLUS2 "d", INT_MIN ); + PRINTF_TEST( INT_DIG + 2, INT_MAX_DEZ_STR " ", "%-" INT_DIG_PLUS2 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%0" INT_DIG_LESS1 "d", INT_MIN ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%0" INT_DIG_LESS1 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%0" sym2s(INT_DIG) "d", INT_MIN ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%0" sym2s(INT_DIG) "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%0" INT_DIG_PLUS1 "d", INT_MIN ); + PRINTF_TEST( INT_DIG + 1, "0" INT_MAX_DEZ_STR, "%0" INT_DIG_PLUS1 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 2, "-0" INT_MIN_DEZ_STR, "%0" INT_DIG_PLUS2 "d", INT_MIN ); + PRINTF_TEST( INT_DIG + 2, "00" INT_MAX_DEZ_STR, "%0" INT_DIG_PLUS2 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%-0" INT_DIG_LESS1 "d", INT_MIN ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%-0" INT_DIG_LESS1 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%-0" sym2s(INT_DIG) "d", INT_MIN ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%-0" sym2s(INT_DIG) "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%-0" INT_DIG_PLUS1 "d", INT_MIN ); + PRINTF_TEST( INT_DIG + 1, INT_MAX_DEZ_STR " ", "%-0" INT_DIG_PLUS1 "d", INT_MAX ); + PRINTF_TEST( INT_DIG + 2, "-" INT_MIN_DEZ_STR " ", "%-0" INT_DIG_PLUS2 "d", INT_MIN ); + PRINTF_TEST( INT_DIG + 2, INT_MAX_DEZ_STR " ", "%-0" INT_DIG_PLUS2 "d", INT_MAX ); + /* FIXME: This test not yet 32/64 bit agnostic */ + PRINTF_TEST( 30, " 00000000002147483647", "%030.20d", INT_MAX ); + PRINTF_TEST( (int)strlen( INT_HEXDIG ) + 1, "f" INT_hexdig, "%.6x", UINT_MAX ); + PRINTF_TEST( (int)strlen( INT_HEXDIG ) + 3, "0xf" INT_hexdig, "%#6.3x", UINT_MAX ); + PRINTF_TEST( (int)strlen( INT_HEXDIG ) + 3, "0xf" INT_hexdig, "%#3.6x", UINT_MAX ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%.6d", INT_MIN ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%6.3d", INT_MIN ); + PRINTF_TEST( INT_DIG + 1, "-" INT_MIN_DEZ_STR, "%3.6d", INT_MIN ); + PRINTF_TEST( UINT_DIG, "0xf" INT_hexdig, "%#0.6x", UINT_MAX ); + PRINTF_TEST( UINT_DIG, "0xf" INT_hexdig, "%#06.3x", UINT_MAX ); + PRINTF_TEST( UINT_DIG, "0xf" INT_hexdig, "%#03.6x", UINT_MAX ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%#0.6d", INT_MAX ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%#06.3d", INT_MAX ); + PRINTF_TEST( INT_DIG, INT_MAX_DEZ_STR, "%#03.6d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "+" INT_MAX_DEZ_STR, "%#+.6d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "+" INT_MAX_DEZ_STR, "%#+6.3d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "+" INT_MAX_DEZ_STR, "%#+3.6d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "+" INT_MAX_DEZ_STR, "%+0.6d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "+" INT_MAX_DEZ_STR, "%+06.3d", INT_MAX ); + PRINTF_TEST( INT_DIG + 1, "+" INT_MAX_DEZ_STR, "%+03.6d", INT_MAX ); +#ifndef TEST_CONVERSION_ONLY + PRINTF_TEST( INT_DIG + 2, "- " INT_MAX_DEZ_STR, "- %d", INT_MAX ); + PRINTF_TEST( INT_DIG * 2 + 6, "- " INT_MAX_DEZ_STR " % -" INT_MIN_DEZ_STR, "- %d %% %d", INT_MAX, INT_MIN ); +#endif + PRINTF_TEST( 1, "x", "%c", 'x' ); + PRINTF_TEST( 6, "abcdef", "%s", "abcdef" ); + /* FIXME: This test not yet 32/64 bit agnostic */ + PRINTF_TEST( 10, "0xdeadbeef", "%p", (void *)0xdeadbeef ); + PRINTF_TEST( 6, " 0x1", "%#6x", 1 ); +#ifndef TEST_CONVERSION_ONLY + { + int val1, val2; + PRINTF_TEST( 9, "123456789", "123456%n789%n", &val1, &val2 ); + TESTCASE( val1 == 6 ); + TESTCASE( val2 == 9 ); + } +#endif + } + /* PDCLIB-20: Verify "unusual" combinations of length and signedness */ + PRINTF_TEST( 1, "1", "%tu", (ptrdiff_t) 1); /* unsigned prtdiff_t */ + PRINTF_TEST( 2, "-1", "%jd", (intmax_t) -1); /* intmax_t */ + PRINTF_TEST( 1, "1", "%ju", (uintmax_t) 1); /* uintmax_t */ + PRINTF_TEST( 1, "1", "%zd", (size_t) 1); /* signed size_t */ + +/****************************************************************************** + * NOTE: The following test cases are imported from the Tyndur project. They * + * are therefore under the license of said project, not CC0. * + * As said code comprises test cases, it does not form part of the * + * final compiled library, and has no bearing on its licensing. * + * * + * See bug PDCLIB-6 for full details * + ******************************************************************************/ +/* + * Copyright (c) 2011 The tyndur Project. All rights reserved. + * + * This code is derived from software contributed to the tyndur Project + * by Kevin Wolf. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 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 HOLDERS 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 TEST_CONVERSION_ONLY + /* Ein String ohne alles */ + PRINTF_TEST(12, "Hallo heimur", "Hallo heimur"); +#endif + /* Einfache Konvertierungen */ + PRINTF_TEST(12, "Hallo heimur", "%s", "Hallo heimur"); + PRINTF_TEST(4, "1024", "%d", 1024); + PRINTF_TEST(5, "-1024", "%d", -1024); + PRINTF_TEST(4, "1024", "%i", 1024); + PRINTF_TEST(5, "-1024", "%i", -1024); + PRINTF_TEST(4, "1024", "%u", 1024u); + PRINTF_TEST(10, "4294966272", "%u", -1024u); + PRINTF_TEST(3, "777", "%o", 0777u); + PRINTF_TEST(11, "37777777001", "%o", -0777u); + PRINTF_TEST(8, "1234abcd", "%x", 0x1234abcdu); + PRINTF_TEST(8, "edcb5433", "%x", -0x1234abcdu); + PRINTF_TEST(8, "1234ABCD", "%X", 0x1234abcdu); + PRINTF_TEST(8, "EDCB5433", "%X", -0x1234abcdu); + PRINTF_TEST(1, "x", "%c", 'x'); + PRINTF_TEST(1, "%", "%%"); + /* Mit %c kann man auch Nullbytes ausgeben */ + PRINTF_TEST(1, "\0", "%c", '\0'); + /* Vorzeichen erzwingen (Flag +) */ + PRINTF_TEST(12, "Hallo heimur", "%+s", "Hallo heimur"); + PRINTF_TEST(5, "+1024", "%+d", 1024); + PRINTF_TEST(5, "-1024", "%+d", -1024); + PRINTF_TEST(5, "+1024", "%+i", 1024); + PRINTF_TEST(5, "-1024", "%+i", -1024); + PRINTF_TEST(4, "1024", "%+u", 1024u); + PRINTF_TEST(10, "4294966272", "%+u", -1024u); + PRINTF_TEST(3, "777", "%+o", 0777u); + PRINTF_TEST(11, "37777777001", "%+o", -0777u); + PRINTF_TEST(8, "1234abcd", "%+x", 0x1234abcdu); + PRINTF_TEST(8, "edcb5433", "%+x", -0x1234abcdu); + PRINTF_TEST(8, "1234ABCD", "%+X", 0x1234abcdu); + PRINTF_TEST(8, "EDCB5433", "%+X", -0x1234abcdu); + PRINTF_TEST(1, "x", "%+c", 'x'); + /* Vorzeichenplatzhalter erzwingen (Flag ) */ + PRINTF_TEST(12, "Hallo heimur", "% s", "Hallo heimur"); + PRINTF_TEST(5, " 1024", "% d", 1024); + PRINTF_TEST(5, "-1024", "% d", -1024); + PRINTF_TEST(5, " 1024", "% i", 1024); + PRINTF_TEST(5, "-1024", "% i", -1024); + PRINTF_TEST(4, "1024", "% u", 1024u); + PRINTF_TEST(10, "4294966272", "% u", -1024u); + PRINTF_TEST(3, "777", "% o", 0777u); + PRINTF_TEST(11, "37777777001", "% o", -0777u); + PRINTF_TEST(8, "1234abcd", "% x", 0x1234abcdu); + PRINTF_TEST(8, "edcb5433", "% x", -0x1234abcdu); + PRINTF_TEST(8, "1234ABCD", "% X", 0x1234abcdu); + PRINTF_TEST(8, "EDCB5433", "% X", -0x1234abcdu); + PRINTF_TEST(1, "x", "% c", 'x'); + /* Flag + hat Vorrang über */ + PRINTF_TEST(12, "Hallo heimur", "%+ s", "Hallo heimur"); + PRINTF_TEST(5, "+1024", "%+ d", 1024); + PRINTF_TEST(5, "-1024", "%+ d", -1024); + PRINTF_TEST(5, "+1024", "%+ i", 1024); + PRINTF_TEST(5, "-1024", "%+ i", -1024); + PRINTF_TEST(4, "1024", "%+ u", 1024u); + PRINTF_TEST(10, "4294966272", "%+ u", -1024u); + PRINTF_TEST(3, "777", "%+ o", 0777u); + PRINTF_TEST(11, "37777777001", "%+ o", -0777u); + PRINTF_TEST(8, "1234abcd", "%+ x", 0x1234abcdu); + PRINTF_TEST(8, "edcb5433", "%+ x", -0x1234abcdu); + PRINTF_TEST(8, "1234ABCD", "%+ X", 0x1234abcdu); + PRINTF_TEST(8, "EDCB5433", "%+ X", -0x1234abcdu); + PRINTF_TEST(1, "x", "%+ c", 'x'); + /* Alternative Form */ + PRINTF_TEST(4, "0777", "%#o", 0777u); + PRINTF_TEST(12, "037777777001", "%#o", -0777u); + PRINTF_TEST(10, "0x1234abcd", "%#x", 0x1234abcdu); + PRINTF_TEST(10, "0xedcb5433", "%#x", -0x1234abcdu); + PRINTF_TEST(10, "0X1234ABCD", "%#X", 0x1234abcdu); + PRINTF_TEST(10, "0XEDCB5433", "%#X", -0x1234abcdu); + PRINTF_TEST(1, "0", "%#o", 0u); + PRINTF_TEST(1, "0", "%#x", 0u); + PRINTF_TEST(1, "0", "%#X", 0u); + /* Feldbreite: Kleiner als Ausgabe */ + PRINTF_TEST(12, "Hallo heimur", "%1s", "Hallo heimur"); + PRINTF_TEST(4, "1024", "%1d", 1024); + PRINTF_TEST(5, "-1024", "%1d", -1024); + PRINTF_TEST(4, "1024", "%1i", 1024); + PRINTF_TEST(5, "-1024", "%1i", -1024); + PRINTF_TEST(4, "1024", "%1u", 1024u); + PRINTF_TEST(10, "4294966272", "%1u", -1024u); + PRINTF_TEST(3, "777", "%1o", 0777u); + PRINTF_TEST(11, "37777777001", "%1o", -0777u); + PRINTF_TEST(8, "1234abcd", "%1x", 0x1234abcdu); + PRINTF_TEST(8, "edcb5433", "%1x", -0x1234abcdu); + PRINTF_TEST(8, "1234ABCD", "%1X", 0x1234abcdu); + PRINTF_TEST(8, "EDCB5433", "%1X", -0x1234abcdu); + PRINTF_TEST(1, "x", "%1c", 'x'); + /* Feldbreite: Größer als Ausgabe */ + PRINTF_TEST(20, " Hallo", "%20s", "Hallo"); + PRINTF_TEST(20, " 1024", "%20d", 1024); + PRINTF_TEST(20, " -1024", "%20d", -1024); + PRINTF_TEST(20, " 1024", "%20i", 1024); + PRINTF_TEST(20, " -1024", "%20i", -1024); + PRINTF_TEST(20, " 1024", "%20u", 1024u); + PRINTF_TEST(20, " 4294966272", "%20u", -1024u); + PRINTF_TEST(20, " 777", "%20o", 0777u); + PRINTF_TEST(20, " 37777777001", "%20o", -0777u); + PRINTF_TEST(20, " 1234abcd", "%20x", 0x1234abcdu); + PRINTF_TEST(20, " edcb5433", "%20x", -0x1234abcdu); + PRINTF_TEST(20, " 1234ABCD", "%20X", 0x1234abcdu); + PRINTF_TEST(20, " EDCB5433", "%20X", -0x1234abcdu); + PRINTF_TEST(20, " x", "%20c", 'x'); + /* Feldbreite: Linksbündig */ + PRINTF_TEST(20, "Hallo ", "%-20s", "Hallo"); + PRINTF_TEST(20, "1024 ", "%-20d", 1024); + PRINTF_TEST(20, "-1024 ", "%-20d", -1024); + PRINTF_TEST(20, "1024 ", "%-20i", 1024); + PRINTF_TEST(20, "-1024 ", "%-20i", -1024); + PRINTF_TEST(20, "1024 ", "%-20u", 1024u); + PRINTF_TEST(20, "4294966272 ", "%-20u", -1024u); + PRINTF_TEST(20, "777 ", "%-20o", 0777u); + PRINTF_TEST(20, "37777777001 ", "%-20o", -0777u); + PRINTF_TEST(20, "1234abcd ", "%-20x", 0x1234abcdu); + PRINTF_TEST(20, "edcb5433 ", "%-20x", -0x1234abcdu); + PRINTF_TEST(20, "1234ABCD ", "%-20X", 0x1234abcdu); + PRINTF_TEST(20, "EDCB5433 ", "%-20X", -0x1234abcdu); + PRINTF_TEST(20, "x ", "%-20c", 'x'); + /* Feldbreite: Padding mit 0 */ + PRINTF_TEST(20, "00000000000000001024", "%020d", 1024); + PRINTF_TEST(20, "-0000000000000001024", "%020d", -1024); + PRINTF_TEST(20, "00000000000000001024", "%020i", 1024); + PRINTF_TEST(20, "-0000000000000001024", "%020i", -1024); + PRINTF_TEST(20, "00000000000000001024", "%020u", 1024u); + PRINTF_TEST(20, "00000000004294966272", "%020u", -1024u); + PRINTF_TEST(20, "00000000000000000777", "%020o", 0777u); + PRINTF_TEST(20, "00000000037777777001", "%020o", -0777u); + PRINTF_TEST(20, "0000000000001234abcd", "%020x", 0x1234abcdu); + PRINTF_TEST(20, "000000000000edcb5433", "%020x", -0x1234abcdu); + PRINTF_TEST(20, "0000000000001234ABCD", "%020X", 0x1234abcdu); + PRINTF_TEST(20, "000000000000EDCB5433", "%020X", -0x1234abcdu); + /* Feldbreite: Padding und alternative Form */ + PRINTF_TEST(20, " 0777", "%#20o", 0777u); + PRINTF_TEST(20, " 037777777001", "%#20o", -0777u); + PRINTF_TEST(20, " 0x1234abcd", "%#20x", 0x1234abcdu); + PRINTF_TEST(20, " 0xedcb5433", "%#20x", -0x1234abcdu); + PRINTF_TEST(20, " 0X1234ABCD", "%#20X", 0x1234abcdu); + PRINTF_TEST(20, " 0XEDCB5433", "%#20X", -0x1234abcdu); + PRINTF_TEST(20, "00000000000000000777", "%#020o", 0777u); + PRINTF_TEST(20, "00000000037777777001", "%#020o", -0777u); + PRINTF_TEST(20, "0x00000000001234abcd", "%#020x", 0x1234abcdu); + PRINTF_TEST(20, "0x0000000000edcb5433", "%#020x", -0x1234abcdu); + PRINTF_TEST(20, "0X00000000001234ABCD", "%#020X", 0x1234abcdu); + PRINTF_TEST(20, "0X0000000000EDCB5433", "%#020X", -0x1234abcdu); + /* Feldbreite: - hat Vorrang vor 0 */ + PRINTF_TEST(20, "Hallo ", "%0-20s", "Hallo"); + PRINTF_TEST(20, "1024 ", "%0-20d", 1024); + PRINTF_TEST(20, "-1024 ", "%0-20d", -1024); + PRINTF_TEST(20, "1024 ", "%0-20i", 1024); + PRINTF_TEST(20, "-1024 ", "%0-20i", -1024); + PRINTF_TEST(20, "1024 ", "%0-20u", 1024u); + PRINTF_TEST(20, "4294966272 ", "%0-20u", -1024u); + PRINTF_TEST(20, "777 ", "%-020o", 0777u); + PRINTF_TEST(20, "37777777001 ", "%-020o", -0777u); + PRINTF_TEST(20, "1234abcd ", "%-020x", 0x1234abcdu); + PRINTF_TEST(20, "edcb5433 ", "%-020x", -0x1234abcdu); + PRINTF_TEST(20, "1234ABCD ", "%-020X", 0x1234abcdu); + PRINTF_TEST(20, "EDCB5433 ", "%-020X", -0x1234abcdu); + PRINTF_TEST(20, "x ", "%-020c", 'x'); + /* Feldbreite: Aus Parameter */ + PRINTF_TEST(20, " Hallo", "%*s", 20, "Hallo"); + PRINTF_TEST(20, " 1024", "%*d", 20, 1024); + PRINTF_TEST(20, " -1024", "%*d", 20, -1024); + PRINTF_TEST(20, " 1024", "%*i", 20, 1024); + PRINTF_TEST(20, " -1024", "%*i", 20, -1024); + PRINTF_TEST(20, " 1024", "%*u", 20, 1024u); + PRINTF_TEST(20, " 4294966272", "%*u", 20, -1024u); + PRINTF_TEST(20, " 777", "%*o", 20, 0777u); + PRINTF_TEST(20, " 37777777001", "%*o", 20, -0777u); + PRINTF_TEST(20, " 1234abcd", "%*x", 20, 0x1234abcdu); + PRINTF_TEST(20, " edcb5433", "%*x", 20, -0x1234abcdu); + PRINTF_TEST(20, " 1234ABCD", "%*X", 20, 0x1234abcdu); + PRINTF_TEST(20, " EDCB5433", "%*X", 20, -0x1234abcdu); + PRINTF_TEST(20, " x", "%*c", 20, 'x'); + /* Präzision / Mindestanzahl von Ziffern */ + PRINTF_TEST(12, "Hallo heimur", "%.20s", "Hallo heimur"); + PRINTF_TEST(20, "00000000000000001024", "%.20d", 1024); + PRINTF_TEST(21, "-00000000000000001024", "%.20d", -1024); + PRINTF_TEST(20, "00000000000000001024", "%.20i", 1024); + PRINTF_TEST(21, "-00000000000000001024", "%.20i", -1024); + PRINTF_TEST(20, "00000000000000001024", "%.20u", 1024u); + PRINTF_TEST(20, "00000000004294966272", "%.20u", -1024u); + PRINTF_TEST(20, "00000000000000000777", "%.20o", 0777u); + PRINTF_TEST(20, "00000000037777777001", "%.20o", -0777u); + PRINTF_TEST(20, "0000000000001234abcd", "%.20x", 0x1234abcdu); + PRINTF_TEST(20, "000000000000edcb5433", "%.20x", -0x1234abcdu); + PRINTF_TEST(20, "0000000000001234ABCD", "%.20X", 0x1234abcdu); + PRINTF_TEST(20, "000000000000EDCB5433", "%.20X", -0x1234abcdu); + /* Feldbreite und Präzision */ + PRINTF_TEST(20, " Hallo", "%20.5s", "Hallo heimur"); + PRINTF_TEST(20, " 01024", "%20.5d", 1024); + PRINTF_TEST(20, " -01024", "%20.5d", -1024); + PRINTF_TEST(20, " 01024", "%20.5i", 1024); + PRINTF_TEST(20, " -01024", "%20.5i", -1024); + PRINTF_TEST(20, " 01024", "%20.5u", 1024u); + PRINTF_TEST(20, " 4294966272", "%20.5u", -1024u); + PRINTF_TEST(20, " 00777", "%20.5o", 0777u); + PRINTF_TEST(20, " 37777777001", "%20.5o", -0777u); + PRINTF_TEST(20, " 1234abcd", "%20.5x", 0x1234abcdu); + PRINTF_TEST(20, " 00edcb5433", "%20.10x", -0x1234abcdu); + PRINTF_TEST(20, " 1234ABCD", "%20.5X", 0x1234abcdu); + PRINTF_TEST(20, " 00EDCB5433", "%20.10X", -0x1234abcdu); + /* Präzision: 0 wird ignoriert */ + PRINTF_TEST(20, " Hallo", "%020.5s", "Hallo heimur"); + PRINTF_TEST(20, " 01024", "%020.5d", 1024); + PRINTF_TEST(20, " -01024", "%020.5d", -1024); + PRINTF_TEST(20, " 01024", "%020.5i", 1024); + PRINTF_TEST(20, " -01024", "%020.5i", -1024); + PRINTF_TEST(20, " 01024", "%020.5u", 1024u); + PRINTF_TEST(20, " 4294966272", "%020.5u", -1024u); + PRINTF_TEST(20, " 00777", "%020.5o", 0777u); + PRINTF_TEST(20, " 37777777001", "%020.5o", -0777u); + PRINTF_TEST(20, " 1234abcd", "%020.5x", 0x1234abcdu); + PRINTF_TEST(20, " 00edcb5433", "%020.10x", -0x1234abcdu); + PRINTF_TEST(20, " 1234ABCD", "%020.5X", 0x1234abcdu); + PRINTF_TEST(20, " 00EDCB5433", "%020.10X", -0x1234abcdu); + /* Präzision 0 */ + PRINTF_TEST(0, "", "%.0s", "Hallo heimur"); + PRINTF_TEST(20, " ", "%20.0s", "Hallo heimur"); + PRINTF_TEST(0, "", "%.s", "Hallo heimur"); + PRINTF_TEST(20, " ", "%20.s", "Hallo heimur"); + PRINTF_TEST(20, " 1024", "%20.0d", 1024); + PRINTF_TEST(20, " -1024", "%20.d", -1024); + PRINTF_TEST(20, " ", "%20.d", 0); + PRINTF_TEST(20, " 1024", "%20.0i", 1024); + PRINTF_TEST(20, " -1024", "%20.i", -1024); + PRINTF_TEST(20, " ", "%20.i", 0); + PRINTF_TEST(20, " 1024", "%20.u", 1024u); + PRINTF_TEST(20, " 4294966272", "%20.0u", -1024u); + PRINTF_TEST(20, " ", "%20.u", 0u); + PRINTF_TEST(20, " 777", "%20.o", 0777u); + PRINTF_TEST(20, " 37777777001", "%20.0o", -0777u); + PRINTF_TEST(20, " ", "%20.o", 0u); + PRINTF_TEST(20, " 1234abcd", "%20.x", 0x1234abcdu); + PRINTF_TEST(20, " edcb5433", "%20.0x", -0x1234abcdu); + PRINTF_TEST(20, " ", "%20.x", 0u); + PRINTF_TEST(20, " 1234ABCD", "%20.X", 0x1234abcdu); + PRINTF_TEST(20, " EDCB5433", "%20.0X", -0x1234abcdu); + PRINTF_TEST(20, " ", "%20.X", 0u); + /* Negative Präzision wird ignoriert */ + /* XXX glibc tut nicht, was ich erwartet habe, vorerst deaktiviert... */ + /* + * Präzision und Feldbreite aus Parameter. + * + hat Vorrang vor , - hat Vorrang vor 0 (das eh ignoriert wird, + * weil eine Präzision angegeben ist) + */ + PRINTF_TEST(20, "Hallo ", "% -0+*.*s", 20, 5, "Hallo heimur"); + PRINTF_TEST(20, "+01024 ", "% -0+*.*d", 20, 5, 1024); + PRINTF_TEST(20, "-01024 ", "% -0+*.*d", 20, 5, -1024); + PRINTF_TEST(20, "+01024 ", "% -0+*.*i", 20, 5, 1024); + PRINTF_TEST(20, "-01024 ", "% 0-+*.*i", 20, 5, -1024); + PRINTF_TEST(20, "01024 ", "% 0-+*.*u", 20, 5, 1024u); + PRINTF_TEST(20, "4294966272 ", "% 0-+*.*u", 20, 5, -1024u); + PRINTF_TEST(20, "00777 ", "%+ -0*.*o", 20, 5, 0777u); + PRINTF_TEST(20, "37777777001 ", "%+ -0*.*o", 20, 5, -0777u); + PRINTF_TEST(20, "1234abcd ", "%+ -0*.*x", 20, 5, 0x1234abcdu); + PRINTF_TEST(20, "00edcb5433 ", "%+ -0*.*x", 20, 10, -0x1234abcdu); + PRINTF_TEST(20, "1234ABCD ", "% -+0*.*X", 20, 5, 0x1234abcdu); + PRINTF_TEST(20, "00EDCB5433 ", "% -+0*.*X", 20, 10, -0x1234abcdu); +#pragma clang diagnostic pop + } + +#undef PRINTF_TEST +/******************************************************************************/ diff --git a/src/libraries/libc/tests/scanf_testcases.h b/src/libraries/libc/tests/scanf_testcases.h new file mode 100644 index 0000000..e654724 --- /dev/null +++ b/src/libraries/libc/tests/scanf_testcases.h @@ -0,0 +1,96 @@ +#define SCANF_TEST( expected_rc, input_string, ... ) do { \ + TEST_RESULTS += DO_TESTSCANF(IMPLFILE, __FILE__, __LINE__, expected_rc, input_string, __VA_ARGS__); \ + } while (0); + +{ + char buffer[100]; + int i; + unsigned int u; + int * p; + /* basic: reading of three-char string */ + SCANF_TEST( 1, "foo", "%3c", buffer ); + TESTCASE( memcmp( buffer, "foo", 3 ) == 0 ); +#ifndef TEST_CONVERSION_ONLY + /* %% for single % */ + SCANF_TEST( 1, "%x", "%%%c", buffer ); + TESTCASE( buffer[0] == 'x' ); + /* * to skip assignment */ + SCANF_TEST( 1, "3xfoo", "%*dx%3c", buffer ); + TESTCASE( memcmp( buffer, "foo", 3 ) == 0 ); +#endif + /* domain testing on 'int' type */ + SCANF_TEST( 1, "-" INT_MIN_DEZ_STR, "%d", &i ); + TESTCASE( i == INT_MIN ); + SCANF_TEST( 1, INT_MAX_DEZ_STR, "%d", &i ); + TESTCASE( i == INT_MAX ); + SCANF_TEST( 1, "-1", "%d", &i ); + TESTCASE( i == -1 ); + SCANF_TEST( 1, "0", "%d", &i ); + TESTCASE( i == 0 ); + SCANF_TEST( 1, "1", "%d", &i ); + TESTCASE( i == 1 ); + SCANF_TEST( 1, "-" INT_MIN_DEZ_STR, "%i", &i ); + TESTCASE( i == INT_MIN ); + SCANF_TEST( 1, INT_MAX_DEZ_STR, "%i", &i ); + TESTCASE( i == INT_MAX ); + SCANF_TEST( 1, "-1", "%i", &i ); + TESTCASE( i == -1 ); + SCANF_TEST( 1, "0", "%i", &i ); + TESTCASE( i == 0 ); + SCANF_TEST( 1, "1", "%i", &i ); + TESTCASE( i == 1 ); + SCANF_TEST( 1, "0x7" INT_HEXDIG, "%i", &i ); + TESTCASE( i == INT_MAX ); + SCANF_TEST( 1, "0x0", "%i", &i ); + TESTCASE( i == 0 ); +#ifndef TEST_CONVERSION_ONLY + SCANF_TEST( 1, "00", "%i%n", &i, &u ); + TESTCASE( i == 0 ); + TESTCASE( u == 2 ); +#endif + /* domain testing on 'unsigned int' type */ + SCANF_TEST( 1, UINT_MAX_DEZ_STR, "%u", &u ); + TESTCASE( u == UINT_MAX ); + SCANF_TEST( 1, "0", "%u", &u ); + TESTCASE( u == 0 ); + SCANF_TEST( 1, "f" INT_HEXDIG, "%x", &u ); + TESTCASE( u == UINT_MAX ); + SCANF_TEST( 1, "7" INT_HEXDIG, "%x", &u ); + TESTCASE( u == INT_MAX ); + SCANF_TEST( 1, "0", "%o", &u ); + TESTCASE( u == 0 ); + SCANF_TEST( 1, INT_OCTDIG, "%o", &u ); + TESTCASE( u == UINT_MAX ); + /* testing %c */ + memset( buffer, '\0', 100 ); + SCANF_TEST( 1, "x", "%c", buffer ); + TESTCASE( memcmp( buffer, "x\0", 2 ) == 0 ); + /* testing %s */ + memset( buffer, '\0', 100 ); + SCANF_TEST( 1, "foo bar", "%s", buffer ); + TESTCASE( memcmp( buffer, "foo\0", 4 ) == 0 ); +#ifndef TEST_CONVERSION_ONLY + SCANF_TEST( 2, "foo bar baz", "%s %s %n", buffer, buffer + 4, &u ); + TESTCASE( u == 9 ); + TESTCASE( memcmp( buffer, "foo\0bar\0", 8 ) == 0 ); +#endif + /* testing %[ */ + SCANF_TEST( 1, "abcdefg", "%[cba]", buffer ); + TESTCASE( memcmp( buffer, "abc\0", 4 ) == 0 ); + /* testing %p */ + p = NULL; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-pedantic" + sprintf( buffer, "%p", p ); + p = &i; + SCANF_TEST( 1, buffer, "%p", &p ); + TESTCASE( p == NULL ); + p = &i; + sprintf( buffer, "%p", p ); + p = NULL; + SCANF_TEST( 1, buffer, "%p", &p ); + TESTCASE( p == &i ); +#pragma clang diagnostic pop +} + +#undef SCANF_TEST diff --git a/src/libraries/libc/tests/test_assert.c b/src/libraries/libc/tests/test_assert.c new file mode 100644 index 0000000..cf4763e --- /dev/null +++ b/src/libraries/libc/tests/test_assert.c @@ -0,0 +1,50 @@ +#include "_PDCLIB_test.h" + +#define NDEBUG +#include + +#define enabled_assert( expression ) ( ( expression ) ? (void) 0 \ + : _PDCLIB_assert( "Assertion failed: " #expression \ + ", function ", __func__, \ + ", file " __FILE__ \ + ", line " _PDCLIB_symbol2string( __LINE__ ) \ + "." _PDCLIB_endl ) ) + +static int EXPECTED = 0; +static void aborthandler( int sig ) +{ + _exit(!EXPECTED); +} + +static int assert_disabled_test( void ) +{ + int i = 0; + assert( i == 0 ); /* NDEBUG set, condition met */ + assert( i == 1 ); /* NDEBUG set, condition fails */ + return i; +} + +int assert_enabled_test(int val) +{ + enabled_assert(val); + return val; +} + +START_TEST( assert ) +{ + sighandler_t sh = signal(SIGABRT, &aborthandler); + TESTCASE( sh != SIG_ERR ); + + TESTCASE( assert_disabled_test() == 0 ); + TESTCASE( assert_enabled_test(1) ); /* NDEBUG not set, condition met */ + + EXPECTED = 1; + TESTCASE( assert_enabled_test(0) ); /* NDEBUG not set, condition fails - should abort */ +} +END_TEST + +START_SUITE( assert ) +{ + RUN_TEST( assert ); +} +END_SUITE diff --git a/src/libraries/libc/tests/test_ctype.c b/src/libraries/libc/tests/test_ctype.c new file mode 100644 index 0000000..bf99f1e --- /dev/null +++ b/src/libraries/libc/tests/test_ctype.c @@ -0,0 +1,186 @@ +#include "_PDCLIB_test.h" + +START_TEST( isalnum ) +{ + TESTCASE( isalnum( 'a' ) ); + TESTCASE( isalnum( 'z' ) ); + TESTCASE( isalnum( 'A' ) ); + TESTCASE( isalnum( 'Z' ) ); + TESTCASE( isalnum( '0' ) ); + TESTCASE( isalnum( '9' ) ); + TESTCASE( ! isalnum( ' ' ) ); + TESTCASE( ! isalnum( '\n' ) ); + TESTCASE( ! isalnum( '@' ) ); +} +END_TEST + +START_TEST( isalpha ) +{ + TESTCASE( isalpha( 'a' ) ); + TESTCASE( isalpha( 'z' ) ); + TESTCASE( ! isalpha( ' ' ) ); + TESTCASE( ! isalpha( '1' ) ); + TESTCASE( ! isalpha( '@' ) ); +} +END_TEST + +START_TEST( isblank ) +{ + TESTCASE( isblank( ' ' ) ); + TESTCASE( isblank( '\t' ) ); + TESTCASE( ! isblank( '\v' ) ); + TESTCASE( ! isblank( '\r' ) ); + TESTCASE( ! isblank( 'x' ) ); + TESTCASE( ! isblank( '@' ) ); +} +END_TEST + +START_TEST( iscntrl ) +{ + TESTCASE( iscntrl( '\a' ) ); + TESTCASE( iscntrl( '\b' ) ); + TESTCASE( iscntrl( '\n' ) ); + TESTCASE( ! iscntrl( ' ' ) ); +} +END_TEST + +START_TEST( isdigit ) +{ + TESTCASE( isdigit( '0' ) ); + TESTCASE( isdigit( '9' ) ); + TESTCASE( ! isdigit( ' ' ) ); + TESTCASE( ! isdigit( 'a' ) ); + TESTCASE( ! isdigit( '@' ) ); +} +END_TEST + +START_TEST( isgraph ) +{ + TESTCASE( isgraph( 'a' ) ); + TESTCASE( isgraph( 'z' ) ); + TESTCASE( isgraph( 'A' ) ); + TESTCASE( isgraph( 'Z' ) ); + TESTCASE( isgraph( '@' ) ); + TESTCASE( ! isgraph( '\t' ) ); + TESTCASE( ! isgraph( '\0' ) ); + TESTCASE( ! isgraph( ' ' ) ); +} +END_TEST + +START_TEST( islower ) +{ + TESTCASE( islower( 'a' ) ); + TESTCASE( islower( 'z' ) ); + TESTCASE( ! islower( 'A' ) ); + TESTCASE( ! islower( 'Z' ) ); + TESTCASE( ! islower( ' ' ) ); + TESTCASE( ! islower( '@' ) ); +} +END_TEST + +START_TEST( isprint ) +{ + TESTCASE( isprint( 'a' ) ); + TESTCASE( isprint( 'z' ) ); + TESTCASE( isprint( 'A' ) ); + TESTCASE( isprint( 'Z' ) ); + TESTCASE( isprint( '@' ) ); + TESTCASE( ! isprint( '\t' ) ); + TESTCASE( ! isprint( '\0' ) ); + TESTCASE( isprint( ' ' ) ); +} +END_TEST + +START_TEST( ispunct ) +{ + TESTCASE( ! ispunct( 'a' ) ); + TESTCASE( ! ispunct( 'z' ) ); + TESTCASE( ! ispunct( 'A' ) ); + TESTCASE( ! ispunct( 'Z' ) ); + TESTCASE( ispunct( '@' ) ); + TESTCASE( ispunct( '.' ) ); + TESTCASE( ! ispunct( '\t' ) ); + TESTCASE( ! ispunct( '\0' ) ); + TESTCASE( ! ispunct( ' ' ) ); +} +END_TEST + +START_TEST( isspace ) +{ + TESTCASE( isspace( ' ' ) ); + TESTCASE( isspace( '\f' ) ); + TESTCASE( isspace( '\n' ) ); + TESTCASE( isspace( '\r' ) ); + TESTCASE( isspace( '\t' ) ); + TESTCASE( isspace( '\v' ) ); + TESTCASE( ! isspace( 'a' ) ); +} +END_TEST + +START_TEST( isupper ) +{ + TESTCASE( isupper( 'A' ) ); + TESTCASE( isupper( 'Z' ) ); + TESTCASE( ! isupper( 'a' ) ); + TESTCASE( ! isupper( 'z' ) ); + TESTCASE( ! isupper( ' ' ) ); + TESTCASE( ! isupper( '@' ) ); +} +END_TEST + +START_TEST( isxdigit ) +{ + TESTCASE( isxdigit( '0' ) ); + TESTCASE( isxdigit( '9' ) ); + TESTCASE( isxdigit( 'a' ) ); + TESTCASE( isxdigit( 'f' ) ); + TESTCASE( ! isxdigit( 'g' ) ); + TESTCASE( isxdigit( 'A' ) ); + TESTCASE( isxdigit( 'F' ) ); + TESTCASE( ! isxdigit( 'G' ) ); + TESTCASE( ! isxdigit( '@' ) ); + TESTCASE( ! isxdigit( ' ' ) ); +} +END_TEST + +START_TEST( tolower ) +{ + TESTCASE( tolower( 'A' ) == 'a' ); + TESTCASE( tolower( 'Z' ) == 'z' ); + TESTCASE( tolower( 'a' ) == 'a' ); + TESTCASE( tolower( 'z' ) == 'z' ); + TESTCASE( tolower( '@' ) == '@' ); + TESTCASE( tolower( '[' ) == '[' ); +} +END_TEST + +START_TEST( toupper ) +{ + TESTCASE( toupper( 'a' ) == 'A' ); + TESTCASE( toupper( 'z' ) == 'Z' ); + TESTCASE( toupper( 'A' ) == 'A' ); + TESTCASE( toupper( 'Z' ) == 'Z' ); + TESTCASE( toupper( '@' ) == '@' ); + TESTCASE( toupper( '[' ) == '[' ); +} +END_TEST + + +START_SUITE( ctype ) +{ + RUN_TEST( isalnum ); + RUN_TEST( isalpha ); + RUN_TEST( isblank ); + RUN_TEST( iscntrl ); + RUN_TEST( isdigit ); + RUN_TEST( isgraph ); + RUN_TEST( islower ); + RUN_TEST( isprint ); + RUN_TEST( ispunct ); + RUN_TEST( isspace ); + RUN_TEST( isupper ); + RUN_TEST( isxdigit ); + RUN_TEST( tolower ); + RUN_TEST( toupper ); +} +END_SUITE diff --git a/src/libraries/libc/tests/test_internal.c b/src/libraries/libc/tests/test_internal.c new file mode 100644 index 0000000..ba95658 --- /dev/null +++ b/src/libraries/libc/tests/test_internal.c @@ -0,0 +1,724 @@ +#include "_PDCLIB_test.h" +#include "_PDCLIB_iotest.h" +#include + +START_TEST( atomax ) +{ + /* basic functionality */ + TESTCASE( _PDCLIB_atomax( "123" ) == 123 ); + /* testing skipping of leading whitespace and trailing garbage */ + TESTCASE( _PDCLIB_atomax( " \n\v\t\f123xyz" ) == 123 ); +} +END_TEST + +START_TEST( digits ) +{ + TESTCASE( strcmp( _PDCLIB_digits, "0123456789abcdefghijklmnopqrstuvwxyz" ) == 0 ); + TESTCASE( strcmp( _PDCLIB_Xdigits, "0123456789ABCDEF" ) == 0 ); +} +END_TEST + +START_TEST( filemode ) +{ + TESTCASE( _PDCLIB_filemode( "r" ) == _PDCLIB_FREAD ); + TESTCASE( _PDCLIB_filemode( "w" ) == _PDCLIB_FWRITE ); + TESTCASE( _PDCLIB_filemode( "a" ) == ( _PDCLIB_FAPPEND | _PDCLIB_FWRITE ) ); + TESTCASE( _PDCLIB_filemode( "r+" ) == ( _PDCLIB_FREAD | _PDCLIB_FRW ) ); + TESTCASE( _PDCLIB_filemode( "w+" ) == ( _PDCLIB_FWRITE | _PDCLIB_FRW ) ); + TESTCASE( _PDCLIB_filemode( "a+" ) == ( _PDCLIB_FAPPEND | _PDCLIB_FWRITE | _PDCLIB_FRW ) ); + TESTCASE( _PDCLIB_filemode( "rb" ) == ( _PDCLIB_FREAD | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "wb" ) == ( _PDCLIB_FWRITE | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "ab" ) == ( _PDCLIB_FAPPEND | _PDCLIB_FWRITE | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "r+b" ) == ( _PDCLIB_FREAD | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "w+b" ) == ( _PDCLIB_FWRITE | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "a+b" ) == ( _PDCLIB_FAPPEND | _PDCLIB_FWRITE | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "rb+" ) == ( _PDCLIB_FREAD | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "wb+" ) == ( _PDCLIB_FWRITE | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "ab+" ) == ( _PDCLIB_FAPPEND | _PDCLIB_FWRITE | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "x" ) == 0 ); + TESTCASE( _PDCLIB_filemode( "r++" ) == 0 ); + TESTCASE( _PDCLIB_filemode( "wbb" ) == 0 ); + TESTCASE( _PDCLIB_filemode( "a+bx" ) == 0 ); +} +END_TEST + +START_TEST( is_leap ) +{ + /* 1901 not leap */ + TESTCASE( ! _PDCLIB_is_leap( 1 ) ); + /* 1904 leap */ + TESTCASE( _PDCLIB_is_leap( 4 ) ); + /* 1900 not leap */ + TESTCASE( ! _PDCLIB_is_leap( 0 ) ); + /* 2000 leap */ + TESTCASE( _PDCLIB_is_leap( 100 ) ); +} +END_TEST + +START_TEST( load_lc_ctype ) +{ + FILE * fh = fopen( "test_ctype.dat", "wb" ); + TESTCASE_REQUIRE( fh != NULL ); + /* For test purposes, let's set up a charset that only has the hex digits */ + /* 0x00..0x09 - digits */ + /* 0x11..0x16 - Xdigits */ + /* 0x21..0x26 - xdigits */ + TESTCASE( j6libc_fprintf( fh, "%x %x\n", 0x00, 0x09 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x %x\n", 0x11, 0x16, 0x21, 0x26 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x00, 0x00 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x01, 0x01 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x02, 0x02 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x03, 0x03 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x04, 0x04 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x05, 0x05 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x06, 0x06 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x07, 0x07 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x08, 0x08 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x09, 0x09 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x0A, 0x0A ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x0B, 0x0B ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x0C, 0x0C ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x0D, 0x0D ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x0E, 0x0E ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x0F, 0x0F ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x10, 0x10 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x11, 0x11 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x12, 0x12 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x13, 0x13 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x14, 0x14 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x15, 0x15 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x16, 0x16 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x17, 0x17 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x18, 0x18 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x19, 0x19 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x1A, 0x1A ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x1B, 0x1B ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x1C, 0x1C ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x1D, 0x1D ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x1E, 0x1E ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x1F, 0x1F ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x20, 0x20 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x21, 0x21 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x22, 0x22 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x23, 0x23 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x24, 0x24 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x25, 0x25 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x26, 0x26 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x27, 0x27 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x28, 0x28 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x29, 0x29 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x2A, 0x2A ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x2B, 0x2B ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x2C, 0x2C ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x2D, 0x2D ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x2E, 0x2E ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x2F, 0x2F ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x30, 0x30 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x31, 0x31 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x32, 0x32 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x33, 0x33 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x34, 0x34 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x35, 0x35 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x36, 0x36 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x37, 0x37 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x38, 0x38 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x39, 0x39 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x3A, 0x3A ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x3B, 0x3B ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x3C, 0x3C ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x3D, 0x3D ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x3E, 0x3E ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x3F, 0x3F ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x40, 0x40 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x41, 0x41 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x42, 0x42 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x43, 0x43 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x44, 0x44 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x45, 0x45 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x46, 0x46 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x47, 0x47 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x48, 0x48 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x49, 0x49 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x4A, 0x4A ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x4B, 0x4B ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x4C, 0x4C ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x4D, 0x4D ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x4E, 0x4E ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x4F, 0x4F ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x50, 0x50 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x51, 0x51 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x52, 0x52 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x53, 0x53 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x54, 0x54 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x55, 0x55 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x56, 0x56 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x57, 0x57 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x58, 0x58 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x59, 0x59 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x5A, 0x5A ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x5B, 0x5B ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x5C, 0x5C ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x5D, 0x5D ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x5E, 0x5E ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x5F, 0x5F ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x60, 0x60 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x61, 0x61 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x62, 0x62 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x63, 0x63 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x64, 0x64 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x65, 0x65 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x66, 0x66 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x67, 0x67 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x68, 0x68 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x69, 0x69 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x6A, 0x6A ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x6B, 0x6B ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x6C, 0x6C ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x6D, 0x6D ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x6E, 0x6E ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x6F, 0x6F ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x70, 0x70 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x71, 0x71 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x72, 0x72 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x73, 0x73 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x74, 0x74 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x75, 0x75 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x76, 0x76 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x77, 0x77 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x78, 0x78 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x79, 0x79 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x7A, 0x7A ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x7B, 0x7B ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x7C, 0x7C ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x7D, 0x7D ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x7E, 0x7E ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x7F, 0x7F ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x80, 0x80 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x81, 0x81 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x82, 0x82 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x83, 0x83 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x84, 0x84 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x85, 0x85 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x86, 0x86 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x87, 0x87 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x88, 0x88 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x89, 0x89 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x8A, 0x8A ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x8B, 0x8B ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x8C, 0x8C ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x8D, 0x8D ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x8E, 0x8E ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x8F, 0x8F ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x90, 0x90 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x91, 0x91 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x92, 0x92 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x93, 0x93 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x94, 0x94 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x95, 0x95 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x96, 0x96 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x97, 0x97 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x98, 0x98 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x99, 0x99 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x9A, 0x9A ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x9B, 0x9B ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x9C, 0x9C ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x9D, 0x9D ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x9E, 0x9E ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0x9F, 0x9F ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xA0, 0xA0 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xA1, 0xA1 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xA2, 0xA2 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xA3, 0xA3 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xA4, 0xA4 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xA5, 0xA5 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xA6, 0xA6 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xA7, 0xA7 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xA8, 0xA8 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xA9, 0xA9 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xAA, 0xAA ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xAB, 0xAB ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xAC, 0xAC ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xAD, 0xAD ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xAE, 0xAE ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xAF, 0xAF ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xB0, 0xB0 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xB1, 0xB1 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xB2, 0xB2 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xB3, 0xB3 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xB4, 0xB4 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xB5, 0xB5 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xB6, 0xB6 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xB7, 0xB7 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xB8, 0xB8 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xB9, 0xB9 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xBA, 0xBA ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xBB, 0xBB ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xBC, 0xBC ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xBD, 0xBD ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xBE, 0xBE ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xBF, 0xBF ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xC0, 0xC0 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xC1, 0xC1 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xC2, 0xC2 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xC3, 0xC3 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xC4, 0xC4 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xC5, 0xC5 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xC6, 0xC6 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xC7, 0xC7 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xC8, 0xC8 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xC9, 0xC9 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xCA, 0xCA ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xCB, 0xCB ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xCC, 0xCC ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xCD, 0xCD ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xCE, 0xCE ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xCF, 0xCF ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xD0, 0xD0 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xD1, 0xD1 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xD2, 0xD2 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xD3, 0xD3 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xD4, 0xD4 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xD5, 0xD5 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xD6, 0xD6 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xD7, 0xD7 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xD8, 0xD8 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xD9, 0xD9 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xDA, 0xDA ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xDB, 0xDB ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xDC, 0xDC ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xDD, 0xDD ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xDE, 0xDE ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xDF, 0xDF ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xE0, 0xE0 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xE1, 0xE1 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xE2, 0xE2 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xE3, 0xE3 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xE4, 0xE4 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xE5, 0xE5 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xE6, 0xE6 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xE7, 0xE7 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xE8, 0xE8 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xE9, 0xE9 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xEA, 0xEA ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xEB, 0xEB ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xEC, 0xEC ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xED, 0xED ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xEE, 0xEE ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xEF, 0xEF ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xF0, 0xF0 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xF1, 0xF1 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xF2, 0xF2 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xF3, 0xF3 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xF4, 0xF4 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xF5, 0xF5 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xF6, 0xF6 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xF7, 0xF7 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xF8, 0xF8 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xF9, 0xF9 ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xFA, 0xFA ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xFB, 0xFB ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xFC, 0xFC ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xFD, 0xFD ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xFE, 0xFE ) ); + TESTCASE( j6libc_fprintf( fh, "%x %x %x\n", 0x00, 0xFF, 0xFF) ); + fclose( fh ); + TESTCASE( _PDCLIB_load_lc_ctype( "./", "test" ) != NULL ); + remove( "test_ctype.dat" ); + /* + TESTCASE( isdigit( 0x00 ) && ! isxdigit( 0x00 ) && ! isalpha( 0x00 ) ); + TESTCASE( ! isdigit( 0x11 ) && isxdigit( 0x11 ) && isalpha( 0x11 ) && isupper( 0x11 ) && ! islower( 0x11 ) ); + TESTCASE( ! isdigit( 0x21 ) && isxdigit( 0x21 ) && isalpha( 0x21 ) && ! isupper( 0x11 ) && islower( 0x11 ) ); + */ +} +END_TEST + +START_TEST( load_lc_messages ) +{ + FILE * fh = fopen( "test_numeric.dat", "wb" ); + struct _PDCLIB_lc_lconv_numeric_t * lc; + + TESTCASE_REQUIRE( fh != NULL ); + + TESTCASE( fputs( ",\n.\n\n", fh ) != EOF ); + fclose( fh ); + TESTCASE( ( lc = _PDCLIB_load_lc_numeric( "./", "test" ) ) ); + remove( "test_numeric.dat" ); + TESTCASE( strcmp( lc->decimal_point, "," ) == 0 ); + TESTCASE( strcmp( lc->thousands_sep, "." ) == 0 ); + TESTCASE( strcmp( lc->grouping, "" ) == 0 ); +} +END_TEST + +START_TEST( load_lc_monetary ) +{ + FILE * fh = fopen( "test_monetary.dat", "wb" ); + struct _PDCLIB_lc_lconv_monetary_t * lc; + TESTCASE_REQUIRE( fh != NULL ); + + j6libc_fprintf( fh, "%s\n", "," ); /* mon_decimal_point */ + j6libc_fprintf( fh, "%s\n", "." ); /* mon_thousands_sep */ + j6libc_fprintf( fh, "%s\n", "3" ); /* mon_grouping */ + j6libc_fprintf( fh, "%s\n", "" ); /* positive_sign */ + j6libc_fprintf( fh, "%s\n", "-" ); /* negative_sign */ + j6libc_fprintf( fh, "%s\n", "\xa4" ); /* currency_symbol */ + j6libc_fprintf( fh, "%s\n", "EUR" ); /* int_curr_symbol */ + fputc( 2, fh ); /* frac_digits */ + fputc( 0, fh ); /* p_cs_precedes */ + fputc( 0, fh ); /* n_cs_precedes */ + fputc( 1, fh ); /* p_sep_by_space */ + fputc( 1, fh ); /* n_sep_by_space */ + fputc( 1, fh ); /* p_sign_posn */ + fputc( 1, fh ); /* n_sign_posn */ + fputc( 2, fh ); /* int_frac_digits */ + fputc( 0, fh ); /* int_p_cs_precedes */ + fputc( 0, fh ); /* int_n_cs_precedes */ + fputc( 1, fh ); /* int_p_sep_by_space */ + fputc( 1, fh ); /* int_n_sep_by_space */ + fputc( 1, fh ); /* int_p_sign_posn */ + fputc( 1, fh ); /* int_n_sign_posn */ + j6libc_fprintf( fh, "\n" ); + fclose( fh ); + TESTCASE( ( lc = _PDCLIB_load_lc_monetary( "./", "test" ) ) ); + remove( "test_monetary.dat" ); + TESTCASE( strcmp( lc->mon_decimal_point, "," ) == 0 ); + TESTCASE( strcmp( lc->mon_thousands_sep, "." ) == 0 ); + TESTCASE( strcmp( lc->mon_grouping, "3" ) == 0 ); + TESTCASE( strcmp( lc->positive_sign, "" ) == 0 ); + TESTCASE( strcmp( lc->negative_sign, "-" ) == 0 ); + TESTCASE( strcmp( lc->currency_symbol, "\xa4" ) == 0 ); + TESTCASE( strcmp( lc->int_curr_symbol, "EUR" ) == 0 ); + + TESTCASE( lc->frac_digits == 2 ); + TESTCASE( lc->p_cs_precedes == 0 ); + TESTCASE( lc->n_cs_precedes == 0 ); + TESTCASE( lc->p_sep_by_space == 1 ); + TESTCASE( lc->n_sep_by_space == 1 ); + TESTCASE( lc->p_sign_posn == 1 ); + TESTCASE( lc->n_sign_posn == 1 ); + TESTCASE( lc->int_frac_digits == 2 ); + TESTCASE( lc->int_p_cs_precedes == 0 ); + TESTCASE( lc->int_n_cs_precedes == 0 ); + TESTCASE( lc->int_p_sep_by_space == 1 ); + TESTCASE( lc->int_n_sep_by_space == 1 ); + TESTCASE( lc->int_p_sign_posn == 1 ); + TESTCASE( lc->int_n_sign_posn == 1 ); +} +END_TEST + +START_TEST( load_lc_numeric ) +{ + FILE * fh = fopen( "test_numeric.dat", "wb" ); + struct _PDCLIB_lc_lconv_numeric_t * lc; + TESTCASE_REQUIRE( fh != NULL ); + TESTCASE( fputs( ",\n.\n\n", fh ) != EOF ); + fclose( fh ); + TESTCASE( ( lc = _PDCLIB_load_lc_numeric( "./", "test" ) ) ); + remove( "test_numeric.dat" ); + TESTCASE( strcmp( lc->decimal_point, "," ) == 0 ); + TESTCASE( strcmp( lc->thousands_sep, "." ) == 0 ); + TESTCASE( strcmp( lc->grouping, "" ) == 0 ); +} +END_TEST + +START_TEST( load_lc_time ) +{ + FILE * fh = fopen( "test_time.dat", "wb" ); + struct _PDCLIB_lc_time_t * lc; + + TESTCASE_REQUIRE( fh != NULL ); + + /* month name abbreviation */ + TESTCASE( j6libc_fprintf( fh, "%s\n", "Jan" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Feb" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "M\xe4r" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Apr" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Mai" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Jun" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Jul" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Aug" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Sep" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Okt" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Nov" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Dez" ) == 4 ); + /* month name full */ + TESTCASE( j6libc_fprintf( fh, "%s\n", "Januar" ) == 7 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Februar" ) == 8 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "M\xe4rz" ) == 5 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "April" ) == 6 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Mai" ) == 4 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Juni" ) == 5 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Juli" ) == 5 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "August" ) == 7 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "September" ) == 10 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Oktober" ) == 8 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "November" ) == 9 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Dezember" ) == 9 ); + /* day name abbreviation */ + TESTCASE( j6libc_fprintf( fh, "%s\n", "So" ) == 3 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Mo" ) == 3 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Di" ) == 3 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Mi" ) == 3 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Do" ) == 3 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Fr" ) == 3 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Sa" ) == 3 ); + /* day name full */ + TESTCASE( j6libc_fprintf( fh, "%s\n", "Sonntag" ) == 8 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Montag" ) == 7 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Dienstag" ) == 9 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Mittwoch" ) == 9 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Donnerstag" ) == 11 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Freitag" ) == 8 ); + TESTCASE( j6libc_fprintf( fh, "%s\n", "Samstag" ) == 8 ); + + TESTCASE( j6libc_fprintf( fh, "%s\n", "%a %d %b %Y %T %Z" ) == 18 ); /* date time format (%c) */ + TESTCASE( j6libc_fprintf( fh, "%s\n", "%I:%M:%S" ) == 9 ); /* 12-hour time format (%r) */ + TESTCASE( j6libc_fprintf( fh, "%s\n", "%d.%m.%Y" ) == 9 ); /* date format (%x) */ + TESTCASE( j6libc_fprintf( fh, "%s\n", "%H:%M:%S" ) == 9 ); /* time format (%X) */ + + TESTCASE( j6libc_fprintf( fh, "%s\n", "" ) == 1 ); /* AM */ + TESTCASE( j6libc_fprintf( fh, "%s\n", "" ) == 1 ); /* PM */ + fclose( fh ); + TESTCASE( ( lc = _PDCLIB_load_lc_time( "./", "test" ) ) ); + remove( "test_time.dat" ); + + TESTCASE( strcmp( lc->month_name_abbr[ 0 ], "Jan" ) == 0 ); + TESTCASE( strcmp( lc->month_name_abbr[ 11 ], "Dez" ) == 0 ); + TESTCASE( strcmp( lc->month_name_full[ 0 ], "Januar" ) == 0 ); + TESTCASE( strcmp( lc->month_name_full[ 11 ], "Dezember" ) == 0 ); + TESTCASE( strcmp( lc->day_name_abbr[ 0 ], "So" ) == 0 ); + TESTCASE( strcmp( lc->day_name_abbr[ 6 ], "Sa" ) == 0 ); + TESTCASE( strcmp( lc->day_name_full[ 0 ], "Sonntag" ) == 0 ); + TESTCASE( strcmp( lc->day_name_full[ 6 ], "Samstag" ) == 0 ); +} +END_TEST + +START_TEST( load_lines ) +{ + FILE * fh = fopen( "test_lines.txt", "w+" ); + char * rc; + + TESTCASE_REQUIRE( fh != NULL ); + TESTCASE( fputs( "Foo\n\nBar\n", fh ) != EOF ); + + rewind( fh ); + rc = _PDCLIB_load_lines( fh, 3 ); + fclose( fh ); + remove( "test_lines.txt" ); + + TESTCASE( rc != NULL ); + TESTCASE( strcmp( rc, "Foo" ) == 0 ); + TESTCASE( strcmp( rc + 4, "" ) == 0 ); + TESTCASE( strcmp( rc + 5, "Bar" ) == 0 ); +} +END_TEST + +static int testprintf( + const char *implfile, + const char *file, + int line, + size_t expected_rc, + const char *expected_out, + const char *format, + ... ) +{ + char buffer[100]; + + /* Members: base, flags, n, i, current, s, width, prec, stream, arg */ + struct _PDCLIB_status_t status; + status.base = 0; + status.flags = 0; + status.n = 100; + status.i = 0; + status.current = 0; + status.s = buffer; + status.width = 0; + status.prec = EOF; + status.stream = NULL; + + va_start( status.arg, format ); + memset( buffer, '\0', 100 ); + if ( *(_PDCLIB_print( format, &status )) != '\0' ) + { + fprintf( stderr, "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format ); + return 1; + } + va_end( status.arg ); + + if (status.i != expected_rc || + strcmp(buffer, expected_out)) + { + fprintf( stderr, + "FAILED: %s (%s), line %d\n" + " format string \"%s\"\n" + " expected %2lu, \"%s\"\n" + " actual %2lu, \"%s\"\n", + file, implfile, line, format, expected_rc, + expected_out, status.i, buffer ); + return 1; + } + + return 0; +} + +START_TEST( print ) +{ +#define IMPLFILE "_PDCLIB/print.c" +#define DO_TESTPRINTF testprintf +#define TEST_CONVERSION_ONLY +#include "printf_testcases.h" +#undef TEST_CONVERSION_ONLY +#undef DO_TESTPRINTF +#undef IMPLFILE +} +END_TEST + +static int testscanf( + const char *implfile, + const char *file, + int line, + size_t expected_rc, + const char *input_string, + const char *format, + ... ) +{ + char buffer[100]; + memcpy( buffer, input_string, strlen(input_string)+1 ); + + struct _PDCLIB_status_t status; + status.n = 0; + status.i = 0; + status.s = (char *)buffer; + status.stream = NULL; + + va_start( status.arg, format ); + if ( *(_PDCLIB_scan( format, &status )) != '\0' ) + { + fprintf( stderr, "_PDCLIB_scan() did not return end-of-specifier on '%s'.\n", format ); + return 1; + } + va_end( status.arg ); + + if ( status.n != expected_rc ) + { + fprintf( stderr, + "FAILED: %s (%s), line %d\n" + " expected %2lu,\n" + " actual %2lu\n", + file, implfile, line, expected_rc, status.n ); + return 1; + } + + return 0; +} + +START_TEST( scan ) +{ +#define IMPLFILE "_PDCLIB/scan.c" +#define DO_TESTSCANF testscanf +#define TEST_CONVERSION_ONLY +#include "scanf_testcases.h" +#undef TEST_CONVERSION_ONLY +#undef DO_TESTSCANF +#undef IMPLFILE +} +END_TEST + +START_TEST( strtox_main ) +{ + const char * p; + char test[] = "123_"; + char fail[] = "xxx"; + char sign = '-'; + errno = 0; + p = test; + + /* basic functionality */ + TESTCASE( + (_PDCLIB_strtox_main(&p, 10u, (uintmax_t)999, (uintmax_t)12, 3, &sign) == 123) && + (errno == 0) && + (p == &test[3]) ); + + /* proper functioning to smaller base */ + TESTCASE( + (_PDCLIB_strtox_main(&p, 8u, (uintmax_t)999, (uintmax_t)12, 3, &sign) == 0123) && + (errno == 0) && + (p == &test[3]) ); + + /* overflowing subject sequence must still return proper endptr */ + TESTCASE( + (_PDCLIB_strtox_main(&p, 4u, (uintmax_t)999, (uintmax_t)1, 2, &sign) == 999) && + (errno == ERANGE) && + (p == &test[3]) && + (sign == '+') ); + + /* testing conversion failure */ + p = fail; + sign = '-'; + TESTCASE( + (_PDCLIB_strtox_main(&p, 10u, (uintmax_t)999, (uintmax_t)99, 8, &sign) == 0) && + (p == NULL) ); +} +END_TEST + +START_TEST( strtox_prelim ) +{ + int base = 0; + char sign = '\0'; + char test1[] = " 123"; + char test2[] = "\t+0123"; + char test3[] = "\v-0x123"; + + TESTCASE( + (_PDCLIB_strtox_prelim(test1, &sign, &base) == &test1[2]) && + (sign == '+') && + (base == 10) ); + + TESTCASE( + (_PDCLIB_strtox_prelim(test2, &sign, &base) == &test2[3]) && + (sign == '+') && + (base == 8) ); + + TESTCASE( + (_PDCLIB_strtox_prelim(test3, &sign, &base) == &test3[4]) && + (sign == '-') && + (base == 16) ); + + base = 10; + TESTCASE( + (_PDCLIB_strtox_prelim(test3, &sign, &base) == &test3[2]) && + (sign == '-') && + (base == 10) ); + + base = 1; + TESTCASE( _PDCLIB_strtox_prelim(test3, &sign, &base) == NULL ); + + base = 37; + TESTCASE( _PDCLIB_strtox_prelim(test3, &sign, &base) == NULL ); +} +END_TEST + + + +START_SUITE( internal ) +{ + RUN_TEST( atomax ); + RUN_TEST( digits ); + RUN_TEST( filemode ); + RUN_TEST( is_leap ); + RUN_TEST( print ); + RUN_TEST( scan ); + RUN_TEST( strtox_main ); + RUN_TEST( strtox_prelim ); + + + // These fail due to lack of open() + /* + RUN_TEST( load_lc_ctype ); + RUN_TEST( load_lc_messages ); + RUN_TEST( load_lc_monetary ); + RUN_TEST( load_lc_numeric ); + RUN_TEST( load_lc_time ); + RUN_TEST( load_lines ); + */ +} +END_SUITE diff --git a/src/libraries/libc/tests/test_inttypes.c b/src/libraries/libc/tests/test_inttypes.c new file mode 100644 index 0000000..28e1798 --- /dev/null +++ b/src/libraries/libc/tests/test_inttypes.c @@ -0,0 +1,194 @@ +#include "_PDCLIB_test.h" + +START_TEST( imaxabs ) +{ + TESTCASE( imaxabs( (intmax_t)0 ) == 0 ); + TESTCASE( imaxabs( INTMAX_MAX ) == INTMAX_MAX ); + TESTCASE( imaxabs( INTMAX_MIN + 1 ) == -( INTMAX_MIN + 1 ) ); +} +END_TEST + +START_TEST( imaxdiv ) +{ + imaxdiv_t result; + result = imaxdiv( (intmax_t)5, (intmax_t)2 ); + TESTCASE( result.quot == 2 && result.rem == 1 ); + result = imaxdiv( (intmax_t)-5, (intmax_t)2 ); + TESTCASE( result.quot == -2 && result.rem == -1 ); + result = imaxdiv( (intmax_t)5, (intmax_t)-2 ); + TESTCASE( result.quot == -2 && result.rem == 1 ); + TESTCASE( sizeof( result.quot ) == sizeof( intmax_t ) ); + TESTCASE( sizeof( result.rem ) == sizeof( intmax_t ) ); +} +END_TEST + +START_TEST( strtoimax ) +{ + char * endptr; + /* this, to base 36, overflows even a 256 bit integer */ + char overflow[] = "-ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ_"; + /* tricky border case */ + char tricky[] = "+0xz"; + errno = 0; + + /* basic functionality */ + TESTCASE( strtoimax( "123", NULL, 10 ) == 123 ); + /* proper detecting of default base 10 */ + TESTCASE( strtoimax( "456", NULL, 0 ) == 456 ); + /* proper functioning to smaller base */ + TESTCASE( strtoimax( "14", NULL, 8 ) == 12 ); + /* proper autodetecting of octal */ + TESTCASE( strtoimax( "016", NULL, 0 ) == 14 ); + /* proper autodetecting of hexadecimal, lowercase 'x' */ + TESTCASE( strtoimax( "0xFF", NULL, 0 ) == 255 ); + /* proper autodetecting of hexadecimal, uppercase 'X' */ + TESTCASE( strtoimax( "0Xa1", NULL, 0 ) == 161 ); + + /* proper handling of border case: 0x followed by non-hexdigit */ + TESTCASE( + (strtoimax(tricky, &endptr, 0) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* proper handling of border case: 0 followed by non-octdigit */ + TESTCASE( + (strtoimax(tricky, &endptr, 8) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* overflowing subject sequence must still return proper endptr */ + TESTCASE( + (strtoimax(overflow, &endptr, 36) == INTMAX_MIN) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* same for positive */ + TESTCASE( + (strtoimax(overflow + 1, &endptr, 36) == INTMAX_MAX) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* testing skipping of leading whitespace */ + TESTCASE( strtoimax( " \n\v\t\f789", NULL, 0 ) == 789 ); + + /* testing conversion failure */ + TESTCASE( (strtoimax(overflow, &endptr, 10) == 0) && (endptr == overflow) ); + TESTCASE( (strtoimax(overflow, &endptr, 0) == 0) && (endptr == overflow) ); + /* These tests assume two-complement, but conversion should work for */ + /* one-complement and signed magnitude just as well. Anyone having a */ + /* platform to test this on? */ +#if INTMAX_MAX >> 62 == 1 + /* testing "odd" overflow, i.e. base is not a power of two */ + TESTCASE( (strtoimax("9223372036854775807", NULL, 0) == INTMAX_MAX) && (errno == 0) ); + TESTCASE( (strtoimax("9223372036854775808", NULL, 0) == INTMAX_MAX) && (errno == ERANGE) ); + TESTCASE( (strtoimax("-9223372036854775807", NULL, 0) == (INTMAX_MIN + 1)) && (errno == 0) ); + TESTCASE( (strtoimax("-9223372036854775808", NULL, 0) == INTMAX_MIN) && (errno == 0) ); + TESTCASE( (strtoimax("-9223372036854775809", NULL, 0) == INTMAX_MIN) && (errno == ERANGE) ); + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtoimax("0x7fffffffffffffff", NULL, 0) == INTMAX_MAX) && (errno == 0) ); + TESTCASE( (strtoimax("0x8000000000000000", NULL, 0 ) == INTMAX_MAX) && (errno == ERANGE) ); + TESTCASE( (strtoimax("-0x7fffffffffffffff", NULL, 0 ) == (INTMAX_MIN + 1)) && (errno == 0) ); + TESTCASE( (strtoimax("-0x8000000000000000", NULL, 0 ) == INTMAX_MIN) && (errno == 0) ); + TESTCASE( (strtoimax("-0x8000000000000001", NULL, 0 ) == INTMAX_MIN) && (errno == ERANGE) ); +#elif LLONG_MAX >> 126 == 1 + /* testing "odd" overflow, i.e. base is not a power of two */ + TESTCASE( (strtoimax("170141183460469231731687303715884105728", NULL, 0 ) == INTMAX_MAX) && (errno == 0) ); + TESTCASE( (strtoimax("170141183460469231731687303715884105729", NULL, 0 ) == INTMAX_MAX) && (errno == ERANGE) ); + TESTCASE( (strtoimax("-170141183460469231731687303715884105728", NULL, 0 ) == (INTMAX_MIN + 1)) && (errno == 0) ); + TESTCASE( (strtoimax("-170141183460469231731687303715884105729", NULL, 0 ) == INTMAX_MIN) && (errno == 0) ); + TESTCASE( (strtoimax("-170141183460469231731687303715884105730", NULL, 0 ) == INTMAX_MIN) && (errno == ERANGE) ); + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtoimax("0x7fffffffffffffffffffffffffffffff", NULL, 0 ) == INTMAX_MAX) && (errno == 0) ); + TESTCASE( (strtoimax("0x80000000000000000000000000000000", NULL, 0 ) == INTMAX_MAX) && (errno == ERANGE) ); + TESTCASE( (strtoimax("-0x7fffffffffffffffffffffffffffffff", NULL, 0 ) == (INTMAX_MIN + 1)) && (errno == 0) ); + TESTCASE( (strtoimax("-0x80000000000000000000000000000000", NULL, 0 ) == INTMAX_MIN) && (errno == 0) ); + TESTCASE( (strtoimax("-0x80000000000000000000000000000001", NULL, 0 ) == INTMAX_MIN) && (errno == ERANGE) ); +#else +#error Unsupported width of 'intmax_t' (neither 64 nor 128 bit). +#endif +} +END_TEST + +START_TEST( strtoumax ) +{ + char * endptr; + /* this, to base 36, overflows even a 256 bit integer */ + char overflow[] = "-ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ_"; + /* tricky border case */ + char tricky[] = "+0xz"; + errno = 0; + + /* basic functionality */ + TESTCASE( strtoumax( "123", NULL, 10 ) == 123 ); + /* proper detecting of default base 10 */ + TESTCASE( strtoumax( "456", NULL, 0 ) == 456 ); + /* proper functioning to smaller base */ + TESTCASE( strtoumax( "14", NULL, 8 ) == 12 ); + /* proper autodetecting of octal */ + TESTCASE( strtoumax( "016", NULL, 0 ) == 14 ); + /* proper autodetecting of hexadecimal, lowercase 'x' */ + TESTCASE( strtoumax( "0xFF", NULL, 0 ) == 255 ); + /* proper autodetecting of hexadecimal, uppercase 'X' */ + TESTCASE( strtoumax( "0Xa1", NULL, 0 ) == 161 ); + + /* proper handling of border case: 0x followed by non-hexdigit */ + TESTCASE( + (strtoumax(tricky, &endptr, 0) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* proper handling of border case: 0 followed by non-octdigit */ + TESTCASE( + (strtoumax(tricky, &endptr, 8) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* overflowing subject sequence must still return proper endptr */ + TESTCASE( + (strtoumax(overflow, &endptr, 36) == UINTMAX_MAX) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* same for positive */ + TESTCASE( + (strtoumax(overflow + 1, &endptr, 36) == UINTMAX_MAX) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* testing skipping of leading whitespace */ + TESTCASE( strtoumax( " \n\v\t\f789", NULL, 0 ) == 789 ); + + /* testing conversion failure */ + TESTCASE( (strtoumax(overflow, &endptr, 10) == 0) && (endptr == overflow) ); + TESTCASE( (strtoumax(overflow, &endptr, 0) == 0) && (endptr == overflow) ); +/* uintmax_t -> long long -> 64 bit */ +#if UINTMAX_MAX >> 63 == 1 + /* testing "odd" overflow, i.e. base is not power of two */ + TESTCASE( (strtoumax("18446744073709551615", NULL, 0) == UINTMAX_MAX) && (errno == 0) ); + TESTCASE( (strtoumax("18446744073709551616", NULL, 0) == UINTMAX_MAX) && (errno == ERANGE) ); + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtoumax("0xFFFFFFFFFFFFFFFF", NULL, 0) == UINTMAX_MAX) && (errno == 0) ); + TESTCASE( (strtoumax("0x10000000000000000", NULL, 0) == UINTMAX_MAX) && (errno == ERANGE) ); +/* uintmax_t -> long long -> 128 bit */ +#elif UINTMAX_MAX >> 127 == 1 + /* testing "odd" overflow, i.e. base is not power of two */ + TESTCASE( (strtoumax("340282366920938463463374607431768211455", NULL, 0) == UINTMAX_MAX) && (errno == 0) ); + TESTCASE( (strtoumax("340282366920938463463374607431768211456", NULL, 0) == UINTMAX_MAX) && (errno == ERANGE) ); + /* testing "even" everflow, i.e. base is power of two */ + TESTCASE( (strtoumax("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NULL, 0) == UINTMAX_MAX) && (errno == 0) ); + TESTCASE( (strtoumax("0x100000000000000000000000000000000", NULL, 0) == UINTMAX_MAX) && (errno == ERANGE) ); +#else +#error Unsupported width of 'uintmax_t' (neither 64 nor 128 bit). +#endif +} +END_TEST + + +START_SUITE( inttypes ) +{ + RUN_TEST( imaxabs ); + RUN_TEST( imaxdiv ); + RUN_TEST( strtoimax ); + RUN_TEST( strtoumax ); +} +END_SUITE diff --git a/src/libraries/libc/tests/test_locale.c b/src/libraries/libc/tests/test_locale.c new file mode 100644 index 0000000..f7979b3 --- /dev/null +++ b/src/libraries/libc/tests/test_locale.c @@ -0,0 +1,7 @@ +#include "_PDCLIB_test.h" + + +START_SUITE( locale ) +{ +} +END_SUITE diff --git a/src/libraries/libc/tests/test_signal.c b/src/libraries/libc/tests/test_signal.c new file mode 100644 index 0000000..88902c2 --- /dev/null +++ b/src/libraries/libc/tests/test_signal.c @@ -0,0 +1,49 @@ +#include "_PDCLIB_test.h" +#include + +static volatile sig_atomic_t flag = 0; + +static int expected_signal = 0; +static int received_signal = 0; + +static void test_handler( int sig ) +{ + flag = 1; +} + +START_TEST( raise ) +{ + /* Could be other than SIG_DFL if you changed the implementation. */ + TESTCASE( signal( SIGABRT, SIG_IGN ) == SIG_DFL ); + /* Should be ignored. */ + TESTCASE( raise( SIGABRT ) == 0 ); + /* Installing test handler, old handler should be returned */ + TESTCASE( signal( SIGABRT, test_handler ) == SIG_IGN ); + /* Raising and checking SIGABRT */ + expected_signal = SIGABRT; + TESTCASE( raise( SIGABRT ) == 0 ); + TESTCASE( flag == 1 ); + TESTCASE( received_signal == SIGABRT ); + /* Re-installing test handler, should have been reset to default */ + /* Could be other than SIG_DFL if you changed the implementation. */ + TESTCASE( signal( SIGABRT, test_handler ) == SIG_DFL ); + /* Raising and checking SIGABRT */ + flag = 0; + TESTCASE( raise( SIGABRT ) == 0 ); + TESTCASE( flag == 1 ); + TESTCASE( received_signal == SIGABRT ); + /* Installing test handler for different signal... */ + TESTCASE( signal( SIGTERM, test_handler ) == SIG_DFL ); + /* Raising and checking SIGTERM */ + expected_signal = SIGTERM; + TESTCASE( raise( SIGTERM ) == 0 ); + TESTCASE( flag == 1 ); + TESTCASE( received_signal == SIGTERM ); +} +END_TEST + +START_SUITE( signal ) +{ + RUN_TEST( raise ); +} +END_SUITE diff --git a/src/libraries/libc/tests/test_stdarg.c b/src/libraries/libc/tests/test_stdarg.c new file mode 100644 index 0000000..681af7d --- /dev/null +++ b/src/libraries/libc/tests/test_stdarg.c @@ -0,0 +1,67 @@ +#include +#include +#include + +#include "_PDCLIB_test.h" + +typedef int (*intfunc_t)( void ); + +enum tag_t +{ + TAG_END, + TAG_INT, + TAG_LONG, + TAG_LLONG, + TAG_DBL, + TAG_LDBL, + TAG_INTPTR, + TAG_LDBLPTR, + TAG_FUNCPTR +}; + +static int dummy( void ) +{ + return INT_MAX; +} + +static int test( enum tag_t s, ... ) +{ + enum tag_t tag = s; + va_list ap; + va_start( ap, s ); + for (;;) + { + switch ( tag ) + { + case TAG_INT: if( va_arg( ap, int ) != INT_MAX ) return 0; break; + case TAG_LONG: if( va_arg( ap, long ) != LONG_MAX ) return 0; break; + case TAG_LLONG: if( va_arg( ap, long long ) != LLONG_MAX ) return 0; break; + case TAG_DBL: if( va_arg( ap, double ) != DBL_MAX ) return 0; break; + case TAG_LDBL: if( va_arg( ap, long double ) != LDBL_MAX ) return 0; break; + case TAG_INTPTR: if( *( va_arg( ap, int * ) ) != INT_MAX ) return 0; break; + case TAG_LDBLPTR: if( *( va_arg( ap, long double * ) ) != LDBL_MAX ) return 0; break; + case TAG_FUNCPTR: if( va_arg( ap, intfunc_t ) != dummy ) return 0; break; + case TAG_END: va_end( ap ); return 1; + } + tag = va_arg( ap, enum tag_t ); + } +} + +START_TEST( stdarg ) +{ + int x = INT_MAX; + long double d = LDBL_MAX; + TESTCASE( test(TAG_END) ); + TESTCASE( test(TAG_INT, INT_MAX, TAG_END) ); + TESTCASE( test(TAG_LONG, LONG_MAX, TAG_LLONG, LLONG_MAX, TAG_END) ); + TESTCASE( test(TAG_DBL, DBL_MAX, TAG_LDBL, LDBL_MAX, TAG_END) ); + TESTCASE( test(TAG_INTPTR, &x, TAG_LDBLPTR, &d, TAG_FUNCPTR, dummy, TAG_END) ); +} +END_TEST + + +START_SUITE( stdarg ) +{ + RUN_TEST( stdarg ); +} +END_SUITE diff --git a/src/libraries/libc/tests/test_stdio.c b/src/libraries/libc/tests/test_stdio.c new file mode 100644 index 0000000..efe3388 --- /dev/null +++ b/src/libraries/libc/tests/test_stdio.c @@ -0,0 +1,649 @@ +#include "_PDCLIB_test.h" +#include "_PDCLIB_iotest.h" +#include + +extern struct _PDCLIB_file_t *_PDCLIB_filelist; + +START_TEST( clearerr ) +{ + FILE * fh = tmpfile(); + + TESTCASE_REQUIRE( fh != NULL ); + + /* Flags should be clear */ + TESTCASE( ! ferror( fh ) ); + TESTCASE( ! feof( fh ) ); + /* Reading from empty stream - should provoke EOF */ + rewind( fh ); + TESTCASE( fgetc( fh ) == EOF ); + TESTCASE( ! ferror( fh ) ); + TESTCASE( feof( fh ) ); + /* clearerr() should clear flags */ + clearerr( fh ); + TESTCASE( ! ferror( fh ) ); + TESTCASE( ! feof( fh ) ); + /* reopen() the file write-only */ + TESTCASE( ( fh = freopen( NULL, "w", fh ) ) != NULL ); + /* Reading from write-only stream - should provoke error */ + TESTCASE( fgetc( fh ) == EOF ); + TESTCASE( ferror( fh ) ); + TESTCASE( ! feof( fh ) ); + /* clearerr() should clear flags */ + clearerr( fh ); + TESTCASE( ! ferror( fh ) ); + TESTCASE( ! feof( fh ) ); + TESTCASE( fclose( fh ) == 0 ); +} +END_TEST + +START_TEST( fclose ) +{ + struct _PDCLIB_file_t * file1 = NULL; + struct _PDCLIB_file_t * file2 = NULL; + remove( testfile1 ); + remove( testfile2 ); + TESTCASE( _PDCLIB_filelist == stdin ); + TESTCASE( ( file1 = fopen( testfile1, "w" ) ) != NULL ); + TESTCASE( _PDCLIB_filelist == file1 ); + TESTCASE( ( file2 = fopen( testfile2, "w" ) ) != NULL ); + TESTCASE( _PDCLIB_filelist == file2 ); + TESTCASE( fclose( file2 ) == 0 ); + TESTCASE( _PDCLIB_filelist == file1 ); + TESTCASE( ( file2 = fopen( testfile2, "w" ) ) != NULL ); + TESTCASE( _PDCLIB_filelist == file2 ); + TESTCASE( fclose( file1 ) == 0 ); + TESTCASE( _PDCLIB_filelist == file2 ); + TESTCASE( fclose( file2 ) == 0 ); + TESTCASE( _PDCLIB_filelist == stdin ); + TESTCASE( remove( testfile1 ) == 0 ); + TESTCASE( remove( testfile2 ) == 0 ); +} +END_TEST + +START_TEST( fgetpos ) +{ + FILE * fh = tmpfile(); + fpos_t pos1, pos2; + TESTCASE_REQUIRE( fh != NULL ); + TESTCASE( fgetpos( fh, &pos1 ) == 0 ); + TESTCASE( fwrite( teststring, 1, strlen( teststring ), fh ) == strlen( teststring ) ); + TESTCASE( fgetpos( fh, &pos2 ) == 0 ); + TESTCASE( fsetpos( fh, &pos1 ) == 0 ); + TESTCASE( ftell( fh ) == 0 ); + TESTCASE( fsetpos( fh, &pos2 ) == 0 ); + TESTCASE( (size_t)ftell( fh ) == strlen( teststring ) ); + TESTCASE( fclose( fh ) == 0 ); +} +END_TEST + +START_TEST( fgets ) +{ + FILE * fh = NULL; + char buffer[10]; + const char * fgets_test = "foo\nbar\0baz\nweenie"; + TESTCASE_REQUIRE( ( fh = fopen( testfile, "wb+" ) ) != NULL ); + TESTCASE( fwrite( fgets_test, 1, 18, fh ) == 18 ); + rewind( fh ); + TESTCASE( fgets( buffer, 10, fh ) == buffer ); + TESTCASE( strcmp( buffer, "foo\n" ) == 0 ); + TESTCASE( fgets( buffer, 10, fh ) == buffer ); + TESTCASE( memcmp( buffer, "bar\0baz\n", 8 ) == 0 ); + TESTCASE( fgets( buffer, 10, fh ) == buffer ); + TESTCASE( strcmp( buffer, "weenie" ) == 0 ); + TESTCASE( feof( fh ) ); + TESTCASE( fseek( fh, -1, SEEK_END ) == 0 ); + TESTCASE( fgets( buffer, 1, fh ) == buffer ); + TESTCASE( strcmp( buffer, "" ) == 0 ); + TESTCASE( fgets( buffer, 0, fh ) == NULL ); + TESTCASE( ! feof( fh ) ); + TESTCASE( fgets( buffer, 1, fh ) == buffer ); + TESTCASE( strcmp( buffer, "" ) == 0 ); + TESTCASE( ! feof( fh ) ); + TESTCASE( fgets( buffer, 2, fh ) == buffer ); + TESTCASE( strcmp( buffer, "e" ) == 0 ); + TESTCASE( fseek( fh, 0, SEEK_END ) == 0 ); + TESTCASE( fgets( buffer, 2, fh ) == NULL ); + TESTCASE( feof( fh ) ); + TESTCASE( fclose( fh ) == 0 ); + TESTCASE( remove( testfile ) == 0 ); +} +END_TEST + +START_TEST( fopen ) +{ + /* Some of the tests are not executed for regression tests, as the libc on + my system is at once less forgiving (segfaults on mode NULL) and more + forgiving (accepts undefined modes). + */ + FILE * fh = NULL; + remove( testfile ); + TESTCASE( fopen( NULL, NULL ) == NULL ); + TESTCASE( fopen( NULL, "w" ) == NULL ); + TESTCASE( fopen( "", NULL ) == NULL ); + TESTCASE( fopen( "", "w" ) == NULL ); + TESTCASE( fopen( "foo", "" ) == NULL ); + TESTCASE( fopen( testfile, "wq" ) == NULL ); /* Undefined mode */ + TESTCASE( fopen( testfile, "wr" ) == NULL ); /* Undefined mode */ + TESTCASE( ( fh = fopen( testfile, "w" ) ) != NULL ); + TESTCASE( fclose( fh ) == 0 ); + TESTCASE( remove( testfile ) == 0 ); +} +END_TEST + +START_TEST( fputs ) +{ + const char * const message = "SUCCESS testing fputs()"; + FILE * fh = tmpfile(); + size_t i; + TESTCASE_REQUIRE( fh != NULL ); + TESTCASE( fputs( message, fh ) >= 0 ); + rewind( fh ); + for ( i = 0; i < 23; ++i ) + { + TESTCASE( fgetc( fh ) == message[i] ); + } + TESTCASE( fclose( fh ) == 0 ); +} +END_TEST + +START_TEST( fread ) +{ + FILE * fh = NULL; + const char * message = "Testing fwrite()...\n"; + char buffer[21]; + buffer[20] = 'x'; + TESTCASE_REQUIRE( ( fh = tmpfile() ) != NULL ); + /* fwrite() / readback */ + TESTCASE( fwrite( message, 1, 20, fh ) == 20 ); + rewind( fh ); + TESTCASE( fread( buffer, 1, 20, fh ) == 20 ); + TESTCASE( memcmp( buffer, message, 20 ) == 0 ); + TESTCASE( buffer[20] == 'x' ); + /* same, different nmemb / size settings */ + rewind( fh ); + TESTCASE( memset( buffer, '\0', 20 ) == buffer ); + TESTCASE( fwrite( message, 5, 4, fh ) == 4 ); + rewind( fh ); + TESTCASE( fread( buffer, 5, 4, fh ) == 4 ); + TESTCASE( memcmp( buffer, message, 20 ) == 0 ); + TESTCASE( buffer[20] == 'x' ); + /* same... */ + rewind( fh ); + TESTCASE( memset( buffer, '\0', 20 ) == buffer ); + TESTCASE( fwrite( message, 20, 1, fh ) == 1 ); + rewind( fh ); + TESTCASE( fread( buffer, 20, 1, fh ) == 1 ); + TESTCASE( memcmp( buffer, message, 20 ) == 0 ); + TESTCASE( buffer[20] == 'x' ); + /* Done. */ + TESTCASE( fclose( fh ) == 0 ); +} +END_TEST + +START_TEST( freopen ) +{ + FILE * fin = NULL; + FILE * fout = NULL; + TESTCASE_REQUIRE( ( fin = fopen( testfile1, "wb+" ) ) != NULL ); + TESTCASE( fputc( 'x', fin ) == 'x' ); + TESTCASE( fclose( fin ) == 0 ); + TESTCASE( ( fin = freopen( testfile1, "rb", stdin ) ) != NULL ); + TESTCASE( getchar() == 'x' ); + + TESTCASE( ( fout = freopen( testfile2, "wb+", stdout ) ) != NULL ); + TESTCASE( putchar( 'x' ) == 'x' ); + rewind( fout ); + TESTCASE( fgetc( fout ) == 'x' ); + + TESTCASE( fclose( fin ) == 0 ); + TESTCASE( fclose( fout ) == 0 ); + TESTCASE( remove( testfile1 ) == 0 ); + TESTCASE( remove( testfile2 ) == 0 ); +} +END_TEST + +START_TEST( fseek ) +{ + FILE * fh = NULL; + TESTCASE( ( fh = tmpfile() ) != NULL ); + TESTCASE( fwrite( teststring, 1, strlen( teststring ), fh ) == strlen( teststring ) ); + /* General functionality */ + TESTCASE( fseek( fh, -1, SEEK_END ) == 0 ); + TESTCASE( (size_t)ftell( fh ) == strlen( teststring ) - 1 ); + TESTCASE( fseek( fh, 0, SEEK_END ) == 0 ); + TESTCASE( (size_t)ftell( fh ) == strlen( teststring ) ); + TESTCASE( fseek( fh, 0, SEEK_SET ) == 0 ); + TESTCASE( ftell( fh ) == 0 ); + TESTCASE( fseek( fh, 5, SEEK_CUR ) == 0 ); + TESTCASE( ftell( fh ) == 5 ); + TESTCASE( fseek( fh, -3, SEEK_CUR ) == 0 ); + TESTCASE( ftell( fh ) == 2 ); + /* Checking behaviour around EOF */ + TESTCASE( fseek( fh, 0, SEEK_END ) == 0 ); + TESTCASE( ! feof( fh ) ); + TESTCASE( fgetc( fh ) == EOF ); + TESTCASE( feof( fh ) ); + TESTCASE( fseek( fh, 0, SEEK_END ) == 0 ); + TESTCASE( ! feof( fh ) ); + /* Checking undo of ungetc() */ + TESTCASE( fseek( fh, 0, SEEK_SET ) == 0 ); + TESTCASE( fgetc( fh ) == teststring[0] ); + TESTCASE( fgetc( fh ) == teststring[1] ); + TESTCASE( fgetc( fh ) == teststring[2] ); + TESTCASE( ftell( fh ) == 3 ); + TESTCASE( ungetc( teststring[2], fh ) == teststring[2] ); + TESTCASE( ftell( fh ) == 2 ); + TESTCASE( fgetc( fh ) == teststring[2] ); + TESTCASE( ftell( fh ) == 3 ); + TESTCASE( ungetc( 'x', fh ) == 'x' ); + TESTCASE( ftell( fh ) == 2 ); + TESTCASE( fgetc( fh ) == 'x' ); + TESTCASE( ungetc( 'x', fh ) == 'x' ); + TESTCASE( ftell( fh ) == 2 ); + TESTCASE( fseek( fh, 2, SEEK_SET ) == 0 ); + TESTCASE( fgetc( fh ) == teststring[2] ); + /* Checking error handling */ + TESTCASE( fseek( fh, -5, SEEK_SET ) == -1 ); + TESTCASE( fseek( fh, 0, SEEK_END ) == 0 ); + TESTCASE( fclose( fh ) == 0 ); +} +END_TEST + +START_TEST( ftell ) +{ + /* Testing all the basic I/O functions individually would result in lots + of duplicated code, so I took the liberty of lumping it all together + here. + */ + /* The following functions delegate their tests to here: + fgetc fflush rewind fputc ungetc fseek + flushbuffer seek fillbuffer prepread prepwrite + */ + char * buffer = (char*)malloc( 4 ); + FILE * fh = NULL; + TESTCASE_REQUIRE( ( fh = tmpfile() ) != NULL ); + TESTCASE( setvbuf( fh, buffer, _IOLBF, 4 ) == 0 ); + /* Testing ungetc() at offset 0 */ + rewind( fh ); + TESTCASE( ungetc( 'x', fh ) == 'x' ); + TESTCASE( ftell( fh ) == -1l ); + rewind( fh ); + TESTCASE( ftell( fh ) == 0l ); + /* Commence "normal" tests */ + TESTCASE( fputc( '1', fh ) == '1' ); + TESTCASE( fputc( '2', fh ) == '2' ); + TESTCASE( fputc( '3', fh ) == '3' ); + /* Positions incrementing as expected? */ + TESTCASE( ftell( fh ) == 3l ); + TESTCASE( fh->pos.offset == 0l ); + TESTCASE( fh->bufidx == 3l ); + /* Buffer properly flushed when full? */ + TESTCASE( fputc( '4', fh ) == '4' ); + TESTCASE( fh->pos.offset == 4l ); + TESTCASE( fh->bufidx == 0 ); + /* fflush() resetting positions as expected? */ + TESTCASE( fputc( '5', fh ) == '5' ); + TESTCASE( fflush( fh ) == 0 ); + TESTCASE( ftell( fh ) == 5l ); + TESTCASE( fh->pos.offset == 5l ); + TESTCASE( fh->bufidx == 0l ); + /* rewind() resetting positions as expected? */ + rewind( fh ); + TESTCASE( ftell( fh ) == 0l ); + TESTCASE( fh->pos.offset == 0 ); + TESTCASE( fh->bufidx == 0 ); + /* Reading back first character after rewind for basic read check */ + TESTCASE( fgetc( fh ) == '1' ); + /* TODO: t.b.c. */ + TESTCASE( fclose( fh ) == 0 ); +} +END_TEST + +START_TEST( perror ) +{ + FILE * fh = NULL; + unsigned long long max = ULLONG_MAX; + char buffer[100]; + sprintf( buffer, "%llu", max ); + TESTCASE_REQUIRE( ( fh = freopen( testfile, "wb+", j6libc_stderr ) ) != NULL ); + TESTCASE( strtol( buffer, NULL, 10 ) == LONG_MAX ); + perror( "Test" ); + rewind( fh ); + TESTCASE( fread( buffer, 1, 7, fh ) == 7 ); + TESTCASE( memcmp( buffer, "Test: ", 6 ) == 0 ); + TESTCASE( fclose( fh ) == 0 ); + TESTCASE( remove( testfile ) == 0 ); +} +END_TEST + +START_TEST( puts ) +{ + FILE * fh = NULL; + const char * message = "SUCCESS testing puts()"; + char buffer[23]; + buffer[22] = 'x'; + TESTCASE_REQUIRE( ( fh = freopen( testfile, "wb+", stdout ) ) != NULL ); + TESTCASE( puts( message ) >= 0 ); + rewind( fh ); + TESTCASE( fread( buffer, 1, 22, fh ) == 22 ); + TESTCASE( memcmp( buffer, message, 22 ) == 0 ); + TESTCASE( buffer[22] == 'x' ); + TESTCASE( fclose( fh ) == 0 ); + TESTCASE( remove( testfile ) == 0 ); +} +END_TEST + +START_TEST( rename ) +{ + FILE * file = NULL; + remove( testfile1 ); + remove( testfile2 ); + /* make sure that neither file exists */ + TESTCASE( fopen( testfile1, "r" ) == NULL ); + TESTCASE( fopen( testfile2, "r" ) == NULL ); + /* rename file 1 to file 2 - expected to fail */ + TESTCASE( rename( testfile1, testfile2 ) == -1 ); + /* create file 1 */ + TESTCASE_REQUIRE( ( file = fopen( testfile1, "w" ) ) != NULL ); + TESTCASE( fputs( "x", file ) != EOF ); + TESTCASE( fclose( file ) == 0 ); + /* check that file 1 exists */ + TESTCASE( ( file = fopen( testfile1, "r" ) ) != NULL ); + TESTCASE( fclose( file ) == 0 ); + /* rename file 1 to file 2 */ + TESTCASE( rename( testfile1, testfile2 ) == 0 ); + /* check that file 2 exists, file 1 does not */ + TESTCASE( fopen( testfile1, "r" ) == NULL ); + TESTCASE( ( file = fopen( testfile2, "r" ) ) != NULL ); + TESTCASE( fclose( file ) == 0 ); + /* create another file 1 */ + TESTCASE( ( file = fopen( testfile1, "w" ) ) != NULL ); + TESTCASE( fputs( "x", file ) != EOF ); + TESTCASE( fclose( file ) == 0 ); + /* check that file 1 exists */ + TESTCASE( ( file = fopen( testfile1, "r" ) ) != NULL ); + TESTCASE( fclose( file ) == 0 ); + /* rename file 1 to file 2 - expected to fail, see comment in + _PDCLIB_rename() itself. + */ + /* NOREG as glibc overwrites existing destination file. */ + TESTCASE( rename( testfile1, testfile2 ) == -1 ); + /* remove both files */ + TESTCASE( remove( testfile1 ) == 0 ); + TESTCASE( remove( testfile2 ) == 0 ); + /* check that they're gone */ + TESTCASE( fopen( testfile1, "r" ) == NULL ); + TESTCASE( fopen( testfile2, "r" ) == NULL ); +} +END_TEST + +START_TEST( setbuf ) +{ + /* TODO: Extend testing once setvbuf() is finished. */ + char buffer[ BUFSIZ + 1 ]; + FILE * fh = NULL; + /* full buffered */ + TESTCASE_REQUIRE( ( fh = tmpfile() ) != NULL ); + setbuf( fh, buffer ); + TESTCASE( fh->buffer == buffer ); + TESTCASE( fh->bufsize == BUFSIZ ); + TESTCASE( ( fh->status & ( _IOFBF | _IONBF | _IOLBF ) ) == _IOFBF ); + TESTCASE( fclose( fh ) == 0 ); + /* not buffered */ + TESTCASE( ( fh = tmpfile() ) != NULL ); + setbuf( fh, NULL ); + TESTCASE( ( fh->status & ( _IOFBF | _IONBF | _IOLBF ) ) == _IONBF ); + TESTCASE( fclose( fh ) == 0 ); +} +END_TEST + + +START_TEST( setvbuf ) +{ +#define BUFFERSIZE 500 + char buffer[ BUFFERSIZE ]; + FILE * fh = NULL; + /* full buffered, user-supplied buffer */ + TESTCASE_REQUIRE( ( fh = tmpfile() ) != NULL ); + TESTCASE( setvbuf( fh, buffer, _IOFBF, BUFFERSIZE ) == 0 ); + TESTCASE( fh->buffer == buffer ); + TESTCASE( fh->bufsize == BUFFERSIZE ); + TESTCASE( ( fh->status & ( _IOFBF | _IONBF | _IOLBF ) ) == _IOFBF ); + TESTCASE( fclose( fh ) == 0 ); + /* line buffered, lib-supplied buffer */ + TESTCASE( ( fh = tmpfile() ) != NULL ); + TESTCASE( setvbuf( fh, NULL, _IOLBF, BUFFERSIZE ) == 0 ); + TESTCASE( fh->buffer != NULL ); + TESTCASE( fh->bufsize == BUFFERSIZE ); + TESTCASE( ( fh->status & ( _IOFBF | _IONBF | _IOLBF ) ) == _IOLBF ); + TESTCASE( fclose( fh ) == 0 ); + /* not buffered, user-supplied buffer */ + TESTCASE( ( fh = tmpfile() ) != NULL ); + TESTCASE( setvbuf( fh, buffer, _IONBF, BUFFERSIZE ) == 0 ); + TESTCASE( ( fh->status & ( _IOFBF | _IONBF | _IOLBF ) ) == _IONBF ); + TESTCASE( fclose( fh ) == 0 ); +#undef BUFFERSIZE +} +END_TEST + +START_TEST( tmpnam ) +{ + TESTCASE( strlen( tmpnam( NULL ) ) < L_tmpnam ); +} +END_TEST + + +FILE * test_vfprintf_stream = NULL; + +static int test_vfprintf( + const char *implfile, + const char *file, + int line, + size_t expected_rc, + const char *expected_out, + const char *format, + ... ) +{ + size_t i; + va_list arg; + va_start( arg, format ); + i = vfprintf( test_vfprintf_stream, format, arg ); + va_end( arg ); + + char result_buffer[100]; + rewind(test_vfprintf_stream); + if ( fread( result_buffer, 1, i, test_vfprintf_stream ) != i ) + { + fprintf( stderr, + "FAILED: %s (%s), line %d\n" + " GET RESULT FAILURE", + file, implfile, line); + return 1; + } + + if (i != expected_rc || + strcmp(result_buffer, expected_out)) + { + fprintf( stderr, + "FAILED: %s (%s), line %d\n" + " format string \"%s\"\n" + " expected %2lu, \"%s\"\n" + " actual %2lu, \"%s\"\n", + file, implfile, line, format, expected_rc, + expected_out, i, result_buffer ); + return 1; + } + + return 0; +} + +START_TEST( vfprintf ) +{ + TESTCASE_REQUIRE( ( test_vfprintf_stream = tmpfile() ) != NULL ); +#define IMPLFILE "stdio/vfprintf.c" +#define DO_TESTPRINTF test_vfprintf +#include "printf_testcases.h" +#undef DO_TESTPRINTF +#undef IMPLFILE + TESTCASE( fclose( test_vfprintf_stream ) == 0 ); +} +END_TEST + +FILE * test_vfscanf_stream = NULL; + +static int test_vfscanf( + const char *implfile, + const char *file, + int line, + size_t expected_rc, + const char *input_string, + const char *format, + ... ) +{ + rewind( test_vfscanf_stream ); + fwrite( input_string, 1, strlen(input_string)+1, test_vfscanf_stream ); + rewind( test_vfscanf_stream ); + + va_list ap; + size_t result; + va_start( ap, format ); + result = vfscanf( test_vfscanf_stream, format, ap ); + va_end( ap ); + + if (result != expected_rc ) + { + fprintf( stderr, + "FAILED: %s (%s), line %d\n" + " format string \"%s\"\n" + " expected %2lu\n" + " actual %2lu\n", + file, implfile, line, format, + expected_rc, result); + return 1; + } + + return 0; +} + +START_TEST( vfscanf ) +{ + TESTCASE_REQUIRE( ( test_vfscanf_stream = tmpfile() ) != NULL ); +#define IMPLFILE "stdio/vfscanf.c" +#define DO_TESTSCANF test_vfscanf +#include "scanf_testcases.h" +#undef DO_TESTSCANF +#undef IMPLFILE + TESTCASE( fclose( test_vfprintf_stream ) == 0 ); +} +END_TEST + +static int test_vsnprintf( + const char *implfile, + const char *file, + int line, + size_t expected_rc, + const char *expected_out, + const char *format, + ... ) +{ + char buffer[100]; + size_t i; + va_list arg; + va_start( arg, format ); + i = vsnprintf( buffer, 100, format, arg ); + va_end( arg ); + + if (i != expected_rc || + strcmp(buffer, expected_out)) + { + fprintf( stderr, + "FAILED: %s (%s), line %d\n" + " format string \"%s\"\n" + " expected %2lu, \"%s\"\n" + " actual %2lu, \"%s\"\n", + file, implfile, line, format, expected_rc, + expected_out, i, buffer ); + return 1; + } + + return 0; +} + +START_TEST( vsnprintf ) +{ +#define IMPLFILE "stdio/vsnprintf.c" +#define DO_TESTPRINTF test_vsnprintf +#include "printf_testcases.h" +#undef DO_TESTPRINTF +#undef IMPLFILE +} +END_TEST + +static int test_vsscanf( + const char *implfile, + const char *file, + int line, + size_t expected_rc, + const char *input_string, + const char *format, + ... ) +{ + va_list ap; + size_t result; + va_start( ap, format ); + result = vsscanf( input_string, format, ap ); + va_end( ap ); + + if (result != expected_rc ) + { + fprintf( stderr, + "FAILED: %s (%s), line %d\n" + " format string \"%s\"\n" + " expected %2lu\n" + " actual %2lu\n", + file, implfile, line, format, + expected_rc, result); + return 1; + } + + return 0; +} + +START_TEST( vsscanf ) +{ +#define IMPLFILE "stdio/vsscanf.c" +#define DO_TESTSCANF test_vsscanf +#include "scanf_testcases.h" +#undef DO_TESTSCANF +#undef IMPLFILE +} +END_TEST + + +START_SUITE( stdio ) +{ + /* TODO: File IO not yet implemented + RUN_TEST( clearerr ); + RUN_TEST( fclose ); + RUN_TEST( fgetpos ); + RUN_TEST( fgets ); + RUN_TEST( fopen ); + RUN_TEST( fputs ); + RUN_TEST( fread ); + RUN_TEST( freopen ); + RUN_TEST( fseek ); + RUN_TEST( ftell ); + RUN_TEST( perror ); + RUN_TEST( puts ); + RUN_TEST( rename ); + RUN_TEST( setbuf ); + RUN_TEST( setvbuf ); + RUN_TEST( vfprintf ); + RUN_TEST( vfscanf ); + */ + + RUN_TEST( tmpnam ); + RUN_TEST( vsnprintf ); + RUN_TEST( vsscanf ); +} +END_SUITE diff --git a/src/libraries/libc/tests/test_stdlib.c b/src/libraries/libc/tests/test_stdlib.c new file mode 100644 index 0000000..87e3616 --- /dev/null +++ b/src/libraries/libc/tests/test_stdlib.c @@ -0,0 +1,515 @@ +#include "_PDCLIB_test.h" +#include + + +static int test_exit() { _Exit(0); return 0; } + +START_TEST( _Exit ) +{ + TESTCASE( test_exit() ); +} +END_TEST + +static void aborthandler(int s) { _exit(0); } +static int test_abort() { abort(); return 0; } + +START_TEST( abort ) +{ + TESTCASE( signal( SIGABRT, &aborthandler ) != SIG_ERR ); + TESTCASE( test_abort() ); +} +END_TEST + +START_TEST( abs ) +{ + TESTCASE( abs( 0 ) == 0 ); + TESTCASE( abs( INT_MAX ) == INT_MAX ); + TESTCASE( abs( INT_MIN + 1 ) == -( INT_MIN + 1 ) ); +} +END_TEST + +static int flags[ 32 ]; + +static void counthandler( void ) +{ + static int count = 0; + flags[ count ] = count; + ++count; +} + +static void checkhandler( void ) +{ + for ( int i = 0; i < 31; ++i ) + assert( flags[ i ] == i ); +} + +int test_atexit() +{ + if( atexit( &checkhandler ) != 0 ) + return 0; + + for ( int i = 0; i < 31; ++i ) + if( atexit( &counthandler ) != 0 ) + return 0; + + return 1; +} + +START_TEST( atexit ) +{ + TESTCASE( test_atexit() ); +} +END_TEST + +static int compare( const void * left, const void * right ) +{ + return *( (unsigned char *)left ) - *( (unsigned char *)right ); +} + +START_TEST( bsearch ) +{ + TESTCASE( bsearch( "e", abcde, 4, 1, compare ) == NULL ); + TESTCASE( bsearch( "e", abcde, 5, 1, compare ) == &abcde[4] ); + TESTCASE( bsearch( "a", abcde + 1, 4, 1, compare ) == NULL ); + TESTCASE( bsearch( "0", abcde, 1, 1, compare ) == NULL ); + TESTCASE( bsearch( "a", abcde, 1, 1, compare ) == &abcde[0] ); + TESTCASE( bsearch( "a", abcde, 0, 1, compare ) == NULL ); + TESTCASE( bsearch( "e", abcde, 3, 2, compare ) == &abcde[4] ); + TESTCASE( bsearch( "b", abcde, 3, 2, compare ) == NULL ); +} +END_TEST + +START_TEST( div ) +{ + div_t result = div( 5, 2 ); + TESTCASE( result.quot == 2 && result.rem == 1 ); + result = div( -5, 2 ); + TESTCASE( result.quot == -2 && result.rem == -1 ); + result = div( 5, -2 ); + TESTCASE( result.quot == -2 && result.rem == 1 ); + TESTCASE( sizeof( result.quot ) == sizeof( int ) ); + TESTCASE( sizeof( result.rem ) == sizeof( int ) ); +} +END_TEST + +START_TEST( getenv ) +{ + TESTCASE( strcmp( getenv( "SHELL" ), "/bin/bash" ) == 0 ); +} +END_TEST + +START_TEST( labs ) +{ + TESTCASE( labs( 0 ) == 0 ); + TESTCASE( labs( LONG_MAX ) == LONG_MAX ); + TESTCASE( labs( LONG_MIN + 1 ) == -( LONG_MIN + 1 ) ); +} +END_TEST + +START_TEST( ldiv ) +{ + ldiv_t result = ldiv( 5, 2 ); + TESTCASE( result.quot == 2 && result.rem == 1 ); + result = ldiv( -5, 2 ); + TESTCASE( result.quot == -2 && result.rem == -1 ); + result = ldiv( 5, -2 ); + TESTCASE( result.quot == -2 && result.rem == 1 ); + TESTCASE( sizeof( result.quot ) == sizeof( long ) ); + TESTCASE( sizeof( result.rem ) == sizeof( long ) ); +} +END_TEST + +START_TEST( llabs ) +{ + TESTCASE( llabs( 0ll ) == 0 ); + TESTCASE( llabs( LLONG_MAX ) == LLONG_MAX ); + TESTCASE( llabs( LLONG_MIN + 1 ) == -( LLONG_MIN + 1 ) ); +} +END_TEST + +START_TEST( lldiv ) +{ + lldiv_t result = lldiv( 5ll, 2ll ); + TESTCASE( result.quot == 2 && result.rem == 1 ); + result = lldiv( -5ll, 2ll ); + TESTCASE( result.quot == -2 && result.rem == -1 ); + result = lldiv( 5ll, -2ll ); + TESTCASE( result.quot == -2 && result.rem == 1 ); + TESTCASE( sizeof( result.quot ) == sizeof( long long ) ); + TESTCASE( sizeof( result.rem ) == sizeof( long long ) ); +} +END_TEST + +START_TEST( qsort ) +{ + char presort[] = { "shreicnyjqpvozxmbt" }; + char sorted1[] = { "bcehijmnopqrstvxyz" }; + char sorted2[] = { "bticjqnyozpvreshxm" }; + char s[19]; + strcpy( s, presort ); + qsort( s, 18, 1, compare ); + TESTCASE( strcmp( s, sorted1 ) == 0 ); + strcpy( s, presort ); + qsort( s, 9, 2, compare ); + TESTCASE( strcmp( s, sorted2 ) == 0 ); + strcpy( s, presort ); + qsort( s, 1, 1, compare ); + TESTCASE( strcmp( s, presort ) == 0 ); + qsort( s, 100, 0, compare ); + TESTCASE( strcmp( s, presort ) == 0 ); +} +END_TEST + +START_TEST( rand ) +{ + int rnd1 = RAND_MAX, rnd2 = RAND_MAX; + TESTCASE( ( rnd1 = rand() ) < RAND_MAX ); + TESTCASE( ( rnd2 = rand() ) < RAND_MAX ); + srand( 1 ); + TESTCASE( rand() == rnd1 ); + TESTCASE( rand() == rnd2 ); +} +END_TEST + +START_TEST( strtol ) +{ + char * endptr = NULL; + /* this, to base 36, overflows even a 256 bit integer */ + char overflow[] = "-ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ_"; + /* tricky border case */ + char tricky[] = "+0xz"; + errno = 0; + + /* basic functionality */ + TESTCASE( strtol( "123", NULL, 10 ) == 123 ); + /* proper detecting of default base 10 */ + TESTCASE( strtol( "456", NULL, 0 ) == 456 ); + /* proper functioning to smaller base */ + TESTCASE( strtol( "14", NULL, 8 ) == 12 ); + /* proper autodetecting of octal */ + TESTCASE( strtol( "016", NULL, 0 ) == 14 ); + /* proper autodetecting of hexadecimal, lowercase 'x' */ + TESTCASE( strtol( "0xFF", NULL, 0 ) == 255 ); + /* proper autodetecting of hexadecimal, uppercase 'X' */ + TESTCASE( strtol( "0Xa1", NULL, 0 ) == 161 ); + + /* proper handling of border case: 0x followed by non-hexdigit */ + TESTCASE( + (strtol(tricky, &endptr, 0) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* proper handling of border case: 0 followed by non-octdigit */ + TESTCASE( + (strtol(tricky, &endptr, 8) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* overflowing subject sequence must still return proper endptr */ + TESTCASE( + (strtol(overflow, &endptr, 36) == LONG_MIN) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* same for positive */ + TESTCASE( + (strtol(overflow + 1, &endptr, 36) == LONG_MAX) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* testing skipping of leading whitespace */ + TESTCASE( strtol( " \n\v\t\f789", NULL, 0 ) == 789 ); + + /* testing conversion failure */ + TESTCASE( + (strtol(overflow, &endptr, 10) == 0) && + (endptr == overflow) ); + + TESTCASE( + (strtol(overflow, &endptr, 0) == 0) && + (endptr == overflow) ); + +#if __SIZEOF_LONG__ == 4 + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtol("2147483647", NULL, 0) == 0x7fffffff) && (errno == 0) ); + TESTCASE( (strtol("2147483648", NULL, 0) == LONG_MAX) && (errno == ERANGE) ); + TESTCASE( (strtol("-2147483647", NULL, 0) == (long)0x80000001) && (errno == 0) ); + TESTCASE( (strtol("-2147483648", NULL, 0) == LONG_MIN) && (errno == 0) ); + TESTCASE( (strtol("-2147483649", NULL, 0) == LONG_MIN) && (errno == ERANGE) ); + /* TODO: test "odd" overflow, i.e. base is not power of two */ +#elif __SIZEOF_LONG__ == 8 + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtol("9223372036854775807", NULL, 0) == 0x7fffffffffffffff) && (errno == 0) ); + TESTCASE( (strtol("9223372036854775808", NULL, 0) == LONG_MAX) && (errno == ERANGE) ); + TESTCASE( (strtol("-9223372036854775807", NULL, 0) == (long)0x8000000000000001) && (errno == 0) ); + TESTCASE( (strtol("-9223372036854775808", NULL, 0) == LONG_MIN) && (errno == 0) ); + TESTCASE( (strtol("-9223372036854775809", NULL, 0) == LONG_MIN) && (errno == ERANGE) ); + /* TODO: test "odd" overflow, i.e. base is not power of two */ +#else +#error Unsupported width of 'long' (neither 32 nor 64 bit). +#endif +} +END_TEST + +START_TEST( strtoll ) +{ + char * endptr = NULL; + /* this, to base 36, overflows even a 256 bit integer */ + char overflow[] = "-ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ_"; + /* tricky border case */ + char tricky[] = "+0xz"; + errno = 0; + + /* basic functionality */ + TESTCASE( strtoll( "123", NULL, 10 ) == 123 ); + /* proper detecting of default base 10 */ + TESTCASE( strtoll( "456", NULL, 0 ) == 456 ); + /* proper functioning to smaller base */ + TESTCASE( strtoll( "14", NULL, 8 ) == 12 ); + /* proper autodetecting of octal */ + TESTCASE( strtoll( "016", NULL, 0 ) == 14 ); + /* proper autodetecting of hexadecimal, lowercase 'x' */ + TESTCASE( strtoll( "0xFF", NULL, 0 ) == 255 ); + /* proper autodetecting of hexadecimal, uppercase 'X' */ + TESTCASE( strtoll( "0Xa1", NULL, 0 ) == 161 ); + + /* proper handling of border case: 0x followed by non-hexdigit */ + TESTCASE( + (strtoll(tricky, &endptr, 0) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* proper handling of border case: 0 followed by non-octdigit */ + TESTCASE( + (strtoll(tricky, &endptr, 8) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* overflowing subject sequence must still return proper endptr */ + TESTCASE( + (strtoll(overflow, &endptr, 36) == LLONG_MIN) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* same for positive */ + TESTCASE( + (strtoll(overflow + 1, &endptr, 36) == LLONG_MAX) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* testing skipping of leading whitespace */ + TESTCASE( strtoll( " \n\v\t\f789", NULL, 0 ) == 789 ); + + /* testing conversion failure */ + TESTCASE( (strtoll(overflow, &endptr, 10) == 0) && (endptr == overflow) ); + TESTCASE( (strtoll(overflow, &endptr, 0) == 0) && (endptr == overflow) ); + + /* TODO: These tests assume two-complement, but conversion should work */ + /* for one-complement and signed magnitude just as well. Anyone having */ + /* a platform to test this on? */ +#if __SIZEOF_LONG_LONG__ == 8 + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtoll("9223372036854775807", NULL, 0) == 0x7fffffffffffffff) && (errno == 0) ); + TESTCASE( (strtoll("9223372036854775808", NULL, 0) == LLONG_MAX) && (errno == ERANGE) ); + TESTCASE( (strtoll("-9223372036854775807", NULL, 0) == (long long)0x8000000000000001) && (errno == 0) ); + TESTCASE( (strtoll("-9223372036854775808", NULL, 0) == LLONG_MIN) && (errno == 0) ); + TESTCASE( (strtoll("-9223372036854775809", NULL, 0) == LLONG_MIN) && (errno == ERANGE) ); + /* TODO: test "odd" overflow, i.e. base is not power of two */ +#elif __SIZEOF_LONG_LONG__ == 16 + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtoll("170141183460469231731687303715884105728", NULL, 0) == 0x7fffffffffffffffffffffffffffffff) && (errno == 0) ); + TESTCASE( (strtoll("170141183460469231731687303715884105729", NULL, 0) == LLONG_MAX) && (errno == ERANGE) ); + TESTCASE( (strtoll("-170141183460469231731687303715884105728", NULL, 0) == -0x80000000000000000000000000000001) && (errno == 0) ); + TESTCASE( (strtoll("-170141183460469231731687303715884105729", NULL, 0) == LLONG_MIN) && (errno == 0) ); + TESTCASE( (strtoll("-170141183460469231731687303715884105730", NULL, 0) == LLONG_MIN) && (errno == ERANGE) ); + /* TODO: test "odd" overflow, i.e. base is not power of two */ +#else +#error Unsupported width of 'long long' (neither 64 nor 128 bit). +#endif +} +END_TEST + +START_TEST( strtoul ) +{ + char * endptr = NULL; + /* this, to base 36, overflows even a 256 bit integer */ + char overflow[] = "-ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ_"; + /* tricky border case */ + char tricky[] = "+0xz"; + errno = 0; + /* basic functionality */ + TESTCASE( strtoul( "123", NULL, 10 ) == 123 ); + /* proper detecting of default base 10 */ + TESTCASE( strtoul( "456", NULL, 0 ) == 456 ); + /* proper functioning to smaller base */ + TESTCASE( strtoul( "14", NULL, 8 ) == 12 ); + /* proper autodetecting of octal */ + TESTCASE( strtoul( "016", NULL, 0 ) == 14 ); + /* proper autodetecting of hexadecimal, lowercase 'x' */ + TESTCASE( strtoul( "0xFF", NULL, 0 ) == 255 ); + /* proper autodetecting of hexadecimal, uppercase 'X' */ + TESTCASE( strtoul( "0Xa1", NULL, 0 ) == 161 ); + + /* proper handling of border case: 0x followed by non-hexdigit */ + TESTCASE( + (strtoul(tricky, &endptr, 0) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* proper handling of border case: 0 followed by non-octdigit */ + TESTCASE( + (strtoul(tricky, &endptr, 8) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* overflowing subject sequence must still return proper endptr */ + TESTCASE( + (strtoul(overflow, &endptr, 36) == ULONG_MAX) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* same for positive */ + TESTCASE( + (strtoul(overflow + 1, &endptr, 36) == ULONG_MAX) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* testing skipping of leading whitespace */ + TESTCASE( strtoul( " \n\v\t\f789", NULL, 0 ) == 789 ); + + /* testing conversion failure */ + TESTCASE( (strtoul(overflow, &endptr, 10) == 0) && (endptr == overflow) ); + TESTCASE( (strtoul(overflow, &endptr, 0) == 0) && (endptr == overflow) ); + + /* TODO: These tests assume two-complement, but conversion should work */ + /* for one-complement and signed magnitude just as well. Anyone having */ + /* a platform to test this on? */ +/* long -> 32 bit */ +#if __SIZEOF_LONG__ == 4 + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtoul("4294967295", NULL, 0) == ULONG_MAX) && (errno == 0) ); + TESTCASE( (strtoul("4294967296", NULL, 0) == ULONG_MAX) && (errno == ERANGE) ); + /* TODO: test "odd" overflow, i.e. base is not power of two */ +/* long -> 64 bit */ +#elif __SIZEOF_LONG__ == 8 + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtoul("18446744073709551615", NULL, 0) == ULONG_MAX) && (errno == 0) ); + TESTCASE( (strtoul("18446744073709551616", NULL, 0) == ULONG_MAX) && (errno == ERANGE) ); + /* TODO: test "odd" overflow, i.e. base is not power of two */ +#else +#error Unsupported width of 'long' (neither 32 nor 64 bit). +#endif +} +END_TEST + +START_TEST( strtoull ) +{ + char * endptr = NULL; + /* this, to base 36, overflows even a 256 bit integer */ + char overflow[] = "-ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ_"; + /* tricky border case */ + char tricky[] = "+0xz"; + errno = 0; + + /* basic functionality */ + TESTCASE( strtoull( "123", NULL, 10 ) == 123 ); + /* proper detecting of default base 10 */ + TESTCASE( strtoull( "456", NULL, 0 ) == 456 ); + /* proper functioning to smaller base */ + TESTCASE( strtoull( "14", NULL, 8 ) == 12 ); + /* proper autodetecting of octal */ + TESTCASE( strtoull( "016", NULL, 0 ) == 14 ); + /* proper autodetecting of hexadecimal, lowercase 'x' */ + TESTCASE( strtoull( "0xFF", NULL, 0 ) == 255 ); + /* proper autodetecting of hexadecimal, uppercase 'X' */ + TESTCASE( strtoull( "0Xa1", NULL, 0 ) == 161 ); + + /* proper handling of border case: 0x followed by non-hexdigit */ + TESTCASE( + (strtoull(tricky, &endptr, 0) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* proper handling of border case: 0 followed by non-octdigit */ + TESTCASE( + (strtoull(tricky, &endptr, 8) == 0) && + (endptr == tricky + 2) && + (errno == 0) ); + + /* overflowing subject sequence must still return proper endptr */ + TESTCASE( + (strtoull(overflow, &endptr, 36) == ULLONG_MAX) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* same for positive */ + TESTCASE( + (strtoull(overflow + 1, &endptr, 36) == ULLONG_MAX) && + (errno == ERANGE) && + ((endptr - overflow) == 53) ); + + /* testing skipping of leading whitespace */ + TESTCASE( strtoull( " \n\v\t\f789", NULL, 0 ) == 789 ); + + /* testing conversion failure */ + TESTCASE( (strtoull(overflow, &endptr, 10) == 0) && (endptr == overflow) ); + TESTCASE( (strtoull(overflow, &endptr, 0) == 0) && (endptr == overflow) ); +/* long long -> 64 bit */ +#if __SIZEOF_LONG_LONG__ == 8 + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtoull("18446744073709551615", NULL, 0) == ULLONG_MAX) && (errno == 0) ); + TESTCASE( (strtoull("18446744073709551616", NULL, 0) == ULLONG_MAX) && (errno == ERANGE) ); + /* TODO: test "odd" overflow, i.e. base is not power of two */ +/* long long -> 128 bit */ +#elif __SIZEOF_LONG_LONG__ == 16 + /* testing "even" overflow, i.e. base is power of two */ + TESTCASE( (strtoull("340282366920938463463374607431768211455", NULL, 0) == ULLONG_MAX) && (errno == 0) ); + TESTCASE( (strtoull("340282366920938463463374607431768211456", NULL, 0) == ULLONG_MAX) && (errno == ERANGE) ); + /* TODO: test "odd" overflow, i.e. base is not power of two */ +#else +#error Unsupported width of 'long long' (neither 64 nor 128 bit). +#endif +} +END_TEST + + +#define SYSTEM_MESSAGE "SUCCESS testing system()" +#define SYSTEM_COMMAND "echo '" SYSTEM_MESSAGE "'" + +START_TEST( system ) +{ + FILE * fh; + char buffer[25]; + buffer[24] = 'x'; + TESTCASE( ( fh = freopen( testfile, "wb+", stdout ) ) != NULL ); + TESTCASE( system( SYSTEM_COMMAND ) ); + rewind( fh ); + TESTCASE( fread( buffer, 1, 24, fh ) == 24 ); + TESTCASE( memcmp( buffer, SYSTEM_MESSAGE, 24 ) == 0 ); + TESTCASE( buffer[24] == 'x' ); + TESTCASE( fclose( fh ) == 0 ); + TESTCASE( remove( testfile ) == 0 ); +} +END_TEST + +START_SUITE( stdlib ) +{ + RUN_TEST( _Exit ); + RUN_TEST( abort ); + RUN_TEST( abs ); + RUN_TEST( atexit ); + RUN_TEST( bsearch ); + RUN_TEST( div ); + RUN_TEST( getenv ); + RUN_TEST( labs ); + RUN_TEST( ldiv ); + RUN_TEST( llabs ); + RUN_TEST( lldiv ); + RUN_TEST( qsort ); + RUN_TEST( strtol ); + RUN_TEST( strtoll ); + RUN_TEST( strtoul ); + RUN_TEST( strtoull ); + RUN_TEST( system ); +} +END_SUITE diff --git a/src/libraries/libc/tests/test_string.c b/src/libraries/libc/tests/test_string.c new file mode 100644 index 0000000..eb96353 --- /dev/null +++ b/src/libraries/libc/tests/test_string.c @@ -0,0 +1,386 @@ +#include "_PDCLIB_test.h" + +START_TEST( memchr ) +{ + TESTCASE( memchr( abcde, 'c', 5 ) == &abcde[2] ); + TESTCASE( memchr( abcde, 'a', 1 ) == &abcde[0] ); + TESTCASE( memchr( abcde, 'a', 0 ) == NULL ); + TESTCASE( memchr( abcde, '\0', 5 ) == NULL ); + TESTCASE( memchr( abcde, '\0', 6 ) == &abcde[5] ); +} +END_TEST + +START_TEST( memcmp ) +{ + const char xxxxx[] = "xxxxx"; + TESTCASE( memcmp( abcde, abcdx, 5 ) < 0 ); + TESTCASE( memcmp( abcde, abcdx, 4 ) == 0 ); + TESTCASE( memcmp( abcde, xxxxx, 0 ) == 0 ); + TESTCASE( memcmp( xxxxx, abcde, 1 ) > 0 ); +} +END_TEST + +START_TEST( memcpy ) +{ + char s[] = "xxxxxxxxxxx"; + char *r = memcpy(s, abcde, 6); + TESTCASE( r == s ); + TESTCASE( s[4] == 'e' ); + TESTCASE( s[5] == '\0' ); + + r = memcpy(s + 5, abcde, 5); + TESTCASE( r == s + 5 ); + TESTCASE( s[9] == 'e' ); + TESTCASE( s[10] == 'x' ); +} +END_TEST + +START_TEST( memmove ) +{ + char s[] = "xxxxabcde"; + char *r = memmove(s, s + 4, 5); + TESTCASE( r == s ); + TESTCASE( s[0] == 'a' ); + TESTCASE( s[4] == 'e' ); + TESTCASE( s[5] == 'b' ); + + r = memmove(s + 4, s, 5); + TESTCASE( r == s + 4 ); + TESTCASE( s[4] == 'a' ); +} +END_TEST + +START_TEST( memset ) +{ + char s[] = "xxxxxxxxx"; + void *r = memset(s, 'o', 10); + TESTCASE( r == s ); + TESTCASE( s[9] == 'o' ); + + r = memset(s, '_', 0); + TESTCASE( r == s ); + TESTCASE( s[0] == 'o' ); + + r = memset(s, '_', 1); + TESTCASE( r == s ); + TESTCASE( s[0] == '_' ); + TESTCASE( s[1] == 'o' ); +} +END_TEST + +START_TEST( strcat ) +{ + char s[] = "xx\0xxxxxx"; + char *r = strcat(s, abcde); + TESTCASE( r == s ); + TESTCASE( s[2] == 'a' ); + TESTCASE( s[6] == 'e' ); + TESTCASE( s[7] == '\0' ); + TESTCASE( s[8] == 'x' ); + + s[0] = '\0'; + r = strcat(s, abcdx); + TESTCASE( r == s ); + TESTCASE( s[4] == 'x' ); + TESTCASE( s[5] == '\0' ); + + r = strcat(s, "\0"); + TESTCASE( r == s ); + TESTCASE( s[5] == '\0' ); + TESTCASE( s[6] == 'e' ); +} +END_TEST + +START_TEST( strchr ) +{ + char abccd[] = "abccd"; + TESTCASE( strchr( abccd, 'x' ) == NULL ); + TESTCASE( strchr( abccd, 'a' ) == &abccd[0] ); + TESTCASE( strchr( abccd, 'd' ) == &abccd[4] ); + TESTCASE( strchr( abccd, '\0' ) == &abccd[5] ); + TESTCASE( strchr( abccd, 'c' ) == &abccd[2] ); +} +END_TEST + +START_TEST( strcmp ) +{ + char cmpabcde[] = "abcde"; + char cmpabcd_[] = "abcd\xfc"; + char empty[] = ""; + TESTCASE( strcmp( abcde, cmpabcde ) == 0 ); + TESTCASE( strcmp( abcde, abcdx ) < 0 ); + TESTCASE( strcmp( abcdx, abcde ) > 0 ); + TESTCASE( strcmp( empty, abcde ) < 0 ); + TESTCASE( strcmp( abcde, empty ) > 0 ); + TESTCASE( strcmp( abcde, cmpabcd_ ) < 0 ); +} +END_TEST + +START_TEST( strcoll ) +{ + char cmpabcde[] = "abcde"; + char empty[] = ""; + TESTCASE( strcoll( abcde, cmpabcde ) == 0 ); + TESTCASE( strcoll( abcde, abcdx ) < 0 ); + TESTCASE( strcoll( abcdx, abcde ) > 0 ); + TESTCASE( strcoll( empty, abcde ) < 0 ); + TESTCASE( strcoll( abcde, empty ) > 0 ); +} +END_TEST + +START_TEST( strcpy ) +{ + char s[] = "xxxxx"; + char *r = strcpy(s, ""); + TESTCASE( r == s ); + TESTCASE( s[0] == '\0' ); + TESTCASE( s[1] == 'x' ); + + r = strcpy(s, abcde); + TESTCASE( r == s ); + TESTCASE( s[0] == 'a' ); + TESTCASE( s[4] == 'e' ); + TESTCASE( s[5] == '\0' ); +} +END_TEST + +START_TEST( strcspn ) +{ + TESTCASE( strcspn( abcde, "x" ) == 5 ); + TESTCASE( strcspn( abcde, "xyz" ) == 5 ); + TESTCASE( strcspn( abcde, "zyx" ) == 5 ); + TESTCASE( strcspn( abcdx, "x" ) == 4 ); + TESTCASE( strcspn( abcdx, "xyz" ) == 4 ); + TESTCASE( strcspn( abcdx, "zyx" ) == 4 ); + TESTCASE( strcspn( abcde, "a" ) == 0 ); + TESTCASE( strcspn( abcde, "abc" ) == 0 ); + TESTCASE( strcspn( abcde, "cba" ) == 0 ); +} +END_TEST + +START_TEST( strerror ) +{ + TESTCASE( strerror(ERANGE) != strerror(EDOM) ); +} +END_TEST + +START_TEST( strlen ) +{ + TESTCASE( strlen( abcde ) == 5 ); + TESTCASE( strlen( "" ) == 0 ); +} +END_TEST + +START_TEST( strncat ) +{ + char s[] = "xx\0xxxxxx"; + char *r = strncat(s, abcde, 10); + TESTCASE( r == s ); + TESTCASE( s[2] == 'a' ); + TESTCASE( s[6] == 'e' ); + TESTCASE( s[7] == '\0' ); + TESTCASE( s[8] == 'x' ); + + s[0] = '\0'; + r = strncat(s, abcdx, 10); + TESTCASE( r == s ); + TESTCASE( s[4] == 'x' ); + TESTCASE( s[5] == '\0' ); + + r = strncat(s, "\0", 10); + TESTCASE( r == s ); + TESTCASE( s[5] == '\0' ); + TESTCASE( s[6] == 'e' ); + + r = strncat(s, abcde, 0); + TESTCASE( r == s ); + TESTCASE( s[5] == '\0' ); + TESTCASE( s[6] == 'e' ); + + r = strncat(s, abcde, 3); + TESTCASE( r == s ); + TESTCASE( s[5] == 'a' ); + TESTCASE( s[7] == 'c' ); + TESTCASE( s[8] == '\0' ); +} +END_TEST + +START_TEST( strncmp ) +{ + char cmpabcde[] = "abcde\0f"; + char cmpabcd_[] = "abcde\xfc"; + char empty[] = ""; + char x[] = "x"; + TESTCASE( strncmp( abcde, cmpabcde, 5 ) == 0 ); + TESTCASE( strncmp( abcde, cmpabcde, 10 ) == 0 ); + TESTCASE( strncmp( abcde, abcdx, 5 ) < 0 ); + TESTCASE( strncmp( abcdx, abcde, 5 ) > 0 ); + TESTCASE( strncmp( empty, abcde, 5 ) < 0 ); + TESTCASE( strncmp( abcde, empty, 5 ) > 0 ); + TESTCASE( strncmp( abcde, abcdx, 4 ) == 0 ); + TESTCASE( strncmp( abcde, x, 0 ) == 0 ); + TESTCASE( strncmp( abcde, x, 1 ) < 0 ); + TESTCASE( strncmp( abcde, cmpabcd_, 10 ) < 0 ); +} +END_TEST + +START_TEST( strncpy ) +{ + char s[] = "xxxxxxx"; + char *r = strncpy(s, "", 1); + TESTCASE( r == s ); + TESTCASE( s[0] == '\0' ); + TESTCASE( s[1] == 'x' ); + + r = strncpy(s, abcde, 6); + TESTCASE( r == s ); + TESTCASE( s[0] == 'a' ); + TESTCASE( s[4] == 'e' ); + TESTCASE( s[5] == '\0' ); + TESTCASE( s[6] == 'x' ); + + r = strncpy(s, abcde, 7); + TESTCASE( r == s ); + TESTCASE( s[6] == '\0' ); + + r = strncpy(s, "xxxx", 3); + TESTCASE( r == s ); + TESTCASE( s[0] == 'x' ); + TESTCASE( s[2] == 'x' ); + TESTCASE( s[3] == 'd' ); +} +END_TEST + +START_TEST( strpbrk ) +{ + TESTCASE( strpbrk( abcde, "x" ) == NULL ); + TESTCASE( strpbrk( abcde, "xyz" ) == NULL ); + TESTCASE( strpbrk( abcdx, "x" ) == &abcdx[4] ); + TESTCASE( strpbrk( abcdx, "xyz" ) == &abcdx[4] ); + TESTCASE( strpbrk( abcdx, "zyx" ) == &abcdx[4] ); + TESTCASE( strpbrk( abcde, "a" ) == &abcde[0] ); + TESTCASE( strpbrk( abcde, "abc" ) == &abcde[0] ); + TESTCASE( strpbrk( abcde, "cba" ) == &abcde[0] ); +} +END_TEST + +START_TEST( strrchr ) +{ + char abccd[] = "abccd"; + TESTCASE( strrchr( abcde, '\0' ) == &abcde[5] ); + TESTCASE( strrchr( abcde, 'e' ) == &abcde[4] ); + TESTCASE( strrchr( abcde, 'a' ) == &abcde[0] ); + TESTCASE( strrchr( abccd, 'c' ) == &abccd[3] ); +} +END_TEST + +START_TEST( strspn ) +{ + TESTCASE( strspn( abcde, "abc" ) == 3 ); + TESTCASE( strspn( abcde, "b" ) == 0 ); + TESTCASE( strspn( abcde, abcde ) == 5 ); +} +END_TEST + +START_TEST( strstr ) +{ + char s[] = "abcabcabcdabcde"; + TESTCASE( strstr( s, "x" ) == NULL ); + TESTCASE( strstr( s, "xyz" ) == NULL ); + TESTCASE( strstr( s, "a" ) == &s[0] ); + TESTCASE( strstr( s, "abc" ) == &s[0] ); + TESTCASE( strstr( s, "abcd" ) == &s[6] ); + TESTCASE( strstr( s, "abcde" ) == &s[10] ); +} +END_TEST + +START_TEST( strtok ) +{ + char s[] = "_a_bc__d_"; + char *r = strtok(s, "_"); + TESTCASE( r == &s[1] ); + TESTCASE( s[1] == 'a' ); + TESTCASE( s[2] == '\0' ); + + r = strtok(NULL, "_"); + TESTCASE( r == &s[3] ); + TESTCASE( s[3] == 'b' ); + TESTCASE( s[4] == 'c' ); + TESTCASE( s[5] == '\0' ); + + r = strtok(NULL, "_"); + TESTCASE( r == &s[7] ); + TESTCASE( s[6] == '_' ); + TESTCASE( s[7] == 'd' ); + TESTCASE( s[8] == '\0' ); + + r = strtok(NULL, "_"); + TESTCASE( r == NULL ); + + strcpy( s, "ab_cd" ); + r = strtok(s, "_"); + TESTCASE( r == &s[0] ); + TESTCASE( s[0] == 'a' ); + TESTCASE( s[1] == 'b' ); + TESTCASE( s[2] == '\0' ); + + r = strtok(NULL, "_"); + TESTCASE( r == &s[3] ); + TESTCASE( s[3] == 'c' ); + TESTCASE( s[4] == 'd' ); + TESTCASE( s[5] == '\0' ); + + r = strtok(NULL, "_"); + TESTCASE( r == NULL ); +} +END_TEST + +START_TEST( strxfrm ) +{ + char s[] = "xxxxxxxxxxx"; + size_t r = strxfrm(NULL, "123456789012", 0); + TESTCASE( r == 12 ); + + r = strxfrm(s, "123456789012", 12); + TESTCASE( r == 12 ); + + /* + The following test case is true in *this* implementation, but doesn't have to. + TESTCASE( s[0] == 'x' ); + */ + + r = strxfrm(s, "1234567890", 11); + TESTCASE( r == 10 ); + TESTCASE( s[0] == '1' ); + TESTCASE( s[9] == '0' ); + TESTCASE( s[10] == '\0' ); +} +END_TEST + + +START_SUITE( string ) +{ + RUN_TEST( memchr ); + RUN_TEST( memcmp ); + RUN_TEST( memcpy ); + RUN_TEST( memmove ); + RUN_TEST( memset ); + RUN_TEST( strcat ); + RUN_TEST( strchr ); + RUN_TEST( strcmp ); + RUN_TEST( strcoll ); + RUN_TEST( strcpy ); + RUN_TEST( strcspn ); + RUN_TEST( strerror ); + RUN_TEST( strlen ); + RUN_TEST( strncat ); + RUN_TEST( strncmp ); + RUN_TEST( strncpy ); + RUN_TEST( strpbrk ); + RUN_TEST( strrchr ); + RUN_TEST( strspn ); + RUN_TEST( strstr ); + RUN_TEST( strtok ); + RUN_TEST( strxfrm ); +} +END_SUITE diff --git a/src/libraries/libc/tests/test_time.c b/src/libraries/libc/tests/test_time.c new file mode 100644 index 0000000..e03147b --- /dev/null +++ b/src/libraries/libc/tests/test_time.c @@ -0,0 +1,1167 @@ +#include "_PDCLIB_test.h" +#include + +#define MKTIME( tm, sec, min, hour, day, month, year, wday, yday ) tm.tm_sec = sec; tm.tm_min = min; tm.tm_hour = hour; tm.tm_mday = day; tm.tm_mon = month; tm.tm_year = year; tm.tm_wday = wday; tm.tm_yday = yday; tm.tm_isdst = -1; + +/* Test data generated by reference mktime() / strftime(), listing: + * tm_year + * tm_wday + * tm_yday + * '%U' result + * '%V' result + * '%W' result +*/ +int data[1020][6] = +{ +{ 70, 4, 0, 0, 1, 0 }, +{ 70, 5, 1, 0, 1, 0 }, +{ 70, 6, 2, 0, 1, 0 }, +{ 70, 0, 3, 1, 1, 0 }, +{ 70, 1, 4, 1, 2, 1 }, +{ 70, 2, 5, 1, 2, 1 }, +{ 70, 3, 6, 1, 2, 1 }, +{ 70, 4, 357, 51, 52, 51 }, +{ 70, 5, 358, 51, 52, 51 }, +{ 70, 6, 359, 51, 52, 51 }, +{ 70, 0, 360, 52, 52, 51 }, +{ 70, 1, 361, 52, 53, 52 }, +{ 70, 2, 362, 52, 53, 52 }, +{ 70, 3, 363, 52, 53, 52 }, +{ 70, 4, 364, 52, 53, 52 }, +{ 71, 5, 0, 0, 53, 0 }, +{ 71, 6, 1, 0, 53, 0 }, +{ 71, 0, 2, 1, 53, 0 }, +{ 71, 1, 3, 1, 1, 1 }, +{ 71, 2, 4, 1, 1, 1 }, +{ 71, 3, 5, 1, 1, 1 }, +{ 71, 4, 6, 1, 1, 1 }, +{ 71, 5, 357, 51, 51, 51 }, +{ 71, 6, 358, 51, 51, 51 }, +{ 71, 0, 359, 52, 51, 51 }, +{ 71, 1, 360, 52, 52, 52 }, +{ 71, 2, 361, 52, 52, 52 }, +{ 71, 3, 362, 52, 52, 52 }, +{ 71, 4, 363, 52, 52, 52 }, +{ 71, 5, 364, 52, 52, 52 }, +{ 72, 6, 0, 0, 52, 0 }, +{ 72, 0, 1, 1, 52, 0 }, +{ 72, 1, 2, 1, 1, 1 }, +{ 72, 2, 3, 1, 1, 1 }, +{ 72, 3, 4, 1, 1, 1 }, +{ 72, 4, 5, 1, 1, 1 }, +{ 72, 5, 6, 1, 1, 1 }, +{ 72, 0, 358, 52, 51, 51 }, +{ 72, 1, 359, 52, 52, 52 }, +{ 72, 2, 360, 52, 52, 52 }, +{ 72, 3, 361, 52, 52, 52 }, +{ 72, 4, 362, 52, 52, 52 }, +{ 72, 5, 363, 52, 52, 52 }, +{ 72, 6, 364, 52, 52, 52 }, +{ 72, 0, 365, 53, 52, 52 }, +{ 73, 1, 0, 0, 1, 1 }, +{ 73, 2, 1, 0, 1, 1 }, +{ 73, 3, 2, 0, 1, 1 }, +{ 73, 4, 3, 0, 1, 1 }, +{ 73, 5, 4, 0, 1, 1 }, +{ 73, 6, 5, 0, 1, 1 }, +{ 73, 0, 6, 1, 1, 1 }, +{ 73, 1, 357, 51, 52, 52 }, +{ 73, 2, 358, 51, 52, 52 }, +{ 73, 3, 359, 51, 52, 52 }, +{ 73, 4, 360, 51, 52, 52 }, +{ 73, 5, 361, 51, 52, 52 }, +{ 73, 6, 362, 51, 52, 52 }, +{ 73, 0, 363, 52, 52, 52 }, +{ 73, 1, 364, 52, 1, 53 }, +{ 74, 2, 0, 0, 1, 0 }, +{ 74, 3, 1, 0, 1, 0 }, +{ 74, 4, 2, 0, 1, 0 }, +{ 74, 5, 3, 0, 1, 0 }, +{ 74, 6, 4, 0, 1, 0 }, +{ 74, 0, 5, 1, 1, 0 }, +{ 74, 1, 6, 1, 2, 1 }, +{ 74, 2, 357, 51, 52, 51 }, +{ 74, 3, 358, 51, 52, 51 }, +{ 74, 4, 359, 51, 52, 51 }, +{ 74, 5, 360, 51, 52, 51 }, +{ 74, 6, 361, 51, 52, 51 }, +{ 74, 0, 362, 52, 52, 51 }, +{ 74, 1, 363, 52, 1, 52 }, +{ 74, 2, 364, 52, 1, 52 }, +{ 75, 3, 0, 0, 1, 0 }, +{ 75, 4, 1, 0, 1, 0 }, +{ 75, 5, 2, 0, 1, 0 }, +{ 75, 6, 3, 0, 1, 0 }, +{ 75, 0, 4, 1, 1, 0 }, +{ 75, 1, 5, 1, 2, 1 }, +{ 75, 2, 6, 1, 2, 1 }, +{ 75, 3, 357, 51, 52, 51 }, +{ 75, 4, 358, 51, 52, 51 }, +{ 75, 5, 359, 51, 52, 51 }, +{ 75, 6, 360, 51, 52, 51 }, +{ 75, 0, 361, 52, 52, 51 }, +{ 75, 1, 362, 52, 1, 52 }, +{ 75, 2, 363, 52, 1, 52 }, +{ 75, 3, 364, 52, 1, 52 }, +{ 76, 4, 0, 0, 1, 0 }, +{ 76, 5, 1, 0, 1, 0 }, +{ 76, 6, 2, 0, 1, 0 }, +{ 76, 0, 3, 1, 1, 0 }, +{ 76, 1, 4, 1, 2, 1 }, +{ 76, 2, 5, 1, 2, 1 }, +{ 76, 3, 6, 1, 2, 1 }, +{ 76, 5, 358, 51, 52, 51 }, +{ 76, 6, 359, 51, 52, 51 }, +{ 76, 0, 360, 52, 52, 51 }, +{ 76, 1, 361, 52, 53, 52 }, +{ 76, 2, 362, 52, 53, 52 }, +{ 76, 3, 363, 52, 53, 52 }, +{ 76, 4, 364, 52, 53, 52 }, +{ 76, 5, 365, 52, 53, 52 }, +{ 77, 6, 0, 0, 53, 0 }, +{ 77, 0, 1, 1, 53, 0 }, +{ 77, 1, 2, 1, 1, 1 }, +{ 77, 2, 3, 1, 1, 1 }, +{ 77, 3, 4, 1, 1, 1 }, +{ 77, 4, 5, 1, 1, 1 }, +{ 77, 5, 6, 1, 1, 1 }, +{ 77, 6, 357, 51, 51, 51 }, +{ 77, 0, 358, 52, 51, 51 }, +{ 77, 1, 359, 52, 52, 52 }, +{ 77, 2, 360, 52, 52, 52 }, +{ 77, 3, 361, 52, 52, 52 }, +{ 77, 4, 362, 52, 52, 52 }, +{ 77, 5, 363, 52, 52, 52 }, +{ 77, 6, 364, 52, 52, 52 }, +{ 78, 0, 0, 1, 52, 0 }, +{ 78, 1, 1, 1, 1, 1 }, +{ 78, 2, 2, 1, 1, 1 }, +{ 78, 3, 3, 1, 1, 1 }, +{ 78, 4, 4, 1, 1, 1 }, +{ 78, 5, 5, 1, 1, 1 }, +{ 78, 6, 6, 1, 1, 1 }, +{ 78, 0, 357, 52, 51, 51 }, +{ 78, 1, 358, 52, 52, 52 }, +{ 78, 2, 359, 52, 52, 52 }, +{ 78, 3, 360, 52, 52, 52 }, +{ 78, 4, 361, 52, 52, 52 }, +{ 78, 5, 362, 52, 52, 52 }, +{ 78, 6, 363, 52, 52, 52 }, +{ 78, 0, 364, 53, 52, 52 }, +{ 79, 1, 0, 0, 1, 1 }, +{ 79, 2, 1, 0, 1, 1 }, +{ 79, 3, 2, 0, 1, 1 }, +{ 79, 4, 3, 0, 1, 1 }, +{ 79, 5, 4, 0, 1, 1 }, +{ 79, 6, 5, 0, 1, 1 }, +{ 79, 0, 6, 1, 1, 1 }, +{ 79, 1, 357, 51, 52, 52 }, +{ 79, 2, 358, 51, 52, 52 }, +{ 79, 3, 359, 51, 52, 52 }, +{ 79, 4, 360, 51, 52, 52 }, +{ 79, 5, 361, 51, 52, 52 }, +{ 79, 6, 362, 51, 52, 52 }, +{ 79, 0, 363, 52, 52, 52 }, +{ 79, 1, 364, 52, 1, 53 }, +{ 80, 2, 0, 0, 1, 0 }, +{ 80, 3, 1, 0, 1, 0 }, +{ 80, 4, 2, 0, 1, 0 }, +{ 80, 5, 3, 0, 1, 0 }, +{ 80, 6, 4, 0, 1, 0 }, +{ 80, 0, 5, 1, 1, 0 }, +{ 80, 1, 6, 1, 2, 1 }, +{ 80, 3, 358, 51, 52, 51 }, +{ 80, 4, 359, 51, 52, 51 }, +{ 80, 5, 360, 51, 52, 51 }, +{ 80, 6, 361, 51, 52, 51 }, +{ 80, 0, 362, 52, 52, 51 }, +{ 80, 1, 363, 52, 1, 52 }, +{ 80, 2, 364, 52, 1, 52 }, +{ 80, 3, 365, 52, 1, 52 }, +{ 81, 4, 0, 0, 1, 0 }, +{ 81, 5, 1, 0, 1, 0 }, +{ 81, 6, 2, 0, 1, 0 }, +{ 81, 0, 3, 1, 1, 0 }, +{ 81, 1, 4, 1, 2, 1 }, +{ 81, 2, 5, 1, 2, 1 }, +{ 81, 3, 6, 1, 2, 1 }, +{ 81, 4, 357, 51, 52, 51 }, +{ 81, 5, 358, 51, 52, 51 }, +{ 81, 6, 359, 51, 52, 51 }, +{ 81, 0, 360, 52, 52, 51 }, +{ 81, 1, 361, 52, 53, 52 }, +{ 81, 2, 362, 52, 53, 52 }, +{ 81, 3, 363, 52, 53, 52 }, +{ 81, 4, 364, 52, 53, 52 }, +{ 82, 5, 0, 0, 53, 0 }, +{ 82, 6, 1, 0, 53, 0 }, +{ 82, 0, 2, 1, 53, 0 }, +{ 82, 1, 3, 1, 1, 1 }, +{ 82, 2, 4, 1, 1, 1 }, +{ 82, 3, 5, 1, 1, 1 }, +{ 82, 4, 6, 1, 1, 1 }, +{ 82, 5, 357, 51, 51, 51 }, +{ 82, 6, 358, 51, 51, 51 }, +{ 82, 0, 359, 52, 51, 51 }, +{ 82, 1, 360, 52, 52, 52 }, +{ 82, 2, 361, 52, 52, 52 }, +{ 82, 3, 362, 52, 52, 52 }, +{ 82, 4, 363, 52, 52, 52 }, +{ 82, 5, 364, 52, 52, 52 }, +{ 83, 6, 0, 0, 52, 0 }, +{ 83, 0, 1, 1, 52, 0 }, +{ 83, 1, 2, 1, 1, 1 }, +{ 83, 2, 3, 1, 1, 1 }, +{ 83, 3, 4, 1, 1, 1 }, +{ 83, 4, 5, 1, 1, 1 }, +{ 83, 5, 6, 1, 1, 1 }, +{ 83, 6, 357, 51, 51, 51 }, +{ 83, 0, 358, 52, 51, 51 }, +{ 83, 1, 359, 52, 52, 52 }, +{ 83, 2, 360, 52, 52, 52 }, +{ 83, 3, 361, 52, 52, 52 }, +{ 83, 4, 362, 52, 52, 52 }, +{ 83, 5, 363, 52, 52, 52 }, +{ 83, 6, 364, 52, 52, 52 }, +{ 84, 0, 0, 1, 52, 0 }, +{ 84, 1, 1, 1, 1, 1 }, +{ 84, 2, 2, 1, 1, 1 }, +{ 84, 3, 3, 1, 1, 1 }, +{ 84, 4, 4, 1, 1, 1 }, +{ 84, 5, 5, 1, 1, 1 }, +{ 84, 6, 6, 1, 1, 1 }, +{ 84, 1, 358, 52, 52, 52 }, +{ 84, 2, 359, 52, 52, 52 }, +{ 84, 3, 360, 52, 52, 52 }, +{ 84, 4, 361, 52, 52, 52 }, +{ 84, 5, 362, 52, 52, 52 }, +{ 84, 6, 363, 52, 52, 52 }, +{ 84, 0, 364, 53, 52, 52 }, +{ 84, 1, 365, 53, 1, 53 }, +{ 85, 2, 0, 0, 1, 0 }, +{ 85, 3, 1, 0, 1, 0 }, +{ 85, 4, 2, 0, 1, 0 }, +{ 85, 5, 3, 0, 1, 0 }, +{ 85, 6, 4, 0, 1, 0 }, +{ 85, 0, 5, 1, 1, 0 }, +{ 85, 1, 6, 1, 2, 1 }, +{ 85, 2, 357, 51, 52, 51 }, +{ 85, 3, 358, 51, 52, 51 }, +{ 85, 4, 359, 51, 52, 51 }, +{ 85, 5, 360, 51, 52, 51 }, +{ 85, 6, 361, 51, 52, 51 }, +{ 85, 0, 362, 52, 52, 51 }, +{ 85, 1, 363, 52, 1, 52 }, +{ 85, 2, 364, 52, 1, 52 }, +{ 86, 3, 0, 0, 1, 0 }, +{ 86, 4, 1, 0, 1, 0 }, +{ 86, 5, 2, 0, 1, 0 }, +{ 86, 6, 3, 0, 1, 0 }, +{ 86, 0, 4, 1, 1, 0 }, +{ 86, 1, 5, 1, 2, 1 }, +{ 86, 2, 6, 1, 2, 1 }, +{ 86, 3, 357, 51, 52, 51 }, +{ 86, 4, 358, 51, 52, 51 }, +{ 86, 5, 359, 51, 52, 51 }, +{ 86, 6, 360, 51, 52, 51 }, +{ 86, 0, 361, 52, 52, 51 }, +{ 86, 1, 362, 52, 1, 52 }, +{ 86, 2, 363, 52, 1, 52 }, +{ 86, 3, 364, 52, 1, 52 }, +{ 87, 4, 0, 0, 1, 0 }, +{ 87, 5, 1, 0, 1, 0 }, +{ 87, 6, 2, 0, 1, 0 }, +{ 87, 0, 3, 1, 1, 0 }, +{ 87, 1, 4, 1, 2, 1 }, +{ 87, 2, 5, 1, 2, 1 }, +{ 87, 3, 6, 1, 2, 1 }, +{ 87, 4, 357, 51, 52, 51 }, +{ 87, 5, 358, 51, 52, 51 }, +{ 87, 6, 359, 51, 52, 51 }, +{ 87, 0, 360, 52, 52, 51 }, +{ 87, 1, 361, 52, 53, 52 }, +{ 87, 2, 362, 52, 53, 52 }, +{ 87, 3, 363, 52, 53, 52 }, +{ 87, 4, 364, 52, 53, 52 }, +{ 88, 5, 0, 0, 53, 0 }, +{ 88, 6, 1, 0, 53, 0 }, +{ 88, 0, 2, 1, 53, 0 }, +{ 88, 1, 3, 1, 1, 1 }, +{ 88, 2, 4, 1, 1, 1 }, +{ 88, 3, 5, 1, 1, 1 }, +{ 88, 4, 6, 1, 1, 1 }, +{ 88, 6, 358, 51, 51, 51 }, +{ 88, 0, 359, 52, 51, 51 }, +{ 88, 1, 360, 52, 52, 52 }, +{ 88, 2, 361, 52, 52, 52 }, +{ 88, 3, 362, 52, 52, 52 }, +{ 88, 4, 363, 52, 52, 52 }, +{ 88, 5, 364, 52, 52, 52 }, +{ 88, 6, 365, 52, 52, 52 }, +{ 89, 0, 0, 1, 52, 0 }, +{ 89, 1, 1, 1, 1, 1 }, +{ 89, 2, 2, 1, 1, 1 }, +{ 89, 3, 3, 1, 1, 1 }, +{ 89, 4, 4, 1, 1, 1 }, +{ 89, 5, 5, 1, 1, 1 }, +{ 89, 6, 6, 1, 1, 1 }, +{ 89, 0, 357, 52, 51, 51 }, +{ 89, 1, 358, 52, 52, 52 }, +{ 89, 2, 359, 52, 52, 52 }, +{ 89, 3, 360, 52, 52, 52 }, +{ 89, 4, 361, 52, 52, 52 }, +{ 89, 5, 362, 52, 52, 52 }, +{ 89, 6, 363, 52, 52, 52 }, +{ 89, 0, 364, 53, 52, 52 }, +{ 90, 1, 0, 0, 1, 1 }, +{ 90, 2, 1, 0, 1, 1 }, +{ 90, 3, 2, 0, 1, 1 }, +{ 90, 4, 3, 0, 1, 1 }, +{ 90, 5, 4, 0, 1, 1 }, +{ 90, 6, 5, 0, 1, 1 }, +{ 90, 0, 6, 1, 1, 1 }, +{ 90, 1, 357, 51, 52, 52 }, +{ 90, 2, 358, 51, 52, 52 }, +{ 90, 3, 359, 51, 52, 52 }, +{ 90, 4, 360, 51, 52, 52 }, +{ 90, 5, 361, 51, 52, 52 }, +{ 90, 6, 362, 51, 52, 52 }, +{ 90, 0, 363, 52, 52, 52 }, +{ 90, 1, 364, 52, 1, 53 }, +{ 91, 2, 0, 0, 1, 0 }, +{ 91, 3, 1, 0, 1, 0 }, +{ 91, 4, 2, 0, 1, 0 }, +{ 91, 5, 3, 0, 1, 0 }, +{ 91, 6, 4, 0, 1, 0 }, +{ 91, 0, 5, 1, 1, 0 }, +{ 91, 1, 6, 1, 2, 1 }, +{ 91, 2, 357, 51, 52, 51 }, +{ 91, 3, 358, 51, 52, 51 }, +{ 91, 4, 359, 51, 52, 51 }, +{ 91, 5, 360, 51, 52, 51 }, +{ 91, 6, 361, 51, 52, 51 }, +{ 91, 0, 362, 52, 52, 51 }, +{ 91, 1, 363, 52, 1, 52 }, +{ 91, 2, 364, 52, 1, 52 }, +{ 92, 3, 0, 0, 1, 0 }, +{ 92, 4, 1, 0, 1, 0 }, +{ 92, 5, 2, 0, 1, 0 }, +{ 92, 6, 3, 0, 1, 0 }, +{ 92, 0, 4, 1, 1, 0 }, +{ 92, 1, 5, 1, 2, 1 }, +{ 92, 2, 6, 1, 2, 1 }, +{ 92, 4, 358, 51, 52, 51 }, +{ 92, 5, 359, 51, 52, 51 }, +{ 92, 6, 360, 51, 52, 51 }, +{ 92, 0, 361, 52, 52, 51 }, +{ 92, 1, 362, 52, 53, 52 }, +{ 92, 2, 363, 52, 53, 52 }, +{ 92, 3, 364, 52, 53, 52 }, +{ 92, 4, 365, 52, 53, 52 }, +{ 93, 5, 0, 0, 53, 0 }, +{ 93, 6, 1, 0, 53, 0 }, +{ 93, 0, 2, 1, 53, 0 }, +{ 93, 1, 3, 1, 1, 1 }, +{ 93, 2, 4, 1, 1, 1 }, +{ 93, 3, 5, 1, 1, 1 }, +{ 93, 4, 6, 1, 1, 1 }, +{ 93, 5, 357, 51, 51, 51 }, +{ 93, 6, 358, 51, 51, 51 }, +{ 93, 0, 359, 52, 51, 51 }, +{ 93, 1, 360, 52, 52, 52 }, +{ 93, 2, 361, 52, 52, 52 }, +{ 93, 3, 362, 52, 52, 52 }, +{ 93, 4, 363, 52, 52, 52 }, +{ 93, 5, 364, 52, 52, 52 }, +{ 94, 6, 0, 0, 52, 0 }, +{ 94, 0, 1, 1, 52, 0 }, +{ 94, 1, 2, 1, 1, 1 }, +{ 94, 2, 3, 1, 1, 1 }, +{ 94, 3, 4, 1, 1, 1 }, +{ 94, 4, 5, 1, 1, 1 }, +{ 94, 5, 6, 1, 1, 1 }, +{ 94, 6, 357, 51, 51, 51 }, +{ 94, 0, 358, 52, 51, 51 }, +{ 94, 1, 359, 52, 52, 52 }, +{ 94, 2, 360, 52, 52, 52 }, +{ 94, 3, 361, 52, 52, 52 }, +{ 94, 4, 362, 52, 52, 52 }, +{ 94, 5, 363, 52, 52, 52 }, +{ 94, 6, 364, 52, 52, 52 }, +{ 95, 0, 0, 1, 52, 0 }, +{ 95, 1, 1, 1, 1, 1 }, +{ 95, 2, 2, 1, 1, 1 }, +{ 95, 3, 3, 1, 1, 1 }, +{ 95, 4, 4, 1, 1, 1 }, +{ 95, 5, 5, 1, 1, 1 }, +{ 95, 6, 6, 1, 1, 1 }, +{ 95, 0, 357, 52, 51, 51 }, +{ 95, 1, 358, 52, 52, 52 }, +{ 95, 2, 359, 52, 52, 52 }, +{ 95, 3, 360, 52, 52, 52 }, +{ 95, 4, 361, 52, 52, 52 }, +{ 95, 5, 362, 52, 52, 52 }, +{ 95, 6, 363, 52, 52, 52 }, +{ 95, 0, 364, 53, 52, 52 }, +{ 96, 1, 0, 0, 1, 1 }, +{ 96, 2, 1, 0, 1, 1 }, +{ 96, 3, 2, 0, 1, 1 }, +{ 96, 4, 3, 0, 1, 1 }, +{ 96, 5, 4, 0, 1, 1 }, +{ 96, 6, 5, 0, 1, 1 }, +{ 96, 0, 6, 1, 1, 1 }, +{ 96, 2, 358, 51, 52, 52 }, +{ 96, 3, 359, 51, 52, 52 }, +{ 96, 4, 360, 51, 52, 52 }, +{ 96, 5, 361, 51, 52, 52 }, +{ 96, 6, 362, 51, 52, 52 }, +{ 96, 0, 363, 52, 52, 52 }, +{ 96, 1, 364, 52, 1, 53 }, +{ 96, 2, 365, 52, 1, 53 }, +{ 97, 3, 0, 0, 1, 0 }, +{ 97, 4, 1, 0, 1, 0 }, +{ 97, 5, 2, 0, 1, 0 }, +{ 97, 6, 3, 0, 1, 0 }, +{ 97, 0, 4, 1, 1, 0 }, +{ 97, 1, 5, 1, 2, 1 }, +{ 97, 2, 6, 1, 2, 1 }, +{ 97, 3, 357, 51, 52, 51 }, +{ 97, 4, 358, 51, 52, 51 }, +{ 97, 5, 359, 51, 52, 51 }, +{ 97, 6, 360, 51, 52, 51 }, +{ 97, 0, 361, 52, 52, 51 }, +{ 97, 1, 362, 52, 1, 52 }, +{ 97, 2, 363, 52, 1, 52 }, +{ 97, 3, 364, 52, 1, 52 }, +{ 98, 4, 0, 0, 1, 0 }, +{ 98, 5, 1, 0, 1, 0 }, +{ 98, 6, 2, 0, 1, 0 }, +{ 98, 0, 3, 1, 1, 0 }, +{ 98, 1, 4, 1, 2, 1 }, +{ 98, 2, 5, 1, 2, 1 }, +{ 98, 3, 6, 1, 2, 1 }, +{ 98, 4, 357, 51, 52, 51 }, +{ 98, 5, 358, 51, 52, 51 }, +{ 98, 6, 359, 51, 52, 51 }, +{ 98, 0, 360, 52, 52, 51 }, +{ 98, 1, 361, 52, 53, 52 }, +{ 98, 2, 362, 52, 53, 52 }, +{ 98, 3, 363, 52, 53, 52 }, +{ 98, 4, 364, 52, 53, 52 }, +{ 99, 5, 0, 0, 53, 0 }, +{ 99, 6, 1, 0, 53, 0 }, +{ 99, 0, 2, 1, 53, 0 }, +{ 99, 1, 3, 1, 1, 1 }, +{ 99, 2, 4, 1, 1, 1 }, +{ 99, 3, 5, 1, 1, 1 }, +{ 99, 4, 6, 1, 1, 1 }, +{ 99, 5, 357, 51, 51, 51 }, +{ 99, 6, 358, 51, 51, 51 }, +{ 99, 0, 359, 52, 51, 51 }, +{ 99, 1, 360, 52, 52, 52 }, +{ 99, 2, 361, 52, 52, 52 }, +{ 99, 3, 362, 52, 52, 52 }, +{ 99, 4, 363, 52, 52, 52 }, +{ 99, 5, 364, 52, 52, 52 }, +{ 100, 6, 0, 0, 52, 0 }, +{ 100, 0, 1, 1, 52, 0 }, +{ 100, 1, 2, 1, 1, 1 }, +{ 100, 2, 3, 1, 1, 1 }, +{ 100, 3, 4, 1, 1, 1 }, +{ 100, 4, 5, 1, 1, 1 }, +{ 100, 5, 6, 1, 1, 1 }, +{ 100, 0, 358, 52, 51, 51 }, +{ 100, 1, 359, 52, 52, 52 }, +{ 100, 2, 360, 52, 52, 52 }, +{ 100, 3, 361, 52, 52, 52 }, +{ 100, 4, 362, 52, 52, 52 }, +{ 100, 5, 363, 52, 52, 52 }, +{ 100, 6, 364, 52, 52, 52 }, +{ 100, 0, 365, 53, 52, 52 }, +{ 101, 1, 0, 0, 1, 1 }, +{ 101, 2, 1, 0, 1, 1 }, +{ 101, 3, 2, 0, 1, 1 }, +{ 101, 4, 3, 0, 1, 1 }, +{ 101, 5, 4, 0, 1, 1 }, +{ 101, 6, 5, 0, 1, 1 }, +{ 101, 0, 6, 1, 1, 1 }, +{ 101, 1, 357, 51, 52, 52 }, +{ 101, 2, 358, 51, 52, 52 }, +{ 101, 3, 359, 51, 52, 52 }, +{ 101, 4, 360, 51, 52, 52 }, +{ 101, 5, 361, 51, 52, 52 }, +{ 101, 6, 362, 51, 52, 52 }, +{ 101, 0, 363, 52, 52, 52 }, +{ 101, 1, 364, 52, 1, 53 }, +{ 102, 2, 0, 0, 1, 0 }, +{ 102, 3, 1, 0, 1, 0 }, +{ 102, 4, 2, 0, 1, 0 }, +{ 102, 5, 3, 0, 1, 0 }, +{ 102, 6, 4, 0, 1, 0 }, +{ 102, 0, 5, 1, 1, 0 }, +{ 102, 1, 6, 1, 2, 1 }, +{ 102, 2, 357, 51, 52, 51 }, +{ 102, 3, 358, 51, 52, 51 }, +{ 102, 4, 359, 51, 52, 51 }, +{ 102, 5, 360, 51, 52, 51 }, +{ 102, 6, 361, 51, 52, 51 }, +{ 102, 0, 362, 52, 52, 51 }, +{ 102, 1, 363, 52, 1, 52 }, +{ 102, 2, 364, 52, 1, 52 }, +{ 103, 3, 0, 0, 1, 0 }, +{ 103, 4, 1, 0, 1, 0 }, +{ 103, 5, 2, 0, 1, 0 }, +{ 103, 6, 3, 0, 1, 0 }, +{ 103, 0, 4, 1, 1, 0 }, +{ 103, 1, 5, 1, 2, 1 }, +{ 103, 2, 6, 1, 2, 1 }, +{ 103, 3, 357, 51, 52, 51 }, +{ 103, 4, 358, 51, 52, 51 }, +{ 103, 5, 359, 51, 52, 51 }, +{ 103, 6, 360, 51, 52, 51 }, +{ 103, 0, 361, 52, 52, 51 }, +{ 103, 1, 362, 52, 1, 52 }, +{ 103, 2, 363, 52, 1, 52 }, +{ 103, 3, 364, 52, 1, 52 }, +{ 104, 4, 0, 0, 1, 0 }, +{ 104, 5, 1, 0, 1, 0 }, +{ 104, 6, 2, 0, 1, 0 }, +{ 104, 0, 3, 1, 1, 0 }, +{ 104, 1, 4, 1, 2, 1 }, +{ 104, 2, 5, 1, 2, 1 }, +{ 104, 3, 6, 1, 2, 1 }, +{ 104, 5, 358, 51, 52, 51 }, +{ 104, 6, 359, 51, 52, 51 }, +{ 104, 0, 360, 52, 52, 51 }, +{ 104, 1, 361, 52, 53, 52 }, +{ 104, 2, 362, 52, 53, 52 }, +{ 104, 3, 363, 52, 53, 52 }, +{ 104, 4, 364, 52, 53, 52 }, +{ 104, 5, 365, 52, 53, 52 }, +{ 105, 6, 0, 0, 53, 0 }, +{ 105, 0, 1, 1, 53, 0 }, +{ 105, 1, 2, 1, 1, 1 }, +{ 105, 2, 3, 1, 1, 1 }, +{ 105, 3, 4, 1, 1, 1 }, +{ 105, 4, 5, 1, 1, 1 }, +{ 105, 5, 6, 1, 1, 1 }, +{ 105, 6, 357, 51, 51, 51 }, +{ 105, 0, 358, 52, 51, 51 }, +{ 105, 1, 359, 52, 52, 52 }, +{ 105, 2, 360, 52, 52, 52 }, +{ 105, 3, 361, 52, 52, 52 }, +{ 105, 4, 362, 52, 52, 52 }, +{ 105, 5, 363, 52, 52, 52 }, +{ 105, 6, 364, 52, 52, 52 }, +{ 106, 0, 0, 1, 52, 0 }, +{ 106, 1, 1, 1, 1, 1 }, +{ 106, 2, 2, 1, 1, 1 }, +{ 106, 3, 3, 1, 1, 1 }, +{ 106, 4, 4, 1, 1, 1 }, +{ 106, 5, 5, 1, 1, 1 }, +{ 106, 6, 6, 1, 1, 1 }, +{ 106, 0, 357, 52, 51, 51 }, +{ 106, 1, 358, 52, 52, 52 }, +{ 106, 2, 359, 52, 52, 52 }, +{ 106, 3, 360, 52, 52, 52 }, +{ 106, 4, 361, 52, 52, 52 }, +{ 106, 5, 362, 52, 52, 52 }, +{ 106, 6, 363, 52, 52, 52 }, +{ 106, 0, 364, 53, 52, 52 }, +{ 107, 1, 0, 0, 1, 1 }, +{ 107, 2, 1, 0, 1, 1 }, +{ 107, 3, 2, 0, 1, 1 }, +{ 107, 4, 3, 0, 1, 1 }, +{ 107, 5, 4, 0, 1, 1 }, +{ 107, 6, 5, 0, 1, 1 }, +{ 107, 0, 6, 1, 1, 1 }, +{ 107, 1, 357, 51, 52, 52 }, +{ 107, 2, 358, 51, 52, 52 }, +{ 107, 3, 359, 51, 52, 52 }, +{ 107, 4, 360, 51, 52, 52 }, +{ 107, 5, 361, 51, 52, 52 }, +{ 107, 6, 362, 51, 52, 52 }, +{ 107, 0, 363, 52, 52, 52 }, +{ 107, 1, 364, 52, 1, 53 }, +{ 108, 2, 0, 0, 1, 0 }, +{ 108, 3, 1, 0, 1, 0 }, +{ 108, 4, 2, 0, 1, 0 }, +{ 108, 5, 3, 0, 1, 0 }, +{ 108, 6, 4, 0, 1, 0 }, +{ 108, 0, 5, 1, 1, 0 }, +{ 108, 1, 6, 1, 2, 1 }, +{ 108, 3, 358, 51, 52, 51 }, +{ 108, 4, 359, 51, 52, 51 }, +{ 108, 5, 360, 51, 52, 51 }, +{ 108, 6, 361, 51, 52, 51 }, +{ 108, 0, 362, 52, 52, 51 }, +{ 108, 1, 363, 52, 1, 52 }, +{ 108, 2, 364, 52, 1, 52 }, +{ 108, 3, 365, 52, 1, 52 }, +{ 109, 4, 0, 0, 1, 0 }, +{ 109, 5, 1, 0, 1, 0 }, +{ 109, 6, 2, 0, 1, 0 }, +{ 109, 0, 3, 1, 1, 0 }, +{ 109, 1, 4, 1, 2, 1 }, +{ 109, 2, 5, 1, 2, 1 }, +{ 109, 3, 6, 1, 2, 1 }, +{ 109, 4, 357, 51, 52, 51 }, +{ 109, 5, 358, 51, 52, 51 }, +{ 109, 6, 359, 51, 52, 51 }, +{ 109, 0, 360, 52, 52, 51 }, +{ 109, 1, 361, 52, 53, 52 }, +{ 109, 2, 362, 52, 53, 52 }, +{ 109, 3, 363, 52, 53, 52 }, +{ 109, 4, 364, 52, 53, 52 }, +{ 110, 5, 0, 0, 53, 0 }, +{ 110, 6, 1, 0, 53, 0 }, +{ 110, 0, 2, 1, 53, 0 }, +{ 110, 1, 3, 1, 1, 1 }, +{ 110, 2, 4, 1, 1, 1 }, +{ 110, 3, 5, 1, 1, 1 }, +{ 110, 4, 6, 1, 1, 1 }, +{ 110, 5, 357, 51, 51, 51 }, +{ 110, 6, 358, 51, 51, 51 }, +{ 110, 0, 359, 52, 51, 51 }, +{ 110, 1, 360, 52, 52, 52 }, +{ 110, 2, 361, 52, 52, 52 }, +{ 110, 3, 362, 52, 52, 52 }, +{ 110, 4, 363, 52, 52, 52 }, +{ 110, 5, 364, 52, 52, 52 }, +{ 111, 6, 0, 0, 52, 0 }, +{ 111, 0, 1, 1, 52, 0 }, +{ 111, 1, 2, 1, 1, 1 }, +{ 111, 2, 3, 1, 1, 1 }, +{ 111, 3, 4, 1, 1, 1 }, +{ 111, 4, 5, 1, 1, 1 }, +{ 111, 5, 6, 1, 1, 1 }, +{ 111, 6, 357, 51, 51, 51 }, +{ 111, 0, 358, 52, 51, 51 }, +{ 111, 1, 359, 52, 52, 52 }, +{ 111, 2, 360, 52, 52, 52 }, +{ 111, 3, 361, 52, 52, 52 }, +{ 111, 4, 362, 52, 52, 52 }, +{ 111, 5, 363, 52, 52, 52 }, +{ 111, 6, 364, 52, 52, 52 }, +{ 112, 0, 0, 1, 52, 0 }, +{ 112, 1, 1, 1, 1, 1 }, +{ 112, 2, 2, 1, 1, 1 }, +{ 112, 3, 3, 1, 1, 1 }, +{ 112, 4, 4, 1, 1, 1 }, +{ 112, 5, 5, 1, 1, 1 }, +{ 112, 6, 6, 1, 1, 1 }, +{ 112, 1, 358, 52, 52, 52 }, +{ 112, 2, 359, 52, 52, 52 }, +{ 112, 3, 360, 52, 52, 52 }, +{ 112, 4, 361, 52, 52, 52 }, +{ 112, 5, 362, 52, 52, 52 }, +{ 112, 6, 363, 52, 52, 52 }, +{ 112, 0, 364, 53, 52, 52 }, +{ 112, 1, 365, 53, 1, 53 }, +{ 113, 2, 0, 0, 1, 0 }, +{ 113, 3, 1, 0, 1, 0 }, +{ 113, 4, 2, 0, 1, 0 }, +{ 113, 5, 3, 0, 1, 0 }, +{ 113, 6, 4, 0, 1, 0 }, +{ 113, 0, 5, 1, 1, 0 }, +{ 113, 1, 6, 1, 2, 1 }, +{ 113, 2, 357, 51, 52, 51 }, +{ 113, 3, 358, 51, 52, 51 }, +{ 113, 4, 359, 51, 52, 51 }, +{ 113, 5, 360, 51, 52, 51 }, +{ 113, 6, 361, 51, 52, 51 }, +{ 113, 0, 362, 52, 52, 51 }, +{ 113, 1, 363, 52, 1, 52 }, +{ 113, 2, 364, 52, 1, 52 }, +{ 114, 3, 0, 0, 1, 0 }, +{ 114, 4, 1, 0, 1, 0 }, +{ 114, 5, 2, 0, 1, 0 }, +{ 114, 6, 3, 0, 1, 0 }, +{ 114, 0, 4, 1, 1, 0 }, +{ 114, 1, 5, 1, 2, 1 }, +{ 114, 2, 6, 1, 2, 1 }, +{ 114, 3, 357, 51, 52, 51 }, +{ 114, 4, 358, 51, 52, 51 }, +{ 114, 5, 359, 51, 52, 51 }, +{ 114, 6, 360, 51, 52, 51 }, +{ 114, 0, 361, 52, 52, 51 }, +{ 114, 1, 362, 52, 1, 52 }, +{ 114, 2, 363, 52, 1, 52 }, +{ 114, 3, 364, 52, 1, 52 }, +{ 115, 4, 0, 0, 1, 0 }, +{ 115, 5, 1, 0, 1, 0 }, +{ 115, 6, 2, 0, 1, 0 }, +{ 115, 0, 3, 1, 1, 0 }, +{ 115, 1, 4, 1, 2, 1 }, +{ 115, 2, 5, 1, 2, 1 }, +{ 115, 3, 6, 1, 2, 1 }, +{ 115, 4, 357, 51, 52, 51 }, +{ 115, 5, 358, 51, 52, 51 }, +{ 115, 6, 359, 51, 52, 51 }, +{ 115, 0, 360, 52, 52, 51 }, +{ 115, 1, 361, 52, 53, 52 }, +{ 115, 2, 362, 52, 53, 52 }, +{ 115, 3, 363, 52, 53, 52 }, +{ 115, 4, 364, 52, 53, 52 }, +{ 116, 5, 0, 0, 53, 0 }, +{ 116, 6, 1, 0, 53, 0 }, +{ 116, 0, 2, 1, 53, 0 }, +{ 116, 1, 3, 1, 1, 1 }, +{ 116, 2, 4, 1, 1, 1 }, +{ 116, 3, 5, 1, 1, 1 }, +{ 116, 4, 6, 1, 1, 1 }, +{ 116, 6, 358, 51, 51, 51 }, +{ 116, 0, 359, 52, 51, 51 }, +{ 116, 1, 360, 52, 52, 52 }, +{ 116, 2, 361, 52, 52, 52 }, +{ 116, 3, 362, 52, 52, 52 }, +{ 116, 4, 363, 52, 52, 52 }, +{ 116, 5, 364, 52, 52, 52 }, +{ 116, 6, 365, 52, 52, 52 }, +{ 117, 0, 0, 1, 52, 0 }, +{ 117, 1, 1, 1, 1, 1 }, +{ 117, 2, 2, 1, 1, 1 }, +{ 117, 3, 3, 1, 1, 1 }, +{ 117, 4, 4, 1, 1, 1 }, +{ 117, 5, 5, 1, 1, 1 }, +{ 117, 6, 6, 1, 1, 1 }, +{ 117, 0, 357, 52, 51, 51 }, +{ 117, 1, 358, 52, 52, 52 }, +{ 117, 2, 359, 52, 52, 52 }, +{ 117, 3, 360, 52, 52, 52 }, +{ 117, 4, 361, 52, 52, 52 }, +{ 117, 5, 362, 52, 52, 52 }, +{ 117, 6, 363, 52, 52, 52 }, +{ 117, 0, 364, 53, 52, 52 }, +{ 118, 1, 0, 0, 1, 1 }, +{ 118, 2, 1, 0, 1, 1 }, +{ 118, 3, 2, 0, 1, 1 }, +{ 118, 4, 3, 0, 1, 1 }, +{ 118, 5, 4, 0, 1, 1 }, +{ 118, 6, 5, 0, 1, 1 }, +{ 118, 0, 6, 1, 1, 1 }, +{ 118, 1, 357, 51, 52, 52 }, +{ 118, 2, 358, 51, 52, 52 }, +{ 118, 3, 359, 51, 52, 52 }, +{ 118, 4, 360, 51, 52, 52 }, +{ 118, 5, 361, 51, 52, 52 }, +{ 118, 6, 362, 51, 52, 52 }, +{ 118, 0, 363, 52, 52, 52 }, +{ 118, 1, 364, 52, 1, 53 }, +{ 119, 2, 0, 0, 1, 0 }, +{ 119, 3, 1, 0, 1, 0 }, +{ 119, 4, 2, 0, 1, 0 }, +{ 119, 5, 3, 0, 1, 0 }, +{ 119, 6, 4, 0, 1, 0 }, +{ 119, 0, 5, 1, 1, 0 }, +{ 119, 1, 6, 1, 2, 1 }, +{ 119, 2, 357, 51, 52, 51 }, +{ 119, 3, 358, 51, 52, 51 }, +{ 119, 4, 359, 51, 52, 51 }, +{ 119, 5, 360, 51, 52, 51 }, +{ 119, 6, 361, 51, 52, 51 }, +{ 119, 0, 362, 52, 52, 51 }, +{ 119, 1, 363, 52, 1, 52 }, +{ 119, 2, 364, 52, 1, 52 }, +{ 120, 3, 0, 0, 1, 0 }, +{ 120, 4, 1, 0, 1, 0 }, +{ 120, 5, 2, 0, 1, 0 }, +{ 120, 6, 3, 0, 1, 0 }, +{ 120, 0, 4, 1, 1, 0 }, +{ 120, 1, 5, 1, 2, 1 }, +{ 120, 2, 6, 1, 2, 1 }, +{ 120, 4, 358, 51, 52, 51 }, +{ 120, 5, 359, 51, 52, 51 }, +{ 120, 6, 360, 51, 52, 51 }, +{ 120, 0, 361, 52, 52, 51 }, +{ 120, 1, 362, 52, 53, 52 }, +{ 120, 2, 363, 52, 53, 52 }, +{ 120, 3, 364, 52, 53, 52 }, +{ 120, 4, 365, 52, 53, 52 }, +{ 121, 5, 0, 0, 53, 0 }, +{ 121, 6, 1, 0, 53, 0 }, +{ 121, 0, 2, 1, 53, 0 }, +{ 121, 1, 3, 1, 1, 1 }, +{ 121, 2, 4, 1, 1, 1 }, +{ 121, 3, 5, 1, 1, 1 }, +{ 121, 4, 6, 1, 1, 1 }, +{ 121, 5, 357, 51, 51, 51 }, +{ 121, 6, 358, 51, 51, 51 }, +{ 121, 0, 359, 52, 51, 51 }, +{ 121, 1, 360, 52, 52, 52 }, +{ 121, 2, 361, 52, 52, 52 }, +{ 121, 3, 362, 52, 52, 52 }, +{ 121, 4, 363, 52, 52, 52 }, +{ 121, 5, 364, 52, 52, 52 }, +{ 122, 6, 0, 0, 52, 0 }, +{ 122, 0, 1, 1, 52, 0 }, +{ 122, 1, 2, 1, 1, 1 }, +{ 122, 2, 3, 1, 1, 1 }, +{ 122, 3, 4, 1, 1, 1 }, +{ 122, 4, 5, 1, 1, 1 }, +{ 122, 5, 6, 1, 1, 1 }, +{ 122, 6, 357, 51, 51, 51 }, +{ 122, 0, 358, 52, 51, 51 }, +{ 122, 1, 359, 52, 52, 52 }, +{ 122, 2, 360, 52, 52, 52 }, +{ 122, 3, 361, 52, 52, 52 }, +{ 122, 4, 362, 52, 52, 52 }, +{ 122, 5, 363, 52, 52, 52 }, +{ 122, 6, 364, 52, 52, 52 }, +{ 123, 0, 0, 1, 52, 0 }, +{ 123, 1, 1, 1, 1, 1 }, +{ 123, 2, 2, 1, 1, 1 }, +{ 123, 3, 3, 1, 1, 1 }, +{ 123, 4, 4, 1, 1, 1 }, +{ 123, 5, 5, 1, 1, 1 }, +{ 123, 6, 6, 1, 1, 1 }, +{ 123, 0, 357, 52, 51, 51 }, +{ 123, 1, 358, 52, 52, 52 }, +{ 123, 2, 359, 52, 52, 52 }, +{ 123, 3, 360, 52, 52, 52 }, +{ 123, 4, 361, 52, 52, 52 }, +{ 123, 5, 362, 52, 52, 52 }, +{ 123, 6, 363, 52, 52, 52 }, +{ 123, 0, 364, 53, 52, 52 }, +{ 124, 1, 0, 0, 1, 1 }, +{ 124, 2, 1, 0, 1, 1 }, +{ 124, 3, 2, 0, 1, 1 }, +{ 124, 4, 3, 0, 1, 1 }, +{ 124, 5, 4, 0, 1, 1 }, +{ 124, 6, 5, 0, 1, 1 }, +{ 124, 0, 6, 1, 1, 1 }, +{ 124, 2, 358, 51, 52, 52 }, +{ 124, 3, 359, 51, 52, 52 }, +{ 124, 4, 360, 51, 52, 52 }, +{ 124, 5, 361, 51, 52, 52 }, +{ 124, 6, 362, 51, 52, 52 }, +{ 124, 0, 363, 52, 52, 52 }, +{ 124, 1, 364, 52, 1, 53 }, +{ 124, 2, 365, 52, 1, 53 }, +{ 125, 3, 0, 0, 1, 0 }, +{ 125, 4, 1, 0, 1, 0 }, +{ 125, 5, 2, 0, 1, 0 }, +{ 125, 6, 3, 0, 1, 0 }, +{ 125, 0, 4, 1, 1, 0 }, +{ 125, 1, 5, 1, 2, 1 }, +{ 125, 2, 6, 1, 2, 1 }, +{ 125, 3, 357, 51, 52, 51 }, +{ 125, 4, 358, 51, 52, 51 }, +{ 125, 5, 359, 51, 52, 51 }, +{ 125, 6, 360, 51, 52, 51 }, +{ 125, 0, 361, 52, 52, 51 }, +{ 125, 1, 362, 52, 1, 52 }, +{ 125, 2, 363, 52, 1, 52 }, +{ 125, 3, 364, 52, 1, 52 }, +{ 126, 4, 0, 0, 1, 0 }, +{ 126, 5, 1, 0, 1, 0 }, +{ 126, 6, 2, 0, 1, 0 }, +{ 126, 0, 3, 1, 1, 0 }, +{ 126, 1, 4, 1, 2, 1 }, +{ 126, 2, 5, 1, 2, 1 }, +{ 126, 3, 6, 1, 2, 1 }, +{ 126, 4, 357, 51, 52, 51 }, +{ 126, 5, 358, 51, 52, 51 }, +{ 126, 6, 359, 51, 52, 51 }, +{ 126, 0, 360, 52, 52, 51 }, +{ 126, 1, 361, 52, 53, 52 }, +{ 126, 2, 362, 52, 53, 52 }, +{ 126, 3, 363, 52, 53, 52 }, +{ 126, 4, 364, 52, 53, 52 }, +{ 127, 5, 0, 0, 53, 0 }, +{ 127, 6, 1, 0, 53, 0 }, +{ 127, 0, 2, 1, 53, 0 }, +{ 127, 1, 3, 1, 1, 1 }, +{ 127, 2, 4, 1, 1, 1 }, +{ 127, 3, 5, 1, 1, 1 }, +{ 127, 4, 6, 1, 1, 1 }, +{ 127, 5, 357, 51, 51, 51 }, +{ 127, 6, 358, 51, 51, 51 }, +{ 127, 0, 359, 52, 51, 51 }, +{ 127, 1, 360, 52, 52, 52 }, +{ 127, 2, 361, 52, 52, 52 }, +{ 127, 3, 362, 52, 52, 52 }, +{ 127, 4, 363, 52, 52, 52 }, +{ 127, 5, 364, 52, 52, 52 }, +{ 128, 6, 0, 0, 52, 0 }, +{ 128, 0, 1, 1, 52, 0 }, +{ 128, 1, 2, 1, 1, 1 }, +{ 128, 2, 3, 1, 1, 1 }, +{ 128, 3, 4, 1, 1, 1 }, +{ 128, 4, 5, 1, 1, 1 }, +{ 128, 5, 6, 1, 1, 1 }, +{ 128, 0, 358, 52, 51, 51 }, +{ 128, 1, 359, 52, 52, 52 }, +{ 128, 2, 360, 52, 52, 52 }, +{ 128, 3, 361, 52, 52, 52 }, +{ 128, 4, 362, 52, 52, 52 }, +{ 128, 5, 363, 52, 52, 52 }, +{ 128, 6, 364, 52, 52, 52 }, +{ 128, 0, 365, 53, 52, 52 }, +{ 129, 1, 0, 0, 1, 1 }, +{ 129, 2, 1, 0, 1, 1 }, +{ 129, 3, 2, 0, 1, 1 }, +{ 129, 4, 3, 0, 1, 1 }, +{ 129, 5, 4, 0, 1, 1 }, +{ 129, 6, 5, 0, 1, 1 }, +{ 129, 0, 6, 1, 1, 1 }, +{ 129, 1, 357, 51, 52, 52 }, +{ 129, 2, 358, 51, 52, 52 }, +{ 129, 3, 359, 51, 52, 52 }, +{ 129, 4, 360, 51, 52, 52 }, +{ 129, 5, 361, 51, 52, 52 }, +{ 129, 6, 362, 51, 52, 52 }, +{ 129, 0, 363, 52, 52, 52 }, +{ 129, 1, 364, 52, 1, 53 }, +{ 130, 2, 0, 0, 1, 0 }, +{ 130, 3, 1, 0, 1, 0 }, +{ 130, 4, 2, 0, 1, 0 }, +{ 130, 5, 3, 0, 1, 0 }, +{ 130, 6, 4, 0, 1, 0 }, +{ 130, 0, 5, 1, 1, 0 }, +{ 130, 1, 6, 1, 2, 1 }, +{ 130, 2, 357, 51, 52, 51 }, +{ 130, 3, 358, 51, 52, 51 }, +{ 130, 4, 359, 51, 52, 51 }, +{ 130, 5, 360, 51, 52, 51 }, +{ 130, 6, 361, 51, 52, 51 }, +{ 130, 0, 362, 52, 52, 51 }, +{ 130, 1, 363, 52, 1, 52 }, +{ 130, 2, 364, 52, 1, 52 }, +{ 131, 3, 0, 0, 1, 0 }, +{ 131, 4, 1, 0, 1, 0 }, +{ 131, 5, 2, 0, 1, 0 }, +{ 131, 6, 3, 0, 1, 0 }, +{ 131, 0, 4, 1, 1, 0 }, +{ 131, 1, 5, 1, 2, 1 }, +{ 131, 2, 6, 1, 2, 1 }, +{ 131, 3, 357, 51, 52, 51 }, +{ 131, 4, 358, 51, 52, 51 }, +{ 131, 5, 359, 51, 52, 51 }, +{ 131, 6, 360, 51, 52, 51 }, +{ 131, 0, 361, 52, 52, 51 }, +{ 131, 1, 362, 52, 1, 52 }, +{ 131, 2, 363, 52, 1, 52 }, +{ 131, 3, 364, 52, 1, 52 }, +{ 132, 4, 0, 0, 1, 0 }, +{ 132, 5, 1, 0, 1, 0 }, +{ 132, 6, 2, 0, 1, 0 }, +{ 132, 0, 3, 1, 1, 0 }, +{ 132, 1, 4, 1, 2, 1 }, +{ 132, 2, 5, 1, 2, 1 }, +{ 132, 3, 6, 1, 2, 1 }, +{ 132, 5, 358, 51, 52, 51 }, +{ 132, 6, 359, 51, 52, 51 }, +{ 132, 0, 360, 52, 52, 51 }, +{ 132, 1, 361, 52, 53, 52 }, +{ 132, 2, 362, 52, 53, 52 }, +{ 132, 3, 363, 52, 53, 52 }, +{ 132, 4, 364, 52, 53, 52 }, +{ 132, 5, 365, 52, 53, 52 }, +{ 133, 6, 0, 0, 53, 0 }, +{ 133, 0, 1, 1, 53, 0 }, +{ 133, 1, 2, 1, 1, 1 }, +{ 133, 2, 3, 1, 1, 1 }, +{ 133, 3, 4, 1, 1, 1 }, +{ 133, 4, 5, 1, 1, 1 }, +{ 133, 5, 6, 1, 1, 1 }, +{ 133, 6, 357, 51, 51, 51 }, +{ 133, 0, 358, 52, 51, 51 }, +{ 133, 1, 359, 52, 52, 52 }, +{ 133, 2, 360, 52, 52, 52 }, +{ 133, 3, 361, 52, 52, 52 }, +{ 133, 4, 362, 52, 52, 52 }, +{ 133, 5, 363, 52, 52, 52 }, +{ 133, 6, 364, 52, 52, 52 }, +{ 134, 0, 0, 1, 52, 0 }, +{ 134, 1, 1, 1, 1, 1 }, +{ 134, 2, 2, 1, 1, 1 }, +{ 134, 3, 3, 1, 1, 1 }, +{ 134, 4, 4, 1, 1, 1 }, +{ 134, 5, 5, 1, 1, 1 }, +{ 134, 6, 6, 1, 1, 1 }, +{ 134, 0, 357, 52, 51, 51 }, +{ 134, 1, 358, 52, 52, 52 }, +{ 134, 2, 359, 52, 52, 52 }, +{ 134, 3, 360, 52, 52, 52 }, +{ 134, 4, 361, 52, 52, 52 }, +{ 134, 5, 362, 52, 52, 52 }, +{ 134, 6, 363, 52, 52, 52 }, +{ 134, 0, 364, 53, 52, 52 }, +{ 135, 1, 0, 0, 1, 1 }, +{ 135, 2, 1, 0, 1, 1 }, +{ 135, 3, 2, 0, 1, 1 }, +{ 135, 4, 3, 0, 1, 1 }, +{ 135, 5, 4, 0, 1, 1 }, +{ 135, 6, 5, 0, 1, 1 }, +{ 135, 0, 6, 1, 1, 1 }, +{ 135, 1, 357, 51, 52, 52 }, +{ 135, 2, 358, 51, 52, 52 }, +{ 135, 3, 359, 51, 52, 52 }, +{ 135, 4, 360, 51, 52, 52 }, +{ 135, 5, 361, 51, 52, 52 }, +{ 135, 6, 362, 51, 52, 52 }, +{ 135, 0, 363, 52, 52, 52 }, +{ 135, 1, 364, 52, 1, 53 }, +{ 136, 2, 0, 0, 1, 0 }, +{ 136, 3, 1, 0, 1, 0 }, +{ 136, 4, 2, 0, 1, 0 }, +{ 136, 5, 3, 0, 1, 0 }, +{ 136, 6, 4, 0, 1, 0 }, +{ 136, 0, 5, 1, 1, 0 }, +{ 136, 1, 6, 1, 2, 1 }, +{ 136, 3, 358, 51, 52, 51 }, +{ 136, 4, 359, 51, 52, 51 }, +{ 136, 5, 360, 51, 52, 51 }, +{ 136, 6, 361, 51, 52, 51 }, +{ 136, 0, 362, 52, 52, 51 }, +{ 136, 1, 363, 52, 1, 52 }, +{ 136, 2, 364, 52, 1, 52 }, +{ 136, 3, 365, 52, 1, 52 }, +{ 137, 4, 0, 0, 1, 0 }, +{ 137, 5, 1, 0, 1, 0 }, +{ 137, 6, 2, 0, 1, 0 }, +{ 137, 0, 3, 1, 1, 0 }, +{ 137, 1, 4, 1, 2, 1 }, +{ 137, 2, 5, 1, 2, 1 }, +{ 137, 3, 6, 1, 2, 1 }, +{ 137, 4, 357, 51, 52, 51 }, +{ 137, 5, 358, 51, 52, 51 }, +{ 137, 6, 359, 51, 52, 51 }, +{ 137, 0, 360, 52, 52, 51 }, +{ 137, 1, 361, 52, 53, 52 }, +{ 137, 2, 362, 52, 53, 52 }, +{ 137, 3, 363, 52, 53, 52 }, +{ 137, 4, 364, 52, 53, 52 }, +}; + +static int test_week_calc( void ) +{ + char buffer[100]; + int rc = 1; + int i; + for ( i = 0; i < 1020; ++i ) + { + struct tm t; + int U, V, W; + t.tm_year = data[i][0]; + t.tm_wday = data[i][1]; + t.tm_yday = data[i][2]; + assert( strftime( buffer, 100, "%U %V %W", &t ) == 8 ); + assert( sscanf( buffer, "%d %d %d", &U, &V, &W ) == 3 ); + if ( data[i][3] != U || data[i][4] != V || data[i][5] != W ) + { + printf( "Fehler in { %d, %d, %d, %d, %d, %d } (encountered { %d, %d, %d })\n", data[i][0], data[i][1], data[i][2], data[i][3], data[i][4], data[i][5], U, V, W ); + rc = 0; + } + } + return rc; +} + +START_TEST( strftime ) +{ + char buffer[100]; + /* Basic functionality */ + struct tm timeptr; + MKTIME( timeptr, 59, 30, 12, 1, 9, 72, 0, 274 ); + + TESTCASE( + (strftime(buffer, 100, "%a ", &timeptr) == 4) && + (strcmp(buffer, "Sun ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%A ", &timeptr) == 7) && + (strcmp(buffer, "Sunday ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%b ", &timeptr) == 4) && + (strcmp(buffer, "Oct ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%h ", &timeptr) == 4) && + (strcmp(buffer, "Oct ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%B ", &timeptr) == 8) && + (strcmp(buffer, "October ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%c ", &timeptr) == 25) && + (strcmp(buffer, "Sun Oct 1 12:30:59 1972 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%C ", &timeptr) == 3) && + (strcmp(buffer, "19 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%d ", &timeptr) == 3) && + (strcmp(buffer, "01 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%D ", &timeptr) == 9) && + (strcmp(buffer, "10/01/72 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%e ", &timeptr) == 3) && + (strcmp(buffer, " 1 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%F ", &timeptr) == 11) && + (strcmp(buffer, "1972-10-01 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%H ", &timeptr) == 3) && + (strcmp(buffer, "12 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%I ", &timeptr) == 3) && + (strcmp(buffer, "12 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%j ", &timeptr) == 4) && + (strcmp(buffer, "275 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%m ", &timeptr) == 3) && + (strcmp(buffer, "10 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%M ", &timeptr) == 3) && + (strcmp(buffer, "30 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%p ", &timeptr) == 3) && + (strcmp(buffer, "PM ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%r ", &timeptr) == 12) && + (strcmp(buffer, "12:30:59 PM ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%R ", &timeptr) == 6) && + (strcmp(buffer, "12:30 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%S ", &timeptr) == 3) && + (strcmp(buffer, "59 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%T ", &timeptr) == 9) && + (strcmp(buffer, "12:30:59 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%u ", &timeptr) == 2) && + (strcmp(buffer, "7 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%w ", &timeptr) == 2) && + (strcmp(buffer, "0 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%x ", &timeptr) == 9) && + (strcmp(buffer, "10/01/72 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%X ", &timeptr) == 9) && + (strcmp(buffer, "12:30:59 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%y ", &timeptr) == 3) && + (strcmp(buffer, "72 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%Y ", &timeptr) == 5) && + (strcmp(buffer, "1972 ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%% ", &timeptr) == 2) && + (strcmp(buffer, "% ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%n ", &timeptr) == 2) && + (strcmp(buffer, "\n ") == 0) ); + TESTCASE( + (strftime(buffer, 100, "%t ", &timeptr) == 2) && + (strcmp(buffer, "\t ") == 0) ); + + TESTCASE( test_week_calc() ); +} +END_TEST + +START_SUITE( time ) +{ + RUN_TEST( strftime ); +} +END_SUITE diff --git a/src/libraries/libc/tests/testfile.txt b/src/libraries/libc/tests/testfile.txt new file mode 100644 index 0000000000000000000000000000000000000000..f08c4d3b87ef22e66285ea7553db8181555b0561 GIT binary patch literal 532 zcmZWl$x_2G4DA(vg)TXHJR{4JWq%99fz7OC9ia5*i<~qio$8?FEs~Q+i6v{P7*{EX z*HzsSFk~ozy9j3>!vKITAb5ljseq59;7J5RBFzT$Vo(-I;sQlhE-AAyhu&zW$~7xh z(^R6AlrZHYtSSXfd>nNb zi7|3=bgZ?$v-_5@-_ezG?Ds8I&Ue!8_UtjLeJdu*Vay)2$HZ7;hfK0W=E*#Bm}oJX ycP5UiU7l7Qp*9?DoDi2+*EhHA?mj#`K0U8rUf<5&KR$=A@1Kj!@3#Mgt@{J5?0f0} literal 0 HcmV?d00001 diff --git a/src/libraries/libc/time/asctime.c b/src/libraries/libc/time/asctime.c new file mode 100644 index 0000000..382f6ff --- /dev/null +++ b/src/libraries/libc/time/asctime.c @@ -0,0 +1,12 @@ +/* asctime( const struct tm * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * asctime( const struct tm * timeptr ) +{ + return NULL; +} diff --git a/src/libraries/libc/time/clock.c b/src/libraries/libc/time/clock.c new file mode 100644 index 0000000..c911aa5 --- /dev/null +++ b/src/libraries/libc/time/clock.c @@ -0,0 +1,12 @@ +/* clock( void ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +clock_t clock( void ) +{ + return -1; +} diff --git a/src/libraries/libc/time/ctime.c b/src/libraries/libc/time/ctime.c new file mode 100644 index 0000000..cd47230 --- /dev/null +++ b/src/libraries/libc/time/ctime.c @@ -0,0 +1,12 @@ +/* ctime( const time_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +char * ctime( const time_t * timer ) +{ + return NULL; +} diff --git a/src/libraries/libc/time/difftime.c b/src/libraries/libc/time/difftime.c new file mode 100644 index 0000000..21149ab --- /dev/null +++ b/src/libraries/libc/time/difftime.c @@ -0,0 +1,56 @@ +/* difftime( time_t, time_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +#ifndef DISABLE_SSE +double difftime( time_t time1, time_t time0 ) +{ + /* If we want to avoid rounding errors and overflows, we need to be + careful with the exact type of time_t being unknown to us. + The code below is based on tzcode's difftime.c, which is in the + public domain, so clarified as of 1996-06-05 by Arthur David Olson. + */ + + /* If double is large enough, simply covert and substract + (assuming that the larger type has more precision). + */ + if ( sizeof( time_t ) < sizeof( double ) ) + { + return (double)time1 - (double)time0; + } + + /* The difference of two unsigned values cannot overflow if the + minuend is greater or equal to the subtrahend. + */ + if ( ! _PDCLIB_TYPE_SIGNED( time_t ) ) + { + return ( time1 >= time0 ) ? (double)( time1 - time0 ) : -(double)( time0 - time1 ); + } + + /* Use uintmax_t if wide enough. */ + if ( sizeof( time_t ) <= sizeof( uintmax_t ) ) + { + uintmax_t t1 = time1, t0 = time0; + return ( time1 >= time0 ) ? t1 - t0 : -(double)( t0 - t1 ); + } + + /* If both times have the same sign, their difference cannot overflow. */ + if ( ( time1 < 0 ) == ( time0 < 0 ) ) + { + return time1 - time0; + } + + /* The times have opposite signs, and uintmax_t is too narrow. + This suffers from double rounding; attempt to lessen that + by using long double temporaries. + */ + { + long double t1 = time1, t0 = time0; + return t1 - t0; + } +} +#endif diff --git a/src/libraries/libc/time/gmtime.c b/src/libraries/libc/time/gmtime.c new file mode 100644 index 0000000..34a831a --- /dev/null +++ b/src/libraries/libc/time/gmtime.c @@ -0,0 +1,12 @@ +/* gmtime( const time_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +struct tm * gmtime( const time_t * timer ) +{ + return NULL; +} diff --git a/src/libraries/libc/time/localtime.c b/src/libraries/libc/time/localtime.c new file mode 100644 index 0000000..f405621 --- /dev/null +++ b/src/libraries/libc/time/localtime.c @@ -0,0 +1,12 @@ +/* localtime( const time_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +struct tm * localtime( const time_t * timer ) +{ + return NULL; +} diff --git a/src/libraries/libc/time/mktime.c b/src/libraries/libc/time/mktime.c new file mode 100644 index 0000000..bfaef40 --- /dev/null +++ b/src/libraries/libc/time/mktime.c @@ -0,0 +1,12 @@ +/* mktime( struct tm * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +time_t mktime( struct tm * timeptr ) +{ + return -1; +} diff --git a/src/libraries/libc/time/strftime.c b/src/libraries/libc/time/strftime.c new file mode 100644 index 0000000..b96be49 --- /dev/null +++ b/src/libraries/libc/time/strftime.c @@ -0,0 +1,582 @@ +/* strftime( char * restrict, size_t, const char * restrict, const struct tm * restrict ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include +#include +#include + +/* TODO: Alternative representations / numerals not supported. Multibyte support missing. */ + +/* This implementation's code is highly repetitive, but I did not really + care for putting it into a number of macros / helper functions. +*/ + +enum wstart_t +{ + E_SUNDAY = 0, + E_MONDAY = 1, + E_ISO_WEEK, + E_ISO_YEAR +}; + +#include + +static int week_calc( const struct tm * timeptr, int wtype ) +{ + int wday; + int bias; + int week; + + if ( wtype <= E_MONDAY ) + { + /* Simple -- first week starting with E_SUNDAY / E_MONDAY, + days before that are week 0. + */ + div_t weeks = div( timeptr->tm_yday, 7 ); + wday = ( timeptr->tm_wday + 7 - wtype ) % 7; + if ( weeks.rem >= wday ) + { + ++weeks.quot; + } + return weeks.quot; + } + + /* calculating ISO week; relies on Sunday == 7 */ + wday = timeptr->tm_wday; + if ( wday == 0 ) + { + wday = 7; + } + /* https://en.wikipedia.org/wiki/ISO_week_date */ + week = ( timeptr->tm_yday - wday + 11 ) / 7; + if ( week == 53 ) + { + /* date *may* belong to the *next* year, if: + * it is 31.12. and Monday - Wednesday + * it is 30.12. and Monday - Tuesday + * it is 29.12. and Monday + We can safely assume December... + */ + if ( ( timeptr->tm_yday - wday - _PDCLIB_is_leap( timeptr->tm_year ) ) > 360 ) + { + week = 1; + } + } + else if ( week == 0 ) + { + /* date *does* belong to *previous* year, + i.e. has week 52 *unless*... + * current year started on a Friday, or + * previous year is leap and this year + started on a Saturday. + */ + int firstday = timeptr->tm_wday - ( timeptr->tm_yday % 7 ); + if ( firstday < 0 ) + { + firstday += 7; + } + if ( ( firstday == 5 ) || ( _PDCLIB_is_leap( timeptr->tm_year - 1 ) && firstday == 6 ) ) + { + week = 53; + } + else + { + week = 52; + } + } + if ( wtype == E_ISO_WEEK ) + { + return week; + } + + /* E_ISO_YEAR -- determine the "week-based year" */ + bias = 0; + if ( week >= 52 && timeptr->tm_mon == 0 ) + { + --bias; + } + else if ( week == 1 && timeptr->tm_mon == 11 ) + { + ++bias; + } + return timeptr->tm_year + 1900 + bias; +} + +/* Assuming presence of s, rc, maxsize. + Checks index for valid range, target buffer for sufficient remaining + capacity, and copies the locale-specific string (or "?" if index out + of range). Returns with zero if buffer capacity insufficient. +*/ +#define SPRINTSTR( array, index, max ) \ + { \ + int ind = (index); \ + const char * str = "?"; \ + size_t len; \ + if ( ind >= 0 && ind <= max ) \ + { \ + str = array[ ind ]; \ + } \ + len = strlen( str ); \ + if ( rc < ( maxsize - len ) ) \ + { \ + strcpy( s + rc, str ); \ + rc += len; \ + } \ + else \ + { \ + return 0; \ + } \ + } + +#define SPRINTREC( format ) \ + { \ + size_t count = strftime( s + rc, maxsize - rc, format, timeptr ); \ + if ( count == 0 ) \ + { \ + return 0; \ + } \ + else \ + { \ + rc += count; \ + } \ + } + +size_t strftime( char * restrict s, size_t maxsize, const char * restrict format, const struct tm * restrict timeptr ) +{ + size_t rc = 0; + + while ( rc < maxsize ) + { + if ( *format != '%' ) + { + if ( ( s[rc] = *format++ ) == '\0' ) + { + return rc; + } + else + { + ++rc; + } + } + else + { + /* char flag = 0; */ + switch ( *++format ) + { + case 'E': + case 'O': + /* flag = *format++; */ + break; + default: + /* EMPTY */ + break; + } + switch( *format++ ) + { + case 'a': + { + /* tm_wday abbreviated */ + SPRINTSTR( _PDCLIB_lc_time.day_name_abbr, timeptr->tm_wday, 6 ); + break; + } + case 'A': + { + /* tm_wday full */ + SPRINTSTR( _PDCLIB_lc_time.day_name_full, timeptr->tm_wday, 6 ); + break; + } + case 'b': + case 'h': + { + /* tm_mon abbreviated */ + SPRINTSTR( _PDCLIB_lc_time.month_name_abbr, timeptr->tm_mon, 11 ); + break; + } + case 'B': + { + /* tm_mon full */ + SPRINTSTR( _PDCLIB_lc_time.month_name_full, timeptr->tm_mon, 11 ); + break; + } + case 'c': + { + /* locale's date / time representation, %a %b %e %T %Y for C locale */ + /* 'E' for locale's alternative representation */ + SPRINTREC( _PDCLIB_lc_time.date_time_format ); + break; + } + case 'C': + { + /* tm_year divided by 100, truncated to decimal (00-99) */ + /* 'E' for base year (period) in locale's alternative representation */ + if ( rc < ( maxsize - 2 ) ) + { + div_t period = div( ( ( timeptr->tm_year + 1900 ) / 100 ), 10 ); + s[rc++] = '0' + period.quot; + s[rc++] = '0' + period.rem; + } + else + { + return 0; + } + break; + } + case 'd': + { + /* tm_mday as decimal (01-31) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t day = div( timeptr->tm_mday, 10 ); + s[rc++] = '0' + day.quot; + s[rc++] = '0' + day.rem; + } + else + { + return 0; + } + break; + } + case 'D': + { + /* %m/%d/%y */ + SPRINTREC( "%m/%d/%y" ); + break; + } + case 'e': + { + /* tm_mday as decimal ( 1-31) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t day = div( timeptr->tm_mday, 10 ); + s[rc++] = ( day.quot > 0 ) ? '0' + day.quot : ' '; + s[rc++] = '0' + day.rem; + } + else + { + return 0; + } + break; + } + case 'F': + { + /* %Y-%m-%d */ + SPRINTREC( "%Y-%m-%d" ); + break; + } + case 'g': + { + /* last 2 digits of the week-based year as decimal (00-99) */ + if ( rc < ( maxsize - 2 ) ) + { + div_t year = div( week_calc( timeptr, E_ISO_YEAR ) % 100, 10 ); + s[rc++] = '0' + year.quot; + s[rc++] = '0' + year.rem; + } + else + { + return 0; + } + break; + } + case 'G': + { + /* week-based year as decimal (e.g. 1997) */ + if ( rc < ( maxsize - 4 ) ) + { + int year = week_calc( timeptr, E_ISO_YEAR ); + int i; + for ( i = 3; i >= 0; --i ) + { + div_t digit = div( year, 10 ); + s[ rc + i ] = '0' + digit.rem; + year = digit.quot; + } + + rc += 4; + } + else + { + return 0; + } + break; + } + case 'H': + { + /* tm_hour as 24h decimal (00-23) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t hour = div( timeptr->tm_hour, 10 ); + s[rc++] = '0' + hour.quot; + s[rc++] = '0' + hour.rem; + } + else + { + return 0; + } + break; + } + case 'I': + { + /* tm_hour as 12h decimal (01-12) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t hour = div( ( timeptr->tm_hour + 11 ) % 12 + 1, 10 ); + s[rc++] = '0' + hour.quot; + s[rc++] = '0' + hour.rem; + } + else + { + return 0; + } + break; + } + case 'j': + { + /* tm_yday as decimal (001-366) */ + if ( rc < ( maxsize - 3 ) ) + { + div_t yday = div( timeptr->tm_yday + 1, 100 ); + s[rc++] = '0' + yday.quot; + s[rc++] = '0' + yday.rem / 10; + s[rc++] = '0' + yday.rem % 10; + } + else + { + return 0; + } + break; + } + case 'm': + { + /* tm_mon as decimal (01-12) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t mon = div( timeptr->tm_mon + 1, 10 ); + s[rc++] = '0' + mon.quot; + s[rc++] = '0' + mon.rem; + } + else + { + return 0; + } + break; + } + case 'M': + { + /* tm_min as decimal (00-59) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t min = div( timeptr->tm_min, 10 ); + s[rc++] = '0' + min.quot; + s[rc++] = '0' + min.rem; + } + else + { + return 0; + } + break; + } + case 'n': + { + /* newline */ + s[rc++] = '\n'; + break; + } + case 'p': + { + /* tm_hour locale's AM/PM designations */ + SPRINTSTR( _PDCLIB_lc_time.am_pm, timeptr->tm_hour > 11, 1 ); + break; + } + case 'r': + { + /* tm_hour / tm_min / tm_sec as locale's 12-hour clock time, %I:%M:%S %p for C locale */ + SPRINTREC( _PDCLIB_lc_time.time_format_12h ); + break; + } + case 'R': + { + /* %H:%M */ + SPRINTREC( "%H:%M" ); + break; + } + case 'S': + { + /* tm_sec as decimal (00-60) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t sec = div( timeptr->tm_sec, 10 ); + s[rc++] = '0' + sec.quot; + s[rc++] = '0' + sec.rem; + } + else + { + return 0; + } + break; + } + case 't': + { + /* tabulator */ + s[rc++] = '\t'; + break; + } + case 'T': + { + /* %H:%M:%S */ + SPRINTREC( "%H:%M:%S" ); + break; + } + case 'u': + { + /* tm_wday as decimal (1-7) with Monday == 1 */ + /* 'O' for locale's alternative numeric symbols */ + s[rc++] = ( timeptr->tm_wday == 0 ) ? '7' : '0' + timeptr->tm_wday; + break; + } + case 'U': + { + /* week number of the year (first Sunday as the first day of week 1) as decimal (00-53) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t week = div( week_calc( timeptr, E_SUNDAY ), 10 ); + s[rc++] = '0' + week.quot; + s[rc++] = '0' + week.rem; + } + else + { + return 0; + } + break; + } + case 'V': + { + /* ISO week number as decimal (01-53) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t week = div( week_calc( timeptr, E_ISO_WEEK ), 10 ); + s[rc++] = '0' + week.quot; + s[rc++] = '0' + week.rem; + } + else + { + return 0; + } + break; + } + case 'w': + { + /* tm_wday as decimal number (0-6) with Sunday == 0 */ + /* 'O' for locale's alternative numeric symbols */ + s[rc++] = '0' + timeptr->tm_wday; + break; + } + case 'W': + { + /* week number of the year (first Monday as the first day of week 1) as decimal (00-53) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t week = div( week_calc( timeptr, E_MONDAY ), 10 ); + s[rc++] = '0' + week.quot; + s[rc++] = '0' + week.rem; + } + else + { + return 0; + } + break; + } + case 'x': + { + /* locale's date representation, %m/%d/%y for C locale */ + /* 'E' for locale's alternative representation */ + SPRINTREC( _PDCLIB_lc_time.date_format ); + break; + } + case 'X': + { + /* locale's time representation, %T for C locale */ + /* 'E' for locale's alternative representation */ + SPRINTREC( _PDCLIB_lc_time.time_format ); + break; + } + case 'y': + { + /* last 2 digits of tm_year as decimal (00-99) */ + /* 'E' for offset from %EC (year only) in locale's alternative representation */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t year = div( ( timeptr->tm_year % 100 ), 10 ); + s[rc++] = '0' + year.quot; + s[rc++] = '0' + year.rem; + } + else + { + return 0; + } + break; + } + case 'Y': + { + /* tm_year as decimal (e.g. 1997) */ + /* 'E' for locale's alternative representation */ + if ( rc < ( maxsize - 4 ) ) + { + int year = timeptr->tm_year + 1900; + int i; + + for ( i = 3; i >= 0; --i ) + { + div_t digit = div( year, 10 ); + s[ rc + i ] = '0' + digit.rem; + year = digit.quot; + } + + rc += 4; + } + else + { + return 0; + } + break; + } + case 'z': + { + /* tm_isdst / UTC offset in ISO8601 format (e.g. -0430 meaning 4 hours 30 minutes behind Greenwich), or no characters */ + /* TODO: 'z' */ + break; + } + case 'Z': + { + /* tm_isdst / locale's time zone name or abbreviation, or no characters */ + /* TODO: 'Z' */ + break; + } + case '%': + { + /* '%' character */ + s[rc++] = '%'; + break; + } + } + } + } + + return 0; +} diff --git a/src/libraries/libc/time/time.c b/src/libraries/libc/time/time.c new file mode 100644 index 0000000..ae6f7ed --- /dev/null +++ b/src/libraries/libc/time/time.c @@ -0,0 +1,13 @@ +/* time( time_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +/* See comments in time.h on the semantics of time_t. */ + +time_t time( time_t * timer ) +{ + return -1; +} diff --git a/src/libraries/libc/time/timespec_get.c b/src/libraries/libc/time/timespec_get.c new file mode 100644 index 0000000..1af674c --- /dev/null +++ b/src/libraries/libc/time/timespec_get.c @@ -0,0 +1,12 @@ +/* timespec_get( struct timespec *, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include + +int timespec_get( struct timespec * ts, int base ) +{ + return 0; +}