diff options
Diffstat (limited to 'src')
261 files changed, 22600 insertions, 11465 deletions
diff --git a/src/fs/initrd.c b/src/fs/initrd.c index efa430c..5731ab5 100644 --- a/src/fs/initrd.c +++ b/src/fs/initrd.c @@ -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 index 0000000..6cea2a3 --- /dev/null +++ b/src/fs/stdio.c @@ -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 index 0000000..90d0f68 --- /dev/null +++ b/src/fs/stdio.h @@ -0,0 +1,8 @@ +#ifndef INTIRD_H_ +#define INITRD_H_ + +#include <kernel/vfs.h> + +extern const vfs_volume_funcs stdio_funcs; + +#endif // INITRD_H_ diff --git a/src/kernel/init.c b/src/kernel/init.c index 2ba607d..3bd6b39 100644 --- a/src/kernel/init.c +++ b/src/kernel/init.c @@ -21,6 +21,7 @@ #include "clock.h"
#include "gpio.h"
#include "heap.h"
+#include "serial.h"
#include "task.h"
#include "vfs.h"
#include <fs/initrd.h>
@@ -46,6 +47,7 @@ int main(void) heap_init(&__bss_end__);
gpio_init();
+ serial_init(9600);
vfs_init();
initrd_init();
diff --git a/src/kernel/serial.c b/src/kernel/serial.c new file mode 100644 index 0000000..6f112a8 --- /dev/null +++ b/src/kernel/serial.c @@ -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 index 0000000..e721d6c --- /dev/null +++ b/src/kernel/serial.h @@ -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_ diff --git a/src/kernel/svc.c b/src/kernel/svc.c index 83bd568..7d171c3 100644 --- a/src/kernel/svc.c +++ b/src/kernel/svc.c @@ -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; diff --git a/src/kernel/task.c b/src/kernel/task.c index 2550e4b..dfe50a7 100644 --- a/src/kernel/task.c +++ b/src/kernel/task.c @@ -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; diff --git a/src/kernel/task.h b/src/kernel/task.h index 30e3e66..5875e3c 100644 --- a/src/kernel/task.h +++ b/src/kernel/task.h @@ -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_ diff --git a/src/kernel/vfs.c b/src/kernel/vfs.c index 1720a6b..d6f9e14 100644 --- a/src/kernel/vfs.c +++ b/src/kernel/vfs.c @@ -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) diff --git a/src/kernel/vfs.h b/src/kernel/vfs.h index a6de690..c9c9179 100644 --- a/src/kernel/vfs.h +++ b/src/kernel/vfs.h @@ -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 index 0000000..d4ef6a2 --- /dev/null +++ b/src/pdclib/.gitignore @@ -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 index 0000000..0e259d4 --- /dev/null +++ b/src/pdclib/COPYING.CC0 @@ -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 index 0000000..f069e4a --- /dev/null +++ b/src/pdclib/Internals.txt @@ -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 index 0000000..ebb0ad4 --- /dev/null +++ b/src/pdclib/Makefile @@ -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 index 0000000..b4efb8b --- /dev/null +++ b/src/pdclib/Notes.txt @@ -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 index 0000000..9a1b335 --- /dev/null +++ b/src/pdclib/Readme.txt @@ -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 index 0000000..0d34b98 --- /dev/null +++ b/src/pdclib/auxiliary/uctype/Makefile @@ -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 index 0000000..c024efe --- /dev/null +++ b/src/pdclib/auxiliary/uctype/derived_properties.c @@ -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 index 0000000..d06ac84 --- /dev/null +++ b/src/pdclib/auxiliary/uctype/derived_properties.h @@ -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 index 0000000..ef60bb4 --- /dev/null +++ b/src/pdclib/auxiliary/uctype/main.c @@ -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 index 0000000..3cd33a8 --- /dev/null +++ b/src/pdclib/auxiliary/uctype/test.h @@ -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 index 0000000..20973d9 --- /dev/null +++ b/src/pdclib/auxiliary/uctype/text_utilities.c @@ -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 index 0000000..f961e6b --- /dev/null +++ b/src/pdclib/auxiliary/uctype/text_utilities.h @@ -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 index 0000000..ce8d8ef --- /dev/null +++ b/src/pdclib/auxiliary/uctype/uctype.c @@ -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 index 0000000..8cdda43 --- /dev/null +++ b/src/pdclib/auxiliary/uctype/uctype.h @@ -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 index 0000000..5d92fda --- /dev/null +++ b/src/pdclib/auxiliary/uctype/unicode_data.c @@ -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 index 0000000..8cd4832 --- /dev/null +++ b/src/pdclib/auxiliary/uctype/unicode_data.h @@ -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 index 0000000..13ad05c --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/Readme.txt @@ -0,0 +1,10 @@ +This directory holds various "internals" of PDCLib: + +- definitions of helper functions not specified by the standard (hidden in the + _PDCLIB_* namespace); + +- definitions of data objects, both internal (like _PDCLIB_digits) and specified by + the standard (_PDCLIB_errno); + +- test drivers for functionality that does not have its own implementation + file to put the test driver in (stdarg). diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_atomax.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_atomax.c new file mode 100644 index 0000000..eaf7ced --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_atomax.c @@ -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 index 0000000..3c928e7 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_closeall.c @@ -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 index 0000000..69c4f2b --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_digits.c @@ -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 index 0000000..2555a00 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_filemode.c @@ -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 index 0000000..2407337 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_is_leap.c @@ -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 index 0000000..d66b996 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_collate.c @@ -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 index 0000000..81fb18f --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_ctype.c @@ -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 index 0000000..6ff2a50 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_messages.c @@ -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 index 0000000..e2dcbb4 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_monetary.c @@ -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 index 0000000..de42bbd --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_numeric.c @@ -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 index 0000000..733e5c7 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_time.c @@ -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 index 0000000..aaaa743 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lines.c @@ -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 index 0000000..1d60642 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_prepread.c @@ -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 index 0000000..dcdc970 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_prepwrite.c @@ -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 index 0000000..3c13da2 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_print.c @@ -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 index 0000000..1f8fd71 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_scan.c @@ -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 index 0000000..ce7c31c --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_seed.c @@ -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 index 0000000..4808952 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_main.c @@ -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 index 0000000..6e16b35 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_prelim.c @@ -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 index 0000000..f84265f --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/assert.c @@ -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 index 0000000..13270fc --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/errno.c @@ -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 index 0000000..7ad1087 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/stdarg.c @@ -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 index 0000000..d3ef7c9 --- /dev/null +++ b/src/pdclib/functions/ctype/isalnum.c @@ -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 index 0000000..b3fa513 --- /dev/null +++ b/src/pdclib/functions/ctype/isalpha.c @@ -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 index 0000000..dd6af44 --- /dev/null +++ b/src/pdclib/functions/ctype/isblank.c @@ -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 index 0000000..14d50e5 --- /dev/null +++ b/src/pdclib/functions/ctype/iscntrl.c @@ -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 index 0000000..00d6bcf --- /dev/null +++ b/src/pdclib/functions/ctype/isdigit.c @@ -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 index 0000000..fdc5819 --- /dev/null +++ b/src/pdclib/functions/ctype/isgraph.c @@ -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 index 0000000..5d225db --- /dev/null +++ b/src/pdclib/functions/ctype/islower.c @@ -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 index 0000000..d3f1489 --- /dev/null +++ b/src/pdclib/functions/ctype/isprint.c @@ -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 index 0000000..2afdb0c --- /dev/null +++ b/src/pdclib/functions/ctype/ispunct.c @@ -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 index 0000000..a724de6 --- /dev/null +++ b/src/pdclib/functions/ctype/isspace.c @@ -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 index 0000000..79b55a3 --- /dev/null +++ b/src/pdclib/functions/ctype/isupper.c @@ -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 index 0000000..30839c0 --- /dev/null +++ b/src/pdclib/functions/ctype/isxdigit.c @@ -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 index 0000000..bbb76d1 --- /dev/null +++ b/src/pdclib/functions/ctype/tolower.c @@ -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 index 0000000..79e6e2b --- /dev/null +++ b/src/pdclib/functions/ctype/toupper.c @@ -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 index 0000000..06d029e --- /dev/null +++ b/src/pdclib/functions/inttypes/imaxabs.c @@ -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 index 0000000..7143c3d --- /dev/null +++ b/src/pdclib/functions/inttypes/imaxdiv.c @@ -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 index 0000000..29a3c92 --- /dev/null +++ b/src/pdclib/functions/inttypes/strtoimax.c @@ -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 index 0000000..9ed16c1 --- /dev/null +++ b/src/pdclib/functions/inttypes/strtoumax.c @@ -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 index 0000000..cdcb1b0 --- /dev/null +++ b/src/pdclib/functions/locale/localeconv.c @@ -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 index 0000000..585504a --- /dev/null +++ b/src/pdclib/functions/locale/setlocale.c @@ -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 index 0000000..91cd00a --- /dev/null +++ b/src/pdclib/functions/stdio/clearerr.c @@ -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 index 0000000..9a1388d --- /dev/null +++ b/src/pdclib/functions/stdio/fclose.c @@ -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 index 0000000..a57071f --- /dev/null +++ b/src/pdclib/functions/stdio/feof.c @@ -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 index 0000000..54f43f0 --- /dev/null +++ b/src/pdclib/functions/stdio/ferror.c @@ -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 index 0000000..3d0b297 --- /dev/null +++ b/src/pdclib/functions/stdio/fflush.c @@ -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 index 0000000..bdb233e --- /dev/null +++ b/src/pdclib/functions/stdio/fgetc.c @@ -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 index 0000000..db9be06 --- /dev/null +++ b/src/pdclib/functions/stdio/fgetpos.c @@ -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 index 0000000..0499afd --- /dev/null +++ b/src/pdclib/functions/stdio/fgets.c @@ -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 index 0000000..d5241a7 --- /dev/null +++ b/src/pdclib/functions/stdio/fopen.c @@ -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 index 0000000..e16adf8 --- /dev/null +++ b/src/pdclib/functions/stdio/fprintf.c @@ -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 index 0000000..05ac792 --- /dev/null +++ b/src/pdclib/functions/stdio/fputc.c @@ -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 index 0000000..a5d7e56 --- /dev/null +++ b/src/pdclib/functions/stdio/fputs.c @@ -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 index 0000000..319e9ba --- /dev/null +++ b/src/pdclib/functions/stdio/fread.c @@ -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 index 0000000..2cb774c --- /dev/null +++ b/src/pdclib/functions/stdio/freopen.c @@ -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 index 0000000..cc8c031 --- /dev/null +++ b/src/pdclib/functions/stdio/fscanf.c @@ -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 index 0000000..54f4c4b --- /dev/null +++ b/src/pdclib/functions/stdio/fseek.c @@ -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 index 0000000..323aaae --- /dev/null +++ b/src/pdclib/functions/stdio/fsetpos.c @@ -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 index 0000000..f4bd300 --- /dev/null +++ b/src/pdclib/functions/stdio/ftell.c @@ -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 index 0000000..7958a50 --- /dev/null +++ b/src/pdclib/functions/stdio/fwrite.c @@ -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 index 0000000..3d082b3 --- /dev/null +++ b/src/pdclib/functions/stdio/getc.c @@ -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 index 0000000..34ee545 --- /dev/null +++ b/src/pdclib/functions/stdio/getchar.c @@ -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 index 0000000..dd6633c --- /dev/null +++ b/src/pdclib/functions/stdio/perror.c @@ -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 index 0000000..6d10113 --- /dev/null +++ b/src/pdclib/functions/stdio/printf.c @@ -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 index 0000000..c1eb2b0 --- /dev/null +++ b/src/pdclib/functions/stdio/putc.c @@ -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 index 0000000..15bf299 --- /dev/null +++ b/src/pdclib/functions/stdio/putchar.c @@ -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 index 0000000..89240cb --- /dev/null +++ b/src/pdclib/functions/stdio/puts.c @@ -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 index 0000000..5d45f1f --- /dev/null +++ b/src/pdclib/functions/stdio/rename.c @@ -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 index 0000000..a449b7d --- /dev/null +++ b/src/pdclib/functions/stdio/rewind.c @@ -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 index 0000000..b29b1d5 --- /dev/null +++ b/src/pdclib/functions/stdio/scanf.c @@ -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 index 0000000..6c32072 --- /dev/null +++ b/src/pdclib/functions/stdio/setbuf.c @@ -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 index 0000000..00592ff --- /dev/null +++ b/src/pdclib/functions/stdio/setvbuf.c @@ -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 index 0000000..f095b8c --- /dev/null +++ b/src/pdclib/functions/stdio/snprintf.c @@ -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 index 0000000..ce3a7ff --- /dev/null +++ b/src/pdclib/functions/stdio/sprintf.c @@ -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 index 0000000..9ba830d --- /dev/null +++ b/src/pdclib/functions/stdio/sscanf.c @@ -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 Binary files differnew file mode 100644 index 0000000..f08c4d3 --- /dev/null +++ b/src/pdclib/functions/stdio/testfile.txt diff --git a/src/pdclib/functions/stdio/tmpnam.c b/src/pdclib/functions/stdio/tmpnam.c new file mode 100644 index 0000000..5909aff --- /dev/null +++ b/src/pdclib/functions/stdio/tmpnam.c @@ -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 index 0000000..dc5260b --- /dev/null +++ b/src/pdclib/functions/stdio/ungetc.c @@ -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 index 0000000..6f31bbd --- /dev/null +++ b/src/pdclib/functions/stdio/vfprintf.c @@ -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 index 0000000..bb24456 --- /dev/null +++ b/src/pdclib/functions/stdio/vfscanf.c @@ -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 index 0000000..fe80152 --- /dev/null +++ b/src/pdclib/functions/stdio/vprintf.c @@ -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 index 0000000..d5ae5b3 --- /dev/null +++ b/src/pdclib/functions/stdio/vscanf.c @@ -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 index 0000000..e57e682 --- /dev/null +++ b/src/pdclib/functions/stdio/vsnprintf.c @@ -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 index 0000000..4ab2742 --- /dev/null +++ b/src/pdclib/functions/stdio/vsprintf.c @@ -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 index 0000000..e76e70e --- /dev/null +++ b/src/pdclib/functions/stdio/vsscanf.c @@ -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 index 0000000..de387db --- /dev/null +++ b/src/pdclib/functions/stdlib/_Exit.c @@ -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 index 0000000..69422f9 --- /dev/null +++ b/src/pdclib/functions/stdlib/abort.c @@ -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 index 0000000..7a634e3 --- /dev/null +++ b/src/pdclib/functions/stdlib/abs.c @@ -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 index 0000000..489c3df --- /dev/null +++ b/src/pdclib/functions/stdlib/atexit.c @@ -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 index 0000000..eaa9100 --- /dev/null +++ b/src/pdclib/functions/stdlib/atoi.c @@ -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 index 0000000..df17e83 --- /dev/null +++ b/src/pdclib/functions/stdlib/atol.c @@ -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 index 0000000..8b65b35 --- /dev/null +++ b/src/pdclib/functions/stdlib/atoll.c @@ -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 index 0000000..211c55c --- /dev/null +++ b/src/pdclib/functions/stdlib/bsearch.c @@ -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 index 0000000..d736065 --- /dev/null +++ b/src/pdclib/functions/stdlib/calloc.c @@ -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 index 0000000..5d502c8 --- /dev/null +++ b/src/pdclib/functions/stdlib/div.c @@ -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 index 0000000..e636a32 --- /dev/null +++ b/src/pdclib/functions/stdlib/exit.c @@ -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 index 0000000..126a17b --- /dev/null +++ b/src/pdclib/functions/stdlib/free.c @@ -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 index 0000000..3653453 --- /dev/null +++ b/src/pdclib/functions/stdlib/labs.c @@ -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 index 0000000..0f51944 --- /dev/null +++ b/src/pdclib/functions/stdlib/ldiv.c @@ -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 index 0000000..bc05bf3 --- /dev/null +++ b/src/pdclib/functions/stdlib/llabs.c @@ -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 index 0000000..4219e31 --- /dev/null +++ b/src/pdclib/functions/stdlib/lldiv.c @@ -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 index 0000000..ed57e9a --- /dev/null +++ b/src/pdclib/functions/stdlib/malloc.c @@ -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 index 0000000..d28f74c --- /dev/null +++ b/src/pdclib/functions/stdlib/qsort.c @@ -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 index 0000000..779b3b2 --- /dev/null +++ b/src/pdclib/functions/stdlib/rand.c @@ -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 index 0000000..cbc01d4 --- /dev/null +++ b/src/pdclib/functions/stdlib/realloc.c @@ -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 index 0000000..39c37ec --- /dev/null +++ b/src/pdclib/functions/stdlib/srand.c @@ -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 index 0000000..d89a648 --- /dev/null +++ b/src/pdclib/functions/stdlib/strtol.c @@ -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 index 0000000..24c3e8e --- /dev/null +++ b/src/pdclib/functions/stdlib/strtoll.c @@ -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 index 0000000..1a93477 --- /dev/null +++ b/src/pdclib/functions/stdlib/strtoul.c @@ -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 index 0000000..e207110 --- /dev/null +++ b/src/pdclib/functions/stdlib/strtoull.c @@ -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 index 0000000..29598a7 --- /dev/null +++ b/src/pdclib/functions/string/memchr.c @@ -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 index 0000000..709b941 --- /dev/null +++ b/src/pdclib/functions/string/memcmp.c @@ -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 index 0000000..21ef10e --- /dev/null +++ b/src/pdclib/functions/string/memcpy.c @@ -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 index 0000000..af89813 --- /dev/null +++ b/src/pdclib/functions/string/memmove.c @@ -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 index 0000000..522ad77 --- /dev/null +++ b/src/pdclib/functions/string/memset.c @@ -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 index 0000000..18fd409 --- /dev/null +++ b/src/pdclib/functions/string/strcat.c @@ -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 index 0000000..621100e --- /dev/null +++ b/src/pdclib/functions/string/strchr.c @@ -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 index 0000000..639fc10 --- /dev/null +++ b/src/pdclib/functions/string/strcmp.c @@ -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 index 0000000..41d466a --- /dev/null +++ b/src/pdclib/functions/string/strcoll.c @@ -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 index 0000000..e0357c8 --- /dev/null +++ b/src/pdclib/functions/string/strcpy.c @@ -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 index 0000000..84f8af1 --- /dev/null +++ b/src/pdclib/functions/string/strcspn.c @@ -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 index 0000000..4506376 --- /dev/null +++ b/src/pdclib/functions/string/strerror.c @@ -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 index 0000000..c1a620e --- /dev/null +++ b/src/pdclib/functions/string/strlen.c @@ -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 index 0000000..ba20edc --- /dev/null +++ b/src/pdclib/functions/string/strncat.c @@ -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 index 0000000..4bb3592 --- /dev/null +++ b/src/pdclib/functions/string/strncmp.c @@ -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 index 0000000..0627c41 --- /dev/null +++ b/src/pdclib/functions/string/strncpy.c @@ -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 index 0000000..e95f0c1 --- /dev/null +++ b/src/pdclib/functions/string/strpbrk.c @@ -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 index 0000000..c2369fa --- /dev/null +++ b/src/pdclib/functions/string/strrchr.c @@ -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 index 0000000..7b55b08 --- /dev/null +++ b/src/pdclib/functions/string/strspn.c @@ -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 index 0000000..aee282f --- /dev/null +++ b/src/pdclib/functions/string/strstr.c @@ -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 index 0000000..69c2d68 --- /dev/null +++ b/src/pdclib/functions/string/strtok.c @@ -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 index 0000000..e08dba2 --- /dev/null +++ b/src/pdclib/functions/string/strxfrm.c @@ -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 index 0000000..8c9db60 --- /dev/null +++ b/src/pdclib/functions/time/asctime.c @@ -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 index 0000000..d021a7d --- /dev/null +++ b/src/pdclib/functions/time/ctime.c @@ -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 index 0000000..a6d94f8 --- /dev/null +++ b/src/pdclib/functions/time/difftime.c @@ -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 index 0000000..3bd001c --- /dev/null +++ b/src/pdclib/functions/time/gmtime.c @@ -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 index 0000000..91c0618 --- /dev/null +++ b/src/pdclib/functions/time/localtime.c @@ -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 index 0000000..0b8596a --- /dev/null +++ b/src/pdclib/functions/time/mktime.c @@ -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 index 0000000..81ae13d --- /dev/null +++ b/src/pdclib/functions/time/strftime.c @@ -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 index 0000000..a5c1a8e --- /dev/null +++ b/src/pdclib/include/assert.h @@ -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 index 0000000..07fad9c --- /dev/null +++ b/src/pdclib/include/ctype.h @@ -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 index 0000000..bdc99aa --- /dev/null +++ b/src/pdclib/include/errno.h @@ -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 index 0000000..5252937 --- /dev/null +++ b/src/pdclib/include/inttypes.h @@ -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 index 0000000..98a023f --- /dev/null +++ b/src/pdclib/include/iso646.h @@ -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 index 0000000..4349acd --- /dev/null +++ b/src/pdclib/include/limits.h @@ -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 index 0000000..c8467a8 --- /dev/null +++ b/src/pdclib/include/locale.h @@ -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 index 0000000..d6ca14e --- /dev/null +++ b/src/pdclib/include/pdclib/_PDCLIB_aux.h @@ -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 index 0000000..fc1bbb2 --- /dev/null +++ b/src/pdclib/include/pdclib/_PDCLIB_glue.h @@ -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 index 0000000..6eaded1 --- /dev/null +++ b/src/pdclib/include/pdclib/_PDCLIB_int.h @@ -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 index 0000000..de806a1 --- /dev/null +++ b/src/pdclib/include/stdalign.h @@ -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 index 0000000..84c05d2 --- /dev/null +++ b/src/pdclib/include/stdarg.h @@ -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 index 0000000..8f6a1d0 --- /dev/null +++ b/src/pdclib/include/stdbool.h @@ -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 index 0000000..7cba7c0 --- /dev/null +++ b/src/pdclib/include/stddef.h @@ -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 index 0000000..544d1dd --- /dev/null +++ b/src/pdclib/include/stdint.h @@ -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 index 0000000..d3d0a64 --- /dev/null +++ b/src/pdclib/include/stdio.h @@ -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 index 0000000..5377bcd --- /dev/null +++ b/src/pdclib/include/stdlib.h @@ -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 index 0000000..8c18143 --- /dev/null +++ b/src/pdclib/include/stdnoreturn.h @@ -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 index 0000000..dc0af15 --- /dev/null +++ b/src/pdclib/include/string.h @@ -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 index 0000000..fb1af0b --- /dev/null +++ b/src/pdclib/include/time.h @@ -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 index 0000000..5449930 --- /dev/null +++ b/src/pdclib/include/wctype.h @@ -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 index 0000000..07dc20e --- /dev/null +++ b/src/pdclib/platform/example/Readme.txt @@ -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 index 0000000..d2e6ee4 --- /dev/null +++ b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_Exit.c @@ -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 index 0000000..d2e6ee4 --- /dev/null +++ b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB__Exit.c @@ -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 index 0000000..d46d46f --- /dev/null +++ b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_allocpages.c @@ -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 index 0000000..113290a --- /dev/null +++ b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_close.c @@ -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 index 0000000..012eed8 --- /dev/null +++ b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_fillbuffer.c @@ -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 index 0000000..ca6b998 --- /dev/null +++ b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_flushbuffer.c @@ -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 index 0000000..e35d65d --- /dev/null +++ b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_open.c @@ -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 index 0000000..8c23f79 --- /dev/null +++ b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_rename.c @@ -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 index 0000000..4d09460 --- /dev/null +++ b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_seek.c @@ -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 index 0000000..52b0651 --- /dev/null +++ b/src/pdclib/platform/example/functions/_PDCLIB/_PDCLIB_stdinit.c @@ -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 index 0000000..59ccc9f --- /dev/null +++ b/src/pdclib/platform/example/functions/signal/raise.c @@ -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 index 0000000..e6775e7 --- /dev/null +++ b/src/pdclib/platform/example/functions/signal/signal.c @@ -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 index 0000000..aca3eaf --- /dev/null +++ b/src/pdclib/platform/example/functions/stdio/remove.c @@ -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 index 0000000..585a61d --- /dev/null +++ b/src/pdclib/platform/example/functions/stdio/tmpfile.c @@ -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 index 0000000..72bbcd2 --- /dev/null +++ b/src/pdclib/platform/example/functions/stdlib/getenv.c @@ -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 index 0000000..15603c3 --- /dev/null +++ b/src/pdclib/platform/example/functions/stdlib/system.c @@ -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 index 0000000..825e040 --- /dev/null +++ b/src/pdclib/platform/example/functions/time/clock.c @@ -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 index 0000000..cbb29e1 --- /dev/null +++ b/src/pdclib/platform/example/functions/time/time.c @@ -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 index 0000000..d8cbab7 --- /dev/null +++ b/src/pdclib/platform/example/functions/time/timespec_get.c @@ -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 index 0000000..538d69e --- /dev/null +++ b/src/pdclib/platform/example/include/float.h @@ -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 index 0000000..9731f86 --- /dev/null +++ b/src/pdclib/platform/example/include/pdclib/_PDCLIB_config.h @@ -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 index 0000000..c5f6f28 --- /dev/null +++ b/src/pdclib/platform/example/include/signal.h @@ -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 index 0000000..07dc20e --- /dev/null +++ b/src/pdclib/platform/stmos/Readme.txt @@ -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 index 0000000..12b5c7f --- /dev/null +++ b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_Exit.c @@ -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 index 0000000..12b5c7f --- /dev/null +++ b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB__Exit.c @@ -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 index 0000000..085857f --- /dev/null +++ b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_allocpages.c @@ -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 index 0000000..3737ab6 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_close.c @@ -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 index 0000000..c13f9ba --- /dev/null +++ b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_fillbuffer.c @@ -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 index 0000000..d3b8842 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_flushbuffer.c @@ -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 index 0000000..fb4f769 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_open.c @@ -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 index 0000000..c9f54b0 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_rename.c @@ -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 index 0000000..a7935fa --- /dev/null +++ b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_seek.c @@ -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 index 0000000..52b0651 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/_PDCLIB/_PDCLIB_stdinit.c @@ -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 index 0000000..59ccc9f --- /dev/null +++ b/src/pdclib/platform/stmos/functions/signal/raise.c @@ -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 index 0000000..e6775e7 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/signal/signal.c @@ -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 index 0000000..aca3eaf --- /dev/null +++ b/src/pdclib/platform/stmos/functions/stdio/remove.c @@ -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 index 0000000..585a61d --- /dev/null +++ b/src/pdclib/platform/stmos/functions/stdio/tmpfile.c @@ -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 index 0000000..72bbcd2 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/stdlib/getenv.c @@ -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 index 0000000..15603c3 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/stdlib/system.c @@ -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 index 0000000..825e040 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/time/clock.c @@ -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 index 0000000..cbb29e1 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/time/time.c @@ -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 index 0000000..d8cbab7 --- /dev/null +++ b/src/pdclib/platform/stmos/functions/time/timespec_get.c @@ -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 index 0000000..538d69e --- /dev/null +++ b/src/pdclib/platform/stmos/include/float.h @@ -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 index 0000000..9731f86 --- /dev/null +++ b/src/pdclib/platform/stmos/include/pdclib/_PDCLIB_config.h @@ -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 index 0000000..c5f6f28 --- /dev/null +++ b/src/pdclib/platform/stmos/include/signal.h @@ -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 index 0000000..ef4d771 --- /dev/null +++ b/src/pdclib/testing/_PDCLIB_iotest.h @@ -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 index 0000000..65cf5b5 --- /dev/null +++ b/src/pdclib/testing/_PDCLIB_test.h @@ -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 index 0000000..c714499 --- /dev/null +++ b/src/pdclib/testing/printf_testcases.h @@ -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 index 0000000..f9007d6 --- /dev/null +++ b/src/pdclib/testing/scanf_testcases.h @@ -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 index c87d6d5..0000000 --- a/src/sash/CHANGES +++ /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 index 02a895a..0000000 --- a/src/sash/Makefile +++ /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 index 154f1f7..0000000 --- a/src/sash/README +++ /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 index 6c6181e..0000000 --- a/src/sash/cmd_ar.c +++ /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 index df50c22..0000000 --- a/src/sash/cmd_chattr.c +++ /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 index 80338b6..0000000 --- a/src/sash/cmd_dd.c +++ /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 index e935c0d..0000000 --- a/src/sash/cmd_ed.c +++ /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 index caf2a30..0000000 --- a/src/sash/cmd_file.c +++ /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 index fde9006..0000000 --- a/src/sash/cmd_find.c +++ /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 index b9e0821..0000000 --- a/src/sash/cmd_grep.c +++ /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 index 6bcec11..0000000 --- a/src/sash/cmd_gzip.c +++ /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 index 41f31cc..0000000 --- a/src/sash/cmd_ls.c +++ /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 index 5dd5f21..0000000 --- a/src/sash/cmd_tar.c +++ /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 index ae346b5..0000000 --- a/src/sash/cmds.c +++ /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 index 77db3b4..0000000 --- a/src/sash/dirent.h +++ /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 index 8c61b9b..0000000 --- a/src/sash/sash.1 +++ /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 index 8c15711..0000000 --- a/src/sash/sash.c +++ /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 index b0ed254..0000000 --- a/src/sash/sash.h +++ /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 index f6bee83..0000000 --- a/src/sash/utils.c +++ /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, ×)); - } - - 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 index 0000000..c6a0c61 --- /dev/null +++ b/src/user/syscalls.h @@ -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_ diff --git a/src/user/user.c b/src/user/user.c index eb2d662..218ac97 100644 --- a/src/user/user.c +++ b/src/user/user.c @@ -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); |