Initial commit

This commit is contained in:
Edgar Su 2021-10-10 14:11:45 +08:00
commit 90fb68940b
130 changed files with 18721 additions and 0 deletions

2
.clangd Normal file
View File

@ -0,0 +1,2 @@
CompileFlags:
Add: ["-I/usr/include/efi", "-I/usr/include/efi/x86_64", "-DHELOS"]

22
.gitignore vendored Normal file
View File

@ -0,0 +1,22 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Compiled result
Main.efi
*.o
# Test files compiled result
a.out
# macOS trash
.DS_Store
# vscode trash
.vscode
# Dolphin trash
.directory

199
Linker.ld Normal file
View File

@ -0,0 +1,199 @@
/* Default linker script, for normal executables */
/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
Copying and distribution of this script, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. */
OUTPUT_FORMAT(pei-x86-64)
SEARCH_DIR("/usr/x86_64-w64-mingw32/lib");
__section_alignment__ = 4096;
SECTIONS
{
/* Make the virtual address and file offset synced if the alignment is
lower than the target page size. */
. = SIZEOF_HEADERS;
. = ALIGN(__section_alignment__);
.text __image_base__ + ( __section_alignment__ < 0x1000 ? . : __section_alignment__ ) :
{
PROVIDE(link_TextStart = .);
KEEP (*(SORT_NONE(.init)))
*(.text)
*(SORT(.text$*))
*(.text.*)
*(.gnu.linkonce.t.*)
*(.glue_7t)
*(.glue_7)
. = ALIGN(8);
/* Note: we always define __CTOR_LIST__ and ___CTOR_LIST__ here,
we do not PROVIDE them. This is because the ctors.o startup
code in libgcc defines them as common symbols, with the
expectation that they will be overridden by the definitions
here. If we PROVIDE the symbols then they will not be
overridden and global constructors will not be run.
See PR 22762 for more details.
This does mean that it is not possible for a user to define
their own __CTOR_LIST__ and __DTOR_LIST__ symbols; if they do,
the content from those variables are included but the symbols
defined here silently take precedence. If they truly need to
be redefined, a custom linker script will have to be used.
(The custom script can just be a copy of this script with the
PROVIDE() qualifiers added).
In particular this means that ld -Ur does not work, because
the proper __CTOR_LIST__ set by ld -Ur is overridden by a
bogus __CTOR_LIST__ set by the final link. See PR 46. */
___CTOR_LIST__ = .;
__CTOR_LIST__ = .;
LONG (-1); LONG (-1);
KEEP (*(.ctors));
KEEP (*(.ctor));
KEEP (*(SORT_BY_NAME(.ctors.*)));
LONG (0); LONG (0);
/* See comment about __CTOR_LIST__ above. The same reasoning
applies here too. */
___DTOR_LIST__ = .;
__DTOR_LIST__ = .;
LONG (-1); LONG (-1);
KEEP (*(.dtors));
KEEP (*(.dtor));
KEEP (*(SORT_BY_NAME(.dtors.*)));
LONG (0); LONG (0);
KEEP (*(SORT_NONE(.fini)))
/* ??? Why is .gcc_exc here? */
*(.gcc_exc)
PROVIDE (etext = .);
PROVIDE(link_TextEnd = .);
KEEP (*(.gcc_except_table))
}
/* The Cygwin32 library uses a section to avoid copying certain data
on fork. This used to be named ".data". The linker used
to include this between __data_start__ and __data_end__, but that
breaks building the cygwin32 dll. Instead, we name the section
".data_cygwin_nocopy" and explicitly include it after __data_end__. */
.data BLOCK(__section_alignment__) :
{
PROVIDE(link_DataStart = .);
__data_start__ = . ;
*(.data)
*(.data2)
*(SORT(.data$*))
KEEP(*(.jcr))
__data_end__ = . ;
*(.data_cygwin_nocopy)
}
.idata BLOCK(__section_alignment__) :
{
/* This cannot currently be handled with grouped sections.
See pep.em:sort_sections. */
KEEP (SORT(*)(.idata$2))
KEEP (SORT(*)(.idata$3))
/* These zeroes mark the end of the import list. */
LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
KEEP (SORT(*)(.idata$4))
__IAT_start__ = .;
SORT(*)(.idata$5)
__IAT_end__ = .;
KEEP (SORT(*)(.idata$6))
KEEP (SORT(*)(.idata$7))
PROVIDE(link_DataEnd = .);
}
.rdata BLOCK(__section_alignment__) :
{
PROVIDE(link_RodataStart = .);
*(.rdata)
*(SORT(.rdata$*))
*(.rodata)
. = ALIGN(4);
__rt_psrelocs_start = .;
KEEP(*(.rdata_runtime_pseudo_reloc))
__rt_psrelocs_end = .;
PROVIDE(link_RodataEnd = .);
}
__rt_psrelocs_size = __rt_psrelocs_end - __rt_psrelocs_start;
___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
__RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
___RUNTIME_PSEUDO_RELOC_LIST__ = . - __rt_psrelocs_size;
__RUNTIME_PSEUDO_RELOC_LIST__ = . - __rt_psrelocs_size;
.eh_frame BLOCK(__section_alignment__) :
{
KEEP (*(.eh_frame*))
}
.pdata BLOCK(__section_alignment__) :
{
KEEP(*(.pdata*))
}
.xdata BLOCK(__section_alignment__) :
{
KEEP(*(.xdata*))
}
.edata BLOCK(__section_alignment__) :
{
*(.edata)
}
.bss BLOCK(__section_alignment__) :
{
PROVIDE(link_BssStart = .);
__bss_start__ = . ;
*(.bss)
*(COMMON)
__bss_end__ = . ;
PROVIDE(link_BssEnd = .);
}
/DISCARD/ :
{
*(.debug$S)
*(.debug$T)
*(.debug$F)
*(.drectve)
*(.note.GNU-stack)
*(.gnu.lto_*)
}
.CRT BLOCK(__section_alignment__) :
{
___crt_xc_start__ = . ;
KEEP (*(SORT(.CRT$XC*))) /* C initialization */
___crt_xc_end__ = . ;
___crt_xi_start__ = . ;
KEEP (*(SORT(.CRT$XI*))) /* C++ initialization */
___crt_xi_end__ = . ;
___crt_xl_start__ = . ;
KEEP (*(SORT(.CRT$XL*))) /* TLS callbacks */
/* ___crt_xl_end__ is defined in the TLS Directory support code */
___crt_xp_start__ = . ;
KEEP (*(SORT(.CRT$XP*))) /* Pre-termination */
___crt_xp_end__ = . ;
___crt_xt_start__ = . ;
KEEP (*(SORT(.CRT$XT*))) /* Termination */
___crt_xt_end__ = . ;
}
/* Windows TLS expects .tls$AAA to be at the start and .tls$ZZZ to be
at the end of the .tls section. This is important because _tls_start MUST
be at the beginning of the section to enable SECREL32 relocations with TLS
data. */
.tls BLOCK(__section_alignment__) :
{
___tls_start__ = . ;
KEEP (*(.tls$AAA))
KEEP (*(.tls))
KEEP (*(.tls$))
KEEP (*(SORT(.tls$*)))
KEEP (*(.tls$ZZZ))
___tls_end__ = . ;
}
.endjunk BLOCK(__section_alignment__) :
{
/* end is deprecated, don't use it */
PROVIDE (end = .);
PROVIDE ( _end = .);
__end__ = .;
}
.reloc BLOCK(__section_alignment__) :
{
PROVIDE(link_RelocStart = .);
*(.reloc)
PROVIDE(link_RelocEnd = .);
}
}

62
Linker.ld.old Normal file
View File

@ -0,0 +1,62 @@
OUTPUT_FORMAT(pei-x86-64)
SEARCH_DIR("/usr/x86_64-w64-mingw32/lib");
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* Make the virtual address and file offset synced if the alignment is
lower than the target page size. */
. = SIZEOF_HEADERS;
. = ALIGN(__section_alignment__);
/* First put the multiboot header, as it is required to be put very early
early in the image or the bootloader won't recognize the file format.
Next we'll put the .text section. */
.text BLOCK(4K) : ALIGN(4K)
{
PROVIDE(link_TextStart = .);
/* *(.multiboot) */
*(.text)
PROVIDE(link_TextEnd = .);
}
/* Read-only data. */
.rdata BLOCK(4K) : ALIGN(4K)
{
PROVIDE(link_RodataStart = .);
/* *(.rodata) */ /* as in ELF/SysV style */
*(.rdata)
*(SORT(.rdata$*))
. = ALIGN(4);
__rt_psrelocs_start = .;
KEEP(*(.rdata_runtime_pseudo_reloc))
__rt_psrelocs_end = .;
PROVIDE(link_RodataEnd = .);
}
/* Read-write data (initialized) */
.data BLOCK(4K) : ALIGN(4K)
{
PROVIDE(link_DataStart = .);
*(.data)
*(.data)
*(.data2)
*(SORT(.data$*))
KEEP(*(.jcr))
PROVIDE(link_DataEnd = .);
}
/* Read-write data (uninitialized) and stack */
.bss BLOCK(4K) : ALIGN(4K)
{
PROVIDE(link_BssStart = .);
*(COMMON)
*(.bss)
PROVIDE(link_BssEnd = .);
}
/* The compiler may produce other sections, by default it will put them in
a segment with the same name. Simply add stuff here as needed. */
}

33
Makefile Normal file
View File

@ -0,0 +1,33 @@
.SILENT:
include Makefile.flags
objects = $(patsubst %.c,%.o,$(shell find . -name "*.c")) $(patsubst %.cpp,%.o,$(shell find . -name "*.cpp"))
objects_fasm = $(patsubst %.S,%.o,$(shell find . -name "*.S"))
objects_test = $(patsubst %.c,%.o,$(shell find . -name "test_*.c")) $(patsubst %.cpp,%.o,$(shell find . -name "test_*.cpp"))
objects := $(filter-out $(objects_test),$(objects))
all: Main.efi
Main.efi: $(objects) $(objects_fasm)
$(LD) $(LDFLAGS) $(objects) $(objects_fasm) $(LDLIBS)
clean:
echo " -RM Main.efi $(objects)"
-$(RM) -f Main.efi $(objects) $(objects_fasm)
install: Main.efi
echo " CP Main.efi ../FAT/EFI/Boot/bootx64.efi"
$(CP) Main.efi ../FAT/EFI/Boot/bootx64.efi
upload: Main.efi
echo " SCP Main.efi router.edgaru089.ml:/opt/tftp/"
scp -P 29657 Main.efi root@router.edgaru089.ml:/opt/tftp/
.PHONY: all clean install upload

28
Makefile.flags Normal file
View File

@ -0,0 +1,28 @@
.SILENT:
FLAGS_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
SELF_DIR = $(dir $@)
export RM = rm
export CP = cp
export AR = echo " AR $<" && x86_64-w64-mingw32-ar
export AS = echo " AS $^" && x86_64-w64-mingw32-as
export CC = echo " CC $^" && x86_64-w64-mingw32-gcc
export CXX = echo " CXX $^" && x86_64-w64-mingw32-g++
export LD = echo " LD $@" && x86_64-w64-mingw32-gcc
export FASM = echo " FASM $^" && fasm >/dev/null
export INCLUDEFLAGS = -I/usr/include/efi -I/usr/include/efi/x86_64
export CPPFLAGS =
export CFLAGS = $(INCLUDEFLAGS) -DHELOS -O2 -Wno-attributes -fPIE -ffreestanding -nostdlib -mcmodel=large -mno-red-zone
export CXXFLAGS = $(INCLUDEFLAGS) -DHELOS -O2 -Wno-unused-result -std=c++17 -fPIE -ffreestanding -nostdlib -mcmodel=large -mno-red-zone -fno-exceptions -fno-rtti
export LDFLAGS = -T Linker.ld -O2 -eefiMain -nostdlib -shared -fPIE -ffreestanding -Wl,--dynamicbase,--subsystem,10 -o Main.efi -s
export LDLIBS = ../Unifont/unifont.o -lgcc
# Pattern rule for FASM assembly
%.o: %.S
$(FASM) $^ $@

19
Makefile.subdir Normal file
View File

@ -0,0 +1,19 @@
SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
objects = $(patsubst %.c,%.o,$(wildcard *.c)) $(patsubst %.cpp,%.o,$(wildcard *.cpp))
objects_fasm = $(patsubst %.S,%.o,$(wildcard *.S))
objects_test = $(patsubst %.c,%.o,$(wildcard test_*.c)) $(patsubst %.cpp,%.o,$(wildcard test_*.cpp))
objects := $(filter-out $(objects_test),$(objects))
all: $(objects) $(objects_fasm)
$(objects_fasm):
$(FASM) $(patsubst %.o,%.S,$@) $@
clean:
-$(RM) *.o

256
execformat/pe/format.h Normal file
View File

@ -0,0 +1,256 @@
#pragma once
#include "../../main.h"
#include "../../runtime/panic_assert.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// PE Header Magic, comes after the MS-DOS stub and right before PE Header
#define EXECFORMAT_PE_HEADER_MAGIC "PE\0" // comes with a \0 itself
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
typedef struct {
uint16_t machineType;
uint16_t numSections;
uint32_t unixTimestamp;
uint32_t offsetSymbolTable;
uint32_t numSymbols;
uint16_t sizeofOptionalHeader;
uint16_t flags; // "Characteristics"
} PACKED execformat_pe_Header;
// Machine types
#define EXECFORMAT_PE_MACHINE_UNKNOWN 0x0u // The content of this field is assumed to be applicable to any machine type
#define EXECFORMAT_PE_MACHINE_AMD64 0x8664u // x64
#define EXECFORMAT_PE_MACHINE_I386 0x14cu // Intel 386 or later processors and compatible processors
#define EXECFORMAT_PE_MACHINE_EBC 0xebcu // EFI byte code
// Characteristics
#define EXECFORMAT_PE_FLAG_RELOCS_STRIPPED 0x0001u // base relocs removed and must be loaded at its preferred base address.
#define EXECFORMAT_PE_FLAG_EXECUTABLE_IMAGE 0x0002u // the image file is valid and can be run.
#define EXECFORMAT_PE_FLAG_LINE_NUMS_STRIPPED 0x0004u // COFF line numbers removed. deprecated and should be zero.
#define EXECFORMAT_PE_FLAG_LOCAL_SYMS_STRIPPED 0x0008u // COFF symbol table entries for local symbols removed. deprecated and should be zero.
#define EXECFORMAT_PE_FLAG_AGGRESSIVE_WS_TRIM 0x0010u // Aggressively trim working set. deprecated and later and must be zero.
#define EXECFORMAT_PE_FLAG_LARGE_ADDRESS_AWARE 0x0020u // Application can handle >2GB addresses.
#define EXECFORMAT_PE_FLAG_RESERVED_0x0040 0x0040u
#define EXECFORMAT_PE_FLAG_BYTES_REVERSED_LO 0x0080u // Little endian. This flag is deprecated and should be zero.
#define EXECFORMAT_PE_FLAG_32BIT_MACHINE 0x0100u // Machine is based on a 32-bit-word architecture.
#define EXECFORMAT_PE_FLAG_DEBUG_STRIPPED 0x0200u // Debugging information is removed from the image file.
#define EXECFORMAT_PE_FLAG_REMOVABLE_RUN_FROM_SWAP 0x0400u // If the image is on removable media, fully load it and copy it to the swap file.
#define EXECFORMAT_PE_FLAG_NET_RUN_FROM_SWAP 0x0800u // If the image is on network media, fully load it and copy it to the swap file.
#define EXECFORMAT_PE_FLAG_SYSTEM 0x1000u // The image file is a system file, not a user program.
#define EXECFORMAT_PE_FLAG_DLL 0x2000u // The image file is a dynamic-link library (DLL).
#define EXECFORMAT_PE_FLAG_UP_SYSTEM_ONLY 0x4000u // The file should be run only on a uniprocessor machine.
#define EXECFORMAT_PE_FLAG_BYTES_REVERSED_HI 0x8000u // Big endian. This flag is deprecated and should be zero.
// Optional Header Magic
#define EXECFORMAT_PE_OPTIONAL_HEADER_MAGIC_PE32 0x10bu // 2-byte magic for PE32 Optional Header, right following the PE Header.
#define EXECFORMAT_PE_OPTIONAL_HEADER_MAGIC_PE32P 0x20bu // 2-byte magic for PE32+ Optional Header, right following the PE Header.
typedef struct {
uint16_t magic; // either 0x10b for PE32, or 0x20b for PE32+
uint8_t majorLinkerVersion, minorLinkerVersion;
uint32_t sizeofText; // size of the .text section, or the sum of all text sections
uint32_t sizeofData; // size of initialized data sections
uint32_t sizeofBss; // size of uninitialized data
uint32_t offsetofEntryPoint; // offset of the entry point (starting address) relative to the image base
uint32_t offsetofText; // offset of the start of .text section relative to the image base
} PACKED execformat_pe_OptionalHeader_StandardFields_PE32P;
typedef struct {
uint16_t magic; // either 0x10b for PE32, or 0x20b for PE32+
uint8_t majorLinkerVersion, minorLinkerVersion;
uint32_t sizeofText; // size of the .text section, or the sum of all text sections
uint32_t sizeofData; // size of initialized data sections
uint32_t sizeofBss; // size of uninitialized data
uint32_t offsetofEntryPoint; // offset of the entry point (starting address) relative to the image base
uint32_t offsetofText; // offset of the start of .text section relative to the image base
uint32_t offsetofData; // only present in PE32 images
} PACKED execformat_pe_OptionalHeader_StandardFields_PE32;
// PE32+ struct with a 64-bit ImageBase
typedef struct {
uint64_t imageBase; // The preferred address of the first byte of image when loaded into memory; must be aligned to 64K.
uint32_t sectionAlignment; // The alignment (in bytes) of sections when they are loaded into memory.
uint32_t fileAlignment; // The alignment factor (in bytes) that is used to align the raw data of sections in the image file.
uint16_t majorOSVersion, minorOSVersion; // The version number of the required operating system.
uint16_t majorImageVersion, minorImageVersion;
uint16_t majorSubsystemVersion, minorSubsystemVersion;
uint32_t win32VersionValue; // Reserved, must be 0
uint32_t sizeofImage; // Size in bytes of the image, including all headers as the image is loaded into memory.
uint32_t sizeofHeaders; // Combined size of the MS-DOS stub, PE header, and section headers rounded up to FileAlignment.
uint32_t checksum; // Checksum of an unknown(???) algorithm
uint16_t subsystem; // Subsystem
uint16_t dllFlags; // DLL Characteristics
uint64_t sizeofStackReserve, sizeofStackCommit;
uint64_t sizeofHeapReserve, sizeofHeapCommit;
uint32_t loaderFlags; // Reserved, must be 0
uint32_t numRVAandSizes; // The number of data-directory entries in the remainder of the optional header. Each describes a location and size.
} PACKED execformat_pe_OptionalHeader_WindowsFields_PE32P;
// PE32 struct with a 32-bit ImageBase
typedef struct {
uint32_t imageBase; // The preferred address of the first byte of image when loaded into memory; must be aligned to 64K.
uint32_t sectionAlignment; // The alignment (in bytes) of sections when they are loaded into memory.
uint32_t fileAlignment; // The alignment factor (in bytes) that is used to align the raw data of sections in the image file.
uint16_t majorOSVersion, minorOSVersion; // The version number of the required operating system.
uint16_t majorImageVersion, minorImageVersion;
uint16_t majorSubsystemVersion, minorSubsystemVersion;
uint32_t win32VersionValue; // Reserved, must be 0
uint32_t sizeofImage; // Size in bytes of the image, including all headers as the image is loaded into memory.
uint32_t sizeofHeaders; // Combined size of the MS-DOS stub, PE header, and section headers rounded up to FileAlignment.
uint32_t checksum; // Checksum of an unknown(???) algorithm
uint16_t subsystem; // Subsystem
uint16_t dllFlags; // DLL Characteristics
uint32_t sizeofStackReserve, sizeofStackCommit;
uint32_t sizeofHeapReserve, sizeofHeapCommit;
uint32_t loaderFlags; // Reserved, must be 0
uint32_t numRVAandSizes; // The number of data-directory entries in the remainder of the optional header. Each describes a location and size.
} PACKED execformat_pe_OptionalHeader_WindowsFields_PE32;
// Subsystems
#define EXECFORMAT_PE_SUBSYSTEM_UNKNOWN 0
#define EXECFORMAT_PE_SUBSYSTEM_NATIVE 1 // Device drivers and native Windows processes
#define EXECFORMAT_PE_SUBSYSTEM_WINDOWS_GUI 2 // Windows graphical user interface (GUI) subsystem
#define EXECFORMAT_PE_SUBSYSTEM_WINDOWS_CUI 3 // Windows character subsystem
#define EXECFORMAT_PE_SUBSYSTEM_OS2_CUI 5 // OS/2 character subsystem
#define EXECFORMAT_PE_SUBSYSTEM_POSIX_CUI 7 // POSIX character subsystem
#define EXECFORMAT_PE_SUBSYSTEM_NATIVE_WINDOWS 8 // Native Win9x driver
#define EXECFORMAT_PE_SUBSYSTEM_WINDOWS_CE_GUI 9 // Windows CE
#define EXECFORMAT_PE_SUBSYSTEM_EFI_APPLICATION 10 // Extensible Firmware Interface (EFI) application
#define EXECFORMAT_PE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 // EFI Boot Services driver
#define EXECFORMAT_PE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 // EFI Runtime driver
#define EXECFORMAT_PE_SUBSYSTEM_EFI_ROM 13 // EFI ROM image
#define EXECFORMAT_PE_SUBSYSTEM_XBOX 14 // XBOX
#define EXECFORMAT_PE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16 // Windows boot application
// DLL Characteristics
#define EXECFORMAT_PE_DLLFLAG_RESERVED_0x0001 0x0001u // Reserved, must be 0
#define EXECFORMAT_PE_DLLFLAG_RESERVED_0x0002 0x0002u // Reserved, must be 0
#define EXECFORMAT_PE_DLLFLAG_RESERVED_0x0004 0x0004u // Reserved, must be 0
#define EXECFORMAT_PE_DLLFLAG_RESERVED_0x0008 0x0008u // Reserved, must be 0
#define EXECFORMAT_PE_DLLFLAG_HIGH_ENTROPY_VA 0x0020u // Image can handle a high entropy 64-bit virtual address space.
#define EXECFORMAT_PE_DLLFLAG_DYNAMIC_BASE 0x0040u // DLL can be relocated at load time.
#define EXECFORMAT_PE_DLLFLAG_FORCE_INTEGRITY 0x0080u // Code Integrity checks are enforced.
#define EXECFORMAT_PE_DLLFLAG_NX_COMPAT 0x0100u // Image is NX compatible.
#define EXECFORMAT_PE_DLLFLAG_NO_ISOLATION 0x0200u // Isolation aware, but do not isolate the image.
#define EXECFORMAT_PE_DLLFLAG_NO_SEH 0x0400u // Does not use structured exception (SE) handling.
#define EXECFORMAT_PE_DLLFLAG_NO_BIND 0x0800u // Do not bind the image.
#define EXECFORMAT_PE_DLLFLAG_APPCONTAINER 0x1000u // Image must execute in an AppContainer.
#define EXECFORMAT_PE_DLLFLAG_WDM_DRIVER 0x2000u // A WDM driver.
#define EXECFORMAT_PE_DLLFLAG_GUARD_CF 0x4000u // Image supports Control Flow Guard.
#define EXECFORMAT_PE_DLLFLAG_TERMINAL_SERVER_AWARE 0x8000u // Terminal Server aware.
typedef struct {
uint32_t Offset;
uint32_t Size;
} PACKED execformat_pe_OptionalHeader_DataDirectory;
// Data Directory entry indexes
#define EXECFORMAT_PE_DATADIR_INDEX_EXPORT 0 // .edata
#define EXECFORMAT_PE_DATADIR_INDEX_IMPORT 1 // .idata
#define EXECFORMAT_PE_DATADIR_INDEX_RESOURCE 2 // .rsrc
#define EXECFORMAT_PE_DATADIR_INDEX_EXCEPTION 3 // .pdata
#define EXECFORMAT_PE_DATADIR_INDEX_CERTIFICATE 4
#define EXECFORMAT_PE_DATADIR_INDEX_BASE_RELOCATION 5 // .reloc
#define EXECFORMAT_PE_DATADIR_INDEX_DEBUG 6 // .debug
#define EXECFORMAT_PE_DATADIR_INDEX_ARCHITECTURE 7 // Reserved, must be 0
#define EXECFORMAT_PE_DATADIR_INDEX_GLOBAL_PTR 8 // The RVA of the value to be stored in the global pointer register. The size must be 0.
#define EXECFORMAT_PE_DATADIR_INDEX_TLS 9 // .tls
#define EXECFORMAT_PE_DATADIR_INDEX_LOAD_CONFIG 10
#define EXECFORMAT_PE_DATADIR_INDEX_BOUND_IMPORT 11
#define EXECFORMAT_PE_DATADIR_INDEX_IAT 12 // Import Address Table
#define EXECFORMAT_PE_DATADIR_INDEX_DELAY_IMPORT_DESC 13 // Delay Import Descriptor
#define EXECFORMAT_PE_DATADIR_INDEX_CLR_RUNTIME_HEADER 14 // Common Language Runtime header
#define EXECFORMAT_PE_DATADIR_INDEX_RESERVED_15 15 // Reserved, must be 0
typedef struct {
execformat_pe_OptionalHeader_StandardFields_PE32P std;
execformat_pe_OptionalHeader_WindowsFields_PE32P win;
execformat_pe_OptionalHeader_DataDirectory data[1];
} PACKED execformat_pe_OptionalHeader_PE32P;
typedef struct {
execformat_pe_OptionalHeader_StandardFields_PE32 std;
execformat_pe_OptionalHeader_WindowsFields_PE32 win;
execformat_pe_OptionalHeader_DataDirectory data[1];
} PACKED execformat_pe_OptionalHeader_PE32;
static inline void execformat_pe_OptionalHeader_CheckPacking() {
assert(sizeof(execformat_pe_OptionalHeader_StandardFields_PE32) == 28);
assert(sizeof(execformat_pe_OptionalHeader_StandardFields_PE32P) == 24);
assert(sizeof(execformat_pe_OptionalHeader_WindowsFields_PE32) == 68);
assert(sizeof(execformat_pe_OptionalHeader_WindowsFields_PE32P) == 88);
assert(offsetof(execformat_pe_OptionalHeader_PE32, std) == 0);
assert(offsetof(execformat_pe_OptionalHeader_PE32, win) == 28);
assert(offsetof(execformat_pe_OptionalHeader_PE32, data[0]) == 96);
assert(offsetof(execformat_pe_OptionalHeader_PE32P, std) == 0);
assert(offsetof(execformat_pe_OptionalHeader_PE32P, win) == 24);
assert(offsetof(execformat_pe_OptionalHeader_PE32P, data[0]) == 112);
}
typedef struct {
char name[8];
uint32_t virtualSize;
uint32_t virtualAddr; // Offset(RVA) instead of a real Virtual Address
uint32_t sizeofRawData;
uint32_t pointerToRawData; // The file pointer to the first page of the section within the file.
uint32_t pointerToRelocations;
uint32_t pointerToLineNumbers;
uint16_t numRelocations;
uint16_t numLineNumbers; // deprecated, should be 0
uint32_t flags; // The flags that describe the characteristics of the section.
} PACKED execformat_pe_SectionHeader;
// Section Characteristics
#define EXECFORMAT_PE_SECTIONFLAG_NO_PAD 0x00000008u // section should not be padded to the next boundary, obsolete.
#define EXECFORMAT_PE_SECTIONFLAG_CNT_CODE 0x00000020u // section contains executable code.
#define EXECFORMAT_PE_SECTIONFLAG_CNT_INITIALIZED_DATA 0x00000040u // section contains initialized data.
#define EXECFORMAT_PE_SECTIONFLAG_CNT_UNINITIALIZED_DATA 0x00000080u // section contains uninitialized data.
#define EXECFORMAT_PE_SECTIONFLAG_LNK_OTHER 0x00000100u // Reserved.
#define EXECFORMAT_PE_SECTIONFLAG_LNK_INFO 0x00000200u // section contains comments or other info. .drectve section has this type.
#define EXECFORMAT_PE_SECTIONFLAG_LNK_REMOVE 0x00000800u // section will not become part of the image. valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_LNK_COMDAT 0x00001000u // The section contains COMDAT data. valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_GPREL 0x00008000u // section contains data referenced through the global pointer (GP).
#define EXECFORMAT_PE_SECTIONFLAG_MEM_PURGEABLE 0x00020000u // Reserved.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_16BIT 0x00020000u // Reserved.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_LOCKED 0x00040000u // Reserved.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_PRELOAD 0x00080000u // Reserved.
// Section Characteristics - Alignment (only for object files)
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_1BYTES 0x00100000u // Align data on a 1-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_2BYTES 0x00200000u // Align data on a 2-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_4BYTES 0x00300000u // Align data on a 4-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_8BYTES 0x00400000u // Align data on a 8-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_16BYTES 0x00500000u // Align data on a 16-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_32BYTES 0x00600000u // Align data on a 32-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_64BYTES 0x00700000u // Align data on a 64-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_128BYTES 0x00800000u // Align data on a 128-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_256BYTES 0x00900000u // Align data on a 256-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_512BYTES 0x00a00000u // Align data on a 512-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_1024BYTES 0x00b00000u // Align data on a 1024-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_2048BYTES 0x00c00000u // Align data on a 2048-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_4096BYTES 0x00d00000u // Align data on a 4096-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_8192BYTES 0x00e00000u // Align data on a 8192-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_MASK 0x00f00000u // Mask for section data alignment type.
// Section Characteristics - Memory
#define EXECFORMAT_PE_SECTIONFLAG_LNK_NRELOC_OVFL 0x01000000u // section contains extended relocations.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_DISCARDABLE 0x02000000u // section can be discarded as needed.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_NOT_CACHED 0x04000000u // section cannot be cached.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_NOT_PAGED 0x08000000u // section cannot be paged out.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_SHARED 0x10000000u // section can be shared in memory.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_EXECUTE 0x20000000u // section can be executed as code.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_READ 0x40000000u // section can be read.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_WRITE 0x80000000u // section can be written to.
#ifdef __cplusplus
} // extern "C"
#endif

View File

@ -0,0 +1,106 @@
#include "format.h"
#include "struct.h"
#include "../../memory/memory.h"
#include <string.h>
static const char *caseMachine(uint16_t type) {
#define CASE(expr) \
case EXECFORMAT_PE_##expr: \
return #expr;
switch (type) {
CASE(MACHINE_UNKNOWN)
CASE(MACHINE_AMD64)
CASE(MACHINE_I386)
CASE(MACHINE_EBC)
default:
return "(Unknown)";
}
#undef CASE
}
static void caseFlags(uint32_t flags) {
#define CASE(expr) \
if (flags & EXECFORMAT_PE_FLAG_##expr) { \
printf("|" #expr); \
}
CASE(RELOCS_STRIPPED)
CASE(EXECUTABLE_IMAGE)
CASE(LINE_NUMS_STRIPPED)
CASE(LOCAL_SYMS_STRIPPED)
CASE(AGGRESSIVE_WS_TRIM)
CASE(LARGE_ADDRESS_AWARE)
CASE(RESERVED_0x0040)
CASE(BYTES_REVERSED_LO)
CASE(32BIT_MACHINE)
CASE(DEBUG_STRIPPED)
CASE(REMOVABLE_RUN_FROM_SWAP)
CASE(NET_RUN_FROM_SWAP)
CASE(SYSTEM)
CASE(DLL)
CASE(UP_SYSTEM_ONLY)
CASE(BYTES_REVERSED_HI)
printf("|\n");
#undef CASE
}
static void caseSectionFlags(uint32_t flags) {
#define CASE(expr) \
if (flags & EXECFORMAT_PE_SECTIONFLAG_##expr) { \
printf("|" #expr); \
}
CASE(NO_PAD)
CASE(CNT_CODE)
CASE(CNT_INITIALIZED_DATA)
CASE(CNT_UNINITIALIZED_DATA)
CASE(LNK_INFO)
CASE(LNK_REMOVE)
CASE(LNK_COMDAT)
CASE(GPREL)
CASE(LNK_NRELOC_OVFL)
CASE(MEM_DISCARDABLE)
CASE(MEM_NOT_CACHED)
CASE(MEM_NOT_PAGED)
CASE(MEM_SHARED)
CASE(MEM_EXECUTE)
CASE(MEM_READ)
CASE(MEM_WRITE)
printf("|\n");
#undef CASE
}
void execformat_pe_ReadSystemHeader(execformat_pe_PortableExecutable *pe) {
execformat_pe_OptionalHeader_CheckPacking();
uint64_t a;
asm volatile("leaq (%%rip), %0"
: "=r"(a));
io_Printf("Stack position: %llx, RIP=%llx\n", &a, a);
void *addr = (void *)paging_LoaderCodeAddress;
execformat_pe_PortableExecutable rpe;
if (pe == 0)
pe = &rpe;
execformat_pe_LoadMemory(pe, addr, paging_LoaderCodeSize);
printf("%s Executable, Machine Type=0x%x (%s), Flags=", pe->isPE32P ? "PE32+" : "PE32", pe->header->machineType, caseMachine(pe->header->machineType));
caseFlags(pe->header->flags);
printf("\nImageBase=%d (0x%x)\n", ((execformat_pe_OptionalHeader_PE32P *)pe->optional)->win.imageBase, ((execformat_pe_OptionalHeader_PE32P *)pe->optional)->win.imageBase);
printf("numRVAandSizes=%d (want %d)\n\n", pe->numDataDir, EXECFORMAT_PE_DATADIR_INDEX_CLR_RUNTIME_HEADER + 2);
printf("numSections=%d\n", pe->numSections);
char name[9];
for (int i = 0; i < pe->numSections; i++) {
name[8] = 0;
strncpy(name, pe->sections[i].name, 8);
printf(" %8s RVA0x%08x File0x%08x Len0x%08x ", name, pe->sections[i].virtualAddr, pe->sections[i].pointerToRawData, pe->sections[i].virtualSize);
caseSectionFlags(pe->sections[i].flags);
}
}

108
execformat/pe/reloc.c Normal file
View File

@ -0,0 +1,108 @@
#include "reloc.h"
#include "../../runtime/panic_assert.h"
#include <string.h>
static inline int __local_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);
}
}
static const char *caseFlags(uint16_t type) {
#define CASE(expr) \
case EXECFORMAT_PE_BASERELOC_##expr: \
return #expr;
switch (type) {
CASE(ABSOLUTE)
CASE(HIGH)
CASE(LOW)
CASE(HIGHLOW)
CASE(HIGHADJ)
CASE(DIR64)
default:
return "(Unknown)";
}
#undef CASE
}
void execformat_pe_BaseRelocate(execformat_pe_PortableExecutable *pe, void *relocBase, void *relocEnd, uint64_t currentBase, uint64_t targetBase) {
if (pe->begin == 0)
return;
if (currentBase == 0) {
if (pe->isPE32P)
currentBase = ((execformat_pe_OptionalHeader_PE32P *)pe->optional)->win.imageBase;
else
currentBase = ((execformat_pe_OptionalHeader_PE32 *)pe->optional)->win.imageBase;
}
void *reloc = 0, *reloc_end;
if (relocBase != 0 && relocEnd != 0) {
reloc = relocBase;
reloc_end = relocEnd;
} else {
// find the .reloc section
for (int i = 0; i < pe->numSections; i++)
if (__local_strncmp(pe->sections[i].name, ".reloc", 6) == 0) {
reloc = pe->begin + pe->sections[i].virtualAddr;
reloc_end = reloc + pe->sections[i].virtualSize;
break;
}
if (reloc == 0)
return; // no .reloc section
}
uint64_t diff = targetBase - currentBase;
while (reloc < reloc_end) {
execformat_pe_BaseRelocBlock *block = reloc;
int entries = (block->blockSize - 8) / 2;
assert((block->blockSize - 8) % 2 == 0);
printf("BaseReloc Block RVA=0x%08x, entries=%d\n", block->pageOffset, entries);
for (int i = 0; i < entries; i++) {
void *target = pe->begin + block->pageOffset + (block->entries[i] & EXECFORMAT_PE_BASERELOC_OFFSET_MASK);
//uint64_t cur = 0, new = 0;
switch (block->entries[i] & EXECFORMAT_PE_BASERELOC_FLAG_MASK) {
case EXECFORMAT_PE_BASERELOC_HIGH:
//cur = *((uint16_t *)target);
*((uint16_t *)target) += (uint16_t)(diff >> 16);
//new = *((uint16_t *)target);
break;
case EXECFORMAT_PE_BASERELOC_LOW:
//cur = *((uint16_t *)target);
*((uint16_t *)target) += (uint16_t)(diff);
//new = *((uint16_t *)target);
break;
case EXECFORMAT_PE_BASERELOC_HIGHLOW:
//cur = *((uint32_t *)target);
*((uint32_t *)target) += (uint32_t)(diff);
//new = *((uint32_t *)target);
break;
case EXECFORMAT_PE_BASERELOC_DIR64:
//cur = *((uint64_t *)target);
*((uint64_t *)target) += (diff);
//new = *((uint64_t *)target);
break;
case EXECFORMAT_PE_BASERELOC_HIGHADJ:
i++;
break;
}
//printf(" Reloc Off0x%08llx CUR=0x%08llx(Off0x%08llx), NEW=0x%llx, Type=%s\n", target - pe->begin, cur, cur - currentBase, new, caseFlags(block->entries[i] & EXECFORMAT_PE_BASERELOC_FLAG_MASK));
}
reloc += block->blockSize;
}
}

30
execformat/pe/reloc.h Normal file
View File

@ -0,0 +1,30 @@
#include "format.h"
#include "struct.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
uint32_t pageOffset;
uint32_t blockSize; // the size in bytes of the Relocation Block, including the 2 header fields and the entries that follow
uint16_t entries[1];
} PACKED execformat_pe_BaseRelocBlock;
#define EXECFORMAT_PE_BASERELOC_ABSOLUTE (0x0000u) // The base relocation is skipped. This type can be used to pad a block.
#define EXECFORMAT_PE_BASERELOC_HIGH (0x1000u) // The base relocation adds the high 16 bits of the difference to the 16-bit field at offset.
#define EXECFORMAT_PE_BASERELOC_LOW (0x2000u) // The base relocation adds the low 16 bits of the difference to the 16-bit field at offset.
#define EXECFORMAT_PE_BASERELOC_HIGHLOW (0x3000u) // The base relocation applies all 32 bits of the difference to the 32-bit field at offset.
#define EXECFORMAT_PE_BASERELOC_HIGHADJ (0x4000u) // Weird, ignored (together with the next entry).
#define EXECFORMAT_PE_BASERELOC_DIR64 (0xa000u) // The base relocation applies the difference to the 64-bit field at offset.
#define EXECFORMAT_PE_BASERELOC_FLAG_MASK (0xf000u)
#define EXECFORMAT_PE_BASERELOC_OFFSET_MASK (0x0fffu)
void execformat_pe_BaseRelocate(execformat_pe_PortableExecutable *pe, void *relocBase, void *relocEnd, uint64_t currentBase, uint64_t targetBase);
#ifdef __cplusplus
} // extern "C"
#endif

50
execformat/pe/struct.c Normal file
View File

@ -0,0 +1,50 @@
#include "struct.h"
#include "../../runtime/panic_assert.h"
#include <string.h>
void execformat_pe_LoadMemory(execformat_pe_PortableExecutable *pe, void *image, uint32_t size) {
pe->begin = pe->pemagic = 0;
if (size < 2 || ((char *)image)[0] != 'M' || ((char *)image)[1] != 'Z')
return;
// look for the "PE\0\0" magic on a 8-byte boundary
for (int i = 8; i < size; i++) {
if (memcmp(image + i, EXECFORMAT_PE_HEADER_MAGIC, 4) == 0) {
pe->pemagic = image + i;
break;
}
}
if (pe->pemagic == 0)
return; // not found
pe->header = (execformat_pe_Header *)(pe->pemagic + 4);
pe->numSections = pe->header->numSections;
pe->optional = (void *)pe->header + sizeof(execformat_pe_Header);
pe->isPE32P = (*((uint16_t *)pe->optional) == EXECFORMAT_PE_OPTIONAL_HEADER_MAGIC_PE32P);
if (pe->isPE32P) {
pe->numDataDir = ((execformat_pe_OptionalHeader_PE32P *)pe->optional)->win.numRVAandSizes;
pe->sections =
pe->optional +
sizeof(execformat_pe_OptionalHeader_StandardFields_PE32P) +
sizeof(execformat_pe_OptionalHeader_WindowsFields_PE32P) +
sizeof(execformat_pe_OptionalHeader_DataDirectory) * pe->numDataDir;
assert((void *)pe->sections - pe->optional == pe->header->sizeofOptionalHeader && "PE32P OptionalHeader size mismatch");
} else {
pe->numDataDir = ((execformat_pe_OptionalHeader_PE32 *)pe->optional)->win.numRVAandSizes;
pe->sections =
pe->optional +
sizeof(execformat_pe_OptionalHeader_StandardFields_PE32) +
sizeof(execformat_pe_OptionalHeader_WindowsFields_PE32) +
sizeof(execformat_pe_OptionalHeader_DataDirectory) * pe->numDataDir;
assert((void *)pe->sections - pe->optional == pe->header->sizeofOptionalHeader && "PE32 OptionalHeader size mismatch");
}
pe->begin = image;
pe->size = size;
}

29
execformat/pe/struct.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include "../../main.h"
#include "format.h"
#include "stdbool.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
void * begin; // beginning of the image
char * pemagic; // PE magic "PE\0\0", after the MS-DOS stub
execformat_pe_Header * header; // PE headers, 4 bytes right after the magic
void * optional; // optional headers, converted to OptionalHeader_PE32/PE32P on access
execformat_pe_SectionHeader *sections; // start of the section header tables
uint32_t size; // size of the file in bytes
int numDataDir; // shorthand for ((execformat_pe_OptionalHeader_PE32P*)pe->optional)->win.numRVAandSizes
int numSections;
bool isPE32P;
} execformat_pe_PortableExecutable;
void execformat_pe_LoadMemory(execformat_pe_PortableExecutable *pe, void *image, uint32_t size);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@ -0,0 +1,118 @@
#include "format.h"
#include "struct.h"
#include "reloc.h"
#include "stdio.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "sys/mman.h"
#include <assert.h>
#include <string.h>
#include <malloc.h>
const char *caseMachine(uint16_t type) {
#define CASE(expr) \
case EXECFORMAT_PE_##expr: \
return #expr;
switch (type) {
CASE(MACHINE_UNKNOWN)
CASE(MACHINE_AMD64)
CASE(MACHINE_I386)
CASE(MACHINE_EBC)
default:
return "(Unknown)";
}
#undef CASE
}
void caseFlags(uint32_t flags) {
#define CASE(expr) \
if (flags & EXECFORMAT_PE_FLAG_##expr) { \
printf("|" #expr); \
}
CASE(RELOCS_STRIPPED)
CASE(EXECUTABLE_IMAGE)
CASE(LINE_NUMS_STRIPPED)
CASE(LOCAL_SYMS_STRIPPED)
CASE(AGGRESSIVE_WS_TRIM)
CASE(LARGE_ADDRESS_AWARE)
CASE(RESERVED_0x0040)
CASE(BYTES_REVERSED_LO)
CASE(32BIT_MACHINE)
CASE(DEBUG_STRIPPED)
CASE(REMOVABLE_RUN_FROM_SWAP)
CASE(NET_RUN_FROM_SWAP)
CASE(SYSTEM)
CASE(DLL)
CASE(UP_SYSTEM_ONLY)
CASE(BYTES_REVERSED_HI)
printf("|\n");
#undef CASE
}
void caseSectionFlags(uint32_t flags) {
#define CASE(expr) \
if (flags & EXECFORMAT_PE_SECTIONFLAG_##expr) { \
printf("|" #expr); \
}
CASE(NO_PAD)
CASE(CNT_CODE)
CASE(CNT_INITIALIZED_DATA)
CASE(CNT_UNINITIALIZED_DATA)
CASE(LNK_INFO)
CASE(LNK_REMOVE)
CASE(LNK_COMDAT)
CASE(GPREL)
CASE(LNK_NRELOC_OVFL)
CASE(MEM_DISCARDABLE)
CASE(MEM_NOT_CACHED)
CASE(MEM_NOT_PAGED)
CASE(MEM_SHARED)
CASE(MEM_EXECUTE)
CASE(MEM_READ)
CASE(MEM_WRITE)
printf("|\n");
#undef CASE
}
int main(int argc, char *argv[]) {
execformat_pe_OptionalHeader_CheckPacking();
int f = open("../../Main.efi", O_RDONLY);
assert(f != -1 && "File open failed");
struct stat st;
fstat(f, &st);
void *maddr = mmap(0, st.st_size, PROT_READ, MAP_SHARED, f, 0);
void *addr = malloc(st.st_size);
memcpy(addr, maddr, st.st_size);
execformat_pe_PortableExecutable pe;
execformat_pe_LoadMemory(&pe, addr, st.st_size);
printf("%s Executable, Machine Type=0x%x (%s), Flags=", pe.isPE32P ? "PE32+" : "PE32", pe.header->machineType, caseMachine(pe.header->machineType));
caseFlags(pe.header->flags);
printf("\nImageBase=%d (0x%x)\n", ((execformat_pe_OptionalHeader_PE32P *)pe.optional)->win.imageBase, ((execformat_pe_OptionalHeader_PE32P *)pe.optional)->win.imageBase);
printf("numRVAandSizes=%d (want %d)\n\n", pe.numDataDir, EXECFORMAT_PE_DATADIR_INDEX_CLR_RUNTIME_HEADER + 2);
printf("numSections=%d\n", pe.numSections);
char name[9];
for (int i = 0; i < pe.numSections; i++) {
name[8] = 0;
strncpy(name, pe.sections[i].name, 8);
printf(" %8s Rva0x%08x File0x%08x Len0x%08x ", name, pe.sections[i].virtualAddr, pe.sections[i].pointerToRawData, pe.sections[i].virtualSize);
caseSectionFlags(pe.sections[i].flags);
}
execformat_pe_BaseRelocate(&pe, 0, 0xFFFFFFFFC0000000ull);
return 0;
}

20
extlib/Makefile.old Normal file
View File

@ -0,0 +1,20 @@
SELF_DIR = $(dir $@)
export AR = echo " AR extlib/$(if $<,$<,$(SELF_DIR)$$<)" && x86_64-w64-mingw32-ar
export AS = echo " AS extlib/$(if $<,$<,$(SELF_DIR)$$<)" && x86_64-w64-mingw32-as
export CC = echo " CC extlib/$(if $<,$<,$(SELF_DIR)$$<)" && x86_64-w64-mingw32-gcc
export CXX = echo " CXX extlib/$(if $<,$<,$(SELF_DIR)$$<)" && x86_64-w64-mingw32-g++
export LD = echo " LD extlib/$@" && x86_64-w64-mingw32-gcc
export FASM = echo " FASM extlib/$(SELF_DIR)$$(patsubst %.o,%.S,$$@)" && fasm >/dev/null
SUBDIRS = $(wildcard */.)
$(SUBDIRS):
echo $@
echo $(SUBDIRS)
$(MAKE) -sC $@
all: $(SUBDIRS)
.PHONY: all $(SUBDIRS)

26
extlib/liballoc/HOWTO Normal file
View File

@ -0,0 +1,26 @@
This is a step-by-step instruction guide detailing how to
implement the library on your own system.
1. Copy the "liballoc.c" and "liballoc.h" files into
your working directory. (Whatever project you want
to use it in).
2. Create the hooks that the library needs:
Make a new file called "liballoc_hooks.c" (or
whatever) and implement the functions detailed
in the README and explained in "liballoc.h"
Look at "linux.c" for an example. It implements
the hooks for a Linux system.
3. Be sure to include the "liballoc.h" header
into your C/C++ files that are using the
malloc/free/memory operations. (So that
malloc/free/etc are declared.. obviously.)
4. Compile as per your normal method and test.

10
extlib/liballoc/LICENSE Normal file
View File

@ -0,0 +1,10 @@
This code is released into the public domain. Use this code at your own
risk. Feel free to use it for whatever purpose you want. I take no responsibilty or
whatever if anything goes wrong. Use it at your own risk.
If you have any fixes or patches, please email me.
Durand Miller <clutter@djm.co.za>

68
extlib/liballoc/README.md Normal file
View File

@ -0,0 +1,68 @@
https://github.com/blanham/liballoc
liballoc - a small memory allocator
===================================
This is liballoc, a memory allocator for hobby operating systems, originally
written by Durand. According to the original page for liballoc it was released
into the public domain, but the copy I have contains the 3 clause BSD license.
liballoc.c/h are the original release of liballoc taken from the spoon tarball
while liballoc_1_1.c/h are later versions found by detective work using Google.
Using liballoc
==============
There are 4 functions which you need to implement on your system:
int liballoc_lock();
int liballoc_unlock();
void* liballoc_alloc(int);
int liballoc_free(void*,int);
1) Have a look at liballoc.h for information about what each function is
supposed to do.
2) Have a look at linux.c for an example of how to implement the library
on linux.
NOTE: There are two ways to build the library:
1) Compile the library with a new system file. For example, I've
left linux.c with the default distribution. It gets compiled
directly into the liballoc_linux.so file.
2) Implement the functions in your application and then just
link against the default liballoc.so library when you compile
your app.
Quick Start
===========
You can simply type: "make linux" to build the linux shared
library. Thereafter, you can link it directly into your applications
during build or afterwards by export the LD_PRELOAD environment
variable.
To run bash with the library, for example:
LD_PRELOAD=/full/path/to/liballoc.so bash
The above command will pre-link the library into the application,
essentially replacing the default malloc/free calls at runtime. It's
quite cool.
Originally by:
Durand Miller

View File

@ -0,0 +1,832 @@
#include "liballoc_1_1.h"
/** Durand's Amazing Super Duper Memory functions. */
#define VERSION "1.1"
#define ALIGNMENT 16ul//4ul ///< This is the byte alignment that memory must be allocated on. IMPORTANT for GTK and other stuff.
#define ALIGN_TYPE char ///unsigned char[16] /// unsigned short
#define ALIGN_INFO sizeof(ALIGN_TYPE)*16 ///< Alignment information is stored right before the pointer. This is the number of bytes of information stored there.
#define USE_CASE1
#define USE_CASE2
#define USE_CASE3
#define USE_CASE4
#define USE_CASE5
/** This macro will conveniently align our pointer upwards */
#define ALIGN( ptr ) \
if ( ALIGNMENT > 1 ) \
{ \
uintptr_t diff; \
ptr = (void*)((uintptr_t)ptr + ALIGN_INFO); \
diff = (uintptr_t)ptr & (ALIGNMENT-1); \
if ( diff != 0 ) \
{ \
diff = ALIGNMENT - diff; \
ptr = (void*)((uintptr_t)ptr + diff); \
} \
*((ALIGN_TYPE*)((uintptr_t)ptr - ALIGN_INFO)) = \
diff + ALIGN_INFO; \
}
#define UNALIGN( ptr ) \
if ( ALIGNMENT > 1 ) \
{ \
uintptr_t diff = *((ALIGN_TYPE*)((uintptr_t)ptr - ALIGN_INFO)); \
if ( diff < (ALIGNMENT + ALIGN_INFO) ) \
{ \
ptr = (void*)((uintptr_t)ptr - diff); \
} \
}
#define LIBALLOC_MAGIC 0xc001c0de
#define LIBALLOC_DEAD 0xdeaddead
#if defined DEBUG || defined INFO
#include <stdio.h>
#include <stdlib.h>
#define FLUSH() fflush( stdout )
#endif
/** A structure found at the top of all system allocated
* memory blocks. It details the usage of the memory block.
*/
struct liballoc_major
{
struct liballoc_major *prev; ///< Linked list information.
struct liballoc_major *next; ///< Linked list information.
unsigned int pages; ///< The number of pages in the block.
unsigned int size; ///< The number of pages in the block.
unsigned int usage; ///< The number of bytes used in the block.
struct liballoc_minor *first; ///< A pointer to the first allocated memory in the block.
};
/** This is a structure found at the beginning of all
* sections in a major block which were allocated by a
* malloc, calloc, realloc call.
*/
struct liballoc_minor
{
struct liballoc_minor *prev; ///< Linked list information.
struct liballoc_minor *next; ///< Linked list information.
struct liballoc_major *block; ///< The owning block. A pointer to the major structure.
unsigned int magic; ///< A magic number to idenfity correctness.
unsigned int size; ///< The size of the memory allocated. Could be 1 byte or more.
unsigned int req_size; ///< The size of memory requested.
};
static struct liballoc_major *l_memRoot = NULL; ///< The root memory block acquired from the system.
static struct liballoc_major *l_bestBet = NULL; ///< The major with the most free memory.
static unsigned int l_pageSize = 4096; ///< The size of an individual page. Set up in liballoc_init.
static unsigned int l_pageCount = 16; ///< The number of pages to request per chunk. Set up in liballoc_init.
static unsigned long long l_allocated = 0; ///< Running total of allocated memory.
static unsigned long long l_inuse = 0; ///< Running total of used memory.
static long long l_warningCount = 0; ///< Number of warnings encountered
static long long l_errorCount = 0; ///< Number of actual errors
static long long l_possibleOverruns = 0; ///< Number of possible overruns
// *********** HELPER FUNCTIONS *******************************
static void *liballoc_memset(void* s, int c, size_t n)
{
unsigned int i;
for ( i = 0; i < n ; i++)
((char*)s)[i] = c;
return s;
}
static void* liballoc_memcpy(void* s1, const void* s2, size_t n)
{
char *cdest;
char *csrc;
unsigned int *ldest = (unsigned int*)s1;
unsigned int *lsrc = (unsigned int*)s2;
while ( n >= sizeof(unsigned int) )
{
*ldest++ = *lsrc++;
n -= sizeof(unsigned int);
}
cdest = (char*)ldest;
csrc = (char*)lsrc;
while ( n > 0 )
{
*cdest++ = *csrc++;
n -= 1;
}
return s1;
}
#if defined DEBUG || defined INFO
static void liballoc_dump()
{
#ifdef DEBUG
struct liballoc_major *maj = l_memRoot;
struct liballoc_minor *min = NULL;
#endif
printf( "liballoc: ------ Memory data ---------------\n");
printf( "liballoc: System memory allocated: %i bytes\n", l_allocated );
printf( "liballoc: Memory in used (malloc'ed): %i bytes\n", l_inuse );
printf( "liballoc: Warning count: %i\n", l_warningCount );
printf( "liballoc: Error count: %i\n", l_errorCount );
printf( "liballoc: Possible overruns: %i\n", l_possibleOverruns );
#ifdef DEBUG
while ( maj != NULL )
{
printf( "liballoc: %x: total = %i, used = %i\n",
maj,
maj->size,
maj->usage );
min = maj->first;
while ( min != NULL )
{
printf( "liballoc: %x: %i bytes\n",
min,
min->size );
min = min->next;
}
maj = maj->next;
}
#endif
FLUSH();
}
#endif
// ***************************************************************
static struct liballoc_major *allocate_new_page( unsigned int size )
{
unsigned int st;
struct liballoc_major *maj;
// This is how much space is required.
st = size + sizeof(struct liballoc_major);
st += sizeof(struct liballoc_minor);
// Perfect amount of space?
if ( (st % l_pageSize) == 0 )
st = st / (l_pageSize);
else
st = st / (l_pageSize) + 1;
// No, add the buffer.
// Make sure it's >= the minimum size.
if ( st < l_pageCount ) st = l_pageCount;
maj = (struct liballoc_major*)liballoc_alloc( st );
if ( maj == NULL )
{
l_warningCount += 1;
#if defined DEBUG || defined INFO
printf( "liballoc: WARNING: liballoc_alloc( %i ) return NULL\n", st );
FLUSH();
#endif
return NULL; // uh oh, we ran out of memory.
}
maj->prev = NULL;
maj->next = NULL;
maj->pages = st;
maj->size = st * l_pageSize;
maj->usage = sizeof(struct liballoc_major);
maj->first = NULL;
l_allocated += maj->size;
#ifdef DEBUG
printf( "liballoc: Resource allocated %x of %i pages (%i bytes) for %i size.\n", maj, st, maj->size, size );
printf( "liballoc: Total memory usage = %i KB\n", (int)((l_allocated / (1024))) );
FLUSH();
#endif
return maj;
}
void *PREFIX(malloc)(size_t req_size)
{
int startedBet = 0;
unsigned long long bestSize = 0;
void *p = NULL;
uintptr_t diff;
struct liballoc_major *maj;
struct liballoc_minor *min;
struct liballoc_minor *new_min;
unsigned long size = req_size;
// For alignment, we adjust size so there's enough space to align.
if ( ALIGNMENT > 1 )
{
size += ALIGNMENT + ALIGN_INFO;
}
// So, ideally, we really want an alignment of 0 or 1 in order
// to save space.
liballoc_lock();
if ( size == 0 )
{
l_warningCount += 1;
#if defined DEBUG || defined INFO
printf( "liballoc: WARNING: alloc( 0 ) called from %x\n",
__builtin_return_address(0) );
FLUSH();
#endif
liballoc_unlock();
return PREFIX(malloc)(1);
}
if ( l_memRoot == NULL )
{
#if defined DEBUG || defined INFO
#ifdef DEBUG
printf( "liballoc: initialization of liballoc " VERSION "\n" );
#endif
atexit( liballoc_dump );
FLUSH();
#endif
// This is the first time we are being used.
l_memRoot = allocate_new_page( size );
if ( l_memRoot == NULL )
{
liballoc_unlock();
#ifdef DEBUG
printf( "liballoc: initial l_memRoot initialization failed\n", p);
FLUSH();
#endif
return NULL;
}
#ifdef DEBUG
printf( "liballoc: set up first memory major %x\n", l_memRoot );
FLUSH();
#endif
}
#ifdef DEBUG
printf( "liballoc: %x PREFIX(malloc)( %i ): ",
__builtin_return_address(0),
size );
FLUSH();
#endif
// Now we need to bounce through every major and find enough space....
maj = l_memRoot;
startedBet = 0;
// Start at the best bet....
if ( l_bestBet != NULL )
{
bestSize = l_bestBet->size - l_bestBet->usage;
if ( bestSize > (size + sizeof(struct liballoc_minor)))
{
maj = l_bestBet;
startedBet = 1;
}
}
while ( maj != NULL )
{
diff = maj->size - maj->usage;
// free memory in the block
if ( bestSize < diff )
{
// Hmm.. this one has more memory then our bestBet. Remember!
l_bestBet = maj;
bestSize = diff;
}
#ifdef USE_CASE1
// CASE 1: There is not enough space in this major block.
if ( diff < (size + sizeof( struct liballoc_minor )) )
{
#ifdef DEBUG
printf( "CASE 1: Insufficient space in block %x\n", maj);
FLUSH();
#endif
// Another major block next to this one?
if ( maj->next != NULL )
{
maj = maj->next; // Hop to that one.
continue;
}
if ( startedBet == 1 ) // If we started at the best bet,
{ // let's start all over again.
maj = l_memRoot;
startedBet = 0;
continue;
}
// Create a new major block next to this one and...
maj->next = allocate_new_page( size ); // next one will be okay.
if ( maj->next == NULL ) break; // no more memory.
maj->next->prev = maj;
maj = maj->next;
// .. fall through to CASE 2 ..
}
#endif
#ifdef USE_CASE2
// CASE 2: It's a brand new block.
if ( maj->first == NULL )
{
maj->first = (struct liballoc_minor*)((uintptr_t)maj + sizeof(struct liballoc_major) );
maj->first->magic = LIBALLOC_MAGIC;
maj->first->prev = NULL;
maj->first->next = NULL;
maj->first->block = maj;
maj->first->size = size;
maj->first->req_size = req_size;
maj->usage += size + sizeof( struct liballoc_minor );
l_inuse += size;
p = (void*)((uintptr_t)(maj->first) + sizeof( struct liballoc_minor ));
ALIGN( p );
#ifdef DEBUG
printf( "CASE 2: returning %x\n", p);
FLUSH();
#endif
liballoc_unlock(); // release the lock
return p;
}
#endif
#ifdef USE_CASE3
// CASE 3: Block in use and enough space at the start of the block.
diff = (uintptr_t)(maj->first);
diff -= (uintptr_t)maj;
diff -= sizeof(struct liballoc_major);
if ( diff >= (size + sizeof(struct liballoc_minor)) )
{
// Yes, space in front. Squeeze in.
maj->first->prev = (struct liballoc_minor*)((uintptr_t)maj + sizeof(struct liballoc_major) );
maj->first->prev->next = maj->first;
maj->first = maj->first->prev;
maj->first->magic = LIBALLOC_MAGIC;
maj->first->prev = NULL;
maj->first->block = maj;
maj->first->size = size;
maj->first->req_size = req_size;
maj->usage += size + sizeof( struct liballoc_minor );
l_inuse += size;
p = (void*)((uintptr_t)(maj->first) + sizeof( struct liballoc_minor ));
ALIGN( p );
#ifdef DEBUG
printf( "CASE 3: returning %x\n", p);
FLUSH();
#endif
liballoc_unlock(); // release the lock
return p;
}
#endif
#ifdef USE_CASE4
// CASE 4: There is enough space in this block. But is it contiguous?
min = maj->first;
// Looping within the block now...
while ( min != NULL )
{
// CASE 4.1: End of minors in a block. Space from last and end?
if ( min->next == NULL )
{
// the rest of this block is free... is it big enough?
diff = (uintptr_t)(maj) + maj->size;
diff -= (uintptr_t)min;
diff -= sizeof( struct liballoc_minor );
diff -= min->size;
// minus already existing usage..
if ( diff >= (size + sizeof( struct liballoc_minor )) )
{
// yay....
min->next = (struct liballoc_minor*)((uintptr_t)min + sizeof( struct liballoc_minor ) + min->size);
min->next->prev = min;
min = min->next;
min->next = NULL;
min->magic = LIBALLOC_MAGIC;
min->block = maj;
min->size = size;
min->req_size = req_size;
maj->usage += size + sizeof( struct liballoc_minor );
l_inuse += size;
p = (void*)((uintptr_t)min + sizeof( struct liballoc_minor ));
ALIGN( p );
#ifdef DEBUG
printf( "CASE 4.1: returning %x\n", p);
FLUSH();
#endif
liballoc_unlock(); // release the lock
return p;
}
}
// CASE 4.2: Is there space between two minors?
if ( min->next != NULL )
{
// is the difference between here and next big enough?
diff = (uintptr_t)(min->next);
diff -= (uintptr_t)min;
diff -= sizeof( struct liballoc_minor );
diff -= min->size;
// minus our existing usage.
if ( diff >= (size + sizeof( struct liballoc_minor )) )
{
// yay......
new_min = (struct liballoc_minor*)((uintptr_t)min + sizeof( struct liballoc_minor ) + min->size);
new_min->magic = LIBALLOC_MAGIC;
new_min->next = min->next;
new_min->prev = min;
new_min->size = size;
new_min->req_size = req_size;
new_min->block = maj;
min->next->prev = new_min;
min->next = new_min;
maj->usage += size + sizeof( struct liballoc_minor );
l_inuse += size;
p = (void*)((uintptr_t)new_min + sizeof( struct liballoc_minor ));
ALIGN( p );
#ifdef DEBUG
printf( "CASE 4.2: returning %x\n", p);
FLUSH();
#endif
liballoc_unlock(); // release the lock
return p;
}
} // min->next != NULL
min = min->next;
} // while min != NULL ...
#endif
#ifdef USE_CASE5
// CASE 5: Block full! Ensure next block and loop.
if ( maj->next == NULL )
{
#ifdef DEBUG
printf( "CASE 5: block full\n");
FLUSH();
#endif
if ( startedBet == 1 )
{
maj = l_memRoot;
startedBet = 0;
continue;
}
// we've run out. we need more...
maj->next = allocate_new_page( size ); // next one guaranteed to be okay
if ( maj->next == NULL ) break; // uh oh, no more memory.....
maj->next->prev = maj;
}
#endif
maj = maj->next;
} // while (maj != NULL)
liballoc_unlock(); // release the lock
#ifdef DEBUG
printf( "All cases exhausted. No memory available.\n");
FLUSH();
#endif
#if defined DEBUG || defined INFO
printf( "liballoc: WARNING: PREFIX(malloc)( %i ) returning NULL.\n", size);
liballoc_dump();
FLUSH();
#endif
return NULL;
}
void PREFIX(free)(void *ptr)
{
struct liballoc_minor *min;
struct liballoc_major *maj;
if ( ptr == NULL )
{
l_warningCount += 1;
#if defined DEBUG || defined INFO
printf( "liballoc: WARNING: PREFIX(free)( NULL ) called from %x\n",
__builtin_return_address(0) );
FLUSH();
#endif
return;
}
UNALIGN( ptr );
liballoc_lock(); // lockit
min = (struct liballoc_minor*)((uintptr_t)ptr - sizeof( struct liballoc_minor ));
if ( min->magic != LIBALLOC_MAGIC )
{
l_errorCount += 1;
// Check for overrun errors. For all bytes of LIBALLOC_MAGIC
if (
((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF)) ||
((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF)) ||
((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))
)
{
l_possibleOverruns += 1;
#if defined DEBUG || defined INFO
printf( "liballoc: ERROR: Possible 1-3 byte overrun for magic %x != %x\n",
min->magic,
LIBALLOC_MAGIC );
FLUSH();
#endif
}
if ( min->magic == LIBALLOC_DEAD )
{
#if defined DEBUG || defined INFO
printf( "liballoc: ERROR: multiple PREFIX(free)() attempt on %x from %x.\n",
ptr,
__builtin_return_address(0) );
FLUSH();
#endif
}
else
{
#if defined DEBUG || defined INFO
printf( "liballoc: ERROR: Bad PREFIX(free)( %x ) called from %x\n",
ptr,
__builtin_return_address(0) );
FLUSH();
#endif
}
// being lied to...
liballoc_unlock(); // release the lock
return;
}
#ifdef DEBUG
printf( "liballoc: %x PREFIX(free)( %x ): ",
__builtin_return_address( 0 ),
ptr );
FLUSH();
#endif
maj = min->block;
l_inuse -= min->size;
maj->usage -= (min->size + sizeof( struct liballoc_minor ));
min->magic = LIBALLOC_DEAD; // No mojo.
if ( min->next != NULL ) min->next->prev = min->prev;
if ( min->prev != NULL ) min->prev->next = min->next;
if ( min->prev == NULL ) maj->first = min->next;
// Might empty the block. This was the first
// minor.
// We need to clean up after the majors now....
if ( maj->first == NULL ) // Block completely unused.
{
if ( l_memRoot == maj ) l_memRoot = maj->next;
if ( l_bestBet == maj ) l_bestBet = NULL;
if ( maj->prev != NULL ) maj->prev->next = maj->next;
if ( maj->next != NULL ) maj->next->prev = maj->prev;
l_allocated -= maj->size;
liballoc_free( maj, maj->pages );
}
else
{
if ( l_bestBet != NULL )
{
int bestSize = l_bestBet->size - l_bestBet->usage;
int majSize = maj->size - maj->usage;
if ( majSize > bestSize ) l_bestBet = maj;
}
}
#ifdef DEBUG
printf( "OK\n");
FLUSH();
#endif
liballoc_unlock(); // release the lock
}
void* PREFIX(calloc)(size_t nobj, size_t size)
{
int real_size;
void *p;
real_size = nobj * size;
p = PREFIX(malloc)( real_size );
liballoc_memset( p, 0, real_size );
return p;
}
void* PREFIX(realloc)(void *p, size_t size)
{
void *ptr;
struct liballoc_minor *min;
unsigned int real_size;
// Honour the case of size == 0 => free old and return NULL
if ( size == 0 )
{
PREFIX(free)( p );
return NULL;
}
// In the case of a NULL pointer, return a simple malloc.
if ( p == NULL ) return PREFIX(malloc)( size );
// Unalign the pointer if required.
ptr = p;
UNALIGN(ptr);
liballoc_lock(); // lockit
min = (struct liballoc_minor*)((uintptr_t)ptr - sizeof( struct liballoc_minor ));
// Ensure it is a valid structure.
if ( min->magic != LIBALLOC_MAGIC )
{
l_errorCount += 1;
// Check for overrun errors. For all bytes of LIBALLOC_MAGIC
if (
((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF)) ||
((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF)) ||
((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))
)
{
l_possibleOverruns += 1;
#if defined DEBUG || defined INFO
printf( "liballoc: ERROR: Possible 1-3 byte overrun for magic %x != %x\n",
min->magic,
LIBALLOC_MAGIC );
FLUSH();
#endif
}
if ( min->magic == LIBALLOC_DEAD )
{
#if defined DEBUG || defined INFO
printf( "liballoc: ERROR: multiple PREFIX(free)() attempt on %x from %x.\n",
ptr,
__builtin_return_address(0) );
FLUSH();
#endif
}
else
{
#if defined DEBUG || defined INFO
printf( "liballoc: ERROR: Bad PREFIX(free)( %x ) called from %x\n",
ptr,
__builtin_return_address(0) );
FLUSH();
#endif
}
// being lied to...
liballoc_unlock(); // release the lock
return NULL;
}
// Definitely a memory block.
real_size = min->req_size;
if ( real_size >= size )
{
min->req_size = size;
liballoc_unlock();
return p;
}
liballoc_unlock();
// If we got here then we're reallocating to a block bigger than us.
ptr = PREFIX(malloc)( size ); // We need to allocate new memory
liballoc_memcpy( ptr, p, real_size );
PREFIX(free)( p );
return ptr;
}

View File

@ -0,0 +1,87 @@
#ifndef _LIBALLOC_H
#define _LIBALLOC_H
/** \defgroup ALLOCHOOKS liballoc hooks
*
* These are the OS specific functions which need to
* be implemented on any platform that the library
* is expected to work on.
*/
/** @{ */
#include <stdlib.h>
#include <stdint.h>
// If we are told to not define our own size_t, then we skip the define.
//#define _HAVE_UINTPTR_T
//typedef unsigned long uintptr_t;
typedef uint64_t uintptr_t;
//This lets you prefix malloc and friends
#ifndef PREFIX
#define PREFIX(func) liballoc_k ## func
#endif
#ifdef __cplusplus
extern "C" {
#endif
/** This function is supposed to lock the memory data structures. It
* could be as simple as disabling interrupts or acquiring a spinlock.
* It's up to you to decide.
*
* \return 0 if the lock was acquired successfully. Anything else is
* failure.
*/
extern int liballoc_lock();
/** This function unlocks what was previously locked by the liballoc_lock
* function. If it disabled interrupts, it enables interrupts. If it
* had acquiried a spinlock, it releases the spinlock. etc.
*
* \return 0 if the lock was successfully released.
*/
extern int liballoc_unlock();
/** This is the hook into the local system which allocates pages. It
* accepts an integer parameter which is the number of pages
* required. The page size was set up in the liballoc_init function.
*
* \return NULL if the pages were not allocated.
* \return A pointer to the allocated memory.
*/
extern void* liballoc_alloc(size_t);
/** This frees previously allocated memory. The void* parameter passed
* to the function is the exact same value returned from a previous
* liballoc_alloc call.
*
* The integer value is the number of pages to free.
*
* \return 0 if the memory was successfully freed.
*/
extern int liballoc_free(void*,size_t);
extern void *PREFIX(malloc)(size_t); ///< The standard function.
extern void *PREFIX(realloc)(void *, size_t); ///< The standard function.
extern void *PREFIX(calloc)(size_t, size_t); ///< The standard function.
extern void PREFIX(free)(void *); ///< The standard function.
#ifdef __cplusplus
}
#endif
/** @} */
#endif

23
extlib/libvterm/LICENSE Normal file
View File

@ -0,0 +1,23 @@
The MIT License
Copyright (c) 2008 Paul Evans <leonerd@leonerd.org.uk>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

223
extlib/libvterm/encoding.c Normal file
View File

@ -0,0 +1,223 @@
#include "vterm_internal.h"
#define UNICODE_INVALID 0xFFFD
#if defined(DEBUG) && DEBUG > 1
#define DEBUG_PRINT_UTF8
#endif
struct UTF8DecoderData {
// number of bytes remaining in this codepoint
int bytes_remaining;
// number of bytes total in this codepoint once it's finished
// (for detecting overlongs)
int bytes_total;
int this_cp;
};
static void init_utf8(VTermEncoding *enc, void *data_) {
struct UTF8DecoderData *data = data_;
data->bytes_remaining = 0;
data->bytes_total = 0;
}
static void decode_utf8(VTermEncoding *enc, void *data_, uint32_t cp[], int *cpi, int cplen, const char bytes[], size_t *pos, size_t bytelen) {
struct UTF8DecoderData *data = data_;
#ifdef DEBUG_PRINT_UTF8
printf("BEGIN UTF-8\n");
#endif
for (; *pos < bytelen && *cpi < cplen; (*pos)++) {
unsigned char c = bytes[*pos];
#ifdef DEBUG_PRINT_UTF8
printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
#endif
if (c < 0x20) // C0
return;
else if (c >= 0x20 && c < 0x7f) {
if (data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
cp[(*cpi)++] = c;
#ifdef DEBUG_PRINT_UTF8
printf(" UTF-8 char: U+%04x\n", c);
#endif
data->bytes_remaining = 0;
}
else if (c == 0x7f) // DEL
return;
else if (c >= 0x80 && c < 0xc0) {
if (!data->bytes_remaining) {
cp[(*cpi)++] = UNICODE_INVALID;
continue;
}
data->this_cp <<= 6;
data->this_cp |= c & 0x3f;
data->bytes_remaining--;
if (!data->bytes_remaining) {
#ifdef DEBUG_PRINT_UTF8
printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, data->bytes_total);
#endif
// Check for overlong sequences
switch (data->bytes_total) {
case 2:
if (data->this_cp < 0x0080)
data->this_cp = UNICODE_INVALID;
break;
case 3:
if (data->this_cp < 0x0800)
data->this_cp = UNICODE_INVALID;
break;
case 4:
if (data->this_cp < 0x10000)
data->this_cp = UNICODE_INVALID;
break;
case 5:
if (data->this_cp < 0x200000)
data->this_cp = UNICODE_INVALID;
break;
case 6:
if (data->this_cp < 0x4000000)
data->this_cp = UNICODE_INVALID;
break;
}
// Now look for plain invalid ones
if ((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF) ||
data->this_cp == 0xFFFE ||
data->this_cp == 0xFFFF)
data->this_cp = UNICODE_INVALID;
#ifdef DEBUG_PRINT_UTF8
printf(" char: U+%04x\n", data->this_cp);
#endif
cp[(*cpi)++] = data->this_cp;
}
}
else if (c >= 0xc0 && c < 0xe0) {
if (data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
data->this_cp = c & 0x1f;
data->bytes_total = 2;
data->bytes_remaining = 1;
}
else if (c >= 0xe0 && c < 0xf0) {
if (data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
data->this_cp = c & 0x0f;
data->bytes_total = 3;
data->bytes_remaining = 2;
}
else if (c >= 0xf0 && c < 0xf8) {
if (data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
data->this_cp = c & 0x07;
data->bytes_total = 4;
data->bytes_remaining = 3;
}
else if (c >= 0xf8 && c < 0xfc) {
if (data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
data->this_cp = c & 0x03;
data->bytes_total = 5;
data->bytes_remaining = 4;
}
else if (c >= 0xfc && c < 0xfe) {
if (data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
data->this_cp = c & 0x01;
data->bytes_total = 6;
data->bytes_remaining = 5;
}
else {
cp[(*cpi)++] = UNICODE_INVALID;
}
}
}
static VTermEncoding encoding_utf8 = {
.init = &init_utf8,
.decode = &decode_utf8,
};
static void decode_usascii(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen, const char bytes[], size_t *pos, size_t bytelen) {
int is_gr = bytes[*pos] & 0x80;
for (; *pos < bytelen && *cpi < cplen; (*pos)++) {
unsigned char c = bytes[*pos] ^ is_gr;
if (c < 0x20 || c == 0x7f || c >= 0x80)
return;
cp[(*cpi)++] = c;
}
}
static VTermEncoding encoding_usascii = {
.decode = &decode_usascii,
};
struct StaticTableEncoding {
const VTermEncoding enc;
const uint32_t chars[128];
};
static void decode_table(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen, const char bytes[], size_t *pos, size_t bytelen) {
struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
int is_gr = bytes[*pos] & 0x80;
for (; *pos < bytelen && *cpi < cplen; (*pos)++) {
unsigned char c = bytes[*pos] ^ is_gr;
if (c < 0x20 || c == 0x7f || c >= 0x80)
return;
if (table->chars[c])
cp[(*cpi)++] = table->chars[c];
else
cp[(*cpi)++] = c;
}
}
#include "encoding/DECdrawing.inc"
#include "encoding/uk.inc"
static struct {
VTermEncodingType type;
char designation;
VTermEncoding * enc;
} encodings[] = {
{ENC_UTF8, 'u', &encoding_utf8},
{ENC_SINGLE_94, '0', (VTermEncoding *)&encoding_DECdrawing},
{ENC_SINGLE_94, 'A', (VTermEncoding *)&encoding_uk},
{ENC_SINGLE_94, 'B', &encoding_usascii},
{0},
};
/* This ought to be INTERNAL but isn't because it's used by unit testing */
VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation) {
for (int i = 0; encodings[i].designation; i++)
if (encodings[i].type == type && encodings[i].designation == designation)
return encodings[i].enc;
return NULL;
}

View File

@ -0,0 +1,36 @@
static const struct StaticTableEncoding encoding_DECdrawing = {
{ .decode = &decode_table },
{
[0x60] = 0x25C6,
[0x61] = 0x2592,
[0x62] = 0x2409,
[0x63] = 0x240C,
[0x64] = 0x240D,
[0x65] = 0x240A,
[0x66] = 0x00B0,
[0x67] = 0x00B1,
[0x68] = 0x2424,
[0x69] = 0x240B,
[0x6a] = 0x2518,
[0x6b] = 0x2510,
[0x6c] = 0x250C,
[0x6d] = 0x2514,
[0x6e] = 0x253C,
[0x6f] = 0x23BA,
[0x70] = 0x23BB,
[0x71] = 0x2500,
[0x72] = 0x23BC,
[0x73] = 0x23BD,
[0x74] = 0x251C,
[0x75] = 0x2524,
[0x76] = 0x2534,
[0x77] = 0x252C,
[0x78] = 0x2502,
[0x79] = 0x2A7D,
[0x7a] = 0x2A7E,
[0x7b] = 0x03C0,
[0x7c] = 0x2260,
[0x7d] = 0x00A3,
[0x7e] = 0x00B7,
}
};

View File

@ -0,0 +1,6 @@
static const struct StaticTableEncoding encoding_uk = {
{ .decode = &decode_table },
{
[0x23] = 0x00a3,
}
};

View File

@ -0,0 +1,104 @@
{ 0x1100, 0x115f },
{ 0x231a, 0x231b },
{ 0x2329, 0x232a },
{ 0x23e9, 0x23ec },
{ 0x23f0, 0x23f0 },
{ 0x23f3, 0x23f3 },
{ 0x25fd, 0x25fe },
{ 0x2614, 0x2615 },
{ 0x2648, 0x2653 },
{ 0x267f, 0x267f },
{ 0x2693, 0x2693 },
{ 0x26a1, 0x26a1 },
{ 0x26aa, 0x26ab },
{ 0x26bd, 0x26be },
{ 0x26c4, 0x26c5 },
{ 0x26ce, 0x26ce },
{ 0x26d4, 0x26d4 },
{ 0x26ea, 0x26ea },
{ 0x26f2, 0x26f3 },
{ 0x26f5, 0x26f5 },
{ 0x26fa, 0x26fa },
{ 0x26fd, 0x26fd },
{ 0x2705, 0x2705 },
{ 0x270a, 0x270b },
{ 0x2728, 0x2728 },
{ 0x274c, 0x274c },
{ 0x274e, 0x274e },
{ 0x2753, 0x2755 },
{ 0x2757, 0x2757 },
{ 0x2795, 0x2797 },
{ 0x27b0, 0x27b0 },
{ 0x27bf, 0x27bf },
{ 0x2b1b, 0x2b1c },
{ 0x2b50, 0x2b50 },
{ 0x2b55, 0x2b55 },
{ 0x2e80, 0x2e99 },
{ 0x2e9b, 0x2ef3 },
{ 0x2f00, 0x2fd5 },
{ 0x2ff0, 0x2ffb },
{ 0x3000, 0x303e },
{ 0x3041, 0x3096 },
{ 0x3099, 0x30ff },
{ 0x3105, 0x312d },
{ 0x3131, 0x318e },
{ 0x3190, 0x31ba },
{ 0x31c0, 0x31e3 },
{ 0x31f0, 0x321e },
{ 0x3220, 0x3247 },
{ 0x3250, 0x32fe },
{ 0x3300, 0x4dbf },
{ 0x4e00, 0xa48c },
{ 0xa490, 0xa4c6 },
{ 0xa960, 0xa97c },
{ 0xac00, 0xd7a3 },
{ 0xf900, 0xfaff },
{ 0xfe10, 0xfe19 },
{ 0xfe30, 0xfe52 },
{ 0xfe54, 0xfe66 },
{ 0xfe68, 0xfe6b },
{ 0xff01, 0xff60 },
{ 0xffe0, 0xffe6 },
{ 0x16fe0, 0x16fe0 },
{ 0x17000, 0x187ec },
{ 0x18800, 0x18af2 },
{ 0x1b000, 0x1b001 },
{ 0x1f004, 0x1f004 },
{ 0x1f0cf, 0x1f0cf },
{ 0x1f18e, 0x1f18e },
{ 0x1f191, 0x1f19a },
{ 0x1f200, 0x1f202 },
{ 0x1f210, 0x1f23b },
{ 0x1f240, 0x1f248 },
{ 0x1f250, 0x1f251 },
{ 0x1f300, 0x1f320 },
{ 0x1f32d, 0x1f335 },
{ 0x1f337, 0x1f37c },
{ 0x1f37e, 0x1f393 },
{ 0x1f3a0, 0x1f3ca },
{ 0x1f3cf, 0x1f3d3 },
{ 0x1f3e0, 0x1f3f0 },
{ 0x1f3f4, 0x1f3f4 },
{ 0x1f3f8, 0x1f43e },
{ 0x1f440, 0x1f440 },
{ 0x1f442, 0x1f4fc },
{ 0x1f4ff, 0x1f53d },
{ 0x1f54b, 0x1f54e },
{ 0x1f550, 0x1f567 },
{ 0x1f57a, 0x1f57a },
{ 0x1f595, 0x1f596 },
{ 0x1f5a4, 0x1f5a4 },
{ 0x1f5fb, 0x1f64f },
{ 0x1f680, 0x1f6c5 },
{ 0x1f6cc, 0x1f6cc },
{ 0x1f6d0, 0x1f6d2 },
{ 0x1f6eb, 0x1f6ec },
{ 0x1f6f4, 0x1f6f6 },
{ 0x1f910, 0x1f91e },
{ 0x1f920, 0x1f927 },
{ 0x1f930, 0x1f930 },
{ 0x1f933, 0x1f93e },
{ 0x1f940, 0x1f94b },
{ 0x1f950, 0x1f95e },
{ 0x1f980, 0x1f991 },
{ 0x1f9c0, 0x1f9c0 },

228
extlib/libvterm/keyboard.c Normal file
View File

@ -0,0 +1,228 @@
#include "vterm_internal.h"
#include <stdio.h>
#include "utf8.h"
void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod) {
/* The shift modifier is never important for Unicode characters
* apart from Space
*/
if (c != ' ')
mod &= ~VTERM_MOD_SHIFT;
if (mod == 0) {
// Normal text - ignore just shift
char str[6];
int seqlen = fill_utf8(c, str);
vterm_push_output_bytes(vt, str, seqlen);
return;
}
int needs_CSIu;
switch (c) {
/* Special Ctrl- letters that can't be represented elsewise */
case 'i':
case 'j':
case 'm':
case '[':
needs_CSIu = 1;
break;
/* Ctrl-\ ] ^ _ don't need CSUu */
case '\\':
case ']':
case '^':
case '_':
needs_CSIu = 0;
break;
/* Shift-space needs CSIu */
case ' ':
needs_CSIu = !!(mod & VTERM_MOD_SHIFT);
break;
/* All other characters needs CSIu except for letters a-z */
default:
needs_CSIu = (c < 'a' || c > 'z');
}
/* ALT we can just prefix with ESC; anything else requires CSI u */
if (needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod + 1);
return;
}
if (mod & VTERM_MOD_CTRL)
c &= 0x1f;
vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
}
typedef struct {
enum {
KEYCODE_NONE,
KEYCODE_LITERAL,
KEYCODE_TAB,
KEYCODE_ENTER,
KEYCODE_SS3,
KEYCODE_CSI,
KEYCODE_CSI_CURSOR,
KEYCODE_CSINUM,
KEYCODE_KEYPAD,
} type;
char literal;
int csinum;
} keycodes_s;
static keycodes_s keycodes[] = {
{KEYCODE_NONE}, // NONE
{KEYCODE_ENTER, '\r'}, // ENTER
{KEYCODE_TAB, '\t'}, // TAB
{KEYCODE_LITERAL, '\x7f'}, // BACKSPACE == ASCII DEL
{KEYCODE_LITERAL, '\x1b'}, // ESCAPE
{KEYCODE_CSI_CURSOR, 'A'}, // UP
{KEYCODE_CSI_CURSOR, 'B'}, // DOWN
{KEYCODE_CSI_CURSOR, 'D'}, // LEFT
{KEYCODE_CSI_CURSOR, 'C'}, // RIGHT
{KEYCODE_CSINUM, '~', 2}, // INS
{KEYCODE_CSINUM, '~', 3}, // DEL
{KEYCODE_CSI_CURSOR, 'H'}, // HOME
{KEYCODE_CSI_CURSOR, 'F'}, // END
{KEYCODE_CSINUM, '~', 5}, // PAGEUP
{KEYCODE_CSINUM, '~', 6}, // PAGEDOWN
};
static keycodes_s keycodes_fn[] = {
{KEYCODE_NONE}, // F0 - shouldn't happen
{KEYCODE_SS3, 'P'}, // F1
{KEYCODE_SS3, 'Q'}, // F2
{KEYCODE_SS3, 'R'}, // F3
{KEYCODE_SS3, 'S'}, // F4
{KEYCODE_CSINUM, '~', 15}, // F5
{KEYCODE_CSINUM, '~', 17}, // F6
{KEYCODE_CSINUM, '~', 18}, // F7
{KEYCODE_CSINUM, '~', 19}, // F8
{KEYCODE_CSINUM, '~', 20}, // F9
{KEYCODE_CSINUM, '~', 21}, // F10
{KEYCODE_CSINUM, '~', 23}, // F11
{KEYCODE_CSINUM, '~', 24}, // F12
};
static keycodes_s keycodes_kp[] = {
{KEYCODE_KEYPAD, '0', 'p'}, // KP_0
{KEYCODE_KEYPAD, '1', 'q'}, // KP_1
{KEYCODE_KEYPAD, '2', 'r'}, // KP_2
{KEYCODE_KEYPAD, '3', 's'}, // KP_3
{KEYCODE_KEYPAD, '4', 't'}, // KP_4
{KEYCODE_KEYPAD, '5', 'u'}, // KP_5
{KEYCODE_KEYPAD, '6', 'v'}, // KP_6
{KEYCODE_KEYPAD, '7', 'w'}, // KP_7
{KEYCODE_KEYPAD, '8', 'x'}, // KP_8
{KEYCODE_KEYPAD, '9', 'y'}, // KP_9
{KEYCODE_KEYPAD, '*', 'j'}, // KP_MULT
{KEYCODE_KEYPAD, '+', 'k'}, // KP_PLUS
{KEYCODE_KEYPAD, ',', 'l'}, // KP_COMMA
{KEYCODE_KEYPAD, '-', 'm'}, // KP_MINUS
{KEYCODE_KEYPAD, '.', 'n'}, // KP_PERIOD
{KEYCODE_KEYPAD, '/', 'o'}, // KP_DIVIDE
{KEYCODE_KEYPAD, '\n', 'M'}, // KP_ENTER
{KEYCODE_KEYPAD, '=', 'X'}, // KP_EQUAL
};
void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod) {
if (key == VTERM_KEY_NONE)
return;
keycodes_s k;
if (key < VTERM_KEY_FUNCTION_0) {
if (key >= sizeof(keycodes) / sizeof(keycodes[0]))
return;
k = keycodes[key];
} else if (key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
if ((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn) / sizeof(keycodes_fn[0]))
return;
k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
} else if (key >= VTERM_KEY_KP_0) {
if ((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp) / sizeof(keycodes_kp[0]))
return;
k = keycodes_kp[key - VTERM_KEY_KP_0];
}
switch (k.type) {
case KEYCODE_NONE:
break;
case KEYCODE_TAB:
/* Shift-Tab is CSI Z but plain Tab is 0x09 */
if (mod == VTERM_MOD_SHIFT)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
else if (mod & VTERM_MOD_SHIFT)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod + 1);
else
goto case_LITERAL;
break;
case KEYCODE_ENTER:
/* Enter is CRLF in newline mode, but just LF in linefeed */
if (vt->state->mode.newline)
vterm_push_output_sprintf(vt, "\r\n");
else
goto case_LITERAL;
break;
case KEYCODE_LITERAL:
case_LITERAL:
if (mod & (VTERM_MOD_SHIFT | VTERM_MOD_CTRL))
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod + 1);
else
vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
break;
case KEYCODE_SS3:
case_SS3:
if (mod == 0)
vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
else
goto case_CSI;
break;
case KEYCODE_CSI:
case_CSI:
if (mod == 0)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
else
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
break;
case KEYCODE_CSINUM:
if (mod == 0)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
else
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
break;
case KEYCODE_CSI_CURSOR:
if (vt->state->mode.cursor)
goto case_SS3;
else
goto case_CSI;
case KEYCODE_KEYPAD:
if (vt->state->mode.keypad) {
k.literal = k.csinum;
goto case_SS3;
} else
goto case_LITERAL;
}
}
void vterm_keyboard_start_paste(VTerm *vt) {
if (vt->state->mode.bracketpaste)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~");
}
void vterm_keyboard_end_paste(VTerm *vt) {
if (vt->state->mode.bracketpaste)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~");
}

89
extlib/libvterm/mouse.c Normal file
View File

@ -0,0 +1,89 @@
#include "vterm_internal.h"
#include "utf8.h"
static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row) {
modifiers <<= 2;
switch (state->mouse_protocol) {
case MOUSE_X10:
if (col + 0x21 > 0xff)
col = 0xff - 0x21;
if (row + 0x21 > 0xff)
row = 0xff - 0x21;
if (!pressed)
code = 3;
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c", (code | modifiers) + 0x20, col + 0x21, row + 0x21);
break;
case MOUSE_UTF8: {
char utf8[18];
size_t len = 0;
if (!pressed)
code = 3;
len += fill_utf8((code | modifiers) + 0x20, utf8 + len);
len += fill_utf8(col + 0x21, utf8 + len);
len += fill_utf8(row + 0x21, utf8 + len);
utf8[len] = 0;
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8);
} break;
case MOUSE_SGR:
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c", code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm');
break;
case MOUSE_RXVT:
if (!pressed)
code = 3;
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM", code | modifiers, col + 1, row + 1);
break;
}
}
void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod) {
VTermState *state = vt->state;
if (col == state->mouse_col && row == state->mouse_row)
return;
state->mouse_col = col;
state->mouse_row = row;
if ((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons) ||
(state->mouse_flags & MOUSE_WANT_MOVE)) {
int button = state->mouse_buttons & 0x01 ? 1 :
state->mouse_buttons & 0x02 ? 2 :
state->mouse_buttons & 0x04 ? 3 :
4;
output_mouse(state, button - 1 + 0x20, 1, mod, col, row);
}
}
void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod) {
VTermState *state = vt->state;
int old_buttons = state->mouse_buttons;
if (button > 0 && button <= 3) {
if (pressed)
state->mouse_buttons |= (1 << (button - 1));
else
state->mouse_buttons &= ~(1 << (button - 1));
}
/* Most of the time we don't get button releases from 4/5 */
if (state->mouse_buttons == old_buttons && button < 4)
return;
if (button < 4) {
output_mouse(state, button - 1, pressed, mod, state->mouse_col, state->mouse_row);
} else if (button < 6) {
output_mouse(state, button - 4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row);
}
}

329
extlib/libvterm/parser.c Normal file
View File

@ -0,0 +1,329 @@
#include "vterm_internal.h"
#include <stdio.h>
#include <string.h>
#include "../../runtime/printf.h"
#undef DEBUG_PARSER
static bool is_intermed(unsigned char c) {
return c >= 0x20 && c <= 0x2f;
}
static void do_control(VTerm *vt, unsigned char control) {
if (vt->parser.callbacks && vt->parser.callbacks->control)
if ((*vt->parser.callbacks->control)(control, vt->parser.cbdata))
return;
DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control);
}
static void do_csi(VTerm *vt, char command) {
#ifdef DEBUG_PARSER
printf("Parsed CSI args as:\n", arglen, args);
printf(" leader: %s\n", vt->parser.csi_leader);
for (int argi = 0; argi < vt->parser.csi_argi; argi++) {
printf(" %lu", CSI_ARG(vt->parser.csi_args[argi]));
if (!CSI_ARG_HAS_MORE(vt->parser.csi_args[argi]))
printf("\n");
printf(" intermed: %s\n", vt->parser.intermed);
}
#endif
if (vt->parser.callbacks && vt->parser.callbacks->csi)
if ((*vt->parser.callbacks->csi)(
vt->parser.csi_leaderlen ? vt->parser.csi_leader : NULL,
vt->parser.csi_args,
vt->parser.csi_argi,
vt->parser.intermedlen ? vt->parser.intermed : NULL,
command,
vt->parser.cbdata))
return;
DEBUG_LOG("libvterm: Unhandled CSI %c\n", command);
}
static void do_escape(VTerm *vt, char command) {
char seq[INTERMED_MAX + 1];
size_t len = vt->parser.intermedlen;
strncpy(seq, vt->parser.intermed, len);
seq[len++] = command;
seq[len] = 0;
if (vt->parser.callbacks && vt->parser.callbacks->escape)
if ((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata))
return;
DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command);
}
static void append_strbuffer(VTerm *vt, const char *str, size_t len) {
if (len > vt->parser.strbuffer_len - vt->parser.strbuffer_cur) {
len = vt->parser.strbuffer_len - vt->parser.strbuffer_cur;
DEBUG_LOG("Truncating strbuffer preserve to %zd bytes\n", len);
}
if (len > 0) {
strncpy(vt->parser.strbuffer + vt->parser.strbuffer_cur, str, len);
vt->parser.strbuffer_cur += len;
}
}
static void start_string(VTerm *vt, VTermParserStringType type) {
vt->parser.stringtype = type;
vt->parser.strbuffer_cur = 0;
}
static void more_string(VTerm *vt, const char *str, size_t len) {
append_strbuffer(vt, str, len);
}
static void done_string(VTerm *vt, const char *str, size_t len) {
if (vt->parser.strbuffer_cur) {
if (str)
append_strbuffer(vt, str, len);
str = vt->parser.strbuffer;
len = vt->parser.strbuffer_cur;
} else if (!str) {
DEBUG_LOG("parser.c: TODO: No strbuffer _and_ no final fragment???\n");
len = 0;
}
switch (vt->parser.stringtype) {
case VTERM_PARSER_OSC:
if (vt->parser.callbacks && vt->parser.callbacks->osc)
if ((*vt->parser.callbacks->osc)(str, len, vt->parser.cbdata))
return;
DEBUG_LOG("libvterm: Unhandled OSC %.*s\n", (int)len, str);
return;
case VTERM_PARSER_DCS:
if (vt->parser.callbacks && vt->parser.callbacks->dcs)
if ((*vt->parser.callbacks->dcs)(str, len, vt->parser.cbdata))
return;
DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)len, str);
return;
case VTERM_N_PARSER_TYPES:
return;
}
}
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len) {
size_t pos = 0;
const char *string_start;
switch (vt->parser.state) {
case NORMAL:
case CSI_LEADER:
case CSI_ARGS:
case CSI_INTERMED:
case ESC:
string_start = NULL;
break;
case STRING:
case ESC_IN_STRING:
string_start = bytes;
break;
}
#define ENTER_STRING_STATE(st) \
do { \
vt->parser.state = STRING; \
string_start = bytes + pos + 1; \
} while (0)
#define ENTER_STATE(st) \
do { \
vt->parser.state = st; \
string_start = NULL; \
} while (0)
#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
for (; pos < len; pos++) {
unsigned char c = bytes[pos];
if (c == 0x00 || c == 0x7f) { // NUL, DEL
if (vt->parser.state >= STRING) {
more_string(vt, string_start, bytes + pos - string_start);
string_start = bytes + pos + 1;
}
continue;
}
if (c == 0x18 || c == 0x1a) { // CAN, SUB
ENTER_NORMAL_STATE();
continue;
} else if (c == 0x1b) { // ESC
vt->parser.intermedlen = 0;
if (vt->parser.state == STRING)
vt->parser.state = ESC_IN_STRING;
else
ENTER_STATE(ESC);
continue;
} else if (c == 0x07 && // BEL, can stand for ST in OSC or DCS state
vt->parser.state == STRING) {
// fallthrough
} else if (c < 0x20) { // other C0
if (vt->parser.state >= STRING)
more_string(vt, string_start, bytes + pos - string_start);
do_control(vt, c);
if (vt->parser.state >= STRING)
string_start = bytes + pos + 1;
continue;
}
// else fallthrough
switch (vt->parser.state) {
case ESC_IN_STRING:
if (c == 0x5c) { // ST
vt->parser.state = STRING;
done_string(vt, string_start, bytes + pos - string_start - 1);
ENTER_NORMAL_STATE();
break;
}
vt->parser.state = ESC;
// else fallthrough
case ESC:
switch (c) {
case 0x50: // DCS
start_string(vt, VTERM_PARSER_DCS);
ENTER_STRING_STATE();
break;
case 0x5b: // CSI
vt->parser.csi_leaderlen = 0;
ENTER_STATE(CSI_LEADER);
break;
case 0x5d: // OSC
start_string(vt, VTERM_PARSER_OSC);
ENTER_STRING_STATE();
break;
default:
if (is_intermed(c)) {
if (vt->parser.intermedlen < INTERMED_MAX - 1)
vt->parser.intermed[vt->parser.intermedlen++] = c;
} else if (!vt->parser.intermedlen && c >= 0x40 && c < 0x60) {
do_control(vt, c + 0x40);
ENTER_NORMAL_STATE();
} else if (c >= 0x30 && c < 0x7f) {
do_escape(vt, c);
ENTER_NORMAL_STATE();
} else {
DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
}
}
break;
case CSI_LEADER:
/* Extract leader bytes 0x3c to 0x3f */
if (c >= 0x3c && c <= 0x3f) {
if (vt->parser.csi_leaderlen < CSI_LEADER_MAX - 1)
vt->parser.csi_leader[vt->parser.csi_leaderlen++] = c;
break;
}
/* else fallthrough */
vt->parser.csi_leader[vt->parser.csi_leaderlen] = 0;
vt->parser.csi_argi = 0;
vt->parser.csi_args[0] = CSI_ARG_MISSING;
vt->parser.state = CSI_ARGS;
/* fallthrough */
case CSI_ARGS:
/* Numerical value of argument */
if (c >= '0' && c <= '9') {
if (vt->parser.csi_args[vt->parser.csi_argi] == CSI_ARG_MISSING)
vt->parser.csi_args[vt->parser.csi_argi] = 0;
vt->parser.csi_args[vt->parser.csi_argi] *= 10;
vt->parser.csi_args[vt->parser.csi_argi] += c - '0';
break;
}
if (c == ':') {
vt->parser.csi_args[vt->parser.csi_argi] |= CSI_ARG_FLAG_MORE;
c = ';';
}
if (c == ';') {
vt->parser.csi_argi++;
vt->parser.csi_args[vt->parser.csi_argi] = CSI_ARG_MISSING;
break;
}
/* else fallthrough */
vt->parser.csi_argi++;
vt->parser.intermedlen = 0;
vt->parser.state = CSI_INTERMED;
case CSI_INTERMED:
if (is_intermed(c)) {
if (vt->parser.intermedlen < INTERMED_MAX - 1)
vt->parser.intermed[vt->parser.intermedlen++] = c;
break;
} else if (c == 0x1b) {
/* ESC in CSI cancels */
} else if (c >= 0x40 && c <= 0x7e) {
vt->parser.intermed[vt->parser.intermedlen] = 0;
do_csi(vt, c);
}
/* else was invalid CSI */
ENTER_NORMAL_STATE();
break;
case STRING:
if (c == 0x07 || (c == 0x9c && !vt->mode.utf8)) {
done_string(vt, string_start, bytes + pos - string_start);
ENTER_NORMAL_STATE();
}
break;
case NORMAL:
if (c >= 0x80 && c < 0xa0 && !vt->mode.utf8) {
switch (c) {
case 0x90: // DCS
start_string(vt, VTERM_PARSER_DCS);
ENTER_STRING_STATE();
break;
case 0x9b: // CSI
ENTER_STATE(CSI_LEADER);
break;
case 0x9d: // OSC
start_string(vt, VTERM_PARSER_OSC);
ENTER_STRING_STATE();
break;
default:
do_control(vt, c);
break;
}
} else {
size_t eaten = 0;
if (vt->parser.callbacks && vt->parser.callbacks->text)
eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
if (!eaten) {
DEBUG_LOG("libvterm: Text callback did not consume any input\n");
/* force it to make progress */
eaten = 1;
}
pos += (eaten - 1); // we'll ++ it again in a moment
}
break;
}
}
return len;
}
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user) {
vt->parser.callbacks = callbacks;
vt->parser.cbdata = user;
}
void *vterm_parser_get_cbdata(VTerm *vt) {
return vt->parser.cbdata;
}

586
extlib/libvterm/pen.c Normal file
View File

@ -0,0 +1,586 @@
#include "vterm_internal.h"
#include <stdio.h>
/**
* Structure used to store RGB triples without the additional metadata stored in
* VTermColor.
*/
typedef struct {
uint8_t red, green, blue;
} VTermRGB;
static const VTermRGB ansi_colors[] = {
/* R G B */
{0, 0, 0}, // black
{224, 0, 0}, // red
{0, 224, 0}, // green
{224, 224, 0}, // yellow
{0, 0, 224}, // blue
{224, 0, 224}, // magenta
{0, 224, 224}, // cyan
{224, 224, 224}, // white == light grey
// high intensity
{128, 128, 128}, // black
{255, 64, 64}, // red
{64, 255, 64}, // green
{255, 255, 64}, // yellow
{64, 64, 255}, // blue
{255, 64, 255}, // magenta
{64, 255, 255}, // cyan
{255, 255, 255}, // white for real
};
static int ramp6[] = {
0x00,
0x33,
0x66,
0x99,
0xCC,
0xFF,
};
static int ramp24[] = {
0x00,
0x0B,
0x16,
0x21,
0x2C,
0x37,
0x42,
0x4D,
0x58,
0x63,
0x6E,
0x79,
0x85,
0x90,
0x9B,
0xA6,
0xB1,
0xBC,
0xC7,
0xD2,
0xDD,
0xE8,
0xF3,
0xFF,
};
static void lookup_default_colour_ansi(long idx, VTermColor *col) {
if (idx >= 0 && idx < 16) {
vterm_color_rgb(
col,
ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
}
}
static bool lookup_colour_ansi(const VTermState *state, long index, VTermColor *col) {
if (index >= 0 && index < 16) {
*col = state->colors[index];
return true;
}
return false;
}
static bool lookup_colour_palette(const VTermState *state, long index, VTermColor *col) {
if (index >= 0 && index < 16) {
// Normal 8 colours or high intensity - parse as palette 0
return lookup_colour_ansi(state, index, col);
} else if (index >= 16 && index < 232) {
// 216-colour cube
index -= 16;
vterm_color_rgb(col, ramp6[index / 6 / 6 % 6], ramp6[index / 6 % 6], ramp6[index % 6]);
return true;
} else if (index >= 232 && index < 256) {
// 24 greyscales
index -= 232;
vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
return true;
}
return false;
}
static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col) {
switch (palette) {
case 2: // RGB mode - 3 args contain colour values directly
if (argcount < 3)
return argcount;
vterm_color_rgb(col, CSI_ARG(args[0]), CSI_ARG(args[1]), CSI_ARG(args[2]));
return 3;
case 5: // XTerm 256-colour mode
if (!argcount || CSI_ARG_IS_MISSING(args[0])) {
return argcount ? 1 : 0;
}
vterm_color_indexed(col, args[0]);
return argcount ? 1 : 0;
default:
DEBUG_LOG("Unrecognised colour palette %d\n", palette);
return 0;
}
}
// Some conveniences
static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val) {
#ifdef DEBUG
if (type != vterm_get_attr_type(attr)) {
DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n", attr, vterm_get_attr_type(attr), type);
return;
}
#endif
if (state->callbacks && state->callbacks->setpenattr)
(*state->callbacks->setpenattr)(attr, val, state->cbdata);
}
static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean) {
VTermValue val = {.boolean = boolean};
setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
}
static void setpenattr_int(VTermState *state, VTermAttr attr, int number) {
VTermValue val = {.number = number};
setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
}
static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color) {
VTermValue val = {.color = color};
setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
}
static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col) {
VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
vterm_color_indexed(colp, col);
setpenattr_col(state, attr, *colp);
}
INTERNAL void vterm_state_newpen(VTermState *state) {
// 90% grey so that pure white is brighter
vterm_color_rgb(&state->default_fg, 240, 240, 240);
vterm_color_rgb(&state->default_bg, 0, 0, 0);
vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
for (int col = 0; col < 16; col++)
lookup_default_colour_ansi(col, &state->colors[col]);
}
INTERNAL void vterm_state_resetpen(VTermState *state) {
state->pen.bold = 0;
setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
state->pen.underline = 0;
setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
state->pen.italic = 0;
setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
state->pen.blink = 0;
setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
state->pen.reverse = 0;
setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
state->pen.strike = 0;
setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
state->pen.font = 0;
setpenattr_int(state, VTERM_ATTR_FONT, 0);
state->pen.fg = state->default_fg;
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
state->pen.bg = state->default_bg;
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
}
INTERNAL void vterm_state_savepen(VTermState *state, int save) {
if (save) {
state->saved.pen = state->pen;
} else {
state->pen = state->saved.pen;
setpenattr_bool(state, VTERM_ATTR_BOLD, state->pen.bold);
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
}
}
int vterm_color_is_equal(const VTermColor *a, const VTermColor *b) {
/* First make sure that the two colours are of the same type (RGB/Indexed) */
if (a->type != b->type) {
return false;
}
/* Depending on the type inspect the corresponding members */
if (VTERM_COLOR_IS_INDEXED(a)) {
return a->indexed.idx == b->indexed.idx;
} else if (VTERM_COLOR_IS_RGB(a)) {
return (a->rgb.red == b->rgb.red) && (a->rgb.green == b->rgb.green) && (a->rgb.blue == b->rgb.blue);
}
return 0;
}
void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg) {
*default_fg = state->default_fg;
*default_bg = state->default_bg;
}
void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col) {
lookup_colour_palette(state, index, col);
}
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg) {
/* Copy the given colors */
state->default_fg = *default_fg;
state->default_bg = *default_bg;
/* Make sure the correct type flags are set */
state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK) | VTERM_COLOR_DEFAULT_FG;
state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK) | VTERM_COLOR_DEFAULT_BG;
}
void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col) {
if (index >= 0 && index < 16)
state->colors[index] = *col;
}
void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col) {
if (VTERM_COLOR_IS_INDEXED(col)) { /* Convert indexed colors to RGB */
lookup_colour_palette(state, col->indexed.idx, col);
}
col->type &= VTERM_COLOR_TYPE_MASK; /* Reset any metadata but the type */
}
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright) {
state->bold_is_highbright = bold_is_highbright;
}
INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount) {
// SGR - ECMA-48 8.3.117
int argi = 0;
int value;
while (argi < argcount) {
// This logic is easier to do 'done' backwards; set it true, and make it
// false again in the 'default' case
int done = 1;
long arg;
switch (arg = CSI_ARG(args[argi])) {
case CSI_ARG_MISSING:
case 0: // Reset
vterm_state_resetpen(state);
break;
case 1: { // Bold on
const VTermColor *fg = &state->pen.fg;
state->pen.bold = 1;
setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
if (!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8 && state->bold_is_highbright)
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
break;
}
case 3: // Italic on
state->pen.italic = 1;
setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
break;
case 4: // Underline
state->pen.underline = VTERM_UNDERLINE_SINGLE;
if (CSI_ARG_HAS_MORE(args[argi])) {
argi++;
switch (CSI_ARG(args[argi])) {
case 0:
state->pen.underline = 0;
break;
case 1:
state->pen.underline = VTERM_UNDERLINE_SINGLE;
break;
case 2:
state->pen.underline = VTERM_UNDERLINE_DOUBLE;
break;
case 3:
state->pen.underline = VTERM_UNDERLINE_CURLY;
break;
}
}
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
break;
case 5: // Blink
state->pen.blink = 1;
setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
break;
case 7: // Reverse on
state->pen.reverse = 1;
setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
break;
case 9: // Strikethrough on
state->pen.strike = 1;
setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
break;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19: // Select font
state->pen.font = CSI_ARG(args[argi]) - 10;
setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
break;
case 21: // Underline double
state->pen.underline = VTERM_UNDERLINE_DOUBLE;
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
break;
case 22: // Bold off
state->pen.bold = 0;
setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
break;
case 23: // Italic and Gothic (currently unsupported) off
state->pen.italic = 0;
setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
break;
case 24: // Underline off
state->pen.underline = 0;
setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
break;
case 25: // Blink off
state->pen.blink = 0;
setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
break;
case 27: // Reverse off
state->pen.reverse = 0;
setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
break;
case 29: // Strikethrough off
state->pen.strike = 0;
setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
break;
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37: // Foreground colour palette
value = CSI_ARG(args[argi]) - 30;
if (state->pen.bold && state->bold_is_highbright)
value += 8;
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
break;
case 38: // Foreground colour alternative palette
if (argcount - argi < 1)
return;
argi += 1 + lookup_colour(state, CSI_ARG(args[argi + 1]), args + argi + 2, argcount - argi - 2, &state->pen.fg);
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
break;
case 39: // Foreground colour default
state->pen.fg = state->default_fg;
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
break;
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47: // Background colour palette
value = CSI_ARG(args[argi]) - 40;
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
break;
case 48: // Background colour alternative palette
if (argcount - argi < 1)
return;
argi += 1 + lookup_colour(state, CSI_ARG(args[argi + 1]), args + argi + 2, argcount - argi - 2, &state->pen.bg);
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
break;
case 49: // Default background
state->pen.bg = state->default_bg;
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
break;
case 90:
case 91:
case 92:
case 93:
case 94:
case 95:
case 96:
case 97: // Foreground colour high-intensity palette
value = CSI_ARG(args[argi]) - 90 + 8;
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
break;
case 100:
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107: // Background colour high-intensity palette
value = CSI_ARG(args[argi]) - 100 + 8;
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
break;
default:
done = 0;
break;
}
if (!done)
DEBUG_LOG("libvterm: Unhandled CSI SGR %lu\n", arg);
while (CSI_ARG_HAS_MORE(args[argi++]))
;
}
}
static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg) {
/* Do nothing if the given color is the default color */
if ((fg && VTERM_COLOR_IS_DEFAULT_FG(col)) ||
(!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
return argi;
}
/* Decide whether to send an indexed color or an RGB color */
if (VTERM_COLOR_IS_INDEXED(col)) {
const uint8_t idx = col->indexed.idx;
if (idx < 8) {
args[argi++] = (idx + (fg ? 30 : 40));
} else if (idx < 16) {
args[argi++] = (idx - 8 + (fg ? 90 : 100));
} else {
args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
args[argi++] = CSI_ARG_FLAG_MORE | 5;
args[argi++] = idx;
}
} else if (VTERM_COLOR_IS_RGB(col)) {
args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
args[argi++] = CSI_ARG_FLAG_MORE | 2;
args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
args[argi++] = col->rgb.blue;
}
return argi;
}
INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount) {
int argi = 0;
if (state->pen.bold)
args[argi++] = 1;
if (state->pen.italic)
args[argi++] = 3;
if (state->pen.underline == VTERM_UNDERLINE_SINGLE)
args[argi++] = 4;
if (state->pen.underline == VTERM_UNDERLINE_CURLY)
args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
if (state->pen.blink)
args[argi++] = 5;
if (state->pen.reverse)
args[argi++] = 7;
if (state->pen.strike)
args[argi++] = 9;
if (state->pen.font)
args[argi++] = 10 + state->pen.font;
if (state->pen.underline == VTERM_UNDERLINE_DOUBLE)
args[argi++] = 21;
argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false);
return argi;
}
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val) {
switch (attr) {
case VTERM_ATTR_BOLD:
val->boolean = state->pen.bold;
return 1;
case VTERM_ATTR_UNDERLINE:
val->number = state->pen.underline;
return 1;
case VTERM_ATTR_ITALIC:
val->boolean = state->pen.italic;
return 1;
case VTERM_ATTR_BLINK:
val->boolean = state->pen.blink;
return 1;
case VTERM_ATTR_REVERSE:
val->boolean = state->pen.reverse;
return 1;
case VTERM_ATTR_STRIKE:
val->boolean = state->pen.strike;
return 1;
case VTERM_ATTR_FONT:
val->number = state->pen.font;
return 1;
case VTERM_ATTR_FOREGROUND:
val->color = state->pen.fg;
return 1;
case VTERM_ATTR_BACKGROUND:
val->color = state->pen.bg;
return 1;
case VTERM_N_ATTRS:
return 0;
}
return 0;
}

56
extlib/libvterm/rect.h Normal file
View File

@ -0,0 +1,56 @@
/*
* Some utility functions on VTermRect structures
*/
#define STRFrect "(%d,%d-%d,%d)"
#define ARGSrect(r) (r).start_row, (r).start_col, (r).end_row, (r).end_col
/* Expand dst to contain src as well */
static void rect_expand(VTermRect *dst, VTermRect *src)
{
if(dst->start_row > src->start_row) dst->start_row = src->start_row;
if(dst->start_col > src->start_col) dst->start_col = src->start_col;
if(dst->end_row < src->end_row) dst->end_row = src->end_row;
if(dst->end_col < src->end_col) dst->end_col = src->end_col;
}
/* Clip the dst to ensure it does not step outside of bounds */
static void rect_clip(VTermRect *dst, VTermRect *bounds)
{
if(dst->start_row < bounds->start_row) dst->start_row = bounds->start_row;
if(dst->start_col < bounds->start_col) dst->start_col = bounds->start_col;
if(dst->end_row > bounds->end_row) dst->end_row = bounds->end_row;
if(dst->end_col > bounds->end_col) dst->end_col = bounds->end_col;
/* Ensure it doesn't end up negatively-sized */
if(dst->end_row < dst->start_row) dst->end_row = dst->start_row;
if(dst->end_col < dst->start_col) dst->end_col = dst->start_col;
}
/* True if the two rectangles are equal */
static int rect_equal(VTermRect *a, VTermRect *b)
{
return (a->start_row == b->start_row) &&
(a->start_col == b->start_col) &&
(a->end_row == b->end_row) &&
(a->end_col == b->end_col);
}
/* True if small is contained entirely within big */
static int rect_contains(VTermRect *big, VTermRect *small)
{
if(small->start_row < big->start_row) return 0;
if(small->start_col < big->start_col) return 0;
if(small->end_row > big->end_row) return 0;
if(small->end_col > big->end_col) return 0;
return 1;
}
/* True if the rectangles overlap at all */
static int rect_intersects(VTermRect *a, VTermRect *b)
{
if(a->start_row > b->end_row || b->start_row > a->end_row)
return 0;
if(a->start_col > b->end_col || b->start_col > a->end_col)
return 0;
return 1;
}

877
extlib/libvterm/screen.c Normal file
View File

@ -0,0 +1,877 @@
#include "vterm_internal.h"
#include <stdio.h>
#include <string.h>
#include "rect.h"
#include "utf8.h"
#define UNICODE_SPACE 0x20
#define UNICODE_LINEFEED 0x0a
/* State of the pen at some moment in time, also used in a cell */
typedef struct
{
/* After the bitfield */
VTermColor fg, bg;
unsigned int bold : 1;
unsigned int underline : 2;
unsigned int italic : 1;
unsigned int blink : 1;
unsigned int reverse : 1;
unsigned int strike : 1;
unsigned int font : 4; /* 0 to 9 */
/* Extra state storage that isn't strictly pen-related */
unsigned int protected_cell : 1;
unsigned int dwl : 1; /* on a DECDWL or DECDHL line */
unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */
} ScreenPen;
/* Internal representation of a screen cell */
typedef struct
{
uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
ScreenPen pen;
} ScreenCell;
static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell);
struct VTermScreen {
VTerm * vt;
VTermState *state;
const VTermScreenCallbacks *callbacks;
void * cbdata;
VTermDamageSize damage_merge;
/* start_row == -1 => no damage */
VTermRect damaged;
VTermRect pending_scrollrect;
int pending_scroll_downward, pending_scroll_rightward;
int rows;
int cols;
int global_reverse;
/* Primary and Altscreen. buffers[1] is lazily allocated as needed */
ScreenCell *buffers[2];
/* buffer will == buffers[0] or buffers[1], depending on altscreen */
ScreenCell *buffer;
/* buffer for a single screen row used in scrollback storage callbacks */
VTermScreenCell *sb_buffer;
ScreenPen pen;
};
static inline ScreenCell *getcell(const VTermScreen *screen, int row, int col) {
if (row < 0 || row >= screen->rows)
return NULL;
if (col < 0 || col >= screen->cols)
return NULL;
return screen->buffer + (screen->cols * row) + col;
}
static ScreenCell *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int new_rows, int new_cols) {
ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols);
for (int row = 0; row < new_rows; row++) {
for (int col = 0; col < new_cols; col++) {
ScreenCell *new_cell = new_buffer + row * new_cols + col;
if (buffer && row < screen->rows && col < screen->cols)
*new_cell = buffer[row * screen->cols + col];
else {
new_cell->chars[0] = 0;
new_cell->pen = screen->pen;
}
}
}
if (buffer)
vterm_allocator_free(screen->vt, buffer);
return new_buffer;
}
static void damagerect(VTermScreen *screen, VTermRect rect) {
VTermRect emit;
switch (screen->damage_merge) {
case VTERM_DAMAGE_CELL:
/* Always emit damage event */
emit = rect;
break;
case VTERM_DAMAGE_ROW:
/* Emit damage longer than one row. Try to merge with existing damage in
* the same row */
if (rect.end_row > rect.start_row + 1) {
// Bigger than 1 line - flush existing, emit this
vterm_screen_flush_damage(screen);
emit = rect;
} else if (screen->damaged.start_row == -1) {
// None stored yet
screen->damaged = rect;
return;
} else if (rect.start_row == screen->damaged.start_row) {
// Merge with the stored line
if (screen->damaged.start_col > rect.start_col)
screen->damaged.start_col = rect.start_col;
if (screen->damaged.end_col < rect.end_col)
screen->damaged.end_col = rect.end_col;
return;
} else {
// Emit the currently stored line, store a new one
emit = screen->damaged;
screen->damaged = rect;
}
break;
case VTERM_DAMAGE_SCREEN:
case VTERM_DAMAGE_SCROLL:
/* Never emit damage event */
if (screen->damaged.start_row == -1)
screen->damaged = rect;
else {
rect_expand(&screen->damaged, &rect);
}
return;
default:
DEBUG_LOG("TODO: Maybe merge damage for level %d\n", screen->damage_merge);
return;
}
if (screen->callbacks && screen->callbacks->damage)
(*screen->callbacks->damage)(emit, screen->cbdata);
}
static void damagescreen(VTermScreen *screen) {
VTermRect rect = {
.start_row = 0,
.end_row = screen->rows,
.start_col = 0,
.end_col = screen->cols,
};
damagerect(screen, rect);
}
static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) {
VTermScreen *screen = user;
ScreenCell * cell = getcell(screen, pos.row, pos.col);
if (!cell)
return 0;
int i;
for (i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) {
cell->chars[i] = info->chars[i];
cell->pen = screen->pen;
}
if (i < VTERM_MAX_CHARS_PER_CELL)
cell->chars[i] = 0;
for (int col = 1; col < info->width; col++)
getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1;
VTermRect rect = {
.start_row = pos.row,
.end_row = pos.row + 1,
.start_col = pos.col,
.end_col = pos.col + info->width,
};
cell->pen.protected_cell = info->protected_cell;
cell->pen.dwl = info->dwl;
cell->pen.dhl = info->dhl;
damagerect(screen, rect);
return 1;
}
static int moverect_internal(VTermRect dest, VTermRect src, void *user) {
VTermScreen *screen = user;
if (screen->callbacks && screen->callbacks->sb_pushline &&
dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner
dest.end_col == screen->cols && // full width
screen->buffer == screen->buffers[0]) { // not altscreen
VTermPos pos;
for (pos.row = 0; pos.row < src.start_row; pos.row++) {
for (pos.col = 0; pos.col < screen->cols; pos.col++)
vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
(screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
}
}
int cols = src.end_col - src.start_col;
int downward = src.start_row - dest.start_row;
int init_row, test_row, inc_row;
if (downward < 0) {
init_row = dest.end_row - 1;
test_row = dest.start_row - 1;
inc_row = -1;
} else {
init_row = dest.start_row;
test_row = dest.end_row;
inc_row = +1;
}
for (int row = init_row; row != test_row; row += inc_row)
memmove(getcell(screen, row, dest.start_col), getcell(screen, row + downward, src.start_col), cols * sizeof(ScreenCell));
return 1;
}
static int moverect_user(VTermRect dest, VTermRect src, void *user) {
VTermScreen *screen = user;
if (screen->callbacks && screen->callbacks->moverect) {
if (screen->damage_merge != VTERM_DAMAGE_SCROLL)
// Avoid an infinite loop
vterm_screen_flush_damage(screen);
if ((*screen->callbacks->moverect)(dest, src, screen->cbdata))
return 1;
}
damagerect(screen, dest);
return 1;
}
static int erase_internal(VTermRect rect, int selective, void *user) {
VTermScreen *screen = user;
for (int row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) {
const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row);
for (int col = rect.start_col; col < rect.end_col; col++) {
ScreenCell *cell = getcell(screen, row, col);
if (selective && cell->pen.protected_cell)
continue;
cell->chars[0] = 0;
cell->pen = screen->pen;
cell->pen.dwl = info->doublewidth;
cell->pen.dhl = info->doubleheight;
}
}
return 1;
}
static int erase_user(VTermRect rect, int selective, void *user) {
VTermScreen *screen = user;
damagerect(screen, rect);
return 1;
}
static int erase(VTermRect rect, int selective, void *user) {
erase_internal(rect, selective, user);
return erase_user(rect, 0, user);
}
static int scrollrect(VTermRect rect, int downward, int rightward, void *user) {
VTermScreen *screen = user;
if (screen->damage_merge != VTERM_DAMAGE_SCROLL) {
vterm_scroll_rect(rect, downward, rightward, moverect_internal, erase_internal, screen);
vterm_screen_flush_damage(screen);
vterm_scroll_rect(rect, downward, rightward, moverect_user, erase_user, screen);
return 1;
}
if (screen->damaged.start_row != -1 &&
!rect_intersects(&rect, &screen->damaged)) {
vterm_screen_flush_damage(screen);
}
if (screen->pending_scrollrect.start_row == -1) {
screen->pending_scrollrect = rect;
screen->pending_scroll_downward = downward;
screen->pending_scroll_rightward = rightward;
} else if (rect_equal(&screen->pending_scrollrect, &rect) && ((screen->pending_scroll_downward == 0 && downward == 0) || (screen->pending_scroll_rightward == 0 && rightward == 0))) {
screen->pending_scroll_downward += downward;
screen->pending_scroll_rightward += rightward;
} else {
vterm_screen_flush_damage(screen);
screen->pending_scrollrect = rect;
screen->pending_scroll_downward = downward;
screen->pending_scroll_rightward = rightward;
}
vterm_scroll_rect(rect, downward, rightward, moverect_internal, erase_internal, screen);
if (screen->damaged.start_row == -1)
return 1;
if (rect_contains(&rect, &screen->damaged)) {
/* Scroll region entirely contains the damage; just move it */
vterm_rect_move(&screen->damaged, -downward, -rightward);
rect_clip(&screen->damaged, &rect);
}
/* There are a number of possible cases here, but lets restrict this to only
* the common case where we might actually gain some performance by
* optimising it. Namely, a vertical scroll that neatly cuts the damage
* region in half.
*/
else if (rect.start_col <= screen->damaged.start_col && rect.end_col >= screen->damaged.end_col && rightward == 0) {
if (screen->damaged.start_row >= rect.start_row &&
screen->damaged.start_row < rect.end_row) {
screen->damaged.start_row -= downward;
if (screen->damaged.start_row < rect.start_row)
screen->damaged.start_row = rect.start_row;
if (screen->damaged.start_row > rect.end_row)
screen->damaged.start_row = rect.end_row;
}
if (screen->damaged.end_row >= rect.start_row &&
screen->damaged.end_row < rect.end_row) {
screen->damaged.end_row -= downward;
if (screen->damaged.end_row < rect.start_row)
screen->damaged.end_row = rect.start_row;
if (screen->damaged.end_row > rect.end_row)
screen->damaged.end_row = rect.end_row;
}
} else {
DEBUG_LOG("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n", ARGSrect(screen->damaged), ARGSrect(rect));
}
return 1;
}
static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) {
VTermScreen *screen = user;
if (screen->callbacks && screen->callbacks->movecursor)
return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata);
return 0;
}
static int setpenattr(VTermAttr attr, VTermValue *val, void *user) {
VTermScreen *screen = user;
switch (attr) {
case VTERM_ATTR_BOLD:
screen->pen.bold = val->boolean;
return 1;
case VTERM_ATTR_UNDERLINE:
screen->pen.underline = val->number;
return 1;
case VTERM_ATTR_ITALIC:
screen->pen.italic = val->boolean;
return 1;
case VTERM_ATTR_BLINK:
screen->pen.blink = val->boolean;
return 1;
case VTERM_ATTR_REVERSE:
screen->pen.reverse = val->boolean;
return 1;
case VTERM_ATTR_STRIKE:
screen->pen.strike = val->boolean;
return 1;
case VTERM_ATTR_FONT:
screen->pen.font = val->number;
return 1;
case VTERM_ATTR_FOREGROUND:
screen->pen.fg = val->color;
return 1;
case VTERM_ATTR_BACKGROUND:
screen->pen.bg = val->color;
return 1;
case VTERM_N_ATTRS:
return 0;
}
return 0;
}
static int settermprop(VTermProp prop, VTermValue *val, void *user) {
VTermScreen *screen = user;
switch (prop) {
case VTERM_PROP_ALTSCREEN:
if (val->boolean && !screen->buffers[1])
return 0;
screen->buffer = val->boolean ? screen->buffers[1] : screen->buffers[0];
/* only send a damage event on disable; because during enable there's an
* erase that sends a damage anyway
*/
if (!val->boolean)
damagescreen(screen);
break;
case VTERM_PROP_REVERSE:
screen->global_reverse = val->boolean;
damagescreen(screen);
break;
default:; /* ignore */
}
if (screen->callbacks && screen->callbacks->settermprop)
return (*screen->callbacks->settermprop)(prop, val, screen->cbdata);
return 1;
}
static int bell(void *user) {
VTermScreen *screen = user;
if (screen->callbacks && screen->callbacks->bell)
return (*screen->callbacks->bell)(screen->cbdata);
return 0;
}
static int resize(int new_rows, int new_cols, VTermPos *delta, void *user) {
VTermScreen *screen = user;
int is_altscreen = (screen->buffers[1] && screen->buffer == screen->buffers[1]);
int old_rows = screen->rows;
int old_cols = screen->cols;
if (!is_altscreen && new_rows < old_rows) {
// Fewer rows - determine if we're going to scroll at all, and if so, push
// those lines to scrollback
VTermPos pos = {0, 0};
VTermPos cursor = screen->state->pos;
// Find the first blank row after the cursor.
for (pos.row = old_rows - 1; pos.row >= new_rows; pos.row--)
if (!vterm_screen_is_eol(screen, pos) || cursor.row == pos.row)
break;
int first_blank_row = pos.row + 1;
if (first_blank_row > new_rows) {
VTermRect rect = {
.start_row = 0,
.end_row = old_rows,
.start_col = 0,
.end_col = old_cols,
};
scrollrect(rect, first_blank_row - new_rows, 0, user);
vterm_screen_flush_damage(screen);
delta->row -= first_blank_row - new_rows;
}
}
screen->buffers[0] = realloc_buffer(screen, screen->buffers[0], new_rows, new_cols);
if (screen->buffers[1])
screen->buffers[1] = realloc_buffer(screen, screen->buffers[1], new_rows, new_cols);
screen->buffer = is_altscreen ? screen->buffers[1] : screen->buffers[0];
screen->rows = new_rows;
screen->cols = new_cols;
if (screen->sb_buffer)
vterm_allocator_free(screen->vt, screen->sb_buffer);
screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols);
if (new_cols > old_cols) {
VTermRect rect = {
.start_row = 0,
.end_row = old_rows,
.start_col = old_cols,
.end_col = new_cols,
};
damagerect(screen, rect);
}
if (new_rows > old_rows) {
if (!is_altscreen && screen->callbacks && screen->callbacks->sb_popline) {
int rows = new_rows - old_rows;
while (rows) {
if (!(screen->callbacks->sb_popline(screen->cols, screen->sb_buffer, screen->cbdata)))
break;
VTermRect rect = {
.start_row = 0,
.end_row = screen->rows,
.start_col = 0,
.end_col = screen->cols,
};
scrollrect(rect, -1, 0, user);
VTermPos pos = {0, 0};
for (pos.col = 0; pos.col < screen->cols; pos.col += screen->sb_buffer[pos.col].width)
vterm_screen_set_cell(screen, pos, screen->sb_buffer + pos.col);
rect.end_row = 1;
damagerect(screen, rect);
vterm_screen_flush_damage(screen);
rows--;
delta->row++;
}
}
VTermRect rect = {
.start_row = old_rows,
.end_row = new_rows,
.start_col = 0,
.end_col = new_cols,
};
damagerect(screen, rect);
}
if (screen->callbacks && screen->callbacks->resize)
return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata);
return 1;
}
static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user) {
VTermScreen *screen = user;
if (newinfo->doublewidth != oldinfo->doublewidth ||
newinfo->doubleheight != oldinfo->doubleheight) {
for (int col = 0; col < screen->cols; col++) {
ScreenCell *cell = getcell(screen, row, col);
cell->pen.dwl = newinfo->doublewidth;
cell->pen.dhl = newinfo->doubleheight;
}
VTermRect rect = {
.start_row = row,
.end_row = row + 1,
.start_col = 0,
.end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols,
};
damagerect(screen, rect);
if (newinfo->doublewidth) {
rect.start_col = screen->cols / 2;
rect.end_col = screen->cols;
erase_internal(rect, 0, user);
}
}
return 1;
}
static VTermStateCallbacks state_cbs = {
.putglyph = &putglyph,
.movecursor = &movecursor,
.scrollrect = &scrollrect,
.erase = &erase,
.setpenattr = &setpenattr,
.settermprop = &settermprop,
.bell = &bell,
.resize = &resize,
.setlineinfo = &setlineinfo,
};
static VTermScreen *screen_new(VTerm *vt) {
VTermState *state = vterm_obtain_state(vt);
if (!state)
return NULL;
VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen));
int rows, cols;
vterm_get_size(vt, &rows, &cols);
screen->vt = vt;
screen->state = state;
screen->damage_merge = VTERM_DAMAGE_CELL;
screen->damaged.start_row = -1;
screen->pending_scrollrect.start_row = -1;
screen->rows = rows;
screen->cols = cols;
screen->callbacks = NULL;
screen->cbdata = NULL;
screen->buffers[0] = realloc_buffer(screen, NULL, rows, cols);
screen->buffer = screen->buffers[0];
screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols);
vterm_state_set_callbacks(screen->state, &state_cbs, screen);
return screen;
}
INTERNAL void vterm_screen_free(VTermScreen *screen) {
vterm_allocator_free(screen->vt, screen->buffers[0]);
if (screen->buffers[1])
vterm_allocator_free(screen->vt, screen->buffers[1]);
vterm_allocator_free(screen->vt, screen->sb_buffer);
vterm_allocator_free(screen->vt, screen);
}
void vterm_screen_reset(VTermScreen *screen, int hard) {
screen->damaged.start_row = -1;
screen->pending_scrollrect.start_row = -1;
vterm_state_reset(screen->state, hard);
vterm_screen_flush_damage(screen);
}
static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect) {
size_t outpos = 0;
int padding = 0;
#define PUT(c) \
if (utf8) { \
size_t thislen = utf8_seqlen(c); \
if (buffer && outpos + thislen <= len) \
outpos += fill_utf8((c), (char *)buffer + outpos); \
else \
outpos += thislen; \
} else { \
if (buffer && outpos + 1 <= len) \
((uint32_t *)buffer)[outpos++] = (c); \
else \
outpos++; \
}
for (int row = rect.start_row; row < rect.end_row; row++) {
for (int col = rect.start_col; col < rect.end_col; col++) {
ScreenCell *cell = getcell(screen, row, col);
if (cell->chars[0] == 0)
// Erased cell, might need a space
padding++;
else if (cell->chars[0] == (uint32_t)-1)
// Gap behind a double-width char, do nothing
;
else {
while (padding) {
PUT(UNICODE_SPACE);
padding--;
}
for (int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) {
PUT(cell->chars[i]);
}
}
}
if (row < rect.end_row - 1) {
PUT(UNICODE_LINEFEED);
padding = 0;
}
}
return outpos;
}
size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect) {
return _get_chars(screen, 0, chars, len, rect);
}
size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect) {
return _get_chars(screen, 1, str, len, rect);
}
/* Copy internal to external representation of a screen cell */
int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell) {
ScreenCell *intcell = getcell(screen, pos.row, pos.col);
if (!intcell)
return 0;
for (int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
cell->chars[i] = intcell->chars[i];
if (!intcell->chars[i])
break;
}
cell->attrs.bold = intcell->pen.bold;
cell->attrs.underline = intcell->pen.underline;
cell->attrs.italic = intcell->pen.italic;
cell->attrs.blink = intcell->pen.blink;
cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse;
cell->attrs.strike = intcell->pen.strike;
cell->attrs.font = intcell->pen.font;
cell->attrs.dwl = intcell->pen.dwl;
cell->attrs.dhl = intcell->pen.dhl;
cell->fg = intcell->pen.fg;
cell->bg = intcell->pen.bg;
if (pos.col < (screen->cols - 1) &&
getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1)
cell->width = 2;
else
cell->width = 1;
return 1;
}
/* Copy external to internal representation of a screen cell */
/* static because it's only used internally for sb_popline during resize */
static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell) {
ScreenCell *intcell = getcell(screen, pos.row, pos.col);
if (!intcell)
return 0;
for (int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
intcell->chars[i] = cell->chars[i];
if (!cell->chars[i])
break;
}
intcell->pen.bold = cell->attrs.bold;
intcell->pen.underline = cell->attrs.underline;
intcell->pen.italic = cell->attrs.italic;
intcell->pen.blink = cell->attrs.blink;
intcell->pen.reverse = cell->attrs.reverse ^ screen->global_reverse;
intcell->pen.strike = cell->attrs.strike;
intcell->pen.font = cell->attrs.font;
intcell->pen.fg = cell->fg;
intcell->pen.bg = cell->bg;
if (cell->width == 2)
getcell(screen, pos.row, pos.col + 1)->chars[0] = (uint32_t)-1;
return 1;
}
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos) {
/* This cell is EOL if this and every cell to the right is black */
for (; pos.col < screen->cols; pos.col++) {
ScreenCell *cell = getcell(screen, pos.row, pos.col);
if (cell->chars[0] != 0)
return 0;
}
return 1;
}
VTermScreen *vterm_obtain_screen(VTerm *vt) {
if (vt->screen)
return vt->screen;
VTermScreen *screen = screen_new(vt);
vt->screen = screen;
return screen;
}
void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen) {
if (!screen->buffers[1] && altscreen) {
int rows, cols;
vterm_get_size(screen->vt, &rows, &cols);
screen->buffers[1] = realloc_buffer(screen, NULL, rows, cols);
}
}
void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user) {
screen->callbacks = callbacks;
screen->cbdata = user;
}
void *vterm_screen_get_cbdata(VTermScreen *screen) {
return screen->cbdata;
}
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermParserCallbacks *fallbacks, void *user) {
vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user);
}
void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen) {
return vterm_state_get_unrecognised_fbdata(screen->state);
}
void vterm_screen_flush_damage(VTermScreen *screen) {
if (screen->pending_scrollrect.start_row != -1) {
vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward, screen->pending_scroll_rightward, moverect_user, erase_user, screen);
screen->pending_scrollrect.start_row = -1;
}
if (screen->damaged.start_row != -1) {
if (screen->callbacks && screen->callbacks->damage)
(*screen->callbacks->damage)(screen->damaged, screen->cbdata);
screen->damaged.start_row = -1;
}
}
void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size) {
vterm_screen_flush_damage(screen);
screen->damage_merge = size;
}
static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b) {
if ((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold))
return 1;
if ((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline))
return 1;
if ((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic))
return 1;
if ((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink))
return 1;
if ((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse))
return 1;
if ((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike))
return 1;
if ((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font))
return 1;
if ((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_is_equal(&a->pen.fg, &b->pen.fg))
return 1;
if ((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg))
return 1;
return 0;
}
int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs) {
ScreenCell *target = getcell(screen, pos.row, pos.col);
// TODO: bounds check
extent->start_row = pos.row;
extent->end_row = pos.row + 1;
if (extent->start_col < 0)
extent->start_col = 0;
if (extent->end_col < 0)
extent->end_col = screen->cols;
int col;
for (col = pos.col - 1; col >= extent->start_col; col--)
if (attrs_differ(attrs, target, getcell(screen, pos.row, col)))
break;
extent->start_col = col + 1;
for (col = pos.col + 1; col < extent->end_col; col++)
if (attrs_differ(attrs, target, getcell(screen, pos.row, col)))
break;
extent->end_col = col - 1;
return 1;
}
void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col) {
vterm_state_convert_color_to_rgb(screen->state, col);
}

1801
extlib/libvterm/state.c Normal file

File diff suppressed because it is too large Load Diff

229
extlib/libvterm/unicode.c Normal file
View File

@ -0,0 +1,229 @@
#include "vterm_internal.h"
// ### The following from http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
// With modifications:
// made functions static
// moved 'combining' table to file scope, so other functions can see it
// ###################################################################
/*
* This is an implementation of wcwidth() and wcswidth() (defined in
* IEEE Std 1002.1-2001) for Unicode.
*
* http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
* http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
*
* In fixed-width output devices, Latin characters all occupy a single
* "cell" position of equal width, whereas ideographic CJK characters
* occupy two such cells. Interoperability between terminal-line
* applications and (teletype-style) character terminals using the
* UTF-8 encoding requires agreement on which character should advance
* the cursor by how many cell positions. No established formal
* standards exist at present on which Unicode character shall occupy
* how many cell positions on character terminals. These routines are
* a first attempt of defining such behavior based on simple rules
* applied to data provided by the Unicode Consortium.
*
* For some graphical characters, the Unicode standard explicitly
* defines a character-cell width via the definition of the East Asian
* FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
* In all these cases, there is no ambiguity about which width a
* terminal shall use. For characters in the East Asian Ambiguous (A)
* class, the width choice depends purely on a preference of backward
* compatibility with either historic CJK or Western practice.
* Choosing single-width for these characters is easy to justify as
* the appropriate long-term solution, as the CJK practice of
* displaying these characters as double-width comes from historic
* implementation simplicity (8-bit encoded characters were displayed
* single-width and 16-bit ones double-width, even for Greek,
* Cyrillic, etc.) and not any typographic considerations.
*
* Much less clear is the choice of width for the Not East Asian
* (Neutral) class. Existing practice does not dictate a width for any
* of these characters. It would nevertheless make sense
* typographically to allocate two character cells to characters such
* as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
* represented adequately with a single-width glyph. The following
* routines at present merely assign a single-cell width to all
* neutral characters, in the interest of simplicity. This is not
* entirely satisfactory and should be reconsidered before
* establishing a formal standard in this area. At the moment, the
* decision which Not East Asian (Neutral) characters should be
* represented by double-width glyphs cannot yet be answered by
* applying a simple rule from the Unicode database content. Setting
* up a proper standard for the behavior of UTF-8 character terminals
* will require a careful analysis not only of each Unicode character,
* but also of each presentation form, something the author of these
* routines has avoided to do so far.
*
* http://www.unicode.org/unicode/reports/tr11/
*
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
*
* Permission to use, copy, modify, and distribute this software
* for any purpose and without fee is hereby granted. The author
* disclaims all warranties with regard to this software.
*
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*/
struct interval {
int first;
int last;
};
/* sorted list of non-overlapping intervals of non-spacing characters */
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
static const struct interval combining[] = {
{0x0300, 0x036F}, {0x0483, 0x0486}, {0x0488, 0x0489}, {0x0591, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5}, {0x05C7, 0x05C7}, {0x0600, 0x0603}, {0x0610, 0x0615}, {0x064B, 0x065E}, {0x0670, 0x0670}, {0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07EB, 0x07F3}, {0x0901, 0x0902}, {0x093C, 0x093C}, {0x0941, 0x0948}, {0x094D, 0x094D}, {0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0981}, {0x09BC, 0x09BC}, {0x09C1, 0x09C4}, {0x09CD, 0x09CD}, {0x09E2, 0x09E3}, {0x0A01, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A41, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A70, 0x0A71}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC}, {0x0AC1, 0x0AC5}, {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD}, {0x0AE2, 0x0AE3}, {0x0B01, 0x0B01}, {0x0B3C, 0x0B3C}, {0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, {0x0B4D, 0x0B4D}, {0x0B56, 0x0B56}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0}, {0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0CBC, 0x0CBC}, {0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD}, {0x0CE2, 0x0CE3}, {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D}, {0x0DCA, 0x0DCA}, {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F71, 0x0F7E}, {0x0F80, 0x0F84}, {0x0F86, 0x0F87}, {0x0F90, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102D, 0x1030}, {0x1032, 0x1032}, {0x1036, 0x1037}, {0x1039, 0x1039}, {0x1058, 0x1059}, {0x1160, 0x11FF}, {0x135F, 0x135F}, {0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753}, {0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD}, {0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD}, {0x180B, 0x180D}, {0x18A9, 0x18A9}, {0x1920, 0x1922}, {0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193B}, {0x1A17, 0x1A18}, {0x1B00, 0x1B03}, {0x1B34, 0x1B34}, {0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42}, {0x1B6B, 0x1B73}, {0x1DC0, 0x1DCA}, {0x1DFE, 0x1DFF}, {0x200B, 0x200F}, {0x202A, 0x202E}, {0x2060, 0x2063}, {0x206A, 0x206F}, {0x20D0, 0x20EF}, {0x302A, 0x302F}, {0x3099, 0x309A}, {0xA806, 0xA806}, {0xA80B, 0xA80B}, {0xA825, 0xA826}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F}, {0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, {0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x1D167, 0x1D169}, {0x1D173, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F}, {0xE0100, 0xE01EF}};
/* auxiliary function for binary search in interval table */
static int bisearch(uint32_t ucs, const struct interval *table, int max) {
int min = 0;
int mid;
if (ucs < table[0].first || ucs > table[max].last)
return 0;
while (max >= min) {
mid = (min + max) / 2;
if (ucs > table[mid].last)
min = mid + 1;
else if (ucs < table[mid].first)
max = mid - 1;
else
return 1;
}
return 0;
}
/* The following two functions define the column width of an ISO 10646
* character as follows:
*
* - The null character (U+0000) has a column width of 0.
*
* - Other C0/C1 control characters and DEL will lead to a return
* value of -1.
*
* - Non-spacing and enclosing combining characters (general
* category code Mn or Me in the Unicode database) have a
* column width of 0.
*
* - SOFT HYPHEN (U+00AD) has a column width of 1.
*
* - Other format characters (general category code Cf in the Unicode
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
*
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
* have a column width of 0.
*
* - Spacing characters in the East Asian Wide (W) or East Asian
* Full-width (F) category as defined in Unicode Technical
* Report #11 have a column width of 2.
*
* - All remaining characters (including all printable
* ISO 8859-1 and WGL4 characters, Unicode control characters,
* etc.) have a column width of 1.
*
* This implementation assumes that uint32_t characters are encoded
* in ISO 10646.
*/
static int mk_wcwidth(uint32_t ucs) {
/* test for 8-bit control characters */
if (ucs == 0)
return 0;
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
return -1;
/* binary search in table of non-spacing characters */
if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1))
return 0;
/* if we arrive here, ucs is not a combining or C0/C1 control character */
return 1 +
(ucs >= 0x1100 &&
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
ucs == 0x2329 || ucs == 0x232a ||
(ucs >= 0x2e80 && ucs <= 0xa4cf &&
ucs != 0x303f) || /* CJK ... Yi */
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
(ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
(ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
(ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
(ucs >= 0x30000 && ucs <= 0x3fffd)));
}
static int mk_wcswidth(const uint32_t *pwcs, size_t n) {
int w, width = 0;
for (; *pwcs && n-- > 0; pwcs++)
if ((w = mk_wcwidth(*pwcs)) < 0)
return -1;
else
width += w;
return width;
}
/*
* The following functions are the same as mk_wcwidth() and
* mk_wcswidth(), except that spacing characters in the East Asian
* Ambiguous (A) category as defined in Unicode Technical Report #11
* have a column width of 2. This variant might be useful for users of
* CJK legacy encodings who want to migrate to UCS without changing
* the traditional terminal character-width behaviour. It is not
* otherwise recommended for general use.
*/
static int mk_wcwidth_cjk(uint32_t ucs) {
/* sorted list of non-overlapping intervals of East Asian Ambiguous
* characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
static const struct interval ambiguous[] = {
{0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, {0x00AA, 0x00AA}, {0x00AE, 0x00AE}, {0x00B0, 0x00B4}, {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, {0x2170, 0x2179}, {0x2190, 0x2199}, {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, {0x260E, 0x260F}, {0x2614, 0x2615}, {0x261C, 0x261C}, {0x261E, 0x261E}, {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, {0x266F, 0x266F}, {0x273D, 0x273D}, {0x2776, 0x277F}, {0xE000, 0xF8FF}, {0xFFFD, 0xFFFD}, {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}};
/* binary search in table of non-spacing characters */
if (bisearch(ucs, ambiguous, sizeof(ambiguous) / sizeof(struct interval) - 1))
return 2;
return mk_wcwidth(ucs);
}
static int mk_wcswidth_cjk(const uint32_t *pwcs, size_t n) {
int w, width = 0;
for (; *pwcs && n-- > 0; pwcs++)
if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
return -1;
else
width += w;
return width;
}
// ################################
// ### The rest added by Paul Evans
static const struct interval fullwidth[] = {
#include "fullwidth.inc"
};
INTERNAL int vterm_unicode_width(uint32_t codepoint) {
if (bisearch(codepoint, fullwidth, sizeof(fullwidth) / sizeof(fullwidth[0]) - 1))
return 2;
return mk_wcwidth(codepoint);
}
INTERNAL int vterm_unicode_is_combining(uint32_t codepoint) {
return bisearch(codepoint, combining, sizeof(combining) / sizeof(struct interval) - 1);
}

39
extlib/libvterm/utf8.h Normal file
View File

@ -0,0 +1,39 @@
/* The following functions copied and adapted from libtermkey
*
* http://www.leonerd.org.uk/code/libtermkey/
*/
static inline unsigned int utf8_seqlen(long codepoint)
{
if(codepoint < 0x0000080) return 1;
if(codepoint < 0x0000800) return 2;
if(codepoint < 0x0010000) return 3;
if(codepoint < 0x0200000) return 4;
if(codepoint < 0x4000000) return 5;
return 6;
}
/* Does NOT NUL-terminate the buffer */
static int fill_utf8(long codepoint, char *str)
{
int nbytes = utf8_seqlen(codepoint);
// This is easier done backwards
int b = nbytes;
while(b > 1) {
b--;
str[b] = 0x80 | (codepoint & 0x3f);
codepoint >>= 6;
}
switch(nbytes) {
case 1: str[0] = (codepoint & 0x7f); break;
case 2: str[0] = 0xc0 | (codepoint & 0x1f); break;
case 3: str[0] = 0xe0 | (codepoint & 0x0f); break;
case 4: str[0] = 0xf0 | (codepoint & 0x07); break;
case 5: str[0] = 0xf8 | (codepoint & 0x03); break;
case 6: str[0] = 0xfc | (codepoint & 0x01); break;
}
return nbytes;
}
/* end copy */

360
extlib/libvterm/vterm.c Normal file
View File

@ -0,0 +1,360 @@
#include "vterm_internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "../../runtime/panic_assert.h"
#include "../../runtime/printf.h"
/*****************
* API functions *
*****************/
/*
static void *default_malloc(size_t size, void *allocdata) {
void *ptr = malloc(size);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
static void default_free(void *ptr, void *allocdata) {
free(ptr);
}
*/
static VTermAllocatorFunctions default_allocator = {
//.malloc = &default_malloc,
//.free = &default_free,
};
VTerm *vterm_new(int rows, int cols) {
return vterm_new_with_allocator(rows, cols, &default_allocator, NULL);
}
VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata) {
/* Need to bootstrap using the allocator function directly */
VTerm *vt = (*funcs->malloc)(sizeof(VTerm), allocdata);
vt->allocator = funcs;
vt->allocdata = allocdata;
vt->rows = rows;
vt->cols = cols;
vt->parser.state = NORMAL;
vt->parser.callbacks = NULL;
vt->parser.cbdata = NULL;
vt->parser.strbuffer_len = 64;
vt->parser.strbuffer_cur = 0;
vt->parser.strbuffer = vterm_allocator_malloc(vt, vt->parser.strbuffer_len);
vt->outfunc = NULL;
vt->outdata = NULL;
vt->outbuffer_len = 64;
vt->outbuffer_cur = 0;
vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
vt->tmpbuffer_len = 64;
vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
return vt;
}
void vterm_free(VTerm *vt) {
if (vt->screen)
vterm_screen_free(vt->screen);
if (vt->state)
vterm_state_free(vt->state);
vterm_allocator_free(vt, vt->parser.strbuffer);
vterm_allocator_free(vt, vt->outbuffer);
vterm_allocator_free(vt, vt->tmpbuffer);
vterm_allocator_free(vt, vt);
}
INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size) {
return (*vt->allocator->malloc)(size, vt->allocdata);
}
INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr) {
(*vt->allocator->free)(ptr, vt->allocdata);
}
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp) {
if (rowsp)
*rowsp = vt->rows;
if (colsp)
*colsp = vt->cols;
}
void vterm_set_size(VTerm *vt, int rows, int cols) {
vt->rows = rows;
vt->cols = cols;
if (vt->parser.callbacks && vt->parser.callbacks->resize)
(*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
}
int vterm_get_utf8(const VTerm *vt) {
return vt->mode.utf8;
}
void vterm_set_utf8(VTerm *vt, int is_utf8) {
vt->mode.utf8 = is_utf8;
}
void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user) {
vt->outfunc = func;
vt->outdata = user;
}
INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len) {
if (vt->outfunc) {
(vt->outfunc)(bytes, len, vt->outdata);
return;
}
if (len > vt->outbuffer_len - vt->outbuffer_cur)
return;
memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
vt->outbuffer_cur += len;
}
INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args) {
size_t len = vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len, format, args);
vterm_push_output_bytes(vt, vt->tmpbuffer, len);
}
INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...) {
va_list args;
va_start(args, format);
vterm_push_output_vsprintf(vt, format, args);
va_end(args);
}
INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...) {
size_t cur;
if (ctrl >= 0x80 && !vt->mode.ctrl8bit)
cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40);
else
cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl);
if (cur >= vt->tmpbuffer_len)
return;
va_list args;
va_start(args, fmt);
cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args);
va_end(args);
if (cur >= vt->tmpbuffer_len)
return;
vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
}
INTERNAL void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...) {
size_t cur = 0;
cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
vt->mode.ctrl8bit ? "\x90" : ESC_S "P"); // DCS
if (cur >= vt->tmpbuffer_len)
return;
va_list args;
va_start(args, fmt);
cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args);
va_end(args);
if (cur >= vt->tmpbuffer_len)
return;
cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST
if (cur >= vt->tmpbuffer_len)
return;
vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
}
size_t vterm_output_get_buffer_size(const VTerm *vt) {
return vt->outbuffer_len;
}
size_t vterm_output_get_buffer_current(const VTerm *vt) {
return vt->outbuffer_cur;
}
size_t vterm_output_get_buffer_remaining(const VTerm *vt) {
return vt->outbuffer_len - vt->outbuffer_cur;
}
size_t vterm_output_read(VTerm *vt, char *buffer, size_t len) {
if (len > vt->outbuffer_cur)
len = vt->outbuffer_cur;
memcpy(buffer, vt->outbuffer, len);
if (len < vt->outbuffer_cur)
memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len);
vt->outbuffer_cur -= len;
return len;
}
VTermValueType vterm_get_attr_type(VTermAttr attr) {
switch (attr) {
case VTERM_ATTR_BOLD: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_UNDERLINE: return VTERM_VALUETYPE_INT;
case VTERM_ATTR_ITALIC: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_BLINK: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_REVERSE: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_STRIKE: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_FONT: return VTERM_VALUETYPE_INT;
case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
case VTERM_N_ATTRS: return 0;
}
return 0; /* UNREACHABLE */
}
VTermValueType vterm_get_prop_type(VTermProp prop) {
switch (prop) {
case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_CURSORBLINK: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_ALTSCREEN: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_TITLE: return VTERM_VALUETYPE_STRING;
case VTERM_PROP_ICONNAME: return VTERM_VALUETYPE_STRING;
case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT;
case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT;
case VTERM_N_PROPS: return 0;
}
return 0; /* UNREACHABLE */
}
void vterm_scroll_rect(VTermRect rect, int downward, int rightward, int (*moverect)(VTermRect src, VTermRect dest, void *user), int (*eraserect)(VTermRect rect, int selective, void *user), void *user) {
VTermRect src;
VTermRect dest;
if (abs(downward) >= rect.end_row - rect.start_row ||
abs(rightward) >= rect.end_col - rect.start_col) {
/* Scroll more than area; just erase the lot */
(*eraserect)(rect, 0, user);
return;
}
if (rightward >= 0) {
/* rect: [XXX................]
* src: [----------------]
* dest: [----------------]
*/
dest.start_col = rect.start_col;
dest.end_col = rect.end_col - rightward;
src.start_col = rect.start_col + rightward;
src.end_col = rect.end_col;
} else {
/* rect: [................XXX]
* src: [----------------]
* dest: [----------------]
*/
int leftward = -rightward;
dest.start_col = rect.start_col + leftward;
dest.end_col = rect.end_col;
src.start_col = rect.start_col;
src.end_col = rect.end_col - leftward;
}
if (downward >= 0) {
dest.start_row = rect.start_row;
dest.end_row = rect.end_row - downward;
src.start_row = rect.start_row + downward;
src.end_row = rect.end_row;
} else {
int upward = -downward;
dest.start_row = rect.start_row + upward;
dest.end_row = rect.end_row;
src.start_row = rect.start_row;
src.end_row = rect.end_row - upward;
}
if (moverect)
(*moverect)(dest, src, user);
if (downward > 0)
rect.start_row = rect.end_row - downward;
else if (downward < 0)
rect.end_row = rect.start_row - downward;
if (rightward > 0)
rect.start_col = rect.end_col - rightward;
else if (rightward < 0)
rect.end_col = rect.start_col - rightward;
(*eraserect)(rect, 0, user);
}
void vterm_copy_cells(VTermRect dest, VTermRect src, void (*copycell)(VTermPos dest, VTermPos src, void *user), void *user) {
int downward = src.start_row - dest.start_row;
int rightward = src.start_col - dest.start_col;
int init_row, test_row, init_col, test_col;
int inc_row, inc_col;
if (downward < 0) {
init_row = dest.end_row - 1;
test_row = dest.start_row - 1;
inc_row = -1;
} else /* downward >= 0 */ {
init_row = dest.start_row;
test_row = dest.end_row;
inc_row = +1;
}
if (rightward < 0) {
init_col = dest.end_col - 1;
test_col = dest.start_col - 1;
inc_col = -1;
} else /* rightward >= 0 */ {
init_col = dest.start_col;
test_col = dest.end_col;
inc_col = +1;
}
VTermPos pos;
for (pos.row = init_row; pos.row != test_row; pos.row += inc_row)
for (pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
VTermPos srcpos = {pos.row + downward, pos.col + rightward};
(*copycell)(pos, srcpos, user);
}
}
void vterm_check_version(int major, int minor) {
if (major != VTERM_VERSION_MAJOR) {
//fprintf(stderr, "libvterm major version mismatch; %d (wants) != %d (library)\n", major, VTERM_VERSION_MAJOR);
Panicf("libvterm major version mismatch; %d (wants) != %d (library)\n", major, VTERM_VERSION_MAJOR);
}
if (minor > VTERM_VERSION_MINOR) {
//fprintf(stderr, "libvterm minor version mismatch; %d (wants) > %d (library)\n", minor, VTERM_VERSION_MINOR);
Panicf("libvterm minor version mismatch; %d (wants) > %d (library)\n", minor, VTERM_VERSION_MINOR);
}
// Happy
}

533
extlib/libvterm/vterm.h Normal file
View File

@ -0,0 +1,533 @@
#ifndef __VTERM_H__
#define __VTERM_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "vterm_keycodes.h"
#define VTERM_VERSION_MAJOR 0
#define VTERM_VERSION_MINOR 1
#define VTERM_CHECK_VERSION \
vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR)
typedef struct VTerm VTerm;
typedef struct VTermState VTermState;
typedef struct VTermScreen VTermScreen;
typedef struct {
int row;
int col;
} VTermPos;
/* some small utility functions; we can just keep these static here */
/* order points by on-screen flow order */
static inline int vterm_pos_cmp(VTermPos a, VTermPos b)
{
return (a.row == b.row) ? a.col - b.col : a.row - b.row;
}
typedef struct {
int start_row;
int end_row;
int start_col;
int end_col;
} VTermRect;
/* true if the rect contains the point */
static inline int vterm_rect_contains(VTermRect r, VTermPos p)
{
return p.row >= r.start_row && p.row < r.end_row &&
p.col >= r.start_col && p.col < r.end_col;
}
/* move a rect */
static inline void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
{
rect->start_row += row_delta; rect->end_row += row_delta;
rect->start_col += col_delta; rect->end_col += col_delta;
}
/**
* Bit-field describing the content of the tagged union `VTermColor`.
*/
typedef enum {
/**
* If the lower bit of `type` is not set, the colour is 24-bit RGB.
*/
VTERM_COLOR_RGB = 0x00,
/**
* The colour is an index into a palette of 256 colours.
*/
VTERM_COLOR_INDEXED = 0x01,
/**
* Mask that can be used to extract the RGB/Indexed bit.
*/
VTERM_COLOR_TYPE_MASK = 0x01,
/**
* If set, indicates that this colour should be the default foreground
* color, i.e. there was no SGR request for another colour. When
* rendering this colour it is possible to ignore "idx" and just use a
* colour that is not in the palette.
*/
VTERM_COLOR_DEFAULT_FG = 0x02,
/**
* If set, indicates that this colour should be the default background
* color, i.e. there was no SGR request for another colour. A common
* option when rendering this colour is to not render a background at
* all, for example by rendering the window transparently at this spot.
*/
VTERM_COLOR_DEFAULT_BG = 0x04,
/**
* Mask that can be used to extract the default foreground/background bit.
*/
VTERM_COLOR_DEFAULT_MASK = 0x06
} VTermColorType;
/**
* Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the
* given VTermColor instance is an indexed colour.
*/
#define VTERM_COLOR_IS_INDEXED(col) \
(((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED)
/**
* Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that
* the given VTermColor instance is an rgb colour.
*/
#define VTERM_COLOR_IS_RGB(col) \
(((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB)
/**
* Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating
* that the given VTermColor instance corresponds to the default foreground
* color.
*/
#define VTERM_COLOR_IS_DEFAULT_FG(col) \
(!!((col)->type & VTERM_COLOR_DEFAULT_FG))
/**
* Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating
* that the given VTermColor instance corresponds to the default background
* color.
*/
#define VTERM_COLOR_IS_DEFAULT_BG(col) \
(!!((col)->type & VTERM_COLOR_DEFAULT_BG))
/**
* Tagged union storing either an RGB color or an index into a colour palette.
* In order to convert indexed colours to RGB, you may use the
* vterm_state_convert_color_to_rgb() or vterm_screen_convert_color_to_rgb()
* functions which lookup the RGB colour from the palette maintained by a
* VTermState or VTermScreen instance.
*/
typedef union {
/**
* Tag indicating which union member is actually valid. This variable
* coincides with the `type` member of the `rgb` and the `indexed` struct
* in memory. Please use the `VTERM_COLOR_IS_*` test macros to check whether
* a particular type flag is set.
*/
uint8_t type;
/**
* Valid if `VTERM_COLOR_IS_RGB(type)` is true. Holds the RGB colour values.
*/
struct {
/**
* Same as the top-level `type` member stored in VTermColor.
*/
uint8_t type;
/**
* The actual 8-bit red, green, blue colour values.
*/
uint8_t red, green, blue;
} rgb;
/**
* If `VTERM_COLOR_IS_INDEXED(type)` is true, this member holds the index into
* the colour palette.
*/
struct {
/**
* Same as the top-level `type` member stored in VTermColor.
*/
uint8_t type;
/**
* Index into the colour map.
*/
uint8_t idx;
} indexed;
} VTermColor;
/**
* Constructs a new VTermColor instance representing the given RGB values.
*/
static inline void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green,
uint8_t blue)
{
col->type = VTERM_COLOR_RGB;
col->rgb.red = red;
col->rgb.green = green;
col->rgb.blue = blue;
}
/**
* Construct a new VTermColor instance representing an indexed color with the
* given index.
*/
static inline void vterm_color_indexed(VTermColor *col, uint8_t idx)
{
col->type = VTERM_COLOR_INDEXED;
col->indexed.idx = idx;
}
/**
* Compares two colours. Returns true if the colors are equal, false otherwise.
*/
int vterm_color_is_equal(const VTermColor *a, const VTermColor *b);
typedef enum {
/* VTERM_VALUETYPE_NONE = 0 */
VTERM_VALUETYPE_BOOL = 1,
VTERM_VALUETYPE_INT,
VTERM_VALUETYPE_STRING,
VTERM_VALUETYPE_COLOR,
VTERM_N_VALUETYPES
} VTermValueType;
typedef union {
int boolean;
int number;
char *string;
VTermColor color;
} VTermValue;
typedef enum {
/* VTERM_ATTR_NONE = 0 */
VTERM_ATTR_BOLD = 1, // bool: 1, 22
VTERM_ATTR_UNDERLINE, // number: 4, 21, 24
VTERM_ATTR_ITALIC, // bool: 3, 23
VTERM_ATTR_BLINK, // bool: 5, 25
VTERM_ATTR_REVERSE, // bool: 7, 27
VTERM_ATTR_STRIKE, // bool: 9, 29
VTERM_ATTR_FONT, // number: 10-19
VTERM_ATTR_FOREGROUND, // color: 30-39 90-97
VTERM_ATTR_BACKGROUND, // color: 40-49 100-107
VTERM_N_ATTRS
} VTermAttr;
typedef enum {
/* VTERM_PROP_NONE = 0 */
VTERM_PROP_CURSORVISIBLE = 1, // bool
VTERM_PROP_CURSORBLINK, // bool
VTERM_PROP_ALTSCREEN, // bool
VTERM_PROP_TITLE, // string
VTERM_PROP_ICONNAME, // string
VTERM_PROP_REVERSE, // bool
VTERM_PROP_CURSORSHAPE, // number
VTERM_PROP_MOUSE, // number
VTERM_N_PROPS
} VTermProp;
enum {
VTERM_PROP_CURSORSHAPE_BLOCK = 1,
VTERM_PROP_CURSORSHAPE_UNDERLINE,
VTERM_PROP_CURSORSHAPE_BAR_LEFT,
VTERM_N_PROP_CURSORSHAPES
};
enum {
VTERM_PROP_MOUSE_NONE = 0,
VTERM_PROP_MOUSE_CLICK,
VTERM_PROP_MOUSE_DRAG,
VTERM_PROP_MOUSE_MOVE,
VTERM_N_PROP_MOUSES
};
typedef struct {
const uint32_t *chars;
int width;
unsigned int protected_cell:1; /* DECSCA-protected against DECSEL/DECSED */
unsigned int dwl:1; /* DECDWL or DECDHL double-width line */
unsigned int dhl:2; /* DECDHL double-height line (1=top 2=bottom) */
} VTermGlyphInfo;
typedef struct {
unsigned int doublewidth:1; /* DECDWL or DECDHL line */
unsigned int doubleheight:2; /* DECDHL line (1=top 2=bottom) */
} VTermLineInfo;
typedef struct {
/* libvterm relies on this memory to be zeroed out before it is returned
* by the allocator. */
void *(*malloc)(size_t size, void *allocdata);
void (*free)(void *ptr, void *allocdata);
} VTermAllocatorFunctions;
void vterm_check_version(int major, int minor);
VTerm *vterm_new(int rows, int cols);
VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata);
void vterm_free(VTerm* vt);
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp);
void vterm_set_size(VTerm *vt, int rows, int cols);
int vterm_get_utf8(const VTerm *vt);
void vterm_set_utf8(VTerm *vt, int is_utf8);
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len);
/* Setting output callback will override the buffer logic */
typedef void VTermOutputCallback(const char *s, size_t len, void *user);
void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user);
/* These buffer functions only work if output callback is NOT set
* These are deprecated and will be removed in a later version */
size_t vterm_output_get_buffer_size(const VTerm *vt);
size_t vterm_output_get_buffer_current(const VTerm *vt);
size_t vterm_output_get_buffer_remaining(const VTerm *vt);
/* This too */
size_t vterm_output_read(VTerm *vt, char *buffer, size_t len);
void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod);
void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod);
void vterm_keyboard_start_paste(VTerm *vt);
void vterm_keyboard_end_paste(VTerm *vt);
void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod);
void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod);
// ------------
// Parser layer
// ------------
/* Flag to indicate non-final subparameters in a single CSI parameter.
* Consider
* CSI 1;2:3:4;5a
* 1 4 and 5 are final.
* 2 and 3 are non-final and will have this bit set
*
* Don't confuse this with the final byte of the CSI escape; 'a' in this case.
*/
#define CSI_ARG_FLAG_MORE (1U<<31)
#define CSI_ARG_MASK (~(1U<<31))
#define CSI_ARG_HAS_MORE(a) ((a) & CSI_ARG_FLAG_MORE)
#define CSI_ARG(a) ((a) & CSI_ARG_MASK)
/* Can't use -1 to indicate a missing argument; use this instead */
#define CSI_ARG_MISSING ((1UL<<31)-1)
#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
#define CSI_ARG_OR(a,def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a))
#define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a))
typedef struct {
int (*text)(const char *bytes, size_t len, void *user);
int (*control)(unsigned char control, void *user);
int (*escape)(const char *bytes, size_t len, void *user);
int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
int (*osc)(const char *command, size_t cmdlen, void *user);
int (*dcs)(const char *command, size_t cmdlen, void *user);
int (*resize)(int rows, int cols, void *user);
} VTermParserCallbacks;
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user);
void *vterm_parser_get_cbdata(VTerm *vt);
// -----------
// State layer
// -----------
typedef struct {
int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
int (*moverect)(VTermRect dest, VTermRect src, void *user);
int (*erase)(VTermRect rect, int selective, void *user);
int (*initpen)(void *user);
int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
int (*bell)(void *user);
int (*resize)(int rows, int cols, VTermPos *delta, void *user);
int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
} VTermStateCallbacks;
VTermState *vterm_obtain_state(VTerm *vt);
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
void *vterm_state_get_cbdata(VTermState *state);
// Only invokes control, csi, osc, dcs
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermParserCallbacks *fallbacks, void *user);
void *vterm_state_get_unrecognised_fbdata(VTermState *state);
void vterm_state_reset(VTermState *state, int hard);
void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg);
void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col);
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg);
void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col);
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val);
void vterm_state_focus_in(VTermState *state);
void vterm_state_focus_out(VTermState *state);
const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row);
/**
* Makes sure that the given color `col` is indeed an RGB colour. After this
* function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other
* flags stored in `col->type` will have been reset.
*
* @param state is the VTermState instance from which the colour palette should
* be extracted.
* @param col is a pointer at the VTermColor instance that should be converted
* to an RGB colour.
*/
void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col);
// ------------
// Screen layer
// ------------
typedef struct {
unsigned int bold : 1;
unsigned int underline : 2;
unsigned int italic : 1;
unsigned int blink : 1;
unsigned int reverse : 1;
unsigned int strike : 1;
unsigned int font : 4; /* 0 to 9 */
unsigned int dwl : 1; /* On a DECDWL or DECDHL line */
unsigned int dhl : 2; /* On a DECDHL line (1=top 2=bottom) */
} VTermScreenCellAttrs;
enum {
VTERM_UNDERLINE_OFF,
VTERM_UNDERLINE_SINGLE,
VTERM_UNDERLINE_DOUBLE,
VTERM_UNDERLINE_CURLY,
};
typedef struct {
#define VTERM_MAX_CHARS_PER_CELL 6
uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
char width;
VTermScreenCellAttrs attrs;
VTermColor fg, bg;
} VTermScreenCell;
typedef struct {
int (*damage)(VTermRect rect, void *user);
int (*moverect)(VTermRect dest, VTermRect src, void *user);
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
int (*bell)(void *user);
int (*resize)(int rows, int cols, void *user);
int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
} VTermScreenCallbacks;
VTermScreen *vterm_obtain_screen(VTerm *vt);
void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user);
void *vterm_screen_get_cbdata(VTermScreen *screen);
// Only invokes control, csi, osc, dcs
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermParserCallbacks *fallbacks, void *user);
void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen);
void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen);
typedef enum {
VTERM_DAMAGE_CELL, /* every cell */
VTERM_DAMAGE_ROW, /* entire rows */
VTERM_DAMAGE_SCREEN, /* entire screen */
VTERM_DAMAGE_SCROLL, /* entire screen + scrollrect */
VTERM_N_DAMAGES
} VTermDamageSize;
void vterm_screen_flush_damage(VTermScreen *screen);
void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size);
void vterm_screen_reset(VTermScreen *screen, int hard);
/* Neither of these functions NUL-terminate the buffer */
size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect);
typedef enum {
VTERM_ATTR_BOLD_MASK = 1 << 0,
VTERM_ATTR_UNDERLINE_MASK = 1 << 1,
VTERM_ATTR_ITALIC_MASK = 1 << 2,
VTERM_ATTR_BLINK_MASK = 1 << 3,
VTERM_ATTR_REVERSE_MASK = 1 << 4,
VTERM_ATTR_STRIKE_MASK = 1 << 5,
VTERM_ATTR_FONT_MASK = 1 << 6,
VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
VTERM_ALL_ATTRS_MASK = (1 << 9) - 1
} VTermAttrMask;
int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell);
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
/**
* Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state`
* instance.
*/
void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col);
// ---------
// Utilities
// ---------
VTermValueType vterm_get_attr_type(VTermAttr attr);
VTermValueType vterm_get_prop_type(VTermProp prop);
void vterm_scroll_rect(VTermRect rect,
int downward,
int rightward,
int (*moverect)(VTermRect src, VTermRect dest, void *user),
int (*eraserect)(VTermRect rect, int selective, void *user),
void *user);
void vterm_copy_cells(VTermRect dest,
VTermRect src,
void (*copycell)(VTermPos dest, VTermPos src, void *user),
void *user);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,252 @@
#ifndef __VTERM_INTERNAL_H__
#define __VTERM_INTERNAL_H__
#include "vterm.h"
#include <stdarg.h>
#if defined(__GNUC__)
# define INTERNAL __attribute__((visibility("internal")))
#else
# define INTERNAL
#endif
#ifdef DEBUG
# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
#else
# define DEBUG_LOG(...)
#endif
#define ESC_S "\x1b"
#define INTERMED_MAX 16
#define CSI_ARGS_MAX 16
#define CSI_LEADER_MAX 16
typedef struct VTermEncoding VTermEncoding;
typedef struct {
VTermEncoding *enc;
// This size should be increased if required by other stateful encodings
char data[4*sizeof(uint32_t)];
} VTermEncodingInstance;
struct VTermPen
{
VTermColor fg;
VTermColor bg;
unsigned int bold:1;
unsigned int underline:2;
unsigned int italic:1;
unsigned int blink:1;
unsigned int reverse:1;
unsigned int strike:1;
unsigned int font:4; /* To store 0-9 */
};
struct VTermState
{
VTerm *vt;
const VTermStateCallbacks *callbacks;
void *cbdata;
const VTermParserCallbacks *fallbacks;
void *fbdata;
int rows;
int cols;
/* Current cursor position */
VTermPos pos;
int at_phantom; /* True if we're on the "81st" phantom column to defer a wraparound */
int scrollregion_top;
int scrollregion_bottom; /* -1 means unbounded */
#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > -1 ? (state)->scrollregion_bottom : (state)->rows)
int scrollregion_left;
#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
int scrollregion_right; /* -1 means unbounded */
#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin && (state)->scrollregion_right > -1 ? (state)->scrollregion_right : (state)->cols)
/* Bitvector of tab stops */
unsigned char *tabstops;
VTermLineInfo *lineinfo;
#define ROWWIDTH(state,row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
/* Mouse state */
int mouse_col, mouse_row;
int mouse_buttons;
int mouse_flags;
#define MOUSE_WANT_CLICK 0x01
#define MOUSE_WANT_DRAG 0x02
#define MOUSE_WANT_MOVE 0x04
enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT } mouse_protocol;
/* Last glyph output, for Unicode recombining purposes */
uint32_t *combine_chars;
size_t combine_chars_size; // Number of ELEMENTS in the above
int combine_width; // The width of the glyph above
VTermPos combine_pos; // Position before movement
struct {
unsigned int keypad:1;
unsigned int cursor:1;
unsigned int autowrap:1;
unsigned int insert:1;
unsigned int newline:1;
unsigned int cursor_visible:1;
unsigned int cursor_blink:1;
unsigned int cursor_shape:2;
unsigned int alt_screen:1;
unsigned int origin:1;
unsigned int screen:1;
unsigned int leftrightmargin:1;
unsigned int bracketpaste:1;
unsigned int report_focus:1;
} mode;
VTermEncodingInstance encoding[4], encoding_utf8;
int gl_set, gr_set, gsingle_set;
struct VTermPen pen;
VTermColor default_fg;
VTermColor default_bg;
VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
int bold_is_highbright;
unsigned int protected_cell : 1;
/* Saved state under DEC mode 1048/1049 */
struct {
VTermPos pos;
struct VTermPen pen;
struct {
unsigned int cursor_visible:1;
unsigned int cursor_blink:1;
unsigned int cursor_shape:2;
} mode;
} saved;
};
typedef enum {
VTERM_PARSER_OSC,
VTERM_PARSER_DCS,
VTERM_N_PARSER_TYPES
} VTermParserStringType;
struct VTerm
{
VTermAllocatorFunctions *allocator;
void *allocdata;
int rows;
int cols;
struct {
unsigned int utf8:1;
unsigned int ctrl8bit:1;
} mode;
struct {
enum VTermParserState {
NORMAL,
CSI_LEADER,
CSI_ARGS,
CSI_INTERMED,
ESC,
/* below here are the "string states" */
STRING,
ESC_IN_STRING,
} state;
int intermedlen;
char intermed[INTERMED_MAX];
int csi_leaderlen;
char csi_leader[CSI_LEADER_MAX];
int csi_argi;
long csi_args[CSI_ARGS_MAX];
const VTermParserCallbacks *callbacks;
void *cbdata;
VTermParserStringType stringtype;
char *strbuffer;
size_t strbuffer_len;
size_t strbuffer_cur;
} parser;
/* len == malloc()ed size; cur == number of valid bytes */
VTermOutputCallback *outfunc;
void *outdata;
char *outbuffer;
size_t outbuffer_len;
size_t outbuffer_cur;
char *tmpbuffer;
size_t tmpbuffer_len;
VTermState *state;
VTermScreen *screen;
};
struct VTermEncoding {
void (*init) (VTermEncoding *enc, void *data);
void (*decode)(VTermEncoding *enc, void *data,
uint32_t cp[], int *cpi, int cplen,
const char bytes[], size_t *pos, size_t len);
};
typedef enum {
ENC_UTF8,
ENC_SINGLE_94
} VTermEncodingType;
void *vterm_allocator_malloc(VTerm *vt, size_t size);
void vterm_allocator_free(VTerm *vt, void *ptr);
void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len);
void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args);
void vterm_push_output_sprintf(VTerm *vt, const char *format, ...);
void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...);
void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...);
void vterm_state_free(VTermState *state);
void vterm_state_newpen(VTermState *state);
void vterm_state_resetpen(VTermState *state);
void vterm_state_setpen(VTermState *state, const long args[], int argcount);
int vterm_state_getpen(VTermState *state, long args[], int argcount);
void vterm_state_savepen(VTermState *state, int save);
enum {
C1_SS3 = 0x8f,
C1_DCS = 0x90,
C1_CSI = 0x9b,
C1_ST = 0x9c,
};
void vterm_state_push_output_sprintf_CSI(VTermState *vts, const char *format, ...);
void vterm_screen_free(VTermScreen *screen);
VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation);
int vterm_unicode_width(uint32_t codepoint);
int vterm_unicode_is_combining(uint32_t codepoint);
#endif

View File

@ -0,0 +1,61 @@
#ifndef __VTERM_INPUT_H__
#define __VTERM_INPUT_H__
typedef enum {
VTERM_MOD_NONE = 0x00,
VTERM_MOD_SHIFT = 0x01,
VTERM_MOD_ALT = 0x02,
VTERM_MOD_CTRL = 0x04,
VTERM_ALL_MODS_MASK = 0x07
} VTermModifier;
typedef enum {
VTERM_KEY_NONE,
VTERM_KEY_ENTER,
VTERM_KEY_TAB,
VTERM_KEY_BACKSPACE,
VTERM_KEY_ESCAPE,
VTERM_KEY_UP,
VTERM_KEY_DOWN,
VTERM_KEY_LEFT,
VTERM_KEY_RIGHT,
VTERM_KEY_INS,
VTERM_KEY_DEL,
VTERM_KEY_HOME,
VTERM_KEY_END,
VTERM_KEY_PAGEUP,
VTERM_KEY_PAGEDOWN,
VTERM_KEY_FUNCTION_0 = 256,
VTERM_KEY_FUNCTION_MAX = VTERM_KEY_FUNCTION_0 + 255,
VTERM_KEY_KP_0,
VTERM_KEY_KP_1,
VTERM_KEY_KP_2,
VTERM_KEY_KP_3,
VTERM_KEY_KP_4,
VTERM_KEY_KP_5,
VTERM_KEY_KP_6,
VTERM_KEY_KP_7,
VTERM_KEY_KP_8,
VTERM_KEY_KP_9,
VTERM_KEY_KP_MULT,
VTERM_KEY_KP_PLUS,
VTERM_KEY_KP_COMMA,
VTERM_KEY_KP_MINUS,
VTERM_KEY_KP_PERIOD,
VTERM_KEY_KP_DIVIDE,
VTERM_KEY_KP_ENTER,
VTERM_KEY_KP_EQUAL,
VTERM_KEY_MAX, // Must be last
VTERM_N_KEYS = VTERM_KEY_MAX
} VTermKey;
#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0+(n))
#endif

284
graphics/graphics.c Normal file
View File

@ -0,0 +1,284 @@
#include "graphics.h"
#include "unifont.h"
#include "../runtime/stdio.h"
#include <efiprot.h>
#include <assert.h>
#include <string.h>
#include "../runtime/memcpy.h"
const HelosGraphics_Color
HelosGraphics_Color_Black = {0x00, 0x00, 0x00, 0xff},
HelosGraphics_Color_White = {0xff, 0xff, 0xff, 0xff},
HelosGraphics_Color_Red = {0x00, 0x00, 0xff, 0xff},
HelosGraphics_Color_Green = {0x00, 0xff, 0x00, 0xff},
HelosGraphics_Color_Blue = {0xff, 0x00, 0x00, 0xff},
HelosGraphics_Color_Cyan = {0xff, 0xff, 0x00, 0xff},
HelosGraphics_Color_Magenta = {0xff, 0x00, 0xff, 0xff},
HelosGraphics_Color_Yellow = {0x00, 0xff, 0xff, 0xff};
static EFI_GUID gopID = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
static EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
static UINTN sizeofInfo, nModes, nativeMode;
static HelosGraphics_Mode modes[128];
void *graphics_DeviceFramebuffer;
void * graphics_Framebuffer;
uint64_t graphics_FramebufferSize;
uint32_t graphics_Doublebuffer[2048 * 1024];
void graphics_Init() {
assert(sizeof(HelosGraphics_Color) == sizeof(uint32_t) && "HelosGraphics_Color not packed to be 32-bit(4 bytes)");
EFI_STATUS status = efiBootServices->LocateProtocol(&gopID, NULL, (void **)&gop);
if (EFI_ERROR(status)) {
io_Printf("graphics_Init: Error locating GOP\r\n");
return;
}
status = gop->QueryMode(gop, gop->Mode == NULL ? 0 : gop->Mode->Mode, &sizeofInfo, &info);
if (status == EFI_NOT_STARTED) {
status = gop->SetMode(gop, 0);
}
if (EFI_ERROR(status)) {
io_Printf("graphics_Init: Error getting native modes\r\n");
return;
} else {
nativeMode = gop->Mode->Mode;
nModes = gop->Mode->MaxMode;
}
for (int i = 0; i < nModes; i++) {
status = gop->QueryMode(gop, i, &sizeofInfo, &info);
modes[i].Width = info->HorizontalResolution;
modes[i].Height = info->VerticalResolution;
modes[i].PixelsPerLine = info->PixelsPerScanLine;
modes[i].PixelFormat = info->PixelFormat;
if (modes[i].Width == HELOS_GRAPHICS_TARGET_MODE_WIDTH && modes[i].Height == HELOS_GRAPHICS_TARGET_MODE_HEIGHT)
nativeMode = i;
}
// set the new mode
status = gop->SetMode(gop, nativeMode);
if (EFI_ERROR(status)) {
io_Printf("graphics_Init: Unable to set mode %d\r\n", nativeMode);
return;
}
io_Printf(
"graphics_Init: Framebuffer:\r\n addr %08x len %d, size %dx%d, ppl %d\r\n",
graphics_DeviceFramebuffer = (void *)gop->Mode->FrameBufferBase,
graphics_FramebufferSize = gop->Mode->FrameBufferSize,
gop->Mode->Info->HorizontalResolution,
gop->Mode->Info->VerticalResolution,
gop->Mode->Info->PixelsPerScanLine);
// warn the user if the framebuffer is not RGB
io_Printf("graphics_Init: Framebuffer format: ");
#define CASE(v) \
break; \
case v: io_Printf(#v "\r\n");
switch (gop->Mode->Info->PixelFormat) {
CASE(PixelRedGreenBlueReserved8BitPerColor)
graphics_SetPixel = graphics_SetPixel_RGB;
CASE(PixelBlueGreenRedReserved8BitPerColor)
graphics_SetPixel = graphics_SetPixel_BGR;
CASE(PixelBitMask)
CASE(PixelBltOnly)
CASE(PixelFormatMax)
}
#undef CASE
graphics_Framebuffer = (void *)graphics_Doublebuffer;
graphics_CursorX = graphics_CursorY = 0;
}
graphics_SetPixel_Type *graphics_SetPixel;
void graphics_SetPixel_RGB(int posX, int posY, const HelosGraphics_Color *color) {
struct {
uint8_t R, G, B;
uint8_t A;
} colorRGB = {color->R, color->G, color->B, 0};
*((uint32_t *)(graphics_Framebuffer + modes[nativeMode].PixelsPerLine * 4 * posY + 4 * posX)) = (*((uint32_t *)&colorRGB));
}
void graphics_SetPixel_BGR(int posX, int posY, const HelosGraphics_Color *color) {
*((uint32_t *)(graphics_Framebuffer + modes[nativeMode].PixelsPerLine * 4 * posY + 4 * posX)) = (*((uint32_t *)color)) & 0x00ffffffu;
}
void graphics_GetSize(int *sizeX, int *sizeY, int *bitsPerPixel) {
*sizeX = modes[nativeMode].Width;
*sizeY = modes[nativeMode].Height;
*bitsPerPixel = 24;
}
void graphics_ClearBuffer(const HelosGraphics_Color *color) {
uint32_t data;
if (*((uint32_t *)color) == *((uint32_t *)&HelosGraphics_Color_Black)) {
memset(graphics_Framebuffer, 0, graphics_FramebufferSize);
return;
}
if (modes[nativeMode].PixelFormat == PixelRedGreenBlueReserved8BitPerColor) {
struct {
uint8_t R, G, B;
uint8_t A;
} colorRGB = {color->R, color->G, color->B, 0};
data = (*(uint32_t *)&colorRGB) & 0x00ffffffu;
} else if (modes[nativeMode].PixelFormat == PixelBlueGreenRedReserved8BitPerColor) {
data = (*(uint32_t *)color) & 0x00ffffffu;
}
uint32_t *buffer = graphics_Framebuffer, *end = graphics_Framebuffer + graphics_FramebufferSize / sizeof(uint32_t);
while (buffer != end) {
*buffer = data;
buffer++;
}
}
void graphics_SwapBuffer() {
memcpy(graphics_DeviceFramebuffer, graphics_Framebuffer, graphics_FramebufferSize);
}
void graphics_FillPixel(int startX, int startY, int endX, int endY, const HelosGraphics_Color *color) {
// TODO Optimize this! This is too sloooow
if (gop->Mode->Info->PixelFormat == PixelBlueGreenRedReserved8BitPerColor)
for (int i = startX; i < endX; i++)
for (int j = startY; j < endY; j++)
*((uint32_t *)(graphics_Framebuffer + modes[nativeMode].PixelsPerLine * 4 * j + 4 * i)) = (*((uint32_t *)color)) & 0x00ffffffu;
else if (gop->Mode->Info->PixelFormat == PixelRedGreenBlueReserved8BitPerColor)
for (int i = startX; i < endX; i++)
for (int j = startY; j < endY; j++) {
struct {
uint8_t B, G, R;
uint8_t A;
} colorRGB = {color->B, color->G, color->R, 0};
*((uint32_t *)(graphics_Framebuffer + modes[nativeMode].PixelsPerLine * 4 * j + 4 * i)) = (*((uint32_t *)&colorRGB));
}
}
int graphics_CursorX, graphics_CursorY;
void graphics_Scroll(int scrollY) {
memmove(
graphics_Doublebuffer,
graphics_Doublebuffer + modes[nativeMode].PixelsPerLine * scrollY,
sizeof(uint32_t) * (modes[nativeMode].PixelsPerLine * (modes[nativeMode].Height - scrollY)));
// TODO proper memset instead of this sloooow FillPixel
/*memset(
graphics_Doublebuffer + modes[nativeMode].PixelsPerLine * (modes[nativeMode].Height - scrollY),
0,
sizeof(uint32_t) * modes[nativeMode].PixelsPerLine * (scrollY));*/
graphics_FillPixel(0, modes[nativeMode].Height - scrollY, modes[nativeMode].Width, modes[nativeMode].Height, &HelosGraphics_Color_Black);
}
void graphics_ElementSize(int sizeX, int sizeY) {
if (graphics_CursorX + sizeX >= modes[nativeMode].Width) { // line breaking required
graphics_CursorY += sizeY;
graphics_CursorX = 0;
}
if (graphics_CursorY + sizeY >= modes[nativeMode].Height) { // scrolling required
graphics_Scroll(sizeY + graphics_CursorY - modes[nativeMode].Height);
graphics_CursorY = modes[nativeMode].Height - sizeY;
}
}
void graphics_Newline(int advanceY) {
graphics_CursorY += advanceY;
graphics_CursorX = 0;
}
void console_WriteChar(const HelosGraphics_Color *color, uint32_t c) {
int width;
switch (c) {
case '\n':
graphics_Newline(UNIFONT_CHAR_HEIGHT);
break;
case '\r':
graphics_CursorX = 0;
break;
default:
width = unifont_IsCharDoublewidth(c) ? UNIFONT_CHAR_WIDTH * 2 : UNIFONT_CHAR_WIDTH;
graphics_ElementSize(width, UNIFONT_CHAR_HEIGHT);
graphics_FillPixel(graphics_CursorX, graphics_CursorY, graphics_CursorX + width, graphics_CursorY + UNIFONT_CHAR_HEIGHT, &HelosGraphics_Color_Black);
unifont_DrawChar(graphics_CursorX, graphics_CursorY, color, c);
graphics_CursorX += width;
}
}
void console_Write(const HelosGraphics_Color *color, const uint32_t *str, int len) {
bool wantSwap = false;
if (len == 0) {
while (*str != 0) {
console_WriteChar(color, *str);
if (*str == '\n')
wantSwap = true;
str++;
}
} else {
for (int i = 0; i < len; i++) {
console_WriteChar(color, str[i]);
if (str[i] == '\n')
wantSwap = true;
}
}
if (wantSwap)
graphics_SwapBuffer();
}
void console_WriteUTF16(const HelosGraphics_Color *color, const uint16_t *str, int len) {
bool wantSwap = false;
if (len == 0) {
while (*str != 0) {
console_WriteChar(color, *str);
if (*str == '\n')
wantSwap = true;
str++;
}
} else {
for (int i = 0; i < len; i++) {
console_WriteChar(color, str[i]);
if (str[i] == '\n')
wantSwap = true;
}
}
if (wantSwap)
graphics_SwapBuffer();
}
void console_WriteASCII(const HelosGraphics_Color *color, const char *str, int len) {
bool wantSwap = false;
if (len == 0) {
while (*str != 0) {
console_WriteChar(color, *str);
if (*str == '\n')
wantSwap = true;
str++;
}
} else {
for (int i = 0; i < len; i++) {
console_WriteChar(color, str[i]);
if (str[i] == '\n')
wantSwap = true;
}
}
if (wantSwap)
graphics_SwapBuffer();
}

86
graphics/graphics.h Normal file
View File

@ -0,0 +1,86 @@
#pragma once
#include "../main.h"
#include "efiprot.h"
#ifdef __cplusplus
extern "C" {
#endif
// This defines a default target display mode for graphics_Init().
#define HELOS_GRAPHICS_TARGET_MODE_WIDTH 1600
#define HELOS_GRAPHICS_TARGET_MODE_HEIGHT 900
// HelosGraphics_Color is in ARGB little-endian packed format
// (B,G,R,A in byte order)
typedef struct {
uint8_t B, G, R;
uint8_t A;
} PACKED HelosGraphics_Color;
extern const HelosGraphics_Color
HelosGraphics_Color_Black,
HelosGraphics_Color_White,
HelosGraphics_Color_Red,
HelosGraphics_Color_Green,
HelosGraphics_Color_Blue,
HelosGraphics_Color_Cyan,
HelosGraphics_Color_Magenta,
HelosGraphics_Color_Yellow;
typedef struct {
int Width, Height;
int PixelsPerLine;
EFI_GRAPHICS_PIXEL_FORMAT PixelFormat;
} HelosGraphics_Mode;
extern void * graphics_DeviceFramebuffer; // this is the framebuffer directly for the device via memory mapping.
extern void * graphics_Framebuffer; // this is the double-buffered framebuffer (back buffer)
extern uint64_t graphics_FramebufferSize;
// Init() must be called prior to ExitBootServices()
void graphics_Init();
// bitsPerPixel does not count the alpha bits, i.e., is 24 on most modern monitors
void graphics_GetSize(int *sizeX, int *sizeY, int *bitsPerPixel);
void graphics_ClearBuffer(const HelosGraphics_Color *color);
void graphics_SwapBuffer();
// graphics_SetPixel is set by Init() to match one of SetPixel_RGB/BGR according to the framebuffer format.
typedef void(graphics_SetPixel_Type)(int posX, int posY, const HelosGraphics_Color *color);
extern graphics_SetPixel_Type *graphics_SetPixel;
// graphics_SetPixel_RGB/BGR writes the given pixel to the framebuffer in RGB/BGR format.
void graphics_SetPixel_RGB(int posX, int posY, const HelosGraphics_Color *color);
void graphics_SetPixel_BGR(int posX, int posY, const HelosGraphics_Color *color);
void graphics_FillPixel(int startX, int startY, int endX, int endY, const HelosGraphics_Color *color);
extern int graphics_CursorX, graphics_CursorY;
// graphics_Scroll scrolls the display vertically by scrollY pixels.
void graphics_Scroll(int scrollY);
// graphics_ElementSize handles size of an graphics element: scrolling, line breaking, etc.
//
// It does not change CursorX/Y, however.
void graphics_ElementSize(int sizeX, int sizeY);
void graphics_Newline(int advanceY);
void console_WriteChar(const HelosGraphics_Color *color, uint32_t c);
void console_Write(const HelosGraphics_Color *color, const uint32_t *str, int len);
void console_WriteUTF16(const HelosGraphics_Color *color, const uint16_t *str, int len);
void console_WriteASCII(const HelosGraphics_Color *color, const char *str, int len);
#ifdef __cplusplus
} // extern "C"
#endif

18
graphics/test_unifont.c Normal file
View File

@ -0,0 +1,18 @@
#include <stdio.h>
#include "Unifont.h"
int main() {
printf("Data at 0x%X\n\n", unifont_Data);
for (int i = 0; i < 64; i++) {
printf("%02d(%02X):", i, i);
for (int j = 0; j < 32; j++) {
printf("%02X", (unsigned int)unifont_Data[i * 32 + j]);
}
printf("\n");
}
return 0;
}

43
graphics/unifont.c Normal file
View File

@ -0,0 +1,43 @@
#include "unifont.h"
#include "graphics.h"
#include <stddef.h>
size_t strlen(const char *);
void unifont_DrawChar(int posX, int posY, const HelosGraphics_Color *color, uint32_t codepoint) {
const unsigned char *data = unifont_Data + codepoint * UNIFONT_CHAR_WIDTH * UNIFONT_CHAR_HEIGHT * 2 / 8;
bool wide = unifont_IsCharDoublewidth(codepoint);
int charWidth = UNIFONT_CHAR_WIDTH * (wide ? 2 : 1);
for (int x = 0; x < charWidth; x++)
for (int y = 0; y < UNIFONT_CHAR_HEIGHT; y++) {
int pos = y * charWidth + x;
if (data[pos / 8] & (1u << (7 - pos % 8)))
graphics_SetPixel(posX + x, posY + y, color);
}
}
void unifont_DrawString(int posX, int posY, const HelosGraphics_Color *color, const uint32_t *codepoints, int count) {
for (const uint32_t *end = codepoints + count; codepoints != end; codepoints++) {
unifont_DrawChar(posX, posY, color, *codepoints);
posX += UNIFONT_CHAR_WIDTH * (unifont_IsCharDoublewidth(*codepoints) ? 2 : 1);
}
}
void unifont_DrawStringUTF16(int posX, int posY, const HelosGraphics_Color *color, const uint16_t *codepoints, int count) {
for (const uint16_t *end = codepoints + count; codepoints != end; codepoints++) {
unifont_DrawChar(posX, posY, color, *codepoints);
posX += UNIFONT_CHAR_WIDTH * (unifont_IsCharDoublewidth(*codepoints) ? 2 : 1);
}
}
void unifont_DrawStringASCII(int posX, int posY, const HelosGraphics_Color *color, const char *codepoints, int count) {
if (count == 0) {
count = strlen(codepoints);
}
for (const char *end = codepoints + count; codepoints != end; codepoints++) {
unifont_DrawChar(posX, posY, color, *codepoints);
//posX += UNIFONT_CHAR_WIDTH * (unifont_IsCharDoublewidth(*codepoints) ? 2 : 1);
posX += UNIFONT_CHAR_WIDTH; // visible ASCII chars are all single-width
}
}

29
graphics/unifont.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include "../main.h"
#include "graphics.h"
#include <stdbool.h>
#define UNIFONT_MAX_CHAR (0xffff)
#define UNIFONT_CHAR_COUNT (0xffff + 1)
#define UNIFONT_CHAR_WIDTH 8
#define UNIFONT_CHAR_HEIGHT 16
extern const unsigned char unifont_Data[], unifont_Width[];
extern const unsigned char unifont_Data_End[], unifont_Width_End[]; // Past-the-end pointers for the data files
static inline bool unifont_IsCharDoublewidth(uint32_t codepoint) {
const unsigned char *ptr = unifont_Width + codepoint / 8;
if (ptr < unifont_Width_End)
return (*ptr) & (1u << (7 - codepoint % 8));
else
return false;
}
void unifont_DrawChar(int posX, int posY, const HelosGraphics_Color *color, uint32_t codepoint);
void unifont_DrawString(int posX, int posY, const HelosGraphics_Color *color, const uint32_t *codepoints, int count);
void unifont_DrawStringUTF16(int posX, int posY, const HelosGraphics_Color *color, const uint16_t *codepoints, int count);
void unifont_DrawStringASCII(int posX, int posY, const HelosGraphics_Color *color, const char *codepoints, int count);

View File

@ -0,0 +1,71 @@
#pragma once
#include "../../main.h"
#include "../graphics.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// XCursor file format - see man page xcursor(3)
// https://www.x.org/releases/X11R7.7/doc/man/man3/Xcursor.3.xhtml
// Header
typedef struct {
char magic[4]; // Magic string "Xcur"
uint32_t headerSize; // Size of the header (16)
uint32_t version; // File version number
uint32_t numTOC; // Number of entries in the Table of Contents
} PACKED xcursor_Header;
// Table of Content Entry
typedef struct {
uint32_t type; // Entry chunk type (0xfffe0001 = Comment, 0xfffd0002 = Image)
uint32_t subtype; // Type specific Subtype, size(width=height) for images
uint32_t offset; // Absolute byte position in the file
} PACKED xcursor_TOCEntry;
#define XCURSOR_CHUNKTYPE_COMMENT 0xfffe0001u
#define XCURSOR_CHUNKTYPE_IMAGE 0xfffd0002u
// Common parts in different types of Chunk Headers
typedef struct {
uint32_t headerSize; // Size of the header
uint32_t type; // Type of the Chunk, matches the Entry Type in the TOC
uint32_t subtype; // Type specific subtype
uint32_t version; // Version number of the chunk type
} PACKED xcursor_ChunkHeader;
#define XCURSOR_COMMENT_SUBTYPE_COPYRIGHT 0x00000001u
#define XCURSOR_COMMENT_SUBTYPE_LICENSE 0x00000002u
#define XCURSOR_COMMENT_SUBTYPE_OTHER 0x00000003u
// Chunk Header for type Comment
typedef struct {
uint32_t headerSize; // Size of the header
uint32_t type; // Type of the Chunk, matches the Entry Type in the TOC
uint32_t subtype; // Type specific subtype, Copyright, License or Other
uint32_t version; // Version number of the chunk type, =1
uint32_t length; // Length in bytes of the UTF-8 string
char string[1]; // The UTF-8 string, spanning the rest of the chunk
} PACKED xcursor_ChunkHeader_Comment;
typedef struct {
uint32_t headerSize; // Size of the header
uint32_t type; // Type of the Chunk, matches the Entry Type in the TOC
uint32_t subtype; // Type specific subtype, Copyright, License or Other
uint32_t version; // Version number of the chunk type, =1
uint32_t width, height; // Width/Height, <=0x7fff
uint32_t xhot, yhot; // X/Y hotpoint, <=Width/Height
uint32_t delay; // Delay between animation frames in milliseconds
HelosGraphics_Color pixels[1]; // Packed ARGB little-endian format pixels, with A at the highest byte (BGRA in byte order)
} PACKED xcursor_ChunkHeader_Image;
#ifdef __cplusplus
}
#endif

194
interrupt/handler.asm.S Normal file
View File

@ -0,0 +1,194 @@
format elf64
; sysvx64call void interrupt_Handler(a, b, c, d, e, f)
extrn interrupt_Handler
; sysvx64call void interrupt_Handler128(a, b, c, d, e, f)
; Input: rax(syscall opcode)
extrn interrupt_Handler128
extrn io_WriteConsoleASCII
public interrupt_Int0
public interrupt_Int1
public interrupt_Int2
public interrupt_Int3
public interrupt_Int4
public interrupt_Int5
public interrupt_Int6
public interrupt_Int7
public interrupt_Int8
public interrupt_Int9
public interrupt_Int10
public interrupt_Int11
public interrupt_Int12
public interrupt_Int13
public interrupt_Int14
public interrupt_Int15
public interrupt_Int16
public interrupt_Int17
public interrupt_Int18
public interrupt_Int19
public interrupt_Int20
public interrupt_Int21
public interrupt_Int22
public interrupt_Int23
public interrupt_Int24
public interrupt_Int25
public interrupt_Int26
public interrupt_Int27
public interrupt_Int28
public interrupt_Int29
public interrupt_Int30
public interrupt_Int31
public interrupt_Int128
section '.text' executable
macro inth op1 {
push rdi
mov rdi, op1
push rsi
push rdx
mov rdx, [rsp+24]
push rcx
push r8
push r9
push r10
push r11
push rax
call interrupt_Handler
pop rax
pop r11
pop r10
pop r9
pop r8
pop rcx
pop rdx
pop rsi
pop rdi
iretq
}
macro inth_err op1 {
push rsi
push rdi
mov rdi, op1
mov esi, [rsp+16]
push rdx
mov rdx, [rsp+32]
push rcx
push r8
push r9
push r10
push r11
push rax
call interrupt_Handler
pop rax
pop r11
pop r10
pop r9
pop r8
pop rcx
pop rdx
pop rdi
pop rsi
add rsp, 8 ; pop the error code
iretq
}
interrupt_Int0: ; does not return
inth 0
interrupt_Int1:
inth 1
interrupt_Int2:
inth 2
interrupt_Int3:
inth 3
interrupt_Int4:
inth 4
interrupt_Int5:
inth 5
interrupt_Int6:
inth 6
interrupt_Int7:
inth 7
interrupt_Int8:
inth 8
interrupt_Int9:
inth 9
interrupt_Int10:
inth_err 10
interrupt_Int11:
inth_err 11
interrupt_Int12:
inth_err 12
interrupt_Int13:
inth_err 13
interrupt_Int14:
inth_err 14
interrupt_Int15:
inth 15
interrupt_Int16:
inth 16
interrupt_Int17:
inth_err 17
interrupt_Int18:
inth 18
interrupt_Int19:
inth 19
interrupt_Int20:
inth 20
interrupt_Int21:
inth_err 21
interrupt_Int22:
inth 22
interrupt_Int23:
inth 23
interrupt_Int24:
inth 24
interrupt_Int25:
inth 25
interrupt_Int26:
inth 26
interrupt_Int27:
inth 27
interrupt_Int28:
inth 28
interrupt_Int29:
inth 29
interrupt_Int30:
inth 30
interrupt_Int31:
inth 31
interrupt_Int128:
;sub rsp, 32
;mov rcx, interrupt_string
;call io_WriteConsoleASCII
;add rsp, 32
;iretq
; no need to save the registers
;push rax
;push rdi
;push rsi
;push rdx
;push rcx
;push r8
;push r9
;push r10
;push r11
call interrupt_Handler128
;pop r11
;pop r10
;pop r9
;pop r8
;pop rcx
;pop rdx
;pop rsi
;pop rdi
;pop rax
iretq

52
interrupt/handler.c Normal file
View File

@ -0,0 +1,52 @@
#include "interrupt.h"
#include "../runtime/panic_assert.h"
const char *interrupt_Descriptions[] = {
"Divide Error Execption",
"Debug Exception",
"NMI Interrupt",
"Breakpoint Exception",
"Overflow Exception",
"BOUND Range Exceeded Exception",
"Invalid Opcode Exception",
"Device Not Available Exception",
"Double Fault Exception",
"Coprocessor Segment Overrun",
"Invalid TSS Exception",
"Segment Not Present",
"Stack Fault Exception",
"General Protection Exception",
"Page-Fault Exception",
"Interrupt 15",
"x87 FPU Floating-Point Error",
"Alignment Check Exception",
"Machine-Check Exception",
"SIMD Floating-Point Exception",
"Interrupt 20",
"Control Protection Exception",
"Interrupt 22",
"Interrupt 23",
"Interrupt 24",
"Interrupt 25",
"Interrupt 26",
"Interrupt 27",
"Interrupt 28",
"Interrupt 29",
"Interrupt 30",
"Interrupt 31",
};
SYSV_ABI void interrupt_Handler(int vec, int errcode, uint64_t rip, int c, int d, int e) {
io_Printf("Panic: INT %02xh: %s, err=%d(0x%02x), rip=%llx\n", vec, interrupt_Descriptions[vec], errcode, errcode, rip);
__Panic_HaltSystem();
}
// handler for INT 80h
SYSV_ABI void interrupt_Handler128(int a, int b, int c, int d, int e, int f) {
int opcode;
asm volatile("mov %%eax, %0"
: "=rm"(opcode)); // read the opcode
io_Printf("INT 80h: EAX(opcode)=%d, abcdef=[%d,%d,%d,%d,%d,%d]\n", opcode, a, b, c, d, e, f);
}

38
interrupt/handlers.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
// the functions are not to be called, but to be taken address and put into IDT
void interrupt_Int0();
void interrupt_Int1();
void interrupt_Int2();
void interrupt_Int3();
void interrupt_Int4();
void interrupt_Int5();
void interrupt_Int6();
void interrupt_Int7();
void interrupt_Int8();
void interrupt_Int9();
void interrupt_Int10();
void interrupt_Int11();
void interrupt_Int12();
void interrupt_Int13();
void interrupt_Int14();
void interrupt_Int15();
void interrupt_Int16();
void interrupt_Int17();
void interrupt_Int18();
void interrupt_Int19();
void interrupt_Int20();
void interrupt_Int21();
void interrupt_Int22();
void interrupt_Int23();
void interrupt_Int24();
void interrupt_Int25();
void interrupt_Int26();
void interrupt_Int27();
void interrupt_Int28();
void interrupt_Int29();
void interrupt_Int30();
void interrupt_Int31();
void interrupt_Int128();

118
interrupt/init.c Normal file
View File

@ -0,0 +1,118 @@
#include "interrupt.h"
#include "handlers.h"
#include "../memory/memory.h"
#include "../runtime/stdio.h"
#include "../runtime/panic_assert.h"
#include "testcode.h"
interrupt_DescriptorTableReference *interrupt_IDTR, *interrupt_GDTR;
bool interrupt_Enabled;
/*
SYSV_ABI void interrupt_MapHandler(void *handler, int interrupt) {
//io_Printf("interrupt_MapHandler: handler %llx, int %d\n", handler, interrupt);
uint64_t *base = (uint64_t *)(KERNEL_IDT_MAPPING + interrupt * 16ull);
uint64_t b = 0;
b |= (uint64_t)handler & 0xFFFFull; // Offset[15:0], 15:0
b |= (uint64_t)GDT_EXEC_SELECTOR << 16; // Segment Selector, 31:16
b |= IDT_TYPE_32_INTERRUPT_GATE; // Type = 32-bit Interrupt Gate, 44:40
b |= IDT_RING0; // Ring 0, 46:45
b |= IDT_PRESENT; // Present, 47
b |= ((uint64_t)handler & 0xFFFF0000ull) << 32; // Offset[31:16], 63:48
*base = b;
*(base + 1) = (uint64_t)handler >> 32;
}
*/
// defined in assembly
SYSV_ABI void interrupt_MapHandler(void *handler, int interrupt);
void interrupt_Init() {
assert(sizeof(interrupt_DescriptorTableReference) == 10 && "GDTR/IDTR size must be 10 bytes");
assert(offsetof(interrupt_DescriptorTableReference, base) == 2 && "GDTR/IDTR must be packed");
assert(KERNEL_IDTR_MAPPING % 4 == 0 && "IDTR not aligned to 4-byte");
assert(KERNEL_GDTR_MAPPING % 4 == 0 && "GDTR not aligned to 4-byte");
// allocate GDTR
io_WriteConsoleASCII("interrupt_Init() calling\n");
interrupt_GDTR = (interrupt_DescriptorTableReference *)KERNEL_GDTR_MAPPING;
interrupt_GDTR->length = 4 * GDT_SIZE_BYTES - 1;
interrupt_GDTR->base = (void *)KERNEL_GDT_MAPPING;
io_WriteConsoleASCII("GDTR Written\n");
// set the 2 dummy gdts
uint64_t *gdt = (uint64_t *)KERNEL_GDT_MAPPING;
gdt[0] = 0;
gdt[1] = GDT_EXEC;
gdt[2] = GDT_DATA;
gdt[3] = GDT_EXEC_RING3;
gdt[4] = GDT_DATA_RING3;
io_WriteConsoleASCII("GDT Installed\n");
interrupt_LoadGDT(interrupt_GDTR); // set it!
io_WriteConsoleASCII("GDT OK\n");
//interrupt_Testcode();
io_WriteConsoleASCII("Testcode OK\n");
// allocate IDTR
//interrupt_IDTR = kMalloc(sizeof(interrupt_DescriptorTableReference));
interrupt_IDTR = (interrupt_DescriptorTableReference *)KERNEL_IDTR_MAPPING;
interrupt_IDTR->length = KERNEL_IDT_SIZE - 1;
interrupt_IDTR->base = (void *)KERNEL_IDT_MAPPING;
io_WriteConsoleASCII("IDT Written\n");
interrupt_MapHandler(interrupt_Int0, 0);
interrupt_MapHandler(interrupt_Int1, 1);
interrupt_MapHandler(interrupt_Int2, 2);
interrupt_MapHandler(interrupt_Int3, 3);
interrupt_MapHandler(interrupt_Int4, 4);
interrupt_MapHandler(interrupt_Int5, 5);
interrupt_MapHandler(interrupt_Int6, 6);
interrupt_MapHandler(interrupt_Int7, 7);
interrupt_MapHandler(interrupt_Int8, 8);
interrupt_MapHandler(interrupt_Int9, 9);
interrupt_MapHandler(interrupt_Int10, 10);
interrupt_MapHandler(interrupt_Int11, 11);
interrupt_MapHandler(interrupt_Int12, 12);
interrupt_MapHandler(interrupt_Int13, 13);
interrupt_MapHandler(interrupt_Int14, 14);
interrupt_MapHandler(interrupt_Int15, 15);
interrupt_MapHandler(interrupt_Int16, 16);
interrupt_MapHandler(interrupt_Int17, 17);
interrupt_MapHandler(interrupt_Int18, 18);
interrupt_MapHandler(interrupt_Int19, 19);
interrupt_MapHandler(interrupt_Int20, 20);
interrupt_MapHandler(interrupt_Int21, 21);
interrupt_MapHandler(interrupt_Int22, 22);
interrupt_MapHandler(interrupt_Int23, 23);
interrupt_MapHandler(interrupt_Int24, 24);
interrupt_MapHandler(interrupt_Int25, 25);
interrupt_MapHandler(interrupt_Int26, 26);
interrupt_MapHandler(interrupt_Int27, 27);
interrupt_MapHandler(interrupt_Int28, 28);
interrupt_MapHandler(interrupt_Int29, 29);
interrupt_MapHandler(interrupt_Int30, 30);
interrupt_MapHandler(interrupt_Int31, 31);
interrupt_MapHandler(interrupt_Int128, 128);
io_WriteConsoleASCII("IDT Installed\n");
interrupt_LoadIDT(interrupt_IDTR); // set it!
io_WriteConsoleASCII("IDT OK\n");
interrupt_Enabled = true;
asm volatile("sti");
interrupt_ReloadSegments();
io_WriteConsoleASCII("Segment Registers Reloaded\n");
}

74
interrupt/interrupt.h Normal file
View File

@ -0,0 +1,74 @@
#pragma once
#include "../main.h"
#include "stdbool.h"
#ifdef __cplusplus
extern "C" {
#endif
#define GDT_SIZE_BYTES 4
#define GDT_EXEC 0x00AF9A000000FFFFull // Base=0, Limit=max, Access=Present|Ring0|TypeUser|Exec|Readable, Flag=GranularityPage|Long
#define GDT_DATA 0x00EF92000000FFFFull // Base=0, Limit=max, Access=Present|Ring0|TypeUser|Writable, Flag=GranularityPage|Size
#define GDT_EXEC_RING3 0x00AFFA000000FFFFull // Base=0, Limit=max, Access=Present|Ring3|TypeUser|Exec|Readable, Flag=GranularityPage|Long
#define GDT_DATA_RING3 0x00EFF2000000FFFFull // Base=0, Limit=max, Access=Present|Ring3|TypeUser|Writable, Flag=GranularityPage|Size
#define GDT_EXEC_SELECTOR 0x08 // SelectorIndex=1, TableIndicator=GDT(0), Privilege=Ring0
#define GDT_DATA_SELECTOR 0x10 // SelectorIndex=2, TableIndicator=GDT(0), Privilege=Ring0
#define GDT_EXEC_RING3_SELECTOR 0x1B // SelectorIndex=3, TableIndicator=GDT(0), Privilege=Ring3
#define GDT_DATA_RING3_SELECTOR 0x23 // SelectorIndex=4, TableIndicator=GDT(0), Privilege=Ring3
#define IDT_PRESENT (1ull << 47)
#define IDT_RING0 0
#define IDT_RING1 (1ull << 45)
#define IDT_RING2 (2ull << 45)
#define IDT_RING3 (3ull << 45)
#define IDT_TYPE_32_CALL_GATE (0x0Cull << 40)
#define IDT_TYPE_32_INTERRUPT_GATE (0x0Eull << 40)
#define IDT_TYPE_32_TRAP_GATE (0x0Full << 40)
typedef struct {
uint16_t length;
void * base;
} PACKED interrupt_DescriptorTableReference;
// address of IDTR and GDTR, allocated by kMalloc() and is never freed
extern interrupt_DescriptorTableReference *interrupt_IDTR, *interrupt_GDTR;
// true if Init() has been called and interrupt handling is on
extern bool interrupt_Enabled;
// initializes interrupt handling like IDT and a dummy GDT
void interrupt_Init();
SYSV_ABI void interrupt_MapHandler(void *handler, int interrupt);
// errorcode is 0 if nonexistent
//
// for IRQs, params are documented in assembly
SYSV_ABI void interrupt_Handler(int vec, int errcode, uint64_t rip, int c, int d, int e);
// defined in assembly
SYSV_ABI void interrupt_LoadGDT(void *gdtr);
SYSV_ABI void interrupt_LoadIDT(void *idtr);
SYSV_ABI void interrupt_ReloadSegments();
#define INTERRUPT_DISABLE \
uintptr_t __interrupt_flags; \
asm volatile("pushf\n\tcli\n\tpop %0" \
: "=r"(__interrupt_flags) \
: \
: "memory")
#define INTERRUPT_RESTORE \
asm volatile("push %0\n\tpopf" \
: \
: "rm"(__interrupt_flags) \
: "memory", "cc")
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,57 @@
format elf64
extrn io_WriteConsoleASCII
section '.rodata'
interrupt_string:
db "Interrupt Testcode", 0x0A, 0x00
section '.data' writable
align 4
idt:
rb 50*16
idtr:
dw (50*16)-1
dq idt
section '.text' executable
int_handler:
push rax
push rcx
push rdx
push r8
push r9
push r10
push r11
sub rsp, 32
mov rcx, interrupt_string
call io_WriteConsoleASCII
add rsp, 32
pop r11
pop r10
pop r9
pop r8
pop rdx
pop rcx
pop rax
iretq
public interrupt_Testcode
interrupt_Testcode:
lidt [idtr]
mov rax, int_handler
mov [idt+49*16], ax
mov word [idt+49*16+2], 0x08
mov word [idt+49*16+4], 0x8e00
shr rax, 16
mov [idt+49*16+6], ax
shr rax, 16
mov [idt+49*16+8], rax
int 49
ret

45
interrupt/load_gdt.S Normal file
View File

@ -0,0 +1,45 @@
format elf64
public interrupt_ReloadSegments
public interrupt_LoadGDT
public interrupt_LoadIDT
section '.text' executable
; sysvx64call void interrupt_LoadGDT(void* gdtr)
;
; Input: (void* rdi)
; Clobbers: none
interrupt_LoadGDT:
lgdt [rdi]
ret
; sysvx64call void interrupt_ReloadSegments()
;
; Clobbers: rax
interrupt_ReloadSegments:
mov eax, 0x10 ; my data segment
xor ax, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
;jmp 0x08:.flush
; as in https://forum.osdev.org/viewtopic.php?f=1&t=30739
; farjump does not work in long mode, you need to do a far return:
pop rax
push qword 0x08 ; my code segment
push rax
retfq
; sysvx64call void interrupt_LoadIDT(void* idtr)
;
; Input: (void* rdi)
; Clobbers: none
interrupt_LoadIDT:
lidt [rdi]
ret

25
interrupt/map_handler.S Normal file
View File

@ -0,0 +1,25 @@
format elf64
extrn io_WriteConsoleASCII
public interrupt_MapHandler
section '.text' executable
; sysvx64call void interrupt_MapHandler(uint64_t handler, int interrupt)
;
; Input: (uint64_t rdi, int rsi)
; Clobbers: rax, flags
interrupt_MapHandler:
mov rax, 0xFFFFFFFEC0000000 ; KERNEL_IDT_MAPPING
shl rsi, 4 ; rsi *= 16
add rsi, rax ; rsi += KERNEL_IDT_MAPPING
mov [rsi], di
mov word [rsi+2], 0x08 ; GDT_EXEC_SELECTOR (index=1)
mov word [rsi+4], 0x8e00
shr rdi, 16
mov [rsi+6], di
shr rdi, 16
mov [rsi+8], rdi
ret

18
interrupt/syscall.S Normal file
View File

@ -0,0 +1,18 @@
format elf64
public asm_Syscall as 'Syscall' ; syscall is a reserved token
section ".text" executable
; sysvx64call int Syscall(int syscall_id, int a,b,c,d,e,f)
asm_Syscall:
mov rax, rdi
mov rdi, rsi
mov rsi, rdx
mov rdx, rcx
mov rcx, r8
mov r8, r9
mov r9, [rsp+8]
int 0x80
ret

8
interrupt/syscall.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include "../main.h"
// this function is here, well, mostly just for fun.
//
// userspace in the far future should need this
SYSV_ABI long Syscall(int id, long a, long b, long c, long d, long e, long f);

6
interrupt/testcode.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "../main.h"
SYSV_ABI void interrupt_Testcode();

51
kernel/kmain.c Normal file
View File

@ -0,0 +1,51 @@
#include "../main.h"
#include "kmain.h"
#include "../runtime/stdio.h"
#include "../runtime/panic_assert.h"
#include "../memory/memory.h"
#include "../memory/paging_internal.h"
#include "../interrupt/interrupt.h"
#include "../interrupt/handlers.h"
#include "../interrupt/syscall.h"
#include "../driver/irq/pic/pic.h"
#include "../driver/irq/pic/ps2/ps2.h"
#include "../execformat/pe/reloc.h"
void execformat_pe_ReadSystemHeader(execformat_pe_PortableExecutable *pe);
static void tellRIP() {
uint64_t a, b;
asm volatile("leaq (%%rip), %0\n\tleaq runtime_InitPaging(%%rip), %1"
: "=r"(a), "=r"(b));
io_Printf("tellRIP(): Stack position: %llx, RIP=%llx, kMain_StackPosition:%llx(%llx), interrupt_Int128: %llx\n", &a, a, (uint64_t)&kMain_StackPosition, b, (uint64_t)interrupt_Int128);
}
SYSV_ABI void kMain() {
io_WriteConsoleASCII("Yes! kMain survived!\n");
uint64_t a;
asm volatile("leaq (%%rip), %0"
: "=r"(a));
io_Printf("Stack position: %llx, RIP=%llx, runtime_InitPaging:%llx, interrupt_Int128: %llx\n", &a, a, (uint64_t)runtime_InitPaging, (uint64_t)interrupt_Int128);
interrupt_Init();
io_WriteConsoleASCII("Interrupts initialized\n");
Syscall(4, 1, 2, 3, 4, 5, 6);
io_WriteConsoleASCII("Returning from Syscall()\n");
tellRIP();
irq_pic_Init();
io_WriteConsoleASCII("PIC IRQ OK\n");
irq_pic_ps2_Init();
io_WriteConsoleASCII("PIC PS/2 OK\n");
for (;;) {
asm volatile("hlt");
io_WriteConsoleASCII("kMain: Interrupt hit\n");
}
}

28
kernel/kmain.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include "../main.h"
#ifdef __cplusplus
extern "C" {
#endif
// set the position of the top of stack before calling kMain_Init()
extern uint64_t kMain_StackPosition;
extern char kMain_StackData[], kMain_StackData_End[];
typedef SYSV_ABI void (*kMainType)();
// written in Assembly, this function deals with stack, registers, etc, and then calls kMain.
//
// remember setting kMain_StackPosition before calling kMain_Init()
SYSV_ABI noreturn void kMain_Init();
// this is the real main function.
// it should only be called by kMain_Init()
SYSV_ABI void kMain();
#ifdef __cplusplus
} // extern "C"
#endif

25
kernel/kmain.init.S Normal file
View File

@ -0,0 +1,25 @@
format elf64
extrn kMain
public kMain_StackPosition
public kMain_Init
section '.bss' writable
kMain_StackPosition:
rq 1
section '.text' executable
; sysvx64call void kMain_Init()
kMain_Init:
mov rsp, [kMain_StackPosition]
call kMain
.hlt:
hlt
jmp .hlt

11
libc/README Normal file
View File

@ -0,0 +1,11 @@
The C library code here mostly comes from The Public Domain C Library (pdclib).
Get it here: https://rootdirectory.de/doku.php?id=pdclib:start
and here's the repo: https://github.com/DevSolar/pdclib
The original code is licensed under CC0 (Public Domain), and I'm really having a hard time
keeping track which file has been modified and how, etc.

9
libc/abs.c Normal file
View File

@ -0,0 +1,9 @@
#include <stdint.h>
int abs(int val) {
if (val < 0)
return -val;
else
return val;
}

50
libc/include/assert.h Normal file
View File

@ -0,0 +1,50 @@
/* 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.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_internal.h"
#ifndef _PDCLIB_ASSERT_H
#define _PDCLIB_ASSERT_H _PDCLIB_ASSERT_H
_PDCLIB_PUBLIC void _PDCLIB_assert99( const char * const, const char * const, const char * const );
_PDCLIB_PUBLIC 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_value2string( __LINE__ ) \
".\n" ) )
#else
#define assert( expression ) ( ( expression ) ? (void) 0 \
: _PDCLIB_assert89( "Assertion failed: " #expression \
", file " __FILE__ \
", line " _PDCLIB_value2string( __LINE__ ) \
".\n" ) )
#endif
#endif
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_ASSERT_H
#include _PDCLIB_EXTEND_ASSERT_H
#endif
#ifdef __cplusplus
}
#endif

110
libc/include/ctype.h Normal file
View File

@ -0,0 +1,110 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_internal.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 ) */
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC int isalpha( int c );
/* Returns true if the character isspace() and used for separating words within
a line of text. In the "C" locale, only ' ' and '\t' are considered blanks.
*/
_PDCLIB_PUBLIC int isblank( int c );
/* Returns true if the character is a control character. */
_PDCLIB_PUBLIC int iscntrl( int c );
/* Returns true if the character is a decimal digit. Locale-independent. */
_PDCLIB_PUBLIC 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 ' '.
*/
_PDCLIB_PUBLIC 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().
*/
_PDCLIB_PUBLIC int islower( int c );
/* Returns true for every printing character including space (' '). */
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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().
*/
_PDCLIB_PUBLIC int isupper( int c );
/* Returns true for any hexadecimal-digit character. Locale-independent. */
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC int tolower( int c );
/* Converts a lowercase letter to a corresponding uppercase letter. Input that
is not a lowercase letter remains unchanged.
*/
_PDCLIB_PUBLIC int toupper( int c );
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_CTYPE_H
#include _PDCLIB_EXTEND_CTYPE_H
#endif
#ifdef __cplusplus
}
#endif
#endif

202
libc/include/errno.h Normal file
View File

@ -0,0 +1,202 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_lib_ext1.h"
#include "pdclib/_PDCLIB_internal.h"
/* FIXME: With <threads.h>, this needs to be in thread-specific storage. */
#define errno (*_PDCLIB_errno_func())
/* C only requires the following three */
/* Result too large */
#define ERANGE _PDCLIB_ERANGE
/* Mathematics argument out of domain of function */
#define EDOM _PDCLIB_EDOM
/* Illegal byte sequence */
#define EILSEQ _PDCLIB_EILSEQ
/* C++ additionally requires the folloing */
/* Argument list too long */
#define E2BIG _PDCLIB_E2BIG
/* Permission denied */
#define EACCES _PDCLIB_EACCES
/* Address in use */
#define EADDRINUSE _PDCLIB_EADDRINUSE
/* Address not available */
#define EADDRNOTAVAIL _PDCLIB_EADDRNOTAVAIL
/* Address family not supported */
#define EAFNOSUPPORT _PDCLIB_EAFNOSUPPORT
/* Resource unavailable, try again */
#define EAGAIN _PDCLIB_EAGAIN
/* Connection already in progress */
#define EALREADY _PDCLIB_EALREADY
/* Bad file descriptor */
#define EBADF _PDCLIB_EBADF
/* Bad message */
#define EBADMSG _PDCLIB_EBADMSG
/* Device or resource busy */
#define EBUSY _PDCLIB_EBUSY
/* Operation canceled */
#define ECANCELED _PDCLIB_ECANCELED
/* No child processes */
#define ECHILD _PDCLIB_ECHILD
/* Connection aborted */
#define ECONNABORTED _PDCLIB_ECONNABORTED
/* Connection refused */
#define ECONNREFUSED _PDCLIB_ECONNREFUSED
/* Connection reset */
#define ECONNRESET _PDCLIB_ECONNRESET
/* Resource deadlock would occur */
#define EDEADLK _PDCLIB_EDEADLK
/* Destination address required */
#define EDESTADDRREQ _PDCLIB_EDESTADDRREQ
/* File exists */
#define EEXIST _PDCLIB_EEXIST
/* Bad address */
#define EFAULT _PDCLIB_EFAULT
/* File too large */
#define EFBIG _PDCLIB_EFBIG
/* Host is unreachable */
#define EHOSTUNREACH _PDCLIB_EHOSTUNREACH
/* Identifier removed */
#define EIDRM _PDCLIB_EIDRM
/* Operation in progress */
#define EINPROGRESS _PDCLIB_EINPROGRESS
/* Interrupted function */
#define EINTR _PDCLIB_EINTR
/* Invalid argument */
#define EINVAL _PDCLIB_EINVAL
/* I/O error */
#define EIO _PDCLIB_EIO
/* Socket is connected */
#define EISCONN _PDCLIB_EISCONN
/* Is a directory */
#define EISDIR _PDCLIB_EISDIR
/* Too many levels of symbolic links */
#define ELOOP _PDCLIB_ELOOP
/* File descriptor value too large */
#define EMFILE _PDCLIB_EMFILE
/* Too many links */
#define EMLINK _PDCLIB_EMLINK
/* Message too large */
#define EMSGSIZE _PDCLIB_EMSGSIZE
/* Filename too long */
#define ENAMETOOLONG _PDCLIB_ENAMETOOLONG
/* Network is down */
#define ENETDOWN _PDCLIB_ENETDOWN
/* Connection aborted by network */
#define ENETRESET _PDCLIB_ENETRESET
/* Network unreachable */
#define ENETUNREACH _PDCLIB_ENETUNREACH
/* Too many files open in system */
#define ENFILE _PDCLIB_ENFILE
/* No buffer space available */
#define ENOBUFS _PDCLIB_ENOBUFS
/* No message is available on the STREAM head read queue */
#define ENODATA _PDCLIB_ENODATA
/* No such device */
#define ENODEV _PDCLIB_ENODEV
/* No such file or directory */
#define ENOENT _PDCLIB_ENOENT
/* Executable file format error */
#define ENOEXEC _PDCLIB_ENOEXEC
/* No locks available */
#define ENOLCK _PDCLIB_ENOLCK
/* Link has been severed */
#define ENOLINK _PDCLIB_ENOLINK
/* Not enough space */
#define ENOMEM _PDCLIB_ENOMEM
/* No message of the desired type */
#define ENOMSG _PDCLIB_ENOMSG
/* Protocol not available */
#define ENOPROTOOPT _PDCLIB_ENOPROTOOPT
/* No space left on device */
#define ENOSPC _PDCLIB_ENOSPC
/* No STREAM resources */
#define ENOSR _PDCLIB_ENOSR
/* Not a STREAM */
#define ENOSTR _PDCLIB_ENOSTR
/* Function not supported */
#define ENOSYS _PDCLIB_ENOSYS
/* The socket is not connected */
#define ENOTCONN _PDCLIB_ENOTCONN
/* Not a directory */
#define ENOTDIR _PDCLIB_ENOTDIR
/* Directory not empty */
#define ENOTEMPTY _PDCLIB_ENOTEMPTY
/* State not recoverable */
#define ENOTRECOVERABLE _PDCLIB_ENOTRECOVERABLE
/* Not a socket */
#define ENOTSOCK _PDCLIB_ENOTSOCK
/* Not supported */
#define ENOTSUP _PDCLIB_ENOTSUP
/* Inappropriate I/O control operation */
#define ENOTTY _PDCLIB_ENOTTY
/* No such device or address */
#define ENXIO _PDCLIB_ENXIO
/* Operation not supported on socket */
#define EOPNOTSUPP _PDCLIB_EOPNOTSUPP
/* Value too large to be stored in data type */
#define EOVERFLOW _PDCLIB_EOVERFLOW
/* Previous owner died */
#define EOWNERDEAD _PDCLIB_EOWNERDEAD
/* Operation not permitted */
#define EPERM _PDCLIB_EPERM
/* Broken pipe */
#define EPIPE _PDCLIB_EPIPE
/* Protocol error */
#define EPROTO _PDCLIB_EPROTO
/* Protocol not supported */
#define EPROTONOSUPPORT _PDCLIB_EPROTONOSUPPORT
/* Protocol wrong type for socket */
#define EPROTOTYPE _PDCLIB_EPROTOTYPE
/* Read-only file system */
#define EROFS _PDCLIB_EROFS
/* Invalid seek */
#define ESPIPE _PDCLIB_ESPIPE
/* No such process */
#define ESRCH _PDCLIB_ESRCH
/* Stream ioctl() timeout */
#define ETIME _PDCLIB_ETIME
/* Connection timed out */
#define ETIMEDOUT _PDCLIB_ETIMEDOUT
/* Text file busy */
#define ETXTBSY _PDCLIB_ETXTBSY
/* Operation would block */
#define EWOULDBLOCK _PDCLIB_EWOULDBLOCK
/* Cross-device link */
#define EXDEV _PDCLIB_EXDEV
/* Annex K -- Bounds-checking interfaces */
#if ( __STDC_WANT_LIB_EXT1__ + 0 ) != 0
#ifndef _PDCLIB_ERRNO_T_DEFINED
#define _PDCLIB_ERRNO_T_DEFINED _PDCLIB_ERRNO_T_DEFINED
typedef int errno_t;
#endif
#endif
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_ERRNO_H
#include _PDCLIB_EXTEND_ERRNO_H
#endif
#ifdef __cplusplus
}
#endif
#endif

153
libc/include/float.h Normal file
View File

@ -0,0 +1,153 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_internal.h"
/* The following parameters are used to define the model for each
floating-point type:
s sign (±1)
b base or radix of exponent representation (an integer > 1)
e exponent (an integer between a minimum eₘₙ and a maximum eₘₓ)
p precision (the number of base-b digits in the significand)
ƒ nonnegative integers less than b (the significand digits)
A floating-point number (x) is defined by the following model:
x = sbᵉ ƒb, eₘₙ e eₘₓ
In addition to normalized floating-point numbers (ƒ > 0 if x 0),
floating types may be able to contain other kinds of floating-point
numbers, such as subnormal floating-point numbers (x 0, e = eₘₙ,
ƒ = 0) and unnormalized floating-point numbers (x 0, e > eₘₙ,
ƒ = 0), and values that are not floating-point numbers, such as
infinities and NaNs.
*/
/* Whether rounding toward zero (0), to nearest (1), toward positive
infinity (2), toward negative infinity (3), or indeterminate (-1).
FLT_ROUNDS is not a compile-time constant, and may change due to
calls to fesetround() (in <fenv.h>).
*/
#define FLT_ROUNDS _PDCLIB_FLT_ROUNDS
/* Whether operations are done in the given type (0), float is
evaluated as double (1), float and double are evaluated as
long double (2), or evaluation method is indeterminate (-1).
*/
#define FLT_EVAL_METHOD _PDCLIB_FLT_EVAL_METHOD
/* Whether the type supports subnormal numbers (1), does not support
them (0), or support is indeterminate (-1).
*/
#define FLT_HAS_SUBNORM _PDCLIB_FLT_HAS_SUBNORM
#define DBL_HAS_SUBNORM _PDCLIB_DBL_HAS_SUBNORM
#define LDBL_HAS_SUBNORM _PDCLIB_LDBL_HAS_SUBNORM
/* Radix of exponent representation, b */
#define FLT_RADIX _PDCLIB_FLT_RADIX
/* Number of base-b digits in the floating point significand, p */
#define FLT_MANT_DIG _PDCLIB_FLT_MANT_DIG
#define DBL_MANT_DIG _PDCLIB_DBL_MANT_DIG
#define LDBL_MANT_DIG _PDCLIB_LDBL_MANT_DIG
/* Number of decimal digits, n, so that any floating point number with
p radix b digits can be rounded to a floating point number with n
decimal digits and back without changing the value
pₘₓlogb if b is a power of 10,
1 + pₘₓlogb otherwise.
*/
#define FLT_DECIMAL_DIG _PDCLIB_FLT_DECIMAL_DIG
#define DBL_DECIMAL_DIG _PDCLIB_DBL_DECIMAL_DIG
#define LDBL_DECIMAL_DIG _PDCLIB_LDBL_DECIMAL_DIG
/* As above, for the widest supported type. */
#define DECIMAL_DIG _PDCLIB_DECIMAL_DIG
/* Number of decimal digits, q, so that any floating point number with
q decimal digits can be rounded to a floating point number with p
radix b digits and back without changing the value of the q decimal
digits.
p logb if b is a power of 10,
(p - 1)logb otherwise.
*/
#define FLT_DIG _PDCLIB_FLT_DIG
#define DBL_DIG _PDCLIB_DBL_DIG
#define LDBL_DIG _PDCLIB_LDBL_DIG
/* Minimum negative integer such that FLT_RADIX raised to one less
than that power is a normalized floating point number, eₘₙ
*/
#define FLT_MIN_EXP _PDCLIB_FLT_MIN_EXP
#define DBL_MIN_EXP _PDCLIB_DBL_MIN_EXP
#define LDBL_MIN_EXP _PDCLIB_LDBL_MIN_EXP
/* Minimum negative integer such that 10 raised to one less than that
power is in the range of normalized floating point numbers,
logb^{eₘₙ¹}
*/
#define FLT_MIN_10_EXP _PDCLIB_FLT_MIN_10_EXP
#define DBL_MIN_10_EXP _PDCLIB_DBL_MIN_10_EXP
#define LDBL_MIN_10_EXP _PDCLIB_LDBL_MIN_10_EXP
/* Maximum integer such that FLT_RADIX raised to one less than that
power is a representable finite floating point number, eₘₓ
*/
#define FLT_MAX_EXP _PDCLIB_FLT_MAX_EXP
#define DBL_MAX_EXP _PDCLIB_DBL_MAX_EXP
#define LDBL_MAX_EXP _PDCLIB_LDBL_MAX_EXP
/* Maximum integer such that 10 raised to that power is in the range
of representable finite floating-point numbers,
log((1-b)b^{eₘₓ})
*/
#define FLT_MAX_10_EXP _PDCLIB_FLT_MAX_10_EXP
#define DBL_MAX_10_EXP _PDCLIB_DBL_MAX_10_EXP
#define LDBL_MAX_10_EXP _PDCLIB_LDBL_MAX_10_EXP
/* Maximum representable finite floating-point number, (1-b⁻ᵖ)b^{eₘₓ}
*/
#define FLT_MAX _PDCLIB_FLT_MAX
#define DBL_MAX _PDCLIB_DBL_MAX
#define LDBL_MAX _PDCLIB_LDBL_MAX
/* Difference between 1 and the least value greater than 1 that is
representable in the type, b¹
*/
#define FLT_EPSILON _PDCLIB_FLT_EPSILON
#define DBL_EPSILON _PDCLIB_DBL_EPSILON
#define LDBL_EPSILON _PDCLIB_LDBL_EPSILON
/* Minimum normalized positive floating-point number, b^{eₘₙ⁻¹} */
#define FLT_MIN _PDCLIB_FLT_MIN
#define DBL_MIN _PDCLIB_DBL_MIN
#define LDBL_MIN _PDCLIB_LDBL_MIN
/* Minimum positive floating-point number */
#define FLT_TRUE_MIN _PDCLIB_FLT_TRUE_MIN
#define DBL_TRUE_MIN _PDCLIB_DBL_TRUE_MIN
#define LDBL_TRUE_MIN _PDCLIB_LDBL_TRUE_MIN
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_FLOAT_H
#include _PDCLIB_EXTEND_FLOAT_H
#endif
#ifdef __cplusplus
}
#endif
#endif

368
libc/include/inttypes.h Normal file
View File

@ -0,0 +1,368 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct _PDCLIB_imaxdiv_t imaxdiv_t;
/* 7.8.1 Macros for format specifiers */
/* The various leastN_t, fastN_t, intmax_t, and intptr_t types are typedefs
to native types. But the user does not know which ones, which gives some
problems when trying to *printf() / *scanf() those types. The various
macros defined below allow to give the correct conversion specifiers
without knowing the actual native type they represent.
*/
#if _PDCLIB_INT_LEAST8_MAX > _PDCLIB_INT_MAX
#define PRIdLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, d ) )
#define PRIiLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, i ) )
#define PRIoLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, o ) )
#define PRIuLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, u ) )
#define PRIxLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, x ) )
#define PRIXLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, X ) )
#else
#define PRIdLEAST8 "d"
#define PRIiLEAST8 "i"
#define PRIoLEAST8 "o"
#define PRIuLEAST8 "u"
#define PRIxLEAST8 "x"
#define PRIXLEAST8 "X"
#endif
#if _PDCLIB_INT_FAST8_MAX > _PDCLIB_INT_MAX
#define PRIdFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, d ) )
#define PRIiFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, i ) )
#define PRIoFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, o ) )
#define PRIuFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, u ) )
#define PRIxFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, x ) )
#define PRIXFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, X ) )
#else
#define PRIdFAST8 "d"
#define PRIiFAST8 "i"
#define PRIoFAST8 "o"
#define PRIuFAST8 "u"
#define PRIxFAST8 "x"
#define PRIXFAST8 "X"
#endif
#if _PDCLIB_INT_LEAST16_MAX > _PDCLIB_INT_MAX
#define PRIdLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, d ) )
#define PRIiLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, i ) )
#define PRIoLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, o ) )
#define PRIuLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, u ) )
#define PRIxLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, x ) )
#define PRIXLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, X ) )
#else
#define PRIdLEAST16 "d"
#define PRIiLEAST16 "i"
#define PRIoLEAST16 "o"
#define PRIuLEAST16 "u"
#define PRIxLEAST16 "x"
#define PRIXLEAST16 "X"
#endif
#if _PDCLIB_INT_FAST16_MAX > _PDCLIB_INT_MAX
#define PRIdFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, d ) )
#define PRIiFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, i ) )
#define PRIoFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, o ) )
#define PRIuFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, u ) )
#define PRIxFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, x ) )
#define PRIXFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, X ) )
#else
#define PRIdFAST16 "d"
#define PRIiFAST16 "i"
#define PRIoFAST16 "o"
#define PRIuFAST16 "u"
#define PRIxFAST16 "x"
#define PRIXFAST16 "X"
#endif
#if _PDCLIB_INT_LEAST32_MAX > _PDCLIB_INT_MAX
#define PRIdLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, d ) )
#define PRIiLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, i ) )
#define PRIoLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, o ) )
#define PRIuLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, u ) )
#define PRIxLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, x ) )
#define PRIXLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, X ) )
#else
#define PRIdLEAST32 "d"
#define PRIiLEAST32 "i"
#define PRIoLEAST32 "o"
#define PRIuLEAST32 "u"
#define PRIxLEAST32 "x"
#define PRIXLEAST32 "X"
#endif
#if _PDCLIB_INT_FAST32_MAX > _PDCLIB_INT_MAX
#define PRIdFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, d ) )
#define PRIiFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, i ) )
#define PRIoFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, o ) )
#define PRIuFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, u ) )
#define PRIxFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, x ) )
#define PRIXFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, X ) )
#else
#define PRIdFAST32 "d"
#define PRIiFAST32 "i"
#define PRIoFAST32 "o"
#define PRIuFAST32 "u"
#define PRIxFAST32 "x"
#define PRIXFAST32 "X"
#endif
#if _PDCLIB_INT_LEAST64_MAX > _PDCLIB_INT_MAX
#define PRIdLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, d ) )
#define PRIiLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, i ) )
#define PRIoLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, o ) )
#define PRIuLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, u ) )
#define PRIxLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, x ) )
#define PRIXLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, X ) )
#else
#define PRIdLEAST64 "d"
#define PRIiLEAST64 "i"
#define PRIoLEAST64 "o"
#define PRIuLEAST64 "u"
#define PRIxLEAST64 "x"
#define PRIXLEAST64 "X"
#endif
#if _PDCLIB_INT_FAST64_MAX > _PDCLIB_INT_MAX
#define PRIdFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, d ) )
#define PRIiFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, i ) )
#define PRIoFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, o ) )
#define PRIuFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, u ) )
#define PRIxFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, x ) )
#define PRIXFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, X ) )
#else
#define PRIdFAST64 "d"
#define PRIiFAST64 "i"
#define PRIoFAST64 "o"
#define PRIuFAST64 "u"
#define PRIxFAST64 "x"
#define PRIXFAST64 "X"
#endif
#if _PDCLIB_INTMAX_MAX > _PDCLIB_INT_MAX
#define PRIdMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, d ) )
#define PRIiMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, i ) )
#define PRIoMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, o ) )
#define PRIuMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, u ) )
#define PRIxMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, x ) )
#define PRIXMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, X ) )
#else
#define PRIdMAX "d"
#define PRIiMAX "i"
#define PRIoMAX "o"
#define PRIuMAX "u"
#define PRIxMAX "x"
#define PRIXMAX "X"
#endif
#if _PDCLIB_INTPTR_MAX > _PDCLIB_INT_MAX
#define PRIdPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, d ) )
#define PRIiPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, i ) )
#define PRIoPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, o ) )
#define PRIuPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, u ) )
#define PRIxPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, x ) )
#define PRIXPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, X ) )
#else
#define PRIdPTR "d"
#define PRIiPTR "i"
#define PRIoPTR "o"
#define PRIuPTR "u"
#define PRIxPTR "x"
#define PRIXPTR "X"
#endif
#define SCNdLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, d ) )
#define SCNiLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, i ) )
#define SCNoLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, o ) )
#define SCNuLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, u ) )
#define SCNxLEAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, x ) )
#define SCNdFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, d ) )
#define SCNiFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, i ) )
#define SCNoFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, o ) )
#define SCNuFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, u ) )
#define SCNxFAST8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST8_PREFIX, x ) )
#define SCNdLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, d ) )
#define SCNiLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, i ) )
#define SCNoLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, o ) )
#define SCNuLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, u ) )
#define SCNxLEAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, x ) )
#define SCNdFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, d ) )
#define SCNiFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, i ) )
#define SCNoFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, o ) )
#define SCNuFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, u ) )
#define SCNxFAST16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST16_PREFIX, x ) )
#define SCNdLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, d ) )
#define SCNiLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, i ) )
#define SCNoLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, o ) )
#define SCNuLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, u ) )
#define SCNxLEAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, x ) )
#define SCNdFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, d ) )
#define SCNiFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, i ) )
#define SCNoFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, o ) )
#define SCNuFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, u ) )
#define SCNxFAST32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST32_PREFIX, x ) )
#define SCNdLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, d ) )
#define SCNiLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, i ) )
#define SCNoLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, o ) )
#define SCNuLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, u ) )
#define SCNxLEAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, x ) )
#define SCNdFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, d ) )
#define SCNiFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, i ) )
#define SCNoFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, o ) )
#define SCNuFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, u ) )
#define SCNxFAST64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_FAST64_PREFIX, x ) )
#define SCNdMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, d ) )
#define SCNiMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, i ) )
#define SCNoMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, o ) )
#define SCNuMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, u ) )
#define SCNxMAX _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTMAX_PREFIX, x ) )
#define SCNdPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, d ) )
#define SCNiPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, i ) )
#define SCNoPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, o ) )
#define SCNuPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, u ) )
#define SCNxPTR _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INTPTR_PREFIX, x ) )
/* The exact-width types (int8_t, int16_t, ...) are *optional*, as not all
architectures support the necessary 8-bits-per-byte two's complement
native types.
*/
#if _PDCLIB_TWOS_COMPLEMENT == 1
#if _PDCLIB_INT_LEAST8_MAX == 0x7f
#if _PDCLIB_INT_LEAST8_MAX > _PDCLIB_INT_MAX
#define PRId8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, d ) )
#define PRIi8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, i ) )
#define PRIo8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, o ) )
#define PRIu8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, u ) )
#define PRIx8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, x ) )
#define PRIX8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, X ) )
#endif
#define SCNd8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, d ) )
#define SCNi8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, i ) )
#define SCNo8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, o ) )
#define SCNu8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, u ) )
#define SCNx8 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST8_PREFIX, x ) )
#endif
#if _PDCLIB_INT_LEAST16_MAX == 0x7fff
#if _PDCLIB_INT_LEAST16_MAX > _PDCLIB_INT_MAX
#define PRId16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, d ) )
#define PRIi16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, i ) )
#define PRIo16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, o ) )
#define PRIu16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, u ) )
#define PRIx16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, x ) )
#define PRIX16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, X ) )
#endif
#define SCNd16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, d ) )
#define SCNi16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, i ) )
#define SCNo16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, o ) )
#define SCNu16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, u ) )
#define SCNx16 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST16_PREFIX, x ) )
#endif
#if _PDCLIB_INT_LEAST32_MAX == 0x7fffffffl
#if _PDCLIB_INT_LEAST32_MAX > _PDCLIB_INT_MAX
#define PRId32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, d ) )
#define PRIi32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, i ) )
#define PRIo32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, o ) )
#define PRIu32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, u ) )
#define PRIx32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, x ) )
#define PRIX32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, X ) )
#endif
#define SCNd32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, d ) )
#define SCNi32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, i ) )
#define SCNo32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, o ) )
#define SCNu32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, u ) )
#define SCNx32 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST32_PREFIX, x ) )
#endif
#if _PDCLIB_INT_LEAST64_MAX == 0x7fffffffffffffffll
#if _PDCLIB_INT_LEAST64_MAX > _PDCLIB_INT_MAX
#define PRId64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, d ) )
#define PRIi64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, i ) )
#define PRIo64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, o ) )
#define PRIu64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, u ) )
#define PRIx64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, x ) )
#define PRIX64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, X ) )
#endif
#define SCNd64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, d ) )
#define SCNi64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, i ) )
#define SCNo64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, o ) )
#define SCNu64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, u ) )
#define SCNx64 _PDCLIB_value2string( _PDCLIB_concat( _PDCLIB_INT_LEAST64_PREFIX, x ) )
#endif
#endif
/* 7.8.2 Functions for greatest-width integer types */
/* Calculate the absolute value of j */
_PDCLIB_PUBLIC intmax_t imaxabs( intmax_t j );
/* Return quotient (quot) and remainder (rem) of an integer division in the
imaxdiv_t struct.
*/
_PDCLIB_PUBLIC imaxdiv_t imaxdiv( intmax_t numer, intmax_t denom );
/* Separate 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.
*/
_PDCLIB_PUBLIC intmax_t strtoimax( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_restrict endptr, int base );
_PDCLIB_PUBLIC uintmax_t strtoumax( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_restrict endptr, int base );
/* TODO: wcstoimax(), wcstoumax() */
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_INTTYPES_H
#include _PDCLIB_EXTEND_INTTYPES_H
#endif
#ifdef __cplusplus
}
#endif
#endif

31
libc/include/iso646.h Normal file
View File

@ -0,0 +1,31 @@
/* 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
#ifndef __cplusplus
#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
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_ISO646_H
#include _PDCLIB_EXTEND_ISO646_H
#endif
#endif

50
libc/include/limits.h Normal file
View File

@ -0,0 +1,50 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_internal.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
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_LIMITS_H
#include _PDCLIB_EXTEND_LIMITS_H
#endif
#ifdef __cplusplus
}
#endif
#endif

114
libc/include/locale.h Normal file
View File

@ -0,0 +1,114 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_internal.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 separates them from the value;
otherwise a space separates the symbol from the value
2 - if symbol and sign are adjacent, a space separates them; otherwise a
space separates 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 separating 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 separated by space from pos. qty. */ /* LC_MONETARY */
char n_sep_by_space; /* if it is separated 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.
*/
_PDCLIB_PUBLIC char * setlocale( int category, const char * locale );
/* Returns a struct lconv initialized to the values appropriate for the current
locale setting.
*/
_PDCLIB_PUBLIC struct lconv * localeconv( void );
#ifdef __cplusplus
}
#endif
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_LOCALE_H
#include _PDCLIB_EXTEND_LOCALE_H
#endif
#endif

View File

@ -0,0 +1,904 @@
/* Internal PDCLib configuration <_PDCLIB_config.h>
("Example" platform target, for PDCLib development)
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 */
/* -------------------------------------------------------------------------- */
/* Helper macros also documented in _PDCLIB_internal.h, but defined here as */
/* they are needed in this file already. */
/* _PDCLIB_cc( x, y ) concatenates two preprocessor tokens without extending. */
/* _PDCLIB_concat( x, y ) concatenates two preprocessor tokens with extending */
#define _PDCLIB_cc( x, y ) x ## y
#define _PDCLIB_concat( x, y ) _PDCLIB_cc( x, y )
/* 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 */
/* when compiling with C99/C++ settings, where C11 _Noreturn is unavailable.) */
#define _PDCLIB_NORETURN __attribute__(( noreturn ))
/* -------------------------------------------------------------------------- */
/* Symbol Visibility */
/* -------------------------------------------------------------------------- */
/* This defines _PDCLIB_PUBLIC to indicate external linkage, and _PDCLIB_LOCAL
to indicate local linkage.
*/
#ifdef _PDCLIB_STATIC_DEFINE
#define _PDCLIB_PUBLIC
#define _PDCLIB_LOCAL
#else
#if defined _WIN32 || defined __CYGWIN__
#ifdef _PDCLIB_BUILD
#ifdef __GNUC__
#define _PDCLIB_PUBLIC __attribute__ ((dllexport))
#else
#define _PDCLIB_PUBLIC __declspec(dllexport)
#endif
#else
#ifdef __GNUC__
#define _PDCLIB_PUBLIC __attribute__ ((dllimport))
#else
#define _PDCLIB_PUBLIC __declspec(dllimport)
#endif
#endif
#define _PDCLIB_LOCAL
#else
#if __GNUC__ >= 4
#define _PDCLIB_PUBLIC __attribute__ ((visibility ("default")))
#define _PDCLIB_LOCAL __attribute__ ((visibility ("hidden")))
#else
#define _PDCLIB_PUBLIC
#define _PDCLIB_LOCAL
#endif
#endif
#endif
/* -------------------------------------------------------------------------- */
/* Integers */
/* -------------------------------------------------------------------------- */
/* The defines below make use of predefines offered by GCC and clang. If you */
/* adapt PDCLib for a different compiler family, you will have to use what */
/* that compiler provides, or enter actual values. */
/* -------------------------------------------------------------------------- */
/* At the point of writing, PDCLib makes no provisions for, nor has it been */
/* tested, on a platform that uses signed magnitude or one's complement to */
/* encode its integers. Most importantly, there are no guarantees that the */
/* negative zero of those encodings is in any form handled gracefully. */
#define _PDCLIB_TWOS_COMPLEMENT 1
/* 1234 for little endian, 4321 for big endian; other types not supported. */
#define _PDCLIB_ENDIANESS __BYTE_ORDER__
/* Calculation of a minimum value from a given maximum for two's complement. */
/* (For convenience only, used only in this header file below.) */
#define _PDCLIB_MIN_CALC( max ) ( ( - max ) - 1 )
/* Now, introducting the various predefines to the _PDCLIB_* namespace, so */
/* the rest of PDCLib can work with that and adapting to a different compiler */
/* will require changes only in this one file. */
/* Bits in a char */
#define _PDCLIB_CHAR_BIT __CHAR_BIT__
/* Maximum and minimum values of signed / unsigned char */
#define _PDCLIB_SCHAR_MAX __SCHAR_MAX__
#define _PDCLIB_SCHAR_MIN _PDCLIB_MIN_CALC( __SCHAR_MAX__ )
#define _PDCLIB_UCHAR_MAX ( __SCHAR_MAX__ * 2 + 1 )
/* Whether the 'char' type is unsigned */
#ifdef __CHAR_UNSIGNED__
#define _PDCLIB_CHAR_MAX _PDCLIB_UCHAR_MAX
#define _PDCLIB_CHAR_MIN 0
#else
#define _PDCLIB_CHAR_MAX _PDCLIB_SCHAR_MAX
#define _PDCLIB_CHAR_MIN _PDCLIB_SCHAR_MIN
#endif
/* Maximum and minimum values of signed / unsigned short */
#define _PDCLIB_SHRT_MAX __SHRT_MAX__
#define _PDCLIB_SHRT_MIN _PDCLIB_MIN_CALC( __SHRT_MAX__ )
#define _PDCLIB_USHRT_MAX ( __SHRT_MAX__ * 2u + 1 )
/* Maximum and minimum values of signed / unsigned int */
#define _PDCLIB_INT_MAX __INT_MAX__
#define _PDCLIB_INT_MIN _PDCLIB_MIN_CALC( __INT_MAX__ )
#define _PDCLIB_UINT_MAX ( __INT_MAX__ * 2u + 1 )
/* Maximum and minimum values of signed / unsigned long */
#define _PDCLIB_LONG_MAX __LONG_MAX__
#define _PDCLIB_LONG_MIN _PDCLIB_MIN_CALC( __LONG_MAX__ )
#define _PDCLIB_ULONG_MAX ( __LONG_MAX__ * 2ul + 1 )
/* Maximum and minimum values of signed / unsigned long long */
#define _PDCLIB_LLONG_MAX __LONG_LONG_MAX__
#define _PDCLIB_LLONG_MIN _PDCLIB_MIN_CALC( __LONG_LONG_MAX__ )
#define _PDCLIB_ULLONG_MAX ( __LONG_LONG_MAX__ * 2ull + 1 )
/* -------------------------------------------------------------------------- */
/* <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 first define is the appropriate basic type (e.g. "long int"), second */
/* its max value, the third its min value (both expressed in the given type). */
/* The same follows for the unsigned type (for which the minimum value is */
/* obviously zero and need not be defined). */
/* There *are* predefines provided for the printf()/scanf() length specifiers */
/* but tunneling them through here would have added many lines of repetitive */
/* and mostly redundant defines. They are determined in <_PDCLIB_internal.h>. */
/* -------------------------------------------------------------------------- */
/* int_fast8_t / uint_fast8_t */
#define _PDCLIB_int_fast8_t __INT_FAST8_TYPE__
#define _PDCLIB_INT_FAST8_MAX __INT_FAST8_MAX__
#define _PDCLIB_INT_FAST8_MIN _PDCLIB_MIN_CALC( __INT_FAST8_MAX__ )
#define _PDCLIB_uint_fast8_t __UINT_FAST8_TYPE__
#define _PDCLIB_UINT_FAST8_MAX __UINT_FAST8_MAX__
/* int_least8_t / uint_least8_t */
#define _PDCLIB_int_least8_t __INT_LEAST8_TYPE__
#define _PDCLIB_INT_LEAST8_MAX __INT_LEAST8_MAX__
#define _PDCLIB_INT_LEAST8_MIN _PDCLIB_MIN_CALC( __INT_LEAST8_MAX__ )
#define _PDCLIB_uint_least8_t __UINT_LEAST8_TYPE__
#define _PDCLIB_UINT_LEAST8_MAX __UINT_LEAST8_MAX__
/* int_fast16_t / uint_fast16_t */
#define _PDCLIB_int_fast16_t __INT_FAST16_TYPE__
#define _PDCLIB_INT_FAST16_MAX __INT_FAST16_MAX__
#define _PDCLIB_INT_FAST16_MIN _PDCLIB_MIN_CALC( __INT_FAST16_MAX__ )
#define _PDCLIB_uint_fast16_t __UINT_FAST16_TYPE__
#define _PDCLIB_UINT_FAST16_MAX __UINT_FAST16_MAX__
/* int_least16_t / uint_least16_t */
#define _PDCLIB_int_least16_t __INT_LEAST16_TYPE__
#define _PDCLIB_INT_LEAST16_MAX __INT_LEAST16_MAX__
#define _PDCLIB_INT_LEAST16_MIN _PDCLIB_MIN_CALC( __INT_LEAST16_MAX__ )
#define _PDCLIB_uint_least16_t __UINT_LEAST16_TYPE__
#define _PDCLIB_UINT_LEAST16_MAX __UINT_LEAST16_MAX__
/* int_fast32_t / uint_fast32_t */
#define _PDCLIB_int_fast32_t __INT_FAST32_TYPE__
#define _PDCLIB_INT_FAST32_MAX __INT_FAST32_MAX__
#define _PDCLIB_INT_FAST32_MIN _PDCLIB_MIN_CALC( __INT_FAST32_MAX__ )
#define _PDCLIB_uint_fast32_t __UINT_FAST32_TYPE__
#define _PDCLIB_UINT_FAST32_MAX __UINT_FAST32_MAX__
/* int_least32_t / uint_least32_t */
#define _PDCLIB_int_least32_t __INT_LEAST32_TYPE__
#define _PDCLIB_INT_LEAST32_MAX __INT_LEAST32_MAX__
#define _PDCLIB_INT_LEAST32_MIN _PDCLIB_MIN_CALC( __INT_LEAST32_MAX__ )
#define _PDCLIB_uint_least32_t __UINT_LEAST32_TYPE__
#define _PDCLIB_UINT_LEAST32_MAX __UINT_LEAST32_MAX__
/* int_fast64_t / uint_fast64_t */
#define _PDCLIB_int_fast64_t __INT_FAST64_TYPE__
#define _PDCLIB_INT_FAST64_MAX __INT_FAST64_MAX__
#define _PDCLIB_INT_FAST64_MIN _PDCLIB_MIN_CALC( __INT_FAST64_MAX__ )
#define _PDCLIB_uint_fast64_t __UINT_FAST64_TYPE__
#define _PDCLIB_UINT_FAST64_MAX __UINT_FAST64_MAX__
/* int_least64_t / uint_least64_t */
#define _PDCLIB_int_least64_t __INT_LEAST64_TYPE__
#define _PDCLIB_INT_LEAST64_MAX __INT_LEAST64_MAX__
#define _PDCLIB_INT_LEAST64_MIN _PDCLIB_MIN_CALC( __INT_LEAST64_MAX__ )
#define _PDCLIB_uint_least64_t __UINT_LEAST64_TYPE__
#define _PDCLIB_UINT_LEAST64_MAX __UINT_LEAST64_MAX__
/* Exact-width integer types. These are *optional*. If your platform does not */
/* support types of these exact widths in two's complement encoding, just */
/* leave them undefined. */
#define _PDCLIB_int8_t __INT8_TYPE__
#define _PDCLIB_int16_t __INT16_TYPE__
#define _PDCLIB_int32_t __INT32_TYPE__
#define _PDCLIB_int64_t __INT64_TYPE__
#define _PDCLIB_uint8_t __UINT8_TYPE__
#define _PDCLIB_uint16_t __UINT16_TYPE__
#define _PDCLIB_uint32_t __UINT32_TYPE__
#define _PDCLIB_uint64_t __UINT64_TYPE__
/* INTn_C / UINTn_C macros to define int_leastN_t / uint_leastN_t literals. */
#if defined( __INT8_C )
/* GCC */
#define _PDCLIB_INT_LEAST8_C __INT8_C
#define _PDCLIB_UINT_LEAST8_C __UINT8_C
#define _PDCLIB_INT_LEAST16_C __INT16_C
#define _PDCLIB_UINT_LEAST16_C __UINT16_C
#define _PDCLIB_INT_LEAST32_C __INT32_C
#define _PDCLIB_UINT_LEAST32_C __UINT32_C
#define _PDCLIB_INT_LEAST64_C __INT64_C
#define _PDCLIB_UINT_LEAST64_C __UINT64_C
#elif defined( __INT8_C_SUFFIX__ )
/* Clang */
#define _PDCLIB_INT_LEAST8_C(c) _PDCLIB_concat( c, __INT8_C_SUFFIX__ )
#define _PDCLIB_UINT_LEAST8_C(c) _PDCLIB_concat( c, __UINT8_C_SUFFIX__ )
#define _PDCLIB_INT_LEAST16_C(c) _PDCLIB_concat( c, __INT16_C_SUFFIX__ )
#define _PDCLIB_UINT_LEAST16_C(c) _PDCLIB_concat( c, __UINT16_C_SUFFIX__ )
#define _PDCLIB_INT_LEAST32_C(c) _PDCLIB_concat( c, __INT32_C_SUFFIX__ )
#define _PDCLIB_UINT_LEAST32_C(c) _PDCLIB_concat( c, __UINT32_C_SUFFIX__ )
#define _PDCLIB_INT_LEAST64_C(c) _PDCLIB_concat( c, __INT64_C_SUFFIX__ )
#define _PDCLIB_UINT_LEAST64_C(c) _PDCLIB_concat( c, __UINT64_C_SUFFIX__ )
#else
#error Please create your own _PDCLIB_config.h. Using the existing one as-is will not work. (Unsupported *INTn_C macros.)
#endif
/* <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;
};
/* -------------------------------------------------------------------------- */
/* What follows are a couple of "special" typedefs and their limits. */
/* -------------------------------------------------------------------------- */
/* The result type of substracting two pointers */
#define _PDCLIB_ptrdiff_t __PTRDIFF_TYPE__
#define _PDCLIB_PTRDIFF_MAX __PTRDIFF_MAX__
#define _PDCLIB_PTRDIFF_MIN _PDCLIB_MIN_CALC( __PTRDIFF_MAX__ )
/* An integer type that can be accessed as atomic entity (think asynchronous */
/* interrupts). In a freestanding environment, the type itself need not be */
/* defined, but its limits must. (Don't ask.) GCC is so kind to predefine it, */
/* but clang is only giving us its MAX value, so we use that to identify the */
/* type in _PDCLIB_int.h if the type definition is unavailable. */
#ifdef __SIG_ATOMIC_TYPE__
#define _PDCLIB_sig_atomic_t __SIG_ATOMIC_TYPE__
#endif
#define _PDCLIB_SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
#define _PDCLIB_SIG_ATOMIC_MIN _PDCLIB_MIN_CALC( __SIG_ATOMIC_MAX__ )
/* Result type of the 'sizeof' operator (must be unsigned). */
/* Note: In <stdint.h>, this is taken as the base for RSIZE_MAX, the limit */
/* for the bounds-checking interfaces of Annex K. The recommendation by the */
/* standard is to use ( SIZE_MAX >> 1 ) when "targeting machines with large */
/* addess spaces", whereas small address spaces should use SIZE_MAX directly. */
#define _PDCLIB_size_t __SIZE_TYPE__
#define _PDCLIB_SIZE_MAX __SIZE_MAX__
/* Large enough an integer to hold all character codes of the widest */
/* supported locale. */
#define _PDCLIB_wchar_t __WCHAR_TYPE__
#define _PDCLIB_WCHAR_MAX __WCHAR_MAX__
#define _PDCLIB_WCHAR_MIN __WCHAR_MIN__
/* Large enough an integer to hold all character codes of the widest */
/* supported locale plus WEOF (which needs not to be equal to EOF, nor needs */
/* to be of negative value). */
#define _PDCLIB_wint_t __WINT_TYPE__
#define _PDCLIB_WINT_MAX __WINT_MAX__
#define _PDCLIB_WINT_MIN __WINT_MIN__
/* Integer types 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_t __INTPTR_TYPE__
#define _PDCLIB_INTPTR_MAX __INTPTR_MAX__
#define _PDCLIB_INTPTR_MIN _PDCLIB_MIN_CALC( __INTPTR_MAX__ )
#define _PDCLIB_uintptr_t __UINTPTR_TYPE__
#define _PDCLIB_UINTPTR_MAX __UINTPTR_MAX__
/* Largest supported integer type. Implementation note: see _PDCLIB_atomax(). */
#define _PDCLIB_intmax_t __INTMAX_TYPE__
#define _PDCLIB_INTMAX_MAX __INTMAX_MAX__
#define _PDCLIB_INTMAX_MIN _PDCLIB_MIN_CALC( __INTMAX_MAX__ )
#define _PDCLIB_INTMAX_C __INTMAX_C
#define _PDCLIB_uintmax_t __UINTMAX_TYPE__
#define _PDCLIB_UINTMAX_MAX __UINTMAX_MAX__
#define _PDCLIB_UINTMAX_C __UINTMAX_C
/* <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_t quot;
_PDCLIB_intmax_t rem;
};
/* -------------------------------------------------------------------------- */
/* Time types, limits, constants, and paths */
/* -------------------------------------------------------------------------- */
/* _PDCLIB_time is the type for type_t; _PDCLIB_clock for clock_t. Both types */
/* are defined as "real types capable of representing times". The "range and */
/* precision of times representable" is implementation-defined. */
/* For clock_t, the standard defines that dividing the result of clock() by */
/* CLOCKS_PER_SEC gives the seconds elapsed. */
#ifdef __CYGWIN__
#define _PDCLIB_clock_t unsigned long
#else
#define _PDCLIB_clock_t long
#endif
#define _PDCLIB_CLOCKS_PER_SEC 1000000
/* For time_t, no such divider exists. Most implementations use a count of */
/* seconds since a specified epoch. While PDCLib really should support other */
/* encodings as well, for now "count of seconds" is the only supported one. */
/* MIN / MAX values for time_t are not required by the standard (and they are */
/* not "exported" from the _PDCLIB namespace), but they are useful in support */
/* of the _tzcode implementation. */
#ifdef __MINGW64__
#define _PDCLIB_time_t long long
#define _PDCLIB_TIME_MAX __LONG_LONG_MAX__
#define _PDCLIB_TIME_MIN _PDCLIB_MIN_CALC( __LONG_LONG_MAX__ )
#else
#define _PDCLIB_time_t long
#define _PDCLIB_TIME_MAX __LONG_MAX__
#define _PDCLIB_TIME_MIN _PDCLIB_MIN_CALC( __LONG_MAX__ )
#endif
/* "Unix time" uses 1970-01-01T00:00:00 as "epoch". If your system uses a */
/* different "zero point" for its timestamps, set this to the offset between */
/* your epoch and Unix epoch. (For example, NTP uses 1900-01-01T00:00:00 as */
/* epoch, giving an offset of (70 * 365 + 17) * 86400 = 220898800 seconds.) */
#define _PDCLIB_EPOCH_BIAS INT64_C( 0 )
/* Leave this alone for now. */
#define _PDCLIB_TIME_UTC 1
/* Path to TZ data. */
/* IMPORTANT: *Must* end with separator character! */
/* It does make it much easier for the time data handling code if this detail */
/* can be relied upon and need not be handled in code. */
#define _PDCLIB_TZDIR "/usr/share/zoneinfo/"
/* Path to default (local) timezone */
#define _PDCLIB_TZDEFAULT "/etc/localtime"
/* -------------------------------------------------------------------------- */
/* 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
/* Check <float.h> for explanations on each of these values. */
#define _PDCLIB_FLT_EVAL_METHOD __FLT_EVAL_METHOD__
#define _PDCLIB_FLT_HAS_SUBNORM __FLT_HAS_DENORM__
#define _PDCLIB_DBL_HAS_SUBNORM __DBL_HAS_DENORM__
#define _PDCLIB_LDBL_HAS_SUBNORM __LDBL_HAS_DENORM__
#define _PDCLIB_FLT_RADIX __FLT_RADIX__
#define _PDCLIB_FLT_MANT_DIG __FLT_MANT_DIG__
#define _PDCLIB_DBL_MANT_DIG __DBL_MANT_DIG__
#define _PDCLIB_LDBL_MANT_DIG __LDBL_MANT_DIG__
#define _PDCLIB_FLT_DECIMAL_DIG __FLT_DECIMAL_DIG__
#define _PDCLIB_DBL_DECIMAL_DIG __DBL_DECIMAL_DIG__
#define _PDCLIB_LDBL_DECIMAL_DIG __LDBL_DECIMAL_DIG__
#define _PDCLIB_DECIMAL_DIG __DECIMAL_DIG__
#define _PDCLIB_FLT_DIG __FLT_DIG__
#define _PDCLIB_DBL_DIG __DBL_DIG__
#define _PDCLIB_LDBL_DIG __LDBL_DIG__
#define _PDCLIB_FLT_MIN_EXP __FLT_MIN_EXP__
#define _PDCLIB_DBL_MIN_EXP __DBL_MIN_EXP__
#define _PDCLIB_LDBL_MIN_EXP __LDBL_MIN_EXP__
#define _PDCLIB_FLT_MIN_10_EXP __FLT_MIN_10_EXP__
#define _PDCLIB_DBL_MIN_10_EXP __DBL_MIN_10_EXP__
#define _PDCLIB_LDBL_MIN_10_EXP __LDBL_MIN_10_EXP__
#define _PDCLIB_FLT_MAX_EXP __FLT_MAX_EXP__
#define _PDCLIB_DBL_MAX_EXP __DBL_MAX_EXP__
#define _PDCLIB_LDBL_MAX_EXP __LDBL_MAX_EXP__
#define _PDCLIB_FLT_MAX_10_EXP __FLT_MAX_10_EXP__
#define _PDCLIB_DBL_MAX_10_EXP __DBL_MAX_10_EXP__
#define _PDCLIB_LDBL_MAX_10_EXP __LDBL_MAX_10_EXP__
#define _PDCLIB_FLT_MAX __FLT_MAX__
#define _PDCLIB_DBL_MAX __DBL_MAX__
#define _PDCLIB_LDBL_MAX __LDBL_MAX__
#define _PDCLIB_FLT_EPSILON __FLT_EPSILON__
#define _PDCLIB_DBL_EPSILON __DBL_EPSILON__
#define _PDCLIB_LDBL_EPSILON __LDBL_EPSILON__
#define _PDCLIB_FLT_MIN __FLT_MIN__
#define _PDCLIB_DBL_MIN __DBL_MIN__
#define _PDCLIB_LDBL_MIN __LDBL_MIN__
#define _PDCLIB_FLT_TRUE_MIN __FLT_DENORM_MIN__
#define _PDCLIB_DBL_TRUE_MIN __DBL_DENORM_MIN__
#define _PDCLIB_LDBL_TRUE_MIN __LDBL_DENORM_MIN__
/* Macros for deconstructing floating point values */
#define _PDCLIB_DBL_SIGN( bytes ) ( ( (unsigned)bytes[7] & 0x80 ) >> 7 )
#define _PDCLIB_DBL_DEC( bytes ) ( ( _PDCLIB_DBL_EXP( bytes ) > 0 ) ? 1 : 0 )
#define _PDCLIB_DBL_EXP( bytes ) ( ( ( (unsigned)bytes[7] & 0x7f ) << 4 ) | ( ( (unsigned)bytes[6] & 0xf0 ) >> 4 ) )
#define _PDCLIB_DBL_BIAS 1023
#define _PDCLIB_DBL_MANT_START( bytes ) ( bytes + 6 )
/* Most platforms today use IEEE 754 single precision for 'float', and double */
/* precision for 'double'. But type 'long double' varies. We use what the */
/* compiler states about LDBL_MANT_DIG to determine the type. */
#if _PDCLIB_LDBL_MANT_DIG == 64
/* Intel "Extended Precision" format, using 80 bits (64bit mantissa) */
#define _PDCLIB_LDBL_SIGN( bytes ) ( ( (unsigned)bytes[9] & 0x80 ) >> 7 )
#define _PDCLIB_LDBL_DEC( bytes ) ( ( (unsigned)bytes[7] & 0x80 ) >> 7 )
#define _PDCLIB_LDBL_EXP( bytes ) ( ( ( (unsigned)bytes[9] & 0x7f ) << 8 ) | (unsigned)bytes[8] )
#define _PDCLIB_LDBL_BIAS 16383
#define _PDCLIB_LDBL_MANT_START( bytes ) ( bytes + 7 )
#elif _PDCLIB_LDBL_MANT_DIG == 113
/* IEEE "Quadruple Precision" format, using 128 bits (113bit mantissa) */
#define _PDCLIB_LDBL_SIGN( bytes ) ( ( (unsigned)bytes[15] & 0x80 ) >> 7 )
#define _PDCLIB_LDBL_DEC( bytes ) ( ( _PDCLIB_LDBL_EXP( bytes ) > 0 ) ? 1 : 0 )
#define _PDCLIB_LDBL_EXP( bytes ) ( ( ( (unsigned)bytes[15] & 0x7f ) << 8 ) | (unsigned)bytes[14] )
#define _PDCLIB_LDBL_BIAS 16383
#define _PDCLIB_LDBL_MANT_START( bytes ) ( bytes + 13 )
#else
/* IEEE "Double Precision" format, using 64 bits (53bit mantissa,
same as DBL above) */
#define _PDCLIB_LDBL_SIGN( bytes ) ( ( (unsigned)bytes[7] & 0x80 ) >> 7 )
#define _PDCLIB_LDBL_DEC( bytes ) ( ( _PDCLIB_LDBL_EXP( bytes ) > 0 ) ? 1 : 0 )
#define _PDCLIB_LDBL_EXP( bytes ) ( ( ( (unsigned)bytes[7] & 0x7f ) << 4 ) | ( ( (unsigned)bytes[6] & 0xf0 ) >> 4 ) )
#define _PDCLIB_LDBL_BIAS 1023
#define _PDCLIB_LDBL_MANT_START( bytes ) ( bytes + 6 )
#endif
/* -------------------------------------------------------------------------- */
/* Big Integer Arithmetic */
/* -------------------------------------------------------------------------- */
/* In support of the floating point converstions required by printf() etc., */
/* PDCLib provides rudimentary big integer arithmetics. The _PDCLIB_bigint_t */
/* type stores values in a sequence of integer "digits", which may be of any */
/* uint_leastN_t type with N being 32 or 16. Note that multiplication and */
/* division require the help of the next larger type. So set the define to */
/* 32 if efficient 64bit integer arithmetics are available on your platform, */
/* and to 16 otherwise. */
/* (The value range of _PDCLIB_bigint_t is not affected by this setting.) */
#define _PDCLIB_BIGINT_DIGIT_BITS 16
/* -------------------------------------------------------------------------- */
/* 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__ ) || defined( __ARM_NEON )
/* 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. (Unsupported varargs.)
#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. */
/* -------------------------------------------------------------------------- */
/* I/O ---------------------------------------------------------------------- */
/* The type of the file descriptor returned by _PDCLIB_open(), i.e. whatever */
/* the underlying kernel uses for stream identification. */
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 guarantees can opened */
/* simultaneously. Must be at least 8. Depends largely on how the platform */
/* does the bookkeeping in whatever is called by _PDCLIB_open(). PDCLib puts */
/* no further limits on the number of open files other than 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, you should 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); PDCLib supports larger values, but applications */
/* relying on this would rely on implementation-defined behaviour (not good). */
#define _PDCLIB_UNGETCBUFSIZE 1
/* The number of functions that can be registered with atexit(). Needs to be */
/* at least 33 (32 guaranteed by the standard, plus _PDCLIB_closeall() which */
/* is used internally by PDCLib to close all open streams). */
/* TODO: Should expand dynamically. */
#define _PDCLIB_ATEXIT_SLOTS 40
/* 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 of "translating" between errno values used by the hosting OS 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 'errno == EISDIR', and set '*_PDCLIB_errno_func() */
/* = _PDCLIB_EPERM'. Anyone using PDCLib's <errno.h> will "see" EPERM instead */
/* of EISDIR. */
/* */
/* 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, to avoid confusion. */
/* auxiliary/errno/errno_readout.c provides a convenience program to read */
/* those errno values mandated by the standard from a platform's <errno.h>, */
/* giving output that can readily be pasted here. */
/* Either way, note that the list below, the list in PDCLib's <errno.h>, and */
/* the list in _PDCLIB_stdinit.h, need to be kept in sync. */
/* */
/* The values below are read from a Linux system. */
/* Argument list too long */
#define _PDCLIB_E2BIG 7
/* Permission denied */
#define _PDCLIB_EACCES 13
/* Address in use */
#define _PDCLIB_EADDRINUSE 98
/* Address not available */
#define _PDCLIB_EADDRNOTAVAIL 99
/* Address family not supported */
#define _PDCLIB_EAFNOSUPPORT 97
/* Resource unavailable, try again */
#define _PDCLIB_EAGAIN 11
/* Connection already in progress */
#define _PDCLIB_EALREADY 114
/* Bad file descriptor */
#define _PDCLIB_EBADF 9
/* Bad message */
#define _PDCLIB_EBADMSG 74
/* Device or resource busy */
#define _PDCLIB_EBUSY 16
/* Operation canceled */
#define _PDCLIB_ECANCELED 125
/* No child processes */
#define _PDCLIB_ECHILD 10
/* Connection aborted */
#define _PDCLIB_ECONNABORTED 103
/* Connection refused */
#define _PDCLIB_ECONNREFUSED 111
/* Connection reset */
#define _PDCLIB_ECONNRESET 104
/* Resource deadlock would occur */
#define _PDCLIB_EDEADLK 35
/* Destination address required */
#define _PDCLIB_EDESTADDRREQ 89
/* Mathematics argument out of domain of function */
#define _PDCLIB_EDOM 33
/* File exists */
#define _PDCLIB_EEXIST 17
/* Bad address */
#define _PDCLIB_EFAULT 14
/* File too large */
#define _PDCLIB_EFBIG 27
/* Host is unreachable */
#define _PDCLIB_EHOSTUNREACH 113
/* Identifier removed */
#define _PDCLIB_EIDRM 43
/* Illegal byte sequence */
#define _PDCLIB_EILSEQ 84
/* Operation in progress */
#define _PDCLIB_EINPROGRESS 115
/* Interrupted function */
#define _PDCLIB_EINTR 4
/* Invalid argument */
#define _PDCLIB_EINVAL 22
/* I/O error */
#define _PDCLIB_EIO 5
/* Socket is connected */
#define _PDCLIB_EISCONN 106
/* Is a directory */
#define _PDCLIB_EISDIR 21
/* Too many levels of symbolic links */
#define _PDCLIB_ELOOP 40
/* File descriptor value too large */
#define _PDCLIB_EMFILE 24
/* Too many links */
#define _PDCLIB_EMLINK 31
/* Message too large */
#define _PDCLIB_EMSGSIZE 90
/* Filename too long */
#define _PDCLIB_ENAMETOOLONG 36
/* Network is down */
#define _PDCLIB_ENETDOWN 100
/* Connection aborted by network */
#define _PDCLIB_ENETRESET 102
/* Network unreachable */
#define _PDCLIB_ENETUNREACH 101
/* Too many files open in system */
#define _PDCLIB_ENFILE 23
/* No buffer space available */
#define _PDCLIB_ENOBUFS 105
/* No message is available on the STREAM head read queue */
#define _PDCLIB_ENODATA 61
/* No such device */
#define _PDCLIB_ENODEV 19
/* No such file or directory */
#define _PDCLIB_ENOENT 2
/* Executable file format error */
#define _PDCLIB_ENOEXEC 8
/* No locks available */
#define _PDCLIB_ENOLCK 37
/* Link has been severed */
#define _PDCLIB_ENOLINK 67
/* Not enough space */
#define _PDCLIB_ENOMEM 12
/* No message of the desired type */
#define _PDCLIB_ENOMSG 42
/* Protocol not available */
#define _PDCLIB_ENOPROTOOPT 92
/* No space left on device */
#define _PDCLIB_ENOSPC 28
/* No STREAM resources */
#define _PDCLIB_ENOSR 63
/* Not a STREAM */
#define _PDCLIB_ENOSTR 60
/* Function not supported */
#define _PDCLIB_ENOSYS 38
/* The socket is not connected */
#define _PDCLIB_ENOTCONN 107
/* Not a directory */
#define _PDCLIB_ENOTDIR 20
/* Directory not empty */
#define _PDCLIB_ENOTEMPTY 39
/* State not recoverable */
#define _PDCLIB_ENOTRECOVERABLE 131
/* Not a socket */
#define _PDCLIB_ENOTSOCK 88
/* Not supported */
#define _PDCLIB_ENOTSUP 95
/* Inappropriate I/O control operation */
#define _PDCLIB_ENOTTY 25
/* No such device or address */
#define _PDCLIB_ENXIO 6
/* Operation not supported on socket */
#define _PDCLIB_EOPNOTSUPP 95
/* Value too large to be stored in data type */
#define _PDCLIB_EOVERFLOW 75
/* Previous owner died */
#define _PDCLIB_EOWNERDEAD 130
/* Operation not permitted */
#define _PDCLIB_EPERM 1
/* Broken pipe */
#define _PDCLIB_EPIPE 32
/* Protocol error */
#define _PDCLIB_EPROTO 71
/* Protocol not supported */
#define _PDCLIB_EPROTONOSUPPORT 93
/* Protocol wrong type for socket */
#define _PDCLIB_EPROTOTYPE 91
/* Result too large */
#define _PDCLIB_ERANGE 34
/* Read-only file system */
#define _PDCLIB_EROFS 30
/* Invalid seek */
#define _PDCLIB_ESPIPE 29
/* No such process */
#define _PDCLIB_ESRCH 3
/* Stream ioctl() timeout */
#define _PDCLIB_ETIME 62
/* Connection timed out */
#define _PDCLIB_ETIMEDOUT 110
/* Text file busy */
#define _PDCLIB_ETXTBSY 26
/* Operation would block */
#define _PDCLIB_EWOULDBLOCK 11
/* Cross-device link */
#define _PDCLIB_EXDEV 18
/* The highest defined errno value, plus one. 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.) */
#define _PDCLIB_ERRNO_MAX 132
/* The error message used for unknown error codes (generated by errno_readout */
/* for consistency between the 'holes' in the list of defined error messages */
/* and the text generated by e.g. strerror() for out-of-range error values.) */
#define _PDCLIB_EUNKNOWN_TEXT (char*)"unknown error"
/* 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
/* threads ------------------------------------------------------------------ */
/* This is relying on underlying <pthread.h> implementation to provide thread */
/* support. */
/* The problem here is we cannot just #include <pthread.h> and access the */
/* original definitions. The standard library must not drag identifiers into */
/* the user's namespace, so we have to set our own definitions in the _PDCLIB */
/* namespace. Which are, obviously, platform-specific. */
/* If you do NOT want to provide threads support, define __STDC_NO_THREADS__ */
/* to 1 and simply delete the threads.h header and the corresponding files in */
/* functions/threads/. This makes PDCLib non-thread-safe (obviously), as the */
/* safeguards against race conditions (e.g. in <stdio.h>) will be omitted. */
/* auxiliary/pthread/pthread_readout.c provides a convenience program to read */
/* appropriate definitions from a platform's <pthread.h>, giving output that */
/* can be copy & pasted here. */
typedef unsigned long int _PDCLIB_thrd_t;
typedef union { unsigned char _PDCLIB_cnd_t_data[ 48 ]; long long int _PDCLIB_cnd_t_align; } _PDCLIB_cnd_t;
#if defined( __arm__ ) || defined( __ARM_NEON )
typedef union { unsigned char _PDCLIB_mtx_t_data[ 24 ]; long int _PDCLIB_mtx_t_align; } _PDCLIB_mtx_t;
#else
typedef union { unsigned char _PDCLIB_mtx_t_data[ 40 ]; long int _PDCLIB_mtx_t_align; } _PDCLIB_mtx_t;
#endif
typedef unsigned int _PDCLIB_tss_t;
typedef int _PDCLIB_once_flag;
#define _PDCLIB_ONCE_FLAG_INIT 0
#define _PDCLIB_RECURSIVE_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
/* This one is actually hidden in <limits.h>, and only if __USE_POSIX is */
/* defined prior to #include <limits.h> (PTHREAD_DESTRUCTOR_ITERATIONS). */
#define _PDCLIB_TSS_DTOR_ITERATIONS 4
/* The following are not made public in any header, but used internally for */
/* interfacing with the pthread API. */
typedef union { unsigned char _PDCLIB_cnd_attr_t_data[ 4 ]; int _PDCLIB_cnd_attr_t_align; } _PDCLIB_cnd_attr_t;
typedef union { unsigned char _PDCLIB_mtx_attr_t_data[ 4 ]; int _PDCLIB_mtx_attr_t_align; } _PDCLIB_mtx_attr_t;
#if defined( __arm__ ) || defined( __ARM_NEON )
typedef union { unsigned char _PDCLIB_thrd_attr_t_data[ 36 ]; long int _PDCLIB_thrd_attr_t_align; } _PDCLIB_thrd_attr_t;
#else
typedef union { unsigned char _PDCLIB_thrd_attr_t_data[ 56 ]; long int _PDCLIB_thrd_attr_t_align; } _PDCLIB_thrd_attr_t;
#endif
/* Static initialization of recursive mutex. */
#if defined( __arm__ ) || defined( __ARM_NEON )
#define _PDCLIB_MTX_RECURSIVE_INIT { {\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
/* Static initialization of plain / timeout mutex (identical with pthread). */
#define _PDCLIB_MTX_PLAIN_INIT { {\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
#else
#define _PDCLIB_MTX_RECURSIVE_INIT { {\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
/* Static initialization of plain / timeout mutex (identical with pthread). */
#define _PDCLIB_MTX_PLAIN_INIT { {\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
#endif
/* Termux defines atexit in crtbegin_so.o leading to a multiple definition */
/* error from the linker. This is a crude workaround, which does NOT fix */
/* various run-time issues on Termux likely also related to crt linkage. But */
/* at least things compile OK, and SOME tests can be run. */
#if defined( __ARM_NEON )
#define atexit _PDCLIB_atexit
#endif
#endif

View File

@ -0,0 +1,29 @@
/* Definition guard <_PDCLIB_defguard.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_DEFGUARD_H
#define _PDCLIB_DEFGUARD_H _PDCLIB_DEFGUARD_H
#if defined( __ANDROID__ )
/* typedef sigset_t */
#include "bits/signal_types.h"
#endif
/* Linux defines its own version of struct timespec (from <time.h>) in
some internal header (depending on clib implementation), which leads
to problems when accessing e.g. sys/time.h (type redefinition).
The solution is to set the Linux header's include guard (to avoid
Linux' definition), and to include PDCLib's <time.h> to define the
type unambiguously.
*/
#define _TIMESPEC_DEFINED
#define _SYS__TIMESPEC_H_
#define _STRUCT_TIMESPEC
#include <time.h>
#endif

View File

@ -0,0 +1,93 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_internal.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.
*/
_PDCLIB_LOCAL _PDCLIB_Noreturn void _PDCLIB_Exit( int status ) _PDCLIB_NORETURN;
/* 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_LOCAL _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.
*/
_PDCLIB_LOCAL 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.
*/
_PDCLIB_LOCAL 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_LOCAL _PDCLIB_int_least64_t _PDCLIB_seek( struct _PDCLIB_file_t * stream, _PDCLIB_int_least64_t offset, int whence );
/* A system call that closes a file identified by given file descriptor. Return
zero on success, non-zero otherwise.
*/
_PDCLIB_LOCAL int _PDCLIB_close( _PDCLIB_fd_t fd );
/* A system call that changes the mode of a given stream to that passed as
argument (the argument being the value returned by _PDCLIB_filemode()),
*without* closing the stream. See comments in example implementation
for details. Return zero if the requested mode change is not supported
for this stream and freopen() should try to close and reopen the stream;
return INT_MIN if the change is not supported and freopen() should close
and NOT try to close / reopen (i.e., fail). Return any other value on
success.
*/
_PDCLIB_LOCAL int _PDCLIB_changemode( struct _PDCLIB_file_t * stream, unsigned int mode );
/* A system call that returns a canonicalized absolute filename in
dynamically allocated memory, or NULL if the file does not exist.
*/
_PDCLIB_LOCAL char * _PDCLIB_realpath( const char * path );
/* A system call that removes a file. Return zero on success, non-zero
otherwise.
*/
_PDCLIB_LOCAL int _PDCLIB_remove( const char * pathname );
/* 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.
*/
_PDCLIB_LOCAL int _PDCLIB_rename( const char * oldpath, const char * newpath );
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,776 @@
/* PDCLib internal logic <_PDCLIB_internal.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_INTERNAL_H
#define _PDCLIB_INTERNAL_H _PDCLIB_INTERNAL_H
#ifdef __cplusplus
extern "C" {
#endif
/* -------------------------------------------------------------------------- */
/* 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 "pdclib/_PDCLIB_config.h"
/* -------------------------------------------------------------------------- */
/* 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
#ifndef __STDC_HOSTED__
#error Compiler does not define _ _STDC_HOSTED_ _ (not standard-compliant)!
#elif __STDC_HOSTED__ != 0 && __STDC_HOSTED__ != 1
#error Compiler does not define _ _STDC_HOSTED_ _ to 0 or 1 (not standard-compliant)!
#endif
/* null pointer constant -- ((void *)0) in C, 0 in C++98, nullptr since C++11 */
#ifdef __cplusplus
#if __cplusplus >= 201103L
#define _PDCLIB_NULL nullptr
#else
#define _PDCLIB_NULL 0
#endif
#else
#define _PDCLIB_NULL ((void *)0)
#endif
/* restrict / inline enabled for C99 onward only */
#if defined( __cplusplus ) || ! defined( __STDC_VERSION ) || __STDC_VERSION__ < 199901L
#define _PDCLIB_restrict
#define _PDCLIB_inline
#else
#define _PDCLIB_restrict restrict
#define _PDCLIB_inline inline
#endif
/* noreturn enabled for C11 onward only */
#if defined( __cplusplus ) && __cplusplus >= 201103L
#define _PDCLIB_Noreturn [[noreturn]]
#else
#if defined( __STDC_VERSION__ ) >= 201112L
#define _PDCLIB_Noreturn _Noreturn
#else
#define _PDCLIB_Noreturn
#endif
#endif
/* -------------------------------------------------------------------------- */
/* Helper macros: */
/* */
/* (defined in _PDCLIB_config.h) */
/* _PDCLIB_cc( x, y ) concatenates two preprocessor tokens without extending. */
/* _PDCLIB_concat( x, y ) concatenates two preprocessor tokens with extending */
/* */
/* (defined below) */
/* _PDCLIB_static_assert( e, m ) does a compile-time assertion of expression */
/* e, with m as the failure message. */
/* _PDCLIB_symbol2string( x ) turn symbol into string literal (by adding ""). */
/* _PDCLIB_value2string( x ) expands a preprocessor token and turns it into a */
/* string literal (by adding ""). */
/* _PDCLIB_TYPE_SIGNED( type ) resolves to true if type is signed. */
/* _PDCLIB_LOCK( mtx ) lock a mutex if library has threads support. */
/* _PDCLIB_UNLOCK( mtx ) unlock a mutex if library has threads support. */
/* _PDCLIB_CONSTRAINT_VIOLATION( e ) expand errno number e to parameter list */
/* fit for Annex K constraint violation */
/* handler. */
/* -------------------------------------------------------------------------- */
#define _PDCLIB_static_assert( e, m ) enum { _PDCLIB_concat( _PDCLIB_assert_, __LINE__ ) = 1 / ( !!(e) ) }
#define _PDCLIB_TYPE_SIGNED( type ) (((type) -1) < 0)
#define _PDCLIB_symbol2string( x ) #x
#define _PDCLIB_value2string( x ) _PDCLIB_symbol2string( x )
#ifndef __STDC_NO_THREADS__
#define _PDCLIB_LOCK( mtx ) mtx_lock( &mtx )
#define _PDCLIB_UNLOCK( mtx ) mtx_unlock( &mtx )
#else
#define _PDCLIB_LOCK( mtx )
#define _PDCLIB_UNLOCK( mtx )
#endif
#define _PDCLIB_CONSTRAINT_VIOLATION( e ) _PDCLIB_lc_messages->errno_texts[e], NULL, e
#define _PDCLIB_GETC( fh ) ( ( fh->ungetidx == 0 ) ? ( unsigned char )fh->buffer[ fh->bufidx++ ] : ( unsigned char )fh->ungetbuf[ --fh->ungetidx ] )
#define _PDCLIB_CHECKBUFFER( fh ) ( ( ( fh->bufidx == fh->bufend ) && ( fh->ungetidx == 0 ) ) ? _PDCLIB_fillbuffer( fh ) : 0 )
/* -------------------------------------------------------------------------- */
/* Preparing the length modifiers used in <inttypes.h>. */
/* -------------------------------------------------------------------------- */
/* We use the _MAX value as a proxy for the actual type here. That is crude
but the best we can do, cross-platform wise.
Identifying which type the leastN_t / fastN_t / intmax_t / intptr_t are
and providing the appropriate printf()/scanf() length modifier.
*/
#if _PDCLIB_INT_FAST8_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_INT_FAST8_PREFIX hh
#elif _PDCLIB_INT_FAST8_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_INT_FAST8_PREFIX h
#elif _PDCLIB_INT_FAST8_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_INT_FAST8_PREFIX
#elif _PDCLIB_INT_FAST8_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_INT_FAST8_PREFIX l
#elif _PDCLIB_INT_FAST8_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_INT_FAST8_PREFIX ll
#else
#error No matching native type for int_fast8_t. Please check your setup.
#endif
#if _PDCLIB_INT_FAST16_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_INT_FAST16_PREFIX hh
#elif _PDCLIB_INT_FAST16_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_INT_FAST16_PREFIX h
#elif _PDCLIB_INT_FAST16_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_INT_FAST16_PREFIX
#elif _PDCLIB_INT_FAST16_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_INT_FAST16_PREFIX l
#elif _PDCLIB_INT_FAST16_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_INT_FAST16_PREFIX ll
#else
#error No matching native type for int_fast16_t. Please check your setup.
#endif
#if _PDCLIB_INT_FAST32_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_INT_FAST32_PREFIX hh
#elif _PDCLIB_INT_FAST32_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_INT_FAST32_PREFIX h
#elif _PDCLIB_INT_FAST32_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_INT_FAST32_PREFIX
#elif _PDCLIB_INT_FAST32_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_INT_FAST32_PREFIX l
#elif _PDCLIB_INT_FAST32_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_INT_FAST32_PREFIX ll
#else
#error No matching native type for int_fast32_t. Please check your setup.
#endif
#if _PDCLIB_INT_FAST64_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_INT_FAST64_PREFIX hh
#elif _PDCLIB_INT_FAST64_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_INT_FAST64_PREFIX h
#elif _PDCLIB_INT_FAST64_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_INT_FAST64_PREFIX
#elif _PDCLIB_INT_FAST64_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_INT_FAST64_PREFIX l
#elif _PDCLIB_INT_FAST64_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_INT_FAST64_PREFIX ll
#else
#error No matching native type for int_fast64_t. Please check your setup.
#endif
/* Many of the combinations below can very likely be ruled out logically.
All combinations are still listed for simplicity's sake (and to not fall
into the trap of false assumptions).
*/
#if _PDCLIB_INT_LEAST8_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_INT_LEAST8_PREFIX hh
#elif _PDCLIB_INT_LEAST8_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_INT_LEAST8_PREFIX h
#elif _PDCLIB_INT_LEAST8_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_INT_LEAST8_PREFIX
#elif _PDCLIB_INT_LEAST8_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_INT_LEAST8_PREFIX l
#elif _PDCLIB_INT_LEAST8_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_INT_LEAST8_PREFIX ll
#else
#error No matching native type for int_least8_t. Please check your setup.
#endif
#if _PDCLIB_INT_LEAST16_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_INT_LEAST16_PREFIX hh
#elif _PDCLIB_INT_LEAST16_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_INT_LEAST16_PREFIX h
#elif _PDCLIB_INT_LEAST16_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_INT_LEAST16_PREFIX
#elif _PDCLIB_INT_LEAST16_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_INT_LEAST16_PREFIX l
#elif _PDCLIB_INT_LEAST16_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_INT_LEAST16_PREFIX ll
#else
#error No matching native type for int_least16_t. Please check your setup.
#endif
#if _PDCLIB_INT_LEAST32_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_INT_LEAST32_PREFIX hh
#elif _PDCLIB_INT_LEAST32_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_INT_LEAST32_PREFIX h
#elif _PDCLIB_INT_LEAST32_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_INT_LEAST32_PREFIX
#elif _PDCLIB_INT_LEAST32_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_INT_LEAST32_PREFIX l
#elif _PDCLIB_INT_LEAST32_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_INT_LEAST32_PREFIX ll
#else
#error No matching native type for int_least32_t. Please check your setup.
#endif
#if _PDCLIB_INT_LEAST64_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_INT_LEAST64_PREFIX hh
#elif _PDCLIB_INT_LEAST64_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_INT_LEAST64_PREFIX h
#elif _PDCLIB_INT_LEAST64_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_INT_LEAST64_PREFIX
#elif _PDCLIB_INT_LEAST64_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_INT_LEAST64_PREFIX l
#elif _PDCLIB_INT_LEAST64_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_INT_LEAST64_PREFIX ll
#else
#error No matching native type for int_least64_t. Please check your setup.
#endif
#if _PDCLIB_INTMAX_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_INTMAX_PREFIX hh
#elif _PDCLIB_INTMAX_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_INTMAX_PREFIX h
#elif _PDCLIB_INTMAX_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_INTMAX_PREFIX
#elif _PDCLIB_INTMAX_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_INTMAX_PREFIX l
#elif _PDCLIB_INTMAX_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_INTMAX_PREFIX ll
#else
#error No matching native type for intmax_t. Please check your setup.
#endif
#if _PDCLIB_INTPTR_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_INTPTR_PREFIX hh
#elif _PDCLIB_INTPTR_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_INTPTR_PREFIX h
#elif _PDCLIB_INTPTR_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_INTPTR_PREFIX
#elif _PDCLIB_INTPTR_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_INTPTR_PREFIX l
#elif _PDCLIB_INTPTR_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_INTPTR_PREFIX ll
#else
#error No matching native type for intptr_t. Please check your setup.
#endif
/* We might not have a type definition for sig_atomic_t at this point. The */
/* clang compiler does not provide an appropriate predefine for it. So if we */
/* do not have _PDCLIB_sig_atomic_t, identify the type trough its MAX value. */
#ifndef _PDCLIB_sig_atomic_t
#if _PDCLIB_SIG_ATOMIC_MAX == _PDCLIB_SCHAR_MAX
#define _PDCLIB_sig_atomic_t char
#elif _PDCLIB_SIG_ATOMIC_MAX == _PDCLIB_SHRT_MAX
#define _PDCLIB_sig_atomic_t short
#elif _PDCLIB_SIG_ATOMIC_MAX == _PDCLIB_INT_MAX
#define _PDCLIB_sig_atomic_t int
#elif _PDCLIB_SIG_ATOMIC_MAX == _PDCLIB_LONG_MAX
#define _PDCLIB_sig_atomic_t long
#elif _PDCLIB_SIG_ATOMIC_MAX == _PDCLIB_LLONG_MAX
#define _PDCLIB_sig_atomic_t long long
#else
#error No matching native type for sig_atomic_t. Please check your setup.
#endif
#endif
/* -------------------------------------------------------------------------- */
/* 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 (1u<<3)
#define _PDCLIB_FWRITE (1u<<4)
#define _PDCLIB_FAPPEND (1u<<5)
#define _PDCLIB_FRW (1u<<6)
#define _PDCLIB_FBIN (1u<<7)
/* Internal flags, made to fit the same status field as the flags above. */
/* -------------------------------------------------------------------------- */
/* free() the buffer memory on closing (setvbuf()) */
#define _PDCLIB_FREEBUFFER (1u<<8)
/* stream has encountered error / EOF */
#define _PDCLIB_ERRORFLAG (1u<<9)
#define _PDCLIB_EOFFLAG (1u<<10)
/* stream is wide-oriented */
#define _PDCLIB_WIDESTREAM (1u<<11)
/* stream is byte-oriented */
#define _PDCLIB_BYTESTREAM (1u<<12)
/* file associated with stream should be remove()d on closing (tmpfile()) */
#define _PDCLIB_DELONCLOSE (1u<<13)
/* Position / status structure for getpos() / fsetpos(). */
struct _PDCLIB_fpos_t
{
_PDCLIB_uint_least64_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[_PDCLIB_UNGETCBUFSIZE]; /* ungetc() buffer */
unsigned int status; /* Status flags; see above */
/* multibyte parsing status to be added later */
#ifndef __STDC_NO_THREADS__
_PDCLIB_mtx_t mtx; /* Multithreading safety */
#endif
char * filename; /* Name the current stream has been opened with */
struct _PDCLIB_file_t * next; /* Pointer to next struct (internal) */
};
/* -------------------------------------------------------------------------- */
/* 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 );
};
/* 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_LOCAL _PDCLIB_intmax_t _PDCLIB_atomax( const char * s );
/* Two helper functions used by strtol(), strtoul() and long long variants. */
_PDCLIB_LOCAL const char * _PDCLIB_strtox_prelim( const char * p, char * sign, int * base );
_PDCLIB_LOCAL _PDCLIB_uintmax_t _PDCLIB_strtox_main( const char ** p, unsigned int base, _PDCLIB_uintmax_t error, _PDCLIB_uintmax_t limval, int limdigit, char * sign );
/* A helper function used by strtof(), strtod(), and strtold(). */
_PDCLIB_LOCAL void _PDCLIB_strtod_scan( const char * s, const char ** dec, const char ** frac, const char ** exp, int base );
/* 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.
*/
_PDCLIB_LOCAL 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
*/
_PDCLIB_LOCAL const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status );
/* Parsing any fopen() style filemode string into a number of flags. */
_PDCLIB_LOCAL unsigned int _PDCLIB_filemode( const char * mode );
/* Initialize a FILE structure. If the parameter is NULL, a new FILE structure
is malloc'ed. Returns a pointer to the stream if successful, NULL otherwise.
*/
_PDCLIB_LOCAL struct _PDCLIB_file_t * _PDCLIB_init_file_t( struct _PDCLIB_file_t * stream );
/* 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.
*/
_PDCLIB_LOCAL 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.
*/
_PDCLIB_LOCAL int _PDCLIB_prepwrite( struct _PDCLIB_file_t * stream );
/* Closing all streams on program exit */
_PDCLIB_LOCAL void _PDCLIB_closeall( void );
/* Check if a given year is a leap year. Parameter is offset to 1900. */
_PDCLIB_LOCAL 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.
*/
_PDCLIB_LOCAL char * _PDCLIB_load_lines( struct _PDCLIB_file_t * stream, _PDCLIB_size_t lines );
/* Returns the (locale dependent) error message associated with the argument
errno value.
*/
_PDCLIB_LOCAL char * _PDCLIB_geterrtext( int errnum );
/* Returns non-zero if the given stream is on the internal list of open files,
zero otherwise. Sets the second paramenter (if not NULL) to the previous
stream on the list (or NULL if the given stream is the first on the list).
This function does not lock _PDCLIB_filelist_mtx, this needs to be done by
the calling function (_PDCLIB_getstream() or freopen()).
*/
_PDCLIB_LOCAL int _PDCLIB_isstream( struct _PDCLIB_file_t * stream, struct _PDCLIB_file_t ** previous );
/* Removes the given stream from the internal list of open files. Returns zero
if successful, non-zero otherwise. In case of error, sets errno to EBADF.
This function does not lock _PDCLIB_filelist_mtx, this needs to be done by
the calling function (fclose()).
*/
_PDCLIB_LOCAL int _PDCLIB_getstream( struct _PDCLIB_file_t * stream );
/* Backend for strtok and strtok_s (plus potential extensions like strtok_r). */
_PDCLIB_LOCAL char * _PDCLIB_strtok( char * _PDCLIB_restrict s1, _PDCLIB_size_t * _PDCLIB_restrict s1max, const char * _PDCLIB_restrict s2, char ** _PDCLIB_restrict ptr );
/* -------------------------------------------------------------------------- */
/* errno */
/* -------------------------------------------------------------------------- */
/* A mechanism for delayed evaluation.
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>.
*/
_PDCLIB_PUBLIC 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_C;
extern struct _PDCLIB_lc_collate_t * _PDCLIB_lc_collate;
/* One entry to the _PDCLIB_lc_ctype_t.entry data table */
struct _PDCLIB_lc_ctype_entry_t
{
_PDCLIB_uint_least16_t flags; /* Whether a character is of a given CTYPE */
unsigned char upper; /* Result for toupper() */
unsigned char lower; /* Result for tolower() */
};
struct _PDCLIB_lc_ctype_t
{
int alloced; /* .entry dynamically allocated? */
int digits_low; /* Where decimal digits start */
int digits_high; /* Where decimal digits end */
int Xdigits_low; /* Where A..F start */
int Xdigits_high; /* Where A..F end */
int xdigits_low; /* Where a..f start */
int xdigits_high; /* Where a..f end */
struct _PDCLIB_lc_ctype_entry_t * entry; /* The data table */
};
extern struct _PDCLIB_lc_ctype_t _PDCLIB_lc_ctype_C;
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_C;
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_C;
extern struct _PDCLIB_lc_time_t * _PDCLIB_lc_time;
_PDCLIB_LOCAL struct _PDCLIB_lc_lconv_numeric_t * _PDCLIB_load_lc_numeric( const char * path, const char * locale );
_PDCLIB_LOCAL struct _PDCLIB_lc_lconv_monetary_t * _PDCLIB_load_lc_monetary( const char * path, const char * locale );
_PDCLIB_LOCAL struct _PDCLIB_lc_collate_t * _PDCLIB_load_lc_collate( const char * path, const char * locale );
_PDCLIB_LOCAL struct _PDCLIB_lc_ctype_t * _PDCLIB_load_lc_ctype( const char * path, const char * locale );
_PDCLIB_LOCAL struct _PDCLIB_lc_time_t * _PDCLIB_load_lc_time( const char * path, const char * locale );
_PDCLIB_LOCAL struct _PDCLIB_lc_messages_t * _PDCLIB_load_lc_messages( const char * path, const char * locale );
/* -------------------------------------------------------------------------- */
/* _PDCLIB_bigint_t support (required for floating point conversions) */
/* -------------------------------------------------------------------------- */
/* Must be divisible by 32. */
#define _PDCLIB_BIGINT_BITS 1024
#if _PDCLIB_BIGINT_DIGIT_BITS == 32
#define _PDCLIB_BIGINT_DIGIT_MAX UINT32_C( 0xFFFFFFFF )
#define _PDCLIB_BIGINT_BASE ( UINT64_C(1) << _PDCLIB_BIGINT_DIGIT_BITS )
typedef _PDCLIB_uint_least32_t _PDCLIB_bigint_digit_t;
typedef _PDCLIB_uint_least64_t _PDCLIB_bigint_arith_t;
typedef _PDCLIB_int_least64_t _PDCLIB_bigint_sarith_t;
#elif _PDCLIB_BIGINT_DIGIT_BITS == 16
#define _PDCLIB_BIGINT_DIGIT_MAX UINT16_C( 0xFFFF )
#define _PDCLIB_BIGINT_BASE ( UINT32_C(1) << _PDCLIB_BIGINT_DIGIT_BITS )
typedef _PDCLIB_uint_least16_t _PDCLIB_bigint_digit_t;
typedef _PDCLIB_uint_least32_t _PDCLIB_bigint_arith_t;
typedef _PDCLIB_int_least32_t _PDCLIB_bigint_sarith_t;
#elif _PDCLIB_BIGINT_DIGIT_BITS == 8
/* For testing purposes only. */
#define _PDCLIB_BIGINT_DIGIT_MAX UINT8_C( 0xFF )
#define _PDCLIB_BIGINT_BASE ( UINT16_C(1) << _PDCLIB_BIGINT_DIGIT_BITS )
typedef _PDCLIB_uint_least8_t _PDCLIB_bigint_digit_t;
typedef _PDCLIB_uint_least16_t _PDCLIB_bigint_arith_t;
typedef _PDCLIB_int_least16_t _PDCLIB_bigint_sarith_t;
#else
#error Only 16 or 32 supported for _PDCLIB_BIGINT_DIGIT_BITS.
#endif
/* How many "digits" a _PDCLIB_bigint_t holds. */
#define _PDCLIB_BIGINT_DIGITS _PDCLIB_BIGINT_BITS / _PDCLIB_BIGINT_DIGIT_BITS
/* Maximum number of characters needed for _PDCLIB_bigint_tostring() */
#define _PDCLIB_BIGINT_CHARS ( _PDCLIB_BIGINT_BITS / 4 + _PDCLIB_BIGINT_DIGITS + 2 )
/* Type */
/* ---- */
typedef struct
{
/* Least significant digit first */
_PDCLIB_bigint_digit_t data[ _PDCLIB_BIGINT_DIGITS ];
/* Number of digits used; zero value == zero size */
_PDCLIB_size_t size;
} _PDCLIB_bigint_t;
/* Initializer */
/* ----------- */
/* Sets a bigint to pow2( n ) */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint2( _PDCLIB_bigint_t * bigint, unsigned n );
/* Sets a bigint to pow10( n ) */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint10( _PDCLIB_bigint_t * bigint, unsigned n );
/* Sets a bigint from a 32bit input value. */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint32( _PDCLIB_bigint_t * bigint, _PDCLIB_uint_least32_t value );
/* Sets a bigint from two 32bit input values. */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint64( _PDCLIB_bigint_t * bigint, _PDCLIB_uint_least32_t high, _PDCLIB_uint_least32_t low );
/* Sets a bigint from another bigint. (Copies only value->size digits, so it is
faster than a POD copy of a _PDCLIB_bigint_t in most cases.)
*/
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint( _PDCLIB_bigint_t * _PDCLIB_restrict bigint, _PDCLIB_bigint_t const * _PDCLIB_restrict value );
/* Comparison, Output */
/* ------------------ */
/* Compares two given bigint values. Returns 0 if lhs == rhs, a negative number
if lhs < rhs, and a positive number if lhs > rhs.
*/
_PDCLIB_LOCAL int _PDCLIB_bigint_cmp( _PDCLIB_bigint_t const * _PDCLIB_restrict lhs, _PDCLIB_bigint_t const * _PDCLIB_restrict rhs );
/* Writes a hexadecimal representation of the given bigint into the given buffer.
Buffer should be at least _PDCLIB_BIGINT_CHARS in size.
*/
_PDCLIB_LOCAL char * _PDCLIB_bigint_tostring( _PDCLIB_bigint_t const * _PDCLIB_restrict value, char * _PDCLIB_restrict buffer );
/* Operations (in-place) */
/* --------------------- */
/* Adds to a given bigint another given bigint. */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint_add( _PDCLIB_bigint_t * _PDCLIB_restrict lhs, _PDCLIB_bigint_t const * _PDCLIB_restrict rhs );
/* Substracts from a given bigint another given bigint. */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint_sub( _PDCLIB_bigint_t * _PDCLIB_restrict lhs, _PDCLIB_bigint_t const * _PDCLIB_restrict rhs );
/* Multiplies a given bigint with a given 32bit value. */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint_mul_dig( _PDCLIB_bigint_t * lhs, _PDCLIB_bigint_digit_t rhs );
/* Divides a given bigint by a given 32bit value. */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint_div_dig( _PDCLIB_bigint_t * lhs, _PDCLIB_bigint_digit_t rhs );
/* Shifts a given bigint left by a given count of bits. */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint_shl( _PDCLIB_bigint_t * lhs, unsigned rhs );
/* Operations (into new bigint) */
/* ---------------------------- */
/* Multiplies a given bigint with another given bigint. */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint_mul( _PDCLIB_bigint_t * _PDCLIB_restrict result, _PDCLIB_bigint_t const * _PDCLIB_restrict lhs, _PDCLIB_bigint_t const * _PDCLIB_restrict rhs );
/* Divides a given bigint by another given bigint. */
_PDCLIB_LOCAL _PDCLIB_bigint_t * _PDCLIB_bigint_div( _PDCLIB_bigint_t * _PDCLIB_restrict result, _PDCLIB_bigint_t const * _PDCLIB_restrict lhs, _PDCLIB_bigint_t const * _PDCLIB_restrict rhs );
/* Queries */
/* ------- */
/* Returns the log2() of a given bigint */
_PDCLIB_LOCAL unsigned _PDCLIB_bigint_log2( _PDCLIB_bigint_t const * bigint );
/* FP Conversions */
/* -------------- */
/* Split a float into its integral components.
Returns 1 if value is negative, zero otherwise.
*/
_PDCLIB_LOCAL int _PDCLIB_float_split( float value, unsigned * exponent, _PDCLIB_bigint_t * significand );
/* Split a double into its integral components.
Returns 1 if value is negative, zero otherwise.
*/
_PDCLIB_LOCAL int _PDCLIB_double_split( double value, unsigned * exponent, _PDCLIB_bigint_t * significand );
/* Split a long double into its integral components.
Returns 1 if value is negative, zero otherwise.
*/
_PDCLIB_LOCAL int _PDCLIB_long_double_split( long double value, unsigned * exponent, _PDCLIB_bigint_t * significand );
/* -------------------------------------------------------------------------- */
/* Sanity checks */
/* -------------------------------------------------------------------------- */
/* signed-ness of char */
_PDCLIB_static_assert( _PDCLIB_CHAR_MIN == ((((char) -1) < 0) ? _PDCLIB_SCHAR_MIN : 0), "Compiler disagrees on signed-ness of 'char'." );
/* two's complement */
#if _PDCLIB_TWOS_COMPLEMENT == 1
#if _PDCLIB_CHAR_MIN < 0
_PDCLIB_static_assert( ((char) ~ (char) 0 < 0), "Not two's complement on 'char'." );
#endif
_PDCLIB_static_assert( ((short) ~ (short) 0 < 0), "Not two's complement on 'short'." );
_PDCLIB_static_assert( ((int) ~ (int) 0 < 0), "Not two's complement on 'int'." );
_PDCLIB_static_assert( ((long) ~ (long) 0 < 0), "Not two's complement on 'long'." );
_PDCLIB_static_assert( ((long long) ~ (long long) 0 < 0), "Not two's complement on 'long long'." );
#endif
/* size_t as the result of sizeof */
_PDCLIB_static_assert( sizeof( sizeof( int ) ) == sizeof( _PDCLIB_size_t ), "Compiler disagrees on size_t." );
/* wchar_t as the type of wide character literals */
_PDCLIB_static_assert( sizeof( _PDCLIB_wchar_t ) == sizeof( L'x' ), "Compiler disagrees on wchar_t." );
#ifdef __cplusplus
_PDCLIB_static_assert( sizeof( _PDCLIB_wchar_t ) == sizeof( wchar_t ), "Compiler disagrees on wchar_t (C++)." );
#endif
/* intptr_t/uintptr_t being wide enough to store the value of a pointer */
_PDCLIB_static_assert( sizeof( void * ) == sizeof( _PDCLIB_intptr_t ), "Compiler disagrees on intptr_t." );
_PDCLIB_static_assert( sizeof( void * ) == sizeof( _PDCLIB_uintptr_t ), "Compiler disagrees on uintptr_t." );
/* ptrdiff_t as the result of pointer arithmetic */
_PDCLIB_static_assert( sizeof( &_PDCLIB_digits[1] - &_PDCLIB_digits[0] ) == sizeof( _PDCLIB_ptrdiff_t ), "Compiler disagrees on ptrdiff_t." );
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,32 @@
/* __STDC_WANT_LIB_EXT1__ redefinition guard
This file is part of the Public Domain C Library (PDCLib).
Permission is granted to use, modify, and / or redistribute at will.
*/
#ifndef __STDC_WANT_LIB_EXT1__
#ifdef __STDC_WANT_LIB_EXT1_PREVIOUS__
#if __STDC_WANT_LIB_EXT1_PREVIOUS__ != -1
#error __STDC_WANT_LIB_EXT1__ undefined when it was defined earlier.
#endif
#else
#define __STDC_WANT_LIB_EXT1_PREVIOUS__ -1
#endif
#else
#if ( __STDC_WANT_LIB_EXT1__ + 0 ) == 0 && ( 0 - __STDC_WANT_LIB_EXT1__ - 1 ) == 1
#error __STDC_WANT_LIB_EXT1__ defined but empty. Should be an integer value.
#endif
#ifdef __STDC_WANT_LIB_EXT1_PREVIOUS__
#if ( __STDC_WANT_LIB_EXT1__ + 0 ) != __STDC_WANT_LIB_EXT1_PREVIOUS__
#error __STDC_WANT_LIB_EXT1__ redefined from previous value.
#endif
#else
#if ( __STDC_WANT_LIB_EXT1__ + 0 ) == 0
#define __STDC_WANT_LIB_EXT1_PREVIOUS__ 0
#elif ( __STDC_WANT_LIB_EXT1__ + 0 ) == 1
#define __STDC_WANT_LIB_EXT1_PREVIOUS__ 1
#else
#error __STDC_WANT_LIB_EXT1__ set to value other than 0,1 -- undefined behavior
#endif
#endif
#endif

View File

@ -0,0 +1,71 @@
/* PDCLib printf logic <_PDCLIB_print.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_PRINT_H
#define _PDCLIB_PRINT_H _PDCLIB_PRINT_H
#include "pdclib/_PDCLIB_internal.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
/* 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 )
/* Using an integer's bits as flags for both the conversion flags and length
modifiers.
*/
#define E_minus (INT32_C(1)<<0)
#define E_plus (INT32_C(1)<<1)
#define E_alt (INT32_C(1)<<2)
#define E_space (INT32_C(1)<<3)
#define E_zero (INT32_C(1)<<4)
#define E_done (INT32_C(1)<<5)
#define E_char (INT32_C(1)<<6)
#define E_short (INT32_C(1)<<7)
#define E_long (INT32_C(1)<<8)
#define E_llong (INT32_C(1)<<9)
#define E_intmax (INT32_C(1)<<10)
#define E_size (INT32_C(1)<<11)
#define E_ptrdiff (INT32_C(1)<<12)
#define E_pointer (INT32_C(1)<<13)
#define E_double (INT32_C(1)<<14)
#define E_ldouble (INT32_C(1)<<15)
#define E_decimal (INT32_C(1)<<18)
#define E_exponent (INT32_C(1)<<19)
#define E_generic (INT32_C(1)<<20)
#define E_hexa (INT32_C(1)<<21)
#define E_lower (INT32_C(1)<<16)
#define E_unsigned (INT32_C(1)<<17)
void _PDCLIB_print_integer( struct _PDCLIB_imaxdiv_t div, struct _PDCLIB_status_t * status );
void _PDCLIB_print_string( const char * s, struct _PDCLIB_status_t * status );
void _PDCLIB_print_double( double value, struct _PDCLIB_status_t * status );
void _PDCLIB_print_ldouble( long double value, struct _PDCLIB_status_t * status );
#endif

View File

@ -0,0 +1,153 @@
/* TZ Code declarations and definitions <_PDCLIB_tzcode.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_TZCODE_H
#define _PDCLIB_TZCODE_H _PDCLIB_TZCODE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <pdclib/_PDCLIB_config.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
/* Handy macros that are independent of tzfile implementation. */
#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
#define SECSPERMIN 60
#define MINSPERHOUR 60
#define HOURSPERDAY 24
#define DAYSPERWEEK 7
#define DAYSPERNYEAR 365
#define DAYSPERLYEAR 366
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
#define MONSPERYEAR 12
#define AVGSECSPERYEAR 31556952L
#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
#define TM_SUNDAY 0
#define TM_MONDAY 1
#define TM_TUESDAY 2
#define TM_WEDNESDAY 3
#define TM_THURSDAY 4
#define TM_FRIDAY 5
#define TM_SATURDAY 6
#define TM_YEAR_BASE 1900
#define EPOCH_YEAR 1970
#define EPOCH_WDAY TM_THURSDAY
extern struct tm _PDCLIB_tm;
extern int lcl_is_set;
static const char gmt[] = "GMT";
static const int mon_lengths[ 2 ][ MONSPERYEAR ] =
{
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
static const int year_lengths[2] =
{
DAYSPERNYEAR, DAYSPERLYEAR
};
/* time type information */
struct ttinfo
{
int_fast32_t utoff; /* UT offset in seconds */
bool isdst; /* used to set tm_isdst */
int desigidx; /* abbreviation list index */
bool ttisstd; /* transition is std time */
bool ttisut; /* transition is UT */
};
/* leap second information */
struct lsinfo
{
time_t trans; /* transition time */
int_fast64_t corr; /* correction to apply */
};
#define BIGGEST( a, b ) (((a) > (b)) ? (a) : (b))
#ifndef TZ_MAX_TIMES
#define TZ_MAX_TIMES 2000
#endif
#ifndef TZ_MAX_TYPES
/* This must be at least 17 for Europe/Vilnius. */
/* Limited by what (unsigned char)s can hold */
#define TZ_MAX_TYPES 256
#endif
#ifndef TZ_MAX_CHARS
/* Maximum number of abbreviation characters */
/* Limited by what (unsigned char)s can hold */
#define TZ_MAX_CHARS 50
#endif
#ifndef TZ_MAX_LEAPS
/* Maximum number of leap second corrections */
#define TZ_MAX_LEAPS 50
#endif
#ifdef TZNAME_MAX
#define MY_TZNAME_MAX TZNAME_MAX
#else
#define MY_TZNAME_MAX 255
#endif
struct state
{
int leapcnt;
int timecnt;
int typecnt;
int charcnt;
bool goback;
bool goahead;
time_t ats[ TZ_MAX_TIMES ];
unsigned char types[ TZ_MAX_TIMES ];
struct ttinfo ttis[ TZ_MAX_TYPES ];
char chars[ BIGGEST( BIGGEST( TZ_MAX_CHARS + 1, sizeof gmt ), ( 2 * ( MY_TZNAME_MAX + 1 ) ) ) ];
struct lsinfo lsis[ TZ_MAX_LEAPS ];
/* The time type to use for early times or if no transitions.
It is always zero for recent tzdb releases.
It might be nonzero for data from tzdb 2018e or earlier.
*/
int defaulttype;
};
extern struct state _PDCLIB_lclmem;
extern struct state _PDCLIB_gmtmem;
void _PDCLIB_gmtcheck(void);
struct tm * _PDCLIB_gmtsub( struct state const * sp, time_t const * timep, int_fast32_t offset, struct tm * tmp );
bool _PDCLIB_increment_overflow( int * ip, int j );
void _PDCLIB_init_ttinfo( struct ttinfo * s, int_fast32_t utoff, bool isdst, int desigidx );
struct tm * _PDCLIB_localsub( struct state const * sp, time_t const * timep, int_fast32_t setname, struct tm * const tmp );
struct tm * _PDCLIB_localtime_tzset( time_t const * timep, struct tm * tmp, bool setname );
time_t _PDCLIB_mktime_tzname( struct state * sp, struct tm * tmp, bool setname );
struct tm * _PDCLIB_timesub( const time_t * timep, int_fast32_t offset, const struct state * sp, struct tm * tmp );
int _PDCLIB_tzload( char const * name, struct state * sp, bool doextend );
bool _PDCLIB_tzparse(char const *, struct state *, bool);
void _PDCLIB_tzset_unlocked( void );
void _PDCLIB_update_tzname_etc( struct state const * sp, struct ttinfo const * ttisp );
#ifdef __cplusplus
}
#endif
#endif

91
libc/include/signal.h Normal file
View File

@ -0,0 +1,91 @@
/* Signal handling <signal.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_internal.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_t 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.
*/
_PDCLIB_PUBLIC 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. */
_PDCLIB_PUBLIC int raise( int sig );
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_SIGNAL_H
#include _PDCLIB_EXTEND_SIGNAL_H
#endif
#endif

26
libc/include/stdalign.h Normal file
View File

@ -0,0 +1,26 @@
/* 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
#ifndef __cplusplus
#define alignas _Alignas
#define alignof _Alignof
#endif
#define __alignas_is_defined 1
#define __alignof_is_defined 1
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_STDALIGN_H
#include _PDCLIB_EXTEND_STDALIGN_H
#endif
#endif

34
libc/include/stdarg.h Normal file
View File

@ -0,0 +1,34 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#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 )
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_STDARG_H
#include _PDCLIB_EXTEND_STDARG_H
#endif
#ifdef __cplusplus
}
#endif
#endif

24
libc/include/stdbool.h Normal file
View File

@ -0,0 +1,24 @@
/* 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
#ifndef __cplusplus
#define bool _Bool
#define true 1
#define false 0
#endif
#define __bool_true_false_are_defined 1
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_STDBOOL_H
#include _PDCLIB_EXTEND_STDBOOL_H
#endif
#endif

55
libc/include/stddef.h Normal file
View File

@ -0,0 +1,55 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_lib_ext1.h"
#include "pdclib/_PDCLIB_internal.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
#ifndef __cplusplus
typedef _PDCLIB_wchar_t wchar_t;
#endif
#ifndef _PDCLIB_NULL_DEFINED
#define _PDCLIB_NULL_DEFINED _PDCLIB_NULL_DEFINED
#define NULL _PDCLIB_NULL
#endif
#define offsetof( type, member ) _PDCLIB_offsetof( type, member )
/* Annex K -- Bounds-checking interfaces */
#if ( __STDC_WANT_LIB_EXT1__ + 0 ) != 0
#ifndef _PDCLIB_RSIZE_T_DEFINED
#define _PDCLIB_RSIZE_T_DEFINED _PDCLIB_RSIZE_T_DEFINED
typedef size_t rsize_t;
#endif
#endif
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_STDDEF_H
#include _PDCLIB_EXTEND_STDDEF_H
#endif
#ifdef __cplusplus
}
#endif
#endif

236
libc/include/stdint.h Normal file
View File

@ -0,0 +1,236 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_lib_ext1.h"
#include "pdclib/_PDCLIB_internal.h"
/* 7.18.1.1 Exact-width integer types. */
#ifdef _PDCLIB_int8_t
typedef _PDCLIB_int8_t int8_t;
typedef _PDCLIB_uint8_t uint8_t;
#endif
#ifdef _PDCLIB_int16_t
typedef _PDCLIB_int16_t int16_t;
typedef _PDCLIB_uint16_t uint16_t;
#endif
#ifdef _PDCLIB_int32_t
typedef _PDCLIB_int32_t int32_t;
typedef _PDCLIB_uint32_t uint32_t;
#endif
#ifdef _PDCLIB_int64_t
typedef _PDCLIB_int64_t int64_t;
typedef _PDCLIB_uint64_t uint64_t;
#endif
/* 7.18.1.2 Minimum-width integer types */
/* You are allowed to add more types here, e.g. int_least24_t. */
typedef _PDCLIB_int_least8_t int_least8_t;
typedef _PDCLIB_int_least16_t int_least16_t;
typedef _PDCLIB_int_least32_t int_least32_t;
typedef _PDCLIB_int_least64_t int_least64_t;
typedef _PDCLIB_uint_least8_t uint_least8_t;
typedef _PDCLIB_uint_least16_t uint_least16_t;
typedef _PDCLIB_uint_least32_t uint_least32_t;
typedef _PDCLIB_uint_least64_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 */
#if defined( __cplusplus ) && __cplusplus < 201103L
#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 */
#if _PDCLIB_TWOS_COMPLEMENT == 1
#if _PDCLIB_INT_LEAST8_MAX == 0x7f
#define INT8_MAX _PDCLIB_INT_LEAST8_MAX
#define INT8_MIN _PDCLIB_INT_LEAST8_MIN
#define UINT8_MAX _PDCLIB_UINT_LEAST8_MAX
#endif
#if _PDCLIB_INT_LEAST16_MAX == 0x7fff
#define INT16_MAX _PDCLIB_INT_LEAST16_MAX
#define INT16_MIN _PDCLIB_INT_LEAST16_MIN
#define UINT16_MAX _PDCLIB_UINT_LEAST16_MAX
#endif
#if _PDCLIB_INT_LEAST32_MAX == 0x7fffffffl
#define INT32_MAX _PDCLIB_INT_LEAST32_MAX
#define INT32_MIN _PDCLIB_INT_LEAST32_MIN
#define UINT32_MAX _PDCLIB_UINT_LEAST32_MAX
#endif
#if _PDCLIB_INT_LEAST64_MAX == 0x7fffffffffffffffll
#define INT64_MAX _PDCLIB_INT_LEAST64_MAX
#define INT64_MIN _PDCLIB_INT_LEAST64_MIN
#define UINT64_MAX _PDCLIB_UINT_LEAST64_MAX
#endif
#endif
/* 7.18.2.2 Limits of minimum-width integer types */
#define INT_LEAST8_MIN _PDCLIB_INT_LEAST8_MIN
#define INT_LEAST8_MAX _PDCLIB_INT_LEAST8_MAX
#define UINT_LEAST8_MAX _PDCLIB_UINT_LEAST8_MAX
#define INT_LEAST16_MIN _PDCLIB_INT_LEAST16_MIN
#define INT_LEAST16_MAX _PDCLIB_INT_LEAST16_MAX
#define UINT_LEAST16_MAX _PDCLIB_UINT_LEAST16_MAX
#define INT_LEAST32_MIN _PDCLIB_INT_LEAST32_MIN
#define INT_LEAST32_MAX _PDCLIB_INT_LEAST32_MAX
#define UINT_LEAST32_MAX _PDCLIB_UINT_LEAST32_MAX
#define INT_LEAST64_MIN _PDCLIB_INT_LEAST64_MIN
#define INT_LEAST64_MAX _PDCLIB_INT_LEAST64_MAX
#define UINT_LEAST64_MAX _PDCLIB_UINT_LEAST64_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 */
#if defined( __cplusplus ) && __cplusplus < 201103L
#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 */
/* Expand to an integer constant of specified value and type int_leastN_t */
#define INT8_C _PDCLIB_INT_LEAST8_C
#define INT16_C _PDCLIB_INT_LEAST16_C
#define INT32_C _PDCLIB_INT_LEAST32_C
#define INT64_C _PDCLIB_INT_LEAST64_C
/* Expand to an integer constant of specified value and type uint_leastN_t */
#define UINT8_C _PDCLIB_UINT_LEAST8_C
#define UINT16_C _PDCLIB_UINT_LEAST16_C
#define UINT32_C _PDCLIB_UINT_LEAST32_C
#define UINT64_C _PDCLIB_UINT_LEAST64_C
/* 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
/* Annex K -- Bounds-checking interfaces */
#if ( __STDC_WANT_LIB_EXT1__ + 0 ) != 0
#define RSIZE_MAX ( _PDCLIB_SIZE_MAX >> 1 )
#endif
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_STDINT_H
#include _PDCLIB_EXTEND_STDINT_H
#endif
#ifdef __cplusplus
}
#endif
#endif

935
libc/include/stdio.h Normal file
View File

@ -0,0 +1,935 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_lib_ext1.h"
#include "pdclib/_PDCLIB_internal.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 (1u<<0)
#define _IOLBF (1u<<1)
#define _IONBF (1u<<2)
/* The following are platform-dependent, 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").
*/
_PDCLIB_PUBLIC 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).
*/
_PDCLIB_PUBLIC int rename( const char * oldpath, const char * newpath );
/* 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()).
*/
_PDCLIB_PUBLIC 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().
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.)
Returns a pointer to the stream handle if successfull, NULL otherwise.
*/
_PDCLIB_PUBLIC 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 ).
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC int fscanf( FILE * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, ... );
/* Equivalent to fprintf( stdout, format, ... ). */
_PDCLIB_PUBLIC int printf( const char * _PDCLIB_restrict format, ... );
/* Equivalent to fscanf( stdin, format, ... ). */
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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>.
*/
_PDCLIB_PUBLIC 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>.
*/
_PDCLIB_PUBLIC 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>.
*/
_PDCLIB_PUBLIC 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>.
*/
_PDCLIB_PUBLIC 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>.
*/
_PDCLIB_PUBLIC 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>.
*/
_PDCLIB_PUBLIC 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>.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC int getc( FILE * stream );
/* Equivalent to fgetc( stdin ). */
_PDCLIB_PUBLIC int getchar( void );
/* Equivalent to fputc( c, stream ), but may be overloaded by a macro that
evaluates its parameter more than once.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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().
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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().
*/
_PDCLIB_PUBLIC 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().
*/
_PDCLIB_PUBLIC long int ftell( FILE * stream );
/* Equivalent to (void)fseek( stream, 0L, SEEK_SET ), except that the error
indicator for the stream is also cleared.
*/
_PDCLIB_PUBLIC void rewind( FILE * stream );
/* Error-handling functions */
/* Clear the end-of-file and error indicators for the given stream. */
_PDCLIB_PUBLIC void clearerr( FILE * stream );
/* Return zero if the end-of-file indicator for the given stream is not set,
nonzero otherwise.
*/
_PDCLIB_PUBLIC int feof( FILE * stream );
/* Return zero if the error indicator for the given stream is not set, nonzero
otherwise.
*/
_PDCLIB_PUBLIC 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).
*/
_PDCLIB_PUBLIC void perror( const char * s );
/* Annex K -- Bounds-checking interfaces */
#if ( __STDC_WANT_LIB_EXT1__ + 0 ) != 0
#define L_tmpnam_s _PDCLIB_L_tmpnam
#define TMP_MAX_S _PDCLIB_TMP_MAX
#ifndef _PDCLIB_ERRNO_T_DEFINED
#define _PDCLIB_ERRNO_T_DEFINED _PDCLIB_ERRNO_T_DEFINED
typedef int errno_t;
#endif
#ifndef _PDCLIB_RSIZE_T_DEFINED
#define _PDCLIB_RSIZE_T_DEFINED _PDCLIB_RSIZE_T_DEFINED
typedef _PDCLIB_size_t rsize_t;
#endif
/* 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()).
If successful, the FILE * pointed to by streamptr will be set to point at
the opened file handle, and the function returns zero. If unsuccessful,
the FILE * pointed to by streamptr will be set to NULL and a non-zero
value is returned.
The following conditions will be considered runtime constraint violations:
- streamptr being NULL.
In case of a constraint violation, no file is being created.
This implementation does not remove temporary files if the process aborts
abnormally (e.g. abort()).
*/
_PDCLIB_PUBLIC errno_t tmpfile_s( FILE * _PDCLIB_restrict * _PDCLIB_restrict streamptr );
/* Open the file with the given filename in the given mode, and sets the given
streamptr to point at the file handle for that file, 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.
The following conditions will be considered runtime constraint violations:
- streamptr being NULL.
- filename being NULL.
- mode being NULL.
In case of a constraint violation, no file is opened. If streamptr is not
NULL, *streamptr is set to NULL.
Returns zero if successful, non-zero otherwise.
*/
_PDCLIB_PUBLIC errno_t fopen_s( FILE * _PDCLIB_restrict * _PDCLIB_restrict streamptr, 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.)
The following conditions will be considered runtime constraint violations:
- newstreamptr being NULL.
- mode being NULL.
- stream being NULL.
In case of a constraint violation, no attempt is made to close or open any
file. If newstreamptr is not NULL, *newstreamptr is set to NULL.
Returns zero if successfull, non-zero otherwise.
*/
_PDCLIB_PUBLIC errno_t freopen_s( FILE * _PDCLIB_restrict * _PDCLIB_restrict newstreamptr, const char * _PDCLIB_restrict filename, const char * _PDCLIB_restrict mode, FILE * _PDCLIB_restrict stream );
/* None of these are implemented yet. Placeholder declarations. */
_PDCLIB_PUBLIC errno_t tmpnam_s( char * s, rsize_t maxsize );
_PDCLIB_PUBLIC int fprintf_s( FILE * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, ... );
_PDCLIB_PUBLIC int fscanf_s( FILE * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, ... );
_PDCLIB_PUBLIC int printf_s( const char * _PDCLIB_restrict format, ... );
_PDCLIB_PUBLIC int scanf_s( const char * _PDCLIB_restrict format, ... );
_PDCLIB_PUBLIC int snprintf_s( char * _PDCLIB_restrict s, rsize_t n, const char * _PDCLIB_restrict format, ... );
_PDCLIB_PUBLIC int sprintf_s( char * _PDCLIB_restrict s, rsize_t n, const char * _PDCLIB_restrict format, ... );
_PDCLIB_PUBLIC int sscanf_s( const char * _PDCLIB_restrict s, const char * _PDCLIB_restrict format, ... );
_PDCLIB_PUBLIC int vfprintf_s( FILE * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, _PDCLIB_va_list arg );
_PDCLIB_PUBLIC int vfscanf_s( FILE * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, _PDCLIB_va_list arg );
_PDCLIB_PUBLIC int vprintf_s( const char * _PDCLIB_restrict format, _PDCLIB_va_list arg );
_PDCLIB_PUBLIC int vscanf_s( const char * _PDCLIB_restrict format, _PDCLIB_va_list arg );
_PDCLIB_PUBLIC int vsnprintf_s( char * _PDCLIB_restrict s, rsize_t n, const char * _PDCLIB_restrict format, _PDCLIB_va_list arg );
_PDCLIB_PUBLIC int vsprintf_s( char * _PDCLIB_restrict s, rsize_t n, const char * _PDCLIB_restrict format, _PDCLIB_va_list arg );
_PDCLIB_PUBLIC int vsscanf_s( const char * _PDCLIB_restrict s, const char * _PDCLIB_restrict format, _PDCLIB_va_list arg );
_PDCLIB_PUBLIC char * gets_s( char * s, rsize_t n );
#endif
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_STDIO_H
#include _PDCLIB_EXTEND_STDIO_H
#endif
#ifdef __cplusplus
}
#endif
#endif

379
libc/include/stdlib.h Normal file
View File

@ -0,0 +1,379 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_lib_ext1.h"
#include "pdclib/_PDCLIB_internal.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() */
_PDCLIB_PUBLIC double atof( const char * nptr );
_PDCLIB_PUBLIC double strtod( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_restrict endptr );
_PDCLIB_PUBLIC float strtof( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_restrict endptr );
_PDCLIB_PUBLIC long double strtold( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_restrict endptr );
/* Separate 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.
*/
_PDCLIB_PUBLIC long int strtol( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_restrict endptr, int base );
_PDCLIB_PUBLIC long long int strtoll( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_restrict endptr, int base );
_PDCLIB_PUBLIC unsigned long int strtoul( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_restrict endptr, int base );
_PDCLIB_PUBLIC 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.)
*/
_PDCLIB_PUBLIC int atoi( const char * nptr );
_PDCLIB_PUBLIC long int atol( const char * nptr );
_PDCLIB_PUBLIC 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;)
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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().
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC _PDCLIB_Noreturn void abort( void ) _PDCLIB_NORETURN;
/* Register a function that will be called on quick_exit().
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.
*/
_PDCLIB_PUBLIC int at_quick_exit( void ( *func )( 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC _PDCLIB_Noreturn void exit( int status ) _PDCLIB_NORETURN;
/* Normal process termination. Functions registered by at_quick_exit() (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.)
quick_exit() does not return.
*/
_PDCLIB_PUBLIC _PDCLIB_Noreturn void quick_exit( int status ) _PDCLIB_NORETURN;
/* Normal process termination. Functions registered by atexit()/at_quick_exit()
(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.
*/
_PDCLIB_PUBLIC _PDCLIB_Noreturn void _Exit( int status ) _PDCLIB_NORETURN;
/* 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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 a matching element found, or NULL if no
match is found.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC int abs( int j );
_PDCLIB_PUBLIC long int labs( long int j );
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC div_t div( int numer, int denom );
_PDCLIB_PUBLIC ldiv_t ldiv( long int numer, long int denom );
_PDCLIB_PUBLIC lldiv_t lldiv( long long int numer, long long int denom );
/* TODO: Multibyte / wide character conversion functions */
/* TODO: Macro MB_CUR_MAX */
/*
_PDCLIB_PUBLIC int mblen( const char * s, size_t n );
_PDCLIB_PUBLIC int mbtowc( wchar_t * _PDCLIB_restrict pwc, const char * _PDCLIB_restrict s, size_t n );
_PDCLIB_PUBLIC int wctomb( char * s, wchar_t wc );
_PDCLIB_PUBLIC size_t mbstowcs( wchar_t * _PDCLIB_restrict pwcs, const char * _PDCLIB_restrict s, size_t n );
_PDCLIB_PUBLIC size_t wcstombs( char * _PDCLIB_restrict s, const wchar_t * _PDCLIB_restrict pwcs, size_t n );
*/
/* Annex K -- Bounds-checking interfaces */
#if ( __STDC_WANT_LIB_EXT1__ + 0 ) != 0
#ifndef _PDCLIB_ERRNO_T_DEFINED
#define _PDCLIB_ERRNO_T_DEFINED _PDCLIB_ERRNO_T_DEFINED
typedef int errno_t;
#endif
#ifndef _PDCLIB_RSIZE_T_DEFINED
#define _PDCLIB_RSIZE_T_DEFINED _PDCLIB_RSIZE_T_DEFINED
typedef size_t rsize_t;
#endif
/* A function type that can serve as a constraint handler (see below). The
first parameter is an error message on the constraint violation, the
second parameter a pointer to an implementation-defined object, the
third an error code related to the constraint violation.
If the function calling the constraint handler is defined to return
errno_t, the third parameter will be identical to the return value of
that function.
This implementation sets the second parameter of the constraint handler
call to NULL.
*/
typedef void ( *constraint_handler_t )( const char * _PDCLIB_restrict msg, void * _PDCLIB_restrict ptr, errno_t error );
/* The currently active constraint violation handler. This implementation
sets abort_handler_s as the default constraint violation handler.
*/
extern constraint_handler_t _PDCLIB_constraint_handler;
/* Set the given function as the new constraint violation handler. */
_PDCLIB_PUBLIC constraint_handler_t set_constraint_handler_s( constraint_handler_t handler );
/* One of two predefined constraint violation handlers. When called, it will
print an error message (including the message passed as the first
parameter to the handler function) and call abort().
*/
_PDCLIB_PUBLIC void abort_handler_s( const char * _PDCLIB_restrict msg, void * _PDCLIB_restrict ptr, errno_t error );
/* One of two predefined constraint violation handlers. Simply returns,
ignoring the constraint violation.
*/
_PDCLIB_PUBLIC void ignore_handler_s( const char * _PDCLIB_restrict msg, void * _PDCLIB_restrict ptr, errno_t error );
/* Search an environment-provided key-value map for the given key name.
If the name is found,
- if len is not NULL, the length of the associated value string is stored
in that location.
- if len < maxsize, the value string is copied to the indicated location.
If the name is not found,
- if len is not NULL, a zero is stored in that location.
- if maxsize > 0, value[0] is set to the null character.
Details on the provided keys and how to set / change them are determined by
the hosting OS and its glue function.
The following conditions will be considered runtime constraint violations:
- value being a NULL pointer.
- maxsize == 0 or maxsize > RSIZE_MAX.
In case of a constraint violation, if len is not NULL a zero will be
stored at that location, and the environment key-value map not searched.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t getenv_s( size_t * _PDCLIB_restrict len, char * _PDCLIB_restrict value, rsize_t maxsize, const char * _PDCLIB_restrict name );
/* 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, a pointer to the array member as
second parameter, and the context parameter passed to bsearch_s() as third
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 a matching element found, or NULL if no
match is found.
The following conditions will be considered runtime constraint violations:
- nmemb or size > RSIZE_MAX.
- nmemb > 0 and either of key, base, or compar being a null pointer.
In case of a constraint violation, the array will not be searched.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC void * bsearch_s( const void * key, const void * base, rsize_t nmemb, rsize_t size, int ( *compar )( const void * k, const void * y, void * context ), void * context );
/* 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, with the first two arguments
being pointers to the two objects to be compared, and the third argument
being the context parameter passed to qsort_s. The compar function 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.
The following conditions will be considered runtime constraint violations:
- nmemb or size > RSIZE_MAX.
- nmemb > 0 and either of base or compar being a null pointer.
In case of a constraint violation, the array will not be sorted.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t qsort_s( void * base, rsize_t nmemb, rsize_t size, int ( *compar )( const void * x, const void * y, void * context ), void * context );
/* TODO: Multibyte / wide character functions */
#endif
#ifdef __cplusplus
}
#endif
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_STDLIB_H
#include _PDCLIB_EXTEND_STDLIB_H
#endif
#endif

View File

@ -0,0 +1,26 @@
/* _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
#include "pdclib/_PDCLIB_internal.h"
/* This basically breaks the letter of the standard (which states that
noreturn be defined to _Noreturn). This defines noreturn -> _Noreturn
on C11 compliant compilers only (as older compilers do not know about
_Noreturn).
*/
#define noreturn _PDCLIB_Noreturn
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_STDNORETURN_H
#include _PDCLIB_EXTEND_STDNORETURN_H
#endif
#endif

394
libc/include/string.h Normal file
View File

@ -0,0 +1,394 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_lib_ext1.h"
#include "pdclib/_PDCLIB_internal.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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC char * strcat( char * _PDCLIB_restrict s1, const char * _PDCLIB_restrict s2 );
/* Append a maximum of n characters from the character array s2 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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 separator characters
which can differ from call to call. Leading separators are skipped, the first
trailing separator overwritten with '\0'.
Returns a pointer to the next token.
WARNING: This function uses static storage, and as such is not reentrant.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC char * strerror( int errnum );
/* Returns the length of the string s (excluding terminating '\0').
*/
_PDCLIB_PUBLIC size_t strlen( const char * s );
/* Annex K -- Bounds-checking interfaces */
#if ( __STDC_WANT_LIB_EXT1__ + 0 ) != 0
#ifndef _PDCLIB_ERRNO_T_DEFINED
#define _PDCLIB_ERRNO_T_DEFINED _PDCLIB_ERRNO_T_DEFINED
typedef int errno_t;
#endif
#ifndef _PDCLIB_RSIZE_T_DEFINED
#define _PDCLIB_RSIZE_T_DEFINED _PDCLIB_RSIZE_T_DEFINED
typedef _PDCLIB_size_t rsize_t;
#endif
/* Copy a number of n characters from the memory area pointed to by s2 to the
area pointed to by s1 of size s1max.
Returns zero if successful, non-zero otherwise.
The following conditions will be considered runtime constraint violations:
- s1 or s2 being NULL.
- s1max or n being > RSIZE_MAX.
- n > s1max (not enough space in s1).
- copying between overlapping objects.
In case of a constraint violation, if s1 is not NULL and s1max <= RSIZE_MAX
then the first s1max characters of s1 will be set to zero.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t memcpy_s( void * _PDCLIB_restrict s1, rsize_t s1max, const void * _PDCLIB_restrict s2, rsize_t n );
/* Copy a number of n characters from the memory area pointed to by s2 to the
area pointed to by s1 of size s1max. The two areas may overlap.
Returns zero if successful, non-zero otherwise.
The following conditions will be considered runtime constraint violations:
- s1 or s2 being NULL.
- s1max or n being > RSIZE_MAX.
- n > s1max (not enough space in s1).
In case of a constraint violation, if s1 is not NULL and s1max <= RSIZE_MAX
then the first s1max characters of s1 will be set to zero.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t memmove_s( void * _PDCLIB_restrict s1, rsize_t s1max, const void * _PDCLIB_restrict s2, rsize_t n );
/* Copy the character array s2 (including terminating '\0' byte) into the
character array s1.
Returns zero if successful, non-zero otherwise.
The following conditions will be considered runtime constraint violations:
- s1 or s2 being NULL.
- s1max being zero or > RSIZE_MAX.
- s1max not greater than strnlen_s( s2, s1max ) (not enough space in s1).
- copying between overlapping objects.
In case of a constraint violation, if s1 is not NULL and s1max <= RSIZE_MAX
then s1[0] will be set to '\0'.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t strcpy_s( char * _PDCLIB_restrict s1, rsize_t s1max, const char * _PDCLIB_restrict s2 );
/* Copy a maximum of n characters from the character array s2 into the character
array s1. If s2 is longer than n, s1[n] will be set to '\0'.
Returns zero if successful, non-zero otherwise.
ATTENTION ATTENTION ATTENTION
This function differs in two fundamental ways from strncpy():
- remaining space in s1 will NOT be zeroed. Their value is unspecified.
- s1 WILL be zero-terminated even if there is not enough space to hold
all n characters from s2.
THANK YOU FOR YOUR ATTENTION.
The following conditions will be considered runtime constraint violations:
- s1 or s2 being NULL.
- s1max or n being > RSIZE_MAX.
- s1max being zero.
- n >= s1max and s1max <= strnlen_s( s2, s1max ) (not enough space in s1).
- copying between overlapping objects.
In case of a constraint violation, if s1 is not NULL and s1max is greater
zero and <= RSIZE_MAX, s1[0] will be set to '\0'.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t strncpy_s( char * _PDCLIB_restrict s1, rsize_t s1max, const char * _PDCLIB_restrict s2, rsize_t n );
/* 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).
Elements following the terminating null character (if any) take unspecified
values.
Returns zero if successful, non-zero otherwise.
The following conditions will be considered runtime constraint violations:
- s1 or s2 being NULL.
- s1max being > RSIZE_MAX.
- s1max being zero.
- not enough space in s1 for both s1 and the characters copied from s2.
- copying between overlapping objects.
In case of a constraint violation, if s1 is not NULL and s1max is greater
zero and <= RSIZE_MAX, s1[0] will be set to '\0'.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t strcat_s( char * _PDCLIB_restrict s1, rsize_t s1max, const char * _PDCLIB_restrict s2 );
/* Append a maximum of n characters from the character array s2 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.
Elements following the terminating null character (if any) take unspecified
values.
Returns zero if successful, non-zero otherwise.
The following conditions will be considered runtime constraint violations:
- s1 or s2 being NULL.
- s1max or n being > RSIZE_MAX.
- s1max being zero.
- not enough space in s1 for both s1 and the characters copied from s2.
- copying between overlapping objects.
In case of a constraint violation, if s1 is not NULL and s1max is greater
zero and <= RSIZE_MAX, s1[0] will be set to '\0'.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t strncat_s( char * _PDCLIB_restrict s1, rsize_t s1max, const char * _PDCLIB_restrict s2, rsize_t n );
/* 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 of size *s1max. On subsequent calls, the first argument is
NULL unless you want to start parsing a new string. s2 holds an array of
separator characters which can differ from call to call. Leading separators
are skipped, the first trailing separator overwritten with '\0'.
Returns a pointer to the next token.
The following conditions will be considered runtime constraint violations:
- s1max, s2, or ptr being NULL.
- s1max or n being > RSIZE_MAX.
- s1max being zero.
- not enough space in s1 for both s1 and the characters copied from s2.
- copying between overlapping objects.
In case of a constraint violation, if s1 is not NULL and s1max is greater
zero and <= RSIZE_MAX, s1[0] will be set to '\0'.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC char * strtok_s( char * _PDCLIB_restrict s1, rsize_t * _PDCLIB_restrict s1max, const char * _PDCLIB_restrict s2, char ** _PDCLIB_restrict ptr );
/* Write the character c (interpreted as unsigned char) to the first n
characters of the memory area pointed to by s of size smax.
Returns zero if successful, non-zero otherwise.
The following conditions will be considered runtime constraint violations:
- s being NULL.
- smax or n being > RSIZE_MAX.
- n being > smax.
In case of a constraint violation, if s is not NULL and smax is <= RSIZE_MAX
the value of c (interpreted as unsigned char) is written to the first smax
characters of s.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t memset_s( void * s, rsize_t smax, int c, rsize_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.
If the length of the mapped string is < maxsize, the string is copied to s.
Otherwise, if maxsize is greater than zero, as much of the string as does
fit is copied, and s[maxsize-1] set to '\0'. If maxsize is greater than 3,
the partial string is made to end in "...".
Returns zero if the string was copied successfully in full, non-zero
otherwise.
The following conditions will be considered runtime constraint violations:
- s being NULL.
- maxsize being zero or > RSIZE_MAX.
In case of a constraint violation, s is not modified.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t strerror_s( char * s, rsize_t maxsize, errno_t errnum );
/* Map an error number to a (locale-specific) error message string, the same
way as strerror_s() would do. Error numbers are typically errno values,
but any number is mapped to a message.
TODO: PDCLib does not yet support locales.
Returns the length of the mapped string.
*/
_PDCLIB_PUBLIC size_t strerrorlen_s( errno_t errnum );
/* Returns the length of the string s (excluding terminating '\0').
If there is no null character in the first maxsize characters of s,
rerturns maxsize. If s is NULL, returns zero.
At most the first maxsize characters of s shall be accessed by the
function.
*/
_PDCLIB_PUBLIC size_t strnlen_s( const char * s, size_t maxsize );
#endif
#ifdef __cplusplus
}
#endif
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_STRING_H
#include _PDCLIB_EXTEND_STRING_H
#endif
#endif

260
libc/include/threads.h Normal file
View File

@ -0,0 +1,260 @@
/* Threads <threads.h>
This file is part of the Public Domain C Library (PDCLib).
Permission is granted to use, modify, and / or redistribute at will.
*/
/* This header does not include any platform-specific information, but as
a whole is optional (depending on whether threads are supported or not,
ref. __STDC_NO_THREADS__), which is why it is located in the example
platform instead of the general include directory.
*/
#ifndef _PDCLIB_THREADS_H
#define _PDCLIB_THREADS_H _PDCLIB_THREADS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
#if __STDC_NO_THREADS__ == 1
#error __STDC_NO_THREADS__ defined but <threads.h> included. Something is wrong about your setup.
#endif
#if __STDC_VERSION__ >= 201112L
/* The rest of <threads.h> can work with a pre-C11 compiler just fine. */
#define thread_local _Thread_local
#endif
/* Initializing value for an object of type once_flag */
#define ONCE_FLAG_INIT _PDCLIB_ONCE_FLAG_INIT
/* Maximum number of times destructors are called on thread termination */
#define TSS_DTOR_ITERATIONS _PDCLIB_TSS_DTOR_ITERATIONS
/* Condition variable */
typedef _PDCLIB_cnd_t cnd_t;
/* Thread */
typedef _PDCLIB_thrd_t thrd_t;
/* Thread-specific storage */
typedef _PDCLIB_tss_t tss_t;
/* Mutex */
typedef _PDCLIB_mtx_t mtx_t;
/* TSS destructor */
typedef void ( *tss_dtor_t )( void * );
/* Thread start function */
typedef int ( *thrd_start_t )( void * );
/* Flag for use with call_once() */
typedef _PDCLIB_once_flag once_flag;
/* TODO: Documentation. */
enum
{
mtx_plain,
mtx_recursive,
mtx_timed
};
/* TODO: Documentation. */
enum
{
thrd_timedout,
thrd_success,
thrd_busy,
thrd_error,
thrd_nomem
};
/* Initialization functions */
/* Ensure that func is called only the first time call_once() is called
for a given flag.
*/
_PDCLIB_PUBLIC void call_once( once_flag * flag, void ( *func )( void ) );
/* Condition variable functions */
/* Unblock threads waiting on given condition.
Returns thrd_success if successful, thrd_error if the request could not
be honored.
*/
_PDCLIB_PUBLIC int cnd_broadcast( cnd_t * cond );
/* Destroy condition variable.
No threads may be waiting on a condition when it is destroyed.
*/
_PDCLIB_PUBLIC void cnd_destroy( cnd_t * cond );
/* Initialize condition variable.
Returns thrd_success if successful, thrd_nomem if out of memory, and
thrd_error if the request could not be honored.
Initializes the variable in a way that a thread calling cnd_wait() on it
would block.
*/
_PDCLIB_PUBLIC int cnd_init( cnd_t * cond );
/* Unblock one thread waiting on the condition variable.
Returns thrd_success if successful, thrd_error if the request could not
be honored.
*/
_PDCLIB_PUBLIC int cnd_signal( cnd_t * cond );
/* TODO: Documentation.
Returns thrd_success if successful, thrd_timedout if the specified time
is reached without acquiring the resource, or thrd_error if the request
could not be honored.
*/
_PDCLIB_PUBLIC int cnd_timedwait( cnd_t * _PDCLIB_restrict cond, mtx_t * _PDCLIB_restrict mtx, const struct timespec * _PDCLIB_restrict ts );
/* TODO: Documentation.
Returns thrd_success if successful, thrd_error if the request could not
be honored.
*/
int cnd_wait( cnd_t * cond, mtx_t * mtx );
/* Mutex functions */
/* Destroy mutex variable.
No threads may be waiting on a mutex when it is destroyed.
*/
_PDCLIB_PUBLIC void mtx_destroy( mtx_t * mtx );
/* Initialize mutex variable.
Returns thrd_success if successful, thrd_error if the request could not
be honored.
Type must have one of the following values:
mtx_plain -- non-recursive mutex
mtx_timed -- non-recursive mutex supporting timeout
mtx_plain | mtx_recursive -- recursive mutex
mtx_timed | mtx_recursive -- recursive mutex supporting timeout
*/
_PDCLIB_PUBLIC int mtx_init( mtx_t * mtx, int type );
/* Try to lock the given mutex (blocking).
Returns thrd_success if successful, thrd_error if the request could not
be honored.
If the given mutex is non-recursive, it must not be already locked by
the calling thread.
*/
_PDCLIB_PUBLIC int mtx_lock( mtx_t * mtx );
/* TODO: Documentation.
Returns thrd_success if successful, thrd_timedout if the specified time
is reached without acquiring the resource, or thrd_error if the request
could not be honored.
*/
_PDCLIB_PUBLIC int mtx_timedlock( mtx_t * _PDCLIB_restrict mtx, const struct timespec * _PDCLIB_restrict ts );
/* Try to lock the given mutex (non-blocking).
Returns thrd_success if successful, thrd_busy if the resource is already
locked, or thrd_error if the request could not be honored.
*/
_PDCLIB_PUBLIC int mtx_trylock( mtx_t * mtx );
/* Unlock the given mutex.
Returns thrd_success if successful, thrd_error if the request could not
be honored.
The given mutex must be locked by the calling thread.
*/
_PDCLIB_PUBLIC int mtx_unlock( mtx_t * mtx );
/* Thread functions */
/* Create a new thread.
Returns thrd_success if successful, thrd_nomem if out of memory, and
thrd_error if the request could not be honored.
Create a new thread executing func( arg ), and sets thr to identify
the created thread. (Identifiers may be reused afer a thread exited
and was either detached or joined.)
*/
_PDCLIB_PUBLIC int thrd_create( thrd_t * thr, thrd_start_t func, void * arg );
/* Identify the calling thread.
Returns the identifier of the calling thread.
*/
_PDCLIB_PUBLIC thrd_t thrd_current( void );
/* Notify the OS to destroy all resources of a given thread once it
terminates.
Returns thrd_success if successful, thrd_error if the request could not
be honored.
The given thread must not been previously detached or joined.
*/
_PDCLIB_PUBLIC int thrd_detach( thrd_t thr );
/* Compare two thread identifiers for equality.
Returns nonzero if both parameters identify the same thread, zero
otherwise.
*/
_PDCLIB_PUBLIC int thrd_equal( thrd_t thr0, thrd_t thr1 );
/* Terminate calling thread, set result code to res.
When the last thread of a program terminates the program shall terminate
normally as if by exit( EXIT_SUCCESS ).
FIXME: The result code setting is NOT implemented correctly at this point.
The value is indeterminate.
*/
_PDCLIB_PUBLIC _PDCLIB_Noreturn void thrd_exit( int res ) _PDCLIB_NORETURN;
/* Join the given thread with the calling thread.
Returns thrd_success if successful, thrd_error if the request could not
be honored.
Function blocks until the given thread terminates. If res is not NULL,
the given thread's result code will be stored at that address.
*/
_PDCLIB_PUBLIC int thrd_join( thrd_t thr, int * res );
/* Suspend the calling thread for the given duration or until a signal not
being ignored is received.
Returns zero if the requested time has elapsed, -1 if interrupted by a
signal, negative if the request failed.
If remaining is not NULL, and the sleeping thread received a signal that
is not being ignored, the remaining time (duration minus actually elapsed
time) shall be stored at that address.
*/
_PDCLIB_PUBLIC int thrd_sleep( const struct timespec * duration, struct timespec * remaining );
/* Permit other threads to run. */
_PDCLIB_PUBLIC void thrd_yield( void );
/* Thread-specific storage functions */
/* Initialize thread-specific storage, with optional destructor
Returns thrd_success if successful, thrd_error otherwise (in this case
key is set to an undefined value).
*/
_PDCLIB_PUBLIC int tss_create( tss_t * key, tss_dtor_t dtor );
/* Release all resources of a given thread-specific storage. */
_PDCLIB_PUBLIC void tss_delete( tss_t key );
/* Returns the value for the current thread associated with the given key.
*/
_PDCLIB_PUBLIC void * tss_get( tss_t key );
/* Sets the value associated with the given key for the current thread.
Returns thrd_success if successful, thrd_error if the request could not
be honored.
*/
_PDCLIB_PUBLIC int tss_set( tss_t key, void * val );
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_THREADS_H
#include _PDCLIB_EXTEND_THREADS_H
#endif
#ifdef __cplusplus
}
#endif
#endif

191
libc/include/time.h Normal file
View File

@ -0,0 +1,191 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_lib_ext1.h"
#include "pdclib/_PDCLIB_internal.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 comments in _PDCLIB_config.h on the semantics of time_t and clock_t. */
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
/* Implementor's note: If you change this structure, and are using Pthread
threading support, check auxiliary/pthread/pthread_readout.c for its
twin. It is imperative that Pthread and PDCLib use identical layouts for
struct timespec, as they are implicitly cast from one to the other. This
cannot be checked for in this header (as we may not include host system
headers here), so the assert()s are in pthread_readout.c (which, in turn,
cannot include *this* header, which is why this admonishment to keep the
definitions in sync exists...).
*/
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.
*/
_PDCLIB_PUBLIC clock_t clock( void );
/* Returns the difference between two calendar times in seconds. */
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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".
*/
_PDCLIB_PUBLIC char * asctime( const struct tm * timeptr );
/* Equivalent to asctime( localtime( timer ) ). */
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC size_t strftime( char * _PDCLIB_restrict s, size_t maxsize, const char * _PDCLIB_restrict format, const struct tm * _PDCLIB_restrict timeptr );
/* Annex K -- Bounds-checking interfaces */
#if ( __STDC_WANT_LIB_EXT1__ + 0 ) != 0
#ifndef _PDCLIB_ERRNO_T_DEFINED
#define _PDCLIB_ERRNO_T_DEFINED _PDCLIB_ERRNO_T_DEFINED
typedef int errno_t;
#endif
#ifndef _PDCLIB_RSIZE_T_DEFINED
#define _PDCLIB_RSIZE_T_DEFINED _PDCLIB_RSIZE_T_DEFINED
typedef _PDCLIB_size_t rsize_t;
#endif
/* Converts the broken-down time pointed to by timeptr into a string in the
form "Sun Sep 16 01:03:52 1973\n\0", which is stored in buffer s of maxsize.
Returns zero if the time was successfully converted and stored, non-zero
otherwise.
The following conditions will be considered runtime constraint violations:
- s or timeptr being NULL.
- maxsize being < 26 or > RSIZE_MAX.
- the broken-down time pointed to by timeptr not being normalized.
- the year represented by the broken-down time pointed to by timeptr
being < 0 or > 9999.
In case of a constraint violation, the time will not be converted. If
s is not NULL and maxsize is neither zero nor > RSIZE_MAX, s[0] will be
set to '\0'.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC errno_t asctime_s( char * s, rsize_t maxsize, const struct tm * timeptr );
/* Equivalent to asctime_s( s, maxsize, localtime( timer ) ). */
_PDCLIB_PUBLIC errno_t ctime_s( char * s, rsize_t maxsize, const time_t * timer );
/* Converts the calender time pointed to by timer into a broken-down time
expressed as UTC, which gets stored in the result struct.
Returns a pointer to the broken-down time, or a NULL pointer if if
cannot be represented or stored.
The following conditions will be considered runtime constraint violations:
- timer or result being NULL.
In case of a constraint violation, the time will not be converted.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC struct tm * gmtime_s( const time_t * _PDCLIB_restrict timer, struct tm * _PDCLIB_restrict result );
/* Converts the calender time pointed to by timer into a broken-down time
expressed as local time, which gets stored in the result struct.
Returns a pointer to the broken-down time, or a NULL pointer if if
cannot be represented or stored.
The following conditions will be considered runtime constraint violations:
- timer or result being NULL.
In case of a constraint violation, the time will not be converted.
The currently active constraint violation handler function will be called
(see set_constraint_handler_s()).
*/
_PDCLIB_PUBLIC struct tm * localtime_s( const time_t * _PDCLIB_restrict timer, struct tm * _PDCLIB_restrict result );
#endif
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_TIME_H
#include _PDCLIB_EXTEND_TIME_H
#endif
#ifdef __cplusplus
}
#endif
#endif

152
libc/include/wctype.h Normal file
View File

@ -0,0 +1,152 @@
/* 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
#ifdef __cplusplus
extern "C" {
#endif
#include "pdclib/_PDCLIB_internal.h"
typedef _PDCLIB_wint_t wint_t;
typedef int wctrans_t;
typedef int 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 ) */
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC int iswblank( wint_t wc );
/* Returns true if the wide character is a control character. */
_PDCLIB_PUBLIC int iswcntrl( wint_t wc );
/* Returns true if the wide character is a decimal digit. Locale-
independent. */
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC int iswlower( wint_t wc );
/* Returns true for every printing wide character. */
_PDCLIB_PUBLIC int iswprint( wint_t wc );
/* Returns true for a locale-specific set of punctuation characters that
are neither whitespace nor alphanumeric.
*/
_PDCLIB_PUBLIC int iswpunct( wint_t wc );
/* Returns true for a locale-specific set of whitespace characters that
are neither alphanumeric, graphic, or punctuation.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC int iswupper( wint_t wc );
/* Returns true if the wide character is a hexadecimal digit. Locale-
independent. */
_PDCLIB_PUBLIC 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).
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC 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).
*/
_PDCLIB_PUBLIC 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.
*/
_PDCLIB_PUBLIC wctrans_t wctrans( const char * property );
/* Extension hook for downstream projects that want to have non-standard
extensions to standard headers.
*/
#ifdef _PDCLIB_EXTEND_WCTYPE_H
#include _PDCLIB_EXTEND_WCTYPE_H
#endif
#ifdef __cplusplus
}
#endif
#endif

46
libc/memcmp.c Normal file
View File

@ -0,0 +1,46 @@
/* 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

64
libc/strncat.c Normal file
View File

@ -0,0 +1,64 @@
/* 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 * s1, const char * 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

55
libc/strncmp.c Normal file
View File

@ -0,0 +1,55 @@
/* 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

59
libc/strncpy.c Normal file
View File

@ -0,0 +1,59 @@
/* 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 * s1, const char * 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

67
main.c Normal file
View File

@ -0,0 +1,67 @@
#include "main.h"
#include <string.h>
#include "runtime/panic_assert.h"
#include "runtime/stdio.h"
#include "graphics/graphics.h"
#include "graphics/unifont.h"
#include "memory/memory.h"
FASTCALL_ABI EFI_STATUS efiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
SystemTable->ConOut->EnableCursor(SystemTable->ConOut, TRUE);
efiImageHandle = ImageHandle;
efiSystemTable = SystemTable;
efiBootServices = SystemTable->BootServices;
efiStdin = SystemTable->ConIn;
efiStdout = SystemTable->ConOut;
efiStderr = SystemTable->StdErr;
// disable the watchdog timer so that the application does not reset after 5mins
efiBootServices->SetWatchdogTimer(0, 0, 0, NULL);
io_WriteConsole(PROJECT_NAME "\r\n\r\nAWAITING FOR USER INPUT");
// wait for a user keypress
UINTN index;
SystemTable->BootServices->WaitForEvent(1, &SystemTable->ConIn->WaitForKey, &index);
EFI_INPUT_KEY key;
SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn, &key);
io_WriteConsole("...Key Pressed\r\n");
io_Printf("Pressed key: %d, %d\r\n", (int)key.ScanCode, (int)key.UnicodeChar);
graphics_Init();
io_PauseForKeystroke();
graphics_ClearBuffer(&HelosGraphics_Color_Black);
graphics_FillPixel(0, 0, 20, 20, &HelosGraphics_Color_Black);
graphics_FillPixel(20, 0, 40, 20, &HelosGraphics_Color_White);
graphics_FillPixel(40, 0, 60, 20, &HelosGraphics_Color_Red);
graphics_FillPixel(60, 0, 80, 20, &HelosGraphics_Color_Green);
graphics_FillPixel(80, 0, 100, 20, &HelosGraphics_Color_Blue);
graphics_FillPixel(100, 0, 120, 20, &HelosGraphics_Color_Cyan);
graphics_FillPixel(120, 0, 140, 20, &HelosGraphics_Color_Magenta);
graphics_FillPixel(140, 0, 160, 20, &HelosGraphics_Color_Yellow);
uint32_t helloString[] = {0x89C6, 0x7A97, 0x7CFB, 0x7EDF, 0x4E09, ' ', 'H', 'e', 'l', 'l', 'o', ',', ' ', 'a', 'n', 'd', ',', ' ', 'n', 'i', 'c', 'e', ' ', 't', 'o', ' ', 'm', 'e', 'e', 't', ' ', 'y', 'o', 'u', '!'};
unifont_DrawString(0, 20, &HelosGraphics_Color_White, helloString, sizeof(helloString) / sizeof(uint32_t));
graphics_CursorX = 0;
graphics_CursorY = 52;
runtime_InitPaging();
return EFI_SUCCESS;
}
EFI_HANDLE efiImageHandle;
EFI_SYSTEM_TABLE * efiSystemTable;
EFI_BOOT_SERVICES *efiBootServices;
SIMPLE_TEXT_OUTPUT_INTERFACE *efiStdout, *efiStderr;
SIMPLE_INPUT_INTERFACE * efiStdin;
char Buffer[HELOS_BUFFER_SIZE];

48
main.h Normal file
View File

@ -0,0 +1,48 @@
#ifdef __cplusplus
extern "C" {
#include <efi.h>
}
#else
#include <efi.h>
#endif
#define PROJECT_NAME "Helos1"
#define PROJECT_NAME_LONG L"Helos1"
#define FASTCALL_ABI __attribute__((ms_abi)) // declares a function as Microsoft x64 ABI. Used in the EFI part of the kernel.
#define SYSV_ABI __attribute__((sysv_abi)) // declares a function as System V AMD64 ABI. Used in the Kernel space (other than EFI bootstrap code).
#define ALIGNED(n) __attribute__((__aligned__(n)))
#define PACKED __attribute__((__packed__))
#if (defined __STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define NORETURN _Noreturn
#else
#define NORETURN __attribute__((__noreturn__))
#endif
#define noreturn NORETURN
// Don't (uint64_t) a function symbol directly, use this
#define LOCATE_SYMBOL(var, symbol) \
asm volatile("leaq " #symbol "(%%rip), %0" \
: "=r"(var))
extern EFI_HANDLE efiImageHandle;
extern EFI_SYSTEM_TABLE * efiSystemTable;
extern EFI_BOOT_SERVICES *efiBootServices;
extern SIMPLE_TEXT_OUTPUT_INTERFACE *efiStdout, *efiStderr;
extern SIMPLE_INPUT_INTERFACE * efiStdin;
#define HELOS_BUFFER_SIZE 16384
extern char Buffer[HELOS_BUFFER_SIZE]; // general-purpose buffer, user saved (volatile), not used in interrupt handlers
extern const char link_TextStart[], link_TextEnd[];
extern const char link_DataStart[], link_DataEnd[];
extern const char link_RodataStart[], link_RodataEnd[];
extern const char link_BssStart[], link_BssEnd[];
extern const char link_RelocStart[], link_RelocEnd[];

19
memory/Makefile Normal file
View File

@ -0,0 +1,19 @@
SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
objects = $(patsubst %.c,%.o,$(wildcard *.c)) $(patsubst %.cpp,%.o,$(wildcard *.cpp))
objects_fasm = $(patsubst %.S,%.o,$(wildcard *.S))
objects_test = $(patsubst %.c,%.o,$(wildcard test_*.c)) $(patsubst %.cpp,%.o,$(wildcard test_*.cpp))
objects := $(filter-out $(objects_test),$(objects))
all: $(objects) $(objects_fasm)
$(objects_fasm):
$(FASM) $(patsubst %.o,%.S,$@) $@
clean:
-$(RM) *.o

41
memory/liballoc_impl.c Normal file
View File

@ -0,0 +1,41 @@
#include "memory.h"
#include "paging_internal.h"
#include "../runtime/stdio.h"
#include "../interrupt/interrupt.h"
#include <stdint.h>
#include "../extlib/liballoc/liballoc_1_1.h"
int liballoc_lock() {
if (interrupt_Enabled)
asm volatile("cli");
return 0;
}
int liballoc_unlock() {
if (interrupt_Enabled)
asm volatile("sti");
return 0;
}
static uint64_t heapBreak = KERNEL_HEAP_VIRTUAL;
void *liballoc_alloc(size_t pages) {
void *ret = (void *)heapBreak;
heapBreak += SYSTEM_PAGE_SIZE * pages;
paging_map_PageAllocated((uint64_t)ret, pages, MAP_PROT_READ | MAP_PROT_WRITE);
io_Printf("liballoc_alloc: allocated %u pages at HEAP+%llx (%llx)\n", pages, ret - KERNEL_HEAP_VIRTUAL, ret);
return ret;
}
int liballoc_free(void *ptr, size_t pages) {
paging_map_FreeAllocated((uint64_t)ptr, (uint64_t)ptr + SYSTEM_PAGE_SIZE * pages);
io_Printf("liballoc_free: freed %u pages at HEAP+%llx (%llx)\n", pages, ptr - KERNEL_HEAP_VIRTUAL, ptr);
return 0;
}

40
memory/memory.c Normal file
View File

@ -0,0 +1,40 @@
#include "../main.h"
#include "memory.h"
#include "../runtime/stdio.h"
#include "../extlib/liballoc/liballoc_1_1.h"
#include <string.h>
void *efiMallocTyped(size_t size, EFI_MEMORY_TYPE type) {
void *data;
efiBootServices->AllocatePool(type, size, &data);
memset(data, 0, size);
return data;
}
void *efiMalloc(size_t size) {
return efiMallocTyped(size, EfiLoaderData);
}
void efiFree(void *data) {
efiBootServices->FreePool(data);
}
void *kMalloc(size_t size) {
void *mem = liballoc_kmalloc(size);
io_Printf("kMalloc: size=%llu, pos=0x%llx\n", size, mem);
return mem;
}
void kFree(void *data) {
io_Printf("kFree: 0x%llx\n", data);
liballoc_kfree(data);
}
void *kMemoryMap(void *desiredVirtual, int pageCount, int protectionFlags, int flags, int fd) {
}
void kMemoryUnmap(void *pageStart, int pageCount) {
}

100
memory/memory.h Normal file
View File

@ -0,0 +1,100 @@
#pragma once
#include "../main.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#define KERNEL_CODE_VIRTUAL 0xFFFFFFFFC0000000ull // 2^64 - 1GiB
#define KERNEL_STACK_END_VIRTUAL (KERNEL_CODE_VIRTUAL) // kernel stack sits right below kernel code
#define KERNEL_STACK_INITIAL_SIZE SYSTEM_PAGE_2M_SIZE // currently must be 2 MiB
#define KERNEL_HEAP_VIRTUAL 0xFFFFFFFF00000000ull // 2^64 - 4GiB
#define KERNEL_FRAMEBUFFER_MAPPING 0xFFFFFFFEE0000000ull // 2^64 - 4GiB - 512MiB
#define KERNEL_MISC_MAPPING 0xFFFFFFFEC0000000ull // 2^64 - 5GiB
#define KERNEL_IDT_MAPPING KERNEL_MISC_MAPPING
#define KERNEL_IDT_SIZE (256ull * 16) // fill the 256 interrupt vectors
#define KERNEL_GDT_MAPPING (KERNEL_MISC_MAPPING + KERNEL_IDT_SIZE)
#define KERNEL_GDT_SIZE (16ull * 8)
#define KERNEL_IDTR_MAPPING (KERNEL_MISC_MAPPING + KERNEL_IDT_SIZE + KERNEL_GDT_SIZE)
#define KERNEL_GDTR_MAPPING (KERNEL_MISC_MAPPING + KERNEL_IDT_SIZE + KERNEL_GDT_SIZE + 12)
#define KERNEL_MISC_NEXT (KERNEL_MISC_MAPPING + KERNEL_IDT_SIZE + KERNEL_GDT_SIZE + 24)
#define KERNEL_MISC_SIZE (KERNEL_IDT_SIZE + KERNEL_GDT_SIZE + 24) // add all the misc sizes
extern uint64_t paging_LoaderCodeAddress, paging_LoaderCodeSize; // physical address for loader code section
static inline uint64_t paging_MapFunction(void *func) {
return ((uint64_t)func - paging_LoaderCodeAddress) + KERNEL_CODE_VIRTUAL;
}
// efiMallocTyped allocates from EFI_BOOT_SERVICES.AllocatePool.
void *efiMallocTyped(size_t size, EFI_MEMORY_TYPE type);
// efiMallocTyped allocates from EFI_BOOT_SERVICES.AllocatePool
// with a memory type of EfiLoaderData.
void *efiMalloc(size_t size);
// efiFree frees data allocated from efiMalloc.
void efiFree(void *data);
// kMalloc allocates from system memory directly after paging has been set up
void *kMalloc(size_t size);
// kFree frees data allocated from kMalloc.
void kFree(void *data);
extern EFI_MEMORY_DESCRIPTOR *efiMemoryMap;
extern UINTN efiMemoryMapSize;
extern UINTN efiMemoryMapKey;
extern UINTN efiDescriptorSize;
extern UINT32 efiDescriptorVertion;
// runtime_InitPaging initializes paging and kMalloc/kFree allocator.
// This function calls ExitBootServices()!!! which is great
// Furthermore, it sets up a new stack, calls kMain() and does not return.
//
// If it fails, Panic() is called.
noreturn void runtime_InitPaging();
#define SYSTEM_PAGE_SIZE 4096ull // UEFI uses 4KiB pages by default
#define SYSTEM_PAGE_2M_SIZE 2097152ull // 2 MiB page size
#define SYSTEM_PAGE_1G_SIZE 1073741824ull // 1 GiB page size
#define MAX_SYSTEM_MEMORY_PAGES 16777216ull // 64 GiB
#define MAP_PROT_NONE 0
#define MAP_PROT_EXEC 1
#define MAP_PROT_WRITE 2
#define MAP_PROT_READ 4
#define MAP_DATA 1 // Data, not initialized (kept as-is)
#define MAP_INITIALIZED_DATA 2 // Data, zeroed
#define MAP_FILE 3 // Memory-mapped file IO
// kMemoryPageSize returns the size, in bytes, of the physical memory page.
inline static size_t kMemoryPageSize() {
return SYSTEM_PAGE_SIZE;
}
// kMemoryMap maps new physical pages into the kernel virtual memory space.
//
// The memory is not cleared.
void *kMemoryMap(void *desiredVirtual, int pageCount, int protectionFlags, int dataType, int fd);
// kMemoryUnmap unmaps previously mapped physical memory of the kernel space.
void kMemoryUnmap(void *pageStart, int pageCount);
#ifdef __cplusplus
} // extern "C"
#endif

60
memory/memory.hpp Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include <cstddef>
#include <limits>
#include <new>
#include "memory.h"
namespace helos {
class bad_alloc: public std::exception {
public:
bad_alloc() noexcept {}
bad_alloc(const bad_alloc &other) noexcept {}
bad_alloc &operator=(const bad_alloc &other) noexcept { return *this; }
virtual const char *what() const noexcept override { return "helos::bad_alloc"; }
};
// kAllocator is a class wrapper for kMalloc/Free satisfying the named requirement Allocator.
template<typename Type>
class kAllocator {
public:
typedef Type value_type;
kAllocator() = default;
template<typename Other>
constexpr kAllocator(const kAllocator<Other> &) {}
Type *allocate(std::size_t n) {
return kMalloc(n * sizeof(Type));
}
void deallocate(Type *p, std::size_t n) {
kFree(p);
}
};
template<class T, class U>
bool operator==(const kAllocator<T> &, const kAllocator<U> &) {
return true;
}
template<class T, class U>
bool operator!=(const kAllocator<T> &, const kAllocator<U> &) {
return false;
}
} // namespace helos
// globally overload the new and delete operators
// so keep this header at the top of every source file
//
// operators new and delete only call kMalloc/kFree, so C++ code
// must stay after paging setup
void *operator new(std::size_t size);
void operator delete(void *ptr) noexcept;

13
memory/memory_cpp.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "memory.hpp"
void *operator new(std::size_t size) {
void *data = kMalloc(size);
/*if (!data) {
throw helos::bad_alloc();
}*/
return data;
}
void operator delete(void *ptr) noexcept {
kFree(ptr);
}

Some files were not shown because too many files have changed in this diff Show More