]> code.bitgloo.com Git - clyne/stmos.git/commitdiff
added pdclib, removed sash
authortcsullivan <tullivan99@gmail.com>
Sat, 17 Nov 2018 18:02:57 +0000 (13:02 -0500)
committertcsullivan <tullivan99@gmail.com>
Sat, 17 Nov 2018 18:02:57 +0000 (13:02 -0500)
262 files changed:
link.ld
src/fs/initrd.c
src/fs/stdio.c [new file with mode: 0644]
src/fs/stdio.h [new file with mode: 0644]
src/kernel/init.c
src/kernel/serial.c [new file with mode: 0644]
src/kernel/serial.h [new file with mode: 0644]
src/kernel/svc.c
src/kernel/task.c
src/kernel/task.h
src/kernel/vfs.c
src/kernel/vfs.h
src/pdclib/.gitignore [new file with mode: 0644]
src/pdclib/COPYING.CC0 [new file with mode: 0644]
src/pdclib/Internals.txt [new file with mode: 0644]
src/pdclib/Makefile [new file with mode: 0644]
src/pdclib/Notes.txt [new file with mode: 0644]
src/pdclib/Readme.txt [new file with mode: 0644]
src/pdclib/auxiliary/uctype/Makefile [new file with mode: 0644]
src/pdclib/auxiliary/uctype/derived_properties.c [new file with mode: 0644]
src/pdclib/auxiliary/uctype/derived_properties.h [new file with mode: 0644]
src/pdclib/auxiliary/uctype/main.c [new file with mode: 0644]
src/pdclib/auxiliary/uctype/test.h [new file with mode: 0644]
src/pdclib/auxiliary/uctype/text_utilities.c [new file with mode: 0644]
src/pdclib/auxiliary/uctype/text_utilities.h [new file with mode: 0644]
src/pdclib/auxiliary/uctype/uctype.c [new file with mode: 0644]
src/pdclib/auxiliary/uctype/uctype.h [new file with mode: 0644]
src/pdclib/auxiliary/uctype/unicode_data.c [new file with mode: 0644]
src/pdclib/auxiliary/uctype/unicode_data.h [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/Readme.txt [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_atomax.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_closeall.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_digits.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_filemode.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_is_leap.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_collate.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_ctype.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_messages.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_monetary.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_numeric.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_time.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_load_lines.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_prepread.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_prepwrite.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_print.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_scan.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_seed.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_main.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_prelim.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/assert.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/errno.c [new file with mode: 0644]
src/pdclib/functions/_PDCLIB/stdarg.c [new file with mode: 0644]
src/pdclib/functions/ctype/isalnum.c [new file with mode: 0644]
src/pdclib/functions/ctype/isalpha.c [new file with mode: 0644]
src/pdclib/functions/ctype/isblank.c [new file with mode: 0644]
src/pdclib/functions/ctype/iscntrl.c [new file with mode: 0644]
src/pdclib/functions/ctype/isdigit.c [new file with mode: 0644]
src/pdclib/functions/ctype/isgraph.c [new file with mode: 0644]
src/pdclib/functions/ctype/islower.c [new file with mode: 0644]
src/pdclib/functions/ctype/isprint.c [new file with mode: 0644]
src/pdclib/functions/ctype/ispunct.c [new file with mode: 0644]
src/pdclib/functions/ctype/isspace.c [new file with mode: 0644]
src/pdclib/functions/ctype/isupper.c [new file with mode: 0644]
src/pdclib/functions/ctype/isxdigit.c [new file with mode: 0644]
src/pdclib/functions/ctype/tolower.c [new file with mode: 0644]
src/pdclib/functions/ctype/toupper.c [new file with mode: 0644]
src/pdclib/functions/inttypes/imaxabs.c [new file with mode: 0644]
src/pdclib/functions/inttypes/imaxdiv.c [new file with mode: 0644]
src/pdclib/functions/inttypes/strtoimax.c [new file with mode: 0644]
src/pdclib/functions/inttypes/strtoumax.c [new file with mode: 0644]
src/pdclib/functions/locale/localeconv.c [new file with mode: 0644]
src/pdclib/functions/locale/setlocale.c [new file with mode: 0644]
src/pdclib/functions/stdio/clearerr.c [new file with mode: 0644]
src/pdclib/functions/stdio/fclose.c [new file with mode: 0644]
src/pdclib/functions/stdio/feof.c [new file with mode: 0644]
src/pdclib/functions/stdio/ferror.c [new file with mode: 0644]
src/pdclib/functions/stdio/fflush.c [new file with mode: 0644]
src/pdclib/functions/stdio/fgetc.c [new file with mode: 0644]
src/pdclib/functions/stdio/fgetpos.c [new file with mode: 0644]
src/pdclib/functions/stdio/fgets.c [new file with mode: 0644]
src/pdclib/functions/stdio/fopen.c [new file with mode: 0644]
src/pdclib/functions/stdio/fprintf.c [new file with mode: 0644]
src/pdclib/functions/stdio/fputc.c [new file with mode: 0644]
src/pdclib/functions/stdio/fputs.c [new file with mode: 0644]
src/pdclib/functions/stdio/fread.c [new file with mode: 0644]
src/pdclib/functions/stdio/freopen.c [new file with mode: 0644]
src/pdclib/functions/stdio/fscanf.c [new file with mode: 0644]
src/pdclib/functions/stdio/fseek.c [new file with mode: 0644]
src/pdclib/functions/stdio/fsetpos.c [new file with mode: 0644]
src/pdclib/functions/stdio/ftell.c [new file with mode: 0644]
src/pdclib/functions/stdio/fwrite.c [new file with mode: 0644]
src/pdclib/functions/stdio/getc.c [new file with mode: 0644]
src/pdclib/functions/stdio/getchar.c [new file with mode: 0644]
src/pdclib/functions/stdio/perror.c [new file with mode: 0644]
src/pdclib/functions/stdio/printf.c [new file with mode: 0644]
src/pdclib/functions/stdio/putc.c [new file with mode: 0644]
src/pdclib/functions/stdio/putchar.c [new file with mode: 0644]
src/pdclib/functions/stdio/puts.c [new file with mode: 0644]
src/pdclib/functions/stdio/rename.c [new file with mode: 0644]
src/pdclib/functions/stdio/rewind.c [new file with mode: 0644]
src/pdclib/functions/stdio/scanf.c [new file with mode: 0644]
src/pdclib/functions/stdio/setbuf.c [new file with mode: 0644]
src/pdclib/functions/stdio/setvbuf.c [new file with mode: 0644]
src/pdclib/functions/stdio/snprintf.c [new file with mode: 0644]
src/pdclib/functions/stdio/sprintf.c [new file with mode: 0644]
src/pdclib/functions/stdio/sscanf.c [new file with mode: 0644]
src/pdclib/functions/stdio/testfile.txt [new file with mode: 0644]
src/pdclib/functions/stdio/tmpnam.c [new file with mode: 0644]
src/pdclib/functions/stdio/ungetc.c [new file with mode: 0644]
src/pdclib/functions/stdio/vfprintf.c [new file with mode: 0644]
src/pdclib/functions/stdio/vfscanf.c [new file with mode: 0644]
src/pdclib/functions/stdio/vprintf.c [new file with mode: 0644]
src/pdclib/functions/stdio/vscanf.c [new file with mode: 0644]
src/pdclib/functions/stdio/vsnprintf.c [new file with mode: 0644]
src/pdclib/functions/stdio/vsprintf.c [new file with mode: 0644]
src/pdclib/functions/stdio/vsscanf.c [new file with mode: 0644]
src/pdclib/functions/stdlib/_Exit.c [new file with mode: 0644]
src/pdclib/functions/stdlib/abort.c [new file with mode: 0644]
src/pdclib/functions/stdlib/abs.c [new file with mode: 0644]
src/pdclib/functions/stdlib/atexit.c [new file with mode: 0644]
src/pdclib/functions/stdlib/atoi.c [new file with mode: 0644]
src/pdclib/functions/stdlib/atol.c [new file with mode: 0644]
src/pdclib/functions/stdlib/atoll.c [new file with mode: 0644]
src/pdclib/functions/stdlib/bsearch.c [new file with mode: 0644]
src/pdclib/functions/stdlib/calloc.c [new file with mode: 0644]
src/pdclib/functions/stdlib/div.c [new file with mode: 0644]
src/pdclib/functions/stdlib/exit.c [new file with mode: 0644]
src/pdclib/functions/stdlib/free.c [new file with mode: 0644]
src/pdclib/functions/stdlib/labs.c [new file with mode: 0644]
src/pdclib/functions/stdlib/ldiv.c [new file with mode: 0644]
src/pdclib/functions/stdlib/llabs.c [new file with mode: 0644]
src/pdclib/functions/stdlib/lldiv.c [new file with mode: 0644]
src/pdclib/functions/stdlib/malloc.c [new file with mode: 0644]
src/pdclib/functions/stdlib/qsort.c [new file with mode: 0644]
src/pdclib/functions/stdlib/rand.c [new file with mode: 0644]
src/pdclib/functions/stdlib/realloc.c [new file with mode: 0644]
src/pdclib/functions/stdlib/srand.c [new file with mode: 0644]
src/pdclib/functions/stdlib/strtol.c [new file with mode: 0644]
src/pdclib/functions/stdlib/strtoll.c [new file with mode: 0644]
src/pdclib/functions/stdlib/strtoul.c [new file with mode: 0644]
src/pdclib/functions/stdlib/strtoull.c [new file with mode: 0644]
src/pdclib/functions/string/memchr.c [new file with mode: 0644]
src/pdclib/functions/string/memcmp.c [new file with mode: 0644]
src/pdclib/functions/string/memcpy.c [new file with mode: 0644]
src/pdclib/functions/string/memmove.c [new file with mode: 0644]
src/pdclib/functions/string/memset.c [new file with mode: 0644]
src/pdclib/functions/string/strcat.c [new file with mode: 0644]
src/pdclib/functions/string/strchr.c [new file with mode: 0644]
src/pdclib/functions/string/strcmp.c [new file with mode: 0644]
src/pdclib/functions/string/strcoll.c [new file with mode: 0644]
src/pdclib/functions/string/strcpy.c [new file with mode: 0644]
src/pdclib/functions/string/strcspn.c [new file with mode: 0644]
src/pdclib/functions/string/strerror.c [new file with mode: 0644]
src/pdclib/functions/string/strlen.c [new file with mode: 0644]
src/pdclib/functions/string/strncat.c [new file with mode: 0644]
src/pdclib/functions/string/strncmp.c [new file with mode: 0644]
src/pdclib/functions/string/strncpy.c [new file with mode: 0644]
src/pdclib/functions/string/strpbrk.c [new file with mode: 0644]
src/pdclib/functions/string/strrchr.c [new file with mode: 0644]
src/pdclib/functions/string/strspn.c [new file with mode: 0644]
src/pdclib/functions/string/strstr.c [new file with mode: 0644]
src/pdclib/functions/string/strtok.c [new file with mode: 0644]
src/pdclib/functions/string/strxfrm.c [new file with mode: 0644]
src/pdclib/functions/time/asctime.c [new file with mode: 0644]
src/pdclib/functions/time/ctime.c [new file with mode: 0644]
src/pdclib/functions/time/difftime.c [new file with mode: 0644]
src/pdclib/functions/time/gmtime.c [new file with mode: 0644]
src/pdclib/functions/time/localtime.c [new file with mode: 0644]
src/pdclib/functions/time/mktime.c [new file with mode: 0644]
src/pdclib/functions/time/strftime.c [new file with mode: 0644]
src/pdclib/include/assert.h [new file with mode: 0644]
src/pdclib/include/ctype.h [new file with mode: 0644]
src/pdclib/include/errno.h [new file with mode: 0644]
src/pdclib/include/inttypes.h [new file with mode: 0644]
src/pdclib/include/iso646.h [new file with mode: 0644]
src/pdclib/include/limits.h [new file with mode: 0644]
src/pdclib/include/locale.h [new file with mode: 0644]
src/pdclib/include/pdclib/_PDCLIB_aux.h [new file with mode: 0644]
src/pdclib/include/pdclib/_PDCLIB_glue.h [new file with mode: 0644]
src/pdclib/include/pdclib/_PDCLIB_int.h [new file with mode: 0644]
src/pdclib/include/stdalign.h [new file with mode: 0644]
src/pdclib/include/stdarg.h [new file with mode: 0644]
src/pdclib/include/stdbool.h [new file with mode: 0644]
src/pdclib/include/stddef.h [new file with mode: 0644]
src/pdclib/include/stdint.h [new file with mode: 0644]
src/pdclib/include/stdio.h [new file with mode: 0644]
src/pdclib/include/stdlib.h [new file with mode: 0644]
src/pdclib/include/stdnoreturn.h [new file with mode: 0644]
src/pdclib/include/string.h [new file with mode: 0644]
src/pdclib/include/time.h [new file with mode: 0644]
src/pdclib/include/wctype.h [new file with mode: 0644]
src/pdclib/platform/example/Readme.txt [new file with mode: 0644]
src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_Exit.c [new file with mode: 0644]
src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB__Exit.c [new file with mode: 0644]
src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_allocpages.c [new file with mode: 0644]
src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_close.c [new file with mode: 0644]
src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_fillbuffer.c [new file with mode: 0644]
src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_flushbuffer.c [new file with mode: 0644]
src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_open.c [new file with mode: 0644]
src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_rename.c [new file with mode: 0644]
src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_seek.c [new file with mode: 0644]
src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_stdinit.c [new file with mode: 0644]
src/pdclib/platform/example/functions/signal/raise.c [new file with mode: 0644]
src/pdclib/platform/example/functions/signal/signal.c [new file with mode: 0644]
src/pdclib/platform/example/functions/stdio/remove.c [new file with mode: 0644]
src/pdclib/platform/example/functions/stdio/tmpfile.c [new file with mode: 0644]
src/pdclib/platform/example/functions/stdlib/getenv.c [new file with mode: 0644]
src/pdclib/platform/example/functions/stdlib/system.c [new file with mode: 0644]
src/pdclib/platform/example/functions/time/clock.c [new file with mode: 0644]
src/pdclib/platform/example/functions/time/time.c [new file with mode: 0644]
src/pdclib/platform/example/functions/time/timespec_get.c [new file with mode: 0644]
src/pdclib/platform/example/include/float.h [new file with mode: 0644]
src/pdclib/platform/example/include/pdclib/_PDCLIB_config.h [new file with mode: 0644]
src/pdclib/platform/example/include/signal.h [new file with mode: 0644]
src/pdclib/platform/stmos/Readme.txt [new file with mode: 0644]
src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_Exit.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB__Exit.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_allocpages.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_close.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_fillbuffer.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_flushbuffer.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_open.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_rename.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_seek.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_stdinit.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/signal/raise.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/signal/signal.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/stdio/remove.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/stdio/tmpfile.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/stdlib/getenv.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/stdlib/system.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/time/clock.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/time/time.c [new file with mode: 0644]
src/pdclib/platform/stmos/functions/time/timespec_get.c [new file with mode: 0644]
src/pdclib/platform/stmos/include/float.h [new file with mode: 0644]
src/pdclib/platform/stmos/include/pdclib/_PDCLIB_config.h [new file with mode: 0644]
src/pdclib/platform/stmos/include/signal.h [new file with mode: 0644]
src/pdclib/testing/_PDCLIB_iotest.h [new file with mode: 0644]
src/pdclib/testing/_PDCLIB_test.h [new file with mode: 0644]
src/pdclib/testing/printf_testcases.h [new file with mode: 0644]
src/pdclib/testing/scanf_testcases.h [new file with mode: 0644]
src/sash/CHANGES [deleted file]
src/sash/Makefile [deleted file]
src/sash/README [deleted file]
src/sash/cmd_ar.c [deleted file]
src/sash/cmd_chattr.c [deleted file]
src/sash/cmd_dd.c [deleted file]
src/sash/cmd_ed.c [deleted file]
src/sash/cmd_file.c [deleted file]
src/sash/cmd_find.c [deleted file]
src/sash/cmd_grep.c [deleted file]
src/sash/cmd_gzip.c [deleted file]
src/sash/cmd_ls.c [deleted file]
src/sash/cmd_tar.c [deleted file]
src/sash/cmds.c [deleted file]
src/sash/dirent.h [deleted file]
src/sash/sash.1 [deleted file]
src/sash/sash.c [deleted file]
src/sash/sash.h [deleted file]
src/sash/utils.c [deleted file]
src/user/syscalls.h [new file with mode: 0644]
src/user/user.c

diff --git a/link.ld b/link.ld
index 231269ae3ceb11a3886266bcd1f6662644366f58..e00d450b0db719357e8854954cb073c39c52b52b 100644 (file)
--- a/link.ld
+++ b/link.ld
@@ -94,4 +94,6 @@ SECTIONS {
                . = ALIGN(8);
                __bss_end__ = .;
        } > RAM
+
+       end = .;
 }
index efa430ca77ef10d80ad66fb9ba71fc11c33a2fbe..5731ab51577a5f563fb0fb68ef26712c464cca9d 100644 (file)
@@ -18,12 +18,13 @@ static const uint32_t initrd_size = (uint32_t)_binary_initrd_img_size;
 
 void *initrd_open(const char *file);
 uint32_t initrd_read(void *info, uint32_t count, uint8_t *buffer);
+int initrd_close(void *info);
 
 char *initrd_getfile(uint32_t offset);
 
 static const vfs_volume_funcs initrd_funcs = {
        initrd_open,
-       0, // close
+       initrd_close,
        initrd_read,
        0, // write
        0  // readdir
@@ -52,6 +53,13 @@ void *initrd_open(const char *file)
        return 0;
 }
 
+int initrd_close(void *info)
+{
+       // Nothing to do
+       free(info);
+       return 0;
+}
+
 uint32_t initrd_read(void *info, uint32_t count, uint8_t *buffer)
 {
        initrd_info *iinfo = (initrd_info *)info;
diff --git a/src/fs/stdio.c b/src/fs/stdio.c
new file mode 100644 (file)
index 0000000..6cea2a3
--- /dev/null
@@ -0,0 +1,55 @@
+#include "stdio.h"
+
+#include <kernel/heap.h>
+#include <kernel/serial.h>
+
+void *stdio_open(const char *path);
+int stdio_close(void *info);
+uint32_t stdio_read(void *info, uint32_t count, uint8_t *buffer);
+uint32_t stdio_write(void *info, uint32_t count, const uint8_t *buffer);
+
+const vfs_volume_funcs stdio_funcs = {
+       stdio_open,
+       stdio_close,
+       stdio_read,
+       stdio_write,
+       0, // readdir
+};
+
+void *stdio_open(const char *path)
+{
+       int *id = malloc(sizeof(uint32_t));
+
+       if (path[0] == 'o' && path[1] == 'u' && path[2] == 't')
+               *id = 1;
+       else if (path[0] == 'i' && path[1] == 'n')
+               *id = 0;
+       if (path[0] == 'e' && path[1] == 'r' && path[2] == 'r')
+               *id = 2;
+
+       return id;
+}
+
+int stdio_close(void *info)
+{
+       // Nothing to do
+       free(info);
+       return 0;
+}
+
+uint32_t stdio_read(void *info, uint32_t count, uint8_t *buffer)
+{
+       (void)info;
+       (void)count;
+       (void)buffer;
+       return 0;
+}
+
+uint32_t stdio_write(void *info, uint32_t count, const uint8_t *buffer)
+{
+       (void)info;
+       for (uint32_t i = 0; i < count; i++)
+               serial_put(buffer[count]);
+       return count;
+}
+
diff --git a/src/fs/stdio.h b/src/fs/stdio.h
new file mode 100644 (file)
index 0000000..90d0f68
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef INTIRD_H_
+#define INITRD_H_
+
+#include <kernel/vfs.h>
+
+extern const vfs_volume_funcs stdio_funcs;
+
+#endif // INITRD_H_
index 2ba607daec83afb5f568c74dbeeec97ecf635d4d..3bd6b39534e1aecd23cc472a239f42f059bde7c0 100644 (file)
@@ -21,6 +21,7 @@
 #include "clock.h"\r
 #include "gpio.h"\r
 #include "heap.h"\r
+#include "serial.h"\r
 #include "task.h"\r
 #include "vfs.h"\r
 #include <fs/initrd.h>\r
@@ -46,6 +47,7 @@ int main(void)
        heap_init(&__bss_end__);\r
        gpio_init();\r
 \r
+       serial_init(9600);\r
        vfs_init();\r
        initrd_init();\r
 \r
diff --git a/src/kernel/serial.c b/src/kernel/serial.c
new file mode 100644 (file)
index 0000000..6f112a8
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * @file serial.c
+ * Provides basic serial IO (through STM debug stuff)
+ *
+ * Copyright (C) 2018 Clyne Sullivan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <arch/stm/stm32l476xx.h>
+#include "gpio.h"
+#include "clock.h"
+
+void serial_init(uint32_t baud)
+{
+       gpio_mode(GPIOA, 2, ALTERNATE);
+       gpio_mode(GPIOA, 3, ALTERNATE);
+       GPIOA->AFR[0] &= ~(0x0000FF00);
+       GPIOA->AFR[0] |= 0x00007700;
+       RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;
+
+       // start usart device
+       USART2->BRR = 80000000L / baud;
+       USART2->CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
+}
+
+void serial_put(int c)
+{
+       while (!(USART2->ISR & USART_ISR_TXE));
+       USART2->TDR = c & 0xFF;
+}
+
+char serial_get(void)
+{
+       while (!(USART2->ISR & USART_ISR_RXNE))
+               delay(10);
+       return USART2->RDR & 0xFF;
+}
+
+void serial_gets(char *buf, int max)
+{
+       uint16_t index = 0;
+
+       do {
+               buf[index] = serial_get();
+               serial_put(buf[index]);
+       } while (buf[index] != '\r' && index++ < max);
+       buf[index] = '\0';
+}
diff --git a/src/kernel/serial.h b/src/kernel/serial.h
new file mode 100644 (file)
index 0000000..e721d6c
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * @file serial.h
+ * Provides basic serial IO (through STM debug stuff)
+ *
+ * Copyright (C) 2018 Clyne Sullivan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef SERIAL_H_
+#define SERIAL_H_
+
+/**
+ * Initializes the serial device.
+ */
+void serial_init(uint32_t baud);
+
+/**
+ * Puts the given character through serial.
+ * @param c the character to send
+ */
+void serial_put(int c);
+
+/**
+ * Gets a character from serial.
+ * @return the character
+ */
+char serial_get(void);
+
+/**
+ * Gets a string from serial, cut off by a newline.
+ * @param buf the initialized buffer to fill
+ * @param max the max amount of bytes to write to the buffer
+ */
+void serial_gets(char *buf, int max);
+
+#endif // SERIAL_H_
index 83bd568a49a16c90500094efd19ade40fb266bbc..7d171c3e601884264e26a247cbdf60209f7711e7 100644 (file)
@@ -47,6 +47,7 @@ void SVC_Handler(void) {
                 * 1 - fork
                 * 2 - getpid
                 * 3 - waitpid
+                * 4 - sbrk (TODO bad)
                 */
                task_svc(args);
                break;
@@ -71,6 +72,8 @@ void SVC_Handler(void) {
        case 3: /* Filesystem-related calls
                 * 0 - mount
                 * 1 - open
+                * 2 - close
+                * 3 - read
                 */
                vfs_svc(args);
                break;
index 2550e4b61de68927e42622f0f030b756bd4174a1..dfe50a7ced0720d01faf954206879bce2290fac1 100644 (file)
@@ -46,11 +46,30 @@ void task_svc(uint32_t *args)
                *((int *)args[4]) = task_waitpid(args[1], (int *)args[2],
                        args[3]);
                break;
+       case 4:
+               *((void **)args[2]) = task_sbrk(args[1]);
+               break;
        default:
                break;
        }
 }
 
+void *task_sbrk(uint32_t bytes)
+{
+       if (task_current->heap == 0) {
+               task_current->heap = malloc(1024 * 16);
+               return (uint8_t *)task_current->heap + (1024 * 16);
+       }
+
+       if (bytes == 0) {
+               alloc_t *alloc = (alloc_t *)((uint8_t *)task_current->heap -
+                       sizeof(alloc_t));
+               return (uint8_t *)task_current->heap + alloc->size;
+       }
+
+       return (void *)-1;
+}
+
 void task_hold(uint8_t hold)
 {
        if (hold != 0)
@@ -124,6 +143,7 @@ void task_purge(void)
        // Since we're single core, no one else can claim this memory until
        // a task switch, after which we're done with this memory anyway.
        free(task_current->stack);
+       free(task_current->heap);
        free(task_current);
 
        YIELD;
@@ -163,6 +183,7 @@ task_t *task_create(void (*code)(void), uint16_t stackSize)
        t->status.state = TASK_RUNNING;
        t->status.value = 0;
 
+       t->heap = 0;
        t->stack = (uint32_t *)malloc(stackSize);
        void *sp = (uint8_t *)t->stack + stackSize - 68; // excep. stack + regs
        t->sp = sp;
index 30e3e66d526ab893cba63d0bfcebcd2b6b93faa8..5875e3c89a2569b2e80b3cb58cd09620c961ff7f 100644 (file)
@@ -35,6 +35,7 @@ typedef struct task_t {
        struct task_t *next; /**< pointer to the next task_t instance */
        uint32_t *sp;        /**< pointer to the task's last sp register value */
        uint32_t *stack;     /**< pointer to the task's stack */
+       uint32_t *heap;
        pid_t pid;           /**< Task (Process) ID */
        pid_t pgid;          /**< Process Group ID */
        struct {
@@ -82,5 +83,6 @@ pid_t task_getpid(void);
 
 pid_t task_waitpid(pid_t pid, int *wstatus, int options);
 
+void *task_sbrk(uint32_t bytes);
 
 #endif // TASK_H_
index 1720a6b69ae73d3ed01f4b8216eaf0c70719356a..d6f9e14546b56052261f1262464df124bf0435ad 100644 (file)
@@ -1,9 +1,11 @@
 #include "vfs.h"
 
 #include <kernel/task.h>
+#include <fs/stdio.h>
 
-#define VFS_MAX_VOLS  8
-#define VFS_MAX_FILES 10
+// +1 vol for stdio, +3 fd's for stdout, in, err
+#define VFS_MAX_VOLS  (1 + 8)
+#define VFS_MAX_FILES (3 + 10)
 
 static vfs_volume vfs_volumes[VFS_MAX_VOLS];
 static vfs_file vfs_files[VFS_MAX_FILES];
@@ -19,6 +21,9 @@ void vfs_svc(uint32_t *args)
                *((int *)args[3]) = vfs_open((const char *)args[1], args[2]);
                break;
        case 2:
+               *((int *)args[2]) = vfs_close(args[1]);
+               break;
+       case 3:
                *((int *)args[4]) = vfs_read(args[1], args[2], (uint8_t *)args[3]);
                break;
        default:
@@ -32,6 +37,12 @@ void vfs_init(void)
                vfs_volumes[i].flags = 0;
        for (int i = 0; i < VFS_MAX_FILES; i++)
                vfs_files[i].flags = 0;
+
+       vfs_mount(&stdio_funcs, 0);
+       // order is crucial
+       vfs_open("in", VFS_FILE_READ);
+       vfs_open("out", VFS_FILE_WRITE);
+       vfs_open("err", VFS_FILE_WRITE);
 }
 
 int vfs_mount(vfs_volume_funcs *funcs, uint32_t flags)
@@ -98,6 +109,26 @@ int vfs_open(const char *path, uint32_t flags)
        return file;
 }
 
+int vfs_close(int fd)
+{
+       if (fd < 0 || fd > VFS_MAX_FILES)
+               return -1;
+       if (vfs_volumes[vfs_files[fd].vol].funcs->close == 0)
+               return -1;
+       if (vfs_files[fd].pid != task_getpid())
+               return -1;
+       if (!(vfs_files[fd].flags & VFS_FILE_OPEN))
+               return 0;
+
+       // TODO care
+       /*int ret =*/ vfs_volumes[vfs_files[fd].vol].funcs->close(
+               vfs_files[fd].fsinfo);
+
+       vfs_files[fd].flags = 0;
+       vfs_files[fd].pid = 0;
+       return 0;
+}
+
 uint32_t vfs_read(int fd, uint32_t count, uint8_t *buffer)
 {
        if (fd < 0 || fd > VFS_MAX_FILES || count == 0 || buffer == 0)
index a6de690378bbc4625c27ae22d58aa9fabaa9b84d..c9c91799bb527971683913be6d5717e988e0e02b 100644 (file)
@@ -7,15 +7,17 @@ typedef struct {
        char name[32];
 } vfs_dirent;
 
-typedef struct {
+typedef struct vfs_volume_funcs_t {
        void *(*open)(const char *file);
-       void (*close)(void *info);
+       int (*close)(void *info);
        uint32_t (*read)(void *info, uint32_t count, uint8_t *buffer);
        uint32_t (*write)(void *info, uint32_t count, const uint8_t *buffer);
        vfs_dirent *(*readdir)(const char *path);
 } vfs_volume_funcs;
 
+// Indicates mounted volume
 #define VFS_MOUNTED  (1 << 0)
+// Set if filesystem is read-only
 #define VFS_READONLY (1 << 1)
 
 typedef struct {
@@ -23,24 +25,27 @@ typedef struct {
        vfs_volume_funcs *funcs;
 } vfs_volume;
 
+// Indicates an opened file
 #define VFS_FILE_OPEN  (1 << 0)
+// Indicates read permission on file
 #define VFS_FILE_READ  (1 << 1)
+// Indicates write permission on file
 #define VFS_FILE_WRITE (1 << 2)
-#define VFS_FILE_MODF  (1 << 3)
-#define VFS_TEMPORARY  (1 << 4)
-#define VFS_EOF        (1 << 5)
+// Set if EOF has been reached
+#define VFS_EOF        (1 << 3)
 
 typedef struct {
-       uint32_t flags;
-       uint32_t vol;
-       uint32_t pid;
-       void *fsinfo;
+       uint32_t flags; /**< File attribute flags */
+       uint32_t vol;   /**< Index of volume file is stored on */
+       uint32_t pid;   /**< PID of process handling this file */
+       void *fsinfo;   /**< Filesystem-specific data, handled by fs driver */
 } vfs_file;
 
 void vfs_init(void);
 
 int vfs_mount(vfs_volume_funcs *funcs, uint32_t flags);
 int vfs_open(const char *path, uint32_t flags);
+int vfs_close(int fd);
 uint32_t vfs_read(int fd, uint32_t count, uint8_t *buffer);
 
 #endif // VFS_H_
diff --git a/src/pdclib/.gitignore b/src/pdclib/.gitignore
new file mode 100644 (file)
index 0000000..d4ef6a2
--- /dev/null
@@ -0,0 +1,61 @@
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+
+# PDCLib test drivers
+*_t
+
+# PDCLib regression test drivers
+*_r
+
+# Auxiliary Targets
+get-uctypes
diff --git a/src/pdclib/COPYING.CC0 b/src/pdclib/COPYING.CC0
new file mode 100644 (file)
index 0000000..0e259d4
--- /dev/null
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/src/pdclib/Internals.txt b/src/pdclib/Internals.txt
new file mode 100644 (file)
index 0000000..f069e4a
--- /dev/null
@@ -0,0 +1,65 @@
+Internals
+=========
+
+Large parts of PDCLib (or any standard library, really) work well in isolation,
+and have minimal dependencies. The implementation of <string.h>, for example,
+is really just a collection of stand-alone functions.
+
+Other parts, however, depend on each other, and on "background" functionality
+that all involved parts need to be aware of and agree upon.
+
+This text file is intended to give a rough overview of what those parts are,
+and how PDCLib goes about implementing them.
+
+Numeric conversion functions -- strto*()
+----------------------------------------
+
+The numeric conversion functions -- strtol(), strtoul(), strtoll(), strtoull()
+from <stdlib.h> and strtoimax(), strtoumax() from <inttypes.h> -- all use the
+same two internal functions, _PDCLIB_strtox_prelim() and _PDCLIB_strtox_main().
+The former does skip leading whitespace, determines the sign (if any), and the
+base prefix (0 for octal, 0x for hexadecimal, none for decimal). The latter is
+working on type uintmax_t, and gets the limiting values (to determine when to
+set ERANGE) from the caller.
+
+Numeric conversion functions -- ato*()
+--------------------------------------
+
+The non-checking conversion functions atoi(), atol() and atoll() use the simpler
+backend function _PDCLIB_atomax().
+
+Formatted input / output functions -- *printf(), *scanf()
+---------------------------------------------------------
+
+The rather complex formatting logic used by the functions of the *printf() and
+*scanf() family is provided by _PDCLIB_print() and _PDCLIB_scan(), with the
+individual implementations in functions/stdio/ being rather simple wrappers
+around those two backend functions.
+
+There is some ugliness arising from the fact that the backend functions have to
+work with both file I/O and string I/O. Both backend functions are designed to
+work purely stack-based; there is no dependency on malloc(), which allows to use
+especially the print variety very early in the boot process with only minimal
+adjustments.
+
+File I/O
+--------
+
+The actual handling of stream buffers and I/O is handled by four functions:
+_PDCLIB_prepread() and _PDCLIB_prepwrite() handle the I/O direction of a stream,
+while _PDCLIB_fillbuffer() and _PDCLIB_flushbuffer() are responsible for moving
+data between stream buffers and their associated files.
+
+Localization
+------------
+
+PDCLib does not yet have proper support for different locales. At the point of
+this writing, only the "C" locale is supported. Some of the infrastructure that
+will be required in the future is already in place though.
+
+<locale.h> holds the definition of struct lconv (the return value of the
+localeconv() function, holding all the numerical and monetary formatting
+options). There are also references to _PDCLIB_lc_* structures holding
+the other types of locale-dependent information. The definition of the
+structures is in _PDCLIB_int.h, with initialization (for the "C" locale)
+in _PDCLIB_stdinit.c.
diff --git a/src/pdclib/Makefile b/src/pdclib/Makefile
new file mode 100644 (file)
index 0000000..ebb0ad4
--- /dev/null
@@ -0,0 +1,126 @@
+# This is a list of all non-source files that are part of the distribution.
+AUXFILES := Makefile Readme.txt
+
+# Directories belonging to the project
+PROJDIRS := functions include platform/stmos
+# Directory where binaries should be written
+BUILDDIR := .
+# All source files of the project
+SRCFILES := $(shell find -L $(PROJDIRS) -type f -name "*.c")
+# All header files of the project
+HDRFILES := $(shell find -L $(PROJDIRS) -type f -name "*.h")
+# All object files in the library
+OBJFILES := $(patsubst %.c,$(BUILDDIR)/%.o,$(SRCFILES))
+# All test drivers (.t)
+TSTFILES := $(patsubst %.c,$(BUILDDIR)/%_t,$(SRCFILES))
+# All regression test drivers (.r)
+REGFILES := $(patsubst %.c,$(BUILDDIR)/%_r,$(SRCFILES))
+# All library dependency files (.d)
+DEPFILES := $(patsubst %.c,$(BUILDDIR)/%.d,$(SRCFILES))
+# All test driver dependency files (_t.d)
+TSTDEPFILES := $(patsubst %,$(BUILDDIR)/%.d,$(TSTFILES))
+# All regression test driver dependency files (_r.d)
+REGDEPFILES := $(patsubst %,$(BUILDDIR)/%.d,$(REGFILES))
+# All files belonging to the source distribution
+ALLFILES := $(SRCFILES) $(HDRFILES) $(AUXFILES)
+
+WARNINGS := -Wall -Wextra -pedantic -Wno-unused-parameter -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wno-long-long -Wuninitialized -Wstrict-prototypes -Wdeclaration-after-statement
+CFLAGS := -fno-builtin -g -std=c99 -I./testing -I../user -I./platform/stmos/include $(WARNINGS) $(USERFLAGS)
+
+.PHONY: all clean srcdist tests testdrivers regtests regtestdrivers todos fixmes help
+
+all: $(BUILDDIR)/pdclib.a #testdrivers regtestdrivers
+       #@echo
+       #@echo "========================"
+       #@echo "Executing library tests:"
+       #@echo "========================"
+       #@echo
+       #@$(MAKE) tests | grep -v "^ TST" | grep -v "^Failed"
+       #@echo
+       #@echo "==========================="
+       #@echo "Executing regression tests:"
+       #@echo "==========================="
+       #@echo
+       #@$(MAKE) regtests | grep -v "^ RTST" | grep -v "^Failed"
+       #@echo
+       #@echo "========"
+       #@echo "FIXME's:"
+       #@echo "========"
+       #@echo
+       #@$(MAKE) fixmes
+       #@echo
+       #@echo "======="
+       #@echo "TODO's:"
+       #@echo "======="
+       #@echo
+       #@$(MAKE) todos | head
+       #@echo "..."
+
+$(BUILDDIR)/pdclib.a: $(OBJFILES)
+       @echo " AR      $@"
+       @ar rc $(BUILDDIR)/pdclib.a $?
+       @echo
+
+tests: testdrivers
+       -@rc=0; count=0; failed=""; for file in $(TSTFILES); do echo " TST      $$file"; $$file; test=$$?; if [ $$test != 0 ]; then rc=`expr $$rc + $$test`; failed="$$failed $$file"; fi; count=`expr $$count + 1`; done; echo; echo "Tests executed (linking PDCLib): $$count  Tests failed: $$rc"; echo; for file in $$failed; do echo "Failed: $$file"; done; echo
+
+testdrivers: $(TSTFILES)
+       @echo
+
+regtests: regtestdrivers
+       -@rc=0; count=0; failed=""; for file in $(REGFILES); do echo " RTST     $$file"; $$file; test=$$?; if [ $$test != 0 ]; then rc=`expr $$rc + $$test`; failed="$$failed $$file"; fi; count=`expr $$count + 1`; done; echo; echo "Tests executed (linking system libc): $$count  Tests failed: $$rc"; echo; for file in $$failed; do echo "Failed: $$file"; done; echo
+
+regtestdrivers: $(REGFILES)
+       @echo
+
+-include $(DEPFILES) $(TSTDEPFILES) $(REGDEPFILES)
+
+clean:
+       -@$(RM) $(wildcard $(OBJFILES) $(DEPFILES) $(TSTFILES) $(TSTDEPFILES) $(REGFILES) $(REGDEPFILES) $(BUILDDIR)/pdclib.a pdclib.tgz scanf_testdata_*)
+
+srcdist:
+       @tar czf pdclib.tgz $(ALLFILES)
+
+todos:
+       -@for file in $(ALLFILES:Makefile=); do grep -H TODO $$file; done; true
+
+fixmes:
+       -@for file in $(ALLFILES:Makefile=); do grep -H FIXME $$file; done; true
+
+help:
+       @echo "Available make targets:"
+       @echo
+       @echo "all              - build pdclib.a"
+       @echo "clean            - remove all object files, dependency files and test drivers"
+       @echo "srcdist          - build pdclib.tgz (source tarball)"
+       @echo "tests            - build and run test drivers (link pdclib.a)"
+       @echo "  testdrivers    - build but do not run test drivers"
+       @echo "regtests         - build and run regression test drivers (link system clib)"
+       @echo "  regtestdrivers - build but do not run regression test drivers"
+       @echo "todos            - list all TODO comments in the sources"
+       @echo "fixmes           - list all FIXME comments in the sources"
+       @echo "%.o              - build an individual object file"
+       @echo "%.t              - build an individual test driver"
+       @echo "%.r              - build an individual regression test driver"
+       @echo "help             - print this list"
+       @echo
+       @echo "Any additional compiler flags you want to use can be passed as USERFLAGS"
+       @echo "(Usage: USERFLAGS=\"flags\" make [...])."
+       @echo
+       @echo "If you want to build out-of-source, you can specify BUILDDIR"
+       @echo "(Usage: make [...] BUILDDIR=/path/to/binaries/)."
+
+$(BUILDDIR)/%.o: %.c Makefile
+       @echo " CC      $(patsubst functions/%,%,$@)"
+       @mkdir -p $(dir $@)
+       @$(CC) $(CFLAGS) -MMD -MP -I./include -c $< -o $@
+
+$(BUILDDIR)/%_t: %.c Makefile $(BUILDDIR)/pdclib.a
+       @echo " CC      $(patsubst functions/%,%,$@)"
+       @mkdir -p $(dir $@)
+       @$(CC) $(CFLAGS) -MMD -MP -DTEST -I./include $< $(BUILDDIR)/pdclib.a -o $@
+
+$(BUILDDIR)/%_r: %.c Makefile
+       @echo " CC      $(patsubst functions/%,%,$@)"
+       @mkdir -p $(dir $@)
+       @$(CC) $(CFLAGS) -Wno-deprecated-declarations -Wno-format -MMD -MP -DTEST -DREGTEST $< -o $@
diff --git a/src/pdclib/Notes.txt b/src/pdclib/Notes.txt
new file mode 100644 (file)
index 0000000..b4efb8b
--- /dev/null
@@ -0,0 +1,104 @@
+Credits
+=======
+
+The vast majority of PDCLib is original work by me. I felt it was the only way
+to ensure that the code was indeed free of third-party rights, and thus free to
+be released into the Public Domain.
+
+Another issue was that of coding style, quality and stability of the code, and
+the urge to really understand every miniscule part of the code as to be able to
+maintain it well once v1.0 has been released.
+
+That is not to say there are no credits to be given. To the contrary:
+
+Paul Edwards (author of the PDPCLIB), for inspirational code fragments, thanks.
+
+P.J. Plauger (author of "The Standard C Library"), for a book without which I
+would not have been able to create PDCLib at this quality, thanks.
+
+Paul Bourke (author of mathlib), for allowing me access to a complete math
+library under public domain terms so I could use it as a reference, thanks.
+
+Peter ("Candy") Bindels (netizen of osdev.org), who located a copy of Cody
+& Waite's "Software Manual for the Elementary Functions" for me and saved me
+serious cash in the process, thanks.
+
+Michael Moody, who contributed the generic implementation for <stdarg.h> to
+the Public Domain which can now be found in <_PDCLIB_config.h>, thanks.
+
+Rod Pemberton, for pointing out several flaws in early versions of PDCLib and
+giving other valuable hints, thanks.
+
+Brian Damgaard, for a very friendly exchange over the fine details of the
+Quicksort algorithm and its implementation in PDCLib, thanks.
+
+Rink Springer, for very precise bug reports including patches, a heads-up on
+the capabilities of PDCLib when I most needed it, and for pushing me back in
+the driver's seat, thanks.
+
+Everyone involved in the first, "public" attempt at PDCLib, for bearing with me
+when I restarted from scratch, thanks.
+
+Everyone bearing with me during the "stdio block", a period of many years in
+which PDCLib received not a single update because I was stuck and could not
+find the time and energy to work it out.
+
+Lennart Frid�n and Sammy Nordstr�m, who have been great pals even after I sunk
+some other project that had eaten countless hours of work between the three of
+us, thanks.
+
+My wife, daughter, and son for sharing husband and daddy with this strange
+machine, thanks.
+
+
+Style
+=====
+
+I followed a set of guidelines in creating PDCLib. If you find some piece that
+does not adhere to them, that's a bug worth reporting. I mean it. I am a bit
+obsessive when it comes to coding style. ;-)
+
+- All the stuff that is not part of the standard specification is "hidden" in
+  the _PDCLIB_* namespace - functions, variables, macros, files, directories.
+  This is to make it easier to distinguish between what the standard dictates
+  and what I added to make PDCLib work.
+
+- I always try to minimize the use of local variables. Wherever possible I used
+  parameters passed by-value directly, and deferred declaration of locals to the
+  innermost block of statements, in hopes that it might reduce memory footprint
+  when the library is compiled with a compiler that is not that great at
+  optimization.
+
+- Every function, every static data item that could possibly be shared, got its
+  own implementation file. This means the library itself is probably larger than
+  strictly necessary, and might take a couple of clock cycles longer to link,
+  but it reduces size of object files and executables.
+
+- Where possible, I tried to share functionality between similar functions (as
+  can be seen in the atoi() and strtol() function families). This means one or
+  two additional function calls, but again reduces memory footprint and eases
+  maintenance of the library.
+
+- Function arguments are named exactly as in the standard document.
+
+- The standard is taken quite literally in places. For example, the default
+  implementations of memcpy() really copies char-wise. This runs contrary to
+  earlier claims of performance, but is consistent with the *letter* of the
+  standard, and you will probably use your compiler builtins (through a platform
+  overlay) anyhow.
+
+- PDCLib code has no bias towards POSIX; indeed the absence of POSIX tidbits is
+  one of its hallmarks. However, PDCLib also has no bias *against* POSIX, and
+  when one platform abstraction is as good as another, I chose the POSIX one for
+  sheer familiarity. (This is mainly referring to naming and parameter lists of
+  OS "glue" functions.)
+
+- Identifiers are tersely named, but not cryptically abbreviated, and should be
+  intuitive enough to allow easy understanding of PDCLib inner workings.
+
+- I disagree with the notion that good code needs no comments. Code tells you
+  *how*, but not the *why*, and you have to figure out the *what* yourself. So
+  I added comments to every nontrivial piece of code explaining my motives and
+  hopefully keeping overly ambitious editors from repeating my mistakes. The
+  header files especially should be self-documenting to the point of being a
+  suitable replacement for any library reference book you might be using.
diff --git a/src/pdclib/Readme.txt b/src/pdclib/Readme.txt
new file mode 100644 (file)
index 0000000..9a1b335
--- /dev/null
@@ -0,0 +1,183 @@
+PDCLib - Public Domain C Library
+================================
+
+License
+-------
+
+PDCLib is distributed unter the Creative Commons CC0 License. You
+should have received a copy of the full legal text of this license
+as part of this distribution (COPYING.CC0). It is also available at
+
+https://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+The following is a human-readable summary of that license.
+
+                       No Copyright
+
+The person who associated a work with this deed has dedicated the
+work to the public domain by waiving all of his or her rights to
+the work worldwide under copyright law, including all related and
+neighboring rights, to the extent allowed by law.
+
+You can copy, modify, distribute and perform the work, even for
+commercial purposes, all without asking permission. See Other
+Information below.
+
+                    Other Information
+
+In no way are the patent or trademark rights of any person affected
+by CC0, nor are the rights that other persons may have in the work
+or in how the work is used, such as publicity or privacy rights.
+
+Unless expressly stated otherwise, the person who associated a work
+with this deed makes no warranties about the work, and disclaims
+liability for all uses of the work, to the fullest extent permitted
+by applicable law.
+
+When using or citing the work, you should not imply endorsement by
+the author or the affirmer.
+
+What is it
+----------
+
+This is a C Standard Library. Nothing more, nothing less. No POSIX
+or other extensions, just what's defined in ISO/IEC 9899.
+
+(Well, this is what it will be when the 1.0 release comes out. See
+the "Development Status" section to see what's implemented so far.)
+
+Internals
+---------
+
+As a namespace convention, everything (files, typedefs, functions,
+macros) not defined in ISO/IEC 9899 is prefixed with _PDCLIB.
+The standard defines any identifiers starting with '_' and a capital
+letter as reserved for the implementation, and since the chances of
+your compiler using an identifier in the _PDCLIB range are slim,
+any strictly conforming application should work with this library.
+
+PDCLib consists of several parts:
+
+1) standard headers;
+2) implementation files for standard functions;
+3) internal header files keeping complex stuff out of the standard
+   headers;
+4) the central, platform-specific file _PDCLIB_config.h;
+5) platform-specific implementation files;
+6) platform-specific, optimized "overlay" implementations (optional).
+
+The standard headers (in ./include/) only contain what they are
+defined to contain. Where additional logic or macro magic is
+necessary, that is deferred to the internal files. This has been done
+so that the headers are actually educational as to what they provide
+(as opposed to how the library does it).
+
+There is a seperate implementation file (in ./function/{header}/) for
+every function defined by the standard, named {function}.c. Not only
+does this avoid linking in huge amounts of unused code when you use
+but a single function, it also allows the optimization overlay to work
+(see below).
+
+(The directory ./functions/_PDCLIB/ contains internal and helper
+functions that are not part of the standard.)
+
+Then there are internal header files (in ./include/pdclib/), which
+contain all the "black magic" and "code fu" that was kept out of the
+standard headers. You should not have to touch them if you want to
+adapt PDCLib to a new platform. Note that, if you *do* have to touch
+them, I would consider it a serious design flaw, and would be happy
+to fix it in the next PDCLib release. Any adaption work should be
+covered by the steps detailed below.
+
+For adapting PDCLib to a new platform (the trinity of CPU, operating
+system, and compiler), make a copy of ./platform/example/ named
+./platform/{your_platform}/, and modify the files of your copy to suit
+the constraints of your platform. When you are done, copy the contents
+of your platform directory over the source directory structure
+of PDCLib (or link them into the appropriate places). That should be
+all that is actually required to make PDCLib work for your platform.
+
+Of course, your platform might provide more efficient replacements
+for the generic implementations offered by PDCLib. The math functions
+are an especially "juicy" target for optimization - while PDCLib does
+provide generic implementations for each of them, there are usually
+FPU opcodes that do the same job, only orders of magnitude faster. For
+this, you might want to create an "optimization overlay" for PDCLib.
+
+Optimization Overlay
+--------------------
+
+The basic idea of PDCLib is to provide a generic implementation that
+is useable even on platforms I have never heard of - for example, the
+OS and/or compiler *you* just wrote and now need a C library for. That
+is actually what PDCLib was written for: To provide a C library for
+compiler and OS builders that do not want the usual baggage of POSIX
+and GNU extensions, licensing considerations etc. etc.
+
+Thus, PDCLib provides generic implementations. They do work, and do
+so correctly, but they are not very efficient when compared to hand-
+crafted assembler or compiler build-ins. So I wanted to provide a
+means to modify PDCLib to run more efficiently on a given platform,
+without cluttering the main branch with tons of #ifdef statements and
+"featureset #defines" that grow stale quickly.
+
+The solution is the "optimization overlay". Every function has its
+own implementation file, which makes it possible to replace them
+piecemeal by copying a platform-specific overlay over the main PDCLib
+branch to create a PDCLib adapted / optimized for the platform in
+question. That overlay could be part of the PDCLib source tree (for
+established platforms where maintainers won't bother with PDCLib), or
+part of that platform's source tree (for under-development platforms
+PDCLib maintainers won't bother with).
+
+So, to use PDCLib on your given platform, you unpack PDCLib (as you
+obviously have done already since you are reading this), and copy
+the overlay for your platform over the PDCLib source tree structure.
+
+Development Status
+------------------
+
+Note that pre-v1.0 "releases" are internal milestones only, and that
+you are strongly encouraged to use the latest source snapshot at all
+times.
+
+v0.1 - 2004-12-12
+Freestanding-only C99 implementation without any overlay, and missing
+the INTN_C() / UINTN_C() macros. <float.h> still has the enquire.c
+values hardcoded into it; not sure whether to include enquire.c in the
+package, to leave <float.h> to the overlay, or devise some parameterized
+macro magic as for <limits.h> / <stdint.h>. Not thoroughly tested, but
+I had to make the 0.1 release sometime so why not now.
+
+v0.2 - 2005-01-12
+Adds implementations for <string.h> (excluding strerror()), INTN_C() /
+UINTN_C() macros, and some improvements in the internal headers.
+Test drivers still missing, but added warnings about that.
+
+v0.3 - 2005-11-21
+Adds test drivers, fixes some bugs in <string.h>.
+
+v0.4 - 2005-02-06
+Implementations for parts of <stdlib.h>. Still missing are the floating
+point conversions, and the wide-/multibyte-character functions.
+
+v0.4.1 - 2006-11-16
+With v0.5 (<stdio.h>) taking longer than expected, v0.4.1 was set up as
+a backport of bugfixes in the current development code.
+- #1  realloc( NULL, size ) fails           (fixed)
+- #2  stdlib.h - insufficient documentation (fixed)
+- #4  Misspelled name in credits            (fixed)
+- #5  malloc() splits off too-small nodes   (fixed)
+- #6  qsort() stack overflow                (fixed)
+- #7  malloc() bug in list handling         (fixed)
+- #8  strncmp() does not terminate at '\0'  (fixed)
+- #9  stdint.h dysfunctional                (fixed)
+- #10 NULL redefinition warnings            (fixed)
+
+v0.5 - 2010-12-22
+Implementations for <inttypes.h>, <errno.h>, most parts of <stdio.h>,
+and strerror() from <string.h>.
+Still no locale / wide-char support. Enabled all GCC compiler warnings I
+could find, and fixed everything that threw a warning. (You see this,
+maintainers of Open Source software? No warnings whatsoever. Stop telling
+me it cannot be done.) Fixed all known bugs in the v0.4 release.
diff --git a/src/pdclib/auxiliary/uctype/Makefile b/src/pdclib/auxiliary/uctype/Makefile
new file mode 100644 (file)
index 0000000..0d34b98
--- /dev/null
@@ -0,0 +1,48 @@
+TARGET := get-uctypes
+# All source files of the project
+SRCFILES := $(wildcard *.c)
+# All header files of the project
+HDRFILES := $(wildcard *.h)
+# All object files in the project
+OBJFILES := $(patsubst %.c,%.o,$(SRCFILES))
+# All test drivers (_t)
+TSTFILES := $(patsubst %.c,%_t,$(SRCFILES))
+# All dependency files (.d)
+DEPFILES := $(patsubst %.c,%.d,$(SRCFILES))
+# All test driver dependency files (_t.d)
+TSTDEPFILES := $(patsubst %,%.d,$(TSTFILES))
+# All test driver dependency files (_t.d)
+
+WARNINGS := -Wall -Wextra -pedantic -Wno-unused-parameter -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wno-long-long -Wuninitialized -Wstrict-prototypes -Wdeclaration-after-statement
+CFLAGS := -g -std=c99 $(WARNINGS) $(USERFLAGS) -I.
+
+.PHONY: all clean tests
+
+all: $(TARGET)
+
+$(TARGET): $(OBJFILES)
+       @echo " CC      $@"
+       @$(CC) $^ -o $@
+       @echo
+
+tests: testdrivers
+       -@rc=0; count=0; failed=""; for file in $(TSTFILES); do echo " TST     $$file"; ./$$file; test=$$?; if [ $$test != 0 ]; then rc=`expr $$rc + $$test`; failed="$$failed $$file"; fi; count=`expr $$count + 1`; done; echo; echo "Tests executed: $$count  Tests failed: $$rc"; echo; for file in $$failed; do echo "Failed: $$file"; done; echo
+
+testdrivers: $(TSTFILES)
+       @echo
+
+-include $(DEPFILES) $(TSTDEPFILES)
+
+clean:
+       -@$(RM) $(wildcard $(OBJFILES) $(DEPFILES) $(TSTFILES) $(TSTDEPFILES) $(TARGET) aux.a)
+
+%.o: %.c Makefile
+       @echo " CC      $@"
+       @$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
+
+%_t: %.c Makefile aux.a
+       @echo " CC      $@"
+       @$(CC) $(CFLAGS) -MMD -MP -DTEST $< aux.a -o $@
+
+aux.a: $(OBJFILES)
+       @ar rc $@ $^
diff --git a/src/pdclib/auxiliary/uctype/derived_properties.c b/src/pdclib/auxiliary/uctype/derived_properties.c
new file mode 100644 (file)
index 0000000..c024efe
--- /dev/null
@@ -0,0 +1,300 @@
+/* derived properties
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "text_utilities.h"
+
+#include "derived_properties.h"
+
+#define LINE_BUFFER_SIZE 500u
+
+struct derived_properties_t * read_derived_properties( const char * filename )
+{
+    FILE * fh;
+    char buffer[ LINE_BUFFER_SIZE ];
+    struct derived_properties_t * dp = NULL;
+    size_t code_points = 0;
+    size_t properties = 0;
+    const char * code_point_count = "# Total code points: ";
+
+    if ( ( fh = fopen( filename, "r" ) ) == NULL )
+    {
+        fprintf( stderr, "Could not open '%s' for reading.\n", filename );
+        return NULL;
+    }
+
+    if ( ( check_file( fh, LINE_BUFFER_SIZE, ';', sizeof( derived_properties_fields ) / sizeof( int ), derived_properties_fields ) ) != (size_t)-1 )
+    {
+        while ( fgets( buffer, LINE_BUFFER_SIZE, fh ) )
+        {
+            if ( strstr( buffer, code_point_count ) != NULL )
+            {
+                size_t count = strtoul( buffer + strlen( code_point_count ), NULL, 10 );
+
+                if ( ( SIZE_MAX - count ) < code_points )
+                {
+                    fprintf( stderr, "Summing up total code points in '%s' would overflow.\n", filename );
+                    fclose( fh );
+                    return NULL;
+                }
+
+                code_points += count;
+                ++properties;
+            }
+        }
+
+        rewind( fh );
+
+        if ( ( dp = malloc( sizeof( struct derived_properties_t ) ) ) )
+        {
+            dp->count = properties;
+
+            if ( ( dp->name = calloc( properties, sizeof( char * ) ) ) )
+            {
+                if ( ( dp->begin = calloc( properties, sizeof( size_t ) ) ) )
+                {
+                    if ( ( dp->end = calloc( properties, sizeof( size_t ) ) ) )
+                    {
+                        if ( ( dp->code_points = malloc( code_points * sizeof( size_t ) ) ) )
+                        {
+                            char * p;
+                            char * range;
+                            properties = 0; /* Re-using the variable */
+                            code_points = 0; /* Re-using the variable */
+
+                            while ( fgets( buffer, LINE_BUFFER_SIZE, fh ) )
+                            {
+                                /* Remove comments */
+                                if ( ( p = strchr( buffer, '#' ) ) != NULL )
+                                {
+                                    *p = '\0';
+                                }
+
+                                /* > 0 because of newline */
+                                if ( strlen( buffer ) > 1 )
+                                {
+                                    size_t first;
+                                    size_t last;
+
+                                    range = next_token( buffer, ';' );
+                                    p = next_token( NULL, ';' );
+
+                                    if ( ! range || ! p )
+                                    {
+                                        size_t i;
+
+                                        fprintf( stderr, "Parse error, malformed input.\n" );
+
+                                        for ( i = 0; i < properties; ++i )
+                                        {
+                                            free( dp->name[ i ] );
+                                        }
+
+                                        free( dp->name );
+                                        free( dp->begin );
+                                        free( dp->end );
+                                        free( dp->code_points );
+                                        free( dp );
+                                        return NULL;
+                                    }
+
+                                    /* If we got to a new property (except the first) */
+                                    if ( dp->name[ properties ] && strcmp( p, dp->name[ properties ] ) )
+                                    {
+                                        /* Index into ->code_points where the previous property ends */
+                                        dp->end[ properties ] = code_points;
+                                        ++properties;
+                                    }
+
+                                    /* If we got to a new property, even the first */
+                                    if ( dp->name[ properties ] == NULL )
+                                    {
+                                        dp->name[ properties ] = malloc( strlen( p ) + 1 );
+                                        strcpy( dp->name[ properties ], p );
+
+                                        /* Index into ->code_points where this property begins */
+                                        dp->begin[ properties ] = code_points;
+                                    }
+
+                                    /* Re-using p, as we have done everything related to the property
+                                       name at this point.
+                                    */
+                                    first = strtoul( range, &p, 16 );
+
+                                    if ( *p == '\0' )
+                                    {
+                                        last = first;
+                                    }
+                                    else
+                                    {
+                                        while ( *p && ! isxdigit( *p ) )
+                                        {
+                                            ++p;
+                                        }
+
+                                        last = strtoul( p, NULL, 16 );
+
+                                        if ( last <= first )
+                                        {
+                                            size_t i;
+
+                                            fprintf( stderr, "Parse error, malformed input.\n" );
+
+                                            for ( i = 0; i < properties; ++i )
+                                            {
+                                                free( dp->name[ i ] );
+                                            }
+
+                                            free( dp->name );
+                                            free( dp->begin );
+                                            free( dp->end );
+                                            free( dp->code_points );
+                                            free( dp );
+                                            return NULL;
+                                        }
+                                    }
+
+                                    for ( ; first <= last; ++first )
+                                    {
+                                        dp->code_points[ code_points++ ] = first;
+                                    }
+                                }
+                            }
+
+                            /* Have to end the last property as well */
+                            dp->end[ properties ] = code_points;
+                        }
+                        else
+                        {
+                            fprintf( stderr, "Memory allocation failure.\n" );
+                            free( dp->name );
+                            free( dp->begin );
+                            free( dp->end );
+                            free( dp );
+                            dp = NULL;
+                        }
+                    }
+                    else
+                    {
+                        fprintf( stderr, "Memory allocation failure.\n" );
+                        free( dp->name );
+                        free( dp->begin );
+                        free( dp );
+                        dp = NULL;
+                    }
+                }
+                else
+                {
+                    fprintf( stderr, "Memory allocation failure.\n" );
+                    free( dp->name );
+                    free( dp );
+                    dp = NULL;
+                }
+            }
+            else
+            {
+                fprintf( stderr, "Memory allocation failure.\n" );
+                free( dp );
+                dp = NULL;
+            }
+        }
+        else
+        {
+            fprintf( stderr, "Memory allocation failure.\n" );
+        }
+    }
+
+    fclose( fh );
+    return dp;
+}
+
+static int comp( const void * l, const void *  r )
+{
+    const size_t * lhs = l;
+    const size_t * rhs = r;
+
+    return ( *lhs < *rhs ) ? -1 : ( *lhs > *rhs ) ? 1 : 0;
+}
+
+int lookup_property( struct derived_properties_t * dp, const char * property, size_t codepoint )
+{
+    size_t i;
+
+    for ( i = 0; i < dp->count; ++i )
+    {
+        /* Look for the requested property */
+        if ( strcmp( dp->name[ i ], property ) == 0 )
+        {
+            size_t cp = dp->begin[ i ];
+
+            return bsearch( &codepoint, dp->code_points + cp, dp->end[ i ] - cp, sizeof( size_t ), comp ) != NULL;
+        }
+    }
+
+    return 0;
+}
+
+void release_derived_properties( struct derived_properties_t * dp )
+{
+    size_t i;
+
+    for ( i = 0; i < dp->count; ++i )
+    {
+        free( dp->name[ i ] );
+    }
+
+    free( dp->name );
+    free( dp->begin );
+    free( dp->end );
+    free( dp->code_points );
+    free( dp );
+}
+
+#ifdef TEST
+
+#include "test.h"
+
+int main( void )
+{
+    FILE * fh = fopen( "test.txt", "wb+" );
+    struct derived_properties_t * dp;
+
+    TESTCASE( fh != NULL );
+    TESTCASE( fprintf( fh, "0000..0006 ; Test1 \n" ) == 20 );
+    TESTCASE( fprintf( fh, "# Total code points: 7\n" ) == 23 );
+    TESTCASE( fprintf( fh, "0001;Test2\n" ) == 11 );
+    TESTCASE( fprintf( fh, "# Total code points: 1\n" ) == 23 );
+
+    fclose( fh );
+    dp = read_derived_properties( "test.txt" );
+
+    TESTCASE( dp != NULL );
+    TESTCASE( dp->count == 2 );
+    TESTCASE( ! strcmp( dp->name[0], "Test1" ) );
+    TESTCASE( ! strcmp( dp->name[1], "Test2" ) );
+
+    TESTCASE( lookup_property( dp, "Test1", 0 ) );
+    TESTCASE( lookup_property( dp, "Test1", 6 ) );
+    TESTCASE( ! lookup_property( dp, "Test1", 7 ) );
+
+    TESTCASE( ! lookup_property( dp, "Test2", 0 ) );
+    TESTCASE( lookup_property( dp, "Test2", 1 ) );
+    TESTCASE( ! lookup_property( dp, "Test2", 2 ) );
+
+    TESTCASE( ! lookup_property( dp, "Test", 0 ) );
+    TESTCASE( ! lookup_property( dp, "Test3", 0 ) );
+
+    release_derived_properties( dp );
+    remove( "test.txt" );
+
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/auxiliary/uctype/derived_properties.h b/src/pdclib/auxiliary/uctype/derived_properties.h
new file mode 100644 (file)
index 0000000..d06ac84
--- /dev/null
@@ -0,0 +1,34 @@
+/* derived properties
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef DERIVED_PROPERTIES
+#define DERIVED_PROPERTIES DERIVED_PROPERTIES
+
+#include <stddef.h>
+
+/* https://www.unicode.org/reports/tr44/#DerivedCoreProperties.txt */
+
+struct derived_properties_t
+{
+    size_t count;
+    char * * name;
+    size_t * begin;
+    size_t * end;
+    size_t * code_points;
+};
+
+static const int derived_properties_fields[] = {
+    -1, /* code point or code point range */
+    -1  /* property name */
+};
+
+struct derived_properties_t * read_derived_properties( const char * filename );
+
+int lookup_property( struct derived_properties_t * dp, const char * property, size_t codepoint );
+
+void release_derived_properties( struct derived_properties_t * dp );
+
+#endif
diff --git a/src/pdclib/auxiliary/uctype/main.c b/src/pdclib/auxiliary/uctype/main.c
new file mode 100644 (file)
index 0000000..ef60bb4
--- /dev/null
@@ -0,0 +1,300 @@
+/* main
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef TEST
+#include <wctype.h>
+#endif
+
+#include "uctype.h"
+
+/* RLE Compressed Output
+
+   <wctype.h> requires *11* flags:
+   iswupper, iswlower, iswalpha, iswdigit, iswblank, iswspace,
+   iswcntrl, iswxdigit, iswgraph, iswprint.
+   iswalnum (the 12th classification function) is *defined* as
+   iswalpha || iswdigit. And iswdigit and iswxdigit are defined
+   in a rather restrictive way that can be expressed by simple
+   ranges instead of lookup tables. And isgraph is defined as
+   isprint && ! isspace (which is trivial to check that it holds
+   true for all the records provided by get-unicode-ctype, at
+   least up to Unicode 11.0).
+   So we have only 8 flags we actually need in a lookup... nicely
+   reducing the storage requirement to an unsigned char.
+
+   Another trick is to express toupper / tolower as offsets
+   instead of absolute values, which will allow run-time-length
+   compression of the data.
+*/
+
+struct output_record_t
+{
+    size_t codepoint;
+    int toupper_diff;
+    int tolower_diff;
+    unsigned char flags;
+};
+
+#ifdef TEST
+static void print_codepoint_age( size_t codepoint, struct derived_properties_t * age )
+{
+    size_t index = age->count;
+
+    while ( index )
+    {
+        --index;
+
+        if ( lookup_property( age, age->name[ index ], codepoint ) )
+        {
+            printf( "%s", age->name[ index ] );
+            return;
+        }
+    }
+}
+
+static void print_additional_codepoint_info( size_t codepoint, struct unicode_record_t * ur )
+{
+    printf( " - %s",      ur->name );
+    printf( " - %s",      ur->general_category );
+    printf( " - %d",      ur->canonical_combining_class );
+    printf( " - %s",      ur->bidi_class );
+    printf( " - %s",      ( ur->decomposition ? ur->decomposition : "NULL" ) );
+    printf( " - %d",      ur->numeric_type );
+    printf( " - %d",      ur->numeric_digit );
+    printf( " - %s",      ( ur->numeric_value ? ur->numeric_value : "NULL" ) );
+    printf( " - %c",      ur->bidi_mirrored );
+    printf( " - U+%06zx", ur->simple_uppercase_mapping );
+    printf( " - U+%06zx", ur->simple_lowercase_mapping );
+    printf( " - U+%06zx", ur->simple_titlecase_mapping );
+    printf( " - " );
+
+    /* Implementations are at liberty to return non-zero values other
+       than 1 for "true".
+    */
+    printf( "%d", ( iswupper( codepoint ) )  ? 1 : 0 );
+    printf( "%d", ( iswlower( codepoint ) )  ? 1 : 0 );
+    printf( "%d", ( iswalpha( codepoint ) )  ? 1 : 0 );
+    printf( "%d", ( iswdigit( codepoint ) )  ? 1 : 0 );
+    printf( "%d", ( iswblank( codepoint ) )  ? 1 : 0 );
+    printf( "%d", ( iswspace( codepoint ) )  ? 1 : 0 );
+    printf( "%d", ( iswcntrl( codepoint ) )  ? 1 : 0 );
+    printf( "%d", ( iswxdigit( codepoint ) ) ? 1 : 0 );
+    printf( "%d", ( iswgraph( codepoint ) )  ? 1 : 0 );
+    printf( "%d", ( iswprint( codepoint ) )  ? 1 : 0 );
+    printf( "%d", ( iswpunct( codepoint ) )  ? 1 : 0 );
+}
+
+static void print_codepoint_info( size_t codepoint, struct unicode_record_t * ur, struct derived_properties_t * core, struct derived_properties_t * age )
+{
+    int rc;
+    int equal = 1;
+
+    if ( codepoint % 20 == 0 )
+    {
+        printf( "   cp      up       low    UlA0_WCXGP.\n" );
+    }
+
+    printf( "U+%06zX ", codepoint );
+    rc = get_towupper( codepoint, ur );       equal &= ( (unsigned)rc == towupper( codepoint ) ); printf( "U+%06X ", rc );
+    rc = get_towlower( codepoint, ur );       equal &= ( (unsigned)rc == towlower( codepoint ) ); printf( "U+%06X ", rc );
+    rc = get_iswupper( codepoint, ur, core ); equal &= ( iswupper( codepoint )  ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+    rc = get_iswlower( codepoint, ur, core ); equal &= ( iswlower( codepoint )  ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+    rc = get_iswalpha( codepoint, ur, core ); equal &= ( iswalpha( codepoint )  ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+    rc = get_iswdigit( codepoint );           equal &= ( iswdigit( codepoint )  ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+    rc = get_iswblank( codepoint, ur );       equal &= ( iswblank( codepoint )  ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+    rc = get_iswspace( codepoint, ur );       equal &= ( iswspace( codepoint )  ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+    rc = get_iswcntrl( codepoint, ur );       equal &= ( iswcntrl( codepoint )  ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+    rc = get_iswxdigit( codepoint );          equal &= ( iswxdigit( codepoint ) ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+    rc = get_iswgraph( codepoint, ur );       equal &= ( iswgraph( codepoint )  ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+    rc = get_iswprint( codepoint, ur );       equal &= ( iswprint( codepoint )  ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+    rc = get_iswpunct( codepoint, ur, core ); equal &= ( iswpunct( codepoint )  ? 1 : 0 == rc );  printf( "%d", rc ? 1 : 0 );
+
+    if ( codepoint != ur->code_point )
+    {
+        /* These two may only differ for codepoint "ranges", which are
+           signified by "..., First>" / "..., Last>" pairs in UnicodeData.
+           If they differ and it's NOT a range, that is an error of some
+           kind.
+        */
+        if ( ! strstr( ur->name, ", Last>" ) || codepoint < ( ur - 1 )->code_point )
+        {
+            printf( " ERROR: U+%06zX != U+%06zX outside of First, Last codepoint range.  ", codepoint, ur->code_point );
+        }
+    }
+
+    if ( ! equal )
+    {
+        printf( " ERROR: Deviation from SysLib: " );
+        print_codepoint_age( codepoint, age );
+        print_additional_codepoint_info( codepoint, ur );
+    }
+
+    printf( "\n" );
+}
+#else
+static struct output_record_t get_output_record( size_t codepoint, struct unicode_record_t * ur, struct derived_properties_t * core )
+{
+    struct output_record_t rc;
+    char buffer[ 9 ];
+
+    rc.codepoint = codepoint;
+    rc.toupper_diff = get_towupper( codepoint, ur ) - codepoint;
+    rc.tolower_diff = get_towlower( codepoint, ur ) - codepoint;
+
+    sprintf( buffer, "%zu%zu%zu%zu%zu%zu%zu%zu",
+             get_iswupper( codepoint, ur, core ),
+             get_iswlower( codepoint, ur, core ),
+             get_iswalpha( codepoint, ur, core ),
+             get_iswblank( codepoint, ur ),
+             get_iswspace( codepoint, ur ),
+             get_iswcntrl( codepoint, ur ),
+             get_iswprint( codepoint, ur ),
+             get_iswpunct( codepoint, ur, core ) );
+
+    rc.flags = strtoul( buffer, NULL, 2 );
+
+    return rc;
+}
+#endif
+
+int main( int argc, char * argv[] )
+{
+    struct unicode_data_t * ud;
+    struct derived_properties_t * core;
+#ifdef TEST
+    struct derived_properties_t * age;
+#endif
+
+    char * locale = setlocale( LC_CTYPE, "" );
+
+    if ( ! strstr( locale, "UTF-8" ) || strstr( locale, "TR" ) || strstr( locale, "tr" ) )
+    {
+        fprintf( stderr, "Need non-turkish locale to work correctly.\n'%s' will not do.\n", locale );
+        return EXIT_FAILURE;
+    }
+
+    if ( argc != 4 )
+    {
+        printf( "\n"
+                "Usage: get-uctypes <UnicodeData.txt> <DerivedCoreProperties.txt>"
+#ifdef TEST
+                " <DerivedAge.txt>"
+#endif
+                "\n\n"
+                "Generates lookup tables for <wctype.h> from files available from\n"
+                "the Unicode Consortium.\n"
+                "\n"
+                "The required files can be retrieved from the following URL:\n"
+                "\n"
+                "http://www.unicode.org/Public/UCD/latest/ucd/\n"
+                "\n" );
+        return EXIT_FAILURE;
+    }
+
+    if ( ( ud = read_unicode_data( argv[ 1 ] ) ) != NULL )
+    {
+        if ( ( core = read_derived_properties( argv[ 2 ] ) ) != NULL )
+        {
+#ifndef TEST
+            /* Print (to file) RLE compressed data */
+            FILE * fh = fopen( "ctype.dat", "wb" );
+
+            if ( fh )
+            {
+                size_t codepoint = 0;
+                size_t i = 0;
+                struct unicode_record_t * ur = &(ud->records[i]);
+                /* Name substring indicating a code point _range_ */
+                const char * last = ", Last>";
+
+                struct output_record_t previous = get_output_record( codepoint, ur, core );
+
+                fprintf( fh, "%zx ", previous.codepoint );
+
+                for ( codepoint = 1; codepoint < 0x10fffe; ++codepoint )
+                {
+                    struct output_record_t current;
+
+                    while ( codepoint > ur->code_point )
+                    {
+                        ur = &(ud->records[++i]);
+                    }
+
+                    if ( codepoint != ur->code_point && ( ur->name && ( strstr( ur->name, last ) != ( ur->name + strlen( ur->name ) - strlen( last ) ) ) ) )
+                    {
+                        /* Unregistered Code Point */
+                        continue;
+                    }
+
+                    current = get_output_record( codepoint, ur, core );
+
+                    /* RLE */
+                    if ( current.codepoint != previous.codepoint + 1 ||
+                         current.toupper_diff != previous.toupper_diff ||
+                         current.tolower_diff != previous.tolower_diff ||
+                         current.flags != previous.flags )
+                    {
+                        fprintf( fh, "%zx %d %d %hhx\n", previous.codepoint, previous.toupper_diff, previous.tolower_diff, previous.flags );
+                        fprintf( fh, "%zx ", current.codepoint );
+                    }
+
+                    previous = current;
+                }
+
+                fprintf( fh, "%zx %d %d %hhx\n", previous.codepoint, previous.toupper_diff, previous.tolower_diff, previous.flags );
+                fclose( fh );
+            }
+            else
+            {
+                fprintf( stderr, "Could not open 'ctype.dat' for writing.\n" );
+            }
+#else
+            if ( ( age = read_derived_properties( argv[ 3 ] ) ) != NULL )
+            {
+                /* Print (to screen) raw data comparing our results
+                   to the system library.
+                   Differences are often because the system library
+                   uses older data, which is why we add the age to
+                   the output.
+                */
+                size_t codepoint = 0;
+                size_t i = 0;
+                struct unicode_record_t * ur = &(ud->records[i]);
+                /* Name substring indicating a code point _range_ */
+                const char * last = ", Last>";
+
+                for ( codepoint = 0; codepoint < 0x10fffe; ++codepoint )
+                {
+                    while ( codepoint > ur->code_point )
+                    {
+                        ur = &(ud->records[++i]);
+                    }
+
+                    if ( codepoint != ur->code_point && ! name_ends_with( ur, last ) )
+                    {
+                        /* Unregistered Code Point */
+                        continue;
+                    }
+
+                    print_codepoint_info( codepoint, ur, core, age );
+                }
+
+                release_derived_properties( age );
+            }
+#endif
+
+            release_derived_properties( core );
+        }
+
+        release_unicode_data( ud );
+    }
+
+    return EXIT_SUCCESS;
+}
diff --git a/src/pdclib/auxiliary/uctype/test.h b/src/pdclib/auxiliary/uctype/test.h
new file mode 100644 (file)
index 0000000..3cd33a8
--- /dev/null
@@ -0,0 +1,19 @@
+/* test
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef TEST_H
+#define TEST_H TEST_H
+
+#include <stdio.h>
+
+#define NO_TESTDRIVER 0
+
+static int TEST_RESULTS = 0;
+
+#define TESTCASE( x ) if ( x ) {} \
+                      else { TEST_RESULTS += 1; printf( "FAILED: " __FILE__ ", line %d - %s\n", __LINE__, #x ); }
+
+#endif
diff --git a/src/pdclib/auxiliary/uctype/text_utilities.c b/src/pdclib/auxiliary/uctype/text_utilities.c
new file mode 100644 (file)
index 0000000..20973d9
--- /dev/null
@@ -0,0 +1,206 @@
+/* text utilities
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#include "text_utilities.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+char * trim( char * s )
+{
+    char * p;
+
+    /* Skip over leading whitespace */
+    while ( *s && isspace( *s ) )
+    {
+        ++s;
+    }
+
+    /* Trim trailing whitespace */
+    p = s;
+
+    while ( *p )
+    {
+        ++p;
+    }
+
+    while ( isspace( *(--p) ) )
+    {
+        *p = '\0';
+    }
+
+    return s;
+}
+
+char * next_token( char * s, char delim )
+{
+    static char * p = NULL;
+    char * rc;
+
+    if ( s != NULL )
+    {
+        /* Re-init the to-be-tokenized string */
+        p = s;
+    }
+
+    /* Remembering the start of the next token */
+    rc = p;
+
+    /* In case the function has not been initialized, or the previous
+       string been exhaused, do nothing.
+    */
+    if ( p )
+    {
+        /* Re-using s here */
+        if ( ( s = strchr( p, delim ) ) )
+        {
+            /* Null the delimiter */
+            *s = '\0';
+            /* Make the internal, static pointer point to the next token */
+            p = s + 1;
+        }
+        else
+        {
+            /* Delimiter not found, end-of-string reached. */
+            p = NULL;
+        }
+
+        /* Trim the result */
+        rc = trim( rc );
+    }
+
+    return rc;
+}
+
+size_t check_file( FILE * fh, size_t buffer_size, char delim, size_t fields, int const * widths )
+{
+    /* Dynamically allocated buffer */
+    char * buffer = malloc( buffer_size );
+    size_t lines = 0;
+
+    rewind( fh );
+
+    while ( fgets( buffer, buffer_size, fh ) )
+    {
+        size_t i;
+        char * p;
+
+        ++lines;
+
+        /* Check line for complete read */
+        if ( buffer[ strlen( buffer ) - 1 ] != '\n' )
+        {
+            fprintf( stderr, "Line %zu will not fit into a %zu-character buffer.\n", lines, buffer_size );
+            rewind( fh );
+            free( buffer );
+            return -1;
+        }
+
+        /* Remove comments */
+        if ( ( p = strchr( buffer, '#' ) ) != NULL )
+        {
+            *p = '\0';
+        }
+
+        /* > 1 because of newline */
+        if ( strlen( buffer ) > 1 )
+        {
+            /* Check field count and field widths */
+            p = next_token( buffer, delim );
+
+            for ( i = 0; i < fields; ++i )
+            {
+                if ( ! p )
+                {
+                    fprintf( stderr, "Line %zu contains less than %zu fields.\n", lines, fields );
+                    rewind( fh );
+                    free( buffer );
+                    return -1;
+                }
+
+                if ( widths[ i ] >= 0 && strlen( p ) >= (unsigned)widths[ i ] )
+                {
+                    fprintf( stderr, "Line %zu: Field %zu '%s' will not fit in a %d character string.\n", lines, i + 1, p, widths[ i ] );
+                    rewind( fh );
+                    free( buffer );
+                    return -1;
+                }
+
+                p = next_token( NULL, delim );
+            }
+
+            if ( p )
+            {
+                fprintf( stderr, "Line %zu contains more than %zu fields.\n", lines, fields );
+                rewind( fh );
+                free( buffer );
+                return -1;
+            }
+        }
+    }
+
+    /* Rewind, free the buffer, and report the number of lines */
+    rewind( fh );
+    free( buffer );
+    return lines;
+}
+
+#ifdef TEST
+
+#include "test.h"
+
+int main( void )
+{
+    FILE * fh = fopen( "test.txt", "wb+" );
+    int widths[] = { 4, 4, 4 };
+    char buffer[ 500 ];
+
+    /* check_file() (and as dependency, next_token() */
+
+    /* All ok */
+    TESTCASE( fprintf( fh, "%s;%s;%s\n", "1", "123", "12" ) == 9 );
+    TESTCASE( fprintf( fh, ";;\n" ) == 3 );
+    TESTCASE( check_file( fh, 10, ';', 3, widths ) == 2 );
+    /* Field 1 too long */
+    TESTCASE( fprintf( fh, "%s;%s;%s\n", "", "1234", "1" ) == 8 );
+    TESTCASE( check_file( fh, 10, ';', 3, widths ) == (size_t)-1 );
+    /* Too few fields */
+    TESTCASE( fprintf( fh, "%s;%s\n", "123", "123" ) == 8 );
+    TESTCASE( check_file( fh, 10, ';', 3, widths )== (size_t)-1 );
+    /* Too many fields */
+    TESTCASE( fprintf( fh, "%s;%s;%s;%s\n", "1", "1", "1", "1" ) == 8 );
+    TESTCASE( check_file( fh, 10, ';', 3, widths )== (size_t)-1 );
+    /* Line too long */
+    TESTCASE( fprintf( fh, "%s;%s;%s\n", "12", "123", "12" ) == 10 );
+    TESTCASE( check_file( fh, 10, ';', 3, widths )== (size_t)-1 );
+
+    fclose( fh );
+    remove( "test.txt" );
+
+    /* trim() */
+
+    strcpy( buffer, "  xyz" );
+    TESTCASE( ! strcmp( trim( buffer ), "xyz" ) );
+    strcpy( buffer, "xyz  " );
+    TESTCASE( ! strcmp( trim( buffer ), "xyz" ) );
+    strcpy( buffer, "  xyz  " );
+    TESTCASE( ! strcmp( trim( buffer ), "xyz" ) );
+    strcpy( buffer, "  x" );
+    TESTCASE( ! strcmp( trim( buffer ), "x" ) );
+    strcpy( buffer, "x  " );
+    TESTCASE( ! strcmp( trim( buffer ), "x" ) );
+    strcpy( buffer, " " );
+    TESTCASE( ! strcmp( trim( buffer ), "" ) );
+    strcpy( buffer, "  " );
+    TESTCASE( ! strcmp( trim( buffer ), "" ) );
+    strcpy( buffer, "" );
+    TESTCASE( ! strcmp( trim( buffer ), "" ) );
+
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/auxiliary/uctype/text_utilities.h b/src/pdclib/auxiliary/uctype/text_utilities.h
new file mode 100644 (file)
index 0000000..f961e6b
--- /dev/null
@@ -0,0 +1,59 @@
+/* text utilities
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef TEXT_UTILITIES_H
+#define TEXT_UTILITIES_H TEXT_UTILITIES_H
+
+#include <inttypes.h>
+#include <stdio.h>
+
+/* Trim leading and trailing whitespace from a given string.
+   Will return a pointer beyond leading whitespace, and overwrite trailing
+   whitespace with null bytes.
+*/
+char * trim( char * s );
+
+/* A function similar to strtok(), that returns the next token in a string,
+   up to the next separator character (which is replaced with a null byte)
+   or up to end-of-string.
+   As opposed to strtok(), which treats consecutive separators as one, this
+   function will work "correctly" for those as well, returning a (pointer
+   to an) empty string in those cases.
+   Pass the string as first parameter IN THE FIRST CALL ONLY, and NULL in
+   subsequent calls. The function holds an internal, static pointer to the
+   string being processed. This, of course, means the function is not thread-
+   safe.
+*/
+char * next_token( char * s, char delim );
+
+/* When processing a file with delimited-values, there are a couple of things
+   you want to be sure about before parsing it:
+   - the number of lines (data records) in the file;
+   - that all lines of the file will fit the intended line buffer size;
+   - that all records in the file indeed have the expected number of fields;
+   - that none of the fields for which you are assuming a given size exceeds
+     that size.
+   (For line buffer size, consider that the buffer must be large enough for
+   the line contents, the newline (to check that the line was actually read
+   in full), and the null terminator.)
+   This function does all that for you in a single pass. The parameters are:
+   - FILE handle to the file (function will rewind the file before checking,
+     and rewind again when it is done);
+   - the intended line buffer size;
+   - the field delimiter;
+   - the expected number of fields;
+   - a pointer to an array holding the expected maximum width for each field,
+     with a negative value indicating that this field's width need not be
+     checked.
+   The function will return the number of lines in the file, or (size_t)-1
+   if one of the checks failed. The reason for the failed check will be
+   written to stderr. (The file will not be rewound in this case.)
+   This requires reading and tokenizing the file twice, but removes lots of
+   error checking from the actual parsing, making for cleaner code.
+*/
+size_t check_file( FILE * fh, size_t max_line_length, char delim, size_t fields, int const * widths );
+
+#endif
diff --git a/src/pdclib/auxiliary/uctype/uctype.c b/src/pdclib/auxiliary/uctype/uctype.c
new file mode 100644 (file)
index 0000000..ce8d8ef
--- /dev/null
@@ -0,0 +1,85 @@
+/* uctype
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#include "uctype.h"
+
+size_t get_towupper( size_t codepoint, struct unicode_record_t * ur )
+{
+    return towupper_differs( ur, codepoint ) ? ur->simple_uppercase_mapping : codepoint;
+}
+
+size_t get_towlower( size_t codepoint, struct unicode_record_t * ur )
+{
+    return towlower_differs( ur, codepoint ) ? ur->simple_lowercase_mapping : codepoint;
+}
+
+size_t get_iswupper( size_t codepoint, struct unicode_record_t * ur, struct derived_properties_t * core )
+{
+    return towlower_differs( ur, codepoint ) || lookup_property( core, "Uppercase", codepoint );
+}
+
+size_t get_iswlower( size_t codepoint, struct unicode_record_t * ur, struct derived_properties_t * core )
+{
+    return towupper_differs( ur, codepoint ) || lookup_property( core, "Lowercase", codepoint );
+}
+
+size_t get_iswalpha( size_t codepoint, struct unicode_record_t * ur, struct derived_properties_t * core )
+{
+    return lookup_property( core, "Alphabetic", codepoint ) || ( is_general_category( ur, "Nd" ) && ! get_iswdigit( codepoint ) );
+}
+
+size_t get_iswdigit( size_t codepoint )
+{
+    return codepoint >= 0x0030 && codepoint <= 0x0039;
+}
+
+size_t get_iswxdigit( size_t codepoint )
+{
+    return get_iswdigit( codepoint ) || ( codepoint >= 0x0041 && codepoint <= 0x0046 ) || ( codepoint >= 0x0061 && codepoint <= 0x0066 );
+}
+
+size_t get_iswblank( size_t codepoint, struct unicode_record_t * ur )
+{
+    return ( codepoint == 0x0009 ) || ( is_general_category( ur, "Zs" ) && ! decomposition_contains( ur, "<noBreak>" ) );
+}
+
+size_t get_iswspace( size_t codepoint, struct unicode_record_t * ur )
+{
+    return is_general_category( ur, "Zl" ) || is_general_category( ur, "Zp" ) || ( is_general_category( ur, "Zs" ) && ! decomposition_contains( ur, "<noBreak>" ) ) || ( codepoint == 0x0020 ) || ( codepoint >= 0x0009 && codepoint <= 0x000D );
+}
+
+size_t get_iswcntrl( size_t codepoint, struct unicode_record_t * ur )
+{
+    return is_general_category( ur, "Zl" ) || is_general_category( ur, "Zp" ) || has_name( ur, "<control>" );
+}
+
+size_t get_iswgraph( size_t codepoint, struct unicode_record_t * ur )
+{
+    return ! is_general_category( ur, "Cs" ) && ! has_name( ur, "<control>" ) && ! get_iswspace( codepoint, ur );
+}
+
+size_t get_iswprint( size_t codepoint, struct unicode_record_t * ur )
+{
+    return ! is_general_category( ur, "Zp" ) && ! is_general_category( ur, "Zl" ) && ! is_general_category( ur, "Cs" ) && ! has_name( ur, "<control>" );
+}
+
+size_t get_iswpunct( size_t codepoint, struct unicode_record_t * ur, struct derived_properties_t * core )
+{
+    return ! get_iswalpha( codepoint, ur, core ) && ! get_iswdigit( codepoint ) && ( ! has_name( ur, "<control>" ) && ! get_iswspace( codepoint, ur ) ) && ! is_general_category( ur, "Cs" );
+}
+
+#ifdef TEST
+
+#include "test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/auxiliary/uctype/uctype.h b/src/pdclib/auxiliary/uctype/uctype.h
new file mode 100644 (file)
index 0000000..8cdda43
--- /dev/null
@@ -0,0 +1,29 @@
+/* uctype data
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef UCTYPE
+#define UCTYPE
+
+#include "derived_properties.h"
+#include "unicode_data.h"
+
+size_t get_towupper( size_t codepoint, struct unicode_record_t * ur );
+size_t get_towlower( size_t codepoint, struct unicode_record_t * ur );
+size_t get_iswupper( size_t codepoint, struct unicode_record_t * ur, struct derived_properties_t * core );
+size_t get_iswlower( size_t codepoint, struct unicode_record_t * ur, struct derived_properties_t * core );
+size_t get_iswalpha( size_t codepoint, struct unicode_record_t * ur, struct derived_properties_t * core );
+size_t get_iswdigit( size_t codepoint );
+size_t get_iswxdigit( size_t codepoint );
+size_t get_iswblank( size_t codepoint, struct unicode_record_t * ur );
+size_t get_iswspace( size_t codepoint, struct unicode_record_t * ur );
+size_t get_iswcntrl( size_t codepoint, struct unicode_record_t * ur );
+size_t get_iswgraph( size_t codepoint, struct unicode_record_t * ur );
+size_t get_iswprint( size_t codepoint, struct unicode_record_t * ur );
+size_t get_iswpunct( size_t codepoint, struct unicode_record_t * ur, struct derived_properties_t * core );
+
+
+#endif
+
diff --git a/src/pdclib/auxiliary/uctype/unicode_data.c b/src/pdclib/auxiliary/uctype/unicode_data.c
new file mode 100644 (file)
index 0000000..5d92fda
--- /dev/null
@@ -0,0 +1,224 @@
+/* unicode data
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "text_utilities.h"
+
+#include "unicode_data.h"
+
+#define LINE_BUFFER_SIZE 500u
+
+struct unicode_data_t * read_unicode_data( const char * filename )
+{
+    FILE * fh;
+    char buffer[ LINE_BUFFER_SIZE ];
+    struct unicode_data_t * ud = NULL;
+    size_t lines;
+
+    if ( ( fh = fopen( filename, "r" ) ) == NULL )
+    {
+        fprintf( stderr, "Could not open '%s' for reading.\n", filename );
+        return NULL;
+    }
+
+    if ( ( lines = check_file( fh, LINE_BUFFER_SIZE, ';', sizeof( unicode_record_fields ) / sizeof( int ), unicode_record_fields ) ) != (size_t)-1 )
+    {
+        if ( ( ud = malloc( sizeof( struct unicode_data_t ) ) ) )
+        {
+            ud->size = lines;
+
+            if ( ( ud->records = calloc( lines, sizeof( struct unicode_record_t ) ) ) )
+            {
+                size_t i;
+
+                for ( i = 0; i < lines; ++i )
+                {
+                    char *  p;
+
+                    fgets( buffer, LINE_BUFFER_SIZE, fh );
+
+                    ud->records[ i ].code_point = strtoul( next_token( buffer, ';' ), NULL, 16 );
+
+                    p = next_token( NULL, ';' );
+                    if ( *p )
+                    {
+                        ud->records[ i ].name = malloc( strlen( p ) + 1 );
+                        strcpy( ud->records[ i ].name, p );
+                    }
+
+                    strcpy( ud->records[ i ].general_category, next_token( NULL, ';' ) );
+
+                    p = next_token( NULL, ';' );
+                    ud->records[ i ].canonical_combining_class = ( *p ) ? strtol( p, NULL, 10 ) : -1l;
+
+                    strcpy( ud->records[ i ].bidi_class, next_token( NULL, ';' ) );
+
+                    p = next_token( NULL, ';' );
+                    if ( *p )
+                    {
+                        ud->records[ i ].decomposition = malloc( strlen( p ) + 1 );
+                        strcpy( ud->records[ i ].decomposition, p );
+                    }
+
+                    p = next_token( NULL, ';' );
+                    ud->records[ i ].numeric_type = ( *p ) ? strtol( p, NULL, 10 ) : -1l;
+
+                    p = next_token( NULL, ';' );
+                    ud->records[ i ].numeric_digit = ( *p ) ? strtol( p, NULL, 10 ) : -1l;
+
+                    p = next_token( NULL, ';' );
+                    if ( *p )
+                    {
+                        ud->records[ i ].numeric_value = malloc( strlen( p ) + 1 );
+                        strcpy( ud->records[ i ].numeric_value, p );
+                    }
+
+                    p = next_token( NULL, ';' );
+                    ud->records[ i ].bidi_mirrored = ( *p ) ? *p : '\0';
+
+                    next_token( NULL, ';' ); /* Unicode_1_Name */
+                    next_token( NULL, ';' ); /* ISO_Comment */
+
+                    ud->records[ i ].simple_uppercase_mapping = strtoul( next_token( NULL, ';' ), NULL, 16 );
+                    ud->records[ i ].simple_lowercase_mapping = strtoul( next_token( NULL, ';' ), NULL, 16 );
+                    ud->records[ i ].simple_titlecase_mapping = strtoul( next_token( NULL, ';' ), NULL, 16 );
+                }
+            }
+            else
+            {
+                fprintf( stderr, "Memory allocation failure.\n" );
+                free( ud );
+                ud = NULL;
+            }
+        }
+        else
+        {
+            fprintf( stderr, "Memory allocation failure.\n" );
+        }
+    }
+
+    fclose( fh );
+    return ud;
+}
+
+int has_name( struct unicode_record_t * ur, const char * name )
+{
+    return strcmp( ur->name, name ) == 0;
+}
+
+int name_ends_with( struct unicode_record_t * ur, const char * name )
+{
+    return strstr( ur->name, name ) == ( ur->name + ( strlen( ur->name ) - strlen( name ) ) );
+}
+
+int is_general_category( struct unicode_record_t * ur, const char * category )
+{
+    return strcmp( ur->general_category, category ) == 0;
+}
+
+int decomposition_contains( struct unicode_record_t * ur, const char * substring )
+{
+    return ur->decomposition && strstr( ur->decomposition, substring ) != NULL;
+}
+
+int towupper_differs( struct unicode_record_t * ur, size_t codepoint )
+{
+    return ur->simple_uppercase_mapping && ( ur->simple_uppercase_mapping != codepoint );
+}
+
+int towlower_differs( struct unicode_record_t * ur, size_t codepoint )
+{
+    return ur->simple_lowercase_mapping && ( ur->simple_lowercase_mapping != codepoint );
+}
+
+void release_unicode_data( struct unicode_data_t * ud )
+{
+    size_t i;
+
+    for ( i = 0; i < ud->size; ++i )
+    {
+        free( ud->records[i].name );
+        free( ud->records[i].decomposition );
+        free( ud->records[i].numeric_value );
+    }
+
+    free( ud->records );
+    free( ud );
+}
+
+#ifdef TEST
+
+#include "test.h"
+
+#include <inttypes.h>
+
+int main( void )
+{
+    FILE * fh = fopen( "test.txt", "w" );
+    struct unicode_data_t * ud;
+    int rc;
+
+    TESTCASE( fh != NULL );
+    TESTCASE( fprintf( fh, "%04x;%s;%s;%d;%s;;;;;%c;%s;;;;\n", 0, "<control>", "Cc", 0, "BN", 'N', "NULL" ) == 38 );
+    TESTCASE( ( rc = fprintf( fh, "%04x;%s;%s;%d;%s;%s;;;%s;%c;;;%04x;;%04x\n", 0x2170, "SMALL ROMAN NUMERAL ONE", "Nl", 0, "L", "<compat> 0069", "1", 'N', 0x2160, 0x2160 ) ) == 69 );
+
+    fclose( fh );
+    ud = read_unicode_data( "test.txt" );
+    remove( "test.txt" );
+
+    TESTCASE( ud != NULL );
+    TESTCASE( ud->size == 2 );
+
+    TESTCASE( ud->records[0].code_point == 0 );
+    TESTCASE( strcmp( ud->records[0].name, "<control>" ) == 0 );
+    TESTCASE( strcmp( ud->records[0].general_category, "Cc" ) == 0 );
+    TESTCASE( ud->records[0].canonical_combining_class == 0 );
+    TESTCASE( strcmp( ud->records[0].bidi_class, "BN" ) == 0 );
+    TESTCASE( ud->records[0].decomposition == NULL );
+    TESTCASE( ud->records[0].numeric_type == -1 );
+    TESTCASE( ud->records[0].numeric_digit == -1 );
+    TESTCASE( ud->records[0].numeric_value == NULL );
+    TESTCASE( ud->records[0].bidi_mirrored == 'N' );
+    TESTCASE( ud->records[0].simple_uppercase_mapping == 0 );
+    TESTCASE( ud->records[0].simple_lowercase_mapping == 0 );
+    TESTCASE( ud->records[0].simple_titlecase_mapping == 0 );
+
+    TESTCASE( ud->records[1].code_point == 0x2170 );
+    TESTCASE( strcmp( ud->records[1].name, "SMALL ROMAN NUMERAL ONE" ) == 0 );
+    TESTCASE( strcmp( ud->records[1].general_category, "Nl" ) == 0 );
+    TESTCASE( ud->records[1].canonical_combining_class == 0 );
+    TESTCASE( strcmp( ud->records[1].bidi_class, "L" ) == 0 );
+    TESTCASE( strcmp( ud->records[1].decomposition, "<compat> 0069" ) == 0 );
+    TESTCASE( ud->records[1].numeric_type == -1 );
+    TESTCASE( ud->records[1].numeric_digit == -1 );
+    TESTCASE( strcmp( ud->records[1].numeric_value, "1" ) == 0 );
+    TESTCASE( ud->records[1].bidi_mirrored == 'N' );
+    TESTCASE( ud->records[1].simple_uppercase_mapping == 0x2160 );
+    TESTCASE( ud->records[1].simple_lowercase_mapping == 0 );
+    TESTCASE( ud->records[1].simple_titlecase_mapping == 0x2160 );
+
+    TESTCASE( is_general_category( &(ud->records[0]), "Cc" ) );
+    TESTCASE( ! is_general_category( &(ud->records[0]), "" ) );
+    TESTCASE( is_general_category( &(ud->records[1]), "Nl" ) );
+    TESTCASE( ! is_general_category( &(ud->records[1]), "Foo" ) );
+
+    TESTCASE( decomposition_contains( &(ud->records[1]), "<compat>" ) );
+    TESTCASE( ! decomposition_contains( &(ud->records[1]), "Foo" ) );
+
+    TESTCASE( ! towupper_differs( &(ud->records[0]), 0 ) );
+    TESTCASE( ! towlower_differs( &(ud->records[0]), 0 ) );
+    TESTCASE( towupper_differs( &(ud->records[1]), 0x2170 ) );
+    TESTCASE( ! towlower_differs( &(ud->records[1]), 0x2170 ) );
+
+    release_unicode_data( ud );
+
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/auxiliary/uctype/unicode_data.h b/src/pdclib/auxiliary/uctype/unicode_data.h
new file mode 100644 (file)
index 0000000..8cd4832
--- /dev/null
@@ -0,0 +1,77 @@
+/* unicode data
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef UNICODE_DATA
+#define UNICODE_DATA UNICODE_DATA
+
+#include <stddef.h>
+
+/* https://www.unicode.org/reports/tr44/#UnicodeData.txt */
+
+/* We do not need all these fields at this point, but we read them anyway
+   so we do not need to change much should the need arise later.
+*/
+struct unicode_record_t
+{
+    size_t code_point;
+    char * name;
+    char general_category[ 3 ];
+    int canonical_combining_class;
+    char bidi_class[ 4 ];
+    char * decomposition;
+    int numeric_type;
+    int numeric_digit;
+    char * numeric_value;
+    char bidi_mirrored;
+    /*char * unicode_1_name;*/ /* Obsolete as of 6.2.0 */
+    /*char * iso_comment;*/ /* Obsoöete as of 5.2.0 */
+    size_t simple_uppercase_mapping;
+    size_t simple_lowercase_mapping;
+    size_t simple_titlecase_mapping;
+};
+
+struct unicode_data_t
+{
+    size_t size;
+    struct unicode_record_t * records;
+};
+
+/* The assumed field widths, for use with check_file(). */
+static const int unicode_record_fields[] = {
+    -1, /* code_point */
+    -1, /* name */
+     3, /* general_category */
+    -1, /* canonical_combining_class */
+     4, /* bidi_class */
+    -1, /* decomposition */
+    -1, /* numeric_type */
+    -1, /* numeric_digit */
+    -1, /* numeric_value */
+     2, /* bidi_mirrored */
+    -1, /* unicode_1_name */
+    -1, /* iso_comment */
+    -1, /* simple_uppercase_mapping */
+    -1, /* simple_lowercase_mapping */
+    -1  /* simple_titlecase_mapping */
+};
+
+struct unicode_data_t * read_unicode_data( const char * filename );
+
+int has_name( struct unicode_record_t * ur, const char * name );
+
+int name_ends_with( struct unicode_record_t * ur, const char * name );
+
+int is_general_category( struct unicode_record_t * ur, const char * category );
+
+int decomposition_contains( struct unicode_record_t * ur, const char * substring );
+
+int towupper_differs( struct unicode_record_t * ur, size_t codepoint );
+
+int towlower_differs( struct unicode_record_t * ur, size_t codepoint );
+
+void release_unicode_data( struct unicode_data_t * ud );
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/Readme.txt b/src/pdclib/functions/_PDCLIB/Readme.txt
new file mode 100644 (file)
index 0000000..13ad05c
--- /dev/null
@@ -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/pdclib/functions/_PDCLIB/_PDCLIB_atomax.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_atomax.c
new file mode 100644 (file)
index 0000000..eaf7ced
--- /dev/null
@@ -0,0 +1,46 @@
+/* _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 <string.h>
+#include <ctype.h>
+
+#ifndef REGTEST
+
+_PDCLIB_intmax_t _PDCLIB_atomax( const char * s )
+{
+    _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    /* basic functionality */
+    TESTCASE( _PDCLIB_atomax( "123" ) == 123 );
+    /* testing skipping of leading whitespace and trailing garbage */
+    TESTCASE( _PDCLIB_atomax( " \n\v\t\f123xyz" ) == 123 );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_closeall.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_closeall.c
new file mode 100644 (file)
index 0000000..3c928e7
--- /dev/null
@@ -0,0 +1,37 @@
+/* _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 <stdio.h>
+
+#ifndef REGTEST
+
+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;
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* No testdriver */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_digits.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_digits.c
new file mode 100644 (file)
index 0000000..69c4f2b
--- /dev/null
@@ -0,0 +1,33 @@
+/* _PDCLIB_digits
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_int.h"
+
+const char _PDCLIB_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+/* For _PDCLIB/print.c only; obsolete with ctype.h */
+const char _PDCLIB_Xdigits[] = "0123456789ABCDEF";
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <string.h>
+
+int main( void )
+{
+#ifndef REGTEST
+    TESTCASE( strcmp( _PDCLIB_digits, "0123456789abcdefghijklmnopqrstuvwxyz" ) == 0 );
+    TESTCASE( strcmp( _PDCLIB_Xdigits, "0123456789ABCDEF" ) == 0 );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_filemode.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_filemode.c
new file mode 100644 (file)
index 0000000..2555a00
--- /dev/null
@@ -0,0 +1,90 @@
+/* _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 <stddef.h>
+
+#ifndef REGTEST
+
+/* 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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    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 );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_is_leap.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_is_leap.c
new file mode 100644 (file)
index 0000000..2407337
--- /dev/null
@@ -0,0 +1,39 @@
+/* _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.
+*/
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_int.h"
+
+int _PDCLIB_is_leap( int year_offset )
+{
+    /* year given as offset from 1900, matching tm.tm_year in <time.h> */
+    long long year = year_offset + 1900ll;
+    return ( ( year % 4 ) == 0 && ( ( year % 25 ) != 0 || ( year % 400 ) == 0 ) );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    /* 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 ) );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_collate.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_collate.c
new file mode 100644 (file)
index 0000000..d66b996
--- /dev/null
@@ -0,0 +1,63 @@
+/* _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.
+*/
+
+#ifndef REGTEST
+
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pdclib/_PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    TESTCASE( NO_TESTDRIVER );
+#endif
+
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_ctype.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_ctype.c
new file mode 100644 (file)
index 0000000..81fb18f
--- /dev/null
@@ -0,0 +1,365 @@
+/* _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.
+*/
+
+#ifndef REGTEST
+
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pdclib/_PDCLIB_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, "%x %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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <ctype.h>
+
+int main( void )
+{
+#ifndef REGTEST
+    FILE * fh = fopen( "test_ctype.dat", "wb" );
+    TESTCASE( 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( fprintf( fh, "%x %x\n", 0x00, 0x09 ) );
+    TESTCASE( fprintf( fh, "%x %x %x %x\n", 0x11, 0x16, 0x21, 0x26 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x00, 0x00 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x01, 0x01 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x02, 0x02 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x03, 0x03 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x04, 0x04 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x05, 0x05 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x06, 0x06 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x07, 0x07 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x08, 0x08 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x09, 0x09 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0A, 0x0A ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0B, 0x0B ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0C, 0x0C ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0D, 0x0D ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0E, 0x0E ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0F, 0x0F ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x10, 0x10 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x11, 0x11 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x12, 0x12 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x13, 0x13 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x14, 0x14 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x15, 0x15 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x16, 0x16 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x17, 0x17 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x18, 0x18 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x19, 0x19 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1A, 0x1A ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1B, 0x1B ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1C, 0x1C ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1D, 0x1D ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1E, 0x1E ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1F, 0x1F ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x20, 0x20 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x21, 0x21 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x22, 0x22 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x23, 0x23 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x24, 0x24 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x25, 0x25 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x26, 0x26 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x27, 0x27 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x28, 0x28 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x29, 0x29 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2A, 0x2A ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2B, 0x2B ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2C, 0x2C ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2D, 0x2D ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2E, 0x2E ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2F, 0x2F ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x30, 0x30 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x31, 0x31 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x32, 0x32 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x33, 0x33 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x34, 0x34 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x35, 0x35 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x36, 0x36 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x37, 0x37 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x38, 0x38 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x39, 0x39 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3A, 0x3A ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3B, 0x3B ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3C, 0x3C ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3D, 0x3D ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3E, 0x3E ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3F, 0x3F ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x40, 0x40 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x41, 0x41 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x42, 0x42 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x43, 0x43 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x44, 0x44 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x45, 0x45 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x46, 0x46 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x47, 0x47 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x48, 0x48 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x49, 0x49 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4A, 0x4A ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4B, 0x4B ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4C, 0x4C ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4D, 0x4D ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4E, 0x4E ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4F, 0x4F ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x50, 0x50 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x51, 0x51 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x52, 0x52 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x53, 0x53 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x54, 0x54 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x55, 0x55 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x56, 0x56 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x57, 0x57 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x58, 0x58 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x59, 0x59 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5A, 0x5A ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5B, 0x5B ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5C, 0x5C ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5D, 0x5D ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5E, 0x5E ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5F, 0x5F ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x60, 0x60 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x61, 0x61 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x62, 0x62 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x63, 0x63 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x64, 0x64 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x65, 0x65 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x66, 0x66 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x67, 0x67 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x68, 0x68 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x69, 0x69 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6A, 0x6A ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6B, 0x6B ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6C, 0x6C ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6D, 0x6D ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6E, 0x6E ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6F, 0x6F ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x70, 0x70 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x71, 0x71 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x72, 0x72 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x73, 0x73 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x74, 0x74 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x75, 0x75 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x76, 0x76 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x77, 0x77 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x78, 0x78 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x79, 0x79 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7A, 0x7A ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7B, 0x7B ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7C, 0x7C ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7D, 0x7D ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7E, 0x7E ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7F, 0x7F ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x80, 0x80 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x81, 0x81 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x82, 0x82 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x83, 0x83 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x84, 0x84 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x85, 0x85 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x86, 0x86 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x87, 0x87 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x88, 0x88 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x89, 0x89 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8A, 0x8A ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8B, 0x8B ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8C, 0x8C ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8D, 0x8D ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8E, 0x8E ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8F, 0x8F ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x90, 0x90 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x91, 0x91 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x92, 0x92 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x93, 0x93 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x94, 0x94 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x95, 0x95 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x96, 0x96 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x97, 0x97 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x98, 0x98 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x99, 0x99 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9A, 0x9A ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9B, 0x9B ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9C, 0x9C ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9D, 0x9D ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9E, 0x9E ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9F, 0x9F ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA0, 0xA0 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA1, 0xA1 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA2, 0xA2 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA3, 0xA3 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA4, 0xA4 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA5, 0xA5 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA6, 0xA6 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA7, 0xA7 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA8, 0xA8 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA9, 0xA9 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAA, 0xAA ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAB, 0xAB ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAC, 0xAC ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAD, 0xAD ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAE, 0xAE ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAF, 0xAF ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB0, 0xB0 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB1, 0xB1 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB2, 0xB2 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB3, 0xB3 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB4, 0xB4 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB5, 0xB5 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB6, 0xB6 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB7, 0xB7 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB8, 0xB8 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB9, 0xB9 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBA, 0xBA ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBB, 0xBB ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBC, 0xBC ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBD, 0xBD ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBE, 0xBE ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBF, 0xBF ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC0, 0xC0 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC1, 0xC1 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC2, 0xC2 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC3, 0xC3 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC4, 0xC4 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC5, 0xC5 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC6, 0xC6 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC7, 0xC7 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC8, 0xC8 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC9, 0xC9 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCA, 0xCA ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCB, 0xCB ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCC, 0xCC ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCD, 0xCD ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCE, 0xCE ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCF, 0xCF ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD0, 0xD0 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD1, 0xD1 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD2, 0xD2 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD3, 0xD3 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD4, 0xD4 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD5, 0xD5 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD6, 0xD6 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD7, 0xD7 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD8, 0xD8 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD9, 0xD9 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDA, 0xDA ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDB, 0xDB ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDC, 0xDC ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDD, 0xDD ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDE, 0xDE ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDF, 0xDF ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE0, 0xE0 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE1, 0xE1 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE2, 0xE2 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE3, 0xE3 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE4, 0xE4 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE5, 0xE5 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE6, 0xE6 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE7, 0xE7 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE8, 0xE8 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE9, 0xE9 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xEA, 0xEA ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xEB, 0xEB ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xEC, 0xEC ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xED, 0xED ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xEE, 0xEE ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xEF, 0xEF ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF0, 0xF0 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF1, 0xF1 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF2, 0xF2 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF3, 0xF3 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF4, 0xF4 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF5, 0xF5 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF6, 0xF6 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF7, 0xF7 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF8, 0xF8 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF9, 0xF9 ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFA, 0xFA ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFB, 0xFB ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFC, 0xFC ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFD, 0xFD ) );
+    TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFE, 0xFE ) );
+    TESTCASE( 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 ) );
+    */
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_messages.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_messages.c
new file mode 100644 (file)
index 0000000..6ff2a50
--- /dev/null
@@ -0,0 +1,88 @@
+/* _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.
+*/
+
+#ifndef REGTEST
+
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pdclib/_PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    FILE * fh = fopen( "test_numeric.dat", "wb" );
+    struct _PDCLIB_lc_lconv_numeric_t * lc;
+    TESTCASE( 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 );
+#endif
+
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_monetary.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_monetary.c
new file mode 100644 (file)
index 0000000..e2dcbb4
--- /dev/null
@@ -0,0 +1,158 @@
+/* _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.
+*/
+
+#ifndef REGTEST
+
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pdclib/_PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    FILE * fh = fopen( "test_monetary.dat", "wb" );
+    struct _PDCLIB_lc_lconv_monetary_t * lc;
+    TESTCASE( fh != NULL );
+    fprintf( fh, "%s\n", "," );    /* mon_decimal_point */
+    fprintf( fh, "%s\n", "." );    /* mon_thousands_sep */
+    fprintf( fh, "%s\n", "3" );    /* mon_grouping */
+    fprintf( fh, "%s\n", "" );     /* positive_sign */
+    fprintf( fh, "%s\n", "-" );    /* negative_sign */
+    fprintf( fh, "%s\n", "\xa4" ); /* currency_symbol */
+    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 */
+    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 );
+#endif
+
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_numeric.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_numeric.c
new file mode 100644 (file)
index 0000000..de42bbd
--- /dev/null
@@ -0,0 +1,84 @@
+/* _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.
+*/
+
+#ifndef REGTEST
+
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pdclib/_PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    FILE * fh = fopen( "test_numeric.dat", "wb" );
+    struct _PDCLIB_lc_lconv_numeric_t * lc;
+    TESTCASE( 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 );
+#endif
+
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_time.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_time.c
new file mode 100644 (file)
index 0000000..733e5c7
--- /dev/null
@@ -0,0 +1,165 @@
+/* _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.
+*/
+
+#ifndef REGTEST
+
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pdclib/_PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    FILE * fh = fopen( "test_time.dat", "wb" );
+    struct _PDCLIB_lc_time_t * lc;
+
+    TESTCASE( fh != NULL );
+
+    /* month name abbreviation */
+    TESTCASE( fprintf( fh, "%s\n", "Jan" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Feb" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "M\xe4r" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Apr" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Mai" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Jun" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Jul" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Aug" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Sep" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Okt" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Nov" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Dez" ) == 4 );
+    /* month name full */
+    TESTCASE( fprintf( fh, "%s\n", "Januar" ) == 7 );
+    TESTCASE( fprintf( fh, "%s\n", "Februar" ) == 8 );
+    TESTCASE( fprintf( fh, "%s\n", "M\xe4rz" ) == 5 );
+    TESTCASE( fprintf( fh, "%s\n", "April" ) == 6 );
+    TESTCASE( fprintf( fh, "%s\n", "Mai" ) == 4 );
+    TESTCASE( fprintf( fh, "%s\n", "Juni" ) == 5 );
+    TESTCASE( fprintf( fh, "%s\n", "Juli" ) == 5 );
+    TESTCASE( fprintf( fh, "%s\n", "August" ) == 7 );
+    TESTCASE( fprintf( fh, "%s\n", "September" ) == 10 );
+    TESTCASE( fprintf( fh, "%s\n", "Oktober" ) == 8 );
+    TESTCASE( fprintf( fh, "%s\n", "November" ) == 9 );
+    TESTCASE( fprintf( fh, "%s\n", "Dezember" ) == 9 );
+    /* day name abbreviation */
+    TESTCASE( fprintf( fh, "%s\n", "So" ) == 3 );
+    TESTCASE( fprintf( fh, "%s\n", "Mo" ) == 3 );
+    TESTCASE( fprintf( fh, "%s\n", "Di" ) == 3 );
+    TESTCASE( fprintf( fh, "%s\n", "Mi" ) == 3 );
+    TESTCASE( fprintf( fh, "%s\n", "Do" ) == 3 );
+    TESTCASE( fprintf( fh, "%s\n", "Fr" ) == 3 );
+    TESTCASE( fprintf( fh, "%s\n", "Sa" ) == 3 );
+    /* day name full */
+    TESTCASE( fprintf( fh, "%s\n", "Sonntag" ) == 8 );
+    TESTCASE( fprintf( fh, "%s\n", "Montag" ) == 7 );
+    TESTCASE( fprintf( fh, "%s\n", "Dienstag" ) == 9 );
+    TESTCASE( fprintf( fh, "%s\n", "Mittwoch" ) == 9 );
+    TESTCASE( fprintf( fh, "%s\n", "Donnerstag" ) == 11 );
+    TESTCASE( fprintf( fh, "%s\n", "Freitag" ) == 8 );
+    TESTCASE( fprintf( fh, "%s\n", "Samstag" ) == 8 );
+
+    TESTCASE( fprintf( fh, "%s\n", "%a %d %b %Y %T %Z" ) == 18 ); /* date time format (%c) */
+    TESTCASE( fprintf( fh, "%s\n", "%I:%M:%S" ) == 9 ); /* 12-hour time format (%r) */
+    TESTCASE( fprintf( fh, "%s\n", "%d.%m.%Y" ) == 9 ); /* date format (%x) */
+    TESTCASE( fprintf( fh, "%s\n", "%H:%M:%S" ) == 9 ); /* time format (%X) */
+
+    TESTCASE( fprintf( fh, "%s\n", "" ) == 1 ); /* AM */
+    TESTCASE( 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 );
+
+#endif
+
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lines.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lines.c
new file mode 100644 (file)
index 0000000..aaaa743
--- /dev/null
@@ -0,0 +1,81 @@
+/* _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 <stdio.h>
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    FILE * fh = fopen( "test_lines.txt", "w+" );
+    char * rc;
+
+    TESTCASE( 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 );
+
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_prepread.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_prepread.c
new file mode 100644 (file)
index 0000000..1d60642
--- /dev/null
@@ -0,0 +1,50 @@
+/* _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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_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_config.h>.
+        */
+        _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;
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_prepwrite.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_prepwrite.c
new file mode 100644 (file)
index 0000000..dcdc970
--- /dev/null
@@ -0,0 +1,41 @@
+/* _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 <stdio.h>
+
+#ifndef REGTEST
+
+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_config.h>.
+        */
+        _PDCLIB_errno = _PDCLIB_ERROR;
+        stream->status |= _PDCLIB_ERRORFLAG;
+        return EOF;
+    }
+    stream->status |= _PDCLIB_FWRITE | _PDCLIB_BYTESTREAM;
+    return 0;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_print.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_print.c
new file mode 100644 (file)
index 0000000..3c13da2
--- /dev/null
@@ -0,0 +1,625 @@
+/* _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 <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#ifndef REGTEST
+
+/* 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 = ( (_PDCLIB_size_t)status->prec > status->current ) ? ( (_PDCLIB_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 > (_PDCLIB_size_t)status->prec ) ? status->current : (_PDCLIB_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);
+        }
+    }
+    }
+}
+
+
+/* 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.
+*/
+#define INT2BASE() \
+do \
+{ \
+    /* 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 ] ); \
+        } \
+        } \
+    } \
+} while ( 0 )
+
+
+static void int2base( intmax_t value, struct _PDCLIB_status_t * status )
+{
+    INT2BASE();
+}
+
+
+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 > (_PDCLIB_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;
+            }
+            INT2BASE();
+        }
+        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();
+        }
+        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;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "_PDCLIB/print.c"
+#define _PDCLIB_STRINGIO
+
+#include "_PDCLIB_test.h"
+
+#ifndef REGTEST
+
+static int testprintf( char * buffer, const char * format, ... )
+{
+    /* 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' )
+    {
+        printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
+        ++TEST_RESULTS;
+    }
+    va_end( status.arg );
+    return status.i;
+}
+
+#endif
+
+#define TEST_CONVERSION_ONLY
+
+int main( void )
+{
+#ifndef REGTEST
+    char target[100];
+#include "printf_testcases.h"
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_scan.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_scan.c
new file mode 100644 (file)
index 0000000..1f8fd71
--- /dev/null
@@ -0,0 +1,639 @@
+/* _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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <string.h>
+#include <stddef.h>
+#include <limits.h>
+
+#ifndef REGTEST
+
+/* 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;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "_PDCLIB/scan.c"
+#define _PDCLIB_STRINGIO
+
+#include "_PDCLIB_test.h"
+
+#ifndef REGTEST
+
+static int testscanf( const char * s, const char * format, ... )
+{
+    struct _PDCLIB_status_t status;
+    status.n = 0;
+    status.i = 0;
+    status.s = (char *)s;
+    status.stream = NULL;
+    va_start( status.arg, format );
+    if ( *(_PDCLIB_scan( format, &status )) != '\0' )
+    {
+        printf( "_PDCLIB_scan() did not return end-of-specifier on '%s'.\n", format );
+        ++TEST_RESULTS;
+    }
+    va_end( status.arg );
+    return status.n;
+}
+
+#endif
+
+#define TEST_CONVERSION_ONLY
+
+int main( void )
+{
+#ifndef REGTEST
+    char source[100];
+#include "scanf_testcases.h"
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_seed.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_seed.c
new file mode 100644 (file)
index 0000000..ce7c31c
--- /dev/null
@@ -0,0 +1,19 @@
+/* _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;
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* no tests for raw data */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_main.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_main.c
new file mode 100644 (file)
index 0000000..4808952
--- /dev/null
@@ -0,0 +1,88 @@
+/* _PDCLIB_strtox_main( const char * *, int, _PDCLIB_uintmax_t, _PDCLIB_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 <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#ifndef REGTEST
+
+_PDCLIB_uintmax_t _PDCLIB_strtox_main( const char ** p, unsigned int base, uintmax_t error, uintmax_t limval, int limdigit, char * sign )
+{
+    _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <errno.h>
+
+int main( void )
+{
+#ifndef REGTEST
+    const char * p;
+    char test[] = "123_";
+    char fail[] = "xxx";
+    char sign = '-';
+    /* basic functionality */
+    p = test;
+    errno = 0;
+    TESTCASE( _PDCLIB_strtox_main( &p, 10u, (uintmax_t)999, (uintmax_t)12, 3, &sign ) == 123 );
+    TESTCASE( errno == 0 );
+    TESTCASE( p == &test[3] );
+    /* proper functioning to smaller base */
+    p = test;
+    TESTCASE( _PDCLIB_strtox_main( &p, 8u, (uintmax_t)999, (uintmax_t)12, 3, &sign ) == 0123 );
+    TESTCASE( errno == 0 );
+    TESTCASE( p == &test[3] );
+    /* overflowing subject sequence must still return proper endptr */
+    p = test;
+    TESTCASE( _PDCLIB_strtox_main( &p, 4u, (uintmax_t)999, (uintmax_t)1, 2, &sign ) == 999 );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( p == &test[3] );
+    TESTCASE( sign == '+' );
+    /* testing conversion failure */
+    errno = 0;
+    p = fail;
+    sign = '-';
+    TESTCASE( _PDCLIB_strtox_main( &p, 10u, (uintmax_t)999, (uintmax_t)99, 8, &sign ) == 0 );
+    TESTCASE( p == NULL );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_prelim.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_prelim.c
new file mode 100644 (file)
index 0000000..6e16b35
--- /dev/null
@@ -0,0 +1,94 @@
+/* _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 <ctype.h>
+#include <stddef.h>
+#include <string.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    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] );
+    TESTCASE( sign == '+' );
+    TESTCASE( base == 10 );
+    base = 0;
+    sign = '\0';
+    TESTCASE( _PDCLIB_strtox_prelim( test2, &sign, &base ) == &test2[3] );
+    TESTCASE( sign == '+' );
+    TESTCASE( base == 8 );
+    base = 0;
+    sign = '\0';
+    TESTCASE( _PDCLIB_strtox_prelim( test3, &sign, &base ) == &test3[4] );
+    TESTCASE( sign == '-' );
+    TESTCASE( base == 16 );
+    base = 10;
+    sign = '\0';
+    TESTCASE( _PDCLIB_strtox_prelim( test3, &sign, &base ) == &test3[2] );
+    TESTCASE( sign == '-' );
+    TESTCASE( base == 10 );
+    base = 1;
+    TESTCASE( _PDCLIB_strtox_prelim( test3, &sign, &base ) == NULL );
+    base = 37;
+    TESTCASE( _PDCLIB_strtox_prelim( test3, &sign, &base ) == NULL );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/assert.c b/src/pdclib/functions/_PDCLIB/assert.c
new file mode 100644 (file)
index 0000000..f84265f
--- /dev/null
@@ -0,0 +1,71 @@
+/* _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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_aux.h"
+
+void _PDCLIB_assert99( const char * const message1, const char * const function, const char * const message2 )
+{
+    fputs( message1, stderr );
+    fputs( function, stderr );
+    fputs( message2, stderr );
+    abort();
+}
+
+void _PDCLIB_assert89( const char * const message )
+{
+    fputs( message, stderr );
+    abort();
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <signal.h>
+
+static int EXPECTED_ABORT = 0;
+static int UNEXPECTED_ABORT = 1;
+
+static void aborthandler( int sig )
+{
+    TESTCASE( ! EXPECTED_ABORT );
+    exit( (signed int)TEST_RESULTS );
+}
+
+#define NDEBUG
+
+#include <assert.h>
+
+static int disabled_test( void )
+{
+    int i = 0;
+    assert( i == 0 ); /* NDEBUG set, condition met */
+    assert( i == 1 ); /* NDEBUG set, condition fails */
+    return i;
+}
+
+#undef NDEBUG
+
+#include <assert.h>
+
+int main( void )
+{
+    TESTCASE( signal( SIGABRT, &aborthandler ) != SIG_ERR );
+    TESTCASE( disabled_test() == 0 );
+    assert( UNEXPECTED_ABORT ); /* NDEBUG not set, condition met */
+    assert( EXPECTED_ABORT ); /* NDEBUG not set, condition fails - should abort */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/errno.c b/src/pdclib/functions/_PDCLIB/errno.c
new file mode 100644 (file)
index 0000000..13270fc
--- /dev/null
@@ -0,0 +1,37 @@
+/* _PDCLIB_errno
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_int.h"
+
+int _PDCLIB_errno = 0;
+
+int * _PDCLIB_errno_func()
+{
+    return &_PDCLIB_errno;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <errno.h>
+
+int main( void )
+{
+    errno = 0;
+    TESTCASE( errno == 0 );
+    errno = EDOM;
+    TESTCASE( errno == EDOM );
+    errno = ERANGE;
+    TESTCASE( errno == ERANGE );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/_PDCLIB/stdarg.c b/src/pdclib/functions/_PDCLIB/stdarg.c
new file mode 100644 (file)
index 0000000..7ad1087
--- /dev/null
@@ -0,0 +1,115 @@
+/* stdarg
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#include <stdarg.h>
+#include <limits.h>
+#include <float.h>
+
+#ifdef TEST
+
+#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:
+            {
+                TESTCASE( va_arg( ap, int ) == INT_MAX );
+                tag = va_arg( ap, enum tag_t );
+                break;
+            }
+            case TAG_LONG:
+            {
+                TESTCASE( va_arg( ap, long ) == LONG_MAX );
+                tag = va_arg( ap, enum tag_t );
+                break;
+            }
+            case TAG_LLONG:
+            {
+                TESTCASE( va_arg( ap, long long ) == LLONG_MAX );
+                tag = va_arg( ap, enum tag_t );
+                break;
+            }
+            case TAG_DBL:
+            {
+                TESTCASE( va_arg( ap, double ) == DBL_MAX );
+                tag = va_arg( ap, enum tag_t );
+                break;
+            }
+            case TAG_LDBL:
+            {
+                TESTCASE( va_arg( ap, long double ) == LDBL_MAX );
+                tag = va_arg( ap, enum tag_t );
+                break;
+            }
+            case TAG_INTPTR:
+            {
+                TESTCASE( *( va_arg( ap, int * ) ) == INT_MAX );
+                tag = va_arg( ap, enum tag_t );
+                break;
+            }
+            case TAG_LDBLPTR:
+            {
+                TESTCASE( *( va_arg( ap, long double * ) ) == LDBL_MAX );
+                tag = va_arg( ap, enum tag_t );
+                break;
+            }
+            case TAG_FUNCPTR:
+            {
+                intfunc_t function;
+                TESTCASE( ( function = va_arg( ap, intfunc_t ) ) == dummy );
+                TESTCASE( function() == INT_MAX );
+                tag = va_arg( ap, enum tag_t );
+                break;
+            }
+            case TAG_END:
+            {
+                va_end( ap );
+                return 0;
+            }
+        }
+    }
+}
+
+int main( void )
+{
+    int x = INT_MAX;
+    long double d = LDBL_MAX;
+    test( TAG_END );
+    test( TAG_INT, INT_MAX, TAG_END );
+    test( TAG_LONG, LONG_MAX, TAG_LLONG, LLONG_MAX, TAG_END );
+    test( TAG_DBL, DBL_MAX, TAG_LDBL, LDBL_MAX, TAG_END );
+    test( TAG_INTPTR, &x, TAG_LDBLPTR, &d, TAG_FUNCPTR, dummy, TAG_END );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/isalnum.c b/src/pdclib/functions/ctype/isalnum.c
new file mode 100644 (file)
index 0000000..d3ef7c9
--- /dev/null
@@ -0,0 +1,38 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int isalnum( int c )
+{
+    return ( isdigit( c ) || isalpha( c ) );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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( '@' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/isalpha.c b/src/pdclib/functions/ctype/isalpha.c
new file mode 100644 (file)
index 0000000..b3fa513
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int isalpha( int c )
+{
+    return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_ALPHA );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( isalpha( 'a' ) );
+    TESTCASE( isalpha( 'z' ) );
+    TESTCASE( ! isalpha( ' ' ) );
+    TESTCASE( ! isalpha( '1' ) );
+    TESTCASE( ! isalpha( '@' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/isblank.c b/src/pdclib/functions/ctype/isblank.c
new file mode 100644 (file)
index 0000000..dd6af44
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int isblank( int c )
+{
+    return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_BLANK );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( isblank( ' ' ) );
+    TESTCASE( isblank( '\t' ) );
+    TESTCASE( ! isblank( '\v' ) );
+    TESTCASE( ! isblank( '\r' ) );
+    TESTCASE( ! isblank( 'x' ) );
+    TESTCASE( ! isblank( '@' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/iscntrl.c b/src/pdclib/functions/ctype/iscntrl.c
new file mode 100644 (file)
index 0000000..14d50e5
--- /dev/null
@@ -0,0 +1,33 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int iscntrl( int c )
+{
+    return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_CNTRL );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( iscntrl( '\a' ) );
+    TESTCASE( iscntrl( '\b' ) );
+    TESTCASE( iscntrl( '\n' ) );
+    TESTCASE( ! iscntrl( ' ' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/isdigit.c b/src/pdclib/functions/ctype/isdigit.c
new file mode 100644 (file)
index 0000000..00d6bcf
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int isdigit( int c )
+{
+    return ( c >= _PDCLIB_lc_ctype.digits_low && c <= _PDCLIB_lc_ctype.digits_high );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( isdigit( '0' ) );
+    TESTCASE( isdigit( '9' ) );
+    TESTCASE( ! isdigit( ' ' ) );
+    TESTCASE( ! isdigit( 'a' ) );
+    TESTCASE( ! isdigit( '@' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/isgraph.c b/src/pdclib/functions/ctype/isgraph.c
new file mode 100644 (file)
index 0000000..fdc5819
--- /dev/null
@@ -0,0 +1,37 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int isgraph( int c )
+{
+    return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_GRAPH );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( isgraph( 'a' ) );
+    TESTCASE( isgraph( 'z' ) );
+    TESTCASE( isgraph( 'A' ) );
+    TESTCASE( isgraph( 'Z' ) );
+    TESTCASE( isgraph( '@' ) );
+    TESTCASE( ! isgraph( '\t' ) );
+    TESTCASE( ! isgraph( '\0' ) );
+    TESTCASE( ! isgraph( ' ' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/islower.c b/src/pdclib/functions/ctype/islower.c
new file mode 100644 (file)
index 0000000..5d225db
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int islower( int c )
+{
+    return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_LOWER );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( islower( 'a' ) );
+    TESTCASE( islower( 'z' ) );
+    TESTCASE( ! islower( 'A' ) );
+    TESTCASE( ! islower( 'Z' ) );
+    TESTCASE( ! islower( ' ' ) );
+    TESTCASE( ! islower( '@' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/isprint.c b/src/pdclib/functions/ctype/isprint.c
new file mode 100644 (file)
index 0000000..d3f1489
--- /dev/null
@@ -0,0 +1,38 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+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 == ' ' );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( isprint( 'a' ) );
+    TESTCASE( isprint( 'z' ) );
+    TESTCASE( isprint( 'A' ) );
+    TESTCASE( isprint( 'Z' ) );
+    TESTCASE( isprint( '@' ) );
+    TESTCASE( ! isprint( '\t' ) );
+    TESTCASE( ! isprint( '\0' ) );
+    TESTCASE( isprint( ' ' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/ispunct.c b/src/pdclib/functions/ctype/ispunct.c
new file mode 100644 (file)
index 0000000..2afdb0c
--- /dev/null
@@ -0,0 +1,38 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int ispunct( int c )
+{
+    return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_PUNCT );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( ! ispunct( 'a' ) );
+    TESTCASE( ! ispunct( 'z' ) );
+    TESTCASE( ! ispunct( 'A' ) );
+    TESTCASE( ! ispunct( 'Z' ) );
+    TESTCASE( ispunct( '@' ) );
+    TESTCASE( ispunct( '.' ) );
+    TESTCASE( ! ispunct( '\t' ) );
+    TESTCASE( ! ispunct( '\0' ) );
+    TESTCASE( ! ispunct( ' ' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/isspace.c b/src/pdclib/functions/ctype/isspace.c
new file mode 100644 (file)
index 0000000..a724de6
--- /dev/null
@@ -0,0 +1,36 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int isspace( int c )
+{
+    return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_SPACE );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( isspace( ' ' ) );
+    TESTCASE( isspace( '\f' ) );
+    TESTCASE( isspace( '\n' ) );
+    TESTCASE( isspace( '\r' ) );
+    TESTCASE( isspace( '\t' ) );
+    TESTCASE( isspace( '\v' ) );
+    TESTCASE( ! isspace( 'a' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/isupper.c b/src/pdclib/functions/ctype/isupper.c
new file mode 100644 (file)
index 0000000..79b55a3
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int isupper( int c )
+{
+    return ( _PDCLIB_lc_ctype.entry[c].flags & _PDCLIB_CTYPE_UPPER );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( isupper( 'A' ) );
+    TESTCASE( isupper( 'Z' ) );
+    TESTCASE( ! isupper( 'a' ) );
+    TESTCASE( ! isupper( 'z' ) );
+    TESTCASE( ! isupper( ' ' ) );
+    TESTCASE( ! isupper( '@' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/isxdigit.c b/src/pdclib/functions/ctype/isxdigit.c
new file mode 100644 (file)
index 0000000..30839c0
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+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 ) );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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( ' ' ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/tolower.c b/src/pdclib/functions/ctype/tolower.c
new file mode 100644 (file)
index 0000000..bbb76d1
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int tolower( int c )
+{
+    return _PDCLIB_lc_ctype.entry[c].lower;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( tolower( 'A' ) == 'a' );
+    TESTCASE( tolower( 'Z' ) == 'z' );
+    TESTCASE( tolower( 'a' ) == 'a' );
+    TESTCASE( tolower( 'z' ) == 'z' );
+    TESTCASE( tolower( '@' ) == '@' );
+    TESTCASE( tolower( '[' ) == '[' );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/ctype/toupper.c b/src/pdclib/functions/ctype/toupper.c
new file mode 100644 (file)
index 0000000..79e6e2b
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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 <ctype.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+int toupper( int c )
+{
+    return _PDCLIB_lc_ctype.entry[c].upper;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( toupper( 'a' ) == 'A' );
+    TESTCASE( toupper( 'z' ) == 'Z' );
+    TESTCASE( toupper( 'A' ) == 'A' );
+    TESTCASE( toupper( 'Z' ) == 'Z' );
+    TESTCASE( toupper( '@' ) == '@' );
+    TESTCASE( toupper( '[' ) == '[' );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/inttypes/imaxabs.c b/src/pdclib/functions/inttypes/imaxabs.c
new file mode 100644 (file)
index 0000000..06d029e
--- /dev/null
@@ -0,0 +1,32 @@
+/* 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 <inttypes.h>
+
+#ifndef REGTEST
+
+intmax_t imaxabs( intmax_t j )
+{
+    return ( j >= 0 ) ? j : -j;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <limits.h>
+
+int main( void )
+{
+    TESTCASE( imaxabs( (intmax_t)0 ) == 0 );
+    TESTCASE( imaxabs( INTMAX_MAX ) == INTMAX_MAX );
+    TESTCASE( imaxabs( INTMAX_MIN + 1 ) == -( INTMAX_MIN + 1 ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/inttypes/imaxdiv.c b/src/pdclib/functions/inttypes/imaxdiv.c
new file mode 100644 (file)
index 0000000..7143c3d
--- /dev/null
@@ -0,0 +1,39 @@
+/* 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 <inttypes.h>
+
+#ifndef REGTEST
+
+imaxdiv_t imaxdiv( intmax_t numer, intmax_t denom )
+{
+    imaxdiv_t rc;
+    rc.quot = numer / denom;
+    rc.rem  = numer % denom;
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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 ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/inttypes/strtoimax.c b/src/pdclib/functions/inttypes/strtoimax.c
new file mode 100644 (file)
index 0000000..29a3c92
--- /dev/null
@@ -0,0 +1,147 @@
+/* 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 <limits.h>
+#include <inttypes.h>
+
+#ifndef REGTEST
+
+#include <stddef.h>
+
+intmax_t strtoimax( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <errno.h>
+
+int main( void )
+{
+    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 );
+    TESTCASE( endptr == tricky + 2 );
+    /* proper handling of border case: 0 followed by non-octdigit */
+    TESTCASE( strtoimax( tricky, &endptr, 8 ) == 0 );
+    TESTCASE( endptr == tricky + 2 );
+    /* errno should still be 0 */
+    TESTCASE( errno == 0 );
+    /* overflowing subject sequence must still return proper endptr */
+    TESTCASE( strtoimax( overflow, &endptr, 36 ) == INTMAX_MIN );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( endptr - overflow ) == 53 );
+    /* same for positive */
+    errno = 0;
+    TESTCASE( strtoimax( overflow + 1, &endptr, 36 ) == INTMAX_MAX );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( 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 );
+    TESTCASE( endptr == overflow );
+    endptr = NULL;
+    TESTCASE( strtoimax( overflow, &endptr, 0 ) == 0 );
+    TESTCASE( 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?                                           */
+    errno = 0;
+#if INTMAX_MAX >> 62 == 1
+    /* testing "odd" overflow, i.e. base is not a power of two */
+    TESTCASE( strtoimax( "9223372036854775807", NULL, 0 ) == INTMAX_MAX );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "9223372036854775808", NULL, 0 ) == INTMAX_MAX );
+    TESTCASE( errno == ERANGE );
+    errno = 0;
+    TESTCASE( strtoimax( "-9223372036854775807", NULL, 0 ) == (INTMAX_MIN + 1) );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "-9223372036854775808", NULL, 0 ) == INTMAX_MIN );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "-9223372036854775809", NULL, 0 ) == INTMAX_MIN );
+    TESTCASE( errno == ERANGE );
+    /* testing "even" overflow, i.e. base is power of two */
+    errno = 0;
+    TESTCASE( strtoimax( "0x7fffffffffffffff", NULL, 0 ) == INTMAX_MAX );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "0x8000000000000000", NULL, 0 ) == INTMAX_MAX );
+    TESTCASE( errno == ERANGE );
+    errno = 0;
+    TESTCASE( strtoimax( "-0x7fffffffffffffff", NULL, 0 ) == (INTMAX_MIN + 1) );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "-0x8000000000000000", NULL, 0 ) == INTMAX_MIN );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "-0x8000000000000001", NULL, 0 ) == INTMAX_MIN );
+    TESTCASE( 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 );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "170141183460469231731687303715884105729", NULL, 0 ) == INTMAX_MAX );
+    TESTCASE( errno == ERANGE );
+    errno = 0;
+    TESTCASE( strtoimax( "-170141183460469231731687303715884105728", NULL, 0 ) == (INTMAX_MIN + 1) );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "-170141183460469231731687303715884105729", NULL, 0 ) == INTMAX_MIN );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "-170141183460469231731687303715884105730", NULL, 0 ) == INTMAX_MIN );
+    TESTCASE( errno == ERANGE );
+    /* testing "even" overflow, i.e. base is power of two */
+    errno = 0;
+    TESTCASE( strtoimax( "0x7fffffffffffffffffffffffffffffff", NULL, 0 ) == INTMAX_MAX );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "0x80000000000000000000000000000000", NULL, 0 ) == INTMAX_MAX );
+    TESTCASE( errno == ERANGE );
+    errno = 0;
+    TESTCASE( strtoimax( "-0x7fffffffffffffffffffffffffffffff", NULL, 0 ) == (INTMAX_MIN + 1) );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "-0x80000000000000000000000000000000", NULL, 0 ) == INTMAX_MIN );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoimax( "-0x80000000000000000000000000000001", NULL, 0 ) == INTMAX_MIN );
+    TESTCASE( errno == ERANGE );
+#else
+#error Unsupported width of 'intmax_t' (neither 64 nor 128 bit).
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/inttypes/strtoumax.c b/src/pdclib/functions/inttypes/strtoumax.c
new file mode 100644 (file)
index 0000000..9ed16c1
--- /dev/null
@@ -0,0 +1,111 @@
+/* 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 <limits.h>
+#include <inttypes.h>
+
+#ifndef REGTEST
+
+#include <stddef.h>
+
+uintmax_t strtoumax( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <errno.h>
+
+int main( void )
+{
+    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 );
+    TESTCASE( endptr == tricky + 2 );
+    /* proper handling of border case: 0 followed by non-octdigit */
+    TESTCASE( strtoumax( tricky, &endptr, 8 ) == 0 );
+    TESTCASE( endptr == tricky + 2 );
+    /* errno should still be 0 */
+    TESTCASE( errno == 0 );
+    /* overflowing subject sequence must still return proper endptr */
+    TESTCASE( strtoumax( overflow, &endptr, 36 ) == UINTMAX_MAX );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( endptr - overflow ) == 53 );
+    /* same for positive */
+    errno = 0;
+    TESTCASE( strtoumax( overflow + 1, &endptr, 36 ) == UINTMAX_MAX );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( 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 );
+    TESTCASE( endptr == overflow );
+    endptr = NULL;
+    TESTCASE( strtoumax( overflow, &endptr, 0 ) == 0 );
+    TESTCASE( endptr == overflow );
+    errno = 0;
+/* 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 );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoumax( "18446744073709551616", NULL, 0 ) == UINTMAX_MAX );
+    TESTCASE( errno == ERANGE );
+    /* testing "even" overflow, i.e. base is power of two */
+    errno = 0;
+    TESTCASE( strtoumax( "0xFFFFFFFFFFFFFFFF", NULL, 0 ) == UINTMAX_MAX );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoumax( "0x10000000000000000", NULL, 0 ) == UINTMAX_MAX );
+    TESTCASE( 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 );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoumax( "340282366920938463463374607431768211456", NULL, 0 ) == UINTMAX_MAX );
+    TESTCASE( errno == ERANGE );
+    /* testing "even" everflow, i.e. base is power of two */
+    errno = 0;
+    TESTCASE( strtoumax( "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NULL, 0 ) == UINTMAX_MAX );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoumax( "0x100000000000000000000000000000000", NULL, 0 ) == UINTMAX_MAX );
+    TESTCASE( errno == ERANGE );
+#else
+#error Unsupported width of 'uintmax_t' (neither 64 nor 128 bit).
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/locale/localeconv.c b/src/pdclib/functions/locale/localeconv.c
new file mode 100644 (file)
index 0000000..cdcb1b0
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <locale.h>
+
+#ifndef REGTEST
+
+struct lconv * localeconv( void )
+{
+    return _PDCLIB_lc_numeric_monetary.lconv;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/locale/setlocale.c b/src/pdclib/functions/locale/setlocale.c
new file mode 100644 (file)
index 0000000..585504a
--- /dev/null
@@ -0,0 +1,257 @@
+/* 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 <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+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 _PDCLIB_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
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    setlocale(0,"");
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/clearerr.c b/src/pdclib/functions/stdio/clearerr.c
new file mode 100644 (file)
index 0000000..91cd00a
--- /dev/null
@@ -0,0 +1,52 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+void clearerr( struct _PDCLIB_file_t * stream )
+{
+    stream->status &= ~( _PDCLIB_ERRORFLAG | _PDCLIB_EOFFLAG );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    FILE * fh;
+    TESTCASE( ( fh = tmpfile() ) != 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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fclose.c b/src/pdclib/functions/stdio/fclose.c
new file mode 100644 (file)
index 0000000..9a1388d
--- /dev/null
@@ -0,0 +1,106 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_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_config.h>.
+    */
+    _PDCLIB_errno = _PDCLIB_ERROR;
+    return -1;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    struct _PDCLIB_file_t * file1;
+    struct _PDCLIB_file_t * file2;
+    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 );
+#else
+    puts( " NOTEST fclose() test driver is PDCLib-specific." );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/feof.c b/src/pdclib/functions/stdio/feof.c
new file mode 100644 (file)
index 0000000..a57071f
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+int feof( struct _PDCLIB_file_t * stream )
+{
+    return stream->status & _PDCLIB_EOFFLAG;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by clearerr(). */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/ferror.c b/src/pdclib/functions/stdio/ferror.c
new file mode 100644 (file)
index 0000000..54f43f0
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+int ferror( struct _PDCLIB_file_t * stream )
+{
+    return stream->status & _PDCLIB_ERRORFLAG;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by clearerr(). */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fflush.c b/src/pdclib/functions/stdio/fflush.c
new file mode 100644 (file)
index 0000000..3d0b297
--- /dev/null
@@ -0,0 +1,53 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_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 );
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fgetc.c b/src/pdclib/functions/stdio/fgetc.c
new file mode 100644 (file)
index 0000000..bdb233e
--- /dev/null
@@ -0,0 +1,38 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_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++];
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fgetpos.c b/src/pdclib/functions/stdio/fgetpos.c
new file mode 100644 (file)
index 0000000..db9be06
--- /dev/null
@@ -0,0 +1,43 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+int fgetpos( struct _PDCLIB_file_t * _PDCLIB_restrict stream, struct _PDCLIB_fpos_t * _PDCLIB_restrict pos )
+{
+    pos->offset = stream->pos.offset + stream->bufidx - stream->ungetidx;
+    pos->status = stream->pos.status;
+    /* TODO: Add mbstate. */
+    return 0;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <string.h>
+
+int main( void )
+{
+    FILE * fh;
+    fpos_t pos1, pos2;
+    TESTCASE( ( fh = tmpfile() ) != 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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fgets.c b/src/pdclib/functions/stdio/fgets.c
new file mode 100644 (file)
index 0000000..0499afd
--- /dev/null
@@ -0,0 +1,88 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+char * fgets( char * _PDCLIB_restrict s, int size, struct _PDCLIB_file_t * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <string.h>
+
+int main( void )
+{
+    FILE * fh;
+    char buffer[10];
+    const char * fgets_test = "foo\nbar\0baz\nweenie";
+    TESTCASE( ( 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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fopen.c b/src/pdclib/functions/stdio/fopen.c
new file mode 100644 (file)
index 0000000..d5241a7
--- /dev/null
@@ -0,0 +1,102 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+#include <string.h>
+
+extern struct _PDCLIB_file_t * _PDCLIB_filelist;
+
+struct _PDCLIB_file_t * fopen( const char * _PDCLIB_restrict filename, const char * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* 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;
+    remove( testfile );
+    TESTCASE_NOREG( fopen( NULL, NULL ) == NULL );
+    TESTCASE( fopen( NULL, "w" ) == NULL );
+    TESTCASE_NOREG( fopen( "", NULL ) == NULL );
+    TESTCASE( fopen( "", "w" ) == NULL );
+    TESTCASE( fopen( "foo", "" ) == NULL );
+    TESTCASE_NOREG( fopen( testfile, "wq" ) == NULL ); /* Undefined mode */
+    TESTCASE_NOREG( fopen( testfile, "wr" ) == NULL ); /* Undefined mode */
+    TESTCASE( ( fh = fopen( testfile, "w" ) ) != NULL );
+    TESTCASE( fclose( fh ) == 0 );
+    TESTCASE( remove( testfile ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fprintf.c b/src/pdclib/functions/stdio/fprintf.c
new file mode 100644 (file)
index 0000000..e16adf8
--- /dev/null
@@ -0,0 +1,43 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int fprintf( struct _PDCLIB_file_t * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, ... )
+{
+    int rc;
+    va_list ap;
+    va_start( ap, format );
+    rc = vfprintf( stream, format, ap );
+    va_end( ap );
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+#include <stdint.h>
+#include <stddef.h>
+#define _PDCLIB_FILEID "stdio/fprintf.c"
+#define _PDCLIB_FILEIO
+
+#include "_PDCLIB_test.h"
+
+#define testprintf( stream, ... ) fprintf( stream, __VA_ARGS__ )
+
+int main( void )
+{
+    FILE * target;
+    TESTCASE( ( target = tmpfile() ) != NULL );
+#include "printf_testcases.h"
+    TESTCASE( fclose( target ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fputc.c b/src/pdclib/functions/stdio/fputc.c
new file mode 100644 (file)
index 0000000..05ac792
--- /dev/null
@@ -0,0 +1,47 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fputs.c b/src/pdclib/functions/stdio/fputs.c
new file mode 100644 (file)
index 0000000..a5d7e56
--- /dev/null
@@ -0,0 +1,69 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+int fputs( const char * _PDCLIB_restrict s, struct _PDCLIB_file_t * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    const char * const message = "SUCCESS testing fputs()";
+    FILE * fh;
+    size_t i;
+    TESTCASE( ( fh = tmpfile() ) != NULL );
+    TESTCASE( fputs( message, fh ) >= 0 );
+    rewind( fh );
+    for ( i = 0; i < 23; ++i )
+    {
+        TESTCASE( fgetc( fh ) == message[i] );
+    }
+    TESTCASE( fclose( fh ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fread.c b/src/pdclib/functions/stdio/fread.c
new file mode 100644 (file)
index 0000000..319e9ba
--- /dev/null
@@ -0,0 +1,81 @@
+/* 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 <stdio.h>
+#include <string.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+size_t fread( void * _PDCLIB_restrict ptr, size_t size, size_t nmemb, struct _PDCLIB_file_t * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    FILE * fh;
+    const char * message = "Testing fwrite()...\n";
+    char buffer[21];
+    buffer[20] = 'x';
+    TESTCASE( ( 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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/freopen.c b/src/pdclib/functions/stdio/freopen.c
new file mode 100644 (file)
index 0000000..2cb774c
--- /dev/null
@@ -0,0 +1,104 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+struct _PDCLIB_file_t * freopen( const char * _PDCLIB_restrict filename, const char * _PDCLIB_restrict mode, struct _PDCLIB_file_t * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    FILE * fin;
+    FILE * fout;
+    TESTCASE( ( 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 );
+
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fscanf.c b/src/pdclib/functions/stdio/fscanf.c
new file mode 100644 (file)
index 0000000..cc8c031
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int fscanf( FILE * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, ... )
+{
+    int rc;
+    va_list ap;
+    va_start( ap, format );
+    rc = vfscanf( stream, format, ap );
+    va_end( ap );
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/fscanf.c"
+#define _PDCLIB_FILEIO
+
+#include "_PDCLIB_test.h"
+
+#define testscanf( stream, format, ... ) fscanf( stream, format, __VA_ARGS__ )
+
+int main( void )
+{
+    FILE * source;
+    TESTCASE( ( source = tmpfile() ) != NULL );
+#include "scanf_testcases.h"
+    TESTCASE( fclose( source ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fseek.c b/src/pdclib/functions/stdio/fseek.c
new file mode 100644 (file)
index 0000000..54f4c4b
--- /dev/null
@@ -0,0 +1,85 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <string.h>
+
+int main( void )
+{
+    FILE * fh;
+    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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fsetpos.c b/src/pdclib/functions/stdio/fsetpos.c
new file mode 100644 (file)
index 0000000..323aaae
--- /dev/null
@@ -0,0 +1,43 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* fsetpos() tested together with fsetpos(). */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/ftell.c b/src/pdclib/functions/stdio/ftell.c
new file mode 100644 (file)
index 0000000..f4bd300
--- /dev/null
@@ -0,0 +1,100 @@
+/* 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 <stdio.h>
+#include <limits.h>
+
+#ifndef REGTEST
+
+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 ) );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdlib.h>
+
+int main( void )
+{
+    /* 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;
+    TESTCASE( ( 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_NOREG( fh->pos.offset == 0l );
+    TESTCASE_NOREG( fh->bufidx == 3l );
+    /* Buffer properly flushed when full? */
+    TESTCASE( fputc( '4', fh ) == '4' );
+    TESTCASE_NOREG( fh->pos.offset == 4l );
+    TESTCASE_NOREG( fh->bufidx == 0 );
+    /* fflush() resetting positions as expected? */
+    TESTCASE( fputc( '5', fh ) == '5' );
+    TESTCASE( fflush( fh ) == 0 );
+    TESTCASE( ftell( fh ) == 5l );
+    TESTCASE_NOREG( fh->pos.offset == 5l );
+    TESTCASE_NOREG( fh->bufidx == 0l );
+    /* rewind() resetting positions as expected? */
+    rewind( fh );
+    TESTCASE( ftell( fh ) == 0l );
+    TESTCASE_NOREG( fh->pos.offset == 0 );
+    TESTCASE_NOREG( 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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/fwrite.c b/src/pdclib/functions/stdio/fwrite.c
new file mode 100644 (file)
index 0000000..7958a50
--- /dev/null
@@ -0,0 +1,93 @@
+/* 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 <stdio.h>
+#include <string.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+size_t fwrite( const void * _PDCLIB_restrict ptr, size_t size, size_t nmemb, struct _PDCLIB_file_t * _PDCLIB_restrict stream )
+{
+    _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by fread(). */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/getc.c b/src/pdclib/functions/stdio/getc.c
new file mode 100644 (file)
index 0000000..3d082b3
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+int getc( struct _PDCLIB_file_t * stream )
+{
+    return fgetc( stream );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/getchar.c b/src/pdclib/functions/stdio/getchar.c
new file mode 100644 (file)
index 0000000..34ee545
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+int getchar( void )
+{
+    return fgetc( stdin );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/perror.c b/src/pdclib/functions/stdio/perror.c
new file mode 100644 (file)
index 0000000..dd6633c
--- /dev/null
@@ -0,0 +1,58 @@
+/* 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 <stdio.h>
+#include <errno.h>
+#include <locale.h>
+
+#ifndef REGTEST
+
+/* 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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+int main( void )
+{
+    FILE * fh;
+    unsigned long long max = ULLONG_MAX;
+    char buffer[100];
+    sprintf( buffer, "%llu", max );
+    TESTCASE( ( fh = freopen( testfile, "wb+", 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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/printf.c b/src/pdclib/functions/stdio/printf.c
new file mode 100644 (file)
index 0000000..6d10113
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int printf( const char * _PDCLIB_restrict format, ... )
+{
+    int rc;
+    va_list ap;
+    va_start( ap, format );
+    rc = vfprintf( stdout, format, ap );
+    va_end( ap );
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/printf.c"
+#define _PDCLIB_FILEIO
+#include <stdint.h>
+#include <stddef.h>
+
+#include "_PDCLIB_test.h"
+
+#define testprintf( stream, ... ) printf( __VA_ARGS__ )
+
+int main( void )
+{
+    FILE * target;
+    TESTCASE( ( target = freopen( testfile, "wb+", stdout ) ) != NULL );
+#include "printf_testcases.h"
+    TESTCASE( fclose( target ) == 0 );
+    TESTCASE( remove( testfile ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/putc.c b/src/pdclib/functions/stdio/putc.c
new file mode 100644 (file)
index 0000000..c1eb2b0
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+int putc( int c, struct _PDCLIB_file_t * stream )
+{
+    return fputc( c, stream );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/putchar.c b/src/pdclib/functions/stdio/putchar.c
new file mode 100644 (file)
index 0000000..15bf299
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+int putchar( int c )
+{
+    return fputc( c, stdout );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/puts.c b/src/pdclib/functions/stdio/puts.c
new file mode 100644 (file)
index 0000000..89240cb
--- /dev/null
@@ -0,0 +1,67 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_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;
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    FILE * fh;
+    const char * message = "SUCCESS testing puts()";
+    char buffer[23];
+    buffer[22] = 'x';
+    TESTCASE( ( 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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/rename.c b/src/pdclib/functions/stdio/rename.c
new file mode 100644 (file)
index 0000000..5d45f1f
--- /dev/null
@@ -0,0 +1,84 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+#include <string.h>
+
+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 );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdlib.h>
+
+int main( void )
+{
+    FILE * file;
+    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( ( 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_NOREG( 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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/rewind.c b/src/pdclib/functions/stdio/rewind.c
new file mode 100644 (file)
index 0000000..a449b7d
--- /dev/null
@@ -0,0 +1,29 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+void rewind( struct _PDCLIB_file_t * stream )
+{
+    stream->status &= ~ _PDCLIB_ERRORFLAG;
+    fseek( stream, 0L, SEEK_SET );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/scanf.c b/src/pdclib/functions/stdio/scanf.c
new file mode 100644 (file)
index 0000000..b29b1d5
--- /dev/null
@@ -0,0 +1,39 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int scanf( const char * _PDCLIB_restrict format, ... )
+{
+    va_list ap;
+    va_start( ap, format );
+    return vfscanf( stdin, format, ap );
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/scanf.c"
+#define _PDCLIB_FILEIO
+
+#include "_PDCLIB_test.h"
+
+#define testscanf( stream, format, ... ) scanf( format, __VA_ARGS__ )
+
+int main( void )
+{
+    FILE * source;
+    TESTCASE( ( source = freopen( testfile, "wb+", stdin ) ) != NULL );
+#include "scanf_testcases.h"
+    TESTCASE( fclose( source ) == 0 );
+    TESTCASE( remove( testfile ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/setbuf.c b/src/pdclib/functions/stdio/setbuf.c
new file mode 100644 (file)
index 0000000..6c32072
--- /dev/null
@@ -0,0 +1,55 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+void setbuf( struct _PDCLIB_file_t * _PDCLIB_restrict stream, char * _PDCLIB_restrict buf )
+{
+    if ( buf == NULL )
+    {
+        setvbuf( stream, buf, _IONBF, BUFSIZ );
+    }
+    else
+    {
+        setvbuf( stream, buf, _IOFBF, BUFSIZ );
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdlib.h>
+
+int main( void )
+{
+    /* TODO: Extend testing once setvbuf() is finished. */
+#ifndef REGTEST
+    char buffer[ BUFSIZ + 1 ];
+    FILE * fh;
+    /* full buffered */
+    TESTCASE( ( 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 );
+#else
+    puts( " NOTEST setbuf() test driver is PDCLib-specific." );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/setvbuf.c b/src/pdclib/functions/stdio/setvbuf.c
new file mode 100644 (file)
index 0000000..00592ff
--- /dev/null
@@ -0,0 +1,108 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#ifndef REGTEST
+
+int setvbuf( struct _PDCLIB_file_t * _PDCLIB_restrict stream, char * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <errno.h>
+
+#define BUFFERSIZE 500
+
+int main( void )
+{
+#ifndef REGTEST
+    char buffer[ BUFFERSIZE ];
+    FILE * fh;
+    /* full buffered, user-supplied buffer */
+    TESTCASE( ( 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 );
+#else
+    puts( " NOTEST setvbuf() test driver is PDCLib-specific." );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/snprintf.c b/src/pdclib/functions/stdio/snprintf.c
new file mode 100644 (file)
index 0000000..f095b8c
--- /dev/null
@@ -0,0 +1,43 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int snprintf( char * _PDCLIB_restrict s, size_t n, const char * _PDCLIB_restrict format, ...)
+{
+    int rc;
+    va_list ap;
+    va_start( ap, format );
+    rc = vsnprintf( s, n, format, ap );
+    va_end( ap );
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/snprintf.c"
+#define _PDCLIB_STRINGIO
+#include <stdint.h>
+#include <stddef.h>
+
+#include "_PDCLIB_test.h"
+
+#define testprintf( s, ... ) snprintf( s, 100, __VA_ARGS__ )
+
+int main( void )
+{
+    char target[100];
+#include "printf_testcases.h"
+    TESTCASE( snprintf( NULL, 0, "foo" ) == 3 );
+    TESTCASE( snprintf( NULL, 0, "%d", 100 ) == 3 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/sprintf.c b/src/pdclib/functions/stdio/sprintf.c
new file mode 100644 (file)
index 0000000..ce3a7ff
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int sprintf( char * _PDCLIB_restrict s, const char * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/sprintf.c"
+#define _PDCLIB_STRINGIO
+#include <stddef.h>
+
+#include "_PDCLIB_test.h"
+
+#define testprintf( s, ... ) sprintf( s, __VA_ARGS__ )
+
+int main( void )
+{
+    char target[100];
+#include "printf_testcases.h"
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/sscanf.c b/src/pdclib/functions/stdio/sscanf.c
new file mode 100644 (file)
index 0000000..9ba830d
--- /dev/null
@@ -0,0 +1,39 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int sscanf( const char * _PDCLIB_restrict s, const char * _PDCLIB_restrict format, ... )
+{
+    int rc;
+    va_list ap;
+    va_start( ap, format );
+    rc = vsscanf( s, format, ap );
+    va_end( ap );
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/sscanf.c"
+#define _PDCLIB_STRINGIO
+
+#include "_PDCLIB_test.h"
+
+#define testscanf( s, format, ... ) sscanf( s, format, __VA_ARGS__ )
+
+int main( void )
+{
+    char source[100];
+#include "scanf_testcases.h"
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/testfile.txt b/src/pdclib/functions/stdio/testfile.txt
new file mode 100644 (file)
index 0000000..f08c4d3
Binary files /dev/null and b/src/pdclib/functions/stdio/testfile.txt differ
diff --git a/src/pdclib/functions/stdio/tmpnam.c b/src/pdclib/functions/stdio/tmpnam.c
new file mode 100644 (file)
index 0000000..5909aff
--- /dev/null
@@ -0,0 +1,42 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+#include <string.h>
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <string.h>
+
+int main( void )
+{
+    TESTCASE( strlen( tmpnam( NULL ) ) < L_tmpnam );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/ungetc.c b/src/pdclib/functions/stdio/ungetc.c
new file mode 100644 (file)
index 0000000..dc5260b
--- /dev/null
@@ -0,0 +1,32 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/vfprintf.c b/src/pdclib/functions/stdio/vfprintf.c
new file mode 100644 (file)
index 0000000..6f31bbd
--- /dev/null
@@ -0,0 +1,74 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#ifndef REGTEST
+
+int vfprintf( struct _PDCLIB_file_t * _PDCLIB_restrict stream, const char * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/vfprintf.c"
+#define _PDCLIB_FILEIO
+#include <stddef.h>
+#include "_PDCLIB_test.h"
+
+static int testprintf( FILE * stream, const char * format, ... )
+{
+    int i;
+    va_list arg;
+    va_start( arg, format );
+    i = vfprintf( stream, format, arg );
+    va_end( arg );
+    return i;
+}
+
+int main( void )
+{
+    FILE * target;
+    TESTCASE( ( target = tmpfile() ) != NULL );
+#include "printf_testcases.h"
+    TESTCASE( fclose( target ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/vfscanf.c b/src/pdclib/functions/stdio/vfscanf.c
new file mode 100644 (file)
index 0000000..bb24456
--- /dev/null
@@ -0,0 +1,113 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#ifndef REGTEST
+
+int vfscanf( FILE * _PDCLIB_restrict stream, const char * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/vfscanf.c"
+#define _PDCLIB_FILEIO
+
+#include "_PDCLIB_test.h"
+
+static int testscanf( FILE * stream, const char * format, ... )
+{
+    va_list ap;
+    int result;
+    va_start( ap, format );
+    result = vfscanf( stream, format, ap );
+    va_end( ap );
+    return result;
+}
+
+int main( void )
+{
+    FILE * source;
+    TESTCASE( ( source = tmpfile() ) != NULL );
+#include "scanf_testcases.h"
+    TESTCASE( fclose( source ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/vprintf.c b/src/pdclib/functions/stdio/vprintf.c
new file mode 100644 (file)
index 0000000..fe80152
--- /dev/null
@@ -0,0 +1,46 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int vprintf( const char * _PDCLIB_restrict format, _PDCLIB_va_list arg )
+{
+    return vfprintf( stdout, format, arg );
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/vprintf.c"
+#define _PDCLIB_FILEIO
+#include <stdint.h>
+#include <stddef.h>
+#include "_PDCLIB_test.h"
+
+static int testprintf( FILE * stream, const char * format, ... )
+{
+    int i;
+    va_list arg;
+    va_start( arg, format );
+    i = vprintf( format, arg );
+    va_end( arg );
+    return i;
+}
+
+int main( void )
+{
+    FILE * target;
+    TESTCASE( ( target = freopen( testfile, "wb+", stdout ) ) != NULL );
+#include "printf_testcases.h"
+    TESTCASE( fclose( target ) == 0 );
+    TESTCASE( remove( testfile ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/vscanf.c b/src/pdclib/functions/stdio/vscanf.c
new file mode 100644 (file)
index 0000000..d5ae5b3
--- /dev/null
@@ -0,0 +1,45 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int vscanf( const char * _PDCLIB_restrict format, _PDCLIB_va_list arg )
+{
+    return vfscanf( stdin, format, arg );
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/vscanf.c"
+#define _PDCLIB_FILEIO
+
+#include "_PDCLIB_test.h"
+
+static int testscanf( FILE * stream, const char * format, ... )
+{
+    int i;
+    va_list arg;
+    va_start( arg, format );
+    i = vscanf( format, arg );
+    va_end( arg );
+    return i;
+}
+
+int main( void )
+{
+    FILE * source;
+    TESTCASE( ( source = freopen( testfile, "wb+", stdin ) ) != NULL );
+#include "scanf_testcases.h"
+    TESTCASE( fclose( source ) == 0 );
+    TESTCASE( remove( testfile ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/vsnprintf.c b/src/pdclib/functions/stdio/vsnprintf.c
new file mode 100644 (file)
index 0000000..e57e682
--- /dev/null
@@ -0,0 +1,80 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int vsnprintf( char * _PDCLIB_restrict s, size_t n, const char * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/vsnprintf.c"
+#define _PDCLIB_STRINGIO
+#include <stdint.h>
+#include <stddef.h>
+#include "_PDCLIB_test.h"
+
+static int testprintf( char * s, const char * format, ... )
+{
+    int i;
+    va_list arg;
+    va_start( arg, format );
+    i = vsnprintf( s, 100, format, arg );
+    va_end( arg );
+    return i;
+}
+
+int main( void )
+{
+    char target[100];
+#include "printf_testcases.h"
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/vsprintf.c b/src/pdclib/functions/stdio/vsprintf.c
new file mode 100644 (file)
index 0000000..4ab2742
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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 <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#ifndef REGTEST
+
+int vsprintf( char * _PDCLIB_restrict s, const char * _PDCLIB_restrict format, va_list arg )
+{
+    return vsnprintf( s, SIZE_MAX, format, arg ); /* TODO: Replace with a non-checking call */
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/vsprintf.c"
+#define _PDCLIB_STRINGIO
+#include <stdint.h>
+#include <stddef.h>
+#include "_PDCLIB_test.h"
+
+static int testprintf( char * s, const char * format, ... )
+{
+    int i;
+    va_list arg;
+    va_start( arg, format );
+    i = vsprintf( s, format, arg );
+    va_end( arg );
+    return i;
+}
+
+int main( void )
+{
+    char target[100];
+#include "printf_testcases.h"
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdio/vsscanf.c b/src/pdclib/functions/stdio/vsscanf.c
new file mode 100644 (file)
index 0000000..e76e70e
--- /dev/null
@@ -0,0 +1,109 @@
+/* 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 <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#ifndef REGTEST
+
+int vsscanf( const char * _PDCLIB_restrict s, const char * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+#define _PDCLIB_FILEID "stdio/vsscanf.c"
+#define _PDCLIB_STRINGIO
+
+#include "_PDCLIB_test.h"
+
+static int testscanf( const char * stream, const char * format, ... )
+{
+    va_list ap;
+    int result;
+    va_start( ap, format );
+    result = vsscanf( stream, format, ap );
+    va_end( ap );
+    return result;
+}
+
+int main( void )
+{
+    char source[100];
+#include "scanf_testcases.h"
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/_Exit.c b/src/pdclib/functions/stdlib/_Exit.c
new file mode 100644 (file)
index 0000000..de387db
--- /dev/null
@@ -0,0 +1,36 @@
+/* _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 <stdlib.h>
+#include <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_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 );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    int UNEXPECTED_RETURN = 0;
+    _Exit( 0 );
+    TESTCASE( UNEXPECTED_RETURN );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/abort.c b/src/pdclib/functions/stdlib/abort.c
new file mode 100644 (file)
index 0000000..69422f9
--- /dev/null
@@ -0,0 +1,40 @@
+/* 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 <stdlib.h>
+#include <signal.h>
+
+#ifndef REGTEST
+
+void abort( void )
+{
+    raise( SIGABRT );
+    exit( EXIT_FAILURE );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdio.h>
+
+static void aborthandler( int sig )
+{
+    exit( 0 );
+}
+
+int main( void )
+{
+    int UNEXPECTED_RETURN_FROM_ABORT = 0;
+    TESTCASE( signal( SIGABRT, &aborthandler ) != SIG_ERR );
+    abort();
+    TESTCASE( UNEXPECTED_RETURN_FROM_ABORT );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/abs.c b/src/pdclib/functions/stdlib/abs.c
new file mode 100644 (file)
index 0000000..7a634e3
--- /dev/null
@@ -0,0 +1,32 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+int abs( int j )
+{
+    return ( j >= 0 ) ? j : -j;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <limits.h>
+
+int main( void )
+{
+    TESTCASE( abs( 0 ) == 0 );
+    TESTCASE( abs( INT_MAX ) == INT_MAX );
+    TESTCASE( abs( INT_MIN + 1 ) == -( INT_MIN + 1 ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/atexit.c b/src/pdclib/functions/stdlib/atexit.c
new file mode 100644 (file)
index 0000000..489c3df
--- /dev/null
@@ -0,0 +1,64 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+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;
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <assert.h>
+
+static int flags[ 32 ];
+
+static void counthandler( void )
+{
+    static int count = 0;
+    flags[ count ] = count;
+    ++count;
+}
+
+static void checkhandler( void )
+{
+    int i;
+    for ( i = 0; i < 31; ++i )
+    {
+        assert( flags[ i ] == i );
+    }
+}
+
+int main( void )
+{
+    int i;
+    TESTCASE( atexit( &checkhandler ) == 0 );
+    for ( i = 0; i < 31; ++i )
+    {
+        TESTCASE( atexit( &counthandler ) == 0 );
+    }
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/atoi.c b/src/pdclib/functions/stdlib/atoi.c
new file mode 100644 (file)
index 0000000..eaa9100
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+int atoi( const char * s )
+{
+    return (int) _PDCLIB_atomax( s );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* no tests for a simple wrapper */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/atol.c b/src/pdclib/functions/stdlib/atol.c
new file mode 100644 (file)
index 0000000..df17e83
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+long int atol( const char * s )
+{
+    return (long int) _PDCLIB_atomax( s );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* no tests for a simple wrapper */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/atoll.c b/src/pdclib/functions/stdlib/atoll.c
new file mode 100644 (file)
index 0000000..8b65b35
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+long long int atoll( const char * s )
+{
+    return (long long int) _PDCLIB_atomax( s );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* no tests for a simple wrapper */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/bsearch.c b/src/pdclib/functions/stdlib/bsearch.c
new file mode 100644 (file)
index 0000000..211c55c
--- /dev/null
@@ -0,0 +1,61 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+static int compare( const void * left, const void * right )
+{
+    return *( (unsigned char *)left ) - *( (unsigned char *)right );
+}
+
+int main( void )
+{
+    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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/calloc.c b/src/pdclib/functions/stdlib/calloc.c
new file mode 100644 (file)
index 0000000..d736065
--- /dev/null
@@ -0,0 +1,48 @@
+/* void * calloc( size_t, 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 <stdlib.h>
+#include <string.h>
+
+#ifndef REGTEST
+
+void * calloc( size_t nmemb, size_t size )
+{
+    /* assign memory for nmemb elements of given size */
+    void * rc = malloc( nmemb * size );
+    if ( rc != NULL )
+    {
+        /* zero-initialize the memory */
+        memset( rc, 0, nmemb * size );
+    }
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char * s;
+    TESTCASE( ( s = calloc( 3, 2 ) ) != NULL );
+    TESTCASE( s[0] == '\0' );
+    TESTCASE( s[5] == '\0' );
+    free( s );
+    TESTCASE( ( s = calloc( 6, 1 ) ) != NULL );
+    TESTCASE( s[0] == '\0' );
+    TESTCASE( s[5] == '\0' );
+    free( s );
+    TESTCASE( ( s = calloc( 1, 6 ) ) != NULL );
+    TESTCASE( s[0] == '\0' );
+    TESTCASE( s[5] == '\0' );
+    free( s );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/div.c b/src/pdclib/functions/stdlib/div.c
new file mode 100644 (file)
index 0000000..5d502c8
--- /dev/null
@@ -0,0 +1,40 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    div_t result;
+    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 ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/exit.c b/src/pdclib/functions/stdlib/exit.c
new file mode 100644 (file)
index 0000000..e636a32
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+/* 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 );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Unwinding of regstack tested in atexit(). */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/free.c b/src/pdclib/functions/stdlib/free.c
new file mode 100644 (file)
index 0000000..126a17b
--- /dev/null
@@ -0,0 +1,52 @@
+/* void free( void * )
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_int.h"
+
+/* TODO: Primitive placeholder. Much room for improvement. */
+
+/* structure holding first and last element of free node list */
+extern struct _PDCLIB_headnode_t _PDCLIB_memlist;
+
+void free( void * ptr )
+{
+    if ( ptr == NULL )
+    {
+        return;
+    }
+    ptr = (void *)( (char *)ptr - sizeof( struct _PDCLIB_memnode_t ) );
+    ( (struct _PDCLIB_memnode_t *)ptr )->next = NULL;
+    if ( _PDCLIB_memlist.last != NULL )
+    {
+        _PDCLIB_memlist.last->next = ptr;
+    }
+    else
+    {
+        _PDCLIB_memlist.first = ptr;
+    }
+    _PDCLIB_memlist.last = ptr;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdbool.h>
+
+int main( void )
+{
+    free( NULL );
+    TESTCASE( true );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/labs.c b/src/pdclib/functions/stdlib/labs.c
new file mode 100644 (file)
index 0000000..3653453
--- /dev/null
@@ -0,0 +1,32 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+long int labs( long int j )
+{
+    return ( j >= 0 ) ? j : -j;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <limits.h>
+
+int main( void )
+{
+    TESTCASE( labs( 0 ) == 0 );
+    TESTCASE( labs( LONG_MAX ) == LONG_MAX );
+    TESTCASE( labs( LONG_MIN + 1 ) == -( LONG_MIN + 1 ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/ldiv.c b/src/pdclib/functions/stdlib/ldiv.c
new file mode 100644 (file)
index 0000000..0f51944
--- /dev/null
@@ -0,0 +1,40 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    ldiv_t result;
+    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 ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/llabs.c b/src/pdclib/functions/stdlib/llabs.c
new file mode 100644 (file)
index 0000000..bc05bf3
--- /dev/null
@@ -0,0 +1,32 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+long long int llabs( long long int j )
+{
+    return ( j >= 0 ) ? j : -j;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <limits.h>
+
+int main( void )
+{
+    TESTCASE( llabs( 0ll ) == 0 );
+    TESTCASE( llabs( LLONG_MAX ) == LLONG_MAX );
+    TESTCASE( llabs( LLONG_MIN + 1 ) == -( LLONG_MIN + 1 ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/lldiv.c b/src/pdclib/functions/stdlib/lldiv.c
new file mode 100644 (file)
index 0000000..4219e31
--- /dev/null
@@ -0,0 +1,40 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    lldiv_t result;
+    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 ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/malloc.c b/src/pdclib/functions/stdlib/malloc.c
new file mode 100644 (file)
index 0000000..ed57e9a
--- /dev/null
@@ -0,0 +1,427 @@
+/* void * malloc( 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 <stdlib.h>
+#include <stdint.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+/* TODO: Primitive placeholder. Much room for improvement. */
+
+/* Keeping pointers to the first and the last element of the free list. */
+struct _PDCLIB_headnode_t _PDCLIB_memlist = { NULL, NULL };
+
+void * malloc( size_t size )
+{
+    if ( size == 0 )
+    {
+        return NULL;
+    }
+    if ( size < _PDCLIB_MINALLOC )
+    {
+        size = _PDCLIB_MINALLOC;
+    }
+    {
+    struct _PDCLIB_memnode_t * current = _PDCLIB_memlist.first;
+    struct _PDCLIB_memnode_t * previous = NULL;
+    struct _PDCLIB_memnode_t * firstfit = NULL;
+    struct _PDCLIB_memnode_t * firstfit_previous = NULL;
+    /* Trying exact fit */
+    while ( current != NULL )
+    {
+        if ( current->size == size )
+        {
+            /* Found exact fit, allocate node */
+            if ( previous != NULL )
+            {
+                /* Node in the middle of the list */
+                previous->next = current->next;
+            }
+            else
+            {
+                /* Node is first in list */
+                _PDCLIB_memlist.first = current->next;
+            }
+            if ( _PDCLIB_memlist.last == current )
+            {
+                /* Node is last in list */
+                _PDCLIB_memlist.last = previous;
+            }
+            return (char *)current + sizeof( struct _PDCLIB_memnode_t );
+        }
+        else if ( current->size > size && ( firstfit == NULL || current->size < firstfit->size ) )
+        {
+            /* Remember previous node in case we do not get an exact fit.
+               Note that this is the node *pointing to* the first fit,
+               as we need that for allocating (i.e., changing next pointer).
+            */
+            firstfit_previous = previous;
+            firstfit = current;
+        }
+        /* Skip to next node */
+        previous = current;
+        current = current->next;
+    }
+    /* No exact fit; go for first fit */
+    if ( firstfit != NULL )
+    {
+        int node_split = 0;
+        if ( ( firstfit->size - size ) > ( _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ) ) )
+        {
+            /* Oversized - split into two nodes */
+            struct _PDCLIB_memnode_t * newnode = (struct _PDCLIB_memnode_t *)( (char *)firstfit + sizeof( struct _PDCLIB_memnode_t ) + size );
+            newnode->size = firstfit->size - size - sizeof( struct _PDCLIB_memnode_t );
+            newnode->next = firstfit->next;
+            firstfit->next = newnode;
+            firstfit->size = firstfit->size - newnode->size - sizeof( struct _PDCLIB_memnode_t );
+            node_split = 1;
+        }
+        if ( firstfit_previous != NULL )
+        {
+            /* Node in the middle of the list */
+            firstfit_previous->next = firstfit->next;
+        }
+        else
+        {
+            /* Node is first in list */
+            _PDCLIB_memlist.first = firstfit->next;
+        }
+        if ( _PDCLIB_memlist.last == firstfit )
+        {
+            /* Node is last in list */
+            if ( node_split )
+            {
+                _PDCLIB_memlist.last = firstfit->next;
+            }
+            else
+            {
+                _PDCLIB_memlist.last = firstfit_previous;
+            }
+        }
+        return (char *)firstfit + sizeof( struct _PDCLIB_memnode_t );
+    }
+    }
+    {
+    /* No fit possible; how many additional pages do we need? */
+    size_t pages = ( ( size + sizeof( struct _PDCLIB_memnode_t ) - 1 ) / _PDCLIB_PAGESIZE ) + 1;
+    /* Allocate more pages */
+    struct _PDCLIB_memnode_t * newnode = (struct _PDCLIB_memnode_t *)_PDCLIB_allocpages( (int)pages );
+    if ( newnode != NULL )
+    {
+        newnode->next = NULL;
+        newnode->size = pages * _PDCLIB_PAGESIZE - sizeof( struct _PDCLIB_memnode_t );
+        if ( ( newnode->size - size ) > ( _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ) ) )
+        {
+            /* Oversized - split into two nodes */
+            struct _PDCLIB_memnode_t * splitnode = (struct _PDCLIB_memnode_t *)( (char *)newnode + sizeof( struct _PDCLIB_memnode_t ) + size );
+            splitnode->size = newnode->size - size - sizeof( struct _PDCLIB_memnode_t );
+            newnode->size = size;
+            /* Add splitted node as last element to free node list */
+            if ( _PDCLIB_memlist.last == NULL )
+            {
+                _PDCLIB_memlist.first = splitnode;
+            }
+            else
+            {
+                _PDCLIB_memlist.last->next = splitnode;
+            }
+            splitnode->next = NULL; /* TODO: This is bug #7, uncovered by testdriver yet. */
+            _PDCLIB_memlist.last = splitnode;
+        }
+        return (char *)newnode + sizeof( struct _PDCLIB_memnode_t );
+    }
+    }
+    /* No fit, heap extension not possible - out of memory */
+    return NULL;
+}
+
+#endif
+
+
+#ifdef TEST
+#include "_PDCLIB_test.h"
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+
+#ifndef REGTEST
+
+/* Effective page size, i.e. how many bytes can be allocated and still be on
+   one page of memory.
+*/
+#define EFFECTIVE _PDCLIB_PAGESIZE - sizeof( struct _PDCLIB_memnode_t )
+#define MEMTEST( ptr, size ) ( ( ptr = malloc( size ) ) != NULL ) && ( memset( ptr, 0, size ) == ptr )
+
+char * pages_start = 0;
+int test_nodes( const char * const, int, ... );
+void PRINT( const char * const, ... );
+
+/* This can be enabled to give a dump of node information */
+#if 0
+void PRINT( const char * const format, ... )
+{
+    va_list( ap );
+    va_start( ap, format );
+    vprintf( format, ap );
+}
+#else
+void PRINT( const char * const format, ... )
+{
+    /* EMPTY */
+}
+#endif
+
+/* Helper function checking number of allocated memory pages and the nodes
+   in the free memory list against expectations.
+*/
+int test_nodes( const char * const action, int expected_pages, ... )
+{
+    static int count = 1;
+    int result = 1;
+    /* Determining the amount of allocated pages */
+    int allocated_pages = ( (intptr_t)_PDCLIB_allocpages( 0 ) - (intptr_t)pages_start ) / _PDCLIB_PAGESIZE;
+    PRINT( action );
+    PRINT( "Test #%2d, %d allocated pages", count++, allocated_pages );
+    if ( allocated_pages != expected_pages )
+    {
+        PRINT( " - MISMATCH, expected\n          %d pages\n", expected_pages );
+        result = 0;
+    }
+    else
+    {
+        PRINT( "\n" );
+    }
+    {
+    /* Now moving through the free nodes list */
+    va_list( ap );
+    struct _PDCLIB_memnode_t * tracer = _PDCLIB_memlist.first;
+    int firstnode = 0;
+    int lastnode = 0;
+    va_start( ap, expected_pages );
+    while ( tracer != NULL )
+    {
+        /* Expected data */
+        size_t expected_location = va_arg( ap, size_t );
+        /* Data from node */
+        size_t node_location = (char *)tracer - (char *)pages_start;
+        PRINT( "   - node %.4p, size %#.4x", node_location, tracer->size );
+        if ( expected_location == 0 )
+        {
+            PRINT( " - UNEXPECTED NODE\n" );
+            result = 0;
+            continue;
+        }
+        /* Memorizing first and last expected node for later comparison. */
+        if ( firstnode == 0 )
+        {
+            firstnode = expected_location;
+        }
+        lastnode = expected_location;
+        {
+        /* Comparing expected node against current node */
+        size_t expected_size = va_arg( ap, size_t );
+        if ( ( node_location != expected_location ) || ( tracer->size != expected_size ) )
+        {
+            PRINT( " - MISMATCH, expected values\n          %.4p       %#.4p\n", expected_location, expected_size );
+            result = 0;
+        }
+        else
+        {
+            PRINT( "\n" );
+        }
+        }
+        tracer = tracer->next;
+    }
+    /* Comparing first and last node in memlist against expectations. */
+    PRINT( "   - memlist first: %#.4x - last: %#.4x",
+            ( _PDCLIB_memlist.first == NULL ) ? NULL : (char *)_PDCLIB_memlist.first - (char *)pages_start,
+            ( _PDCLIB_memlist.last == NULL ) ? NULL : (char *)_PDCLIB_memlist.last - (char *)pages_start );
+    if ( ( firstnode != 0 ) &&
+         ( ( ( (char *)_PDCLIB_memlist.first - (char *)pages_start ) != firstnode )
+         || ( ( (char *)_PDCLIB_memlist.last  - (char *)pages_start ) != lastnode ) ) )
+    {
+        PRINT( " - MISMATCH, expected values\n                    %#.4x - last: %#.4x\n", firstnode, lastnode );
+        result = 0;
+    }
+    else
+    {
+        PRINT( "\n" );
+    }
+    }
+    PRINT( "\n" );
+    return result;
+}
+
+#endif
+
+/* Note that this test driver heavily tests *internals* of the implementation
+   above (and of free() and realloc(), too). That means that changes in the
+   implementation must be accompanied with appropriate changes of the test
+   driver. It does *not* make a good regression tester for the implementation,
+   I am afraid, and thus there is no REGTEST equivalent.
+*/
+
+int main( void )
+{
+#ifndef REGTEST
+    void * ptr1, * ptr2, * ptr3, * ptr4, * ptr5, * ptr6, * ptr7, * ptr8, * ptr9, * ptrA, * ptrB, * ptrC;
+
+    pages_start = _PDCLIB_allocpages( 0 );
+    PRINT( "\nEffective is: %#.4x\nsizeof( memnode ) is: %#.2x\n\n", EFFECTIVE, sizeof( struct _PDCLIB_memnode_t ) );
+
+    /* Allocating 10 bytes; expecting one page allocation and a node split */
+    TESTCASE( MEMTEST( ptr1, 10 ) );
+    TESTCASE( test_nodes( "Allocating 10 bytes.", 1,
+               sizeof( struct _PDCLIB_memnode_t ) + 10, EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - 10,
+               0 ) );
+
+    /* Allocating the rest of the page; expecting no page allocation and assignment of the remaining node */
+    TESTCASE( MEMTEST( ptr2, EFFECTIVE - 10 - sizeof( struct _PDCLIB_memnode_t ) ) );
+    TESTCASE( test_nodes( "Allocating the rest of the page.", 1,
+               0 ) );
+
+    /* Allocating a full page; expecting one page allocation, no node split */
+    TESTCASE( MEMTEST( ptr3, EFFECTIVE ) );
+    TESTCASE( test_nodes( "Allocating a full page.", 2,
+               0 ) );
+
+    /* Allocating *almost* a full page; expecting one page allocation, no node split */
+    TESTCASE( MEMTEST( ptr4, EFFECTIVE - 4 ) );
+    TESTCASE( test_nodes( "Allocating *almost* a full page.", 3,
+               0 ) );
+
+    /* Freeing and re-allocating the "almost" full page; expecting no page allocation, no node split */
+    free( ptr4 );
+    TESTCASE( MEMTEST( ptr5, EFFECTIVE - 4 ) );
+    TESTCASE( ptr4 == ptr5 );
+    TESTCASE( test_nodes( "Freeing and re-allocating the \"almost\" full page.", 3 ) );
+
+    /* Freeing the full page from test #3; expecting a full-sized free node. */
+    free( ptr3 );
+    TESTCASE( test_nodes( "Freeing the full page from test #3.", 3,
+               _PDCLIB_PAGESIZE * 1, EFFECTIVE,
+               0 ) );
+
+    /* Allocating two full pages; expecting two page allocations, no node split */
+    TESTCASE( MEMTEST( ptr3, EFFECTIVE + _PDCLIB_PAGESIZE ) );
+    TESTCASE( test_nodes( "Allocating two full pages.", 5,
+               _PDCLIB_PAGESIZE * 1, EFFECTIVE,
+               0 ) );
+
+    /* Re-allocating to size of 10 bytes; expecting no page allocation, no node split */
+    /* TODO: Shouldn't realloc() split the now much-too-large node? */
+    TESTCASE( realloc( ptr3, 10 ) == ptr3 );
+    TESTCASE( test_nodes( "Re-allocating to size of 10 bytes.", 5,
+               _PDCLIB_PAGESIZE * 1, EFFECTIVE,
+               0 ) );
+
+    /* Re-allocating to size of two full pages; expecting no page allocation, no node split */
+    TESTCASE( realloc( ptr3, EFFECTIVE + _PDCLIB_PAGESIZE ) == ptr3 );
+    TESTCASE( test_nodes( "Re-allocating to size of two full pages.", 5,
+               _PDCLIB_PAGESIZE * 1, EFFECTIVE,
+               0 ) );
+
+    /* Re-allocating to size of three full pages; expecting three page allocation, freeing of two-page node */
+    TESTCASE( realloc( ptr3, EFFECTIVE + _PDCLIB_PAGESIZE * 2 ) != ptr3 );
+    TESTCASE( test_nodes( "Re-allocating to size of three full pages.", 8,
+               _PDCLIB_PAGESIZE * 1, EFFECTIVE,
+               _PDCLIB_PAGESIZE * 3, EFFECTIVE + _PDCLIB_PAGESIZE,
+               0 ) );
+
+    /* Allocating two full pages; expecting allocation of the available two-page node */
+    TESTCASE( MEMTEST( ptr4, EFFECTIVE + _PDCLIB_PAGESIZE ) );
+    TESTCASE( test_nodes( "Allocating two full pages.", 8,
+               _PDCLIB_PAGESIZE * 1, EFFECTIVE,
+               0 ) );
+
+    /* Allocating zero bytes; expecting no change */
+    TESTCASE( ! MEMTEST( ptr6, 0 ) );
+    TESTCASE( test_nodes( "Allocating zero bytes.", 8,
+               _PDCLIB_PAGESIZE * 1, EFFECTIVE,
+               0 ) );
+
+    /* Allocating 4 bytes; expecting upsizing of requestupsizing of size, node split */
+    TESTCASE( MEMTEST( ptr7, 4 ) );
+    TESTCASE( test_nodes( "Allocating 4 bytes.", 8,
+               _PDCLIB_PAGESIZE * 1 + _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ),
+               EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ),
+               0 ) );
+
+    /* Allocating the rest of the page; expecting no page allocation and assignment of the remaining node */
+    TESTCASE( MEMTEST( ptr8, EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) ) );
+    TESTCASE( test_nodes( "Allocating the rest of the page.", 8, 0 ) );
+
+    /* Freeing the node from the previous test; expecting node to re-appear in free list */
+    free( ptr8 );
+    TESTCASE( test_nodes( "Freeing the node from the previous test.", 8,
+               _PDCLIB_PAGESIZE * 1 + _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ),
+               EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ),
+               0 ) );
+
+    /* Allocating one byte more than available in free node; expecting page allocation */
+    TESTCASE( MEMTEST( ptr8, EFFECTIVE + 1 - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) ) );
+    TESTCASE( test_nodes( "Allocating one byte more than available in free node.", 9,
+               _PDCLIB_PAGESIZE * 1 + _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ),
+               EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ),
+               0 ) );
+
+    /* Re-allocating with NULL pointer; expecting no page allocation, no node split */
+    ptr9 = realloc( NULL, EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) );
+    TESTCASE( ptr9 != NULL );
+    TESTCASE( memset( ptr9, 0, EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) ) == ptr9 );
+    TESTCASE( test_nodes( "Re-allocating with NULL pointer.", 9, 0 ) );
+
+    /* Allocating a bit more than half a page; expecting page allocation, node split */
+#define TESTSIZE 3000
+    TESTCASE( MEMTEST( ptrA, TESTSIZE ) );
+    TESTCASE( test_nodes( "Allocating a bit more than half a page.", 10,
+               _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
+               EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
+               0 ) );
+
+    /* Allocating a bit more than half a page; expecting page allocation, node split */
+    TESTCASE( MEMTEST( ptrB, TESTSIZE ) );
+    TESTCASE( test_nodes( "Allocating a bit more than half a page.", 11,
+               _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
+               EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
+               _PDCLIB_PAGESIZE * 10 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
+               EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
+               0 ) );
+
+    /* Allocating a bit more than half a page; expecting page allocation, node split */
+    TESTCASE( MEMTEST( ptrC, TESTSIZE ) );
+    TESTCASE( test_nodes( "Allocating a bit more than half a page.", 12,
+               _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
+               EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
+               _PDCLIB_PAGESIZE * 10 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
+               EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
+               _PDCLIB_PAGESIZE * 11 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
+               EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
+               0 ) );
+
+    /* Freeing the middle node */
+    free( ptrB );
+    TESTCASE( test_nodes( "Freeing the middle node.", 12,
+               _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
+               EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
+               _PDCLIB_PAGESIZE * 10 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
+               EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
+               _PDCLIB_PAGESIZE * 11 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
+               EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
+               _PDCLIB_PAGESIZE * 10,
+               TESTSIZE,
+               0 ) );
+
+#else
+    puts( " NOTEST malloc() test driver is PDCLib-specific." );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/qsort.c b/src/pdclib/functions/stdlib/qsort.c
new file mode 100644 (file)
index 0000000..d28f74c
--- /dev/null
@@ -0,0 +1,166 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+/* 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 _PDCLIB_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 _PDCLIB_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;
+    _PDCLIB_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;
+            }
+        }
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <string.h>
+#include <limits.h>
+
+static int compare( const void * left, const void * right )
+{
+    return *( (unsigned char *)left ) - *( (unsigned char *)right );
+}
+
+int main( void )
+{
+    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 );
+#if defined( REGTEST ) && ( defined( __BSD_VISIBLE ) || defined( __APPLE__ ) )
+    puts( "qsort.c: Skipping test #4 for BSD as it goes into endless loop here." );
+#else
+    qsort( s, 100, 0, compare );
+    TESTCASE( strcmp( s, presort ) == 0 );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/rand.c b/src/pdclib/functions/stdlib/rand.c
new file mode 100644 (file)
index 0000000..779b3b2
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+int rand( void )
+{
+    _PDCLIB_seed = _PDCLIB_seed * 1103515245 + 12345;
+    return (int)( _PDCLIB_seed / 65536 ) % 32768;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    int rnd1, rnd2;
+    TESTCASE( ( rnd1 = rand() ) < RAND_MAX );
+    TESTCASE( ( rnd2 = rand() ) < RAND_MAX );
+    srand( 1 );
+    TESTCASE( rand() == rnd1 );
+    TESTCASE( rand() == rnd2 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/realloc.c b/src/pdclib/functions/stdlib/realloc.c
new file mode 100644 (file)
index 0000000..cbc01d4
--- /dev/null
@@ -0,0 +1,56 @@
+/* void * realloc( 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 <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+
+#ifndef REGTEST
+
+/* TODO: Primitive placeholder. Improve. */
+
+void * realloc( void * ptr, size_t size )
+{
+    void * newptr = NULL;
+    if ( ptr == NULL )
+    {
+        return malloc( size );
+    }
+    if ( size > 0 )
+    {
+        struct _PDCLIB_memnode_t * baseptr = (struct _PDCLIB_memnode_t *)( (char *)ptr - sizeof( struct _PDCLIB_memnode_t ) );
+        if ( baseptr->size >= size )
+        {
+            /* Current memnode is large enough; nothing to do. */
+            return ptr;
+        }
+        else
+        {
+            /* Get larger memnode and copy over contents. */
+            if ( ( newptr = malloc( size ) ) == NULL )
+            {
+                return NULL;
+            }
+            memcpy( newptr, ptr, baseptr->size );
+        }
+    }
+    free( ptr );
+    return newptr;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* tests covered in malloc test driver */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/srand.c b/src/pdclib/functions/stdlib/srand.c
new file mode 100644 (file)
index 0000000..39c37ec
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <stdlib.h>
+
+#ifndef REGTEST
+
+void srand( unsigned int seed )
+{
+    _PDCLIB_seed = seed;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* tested in rand.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/strtol.c b/src/pdclib/functions/stdlib/strtol.c
new file mode 100644 (file)
index 0000000..d89a648
--- /dev/null
@@ -0,0 +1,129 @@
+/* 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 <limits.h>
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+#include <stdint.h>
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <errno.h>
+
+int main( void )
+{
+    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( 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 );
+    TESTCASE( endptr == tricky + 2 );
+    /* proper handling of border case: 0 followed by non-octdigit */
+    TESTCASE( strtol( tricky, &endptr, 8 ) == 0 );
+    TESTCASE( endptr == tricky + 2 );
+    /* errno should still be 0 */
+    TESTCASE( errno == 0 );
+    /* overflowing subject sequence must still return proper endptr */
+    TESTCASE( strtol( overflow, &endptr, 36 ) == LONG_MIN );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( endptr - overflow ) == 53 );
+    /* same for positive */
+    errno = 0;
+    TESTCASE( strtol( overflow + 1, &endptr, 36 ) == LONG_MAX );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( 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 );
+    TESTCASE( endptr == overflow );
+    endptr = NULL;
+    TESTCASE( strtol( overflow, &endptr, 0 ) == 0 );
+    TESTCASE( 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?                                         */
+    errno = 0;
+#if LONG_MAX >> 30 == 1
+    /* testing "even" overflow, i.e. base is power of two */
+    TESTCASE( strtol( "2147483647", NULL, 0 ) == 0x7fffffff );
+    TESTCASE( errno == 0 );
+    errno = 0;
+    TESTCASE( strtol( "2147483648", NULL, 0 ) == LONG_MAX );
+    TESTCASE( errno == ERANGE );
+    errno = 0;
+    TESTCASE( strtol( "-2147483647", NULL, 0 ) == (long)0x80000001 );
+    TESTCASE( errno == 0 );
+    errno = 0;
+    TESTCASE( strtol( "-2147483648", NULL, 0 ) == LONG_MIN );
+    TESTCASE( errno == 0 );
+    errno = 0;
+    TESTCASE( strtol( "-2147483649", NULL, 0 ) == LONG_MIN );
+    TESTCASE( errno == ERANGE );
+    /* TODO: test "odd" overflow, i.e. base is not power of two */
+#elif LONG_MAX >> 62 == 1
+    /* testing "even" overflow, i.e. base is power of two */
+    TESTCASE( strtol( "9223372036854775807", NULL, 0 ) == 0x7fffffffffffffff );
+    TESTCASE( errno == 0 );
+    errno = 0;
+    TESTCASE( strtol( "9223372036854775808", NULL, 0 ) == LONG_MAX );
+    TESTCASE( errno == ERANGE );
+    errno = 0;
+    TESTCASE( strtol( "-9223372036854775807", NULL, 0 ) == (long)0x8000000000000001 );
+    TESTCASE( errno == 0 );
+    errno = 0;
+    TESTCASE( strtol( "-9223372036854775808", NULL, 0 ) == LONG_MIN );
+    TESTCASE( errno == 0 );
+    errno = 0;
+    TESTCASE( strtol( "-9223372036854775809", NULL, 0 ) == LONG_MIN );
+    TESTCASE( 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
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/strtoll.c b/src/pdclib/functions/stdlib/strtoll.c
new file mode 100644 (file)
index 0000000..24c3e8e
--- /dev/null
@@ -0,0 +1,123 @@
+/* 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 <limits.h>
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+#include <stdint.h>
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <errno.h>
+
+int main( void )
+{
+    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( 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 );
+    TESTCASE( endptr == tricky + 2 );
+    /* proper handling of border case: 0 followed by non-octdigit */
+    TESTCASE( strtoll( tricky, &endptr, 8 ) == 0 );
+    TESTCASE( endptr == tricky + 2 );
+    /* errno should still be 0 */
+    TESTCASE( errno == 0 );
+    /* overflowing subject sequence must still return proper endptr */
+    TESTCASE( strtoll( overflow, &endptr, 36 ) == LLONG_MIN );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( endptr - overflow ) == 53 );
+    /* same for positive */
+    errno = 0;
+    TESTCASE( strtoll( overflow + 1, &endptr, 36 ) == LLONG_MAX );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( 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 );
+    TESTCASE( endptr == overflow );
+    endptr = NULL;
+    TESTCASE( strtoll( overflow, &endptr, 0 ) == 0 );
+    TESTCASE( 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?                                         */
+    errno = 0;
+#if LLONG_MAX >> 62 == 1
+    /* testing "even" overflow, i.e. base is power of two */
+    TESTCASE( strtoll( "9223372036854775807", NULL, 0 ) == 0x7fffffffffffffff );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoll( "9223372036854775808", NULL, 0 ) == LLONG_MAX );
+    TESTCASE( errno == ERANGE );
+    errno = 0;
+    TESTCASE( strtoll( "-9223372036854775807", NULL, 0 ) == (long long)0x8000000000000001 );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoll( "-9223372036854775808", NULL, 0 ) == LLONG_MIN );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoll( "-9223372036854775809", NULL, 0 ) == LLONG_MIN );
+    TESTCASE( errno == ERANGE );
+    /* TODO: test "odd" overflow, i.e. base is not power of two */
+#elif LLONG_MAX >> 126 == 1
+    /* testing "even" overflow, i.e. base is power of two */
+    TESTCASE( strtoll( "170141183460469231731687303715884105728", NULL, 0 ) == 0x7fffffffffffffffffffffffffffffff );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoll( "170141183460469231731687303715884105729", NULL, 0 ) == LLONG_MAX );
+    TESTCASE( errno == ERANGE );
+    errno = 0;
+    TESTCASE( strtoll( "-170141183460469231731687303715884105728", NULL, 0 ) == -0x80000000000000000000000000000001 );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoll( "-170141183460469231731687303715884105729", NULL, 0 ) == LLONG_MIN );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoll( "-170141183460469231731687303715884105730", NULL, 0 ) == LLONG_MIN );
+    TESTCASE( 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
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/strtoul.c b/src/pdclib/functions/stdlib/strtoul.c
new file mode 100644 (file)
index 0000000..1a93477
--- /dev/null
@@ -0,0 +1,106 @@
+/* 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 <limits.h>
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+#include <stdint.h>
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <errno.h>
+
+int main( void )
+{
+    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( 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 );
+    TESTCASE( endptr == tricky + 2 );
+    /* proper handling of border case: 0 followed by non-octdigit */
+    TESTCASE( strtoul( tricky, &endptr, 8 ) == 0 );
+    TESTCASE( endptr == tricky + 2 );
+    /* errno should still be 0 */
+    TESTCASE( errno == 0 );
+    /* overflowing subject sequence must still return proper endptr */
+    TESTCASE( strtoul( overflow, &endptr, 36 ) == ULONG_MAX );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( endptr - overflow ) == 53 );
+    /* same for positive */
+    errno = 0;
+    TESTCASE( strtoul( overflow + 1, &endptr, 36 ) == ULONG_MAX );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( 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 );
+    TESTCASE( endptr == overflow );
+    endptr = NULL;
+    TESTCASE( strtoul( overflow, &endptr, 0 ) == 0 );
+    TESTCASE( 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?                                         */
+    errno = 0;
+/* long -> 32 bit */
+#if ULONG_MAX >> 31 == 1
+    /* testing "even" overflow, i.e. base is power of two */
+    TESTCASE( strtoul( "4294967295", NULL, 0 ) == ULONG_MAX );
+    TESTCASE( errno == 0 );
+    errno = 0;
+    TESTCASE( strtoul( "4294967296", NULL, 0 ) == ULONG_MAX );
+    TESTCASE( errno == ERANGE );
+    /* TODO: test "odd" overflow, i.e. base is not power of two */
+/* long -> 64 bit */
+#elif ULONG_MAX >> 63 == 1
+    /* testing "even" overflow, i.e. base is power of two */
+    TESTCASE( strtoul( "18446744073709551615", NULL, 0 ) == ULONG_MAX );
+    TESTCASE( errno == 0 );
+    errno = 0;
+    TESTCASE( strtoul( "18446744073709551616", NULL, 0 ) == ULONG_MAX );
+    TESTCASE( 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
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/stdlib/strtoull.c b/src/pdclib/functions/stdlib/strtoull.c
new file mode 100644 (file)
index 0000000..e207110
--- /dev/null
@@ -0,0 +1,101 @@
+/* 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 <limits.h>
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+#include <stdint.h>
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <errno.h>
+
+int main( void )
+{
+    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( 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 );
+    TESTCASE( endptr == tricky + 2 );
+    /* proper handling of border case: 0 followed by non-octdigit */
+    TESTCASE( strtoull( tricky, &endptr, 8 ) == 0 );
+    TESTCASE( endptr == tricky + 2 );
+    /* errno should still be 0 */
+    TESTCASE( errno == 0 );
+    /* overflowing subject sequence must still return proper endptr */
+    TESTCASE( strtoull( overflow, &endptr, 36 ) == ULLONG_MAX );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( endptr - overflow ) == 53 );
+    /* same for positive */
+    errno = 0;
+    TESTCASE( strtoull( overflow + 1, &endptr, 36 ) == ULLONG_MAX );
+    TESTCASE( errno == ERANGE );
+    TESTCASE( ( 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 );
+    TESTCASE( endptr == overflow );
+    endptr = NULL;
+    TESTCASE( strtoull( overflow, &endptr, 0 ) == 0 );
+    TESTCASE( endptr == overflow );
+    errno = 0;
+/* long long -> 64 bit */
+#if ULLONG_MAX >> 63 == 1
+    /* testing "even" overflow, i.e. base is power of two */
+    TESTCASE( strtoull( "18446744073709551615", NULL, 0 ) == ULLONG_MAX );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoull( "18446744073709551616", NULL, 0 ) == ULLONG_MAX );
+    TESTCASE( errno == ERANGE );
+    /* TODO: test "odd" overflow, i.e. base is not power of two */
+/* long long -> 128 bit */
+#elif ULLONG_MAX >> 127 == 1
+    /* testing "even" overflow, i.e. base is power of two */
+    TESTCASE( strtoull( "340282366920938463463374607431768211455", NULL, 0 ) == ULLONG_MAX );
+    TESTCASE( errno == 0 );
+    TESTCASE( strtoull( "340282366920938463463374607431768211456", NULL, 0 ) == ULLONG_MAX );
+    TESTCASE( 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
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/memchr.c b/src/pdclib/functions/string/memchr.c
new file mode 100644 (file)
index 0000000..29598a7
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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] );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/memcmp.c b/src/pdclib/functions/string/memcmp.c
new file mode 100644 (file)
index 0000000..709b941
--- /dev/null
@@ -0,0 +1,43 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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 );
+    return 0;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/memcpy.c b/src/pdclib/functions/string/memcpy.c
new file mode 100644 (file)
index 0000000..21ef10e
--- /dev/null
@@ -0,0 +1,40 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+void * memcpy( void * _PDCLIB_restrict s1, const void * _PDCLIB_restrict s2, size_t n )
+{
+    char * dest = (char *) s1;
+    const char * src = (const char *) s2;
+    while ( n-- )
+    {
+        *dest++ = *src++;
+    }
+    return s1;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char s[] = "xxxxxxxxxxx";
+    TESTCASE( memcpy( s, abcde, 6 ) == s );
+    TESTCASE( s[4] == 'e' );
+    TESTCASE( s[5] == '\0' );
+    TESTCASE( memcpy( s + 5, abcde, 5 ) == s + 5 );
+    TESTCASE( s[9] == 'e' );
+    TESTCASE( s[10] == 'x' );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/memmove.c b/src/pdclib/functions/string/memmove.c
new file mode 100644 (file)
index 0000000..af89813
--- /dev/null
@@ -0,0 +1,52 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char s[] = "xxxxabcde";
+    TESTCASE( memmove( s, s + 4, 5 ) == s );
+    TESTCASE( s[0] == 'a' );
+    TESTCASE( s[4] == 'e' );
+    TESTCASE( s[5] == 'b' );
+    TESTCASE( memmove( s + 4, s, 5 ) == s + 4 );
+    TESTCASE( s[4] == 'a' );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/memset.c b/src/pdclib/functions/string/memset.c
new file mode 100644 (file)
index 0000000..522ad77
--- /dev/null
@@ -0,0 +1,40 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+void * memset( void * s, int c, size_t n )
+{
+    unsigned char * p = (unsigned char *) s;
+    while ( n-- )
+    {
+        *p++ = (unsigned char) c;
+    }
+    return s;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char s[] = "xxxxxxxxx";
+    TESTCASE( memset( s, 'o', 10 ) == s );
+    TESTCASE( s[9] == 'o' );
+    TESTCASE( memset( s, '_', 0 ) == s );
+    TESTCASE( s[0] == 'o' );
+    TESTCASE( memset( s, '_', 1 ) == s );
+    TESTCASE( s[0] == '_' );
+    TESTCASE( s[1] == 'o' );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strcat.c b/src/pdclib/functions/string/strcat.c
new file mode 100644 (file)
index 0000000..18fd409
--- /dev/null
@@ -0,0 +1,46 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+char * strcat( char * _PDCLIB_restrict s1, const char * _PDCLIB_restrict s2 )
+{
+    char * rc = s1;
+    if ( *s1 )
+    {
+        while ( *++s1 );
+    }
+    while ( (*s1++ = *s2++) );
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char s[] = "xx\0xxxxxx";
+    TESTCASE( strcat( s, abcde ) == s );
+    TESTCASE( s[2] == 'a' );
+    TESTCASE( s[6] == 'e' );
+    TESTCASE( s[7] == '\0' );
+    TESTCASE( s[8] == 'x' );
+    s[0] = '\0';
+    TESTCASE( strcat( s, abcdx ) == s );
+    TESTCASE( s[4] == 'x' );
+    TESTCASE( s[5] == '\0' );
+    TESTCASE( strcat( s, "\0" ) == s );
+    TESTCASE( s[5] == '\0' );
+    TESTCASE( s[6] == 'e' );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strchr.c b/src/pdclib/functions/string/strchr.c
new file mode 100644 (file)
index 0000000..621100e
--- /dev/null
@@ -0,0 +1,40 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+char * strchr( const char * s, int c )
+{
+    do
+    {
+        if ( *s == (char) c )
+        {
+            return (char *) s;
+        }
+    } while ( *s++ );
+    return NULL;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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] );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strcmp.c b/src/pdclib/functions/string/strcmp.c
new file mode 100644 (file)
index 0000000..639fc10
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+int strcmp( const char * s1, const char * s2 )
+{
+    while ( ( *s1 ) && ( *s1 == *s2 ) )
+    {
+        ++s1;
+        ++s2;
+    }
+    return ( *(unsigned char *)s1 - *(unsigned char *)s2 );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strcoll.c b/src/pdclib/functions/string/strcoll.c
new file mode 100644 (file)
index 0000000..41d466a
--- /dev/null
@@ -0,0 +1,46 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+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 );
+    */
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char cmpabcde[] = "abcde";
+    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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strcpy.c b/src/pdclib/functions/string/strcpy.c
new file mode 100644 (file)
index 0000000..e0357c8
--- /dev/null
@@ -0,0 +1,37 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+char * strcpy( char * _PDCLIB_restrict s1, const char * _PDCLIB_restrict s2 )
+{
+    char * rc = s1;
+    while ( ( *s1++ = *s2++ ) );
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char s[] = "xxxxx";
+    TESTCASE( strcpy( s, "" ) == s );
+    TESTCASE( s[0] == '\0' );
+    TESTCASE( s[1] == 'x' );
+    TESTCASE( strcpy( s, abcde ) == s );
+    TESTCASE( s[0] == 'a' );
+    TESTCASE( s[4] == 'e' );
+    TESTCASE( s[5] == '\0' );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strcspn.c b/src/pdclib/functions/string/strcspn.c
new file mode 100644 (file)
index 0000000..84f8af1
--- /dev/null
@@ -0,0 +1,50 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strerror.c b/src/pdclib/functions/string/strerror.c
new file mode 100644 (file)
index 0000000..4506376
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+/* 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];
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+int main( void )
+{
+    TESTCASE( strerror( ERANGE ) != strerror( EDOM ) );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strlen.c b/src/pdclib/functions/string/strlen.c
new file mode 100644 (file)
index 0000000..c1a620e
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+size_t strlen( const char * s )
+{
+    size_t rc = 0;
+    while ( s[rc] )
+    {
+        ++rc;
+    }
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( strlen( abcde ) == 5 );
+    TESTCASE( strlen( "" ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strncat.c b/src/pdclib/functions/string/strncat.c
new file mode 100644 (file)
index 0000000..ba20edc
--- /dev/null
@@ -0,0 +1,60 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+char * strncat( char * _PDCLIB_restrict s1, const char * _PDCLIB_restrict s2, size_t n )
+{
+    char * rc = s1;
+    while ( *s1 )
+    {
+        ++s1;
+    }
+    while ( n && ( *s1++ = *s2++ ) )
+    {
+        --n;
+    }
+    if ( n == 0 )
+    {
+        *s1 = '\0';
+    }
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char s[] = "xx\0xxxxxx";
+    TESTCASE( strncat( s, abcde, 10 ) == s );
+    TESTCASE( s[2] == 'a' );
+    TESTCASE( s[6] == 'e' );
+    TESTCASE( s[7] == '\0' );
+    TESTCASE( s[8] == 'x' );
+    s[0] = '\0';
+    TESTCASE( strncat( s, abcdx, 10 ) == s );
+    TESTCASE( s[4] == 'x' );
+    TESTCASE( s[5] == '\0' );
+    TESTCASE( strncat( s, "\0", 10 ) == s );
+    TESTCASE( s[5] == '\0' );
+    TESTCASE( s[6] == 'e' );
+    TESTCASE( strncat( s, abcde, 0 ) == s );
+    TESTCASE( s[5] == '\0' );
+    TESTCASE( s[6] == 'e' );
+    TESTCASE( strncat( s, abcde, 3 ) == s );
+    TESTCASE( s[5] == 'a' );
+    TESTCASE( s[7] == 'c' );
+    TESTCASE( s[8] == '\0' );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strncmp.c b/src/pdclib/functions/string/strncmp.c
new file mode 100644 (file)
index 0000000..4bb3592
--- /dev/null
@@ -0,0 +1,54 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+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 );
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strncpy.c b/src/pdclib/functions/string/strncpy.c
new file mode 100644 (file)
index 0000000..0627c41
--- /dev/null
@@ -0,0 +1,56 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+char * strncpy( char * _PDCLIB_restrict s1, const char * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char s[] = "xxxxxxx";
+    TESTCASE( strncpy( s, "", 1 ) == s );
+    TESTCASE( s[0] == '\0' );
+    TESTCASE( s[1] == 'x' );
+    TESTCASE( strncpy( s, abcde, 6 ) == s );
+    TESTCASE( s[0] == 'a' );
+    TESTCASE( s[4] == 'e' );
+    TESTCASE( s[5] == '\0' );
+    TESTCASE( s[6] == 'x' );
+    TESTCASE( strncpy( s, abcde, 7 ) == s );
+    TESTCASE( s[6] == '\0' );
+    TESTCASE( strncpy( s, "xxxx", 3 ) == s );
+    TESTCASE( s[0] == 'x' );
+    TESTCASE( s[2] == 'x' );
+    TESTCASE( s[3] == 'd' );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strpbrk.c b/src/pdclib/functions/string/strpbrk.c
new file mode 100644 (file)
index 0000000..e95f0c1
--- /dev/null
@@ -0,0 +1,49 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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] );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strrchr.c b/src/pdclib/functions/string/strrchr.c
new file mode 100644 (file)
index 0000000..c2369fa
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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] );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strspn.c b/src/pdclib/functions/string/strspn.c
new file mode 100644 (file)
index 0000000..7b55b08
--- /dev/null
@@ -0,0 +1,49 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( strspn( abcde, "abc" ) == 3 );
+    TESTCASE( strspn( abcde, "b" ) == 0 );
+    TESTCASE( strspn( abcde, abcde ) == 5 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strstr.c b/src/pdclib/functions/string/strstr.c
new file mode 100644 (file)
index 0000000..aee282f
--- /dev/null
@@ -0,0 +1,51 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    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] );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strtok.c b/src/pdclib/functions/string/strtok.c
new file mode 100644 (file)
index 0000000..69c2d68
--- /dev/null
@@ -0,0 +1,107 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+char * strtok( char * _PDCLIB_restrict s1, const char * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char s[] = "_a_bc__d_";
+    TESTCASE( strtok( s, "_" ) == &s[1] );
+    TESTCASE( s[1] == 'a' );
+    TESTCASE( s[2] == '\0' );
+    TESTCASE( strtok( NULL, "_" ) == &s[3] );
+    TESTCASE( s[3] == 'b' );
+    TESTCASE( s[4] == 'c' );
+    TESTCASE( s[5] == '\0' );
+    TESTCASE( strtok( NULL, "_" ) == &s[7] );
+    TESTCASE( s[6] == '_' );
+    TESTCASE( s[7] == 'd' );
+    TESTCASE( s[8] == '\0' );
+    TESTCASE( strtok( NULL, "_" ) == NULL );
+    strcpy( s, "ab_cd" );
+    TESTCASE( strtok( s, "_" ) == &s[0] );
+    TESTCASE( s[0] == 'a' );
+    TESTCASE( s[1] == 'b' );
+    TESTCASE( s[2] == '\0' );
+    TESTCASE( strtok( NULL, "_" ) == &s[3] );
+    TESTCASE( s[3] == 'c' );
+    TESTCASE( s[4] == 'd' );
+    TESTCASE( s[5] == '\0' );
+    TESTCASE( strtok( NULL, "_" ) == NULL );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/string/strxfrm.c b/src/pdclib/functions/string/strxfrm.c
new file mode 100644 (file)
index 0000000..e08dba2
--- /dev/null
@@ -0,0 +1,50 @@
+/* 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 <string.h>
+
+#ifndef REGTEST
+
+#include <locale.h>
+
+size_t strxfrm( char * _PDCLIB_restrict s1, const char * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    char s[] = "xxxxxxxxxxx";
+    TESTCASE( strxfrm( NULL, "123456789012", 0 ) == 12 );
+    TESTCASE( strxfrm( s, "123456789012", 12 ) == 12 );
+    /*
+    The following test case is true in *this* implementation, but doesn't have to.
+    TESTCASE( s[0] == 'x' );
+    */
+    TESTCASE( strxfrm( s, "1234567890", 11 ) == 10 );
+    TESTCASE( s[0] == '1' );
+    TESTCASE( s[9] == '0' );
+    TESTCASE( s[10] == '\0' );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/time/asctime.c b/src/pdclib/functions/time/asctime.c
new file mode 100644 (file)
index 0000000..8c9db60
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+char * asctime( const struct tm * timeptr )
+{
+    return NULL;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/time/ctime.c b/src/pdclib/functions/time/ctime.c
new file mode 100644 (file)
index 0000000..d021a7d
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+char * ctime( const time_t * timer )
+{
+    return NULL;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/time/difftime.c b/src/pdclib/functions/time/difftime.c
new file mode 100644 (file)
index 0000000..a6d94f8
--- /dev/null
@@ -0,0 +1,70 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+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( _PDCLIB_uintmax_t ) )
+    {
+        _PDCLIB_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
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/time/gmtime.c b/src/pdclib/functions/time/gmtime.c
new file mode 100644 (file)
index 0000000..3bd001c
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+struct tm * gmtime( const time_t * timer )
+{
+    return NULL;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/time/localtime.c b/src/pdclib/functions/time/localtime.c
new file mode 100644 (file)
index 0000000..91c0618
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+struct tm * localtime( const time_t * timer )
+{
+    return NULL;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/time/mktime.c b/src/pdclib/functions/time/mktime.c
new file mode 100644 (file)
index 0000000..0b8596a
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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.h>
+
+#ifndef REGTEST
+
+time_t mktime( struct tm * timeptr )
+{
+    return -1;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/functions/time/strftime.c b/src/pdclib/functions/time/strftime.c
new file mode 100644 (file)
index 0000000..81ae13d
--- /dev/null
@@ -0,0 +1,1719 @@
+/* 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 <time.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <assert.h>
+
+#ifndef REGTEST
+
+/* 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 <stdio.h>
+
+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 * _PDCLIB_restrict s, size_t maxsize, const char * _PDCLIB_restrict format, const struct tm * _PDCLIB_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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#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 = { 0 };
+        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;
+}
+
+int main( void )
+{
+    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 );
+    TESTCASE( strcmp( buffer, "Sun " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%A ", &timeptr ) == 7 );
+    TESTCASE( strcmp( buffer, "Sunday " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%b ", &timeptr ) == 4 );
+    TESTCASE( strcmp( buffer, "Oct " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%h ", &timeptr ) == 4 );
+    TESTCASE( strcmp( buffer, "Oct " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%B ", &timeptr ) == 8 );
+    TESTCASE( strcmp( buffer, "October " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%c ", &timeptr ) == 25 );
+    TESTCASE( strcmp( buffer, "Sun Oct  1 12:30:59 1972 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%C ", &timeptr ) == 3 );
+    TESTCASE( strcmp( buffer, "19 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%d ", &timeptr ) == 3 );
+    TESTCASE( strcmp( buffer, "01 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%D ", &timeptr ) == 9 );
+    TESTCASE( strcmp( buffer, "10/01/72 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%e ", &timeptr ) == 3 );
+    TESTCASE( strcmp( buffer, " 1 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%F ", &timeptr ) == 11 );
+    TESTCASE( strcmp( buffer, "1972-10-01 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%H ", &timeptr ) == 3 );
+    TESTCASE( strcmp( buffer, "12 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%I ", &timeptr ) == 3 );
+    TESTCASE( strcmp( buffer, "12 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%j ", &timeptr ) == 4 );
+    TESTCASE( strcmp( buffer, "275 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%m ", &timeptr ) == 3 );
+    TESTCASE( strcmp( buffer, "10 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%M ", &timeptr ) == 3 );
+    TESTCASE( strcmp( buffer, "30 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%p ", &timeptr ) == 3 );
+    TESTCASE( strcmp( buffer, "PM " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%r ", &timeptr ) == 12 );
+    TESTCASE( strcmp( buffer, "12:30:59 PM " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%R ", &timeptr ) == 6 );
+    TESTCASE( strcmp( buffer, "12:30 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%S ", &timeptr ) == 3 );
+    TESTCASE( strcmp( buffer, "59 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%T ", &timeptr ) == 9 );
+    TESTCASE( strcmp( buffer, "12:30:59 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%u ", &timeptr ) == 2 );
+    TESTCASE( strcmp( buffer, "7 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%w ", &timeptr ) == 2 );
+    TESTCASE( strcmp( buffer, "0 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%x ", &timeptr ) == 9 );
+    TESTCASE( strcmp( buffer, "10/01/72 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%X ", &timeptr ) == 9 );
+    TESTCASE( strcmp( buffer, "12:30:59 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%y ", &timeptr ) == 3 );
+    TESTCASE( strcmp( buffer, "72 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%Y ", &timeptr ) == 5 );
+    TESTCASE( strcmp( buffer, "1972 " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%% ", &timeptr ) == 2 );
+    TESTCASE( strcmp( buffer, "% " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%n ", &timeptr ) == 2 );
+    TESTCASE( strcmp( buffer, "\n " ) == 0 );
+    TESTCASE( strftime( buffer, 100, "%t ", &timeptr ) == 2 );
+    TESTCASE( strcmp( buffer, "\t " ) == 0 );
+    TESTCASE( test_week_calc() );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/include/assert.h b/src/pdclib/include/assert.h
new file mode 100644 (file)
index 0000000..a5c1a8e
--- /dev/null
@@ -0,0 +1,35 @@
+/* Diagnostics <assert.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#include "pdclib/_PDCLIB_aux.h"
+
+#ifndef _PDCLIB_ASSERT_H
+#define _PDCLIB_ASSERT_H _PDCLIB_ASSERT_H
+void _PDCLIB_assert99( const char * const, const char * const, const char * const );
+void _PDCLIB_assert89( const char * const );
+#endif
+
+/* If NDEBUG is set, assert() is a null operation. */
+#undef assert
+
+#ifdef NDEBUG
+#define assert( ignore ) ( (void) 0 )
+#else
+#if __STDC_VERSION__ >= 199901L
+#define assert( expression ) ( ( expression ) ? (void) 0 \
+        : _PDCLIB_assert99( "Assertion failed: " #expression \
+                            ", function ", __func__, \
+                            ", file " __FILE__ \
+                            ", line " _PDCLIB_symbol2string( __LINE__ ) \
+                            "." _PDCLIB_endl ) )
+#else
+#define assert( expression ) ( ( expression ) ? (void) 0 \
+        : _PDCLIB_assert89( "Assertion failed: " #expression \
+                            ", file " __FILE__ \
+                            ", line " _PDCLIB_symbol2string( __LINE__ ) \
+                            "." _PDCLIB_endl ) )
+#endif
+#endif
diff --git a/src/pdclib/include/ctype.h b/src/pdclib/include/ctype.h
new file mode 100644 (file)
index 0000000..07fad9c
--- /dev/null
@@ -0,0 +1,95 @@
+/* Character handling <ctype.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_CTYPE_H
+#define _PDCLIB_CTYPE_H _PDCLIB_CTYPE_H
+
+#include "pdclib/_PDCLIB_int.h"
+
+/* 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 <wctype.h>,
+         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 );
+
+#endif
diff --git a/src/pdclib/include/errno.h b/src/pdclib/include/errno.h
new file mode 100644 (file)
index 0000000..bdc99aa
--- /dev/null
@@ -0,0 +1,17 @@
+/* Errors <errno.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_ERRNO_H
+#define _PDCLIB_ERRNO_H _PDCLIB_ERRNO_H
+
+#include "pdclib/_PDCLIB_int.h"
+
+#define errno (*_PDCLIB_errno_func())
+
+#define ERANGE _PDCLIB_ERANGE
+#define EDOM _PDCLIB_EDOM
+
+#endif
diff --git a/src/pdclib/include/inttypes.h b/src/pdclib/include/inttypes.h
new file mode 100644 (file)
index 0000000..5252937
--- /dev/null
@@ -0,0 +1,249 @@
+/* Format conversion of integer types <inttypes.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_INTTYPES_H
+#define _PDCLIB_INTTYPES_H _PDCLIB_INTTYPES_H
+
+#include <stdint.h>
+
+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 <stdlib.h>, but on
+   the potentially larger type.
+*/
+intmax_t strtoimax( const char * _PDCLIB_restrict nptr, char * * _PDCLIB_restrict endptr, int base );
+uintmax_t strtoumax( const char * _PDCLIB_restrict nptr, char * * _PDCLIB_restrict endptr, int base );
+
+/* TODO: wcstoimax(), wcstoumax() */
+
+#endif
diff --git a/src/pdclib/include/iso646.h b/src/pdclib/include/iso646.h
new file mode 100644 (file)
index 0000000..98a023f
--- /dev/null
@@ -0,0 +1,22 @@
+/* Alternative spellings <iso646.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_ISO646_H
+#define _PDCLIB_ISO646_H _PDCLIB_ISO646_H
+
+#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 ^=
+
+#endif
diff --git a/src/pdclib/include/limits.h b/src/pdclib/include/limits.h
new file mode 100644 (file)
index 0000000..4349acd
--- /dev/null
@@ -0,0 +1,35 @@
+/* Sizes of integer types <limits.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_LIMITS_H
+#define _PDCLIB_LIMITS_H _PDCLIB_LIMITS_H
+
+#include "pdclib/_PDCLIB_int.h"
+
+/* TODO: Defined to 1 as multibyte characters are not supported yet. */
+#define MB_LEN_MAX 1
+
+#define LLONG_MIN  _PDCLIB_LLONG_MIN
+#define LLONG_MAX  _PDCLIB_LLONG_MAX
+#define ULLONG_MAX _PDCLIB_ULLONG_MAX
+
+#define CHAR_BIT   _PDCLIB_CHAR_BIT
+#define CHAR_MAX   _PDCLIB_CHAR_MAX
+#define CHAR_MIN   _PDCLIB_CHAR_MIN
+#define SCHAR_MAX  _PDCLIB_SCHAR_MAX
+#define SCHAR_MIN  _PDCLIB_SCHAR_MIN
+#define UCHAR_MAX  _PDCLIB_UCHAR_MAX
+#define SHRT_MAX   _PDCLIB_SHRT_MAX
+#define SHRT_MIN   _PDCLIB_SHRT_MIN
+#define INT_MAX    _PDCLIB_INT_MAX
+#define INT_MIN    _PDCLIB_INT_MIN
+#define LONG_MAX   _PDCLIB_LONG_MAX
+#define LONG_MIN   _PDCLIB_LONG_MIN
+#define USHRT_MAX  _PDCLIB_USHRT_MAX
+#define UINT_MAX   _PDCLIB_UINT_MAX
+#define ULONG_MAX  _PDCLIB_ULONG_MAX
+
+#endif
diff --git a/src/pdclib/include/locale.h b/src/pdclib/include/locale.h
new file mode 100644 (file)
index 0000000..c8467a8
--- /dev/null
@@ -0,0 +1,99 @@
+/* Localization <locale.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_LOCALE_H
+#define _PDCLIB_LOCALE_H _PDCLIB_LOCALE_H
+
+#include "pdclib/_PDCLIB_int.h"
+
+#ifndef _PDCLIB_NULL_DEFINED
+#define _PDCLIB_NULL_DEFINED _PDCLIB_NULL_DEFINED
+#define NULL _PDCLIB_NULL
+#endif
+
+/* 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 (<ctype.h>, <wctype.h>) */
+#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 );
+
+#endif
diff --git a/src/pdclib/include/pdclib/_PDCLIB_aux.h b/src/pdclib/include/pdclib/_PDCLIB_aux.h
new file mode 100644 (file)
index 0000000..d6ca14e
--- /dev/null
@@ -0,0 +1,65 @@
+/* Auxiliary PDCLib code <_PDCLIB_aux.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_AUX_H
+#define _PDCLIB_AUX_H _PDCLIB_AUX_H
+
+/* -------------------------------------------------------------------------- */
+/* 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
+
+#if __STDC_VERSION__ < 199901L
+#define _PDCLIB_restrict
+#define _PDCLIB_inline
+#else
+#define _PDCLIB_restrict restrict
+#define _PDCLIB_inline inline
+#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 )
+
+#endif
diff --git a/src/pdclib/include/pdclib/_PDCLIB_glue.h b/src/pdclib/include/pdclib/_PDCLIB_glue.h
new file mode 100644 (file)
index 0000000..fc1bbb2
--- /dev/null
@@ -0,0 +1,71 @@
+/* OS glue functions declaration <_PDCLIB_glue.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_GLUE_H
+#define _PDCLIB_GLUE_H _PDCLIB_GLUE_H
+
+#include "pdclib/_PDCLIB_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 _PDCLIB_config.h.          */
+/* -------------------------------------------------------------------------- */
+
+/* stdlib.h */
+
+/* A system call that terminates the calling process, returning a given status
+   to the environment.
+*/
+void _PDCLIB_Exit( int status ) _PDCLIB_NORETURN;
+
+/* 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.
+*/
+_PDCLIB_int64_t _PDCLIB_seek( struct _PDCLIB_file_t * stream, _PDCLIB_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 );
+
+#endif
diff --git a/src/pdclib/include/pdclib/_PDCLIB_int.h b/src/pdclib/include/pdclib/_PDCLIB_int.h
new file mode 100644 (file)
index 0000000..6eaded1
--- /dev/null
@@ -0,0 +1,582 @@
+/* PDCLib internal integer logic <_PDCLIB_int.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_INT_H
+#define _PDCLIB_INT_H _PDCLIB_INT_H
+
+/* -------------------------------------------------------------------------- */
+/* 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 <stdbool.h>
+
+#include "pdclib/_PDCLIB_config.h"
+#include "pdclib/_PDCLIB_aux.h"
+
+/* null pointer constant */
+#define _PDCLIB_NULL 0
+
+/* -------------------------------------------------------------------------- */
+/* Limits of native datatypes                                                 */
+/* -------------------------------------------------------------------------- */
+/* The definition of minimum limits for unsigned datatypes is done because    */
+/* later on we will "construct" limits for other abstract types:              */
+/* USHRT -> _PDCLIB_ + USHRT + _MIN -> _PDCLIB_USHRT_MIN -> 0                 */
+/* INT -> _PDCLIB_ + INT + _MIN -> _PDCLIB_INT_MIN -> ... you get the idea.   */
+/* -------------------------------------------------------------------------- */
+
+/* Setting 'char' limits                                                      */
+#define _PDCLIB_CHAR_BIT    8
+#define _PDCLIB_UCHAR_MIN   0
+#define _PDCLIB_UCHAR_MAX   0xff
+#define _PDCLIB_SCHAR_MIN   (-0x7f - 1)
+#define _PDCLIB_SCHAR_MAX   0x7f
+#if _PDCLIB_CHAR_SIGNED == 1
+#define _PDCLIB_CHAR_MIN    _PDCLIB_SCHAR_MIN
+#define _PDCLIB_CHAR_MAX    _PDCLIB_SCHAR_MAX
+#else
+#define _PDCLIB_CHAR_MIN    0
+#define _PDCLIB_CHAR_MAX    _PDCLIB_UCHAR_MAX
+#endif
+
+/* Setting 'short' limits                                                     */
+#if     _PDCLIB_SHRT_BYTES == 2
+#define _PDCLIB_SHRT_MAX      0x7fff
+#define _PDCLIB_SHRT_MIN      (-0x7fff - 1)
+#define _PDCLIB_USHRT_MAX     0xffff
+#else
+#error Unsupported width of 'short' (not 16 bit).
+#endif
+#define _PDCLIB_USHRT_MIN 0
+
+#if _PDCLIB_INT_BYTES < _PDCLIB_SHRT_BYTES
+#error Bogus setting: short > int? Check _PDCLIB_config.h.
+#endif
+
+/* Setting 'int' limits                                                       */
+#if     _PDCLIB_INT_BYTES == 2
+#define _PDCLIB_INT_MAX   0x7fff
+#define _PDCLIB_INT_MIN   (-0x7fff - 1)
+#define _PDCLIB_UINT_MAX  0xffffU
+#elif   _PDCLIB_INT_BYTES == 4
+#define _PDCLIB_INT_MAX   0x7fffffff
+#define _PDCLIB_INT_MIN   (-0x7fffffff - 1)
+#define _PDCLIB_UINT_MAX  0xffffffffU
+#elif _PDCLIB_INT_BYTES   == 8
+#define _PDCLIB_INT_MAX   0x7fffffffffffffff
+#define _PDCLIB_INT_MIN   (-0x7fffffffffffffff - 1)
+#define _PDCLIB_UINT_MAX  0xffffffffffffffff
+#else
+#error Unsupported width of 'int' (neither 16, 32, nor 64 bit).
+#endif
+#define _PDCLIB_UINT_MIN 0
+
+/* Setting 'long' limits                                                      */
+#if   _PDCLIB_LONG_BYTES   == 4
+#define _PDCLIB_LONG_MAX   0x7fffffffL
+#define _PDCLIB_LONG_MIN   (-0x7fffffffL - 1L)
+#define _PDCLIB_ULONG_MAX  0xffffffffUL
+#elif   _PDCLIB_LONG_BYTES == 8
+#define _PDCLIB_LONG_MAX   0x7fffffffffffffffL
+#define _PDCLIB_LONG_MIN   (-0x7fffffffffffffffL - 1L)
+#define _PDCLIB_ULONG_MAX  0xffffffffffffffffUL
+#else
+#error Unsupported width of 'long' (neither 32 nor 64 bit).
+#endif
+#define _PDCLIB_ULONG_MIN 0
+
+/* Setting 'long long' limits                                                 */
+#if _PDCLIB_LLONG_BYTES    == 8
+#define _PDCLIB_LLONG_MAX  0x7fffffffffffffffLL
+#define _PDCLIB_LLONG_MIN  (-0x7fffffffffffffffLL - 1LL)
+#define _PDCLIB_ULLONG_MAX 0xffffffffffffffffULL
+#elif _PDCLIB_LLONG_BYTES  == 16
+#define _PDCLIB_LLONG_MAX  0x7fffffffffffffffffffffffffffffffLL
+#define _PDCLIB_LLONG_MIN  (-0x7fffffffffffffffffffffffffffffffLL - 1LL)
+#define _PDCLIB_ULLONG_MAX 0xffffffffffffffffffffffffffffffffULL
+#else
+#error Unsupported width of 'long long' (neither 64 nor 128 bit).
+#endif
+#define _PDCLIB_ULLONG_MIN 0
+
+/* -------------------------------------------------------------------------- */
+/* <stdint.h> exact-width types and their limits                              */
+/* -------------------------------------------------------------------------- */
+/* Note that, for the "standard" widths of 8, 16, 32 and 64 bit, the "LEAST"  */
+/* types are identical to the "exact-width" types, by definition.             */
+
+/* Setting 'int8_t', its limits, its literal, and conversion macros.          */
+#if     _PDCLIB_CHAR_BIT == 8
+typedef signed char        _PDCLIB_int8_t;
+typedef unsigned char      _PDCLIB_uint8_t;
+#define _PDCLIB_INT8_MAX   _PDCLIB_CHAR_MAX
+#define _PDCLIB_INT8_MIN   _PDCLIB_CHAR_MIN
+#define _PDCLIB_UINT8_MAX  _PDCLIB_UCHAR_MAX
+#define _PDCLIB_8_CONV     hh
+#else
+#error Unsupported width of char (not 8 bits).
+#endif
+
+/* Setting 'int16_t', its limits, its literal, and conversion macros.         */
+#if     _PDCLIB_INT_BYTES  == 2
+typedef signed int         _PDCLIB_int16_t;
+typedef unsigned int       _PDCLIB_uint16_t;
+#define _PDCLIB_INT16_MAX  _PDCLIB_INT_MAX
+#define _PDCLIB_INT16_MIN  _PDCLIB_INT_MIN
+#define _PDCLIB_UINT16_MAX _PDCLIB_UINT_MAX
+#define _PDCLIB_16_CONV
+#elif   _PDCLIB_SHRT_BYTES == 2
+typedef signed short       _PDCLIB_int16_t;
+typedef unsigned short     _PDCLIB_uint16_t;
+#define _PDCLIB_INT16_MAX  _PDCLIB_SHRT_MAX
+#define _PDCLIB_INT16_MIN  _PDCLIB_SHRT_MIN
+#define _PDCLIB_UINT16_MAX _PDCLIB_USHRT_MAX
+#define _PDCLIB_16_CONV    h
+#else
+#error Neither 'short' nor 'int' are 16-bit.
+#endif
+
+/* Setting 'int32_t', its limits, its literal, and conversion macros.         */
+#if     _PDCLIB_INT_BYTES  == 4
+typedef signed int         _PDCLIB_int32_t;
+typedef unsigned int       _PDCLIB_uint32_t;
+#define _PDCLIB_INT32_MAX  _PDCLIB_INT_MAX
+#define _PDCLIB_INT32_MIN  _PDCLIB_INT_MIN
+#define _PDCLIB_UINT32_MAX _PDCLIB_UINT_MAX
+#define _PDCLIB_INT32_LITERAL
+#define _PDCLIB_UINT32_LITERAL
+#define _PDCLIB_32_CONV
+#elif   _PDCLIB_LONG_BYTES == 4
+typedef signed long        _PDCLIB_int32_t;
+typedef unsigned long      _PDCLIB_uint32_t;
+#define _PDCLIB_INT32_MAX  _PDCLIB_LONG_MAX
+#define _PDCLIB_INT32_MIN  _PDCLIB_LONG_MIN
+#define _PDCLIB_UINT32_MAX _PDCLIB_LONG_MAX
+#define _PDCLIB_INT32_LITERAL  l
+#define _PDCLIB_UINT32_LITERAL ul
+#define _PDCLIB_32_CONV        l
+#else
+#error Neither 'int' nor 'long' are 32-bit.
+#endif
+
+/* Setting 'int64_t', its limits, its literal, and conversion macros.         */
+#if     _PDCLIB_LONG_BYTES == 8
+typedef signed long        _PDCLIB_int64_t;
+typedef unsigned long      _PDCLIB_uint64_t;
+#define _PDCLIB_INT64_MAX  _PDCLIB_LONG_MAX
+#define _PDCLIB_INT64_MIN  _PDCLIB_LONG_MIN
+#define _PDCLIB_UINT64_MAX  _PDCLIB_ULONG_MAX
+#define _PDCLIB_INT64_LITERAL  l
+#define _PDCLIB_UINT64_LITERAL ul
+#define _PDCLIB_64_CONV        l
+#elif _PDCLIB_LLONG_BYTES  == 8
+typedef signed long long   _PDCLIB_int64_t;
+typedef unsigned long long _PDCLIB_uint64_t;
+#define _PDCLIB_INT64_MAX  _PDCLIB_LLONG_MAX
+#define _PDCLIB_INT64_MIN  _PDCLIB_LLONG_MIN
+#define _PDCLIB_UINT64_MAX  _PDCLIB_ULLONG_MAX
+#define _PDCLIB_INT64_LITERAL  ll
+#define _PDCLIB_UINT64_LITERAL ull
+#define _PDCLIB_64_CONV        ll
+#else
+#error Neither 'long' nor 'long long' are 64-bit.
+#endif
+
+/* -------------------------------------------------------------------------- */
+/* <stdint.h> "fastest" types and their limits                                */
+/* -------------------------------------------------------------------------- */
+/* This is, admittedly, butt-ugly. But at least it's ugly where the average   */
+/* user of PDCLib will never see it, and makes <_PDCLIB_config.h> much        */
+/* cleaner.                                                                   */
+/* -------------------------------------------------------------------------- */
+
+typedef _PDCLIB_fast8          _PDCLIB_int_fast8_t;
+typedef unsigned _PDCLIB_fast8 _PDCLIB_uint_fast8_t;
+#define _PDCLIB_INT_FAST8_MIN  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_FAST8 ), _MIN )
+#define _PDCLIB_INT_FAST8_MAX  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_FAST8 ), _MAX )
+#define _PDCLIB_UINT_FAST8_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_U, _PDCLIB_FAST8 ), _MAX )
+
+typedef _PDCLIB_fast16          _PDCLIB_int_fast16_t;
+typedef unsigned _PDCLIB_fast16 _PDCLIB_uint_fast16_t;
+#define _PDCLIB_INT_FAST16_MIN  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_FAST16 ), _MIN )
+#define _PDCLIB_INT_FAST16_MAX  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_FAST16 ), _MAX )
+#define _PDCLIB_UINT_FAST16_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_U, _PDCLIB_FAST16 ), _MAX )
+
+typedef _PDCLIB_fast32          _PDCLIB_int_fast32_t;
+typedef unsigned _PDCLIB_fast32 _PDCLIB_uint_fast32_t;
+#define _PDCLIB_INT_FAST32_MIN  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_FAST32 ), _MIN )
+#define _PDCLIB_INT_FAST32_MAX  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_FAST32 ), _MAX )
+#define _PDCLIB_UINT_FAST32_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_U, _PDCLIB_FAST32 ), _MAX )
+
+typedef _PDCLIB_fast64          _PDCLIB_int_fast64_t;
+typedef unsigned _PDCLIB_fast64 _PDCLIB_uint_fast64_t;
+#define _PDCLIB_INT_FAST64_MIN  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_FAST64 ), _MIN )
+#define _PDCLIB_INT_FAST64_MAX  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_FAST64 ), _MAX )
+#define _PDCLIB_UINT_FAST64_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_U, _PDCLIB_FAST64 ), _MAX )
+
+/* -------------------------------------------------------------------------- */
+/* Various <stddef.h> typedefs and limits                                     */
+/* -------------------------------------------------------------------------- */
+
+typedef _PDCLIB_ptrdiff     _PDCLIB_ptrdiff_t;
+#define _PDCLIB_PTRDIFF_MIN _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_PTRDIFF ), _MIN )
+#define _PDCLIB_PTRDIFF_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_PTRDIFF ), _MAX )
+
+#define _PDCLIB_SIG_ATOMIC_MIN _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_SIG_ATOMIC ), _MIN )
+#define _PDCLIB_SIG_ATOMIC_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_SIG_ATOMIC ), _MAX )
+
+typedef _PDCLIB_size     _PDCLIB_size_t;
+#define _PDCLIB_SIZE_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_SIZE ), _MAX )
+
+typedef _PDCLIB_wchar     _PDCLIB_wchar_t;
+#define _PDCLIB_WCHAR_MIN _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_WCHAR ), _MIN )
+#define _PDCLIB_WCHAR_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_WCHAR ), _MAX )
+
+typedef _PDCLIB_wint     _PDCLIB_wint_t;
+#define _PDCLIB_WINT_MIN _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_WINT ), _MIN )
+#define _PDCLIB_WINT_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_WINT ), _MAX )
+
+typedef _PDCLIB_intptr          _PDCLIB_intptr_t;
+typedef unsigned _PDCLIB_intptr _PDCLIB_uintptr_t;
+#define _PDCLIB_INTPTR_MIN  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_INTPTR ), _MIN )
+#define _PDCLIB_INTPTR_MAX  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_INTPTR ), _MAX )
+#define _PDCLIB_UINTPTR_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_U, _PDCLIB_INTPTR ), _MAX )
+
+typedef _PDCLIB_intmax          _PDCLIB_intmax_t;
+typedef unsigned _PDCLIB_intmax _PDCLIB_uintmax_t;
+#define _PDCLIB_INTMAX_MIN  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_INTMAX ), _MIN )
+#define _PDCLIB_INTMAX_MAX  _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_, _PDCLIB_INTMAX ), _MAX )
+#define _PDCLIB_UINTMAX_MAX _PDCLIB_concat( _PDCLIB_concat( _PDCLIB_U, _PDCLIB_INTMAX ), _MAX )
+#define _PDCLIB_INTMAX_C( value )  _PDCLIB_concat( value, _PDCLIB_INTMAX_LITERAL )
+#define _PDCLIB_UINTMAX_C( value ) _PDCLIB_concat( value, _PDCLIB_concat( u, _PDCLIB_INTMAX_LITERAL ) )
+
+/* -------------------------------------------------------------------------- */
+/* Various <stdio.h> internals                                                */
+/* -------------------------------------------------------------------------- */
+
+/* Flags for representing mode (see fopen()). Note these must fit the same
+   status field as the _IO?BF flags in <stdio.h> 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
+{
+    _PDCLIB_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 */
+    _PDCLIB_size_t          bufsize;  /* Size of buffer */
+    _PDCLIB_size_t          bufidx;   /* Index of current position in buffer */
+    _PDCLIB_size_t          bufend;   /* Index of last pre-read character in buffer */
+    struct _PDCLIB_fpos_t   pos;      /* Offset and multibyte parsing state */
+    _PDCLIB_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 <time.h> 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
+{
+    _PDCLIB_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   */
+    _PDCLIB_int_fast32_t flags; /* flags and length modifiers                */
+    _PDCLIB_size_t   n;      /* print: maximum characters to be written      */
+                             /* scan:  number matched conversion specifiers  */
+    _PDCLIB_size_t   i;      /* number of characters read/written            */
+    _PDCLIB_size_t   current;/* chars read/written in the CURRENT conversion */
+    char *           s;      /* *sprintf(): target buffer                    */
+                             /* *sscanf():  source string                    */
+    _PDCLIB_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().            */
+_PDCLIB_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 );
+_PDCLIB_uintmax_t _PDCLIB_strtox_main( const char ** p, unsigned int base, _PDCLIB_uintmax_t error, _PDCLIB_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, _PDCLIB_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 <errno.h>.
+*/
+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 );
+
+/* -------------------------------------------------------------------------- */
+/* <locale.h> 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 << _PDCLIB_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
+{
+    _PDCLIB_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( _PDCLIB_size ), "Compiler disagrees on _PDCLIB_size." );
+
+_PDCLIB_static_assert( sizeof( _PDCLIB_wchar ) == sizeof( L'x' ), "Compiler disagrees on _PDCLIB_wchar." );
+
+_PDCLIB_static_assert( sizeof( void * ) == sizeof( _PDCLIB_intptr ), "Compiler disagrees on _PDCLIB_intptr." );
+
+_PDCLIB_static_assert( sizeof( &_PDCLIB_digits[1] - &_PDCLIB_digits[0] ) == sizeof( _PDCLIB_ptrdiff ), "Compiler disagrees on _PDCLIB_ptrdiff." );
+
+#endif
diff --git a/src/pdclib/include/stdalign.h b/src/pdclib/include/stdalign.h
new file mode 100644 (file)
index 0000000..de806a1
--- /dev/null
@@ -0,0 +1,17 @@
+/* Alignment <stdalign.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_STDALIGN_H
+#define _PDCLIB_ALIGN_H _PDCLIB_ALIGN_H
+
+#define alignas _Alignas
+#define alignof _Alignof
+
+#define __alignas_is_defined 1
+#define __alignof_is_defined 1
+
+#endif
+
diff --git a/src/pdclib/include/stdarg.h b/src/pdclib/include/stdarg.h
new file mode 100644 (file)
index 0000000..84c05d2
--- /dev/null
@@ -0,0 +1,19 @@
+/* Variable arguments <stdarg.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_STDARG_H
+#define _PDCLIB_STDARG_H _PDCLIB_STDARG_H
+
+#include "pdclib/_PDCLIB_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 )
+
+#endif
diff --git a/src/pdclib/include/stdbool.h b/src/pdclib/include/stdbool.h
new file mode 100644 (file)
index 0000000..8f6a1d0
--- /dev/null
@@ -0,0 +1,15 @@
+/* Boolean type and values <stdbool.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_STDBOOL_H
+#define _PDCLIB_STDBOOL_H _PDCLIB_STDBOOL_H
+
+#define bool                          _Bool
+#define true                          1
+#define false                         0
+#define __bool_true_false_are_defined 1
+
+#endif
diff --git a/src/pdclib/include/stddef.h b/src/pdclib/include/stddef.h
new file mode 100644 (file)
index 0000000..7cba7c0
--- /dev/null
@@ -0,0 +1,28 @@
+/* Common definitions <stddef.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_STDDEF_H
+#define _PDCLIB_STDDEF_H _PDCLIB_STDDEF_H
+
+#include "pdclib/_PDCLIB_int.h"
+
+typedef _PDCLIB_ptrdiff_t ptrdiff_t;
+
+#ifndef _PDCLIB_SIZE_T_DEFINED
+#define _PDCLIB_SIZE_T_DEFINED _PDCLIB_SIZE_T_DEFINED
+typedef _PDCLIB_size_t size_t;
+#endif
+
+typedef _PDCLIB_wchar_t   wchar_t;
+
+#ifndef _PDCLIB_NULL_DEFINED
+#define _PDCLIB_NULL_DEFINED _PDCLIB_NULL_DEFINED
+#define NULL _PDCLIB_NULL
+#endif
+
+#define offsetof( type, member ) _PDCLIB_offsetof( type, member )
+
+#endif
diff --git a/src/pdclib/include/stdint.h b/src/pdclib/include/stdint.h
new file mode 100644 (file)
index 0000000..544d1dd
--- /dev/null
@@ -0,0 +1,207 @@
+/* Integer types <stdint.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_STDINT_H
+#define _PDCLIB_STDINT_H _PDCLIB_STDINT_H
+
+#include "pdclib/_PDCLIB_int.h"
+
+/* 7.18.1.1 Exact-width integer types. */
+
+typedef _PDCLIB_int8_t  int8_t;
+typedef _PDCLIB_int16_t int16_t;
+typedef _PDCLIB_int32_t int32_t;
+typedef _PDCLIB_int64_t int64_t;
+
+typedef _PDCLIB_uint8_t  uint8_t;
+typedef _PDCLIB_uint16_t uint16_t;
+typedef _PDCLIB_uint32_t uint32_t;
+typedef _PDCLIB_uint64_t 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 int8_t  int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+
+typedef uint8_t  uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t 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 _PDCLIB_int_fast8_t  int_fast8_t;
+typedef _PDCLIB_int_fast16_t int_fast16_t;
+typedef _PDCLIB_int_fast32_t int_fast32_t;
+typedef _PDCLIB_int_fast64_t int_fast64_t;
+
+typedef _PDCLIB_uint_fast8_t  uint_fast8_t;
+typedef _PDCLIB_uint_fast16_t uint_fast16_t;
+typedef _PDCLIB_uint_fast32_t uint_fast32_t;
+typedef _PDCLIB_uint_fast64_t uint_fast64_t;
+
+/* 7.18.1.4 Integer types capable of holding object pointers */
+
+typedef _PDCLIB_intptr_t  intptr_t;
+typedef _PDCLIB_uintptr_t uintptr_t;
+
+/* 7.18.1.5 Greatest-width integer types */
+
+typedef _PDCLIB_intmax_t  intmax_t;
+typedef _PDCLIB_uintmax_t uintmax_t;
+
+/* 7.18.2 Limits of specified-width integer types */
+
+#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_MIN  _PDCLIB_INT8_MIN
+#define INT8_MAX  _PDCLIB_INT8_MAX
+#define UINT8_MAX _PDCLIB_UINT8_MAX
+
+#define INT16_MIN  _PDCLIB_INT16_MIN
+#define INT16_MAX  _PDCLIB_INT16_MAX
+#define UINT16_MAX _PDCLIB_UINT16_MAX
+
+#define INT32_MIN  _PDCLIB_INT32_MIN
+#define INT32_MAX  _PDCLIB_INT32_MAX
+#define UINT32_MAX _PDCLIB_UINT32_MAX
+
+#define INT64_MIN  _PDCLIB_INT64_MIN
+#define INT64_MAX  _PDCLIB_INT64_MAX
+#define UINT64_MAX _PDCLIB_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_MIN  INT8_MIN
+#define INT_LEAST8_MAX  INT8_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+
+#define INT_LEAST16_MIN  INT16_MIN
+#define INT_LEAST16_MAX  INT16_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+
+#define INT_LEAST32_MIN  INT32_MIN
+#define INT_LEAST32_MAX  INT32_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+
+#define INT_LEAST64_MIN  INT64_MIN
+#define INT_LEAST64_MAX  INT64_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+/* 7.18.2.3 Limits of fastest minimum-width integer types */
+
+#define INT_FAST8_MIN  _PDCLIB_INT_FAST8_MIN
+#define INT_FAST8_MAX  _PDCLIB_INT_FAST8_MAX
+#define UINT_FAST8_MAX _PDCLIB_UINT_FAST8_MAX
+
+#define INT_FAST16_MIN  _PDCLIB_INT_FAST16_MIN
+#define INT_FAST16_MAX  _PDCLIB_INT_FAST16_MAX
+#define UINT_FAST16_MAX _PDCLIB_UINT_FAST16_MAX
+
+#define INT_FAST32_MIN  _PDCLIB_INT_FAST32_MIN
+#define INT_FAST32_MAX  _PDCLIB_INT_FAST32_MAX
+#define UINT_FAST32_MAX _PDCLIB_UINT_FAST32_MAX
+
+#define INT_FAST64_MIN  _PDCLIB_INT_FAST64_MIN
+#define INT_FAST64_MAX  _PDCLIB_INT_FAST64_MAX
+#define UINT_FAST64_MAX _PDCLIB_UINT_FAST64_MAX
+
+/* 7.18.2.4 Limits of integer types capable of holding object pointers */
+
+#define INTPTR_MIN  _PDCLIB_INTPTR_MIN
+#define INTPTR_MAX  _PDCLIB_INTPTR_MAX
+#define UINTPTR_MAX _PDCLIB_UINTPTR_MAX
+
+/* 7.18.2.5 Limits of greatest-width integer types */
+
+#define INTMAX_MIN  _PDCLIB_INTMAX_MIN
+#define INTMAX_MAX  _PDCLIB_INTMAX_MAX
+#define UINTMAX_MAX _PDCLIB_UINTMAX_MAX
+
+/* 7.18.3 Limits of other integer types */
+
+#define PTRDIFF_MIN _PDCLIB_PTRDIFF_MIN
+#define PTRDIFF_MAX _PDCLIB_PTRDIFF_MAX
+
+#define SIG_ATOMIC_MIN _PDCLIB_SIG_ATOMIC_MIN
+#define SIG_ATOMIC_MAX _PDCLIB_SIG_ATOMIC_MAX
+
+#define SIZE_MAX _PDCLIB_SIZE_MAX
+
+#define WCHAR_MIN _PDCLIB_WCHAR_MIN
+#define WCHAR_MAX _PDCLIB_WCHAR_MAX
+
+#define WINT_MIN _PDCLIB_WINT_MIN
+#define WINT_MAX _PDCLIB_WINT_MAX
+
+#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 )  value
+#define INT16_C( value ) value
+#define INT32_C( value ) _PDCLIB_concat( value, _PDCLIB_INT32_LITERAL )
+#define INT64_C( value ) _PDCLIB_concat( value, _PDCLIB_INT64_LITERAL )
+
+/* Expand to an integer constant of specified value and type uint_leastN_t */
+
+#define UINT8_C( value )  value
+#define UINT16_C( value ) value
+#define UINT32_C( value ) _PDCLIB_concat( value, _PDCLIB_UINT32_LITERAL )
+#define UINT64_C( value ) _PDCLIB_concat( value, _PDCLIB_UINT64_LITERAL )
+
+/* 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_INTMAX_C( value )
+
+/* Expand to an integer constant of specified value and type uintmax_t */
+#define UINTMAX_C( value ) _PDCLIB_UINTMAX_C( value )
+
+#endif
+
+#endif
diff --git a/src/pdclib/include/stdio.h b/src/pdclib/include/stdio.h
new file mode 100644 (file)
index 0000000..d3d0a64
--- /dev/null
@@ -0,0 +1,786 @@
+/* Input/output <stdio.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_STDIO_H
+#define _PDCLIB_STDIO_H _PDCLIB_STDIO_H
+
+#include "pdclib/_PDCLIB_int.h"
+
+#ifndef _PDCLIB_SIZE_T_DEFINED
+#define _PDCLIB_SIZE_T_DEFINED _PDCLIB_SIZE_T_DEFINED
+typedef _PDCLIB_size_t size_t;
+#endif
+
+#ifndef _PDCLIB_NULL_DEFINED
+#define _PDCLIB_NULL_DEFINED _PDCLIB_NULL_DEFINED
+#define NULL _PDCLIB_NULL
+#endif
+
+/* See setvbuf(), third argument */
+#define _IOFBF 1
+#define _IOLBF 2
+#define _IONBF 4
+
+/* The following are platform-dependant, and defined in _PDCLIB_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 * old, const char * new );
+
+/* 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 * _PDCLIB_restrict filename, const char * _PDCLIB_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 * _PDCLIB_restrict filename, const char * _PDCLIB_restrict mode, FILE * _PDCLIB_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 * _PDCLIB_restrict stream, char * _PDCLIB_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 * _PDCLIB_restrict stream, char * _PDCLIB_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 * _PDCLIB_restrict stream, const char * _PDCLIB_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 * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, ... );
+
+/* Equivalent to fprintf( stdout, format, ... ). */
+int printf( const char * _PDCLIB_restrict format, ... );
+
+/* Equivalent to fscanf( stdin, format, ... ). */
+int scanf( const char * _PDCLIB_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 * _PDCLIB_restrict s, size_t n, const char * _PDCLIB_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 * _PDCLIB_restrict s, const char * _PDCLIB_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 * _PDCLIB_restrict s, const char * _PDCLIB_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
+   <stdio.h>.
+*/
+int vfprintf( FILE * _PDCLIB_restrict stream, const char * _PDCLIB_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
+   <stdio.h>.
+*/
+int vfscanf( FILE * _PDCLIB_restrict stream, const char * _PDCLIB_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
+   <stdio.h>.
+*/
+int vprintf( const char * _PDCLIB_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
+   <stdio.h>.
+*/
+int vscanf( const char * _PDCLIB_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
+   <stdio.h>.
+   */
+int vsnprintf( char * _PDCLIB_restrict s, size_t n, const char * _PDCLIB_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
+   <stdio.h>.
+*/
+int vsprintf( char * _PDCLIB_restrict s, const char * _PDCLIB_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
+   <stdio.h>.
+*/
+int vsscanf( const char * _PDCLIB_restrict s, const char * _PDCLIB_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 * _PDCLIB_restrict s, int n, FILE * _PDCLIB_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 * _PDCLIB_restrict s, FILE * _PDCLIB_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 * _PDCLIB_restrict ptr, size_t size, size_t nmemb, FILE * _PDCLIB_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 * _PDCLIB_restrict ptr, size_t size, size_t nmemb, FILE * _PDCLIB_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 * _PDCLIB_restrict stream, fpos_t * _PDCLIB_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 );
+
+#endif
diff --git a/src/pdclib/include/stdlib.h b/src/pdclib/include/stdlib.h
new file mode 100644 (file)
index 0000000..5377bcd
--- /dev/null
@@ -0,0 +1,242 @@
+/* General utilities <stdlib.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_STDLIB_H
+#define _PDCLIB_STDLIB_H _PDCLIB_STDLIB_H
+
+#include "pdclib/_PDCLIB_int.h"
+
+#ifndef _PDCLIB_SIZE_T_DEFINED
+#define _PDCLIB_SIZE_T_DEFINED _PDCLIB_SIZE_T_DEFINED
+typedef _PDCLIB_size_t size_t;
+#endif
+
+#ifndef _PDCLIB_NULL_DEFINED
+#define _PDCLIB_NULL_DEFINED _PDCLIB_NULL_DEFINED
+#define NULL _PDCLIB_NULL
+#endif
+
+/* Numeric conversion functions */
+
+/* TODO: atof(), strtof(), strtod(), strtold() */
+
+double atof( const char * nptr );
+double strtod( const char * _PDCLIB_restrict nptr, char * * _PDCLIB_restrict endptr );
+float strtof( const char * _PDCLIB_restrict nptr, char * * _PDCLIB_restrict endptr );
+long double strtold( const char * _PDCLIB_restrict nptr, char * * _PDCLIB_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 <inttypes.h> operating on intmax_t /
+   uintmax_t, if the long long versions do not suit your needs.
+*/
+long int strtol( const char * _PDCLIB_restrict nptr, char * * _PDCLIB_restrict endptr, int base );
+long long int strtoll( const char * _PDCLIB_restrict nptr, char * * _PDCLIB_restrict endptr, int base );
+unsigned long int strtoul( const char * _PDCLIB_restrict nptr, char * * _PDCLIB_restrict endptr, int base );
+unsigned long long int strtoull( const char * _PDCLIB_restrict nptr, char * * _PDCLIB_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 * _PDCLIB_restrict pwc, const char * _PDCLIB_restrict s, size_t n );
+int wctomb( char * s, wchar_t wc );
+size_t mbstowcs( wchar_t * _PDCLIB_restrict pwcs, const char * _PDCLIB_restrict s, size_t n );
+size_t wcstombs( char * _PDCLIB_restrict s, const wchar_t * _PDCLIB_restrict pwcs, size_t n );
+*/
+
+#endif
diff --git a/src/pdclib/include/stdnoreturn.h b/src/pdclib/include/stdnoreturn.h
new file mode 100644 (file)
index 0000000..8c18143
--- /dev/null
@@ -0,0 +1,12 @@
+/* _Noreturn <stdnoreturn.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_STDNORETURN_H
+#define _PDCLIB_STDNORETURN_H _PDCLIB_STDNORETURN_H
+
+#define noreturn _Noreturn
+
+#endif
diff --git a/src/pdclib/include/string.h b/src/pdclib/include/string.h
new file mode 100644 (file)
index 0000000..dc0af15
--- /dev/null
@@ -0,0 +1,185 @@
+/* String handling <string.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_STRING_H
+#define _PDCLIB_STRING_H _PDCLIB_STRING_H
+
+#include "pdclib/_PDCLIB_int.h"
+
+#ifndef _PDCLIB_SIZE_T_DEFINED
+#define _PDCLIB_SIZE_T_DEFINED _PDCLIB_SIZE_T_DEFINED
+typedef _PDCLIB_size_t size_t;
+#endif
+
+#ifndef _PDCLIB_NULL_DEFINED
+#define _PDCLIB_NULL_DEFINED _PDCLIB_NULL_DEFINED
+#define NULL _PDCLIB_NULL
+#endif
+
+/* 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 * _PDCLIB_restrict s1, const void * _PDCLIB_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 * _PDCLIB_restrict s1, const void * _PDCLIB_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 * _PDCLIB_restrict s1, const char * _PDCLIB_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 * _PDCLIB_restrict s1, const char * _PDCLIB_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 * _PDCLIB_restrict s1, const char * _PDCLIB_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 * _PDCLIB_restrict s1, const char * _PDCLIB_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 * _PDCLIB_restrict s1, const char * _PDCLIB_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 * _PDCLIB_restrict s1, const char * _PDCLIB_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 );
+
+#endif
diff --git a/src/pdclib/include/time.h b/src/pdclib/include/time.h
new file mode 100644 (file)
index 0000000..fb1af0b
--- /dev/null
@@ -0,0 +1,112 @@
+/* Date and time <time.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_TIME_H
+#define _PDCLIB_TIME_H _PDCLIB_TIMEH
+
+#include "pdclib/_PDCLIB_int.h"
+
+#ifndef _PDCLIB_SIZE_T_DEFINED
+#define _PDCLIB_SIZE_T_DEFINED _PDCLIB_SIZE_T_DEFINED
+typedef _PDCLIB_size_t size_t;
+#endif
+
+#ifndef _PDCLIB_NULL_DEFINED
+#define _PDCLIB_NULL_DEFINED _PDCLIB_NULL_DEFINED
+#define NULL _PDCLIB_NULL
+#endif
+
+/* 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 * _PDCLIB_restrict s, size_t maxsize, const char * _PDCLIB_restrict format, const struct tm * _PDCLIB_restrict timeptr );
+
+#endif
diff --git a/src/pdclib/include/wctype.h b/src/pdclib/include/wctype.h
new file mode 100644 (file)
index 0000000..5449930
--- /dev/null
@@ -0,0 +1,138 @@
+/* Wide character classification and mapping utilities <wctype.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_WCTYPE_H
+#define _PDCLIB_WCTYPE_H _PDCLIB_WCTYPE_H
+
+#include "pdclib/_PDCLIB_int.h"
+
+typedef _PDCLIB_wint_t wint_t;
+
+// 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 <ctype.h>,
+         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 );
+
+#endif
diff --git a/src/pdclib/platform/example/Readme.txt b/src/pdclib/platform/example/Readme.txt
new file mode 100644 (file)
index 0000000..07dc20e
--- /dev/null
@@ -0,0 +1,21 @@
+"Example" Platform Overlay
+==========================
+
+This is an example platform overlay, as described in the main Readme.txt of
+this archive. For ease of development, it applies (and tests) correctly on the
+machine of the author; no other guarantees can be given.
+It should give you a good idea of what is REQUIRED to make a copy of PDCLib
+work. There is a lot more you could do, and even some things you SHOULD do, in
+order to experience anything but abysmal performance:
+
+- Read / write operations on binary streams, and even on text streams for
+  machines that do not do any text conversion, can be made much more efficient
+  by using some sort of page buffer instead of the linear buffer implemented
+  here. It requires some special and platform-dependent manipulations, though,
+  which is why it is not done by default.
+
+- Anything relating to floating point logic is written in generic C. While
+  this is (hopefully) highly portable and should get you started on your
+  platform of choice, it is also highly inefficient and should be replaced by
+  inline assembly. Just make sure that your assembly keeps all the promises
+  the C library makes.
diff --git a/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_Exit.c b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_Exit.c
new file mode 100644 (file)
index 0000000..d2e6ee4
--- /dev/null
@@ -0,0 +1,40 @@
+/* _PDCLIB_exit( 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_exit() fit for use with POSIX
+   kernels.
+*/
+
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+extern void _exit( int status ) _PDCLIB_NORETURN;
+
+void _PDCLIB_Exit( int status )
+{
+    _exit( status );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    int UNEXPECTED_RETURN = 0;
+    _PDCLIB_Exit( 0 );
+    TESTCASE( UNEXPECTED_RETURN );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB__Exit.c b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB__Exit.c
new file mode 100644 (file)
index 0000000..d2e6ee4
--- /dev/null
@@ -0,0 +1,40 @@
+/* _PDCLIB_exit( 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_exit() fit for use with POSIX
+   kernels.
+*/
+
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+extern void _exit( int status ) _PDCLIB_NORETURN;
+
+void _PDCLIB_Exit( int status )
+{
+    _exit( status );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    int UNEXPECTED_RETURN = 0;
+    _PDCLIB_Exit( 0 );
+    TESTCASE( UNEXPECTED_RETURN );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_allocpages.c b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_allocpages.c
new file mode 100644 (file)
index 0000000..d46d46f
--- /dev/null
@@ -0,0 +1,86 @@
+/* _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 <stdint.h>
+#include <stddef.h>
+
+#ifndef REGTEST
+
+int brk( void * );
+void * sbrk( intptr_t );
+
+#include "pdclib/_PDCLIB_glue.h"
+
+static void * membreak = NULL;
+
+void * _PDCLIB_allocpages( int const n )
+{
+    void * oldbreak;
+    if ( membreak == NULL )
+    {
+        /* first call, make sure end-of-heap is page-aligned */
+        intptr_t unaligned = 0;
+        membreak = sbrk( 0 );
+        unaligned = _PDCLIB_PAGESIZE - (intptr_t)membreak % _PDCLIB_PAGESIZE;
+        if ( unaligned < _PDCLIB_PAGESIZE )
+        {
+            /* end-of-heap not page-aligned - adjust */
+            if ( sbrk( unaligned ) != membreak )
+            {
+                /* error */
+                return NULL;
+            }
+            membreak = (char *)membreak + unaligned;
+        }
+    }
+    /* increasing or decreasing heap - standard operation */
+    oldbreak = membreak;
+    membreak = (void *)( (char *)membreak + ( n * _PDCLIB_PAGESIZE ) );
+#ifdef __CYGWIN__
+    if ( sbrk( (char*)membreak - (char*)oldbreak ) == membreak )
+#else
+    if ( brk( membreak ) == 0 )
+#endif
+    {
+        /* successful */
+        return oldbreak;
+    }
+    else
+    {
+        /* out of memory */
+        membreak = oldbreak;
+        return NULL;
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    char * startbreak = sbrk( 0 );
+    TESTCASE( _PDCLIB_allocpages( 0 ) );
+    TESTCASE( ( (char *)sbrk( 0 ) - startbreak ) <= _PDCLIB_PAGESIZE );
+    startbreak = sbrk( 0 );
+    TESTCASE( _PDCLIB_allocpages( 1 ) );
+    TESTCASE( sbrk( 0 ) == startbreak + ( 1 * _PDCLIB_PAGESIZE ) );
+    TESTCASE( _PDCLIB_allocpages( 5 ) );
+    TESTCASE( sbrk( 0 ) == startbreak + ( 6 * _PDCLIB_PAGESIZE ) );
+    TESTCASE( _PDCLIB_allocpages( -3 ) );
+    TESTCASE( sbrk( 0 ) == startbreak + ( 3 * _PDCLIB_PAGESIZE ) );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_close.c b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_close.c
new file mode 100644 (file)
index 0000000..113290a
--- /dev/null
@@ -0,0 +1,36 @@
+/* _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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+extern int close( int fd );
+
+int _PDCLIB_close( int fd )
+{
+    return close( fd );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* No testdriver; tested in driver for _PDCLIB_open(). */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_fillbuffer.c b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_fillbuffer.c
new file mode 100644 (file)
index 0000000..012eed8
--- /dev/null
@@ -0,0 +1,78 @@
+/* _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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+#include </usr/include/errno.h>
+
+typedef long ssize_t;
+extern ssize_t read( int fd, void * buf, size_t count );
+
+int _PDCLIB_fillbuffer( struct _PDCLIB_file_t * stream )
+{
+    /* No need to handle buffers > INT_MAX, as PDCLib doesn't allow them */
+    ssize_t rc = read( stream->handle, stream->buffer, stream->bufsize );
+    if ( rc > 0 )
+    {
+        /* Reading successful. */
+        if ( ! ( stream->status & _PDCLIB_FBIN ) )
+        {
+            /* TODO: Text stream conversion here */
+        }
+        stream->pos.offset += rc;
+        stream->bufend = rc;
+        stream->bufidx = 0;
+        return 0;
+    }
+    if ( rc < 0 )
+    {
+        /* Reading error */
+        switch ( errno )
+        {
+            /* See comments on implementation-defined errno values in
+               <_PDCLIB_config.h>.
+            */
+            case EBADF:
+            case EFAULT:
+            case EINTR:
+            case EINVAL:
+            case EIO:
+                _PDCLIB_errno = _PDCLIB_ERROR;
+                break;
+            default:
+                /* This should be something like EUNKNOWN. */
+                _PDCLIB_errno = _PDCLIB_ERROR;
+                break;
+        }
+        stream->status |= _PDCLIB_ERRORFLAG;
+        return EOF;
+    }
+    /* End-of-File */
+    stream->status |= _PDCLIB_EOFFLAG;
+    return EOF;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_flushbuffer.c b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_flushbuffer.c
new file mode 100644 (file)
index 0000000..ca6b998
--- /dev/null
@@ -0,0 +1,110 @@
+/* _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 <stdio.h>
+#include <string.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+#include </usr/include/errno.h>
+
+typedef long ssize_t;
+extern ssize_t write( int fd, const void * buf, size_t count );
+
+/* The number of attempts to complete an output buffer flushing before giving
+ *    up.
+ *    */
+#define _PDCLIB_IO_RETRIES 1
+
+/* What the system should do after an I/O operation did not succeed, before   */
+/* trying again. (Empty by default.)                                          */
+#define _PDCLIB_IO_RETRY_OP( stream )
+
+int _PDCLIB_flushbuffer( struct _PDCLIB_file_t * stream )
+{
+    /* No need to handle buffers > INT_MAX, as PDCLib doesn't allow them */
+    _PDCLIB_size_t written = 0;
+    int rc;
+    unsigned int retries;
+    if ( ! ( stream->status & _PDCLIB_FBIN ) )
+    {
+        /* TODO: Text stream conversion here */
+    }
+    /* Keep trying to write data until everything is written, an error
+       occurs, or the configured number of retries is exceeded.
+    */
+    for ( retries = _PDCLIB_IO_RETRIES; retries > 0; --retries )
+    {
+        rc = (int)write( stream->handle, stream->buffer + written, stream->bufidx - written );
+        if ( rc < 0 )
+        {
+            /* Write error */
+            switch ( errno )
+            {
+                /* See <_PDCLIB_config.h>. There should be differenciated errno
+                   handling here, possibly even a 1:1 mapping; but that is up
+                   to the individual platform.
+                */
+                case EBADF:
+                case EFAULT:
+                case EFBIG:
+                case EINTR:
+                case EINVAL:
+                case EIO:
+                case ENOSPC:
+                case EPIPE:
+                    _PDCLIB_errno = _PDCLIB_ERROR;
+                    break;
+                default:
+                    /* This should be something like EUNKNOWN. */
+                    _PDCLIB_errno = _PDCLIB_ERROR;
+                    break;
+            }
+            stream->status |= _PDCLIB_ERRORFLAG;
+            /* Move unwritten remains to begin of buffer. */
+            stream->bufidx -= written;
+            memmove( stream->buffer, stream->buffer + written, stream->bufidx );
+            return EOF;
+        }
+        written += (_PDCLIB_size_t)rc;
+        stream->pos.offset += rc;
+        if ( written == stream->bufidx )
+        {
+            /* Buffer written completely. */
+            stream->bufidx = 0;
+            return 0;
+        }
+    }
+    /* Number of retries exceeded. You probably want a different errno value
+       here.
+    */
+    _PDCLIB_errno = _PDCLIB_ERROR;
+    stream->status |= _PDCLIB_ERRORFLAG;
+    /* Move unwritten remains to begin of buffer. */
+    stream->bufidx -= written;
+    memmove( stream->buffer, stream->buffer + written, stream->bufidx );
+    return EOF;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_open.c b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_open.c
new file mode 100644 (file)
index 0000000..e35d65d
--- /dev/null
@@ -0,0 +1,167 @@
+/* _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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "/usr/include/errno.h"
+
+int _PDCLIB_open( const char * const filename, unsigned int mode )
+{
+    /* This is an example implementation of _PDCLIB_open() fit for use with
+       POSIX kernels.
+    */
+    int osmode;
+    int rc;
+    switch ( mode & ( _PDCLIB_FREAD | _PDCLIB_FWRITE | _PDCLIB_FAPPEND | _PDCLIB_FRW ) )
+    {
+        case _PDCLIB_FREAD: /* "r" */
+            osmode = O_RDONLY;
+            break;
+        case _PDCLIB_FWRITE: /* "w" */
+            osmode = O_WRONLY | O_CREAT | O_TRUNC;
+            break;
+        case _PDCLIB_FAPPEND: /* "a" */
+            osmode = O_WRONLY | O_APPEND | O_CREAT;
+            break;
+        case _PDCLIB_FREAD | _PDCLIB_FRW: /* "r+" */
+            osmode = O_RDWR;
+            break;
+        case _PDCLIB_FWRITE | _PDCLIB_FRW: /* "w+" */
+            osmode = O_RDWR | O_CREAT | O_TRUNC;
+            break;
+        case _PDCLIB_FAPPEND | _PDCLIB_FRW: /* "a+" */
+            osmode = O_RDWR | O_APPEND | O_CREAT;
+            break;
+        default: /* Invalid mode */
+            return -1;
+    }
+    if ( osmode & O_CREAT )
+    {
+        rc = open( filename, osmode, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
+    }
+    else
+    {
+        rc = open( filename, osmode );
+    }
+    if ( rc == -1 )
+    {
+        switch ( errno )
+        {
+            /* See the comments on implementation-defined errno values in
+               <_PDCLIB_config.h>.
+            */
+            case EACCES:
+            case EFAULT:
+            case EINTR:
+            case EISDIR:
+            case ELOOP:
+            case EMFILE:
+            case ENAMETOOLONG:
+            case ENFILE:
+            case ENODEV:
+            case ENOENT:
+            case ENOMEM:
+            case ENOSPC:
+            case ENOTDIR:
+            case EOVERFLOW:
+            case EROFS:
+            case ETXTBSY:
+                _PDCLIB_errno = _PDCLIB_ERROR;
+                break;
+            default:
+                /* This should be something like EUNKNOWN. */
+                _PDCLIB_errno = _PDCLIB_ERROR;
+                break;
+        }
+    }
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+int main( void )
+{
+#ifndef REGTEST
+    /* This testdriver assumes POSIX, i.e. _PDCLIB_fd_t being int and being
+       incremented by one on each successful open.
+    */
+    int fh;
+    char buffer[ 10 ];
+    remove( testfile );
+    /* Trying to read non-existent file. */
+    TESTCASE( _PDCLIB_open( testfile, _PDCLIB_FREAD ) == _PDCLIB_NOHANDLE );
+    /* Writing to file, trying to read from it. */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FWRITE ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( write( fh, "test", 4 ) == 4 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( read( fh, buffer, 4 ) == -1 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Reading from file, trying to write to it. */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FREAD ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( write( fh, "test", 4 ) == -1 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Appending to file, trying to read from it. */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FAPPEND ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( write( fh, "app", 3 ) == 3 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( read( fh, buffer, 10 ) == -1 );
+    TESTCASE( write( fh, "end", 3 ) == 3 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Reading and writing from file ("r+"). */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FREAD | _PDCLIB_FRW ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( read( fh, buffer, 10 ) == 10 );
+    TESTCASE( memcmp( buffer, "testappend", 10 ) == 0 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( write( fh, "wedo", 4 ) == 4 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( read( fh, buffer, 10 ) == 10 );
+    TESTCASE( memcmp( buffer, "wedoappend", 10 ) == 0 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Writing and reading from file ("w+"). */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FWRITE | _PDCLIB_FRW ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( write( fh, "test", 4 ) == 4 );
+    TESTCASE( lseek( fh, 1, SEEK_SET ) == 1 );
+    TESTCASE( read( fh, buffer, 2 ) == 2 );
+    TESTCASE( memcmp( buffer, "es", 2 ) == 0 );
+    TESTCASE( write( fh, "sie", 3 ) == 3 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( read( fh, buffer, 6 ) == 6 );
+    TESTCASE( memcmp( buffer, "tessie", 6 ) == 0 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Appending and reading from file ("a+"). */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FAPPEND | _PDCLIB_FRW ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( write( fh, "baby", 4 ) == 4 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( read( fh, buffer, 10 ) == 10 );
+    TESTCASE( memcmp( buffer, "tessiebaby", 10 ) == 0 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Cleaning up. */
+    TESTCASE( remove( testfile ) == 0 );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_rename.c b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_rename.c
new file mode 100644 (file)
index 0000000..8c23f79
--- /dev/null
@@ -0,0 +1,144 @@
+/* _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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+#include </usr/include/errno.h>
+
+extern int unlink( const char * pathname );
+extern int link( const char * old, const char * new );
+
+int _PDCLIB_rename( const char * old, const char * new )
+{
+    /* Note that the behaviour if new file exists is implementation-defined.
+       There is nothing wrong with either overwriting it or failing the
+       operation, but you might want to document whichever you chose.
+       This example fails if new file exists.
+    */
+    if ( link( old, new ) == 0 )
+    {
+        if ( unlink( old ) == EOF )
+        {
+            switch ( errno )
+            {
+                /* See the comments on implementation-defined errno values in
+                   <_PDCLIB_config.h>.
+                */
+                case EACCES:
+                case EFAULT:
+                case EIO:
+                case EISDIR:
+                case ELOOP:
+                case ENAMETOOLONG:
+                case ENOENT:
+                case ENOMEM:
+                case ENOTDIR:
+                case EPERM:
+                case EROFS:
+                    _PDCLIB_errno = _PDCLIB_ERROR;
+                    break;
+                default:
+                    /* This should be something like EUNKNOWN. */
+                    _PDCLIB_errno = _PDCLIB_ERROR;
+                    break;
+            }
+            return -1;
+        }
+        else
+        {
+            return 0;
+        }
+    }
+    else
+    {
+        switch ( errno )
+        {
+            /* See the comments on implementation-defined errno values in
+               <_PDCLIB_config.h>.
+            */
+            case EACCES:
+            case EEXIST:
+            case EFAULT:
+            case EIO:
+            case ELOOP:
+            case EMLINK:
+            case ENAMETOOLONG:
+            case ENOENT:
+            case ENOMEM:
+            case ENOSPC:
+            case ENOTDIR:
+            case EPERM:
+            case EROFS:
+            case EXDEV:
+                _PDCLIB_errno = _PDCLIB_ERROR;
+                break;
+            default:
+                /* This should be something like EUNKNOWN. */
+                _PDCLIB_errno = _PDCLIB_ERROR;
+                break;
+        }
+        return EOF;
+    }
+}
+
+#endif
+
+#ifdef TEST
+#include "_PDCLIB_test.h"
+
+#include <stdlib.h>
+
+int main( void )
+{
+#ifndef REGTEST
+    FILE * file;
+    remove( testfile1 );
+    remove( testfile2 );
+    /* check that neither file exists */
+    TESTCASE( fopen( testfile1, "r" ) == NULL );
+    TESTCASE( fopen( testfile2, "r" ) == NULL );
+    /* rename file 1 to file 2 - expected to fail */
+    TESTCASE( _PDCLIB_rename( testfile1, testfile2 ) == -1 );
+    /* create file 1 */
+    TESTCASE( ( file = fopen( testfile1, "w" ) ) != NULL );
+    TESTCASE( fputc( 'x', file ) == 'x' );
+    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( _PDCLIB_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( fputc( 'x', file ) == 'x' );
+    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.
+    */
+    TESTCASE( _PDCLIB_rename( testfile1, testfile2 ) == -1 );
+    /* remove both files */
+    remove( testfile1 );
+    remove( testfile2 );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_seek.c b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_seek.c
new file mode 100644 (file)
index 0000000..4d09460
--- /dev/null
@@ -0,0 +1,82 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+#include "/usr/include/errno.h"
+
+extern _PDCLIB_int64_t lseek64( int fd, _PDCLIB_int64_t offset, int whence );
+extern long lseek( int fd, long offset, int whence );
+
+_PDCLIB_int64_t _PDCLIB_seek( struct _PDCLIB_file_t * stream, _PDCLIB_int64_t offset, int whence )
+{
+    _PDCLIB_int64_t rc;
+    switch ( whence )
+    {
+        case SEEK_SET:
+        case SEEK_CUR:
+        case SEEK_END:
+            /* EMPTY - OK */
+            break;
+        default:
+            /* See comments on implementation-defined errno values in
+               <_PDCLIB_config.h>.
+            */
+            _PDCLIB_errno = _PDCLIB_ERROR;
+            return EOF;
+            break;
+    }
+#ifdef __CYGWIN__
+    rc = lseek( stream->handle, offset, whence );
+#else
+    rc = lseek64( stream->handle, offset, whence );
+#endif
+    if ( rc != EOF )
+    {
+        stream->ungetidx = 0;
+        stream->bufidx = 0;
+        stream->bufend = 0;
+        stream->pos.offset = rc;
+        return rc;
+    }
+    switch ( errno )
+    {
+        case EBADF:
+        case EFAULT:
+            /* See comments on implementation-defined errno values in
+               <_PDCLIB_config.h>.
+            */
+            _PDCLIB_errno = _PDCLIB_ERROR;
+            break;
+        default:
+            /* This should be something like EUNKNOWN. */
+            _PDCLIB_errno = _PDCLIB_ERROR;
+            break;
+    }
+    return EOF;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_stdinit.c b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_stdinit.c
new file mode 100644 (file)
index 0000000..52b0651
--- /dev/null
@@ -0,0 +1,430 @@
+/* _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 <stdio.h>
+#include <locale.h>
+#include <limits.h>
+
+#ifndef REGTEST
+
+/* 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 <ctype.h> 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"
+    }
+};
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by several other testdrivers using stdin / stdout /
+       stderr.
+    */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/signal/raise.c b/src/pdclib/platform/example/functions/signal/raise.c
new file mode 100644 (file)
index 0000000..59ccc9f
--- /dev/null
@@ -0,0 +1,114 @@
+/* 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 <signal.h>
+
+#ifndef REGTEST
+
+#include <stdio.h>
+#include <stdlib.h>
+
+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 );
+    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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdlib.h>
+
+static volatile sig_atomic_t flag = 0;
+
+static int expected_signal = 0;
+
+static void test_handler( int sig )
+{
+    TESTCASE( sig == expected_signal );
+    flag = 1;
+}
+
+int main( void )
+{
+    /* 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 );
+    /* 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 );
+    /* 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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/signal/signal.c b/src/pdclib/platform/example/functions/signal/signal.c
new file mode 100644 (file)
index 0000000..e6775e7
--- /dev/null
@@ -0,0 +1,75 @@
+/* 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 <signal.h>
+
+#ifndef REGTEST
+
+#include <stdlib.h>
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by raise.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/stdio/remove.c b/src/pdclib/platform/example/functions/stdio/remove.c
new file mode 100644 (file)
index 0000000..aca3eaf
--- /dev/null
@@ -0,0 +1,75 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include <string.h>
+
+#include "/usr/include/errno.h"
+
+extern struct _PDCLIB_file_t * _PDCLIB_filelist;
+
+extern int unlink( const char * pathname );
+
+int remove( const char * pathname )
+{
+    int rc;
+    struct _PDCLIB_file_t * current = _PDCLIB_filelist;
+    while ( current != NULL )
+    {
+        if ( ( current->filename != NULL ) && ( strcmp( current->filename, pathname ) == 0 ) )
+        {
+            return EOF;
+        }
+        current = current->next;
+    }
+    if ( ( rc = unlink( pathname ) ) == -1 )
+    {
+        switch ( errno )
+        {
+            /* See the comments on implementation-defined errno values in
+               <_PDCLIB_config.h>.
+            */
+            case EACCES:
+            case EFAULT:
+            case EIO:
+            case EISDIR:
+            case ELOOP:
+            case ENAMETOOLONG:
+            case ENOENT:
+            case ENOMEM:
+            case ENOTDIR:
+            case EPERM:
+            case EROFS:
+                _PDCLIB_errno = _PDCLIB_ERROR;
+                break;
+            default:
+                /* This should be something like EUNKNOWN. */
+                _PDCLIB_errno = _PDCLIB_ERROR;
+                break;
+        }
+    }
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c (and several others) */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/stdio/tmpfile.c b/src/pdclib/platform/example/functions/stdio/tmpfile.c
new file mode 100644 (file)
index 0000000..585a61d
--- /dev/null
@@ -0,0 +1,114 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+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 )
+{
+    FILE * rc;
+    /* This is the chosen way to get high-quality randomness. Replace as
+       appropriate.
+    */
+    FILE * randomsource = fopen( "/proc/sys/kernel/random/uuid", "rb" );
+    char filename[ L_tmpnam ];
+    _PDCLIB_fd_t fd;
+    if ( randomsource == NULL )
+    {
+        return NULL;
+    }
+    for ( ;; )
+    {
+        /* Get a filename candidate. What constitutes a valid filename and
+           where temporary files are usually located is platform-dependent,
+           which is one reason why this function is located in the platform
+           overlay. The other reason is that a *good* implementation should
+           use high-quality randomness instead of a pseudo-random sequence to
+           generate the filename candidate, which is *also* platform-dependent.
+        */
+        unsigned int random;
+        fscanf( randomsource, "%u", &random );
+        sprintf( filename, "/tmp/%u.tmp", random );
+        /* Check if file of this name exists. Note that fopen() is a very weak
+           check, which does not take e.g. access permissions into account
+           (file might exist but not readable). Replace with something more
+           appropriate.
+        */
+        fd = open( filename, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR );
+        if ( fd != -1 )
+        {
+            break;
+        }
+        close( fd );
+    }
+    fclose( randomsource );
+    /* See fopen(). */
+    if ( ( rc = calloc( 1, sizeof( struct _PDCLIB_file_t ) + _PDCLIB_UNGETCBUFSIZE + L_tmpnam + BUFSIZ ) ) == NULL )
+    {
+        /* No memory to set up FILE structure */
+        close( fd );
+        return NULL;
+    }
+    rc->status = _PDCLIB_filemode( "wb+" ) | _IOLBF | _PDCLIB_DELONCLOSE;
+    rc->handle = fd;
+    rc->ungetbuf = (unsigned char *)rc + sizeof( struct _PDCLIB_file_t );
+    rc->filename = (char *)rc->ungetbuf + _PDCLIB_UNGETCBUFSIZE;
+    rc->buffer   = rc->filename + L_tmpnam;
+    strcpy( rc->filename, filename );
+    rc->bufsize = BUFSIZ;
+    rc->bufidx = 0;
+    rc->ungetidx = 0;
+    rc->next = _PDCLIB_filelist;
+    _PDCLIB_filelist = rc;
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <string.h>
+
+int main( void )
+{
+    FILE * fh;
+#ifndef REGTEST
+    char filename[ L_tmpnam ];
+    FILE * fhtest;
+#endif
+    TESTCASE( ( fh = tmpfile() ) != NULL );
+    TESTCASE( fputc( 'x', fh ) == 'x' );
+    /* Checking that file is actually there */
+    TESTCASE_NOREG( strcpy( filename, fh->filename ) == filename );
+    TESTCASE_NOREG( ( fhtest = fopen( filename, "r" ) ) != NULL );
+    TESTCASE_NOREG( fclose( fhtest ) == 0 );
+    /* Closing tmpfile */
+    TESTCASE( fclose( fh ) == 0 );
+    /* Checking that file was deleted */
+    TESTCASE_NOREG( fopen( filename, "r" ) == NULL );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/stdlib/getenv.c b/src/pdclib/platform/example/functions/stdlib/getenv.c
new file mode 100644 (file)
index 0000000..72bbcd2
--- /dev/null
@@ -0,0 +1,45 @@
+/* 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.
+*/
+
+/* This is an example implementation of getenv() fit for use with POSIX kernels.
+*/
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( strcmp( getenv( "SHELL" ), "/bin/bash" ) == 0 );
+    /* TESTCASE( strcmp( getenv( "SHELL" ), "/bin/sh" ) == 0 ); */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/stdlib/system.c b/src/pdclib/platform/example/functions/stdlib/system.c
new file mode 100644 (file)
index 0000000..15603c3
--- /dev/null
@@ -0,0 +1,57 @@
+/* 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 <stdlib.h>
+
+/* 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;
+}
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#define SHELLCOMMAND "echo 'SUCCESS testing system()'"
+
+int main( void )
+{
+    FILE * fh;
+    char buffer[25];
+    buffer[24] = 'x';
+    TESTCASE( ( fh = freopen( testfile, "wb+", stdout ) ) != NULL );
+    TESTCASE( system( SHELLCOMMAND ) );
+    rewind( fh );
+    TESTCASE( fread( buffer, 1, 24, fh ) == 24 );
+    TESTCASE( memcmp( buffer, "SUCCESS testing system()", 24 ) == 0 );
+    TESTCASE( buffer[24] == 'x' );
+    TESTCASE( fclose( fh ) == 0 );
+    TESTCASE( remove( testfile ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/time/clock.c b/src/pdclib/platform/example/functions/time/clock.c
new file mode 100644 (file)
index 0000000..825e040
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+#include <sys/times.h>
+
+clock_t clock( void )
+{
+    struct tms buf;
+    if ( times( &buf ) != (clock_t)-1 )
+    {
+        return buf.tms_utime + buf.tms_stime;
+    }
+    return -1;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/time/time.c b/src/pdclib/platform/example/functions/time/time.c
new file mode 100644 (file)
index 0000000..cbb29e1
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+#include <sys/time.h>
+
+/* See comments in time.h on the semantics of time_t. */
+
+time_t time( time_t * timer )
+{
+    struct timeval tv;
+    if ( gettimeofday( &tv, NULL ) == 0 )
+    {
+        if ( timer != NULL )
+        {
+            *timer = tv.tv_sec;
+        }
+        return tv.tv_sec;
+    }
+    return -1;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/functions/time/timespec_get.c b/src/pdclib/platform/example/functions/time/timespec_get.c
new file mode 100644 (file)
index 0000000..d8cbab7
--- /dev/null
@@ -0,0 +1,42 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+#include <sys/time.h>
+
+int timespec_get( struct timespec * ts, int base )
+{
+    if ( base == TIME_UTC )
+    {
+        /* We can make do with a really thin wrapper here. */
+        struct timeval tv;
+        if ( gettimeofday( &tv, NULL ) == 0 )
+        {
+            ts->tv_sec = tv.tv_sec;
+            ts->tv_nsec = tv.tv_usec * 1000;
+            return base;
+        }
+    }
+    /* Not supporting any other time base than TIME_UTC for now. */
+    return 0;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/example/include/float.h b/src/pdclib/platform/example/include/float.h
new file mode 100644 (file)
index 0000000..538d69e
--- /dev/null
@@ -0,0 +1,75 @@
+/* Characteristics of floating types <float.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_FLOAT_H
+#define _PDCLIB_FLOAT_H _PDCLIB_FLOAT_H
+
+#include "pdclib/_PDCLIB_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__
+
+#endif
diff --git a/src/pdclib/platform/example/include/pdclib/_PDCLIB_config.h b/src/pdclib/platform/example/include/pdclib/_PDCLIB_config.h
new file mode 100644 (file)
index 0000000..9731f86
--- /dev/null
@@ -0,0 +1,426 @@
+/* Internal PDCLib configuration <_PDCLIB_config.h>
+   (Generic Template)
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_CONFIG_H
+#define _PDCLIB_CONFIG_H _PDCLIB_CONFIG_H
+
+/* -------------------------------------------------------------------------- */
+/* 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 <stdlib.h> 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 );
+
+/* Define this to some compiler directive that can be written after the       */
+/* parameter list of a function declaration to indicate the function does     */
+/* never return. If your compiler does not support such a directive, define   */
+/* to nothing. (This is to avoid warnings with the exit functions under GCC.) */
+#define _PDCLIB_NORETURN __attribute__(( noreturn ))
+
+/* -------------------------------------------------------------------------- */
+/* 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
+
+/* <stdlib.h> 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;
+};
+
+/* -------------------------------------------------------------------------- */
+/* <stdint.h> defines a set of integer types that are of a minimum width, and */
+/* "usually fastest" on the system. (If, for example, accessing a single char */
+/* requires the CPU to access a complete int and then mask out the char, the  */
+/* "usually fastest" type of at least 8 bits would be int, not char.)         */
+/* If you do not have information on the relative performance of the types,   */
+/* the standard allows you to define any type that meets minimum width and    */
+/* signedness requirements.                                                   */
+/* The defines below are just configuration for the real typedefs and limit   */
+/* definitions done in <_PDCLIB_int.h>. The uppercase define shall be either  */
+/* SHRT, INT, LONG, or LLONG (telling which values to use for the *_MIN and   */
+/* *_MAX limits); the lowercase define either short, int, long, or long long  */
+/* (telling the actual type to use).                                          */
+/* The third define is the length modifier used for the type in printf() and  */
+/* scanf() functions (used in <inttypes.h>).                                  */
+/* If you require a non-standard datatype to define the "usually fastest"     */
+/* types, PDCLib as-is doesn't support that. Please contact the author with   */
+/* details on your platform in that case, so support can be added.            */
+/* -------------------------------------------------------------------------- */
+
+#define _PDCLIB_FAST8 INT
+#define _PDCLIB_fast8 int
+#define _PDCLIB_FAST8_CONV
+
+#define _PDCLIB_FAST16 INT
+#define _PDCLIB_fast16 int
+#define _PDCLIB_FAST16_CONV
+
+#define _PDCLIB_FAST32 INT
+#define _PDCLIB_fast32 int
+#define _PDCLIB_FAST32_CONV
+
+#define _PDCLIB_FAST64 LONG
+#define _PDCLIB_fast64 long
+#define _PDCLIB_FAST64_CONV l
+
+/* -------------------------------------------------------------------------- */
+/* What follows are a couple of "special" typedefs and their limits. Again,   */
+/* the actual definition of the limits is done in <_PDCLIB_int.h>, and the    */
+/* defines here are merely "configuration". See above for details.            */
+/* -------------------------------------------------------------------------- */
+
+/* The result type of substracting two pointers */
+#define _PDCLIB_ptrdiff long
+#define _PDCLIB_PTRDIFF LONG
+#define _PDCLIB_PTR_CONV l
+
+/* An integer type that can be accessed as atomic entity (think asynchronous
+   interrupts). The type itself is not defined in a freestanding environment,
+   but its limits are. (Don't ask.)
+*/
+#define _PDCLIB_sig_atomic int
+#define _PDCLIB_SIG_ATOMIC INT
+
+/* Result type of the 'sizeof' operator (must be unsigned) */
+#define _PDCLIB_size unsigned long
+#define _PDCLIB_SIZE ULONG
+
+/* Large enough an integer to hold all character codes of the largest supported
+   locale.
+*/
+#define _PDCLIB_wchar unsigned int
+#define _PDCLIB_WCHAR UINT
+
+/* Large enough an integer to hold all character codes of the largest supported
+   locale plus WEOF (which needs not to be equal to EOF, nor needs to be of
+   negative value).
+*/
+#define _PDCLIB_wint unsigned int
+#define _PDCLIB_WINT UINT
+
+/* (Signed) integer type capable of taking the (cast) value of a void *, and
+   having the value cast back to void *, comparing equal to the original.
+*/
+#define _PDCLIB_intptr long
+#define _PDCLIB_INTPTR LONG
+
+/* Largest supported integer type. Implementation note: see _PDCLIB_atomax(). */
+#define _PDCLIB_intmax long long int
+#define _PDCLIB_INTMAX LLONG
+#define _PDCLIB_MAX_CONV ll
+/* You are also required to state the literal suffix for the intmax type      */
+#define _PDCLIB_INTMAX_LITERAL ll
+
+/* <inttypes.h> defines imaxdiv(), which is equivalent to the div() function  */
+/* family (see further above) with intmax_t as basis.                         */
+
+struct _PDCLIB_imaxdiv_t
+{
+    _PDCLIB_intmax quot;
+    _PDCLIB_intmax rem;
+};
+
+/* -------------------------------------------------------------------------- */
+/* Time types                                                                 */
+/* -------------------------------------------------------------------------- */
+
+/* See <time.h> 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 (<stdarg.h>)
+   The macros defined by <stdarg.h> 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 <stdarg.h>. */
+#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 _PDCLIB_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 _PDCLIB_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 <errno.h> will "see" EPERM instead of EISDIR (the _PDCLIB_*
+   prefix removed by <errno.h> 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
+
+/* 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 <errno.h> 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
+
+
+#endif
diff --git a/src/pdclib/platform/example/include/signal.h b/src/pdclib/platform/example/include/signal.h
new file mode 100644 (file)
index 0000000..c5f6f28
--- /dev/null
@@ -0,0 +1,84 @@
+/* Signal handling <string.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_SIGNAL_H
+#define _PDCLIB_SIGNAL_H _PDCLIB_SIGNAL_H
+
+#include "pdclib/_PDCLIB_config.h"
+
+/* 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
+   <signal.h>.
+
+   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
+
+/* 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 (void (*)( int ))0
+#define SIG_ERR (void (*)( int ))-1
+#define SIG_IGN (void (*)( int ))1
+
+typedef _PDCLIB_sig_atomic sig_atomic_t;
+
+/* 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.
+*/
+void (*signal( int sig, void (*func)( int ) ) )( int );
+
+/* 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 );
+
+#endif
diff --git a/src/pdclib/platform/stmos/Readme.txt b/src/pdclib/platform/stmos/Readme.txt
new file mode 100644 (file)
index 0000000..07dc20e
--- /dev/null
@@ -0,0 +1,21 @@
+"Example" Platform Overlay
+==========================
+
+This is an example platform overlay, as described in the main Readme.txt of
+this archive. For ease of development, it applies (and tests) correctly on the
+machine of the author; no other guarantees can be given.
+It should give you a good idea of what is REQUIRED to make a copy of PDCLib
+work. There is a lot more you could do, and even some things you SHOULD do, in
+order to experience anything but abysmal performance:
+
+- Read / write operations on binary streams, and even on text streams for
+  machines that do not do any text conversion, can be made much more efficient
+  by using some sort of page buffer instead of the linear buffer implemented
+  here. It requires some special and platform-dependent manipulations, though,
+  which is why it is not done by default.
+
+- Anything relating to floating point logic is written in generic C. While
+  this is (hopefully) highly portable and should get you started on your
+  platform of choice, it is also highly inefficient and should be replaced by
+  inline assembly. Just make sure that your assembly keeps all the promises
+  the C library makes.
diff --git a/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_Exit.c b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_Exit.c
new file mode 100644 (file)
index 0000000..12b5c7f
--- /dev/null
@@ -0,0 +1,40 @@
+/* _PDCLIB_exit( 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_exit() fit for use with POSIX
+   kernels.
+*/
+
+#include <syscalls.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+//extern void _exit( int status ) _PDCLIB_NORETURN;
+
+void _PDCLIB_Exit( int status )
+{
+    _exit( status );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    int UNEXPECTED_RETURN = 0;
+    _PDCLIB_Exit( 0 );
+    TESTCASE( UNEXPECTED_RETURN );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB__Exit.c b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB__Exit.c
new file mode 100644 (file)
index 0000000..12b5c7f
--- /dev/null
@@ -0,0 +1,40 @@
+/* _PDCLIB_exit( 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_exit() fit for use with POSIX
+   kernels.
+*/
+
+#include <syscalls.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+//extern void _exit( int status ) _PDCLIB_NORETURN;
+
+void _PDCLIB_Exit( int status )
+{
+    _exit( status );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    int UNEXPECTED_RETURN = 0;
+    _PDCLIB_Exit( 0 );
+    TESTCASE( UNEXPECTED_RETURN );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_allocpages.c b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_allocpages.c
new file mode 100644 (file)
index 0000000..085857f
--- /dev/null
@@ -0,0 +1,82 @@
+/* _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 <syscalls.h>
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+static void * membreak = NULL;
+
+void * _PDCLIB_allocpages( int const n )
+{
+    void * oldbreak;
+    if ( membreak == NULL )
+    {
+        /* first call, make sure end-of-heap is page-aligned */
+        intptr_t unaligned = 0;
+        membreak = sbrk( 0 );
+        unaligned = _PDCLIB_PAGESIZE - (intptr_t)membreak % _PDCLIB_PAGESIZE;
+        if ( unaligned < _PDCLIB_PAGESIZE )
+        {
+            /* end-of-heap not page-aligned - adjust */
+            if ( sbrk( unaligned ) != membreak )
+            {
+                /* error */
+                return NULL;
+            }
+            membreak = (char *)membreak + unaligned;
+        }
+    }
+    /* increasing or decreasing heap - standard operation */
+    oldbreak = membreak;
+    membreak = (void *)( (char *)membreak + ( n * _PDCLIB_PAGESIZE ) );
+
+    if ( sbrk( (char*)membreak - (char*)oldbreak ) == membreak )
+    {
+        /* successful */
+        return oldbreak;
+    }
+    else
+    {
+        /* out of memory */
+        membreak = oldbreak;
+        return NULL;
+    }
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+#ifndef REGTEST
+    char * startbreak = sbrk( 0 );
+    TESTCASE( _PDCLIB_allocpages( 0 ) );
+    TESTCASE( ( (char *)sbrk( 0 ) - startbreak ) <= _PDCLIB_PAGESIZE );
+    startbreak = sbrk( 0 );
+    TESTCASE( _PDCLIB_allocpages( 1 ) );
+    TESTCASE( sbrk( 0 ) == startbreak + ( 1 * _PDCLIB_PAGESIZE ) );
+    TESTCASE( _PDCLIB_allocpages( 5 ) );
+    TESTCASE( sbrk( 0 ) == startbreak + ( 6 * _PDCLIB_PAGESIZE ) );
+    TESTCASE( _PDCLIB_allocpages( -3 ) );
+    TESTCASE( sbrk( 0 ) == startbreak + ( 3 * _PDCLIB_PAGESIZE ) );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_close.c b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_close.c
new file mode 100644 (file)
index 0000000..3737ab6
--- /dev/null
@@ -0,0 +1,34 @@
+/* _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 <syscalls.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+int _PDCLIB_close( int fd )
+{
+    return close( fd );
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* No testdriver; tested in driver for _PDCLIB_open(). */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_fillbuffer.c b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_fillbuffer.c
new file mode 100644 (file)
index 0000000..c13f9ba
--- /dev/null
@@ -0,0 +1,79 @@
+/* _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 <syscalls.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+//#include </usr/include/errno.h>
+
+//typedef long ssize_t;
+//extern ssize_t read( int fd, void * buf, size_t count );
+
+int _PDCLIB_fillbuffer( struct _PDCLIB_file_t * stream )
+{
+    /* No need to handle buffers > INT_MAX, as PDCLib doesn't allow them */
+    //ssize_t rc = read( stream->handle, stream->buffer, stream->bufsize );
+    int rc = read(stream->handle, stream->bufsize, stream->buffer);
+    if ( rc > 0 )
+    {
+        /* Reading successful. */
+        if ( ! ( stream->status & _PDCLIB_FBIN ) )
+        {
+            /* TODO: Text stream conversion here */
+        }
+        stream->pos.offset += rc;
+        stream->bufend = rc;
+        stream->bufidx = 0;
+        return 0;
+    }
+    if ( rc < 0 )
+    {
+        /* Reading error */
+        //switch ( errno )
+        //{
+            /* See comments on implementation-defined errno values in
+               <_PDCLIB_config.h>.
+            */
+        //    case EBADF:
+        //    case EFAULT:
+        //    case EINTR:
+        //    case EINVAL:
+        //    case EIO:
+        //        _PDCLIB_errno = _PDCLIB_ERROR;
+        //        break;
+        //    default:
+                /* This should be something like EUNKNOWN. */
+                _PDCLIB_errno = _PDCLIB_ERROR;
+        //        break;
+        //}
+        stream->status |= _PDCLIB_ERRORFLAG;
+        return EOF;
+    }
+    /* End-of-File */
+    stream->status |= _PDCLIB_EOFFLAG;
+    return EOF;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_flushbuffer.c b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_flushbuffer.c
new file mode 100644 (file)
index 0000000..d3b8842
--- /dev/null
@@ -0,0 +1,110 @@
+/* _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 <syscalls.h>
+#include <string.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+//#include </usr/include/errno.h>
+
+//typedef long ssize_t;
+//extern ssize_t write( int fd, const void * buf, size_t count );
+
+/* The number of attempts to complete an output buffer flushing before giving
+ *    up.
+ *    */
+#define _PDCLIB_IO_RETRIES 1
+
+/* What the system should do after an I/O operation did not succeed, before   */
+/* trying again. (Empty by default.)                                          */
+#define _PDCLIB_IO_RETRY_OP( stream )
+
+int _PDCLIB_flushbuffer( struct _PDCLIB_file_t * stream )
+{
+    /* No need to handle buffers > INT_MAX, as PDCLib doesn't allow them */
+    _PDCLIB_size_t written = 0;
+    int rc;
+    unsigned int retries;
+    if ( ! ( stream->status & _PDCLIB_FBIN ) )
+    {
+        /* TODO: Text stream conversion here */
+    }
+    /* Keep trying to write data until everything is written, an error
+       occurs, or the configured number of retries is exceeded.
+    */
+    for ( retries = _PDCLIB_IO_RETRIES; retries > 0; --retries )
+    {
+        rc = (int)write(stream->handle, stream->bufidx - written, stream->buffer + written);
+        if ( rc < 0 )
+        {
+            /* Write error */
+            //switch ( errno )
+            //{
+                /* See <_PDCLIB_config.h>. There should be differenciated errno
+                   handling here, possibly even a 1:1 mapping; but that is up
+                   to the individual platform.
+                */
+            //    case EBADF:
+            //    case EFAULT:
+            //    case EFBIG:
+            //    case EINTR:
+            //    case EINVAL:
+            //    case EIO:
+            //    case ENOSPC:
+            //    case EPIPE:
+            //        _PDCLIB_errno = _PDCLIB_ERROR;
+            //        break;
+            //    default:
+                    /* This should be something like EUNKNOWN. */
+                    _PDCLIB_errno = _PDCLIB_ERROR;
+            //        break;
+            //}
+            stream->status |= _PDCLIB_ERRORFLAG;
+            /* Move unwritten remains to begin of buffer. */
+            stream->bufidx -= written;
+            memmove( stream->buffer, stream->buffer + written, stream->bufidx );
+            return EOF;
+        }
+        written += (_PDCLIB_size_t)rc;
+        stream->pos.offset += rc;
+        if ( written == stream->bufidx )
+        {
+            /* Buffer written completely. */
+            stream->bufidx = 0;
+            return 0;
+        }
+    }
+    /* Number of retries exceeded. You probably want a different errno value
+       here.
+    */
+    _PDCLIB_errno = _PDCLIB_ERROR;
+    stream->status |= _PDCLIB_ERRORFLAG;
+    /* Move unwritten remains to begin of buffer. */
+    stream->bufidx -= written;
+    memmove( stream->buffer, stream->buffer + written, stream->bufidx );
+    return EOF;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_open.c b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_open.c
new file mode 100644 (file)
index 0000000..fb4f769
--- /dev/null
@@ -0,0 +1,158 @@
+/* _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 <syscalls.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+//#include <sys/types.h>
+//#include <sys/stat.h>
+//#include <fcntl.h>
+//#include <unistd.h>
+
+//#include "/usr/include/errno.h"
+
+int _PDCLIB_open( const char * const filename, unsigned int mode )
+{
+    /* This is an example implementation of _PDCLIB_open() fit for use with
+       POSIX kernels.
+    */
+    uint32_t flags;
+    int rc;
+    switch ( mode & ( _PDCLIB_FREAD | _PDCLIB_FWRITE | _PDCLIB_FAPPEND | _PDCLIB_FRW ) )
+    {
+        case _PDCLIB_FREAD: /* "r" */
+            flags = VFS_FILE_READ;
+            break;
+        case _PDCLIB_FWRITE: /* "w" */
+           flags = VFS_FILE_WRITE;
+            break;
+        case _PDCLIB_FAPPEND: /* "a" */
+           flags = VFS_FILE_WRITE;
+            break;
+        case _PDCLIB_FREAD | _PDCLIB_FRW: /* "r+" */
+        case _PDCLIB_FWRITE | _PDCLIB_FRW: /* "w+" */
+        case _PDCLIB_FAPPEND | _PDCLIB_FRW: /* "a+" */
+           flags = VFS_FILE_READ | VFS_FILE_WRITE;
+            break;
+        default: /* Invalid mode */
+            return -1;
+    }
+
+    rc = open(filename, flags);
+
+    if ( rc == -1 )
+    {
+        //switch ( errno )
+        //{
+            /* See the comments on implementation-defined errno values in
+               <_PDCLIB_config.h>.
+            */
+        //    case EACCES:
+        //    case EFAULT:
+        //    case EINTR:
+        //    case EISDIR:
+        //    case ELOOP:
+        //    case EMFILE:
+        //    case ENAMETOOLONG:
+        //    case ENFILE:
+        //    case ENODEV:
+        //    case ENOENT:
+        //    case ENOMEM:
+        //    case ENOSPC:
+        //    case ENOTDIR:
+        //    case EOVERFLOW:
+        //    case EROFS:
+        //    case ETXTBSY:
+        //        _PDCLIB_errno = _PDCLIB_ERROR;
+        //        break;
+        //    default:
+                /* This should be something like EUNKNOWN. */
+                _PDCLIB_errno = _PDCLIB_ERROR;
+        //        break;
+        //}
+    }
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+int main( void )
+{
+#ifndef REGTEST
+    /* This testdriver assumes POSIX, i.e. _PDCLIB_fd_t being int and being
+       incremented by one on each successful open.
+    */
+    int fh;
+    char buffer[ 10 ];
+    remove( testfile );
+    /* Trying to read non-existent file. */
+    TESTCASE( _PDCLIB_open( testfile, _PDCLIB_FREAD ) == _PDCLIB_NOHANDLE );
+    /* Writing to file, trying to read from it. */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FWRITE ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( write( fh, "test", 4 ) == 4 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( read( fh, buffer, 4 ) == -1 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Reading from file, trying to write to it. */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FREAD ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( write( fh, "test", 4 ) == -1 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Appending to file, trying to read from it. */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FAPPEND ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( write( fh, "app", 3 ) == 3 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( read( fh, buffer, 10 ) == -1 );
+    TESTCASE( write( fh, "end", 3 ) == 3 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Reading and writing from file ("r+"). */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FREAD | _PDCLIB_FRW ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( read( fh, buffer, 10 ) == 10 );
+    TESTCASE( memcmp( buffer, "testappend", 10 ) == 0 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( write( fh, "wedo", 4 ) == 4 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( read( fh, buffer, 10 ) == 10 );
+    TESTCASE( memcmp( buffer, "wedoappend", 10 ) == 0 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Writing and reading from file ("w+"). */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FWRITE | _PDCLIB_FRW ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( write( fh, "test", 4 ) == 4 );
+    TESTCASE( lseek( fh, 1, SEEK_SET ) == 1 );
+    TESTCASE( read( fh, buffer, 2 ) == 2 );
+    TESTCASE( memcmp( buffer, "es", 2 ) == 0 );
+    TESTCASE( write( fh, "sie", 3 ) == 3 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( read( fh, buffer, 6 ) == 6 );
+    TESTCASE( memcmp( buffer, "tessie", 6 ) == 0 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Appending and reading from file ("a+"). */
+    TESTCASE( ( fh = _PDCLIB_open( testfile, _PDCLIB_FAPPEND | _PDCLIB_FRW ) ) != _PDCLIB_NOHANDLE );
+    TESTCASE( write( fh, "baby", 4 ) == 4 );
+    TESTCASE( lseek( fh, 0, SEEK_SET ) == 0 );
+    TESTCASE( read( fh, buffer, 10 ) == 10 );
+    TESTCASE( memcmp( buffer, "tessiebaby", 10 ) == 0 );
+    TESTCASE( _PDCLIB_close( fh ) == 0 );
+    /* Cleaning up. */
+    TESTCASE( remove( testfile ) == 0 );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_rename.c b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_rename.c
new file mode 100644 (file)
index 0000000..c9f54b0
--- /dev/null
@@ -0,0 +1,144 @@
+/* _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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+//#include </usr/include/errno.h>
+
+//extern int unlink( const char * pathname );
+//extern int link( const char * old, const char * new );
+
+int _PDCLIB_rename( const char * old, const char * new )
+{
+    /* Note that the behaviour if new file exists is implementation-defined.
+       There is nothing wrong with either overwriting it or failing the
+       operation, but you might want to document whichever you chose.
+       This example fails if new file exists.
+    */
+//    if ( link( old, new ) == 0 )
+//    {
+//        if ( unlink( old ) == EOF )
+//        {
+//            switch ( errno )
+//            {
+//                /* See the comments on implementation-defined errno values in
+//                   <_PDCLIB_config.h>.
+//                */
+//                case EACCES:
+//                case EFAULT:
+//                case EIO:
+//                case EISDIR:
+//                case ELOOP:
+//                case ENAMETOOLONG:
+//                case ENOENT:
+//                case ENOMEM:
+//                case ENOTDIR:
+//                case EPERM:
+//                case EROFS:
+//                    _PDCLIB_errno = _PDCLIB_ERROR;
+//                    break;
+//                default:
+//                    /* This should be something like EUNKNOWN. */
+                    _PDCLIB_errno = _PDCLIB_ERROR;
+//                    break;
+//            }
+            return -1;
+//        }
+//        else
+//        {
+//            return 0;
+//        }
+//    }
+//    else
+//    {
+//        switch ( errno )
+//        {
+//            /* See the comments on implementation-defined errno values in
+//               <_PDCLIB_config.h>.
+//            */
+//            case EACCES:
+//            case EEXIST:
+//            case EFAULT:
+//            case EIO:
+//            case ELOOP:
+//            case EMLINK:
+//            case ENAMETOOLONG:
+//            case ENOENT:
+//            case ENOMEM:
+//            case ENOSPC:
+//            case ENOTDIR:
+//            case EPERM:
+//            case EROFS:
+//            case EXDEV:
+//                _PDCLIB_errno = _PDCLIB_ERROR;
+//                break;
+//            default:
+//                /* This should be something like EUNKNOWN. */
+//                _PDCLIB_errno = _PDCLIB_ERROR;
+//                break;
+//        }
+//        return EOF;
+//    }
+}
+
+#endif
+
+#ifdef TEST
+#include "_PDCLIB_test.h"
+
+#include <stdlib.h>
+
+int main( void )
+{
+#ifndef REGTEST
+    FILE * file;
+    remove( testfile1 );
+    remove( testfile2 );
+    /* check that neither file exists */
+    TESTCASE( fopen( testfile1, "r" ) == NULL );
+    TESTCASE( fopen( testfile2, "r" ) == NULL );
+    /* rename file 1 to file 2 - expected to fail */
+    TESTCASE( _PDCLIB_rename( testfile1, testfile2 ) == -1 );
+    /* create file 1 */
+    TESTCASE( ( file = fopen( testfile1, "w" ) ) != NULL );
+    TESTCASE( fputc( 'x', file ) == 'x' );
+    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( _PDCLIB_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( fputc( 'x', file ) == 'x' );
+    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.
+    */
+    TESTCASE( _PDCLIB_rename( testfile1, testfile2 ) == -1 );
+    /* remove both files */
+    remove( testfile1 );
+    remove( testfile2 );
+#endif
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_seek.c b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_seek.c
new file mode 100644 (file)
index 0000000..a7935fa
--- /dev/null
@@ -0,0 +1,82 @@
+/* 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 <syscalls.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+//#include "/usr/include/errno.h"
+
+//extern _PDCLIB_int64_t lseek64( int fd, _PDCLIB_int64_t offset, int whence );
+//extern long lseek( int fd, long offset, int whence );
+
+_PDCLIB_int64_t _PDCLIB_seek( struct _PDCLIB_file_t * stream, _PDCLIB_int64_t offset, int whence )
+{
+//    _PDCLIB_int64_t rc;
+//    switch ( whence )
+//    {
+//        case SEEK_SET:
+//        case SEEK_CUR:
+//        case SEEK_END:
+//            /* EMPTY - OK */
+//            break;
+//        default:
+//            /* See comments on implementation-defined errno values in
+//               <_PDCLIB_config.h>.
+//            */
+            _PDCLIB_errno = _PDCLIB_ERROR;
+            return EOF;
+//            break;
+//    }
+//#ifdef __CYGWIN__
+//    rc = lseek( stream->handle, offset, whence );
+//#else
+//    rc = lseek64( stream->handle, offset, whence );
+//#endif
+//    if ( rc != EOF )
+//    {
+//        stream->ungetidx = 0;
+//        stream->bufidx = 0;
+//        stream->bufend = 0;
+//        stream->pos.offset = rc;
+//        return rc;
+//    }
+//    switch ( errno )
+//    {
+//        case EBADF:
+//        case EFAULT:
+//            /* See comments on implementation-defined errno values in
+//               <_PDCLIB_config.h>.
+//            */
+//            _PDCLIB_errno = _PDCLIB_ERROR;
+//            break;
+//        default:
+//            /* This should be something like EUNKNOWN. */
+//            _PDCLIB_errno = _PDCLIB_ERROR;
+//            break;
+//    }
+//    return EOF;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_stdinit.c b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_stdinit.c
new file mode 100644 (file)
index 0000000..52b0651
--- /dev/null
@@ -0,0 +1,430 @@
+/* _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 <stdio.h>
+#include <locale.h>
+#include <limits.h>
+
+#ifndef REGTEST
+
+/* 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 <ctype.h> 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"
+    }
+};
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by several other testdrivers using stdin / stdout /
+       stderr.
+    */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/signal/raise.c b/src/pdclib/platform/stmos/functions/signal/raise.c
new file mode 100644 (file)
index 0000000..59ccc9f
--- /dev/null
@@ -0,0 +1,114 @@
+/* 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 <signal.h>
+
+#ifndef REGTEST
+
+#include <stdio.h>
+#include <stdlib.h>
+
+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 );
+    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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <stdlib.h>
+
+static volatile sig_atomic_t flag = 0;
+
+static int expected_signal = 0;
+
+static void test_handler( int sig )
+{
+    TESTCASE( sig == expected_signal );
+    flag = 1;
+}
+
+int main( void )
+{
+    /* 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 );
+    /* 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 );
+    /* 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 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/signal/signal.c b/src/pdclib/platform/stmos/functions/signal/signal.c
new file mode 100644 (file)
index 0000000..e6775e7
--- /dev/null
@@ -0,0 +1,75 @@
+/* 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 <signal.h>
+
+#ifndef REGTEST
+
+#include <stdlib.h>
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by raise.c */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/stdio/remove.c b/src/pdclib/platform/stmos/functions/stdio/remove.c
new file mode 100644 (file)
index 0000000..aca3eaf
--- /dev/null
@@ -0,0 +1,75 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include <string.h>
+
+#include "/usr/include/errno.h"
+
+extern struct _PDCLIB_file_t * _PDCLIB_filelist;
+
+extern int unlink( const char * pathname );
+
+int remove( const char * pathname )
+{
+    int rc;
+    struct _PDCLIB_file_t * current = _PDCLIB_filelist;
+    while ( current != NULL )
+    {
+        if ( ( current->filename != NULL ) && ( strcmp( current->filename, pathname ) == 0 ) )
+        {
+            return EOF;
+        }
+        current = current->next;
+    }
+    if ( ( rc = unlink( pathname ) ) == -1 )
+    {
+        switch ( errno )
+        {
+            /* See the comments on implementation-defined errno values in
+               <_PDCLIB_config.h>.
+            */
+            case EACCES:
+            case EFAULT:
+            case EIO:
+            case EISDIR:
+            case ELOOP:
+            case ENAMETOOLONG:
+            case ENOENT:
+            case ENOMEM:
+            case ENOTDIR:
+            case EPERM:
+            case EROFS:
+                _PDCLIB_errno = _PDCLIB_ERROR;
+                break;
+            default:
+                /* This should be something like EUNKNOWN. */
+                _PDCLIB_errno = _PDCLIB_ERROR;
+                break;
+        }
+    }
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    /* Testing covered by ftell.c (and several others) */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/stdio/tmpfile.c b/src/pdclib/platform/stmos/functions/stdio/tmpfile.c
new file mode 100644 (file)
index 0000000..585a61d
--- /dev/null
@@ -0,0 +1,114 @@
+/* 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 <stdio.h>
+
+#ifndef REGTEST
+
+#include "pdclib/_PDCLIB_glue.h"
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+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 )
+{
+    FILE * rc;
+    /* This is the chosen way to get high-quality randomness. Replace as
+       appropriate.
+    */
+    FILE * randomsource = fopen( "/proc/sys/kernel/random/uuid", "rb" );
+    char filename[ L_tmpnam ];
+    _PDCLIB_fd_t fd;
+    if ( randomsource == NULL )
+    {
+        return NULL;
+    }
+    for ( ;; )
+    {
+        /* Get a filename candidate. What constitutes a valid filename and
+           where temporary files are usually located is platform-dependent,
+           which is one reason why this function is located in the platform
+           overlay. The other reason is that a *good* implementation should
+           use high-quality randomness instead of a pseudo-random sequence to
+           generate the filename candidate, which is *also* platform-dependent.
+        */
+        unsigned int random;
+        fscanf( randomsource, "%u", &random );
+        sprintf( filename, "/tmp/%u.tmp", random );
+        /* Check if file of this name exists. Note that fopen() is a very weak
+           check, which does not take e.g. access permissions into account
+           (file might exist but not readable). Replace with something more
+           appropriate.
+        */
+        fd = open( filename, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR );
+        if ( fd != -1 )
+        {
+            break;
+        }
+        close( fd );
+    }
+    fclose( randomsource );
+    /* See fopen(). */
+    if ( ( rc = calloc( 1, sizeof( struct _PDCLIB_file_t ) + _PDCLIB_UNGETCBUFSIZE + L_tmpnam + BUFSIZ ) ) == NULL )
+    {
+        /* No memory to set up FILE structure */
+        close( fd );
+        return NULL;
+    }
+    rc->status = _PDCLIB_filemode( "wb+" ) | _IOLBF | _PDCLIB_DELONCLOSE;
+    rc->handle = fd;
+    rc->ungetbuf = (unsigned char *)rc + sizeof( struct _PDCLIB_file_t );
+    rc->filename = (char *)rc->ungetbuf + _PDCLIB_UNGETCBUFSIZE;
+    rc->buffer   = rc->filename + L_tmpnam;
+    strcpy( rc->filename, filename );
+    rc->bufsize = BUFSIZ;
+    rc->bufidx = 0;
+    rc->ungetidx = 0;
+    rc->next = _PDCLIB_filelist;
+    _PDCLIB_filelist = rc;
+    return rc;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#include <string.h>
+
+int main( void )
+{
+    FILE * fh;
+#ifndef REGTEST
+    char filename[ L_tmpnam ];
+    FILE * fhtest;
+#endif
+    TESTCASE( ( fh = tmpfile() ) != NULL );
+    TESTCASE( fputc( 'x', fh ) == 'x' );
+    /* Checking that file is actually there */
+    TESTCASE_NOREG( strcpy( filename, fh->filename ) == filename );
+    TESTCASE_NOREG( ( fhtest = fopen( filename, "r" ) ) != NULL );
+    TESTCASE_NOREG( fclose( fhtest ) == 0 );
+    /* Closing tmpfile */
+    TESTCASE( fclose( fh ) == 0 );
+    /* Checking that file was deleted */
+    TESTCASE_NOREG( fopen( filename, "r" ) == NULL );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/stdlib/getenv.c b/src/pdclib/platform/stmos/functions/stdlib/getenv.c
new file mode 100644 (file)
index 0000000..72bbcd2
--- /dev/null
@@ -0,0 +1,45 @@
+/* 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.
+*/
+
+/* This is an example implementation of getenv() fit for use with POSIX kernels.
+*/
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef REGTEST
+
+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;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( strcmp( getenv( "SHELL" ), "/bin/bash" ) == 0 );
+    /* TESTCASE( strcmp( getenv( "SHELL" ), "/bin/sh" ) == 0 ); */
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/stdlib/system.c b/src/pdclib/platform/stmos/functions/stdlib/system.c
new file mode 100644 (file)
index 0000000..15603c3
--- /dev/null
@@ -0,0 +1,57 @@
+/* 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 <stdlib.h>
+
+/* 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;
+}
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+#define SHELLCOMMAND "echo 'SUCCESS testing system()'"
+
+int main( void )
+{
+    FILE * fh;
+    char buffer[25];
+    buffer[24] = 'x';
+    TESTCASE( ( fh = freopen( testfile, "wb+", stdout ) ) != NULL );
+    TESTCASE( system( SHELLCOMMAND ) );
+    rewind( fh );
+    TESTCASE( fread( buffer, 1, 24, fh ) == 24 );
+    TESTCASE( memcmp( buffer, "SUCCESS testing system()", 24 ) == 0 );
+    TESTCASE( buffer[24] == 'x' );
+    TESTCASE( fclose( fh ) == 0 );
+    TESTCASE( remove( testfile ) == 0 );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/time/clock.c b/src/pdclib/platform/stmos/functions/time/clock.c
new file mode 100644 (file)
index 0000000..825e040
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+#include <sys/times.h>
+
+clock_t clock( void )
+{
+    struct tms buf;
+    if ( times( &buf ) != (clock_t)-1 )
+    {
+        return buf.tms_utime + buf.tms_stime;
+    }
+    return -1;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/time/time.c b/src/pdclib/platform/stmos/functions/time/time.c
new file mode 100644 (file)
index 0000000..cbb29e1
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+#include <sys/time.h>
+
+/* See comments in time.h on the semantics of time_t. */
+
+time_t time( time_t * timer )
+{
+    struct timeval tv;
+    if ( gettimeofday( &tv, NULL ) == 0 )
+    {
+        if ( timer != NULL )
+        {
+            *timer = tv.tv_sec;
+        }
+        return tv.tv_sec;
+    }
+    return -1;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/functions/time/timespec_get.c b/src/pdclib/platform/stmos/functions/time/timespec_get.c
new file mode 100644 (file)
index 0000000..d8cbab7
--- /dev/null
@@ -0,0 +1,42 @@
+/* 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 <time.h>
+
+#ifndef REGTEST
+
+#include <sys/time.h>
+
+int timespec_get( struct timespec * ts, int base )
+{
+    if ( base == TIME_UTC )
+    {
+        /* We can make do with a really thin wrapper here. */
+        struct timeval tv;
+        if ( gettimeofday( &tv, NULL ) == 0 )
+        {
+            ts->tv_sec = tv.tv_sec;
+            ts->tv_nsec = tv.tv_usec * 1000;
+            return base;
+        }
+    }
+    /* Not supporting any other time base than TIME_UTC for now. */
+    return 0;
+}
+
+#endif
+
+#ifdef TEST
+
+#include "_PDCLIB_test.h"
+
+int main( void )
+{
+    TESTCASE( NO_TESTDRIVER );
+    return TEST_RESULTS;
+}
+
+#endif
diff --git a/src/pdclib/platform/stmos/include/float.h b/src/pdclib/platform/stmos/include/float.h
new file mode 100644 (file)
index 0000000..538d69e
--- /dev/null
@@ -0,0 +1,75 @@
+/* Characteristics of floating types <float.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_FLOAT_H
+#define _PDCLIB_FLOAT_H _PDCLIB_FLOAT_H
+
+#include "pdclib/_PDCLIB_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__
+
+#endif
diff --git a/src/pdclib/platform/stmos/include/pdclib/_PDCLIB_config.h b/src/pdclib/platform/stmos/include/pdclib/_PDCLIB_config.h
new file mode 100644 (file)
index 0000000..9731f86
--- /dev/null
@@ -0,0 +1,426 @@
+/* Internal PDCLib configuration <_PDCLIB_config.h>
+   (Generic Template)
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_CONFIG_H
+#define _PDCLIB_CONFIG_H _PDCLIB_CONFIG_H
+
+/* -------------------------------------------------------------------------- */
+/* 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 <stdlib.h> 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 );
+
+/* Define this to some compiler directive that can be written after the       */
+/* parameter list of a function declaration to indicate the function does     */
+/* never return. If your compiler does not support such a directive, define   */
+/* to nothing. (This is to avoid warnings with the exit functions under GCC.) */
+#define _PDCLIB_NORETURN __attribute__(( noreturn ))
+
+/* -------------------------------------------------------------------------- */
+/* 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
+
+/* <stdlib.h> 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;
+};
+
+/* -------------------------------------------------------------------------- */
+/* <stdint.h> defines a set of integer types that are of a minimum width, and */
+/* "usually fastest" on the system. (If, for example, accessing a single char */
+/* requires the CPU to access a complete int and then mask out the char, the  */
+/* "usually fastest" type of at least 8 bits would be int, not char.)         */
+/* If you do not have information on the relative performance of the types,   */
+/* the standard allows you to define any type that meets minimum width and    */
+/* signedness requirements.                                                   */
+/* The defines below are just configuration for the real typedefs and limit   */
+/* definitions done in <_PDCLIB_int.h>. The uppercase define shall be either  */
+/* SHRT, INT, LONG, or LLONG (telling which values to use for the *_MIN and   */
+/* *_MAX limits); the lowercase define either short, int, long, or long long  */
+/* (telling the actual type to use).                                          */
+/* The third define is the length modifier used for the type in printf() and  */
+/* scanf() functions (used in <inttypes.h>).                                  */
+/* If you require a non-standard datatype to define the "usually fastest"     */
+/* types, PDCLib as-is doesn't support that. Please contact the author with   */
+/* details on your platform in that case, so support can be added.            */
+/* -------------------------------------------------------------------------- */
+
+#define _PDCLIB_FAST8 INT
+#define _PDCLIB_fast8 int
+#define _PDCLIB_FAST8_CONV
+
+#define _PDCLIB_FAST16 INT
+#define _PDCLIB_fast16 int
+#define _PDCLIB_FAST16_CONV
+
+#define _PDCLIB_FAST32 INT
+#define _PDCLIB_fast32 int
+#define _PDCLIB_FAST32_CONV
+
+#define _PDCLIB_FAST64 LONG
+#define _PDCLIB_fast64 long
+#define _PDCLIB_FAST64_CONV l
+
+/* -------------------------------------------------------------------------- */
+/* What follows are a couple of "special" typedefs and their limits. Again,   */
+/* the actual definition of the limits is done in <_PDCLIB_int.h>, and the    */
+/* defines here are merely "configuration". See above for details.            */
+/* -------------------------------------------------------------------------- */
+
+/* The result type of substracting two pointers */
+#define _PDCLIB_ptrdiff long
+#define _PDCLIB_PTRDIFF LONG
+#define _PDCLIB_PTR_CONV l
+
+/* An integer type that can be accessed as atomic entity (think asynchronous
+   interrupts). The type itself is not defined in a freestanding environment,
+   but its limits are. (Don't ask.)
+*/
+#define _PDCLIB_sig_atomic int
+#define _PDCLIB_SIG_ATOMIC INT
+
+/* Result type of the 'sizeof' operator (must be unsigned) */
+#define _PDCLIB_size unsigned long
+#define _PDCLIB_SIZE ULONG
+
+/* Large enough an integer to hold all character codes of the largest supported
+   locale.
+*/
+#define _PDCLIB_wchar unsigned int
+#define _PDCLIB_WCHAR UINT
+
+/* Large enough an integer to hold all character codes of the largest supported
+   locale plus WEOF (which needs not to be equal to EOF, nor needs to be of
+   negative value).
+*/
+#define _PDCLIB_wint unsigned int
+#define _PDCLIB_WINT UINT
+
+/* (Signed) integer type capable of taking the (cast) value of a void *, and
+   having the value cast back to void *, comparing equal to the original.
+*/
+#define _PDCLIB_intptr long
+#define _PDCLIB_INTPTR LONG
+
+/* Largest supported integer type. Implementation note: see _PDCLIB_atomax(). */
+#define _PDCLIB_intmax long long int
+#define _PDCLIB_INTMAX LLONG
+#define _PDCLIB_MAX_CONV ll
+/* You are also required to state the literal suffix for the intmax type      */
+#define _PDCLIB_INTMAX_LITERAL ll
+
+/* <inttypes.h> defines imaxdiv(), which is equivalent to the div() function  */
+/* family (see further above) with intmax_t as basis.                         */
+
+struct _PDCLIB_imaxdiv_t
+{
+    _PDCLIB_intmax quot;
+    _PDCLIB_intmax rem;
+};
+
+/* -------------------------------------------------------------------------- */
+/* Time types                                                                 */
+/* -------------------------------------------------------------------------- */
+
+/* See <time.h> 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 (<stdarg.h>)
+   The macros defined by <stdarg.h> 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 <stdarg.h>. */
+#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 _PDCLIB_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 _PDCLIB_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 <errno.h> will "see" EPERM instead of EISDIR (the _PDCLIB_*
+   prefix removed by <errno.h> 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
+
+/* 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 <errno.h> 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
+
+
+#endif
diff --git a/src/pdclib/platform/stmos/include/signal.h b/src/pdclib/platform/stmos/include/signal.h
new file mode 100644 (file)
index 0000000..c5f6f28
--- /dev/null
@@ -0,0 +1,84 @@
+/* Signal handling <string.h>
+
+   This file is part of the Public Domain C Library (PDCLib).
+   Permission is granted to use, modify, and / or redistribute at will.
+*/
+
+#ifndef _PDCLIB_SIGNAL_H
+#define _PDCLIB_SIGNAL_H _PDCLIB_SIGNAL_H
+
+#include "pdclib/_PDCLIB_config.h"
+
+/* 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
+   <signal.h>.
+
+   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
+
+/* 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 (void (*)( int ))0
+#define SIG_ERR (void (*)( int ))-1
+#define SIG_IGN (void (*)( int ))1
+
+typedef _PDCLIB_sig_atomic sig_atomic_t;
+
+/* 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.
+*/
+void (*signal( int sig, void (*func)( int ) ) )( int );
+
+/* 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 );
+
+#endif
diff --git a/src/pdclib/testing/_PDCLIB_iotest.h b/src/pdclib/testing/_PDCLIB_iotest.h
new file mode 100644 (file)
index 0000000..ef4d771
--- /dev/null
@@ -0,0 +1,223 @@
+/* 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.                                           */
+/* -------------------------------------------------------------------------- */
+
+/* ...printf() tests */
+#if defined( _PDCLIB_FILEIO )
+   #define GET_RESULT \
+      rewind( target ); \
+      if ( (int)fread( result_buffer, 1, actual_rc, target ) != actual_rc ) \
+      { \
+          fprintf( stderr, "GET_RESULT failed." ); \
+      }
+   #define RESULT_MISMATCH( act, exp ) strcmp( result_buffer, exp ) != 0
+   #define RESULT_STRING( tgt ) result_buffer
+#elif defined( _PDCLIB_STRINGIO )
+   #define RESULT_MISMATCH( act, exp ) strcmp( act, exp ) != 0
+   #define GET_RESULT
+   #define RESULT_STRING( tgt ) tgt
+#endif
+
+#ifdef _PDCLIB_FILEIO
+#define PREP_RESULT_BUFFER char result_buffer[100] = { 0 }; rewind( target );
+#else
+#define PREP_RESULT_BUFFER
+#endif
+
+#define GETFMT( fmt, ... ) (fmt)
+#define PRINTF_TEST( expected_rc, expected_string, ... ) do { \
+        int actual_rc; \
+        PREP_RESULT_BUFFER \
+        actual_rc = testprintf( target, __VA_ARGS__ ); \
+        GET_RESULT \
+        if ( ( actual_rc != expected_rc ) || \
+             ( RESULT_MISMATCH( target, expected_string ) ) ) \
+        { \
+            ++TEST_RESULTS; \
+            fprintf( stderr, \
+                "FAILED: " __FILE__ " (" _PDCLIB_FILEID "), line %d\n" \
+                "        format string \"%s\"\n" \
+                "        expected %2d, \"%s\"\n" \
+                "        actual   %2d, \"%s\"\n", \
+                 __LINE__, GETFMT(__VA_ARGS__, 0), expected_rc, \
+                 expected_string, actual_rc, RESULT_STRING( target ) ); \
+        } \
+    } while ( 0 )
+
+/* ...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
+
+#define SCANF_TEST( expected_rc, input_string, ... ) do { \
+        int actual_rc; \
+        PREPARE_SOURCE( input_string ); \
+        actual_rc = testscanf( source, __VA_ARGS__ ); \
+        if ( actual_rc != expected_rc ) \
+        { \
+            ++TEST_RESULTS; \
+            fprintf( stderr, "FAILED: " __FILE__ " (" _PDCLIB_FILEID "), line %d\n        expected %2d,        actual   %2d\n", __LINE__, expected_rc, actual_rc ); \
+        } \
+    } while ( 0 )
+
+/* 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 <limits.h>. 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 INT_MAX >> 15 == 1
+
+#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 UINT_MAX >> 31 == 1
+
+#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 UINT_MAX >> 63 == 1
+
+#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 ULONG_MAX >> 31 == 1
+
+#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 ULONG_MAX >> 63 == 1
+
+#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 ULLONG_MAX >> 63 == 1
+
+#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 ULLONG_MAX >> 127 == 1
+
+#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/pdclib/testing/_PDCLIB_test.h b/src/pdclib/testing/_PDCLIB_test.h
new file mode 100644 (file)
index 0000000..65cf5b5
--- /dev/null
@@ -0,0 +1,45 @@
+/* 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                                             */
+/* -------------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <assert.h>
+
+/* Some strings used for <string.h> and <stdlib.h> 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";
+
+#define NO_TESTDRIVER 0
+
+static int TEST_RESULTS = 0;
+
+/* TESTCASE() - generic test */
+#define TESTCASE( x ) if ( x ) {} \
+                      else { TEST_RESULTS += 1; printf( "FAILED: " __FILE__ ", line %d - %s\n", __LINE__, #x ); }
+
+/* TESTCASE_NOREG() - PDCLib-only test */
+#ifndef REGTEST
+    #define TESTCASE_NOREG( x ) TESTCASE( x )
+#else
+    #define TESTCASE_NOREG( x )
+#endif
+
+/* Include printf() / scanf() test macros if required */
+
+#if defined( _PDCLIB_FILEIO ) || defined( _PDCLIB_STRINGIO )
+#include "_PDCLIB_iotest.h"
+#endif
diff --git a/src/pdclib/testing/printf_testcases.h b/src/pdclib/testing/printf_testcases.h
new file mode 100644 (file)
index 0000000..c714499
--- /dev/null
@@ -0,0 +1,434 @@
+    {
+#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 <space>) */
+    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 <space> */
+    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 <space>, - 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);
+    }
+/******************************************************************************/
diff --git a/src/pdclib/testing/scanf_testcases.h b/src/pdclib/testing/scanf_testcases.h
new file mode 100644 (file)
index 0000000..f9007d6
--- /dev/null
@@ -0,0 +1,87 @@
+{
+    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;
+    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 );
+}
diff --git a/src/sash/CHANGES b/src/sash/CHANGES
deleted file mode 100644 (file)
index c87d6d5..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-These are the major changes from version 3.7 to version 3.8:
-
-The Makefile has been updated for several distribution's standards.
-The ext2_fs include file location has been changed.
-Some compiler warnings were fixed.
-
-The -ls command has the -n option to print numeric user and group ids.
-
-The -n option might be needed in case the unsuppressable dynamic
-linking used to lookup the names fails.
-
-The -chroot, -pivot_root, and -losetup commands have been added.
-
-The exit status for commands has been implemented (such as -exit).
-Thanks to Tollef Fog Heen for the patches.
-
-
-These are the major changes from version 3.6 to version 3.7:
-
-A few bugs in the dd command have been fixed.
-
-
-These are the major changes from version 3.5 to version 3.6:
-
-The -mount and -umount commands have been modified to work for both
-Linux and BSD systems.  Thanks to Wilbern Cobb for the patches.
-
-The -e and -s options for the -mount command have been added.
-
-The -f option was added to the command line so that script files
-using sash can be executed.
diff --git a/src/sash/Makefile b/src/sash/Makefile
deleted file mode 100644 (file)
index 02a895a..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#
-# Makefile for sash
-#
-# The HAVE_GZIP definition adds the -gzip and -gunzip commands.
-# The HAVE_LINUX_ATTR definition adds the -chattr and -lsattr commands.
-# The HAVE_LINUX_CHROOT definition adds the -chroot command.
-# The HAVE_LINUX_PIVOT definition adds the -pivot_root command.
-# The HAVE_LINUX_LOSETUP definition adds the -losetup command.
-# The HAVE_LINUX_MOUNT definition makes -mount and -umount work on Linux.
-# The HAVE_BSD_MOUNT definition makes -mount and -umount work on BSD.
-# The MOUNT_TYPE definition sets the default file system type for -mount.
-#
-# Note that the linker may show warnings about 'statically linked
-# programs' requiring getpwnam, getpwuid, getgrnam and getgrgid.
-# This is unavoidable since those routines use dynamic libraries anyway.
-# Sash will still run, but if there are shared library problems then
-# the user might have to be be careful when using the -chown, -chgrp,
-# and -ls commands.
-#
-
-CC=arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb
-
-#HAVE_GZIP             = 1
-#HAVE_LINUX_ATTR       = 1
-#HAVE_LINUX_CHROOT     = 1
-#HAVE_LINUX_LOSETUP    = 1
-#HAVE_LINUX_PIVOT      = 1
-#HAVE_LINUX_MOUNT      = 1
-#HAVE_BSD_MOUNT                = 0
-MOUNT_TYPE             = '""'
-
-OPT = -O3 -ffreestanding
-
-CFLAGS = $(OPT) -Wall -Wmissing-prototypes \
-       -DMOUNT_TYPE=$(MOUNT_TYPE) #\
-       -DHAVE_GZIP=$(HAVE_GZIP) \
-       -DHAVE_LINUX_ATTR=$(HAVE_LINUX_ATTR) \
-       -DHAVE_LINUX_CHROOT=$(HAVE_LINUX_CHROOT) \
-       -DHAVE_LINUX_LOSETUP=$(HAVE_LINUX_LOSETUP) \
-       -DHAVE_LINUX_PIVOT=$(HAVE_LINUX_PIVOT) \
-       -DHAVE_LINUX_MOUNT=$(HAVE_LINUX_MOUNT) \
-       -DHAVE_BSD_MOUNT=$(HAVE_BSD_MOUNT) \
-
-#LDFLAGS = -static
-#LIBS = -lz
-
-
-DESTDIR =
-BINDIR = /bin
-MANDIR = /usr/man
-
-
-#OBJS = sash.o cmds.o cmd_dd.o cmd_ed.o cmd_grep.o cmd_ls.o cmd_tar.o \
-       cmd_gzip.o cmd_find.o cmd_file.o cmd_chattr.o cmd_ar.o utils.o
-OBJS = sash.o cmds.o cmd_ls.o utils.o
-
-
-sash:  $(OBJS)
-       $(CC) $(LDFLAGS) -o sash $(OBJS) $(LIBS)
-       arm-none-eabi-strip sash
-
-clean:
-       rm -f $(OBJS) sash
-
-install: sash
-       cp sash $(DESTDIR)/$(BINDIR)/sash
-       cp sash.1 $(DESTDIR)/$(MANDIR)/man1/sash.1
-
-$(OBJS):       sash.h
diff --git a/src/sash/README b/src/sash/README
deleted file mode 100644 (file)
index 154f1f7..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-This is release 3.8 of sash, my stand-alone shell for Linux or other systems.
-
-The purpose of this program is to make system recovery possible in
-many cases where there are missing shared libraries or executables.
-It does this by firstly being linked statically, and secondly by
-including versions of many of the standard utilities within itself.
-Read the sash.1 documentation for more details.
-
-Type "make install" to build and install the program and man page.
-
-Several options in the Makefile can be changed to build sash for
-other UNIX-like systems.  In particular, dependencies on Linux file
-systems can be removed and the mount command can be configured.
-
-David I. Bell
-dbell@tip.net.au
-8 March 2014
diff --git a/src/sash/cmd_ar.c b/src/sash/cmd_ar.c
deleted file mode 100644 (file)
index 6c6181e..0000000
+++ /dev/null
@@ -1,1019 +0,0 @@
-/*
- * Original:
- * Copyright (c) 1999 by Aaron R. Crane.
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * Modified:
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "ar" built-in command.
- * This allows extraction and listing of ar files.
- */
-
-#include <ar.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <limits.h>
-
-#include "sash.h"
-
-
-/*
- * Structure to hold information about the archive file.
- */
-typedef struct
-{
-       int     fd;             /* file reading archive from */
-       BOOL    eof;            /* end of file has been seen */
-       BOOL    rescan;         /* rescan the header just read */
-       char *  nameTable;      /* long name table */
-
-       /*
-        * Information about the current file read from the archive.
-        * This is extracted from the latest member header read.
-        */
-       char *  name;           /* current file name */
-       time_t  date;           /* date of file */
-       uid_t   uid;            /* user id */
-       gid_t   gid;            /* group id */
-       mode_t  mode;           /* file protection */
-       off_t   size;           /* file size */
-       int     pad;            /* padding to next header */
-} Archive;
-
-
-/*
- * Local procedures.
- */
-static void    initArchive(Archive * arch);
-static BOOL    openArchive(const char * name, Archive * arch);
-static void    closeArchive(Archive * arch);
-static BOOL    readSpecialMember(Archive * arch);
-static BOOL    readNormalMember(Archive * arch);
-static BOOL    readMember(Archive * arch, struct ar_hdr * hdr);
-static BOOL    skipMember(const Archive * arch);
-static BOOL    skipPadding(int fd, int pad);
-static BOOL    writeFile(const Archive * arch, int outfd);
-static int     createFile(const Archive * arch);
-static BOOL    canonicalize(Archive * arch, const struct ar_hdr * hdr);
-static void    listMember(const Archive * arch);
-static int     shortNameMatches44BSD(const char * name);
-static int     shortNameMatchesSysV(const char * name);
-
-static BOOL    wantMember(const Archive * arch, int n_names,
-                       const char ** names);
-
-static BOOL    getNumber(const char * s, unsigned base, int max_digits,
-                       unsigned long * ul);
-
-
-int
-do_ar(int argc, const char ** argv)
-{
-       const char *    options;
-       const char *    archiveName;
-       BOOL            doExtract;
-       BOOL            doTable;
-       BOOL            doPrint;
-       BOOL            verbose;
-       int             r;
-       Archive         arch;
-
-       r = 0;
-       verbose = FALSE;
-       doExtract = FALSE;
-       doTable = FALSE;
-       doPrint = FALSE;
-
-       if (argc < 3)
-       {
-               fprintf(stderr, "Too few arguments for ar\n");
-
-               return 1;
-       }
-
-       /*
-        * Get the option string and archive file name.
-        */
-       options = argv[1];
-       archiveName = argv[2];
-
-       /*
-        * Advance the arguments to the list of file names (if any).
-        */
-       argc -= 3;
-       argv += 3;
-
-       /*
-        * Parse the option characters.
-        */
-       for (; *options; options++)
-       {
-               switch (*options)
-               {
-               case 't':
-                       doTable = TRUE;
-                       break;
-
-               case 'x':
-                       doExtract = TRUE;
-                       break;
-
-               case 'p':
-                       doPrint = TRUE;
-                       break;
-
-               case 'v':
-                       verbose = TRUE;
-                       break;
-
-               case 'd': case 'm': case 'q': case 'r':
-                       fprintf(stderr, "Writing ar files is not supported\n");
-
-                       return 1;
-
-               default:
-                       fprintf(stderr, "Unknown ar flag: %c\n", *options);
-
-                       return 1;
-               }
-       }
-
-       if (doExtract + doTable + doPrint != 1)
-       {
-               fprintf(stderr,
-                       "Exactly one of 'x', 'p' or 't' must be specified\n");
-
-               return 1;
-       }
-
-       /*
-        * Open the archive file.
-        */
-       initArchive(&arch);
-
-       if (!openArchive(archiveName, &arch))
-               return 1;
-
-       /*
-        * Read the first special member of the archive.
-        */
-       if (!readSpecialMember(&arch))
-               return 1;
-
-       /*
-        * Read all of the normal members of the archive.
-        */
-       while (readNormalMember(&arch))
-       {
-               /*
-                * If this file is not wanted then skip it.
-                */
-               if (!wantMember(&arch, argc, argv))
-               {
-                       if (!skipMember(&arch))
-                               break;
-
-                       continue;
-               }
-
-               /*
-                * This file is wanted.
-                */
-               if (doTable)
-               {
-                       if (verbose)
-                               listMember(&arch);
-                       else
-                               puts(arch.name);
-
-                       if (!skipMember(&arch))
-                               break;
-               }
-               else if (doPrint)
-               {
-                       if (verbose)
-                       {
-                               /*
-                                * The verbose format makes me gag,
-                                * but 4.4BSD, GNU and even V7 all
-                                * have the same lossage.
-                                */
-                               printf("\n<%s>\n\n", arch.name);
-                               fflush(stdout);
-                       }
-
-                       if (!writeFile(&arch, STDOUT))
-                               break;
-               }
-               else if (doExtract)
-               {
-                       int     outfd;
-                       BOOL    success;
-
-                       if (verbose)
-                               printf("x - %s\n", arch.name);
-
-                       outfd = createFile(&arch);
-
-                       if (outfd == -1)
-                               break;
-
-                       success = writeFile(&arch, outfd);
-
-                       if (close(outfd) == -1)
-                       {
-                               fprintf(stderr, "Can't close %s: %s\n",
-                                       arch.name, strerror(errno));
-
-                               break;
-                       }
-
-                       if (!success)
-                       {
-                               r = 1;
-                               break;
-                       }
-               }
-               else
-               {
-                       fprintf(stderr, "Oops -- I don't know what to do\n");
-                       r = 1;
-                       break;
-               }
-       }
-
-       closeArchive(&arch);
-
-       return r;
-}
-
-
-/*
- * Open the file for writing for the specified archive structure,
- * setting its mode and owner if possible.  Returns the file handle
- * of the opened file, or -1 on an error.
- */
-static int
-createFile(const Archive * arch)
-{
-       int     fd;
-
-       fd = open(arch->name, O_WRONLY | O_CREAT | O_TRUNC, arch->mode);
-
-       if (fd == -1)
-       {
-               fprintf(stderr, "Can't create \"%s\": %s\n",
-                       arch->name, strerror(errno));
-
-               return -1;
-       }
-
-       /*
-        * Don't worry if these fail.  We have to do the fchmod() despite
-        * specifying the mode in the open() call, because that mode is
-        * munged by the umask.
-        */
-       checkStatus("fchmod", fchmod(fd, arch->mode));
-       checkStatus("fchown", fchown(fd, arch->uid, arch->gid));
-
-       return fd;
-}
-
-
-/*
- * Return whether the current archive member is wanted.
- * This means that the file name is contained in the specified list of
- * file names, or else that the specified list of file names is empty.
- */
-static BOOL
-wantMember(const Archive * arch, int n_names, const char ** name)
-{
-       int     i;
-
-       /*
-        * If there are no names then all archive members are wanted.
-        */
-       if (n_names == 0)
-               return TRUE;
-
-       /*
-        * See if the member file name is contained in the list.
-        */
-       for (i = 0; i < n_names; i++)
-       {
-               if (strcmp(arch->name, name[i]) == 0)
-                       return TRUE;
-       }
-
-       return FALSE;
-}
-
-
-/*
- * Parse a number from the specified string in the specified base.
- * The number is terminated by a null, space, or the specified number
- * of digits.  The number is returned through the supplied pointer.
- * Only non-negatives numbers are parsed.  Returns TRUE on success.
- */
-static BOOL
-getNumber(const char * s, unsigned base, int max_digits, unsigned long * ul)
-{
-       const char *    p;
-       const char *    endp;
-       unsigned long   cutoff;
-       unsigned long   cutlim;
-
-       if (base < 2 || base > 10  ||  s == 0 || *s == 0  ||  ul == 0)
-               return FALSE;
-
-       cutoff = ULONG_MAX / (unsigned long) base;
-       cutlim = ULONG_MAX % (unsigned long) base;
-       *ul = 0;
-
-       endp = (max_digits >= 0) ? s + max_digits : 0;
-
-       for (p = s;  endp ? p < endp : 1;  p++)
-       {
-               unsigned d;
-
-               if (*p == 0 || *p == ' ')
-                       break;  /* end of string */
-
-               if (!isDecimal(*p))
-                       return FALSE; /* non-digit */
-
-               d = *p - '0';
-
-               if (d >= base)
-                       return FALSE; /* digit outside range for base */
-
-               if (*ul > cutoff || (*ul == cutoff && d > cutlim))
-                       return FALSE; /* overflow */
-
-               *ul *= base;
-               *ul += d;
-       }
-
-       if (p == s)
-               return FALSE;   /* nothing was converted */
-
-       if (*p && *p != ' ')
-               return FALSE;   /* trailing garbage */
-
-       return TRUE;
-}
-
-
-/*
- * Initialise the specified Archive structure for use.
- */
-static void
-initArchive(Archive * arch)
-{
-       arch->fd = -1;
-       arch->name = 0;
-       arch->nameTable = 0;
-       arch->eof = FALSE;
-       arch->rescan = FALSE;
-}
-
-
-/*
- * Open the specified archive file name and read the header from it.
- * The file handle is saved in the Archive structure for further use.
- * Returns TRUE on success.
- */
-static BOOL
-openArchive(const char * name, Archive * arch)
-{
-       unsigned char   buf[SARMAG];
-       ssize_t         cc;
-
-       arch->fd = open(name, O_RDONLY);
-
-       if (arch->fd == -1)
-       {
-               fprintf(stderr, "Can't open archive file %s: %s\n",
-                       name, strerror(errno));
-
-               return FALSE;
-       }
-
-       cc = read(arch->fd, buf, SARMAG);
-
-       if (cc == -1)
-       {
-               fprintf(stderr, "Error reading archive header: %s\n",
-                       strerror(errno));
-
-               goto close_and_out;
-       } 
-
-       if (cc != SARMAG)
-       {
-               fprintf(stderr, "Short read of archive header\n");
-
-               goto close_and_out;
-       }
-
-       if (memcmp(buf, ARMAG, SARMAG))
-       {
-               fprintf(stderr, "Invalid archive header\n");
-
-               goto close_and_out;
-       }
-
-       return TRUE;
-
-
-       /*
-        * Here on an error to clean up.
-        */
-close_and_out:
-       (void) close(arch->fd);
-       arch->fd = -1;
-
-       return FALSE;
-}
-
-
-/*
- * Close the archive file.
- */
-static void
-closeArchive(Archive * arch)
-{
-       free(arch->name);
-       arch->name = 0;
-
-       free(arch->nameTable);
-       arch->nameTable = 0;
-
-       if (arch->fd >= 0)
-               (void) close(arch->fd);
-
-       arch->fd = -1;
-}
-
-
-/*
- * Read an archive member header into the specified structure.
- * Returns TRUE on success.  On failure, the eof flag is set if
- * the end of file had been reached.
- */
-static BOOL
-readMember(Archive * arch, struct ar_hdr * hdr)
-{
-       ssize_t cc;
-
-       cc = read(arch->fd, hdr, sizeof(*hdr));
-
-       if (cc < 0)
-       {
-               fprintf(stderr, "Error reading member header: %s\n",
-                       strerror(errno));
-
-               return FALSE;
-       }
-
-       if (cc == 0)
-       {
-               arch->eof = TRUE;
-
-               return FALSE;
-       }
-
-       if (cc != sizeof(*hdr))
-       {
-               fprintf(stderr, "Short read of member header\n");
-
-               return FALSE;
-       }
-
-       if (memcmp(hdr->ar_fmag, ARFMAG, sizeof(hdr->ar_fmag)))
-       {
-               fprintf(stderr, "Invalid member header\n");
-
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-
-/*
- * Check the member file name and see if it matches the 4.4 BSD
- * syntax for long file names.  If so, return the number of characters
- * of the actual long file name.  Returns -1 on an error.
- */
-static int
-shortNameMatches44BSD(const char * name)
-{
-       const char *    p;
-       unsigned long   ul;
-
-       if (strncmp(name, "#1/", 3) != 0)
-               return -1;
-
-       if (!isDecimal(name[3]))
-               return -1;
-
-       for (p = name + 4;  *p;  p++)
-       {
-               if (!isDecimal(*p))
-                       break;
-       }
-
-       while (*p)
-       {
-               if (*p++ != ' ')
-                       return -1;
-       }
-
-       if (!getNumber(name + 3, 10, -1, &ul))
-               return -1;
-
-       if (ul == 0)            /* broken archive */
-               return -1;
-
-       return ul;
-}
-
-
-/*
- * Check the member file name and see if it matches the SYS V syntax
- * for long file names.  If so, return the number of characters of the
- * actual long file name.  Returns -1 on an error.
- */
-static int
-shortNameMatchesSysV(const char * name)
-{
-       const char *    p;
-       unsigned long   ul;
-
-       /* "^/(\d+) *$" */
-       if (name[0] != '/')
-               return -1;
-
-       if (!isDecimal(name[1]))
-               return -1;
-
-       for (p = name + 2;  *p;  p++)
-       {
-               if (!isDecimal(*p))
-                       break;
-       }
-
-       while (*p)
-       {
-               if (*p++ != ' ')
-                       return -1;
-       }
-
-       if (!getNumber(name + 1, 10, -1, &ul))
-               return -1;
-
-       return ul;
-}
-
-
-#define MEMB_NAME_ALLOC(n)                                     \
-       do                                                      \
-       {                                                       \
-               arch->name = malloc(n);                         \
-               if (!arch->name)                                \
-               {                                               \
-                       fprintf(stderr, "Out of memory\n");     \
-                       return FALSE;                           \
-               }                                               \
-       } while (0);
-
-
-/*
- * Examine the archive structure that was read and fill in the
- * current member data with the extracted information.  This handles
- * various types of archive headers.  This can read data from the
- * archive file to obtain a long file name.  Returns TRUE on success.
- */
-static BOOL
-canonicalize(Archive * arch, const struct ar_hdr * hdr)
-{
-       char            buf[sizeof(hdr->ar_name) + 1];
-       int             n;
-       unsigned long   ul;
-       unsigned long   bsd_len;
-
-       bsd_len = 0;
-
-       free(arch->name);
-       arch->name = 0;
-
-       strncpy(buf, hdr->ar_name, sizeof(hdr->ar_name));
-       buf[sizeof(hdr->ar_name)] = 0;
-
-       /*
-        * 1. If shortname matches "^#1/(\d+) *$", then it's a 4.4BSD
-        *    longname.  Read a longname of $1 bytes from ARCH->fd, or
-        *    return FALSE if impossible.
-        */
-       if ((n = shortNameMatches44BSD(buf)) != -1)
-       {
-               /* N is the length of the longname */
-               ssize_t cc;
-
-               bsd_len = n;
-
-               MEMB_NAME_ALLOC(n + 1);
-
-               cc = read(arch->fd, arch->name, n);
-
-               if (cc == -1)
-               {
-                       fprintf(stderr, "Error reading longname: %s\n",
-                               strerror(errno));
-
-                       goto free_and_out;
-               }
-
-               if (cc != n)
-               {
-                       fprintf(stderr, "Unexpected end of file in longname\n");
-
-                       goto free_and_out;
-               }
-
-               arch->name[n] = 0;
-       }
-
-       /*
-        * 2. Otherwise, if shortname matches "^/(\d+) *$", then it's a SysV
-        *    longname.  Get the longname from the nameTable, or return FALSE
-        *    if there is none.
-        */
-       else if ((n = shortNameMatchesSysV(buf)) != -1)
-       {
-               /*
-                * N is the index of the longname
-                */
-               const char *    longname;
-               const char *    p;
-               size_t          len;
-
-               if (n >= strlen(arch->nameTable))
-               {
-                       fprintf(stderr, "Longname index too large\n");
-
-                       return FALSE;
-               }
-
-               longname = arch->nameTable + n;
-
-               p = strchr(longname, '/');
-
-               if (!p)
-               {
-                       fprintf(stderr, "Bad longname index\n");
-
-                       return FALSE;
-               }
-
-               if (p[1] != '\n')
-               {
-                       fprintf(stderr, "Malformed longname table\n");
-
-                       return FALSE;
-               }
-
-               len = p - longname;
-               MEMB_NAME_ALLOC(len + 1);
-               strncpy(arch->name, longname, len);
-               arch->name[len] = '\0';
-       }
-
-       /*
-        * 3. Otherwise, it's just a shortname.  If the shortname contains a
-        *    slash, then the name terminates before the slash; otherwise,
-        *    the name terminates at the first space, or at the end of the
-        *    field if there is none. */
-       else
-       {
-               const char *    p;
-               size_t          len;
-
-               p = strchr(buf, '/');
-
-               if (!p)
-                       p = strchr(buf, ' ');
-
-               if (p)
-                       len = p - buf;
-               else
-                       len = sizeof(hdr->ar_name);
-
-               MEMB_NAME_ALLOC(len + 1);
-               strncpy(arch->name, buf, len);
-               arch->name[len] = 0;
-       }
-
-       /*
-        * 4. Parse the remaining fields of the header.  Return FALSE if any
-        *    are missing or ill-formed.
-        */
-#define FIELD(AFIELD, MFIELD, base)                                    \
-       if (!getNumber(hdr->AFIELD, base, sizeof(hdr->AFIELD), &ul))    \
-       {                                                               \
-               fprintf(stderr, "Malformed archive member header\n");   \
-               goto free_and_out;                                      \
-       }                                                               \
-       arch->MFIELD = ul;
-
-       FIELD(ar_date, date, 10);
-       FIELD(ar_uid,  uid,  10);
-       FIELD(ar_gid,  gid,  10);
-       FIELD(ar_mode, mode,  8);
-       FIELD(ar_size, size, 10);
-#undef FIELD
-
-       /*
-        * 4a. Decide whether a pad byte will be present.u
-        *
-        * The 4.4BSD format is really broken and needs a whole pile of
-        * cruft to deal with it.  There are several cases:
-        *
-        * 1. Even namelen, even memberlen: no pad.
-        * 2. Even namelen, odd memberlen: pad.  Just like SysV.
-        * 3. Odd namelen, even memberlen: pad.  Cruft.
-        * 4. Odd namelen, odd memberlen: no pad.  Cruft.
-        *
-        * Essentially, whenever the namelen is odd, the naive determination
-        * of whether a pad is needed is reversed.
-        */
-       if (!bsd_len)
-               arch->pad = (arch->size % 2) ? 1 : 0;
-       else
-       {
-               arch->size -= bsd_len;
-               arch->pad = (arch->size % 2) ? 1 : 0;
-
-               if (bsd_len % 2)
-                       arch->pad = !arch->pad;
-       }
-
-       /*
-        * 5. Everything was successful.
-        */
-       return TRUE;
-
-
-       /*
-        * 5a. Error exit -- free memory.
-        */
-free_and_out:
-       free(arch->name);
-       arch->name = 0;
-
-       return FALSE;
-}
-
-
-/*
- * Skip the padding character if required to position to the
- * beginning of the next member header.  This is done if the
- * padding value is nonzero.  Returns TRUE on success.
- */
-static BOOL
-skipPadding(int fd, int pad)
-{
-       if (pad)
-       {
-               if (lseek(fd, 1, SEEK_CUR) == -1)
-               {
-                       fprintf(stderr, "Can't skip pad byte: %s\n",
-                               strerror(errno));
-
-                       return FALSE;
-               }
-       }
-
-       return TRUE;
-}
-
-
-/*
- * Read the first member of the archive file and check whether it
- * is a special one, and if so, handle it.  If the first member is
- * a normal archive member, then set up to rescan it for the next
- * readNormalMember call.  Returns TRUE on success.
- */
-static BOOL
-readSpecialMember(Archive * arch)
-{
-       struct ar_hdr   hdr;
-
-       /*
-        * 1. Read a header H.  Fail if impossible.
-        */
-       if (!readMember(arch, &hdr))
-               return FALSE;
-
-       /*
-        * 2. If H is a symbol table, ditch it.
-        * Fail if impossible.
-        */
-       if ((strncmp(hdr.ar_name, "/ ", 2) == 0) ||
-               (strncmp(hdr.ar_name, "__.SYMTAB       ",
-                       sizeof(hdr.ar_name)) == 0))
-       {
-               if (!canonicalize(arch, &hdr))
-                       return FALSE;
-
-               return skipMember(arch);
-       }
-
-       /*
-        * 3. If H is a SysV longname table, read it into ARCH.
-        */
-       if (strncmp(hdr.ar_name, "//", 2) == 0)
-       {
-               unsigned long   len;
-               ssize_t         cc;
-
-               if (!getNumber(hdr.ar_size, 10, sizeof(hdr.ar_size), &len))
-               {
-                       fprintf(stderr, "Invalid name-table size\n");
-
-                       return FALSE;
-               }
-
-               arch->nameTable = malloc(len + 1);
-
-               if (!arch->nameTable)
-               {
-                       fprintf(stderr, "Out of memory\n");
-
-                       return FALSE;
-               }
-
-               cc = read(arch->fd, arch->nameTable, len);
-
-               if (cc == -1)
-               {
-                       fprintf(stderr, "Error reading name-table: %s\n",
-                               strerror(errno));
-
-                       return FALSE;
-               }
-
-               if (cc != (ssize_t) len)
-               {
-                       fprintf(stderr,
-                               "Unexpected end of file in name-table\n");
-
-                       return FALSE;
-               }
-
-               arch->nameTable[len] = 0;
-
-               return skipPadding(arch->fd, len % 2);
-       }
-
-       /*
-        * 4. We read a normal header.
-        * Canonicalize it, and mark it as needing rescanning.
-        */
-       arch->rescan = TRUE;
-
-       return canonicalize(arch, &hdr);
-}
-
-
-/*
- * Read the next normal member of the archive file if possible.
- * If the member is being rescanned, clear the rescan flag and
- * return the header that was already read.  Returns TRUE on
- * success.  On a failure, the eof flag will be set if end of
- * file has been reached.
- */
-static BOOL
-readNormalMember(Archive * arch)
-{
-       struct ar_hdr   hdr;
-
-       /*
-        * If we are rereading an old header then just clear the
-        * rescan flag and return success.
-        */
-       if (arch->rescan)
-       {
-               arch->rescan = FALSE;
-
-               return TRUE;
-       }
-
-       /*
-        * We need to read a new member header.
-        */
-       if (!readMember(arch, &hdr))
-               return FALSE;
-
-       return canonicalize(arch, &hdr);
-}
-
-
-/*
- * Skip the current member of the archive so that we are positioned
- * to tbe beginning of the next member's header (or end of file).
- * Returns TRUE on success.
- */
-static BOOL
-skipMember(const Archive * arch)
-{
-       if (lseek(arch->fd, arch->size, SEEK_CUR) == -1)
-       {
-               fprintf(stderr, "Can't skip past archive member: %s\n",
-                       strerror(errno));
-
-               return FALSE;
-       }
-
-       return skipPadding(arch->fd, arch->pad);
-}
-
-
-/*
- * Copy all of the file data from the archive to the specified
- * open file.  Returns TRUE on success.
- */
-static BOOL
-writeFile(const Archive * arch, int outfd)
-{
-       char    buf[BUF_SIZE];
-       off_t   n;
-
-       n = arch->size;
-
-       while (n > 0)
-       {
-               ssize_t cc;
-
-               cc = read(arch->fd, buf, MIN(n, sizeof(buf)));
-
-               if (cc == -1)
-               {
-                       fprintf(stderr, "Error reading archive member: %s\n",
-                               strerror(errno));
-
-                       return FALSE;
-               }
-
-               if (cc == 0)
-               {
-                       fprintf(stderr, "Unexpected end of file\n");
-
-                       return FALSE;
-               }
-
-               if (fullWrite(outfd, buf, cc) < 0)
-               {
-                       fprintf(stderr, "Write error: %s\n", strerror(errno));
-
-                       return FALSE;
-               }
-
-               n -= cc;
-       }
-
-       if (!skipPadding(arch->fd, arch->pad))
-               return FALSE;
-
-       return TRUE;
-}
-
-
-/*
- * Print one line listing the information about the specified archive member.
- */
-static void
-listMember(const Archive * arch)
-{
-       printf("%s %6ld/%-6ld %8lu %s %s\n",
-              modeString(arch->mode) + 1,
-              (long) arch->uid,
-              (long) arch->gid,
-              (unsigned long) arch->size,
-              timeString(arch->date),
-              arch->name);
-}
-
-
-/* END CODE */
diff --git a/src/sash/cmd_chattr.c b/src/sash/cmd_chattr.c
deleted file mode 100644 (file)
index df50c22..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "chattr" and "lsattr" built-in commands.
- * These commands are optionally built into sash.
- * They manipulate the important ext2 file system file attribute flags.
- */
-
-#if    HAVE_LINUX_ATTR
-
-#include <sys/ioctl.h>
-#include <sys/types.h>
-
-/*
- * These were used for old linux versions.
- * #include <linux/fs.h>
- * #include <linux/ext2_fs.h>
- */
-
-#include <ext2fs/ext2_fs.h>
-
-
-#include "sash.h"
-
-
-/*
- * The chattr command.
- * This can turn on or off the immutable and append-only ext2 flags.
- */
-int
-do_chattr(int argc, const char ** argv)
-{
-       const char *    fileName;
-       const char *    options;
-       int *           flagPointer;
-       int             offFlags;
-       int             onFlags;
-       int             oldFlags;
-       int             newFlags;
-       int             fd;
-       int             r;
-
-       r = 0;
-       argc--;
-       argv++;
-
-       /*
-        * Parse the options.
-        */
-       onFlags = 0;
-       offFlags = 0;
-
-       while ((**argv == '-') || (**argv == '+'))
-       {
-               options = *argv++;
-               argc--;
-
-               /*
-                * Point at the proper flags to be modified.
-                */
-               if (*options++ == '+')
-                       flagPointer = &onFlags;
-               else
-                       flagPointer = &offFlags;
-
-               /*
-                * Parse the flag characters.
-                */
-               while (*options)
-               {
-                       switch (*options++)
-                       {
-                               case 'i':
-                                       *flagPointer |= EXT2_IMMUTABLE_FL;
-                                       break;
-
-                               case 'a':
-                                       *flagPointer |= EXT2_APPEND_FL;
-                                       break;
-
-                               default:
-                                       fprintf(stderr, "Unknown flag '%c'\n",
-                                               options[-1]);
-
-                                       return 1;
-                       }
-               }
-       }
-
-       /*
-        * Make sure that the attributes are reasonable.
-        */
-       if ((onFlags == 0) && (offFlags == 0))
-       {
-               fprintf(stderr, "No attributes specified\n");
-
-               return 1;
-       }
-
-       if ((onFlags & offFlags) != 0)
-       {
-               fprintf(stderr, "Inconsistent attributes specified\n");
-
-               return 1;
-       }
-
-       /*
-        * Make sure there are some files to affect.
-        */
-       if (argc <= 0)
-       {
-               fprintf(stderr, "No files specified for setting attributes\n");
-
-               return 1;
-       }
-
-       /*
-        * Iterate over all of the file names.
-        */
-       while (argc-- > 0)
-       {
-               fileName = *argv++;
-
-               /*
-                * Open the file name.
-                */
-               fd = open(fileName, O_RDONLY | O_NONBLOCK);
-
-               if (fd < 0)
-               {
-                       perror(fileName);
-                       r = 1;
-                       continue;
-               }
-
-               /*
-                * Read the current ext2 flag bits.
-                */
-               if (ioctl(fd, EXT2_IOC_GETFLAGS, &oldFlags) < 0)
-               {
-                       perror(fileName);
-                       r = 1;
-                       (void) close(fd);
-
-                       continue;
-               }
-
-               /*
-                * Modify the flag bits as specified.
-                */
-               newFlags = oldFlags;
-               newFlags |= onFlags;
-               newFlags &= ~offFlags;
-
-               /*
-                * If the flags aren't being changed, then close this
-                * file and continue.
-                */
-               if (newFlags == oldFlags)
-               {
-                       (void) close(fd);
-
-                       continue;
-               }
-
-               /*
-                * Set the new ext2 flag bits.
-                */
-               if (ioctl(fd, EXT2_IOC_SETFLAGS, &newFlags) < 0)
-               {
-                       perror(fileName);
-                       r = 1;
-                       (void) close(fd);
-
-                       continue;
-               }
-
-               /*
-                * Close the file.
-                */
-               (void) close(fd);
-       }
-
-       return r;
-}
-
-
-/*
- * The lsattr command.
- * This lists the immutable and append-only ext2 flags.
- */
-int
-do_lsattr(int argc, const char ** argv)
-{
-       const char *    fileName;
-       int             r;
-       int             fd;
-       int             status;
-       int             flags;
-       char            string[4];
-
-       r = 0;
-       argc--;
-       argv++;
-
-       /*
-        * Iterate over all of the file names.
-        */
-       while (argc-- > 0)
-       {
-               fileName = *argv++;
-
-               /*
-                * Open the file name.
-                */
-               fd = open(fileName, O_RDONLY | O_NONBLOCK);
-
-               if (fd < 0)
-               {
-                       perror(fileName);
-                       r = 1;
-                       continue;
-               }
-
-               /*
-                * Read the ext2 flag bits.
-                */
-               status = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
-
-               /*
-                * Close the file and check the status.
-                */
-               (void) close(fd);
-
-               if (status < 0)
-               {
-                       perror(fileName);
-                       r = 1;
-
-                       continue;
-               }
-
-               /*
-                * Build up the string according to the flags.
-                * This is 'i' for immutable, and 'a' for append only.
-                */
-               string[0] = ((flags & EXT2_IMMUTABLE_FL) ? 'i' : '-');
-               string[1] = ((flags & EXT2_APPEND_FL) ? 'a' : '-');
-               string[2] = '\0';
-
-               /*
-                * Print the flags and the file name.
-                */
-               printf("%s  %s\n", string, fileName);
-       }
-
-       return r;
-}
-
-#endif
-
-
-/* END CODE */
diff --git a/src/sash/cmd_dd.c b/src/sash/cmd_dd.c
deleted file mode 100644 (file)
index 80338b6..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "dd" built-in command.
- */
-
-#include "sash.h"
-
-
-#define        PAR_NONE        0
-#define        PAR_IF          1
-#define        PAR_OF          2
-#define        PAR_BS          3
-#define        PAR_COUNT       4
-#define        PAR_SEEK        5
-#define        PAR_SKIP        6
-
-
-typedef        struct
-{
-       const char *    name;
-       int             value;
-} PARAM;
-
-
-static const PARAM     params[] =
-{
-       {"if",          PAR_IF},
-       {"of",          PAR_OF},
-       {"bs",          PAR_BS},
-       {"count",       PAR_COUNT},
-       {"seek",        PAR_SEEK},
-       {"skip",        PAR_SKIP},
-       {NULL,          PAR_NONE}
-};
-
-
-static long    getNum(const char * cp);
-
-
-int
-do_dd(int argc, const char ** argv)
-{
-       const char *    str;
-       const PARAM *   par;
-       const char *    inFile;
-       const char *    outFile;
-       char *          cp;
-       int             inFd;
-       int             outFd;
-       int             inCc;
-       int             outCc;
-       int             blockSize;
-       long            count;
-       long            seekVal;
-       long            skipVal;
-       long            inFull;
-       long            inPartial;
-       long            outFull;
-       long            outPartial;
-       char *          buf;
-       char            localBuf[BUF_SIZE];
-       int             r;
-
-       inFile = NULL;
-       outFile = NULL;
-       seekVal = 0;
-       skipVal = 0;
-       blockSize = 512;
-       count = -1;
-       r = 0;
-
-       while (--argc > 0)
-       {
-               str = *++argv;
-               cp = strchr(str, '=');
-
-               if (cp == NULL)
-               {
-                       fprintf(stderr, "Bad dd argument\n");
-
-                       return 1;
-               }
-
-               *cp++ = '\0';
-
-               for (par = params; par->name; par++)
-               {
-                       if (strcmp(str, par->name) == 0)
-                               break;
-               }
-
-               switch (par->value)
-               {
-                       case PAR_IF:
-                               if (inFile)
-                               {
-                                       fprintf(stderr, "Multiple input files illegal\n");
-
-                                       return 1;
-                               }
-       
-                               inFile = cp;
-                               break;
-
-                       case PAR_OF:
-                               if (outFile)
-                               {
-                                       fprintf(stderr, "Multiple output files illegal\n");
-
-                                       return 1;
-                               }
-
-                               outFile = cp;
-                               break;
-
-                       case PAR_BS:
-                               blockSize = getNum(cp);
-
-                               if (blockSize <= 0)
-                               {
-                                       fprintf(stderr, "Bad block size value\n");
-
-                                       return 1;
-                               }
-
-                               break;
-
-                       case PAR_COUNT:
-                               count = getNum(cp);
-
-                               if (count < 0)
-                               {
-                                       fprintf(stderr, "Bad count value\n");
-
-                                       return 1;
-                               }
-
-                               break;
-
-                       case PAR_SEEK:
-                               seekVal = getNum(cp);
-
-                               if (seekVal < 0)
-                               {
-                                       fprintf(stderr, "Bad seek value\n");
-
-                                       return 1;
-                               }
-
-                               break;
-
-                       case PAR_SKIP:
-                               skipVal = getNum(cp);
-
-                               if (skipVal < 0)
-                               {
-                                       fprintf(stderr, "Bad skip value\n");
-
-                                       return 1;
-                               }
-
-                               break;
-
-                       default:
-                               fprintf(stderr, "Unknown dd parameter\n");
-
-                               return 1;
-               }
-       }
-
-       if (inFile == NULL)
-       {
-               fprintf(stderr, "No input file specified\n");
-
-               return 1;
-       }
-
-       if (outFile == NULL)
-       {
-               fprintf(stderr, "No output file specified\n");
-
-               return 1;
-       }
-
-       buf = localBuf;
-
-       if (blockSize > sizeof(localBuf))
-       {
-               buf = malloc(blockSize);
-
-               if (buf == NULL)
-               {
-                       fprintf(stderr, "Cannot allocate buffer\n");
-
-                       return 1;
-               }
-       }
-
-       inFull = 0;
-       inPartial = 0;
-       outFull = 0;
-       outPartial = 0;
-
-       inFd = open(inFile, 0);
-
-       if (inFd < 0)
-       {
-               perror(inFile);
-
-               if (buf != localBuf)
-                       free(buf);
-
-               return 1;
-       }
-
-       outFd = creat(outFile, 0666);
-
-       if (outFd < 0)
-       {
-               perror(outFile);
-               close(inFd);
-
-               if (buf != localBuf)
-                       free(buf);
-
-               return 1;
-       }
-
-       if (skipVal)
-       {
-               if (lseek(inFd, skipVal * blockSize, 0) < 0)
-               {
-                       while (skipVal-- > 0)
-                       {
-                               inCc = read(inFd, buf, blockSize);
-
-                               if (inCc < 0)
-                               {
-                                       perror(inFile);
-                                       r = 1;
-                                       goto cleanup;
-                               }
-
-                               if (inCc == 0)
-                               {
-                                       fprintf(stderr, "End of file while skipping\n");
-                                       r = 1;
-                                       goto cleanup;
-                               }
-                       }
-               }
-       }
-
-       if (seekVal)
-       {
-               if (lseek(outFd, seekVal * blockSize, 0) < 0)
-               {
-                       perror(outFile);
-                       r = 1;
-                       goto cleanup;
-               }
-       }
-
-       inCc = 0;
-
-       while (((count < 0) || (inFull + inPartial < count)) &&
-              (inCc = read(inFd, buf, blockSize)) > 0)
-       {
-               if (inCc < blockSize)
-                       inPartial++;
-               else
-                       inFull++;
-               cp = buf;
-
-               if (intFlag)
-               {
-                       fprintf(stderr, "Interrupted\n");
-                       r = 1;
-                       goto cleanup;
-               }
-
-               while (inCc > 0)
-               {
-                       outCc = write(outFd, cp, inCc);
-
-                       if (outCc < 0)
-                       {
-                               perror(outFile);
-                               r = 1;
-                               goto cleanup;
-                       }
-
-                       if (outCc < blockSize)
-                               outPartial++;
-                       else
-                               outFull++;
-                       cp += outCc;
-                       inCc -= outCc;
-               }
-       }
-
-       if (inCc < 0)
-               perror(inFile);
-
-cleanup:
-       close(inFd);
-
-       if (close(outFd) < 0)
-       {
-               perror(outFile);
-               r = 1;
-       }
-
-       if (buf != localBuf)
-               free(buf);
-
-       printf("%ld+%ld records in\n", inFull, inPartial);
-
-       printf("%ld+%ld records out\n", outFull, outPartial);
-
-       return r;
-}
-
-
-/*
- * Read a number with a possible multiplier.
- * Returns -1 if the number format is illegal.
- */
-static long
-getNum(const char * cp)
-{
-       long    value;
-
-       if (!isDecimal(*cp))
-               return -1;
-
-       value = 0;
-
-       while (isDecimal(*cp))
-               value = value * 10 + *cp++ - '0';
-
-       switch (*cp++)
-       {
-               case 'k':
-                       value *= 1024;
-                       break;
-
-               case 'b':
-                       value *= 512;
-                       break;
-
-               case 'w':
-                       value *= 2;
-                       break;
-
-               case '\0':
-                       return value;
-
-               default:
-                       return -1;
-       }
-
-       if (*cp)
-               return -1;
-
-       return value;
-}
-
-/* END CODE */
diff --git a/src/sash/cmd_ed.c b/src/sash/cmd_ed.c
deleted file mode 100644 (file)
index e935c0d..0000000
+++ /dev/null
@@ -1,1440 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "ed" built-in command (much simplified)
- */
-
-#include "sash.h"
-
-#define        USERSIZE        1024    /* max line length typed in by user */
-#define        INITBUF_SIZE    1024    /* initial buffer size */
-
-
-typedef        int     NUM;
-typedef        int     LEN;
-
-typedef        struct  LINE    LINE;
-
-struct LINE
-{
-       LINE *  next;
-       LINE *  prev;
-       LEN     len;
-       char    data[1];
-};
-
-
-static LINE    lines;
-static LINE *  curLine;
-static NUM     curNum;
-static NUM     lastNum;
-static NUM     marks[26];
-static BOOL    dirty;
-static char *  fileName;
-static char    searchString[USERSIZE];
-
-static char *  bufBase;
-static char *  bufPtr;
-static LEN     bufUsed;
-static LEN     bufSize;
-
-
-static void    doCommands(void);
-static void    subCommand(const char * cmd, NUM num1, NUM num2);
-static BOOL    getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum);
-static BOOL    setCurNum(NUM num);
-static BOOL    initEdit(void);
-static void    termEdit(void);
-static void    addLines(NUM num);
-static BOOL    insertLine(NUM num, const char * data, LEN len);
-static BOOL    deleteLines(NUM num1, NUM num2);
-static BOOL    printLines(NUM num1, NUM num2, BOOL expandFlag);
-static BOOL    writeLines(const char * file, NUM num1, NUM num2);
-static BOOL    readLines(const char * file, NUM num);
-static NUM     searchLines(const char * str, NUM num1, NUM num2);
-static LINE *  findLine(NUM num);
-
-static LEN     findString
-       (const LINE * lp, const char * str, LEN len, LEN offset);
-
-
-int
-do_ed(int argc, const char ** argv)
-{
-       if (!initEdit())
-               return 1;
-
-       if (argc > 1)
-       {
-               fileName = strdup(argv[1]);
-
-               if (fileName == NULL)
-               {
-                       fprintf(stderr, "No memory\n");
-                       termEdit();
-
-                       return 1;
-               }
-
-               if (!readLines(fileName, 1))
-               {
-                       termEdit();
-
-                       return 1;
-               }
-
-               if (lastNum)
-                       setCurNum(1);
-
-               dirty = FALSE;
-       }
-
-       doCommands();
-
-       termEdit();
-       return 0;
-}
-
-
-/*
- * Read commands until we are told to stop.
- */
-static void
-doCommands(void)
-{
-       const char *    cp;
-       char *          endbuf;
-       char *          newname;
-       int             len;
-       NUM             num1;
-       NUM             num2;
-       BOOL            have1;
-       BOOL            have2;
-       char            buf[USERSIZE];
-
-       while (TRUE)
-       {
-               intFlag = FALSE;
-               printf(": ");
-               fflush(stdout);
-
-               if (fgets(buf, sizeof(buf), stdin) == NULL)
-                       return;
-
-               len = strlen(buf);
-
-               if (len == 0)
-                       return;
-
-               endbuf = &buf[len - 1];
-
-               if (*endbuf != '\n')
-               {
-                       fprintf(stderr, "Command line too long\n");
-
-                       do
-                       {
-                               len = fgetc(stdin);
-                       }
-                       while ((len != EOF) && (len != '\n'));
-
-                       continue;
-               }
-
-               while ((endbuf > buf) && isBlank(endbuf[-1]))
-                       endbuf--;
-
-               *endbuf = '\0';
-
-               cp = buf;
-
-               while (isBlank(*cp))
-                       cp++;
-
-               have1 = FALSE;
-               have2 = FALSE;
-
-               if ((curNum == 0) && (lastNum > 0))
-               {
-                       curNum = 1;
-                       curLine = lines.next;
-               }
-
-               if (!getNum(&cp, &have1, &num1))
-                       continue;
-
-               while (isBlank(*cp))
-                       cp++;
-
-               if (*cp == ',')
-               {
-                       cp++;
-
-                       if (!getNum(&cp, &have2, &num2))
-                               continue;
-
-                       if (!have1)
-                               num1 = 1;
-
-                       if (!have2)
-                               num2 = lastNum;
-
-                       have1 = TRUE;
-                       have2 = TRUE;
-               }
-
-               if (!have1)
-                       num1 = curNum;
-
-               if (!have2)
-                       num2 = num1;
-
-               switch (*cp++)
-               {
-                       case 'a':
-                               addLines(num1 + 1);
-                               break;
-
-                       case 'c':
-                               deleteLines(num1, num2);
-                               addLines(num1);
-                               break;
-
-                       case 'd':
-                               deleteLines(num1, num2);
-                               break;
-
-                       case 'f':
-                               if (*cp && !isBlank(*cp))
-                               {
-                                       fprintf(stderr, "Bad file command\n");
-                                       break;
-                               }
-
-                               while (isBlank(*cp))
-                                       cp++;
-
-                               if (*cp == '\0')
-                               {
-                                       if (fileName)
-                                               printf("\"%s\"\n", fileName);
-                                       else
-                                               printf("No file name\n");
-
-                                       break;
-                               }
-
-                               newname = strdup(cp);
-
-                               if (newname == NULL)
-                               {
-                                       fprintf(stderr, "No memory for file name\n");
-                                       break;
-                               }
-
-                               if (fileName)
-                                       free(fileName);
-
-                               fileName = newname;
-                               break;
-
-                       case 'i':
-                               addLines(num1);
-                               break;
-
-                       case 'k':
-                               while (isBlank(*cp))
-                                       cp++;
-
-                               if ((*cp < 'a') || (*cp > 'a') || cp[1])
-                               {
-                                       fprintf(stderr, "Bad mark name\n");
-                                       break;
-                               }
-
-                               marks[*cp - 'a'] = num2;
-                               break;
-
-                       case 'l':
-                               printLines(num1, num2, TRUE);
-                               break;
-
-                       case 'p':
-                               printLines(num1, num2, FALSE);
-                               break;
-
-                       case 'q':
-                               while (isBlank(*cp))
-                                       cp++;
-
-                               if (have1 || *cp)
-                               {
-                                       fprintf(stderr, "Bad quit command\n");
-                                       break;
-                               }
-
-                               if (!dirty)
-                                       return;
-
-                               printf("Really quit? ");
-                               fflush(stdout);
-
-                               buf[0] = '\0';
-
-                               if (fgets(buf, sizeof(buf), stdin) == NULL)
-                                       return;
-
-                               cp = buf;
-
-                               while (isBlank(*cp))
-                                       cp++;
-
-                               if ((*cp == 'y') || (*cp == 'Y'))
-                                       return;
-
-                               break;
-
-                       case 'r':
-                               if (*cp && !isBlank(*cp))
-                               {
-                                       fprintf(stderr, "Bad read command\n");
-                                       break;
-                               }
-
-                               while (isBlank(*cp))
-                                       cp++;
-
-                               if (*cp == '\0')
-                               {
-                                       fprintf(stderr, "No file name\n");
-                                       break;
-                               }
-
-                               if (!have1)
-                                       num1 = lastNum;
-
-                               if (readLines(cp, num1 + 1))
-                                       break;
-
-                               if (fileName == NULL)
-                                       fileName = strdup(cp);
-
-                               break;
-
-                       case 's':
-                               subCommand(cp, num1, num2);
-                               break;
-
-                       case 'w':
-                               if (*cp && !isBlank(*cp))
-                               {
-                                       fprintf(stderr, "Bad write command\n");
-                                       break;
-                               }
-
-                               while (isBlank(*cp))
-                                       cp++;
-
-                               if (!have1) {
-                                       num1 = 1;
-                                       num2 = lastNum;
-                               }
-
-                               if (*cp == '\0')
-                                       cp = fileName;
-
-                               if (cp == NULL)
-                               {
-                                       fprintf(stderr, "No file name specified\n");
-                                       break;
-                               }
-       
-                               writeLines(cp, num1, num2);
-                               break;
-
-                       case 'z':
-                               switch (*cp)
-                               {
-                               case '-':
-                                       printLines(curNum-21, curNum, FALSE);
-                                       break;
-                               case '.':
-                                       printLines(curNum-11, curNum+10, FALSE);
-                                       break;
-                               default:
-                                       printLines(curNum, curNum+21, FALSE);
-                                       break;
-                               }
-                               break;
-
-                       case '.':
-                               if (have1)
-                               {
-                                       fprintf(stderr, "No arguments allowed\n");
-                                       break;
-                               }
-
-                               printLines(curNum, curNum, FALSE);
-                               break;
-       
-                       case '-':
-                               if (setCurNum(curNum - 1))
-                                       printLines(curNum, curNum, FALSE);
-
-                               break;
-
-                       case '=':
-                               printf("%d\n", num1);
-                               break;
-
-                       case '\0':
-                               if (have1)
-                               {
-                                       printLines(num2, num2, FALSE);
-                                       break;
-                               }
-
-                               if (setCurNum(curNum + 1))
-                                       printLines(curNum, curNum, FALSE);
-
-                               break;
-
-                       default:
-                               fprintf(stderr, "Unimplemented command\n");
-                               break;
-               }
-       }
-}
-
-
-/*
- * Do the substitute command.
- * The current line is set to the last substitution done.
- */
-static void
-subCommand(const char * cmd, NUM num1, NUM num2)
-{
-       int     delim;
-       char *  cp;
-       char *  oldStr;
-       char *  newStr;
-       LEN     oldLen;
-       LEN     newLen;
-       LEN     deltaLen;
-       LEN     offset;
-       LINE *  lp;
-       LINE *  nlp;
-       BOOL    globalFlag;
-       BOOL    printFlag;
-       BOOL    didSub;
-       BOOL    needPrint;
-       char    buf[USERSIZE];
-
-       if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
-       {
-               fprintf(stderr, "Bad line range for substitute\n");
-
-               return;
-       }
-
-       globalFlag = FALSE;
-       printFlag = FALSE;
-       didSub = FALSE;
-       needPrint = FALSE;
-
-       /*
-        * Copy the command so we can modify it.
-        */
-       strcpy(buf, cmd);
-       cp = buf;
-
-       if (isBlank(*cp) || (*cp == '\0'))
-       {
-               fprintf(stderr, "Bad delimiter for substitute\n");
-
-               return;
-       }
-
-       delim = *cp++;
-       oldStr = cp;
-
-       cp = strchr(cp, delim);
-
-       if (cp == NULL)
-       {
-               fprintf(stderr, "Missing 2nd delimiter for substitute\n");
-
-               return;
-       }
-
-       *cp++ = '\0';
-
-       newStr = cp;
-       cp = strchr(cp, delim);
-
-       if (cp)
-               *cp++ = '\0';
-       else
-               cp = "";
-
-       while (*cp) switch (*cp++)
-       {
-               case 'g':
-                       globalFlag = TRUE;
-                       break;
-
-               case 'p':
-                       printFlag = TRUE;
-                       break;
-
-               default:
-                       fprintf(stderr, "Unknown option for substitute\n");
-
-                       return;
-       }
-
-       if (*oldStr == '\0')
-       {
-               if (searchString[0] == '\0')
-               {
-                       fprintf(stderr, "No previous search string\n");
-
-                       return;
-               }
-
-               oldStr = searchString;
-       }
-
-       if (oldStr != searchString)
-               strcpy(searchString, oldStr);
-
-       lp = findLine(num1);
-
-       if (lp == NULL)
-               return;
-
-       oldLen = strlen(oldStr);
-       newLen = strlen(newStr);
-       deltaLen = newLen - oldLen;
-       offset = 0;
-       nlp = NULL;
-
-       while (num1 <= num2)
-       {
-               offset = findString(lp, oldStr, oldLen, offset);
-
-               if (offset < 0)
-               {
-                       if (needPrint)
-                       {
-                               printLines(num1, num1, FALSE);
-                               needPrint = FALSE;
-                       }
-
-                       offset = 0;
-                       lp = lp->next;
-                       num1++;
-
-                       continue;
-               }
-
-               needPrint = printFlag;
-               didSub = TRUE;
-               dirty = TRUE;
-
-               /*
-                * If the replacement string is the same size or shorter
-                * than the old string, then the substitution is easy.
-                */
-               if (deltaLen <= 0)
-               {
-                       memcpy(&lp->data[offset], newStr, newLen);
-
-                       if (deltaLen)
-                       {
-                               memcpy(&lp->data[offset + newLen],
-                                       &lp->data[offset + oldLen],
-                                       lp->len - offset - oldLen);
-
-                               lp->len += deltaLen;
-                       }
-
-                       offset += newLen;
-
-                       if (globalFlag)
-                               continue;
-
-                       if (needPrint)
-                       {
-                               printLines(num1, num1, FALSE);
-                               needPrint = FALSE;
-                       }
-
-                       lp = lp->next;
-                       num1++;
-
-                       continue;
-               }
-
-               /*
-                * The new string is larger, so allocate a new line
-                * structure and use that.  Link it in in place of
-                * the old line structure.
-                */
-               nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen);
-
-               if (nlp == NULL)
-               {
-                       fprintf(stderr, "Cannot get memory for line\n");
-
-                       return;
-               }
-
-               nlp->len = lp->len + deltaLen;
-
-               memcpy(nlp->data, lp->data, offset);
-
-               memcpy(&nlp->data[offset], newStr, newLen);
-
-               memcpy(&nlp->data[offset + newLen],
-                       &lp->data[offset + oldLen],
-                       lp->len - offset - oldLen);
-
-               nlp->next = lp->next;
-               nlp->prev = lp->prev;
-               nlp->prev->next = nlp;
-               nlp->next->prev = nlp;
-
-               if (curLine == lp)
-                       curLine = nlp;
-
-               free(lp);
-               lp = nlp;
-
-               offset += newLen;
-
-               if (globalFlag)
-                       continue;
-
-               if (needPrint)
-               {
-                       printLines(num1, num1, FALSE);
-                       needPrint = FALSE;
-               }
-
-               lp = lp->next;
-               num1++;
-       }
-
-       if (!didSub)
-               fprintf(stderr, "No substitutions found for \"%s\"\n", oldStr);
-}
-
-
-/*
- * Search a line for the specified string starting at the specified
- * offset in the line.  Returns the offset of the found string, or -1.
- */
-static LEN
-findString( const LINE * lp, const char * str, LEN len, LEN offset)
-{
-       LEN             left;
-       const char *    cp;
-       const char *    ncp;
-
-       cp = &lp->data[offset];
-       left = lp->len - offset;
-
-       while (left >= len)
-       {
-               ncp = memchr(cp, *str, left);
-
-               if (ncp == NULL)
-                       return -1;
-
-               left -= (ncp - cp);
-
-               if (left < len)
-                       return -1;
-
-               cp = ncp;
-
-               if (memcmp(cp, str, len) == 0)
-                       return (cp - lp->data);
-
-               cp++;
-               left--;
-       }
-
-       return -1;
-}
-
-
-/*
- * Add lines which are typed in by the user.
- * The lines are inserted just before the specified line number.
- * The lines are terminated by a line containing a single dot (ugly!),
- * or by an end of file.
- */
-static void
-addLines(NUM num)
-{
-       int     len;
-       char    buf[USERSIZE + 1];
-
-       while (fgets(buf, sizeof(buf), stdin))
-       {
-               if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
-                       return;
-
-               len = strlen(buf);
-
-               if (len == 0)
-                       return;
-
-               if (buf[len - 1] != '\n')
-               {
-                       fprintf(stderr, "Line too long\n");
-
-                       do
-                       {
-                               len = fgetc(stdin);
-                       }
-                       while ((len != EOF) && (len != '\n'));
-
-                       return;
-               }
-
-               if (!insertLine(num++, buf, len))
-                       return;
-       }
-}
-
-
-/*
- * Parse a line number argument if it is present.  This is a sum
- * or difference of numbers, '.', '$', 'x, or a search string.
- * Returns TRUE if successful (whether or not there was a number). 
- * Returns FALSE if there was a parsing error, with a message output.
- * Whether there was a number is returned indirectly, as is the number.
- * The character pointer which stopped the scan is also returned.
- */
-static BOOL
-getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum)
-{
-       const char *    cp;
-       char *          endStr;
-       char            str[USERSIZE];
-       BOOL            haveNum;
-       NUM             value;
-       NUM             num;
-       NUM             sign;
-
-       cp = *retcp;
-       haveNum = FALSE;
-       value = 0;
-       sign = 1;
-
-       while (TRUE)
-       {
-               while (isBlank(*cp))
-                       cp++;
-
-               switch (*cp)
-               {
-                       case '.':
-                               haveNum = TRUE;
-                               num = curNum;
-                               cp++;
-                               break;
-
-                       case '$':
-                               haveNum = TRUE;
-                               num = lastNum;
-                               cp++;
-                               break;
-
-                       case '\'':
-                               cp++;
-
-                               if ((*cp < 'a') || (*cp > 'z'))
-                               {
-                                       fprintf(stderr, "Bad mark name\n");
-
-                                       return FALSE;
-                               }
-
-                               haveNum = TRUE;
-                               num = marks[*cp++ - 'a'];
-                               break;
-
-                       case '/':
-                               strcpy(str, ++cp);
-                               endStr = strchr(str, '/');
-
-                               if (endStr)
-                               {
-                                       *endStr++ = '\0';
-                                       cp += (endStr - str);
-                               }
-                               else
-                                       cp = "";
-
-                               num = searchLines(str, curNum, lastNum);
-
-                               if (num == 0)
-                                       return FALSE;
-
-                               haveNum = TRUE;
-                               break;
-
-                       default:
-                               if (!isDecimal(*cp))
-                               {
-                                       *retcp = cp;
-                                       *retHaveNum = haveNum;
-                                       *retNum = value;
-
-                                       return TRUE;
-                               }
-
-                               num = 0;
-
-                               while (isDecimal(*cp))
-                                       num = num * 10 + *cp++ - '0';
-
-                               haveNum = TRUE;
-                               break;
-               }
-
-               value += num * sign;
-
-               while (isBlank(*cp))
-                       cp++;
-
-               switch (*cp)
-               {
-                       case '-':
-                               sign = -1;
-                               cp++;
-                               break;
-
-                       case '+':
-                               sign = 1;
-                               cp++;
-                               break;
-
-                       default:
-                               *retcp = cp;
-                               *retHaveNum = haveNum;
-                               *retNum = value;
-
-                               return TRUE;
-               }
-       }
-}
-
-
-/*
- * Initialize everything for editing.
- */
-static BOOL
-initEdit(void)
-{
-       int     i;
-
-       bufSize = INITBUF_SIZE;
-       bufBase = malloc(bufSize);
-
-       if (bufBase == NULL)
-       {
-               fprintf(stderr, "No memory for buffer\n");
-
-               return FALSE;
-       }
-
-       bufPtr = bufBase;
-       bufUsed = 0;
-
-       lines.next = &lines;
-       lines.prev = &lines;
-
-       curLine = NULL;
-       curNum = 0;
-       lastNum = 0;
-       dirty = FALSE;
-       fileName = NULL;
-       searchString[0] = '\0';
-
-       for (i = 0; i < 26; i++)
-               marks[i] = 0;
-
-       return TRUE;
-}
-
-
-/*
- * Finish editing.
- */
-static void
-termEdit(void)
-{
-       if (bufBase)
-               free(bufBase);
-
-       bufBase = NULL;
-       bufPtr = NULL;
-       bufSize = 0;
-       bufUsed = 0;
-
-       if (fileName)
-               free(fileName);
-
-       fileName = NULL;
-
-       searchString[0] = '\0';
-
-       if (lastNum)
-               deleteLines(1, lastNum);
-
-       lastNum = 0;
-       curNum = 0;
-       curLine = NULL;
-}
-
-
-/*
- * Read lines from a file at the specified line number.
- * Returns TRUE if the file was successfully read.
- */
-static BOOL
-readLines(const char * file, NUM num)
-{
-       int     fd;
-       int     cc;
-       LEN     len;
-       LEN     lineCount;
-       LEN     charCount;
-       char *  cp;
-
-       if ((num < 1) || (num > lastNum + 1))
-       {
-               fprintf(stderr, "Bad line for read\n");
-
-               return FALSE;
-       }
-
-       fd = open(file, 0);
-
-       if (fd < 0)
-       {
-               perror(file);
-
-               return FALSE;
-       }
-
-       bufPtr = bufBase;
-       bufUsed = 0;
-       lineCount = 0;
-       charCount = 0;
-       cc = 0;
-
-       printf("\"%s\", ", file);
-       fflush(stdout);
-
-       do
-       {
-               if (intFlag)
-               {
-                       printf("INTERRUPTED, ");
-                       bufUsed = 0;
-                       break;
-               }
-
-               cp = memchr(bufPtr, '\n', bufUsed);
-
-               if (cp)
-               {
-                       len = (cp - bufPtr) + 1;
-
-                       if (!insertLine(num, bufPtr, len))
-                       {
-                               close(fd);
-
-                               return FALSE;
-                       }
-
-                       bufPtr += len;
-                       bufUsed -= len;
-                       charCount += len;
-                       lineCount++;
-                       num++;
-
-                       continue;
-               }
-
-               if (bufPtr != bufBase)
-               {
-                       memcpy(bufBase, bufPtr, bufUsed);
-                       bufPtr = bufBase + bufUsed;
-               }
-
-               if (bufUsed >= bufSize)
-               {
-                       len = (bufSize * 3) / 2;
-                       cp = realloc(bufBase, len);
-
-                       if (cp == NULL)
-                       {
-                               fprintf(stderr, "No memory for buffer\n");
-                               close(fd);
-
-                               return FALSE;
-                       }
-
-                       bufBase = cp;
-                       bufPtr = bufBase + bufUsed;
-                       bufSize = len;
-               }
-
-               cc = read(fd, bufPtr, bufSize - bufUsed);
-               bufUsed += cc;
-               bufPtr = bufBase;
-
-       }
-       while (cc > 0);
-
-       if (cc < 0)
-       {
-               perror(file);
-               close(fd);
-
-               return FALSE;
-       }
-
-       if (bufUsed)
-       {
-               if (!insertLine(num, bufPtr, bufUsed))
-               {
-                       close(fd);
-
-                       return -1;
-               }
-
-               lineCount++;
-               charCount += bufUsed;
-       }
-
-       close(fd);
-
-       printf("%d lines%s, %d chars\n", lineCount,
-               (bufUsed ? " (incomplete)" : ""), charCount);
-
-       return TRUE;
-}
-
-
-/*
- * Write the specified lines out to the specified file.
- * Returns TRUE if successful, or FALSE on an error with a message output.
- */
-static BOOL
-writeLines(const char * file, NUM num1, NUM num2)
-{
-       int     fd;
-       LINE *  lp;
-       LEN     lineCount;
-       LEN     charCount;
-
-       if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
-       {
-               fprintf(stderr, "Bad line range for write\n");
-
-               return FALSE;
-       }
-
-       lineCount = 0;
-       charCount = 0;
-
-       fd = creat(file, 0666);
-
-       if (fd < 0) {
-               perror(file);
-
-               return FALSE;
-       }
-
-       printf("\"%s\", ", file);
-       fflush(stdout);
-
-       lp = findLine(num1);
-
-       if (lp == NULL)
-       {
-               close(fd);
-
-               return FALSE;
-       }
-
-       while (num1++ <= num2)
-       {
-               if (write(fd, lp->data, lp->len) != lp->len)
-               {
-                       perror(file);
-                       close(fd);
-
-                       return FALSE;
-               }
-
-               charCount += lp->len;
-               lineCount++;
-               lp = lp->next;
-       }
-
-       if (close(fd) < 0)
-       {
-               perror(file);
-
-               return FALSE;
-       }
-
-       printf("%d lines, %d chars\n", lineCount, charCount);
-
-       return TRUE;
-}
-
-
-/*
- * Print lines in a specified range.
- * The last line printed becomes the current line.
- * If expandFlag is TRUE, then the line is printed specially to
- * show magic characters.
- */
-static BOOL
-printLines(NUM num1, NUM num2, BOOL expandFlag)
-{
-       const LINE *    lp;
-       const char *    cp;
-       int             ch;
-       LEN             count;
-
-       if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
-       {
-               fprintf(stderr, "Bad line range for print\n");
-
-               return FALSE;
-       }
-
-       lp = findLine(num1);
-
-       if (lp == NULL)
-               return FALSE;
-
-       while (!intFlag && (num1 <= num2))
-       {
-               if (!expandFlag)
-               {
-                       tryWrite(STDOUT, lp->data, lp->len);
-                       setCurNum(num1++);
-                       lp = lp->next;
-
-                       continue;
-               }
-
-               /*
-                * Show control characters and characters with the
-                * high bit set specially.
-                */
-               cp = lp->data;
-               count = lp->len;
-
-               if ((count > 0) && (cp[count - 1] == '\n'))
-                       count--;
-
-               while (count-- > 0)
-               {
-                       ch = *cp++ & 0xff;
-
-                       if (ch & 0x80)
-                       {
-                               fputs("M-", stdout);
-                               ch &= 0x7f;
-                       }
-
-                       if (ch < ' ')
-                       {
-                               fputc('^', stdout);
-                               ch += '@';
-                       }
-
-                       if (ch == 0x7f)
-                       {
-                               fputc('^', stdout);
-                               ch = '?';
-                       }
-
-                       fputc(ch, stdout);
-               }
-
-               fputs("$\n", stdout);
-
-               setCurNum(num1++);
-               lp = lp->next;
-       }
-
-       return TRUE;
-}
-
-
-/*
- * Insert a new line with the specified text.
- * The line is inserted so as to become the specified line,
- * thus pushing any existing and further lines down one.
- * The inserted line is also set to become the current line.
- * Returns TRUE if successful.
- */
-static BOOL
-insertLine(NUM num, const char * data, LEN len)
-{
-       LINE *  newLp;
-       LINE *  lp;
-
-       if ((num < 1) || (num > lastNum + 1))
-       {
-               fprintf(stderr, "Inserting at bad line number\n");
-
-               return FALSE;
-       }
-
-       newLp = (LINE *) malloc(sizeof(LINE) + len - 1);
-
-       if (newLp == NULL) 
-       {
-               fprintf(stderr, "Failed to allocate memory for line\n");
-
-               return FALSE;
-       }
-
-       memcpy(newLp->data, data, len);
-       newLp->len = len;
-
-       if (num > lastNum)
-               lp = &lines;
-       else
-       {
-               lp = findLine(num);
-
-               if (lp == NULL)
-               {
-                       free((char *) newLp);
-
-                       return FALSE;
-               }
-       }
-
-       newLp->next = lp;
-       newLp->prev = lp->prev;
-       lp->prev->next = newLp;
-       lp->prev = newLp;
-
-       lastNum++;
-       dirty = TRUE;
-
-       return setCurNum(num);
-}
-
-
-/*
- * Delete lines from the given range.
- */
-static BOOL
-deleteLines(NUM num1, NUM num2)
-{
-       LINE *  lp;
-       LINE *  nlp;
-       LINE *  plp;
-       NUM     count;
-
-       if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
-       {
-               fprintf(stderr, "Bad line numbers for delete\n");
-
-               return FALSE;
-       }
-
-       lp = findLine(num1);
-
-       if (lp == NULL)
-               return FALSE;
-
-       if ((curNum >= num1) && (curNum <= num2))
-       {
-               if (num2 < lastNum)
-                       setCurNum(num2 + 1);
-               else if (num1 > 1)
-                       setCurNum(num1 - 1);
-               else
-                       curNum = 0;
-       }
-
-       count = num2 - num1 + 1;
-
-       if (curNum > num2)
-               curNum -= count;
-
-       lastNum -= count;
-
-       while (count-- > 0)
-       {
-               nlp = lp->next;
-               plp = lp->prev;
-               plp->next = nlp;
-               nlp->prev = plp;
-               lp->next = NULL;
-               lp->prev = NULL;
-               lp->len = 0;
-               free(lp);
-               lp = nlp;
-       }
-
-       dirty = TRUE;
-
-       return TRUE;
-}
-
-
-/*
- * Search for a line which contains the specified string.
- * If the string is NULL, then the previously searched for string
- * is used.  The currently searched for string is saved for future use.
- * Returns the line number which matches, or 0 if there was no match
- * with an error printed.
- */
-static NUM
-searchLines(const char * str, NUM num1, NUM num2)
-{
-       const LINE *    lp;
-       int             len;
-
-       if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
-       {
-               fprintf(stderr, "Bad line numbers for search\n");
-
-               return 0;
-       }
-
-       if (*str == '\0')
-       {
-               if (searchString[0] == '\0')
-               {
-                       fprintf(stderr, "No previous search string\n");
-
-                       return 0;
-               }
-
-               str = searchString;
-       }
-
-       if (str != searchString)
-               strcpy(searchString, str);
-
-       len = strlen(str);
-
-       lp = findLine(num1);
-
-       if (lp == NULL)
-               return 0;
-
-       while (num1 <= num2)
-       {
-               if (findString(lp, str, len, 0) >= 0)
-                       return num1;
-
-               num1++;
-               lp = lp->next;
-       }
-
-       fprintf(stderr, "Cannot find string \"%s\"\n", str);
-
-       return 0;
-}
-
-
-/*
- * Return a pointer to the specified line number.
- */
-static LINE *
-findLine(NUM num)
-{
-       LINE *  lp;
-       NUM     lnum;
-
-       if ((num < 1) || (num > lastNum))
-       {
-               fprintf(stderr, "Line number %d does not exist\n", num);
-
-               return NULL;
-       }
-
-       if (curNum <= 0)
-       {
-               curNum = 1;
-               curLine = lines.next;
-       }
-
-       if (num == curNum)
-               return curLine;
-
-       lp = curLine;
-       lnum = curNum;
-
-       if (num < (curNum / 2))
-       {
-               lp = lines.next;
-               lnum = 1;
-       }
-       else if (num > ((curNum + lastNum) / 2))
-       {
-               lp = lines.prev;
-               lnum = lastNum;
-       }
-
-       while (lnum < num)
-       {
-               lp = lp->next;
-               lnum++;
-       }
-
-       while (lnum > num)
-       {
-               lp = lp->prev;
-               lnum--;
-       }
-
-       return lp;
-}
-
-
-/*
- * Set the current line number.
- * Returns TRUE if successful.
- */
-static BOOL
-setCurNum(NUM num)
-{
-       LINE *  lp;
-
-       lp = findLine(num);
-
-       if (lp == NULL)
-               return FALSE;
-
-       curNum = num;
-       curLine = lp;
-
-       return TRUE;
-}
-
-/* END CODE */
diff --git a/src/sash/cmd_file.c b/src/sash/cmd_file.c
deleted file mode 100644 (file)
index caf2a30..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "file" built-in command.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "sash.h"
-
-
-static const char *    checkFile(const char * name);
-
-
-int
-do_file(int argc, const char ** argv)
-{
-       const char *    name;
-       const char *    info;
-
-       argc--;
-       argv++;
-
-       while (argc-- > 0)
-       {
-               name = *argv++;
-
-               info = checkFile(name);
-
-               if (info == NULL)
-                       info = "No information available";
-
-               printf("%s: %s\n", name, info);
-       }
-
-       return 0;
-}
-
-
-/*
- * Examine the specified file and return a static string which
- * describes the file type.  Returns NULL on a failure.
- */
-static const char *
-checkFile(const char * name)
-{
-       int             mode;
-       int             fd;
-       int             cc;
-       int             i;
-       int             ch;
-       int             badCount;
-       char *          cp;
-       struct  stat    statBuf;
-       char            data[8192];
-       static  char    info[1024];
-
-       cp = info;
-       *cp = '\0';
-
-       if (lstat(name, &statBuf) < 0)
-       {
-               if (errno == ENOENT)
-                       return "non-existent";
-
-               sprintf(cp, "stat failed: %s", strerror(errno));
-
-               return info;
-       }
-
-       /*
-        * Check the file type.
-        */
-       mode = statBuf.st_mode;
-
-       if (S_ISDIR(mode))
-               return "directory";
-
-       if (S_ISCHR(mode))
-               return "character device";
-
-       if (S_ISBLK(mode))
-               return "block device";
-
-       if (S_ISFIFO(mode))
-               return "named pipe";
-
-#ifdef S_ISLNK
-       if (S_ISLNK(mode))
-               return "symbolic link";
-#endif
-
-#ifdef S_ISSOCK
-       if (S_ISSOCK(mode))
-               return "socket";
-#endif
-
-       /*
-        * If the file is not a regular file mention that.
-        */
-       if (!S_ISREG(mode))
-       {
-               sprintf(cp, "unknown mode 0x%x, \n", mode);
-
-               cp += strlen(cp);
-       }
-
-       /*
-        * Check for an executable file.
-        */
-       if ((mode & (S_IEXEC | S_IXGRP | S_IXOTH)) != 0)
-       {
-               strcpy(cp, "executable, ");
-
-               cp += strlen(cp);
-       }
-
-       /*
-        * The file is a normal file.
-        * Open it if we can and read in the first block.
-        */
-       fd = open(name, O_RDONLY);
-
-       if (fd < 0)
-       {
-               sprintf(cp, "unreadable: %s", strerror(errno));
-
-               return info;
-       }
-
-       cc = read(fd, data, sizeof(data));
-
-       if (cc < 0)
-       {
-               sprintf(cp, "read error: %s", strerror(errno));
-
-               (void) close(fd);
-
-               return info;
-       }
-
-       (void) close(fd);
-
-       /*
-        * Check for an empty file.
-        */
-       if (cc == 0)
-       {
-               strcpy(cp, "empty file");
-
-               return info;
-       }
-
-       /*
-        * Check for a script file.
-        */
-       if ((cc > 2) && (data[0] == '#') && (data[1] == '!'))
-       {
-               char *  begin;
-               char *  end;
-
-               data[sizeof(data) - 1] = '\0';
-
-               begin = &data[2];
-
-               while (*begin == ' ')
-                       begin++;
-
-               end = begin;
-
-               while (*end && (*end != ' ') && (*end != '\n'))
-                       end++;
-
-               *end = '\0';
-
-               sprintf(cp, "script for \"%s\"", begin);
-
-               return info;
-       }
-
-       /*
-        * Check for special binary data types.
-        */
-       if ((data[0] == '\037') && (data[1] == '\235'))
-               return "compressed file";
-
-       if ((data[0] == '\037') && (data[1] == '\213'))
-               return "GZIP file";
-
-       if ((data[0] == '\177') && (memcmp(&data[1], "ELF", 3) == 0))
-       {
-               strcpy(cp, "ELF program");
-
-               return info;
-       }
-
-       /*
-        * Check for binary data.
-        */
-       badCount = 0;
-
-       for (i = 0; i < cc; i++)
-       {
-               ch = data[i];
-
-               if ((ch == '\n') || (ch == '\t'))
-                       continue;
-
-               if (isspace(ch) || isprint(ch))
-                       continue;
-
-               badCount++;
-       }
-
-       if (badCount != 0)
-       {
-               strcpy(cp, "binary");
-
-               return info;
-       }
-
-       /*
-        * It is just a text file.
-        */
-       strcpy(cp, "text file");
-
-       return info;
-}
-
-/* END CODE */
diff --git a/src/sash/cmd_find.c b/src/sash/cmd_find.c
deleted file mode 100644 (file)
index fde9006..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "find" built-in command.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <errno.h>
-
-#include "sash.h"
-
-
-#ifdef S_ISLNK
-#define        LSTAT   lstat
-#else
-#define        LSTAT   stat
-#endif
-
-
-#define        MAX_NAME_SIZE   (1024 * 10)
-
-
-/*
- * Items that can be specified to restrict the output.
- */
-static BOOL            xdevFlag;
-static dev_t           xdevDevice;
-static long            fileSize;
-static const char *    filePattern;
-static const char *    fileType;
-
-
-/*
- * Recursive routine to examine the files in a directory.
- */
-static void    examineDirectory(const char * path);
-static BOOL    testFile(const char * fullName, const struct stat * statBuf);
-
-
-
-/*
- * Find files from the specified directory path.
- * This is limited to just printing their file names.
- */
-int
-do_find(int argc, const char ** argv)
-{
-       const char *    cp;
-       const char *    path;
-       struct stat     statBuf;
-
-       argc--;
-       argv++;
-
-       xdevFlag = FALSE;
-       fileType = NULL;
-       filePattern = NULL;
-       fileSize = 0;
-
-       if ((argc <= 0) || (**argv == '-'))
-       {
-               fprintf(stderr, "No path specified\n");
-
-               return 1;
-       }
-
-       path = *argv++;
-       argc--;
-
-       while (argc > 0)
-       {
-               argc--;
-               cp = *argv++;
-
-               if (strcmp(cp, "-xdev") == 0)
-                       xdevFlag = TRUE;
-               else if (strcmp(cp, "-type") == 0)
-               {
-                       if ((argc <= 0) || (**argv == '-'))
-                       {
-                               fprintf(stderr, "Missing type string\n");
-
-                               return 1;
-                       }
-
-                       argc--;
-                       fileType = *argv++;
-               }
-               else if (strcmp(cp, "-name") == 0)
-               {
-                       if ((argc <= 0) || (**argv == '-'))
-                       {
-                               fprintf(stderr, "Missing file name\n");
-
-                               return 1;
-                       }
-
-                       argc--;
-                       filePattern = *argv++;
-               }
-               else if (strcmp(cp, "-size") == 0)
-               {
-                       if ((argc <= 0) || (**argv == '-'))
-                       {
-                               fprintf(stderr, "Missing file size\n");
-
-                               return 1;
-                       }
-
-                       argc--;
-                       cp = *argv++;
-
-                       fileSize = 0;
-
-                       while (isDecimal(*cp))
-                               fileSize = fileSize * 10 + (*cp++ - '0');
-
-                       if (*cp || (fileSize < 0))
-                       {
-                               fprintf(stderr, "Bad file size specified\n");
-
-                               return 1;
-                       }
-               }
-               else
-               {
-                       if (*cp != '-')
-                               fprintf(stderr, "Missing dash in option\n");
-                       else
-                               fprintf(stderr, "Unknown option\n");
-
-                       return 1;
-               }
-       }
-
-       /*
-        * Get information about the path and make sure that it
-        * is a directory.
-        */
-       if (stat(path, &statBuf) < 0)
-       {
-               fprintf(stderr, "Cannot stat \"%s\": %s\n", path,
-                       strerror(errno));
-
-               return 1;
-       }
-
-       if (!S_ISDIR(statBuf.st_mode))
-       {
-               fprintf(stderr, "Path \"%s\" is not a directory\n", path);
-
-               return 1;
-       }
-
-       /*
-        * Remember the device that this directory is on in case we need it.
-        */
-       xdevDevice = statBuf.st_dev;
-
-       /*
-        * If the directory meets the specified criteria, then print it out.
-        */
-       if (testFile(path, &statBuf))
-               printf("%s\n", path);
-
-       /*
-        * Now examine the files in the directory.
-        */
-       examineDirectory(path);
-
-       return 0;
-}
-
-
-/*
- * Recursive routine to examine the files in a directory.
- */
-static void
-examineDirectory(const char * path)
-{
-       DIR *           dir;
-       BOOL            needSlash;
-       struct dirent * entry;
-       struct stat     statBuf;
-       char            fullName[MAX_NAME_SIZE];
-
-       /*
-        * Open the directory.
-        */
-       dir = opendir(path);
-
-       if (dir == NULL)
-       {
-               fprintf(stderr, "Cannot read directory \"%s\": %s\n",
-                       path, strerror(errno));
-
-               return;
-       }
-
-       /*
-        * See if a slash is needed.
-        */
-       needSlash = (*path && (path[strlen(path) - 1] != '/'));
-
-       /*
-        * Read all of the directory entries and check them,
-        * except for the current and parent directory entries.
-        */
-       while (!intFlag && ((entry = readdir(dir)) != NULL))
-       {
-               if ((strcmp(entry->d_name, ".") == 0) ||
-                       (strcmp(entry->d_name, "..") == 0))
-               {
-                       continue;
-               }
-
-               /*
-                * Build the full path name.
-                */
-               strcpy(fullName, path);
-
-               if (needSlash)
-                       strcat(fullName, "/");
-
-               strcat(fullName, entry->d_name);
-
-               /*
-                * Find out about this file.
-                */
-               if (LSTAT(fullName, &statBuf) < 0)
-               {
-                       fprintf(stderr, "Cannot stat \"%s\": %s\n",
-                               fullName, strerror(errno));
-
-                       continue;
-               }
-
-               /*
-                * If this file matches the criteria that was
-                * specified then print its name out.
-                */
-               if (testFile(fullName, &statBuf))
-                       printf("%s\n", fullName);
-
-               /*
-                * If this is a directory and we are allowed to cross
-                * mount points or the directory is still on the same
-                * device, then examine it's files too.
-                */
-               if (S_ISDIR(statBuf.st_mode) &&
-                       (!xdevFlag || (statBuf.st_dev == xdevDevice)))
-               {
-                       examineDirectory(fullName);
-               }
-       }
-
-       closedir(dir);
-}
-
-
-/*
- * Test a file name having the specified status to see if it should
- * be acted on.  Returns TRUE if the file name has been selected.
- */
-static BOOL
-testFile(const char * fullName, const struct stat * statBuf)
-{
-       const char *    cp;
-       const char *    entryName;
-       BOOL            wantType;
-       int             mode;
-
-       mode = statBuf->st_mode;
-
-       /*
-        * Check the file type if it was specified.
-        */
-       if (fileType != NULL)
-       {
-               wantType = FALSE;
-
-               for (cp = fileType; *cp; cp++)
-               {
-                       switch (*cp)
-                       {
-                               case 'f':
-                                       if (S_ISREG(mode))
-                                               wantType = TRUE;
-                                       break;
-
-                               case 'd':
-                                       if (S_ISDIR(mode))
-                                               wantType = TRUE;
-
-                                       break;
-
-                               case 'p':
-                                       if (S_ISFIFO(mode))
-                                               wantType = TRUE;
-
-                                       break;
-
-                               case 'c':
-                                       if (S_ISCHR(mode))
-                                               wantType = TRUE;
-
-                                       break;
-
-                               case 'b':
-                                       if (S_ISBLK(mode))
-                                               wantType = TRUE;
-
-                                       break;
-
-                               case 's':
-                                       if (S_ISSOCK(mode))
-                                               wantType = TRUE;
-
-                                       break;
-
-#ifdef S_ISLNK
-                               case 'l':
-                                       if (S_ISLNK(mode))
-                                               wantType = TRUE;
-
-                                       break;
-#endif
-                               default:
-                                       break;
-                       }
-               }
-
-               if (!wantType)
-                       return FALSE;
-       }
-
-       /*
-        * Check the file size if it was specified.
-        * This check only lets regular files and directories through.
-        */
-       if (fileSize > 0)
-       {
-               if (!S_ISREG(mode) && !S_ISDIR(mode))
-                       return FALSE;
-
-               if (statBuf->st_size < fileSize)
-                       return FALSE;
-       }
-
-       /*
-        * Check the file name pattern if it was specified.
-        */
-       if (filePattern != NULL)
-       {
-               entryName = strrchr(fullName, '/');
-
-               if (entryName)
-                       entryName++;
-               else
-                       entryName = fullName;
-
-               if (!match(entryName, filePattern))
-                       return FALSE;
-       }
-
-       /*
-        * This file name is wanted.
-        */
-       return TRUE;
-}
-
-/* END CODE */
diff --git a/src/sash/cmd_grep.c b/src/sash/cmd_grep.c
deleted file mode 100644 (file)
index b9e0821..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "grep" built-in command.
- */
-
-#include <ctype.h>
-
-#include "sash.h"
-
-
-static BOOL    search
-       (const char * string, const char * word, BOOL ignoreCase);
-
-
-int
-do_grep(int argc, const char ** argv)
-{
-       FILE *          fp;
-       const char *    word;
-       const char *    name;
-       const char *    cp;
-       BOOL            tellName;
-       BOOL            ignoreCase;
-       BOOL            tellLine;
-       long            line;
-       char            buf[BUF_SIZE];
-       int             r;
-
-       r = 1;
-       ignoreCase = FALSE;
-       tellLine = FALSE;
-
-       argc--;
-       argv++;
-
-       if (**argv == '-')
-       {
-               argc--;
-               cp = *argv++;
-
-               while (*++cp) switch (*cp)
-               {
-                       case 'i':
-                               ignoreCase = TRUE;
-                               break;
-
-                       case 'n':
-                               tellLine = TRUE;
-                               break;
-
-                       default:
-                               fprintf(stderr, "Unknown option\n");
-
-                               return 1;
-               }
-       }
-
-       word = *argv++;
-       argc--;
-
-       tellName = (argc > 1);
-
-       while (argc-- > 0)
-       {
-               name = *argv++;
-
-               fp = fopen(name, "r");
-
-               if (fp == NULL)
-               {
-                       perror(name);
-                       r = 1;
-
-                       continue;
-               }
-
-               line = 0;
-
-               while (fgets(buf, sizeof(buf), fp))
-               {
-                       if (intFlag)
-                       {
-                               fclose(fp);
-
-                               return 1;
-                       }
-
-                       line++;
-
-                       cp = &buf[strlen(buf) - 1];
-
-                       if (*cp != '\n')
-                               fprintf(stderr, "%s: Line too long\n", name);
-
-                       if (search(buf, word, ignoreCase))
-                       {
-                               r = 0;
-                               if (tellName)
-                                       printf("%s: ", name);
-
-                               if (tellLine)
-                                       printf("%ld: ", line);
-
-                               fputs(buf, stdout);
-                       }
-               }
-
-               if (ferror(fp))
-                       perror(name);
-
-               fclose(fp);
-       }
-
-       return r;
-}
-
-
-/*
- * See if the specified word is found in the specified string.
- */
-static BOOL
-search(const char * string, const char * word, BOOL ignoreCase)
-{
-       const char *    cp1;
-       const char *    cp2;
-       int             len;
-       int             lowFirst;
-       int             ch1;
-       int             ch2;
-
-       len = strlen(word);
-
-       if (!ignoreCase)
-       {
-               while (TRUE)
-               {
-                       string = strchr(string, word[0]);
-
-                       if (string == NULL)
-                               return FALSE;
-
-                       if (memcmp(string, word, len) == 0)
-                               return TRUE;
-
-                       string++;
-               }
-       }
-
-       /*
-        * Here if we need to check case independence.
-        * Do the search by lower casing both strings.
-        */
-       lowFirst = *word;
-
-       if (isupper(lowFirst))
-               lowFirst = tolower(lowFirst);
-
-       while (TRUE)
-       {
-               while (*string && (*string != lowFirst) &&
-                       (!isupper(*string) || (tolower(*string) != lowFirst)))
-               {
-                       string++;
-               }
-
-               if (*string == '\0')
-                       return FALSE;
-
-               cp1 = string;
-               cp2 = word;
-
-               do
-               {
-                       if (*cp2 == '\0')
-                               return TRUE;
-
-                       ch1 = *cp1++;
-
-                       if (isupper(ch1))
-                               ch1 = tolower(ch1);
-
-                       ch2 = *cp2++;
-
-                       if (isupper(ch2))
-                               ch2 = tolower(ch2);
-
-               }
-               while (ch1 == ch2);
-
-               string++;
-       }
-}
-
-/* END CODE */
diff --git a/src/sash/cmd_gzip.c b/src/sash/cmd_gzip.c
deleted file mode 100644 (file)
index 6bcec11..0000000
+++ /dev/null
@@ -1,698 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "gzip" and "gunzip" built-in commands.
- * These commands are optionally built into sash.
- * This uses the zlib library by Jean-loup Gailly to compress and
- * uncompress the files.
- */
-
-#if    HAVE_GZIP
-
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <zlib.h>
-
-#include "sash.h"
-
-
-#define        GZ_EXT          ".gz"
-#define        TGZ_EXT         ".tgz"
-#define        Z_EXT           ".Z"
-#define        TAR_EXT         ".tar"
-#define        NO_EXT          ""
-
-
-/*
- * Tables of conversions to make to file extensions.
- */
-typedef        struct
-{
-       const char *    input;
-       const char *    output;
-} CONVERT;
-
-
-static const CONVERT   gzipConvertTable[] =
-{
-       {TAR_EXT,       TGZ_EXT},
-       {NO_EXT,        GZ_EXT}
-};
-
-
-static const CONVERT   gunzipConvertTable[] =
-{
-       {TGZ_EXT,       TAR_EXT},
-       {GZ_EXT,        NO_EXT},
-       {Z_EXT,         NO_EXT},
-       {NO_EXT,        NO_EXT}
-};
-
-
-/*
- * Local routines to compress and uncompress files.
- */
-static BOOL    gzip(const char * inputFile, const char * outputFile);
-static BOOL    gunzip(const char * inputFile, const char * outputFile);
-
-static const char * convertName
-       (const CONVERT * table, const char * inFile);
-
-
-int
-do_gzip(int argc, const char ** argv)
-{
-       const char *    outPath;
-       const char *    inFile;
-       const char *    outFile;
-       int             i;
-       int             r;
-
-       r = 0;
-       argc--;
-       argv++;
-
-       /*
-        * Look for the -o option if it is present.
-        * If present, it must be at the end of the command.
-        * Remember the output path and remove it if found.
-        */
-       outPath = NULL;
-
-       if ((argc >= 2) && (strcmp(argv[argc - 2], "-o") == 0) &&
-               (argv[argc - 1][0] != '-'))
-       {
-               argc -= 2;
-               outPath = argv[argc + 1];
-       }
-
-       /*
-        * Now make sure that there are no more options.
-        */
-       for (i = 0; i < argc; i++)
-       {
-               if (argv[i][0] == '-')
-               {
-                       if (strcmp(argv[i], "-o") == 0)
-                               fprintf(stderr, "Illegal use of -o\n");
-                       else
-                               fprintf(stderr, "Illegal option\n");
-
-                       return 1;
-               }
-       }
-       
-       /*
-        * If there is no output path specified, then compress each of
-        * the input files in place using their full paths.  The input
-        * file names are then deleted.
-        */
-       if (outPath == NULL)
-       {
-               while (!intFlag && (argc-- > 0))
-               {
-                       inFile = *argv++;
-
-                       outFile = convertName(gzipConvertTable, inFile);
-
-                       /*
-                        * Try to compress the file.
-                        */
-                       if (!gzip(inFile, outFile))
-                       {
-                               r = 1;
-
-                               continue;
-                       }
-
-                       /*
-                        * This was successful.
-                        * Try to delete the original file now.
-                        */
-                       if (unlink(inFile) < 0)
-                       {
-                               fprintf(stderr, "%s: %s\n", inFile,
-                                       "Compressed ok but unlink failed");
-
-                               r = 1;
-                       }
-               }
-
-               return r;
-       }
-
-       /*
-        * There is an output path specified.
-        * If it is not a directory, then either compress the single
-        * specified input file to the exactly specified output path,
-        * or else complain.
-        */
-       if (!isDirectory(outPath))
-       {
-               if (argc == 1)
-                       r = !gzip(*argv, outPath);
-               else
-               {
-                       fprintf(stderr, "Exactly one input file is required\n");
-                       r = 1;
-               }
-
-               return r;
-       }
-
-       /*
-        * There was an output directory specified.
-        * Compress each of the input files into the specified
-        * output directory, converting their extensions if possible.
-        */
-       while (!intFlag && (argc-- > 0))
-       {
-               inFile = *argv++;
-
-               /*
-                * Strip the path off of the input file name to make
-                * the beginnings of the output file name.
-                */
-               outFile = strrchr(inFile, '/');
-
-               if (outFile)
-                       outFile++;
-               else
-                       outFile = inFile;
-
-               /*
-                * Convert the extension of the output file name if possible.
-                * If we can't, then that is ok.
-                */
-               outFile = convertName(gzipConvertTable, outFile);
-
-               /*
-                * Now build the output path name by prefixing it with
-                * the output directory.
-                */
-               outFile = buildName(outPath, outFile);
-
-               /*
-                * Compress the input file without deleting the input file.
-                */
-               if (!gzip(inFile, outFile))
-                       r = 1;
-       }
-
-       return r;
-}
-
-
-int
-do_gunzip(int argc, const char ** argv)
-{
-       const char *    outPath;
-       const char *    inFile;
-       const char *    outFile;
-       int             i;
-       int             r;
-
-       r = 0;
-       argc--;
-       argv++;
-
-       /*
-        * Look for the -o option if it is present.
-        * If present, it must be at the end of the command.
-        * Remember the output path and remove it if found.
-        */
-       outPath = NULL;
-
-       if ((argc >= 2) && (strcmp(argv[argc - 2], "-o") == 0) &&
-               (argv[argc - 1][0] != '-'))
-       {
-               argc -= 2;
-               outPath = argv[argc + 1];
-       }
-
-       /*
-        * Now make sure that there are no more options.
-        */
-       for (i = 0; i < argc; i++)
-       {
-               if (argv[i][0] == '-')
-               {
-                       if (strcmp(argv[i], "-o") == 0)
-                               fprintf(stderr, "Illegal use of -o\n");
-                       else
-                               fprintf(stderr, "Illegal option\n");
-
-                       return 1;
-               }
-       }
-       
-       /*
-        * If there is no output path specified, then uncompress each of
-        * the input files in place using their full paths.  They must
-        * have one of the proper compression extensions which is converted.
-        * The input file names are then deleted.
-        */
-       if (outPath == NULL)
-       {
-               while (!intFlag && (argc-- > 0))
-               {
-                       inFile = *argv++;
-
-                       outFile = convertName(gunzipConvertTable, inFile);
-
-                       if (inFile == outFile)
-                       {
-                               fprintf(stderr, "%s: %s\n", inFile,
-                                       "missing compression extension");
-                               r = 1;
-
-                               continue;
-                       }
-
-                       /*
-                        * Try to uncompress the file.
-                        */
-                       if (!gunzip(inFile, outFile))
-                       {
-                               r = 1;
-                               continue;
-                       }
-
-                       /*
-                        * This was successful.
-                        * Try to delete the original file now.
-                        */
-                       if (unlink(inFile) < 0)
-                       {
-                               fprintf(stderr, "%s: %s\n", inFile,
-                                       "Uncompressed ok but unlink failed");
-                               r = 1;
-                       }
-               }
-
-               return r;
-       }
-
-       /*
-        * There is an output path specified.
-        * If the output path is a device file then uncompress each of
-        * the input files to the device file.
-        */
-       if (isDevice(outPath))
-       {
-               while (!intFlag && (argc-- > 0))
-               {
-                       if (!gunzip(*argv++, outPath))
-                               r = 1;
-               }
-
-               return r;
-       }
-
-       /*
-        * If the output path is not a directory then either uncompress the
-        * single specified input file to the exactly specified output path,
-        * or else complain.
-        */
-       if (!isDirectory(outPath))
-       {
-               if (argc == 1)
-                       return !gunzip(*argv, outPath);
-               else
-                       fprintf(stderr, "Exactly one input file is required\n");
-
-               return 1;
-       }
-
-       /*
-        * There was an output directory specified.
-        * Uncompress each of the input files into the specified
-        * output directory, converting their extensions if possible.
-        */
-       while (!intFlag && (argc-- > 0))
-       {
-               inFile = *argv++;
-
-               /*
-                * Strip the path off of the input file name to make
-                * the beginnings of the output file name.
-                */
-               outFile = strrchr(inFile, '/');
-
-               if (outFile)
-                       outFile++;
-               else
-                       outFile = inFile;
-
-               /*
-                * Convert the extension of the output file name if possible.
-                * If we can't, then that is ok.
-                */
-               outFile = convertName(gunzipConvertTable, outFile);
-
-               /*
-                * Now build the output path name by prefixing it with
-                * the output directory.
-                */
-               outFile = buildName(outPath, outFile);
-
-               /*
-                * Uncompress the input file without deleting the input file.
-                */
-               if (!gunzip(inFile, outFile))
-                       r = 1;
-       }
-
-       return r;
-}
-
-
-/*
- * Compress the specified input file to produce the output file.
- * Returns TRUE if successful.
- */
-static BOOL
-gzip(const char * inputFileName, const char * outputFileName)
-{
-       gzFile          outGZ;
-       int             inFD;
-       int             len;
-       int             err;
-       struct  stat    statBuf1;
-       struct  stat    statBuf2;
-       char            buf[BUF_SIZE];
-
-       outGZ = NULL;
-       inFD = -1;
-
-       /*
-        * See if the output file is the same as the input file.
-        * If so, complain about it.
-        */
-       if (stat(inputFileName, &statBuf1) < 0)
-       {
-               perror(inputFileName);
-
-               return FALSE;
-       }
-
-       if (stat(outputFileName, &statBuf2) < 0)
-       {
-               statBuf2.st_ino = -1;
-               statBuf2.st_dev = -1;
-       }
-
-       if ((statBuf1.st_dev == statBuf2.st_dev) &&
-               (statBuf1.st_ino == statBuf2.st_ino))
-       {
-               fprintf(stderr,
-                       "Cannot compress file \"%s\" on top of itself\n",
-                       inputFileName);
-
-               return FALSE;
-       }
-
-       /*
-        * Open the input file.
-        */
-       inFD = open(inputFileName, O_RDONLY);
-
-       if (inFD < 0)
-       {
-               perror(inputFileName);
-
-               goto failed;
-       }
-
-       /*
-        * Ask the zlib library to open the output file.
-        */
-       outGZ = gzopen(outputFileName, "wb9");
-
-       if (outGZ == NULL)
-       {
-               fprintf(stderr, "%s: gzopen failed\n", outputFileName);
-
-               goto failed;
-       }
-
-       /*
-        * Read the uncompressed data from the input file and write
-        * the compressed data to the output file.
-        */
-       while ((len = read(inFD, buf, sizeof(buf))) > 0)
-       {
-               if (gzwrite(outGZ, buf, len) != len)
-               {
-                       fprintf(stderr, "%s: %s\n", inputFileName,
-                               gzerror(outGZ, &err));
-
-                       goto failed;
-               }
-
-               if (intFlag)
-                       goto failed;
-       }
-
-       if (len < 0)
-       {
-               perror(inputFileName);
-
-               goto failed;
-       }
-
-       /*
-        * All done, close the files.
-        */
-       if (close(inFD))
-       {
-               perror(inputFileName);
-
-               goto failed;
-       }
-
-       inFD = -1;
-
-       if (gzclose(outGZ) != Z_OK)
-       {
-               fprintf(stderr, "%s: gzclose failed\n", outputFileName);
-
-               goto failed;
-       }
-
-       outGZ = NULL;
-
-       /*
-        * Success.
-        */
-       return TRUE;
-
-
-/*
- * Here on an error, to clean up.
- */
-failed:
-       if (inFD >= 0)
-               (void) close(inFD);
-
-       if (outGZ != NULL)
-               (void) gzclose(outGZ);
-
-       return FALSE;
-}
-
-
-/*
- * Uncompress the input file to produce the output file.
- * Returns TRUE if successful.
- */
-static BOOL
-gunzip(const char * inputFileName, const char * outputFileName)
-{
-       gzFile          inGZ;
-       int             outFD;
-       int             len;
-       int             err;
-       struct  stat    statBuf1;
-       struct  stat    statBuf2;
-       char            buf[BUF_SIZE];
-
-       inGZ = NULL;
-       outFD = -1;
-
-       /*
-        * See if the output file is the same as the input file.
-        * If so, complain about it.
-        */
-       if (stat(inputFileName, &statBuf1) < 0)
-       {
-               perror(inputFileName);
-
-               return FALSE;
-       }
-
-       if (stat(outputFileName, &statBuf2) < 0)
-       {
-               statBuf2.st_ino = -1;
-               statBuf2.st_dev = -1;
-       }
-
-       if ((statBuf1.st_dev == statBuf2.st_dev) &&
-               (statBuf1.st_ino == statBuf2.st_ino))
-       {
-               fprintf(stderr,
-                       "Cannot uncompress file \"%s\" on top of itself\n",
-                       inputFileName);
-
-               return FALSE;
-       }
-
-       /*
-        * Ask the zlib library to open the input file.
-        */
-       inGZ = gzopen(inputFileName, "rb");
-
-       if (inGZ == NULL)
-       {
-               fprintf(stderr, "%s: gzopen failed\n", inputFileName);
-
-               return FALSE;
-       }
-
-       /*
-        * Create the output file.
-        */
-       if (isDevice(outputFileName))
-               outFD = open(outputFileName, O_WRONLY);
-       else
-               outFD = open(outputFileName, O_WRONLY|O_CREAT|O_TRUNC, 0666);
-
-       if (outFD < 0)
-       {
-               perror(outputFileName);
-
-               goto failed;
-       }
-
-       /*
-        * Read the compressed data from the input file and write
-        * the uncompressed data extracted from it to the output file.
-        */
-       while ((len = gzread(inGZ, buf, sizeof(buf))) > 0)
-       {
-               if (fullWrite(outFD, buf, len) < 0)
-               {
-                       perror(outputFileName);
-
-                       goto failed;
-               }
-
-               if (intFlag)
-                       goto failed;
-       }
-
-       if (len < 0)
-       {
-               fprintf(stderr, "%s: %s\n", inputFileName,
-                       gzerror(inGZ, &err));
-
-               goto failed;
-       }
-
-       /*
-        * All done, close the files.
-        */
-       if (close(outFD))
-       {
-               perror(outputFileName);
-
-               goto failed;
-       }
-
-       outFD = -1;
-
-       if (gzclose(inGZ) != Z_OK)
-       {
-               fprintf(stderr, "%s: gzclose failed\n", inputFileName);
-
-               goto failed;
-       }
-
-       inGZ = NULL;
-
-       /*
-        * Success.
-        */
-       return TRUE;
-
-
-/*
- * Here on an error, to clean up.
- */
-failed:
-       if (outFD >= 0)
-               (void) close(outFD);
-
-       if (inGZ != NULL)
-               (void) gzclose(inGZ);
-
-       return FALSE;
-}
-
-
-/*
- * Convert an input file name to an output file name according to
- * the specified convertion table.  The returned name points into a
- * static buffer which is overwritten on each call.  The table must
- * end in an entry which always specifies a successful conversion.
- * If no conversion is done the original input file name pointer is
- * returned.
- */
-const char *
-convertName(const CONVERT * table, const char * inputFile)
-{
-       int             inputLength;
-       int             testLength;
-       static char     buf[PATH_LEN];
-
-       inputLength = strlen(inputFile);
-
-       for (;;)
-       {
-               testLength = strlen(table->input);
-
-               if ((inputLength >= testLength) &&
-                       (memcmp(table->input,
-                               inputFile + inputLength - testLength,
-                               testLength) == 0))
-               {
-                       break;
-               }
-
-               table++;
-       }
-
-       /*
-        * If no conversion was done, return the original pointer.
-        */
-       if ((testLength == 0) && (table->output[0] == '\0'))
-               return inputFile;
-
-       /*
-        * Build the new name and return it.
-        */
-       memcpy(buf, inputFile, inputLength - testLength);
-
-       memcpy(buf + inputLength - testLength, table->output,
-               strlen(table->output) + 1);
-
-       return buf;
-}
-
-
-#endif
-
-/* END CODE */
diff --git a/src/sash/cmd_ls.c b/src/sash/cmd_ls.c
deleted file mode 100644 (file)
index 41f31cc..0000000
+++ /dev/null
@@ -1,597 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "ls" built-in command.
- */
-
-#include "sash.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "dirent.h"
-#include <pwd.h>
-#include <grp.h>
-
-
-#define        LISTSIZE        8192
-
-
-#ifdef S_ISLNK
-#define        LSTAT   lstat
-#else
-#define        LSTAT   stat
-#endif
-
-
-/*
- * Flags for the LS command.
- */
-#define        LSF_LONG        0x01
-#define        LSF_DIR         0x02
-#define        LSF_INODE       0x04
-#define        LSF_MULT        0x08
-#define        LSF_FLAG        0x10
-#define        LSF_COLUMN      0x20
-#define        LSF_NUMERIC     0x40
-
-
-/*
- * Data holding list of files.
- */
-static char ** list;
-static int     listSize;
-static int     listUsed;
-
-/*
- * Cached user and group name data.
- */
-static char    userName[12];
-static int     userId;
-static BOOL    userIdKnown;
-static char    groupName[12];
-static int     groupId;
-static BOOL    groupIdKnown;
-
-
-/*
- * Local procedures.
- */
-static void    listFile(
-       const char *            name,
-       const struct stat *     statBuf,
-       int                     flags,
-       int                     width
-);
-
-static BOOL    addListName(const char * fileName);
-static void    listAllFiles(int flags, int displayWidth);
-static void    clearListNames(void);
-
-
-int
-do_ls(int argc, const char ** argv)
-{
-       const char *    cp;
-       const char *    name;
-       int             flags;
-       int             i;
-       int             displayWidth;
-       BOOL            endSlash;
-       DIR *           dirp;
-       struct dirent * dp;
-       char            fullName[PATH_LEN];
-       struct  stat    statBuf;
-       int             r;
-
-       static const char *     def[] = {"."};
-
-       /*
-        * Reset for a new listing run.
-        */
-       clearListNames();
-
-       userIdKnown = FALSE;
-       groupIdKnown = FALSE;
-
-       displayWidth = 0;
-       flags = 0;
-
-       /*
-        * Handle options.
-        */
-       argc--;
-       argv++;
-
-       while ((argc > 0) && (**argv == '-'))
-       {
-               cp = *argv++ + 1;
-               argc--;
-
-               while (*cp) switch (*cp++)
-               {
-                       case 'l':       flags |= LSF_LONG; break;
-                       case 'n':       flags |= LSF_NUMERIC; break;
-                       case 'd':       flags |= LSF_DIR; break;
-                       case 'i':       flags |= LSF_INODE; break;
-                       case 'F':       flags |= LSF_FLAG; break;
-                       case 'C':       flags |= LSF_COLUMN; break;
-
-                       default:
-                               fprintf(stderr, "Unknown option -%c\n", cp[-1]);
-
-                               return 1;
-               }
-       }
-
-       /*
-        * If long or numeric listing is specified then turn off column listing.
-        */
-       if (flags & (LSF_LONG | LSF_NUMERIC))
-               flags &= ~LSF_COLUMN;
-
-       /*
-        * If column listing is specified then calculate the maximum
-        * width available for the columns of file names.
-        * This is settable using the COLS environment variable.
-        */
-       if (flags & LSF_COLUMN)
-       {
-               name = getenv("COLS");
-
-               if (name)
-                       displayWidth = atoi(name);
-
-               if (displayWidth <= 0)
-                       displayWidth = 80;
-       }
-
-       /*
-        * If no arguments are given set up to show the current directory.
-        */
-       if (argc <= 0)
-       {
-               argc = 1;
-               argv = def;
-       }
-
-       if (argc > 1)
-               flags |= LSF_MULT;
-
-       /*
-        * Make one pass over the file names to collect together
-        * all of the files which are not directories.
-        * We will process them all as one list.
-        */
-       for (i = 0; i < argc; i++)
-       {
-               if ((flags & LSF_DIR) || !isDirectory(argv[i]))
-               {
-                       if (!addListName(argv[i]))
-                               return 1;
-               }
-       }
-
-       /*
-        * List those file names, and then clear the list.
-        */
-       listAllFiles(flags, displayWidth);
-       clearListNames();
-
-       /*
-        * If directories were being listed as themselves, then we are done.
-        */
-       if (flags & LSF_DIR)
-               return r;
-
-       /*
-        * Now iterate over the file names processing the directories.
-        */
-       while (!intFlag && (argc-- > 0))
-       {
-               name = *argv++;
-               endSlash = (*name && (name[strlen(name) - 1] == '/'));
-
-               if (LSTAT(name, &statBuf) < 0)
-               {
-                       perror(name);
-                       r = 1;
-
-                       continue;
-               }
-
-               /*
-                * If this file name is not a directory, then ignore it.
-                */
-               if (!S_ISDIR(statBuf.st_mode))
-                       continue;
-
-               /*
-                * Collect all the files in the directory.
-                */
-               dirp = opendir(name);
-
-               if (dirp == NULL)
-               {
-                       perror(name);
-
-                       continue;
-               }
-
-               if (flags & LSF_MULT)
-                       printf("\n%s:\n", name);
-
-               while (!intFlag && ((dp = readdir(dirp)) != NULL))
-               {
-                       fullName[0] = '\0';
-
-                       if ((*name != '.') || (name[1] != '\0'))
-                       {
-                               strcpy(fullName, name);
-
-                               if (!endSlash)
-                                       strcat(fullName, "/");
-                       }
-
-                       strcat(fullName, dp->d_name);
-
-                       /*
-                        * Save the file name in the list.
-                        */
-                       if (!addListName(fullName))
-                       {
-                               closedir(dirp);
-
-                               return 1;
-                       }
-               }
-
-               closedir(dirp);
-
-               /*
-                * List the files we collected in this directory,
-                * and then clear the list.
-                */
-               listAllFiles(flags, displayWidth);
-               clearListNames();
-       }
-
-       return r;
-}
-
-
-/*
- * List all of the files in the current list of files.
- * The files are displayed according to the specified flags,
- * in the specified display width.
- */
-static void
-listAllFiles(int flags, int displayWidth)
-{
-       const char *    name;
-       const char *    cp;
-       int             fileWidth;
-       int             column;
-       int             len;
-       int             i;
-       struct stat     statBuf;
-
-       /*
-        * Initialise width data until we need it.
-        */
-       fileWidth = 0;
-       column = 0;
-
-       /*
-        * Sort the files in the list.
-        */
-       qsort((void *) list, listUsed, sizeof(char *), nameSort);
-
-       /*
-        * If we are showing the files in columns then calculate the
-        * maximum width of all of the file names, taking into account
-        * various factors.
-        */
-       if (flags & LSF_COLUMN)
-       {
-               for (i = 0; i < listUsed; i++)
-               {
-                       len = strlen(list[i]);
-
-                       if (fileWidth < len)
-                               fileWidth = len;
-               }
-
-               if (flags & LSF_FLAG)
-                       fileWidth++;
-
-               if (flags & LSF_INODE)
-                       fileWidth += 8;
-
-               fileWidth += 2;
-       }
-
-       /*
-        * Now list the fileNames.
-        */
-       for (i = 0; i < listUsed; i++)
-       {
-               name = list[i];
-
-               if (LSTAT(name, &statBuf) < 0)
-               {
-                       perror(name);
-
-                       continue;
-               }
-
-               cp = strrchr(name, '/');
-
-               if (cp)
-                       cp++;
-               else
-                       cp = name;
-
-               /*
-                * List the file in the next column or at the end
-                * of a line depending on the width left.
-                */
-               if (column + fileWidth * 2 >= displayWidth)
-               {
-                       listFile(cp, &statBuf, flags, 0);
-                       column = 0;
-               }
-               else
-               {
-                       listFile(cp, &statBuf, flags, fileWidth);
-                       column += fileWidth;
-               }
-       }
-
-       /*
-        * Terminate the last file name if necessary.
-        */
-       if (column > 0)
-               fputc('\n', stdout);
-}
-
-
-/*
- * Do a listing of a particular file name according to the flags.
- * The output is shown within the specified width if it is nonzero,
- * or on its own line if the width is zero.
- */
-static void
-listFile(
-       const char *            name,
-       const struct stat *     statBuf,
-       int                     flags,
-       int                     width
-)
-{
-       char *          cp;
-       struct passwd * pwd;
-       struct group *  grp;
-       int             len;
-       int             mode;
-       int             flagChar;
-       int             usedWidth;
-       char            buf[PATH_LEN];
-
-       mode = statBuf->st_mode;
-
-       /*
-        * Initialise buffers for use.
-        */
-       cp = buf;
-       buf[0] = '\0';
-       flagChar = '\0';
-
-       /*
-        * Show the inode number if requested.
-        */
-       if (flags & LSF_INODE)
-       {
-               sprintf(cp, "%7ld ", statBuf->st_ino);
-               cp += strlen(cp);
-       }
-
-       /*
-        * Create the long or numeric status line if requested.
-        */
-       if (flags & (LSF_LONG | LSF_NUMERIC))
-       {
-               strcpy(cp, modeString(mode));
-               cp += strlen(cp);
-
-               sprintf(cp, "%3ld ", (long) statBuf->st_nlink);
-               cp += strlen(cp);
-
-               if (!userIdKnown || (statBuf->st_uid != userId))
-               {
-                       if (flags & LSF_NUMERIC)
-                               pwd = 0;
-                       else
-                               pwd = getpwuid(statBuf->st_uid);
-
-                       if (pwd)
-                               strcpy(userName, pwd->pw_name);
-                       else
-                               sprintf(userName, "%d", statBuf->st_uid);
-
-                       userId = statBuf->st_uid;
-                       userIdKnown = TRUE;
-               }
-
-               sprintf(cp, "%-8s ", userName);
-               cp += strlen(cp);
-
-               if (!groupIdKnown || (statBuf->st_gid != groupId))
-               {
-                       if (flags & LSF_NUMERIC)
-                               grp = 0;
-                       else
-                               grp = getgrgid(statBuf->st_gid);
-
-                       if (grp)
-                               strcpy(groupName, grp->gr_name);
-                       else
-                               sprintf(groupName, "%d", statBuf->st_gid);
-
-                       groupId = statBuf->st_gid;
-                       groupIdKnown = TRUE;
-               }
-
-               sprintf(cp, "%-8s ", groupName);
-               cp += strlen(cp);
-
-               if (S_ISBLK(mode) || S_ISCHR(mode))
-               {
-                       sprintf(cp, "%3lu, %3lu ",
-                               ((unsigned long) statBuf->st_rdev) >> 8,
-                               ((unsigned long) statBuf->st_rdev) & 0xff);
-               }
-               else
-                       sprintf(cp, "%8ld ", statBuf->st_size);
-
-               cp += strlen(cp);
-
-               sprintf(cp, " %-12s ", timeString(statBuf->st_mtime));
-       }
-
-       /*
-        * Set the special character if the file is a directory or
-        * symbolic link or executable and the display was requested.
-        */
-       if (flags & LSF_FLAG)
-       {
-               if (S_ISDIR(mode))
-                       flagChar = '/';
-#ifdef S_ISLNK
-               else if (S_ISLNK(mode))
-                       flagChar = '@';
-#endif
-               else if ((mode & 0111) != 0)
-                       flagChar = '*';
-       }
-
-       /*
-        * Print the status info followed by the file name.
-        */
-       fputs(buf, stdout);
-       fputs(name, stdout);
-
-       if (flagChar)
-               fputc(flagChar, stdout);
-
-       /*
-        * Calculate the width used so far.
-        */
-       usedWidth = strlen(buf) + strlen(name);
-
-       if (flagChar)
-               usedWidth++;
-
-       /*
-        * Show where a symbolic link points.
-        */
-#ifdef S_ISLNK
-       if ((flags & LSF_LONG) && S_ISLNK(mode))
-       {
-               len = readlink(name, buf, PATH_LEN - 1);
-
-               if (len >= 0)
-               {
-                       buf[len] = '\0';
-                       printf(" -> %s", buf);
-               }
-
-               usedWidth += strlen(buf) + 4;
-       }
-#endif
-
-       /*
-        * If no width was given then just end the line with a newline.
-        */
-       if (width == 0)
-       {
-               fputc('\n', stdout);
-
-               return;
-       }
-
-       /*
-        * There is a width given.
-        * Print as many spaces as it takes to reach that width.
-        */
-       while (usedWidth++ < width)
-               fputc(' ', stdout);
-}
-
-
-/*
- * Save a file name to the end of the static list, reallocating if necessary.
- * The file name is copied into allocated memory owned by the list.
- * Returns TRUE on success.
- */
-static BOOL
-addListName(const char * fileName)
-{
-       char ** newList;
-
-       /*
-        * Reallocate the list if necessary.
-        */
-       if (listUsed >= listSize)
-       {
-               newList = realloc(list,
-                       ((sizeof(char **)) * (listSize + LISTSIZE)));
-
-               if (newList == NULL)
-               {
-                       fprintf(stderr, "No memory for file name buffer\n");
-
-                       return FALSE;
-               }
-
-               list = newList;
-               listSize += LISTSIZE;
-       }
-
-       /*
-        * Copy the file name into the next entry.
-        */
-       list[listUsed] = strdup(fileName);
-
-       if (list[listUsed] == NULL)
-       {
-               fprintf(stderr, "No memory for file name\n");
-
-               return FALSE;
-       }
-
-       /*
-        * Increment the amount of space used.
-        */
-       listUsed++;
-
-       return TRUE;
-}
-
-
-/*
- * Free all of the names from the list of file names.
- */
-static void
-clearListNames(void)
-{
-       while (listUsed > 0)
-       {
-               listUsed--;
-
-               free(list[listUsed]);
-       }
-}
-
-/* END CODE */
diff --git a/src/sash/cmd_tar.c b/src/sash/cmd_tar.c
deleted file mode 100644 (file)
index 5dd5f21..0000000
+++ /dev/null
@@ -1,1277 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "tar" built-in command.
- * This allows creation, extraction, and listing of tar files.
- */
-
-#include "sash.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <errno.h>
-
-
-/*
- * Tar file constants.
- */
-#define TAR_BLOCK_SIZE 512
-#define TAR_NAME_SIZE  100
-
-
-/*
- * The POSIX (and basic GNU) tar header format.
- * This structure is always embedded in a TAR_BLOCK_SIZE sized block
- * with zero padding.  We only process this information minimally.
- */
-typedef struct
-{
-       char    name[TAR_NAME_SIZE];
-       char    mode[8];
-       char    uid[8];
-       char    gid[8];
-       char    size[12];
-       char    mtime[12];
-       char    checkSum[8];
-       char    typeFlag;
-       char    linkName[TAR_NAME_SIZE];
-       char    magic[6];
-       char    version[2];
-       char    uname[32];
-       char    gname[32];
-       char    devMajor[8];
-       char    devMinor[8];
-       char    prefix[155];
-} TarHeader;
-
-
-#define        TAR_MAGIC       "ustar"
-#define        TAR_VERSION     "00"
-
-#define        TAR_TYPE_REGULAR        '0'
-#define        TAR_TYPE_HARD_LINK      '1'
-#define        TAR_TYPE_SOFT_LINK      '2'
-
-
-/*
- * Static data.
- */
-static BOOL            listFlag;
-static BOOL            extractFlag;
-static BOOL            createFlag;
-static BOOL            verboseFlag;
-
-static BOOL            inHeader;
-static BOOL            badHeader;
-static BOOL            errorFlag;
-static BOOL            skipFileFlag;
-static BOOL            warnedRoot;
-static BOOL            eofFlag;
-static long            dataCc;
-static int             outFd;
-static char            outName[TAR_NAME_SIZE];
-
-
-/*
- * Static data associated with the tar file.
- */
-static const char *    tarName;
-static int             tarFd;
-static dev_t           tarDev;
-static ino_t           tarInode;
-
-
-/*
- * Local procedures to restore files from a tar file.
- */
-static BOOL    readTarFile(int fileCount, const char ** fileTable);
-static BOOL    readData(const char * cp, int count);
-static BOOL    createPath(const char * name, int mode);
-static long    getOctal(const char * cp, int len);
-
-static BOOL    readHeader(const TarHeader * hp,
-                       int fileCount, const char ** fileTable);
-
-
-/*
- * Local procedures to save files into a tar file.
- */
-static void    saveFile(const char * fileName, BOOL seeLinks);
-
-static void    saveRegularFile(const char * fileName,
-                       const struct stat * statbuf);
-
-static void    saveDirectory(const char * fileName,
-                       const struct stat * statbuf);
-
-static BOOL    wantFileName(const char * fileName,
-                       int fileCount, const char ** fileTable);
-
-static void    writeHeader(const char * fileName,
-                       const struct stat * statbuf);
-
-static BOOL    writeTarFile(int fileCount, const char ** fileTable);
-static void    writeTarBlock(const char * buf, int len);
-static BOOL    putOctal(char * cp, int len, long value);
-
-
-
-int
-do_tar(int argc, const char ** argv)
-{
-       const char *    options;
-       BOOL            successFlag;
-
-       argc--;
-       argv++;
-
-       if (argc < 2)
-       {
-               fprintf(stderr, "Too few arguments for tar\n");
-
-               return 1;
-       }
-
-       extractFlag = FALSE;
-       createFlag = FALSE;
-       listFlag = FALSE;
-       verboseFlag = FALSE;
-       tarName = NULL;
-       tarDev = 0;
-       tarInode = 0;
-       tarFd = -1;
-
-       /*
-        * Parse the options.
-        */
-       options = *argv++;
-       argc--;
-
-       for (; *options; options++)
-       {
-               switch (*options)
-               {
-                       case 'f':
-                               if (tarName != NULL)
-                               {
-                                       fprintf(stderr, "Only one 'f' option allowed\n");
-
-                                       return 1;
-                               }
-
-                               tarName = *argv++;
-                               argc--;
-
-                               break;
-
-                       case 't':
-                               listFlag = TRUE;
-                               break;
-
-                       case 'x':
-                               extractFlag = TRUE;
-                               break;
-
-                       case 'c':
-                               createFlag = TRUE;
-                               break;
-
-                       case 'v':
-                               verboseFlag = TRUE;
-                               break;
-
-                       default:
-                               fprintf(stderr, "Unknown tar flag '%c'\n", *options);
-
-                               return 1;
-               }
-       }
-
-       /*
-        * Validate the options.
-        */
-       if (extractFlag + listFlag + createFlag != 1)
-       {
-               fprintf(stderr, "Exactly one of 'c', 'x' or 't' must be specified\n");
-
-               return 1;
-       }
-
-       if (tarName == NULL)
-       {
-               fprintf(stderr, "The 'f' flag must be specified\n");
-
-               return 1;
-       }
-
-       /*
-        * Do the correct type of action supplying the rest of the
-        * command line arguments as the list of files to process.
-        */
-       if (createFlag)
-               successFlag = writeTarFile(argc, argv);
-       else
-               successFlag = readTarFile(argc, argv);
-
-       return !successFlag;
-}
-
-
-/*
- * Read a tar file and extract or list the specified files within it.
- * If the list is empty than all files are extracted or listed.
- * Returns TRUE on success.
- */
-static BOOL
-readTarFile(int fileCount, const char ** fileTable)
-{
-       const char *    cp;
-       BOOL            successFlag;
-       int             cc;
-       int             inCc;
-       int             blockSize;
-       char            buf[BUF_SIZE];
-
-       skipFileFlag = FALSE;
-       badHeader = FALSE;
-       warnedRoot = FALSE;
-       eofFlag = FALSE;
-       inHeader = TRUE;
-       successFlag = TRUE;
-
-       inCc = 0;
-       dataCc = 0;
-       outFd = -1;
-       blockSize = sizeof(buf);
-       cp = buf;
-
-       /*
-        * Open the tar file for reading.
-        */
-       tarFd = open(tarName, O_RDONLY);
-
-       if (tarFd < 0)
-       {
-               perror(tarName);
-
-               return FALSE;
-       }
-
-       /*
-        * Read blocks from the file until an end of file header block
-        * has been seen.  (A real end of file from a read is an error.)
-        */
-       while (!intFlag && !eofFlag)
-       {
-               /*
-                * Read the next block of data if necessary.
-                * This will be a large block if possible, which we will
-                * then process in the small tar blocks.
-                */
-               if (inCc <= 0)
-               {
-                       cp = buf;
-                       inCc = fullRead(tarFd, buf, blockSize);
-
-                       if (inCc < 0)
-                       {
-                               perror(tarName);
-                               successFlag = FALSE;
-
-                               goto done;
-                       }
-
-                       if (inCc == 0)
-                       {
-                               fprintf(stderr,
-                                       "Unexpected end of file from \"%s\"",
-                                       tarName);
-                               successFlag = FALSE;
-
-                               goto done;
-                       }
-               }
-
-               /*
-                * If we are expecting a header block then examine it.
-                */
-               if (inHeader)
-               {
-                       if (!readHeader((const TarHeader *) cp, fileCount, fileTable))
-                               successFlag = FALSE;
-
-                       cp += TAR_BLOCK_SIZE;
-                       inCc -= TAR_BLOCK_SIZE;
-
-                       continue;
-               }
-
-               /*
-                * We are currently handling the data for a file.
-                * Process the minimum of the amount of data we have available
-                * and the amount left to be processed for the file.
-                */
-               cc = inCc;
-
-               if (cc > dataCc)
-                       cc = dataCc;
-
-               if (!readData(cp, cc))
-                       successFlag = FALSE;
-
-               /*
-                * If the amount left isn't an exact multiple of the tar block
-                * size then round it up to the next block boundary since there
-                * is padding at the end of the file.
-                */
-               if (cc % TAR_BLOCK_SIZE)
-                       cc += TAR_BLOCK_SIZE - (cc % TAR_BLOCK_SIZE);
-
-               cp += cc;
-               inCc -= cc;
-       }
-
-       /*
-        * Check for an interrupt.
-        */
-       if (intFlag)
-       {
-               fprintf(stderr, "Interrupted - aborting\n");
-               successFlag = FALSE;
-       }
-
-done:
-       /*
-        * Close the tar file if needed.
-        */
-       if ((tarFd >= 0) && (close(tarFd) < 0))
-       {
-               perror(tarName);
-               successFlag = FALSE;
-       }
-
-       /*
-        * Close the output file if needed.
-        * This is only done here on a previous error and so no
-        * message is required on errors.
-        */
-       if (outFd >= 0)
-               (void) close(outFd);
-
-       return successFlag;
-}
-
-
-/*
- * Examine the header block that was just read.
- * This can specify the information for another file, or it can mark
- * the end of the tar file.  Returns TRUE on success.
- */
-static BOOL
-readHeader(const TarHeader * hp, int fileCount, const char ** fileTable)
-{
-       int             mode;
-       int             uid;
-       int             gid;
-       long            size;
-       time_t          mtime;
-       const char *    name;
-       int             cc;
-       BOOL            hardLink;
-       BOOL            softLink;
-
-       /*
-        * If the block is completely empty, then this is the end of the
-        * archive file.  If the name is null, then just skip this header.
-        */
-       name = hp->name;
-
-       if (*name == '\0')
-       {
-               for (cc = TAR_BLOCK_SIZE; cc > 0; cc--)
-               {
-                       if (*name++)
-                               return TRUE;
-               }
-
-               eofFlag = TRUE;
-
-               return TRUE;
-       }
-
-       /*
-        * There is another file in the archive to examine.
-        * Extract the encoded information and check it.
-        */
-       mode = getOctal(hp->mode, sizeof(hp->mode));
-       uid = getOctal(hp->uid, sizeof(hp->uid));
-       gid = getOctal(hp->gid, sizeof(hp->gid));
-       size = getOctal(hp->size, sizeof(hp->size));
-       mtime = getOctal(hp->mtime, sizeof(hp->mtime));
-
-       if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0))
-       {
-               if (!badHeader)
-                       fprintf(stderr, "Bad tar header, skipping\n");
-
-               badHeader = TRUE;
-
-               return FALSE;
-       }
-
-       badHeader = FALSE;
-       skipFileFlag = FALSE;
-
-       /*
-        * Check for the file modes.
-        */
-       hardLink = ((hp->typeFlag == TAR_TYPE_HARD_LINK) ||
-               (hp->typeFlag == TAR_TYPE_HARD_LINK - '0'));
-
-       softLink = ((hp->typeFlag == TAR_TYPE_SOFT_LINK) ||
-               (hp->typeFlag == TAR_TYPE_SOFT_LINK - '0'));
-
-       /*
-        * Check for a directory or a regular file.
-        */
-       if (name[strlen(name) - 1] == '/')
-               mode |= S_IFDIR;
-       else if ((mode & S_IFMT) == 0)
-               mode |= S_IFREG;
-
-       /*
-        * Check for absolute paths in the file.
-        * If we find any, then warn the user and make them relative.
-        */
-       if (*name == '/')
-       {
-               while (*name == '/')
-                       name++;
-
-               if (!warnedRoot)
-               {
-                       fprintf(stderr,
-                       "Absolute path detected, removing leading slashes\n");
-               }
-
-               warnedRoot = TRUE;
-       }
-
-       /*
-        * See if we want this file to be restored.
-        * If not, then set up to skip it.
-        */
-       if (!wantFileName(name, fileCount, fileTable))
-       {
-               if (!hardLink && !softLink && S_ISREG(mode))
-               {
-                       inHeader = (size == 0);
-                       dataCc = size;
-               }
-
-               skipFileFlag = TRUE;
-
-               return TRUE;
-       }
-
-       /*
-        * This file is to be handled.
-        * If we aren't extracting then just list information about the file.
-        */
-       if (!extractFlag)
-       {
-               if (verboseFlag)
-               {
-                       printf("%s %3d/%-d %9ld %s %s", modeString(mode),
-                               uid, gid, size, timeString(mtime), name);
-               }
-               else
-                       printf("%s", name);
-
-               if (hardLink)
-                       printf(" (link to \"%s\")", hp->linkName);
-               else if (softLink)
-                       printf(" (symlink to \"%s\")", hp->linkName);
-               else if (S_ISREG(mode))
-               {
-                       inHeader = (size == 0);
-                       dataCc = size;
-               }
-
-               printf("\n");
-
-               return TRUE;
-       }
-
-       /*
-        * We really want to extract the file.
-        */
-       if (verboseFlag)
-               printf("x %s\n", name);
-
-       if (hardLink)
-       {
-               if (link(hp->linkName, name) < 0)
-               {
-                       perror(name);
-                       return FALSE;
-               }
-
-               return TRUE;
-       }
-
-       if (softLink)
-       {
-#ifdef S_ISLNK
-               if (symlink(hp->linkName, name) < 0)
-               {
-                       perror(name);
-
-                       return FALSE;
-               }
-
-               return TRUE;
-#else
-               fprintf(stderr, "Cannot create symbolic links\n");
-#endif
-               return FALSE;
-       }
-
-       /*
-        * If the file is a directory, then just create the path.
-        */
-       if (S_ISDIR(mode))
-               return createPath(name, mode);
-
-       /*
-        * There is a file to write.
-        * First create the path to it if necessary with a default permission.
-        */
-       if (!createPath(name, 0777))
-               return FALSE;
-
-       inHeader = (size == 0);
-       dataCc = size;
-
-       /*
-        * Start the output file.
-        */
-       outFd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode);
-
-       if (outFd < 0)
-       {
-               perror(name);
-               skipFileFlag = TRUE;
-
-               return FALSE;
-       }
-
-       /*
-        * If the file is empty, then that's all we need to do.
-        */
-       if (size == 0)
-       {
-               (void) close(outFd);
-               outFd = -1;
-       }
-
-       return TRUE;
-}
-
-
-/*
- * Handle a data block of some specified size that was read.
- * Returns TRUE on success.
- */
-static BOOL
-readData(const char * cp, int count)
-{
-       /*
-        * Reduce the amount of data left in this file.
-        * If there is no more data left, then we need to read
-        * the header again.
-        */
-       dataCc -= count;
-
-       if (dataCc <= 0)
-               inHeader = TRUE;
-
-       /*
-        * If we aren't extracting files or this file is being
-        * skipped then do nothing more.
-        */
-       if (!extractFlag || skipFileFlag)
-               return TRUE;
-
-       /*
-        * Write the data to the output file.
-        */
-       if (fullWrite(outFd, cp, count) < 0)
-       {
-               perror(outName);
-               (void) close(outFd);
-               outFd = -1;
-               skipFileFlag = TRUE;
-
-               return FALSE;
-       }
-
-       /*
-        * If the write failed, close the file and disable further
-        * writes to this file.
-        */
-       if (dataCc <= 0)
-       {
-               if (close(outFd))
-                       perror(outName);
-
-               outFd = -1;
-
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-
-/*
- * Write a tar file containing the specified files.
- * Returns TRUE on success.
- */
-static BOOL
-writeTarFile(int fileCount, const char ** fileTable)
-{
-       struct  stat    statbuf;
-       BOOL            successFlag;
-
-       successFlag = TRUE;
-       errorFlag = FALSE;
-
-       /*
-        * Make sure there is at least one file specified.
-        */
-       if (fileCount <= 0)
-       {
-               fprintf(stderr, "No files specified to be saved\n");
-
-               return FALSE;
-       }
-
-       /*
-        * Create the tar file for writing.
-        */
-       tarFd = open(tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666);
-
-       if (tarFd < 0)
-       {
-               perror(tarName);
-
-               return FALSE;
-       }
-
-       /*
-        * Get the device and inode of the tar file for checking later.
-        */
-       if (fstat(tarFd, &statbuf) < 0)
-       {
-               perror(tarName);
-               successFlag = FALSE;
-
-               goto done;
-       }
-
-       tarDev = statbuf.st_dev;
-       tarInode = statbuf.st_ino;
-
-       /*
-        * Append each file name into the archive file.
-        * Follow symbolic links for these top level file names.
-        */
-       while (!intFlag && !errorFlag && (fileCount-- > 0))
-       {
-               saveFile(*fileTable++, FALSE);
-       }
-
-       if (intFlag)
-       {
-               fprintf(stderr, "Interrupted - aborting archiving\n");
-               successFlag = FALSE;
-       }
-
-       /*
-        * Now write an empty block of zeroes to end the archive.
-        */
-       writeTarBlock("", 1);
-
-
-done:
-       /*
-        * Close the tar file and check for errors if it was opened.
-        */
-       if ((tarFd >= 0) && (close(tarFd) < 0))
-       {
-               perror(tarName);
-               successFlag = FALSE;
-       }
-
-       return successFlag;
-}
-
-
-/*
- * Save one file into the tar file.
- * If the file is a directory, then this will recursively save all of
- * the files and directories within the directory.  The seeLinks
- * flag indicates whether or not we want to see symbolic links as
- * they really are, instead of blindly following them.
- */
-static void
-saveFile(const char * fileName, BOOL seeLinks)
-{
-       int             status;
-       int             mode;
-       struct stat     statbuf;
-
-       if (verboseFlag)
-               printf("a %s\n", fileName);
-
-       /*
-        * Check that the file name will fit in the header.
-        */
-       if (strlen(fileName) >= TAR_NAME_SIZE)
-       {
-               fprintf(stderr, "%s: File name is too long\n", fileName);
-
-               return;
-       }
-
-       /*
-        * Find out about the file.
-        */
-#ifdef S_ISLNK
-       if (seeLinks)
-               status = lstat(fileName, &statbuf);
-       else
-#endif
-               status = stat(fileName, &statbuf);
-
-       if (status < 0)
-       {
-               perror(fileName);
-
-               return;
-       }
-
-       /*
-        * Make sure we aren't trying to save our file into itself.
-        */
-       if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode))
-       {
-               fprintf(stderr, "Skipping saving of archive file itself\n");
-
-               return;
-       }
-
-       /*
-        * Check the type of file.
-        */
-       mode = statbuf.st_mode;
-
-       if (S_ISDIR(mode))
-       {
-               saveDirectory(fileName, &statbuf);
-
-               return;
-       }
-
-       if (S_ISREG(mode))
-       {
-               saveRegularFile(fileName, &statbuf);
-
-               return;
-       }
-
-       /*
-        * The file is a strange type of file, ignore it.
-        */
-       fprintf(stderr, "%s: not a directory or regular file\n", fileName);
-}
-
-
-/*
- * Save a regular file to the tar file.
- */
-static void
-saveRegularFile(const char * fileName, const struct stat * statbuf)
-{
-       BOOL            sawEof;
-       int             fileFd;
-       int             cc;
-       int             dataCount;
-       long            fullDataCount;
-       char            data[TAR_BLOCK_SIZE * 16];
-
-       /*
-        * Open the file for reading.
-        */
-       fileFd = open(fileName, O_RDONLY);
-
-       if (fileFd < 0)
-       {
-               perror(fileName);
-
-               return;
-       }
-
-       /*
-        * Write out the header for the file.
-        */
-       writeHeader(fileName, statbuf);
-
-       /*
-        * Write the data blocks of the file.
-        * We must be careful to write the amount of data that the stat
-        * buffer indicated, even if the file has changed size.  Otherwise
-        * the tar file will be incorrect.
-        */
-       fullDataCount = statbuf->st_size;
-       sawEof = FALSE;
-
-       while (!intFlag && (fullDataCount > 0))
-       {
-               /*
-                * Get the amount to write this iteration which is
-                * the minumum of the amount left to write and the
-                * buffer size.
-                */
-               dataCount = sizeof(data);
-
-               if (dataCount > fullDataCount)
-                       dataCount = (int) fullDataCount;
-
-               /*
-                * Read the data from the file if we haven't seen the
-                * end of file yet.
-                */
-               cc = 0;
-
-               if (!sawEof)
-               {
-                       cc = fullRead(fileFd, data, dataCount);
-
-                       if (cc < 0)
-                       {
-                               perror(fileName);
-
-                               (void) close(fileFd);
-                               errorFlag = TRUE;
-
-                               return;
-                       }
-
-                       /*
-                        * If the file ended too soon, complain and set
-                        * a flag so we will zero fill the rest of it.
-                        */
-                       if (cc < dataCount)
-                       {
-                               fprintf(stderr,
-                                       "%s: Short read - zero filling",
-                                       fileName);
-
-                               sawEof = TRUE;
-                       }
-               }
-
-               /*
-                * Zero fill the rest of the data if necessary.
-                */
-               if (cc < dataCount)
-                       memset(data + cc, 0, dataCount - cc);
-
-               /*
-                * Write the buffer to the TAR file.
-                */
-               writeTarBlock(data, dataCount);
-
-               fullDataCount -= dataCount;
-       }
-
-       /*
-        * Close the file.
-        */
-       if (close(fileFd) < 0)
-               fprintf(stderr, "%s: close: %s\n", fileName, strerror(errno));
-}
-
-
-/*
- * Save a directory and all of its files to the tar file.
- */
-static void
-saveDirectory(const char * dirName, const struct stat * statbuf)
-{
-       DIR *           dir;
-       struct dirent * entry;
-       BOOL            needSlash;
-       char            fullName[PATH_LEN];
-
-       /*
-        * Construct the directory name as used in the tar file by appending
-        * a slash character to it.
-        */
-       strcpy(fullName, dirName);
-       strcat(fullName, "/");
-
-       /*
-        * Write out the header for the directory entry.
-        */
-       writeHeader(fullName, statbuf);
-
-       /*
-        * Open the directory.
-        */
-       dir = opendir(dirName);
-
-       if (dir == NULL)
-       {
-               fprintf(stderr, "Cannot read directory \"%s\": %s\n",
-                       dirName, strerror(errno));
-
-               return;
-       }
-
-       /*
-        * See if a slash is needed.
-        */
-       needSlash = (*dirName && (dirName[strlen(dirName) - 1] != '/'));
-
-       /*
-        * Read all of the directory entries and check them,
-        * except for the current and parent directory entries.
-        */
-       while (!intFlag && !errorFlag && ((entry = readdir(dir)) != NULL))
-       {
-               if ((strcmp(entry->d_name, ".") == 0) ||
-                       (strcmp(entry->d_name, "..") == 0))
-               {
-                       continue;
-               }
-
-               /*
-                * Build the full path name to the file.
-                */
-               strcpy(fullName, dirName);
-
-               if (needSlash)
-                       strcat(fullName, "/");
-
-               strcat(fullName, entry->d_name);
-
-               /*
-                * Write this file to the tar file, noticing whether or not
-                * the file is a symbolic link.
-                */
-               saveFile(fullName, TRUE);
-       }
-
-       /*
-        * All done, close the directory.
-        */
-       closedir(dir);
-}
-
-
-/*
- * Write a tar header for the specified file name and status.
- * It is assumed that the file name fits.
- */
-static void
-writeHeader(const char * fileName, const struct stat * statbuf)
-{
-       long                    checkSum;
-       const unsigned char *   cp;
-       int                     len;
-       TarHeader               header;
-
-       /*
-        * Zero the header block in preparation for filling it in.
-        */
-       memset((char *) &header, 0, sizeof(header));
-
-       /*
-        * Fill in the header.
-        */
-       strcpy(header.name, fileName);
-
-       strncpy(header.magic, TAR_MAGIC, sizeof(header.magic));
-       strncpy(header.version, TAR_VERSION, sizeof(header.version));
-
-       putOctal(header.mode, sizeof(header.mode), statbuf->st_mode & 0777);
-       putOctal(header.uid, sizeof(header.uid), statbuf->st_uid);
-       putOctal(header.gid, sizeof(header.gid), statbuf->st_gid);
-       putOctal(header.size, sizeof(header.size), statbuf->st_size);
-       putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime);
-
-       header.typeFlag = TAR_TYPE_REGULAR;
-
-       /*
-        * Calculate and store the checksum.
-        * This is the sum of all of the bytes of the header,
-        * with the checksum field itself treated as blanks.
-        */
-       memset(header.checkSum, ' ', sizeof(header.checkSum));
-
-       cp = (const unsigned char *) &header;
-       len = sizeof(header);
-       checkSum = 0;
-
-       while (len-- > 0)
-               checkSum += *cp++;
-
-       putOctal(header.checkSum, sizeof(header.checkSum), checkSum);
-
-       /*
-        * Write the tar header.
-        */
-       writeTarBlock((const char *) &header, sizeof(header));
-}
-
-
-/*
- * Write data to one or more blocks of the tar file.
- * The data is always padded out to a multiple of TAR_BLOCK_SIZE.
- * The errorFlag static variable is set on an error.
- */
-static void
-writeTarBlock(const char * buf, int len)
-{
-       int     partialLength;
-       int     completeLength;
-       char    fullBlock[TAR_BLOCK_SIZE];
-
-       /*
-        * If we had a write error before, then do nothing more.
-        */
-       if (errorFlag)
-               return;
-
-       /*
-        * Get the amount of complete and partial blocks.
-        */
-       partialLength = len % TAR_BLOCK_SIZE;
-       completeLength = len - partialLength;
-
-       /*
-        * Write all of the complete blocks.
-        */
-       if ((completeLength > 0) && !fullWrite(tarFd, buf, completeLength))
-       {
-               perror(tarName);
-
-               errorFlag = TRUE;
-
-               return;
-       }
-
-       /*
-        * If there are no partial blocks left, we are done.
-        */
-       if (partialLength == 0)
-               return;
-
-       /*
-        * Copy the partial data into a complete block, and pad the rest
-        * of it with zeroes.
-        */
-       memcpy(fullBlock, buf + completeLength, partialLength);
-       memset(fullBlock + partialLength, 0, TAR_BLOCK_SIZE - partialLength);
-
-       /*
-        * Write the last complete block.
-        */
-       if (!fullWrite(tarFd, fullBlock, TAR_BLOCK_SIZE))
-       {
-               perror(tarName);
-
-               errorFlag = TRUE;
-       }
-}
-
-
-/*
- * Attempt to create the directories along the specified path, except for
- * the final component.  The mode is given for the final directory only,
- * while all previous ones get default protections.  Errors are not reported
- * here, as failures to restore files can be reported later.
- * Returns TRUE on success.
- */
-static BOOL
-createPath(const char * name, int mode)
-{
-       char *  cp;
-       char *  cpOld;
-       char    buf[TAR_NAME_SIZE];
-
-       strcpy(buf, name);
-
-       cp = strchr(buf, '/');
-
-       while (cp)
-       {
-               cpOld = cp;
-               cp = strchr(cp + 1, '/');
-
-               *cpOld = '\0';
-
-               if (mkdir(buf, cp ? 0777 : mode) == 0)
-                       printf("Directory \"%s\" created\n", buf);
-
-               *cpOld = '/';
-       }
-
-       return TRUE;
-}
-
-
-/*
- * Read an octal value in a field of the specified width, with optional
- * spaces on both sides of the number and with an optional null character
- * at the end.  Returns -1 on an illegal format.
- */
-static long
-getOctal(const char * cp, int len)
-{
-       long    val;
-
-       while ((len > 0) && (*cp == ' '))
-       {
-               cp++;
-               len--;
-       }
-
-       if ((len == 0) || !isOctal(*cp))
-               return -1;
-
-       val = 0;
-
-       while ((len > 0) && isOctal(*cp))
-       {
-               val = val * 8 + *cp++ - '0';
-               len--;
-       }
-
-       while ((len > 0) && (*cp == ' '))
-       {
-               cp++;
-               len--;
-       }
-
-       if ((len > 0) && *cp)
-               return -1;
-
-       return val;
-}
-
-
-/*
- * Put an octal string into the specified buffer.
- * The number is zero and space padded and possibly null padded.
- * Returns TRUE if successful.
- */
-static BOOL
-putOctal(char * cp, int len, long value)
-{
-       int     tempLength;
-       char *  tempString;
-       char    tempBuffer[32];
-
-       /*
-        * Create a string of the specified length with an initial space,
-        * leading zeroes and the octal number, and a trailing null.
-        */
-       tempString = tempBuffer;
-
-       sprintf(tempString, " %0*lo", len - 2, value);
-
-       tempLength = strlen(tempString) + 1;
-
-       /*
-        * If the string is too large, suppress the leading space.
-        */
-       if (tempLength > len)
-       {
-               tempLength--;
-               tempString++;
-       }
-
-       /*
-        * If the string is still too large, suppress the trailing null.
-        */
-       if (tempLength > len)
-               tempLength--;
-
-       /*
-        * If the string is still too large, fail.
-        */
-       if (tempLength > len)
-               return FALSE;
-
-       /*
-        * Copy the string to the field.
-        */
-       memcpy(cp, tempString, len);
-
-       return TRUE;
-}
-
-
-/*
- * See if the specified file name belongs to one of the specified list
- * of path prefixes.  An empty list implies that all files are wanted.
- * Returns TRUE if the file is selected.
- */
-static BOOL
-wantFileName(const char * fileName, int fileCount, const char ** fileTable)
-{
-       const char *    pathName;
-       int             fileLength;
-       int             pathLength;
-
-       /*
-        * If there are no files in the list, then the file is wanted.
-        */
-       if (fileCount == 0)
-               return TRUE;
-
-       fileLength = strlen(fileName);
-
-       /*
-        * Check each of the test paths.
-        */
-       while (fileCount-- > 0)
-       {
-               pathName = *fileTable++;
-
-               pathLength = strlen(pathName);
-
-               if (fileLength < pathLength)
-                       continue;
-
-               if (memcmp(fileName, pathName, pathLength) != 0)
-                       continue;
-
-               if ((fileLength == pathLength) ||
-                       (fileName[pathLength] == '/'))
-               {
-                       return TRUE;
-               }
-       }
-
-       return FALSE;
-}
-
-/* END CODE */
diff --git a/src/sash/cmds.c b/src/sash/cmds.c
deleted file mode 100644 (file)
index ae346b5..0000000
+++ /dev/null
@@ -1,1562 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * Most simple built-in commands are here.
- */
-
-#include "sash.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-//#include <sys/mount.h>
-#include <signal.h>
-#include <pwd.h>
-#include <grp.h>
-#include <utime.h>
-#include <errno.h>
-
-#if    HAVE_LINUX_MOUNT
-#include <linux/fs.h>
-#endif
-
-/* Need to tell loop.h what the actual dev_t type is. */
-#undef dev_t
-#if defined(__alpha) || (defined(__sparc__) && defined(__arch64__))
-#define dev_t unsigned int
-#else
-#define dev_t unsigned short
-#endif
-//#include <linux/loop.h>
-#undef dev_t
-#define dev_t dev_t
-
-
-int
-do_echo(int argc, const char ** argv)
-{
-       BOOL    first;
-
-       first = TRUE;
-
-       while (argc-- > 1)
-       {
-               if (!first)
-                       fputc(' ', stdout);
-
-               first = FALSE;
-               fputs(*(++argv), stdout);
-       }
-
-       fputc('\n', stdout);
-
-       return 0;
-}
-
-
-int
-do_pwd(int argc, const char ** argv)
-{
-       char    buf[PATH_LEN];
-
-       if (getcwd(buf, PATH_LEN) == NULL)
-       {
-               fprintf(stderr, "Cannot get current directory\n");
-
-               return 1;
-       }
-
-       printf("%s\n", buf);
-
-       return 0;
-}
-
-
-int
-do_cd(int argc, const char ** argv)
-{
-       const char *    path;
-
-       if (argc > 1)
-               path = argv[1];
-       else
-       {
-               path = getenv("HOME");
-
-               if (path == NULL)
-               {
-                       fprintf(stderr, "No HOME environment variable\n");
-
-                       return 1;
-               }
-       }
-
-       if (chdir(path) < 0)
-       {
-               perror(path);
-
-               return 1;
-       }
-
-       return 0;
-}
-
-
-int
-do_mkdir(int argc, const char ** argv)
-{
-       int r = 0;
-
-       while (argc-- > 1)
-       {
-               if (mkdir(argv[1], 0777) < 0)
-               {
-                       perror(argv[1]);
-                       r = 1;
-               }
-
-               argv++;
-       }
-
-       return r;
-}
-
-
-int
-do_mknod(int argc, const char ** argv)
-{
-       const char *    cp;
-       int             mode;
-       int             major;
-       int             minor;
-
-       mode = 0666;
-
-       if (strcmp(argv[2], "b") == 0)
-               mode |= S_IFBLK;
-       else if (strcmp(argv[2], "c") == 0)
-               mode |= S_IFCHR;
-       else
-       {
-               fprintf(stderr, "Bad device type\n");
-
-               return 1;
-       }
-
-       major = 0;
-       cp = argv[3];
-
-       while (isDecimal(*cp))
-               major = major * 10 + *cp++ - '0';
-
-       if (*cp || (major < 0) || (major > 255))
-       {
-               fprintf(stderr, "Bad major number\n");
-
-               return 1;
-       }
-
-       minor = 0;
-       cp = argv[4];
-
-       while (isDecimal(*cp))
-               minor = minor * 10 + *cp++ - '0';
-
-       if (*cp || (minor < 0) || (minor > 255))
-       {
-               fprintf(stderr, "Bad minor number\n");
-
-               return 1;
-       }
-
-       if (mknod(argv[1], mode, major * 256 + minor) < 0)
-       {
-               perror(argv[1]);
-
-               return 1;
-       }
-
-       return 0;
-}
-
-
-#if HAVE_LINUX_PIVOT
-
-int
-do_pivot_root(int argc, const char ** argv)
-{
-       if (pivot_root(argv[1], argv[2]) < 0)
-       {
-               perror("pivot_root");
-
-               return 1;
-       }
-
-       return 0;
-}
-
-#endif
-
-
-#if HAVE_LINUX_CHROOT
-
-int
-do_chroot(int argc, const char ** argv)
-{
-       if (chroot(argv[1]) < 0)
-       {
-               perror("chroot");
-
-               return 1;
-       }
-
-       return 0;
-}
-
-#endif
-
-
-int
-do_rmdir(int argc, const char ** argv)
-{
-       int r = 0;
-
-       while (argc-- > 1)
-       {
-               if (rmdir(argv[1]) < 0)
-               {
-                       perror(argv[1]);
-                       r = 1;
-               }
-
-               argv++;
-       }
-
-       return r;
-}
-
-
-int
-do_sync(int argc, const char ** argv)
-{
-       sync();
-
-       return 0;
-}
-
-
-int
-do_rm(int argc, const char ** argv)
-{
-       int r = 0;
-
-       while (argc-- > 1)
-       {
-               if (unlink(argv[1]) < 0)
-               {
-                       perror(argv[1]);
-                       r = 1;
-               }
-
-               argv++;
-       }
-
-       return r;
-}
-
-
-int
-do_chmod(int argc, const char ** argv)
-{
-       const char *    cp;
-       int             mode;
-       int             r;
-
-       r = 0;
-       mode = 0;
-       cp = argv[1];
-
-       while (isOctal(*cp))
-               mode = mode * 8 + (*cp++ - '0');
-
-       if (*cp)
-       {
-               fprintf(stderr, "Mode must be octal\n");
-
-               return 1;
-       }
-
-       argc--;
-       argv++;
-
-       while (argc-- > 1)
-       {
-               if (chmod(argv[1], mode) < 0)
-               {
-                       perror(argv[1]);
-                       r = 1;
-               }
-
-               argv++;
-       }
-
-       return r;
-}
-
-
-int
-do_chown(int argc, const char ** argv)
-{
-       const char *    cp;
-       int             uid;
-       struct passwd * pwd;
-       struct stat     statBuf;
-       int             r;
-
-       r = 0;
-       cp = argv[1];
-
-       if (isDecimal(*cp))
-       {
-               uid = 0;
-
-               while (isDecimal(*cp))
-                       uid = uid * 10 + (*cp++ - '0');
-
-               if (*cp)
-               {
-                       fprintf(stderr, "Bad uid value\n");
-
-                       return 1;
-               }
-       }
-       else
-       {
-               pwd = getpwnam(cp);
-
-               if (pwd == NULL)
-               {
-                       fprintf(stderr, "Unknown user name\n");
-
-                       return 1;
-               }
-
-               uid = pwd->pw_uid;
-       }
-
-       argc--;
-       argv++;
-
-       while (argc-- > 1)
-       {
-               argv++;
-
-               if ((stat(*argv, &statBuf) < 0) ||
-                       (chown(*argv, uid, statBuf.st_gid) < 0))
-               {
-                       perror(*argv);
-                       r = 1;
-               }
-       }
-
-       return r;
-}
-
-
-int
-do_chgrp(int argc, const char ** argv)
-{
-       const char *    cp;
-       int             gid;
-       struct group *  grp;
-       struct stat     statBuf;
-       int             r;
-
-       r = 0;
-       cp = argv[1];
-
-       if (isDecimal(*cp))
-       {
-               gid = 0;
-
-               while (isDecimal(*cp))
-                       gid = gid * 10 + (*cp++ - '0');
-
-               if (*cp)
-               {
-                       fprintf(stderr, "Bad gid value\n");
-
-                       return 1;
-               }
-       }
-       else
-       {
-               grp = getgrnam(cp);
-
-               if (grp == NULL)
-               {
-                       fprintf(stderr, "Unknown group name\n");
-
-                       return 1;
-               }
-
-               gid = grp->gr_gid;
-       }
-
-       argc--;
-       argv++;
-
-       while (argc-- > 1)
-       {
-               argv++;
-
-               if ((stat(*argv, &statBuf) < 0) ||
-                       (chown(*argv, statBuf.st_uid, gid) < 0))
-               {
-                       perror(*argv);
-                       r = 1;
-               }
-       }
-
-       return r;
-}
-
-
-int
-do_touch(int argc, const char ** argv)
-{
-       const char *    name;
-       int             fd;
-       struct utimbuf  now;
-       int             r;
-
-       r = 0;
-       time(&now.actime);
-       now.modtime = now.actime;
-
-       while (argc-- > 1)
-       {
-               name = *(++argv);
-
-               fd = open(name, O_CREAT | O_WRONLY | O_EXCL, 0666);
-
-               if (fd >= 0)
-               {
-                       close(fd);
-
-                       continue;
-               }
-
-               if (errno != EEXIST)
-               {
-                       perror(name);
-                       r = 1;
-
-                       continue;
-               }
-
-               if (utime(name, &now) < 0)
-               {
-                       perror(name);
-                       r = 1;
-               }
-       }
-
-       return r;
-}
-
-
-int
-do_mv(int argc, const char ** argv)
-{
-       const char *    srcName;
-       const char *    destName;
-       const char *    lastArg;
-       BOOL            dirFlag;
-       int             r;
-
-       r = 0;
-       lastArg = argv[argc - 1];
-
-       dirFlag = isDirectory(lastArg);
-
-       if ((argc > 3) && !dirFlag)
-       {
-               fprintf(stderr, "%s: not a directory\n", lastArg);
-
-               return 1;
-       }
-
-       while (!intFlag && (argc-- > 2))
-       {
-               srcName = *(++argv);
-
-               if (access(srcName, 0) < 0)
-               {
-                       perror(srcName);
-                       r = 1;
-
-                       continue;
-               }
-
-               destName = lastArg;
-
-               if (dirFlag)
-                       destName = buildName(destName, srcName);
-
-               if (rename(srcName, destName) >= 0)
-                       continue;
-
-               if (errno != EXDEV)
-               {
-                       perror(destName);
-                       r = 1;
-
-                       continue;
-               }
-
-               if (!copyFile(srcName, destName, TRUE))
-               {
-                       r = 1;
-
-                       continue;
-               }
-
-               if (unlink(srcName) < 0)
-               {
-                       perror(srcName);
-                       r = 1;
-               }
-       }
-
-       return r;
-}
-
-
-int
-do_ln(int argc, const char ** argv)
-{
-       const char *    srcName;
-       const char *    destName;
-       const char *    lastArg;
-       BOOL            dirFlag;
-       int             r;
-
-       r = 0;
-
-       if (argv[1][0] == '-')
-       {
-               if (strcmp(argv[1], "-s"))
-               {
-                       fprintf(stderr, "Unknown option\n");
-
-                       return 1;
-               }
-
-               if (argc != 4)
-               {
-                       fprintf(stderr, "Wrong number of arguments for symbolic link\n");
-
-                       return 1;
-               }
-
-#ifdef S_ISLNK
-               if (symlink(argv[2], argv[3]) < 0)
-               {
-                       perror(argv[3]);
-
-                       return 1;
-               }
-
-               return 0;
-#else
-               fprintf(stderr, "Symbolic links are not allowed\n");
-
-               return 1;
-#endif
-       }
-
-       /*
-        * Here for normal hard links.
-        */
-       lastArg = argv[argc - 1];
-       dirFlag = isDirectory(lastArg);
-
-       if ((argc > 3) && !dirFlag)
-       {
-               fprintf(stderr, "%s: not a directory\n", lastArg);
-
-               return 1;
-       }
-
-       while (argc-- > 2)
-       {
-               srcName = *(++argv);
-
-               if (access(srcName, 0) < 0)
-               {
-                       perror(srcName);
-                       r = 1;
-
-                       continue;
-               }
-
-               destName = lastArg;
-
-               if (dirFlag)
-                       destName = buildName(destName, srcName);
-
-               if (link(srcName, destName) < 0)
-               {
-                       perror(destName);
-                       r = 1;
-
-                       continue;
-               }
-       }
-
-       return r;
-}
-
-
-int
-do_cp(int argc, const char ** argv)
-{
-       const char *    srcName;
-       const char *    destName;
-       const char *    lastArg;
-       BOOL            dirFlag;
-       int             r;
-
-       r = 0;
-       lastArg = argv[argc - 1];
-
-       dirFlag = isDirectory(lastArg);
-
-       if ((argc > 3) && !dirFlag)
-       {
-               fprintf(stderr, "%s: not a directory\n", lastArg);
-
-               return 1;
-       }
-
-       while (!intFlag && (argc-- > 2))
-       {
-               srcName = *(++argv);
-               destName = lastArg;
-
-               if (dirFlag)
-                       destName = buildName(destName, srcName);
-
-               if (!copyFile(srcName, destName, FALSE))
-                       r = 1;
-       }
-
-       return r;
-}
-
-
-int
-do_mount(int argc, const char ** argv)
-{
-       const char *    str;
-       const char *    type;
-       int             flags;
-
-       argc--;
-       argv++;
-
-       type = MOUNT_TYPE;
-
-#if    HAVE_LINUX_MOUNT
-       flags = MS_MGC_VAL;
-#else
-       flags = 0;
-#endif
-
-       while ((argc > 0) && (**argv == '-'))
-       {
-               argc--;
-               str = *argv++;
-
-               while (*++str) switch (*str)
-               {
-                       case 't':
-                               if ((argc <= 0) || (**argv == '-'))
-                               {
-                                       fprintf(stderr, "Missing file system type\n");
-
-                                       return 1;
-                               }
-
-                               type = *argv++;
-                               argc--;
-                               break;
-
-#if    HAVE_LINUX_MOUNT
-                       case 'r':
-                               flags |= MS_RDONLY;
-                               break;
-
-                       case 'm':
-                               flags |= MS_REMOUNT;
-                               break;
-
-                       case 's':
-                               flags |= MS_NOSUID;
-                               break;
-
-                       case 'e':
-                               flags |= MS_NOEXEC;
-                               break;
-
-#elif  HAVE_BSD_MOUNT
-                       case 'r':
-                               flags |= MNT_RDONLY;
-                               break;
-
-                       case 's':
-                               flags |= MNT_NOSUID;
-                               break;
-
-                       case 'e':
-                               flags |= MNT_NOEXEC;
-                               break;
-#endif
-                       default:
-                               fprintf(stderr, "Unknown option\n");
-
-                               return 1;
-               }
-       }
-
-       if (argc != 2)
-       {
-               fprintf(stderr, "Wrong number of arguments for mount\n");
-
-               return 1;
-       }
-
-#if    HAVE_LINUX_MOUNT
-
-       if (mount(argv[0], argv[1], type, flags, 0) < 0)
-       {
-               perror("mount failed");
-               return 1;
-       }
-#elif  HAVE_BSD_MOUNT
-       {
-               struct      ufs_args ufs;
-               struct      adosfs_args adosfs;
-               struct      iso_args iso;
-               struct      mfs_args mfs;
-               struct      msdosfs_args msdosfs;
-               void *      args;
-
-               if (!strcmp(type, "ffs") || !strcmp(type, "ufs"))
-               {
-                       ufs.fspec = (char*) argv[0];
-                       args = &ufs;
-               }
-               else if (!strcmp(type, "adosfs"))
-               {
-                       adosfs.fspec = (char*) argv[0];
-                       adosfs.uid = 0;
-                       adosfs.gid = 0;
-                       args = &adosfs;
-               }
-               else if (!strcmp(type, "cd9660"))
-               {
-                       iso.fspec = (char*) argv[0];
-                       args = &iso;
-               }
-               else if (!strcmp(type, "mfs"))
-               {
-                       mfs.fspec = (char*) argv[0];
-                       args = &mfs;
-               }
-               else if (!strcmp(type, "msdos"))
-               {
-                       msdosfs.fspec = (char*) argv[0];
-                       msdosfs.uid = 0;
-                       msdosfs.gid = 0;
-                       args = &msdosfs;
-               }
-               else
-               {
-                       fprintf(stderr, "Unknown filesystem type: %s", type);
-                       fprintf(stderr,
-                           "Supported: ffs ufs adosfs cd9660 mfs msdos\n");
-
-                       return 1;
-               }
-
-               if (mount(type, argv[1], flags, args) < 0)
-               {
-                       perror(argv[0]);
-
-                       return 1;
-               }
-       }
-#endif
-       return 0;
-}
-
-
-int
-do_umount(int argc, const char ** argv)
-{
-#if    HAVE_LINUX_MOUNT
-       if (umount(argv[1]) < 0)
-       {
-               perror(argv[1]);
-
-               return 1;
-       }
-#elif  HAVE_BSD_MOUNT
-       {
-               const char *    str;
-               int             flags = 0;
-
-               for (argc--, argv++;
-                   (argc > 0) && (**argv == '-');)
-               {
-                       argc--;
-                       str = *argv++;
-
-                       while (*++str)
-{
-                               switch (*str)
-                               {
-                                       case 'f':
-                                               flags = MNT_FORCE;
-                                               break;
-                               }
-                       }
-               }
-
-               if (unmount(argv[0], flags) < 0)
-               {
-                       perror(argv[0]);
-
-                       return 1;
-               }
-       }
-#endif
-       return 0;
-}
-
-
-int
-do_cmp(int argc, const char ** argv)
-{
-       int             fd1;
-       int             fd2;
-       int             cc1;
-       int             cc2;
-       long            pos;
-       const char *    bp1;
-       const char *    bp2;
-       char            buf1[BUF_SIZE];
-       char            buf2[BUF_SIZE];
-       struct  stat    statBuf1;
-       struct  stat    statBuf2;
-       int             r;
-
-       r = 0;
-
-       if (stat(argv[1], &statBuf1) < 0)
-       {
-               perror(argv[1]);
-
-               return 1;
-       }
-
-       if (stat(argv[2], &statBuf2) < 0)
-       {
-               perror(argv[2]);
-
-               return 1;
-       }
-
-       if ((statBuf1.st_dev == statBuf2.st_dev) &&
-               (statBuf1.st_ino == statBuf2.st_ino))
-       {
-               printf("Files are links to each other\n");
-
-               return 0;
-       }
-
-       if (statBuf1.st_size != statBuf2.st_size)
-       {
-               printf("Files are different sizes\n");
-
-               return 1;
-       }
-
-       fd1 = open(argv[1], O_RDONLY);
-
-       if (fd1 < 0)
-       {
-               perror(argv[1]);
-
-               return 1;
-       }
-
-       fd2 = open(argv[2], O_RDONLY);
-
-       if (fd2 < 0)
-       {
-               perror(argv[2]);
-               close(fd1);
-
-               return 1;
-       }
-
-       pos = 0;
-
-       while (TRUE)
-       {
-               if (intFlag)
-                       goto closefiles;
-
-               cc1 = read(fd1, buf1, sizeof(buf1));
-
-               if (cc1 < 0)
-               {
-                       perror(argv[1]);
-                       r = 1;
-                       goto closefiles;
-               }
-
-               cc2 = read(fd2, buf2, sizeof(buf2));
-
-               if (cc2 < 0)
-               {
-                       perror(argv[2]);
-                       r = 1;
-                       goto closefiles;
-               }
-
-               if ((cc1 == 0) && (cc2 == 0))
-               {
-                       printf("Files are identical\n");
-                       r = 0;
-                       goto closefiles;
-               }
-
-               if (cc1 < cc2)
-               {
-                       printf("First file is shorter than second\n");
-                       r = 1;
-                       goto closefiles;
-               }
-
-               if (cc1 > cc2)
-               {
-                       printf("Second file is shorter than first\n");
-                       r = 1;
-                       goto closefiles;
-               }
-
-               if (memcmp(buf1, buf2, cc1) == 0)
-               {
-                       pos += cc1;
-
-                       continue;
-               }
-
-               bp1 = buf1;
-               bp2 = buf2;
-
-               while (*bp1++ == *bp2++)
-                       pos++;
-
-               printf("Files differ at byte position %ld\n", pos);
-               r = 1;
-
-               goto closefiles;
-       }
-
-closefiles:
-       close(fd1);
-       close(fd2);
-
-       return r;
-}
-
-
-int
-do_more(int argc, const char ** argv)
-{
-       FILE *          fp;
-       const char *    name;
-       int             ch;
-       int             line;
-       int             col;
-       int             pageLines;
-       int             pageColumns;
-       char            buf[80];
-
-       /*
-        * Get the width and height of the screen if it is set.
-        * If not, then default it.
-        */
-       pageLines = 0;
-       pageColumns = 0;
-
-       name = getenv("LINES");
-
-       if (name)
-               pageLines = atoi(name);
-
-       name = getenv("COLS");
-
-       if (name)
-               pageColumns = atoi(name);
-
-       if (pageLines <= 0)
-               pageLines = 24;
-
-       if (pageColumns <= 0)
-               pageColumns = 80;
-
-       /*
-        * OK, process each file.
-        */
-       while (argc-- > 1)
-       {
-               name = *(++argv);
-
-               fp = fopen(name, "r");
-
-               if (fp == NULL)
-               {
-                       perror(name);
-
-                       return 1;
-               }
-
-               printf("<< %s >>\n", name);
-               line = 1;
-               col = 0;
-
-               while (fp && ((ch = fgetc(fp)) != EOF))
-               {
-                       switch (ch)
-                       {
-                               case '\r':
-                                       col = 0;
-                                       break;
-
-                               case '\n':
-                                       line++;
-                                       col = 0;
-                                       break;
-
-                               case '\t':
-                                       col = ((col + 1) | 0x07) + 1;
-                                       break;
-
-                               case '\b':
-                                       if (col > 0)
-                                               col--;
-                                       break;
-
-                               default:
-                                       col++;
-                       }
-
-                       putchar(ch);
-
-                       if (col >= pageColumns)
-                       {
-                               col -= pageColumns;
-                               line++;
-                       }
-
-                       if (line < pageLines)
-                               continue;
-
-                       if (col > 0)
-                               putchar('\n');
-
-                       printf("--More--");
-                       fflush(stdout);
-
-                       if (intFlag || (read(0, buf, sizeof(buf)) < 0))
-                       {
-                               if (fp)
-                                       fclose(fp);
-
-                               return 0;
-                       }
-
-                       ch = buf[0];
-
-                       if (ch == ':')
-                               ch = buf[1];
-
-                       switch (ch)
-                       {
-                               case 'N':
-                               case 'n':
-                                       fclose(fp);
-                                       fp = NULL;
-                                       break;
-
-                               case 'Q':
-                               case 'q':
-                                       fclose(fp);
-
-                                       return 0;
-                       }
-
-                       col = 0;
-                       line = 1;
-               }
-
-               if (fp)
-                       fclose(fp);
-       }
-
-       return 0;
-}
-
-
-int
-do_sum(int argc, const char ** argv)
-{
-       const char *    name;
-       int             fd;
-       int             cc;
-       int             ch;
-       int             i;
-       unsigned long   checksum;
-       char            buf[BUF_SIZE];
-       int             r;
-
-       argc--;
-       argv++;
-       r = 0;
-
-       while (argc-- > 0)
-       {
-               name = *argv++;
-
-               fd = open(name, O_RDONLY);
-
-               if (fd < 0)
-               {
-                       perror(name);
-                       r = 1;
-
-                       continue;
-               }
-
-               checksum = 0;
-
-               while ((cc = read(fd, buf, sizeof(buf))) > 0)
-               {
-                       for (i = 0; i < cc; i++)
-                       {
-                               ch = buf[i];
-
-                               if ((checksum & 0x01) != 0)
-                                       checksum = (checksum >> 1) + 0x8000;
-                               else
-                                       checksum = (checksum >> 1);
-
-                               checksum = (checksum + ch) & 0xffff;
-                       }
-               }
-
-               if (cc < 0)
-               {
-                       perror(name);
-                       r = 1;
-
-                       (void) close(fd);
-
-                       continue;
-               }
-
-               (void) close(fd);
-
-               printf("%05lu %s\n", checksum, name);
-       }
-
-       return r;
-}
-
-
-int
-do_exit(int argc, const char ** argv)
-{
-       int r = 0;
-
-       if (getpid() == 1)
-       {
-               fprintf(stderr, "You are the INIT process!\n");
-
-               return 1;
-       }
-
-       if (argc == 2)
-       {
-               r = atoi(argv[1]);
-       }
-
-       exit(r);
-
-       return 1;
-}
-
-
-int
-do_setenv(int argc, const char ** argv)
-{
-       const char *    name;
-       const char *    value;
-       char *          str;
-
-       name = argv[1];
-       value = argv[2];
-
-       /*
-        * The value given to putenv must remain around, so we must malloc it.
-        * Note: memory is not reclaimed if the same variable is redefined.
-        */
-       str = malloc(strlen(name) + strlen(value) + 2);
-
-       if (str == NULL)
-       {
-               fprintf(stderr, "Cannot allocate memory\n");
-
-               return 1;
-       }
-
-       strcpy(str, name);
-       strcat(str, "=");
-       strcat(str, value);
-
-       putenv(str);
-
-       return 0;
-}
-
-
-int
-do_printenv(int argc, const char ** argv)
-{
-       const char **   env;
-       extern char **  environ;
-       int             len;
-
-       env = (const char **) environ;
-
-       if (argc == 1)
-       {
-               while (*env)
-                       printf("%s\n", *env++);
-
-               return 0;
-       }
-
-       len = strlen(argv[1]);
-
-       while (*env)
-       {
-               if ((strlen(*env) > len) && (env[0][len] == '=') &&
-                       (memcmp(argv[1], *env, len) == 0))
-               {
-                       printf("%s\n", &env[0][len+1]);
-
-                       return 0;
-               }
-               env++;
-       }
-
-       return 0;
-}
-
-
-int
-do_umask(int argc, const char ** argv)
-{
-       const char *    cp;
-       int             mask;
-
-       if (argc <= 1)
-       {
-               mask = umask(0);
-               umask(mask);
-               printf("%03o\n", mask);
-
-               return 0;
-       }
-
-       mask = 0;
-       cp = argv[1];
-
-       while (isOctal(*cp))
-               mask = mask * 8 + *cp++ - '0';
-
-       if (*cp || (mask & ~0777))
-       {
-               fprintf(stderr, "Bad umask value\n");
-
-               return 1;
-       }
-
-       umask(mask);
-
-       return 0;
-}
-
-
-int
-do_kill(int argc, const char ** argv)
-{
-       const char *    cp;
-       int             sig;
-       int             pid;
-       int             r;
-
-       r = 0;
-       sig = SIGTERM;
-
-       if (argv[1][0] == '-')
-       {
-               cp = &argv[1][1];
-
-               if (strcmp(cp, "HUP") == 0)
-                       sig = SIGHUP;
-               else if (strcmp(cp, "INT") == 0)
-                       sig = SIGINT;
-               else if (strcmp(cp, "QUIT") == 0)
-                       sig = SIGQUIT;
-               else if (strcmp(cp, "KILL") == 0)
-                       sig = SIGKILL;
-               else if (strcmp(cp, "STOP") == 0)
-                       sig = SIGSTOP;
-               else if (strcmp(cp, "CONT") == 0)
-                       sig = SIGCONT;
-               else if (strcmp(cp, "USR1") == 0)
-                       sig = SIGUSR1;
-               else if (strcmp(cp, "USR2") == 0)
-                       sig = SIGUSR2;
-               else if (strcmp(cp, "TERM") == 0)
-                       sig = SIGTERM;
-               else
-               {
-                       sig = 0;
-
-                       while (isDecimal(*cp))
-                               sig = sig * 10 + *cp++ - '0';
-
-                       if (*cp)
-                       {
-                               fprintf(stderr, "Unknown signal\n");
-
-                               return 1;
-                       }
-               }
-
-               argc--;
-               argv++;
-       }
-
-       while (argc-- > 1)
-       {
-               cp = *(++argv);
-               pid = 0;
-
-               while (isDecimal(*cp))
-                       pid = pid * 10 + *cp++ - '0';
-
-               if (*cp)
-               {
-                       fprintf(stderr, "Non-numeric pid\n");
-
-                       return 1;
-               }
-
-               if (kill(pid, sig) < 0)
-               {
-                       perror(*argv);
-                       r = 1;
-               }
-       }
-
-       return r;
-}
-
-
-int
-do_where(int argc, const char ** argv)
-{
-       const char *    program;
-       const char *    dirName;
-       char *          path;
-       char *          endPath;
-       char *          fullPath;
-       BOOL            found;
-       int             r;
-
-       found = FALSE;
-       program = argv[1];
-
-       if (strchr(program, '/') != NULL)
-       {
-               fprintf(stderr, "Program name cannot include a path\n");
-
-               return 1;
-       }
-
-       path = getenv("PATH");
-
-       fullPath = getChunk(strlen(path) + strlen(program) + 2);
-       path = chunkstrdup(path);
-
-       if ((path == NULL) || (fullPath == NULL))
-       {
-               fprintf(stderr, "Memory allocation failed\n");
-
-               return 1;
-       }
-
-       /*
-        * Check out each path to see if the program exists and is
-        * executable in that path.
-        */
-       for (; path; path = endPath)
-       {
-               /*
-                * Find the end of the next path and NULL terminate
-                * it if necessary.
-                */
-               endPath = strchr(path, ':');
-
-               if (endPath)
-                       *endPath++ = '\0';
-
-               /*
-                * Get the directory name, defaulting it to DOT if
-                * it is null.
-                */
-               dirName = path;
-
-               if (dirName == '\0')
-                       dirName = ".";
-
-               /*
-                * Construct the full path of the program.
-                */
-               strcpy(fullPath, dirName);
-               strcat(fullPath, "/");
-               strcat(fullPath, program);
-
-               /*
-                * See if the program exists and is executable.
-                */
-               if (access(fullPath, X_OK) < 0)
-               {
-                       if (errno != ENOENT)
-                       {
-                               perror(fullPath);
-                               r = 1;
-                       }
-
-                       continue;
-               }
-
-               printf("%s\n", fullPath);
-               found = TRUE;
-       }
-
-       if (!found)
-       {
-               printf("Program \"%s\" not found in PATH\n", program);
-               r = 1;
-       }
-
-       return r;
-}
-
-#if HAVE_LINUX_LOSETUP
-
-int
-do_losetup(int argc, const char ** argv)
-{
-       int loopfd;
-       int targfd;
-       struct loop_info loopInfo;
-
-       if (!strcmp(argv[1], "-d"))
-       {
-               loopfd = open(argv[2], O_RDWR);
-
-               if (loopfd < 0)
-               {
-                       fprintf(stderr, "Error opening %s: %s\n", argv[2], 
-                               strerror(errno));
-
-                       return 1;
-               }
-
-               if (ioctl(loopfd, LOOP_CLR_FD, 0))
-               {
-                       fprintf(stderr, "Error unassociating device: %s\n", 
-                               strerror(errno));
-
-                       return 1;
-               }
-       }
-
-       loopfd = open(argv[1], O_RDWR);
-
-       if (loopfd < 0)
-       {
-               fprintf(stderr, "Error opening %s: %s\n", argv[1], 
-                       strerror(errno));
-
-               return 1;
-       }
-
-       targfd = open(argv[2], O_RDWR);
-
-       if (targfd < 0)
-       {
-               fprintf(stderr, "Error opening %s: %s\n", argv[2], 
-                       strerror(errno));
-
-               return 1;
-       }
-
-       if (ioctl(loopfd, LOOP_SET_FD, targfd))
-       {
-               fprintf(stderr, "Error setting up loopback device: %s\n", 
-                       strerror(errno));
-
-               return 1;
-       }
-
-       memset(&loopInfo, 0, sizeof(loopInfo));
-       strcpy(loopInfo.lo_name, argv[2]);
-
-       if (ioctl(loopfd, LOOP_SET_STATUS, &loopInfo))
-       {
-               fprintf(stderr, "Error setting up loopback device: %s\n", 
-                       strerror(errno));
-
-               return 1;
-       }
-
-       return 0;
-}
-
-#endif
-
-/* END CODE */
diff --git a/src/sash/dirent.h b/src/sash/dirent.h
deleted file mode 100644 (file)
index 77db3b4..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef DIRENT_H_
-#define DIRENT_H_
-
-typedef struct {} DIR;
-
-struct dirent {
-       char *d_name;
-};
-
-#endif // DIRENT_H_
diff --git a/src/sash/sash.1 b/src/sash/sash.1
deleted file mode 100644 (file)
index 8c61b9b..0000000
+++ /dev/null
@@ -1,591 +0,0 @@
-.TH SASH 1
-.SH NAME
-sash \- stand-alone shell with built-in commands
-.SH SYNOPSYS
-.B sash [-c command] [-f fileName ] [-p prompt] [-q] [-a]
-.SH DESCRIPTION
-The
-.B sash
-program is a stand-alone shell which is useful for recovering from certain
-types of system failures.
-In particular, it was created in order to cope with the problem of
-missing shared libraries or important executables.
-.PP
-.B Sash
-can execute external programs, as in any shell.  There are no restrictions
-on these commands, as the standard shell is used to execute them if there
-are any non-wildcard meta-characters in the command.
-.PP
-More importantly, however,
-is that many of the standard system commands are built-in to
-.BR sash .
-These built-in commands are:
-.PP
-.nf
-     -ar, -chattr, -chgrp, -chmod, -chown, -chroot, -cmp,
-     -cp, -dd, -echo, -ed, -grep, -file, -find, -gunzip,
-     -gzip, -kill, -losetup, -ln, -ls, -lsattr, -mkdir,
-     -mknod, -more, -mount, -mv, -pivot_root, -printenv, -pwd,
-     -rm, -rmdir, -sum, -sync, -tar, -touch, -umount, -where
-.fi
-.PP
-These commands are generally similar to the standard programs with similar
-names.  However, they are simpler and cruder than the external programs,
-and so many of the options are not implemented.  The restrictions for each
-built-in command are described later.
-.PP
-The built-in commands which correspond to external programs begin with a
-dash character in order to distinguish them from the external programs.
-So typing "ls", for example, will attempt to run the real
-.B ls
-program.
-If "-ls" is typed, then the built-in command which mimics
-.B ls
-is called.
-.PP
-For the built-in commands, file names are expanded so that asterisks,
-question marks, and characters inside of square brackets are recognised
-and are expanded.
-Arguments can be quoted using single quotes, double quotes, or backslashes.
-However, no other command line processing is performed.
-This includes specifying of file redirection, and the specifying of a pipeline.
-.PP
-If an external program is non-existant or fails to run correctly, then
-the "alias" built-in command may be used to redefine the standard command
-so that it automatically runs the built-in command instead.  For example,
-the command "alias ls -ls" redefines "ls" to run the built-in command.
-This saves you the pain of having to remember to type the leading dash
-all of the time.
-If many external programs will not run, then the "aliasall" command may
-be useful to create multiple aliases.
-.PP
-The "help" command will list all of the built-in commands in
-.B sash .
-If an argument is given, it will list only those built-in commands
-which contain the given argument as a sub-string.
-Each built-in command is described below in more detail.
-.PP
-.TP
-.B alias [name [command]]
-If
-.I name
-and
-.I command
-are provided, this defines an alias for a command with the specified name
-which executes the specified command with possible arguments.
-Arguments containing wildcards can be quoted in order to defer their
-expansion until the alias is invoked.
-If just
-.I name
-is provided, then the definition
-of the specified command alias is displayed.  If nothing is provided,
-then the definitions of all aliases are displayed.
-.TP
-.B aliasall
-This defines aliases for all of the built-in commands that start with
-dashes to the corresponding names without the dashes.
-This may be useful when the system is so corrupted that no external
-programs may be executed at all.
-.TP
-.B -ar [txp][v] arfile [filename]...
-List or extract files from an ar archive.
-The arfile argument specifies a file name which contains the archive.
-If no additional filenames are specified, then all files in the archive are
-operated on.
-Otherwise, only those archive members which have the same name 
-as one of the additional filenames are operated on.
-Filenames which do not appear in the archive are ignored.
-Archives cannot be created or modified.
-The archiver correctly handles 4.0BSD archives,
-and understands both the SysV and 4.4BSD extensions for long file names.
-The extended pseudo-BSD formats are not supported;
-nor are the two antediluvian binary formats derived from V7 and earlier.
-(The GNU archiver normally creates archives in the 4.0BSD format with
-SysV extensions.)
-.TP
-.B cd [dirName]
-If
-.I dirName
-is provided, then the current directory is changed to the
-dirName.  If
-.I dirName
-is absent, then the current directory is changed
-to the user's home directory (value of the $HOME environment variable).
-.TP
-.B -chattr [+i] [-i] [+a] [-a] fileName ...
-Change the attributes of the specified files on an ext2 or ext3 file system.
-Using a plus sign adds the specified attribute for the files.
-Using a minus sign removes the specified attributes for the files.
-The 'i' attribute makes a file immutable so that it cannot be changed.
-The 'a' attribute makes a file append-only.
-This command is only available on Linux.
-.TP
-.B -chgrp gid fileName ...
-Change the group id for the specified list of files.  The
-.I gid
-can
-either be a group name, or a decimal value.
-.TP
-.B -chmod mode fileName ...
-Change the mode of the specified list of files.  The
-.I mode
-argument
-can only be an octal value.
-.TP
-.B -chown uid fileName ...
-Change the owner id for the specified list of files.  The
-.I uid
-can
-either be a user name, or a decimal value.
-.TP
-.B -chroot path
-Changes  the  root  directory to that specified in
-.I path.
-This directory
-will be used for path  names  beginning with /. The root directory is
-inherited by all children of the current process.
-.TP
-.B -cmp fileName1 fileName2
-Determines whether or not the specified file names have identical data.
-This says that the files are links to each other, are different sizes,
-differ at a particular byte number, or are identical.
-.TP
-.B -cp srcName ... destName
-Copies one or more files from the
-.I srcName
-to the
-.IR destName .
-If more
-than one srcName is given, or if destName is a directory, then all
-the srcNames are copied into the destName directory with the same
-names as the srcNames.
-.TP
-.B -dd if=name of=name [bs=n] [count=n] [skip=n] [seek=n]
-Copy data from one file to another with the specified parameters.
-The
-.I if
-and
-.I of
-arguments must be provided, so stdin and stdout cannot
-be specified.  The
-.I bs
-argument is the block size, and is a numeric
-value (which defaults to 512 bytes).
-.I Count
-is the number of blocks
-to be copied (which defaults to end of file for the input file).
-.I Skip
-is the number of blocks to ignore before copying (seek is used
-if possible, and the default is 0).
-.I Seek
-is the number of blocks to
-seek in the output file before writing (and defaults to 0).  Any of
-the numeric decimal values can have one or more trailing letters
-from the set 'kbw', which multiplies the value by 1024, 512, and 2
-respectively.  The command reports the number of full blocks read
-and written, and whether or not any partial block was read or written.
-.TP
-.B -echo [args] ...
-Echo the arguments to the -echo command.  Wildcards are expanded,
-so this is a convenient way to get a quick list of file names in a directory.
-The output is always terminated with a newline.
-.TP
-.B -ed [fileName]
-Edit the specified file using line-mode commands.  The following
-.B ed
-commands are provided: = c r w i a d p l s f k z and q.
-Line numbers can be constants, ".", "$", "'x", 
-.RI / string /
-and simple
-arithmetic combinations of these.  The substitute command and the
-search expression can only use literal strings.  There are some
-small differences in the way that some commands behave.
-.TP
-.B exec fileName [args]
-Execute the specified program with the specified arguments.
-This replaces
-.B sash
-completely by the executed program.
-.TP
-.B exit
-Quit from
-.BR sash .
-.TP
-.B -file fileName ...
-Examine the specified files and print out their file type.
-This indicates whether the files are regular files or not,
-whether they contain printable text or shell scripts,
-are executables, or contain binary data.
-.TP
-.B -find dirName [-xdev] [-type chars] [-name pattern] [-size minSize]
-Find all files contained within the specified directory
-tree which meet all of the specified conditions.
-The -xdev option prevents crossing of mount points.
-The -name option specifies a wildcard pattern to match the last
-component of the file names.
-The -type option specifies that the files must have a type
-matching the specified list from the set: f d c b p s l.
-These represent regular files, directories, character devices,
-block devices, named pipes, sockets, and symbolic links.
-The -size option specifies that the files must be regular files or
-directories which contain at least the specified number of bytes.
-.TP
-.B -grep [-in] word fileName ...
-Display lines of the specified files which contain the given word.
-If only one file name is given, then only the matching lines are
-printed.  If multiple file names are given, then the file names are
-printed along with the matching lines.
-.I Word
-must be a single word,
-(ie, not a regular expression).  If -i is given, then case is
-ignored when doing the search.  If -n is given, then the line
-numbers of the matching lines are also printed.
-.TP
-.B -gunzip inputFileName ... [-o outputPath]
-Uncompress one or more files that had been compressed using the
-.I gzip
-or
-.I compress
-algorithms.
-If the -o option is not given,
-then each of the input file names must have one of the
-extensions ".gz", ".tgz", or ".Z",
-and those files will be replaced by the uncompressed versions of those files.
-The original files will be deleted after the output files have been
-successfully created.
-The uncompressed versions of the files have the same names as the original
-file names, except for a simple modification of their extensions.
-If an extension is ".tgz", then the extension is replaced by ".tar".
-Otherwise, the ".gz" or ".Z" extension is removed.
-.sp
-If the -o option is given, then the input files will not be deleted,
-and the uncompressed versions of the files will be created as specified
-by
-.IR outputPath .
-If the output path is a directory, then the uncompressed versions of the
-input files will be placed in that directory with their file names
-modified as described above, or with the same name if the input file name
-does not have one of the special extensions.
-If the output path is a regular file, then only one input file is allowed,
-and the uncompressed version of that input file is created as the output
-path exactly as specified.
-If the output path is a block or character device, then the uncompressed
-versions of the input files are concatenated to the device.
-.sp
-This command is only available if
-.B sash
-was compiled to use the gzip library.
-.TP
-.B -gzip inputFileName ... [-o outputPath]
-Compresses one or more files using the
-.I gzip
-algorithm.
-If the -o option is not given,
-then each of the input file names will be replaced by the compressed
-versions of those files,
-The original files will be deleted after the output files have been
-successfully created.
-The compressed versions of the files have the same names as the original
-file names, except for a simple modification of the extensions.
-If an extension is ".tar", then the extension is replaced by ".tgz".
-Otherwise, the ".gz" extension is added.
-.sp
-If the -o option is given, then the input files will not be deleted,
-and the compressed versions of the files will be created as specified
-by
-.IR outputPath .
-If the output path is a directory, then the compressed versions of the
-input files will be placed in that directory with their file names
-modified as described above.
-If the output path is not a directory, then only one input file is allowed,
-and the compressed version of that input file is created as the output
-path exactly as specified.
-.sp
-This command is only available if
-.B sash
-was compiled to use the gzip library.
-.TP
-.B help [word]
-Displays a list of built-in commands along with their usage strings.
-If a word is given,
-then just those commands whose name or usage contains the word is displayed.
-If a word is specified which exactly matches a built-in command name,
-then a short description of the command and its usage is given.
-.TP
-.B -kill [-signal] pid ...
-Sends the specified signal to the specified list of processes.
-.I Signal
-is a numeric value, or one of the special values HUP, INT,
-QUIT, KILL, TERM, STOP, CONT, USR1 or USR2.
-If no signal is specified then SIGTERM is used.
-.TP
-.B -losetup [-d] loopDev [file]
-Associates loopback devices with files on the system. If
-.I -d
-is not given,
-the loopback device
-.I loopDev
-is associated with
-.I file.
-If
-.I -d
-is given,
-.I loopDev
-is unassociated with the file it's currently configured for.
-.TP
-.B -ln [-s] srcName ... destName
-Links one or more files from the
-.I srcName
-to the specified
-.IR destName .
-If there are
-multiple srcNames, or destName is a directory, then the link is
-put in the destName directory with the same name as the source name.
-The default links are hard links.  Using -s makes symbolic links.
-For symbolic links, only one srcName can be specified.
-.TP
-.B -ls [-lidFC] fileName ...
-Display information about the specified list of file names.
-The normal listing is simply a list of file names, one per line.
-The options available are -l, -n, -i, -d, and -F.
-The -l option produces a long listing giving the normal 'ls' information.
-The -n option is like -l except that numeric user and group ids are shown.
-The -i option displays the inode numbers of the files.
-The -d option displays information about a directory, instead of the
-files within it.
-The -F option appends a slash or asterisk to the file name if the file
-is a directory or is executable.
-The -C option displays the file names in a multi-column format.
-The width of the output is calculated using the COLS environment variable.
-.TP
-.B -lsattr fileName ...
-Display attributes for the specified files on an ext2 or ext3 file system.
-The letter 'i' indicates that the file is immutable and cannot change.
-The letter 'a' indicates that the file is append-only.
-Dashes are shown where the attributes are not set.
-This command is only available on Linux.
-.TP
-.B -mkdir dirName ...
-Creates the specified directories.  They are created with the
-default permissions.
-.TP
-.B -mknod fileName type major minor
-Creates a special device node, either a character file or a block
-file.
-.I Filename
-is the name of the node.
-.I Type
-is either 'c' or 'd'.
-.I Major
-is the major device number.
-.I Minor
-is the minor device number.
-Both of these numbers are decimal.
-.TP
-.B -more fileName ...
-Type out the contents of the specified file names, one page at a
-time.  For each page displayed, you can type 'n' and a return to go
-to the next file, 'q' and a return to quit the command completely,
-or just a return to go to the next page.  The environment variables
-LINES and COLS can be used to set the page size.
-.TP
-.B -mount [-t type] [-r] [-s] [-e] [-m] devName dirName
-Mount a filesystem on a directory name.
-The -t option specifies the type of filesystem being mounted,
-and defaults to "ext3" for Linux and "ffs" for BSD.
-The -r option indicates to mount the filesystem read-only.
-The -s option indicates to mount the filesystem no-suid.
-The -e option indicates to mount the filesystem no-exec.
-The -m option indicates to remount an already mounted filesystem.
-The -m option is only available on Linux.
-.TP
-.B -mv srcName ... destName
-Moves one or more files from the
-.I srcName
-to the
-.IR destName .
-If multiple srcNames are given, or if destName is a directory, then
-the srcNames are copied into the destination directory with the
-same names as the srcNames.  Renames are attempted first, but if
-this fails because of the files being on different filesystems,
-then copies and deletes are done instead.
-.TP
-.B -pivot_root newRoot putOld
-Moves the root file system of the current process to the directory
-.I putOld
-and makes
-.I newRoot
-the  new root file system of the current process.
-.TP
-.B -printenv [name]
-If
-.I name
-is not given, this prints out the values of all the current
-environment variables.  If
-.I name
-is given, then only that environment variable value is printed.
-.TP
-.B prompt [word] ...
-Sets the prompt string that is displayed before reading of a
-command.  A space is always added to the specified prompt.
-.TP
-.B -pwd
-Prints the current working directory.
-.TP
-.B quit
-Exits from
-.BR sash .
-.TP
-.B -rm fileName ...
-Removes one or more files.
-.TP
-.B -rmdir dirName ...
-Removes one or more directories.  The directories must be empty
-for this to be successful.
-.TP
-.B setenv name value
-Set the value of an environment variable.
-.TP
-.B source fileName
-Execute commands which are contained in the specified file name.
-.TP
-.B -sum fileName ...
-Calculates checksums for one or more files.
-This is the 16 bit checksum compatible with the BSD sum program.
-.TP
-.B -sync
-Do a "sync" system call to force dirty blocks out to the disk.
-.TP
-.B -tar [ctxv]f tarFileName [fileName] ...
-Create, list or extract files from a tar archive.
-The f option must be specified, and accepts a device or file name
-argument which contains the tar archive.
-When creating, at least one file name must be specified to be stored.
-If a file name is a directory, then all the files and directories
-within the directory are stored.
-Linked files and other special file types are not handled properly.
-When listing or extracting files, only those files starting with
-the specified file names are processed.
-If no file names are specified, then all files in the archive are processed.
-Leading slashes in the tar archive file names are always removed so that you
-might need to cd to "/" to restore files which had absolute paths.
-.TP
-.B -touch fileName ...
-Updates the modify times of the specifed files.  If a file does not
-exist, then it will be created with the default protection.
-.TP
-.B umask [mask]
-If
-.I mask
-is given, sets the "umask" value used for initializing the
-permissions of newly created files.  If
-.I mask
-is not given, then the
-current umask value is printed.  The mask is an octal value.
-.TP
-.B -umount [-f] fileName
-Unmounts a file system.  The file name can either be the device name
-which is mounted, or else the directory name which the file system
-is mounted onto.
-The -f option unmounts the filesystem even if it is being used.
-The -f option is only available on BSD.
-.TP
-.B unalias name
-Remove the definition for the specified alias.
-.TP
-.B -where program
-Prints out all of paths defined by the PATH environment variable where the
-specified program exists.  If the program exists but cannot be executed,
-then the reason is also printed.
-.SH OPTIONS
-There are several command line options to
-.BR sash .
-.PP
-The -c option executes the next argument as a command (including embedded
-spaces to separate the arguments of the command), and then exits.
-.PP
-The -f option executes the commands contained in the file name specified
-by the next argument, and then exits.
-This feature can be used to create executable scripts for
-.B sash
-by starting the script file with a line similar to:
-.nf
-       #! /bin/sash -f
-.fi
-.PP
-The -p option takes the next argument as the prompt string to be used
-when prompting for commands.
-.PP
-The -q option makes
-.B sash
-quiet, which simply means that it doesn't print its introduction line
-when it starts.
-This option is also implied if the -c or -f options are used.
-.PP
-The -a option creates aliases for the built-in commands so
-that they replace the corresponding standard commands.
-This is the same result as if the 'aliasall' command was used.
-.SH SYSTEM RECOVERY
-This section contains some useful information about using
-.B sash
-with
-.B lilo
-to perform system recovery in some situations.
-Similar concepts should exist for other boot loaders and operating systems.
-.PP
-When important shared libraries are being upgraded,
-it might be a good idea to have
-.B sash
-already running on a console by itself.
-Then if there is a problem with the shared libraries
-.B sash
-will be unaffected and you may be able to use it to fix the problem.
-.PP
-If a problem with the system shows up at boot time so that you cannot
-enter multi-user mode and log in,
-then you can first try booting into single-user mode by adding the
-.I single
-keyword after your kernel image name at the
-.B lilo
-prompt.
-If you manage to reach a shell prompt, then you can run
-.B sash
-from that shell (if necessary).
-One reason for doing this is that you might need to use the
-.B -mount
-command with the -m option to remount the root file system
-so that it can be modified.
-.PP
-If you cannot reach the shell in single-user mode,
-then you can try running sash directly as a replacement for the init process.
-This is done by adding the
-.I init=/bin/sash
-keyword after your kernel image name at the
-.B lilo
-prompt.
-When this is done, then the use of the
-.B aliasall
-command might be useful to reduce attempts to access the root file system
-when running commands.
-.PP
-If your root file system is so corrupted that you cannot get
-.B sash
-to run at all, then you will have to resort to a system recovery floppy.
-.SH WARNINGS
-.B Sash
-should obviously be linked statically, otherwise its purpose is lost.
-Note that even if the rest of the program is linked statically, the
-password and group lookup routines in the C library can still be dynamic.
-For that reason, if there are problems then it might be necessary to
-only use numeric ids for the -chown and -chgrp commands and to use
-the -n option instead of -l for the -ls command.
-.PP
-Several other system commands might be necessary for system recovery,
-but aren't built-in to
-.BR sash .
-.SH AUTHOR
-.nf
-David I. Bell
-dbell@tip.net.au
-5 March 2014
-.fi
diff --git a/src/sash/sash.c b/src/sash/sash.c
deleted file mode 100644 (file)
index 8c15711..0000000
+++ /dev/null
@@ -1,1373 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * Stand-alone shell for system maintainance for Linux.
- * This program should NOT be built using shared libraries.
- */
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <errno.h>
-
-#include "sash.h"
-
-
-static const char * const      version = "3.8";
-
-
-/*
- * The special maximum argument value which means that there is
- * no limit to the number of arguments on the command line.
- */
-#define        INFINITE_ARGS   0x7fffffff
-
-
-/*
- * One entry of the command table.
- */
-typedef struct
-{
-       const char *    name;
-       int             (*func)(int argc, const char ** argv);
-       int             minArgs;
-       int             maxArgs;
-       const char *    description;
-       const char *    usage;
-} CommandEntry;
-
-
-/*
- * The table of built-in commands.
- * This is terminated wih an entry containing NULL values.
- */
-static const CommandEntry      commandEntryTable[] =
-{
-       {
-               "alias",        do_alias,       1,      INFINITE_ARGS,
-               "Define a command alias",
-               "[name [command]]"
-       },
-
-       {
-               "aliasall",     do_aliasall,    1,      1,
-               "Define aliases for all of the build-in commands",
-               ""
-       },
-
-       {
-               "-ar",          do_ar,          3,      INFINITE_ARGS,
-               "Extract or list files from an AR file",
-               "[txp]v arFileName fileName ..."
-       },
-
-       {
-               "cd",           do_cd,          1,      2,
-               "Change current directory",
-               "[dirName]"
-       },
-
-#if    HAVE_LINUX_ATTR
-       {
-               "-chattr",      do_chattr,      3,      INFINITE_ARGS,
-               "Change ext2 file attributes",
-               "[+i] [-i] [+a] [-a] fileName ..."
-       },
-#endif
-
-       {
-               "-chgrp",       do_chgrp,       3,      INFINITE_ARGS,
-               "Change the group id of some files",
-               "gid fileName ..."
-       },
-
-       {
-               "-chmod",       do_chmod,       3,      INFINITE_ARGS,
-               "Change the protection of some files",
-               "mode fileName ..."
-       },
-
-       {
-               "-chown",       do_chown,       3,      INFINITE_ARGS,
-               "Change the owner id of some files",
-               "uid fileName ..."
-       },
-
-       {
-               "-cmp",         do_cmp,         3,      3,
-               "Compare two files for equality",
-               "fileName1 fileName2"
-       },
-
-       {
-               "-cp",          do_cp,          3,      INFINITE_ARGS,
-               "Copy files",
-               "srcName ... destName"
-       },
-
-#ifdef HAVE_LINUX_CHROOT
-       {
-               "-chroot",      do_chroot,      2,      2,
-               "change root file system",
-               "new_root_dir"
-       },
-#endif
-
-       {
-               "-dd",          do_dd,          3,      INFINITE_ARGS,
-               "Copy data between two files",
-               "if=name of=name [bs=n] [count=n] [skip=n] [seek=n]"
-       },
-
-       {
-               "-echo",        do_echo,        1,      INFINITE_ARGS,
-               "Echo the arguments",
-               "[args] ..."
-       },
-
-       {
-               "-ed",          do_ed,          1,      2,
-               "Edit a fileName using simple line mode commands",
-               "[fileName]"
-       },
-
-       {
-               "exec",         do_exec,        2,      INFINITE_ARGS,
-               "Execute another program in place of this sash process",
-               "fileName [args]"
-       },
-
-       {
-               "exit",         do_exit,        1,      2,
-               "Exit from sash",
-               "[exit value]"
-       },
-
-       {
-               "-file",        do_file,        1,      INFINITE_ARGS,
-               "Describe information about files",
-               "fileName ..."
-       },
-
-       {
-               "-find",        do_find,        2,      INFINITE_ARGS,
-               "Find files in a directory tree meeting some conditions",
-               "dirName [-xdev] [-type chars] [-name pattern] [-size minSize]"
-       },
-
-       {
-               "-grep",        do_grep,        3,      INFINITE_ARGS,
-               "Look for lines containing a word in some files",
-               "[-in] word fileName ..."
-       },
-
-#if    HAVE_GZIP
-       {
-               "-gunzip",      do_gunzip,      2,      INFINITE_ARGS,
-               "Uncompress files which were saved in GZIP or compress format",
-               "fileName ... [-o outputPath]"
-       },
-
-       {
-               "-gzip",        do_gzip,        2,      INFINITE_ARGS,
-               "Compress files into GZIP format",
-               "fileName ... [-o outputPath]"
-       },
-#endif
-
-       {
-               "help",         do_help,        1,      2,
-               "Print help about a command",
-               "[word]"
-       },
-
-       {
-               "-kill",        do_kill,        2,      INFINITE_ARGS,
-               "Send a signal to the specified process",
-               "[-sig] pid ..."
-       },
-
-#ifdef HAVE_LINUX_LOSETUP
-       {
-               "-losetup",     do_losetup,     3,      3,
-               "Associate a loopback device with a file",
-               "[-d] device\n       -losetup device filename"
-       },
-#endif
-
-       {
-               "-ln",          do_ln,          3,      INFINITE_ARGS,
-               "Link one fileName to another",
-               "[-s] srcName ... destName"
-       },
-
-       {
-               "-ls",          do_ls,          1,      INFINITE_ARGS,
-               "List information about files or directories",
-               "[-lidFC] fileName ..."
-       },
-
-#if    HAVE_LINUX_ATTR
-       {
-               "-lsattr",      do_lsattr,      2,      INFINITE_ARGS,
-               "List ext2 file attributes",
-               "fileName ..."
-       },
-#endif
-
-       {
-               "-mkdir",       do_mkdir,       2,      INFINITE_ARGS,
-               "Create a directory",
-               "dirName ..."
-       },
-
-       {
-               "-mknod",       do_mknod,       5,      5,
-               "Create a special type of file",
-               "fileName type major minor"
-       },
-
-       {
-               "-more",        do_more,        2,      INFINITE_ARGS,
-               "Type file contents page by page",
-               "fileName ..."
-       },
-
-       {
-               "-mount",       do_mount,       3,      INFINITE_ARGS,
-               "Mount or remount a filesystem on a directory",
-#if    HAVE_LINUX_MOUNT
-               "[-t type] [-r] [-s] [-e] [-m] devName dirName"
-#elif  HAVE_BSD_MOUNT
-               "[-t type] [-r] [-s] [-e] devName dirName"
-#else
-               "[-t type] devName dirName"
-#endif
-       },
-
-       {
-               "-mv",          do_mv,          3,      INFINITE_ARGS,
-               "Move or rename files",
-               "srcName ... destName"
-       },
-
-#ifdef HAVE_LINUX_PIVOT
-       {
-               "-pivot_root",  do_pivot_root,  3,      3,
-               "pivot the root file system",
-               "new_dir old_dir"
-       },
-#endif
-
-       {
-               "-printenv",    do_printenv,    1,      2,
-               "Print environment variables",
-               "[name]"
-       },
-
-       {
-               "prompt",       do_prompt,      2,      INFINITE_ARGS,
-               "Set the prompt string for sash",
-               "string"
-       },
-
-       {
-               "-pwd",         do_pwd,         1,      1,
-               "Print the current working directory",
-               ""
-       },
-
-       {
-               "quit",         do_exit,        1,      1,
-               "Exit from sash",
-               ""
-       },
-
-       {
-               "-rm",          do_rm,          2,      INFINITE_ARGS,
-               "Remove the specified files",
-               "fileName ..."
-       },
-
-       {
-               "-rmdir",       do_rmdir,       2,      INFINITE_ARGS,
-               "Remove the specified empty directories",
-               "dirName ..."
-       },
-
-       {
-               "setenv",       do_setenv,      3,      3,
-               "Set an environment variable value",
-               "name value"
-       },
-
-       {
-               "source",       do_source,      2,      2,
-               "Read commands from the specified file",
-               "fileName"
-       },
-
-       {
-               "-sum",         do_sum,         2,      INFINITE_ARGS,
-               "Calculate checksums of the specified files",
-               "fileName ..."
-       },
-
-       {
-               "-sync",        do_sync,        1,      1,
-               "Sync the disks to force cached data to them",
-               ""
-       },
-
-       {
-               "-tar",         do_tar,         2,      INFINITE_ARGS,
-               "Create, extract, or list files from a TAR file",
-               "[cxtv]f tarFileName fileName ..."
-       },
-
-       {
-               "-touch",       do_touch,       2,      INFINITE_ARGS,
-               "Update times or create the specified files",
-               "fileName ..."
-       },
-
-       {
-               "umask",        do_umask,       1,      2,
-               "Set the umask value for file protections",
-               "[mask]"
-       },
-
-       {
-#if    HAVE_BSD_MOUNT
-               "-umount",      do_umount,      2,      3,
-               "Unmount a filesystem",
-               "[-f] fileName"
-#else
-               "-umount",      do_umount,      2,      2,
-               "Unmount a filesystem",
-               "fileName"
-#endif
-       },
-
-       {
-               "unalias",      do_unalias,     2,      2,
-               "Remove a command alias",
-               "name"
-       },
-
-       {
-               "-where",       do_where,       2,      2,
-               "Type the location of a program",
-               "program"
-       },
-
-       {
-               NULL,           0,              0,      0,
-               NULL,
-               NULL
-       }
-};
-
-
-/*
- * The definition of an command alias.
- */
-typedef struct
-{
-       char *  name;
-       char *  value;
-} Alias;
-
-
-/*
- * Local data.
- */
-static Alias * aliasTable;
-static int     aliasCount;
-
-static FILE *  sourcefiles[MAX_SOURCE];
-static int     sourceCount;
-
-static BOOL    intCrlf = TRUE;
-static char *  prompt;
-
-
-/*
- * Local procedures.
- */
-static void    catchInt(int);
-static void    catchQuit(int);
-static int     readFile(const char * name);
-static int     command(const char * cmd);
-static BOOL    tryBuiltIn(const char * cmd);
-static int     runCmd(const char * cmd);
-static void    childProcess(const char * cmd);
-static void    showPrompt(void);
-static void    usage(void);
-static Alias * findAlias(const char * name);
-static void    expandVariable(char * name);
-
-
-/*
- * Global interrupt flag.
- */
-BOOL   intFlag;
-
-
-int
-main(int argc, const char ** argv)
-{
-       const char *    cp;
-       const char *    singleCommand;
-       const char *    commandFile;
-       BOOL            quietFlag;
-       BOOL            aliasFlag;
-        BOOL           interactiveFlag;
-       char            buf[PATH_LEN];
-
-       singleCommand = NULL;
-       commandFile = NULL;
-       quietFlag = FALSE;
-       aliasFlag = FALSE;
-        interactiveFlag = FALSE;
-
-       /*
-        * Look for options.
-        */
-       argv++;
-       argc--;
-
-       while ((argc > 0) && (**argv == '-'))
-       {
-               cp = *argv++ + 1;
-               argc--;
-
-               while (*cp) switch (*cp++)
-               {
-                        case '-':
-                                /*
-                                 * Ignore.  This is so that we can be
-                                 * run from login.
-                                 */
-                                break;
-
-                       case 'c':
-                               /*
-                                * Execute specified command.
-                                */
-                               if ((argc != 1) || singleCommand || interactiveFlag)
-                                       usage();
-
-                               singleCommand = *argv++;
-                               argc--;
-
-                               break;
-
-                       case 'f':
-                               /*
-                                * Execute commands from file.
-                                * This is used for sash script files.
-                                * The quiet flag is also set.
-                                */
-                               if ((argc != 1) || commandFile)
-                                       usage();
-
-                               quietFlag = TRUE;
-                               commandFile = *argv++;
-                               argc--;
-
-                               break;
-
-                       case 'i':
-                               /*
-                                * Be an interactive shell
-                                * ..is a no-op, but some contexts require this
-                                * ..interactiveFlag is to avoid -ic as a legacy
-                                */
-                                if (singleCommand)
-                                       usage();
-                                
-                                interactiveFlag = TRUE;
-                                break;
-                                
-                       case 'p':
-                               /*
-                                * Set the prompt string.
-                                */
-                               if ((argc <= 0) || (**argv == '-'))
-                                       usage();
-
-                               if (prompt)
-                                       free(prompt);
-
-                               prompt = strdup(*argv++);
-                               argc--;
-
-                               break;
-
-                       case 'q':
-                               quietFlag = TRUE;
-                               break;
-
-                       case 'a':
-                               aliasFlag = TRUE;
-                               break;
-
-                       case 'h':
-                       case '?':
-                               usage();
-                               break;
-
-                       default:
-                               fprintf(stderr, "Unknown option -%c\n", cp[-1]);
-
-                               return 1;
-               }
-       }
-
-       /*
-        * No more arguments are allowed.
-        */
-       if (argc > 0)
-               usage();
-
-       /*
-        * Default our path if it is not set.
-        */
-       if (getenv("PATH") == NULL)
-               putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc");
-
-       /*
-        * If the alias flag is set then define all aliases.
-        */
-       if (aliasFlag)
-               do_aliasall(0, NULL);
-
-       /*
-        * If we are to execute a single command, then do so and exit.
-        */
-       if (singleCommand)
-       {
-               return command(singleCommand);
-       }
-
-       /*
-        * Print a hello message unless we are told to be silent.
-        */
-       if (!quietFlag && isatty(STDIN))
-       {
-               printf("Stand-alone shell (version %s)\n", version);
-
-               if (aliasFlag)
-                       printf("Built-in commands are aliased to standard commands\n");
-       }
-
-       signal(SIGINT, catchInt);
-       signal(SIGQUIT, catchQuit);
-
-       /*
-        * Execute the user's alias file if present.
-        */
-       cp = getenv("HOME");
-
-       if (cp)
-       {
-               strcpy(buf, cp);
-               strcat(buf, "/");
-               strcat(buf, ".aliasrc");
-
-               if ((access(buf, 0) == 0) || (errno != ENOENT))
-                       readFile(buf);
-       }
-
-       /*
-        * Read commands from stdin or from a command file.
-        */
-       return readFile(commandFile);
-
-}
-
-
-/*
- * Read commands from the specified file.
- * A null name pointer indicates to read from stdin.
- */
-static int
-readFile(const char * name)
-{
-       FILE *  fp;
-       int     cc;
-       BOOL    ttyFlag;
-       char    buf[CMD_LEN];
-       int     r = 0;
-
-       if (sourceCount >= MAX_SOURCE)
-       {
-               fprintf(stderr, "Too many source files\n");
-
-               return 1;
-       }
-
-       fp = stdin;
-
-       if (name)
-       {
-               fp = fopen(name, "r");
-
-               if (fp == NULL)
-               {
-                       perror(name);
-
-                       return 1;
-               }
-       }
-
-       sourcefiles[sourceCount++] = fp;
-
-       ttyFlag = isatty(fileno(fp));
-
-       while (TRUE)
-       {
-               if (ttyFlag)
-                       showPrompt();
-
-               if (intFlag && !ttyFlag && (fp != stdin))
-               {
-                       fclose(fp);
-                       sourceCount--;
-
-                       return 1;
-               }
-       
-               if (fgets(buf, CMD_LEN - 1, fp) == NULL)
-               {
-                       if (ferror(fp) && (errno == EINTR))
-                       {
-                               clearerr(fp);
-
-                               continue;
-                       }
-
-                       break;
-               }
-
-               cc = strlen(buf);
-
-               if (buf[cc - 1] == '\n')
-                       cc--;
-
-               while ((cc > 0) && isBlank(buf[cc - 1]))
-                       cc--;
-
-               buf[cc] = '\0';
-
-               r = command(buf);
-       }
-
-       if (ferror(fp))
-       {
-               perror("Reading command line");
-
-               if (fp == stdin)
-                       exit(1);
-       }
-
-       clearerr(fp);
-
-       if (fp != stdin)
-               fclose(fp);
-
-       sourceCount--;
-
-       return r;
-}
-
-
-/*
- * Parse and execute one null-terminated command line string.
- * This breaks the command line up into words, checks to see if the
- * command is an alias, and expands wildcards.
- */
-static int
-command(const char * cmd)
-{
-       const char *    endCmd;
-       const Alias *   alias;
-       char            newCommand[CMD_LEN];
-       char            cmdName[CMD_LEN];
-
-       /*
-        * Rest the interrupt flag and free any memory chunks that
-        * were allocated by the previous command.
-        */
-       intFlag = FALSE;
-
-       freeChunks();
-
-       /*
-        * Skip leading blanks.
-        */
-       while (isBlank(*cmd))
-               cmd++;
-
-       /*
-        * If the command is empty or is a comment then ignore it.
-        */
-       if ((*cmd == '\0') || (*cmd == '#'))
-               return 0;
-
-       /*
-        * Look for the end of the command name and then copy the
-        * command name to a buffer so we can null terminate it.
-        */
-       endCmd = cmd;
-
-       while (*endCmd && !isBlank(*endCmd))
-               endCmd++;
-
-       memcpy(cmdName, cmd, endCmd - cmd);
-
-       cmdName[endCmd - cmd] = '\0';
-
-       /*
-        * Search for the command name in the alias table.
-        * If it is found, then replace the command name with
-        * the alias value, and append the current command
-        * line arguments to that.
-        */
-       alias = findAlias(cmdName);
-
-       if (alias)
-       {
-               strcpy(newCommand, alias->value);
-               strcat(newCommand, endCmd);
-
-               cmd = newCommand;
-       }
-
-       /*
-        * Expand simple environment variables
-        */
-       while (strstr(cmd, "$(")) expandVariable((char *)cmd);
-
-       /*
-        * Now look for the command in the builtin table, and execute
-        * the command if found.
-        */
-       if (tryBuiltIn(cmd))
-               return 0; /* This is a blatant lie */
-
-       /*
-        * The command is not a built-in, so run the program along
-        * the PATH list.
-        */
-       return runCmd(cmd);
-}
-
-
-/*
- * Try to execute a built-in command.
- * Returns TRUE if the command is a built in, whether or not the
- * command succeeds.  Returns FALSE if this is not a built-in command.
- */
-static BOOL
-tryBuiltIn(const char * cmd)
-{
-       const char *            endCmd;
-       const CommandEntry *    entry;
-       int                     argc;
-       const char **           argv;
-       char                    cmdName[CMD_LEN];
-
-       /*
-        * Look for the end of the command name and then copy the
-        * command name to a buffer so we can null terminate it.
-        */
-       endCmd = cmd;
-
-       while (*endCmd && !isBlank(*endCmd))
-               endCmd++;
-
-       memcpy(cmdName, cmd, endCmd - cmd);
-
-       cmdName[endCmd - cmd] = '\0';
-
-       /*
-        * Search the command table looking for the command name.
-        */
-       for (entry = commandEntryTable; entry->name != NULL; entry++)
-       {
-               if (strcmp(entry->name, cmdName) == 0)
-                       break;
-       }
-
-       /*
-        * If the command is not a built-in, return indicating that.
-        */
-       if (entry->name == NULL)
-               return FALSE;
-
-       /*
-        * The command is a built-in.
-        * Break the command up into arguments and expand wildcards.
-        */
-       if (!makeArgs(cmd, &argc, &argv))
-               return TRUE;
-
-       /*
-        * Give a usage string if the number of arguments is too large
-        * or too small.
-        */
-       if ((argc < entry->minArgs) || (argc > entry->maxArgs))
-       {
-               fprintf(stderr, "usage: %s %s\n", entry->name, entry->usage);
-
-               return TRUE;
-       }
-
-       /*
-        * Call the built-in function with the argument list.
-        */
-       entry->func(argc, argv);
-
-       return TRUE;
-}
-
-
-/*
- * Execute the specified command either by forking and executing
- * the program ourself, or else by using the shell.  Returns the
- * exit status, or -1 if the program cannot be executed at all.
- */
-static int
-runCmd(const char * cmd)
-{
-       const char *    cp;
-       BOOL            magic;
-       pid_t           pid;
-       int             status;
-
-       /*
-        * Check the command for any magic shell characters
-        * except for quoting.
-        */
-       magic = FALSE;
-
-       for (cp = cmd; *cp; cp++)
-       {
-               if ((*cp >= 'a') && (*cp <= 'z'))
-                       continue;
-
-               if ((*cp >= 'A') && (*cp <= 'Z'))
-                       continue;
-
-               if (isDecimal(*cp))
-                       continue;
-
-               if (isBlank(*cp))
-                       continue;
-
-               if ((*cp == '.') || (*cp == '/') || (*cp == '-') ||
-                       (*cp == '+') || (*cp == '=') || (*cp == '_') ||
-                       (*cp == ':') || (*cp == ',') || (*cp == '\'') ||
-                       (*cp == '"'))
-               {
-                       continue;
-               }
-
-               magic = TRUE;
-       }
-
-       /*
-        * If there were any magic characters used then run the
-        * command using the shell.
-        */
-       if (magic)
-               return trySystem(cmd);
-
-       /*
-        * No magic characters were in the command, so we can do the fork
-        * and exec ourself.
-        */
-       pid = fork();
-
-       if (pid < 0)
-       {
-               perror("fork failed");
-
-               return -1;
-       }
-
-       /*
-        * If we are the child process, then go execute the program.
-        */
-       if (pid == 0)
-               childProcess(cmd);
-
-       /*
-        * We are the parent process.
-        * Wait for the child to complete.
-        */
-       status = 0;
-       intCrlf = FALSE;
-
-       while (((pid = waitpid(pid, &status, 0)) < 0) && (errno == EINTR))
-               ;
-
-       intCrlf = TRUE;
-
-       if (pid < 0)
-       {
-               fprintf(stderr, "Error from waitpid: %s", strerror(errno));
-
-               return -1;
-       }
-
-       if (WIFSIGNALED(status))
-       {
-               fprintf(stderr, "pid %ld: killed by signal %d\n",
-                       (long) pid, WTERMSIG(status));
-
-               return -1;
-       }
-
-       return WEXITSTATUS(status);
-}
-
-
-/*
- * Here as the child process to try to execute the command.
- * This is only called if there are no meta-characters in the command.
- * This procedure never returns.
- */
-static void
-childProcess(const char * cmd)
-{
-       const char **   argv;
-       int             argc;
-
-       /*
-        * Close any extra file descriptors we have opened.
-        */     
-       while (--sourceCount >= 0)
-       {
-               if (sourcefiles[sourceCount] != stdin)
-                       fclose(sourcefiles[sourceCount]);
-       }
-
-       /*
-        * Break the command line up into individual arguments.
-        * If this fails, then run the shell to execute the command.
-        */
-       if (!makeArgs(cmd, &argc, &argv))
-       {
-               int status = trySystem(cmd);
-
-               if (status == -1)
-                       exit(99);
-
-               exit(status);
-       }
-
-       /*
-        * Try to execute the program directly.
-        */
-       execvp(argv[0], (char **) argv);
-
-       /*
-        * The exec failed, so try to run the command using the shell
-        * in case it is a shell script.
-        */
-       if (errno == ENOEXEC)
-       {
-               int status = trySystem(cmd);
-
-               if (status == -1)
-                       exit(99);
-
-               exit(status);
-       }
-
-       /*
-        * There was something else wrong, complain and exit.
-        */
-       perror(argv[0]);
-       exit(1);
-}
-
-
-int
-do_help(int argc, const char ** argv)
-{
-       const CommandEntry *    entry;
-       const char *            str;
-
-       str = NULL;
-
-       if (argc == 2)
-               str = argv[1];
-
-       /*
-        * Check for an exact match, in which case describe the program.
-        */
-       if (str)
-       {
-               for (entry = commandEntryTable; entry->name; entry++)
-               {
-                       if (strcmp(str, entry->name) == 0)
-                       {
-                               printf("%s\n", entry->description);
-
-                               printf("usage: %s %s\n", entry->name,
-                                       entry->usage);
-
-                               return 0;
-                       }
-               }
-       }
-
-       /*
-        * Print short information about commands which contain the
-        * specified word.
-        */
-       for (entry = commandEntryTable; entry->name; entry++)
-       {
-               if ((str == NULL) || (strstr(entry->name, str) != NULL) ||
-                       (strstr(entry->usage, str) != NULL))
-               {
-                       printf("%-10s %s\n", entry->name, entry->usage);
-               }
-       }
-
-       return 0;
-}
-
-
-int
-do_alias(int argc, const char ** argv)
-{
-       const char *    name;
-       char *          value;
-       Alias *         alias;
-       int             count;
-       char            buf[CMD_LEN];
-
-       if (argc < 2)
-       {
-               count = aliasCount;
-
-               for (alias = aliasTable; count-- > 0; alias++)
-                       printf("%s\t%s\n", alias->name, alias->value);
-
-               return 0;
-       }
-
-       name = argv[1];
-
-       if (argc == 2)
-       {
-               alias = findAlias(name);
-
-               if (alias)
-                       printf("%s\n", alias->value);
-               else
-               {
-                       fprintf(stderr, "Alias \"%s\" is not defined\n", name);
-
-                       return 1;
-               }
-
-               return 0;       
-       }
-
-       if (strcmp(name, "alias") == 0)
-       {
-               fprintf(stderr, "Cannot alias \"alias\"\n");
-
-               return 1;
-       }
-
-       if (!makeString(argc - 2, argv + 2, buf, CMD_LEN))
-               return 1;
-
-       value = malloc(strlen(buf) + 1);
-
-       if (value == NULL)
-       {
-               fprintf(stderr, "No memory for alias value\n");
-
-               return 1;
-       }
-
-       strcpy(value, buf);
-
-       alias = findAlias(name);
-
-       if (alias)
-       {
-               free(alias->value);
-               alias->value = value;
-
-               return 0;
-       }
-
-       if ((aliasCount % ALIAS_ALLOC) == 0)
-       {
-               count = aliasCount + ALIAS_ALLOC;
-
-               if (aliasTable)
-               {
-                       alias = (Alias *) realloc(aliasTable,
-                               sizeof(Alias) * count);
-               }
-               else
-                       alias = (Alias *) malloc(sizeof(Alias) * count);
-
-               if (alias == NULL)
-               {
-                       free(value);
-                       fprintf(stderr, "No memory for alias table\n");
-
-                       return 1;
-               }
-
-               aliasTable = alias;
-       }
-
-       alias = &aliasTable[aliasCount];
-
-       alias->name = malloc(strlen(name) + 1);
-
-       if (alias->name == NULL)
-       {
-               free(value);
-               fprintf(stderr, "No memory for alias name\n");
-
-               return 1;
-       }
-
-       strcpy(alias->name, name);
-       alias->value = value;
-       aliasCount++;
-
-       return 0;
-}
-
-
-/*
- * Build aliases for all of the built-in commands which start with a dash,
- * using the names without the dash.
- */
-int
-do_aliasall(int argc, const char **argv)
-{
-       const CommandEntry *    entry;
-       const char *            name;
-       const char *            newArgv[4];
-
-       for (entry = commandEntryTable; entry->name; entry++)
-       {
-               name = entry->name;
-
-               if (*name != '-')
-                       continue;
-
-               newArgv[0] = "alias";
-               newArgv[1] = name + 1;
-               newArgv[2] = name;
-               newArgv[3] = NULL;
-
-               do_alias(3, newArgv);
-       }
-
-       return 0;
-}
-
-
-/*
- * Look up an alias name, and return a pointer to it.
- * Returns NULL if the name does not exist.
- */
-static Alias *
-findAlias(const char * name)
-{
-       Alias * alias;
-       int     count;
-
-       count = aliasCount;
-
-       for (alias = aliasTable; count-- > 0; alias++)
-       {
-               if (strcmp(name, alias->name) == 0)
-                       return alias;
-       }
-
-       return NULL;
-}
-
-
-int
-do_source(int argc, const char ** argv)
-{
-       return readFile(argv[1]);
-}
-
-
-int
-do_exec(int argc, const char ** argv)
-{
-       const char *    name;
-
-       name = argv[1];
-
-       while (--sourceCount >= 0)
-       {
-               if (sourcefiles[sourceCount] != stdin)
-                       fclose(sourcefiles[sourceCount]);
-       }
-
-       argv[argc] = NULL;
-
-       execvp(name, (char **) argv + 1);
-       perror(name);
-
-       return 1;
-}
-
-
-int
-do_prompt(int argc, const char ** argv)
-{
-       char *  cp;
-       char    buf[CMD_LEN];
-
-       if (!makeString(argc - 1, argv + 1, buf, CMD_LEN))
-               return 1;
-
-       cp = malloc(strlen(buf) + 2);
-
-       if (cp == NULL)
-       {
-               fprintf(stderr, "No memory for prompt\n");
-
-               return 1;
-       }
-
-       strcpy(cp, buf);
-       strcat(cp, " ");
-
-       if (prompt)
-               free(prompt);
-
-       prompt = cp;
-
-       return 0;
-}
-
-
-int
-do_unalias(int argc, const char ** argv)
-{
-       Alias * alias;
-
-       while (--argc > 0)
-       {
-               alias = findAlias(*++argv);
-
-               if (alias == NULL)
-                       continue;
-
-               free(alias->name);
-               free(alias->value);
-               aliasCount--;
-               alias->name = aliasTable[aliasCount].name;
-               alias->value = aliasTable[aliasCount].value;    
-       }
-
-       return 0;
-}
-
-
-/*
- * Display the prompt string.
- */
-static void
-showPrompt(void)
-{
-       const char *    cp;
-
-       cp = "> ";
-
-       if (prompt)
-               cp = prompt;
-
-       tryWrite(STDOUT, cp, strlen(cp));
-}      
-
-
-static void
-catchInt(int val)
-{
-       signal(SIGINT, catchInt);
-
-       intFlag = TRUE;
-
-       if (intCrlf)
-               tryWrite(STDOUT, "\n", 1);
-}
-
-
-static void
-catchQuit(int val)
-{
-       signal(SIGQUIT, catchQuit);
-
-       intFlag = TRUE;
-
-       if (intCrlf)
-               tryWrite(STDOUT, "\n", 1);
-}
-
-
-/*
- * Print the usage information and quit.
- */
-static void
-usage(void)
-{
-       fprintf(stderr, "Stand-alone shell (version %s)\n", version);
-       fprintf(stderr, "\n");
-       fprintf(stderr, "Usage: sash [-a] [-q] [-f fileName] [-c command] [-p prompt] [-i]\n");
-
-       exit(1);
-}
-
-
-/*
- * Expand one environment variable: Syntax $(VAR)
- */
-static void
-expandVariable(char * cmd)
-{
-       char    tmp[CMD_LEN];
-       char    *cp;
-       char    *ep;
-
-       strcpy(tmp, cmd);
-       cp = strstr(tmp, "$(");
-       if (cp) {
-               *cp++ = '\0';
-               strcpy(cmd, tmp);
-               ep = ++cp;
-               while (*ep && (*ep != ')')) ep++;
-               if (*ep == ')') *ep++ = '\0';
-               cp = getenv(cp);
-               if (cp) strcat(cmd, cp);
-               strcat(cmd, ep);
-       }
-       return;
-}
-
-/* END CODE */
diff --git a/src/sash/sash.h b/src/sash/sash.h
deleted file mode 100644 (file)
index b0ed254..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * Definitions for stand-alone shell for system maintainance for Linux.
- */
-
-#ifndef        SASH_H
-#define        SASH_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <memory.h>
-#include <time.h>
-#include <ctype.h>
-
-#if __OpenBSD__
-#include <sys/param.h>
-#endif
-
-#if __Linux__
-#include <malloc.h>
-#endif
-
-
-#define        PATH_LEN        1024
-#define        CMD_LEN         10240
-#define        ALIAS_ALLOC     20
-#define        EXPAND_ALLOC    1024
-#define        STDIN           0
-#define        STDOUT          1
-#define        MAX_SOURCE      10
-#define        BUF_SIZE        8192
-
-
-#define        isBlank(ch)     (((ch) == ' ') || ((ch) == '\t'))
-#define        isDecimal(ch)   (((ch) >= '0') && ((ch) <= '9'))
-#define        isOctal(ch)     (((ch) >= '0') && ((ch) <= '7'))
-#define        isWildCard(ch)  (((ch) == '*') || ((ch) == '?') || ((ch) == '['))
-
-#ifndef MAX
-#define MAX(x, y)      ((x) > (y) ? (x) : (y))
-#endif
-#ifndef MIN
-#define MIN(x, y)      ((x) < (y) ? (x) : (y))
-#endif
-
-typedef        int     BOOL;
-
-#define        FALSE   ((BOOL) 0)
-#define        TRUE    ((BOOL) 1)
-
-
-/*
- * Built-in command functions.
- */
-extern int     do_alias(int argc, const char ** argv);
-extern int     do_aliasall(int argc, const char ** argv);
-extern int     do_cd(int argc, const char ** argv);
-extern int     do_exec(int argc, const char ** argv);
-extern int     do_exit(int argc, const char ** argv);
-extern int     do_prompt(int argc, const char ** argv);
-extern int     do_source(int argc, const char ** argv);
-extern int     do_umask(int argc, const char ** argv);
-extern int     do_unalias(int argc, const char ** argv);
-extern int     do_help(int argc, const char ** argv);
-extern int     do_ln(int argc, const char ** argv);
-extern int     do_cp(int argc, const char ** argv);
-extern int     do_mv(int argc, const char ** argv);
-extern int     do_rm(int argc, const char ** argv);
-extern int     do_chmod(int argc, const char ** argv);
-extern int     do_mkdir(int argc, const char ** argv);
-extern int     do_rmdir(int argc, const char ** argv);
-extern int     do_mknod(int argc, const char ** argv);
-extern int     do_chown(int argc, const char ** argv);
-extern int     do_chgrp(int argc, const char ** argv);
-extern int     do_sum(int argc, const char ** argv);
-extern int     do_sync(int argc, const char ** argv);
-extern int     do_printenv(int argc, const char ** argv);
-extern int     do_more(int argc, const char ** argv);
-extern int     do_cmp(int argc, const char ** argv);
-extern int     do_touch(int argc, const char ** argv);
-extern int     do_ls(int argc, const char ** argv);
-extern int     do_dd(int argc, const char ** argv);
-extern int     do_tar(int argc, const char ** argv);
-extern int     do_ar(int argc, const char ** argv);
-extern int     do_mount(int argc, const char ** argv);
-extern int     do_umount(int argc, const char ** argv);
-extern int     do_setenv(int argc, const char ** argv);
-extern int     do_pwd(int argc, const char ** argv);
-extern int     do_echo(int argc, const char ** argv);
-extern int     do_kill(int argc, const char ** argv);
-extern int     do_grep(int argc, const char ** argv);
-extern int     do_file(int argc, const char ** argv);
-extern int     do_find(int argc, const char ** argv);
-extern int     do_ed(int argc, const char ** argv);
-extern int     do_where(int argc, const char ** argv);
-
-#if    HAVE_GZIP
-extern int     do_gzip(int argc, const char ** argv);
-extern int     do_gunzip(int argc, const char ** argv);
-#endif
-
-#if    HAVE_LINUX_ATTR
-extern int     do_lsattr(int argc, const char ** argv);
-extern int     do_chattr(int argc, const char ** argv);
-#endif
-
-#if    HAVE_LINUX_CHROOT
-extern int     do_chroot(int argc, const char ** argv);
-#endif
-
-#if    HAVE_LINUX_LOSETUP
-extern int     do_losetup(int argc, const char ** argv);
-#endif
-
-#if    HAVE_LINUX_PIVOT
-extern int     do_pivot_root(int argc, const char ** argv);
-extern  int    pivot_root(const char *new_root, const char *put_old);
-#endif
-
-
-/*
- * Global utility routines.
- */
-extern const char *    modeString(int mode);
-extern const char *    timeString(time_t timeVal);
-extern BOOL            isDirectory(const char * name);
-extern BOOL            isDevice(const char * name);
-extern int             nameSort(const void * p1, const void * p2);
-extern char *          getChunk(int size);
-extern char *          chunkstrdup(const char *);
-extern void            freeChunks(void);
-extern int             trySystem(const char * cmd);
-extern void            tryWrite(int fd, const char * buf, int len);
-extern int             fullWrite(int fd, const char * buf, int len);
-extern int             fullRead(int fd, char * buf, int len);
-extern void            checkStatus(const char * name, int status);
-extern BOOL            match(const char * text, const char * pattern);
-
-extern const char *    buildName
-       (const char * dirName, const char * fileName);
-
-extern BOOL    makeArgs
-       (const char * cmd, int * argcPtr, const char *** argvPtr);
-
-extern BOOL    copyFile
-       (const char * srcName, const char * destName, BOOL setModes);
-
-extern BOOL    makeString
-       (int argc, const char ** argv, char * buf, int bufLen);
-
-extern int     expandWildCards
-       (const char * fileNamePattern, const char *** retFileTable);
-
-
-/*
- * Global variable to indicate that an SIGINT occurred.
- * This is used to stop processing.
- */
-extern BOOL    intFlag;
-
-#endif
-
-/* END CODE */
diff --git a/src/sash/utils.c b/src/sash/utils.c
deleted file mode 100644 (file)
index f6bee83..0000000
+++ /dev/null
@@ -1,1144 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * Utility routines.
- */
-
-#include "sash.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "dirent.h"
-#include <utime.h>
-
-
-/*
- * A chunk of data.
- * Chunks contain data which is allocated as needed, but which is
- * not freed until all of the data needs freeing, such as at
- * the beginning of the next command.
- */
-typedef        struct  chunk   CHUNK;
-#define        CHUNK_INIT_SIZE 4
-
-struct chunk
-{
-       CHUNK * next;
-       char    data[CHUNK_INIT_SIZE];  /* actually of varying length */
-};
-
-
-static CHUNK * chunkList;
-
-
-
-/*
- * Return the standard ls-like mode string from a file mode.
- * This is static and so is overwritten on each call.
- */
-const char *
-modeString(int mode)
-{
-       static  char    buf[12];
-
-       strcpy(buf, "----------");
-
-       /*
-        * Fill in the file type.
-        */
-       if (S_ISDIR(mode))
-               buf[0] = 'd';
-       if (S_ISCHR(mode))
-               buf[0] = 'c';
-       if (S_ISBLK(mode))
-               buf[0] = 'b';
-       if (S_ISFIFO(mode))
-               buf[0] = 'p';
-#ifdef S_ISLNK
-       if (S_ISLNK(mode))
-               buf[0] = 'l';
-#endif
-#ifdef S_ISSOCK
-       if (S_ISSOCK(mode))
-               buf[0] = 's';
-#endif
-
-       /*
-        * Now fill in the normal file permissions.
-        */
-       if (mode & S_IRUSR)
-               buf[1] = 'r';
-       if (mode & S_IWUSR)
-               buf[2] = 'w';
-       if (mode & S_IXUSR)
-               buf[3] = 'x';
-       if (mode & S_IRGRP)
-               buf[4] = 'r';
-       if (mode & S_IWGRP)
-               buf[5] = 'w';
-       if (mode & S_IXGRP)
-               buf[6] = 'x';
-       if (mode & S_IROTH)
-               buf[7] = 'r';
-       if (mode & S_IWOTH)
-               buf[8] = 'w';
-       if (mode & S_IXOTH)
-               buf[9] = 'x';
-
-       /*
-        * Finally fill in magic stuff like suid and sticky text.
-        */
-       if (mode & S_ISUID)
-               buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
-       if (mode & S_ISGID)
-               buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
-       if (mode & S_ISVTX)
-               buf[9] = ((mode & S_IXOTH) ? 't' : 'T');
-
-       return buf;
-}
-
-
-/*
- * Get the time string to be used for a file.
- * This is down to the minute for new files, but only the date for old files.
- * The string is returned from a static buffer, and so is overwritten for
- * each call.
- */
-const char *
-timeString(time_t timeVal)
-{
-       time_t          now;
-       char *          str;
-       static  char    buf[26];
-
-       time(&now);
-
-       str = ctime(&timeVal);
-
-       strcpy(buf, &str[4]);
-       buf[12] = '\0';
-
-       if ((timeVal > now) || (timeVal < now - 365*24*60*60L))
-       {
-               strcpy(&buf[7], &str[20]);
-               buf[11] = '\0';
-       }
-
-       return buf;
-}
-
-
-/*
- * Return TRUE if a fileName is a directory.
- * Nonexistant files return FALSE.
- */
-BOOL
-isDirectory(const char * name)
-{
-       struct  stat    statBuf;
-
-       if (stat(name, &statBuf) < 0)
-               return FALSE;
-
-       return S_ISDIR(statBuf.st_mode);
-}
-
-
-/*
- * Return TRUE if a filename is a block or character device.
- * Nonexistant files return FALSE.
- */
-BOOL
-isDevice(const char * name)
-{
-       struct  stat    statBuf;
-
-       if (stat(name, &statBuf) < 0)
-               return FALSE;
-
-       return S_ISBLK(statBuf.st_mode) || S_ISCHR(statBuf.st_mode);
-}
-
-
-/*
- * Copy one file to another, while possibly preserving its modes, times,
- * and modes.  Returns TRUE if successful, or FALSE on a failure with an
- * error message output.  (Failure is not indicted if the attributes cannot
- * be set.)
- */
-BOOL
-copyFile(
-       const char *    srcName,
-       const char *    destName,
-       BOOL            setModes
-)
-{
-       int             rfd;
-       int             wfd;
-       int             rcc;
-       char            buf[BUF_SIZE];
-       struct  stat    statBuf1;
-       struct  stat    statBuf2;
-       struct  utimbuf times;
-       
-       if (stat(srcName, &statBuf1) < 0)
-       {
-               perror(srcName);
-
-               return FALSE;
-       }
-
-       if (stat(destName, &statBuf2) < 0)
-       {
-               statBuf2.st_ino = -1;
-               statBuf2.st_dev = -1;
-       }
-
-       if ((statBuf1.st_dev == statBuf2.st_dev) &&
-               (statBuf1.st_ino == statBuf2.st_ino))
-       {
-               fprintf(stderr, "Copying file \"%s\" to itself\n", srcName);
-
-               return FALSE;
-       }
-
-       rfd = open(srcName, O_RDONLY);
-
-       if (rfd < 0)
-       {
-               perror(srcName);
-
-               return FALSE;
-       }
-
-       wfd = creat(destName, statBuf1.st_mode);
-
-       if (wfd < 0)
-       {
-               perror(destName);
-               close(rfd);
-
-               return FALSE;
-       }
-
-       while ((rcc = read(rfd, buf, sizeof(buf))) > 0)
-       {
-               if (intFlag)
-               {
-                       close(rfd);
-                       close(wfd);
-
-                       return FALSE;
-               }
-
-               if (fullWrite(wfd, buf, rcc) < 0)
-                       goto error_exit;
-       }
-
-       if (rcc < 0)
-       {
-               perror(srcName);
-               goto error_exit;
-       }
-
-       checkStatus("close", close(rfd));
-
-       if (close(wfd) < 0)
-       {
-               perror(destName);
-
-               return FALSE;
-       }
-
-       if (setModes)
-       {
-               checkStatus("chmod", chmod(destName, statBuf1.st_mode));
-
-               checkStatus("chown", chown(destName, statBuf1.st_uid, statBuf1.st_gid));
-
-               times.actime = statBuf1.st_atime;
-               times.modtime = statBuf1.st_mtime;
-
-               checkStatus("utime", utime(destName, &times));
-       }
-
-       return TRUE;
-
-
-error_exit:
-       close(rfd);
-       close(wfd);
-
-       return FALSE;
-}
-
-
-/*
- * Build a path name from the specified directory name and file name.
- * If the directory name is NULL, then the original fileName is returned.
- * The built path is in a static area, and is overwritten for each call.
- */
-const char *
-buildName(const char * dirName, const char * fileName)
-{
-       const char *    cp;
-       static  char    buf[PATH_LEN];
-
-       if ((dirName == NULL) || (*dirName == '\0'))
-               return fileName;
-
-       cp = strrchr(fileName, '/');
-
-       if (cp)
-               fileName = cp + 1;
-
-       strcpy(buf, dirName);
-       strcat(buf, "/");
-       strcat(buf, fileName);
-
-       return buf;
-}
-
-
-/*
- * Expand the wildcards in a fileName wildcard pattern, if any.
- * Returns an argument list with matching fileNames in sorted order.
- * The expanded names are stored in memory chunks which can later all
- * be freed at once.  The returned list is only valid until the next
- * call or until the next command.  Returns zero if the name is not a
- * wildcard, or returns the count of matched files if the name is a
- * wildcard and there was at least one match, or returns -1 if either
- * no fileNames matched or there was an allocation error.
- */
-int
-expandWildCards(const char * fileNamePattern, const char *** retFileTable)
-{
-       const char *    last;
-       const char *    cp1;
-       const char *    cp2;
-       const char *    cp3;
-       char *          str;
-       DIR *           dirp;
-       struct dirent * dp;
-       int             dirLen;
-       int             newFileTableSize;
-       char **         newFileTable;
-       char            dirName[PATH_LEN];
-
-       static int      fileCount;
-       static int      fileTableSize;
-       static char **  fileTable;
-
-       /*
-        * Clear the return values until we know their final values.
-        */
-       fileCount = 0;
-       *retFileTable = NULL;
-
-       /*
-        * Scan the file name pattern for any wildcard characters.
-        */
-       cp1 = strchr(fileNamePattern, '*');
-       cp2 = strchr(fileNamePattern, '?');
-       cp3 = strchr(fileNamePattern, '[');
-
-       /*
-        * If there are no wildcard characters then return zero to
-        * indicate that there was actually no wildcard pattern.
-        */
-       if ((cp1 == NULL) && (cp2 == NULL) && (cp3 == NULL))
-               return 0;
-
-       /*
-        * There are wildcards in the specified filename.
-        * Get the last component of the file name.
-        */
-       last = strrchr(fileNamePattern, '/');
-
-       if (last)
-               last++;
-       else
-               last = fileNamePattern;
-
-       /*
-        * If any wildcards were found before the last filename component
-        * then return an error.
-        */
-       if ((cp1 && (cp1 < last)) || (cp2 && (cp2 < last)) ||
-               (cp3 && (cp3 < last)))
-       {
-               fprintf(stderr,
-               "Wildcards only implemented for last file name component\n");
-
-               return -1;
-       }
-
-       /*
-        * Assume at first that we are scanning the current directory.
-        */
-       dirName[0] = '.';
-       dirName[1] = '\0';
-
-       /*
-        * If there was a directory given as part of the file name then
-        * copy it and null terminate it.
-        */
-       if (last != fileNamePattern)
-       {
-               memcpy(dirName, fileNamePattern, last - fileNamePattern);
-               dirName[last - fileNamePattern - 1] = '\0';
-
-               if (dirName[0] == '\0')
-               {
-                       dirName[0] = '/';
-                       dirName[1] = '\0';
-               }
-       }
-
-       /*
-        * Open the directory containing the files to be checked.
-        */
-       dirp = opendir(dirName);
-
-       if (dirp == NULL)
-       {
-               perror(dirName);
-
-               return -1;
-       }
-
-       /*
-        * Prepare the directory name for use in making full path names.
-        */
-       dirLen = strlen(dirName);
-
-       if (last == fileNamePattern)
-       {
-               dirLen = 0;
-               dirName[0] = '\0';
-       }
-       else if (dirName[dirLen - 1] != '/')
-       {
-               dirName[dirLen++] = '/';
-               dirName[dirLen] = '\0';
-       }
-
-       /*
-        * Find all of the files in the directory and check them against
-        * the wildcard pattern.
-        */
-       while ((dp = readdir(dirp)) != NULL)
-       {
-               /*
-                * Skip the current and parent directories.
-                */
-               if ((strcmp(dp->d_name, ".") == 0) ||
-                       (strcmp(dp->d_name, "..") == 0))
-               {
-                       continue;
-               }
-
-               /*
-                * If the file name doesn't match the pattern then skip it.
-                */
-               if (!match(dp->d_name, last))
-                       continue;
-
-               /*
-                * This file name is selected.
-                * See if we need to reallocate the file name table.
-                */
-               if (fileCount >= fileTableSize)
-               {
-                       /*
-                        * Increment the file table size and reallocate it.
-                        */
-                       newFileTableSize = fileTableSize + EXPAND_ALLOC;
-
-                       newFileTable = (char **) realloc((char *) fileTable,
-                               (newFileTableSize * sizeof(char *)));
-
-                       if (newFileTable == NULL)
-                       {
-                               fprintf(stderr, "Cannot allocate file list\n");
-                               closedir(dirp);
-
-                               return -1;
-                       }
-
-                       fileTable = newFileTable;
-                       fileTableSize = newFileTableSize;
-               }
-
-               /*
-                * Allocate space for storing the file name in a chunk.
-                */
-               str = getChunk(dirLen + strlen(dp->d_name) + 1);
-
-               if (str == NULL)
-               {
-                       fprintf(stderr, "No memory for file name\n");
-                       closedir(dirp);
-
-                       return -1;
-               }
-
-               /*
-                * Save the file name in the chunk.
-                */
-               if (dirLen)
-                       memcpy(str, dirName, dirLen);
-
-               strcpy(str + dirLen, dp->d_name);
-
-               /*
-                * Save the allocated file name into the file table.
-                */
-               fileTable[fileCount++] = str;
-       }
-
-       /*
-        * Close the directory and check for any matches.
-        */
-       closedir(dirp);
-
-       if (fileCount == 0)
-       {
-               fprintf(stderr, "No matches\n");
-
-               return -1;
-       }
-
-       /*
-        * Sort the list of file names.
-        */
-       qsort((void *) fileTable, fileCount, sizeof(char *), nameSort);
-
-       /*
-        * Return the file list and count.
-        */
-       *retFileTable = (const char **) fileTable;
-
-       return fileCount;
-}
-
-
-/*
- * Sort routine for list of fileNames.
- */
-int
-nameSort(const void * p1, const void * p2)
-{
-       const char **   s1;
-       const char **   s2;
-
-       s1 = (const char **) p1;
-       s2 = (const char **) p2;
-
-       return strcmp(*s1, *s2);
-}
-
-
-/*
- * Routine to see if a text string is matched by a wildcard pattern.
- * Returns TRUE if the text is matched, or FALSE if it is not matched
- * or if the pattern is invalid.
- *  *          matches zero or more characters
- *  ?          matches a single character
- *  [abc]      matches 'a', 'b' or 'c'
- *  \c         quotes character c
- *  Adapted from code written by Ingo Wilken.
- */
-BOOL
-match(const char * text, const char * pattern)
-{
-       const char *    retryPat;
-       const char *    retryText;
-       int             ch;
-       BOOL            found;
-
-       retryPat = NULL;
-       retryText = NULL;
-
-       while (*text || *pattern)
-       {
-               ch = *pattern++;
-
-               switch (ch)
-               {
-                       case '*':  
-                               retryPat = pattern;
-                               retryText = text;
-                               break;
-
-                       case '[':  
-                               found = FALSE;
-
-                               while ((ch = *pattern++) != ']')
-                               {
-                                       if (ch == '\\')
-                                               ch = *pattern++;
-
-                                       if (ch == '\0')
-                                               return FALSE;
-
-                                       if (*text == ch)
-                                               found = TRUE;
-                               }
-
-                               if (!found)
-                               {
-                                       pattern = retryPat;
-                                       text = ++retryText;
-                               }
-
-                               /* fall into next case */
-
-                       case '?':  
-                               if (*text++ == '\0')
-                                       return FALSE;
-
-                               break;
-
-                       case '\\':  
-                               ch = *pattern++;
-
-                               if (ch == '\0')
-                                       return FALSE;
-
-                               /* fall into next case */
-
-                       default:        
-                               if (*text == ch)
-                               {
-                                       if (*text)
-                                               text++;
-                                       break;
-                               }
-
-                               if (*text)
-                               {
-                                       pattern = retryPat;
-                                       text = ++retryText;
-                                       break;
-                               }
-
-                               return FALSE;
-               }
-
-               if (pattern == NULL)
-                       return FALSE;
-       }
-
-       return TRUE;
-}
-
-
-/*
- * Take a command string and break it up into an argc, argv list while
- * handling quoting and wildcards.  The returned argument list and
- * strings are in static memory, and so are overwritten on each call.
- * The argument list is ended with a NULL pointer for convenience.
- * Returns TRUE if successful, or FALSE on an error with a message
- * already output.
- */
-BOOL
-makeArgs(const char * cmd, int * retArgc, const char *** retArgv)
-{
-       const char *            argument;
-       char *                  cp;
-       char *                  cpOut;
-       char *                  newStrings;
-       const char **           fileTable;
-       const char **           newArgTable;
-       int                     newArgTableSize;
-       int                     fileCount;
-       int                     len;
-       int                     ch;
-       int                     quote;
-       BOOL                    quotedWildCards;
-       BOOL                    unquotedWildCards;
-
-       static int              stringsLength;
-       static char *           strings;
-       static int              argCount;
-       static int              argTableSize;
-       static const char **    argTable;
-
-       /*
-        * Clear the returned values until we know them.
-        */
-       argCount = 0;
-       *retArgc = 0;
-       *retArgv = NULL;
-
-       /*
-        * Copy the command string into a buffer that we can modify,
-        * reallocating it if necessary.
-        */
-       len = strlen(cmd) + 1;
-
-       if (len > stringsLength)
-       {
-               newStrings = realloc(strings, len);
-
-               if (newStrings == NULL)
-               {
-                       fprintf(stderr, "Cannot allocate string\n");
-
-                       return FALSE;
-               }
-
-               strings = newStrings;
-               stringsLength = len;
-       }
-
-       memcpy(strings, cmd, len);
-       cp = strings;
-
-       /*
-        * Keep parsing the command string as long as there are any
-        * arguments left.
-        */
-       while (*cp)
-       {
-               /*
-                * Save the beginning of this argument.
-                */
-               argument = cp;
-               cpOut = cp;
-
-               /*
-                * Reset quoting and wildcarding for this argument.
-                */
-               quote = '\0';
-               quotedWildCards = FALSE;
-               unquotedWildCards = FALSE;
-
-               /*
-                * Loop over the string collecting the next argument while
-                * looking for quoted strings or quoted characters, and
-                * remembering whether there are any wildcard characters
-                * in the argument.
-                */
-               while (*cp)
-               {
-                       ch = *cp++;
-
-                       /*
-                        * If we are not in a quote and we see a blank then
-                        * this argument is done.
-                        */
-                       if (isBlank(ch) && (quote == '\0'))
-                               break;
-
-                       /*
-                        * If we see a backslash then accept the next
-                        * character no matter what it is.
-                        */
-                       if (ch == '\\')
-                       {
-                               ch = *cp++;
-
-                               /*
-                                * Make sure there is a next character.
-                                */
-                               if (ch == '\0')
-                               {
-                                       fprintf(stderr,
-                                               "Bad quoted character\n");
-
-                                       return FALSE;
-                               }
-
-                               /*
-                                * Remember whether the quoted character
-                                * is a wildcard.
-                                */
-                               if (isWildCard(ch))
-                                       quotedWildCards = TRUE;
-
-                               *cpOut++ = ch;
-
-                               continue;
-                       }
-
-                       /*
-                        * If we see one of the wildcard characters then
-                        * remember whether it was seen inside or outside
-                        * of quotes.
-                        */
-                       if (isWildCard(ch))
-                       {
-                               if (quote)
-                                       quotedWildCards = TRUE;
-                               else
-                                       unquotedWildCards = TRUE;
-                       }
-
-                       /*
-                        * If we were in a quote and we saw the same quote
-                        * character again then the quote is done.
-                        */
-                       if (ch == quote)
-                       {
-                               quote = '\0';
-
-                               continue;
-                       }
-
-                       /*
-                        * If we weren't in a quote and we see either type
-                        * of quote character, then remember that we are
-                        * now inside of a quote.
-                        */
-                       if ((quote == '\0') && ((ch == '\'') || (ch == '"')))
-                       {
-                               quote = ch;
-
-                               continue;
-                       }
-
-                       /*
-                        * Store the character.
-                        */
-                       *cpOut++ = ch;
-               }
-
-               /*
-                * Make sure that quoting is terminated properly.
-                */
-               if (quote)
-               {
-                       fprintf(stderr, "Unmatched quote character\n");
-
-                       return FALSE;
-               }
-
-               /*
-                * Null terminate the argument if it had shrunk, and then
-                * skip over all blanks to the next argument, nulling them
-                * out too.
-                */
-               if (cp != cpOut)
-                       *cpOut = '\0';
-
-               while (isBlank(*cp))
-                       *cp++ = '\0';
-
-               /*
-                * If both quoted and unquoted wildcards were used then
-                * complain since we don't handle them properly.
-                */
-               if (quotedWildCards && unquotedWildCards)
-               {
-                       fprintf(stderr,
-                               "Cannot use quoted and unquoted wildcards\n");
-
-                       return FALSE;
-               }
-
-               /*
-                * Expand the argument into the matching filenames or accept
-                * it as is depending on whether there were any unquoted
-                * wildcard characters in it.
-                */
-               if (unquotedWildCards)
-               {
-                       /*
-                        * Expand the argument into the matching filenames.
-                        */
-                       fileCount = expandWildCards(argument, &fileTable);
-
-                       /*
-                        * Return an error if the wildcards failed to match.
-                        */
-                       if (fileCount < 0)
-                               return FALSE;
-
-                       if (fileCount == 0)
-                       {
-                               fprintf(stderr, "Wildcard expansion error\n");
-
-                               return FALSE;
-                       }
-               }
-               else
-               {
-                       /*
-                        * Set up to only store the argument itself.
-                        */
-                       fileTable = &argument;
-                       fileCount = 1;
-               }
-
-               /*
-                * Now reallocate the argument table to hold the file name.
-                */
-               if (argCount + fileCount >= argTableSize)
-               {
-                       newArgTableSize = argCount + fileCount + 1;
-
-                       newArgTable = (const char **) realloc(argTable,
-                               (sizeof(const char *) * newArgTableSize));
-
-                       if (newArgTable == NULL)
-                       {
-                               fprintf(stderr, "No memory for arg list\n");
-
-                               return FALSE;
-                       }
-
-                       argTable = newArgTable;
-                       argTableSize = newArgTableSize;
-               }
-
-               /*
-                * Copy the new arguments to the end of the old ones.
-                */
-               memcpy((void *) &argTable[argCount], (const void *) fileTable,
-                       (sizeof(const char **) * fileCount));
-
-               /*
-                * Add to the argument count.
-                */
-               argCount += fileCount;
-       }
-
-       /*
-        * Null terminate the argument list and return it.
-        */
-       argTable[argCount] = NULL;
-
-       *retArgc = argCount;
-       *retArgv = argTable;
-
-       return TRUE;
-}
-
-
-/*
- * Make a NULL-terminated string out of an argc, argv pair.
- * Returns TRUE if successful, or FALSE if the string is too long,
- * with an error message given.  This does not handle spaces within
- * arguments correctly.
- */
-BOOL
-makeString(
-       int             argc,
-       const char **   argv,
-       char *          buf,
-       int             bufLen
-)
-{
-       int     len;
-
-       while (argc-- > 0)
-       {
-               len = strlen(*argv);
-
-               if (len >= bufLen)
-               {
-                       fprintf(stderr, "Argument string too long\n");
-
-                       return FALSE;
-               }
-
-               strcpy(buf, *argv++);
-
-               buf += len;
-               bufLen -= len;
-
-               if (argc)
-                       *buf++ = ' ';
-
-               bufLen--; 
-       }
-
-       *buf = '\0';
-
-       return TRUE;
-}
-
-
-/*
- * Allocate a chunk of memory (like malloc).
- * The difference, though, is that the memory allocated is put on a
- * list of chunks which can be freed all at one time.  You CAN NOT free
- * an individual chunk.
- */
-char *
-getChunk(int size)
-{
-       CHUNK * chunk;
-
-       if (size < CHUNK_INIT_SIZE)
-               size = CHUNK_INIT_SIZE;
-
-       chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNK_INIT_SIZE);
-
-       if (chunk == NULL)
-               return NULL;
-
-       chunk->next = chunkList;
-       chunkList = chunk;
-
-       return chunk->data;
-}
-
-
-/*
- * Duplicate a string value using the chunk allocator.
- * The returned string cannot be individually freed, but can only be freed
- * with other strings when freeChunks is called.  Returns NULL on failure.
- */
-char *
-chunkstrdup(const char * str)
-{
-       int     len;
-       char *  newStr;
-
-       len = strlen(str) + 1;
-       newStr = getChunk(len);
-
-       if (newStr)
-               memcpy(newStr, str, len);
-
-       return newStr;
-}
-
-
-/*
- * Free all chunks of memory that had been allocated since the last
- * call to this routine.
- */
-void
-freeChunks(void)
-{
-       CHUNK * chunk;
-
-       while (chunkList)
-       {
-               chunk = chunkList;
-               chunkList = chunk->next;
-               free((char *) chunk);
-       }
-}
-
-
-/*
- * Try writing data to the specified file descriptor.
- * Only the first write error if any is printed.
- * This is used when writing to STDOUT.
- */
-void
-tryWrite(int fd, const char * cp, int len)
-{
-       static int failed = FALSE;
-
-       int status = fullWrite(fd, cp, len);
-
-       if ((status < 0) && !failed)
-       {
-               failed = TRUE;
-               perror("write");
-       }
-}
-
-
-/*
- * Write all of the supplied buffer out to a file.
- * This does multiple writes as necessary.
- * Returns the amount written, or -1 on an error.
- */
-int
-fullWrite(int fd, const char * buf, int len)
-{
-       int     cc;
-       int     total;
-
-       total = 0;
-
-       while (len > 0)
-       {
-               cc = write(fd, buf, len);
-
-               if (cc < 0)
-                       return -1;
-
-               buf += cc;
-               total+= cc;
-               len -= cc;
-       }
-
-       return total;
-}
-
-
-/*
- * Read all of the supplied buffer from a file.
- * This does multiple reads as necessary.
- * Returns the amount read, or -1 on an error.
- * A short read is returned on an end of file.
- */
-int
-fullRead(int fd, char * buf, int len)
-{
-       int     cc;
-       int     total;
-
-       total = 0;
-
-       while (len > 0)
-       {
-               cc = read(fd, buf, len);
-
-               if (cc < 0)
-                       return -1;
-
-               if (cc == 0)
-                       break;
-
-               buf += cc;
-               total+= cc;
-               len -= cc;
-       }
-
-       return total;
-}
-
-
-/*
- * Call system for the specified command and print an error
- * message if the execution fails.  The exit status of the
- * command is returned.
- */
-int
-trySystem(const char * cmd)
-{
-       int status;
-
-       status = system(cmd);
-
-       if (status == -1)
-               fprintf(stderr, "Error starting command: %s\n", cmd);
-
-       return status;
-}
-
-
-/*
- * Check the status for the most recent system call and complain
- * if its value is -1 which indicates it failed.
- */
-void
-checkStatus(const char * name, int status)
-{
-       if (status == -1)
-               perror(name);
-}
-
-/* END CODE */
diff --git a/src/user/syscalls.h b/src/user/syscalls.h
new file mode 100644 (file)
index 0000000..c6a0c61
--- /dev/null
@@ -0,0 +1,146 @@
+#ifndef SYSCALLS_H_
+#define SYSCALLS_H_
+
+#include <stdint.h>
+
+//
+// Task-related calls
+
+inline void _exit(int code)
+{
+       register uint32_t r1 __asm("r1") = code;
+       __asm("\
+               mov r0, 0; \
+               mov r1, %0; \
+               svc 0; \
+       " :: "r" (r1));
+}
+
+inline int fork(void)
+{
+       int ret = 0;
+       register uint32_t r1 __asm("r1") = (uint32_t)&ret;
+       __asm("\
+               mov r0, 1; \
+               mov r1, %0; \
+               svc 0; \
+       " :: "r" (r1));
+       return ret;
+}
+
+inline int getpid(void)
+{
+       int ret = 0;
+       register uint32_t r1 __asm("r1") = (uint32_t)&ret;
+       __asm("\
+               mov r0, 2; \
+               mov r1, %0; \
+               svc 0; \
+       " :: "r" (r1));
+       return ret;
+}
+
+inline void *sbrk(unsigned int bytes)
+{
+       void *ret = 0;
+       register uint32_t r1 __asm("r1") = bytes;
+       register uint32_t r2 __asm("r2") = (uint32_t)&ret;
+       __asm("\
+               mov r0, 4; \
+               mov r1, %0; \
+               mov r2, %1; \
+               svc 0; \
+       " :: "r" (r1), "r" (r2));
+       return ret;
+}
+
+//
+// File-related calls
+
+// Indicates mounted volume
+#define VFS_MOUNTED  (1 << 0)
+// Set if filesystem is read-only
+#define VFS_READONLY (1 << 1)
+
+// Indicates an opened file
+#define VFS_FILE_OPEN  (1 << 0)
+// Indicates read permission on file
+#define VFS_FILE_READ  (1 << 1)
+// Indicates write permission on file
+#define VFS_FILE_WRITE (1 << 2)
+// Set if EOF has been reached
+#define VFS_EOF        (1 << 3)
+
+#define EOF (-1)
+
+struct dirent {
+       char name[32];
+};
+
+struct vfs_volume_funcs_t;
+typedef struct vfs_volume_funcs_t vfs_volume_funcs;
+
+inline int mount(vfs_volume_funcs *funcs, uint32_t flags)
+{
+       int ret = 0;
+       register uint32_t r1 __asm("r1") = (uint32_t)funcs;
+       register uint32_t r2 __asm("r2") = flags;
+       register uint32_t r3 __asm("r3") = (uint32_t)&ret;
+       __asm("\
+               mov r0, 0; \
+               mov r1, %0; \
+               mov r2, %1; \
+               mov r3, %2; \
+               svc 3; \
+       " :: "r" (r1), "r" (r2), "r" (r3));
+       return ret;
+}
+
+inline int open(const char *path, uint32_t flags)
+{
+       int ret = 0;
+       register uint32_t r1 __asm("r1") = (uint32_t)path;
+       register uint32_t r2 __asm("r2") = flags;
+       register uint32_t r3 __asm("r3") = (uint32_t)&ret;
+       __asm("\
+               mov r0, 1; \
+               mov r1, %0; \
+               mov r2, %1; \
+               mov r3, %2; \
+               svc 3; \
+       " :: "r" (r1), "r" (r2), "r" (r3));
+       return ret;
+}
+
+inline int close(int fd)
+{
+       int ret = 0;
+       register uint32_t r1 __asm("r1") = fd;
+       register uint32_t r2 __asm("r2") = (uint32_t)&ret;
+       __asm("\
+               mov r0, 2; \
+               mov r1, %0; \
+               mov r2, %1; \
+               svc 3; \
+       " :: "r" (r1), "r" (r2));
+}
+
+inline int read(int fd, uint32_t count, uint8_t *buffer)
+{
+       int ret = 0;
+       register uint32_t r1 __asm("r1") = fd;
+       register uint32_t r2 __asm("r2") = count;
+       register uint32_t r3 __asm("r3") = (uint32_t)buffer;
+       register uint32_t r4 __asm("r4") = (uint32_t)&ret;
+       __asm("\
+               mov r0, 3; \
+               mov r1, %0; \
+               mov r2, %1; \
+               mov r3, %2; \
+               mov r4, %3; \
+               svc 3; \
+       " :: "r" (r1), "r" (r2), "r" (r3), "r" (r4));
+       return ret;
+}
+
+#endif // SYSCALLS_H_
index eb2d6626cdfe5691cc52f9d514eefbd22c493d6e..218ac97a0d6718cc2856b6fd9f82db931be02aa9 100644 (file)
@@ -4,6 +4,8 @@
 #include <kernel/heap.h>
 #include <kernel/vfs.h>
 
+#include "syscalls.h"
+
 void user_delay(uint32_t ms)
 {
        register uint32_t r1 asm("r1") = ms;
@@ -15,17 +17,6 @@ void user_delay(uint32_t ms)
        " :: "r" (r1));
 }
 
-int fork(void)
-{
-       int result = 0;
-       asm("\
-               mov r0, 1; \
-               mov r1, %0; \
-               svc 0; \
-       " :: "r" (&result));
-       return result;
-}
-
 void user_main(void)
 {
        gpio(GPIO_MODE, 5, OUTPUT);