Initial commit
This commit is contained in:
commit
90fb68940b
2
.clangd
Normal file
2
.clangd
Normal file
@ -0,0 +1,2 @@
|
||||
CompileFlags:
|
||||
Add: ["-I/usr/include/efi", "-I/usr/include/efi/x86_64", "-DHELOS"]
|
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal 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
199
Linker.ld
Normal 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
62
Linker.ld.old
Normal 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
33
Makefile
Normal 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
28
Makefile.flags
Normal 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
19
Makefile.subdir
Normal 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
256
execformat/pe/format.h
Normal 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
|
106
execformat/pe/read_headers.c
Normal file
106
execformat/pe/read_headers.c
Normal 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
108
execformat/pe/reloc.c
Normal 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
30
execformat/pe/reloc.h
Normal 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
50
execformat/pe/struct.c
Normal 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
29
execformat/pe/struct.h
Normal 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
|
118
execformat/pe/test_headers.c
Normal file
118
execformat/pe/test_headers.c
Normal 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
20
extlib/Makefile.old
Normal 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
26
extlib/liballoc/HOWTO
Normal 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
10
extlib/liballoc/LICENSE
Normal 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
68
extlib/liballoc/README.md
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
832
extlib/liballoc/liballoc_1_1.c
Normal file
832
extlib/liballoc/liballoc_1_1.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
87
extlib/liballoc/liballoc_1_1.h
Normal file
87
extlib/liballoc/liballoc_1_1.h
Normal 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
23
extlib/libvterm/LICENSE
Normal 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
223
extlib/libvterm/encoding.c
Normal 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;
|
||||
}
|
36
extlib/libvterm/encoding/DECdrawing.inc
Normal file
36
extlib/libvterm/encoding/DECdrawing.inc
Normal 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,
|
||||
}
|
||||
};
|
6
extlib/libvterm/encoding/uk.inc
Normal file
6
extlib/libvterm/encoding/uk.inc
Normal file
@ -0,0 +1,6 @@
|
||||
static const struct StaticTableEncoding encoding_uk = {
|
||||
{ .decode = &decode_table },
|
||||
{
|
||||
[0x23] = 0x00a3,
|
||||
}
|
||||
};
|
104
extlib/libvterm/fullwidth.inc
Normal file
104
extlib/libvterm/fullwidth.inc
Normal 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
228
extlib/libvterm/keyboard.c
Normal 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
89
extlib/libvterm/mouse.c
Normal 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
329
extlib/libvterm/parser.c
Normal 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
586
extlib/libvterm/pen.c
Normal 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
56
extlib/libvterm/rect.h
Normal 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
877
extlib/libvterm/screen.c
Normal 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
1801
extlib/libvterm/state.c
Normal file
File diff suppressed because it is too large
Load Diff
229
extlib/libvterm/unicode.c
Normal file
229
extlib/libvterm/unicode.c
Normal 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
39
extlib/libvterm/utf8.h
Normal 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
360
extlib/libvterm/vterm.c
Normal 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
533
extlib/libvterm/vterm.h
Normal 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
|
252
extlib/libvterm/vterm_internal.h
Normal file
252
extlib/libvterm/vterm_internal.h
Normal 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
|
61
extlib/libvterm/vterm_keycodes.h
Normal file
61
extlib/libvterm/vterm_keycodes.h
Normal 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
284
graphics/graphics.c
Normal 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
86
graphics/graphics.h
Normal 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
18
graphics/test_unifont.c
Normal 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
43
graphics/unifont.c
Normal 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
29
graphics/unifont.h
Normal 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);
|
71
graphics/xcursor/xcursor.h
Normal file
71
graphics/xcursor/xcursor.h
Normal 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
194
interrupt/handler.asm.S
Normal 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
52
interrupt/handler.c
Normal 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
38
interrupt/handlers.h
Normal 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
118
interrupt/init.c
Normal 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
74
interrupt/interrupt.h
Normal 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
|
57
interrupt/interrupt_testcode.S
Normal file
57
interrupt/interrupt_testcode.S
Normal 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
45
interrupt/load_gdt.S
Normal 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
25
interrupt/map_handler.S
Normal 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
18
interrupt/syscall.S
Normal 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
8
interrupt/syscall.h
Normal 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
6
interrupt/testcode.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../main.h"
|
||||
|
||||
|
||||
SYSV_ABI void interrupt_Testcode();
|
51
kernel/kmain.c
Normal file
51
kernel/kmain.c
Normal 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
28
kernel/kmain.h
Normal 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
25
kernel/kmain.init.S
Normal 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
11
libc/README
Normal 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
9
libc/abs.c
Normal 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
50
libc/include/assert.h
Normal 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
110
libc/include/ctype.h
Normal 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
202
libc/include/errno.h
Normal 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
153
libc/include/float.h
Normal 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ₘₓlog₁₀b if b is a power of 10,
|
||||
⌈1 + pₘₓlog₁₀b⌉ 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 log₁₀b if b is a power of 10,
|
||||
⌊(p - 1)log₁₀b⌋ 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,
|
||||
⌈log₁₀b^{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
368
libc/include/inttypes.h
Normal 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
31
libc/include/iso646.h
Normal 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
50
libc/include/limits.h
Normal 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
114
libc/include/locale.h
Normal 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
|
904
libc/include/pdclib/_PDCLIB_config.h
Normal file
904
libc/include/pdclib/_PDCLIB_config.h
Normal 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
|
29
libc/include/pdclib/_PDCLIB_defguard.h
Normal file
29
libc/include/pdclib/_PDCLIB_defguard.h
Normal 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
|
93
libc/include/pdclib/_PDCLIB_glue.h
Normal file
93
libc/include/pdclib/_PDCLIB_glue.h
Normal 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
|
776
libc/include/pdclib/_PDCLIB_internal.h
Normal file
776
libc/include/pdclib/_PDCLIB_internal.h
Normal 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
|
32
libc/include/pdclib/_PDCLIB_lib_ext1.h
Normal file
32
libc/include/pdclib/_PDCLIB_lib_ext1.h
Normal 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
|
71
libc/include/pdclib/_PDCLIB_print.h
Normal file
71
libc/include/pdclib/_PDCLIB_print.h
Normal 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
|
153
libc/include/pdclib/_PDCLIB_tzcode.h
Normal file
153
libc/include/pdclib/_PDCLIB_tzcode.h
Normal 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
91
libc/include/signal.h
Normal 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
26
libc/include/stdalign.h
Normal 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
34
libc/include/stdarg.h
Normal 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
24
libc/include/stdbool.h
Normal 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
55
libc/include/stddef.h
Normal 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
236
libc/include/stdint.h
Normal 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
935
libc/include/stdio.h
Normal 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
379
libc/include/stdlib.h
Normal 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
|
26
libc/include/stdnoreturn.h
Normal file
26
libc/include/stdnoreturn.h
Normal 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
394
libc/include/string.h
Normal 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
260
libc/include/threads.h
Normal 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
191
libc/include/time.h
Normal 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
152
libc/include/wctype.h
Normal 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
46
libc/memcmp.c
Normal 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
64
libc/strncat.c
Normal 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
55
libc/strncmp.c
Normal 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
59
libc/strncpy.c
Normal 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
67
main.c
Normal 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
48
main.h
Normal 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
19
memory/Makefile
Normal 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
41
memory/liballoc_impl.c
Normal 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
40
memory/memory.c
Normal 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
100
memory/memory.h
Normal 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
60
memory/memory.hpp
Normal 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
13
memory/memory_cpp.cpp
Normal 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
Loading…
Reference in New Issue
Block a user