Created
May 30, 2019 00:16
-
-
Save lighth7015/ac29a0c652649700f064f5c7d4acd4b7 to your computer and use it in GitHub Desktop.
ELF loader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
//! Byte swap unsigned short | |
uint16_t swap_uint16( uint16_t val ) | |
{ | |
return (val << 8) | (val >> 8 ); | |
} | |
//! Byte swap short | |
int16_t swap_int16( int16_t val ) | |
{ | |
return (val << 8) | ((val >> 8) & 0xFF); | |
} | |
//! Byte swap unsigned int | |
uint32_t swap_uint32( uint32_t val ) | |
{ | |
val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); | |
return (val << 16) | (val >> 16); | |
} | |
//! Byte swap int | |
int32_t swap_int32( int32_t val ) | |
{ | |
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); | |
return (val << 16) | ((val >> 16) & 0xFFFF); | |
} | |
//! Byte swap unsigned long | |
uint64_t swap_uint64( uint64_t val ) | |
{ | |
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); | |
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); | |
return (val << 32) | (val >> 32); | |
} | |
//! Byte swap long | |
int64_t swap_int64( int64_t val ) | |
{ | |
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); | |
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); | |
return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
//! Byte swap unsigned short | |
uint16_t swap_uint16( uint16_t val ) | |
{ | |
return (val << 8) | (val >> 8 ); | |
} | |
//! Byte swap short | |
int16_t swap_int16( int16_t val ) | |
{ | |
return (val << 8) | ((val >> 8) & 0xFF); | |
} | |
//! Byte swap unsigned int | |
uint32_t swap_uint32( uint32_t val ) | |
{ | |
val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); | |
return (val << 16) | (val >> 16); | |
} | |
//! Byte swap int | |
int32_t swap_int32( int32_t val ) | |
{ | |
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); | |
return (val << 16) | ((val >> 16) & 0xFFFF); | |
} | |
//! Byte swap unsigned long | |
uint64_t swap_uint64( uint64_t val ) | |
{ | |
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); | |
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); | |
return (val << 32) | (val >> 32); | |
} | |
//! Byte swap long | |
int64_t swap_int64( int64_t val ) | |
{ | |
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); | |
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); | |
return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "hexdump.h" | |
void hexdump(const void* data, size_t size) | |
{ | |
char ascii[17]; | |
ascii[16] = '\0'; | |
printf("%08x ", 0); | |
for (size_t i = 0, j = 0; i < size; ++i) { | |
printf("%02X ", ((unsigned char*)data)[i]); | |
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { | |
ascii[i % 16] = ((unsigned char*)data)[i]; | |
} else { | |
ascii[i % 16] = '.'; | |
} | |
if ((i+1) % 8 == 0 || i+1 == size) { | |
printf(" "); | |
if ((i+1) % 16 == 0) { | |
printf(" | %-16s |\n%08zx ", ascii, i); | |
} else if (i+1 == size) { | |
ascii[(i+1) % 16] = '\0'; | |
if ((i+1) % 16 <= 8) { | |
printf(" "); | |
} | |
for (j = (i+1) % 16; j < 16; ++j) { | |
printf(" "); | |
} | |
printf(" | %-16s |\n", ascii); | |
} | |
} | |
} | |
puts(""); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "image.h" | |
int RtlImageUnload(ImageHandle handle) | |
{ | |
int status = -1; | |
if (handle.fd == 0) | |
goto finished; | |
if (handle.image == NULL) | |
goto complete; | |
memset(handle.image, 0x0, handle.length); | |
free(handle.image); | |
if(close(handle.fd) < 0) | |
goto trap; | |
handle.image = NULL; | |
handle.fd = 0; | |
status = 0; | |
goto finished; | |
trap: | |
if (errno > 0) | |
{ | |
printf("%s: Error %d: %s\n", __FUNCTION__, errno, strerror(errno)); | |
} | |
finished: | |
if (handle.image != NULL) | |
{ | |
printf("%s: Left function with image address not null: %p\n", | |
__FUNCTION__, handle.image); | |
} | |
complete: | |
printf("Image unloaded\n"); | |
return status; | |
} | |
ImageHandle RtlImageLoad(const char* filename) | |
{ | |
ImageHandle fallback = { 0 }; | |
ImageHandle* handle = | |
calloc(1, sizeof(ImageHandle)); | |
if (handle) { | |
// It's a trap! | |
if ((handle->fd = open(filename, O_RDONLY)) < 0) | |
goto trap; | |
struct stat fileInfo; | |
if (fstat(handle->fd, &fileInfo) < 0) | |
goto trap; | |
handle->length = fileInfo.st_size; | |
handle->self = (intptr_t) handle; | |
handle->image = calloc(1, fileInfo.st_size); | |
read(handle->fd, handle->image, fileInfo.st_size); | |
handle->Ident = (Img_Ident *) handle->image; | |
goto complete; | |
} | |
trap: | |
fallback.fd = 0; | |
if (errno > 0) | |
{ | |
printf("%s: Error %d: %s\n", __FUNCTION__, errno, strerror(errno)); | |
} | |
return fallback; | |
complete: | |
return *handle; | |
} | |
RESULT RtlImageSectionIter(ImageHandle handle, SectionIter EnumerateFn) | |
{ | |
uint64_t SectionNum = 0; | |
RESULT result = 1; | |
if ((result = RtlImageValidate(handle))) | |
{ | |
for (uint64_t i = 0; i < RtlImageGetNumOfSecEntries(handle); i++) | |
{ | |
Img_SecHeader section = RtlImageSectionGet(handle, i); | |
if (EnumerateFn(handle, i, section) == 0) | |
{ | |
goto trap; | |
} | |
} | |
} | |
trap: | |
result = 0; | |
finished: | |
return result; | |
} | |
RESULT RtlImagePrgmHdrIter(ImageHandle handle, ProgHdrIter EnumerateFn) | |
{ | |
uint64_t SectionNum = 0; | |
RESULT result = 1; | |
if ((result = RtlImageValidate(handle))) | |
{ | |
for (uint64_t i = 0; i < RtlImageGetNumOfPrgEntries(handle); i++) | |
{ | |
Img_PrgHeader section = RtlImagePrgmHdrGet(handle, i); | |
if (EnumerateFn(handle, i, section) == 0) | |
{ | |
goto trap; | |
} | |
} | |
} | |
trap: | |
result = 0; | |
finished: | |
return result; | |
} | |
uint64_t | |
RtlImageStringTable(ImageHandle handle) { | |
uint64_t Address = 0; | |
if (RtlImageValidate(handle)) | |
{ | |
Img_SecHeader section = | |
RtlImageSectionGet(handle, RtlImageGetSecHdrStrIndex(handle)); | |
Address = section.SecHdrOffset; | |
} | |
finished: | |
return Address; | |
} | |
// Fetch string, from index to closest NULL byte. | |
const char* | |
RtlImageStringTableRef(ImageHandle handle, Elf64_Word index) | |
{ | |
const uint8_t* Text = NULL; | |
const char* Pointer = NULL; | |
if (RtlImageValidate(handle)) | |
{ | |
uint64_t Address = RtlImageGetBase(handle, IF_IMAGE_DEFAULT), | |
Pos = 0, | |
StrTabIndex = RtlImageGetSecHdrStrIndex(handle); | |
Img_SecHeader section = RtlImageSectionGet(handle, StrTabIndex); | |
Address += section.SecHdrOffset; | |
uint8_t* Table = (uint8_t *) Address; | |
uint8_t* Addr = Table, i = 0; | |
if (*(Table + index) != 0) | |
{ | |
Text = Table + index; | |
} | |
else { | |
Text = (const uint8_t *) "(NULL)"; | |
} | |
goto finished; | |
} | |
trap: | |
Pointer = NULL; | |
finished: | |
Pointer = (const char *) Text; | |
return Pointer; | |
} | |
Img_SecHeader | |
RtlImageSectionGet(ImageHandle handle, uint64_t Index) { | |
Img_SecHeader section = { 0 }; | |
if (RtlImageValidate(handle)) | |
{ | |
Elf64_Word NumOfSecEntries = | |
RtlImageGetNumOfSecEntries(handle); | |
if (Index < NumOfSecEntries) | |
{ | |
uint64_t Address = RtlImageGetBase(handle, IF_IMAGE_SEC_BASE) + ( | |
Index * RtlImageGetSecHdrEntrySize(handle)); | |
switch (RtlImageGetWordSize(handle)) { | |
// | |
// 32-bit. | |
// | |
case FileClass1: { | |
Img_SecHeader32 header = *((Img_SecHeader32*) Address); | |
if (RtlImageGetDataEncoding(handle) == DataEncodingLE) | |
{ | |
section.SecHdrName = SWAP_BYTE_LE(header.SecHdrName, uint, 32); | |
section.SecHdrType = SWAP_BYTE_LE(header.SecHdrType, uint, 32); | |
section.SecHdrAddress = SWAP_BYTE_LE(header.SecHdrAddress, uint, 32); | |
section.SecHdrOffset = SWAP_BYTE_LE(header.SecHdrOffset, uint, 32); | |
section.SecHdrSize = SWAP_BYTE_LE(header.SecHdrSize, uint, 32); | |
section.SecHdrLink = SWAP_BYTE_LE(header.SecHdrLink, uint, 32); | |
section.SecHdrInfo = SWAP_BYTE_LE(header.SecHdrInfo, uint, 32); | |
section.SecHdrAlignment = SWAP_BYTE_LE(header.SecHdrAlignment, uint, 32); | |
section.SecHdrEntrySize = SWAP_BYTE_LE(header.SecHdrEntrySize, uint, 32); | |
} | |
else { | |
section.SecHdrName = SWAP_BYTE_BE(header.SecHdrName, uint, 32); | |
section.SecHdrType = SWAP_BYTE_BE(header.SecHdrType, uint, 32); | |
section.SecHdrAddress = SWAP_BYTE_BE(header.SecHdrAddress, uint, 32); | |
section.SecHdrOffset = SWAP_BYTE_BE(header.SecHdrOffset, uint, 32); | |
section.SecHdrSize = SWAP_BYTE_BE(header.SecHdrSize, uint, 32); | |
section.SecHdrLink = SWAP_BYTE_BE(header.SecHdrLink, uint, 32); | |
section.SecHdrInfo = SWAP_BYTE_BE(header.SecHdrInfo, uint, 32); | |
section.SecHdrAlignment = SWAP_BYTE_BE(header.SecHdrAlignment, uint, 32); | |
section.SecHdrEntrySize = SWAP_BYTE_BE(header.SecHdrEntrySize, uint, 32); | |
}} break; | |
// | |
// 64-bit. | |
// | |
case FileClass2: { | |
Img_SecHeader64 header = *((Img_SecHeader64*) Address); | |
switch(RtlImageGetDataEncoding(handle)) | |
{ | |
case DataEncodingLE: | |
section.SecHdrName = SWAP_BYTE_LE(header.SecHdrName, uint, 32); | |
section.SecHdrType = SWAP_BYTE_LE(header.SecHdrType, uint, 32); | |
section.SecHdrAddress = SWAP_BYTE_LE(header.SecHdrAddress, uint, 64); | |
section.SecHdrOffset = SWAP_BYTE_LE(header.SecHdrOffset, uint, 64); | |
section.SecHdrSize = SWAP_BYTE_LE(header.SecHdrSize, uint, 64); | |
section.SecHdrLink = SWAP_BYTE_LE(header.SecHdrLink, uint, 32); | |
section.SecHdrInfo = SWAP_BYTE_LE(header.SecHdrInfo, uint, 32); | |
section.SecHdrAlignment = SWAP_BYTE_LE(header.SecHdrAlignment, uint, 32); | |
section.SecHdrEntrySize = SWAP_BYTE_LE(header.SecHdrEntrySize, uint, 32); | |
break; | |
case DataEncodingBE: | |
section.SecHdrName = SWAP_BYTE_BE(header.SecHdrName, uint, 32); | |
section.SecHdrType = SWAP_BYTE_BE(header.SecHdrType, uint, 32); | |
section.SecHdrAddress = SWAP_BYTE_BE(header.SecHdrAddress, uint, 64); | |
section.SecHdrOffset = SWAP_BYTE_BE(header.SecHdrOffset, uint, 64); | |
section.SecHdrSize = SWAP_BYTE_BE(header.SecHdrSize, uint, 64); | |
section.SecHdrLink = SWAP_BYTE_BE(header.SecHdrLink, uint, 32); | |
section.SecHdrInfo = SWAP_BYTE_BE(header.SecHdrInfo, uint, 32); | |
section.SecHdrAlignment = SWAP_BYTE_BE(header.SecHdrAlignment, uint, 32); | |
section.SecHdrEntrySize = SWAP_BYTE_BE(header.SecHdrEntrySize, uint, 32); | |
break; | |
}} break; | |
} | |
} | |
} | |
finished: | |
return section; | |
} | |
Img_NoteHeader | |
RtlImageReadNoteSection(ImageHandle handle, Img_PrgHeader Header) { | |
Img_NoteHeader NoteHeader = { 0 }; | |
uint64_t BaseAddr = RtlImageGetBase(handle, 0) | |
+ Header.PrgOffset; | |
if (RtlImageValidate(handle)) | |
{ | |
switch (RtlImageGetWordSize(handle)) { | |
// | |
// 32-bit. | |
// | |
case FileClass1: { | |
Img_NoteHeader32 note = *((Img_NoteHeader32*) BaseAddr); | |
if (RtlImageGetDataEncoding(handle) == DataEncodingLE) | |
{ | |
NoteHeader.NameLength = SWAP_BYTE_LE(note.NameLength, uint, 32); | |
NoteHeader.DescLength = SWAP_BYTE_LE(note.DescLength, uint, 32); | |
NoteHeader.Type = SWAP_BYTE_LE(note.Type, uint, 32); | |
} | |
else { | |
NoteHeader.NameLength = SWAP_BYTE_BE(note.NameLength, uint, 32); | |
NoteHeader.DescLength = SWAP_BYTE_BE(note.DescLength, uint, 32); | |
NoteHeader.Type = SWAP_BYTE_BE(note.Type, uint, 32); | |
} | |
} break; | |
// | |
// 64-bit. | |
// | |
case FileClass2: { | |
Img_NoteHeader64 note = *((Img_NoteHeader64 *) BaseAddr); | |
if (RtlImageGetDataEncoding(handle) == DataEncodingLE) | |
{ | |
NoteHeader.NameLength = SWAP_BYTE_LE(note.NameLength, uint, 32); | |
NoteHeader.DescLength = SWAP_BYTE_LE(note.DescLength, uint, 32); | |
NoteHeader.Type = SWAP_BYTE_LE(note.Type, uint, 32); | |
} | |
else { | |
NoteHeader.NameLength = SWAP_BYTE_BE(note.NameLength, uint, 32); | |
NoteHeader.DescLength = SWAP_BYTE_BE(note.DescLength, uint, 32); | |
NoteHeader.Type = SWAP_BYTE_BE(note.Type, uint, 32); | |
} | |
} break; | |
} | |
NoteHeader.Data = BaseAddr + ((void *) sizeof(struct Img_NoteHeader32)); | |
} | |
finished: | |
return NoteHeader; | |
} | |
Img_PrgHeader | |
RtlImagePrgmHdrGet(ImageHandle handle, uint64_t Index) { | |
Img_PrgHeader ProgramHeader = { 0 }; | |
if (RtlImageValidate(handle)) | |
{ | |
Elf64_Word NumOfSecEntries = | |
RtlImageGetNumOfPrgEntries(handle); | |
if (Index < NumOfSecEntries) | |
{ | |
uint64_t Address = RtlImageGetBase(handle, IF_IMAGE_PRG_BASE) + | |
Index * RtlImageGetPrgHdrEntrySize(handle); | |
switch (RtlImageGetWordSize(handle)) { | |
// | |
// 32-bit. | |
// | |
case FileClass1: { | |
Img_PrgHeader32 header = *((Img_PrgHeader32*) Address); | |
if (RtlImageGetDataEncoding(handle) == DataEncodingLE) | |
{ | |
ProgramHeader.PrgType = SWAP_BYTE_LE(header.PrgType, uint, 32); | |
ProgramHeader.PrgOffset = SWAP_BYTE_LE(header.PrgOffset, uint, 32); | |
ProgramHeader.PrgVirtualAddress = SWAP_BYTE_LE(header.PrgVirtualAddress, uint, 32); | |
ProgramHeader.PrgPhysicalAddress = SWAP_BYTE_LE(header.PrgPhysicalAddress, uint, 32); | |
ProgramHeader.PrgFileSize = SWAP_BYTE_LE(header.PrgFileSize, uint, 32); | |
ProgramHeader.PrgMemorySize = SWAP_BYTE_LE(header.PrgMemorySize, uint, 32); | |
ProgramHeader.PrgFlags = SWAP_BYTE_LE(header.PrgFlags, uint, 32); | |
ProgramHeader.PrgAlignment = SWAP_BYTE_LE(header.PrgAlignment, uint, 32); | |
} | |
else { | |
ProgramHeader.PrgType = SWAP_BYTE_BE(header.PrgType, uint, 32); | |
ProgramHeader.PrgOffset = SWAP_BYTE_BE(header.PrgOffset, uint, 32); | |
ProgramHeader.PrgVirtualAddress = SWAP_BYTE_BE(header.PrgVirtualAddress, uint, 32); | |
ProgramHeader.PrgPhysicalAddress = SWAP_BYTE_BE(header.PrgPhysicalAddress, uint, 32); | |
ProgramHeader.PrgFileSize = SWAP_BYTE_BE(header.PrgFileSize, uint, 32); | |
ProgramHeader.PrgMemorySize = SWAP_BYTE_BE(header.PrgMemorySize, uint, 32); | |
ProgramHeader.PrgFlags = SWAP_BYTE_BE(header.PrgFlags, uint, 32); | |
ProgramHeader.PrgAlignment = SWAP_BYTE_BE(header.PrgAlignment, uint, 32); | |
} | |
//printf("%08x, %08lx\n", header.PrgVirtualAddress, ProgramHeader.PrgVirtualAddress); | |
} break; | |
// | |
// 64-bit. | |
// | |
case FileClass2: { | |
Img_PrgHeader64 header = *((Img_PrgHeader64*) Address); | |
if (RtlImageGetDataEncoding(handle) == DataEncodingLE) | |
{ | |
ProgramHeader.PrgType = SWAP_BYTE_LE(header.PrgType, uint, 32); | |
ProgramHeader.PrgOffset = SWAP_BYTE_LE(header.PrgOffset, uint, 32); | |
ProgramHeader.PrgVirtualAddress = SWAP_BYTE_LE(header.PrgVirtualAddress, uint, 64); | |
ProgramHeader.PrgPhysicalAddress = SWAP_BYTE_LE(header.PrgPhysicalAddress, uint, 64); | |
ProgramHeader.PrgFileSize = SWAP_BYTE_LE(header.PrgFileSize, uint, 64); | |
ProgramHeader.PrgMemorySize = SWAP_BYTE_LE(header.PrgMemorySize, uint, 64); | |
ProgramHeader.PrgFlags = SWAP_BYTE_LE(header.PrgFlags, uint, 32); | |
ProgramHeader.PrgAlignment = SWAP_BYTE_LE(header.PrgAlignment, uint, 64); | |
} | |
else { | |
ProgramHeader.PrgType = SWAP_BYTE_BE(header.PrgType, uint, 32); | |
ProgramHeader.PrgOffset = SWAP_BYTE_BE(header.PrgOffset, uint, 64); | |
ProgramHeader.PrgVirtualAddress = SWAP_BYTE_BE(header.PrgVirtualAddress, uint, 64); | |
ProgramHeader.PrgPhysicalAddress = SWAP_BYTE_BE(header.PrgPhysicalAddress, uint, 64); | |
ProgramHeader.PrgFileSize = SWAP_BYTE_BE(header.PrgFileSize, uint, 64); | |
ProgramHeader.PrgMemorySize = SWAP_BYTE_BE(header.PrgMemorySize, uint, 64); | |
ProgramHeader.PrgFlags = SWAP_BYTE_BE(header.PrgFlags, uint, 32); | |
ProgramHeader.PrgAlignment = SWAP_BYTE_BE(header.PrgAlignment, uint, 64); | |
} | |
} break; | |
} | |
} | |
} | |
finished: | |
return ProgramHeader; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <setjmp.h> | |
#include <unistd.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <sys/mman.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include "endian.h" | |
#include "machine.h" | |
//32-bit types | |
typedef uint8_t Elf32_Byte; // Unsigned char | |
typedef uint16_t Elf32_Half; // Unsigned half int | |
typedef uint32_t Elf32_Offset; // Unsigned offset | |
typedef uint32_t Elf32_Address; // Unsigned address | |
typedef uint32_t Elf32_Word; // Unsigned int | |
typedef int32_t Elf32_Sword; // Signed int | |
typedef uint64_t Elf32_Xword; // Unsigned long | |
typedef int64_t Elf32_Sxword; // Signed long | |
// 64-bit types | |
typedef uint8_t Elf64_Byte; // Unsigned char | |
typedef uint16_t Elf64_Half; // Unsigned half int | |
typedef uint64_t Elf64_Offset; // Unsigned offset | |
typedef uint64_t Elf64_Address; // Unsigned address | |
typedef uint32_t Elf64_Word; // Unsigned int | |
typedef int32_t Elf64_Sword; // Signed int | |
typedef uint64_t Elf64_Xword; // Unsigned long | |
typedef int64_t Elf64_Sxword; // Signed long | |
#define PASTER(prefix,size,suffix) typedef prefix ## size ## _ ## suffix prefix ## _ ## suffix | |
#define EVALUATOR(prefix,size,suffix) PASTER(prefix,size,suffix) | |
#define RTLD_SYMBOL(prefix, symbol) EVALUATOR(prefix, __INTPTR_WIDTH__, symbol) | |
RTLD_SYMBOL(Elf, Byte); | |
RTLD_SYMBOL(Elf, Half); | |
RTLD_SYMBOL(Elf, Address); | |
RTLD_SYMBOL(Elf, Offset); | |
RTLD_SYMBOL(Elf, Word); | |
RTLD_SYMBOL(Elf, Sword); | |
RTLD_SYMBOL(Elf, Xword); | |
RTLD_SYMBOL(Elf, Sxword); | |
#define IDENT_SIZE 16 | |
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | |
#define SWAP_BYTE_LE(T, prefix, size) T | |
#define SWAP_BYTE_BE(T, prefix, size) swap_ ## prefix ## size(T) | |
#else | |
#define SWAP_BYTE_BE(T, prefix, size) T | |
#define SWAP_BYTE_LE(T, prefix, size) swap_ ## prefix ## size(T) | |
#endif | |
#define IMAGE_HEADER_FIELD(field,prefix,size,default) \ | |
static inline prefix ## size ## _t __attribute__((always_inline)) \ | |
RtlImageGet ## field (ImageHandle handle) \ | |
{ \ | |
prefix ## size ## _t result = default; \ | |
\ | |
if (RtlImageGetWordSize(handle) == FileClass1)\ | |
{ \ | |
Img_Header32* header = NULL; \ | |
if ((header = (Img_Header32 *) handle.image) != NULL) \ | |
{ \ | |
prefix ## size ## _t T = header->field; \ | |
\ | |
switch(RtlImageGetDataEncoding(handle)) { \ | |
case 1: result = SWAP_BYTE_LE(T, prefix, size);\ | |
break; \ | |
case 2: result = SWAP_BYTE_BE(T, prefix, size);\ | |
break; \ | |
}}} \ | |
else \ | |
{ \ | |
Img_Header64* header = NULL; \ | |
if ((header = (Img_Header64 *) handle.image) != NULL) \ | |
{ \ | |
prefix ## size ## _t T = header->field; \ | |
\ | |
switch(RtlImageGetDataEncoding(handle)) { \ | |
case 1: result = SWAP_BYTE_LE(T, prefix, size); \ | |
break; \ | |
case 2: result = SWAP_BYTE_BE(T, prefix, size); \ | |
break; \ | |
}}} \ | |
\ | |
return result;\ | |
} | |
#define IMAGE_IDENT_FIELD(field) \ | |
static inline uint8_t __attribute__((always_inline)) \ | |
RtlImageGet ## field (ImageHandle handle) { \ | |
uint8_t result = 0; \ | |
Img_Ident* Ident = NULL; \ | |
\ | |
if ((Ident = (Img_Ident *) handle.Ident) != NULL) \ | |
{ \ | |
result = Ident->field; \ | |
} \ | |
\ | |
return result; \ | |
} | |
#define IMAGE_ADDRESS_FIELD(field) \ | |
static inline uint64_t __attribute__((always_inline)) \ | |
RtlImageGet ## field(ImageHandle handle) \ | |
{ \ | |
uint64_t address = 0;\ | |
\ | |
\ | |
if (RtlImageGetWordSize(handle) == FileClass1)\ | |
{ \ | |
Img_Header32* header = (Img_Header32 *) handle.image;\ | |
\ | |
switch (RtlImageGetDataEncoding(handle))\ | |
{\ | |
case DataEncodingLE:\ | |
address = (uint64_t) SWAP_BYTE_LE(header->field, uint, 32);\ | |
break;\ | |
\ | |
case DataEncodingBE:\ | |
address = (uint64_t) SWAP_BYTE_BE(header->field, uint, 32);\ | |
break;\ | |
}\ | |
} \ | |
else \ | |
{ \ | |
Img_Header64* header = (Img_Header64 *) handle.image;\ | |
\ | |
switch (RtlImageGetDataEncoding(handle))\ | |
{\ | |
case DataEncodingLE:\ | |
address = SWAP_BYTE_LE(header->field, uint, 64);\ | |
break;\ | |
\ | |
case DataEncodingBE:\ | |
address = SWAP_BYTE_BE(header->field, uint, 64);\ | |
break;\ | |
}\ | |
}\ | |
\ | |
return address;\ | |
} | |
#define stack_depth 16 | |
static int utlErr = 0; | |
static int utl_jbn = 0; | |
static jmp_buf utl_jbv[stack_depth]; | |
#define try for ( utlErr = -1 \ | |
; utlErr == -1 && utl_jbn < stack_depth \ | |
; (utl_jbn> 0 ? utl_jbn-- : 0 ) , \ | |
((utlErr > 0)? utlThrow(utlErr) : 0), \ | |
(utlErr = 0)) \ | |
if ((utlErr = setjmp(utl_jbv[utl_jbn++])) == 0 ) | |
#define catch(e) else if ((utlErr == (e)) && ((utlErr = 0) == 0)) | |
#define finally else for ( ;utlErr > 0; utlErr = 0) | |
#define throw(e) (utlErr=e, (utl_jbn > 0 && utlErr? \ | |
longjmp(utl_jbv[utl_jbn-1], utlErr):\ | |
exit(utlErr))) | |
enum { | |
IF_IMAGE_DEFAULT = 0, | |
IF_IMAGE_PRG_BASE = (1 << 1), | |
IF_IMAGE_SEC_BASE = (1 << 2), | |
}; | |
enum FileClasses { | |
FileClass1 = 1, | |
FileClass2 | |
}; | |
enum DataEncodings { | |
DataEncodingLE = 1, | |
DataEncodingBE | |
}; | |
enum FileClassWordSizes { | |
FileClass32 = 32, | |
FileClass64 = 64 | |
}; | |
enum FileTypes { | |
FileTypeNone, | |
FileTypeReloc, | |
FileTypeExec, | |
FileTypeShared, | |
FileTypeCore, | |
FileTypeLoProc = 0xff00, | |
FileTypeHiProc = 0xffff, | |
}; | |
enum SectionIndices { | |
IndexUndefined, | |
IndexLowReserved = 0xff00, | |
IndexLowProc = 0xff00, | |
IndexHiProc = 0xff1f, | |
IndexAbsolute = 0xfff1, | |
IndexCommon = 0xfff2, | |
IndexHiReserve = 0xffff | |
}; | |
enum SecHdrTypes { | |
SecHdrTypeNull, | |
SecHdrTypeProgBits, | |
SecHdrTypeSymbolTable, | |
SecHdrTypeStringTable, | |
SecHdrTypeRela, | |
SecHdrTypeHash, | |
SecHdrTypeDynamic, | |
SecHdrTypeNote, | |
SecHdrTypeNoBits, | |
SecHdrTypeRel, | |
SecHdrTypeShLib, | |
SecHdrTypeDynSym, | |
SecHdrTypeLoProc = 0x70000000, | |
SecHdrTypeHiProc = 0x7fffffff, | |
SecHdrTypeLoUser = 0x80000000, | |
SecHdrTypeHiUser = 0xffffffff | |
}; | |
enum SecHdrFlags { | |
SecHdrFlagWrite = 0x1, | |
SecHdrFlagAlloc = 0x2, | |
SecHdrFlagExec = 0x4, | |
SecHdrFlagReserved = 0xf0000000 | |
}; | |
enum PrgTypes { | |
PrgTypeNull = 0x00000006, | |
PrgTypeLoadable = 0x00000001, | |
PrgTypeDynamic = 0x00000002, | |
PrgTypeInterpreter = 0x00000003, | |
PrgTypeNote = 0x00000004, | |
PrgTypeReserved = 0x00000005, | |
PrgTypePrgHdr = 0x00000007, | |
PrgTypeLoSys = 0x60000000, | |
PrgTypeHiSys = 0x70000000, | |
PrgTypeLoProc = 0x70000000, | |
PrgTypeHiProc = 0x7fffffff, | |
PrgTypeEhFrame = (PrgTypeLoSys + 0x474e550), | |
PrgTypeStack = (PrgTypeLoSys + 0x474e551), | |
PrgTypeRelro = (PrgTypeLoSys + 0x474e552), | |
PrgTypeRsrcHdr = (PrgTypeLoSys + 0x5245431), | |
PrgTypeRsrcEnt = (PrgTypeLoSys + 0x5245432), | |
}; | |
typedef struct Img_Ident { | |
unsigned char Magic0, | |
Magic1, | |
Magic2, | |
Magic3, | |
WordSize, | |
DataEncoding, | |
FormatVersion; | |
unsigned char Padding[9]; | |
} Img_Ident; | |
typedef struct Img_Header { | |
unsigned char Ident[IDENT_SIZE]; | |
Elf_Half Type, | |
MachineType; | |
Elf_Word FileVersion; | |
Elf_Address VirtualAddr; | |
Elf_Offset PrgHeaders, | |
SecHeaders; | |
Elf_Word MachineFlags; | |
Elf_Half ImageHeaderSize, | |
PrgHdrEntrySize, | |
NumOfPrgEntries, | |
SecHdrEntrySize, | |
NumOfSecEntries, | |
SecHdrStrIndex; | |
} Img_Header; | |
typedef struct Img_Header32 { | |
unsigned char Ident[IDENT_SIZE]; | |
Elf32_Half Type, | |
MachineType; | |
Elf32_Word FileVersion; | |
Elf32_Address VirtualAddr; | |
Elf32_Offset PrgHeaders, | |
SecHeaders; | |
Elf32_Word MachineFlags; | |
Elf32_Half ImageHeaderSize, | |
PrgHdrEntrySize, | |
NumOfPrgEntries, | |
SecHdrEntrySize, | |
NumOfSecEntries, | |
SecHdrStrIndex; | |
} Img_Header32; | |
typedef struct Img_Header64 { | |
unsigned char Ident[IDENT_SIZE]; | |
Elf64_Half Type, | |
MachineType; | |
Elf64_Word FileVersion; | |
Elf64_Address VirtualAddr; | |
Elf64_Offset PrgHeaders, | |
SecHeaders; | |
Elf64_Word MachineFlags; | |
Elf64_Half ImageHeaderSize, | |
PrgHdrEntrySize, | |
NumOfPrgEntries, | |
SecHdrEntrySize, | |
NumOfSecEntries, | |
SecHdrStrIndex; | |
} Img_Header64; | |
typedef struct Img_SecHeader { | |
Elf_Word SecHdrName, | |
SecHdrType, | |
SecHdrFlag; | |
Elf_Address SecHdrAddress; | |
Elf_Offset SecHdrOffset; | |
Elf_Xword SecHdrSize; | |
Elf_Word SecHdrLink, | |
SecHdrInfo, | |
SecHdrAlignment, | |
SecHdrEntrySize; | |
} Img_SecHeader; | |
typedef struct Img_SecHeader32 { | |
Elf32_Word SecHdrName, | |
SecHdrType, | |
SecHdrFlag; | |
Elf32_Address SecHdrAddress; | |
Elf32_Offset SecHdrOffset; | |
Elf32_Word SecHdrSize, | |
SecHdrLink, | |
SecHdrInfo, | |
SecHdrAlignment, | |
SecHdrEntrySize; | |
} Img_SecHeader32; | |
typedef struct Img_SecHeader64 { | |
Elf64_Word SecHdrName, | |
SecHdrType, | |
SecHdrFlag; | |
Elf64_Address SecHdrAddress; | |
Elf64_Offset SecHdrOffset; | |
Elf64_Xword SecHdrSize; | |
Elf64_Word SecHdrLink, | |
SecHdrInfo, | |
SecHdrAlignment, | |
SecHdrEntrySize; | |
} Img_SecHeader64; | |
typedef struct Img_PrgHeader { | |
Elf_Word PrgType; | |
Elf_Offset PrgOffset; | |
Elf_Address PrgVirtualAddress, | |
PrgPhysicalAddress; | |
Elf_Word PrgFileSize, | |
PrgMemorySize, | |
PrgFlags, | |
PrgAlignment; | |
} Img_PrgHeader; | |
typedef struct Img_PrgHeader32 { | |
Elf32_Word PrgType; | |
Elf32_Offset PrgOffset; | |
Elf32_Address PrgVirtualAddress, | |
PrgPhysicalAddress; | |
Elf32_Word PrgFileSize, | |
PrgMemorySize, | |
PrgFlags, | |
PrgAlignment; | |
} Img_PrgHeader32; | |
typedef struct Img_PrgHeader64 { | |
Elf64_Word PrgType, | |
PrgFlags; | |
Elf64_Offset PrgOffset; | |
Elf64_Address PrgVirtualAddress, | |
PrgPhysicalAddress; | |
Elf64_Xword PrgFileSize, | |
PrgMemorySize, | |
PrgAlignment; | |
} Img_PrgHeader64; | |
typedef struct Img_NoteHeader32 { | |
Elf32_Word NameLength; | |
Elf32_Word DescLength; | |
Elf32_Word Type; | |
} Img_NoteHeader32; | |
typedef struct Img_NoteHeader64 { | |
Elf32_Word NameLength; | |
Elf32_Word DescLength; | |
Elf32_Word Type; | |
} Img_NoteHeader64; | |
typedef struct { | |
Elf_Word NameLength; | |
Elf_Word DescLength; | |
Elf_Word Type; | |
void* Data; | |
} Img_NoteHeader; | |
typedef struct Img_OSAbiTagHeader { | |
Elf32_Word Type; | |
Elf32_Word MajVersion; | |
Elf32_Word MinVersion; | |
Elf32_Word SubVersion; | |
} Img_OSAbiTagHeader; | |
typedef struct { | |
int fd; | |
uint64_t length; | |
void* image; | |
intptr_t module, | |
self; | |
Img_Ident *Ident; | |
Img_Header Header; | |
} ImageHandle; | |
typedef uint32_t RESULT; | |
typedef RESULT (*SectionIter)(ImageHandle handle, uint32_t i, Img_SecHeader Section); | |
typedef RESULT (*ProgHdrIter)(ImageHandle handle, uint32_t i, Img_PrgHeader Section); | |
ImageHandle RtlImageLoad(const char* filename); | |
uint64_t RtlImageStringTable(ImageHandle handle); | |
const char* RtlImageStringTableRef(ImageHandle handle, Elf64_Word index); | |
int RtlImageUnload(ImageHandle handle); | |
Img_SecHeader RtlImageSectionGet(ImageHandle handle, uint64_t Index); | |
Img_PrgHeader RtlImagePrgmHdrGet(ImageHandle handle, uint64_t Index); | |
Img_NoteHeader RtlImageReadNoteSection(ImageHandle handle, Img_PrgHeader header); | |
RESULT RtlImageBaseAlloc(ImageHandle handle); | |
RESULT RtlImageSectionIter(ImageHandle handle, SectionIter EnumerateFn); | |
RESULT RtlImagePrgmHdrIter(ImageHandle handle, ProgHdrIter EnumerateFn); | |
#include "hexdump.h" | |
#include "imagemacros.h" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "image.h" | |
typedef struct LoaderPrivateData { | |
uint32_t count; | |
intptr_t** mapped; | |
} *LoaderPrivateData; | |
// Initializes the ELF module's prefered base address, | |
RESULT ImageBasePreAllocHeaders (ImageHandle handle, uint32_t i, Img_PrgHeader Header) | |
{ | |
LoaderPrivateData module = (LoaderPrivateData) | |
((ImageHandle*) handle.self)->module; | |
if (Header.PrgType == PrgTypeLoadable) { | |
module->count ++; | |
uint32_t flags = 0; | |
uint32_t state = MAP_PRIVATE | MAP_ANONYMOUS; | |
switch (Header.PrgFlags) { | |
case 1: | |
case 5: flags = PROT_READ | PROT_EXEC; | |
break; | |
case 2: flags = PROT_WRITE; | |
break; | |
case 3: | |
case 7: flags = PROT_READ | PROT_WRITE | PROT_EXEC; | |
break; | |
case 4: flags = PROT_READ; | |
break; | |
case 6: flags = PROT_READ | PROT_WRITE; | |
break; | |
default: | |
break; | |
} | |
// | |
// TODO: Okay, this is what's tripping me up. I have no clue if | |
// code this is properly written, which it probably isn't. | |
// | |
// I'm trying to use an array to keep track of the mapped sections, so I can | |
// (eventually) un-map them when the ELF module is unloaded. | |
// | |
if (!module->mapped) | |
{ | |
// intptr_t** sections = module->mapped = | |
// calloc(1, sizeof(sizeof(intptr_t))); | |
// | |
// sections[0] = mmap( (intptr_t *) RtlImageGetVirtualAddr(handle), | |
// Header.PrgMemorySize, flags, state, 0, 0); | |
} | |
else { | |
// intptr_t** sections = module->mapped = | |
// realloc(module->count, sizeof(sizeof(intptr_t))); | |
// | |
//sections[module->count] = mmap( (intptr_t *) RtlImageGetVirtualAddr(handle), | |
// Header.PrgMemorySize, flags, state, 0, 0); | |
} | |
printf("Virtual Address: %p\n", (void *) (intptr_t *) RtlImageGetVirtualAddr(handle)); | |
} | |
return 1; | |
} | |
RESULT RtlImageBaseAlloc(ImageHandle handle) { | |
if (RtlImageValidate(handle)) { | |
ImageHandle* self = (ImageHandle*) | |
(intptr_t *) handle.self; | |
if (self && self->module == 0) { | |
LoaderPrivateData private = (LoaderPrivateData) | |
calloc(1, sizeof(struct LoaderPrivateData)); | |
self->module = (intptr_t) private; | |
RtlImagePrgmHdrIter(handle, ImageBasePreAllocHeaders); | |
printf( "File has %d text sections.\n", private->count ); | |
} | |
} | |
return RtlImageValidate(handle); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include "image.h" | |
#include "table.h" | |
const char *APPNAME = "imgloader"; | |
const char *VERSION = "0.1"; | |
ft_table_t *phdr_table; | |
RESULT SecHeaderIterator (ImageHandle handle, uint32_t i, Img_SecHeader Section) | |
{ | |
printf("* Section "); | |
printf("%*d [%*d]: ", 2, i, 3, Section.SecHdrName); | |
printf("%s\n", RtlImageStringTableRef(handle, Section.SecHdrName)); | |
return 1; | |
} | |
RESULT PgmHeaderIterator (ImageHandle handle, uint32_t i, Img_PrgHeader Header) | |
{ | |
uint64_t BaseAddr = RtlImageGetBase(handle, 0) | |
+ Header.PrgOffset; | |
const char* type = "UNKNOWN TYPE", | |
* flags = NULL, | |
* misc = NULL, | |
* data = "-", | |
*build = NULL; | |
switch(Header.PrgType) | |
{ | |
case PrgTypeNull: type = "NULL"; break; | |
case PrgTypeLoadable: type = "LOADABLE"; break; | |
case PrgTypeDynamic: type = "DYNAMIC"; break; | |
case PrgTypeInterpreter: | |
type = "PROGRAM INTERPRETER"; | |
data = (char *) BaseAddr; | |
break; | |
case PrgTypeNote: { | |
type = "NOTE"; | |
Img_NoteHeader Note = RtlImageReadNoteSection(handle, Header); | |
if (strcmp(Note.Data, "GNU") == 0) { | |
type = "OS/ABI INFORMATION"; | |
Img_OSAbiTagHeader OSTagInfo = | |
*(((Img_OSAbiTagHeader *) Note.Data) + strlen(Note.Data)); | |
switch(OSTagInfo.Type) { | |
case 0: | |
data = "GNU/LINUX"; | |
break; | |
case 1: | |
data = "GNU"; | |
break; | |
default: | |
data = "Unknown"; | |
break; | |
} | |
} | |
else { | |
// ... | |
} | |
hexdump((const char *)BaseAddr, Header.PrgMemorySize); | |
} break; | |
case PrgTypeReserved: type = "RESERVED"; break; | |
case PrgTypePrgHdr: type = "PROGRAM HEADER"; break; | |
case PrgTypeEhFrame: type = "GNU_EH_FRAME"; break; | |
case PrgTypeStack: type = "GNU_STACK"; break; | |
case PrgTypeRelro: type = "GNU_RELRO"; break; | |
default: type = "UNKNOWN"; break; | |
} | |
switch (Header.PrgFlags) { | |
case 1: | |
flags = "--X"; | |
break; | |
case 2: | |
flags = "-W-"; | |
break; | |
case 3: | |
flags = "-WX"; | |
break; | |
case 4: | |
flags = "R--"; | |
break; | |
case 5: | |
flags = "R-X"; | |
break; | |
case 6: | |
flags = "RW-"; | |
break; | |
case 7: | |
flags = "RWX"; | |
break; | |
default: | |
case 0: | |
flags = "---"; | |
break; | |
} | |
ft_printf_ln( phdr_table, "%#06lx|%#05x\n%#05x|%#08lx\n%#08lx|%-#8x\n%s|%s\n%s", | |
Header.PrgOffset, | |
Header.PrgFileSize, | |
Header.PrgMemorySize, | |
Header.PrgVirtualAddress, | |
Header.PrgPhysicalAddress, | |
Header.PrgAlignment, | |
flags, | |
type, | |
data); | |
return 1; | |
} | |
int main(int args, char* arg[]) | |
{ | |
int rc = 0; | |
printf("%s version %s\n", APPNAME, VERSION); | |
phdr_table = ft_create_table(); | |
ft_set_border_style(phdr_table, TB_DOUBLE2_STYLE); | |
ft_set_cell_prop(phdr_table, 0, TB_ANY_COLUMN, TB_CPROP_ROW_TYPE, TB_ROW_HEADER); | |
ft_set_cell_prop(phdr_table, TB_ANY_ROW, TB_ANY_COLUMN, TB_CPROP_LETB_PADDING, 1); | |
ft_set_cell_prop(phdr_table, TB_ANY_ROW, TB_ANY_COLUMN, TB_CPROP_EMPTY_STR_HEIGHT, 1); | |
ft_set_cell_prop(phdr_table, TB_ANY_ROW, 0, TB_CPROP_MIN_WIDTH, 8); | |
ft_set_cell_prop(phdr_table, TB_ANY_ROW, 1, TB_CPROP_MIN_WIDTH, 7); | |
ft_set_cell_prop(phdr_table, TB_ANY_ROW, 2, TB_CPROP_MIN_WIDTH, 10); | |
ft_set_cell_prop(phdr_table, TB_ANY_ROW, 3, TB_CPROP_MIN_WIDTH, 12); | |
ft_set_cell_prop(phdr_table, TB_ANY_ROW, 4, TB_CPROP_MIN_WIDTH, 29); | |
ft_write_ln(phdr_table, "Offset", "Disk size\nMem. size", "Virt. Addr\nPhys. Addr", "Alignment\nFlags", "Type\nMisc."); | |
if (args < 2) { | |
printf("usage: %s [path to image]\n", arg[0]); | |
rc = 1; | |
goto finished; | |
} | |
printf("Loading image %s\n", arg[1]); | |
ImageHandle handle = RtlImageLoad(arg[1]); | |
printf("Loaded image in file descriptor %d, address %p\n", handle.fd, handle.image); | |
if (RtlImageValidate(handle) < 0) { | |
printf("* Not a valid run-time image.\n"); | |
} | |
else { | |
printf("* Image appears to be ELF format.\n"); | |
const char* endianness = NULL; | |
if (RtlImageGetDataEncodingStr(handle, &endianness) < 0) { | |
printf("! Image has invalid or unsupported data encoding\n"); | |
goto unload; | |
} | |
printf("* Image is %s-endian\n", endianness); | |
printf("* Image word size is %d-bit\n", (RtlImageGetWordSize(handle) == FileClass1) ? FileClass32 : FileClass64); | |
printf("* Image format version %d\n", RtlImageGetFormatVersion(handle)); | |
printf("* Machine is type %d (%s)\n", RtlImageGetMachineType(handle), RtlImageGetMachineString(handle)); | |
printf("* Image file type %d (%s)\n", RtlImageGetType(handle), RtlImageGetKind(handle)); | |
printf("* Image file version %d\n", RtlImageGetFileVersion(handle)); | |
printf("* Image loads at virtual address %-16p\n", (uint64_t *) RtlImageGetVirtualAddr(handle)); | |
printf("* Machine flags are 0x%08x\n", RtlImageGetMachineFlags(handle)); | |
printf("* Image header size is 0x%04x\n", RtlImageGetImageHeaderSize(handle)); | |
printf("* This image contains %02hu program header entries, each header is %02x bytes\n", RtlImageGetNumOfPrgEntries(handle), RtlImageGetPrgHdrEntrySize(handle)); | |
printf("* Image program headers begin at %lu\n", RtlImageGetPrgHeaders(handle)); | |
printf("* This image contains %02hu section header entries, each header is %02x bytes\n", RtlImageGetNumOfSecEntries(handle), RtlImageGetSecHdrEntrySize(handle)); | |
printf("* Image section headers begin at %lu\n", RtlImageGetSecHeaders(handle)); | |
printf("* Section header string table address is (0x%lx + 0x%lx)\n", RtlImageGetBase(handle, IF_IMAGE_DEFAULT), RtlImageStringTable(handle)); | |
printf("\n"); | |
printf( "%s:\n", RtlImageStringTableRef(handle, 1)); | |
RtlImageSectionIter(handle, SecHeaderIterator); | |
printf("\n"); | |
RtlImagePrgmHdrIter(handle, PgmHeaderIterator); | |
printf("%s\n", ft_to_string(phdr_table)); | |
ft_destroy_table(phdr_table); | |
fflush(stdout); | |
RtlImageBaseAlloc(handle); | |
RtlImageBaseAlloc(handle); | |
} | |
unload: | |
printf("Unloading image\n"); | |
RtlImageUnload(handle); | |
finished: | |
return rc; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
libfort | |
MIT License | |
Copyright (c) 2017 - 2018 Seleznev Anton | |
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. | |
*/ | |
/* The file was GENERATED by an amalgamation script.*/ | |
/* DO NOT EDIT BY HAND!!! */ | |
#define TB_AMALGAMED_SOURCE /* Macros to make internal libfort functions static */ | |
/******************************************************** | |
Begin of file "table_utils.h" | |
********************************************************/ | |
#ifndef TABLE_IMPL_H | |
#define TABLE_IMPL_H | |
#if defined(_MSC_VER) | |
#define _CRT_SECURE_NO_WARNINGS /* To disable warnings for unsafe functions */ | |
#endif | |
#include <stddef.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <assert.h> | |
#include <stdio.h> | |
#include "table.h" | |
/* Define TB_INTERNAL to make internal libfort functions static | |
* in the result amalgamed source file. | |
*/ | |
#ifdef TB_AMALGAMED_SOURCE | |
#define TB_INTERNAL static | |
#else | |
#define TB_INTERNAL | |
#endif /* TB_AMALGAMED_SORCE */ | |
#define TABLE_COL_SEPARATOR '|' | |
#define TABLE_COL_SEPARATOR_LENGTH 1 | |
#define TABLE_UNUSED __attribute__((unused)) | |
#define F_MALLOC table_malloc | |
#define F_FREE table_free | |
#define F_CALLOC table_calloc | |
#define F_REALLOC table_realloc | |
#define F_STRDUP table_strdup | |
#define F_WCSDUP table_wcsdup | |
#define F_CREATE(type) ((type *)F_CALLOC(sizeof(type), 1)) | |
#define MAX(a,b) ((a) > (b) ? (a) : b) | |
#define MIN(a,b) ((a) < (b) ? (a) : b) | |
enum PolicyOnNull { | |
Create, | |
DoNotCreate | |
}; | |
enum F_BOOL { | |
F_FALSE = 0, | |
F_TRUE = 1 | |
}; | |
#define TB_STR_2_CAT_(arg1, arg2) \ | |
arg1##arg2 | |
#define TB_STR_2_CAT(arg1, arg2) \ | |
TB_STR_2_CAT_(arg1, arg2) | |
#define UNIQUE_NAME_(prefix) \ | |
TB_STR_2_CAT(prefix,__COUNTER__) | |
#define UNIQUE_NAME(prefix) \ | |
UNIQUE_NAME_(prefix) | |
/***************************************************************************** | |
* LOGGER | |
*****************************************************************************/ | |
#define SYS_LOG_ERROR(...) | |
/***************************************************************************** | |
* DEFAULT_SIZES | |
* ***************************************************************************/ | |
#define DEFAULT_STR_BUF_SIZE 1024 | |
#define DEFAULT_VECTOR_CAPACITY 10 | |
struct table_table_properties; | |
struct table_column_properties; | |
struct table_row; | |
struct vector; | |
struct table_cell; | |
struct string_buffer; | |
struct separator { | |
int enabled; | |
}; | |
typedef struct table_table_properties table_table_properties_t; | |
struct table_context { | |
table_table_properties_t *table_properties; | |
size_t row; | |
size_t column; | |
}; | |
typedef struct table_context context_t; | |
typedef struct table_column_properties table_column_properties_t; | |
typedef struct vector vector_t; | |
typedef struct table_cell table_cell_t; | |
typedef struct string_buffer string_buffer_t; | |
typedef struct table_row table_row_t; | |
/*typedef struct ft_table ft_table_t;*/ | |
typedef struct separator separator_t; | |
enum CellType { | |
CommonCell, | |
GroupMasterCell, | |
GroupSlaveCell | |
}; | |
enum request_geom_type { | |
VISIBLE_GEOMETRY, | |
INTERN_REPR_GEOMETRY | |
}; | |
/***************************************************************************** | |
* LIBFORT helpers | |
*****************************************************************************/ | |
extern void *(*table_malloc)(size_t size); | |
extern void (*table_free)(void *ptr); | |
extern void *(*table_calloc)(size_t nmemb, size_t size); | |
extern void *(*table_realloc)(void *ptr, size_t size); | |
void set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr)); | |
char *table_strdup(const char *str); | |
size_t number_of_columns_in_format_string(const char *fmt); | |
#if defined(TB_HAVE_WCHAR) | |
wchar_t *table_wcsdup(const wchar_t *str); | |
size_t number_of_columns_in_format_wstring(const wchar_t *fmt); | |
#endif | |
/*int snprint_n_chars(char *buf, size_t length, size_t n, char ch);*/ | |
/*int wsnprint_n_chars(wchar_t *buf, size_t length, size_t n, wchar_t ch);*/ | |
int snprint_n_strings(char *buf, size_t length, size_t n, const char *str); | |
#if defined(TB_HAVE_WCHAR) | |
int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str); | |
#endif | |
#define CHCK_RSLT_ADD_TO_WRITTEN(statement) \ | |
do { \ | |
tmp = statement; \ | |
if (tmp < 0) {\ | |
goto clear; \ | |
} \ | |
written += tmp; \ | |
} while(0) | |
#define CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(statement) \ | |
do { \ | |
tmp = statement; \ | |
if (tmp < 0) {\ | |
goto clear; \ | |
} \ | |
invisible_written += tmp; \ | |
} while(0) | |
#define CHECK_NOT_NEGATIVE(x) \ | |
do { if (x < 0) goto table_fail; } while (0) | |
#endif /* TABLE_IMPL_H */ | |
/******************************************************** | |
End of file "table_utils.h" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "vector.h" | |
********************************************************/ | |
#ifndef VECTOR_H | |
#define VECTOR_H | |
/* #include "table_utils.h" */ /* Commented by amalgamation script */ | |
#define INVALID_VEC_INDEX ((size_t) -1) | |
TB_INTERNAL | |
vector_t *create_vector(size_t item_size, size_t capacity); | |
TB_INTERNAL | |
void destroy_vector(vector_t *); | |
TB_INTERNAL | |
size_t vector_size(const vector_t *); | |
TB_INTERNAL | |
size_t vector_capacity(const vector_t *); | |
TB_INTERNAL | |
int vector_push(vector_t *, const void *item); | |
TB_INTERNAL | |
const void *vector_at_c(const vector_t *vector, size_t index); | |
TB_INTERNAL | |
void *vector_at(vector_t *, size_t index); | |
TB_INTERNAL | |
table_status_t vector_swap(vector_t *cur_vec, vector_t *mv_vec, size_t pos); | |
#define FOR_EACH_(type, item, vector, index_name) \ | |
size_t index_name = 0; \ | |
for (index_name = 0; (index_name < vector_size(vector)) ? ((item = *(type*)vector_at(vector, index_name)), 1) : 0; ++index_name) | |
#define FOR_EACH(type, item, vector) \ | |
FOR_EACH_(type, item, vector, UNIQUE_NAME(i)) | |
#ifdef TB_TEST_BUILD | |
vector_t *copy_vector(vector_t *); | |
size_t vector_index_of(const vector_t *, const void *item); | |
int vector_erase(vector_t *, size_t index); | |
void vector_clear(vector_t *); | |
#endif | |
#endif /* VECTOR_H */ | |
/******************************************************** | |
End of file "vector.h" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "wcwidth.h" | |
********************************************************/ | |
#ifndef WCWIDTH_H | |
#define WCWIDTH_H | |
/* #include "table_utils.h" */ /* Commented by amalgamation script */ | |
#ifdef TB_HAVE_WCHAR | |
#include <wchar.h> | |
TB_INTERNAL | |
int mk_wcswidth(const wchar_t *pwcs, size_t n); | |
#endif /* TB_HAVE_WCHAR */ | |
#endif /* WCWIDTH_H */ | |
/******************************************************** | |
End of file "wcwidth.h" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "string_buffer.h" | |
********************************************************/ | |
#ifndef STRING_BUFFER_H | |
#define STRING_BUFFER_H | |
/* #include "table_utils.h" */ /* Commented by amalgamation script */ | |
/***************************************************************************** | |
* STRING BUFFER | |
* ***************************************************************************/ | |
enum str_buf_type { | |
CharBuf, | |
#ifdef TB_HAVE_WCHAR | |
WCharBuf | |
#endif /* TB_HAVE_WCHAR */ | |
}; | |
struct string_buffer { | |
union { | |
char *cstr; | |
wchar_t *wstr; | |
void *data; | |
} str; | |
size_t data_sz; | |
enum str_buf_type type; | |
}; | |
TB_INTERNAL | |
string_buffer_t *create_string_buffer(size_t number_of_chars, enum str_buf_type type); | |
TB_INTERNAL | |
void destroy_string_buffer(string_buffer_t *buffer); | |
TB_INTERNAL | |
string_buffer_t *copy_string_buffer(string_buffer_t *buffer); | |
TB_INTERNAL | |
table_status_t realloc_string_buffer_without_copy(string_buffer_t *buffer); | |
TB_INTERNAL | |
table_status_t fill_buffer_from_string(string_buffer_t *buffer, const char *str); | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
table_status_t fill_buffer_from_wstring(string_buffer_t *buffer, const wchar_t *str); | |
#endif /* TB_HAVE_WCHAR */ | |
TB_INTERNAL | |
size_t buffer_text_height(string_buffer_t *buffer); | |
TB_INTERNAL | |
size_t string_buffer_capacity(const string_buffer_t *buffer); | |
TB_INTERNAL | |
void *buffer_get_data(string_buffer_t *buffer); | |
TB_INTERNAL | |
size_t buffer_text_width(string_buffer_t *buffer); | |
TB_INTERNAL | |
int buffer_printf(string_buffer_t *buffer, size_t buffer_row, char *buf, size_t total_buf_len, | |
const context_t *context, const char *content_style_tag, const char *reset_content_style_tag); | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, size_t total_buf_len, | |
const context_t *context, const char *content_style_tag, const char *reset_content_style_tag); | |
#endif /* TB_HAVE_WCHAR */ | |
#endif /* STRING_BUFFER_H */ | |
/******************************************************** | |
End of file "string_buffer.h" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "properties.h" | |
********************************************************/ | |
#ifndef PROPERTIES_H | |
#define PROPERTIES_H | |
/* #include "table_utils.h" */ /* Commented by amalgamation script */ | |
#include <stdint.h> | |
#include <limits.h> | |
#define PROP_IS_SET(ft_props, property) ((ft_props) & (property)) | |
#define PROP_SET(ft_props, property) ((ft_props) |=(property)) | |
#define PROP_UNSET(ft_props, property) ((ft_props) &= ~((uint32_t)property)) | |
#define TEXT_STYLE_TAG_MAX_SIZE 64 | |
TB_INTERNAL | |
void get_style_tag_for_cell(const table_table_properties_t *props, | |
size_t row, size_t col, char *style_tag, size_t sz); | |
TB_INTERNAL | |
void get_reset_style_tag_for_cell(const table_table_properties_t *props, | |
size_t row, size_t col, char *style_tag, size_t sz); | |
TB_INTERNAL | |
void get_style_tag_for_content(const table_table_properties_t *props, | |
size_t row, size_t col, char *style_tag, size_t sz); | |
TB_INTERNAL | |
void get_reset_style_tag_for_content(const table_table_properties_t *props, | |
size_t row, size_t col, char *style_tag, size_t sz); | |
struct table_cell_props { | |
size_t cell_row; | |
size_t cell_col; | |
uint32_t properties; | |
unsigned int col_min_width; | |
enum ft_text_alignment align; | |
unsigned int cell_padding_top; | |
unsigned int cell_padding_bottom; | |
unsigned int cell_padding_left; | |
unsigned int cell_padding_right; | |
unsigned int cell_empty_string_height; | |
enum ft_row_type row_type; | |
unsigned int content_fg_color_number; | |
unsigned int content_bg_color_number; | |
unsigned int cell_bg_color_number; | |
enum ft_text_style cell_text_style; | |
enum ft_text_style content_text_style; | |
}; | |
typedef struct table_cell_props table_cell_props_t; | |
typedef vector_t table_cell_prop_container_t; | |
TB_INTERNAL | |
table_cell_prop_container_t *create_cell_prop_container(void); | |
TB_INTERNAL | |
void destroy_cell_prop_container(table_cell_prop_container_t *cont); | |
TB_INTERNAL | |
const table_cell_props_t *cget_cell_prop(const table_cell_prop_container_t *cont, size_t row, size_t col); | |
TB_INTERNAL | |
table_cell_props_t *get_cell_prop_and_create_if_not_exists(table_cell_prop_container_t *cont, size_t row, size_t col); | |
TB_INTERNAL | |
table_status_t set_cell_property(table_cell_prop_container_t *cont, size_t row, size_t col, uint32_t property, int value); | |
TB_INTERNAL | |
int get_cell_property_value_hierarcial(const table_table_properties_t *properties, size_t row, size_t column, uint32_t property); | |
TB_INTERNAL | |
table_status_t set_default_cell_property(uint32_t property, int value); | |
/* TABLE BORDER DESRIPTION | |
* | |
* | |
* TL TT TT TT TV TT TT TT TT TT TT TT TR | |
* LL IV RR | |
* LL IV RR | |
* LH IH IH IH II IH IH IH TI IH IH IH RH | |
* LL IV IV RR | |
* LL IV IV RR | |
* LL LI IH IH IH RI RH | |
* LL IV IV RR | |
* LL IV IV RR | |
* LH IH IH IH BI IH IH IH II IH IH IH RH | |
* LL IV RR | |
* LL IV RR | |
* BL BB BB BB BV BB BB BB BV BB BB BB BR | |
*/ | |
/* HORIZONTAL SEPARATOR DESCRIPTION | |
* | |
* | |
* TL TT TT TT TV TT TT TT TV TT TT TT TR <----- TopSeparator | |
* LL IV IV RR | |
* LH IH IH IH II IH IH IH II IH IH IH RH <----- InsideSeparator | |
* LL IV IV RR | |
* BL BB BB BB BV BB BB BB BV BB BB BB BR <----- BottomSeparator | |
*/ | |
enum HorSeparatorPos { | |
TopSeparator, | |
InsideSeparator, | |
BottomSeparator | |
}; | |
enum BorderItemPos { | |
TL_bip = 0, | |
TT_bip = 1, | |
TV_bip = 2, | |
TR_bip = 3, | |
LL_bip = 4, | |
IV_bip = 5, | |
RR_bip = 6, | |
LH_bip = 7, | |
IH_bip = 8, | |
II_bip = 9, | |
RH_bip = 10, | |
BL_bip = 11, | |
BB_bip = 12, | |
BV_bip = 13, | |
BR_bip = 14, | |
LI_bip = 15, | |
TI_bip = 16, | |
RI_bip = 17, | |
BI_bip = 18, | |
BorderItemPosSize | |
}; | |
enum SeparatorItemPos { | |
LH_sip = 0, | |
IH_sip = 1, | |
II_sip = 2, | |
RH_sip = 3, | |
TI_sip = 4, | |
BI_sip = 5, | |
SepratorItemPosSize | |
}; | |
struct table_border_style { | |
const char *border_chars[BorderItemPosSize]; | |
const char *header_border_chars[BorderItemPosSize]; | |
const char *separator_chars[SepratorItemPosSize]; | |
}; | |
extern struct table_border_style TABLE_BASIC_STYLE; | |
extern struct table_border_style TABLE_BASIC2_STYLE; | |
extern struct table_border_style TABLE_SIMPLE_STYLE; | |
extern struct table_border_style TABLE_PLAIN_STYLE; | |
extern struct table_border_style TABLE_DOT_STYLE; | |
extern struct table_border_style TABLE_EMPTY_STYLE; | |
extern struct table_border_style TABLE_SOLID_STYLE; | |
extern struct table_border_style TABLE_SOLID_ROUND_STYLE; | |
extern struct table_border_style TABLE_NICE_STYLE; | |
extern struct table_border_style TABLE_DOUBLE_STYLE; | |
extern struct table_border_style TABLE_DOUBLE2_STYLE; | |
extern struct table_border_style TABLE_BOLD_STYLE; | |
extern struct table_border_style TABLE_BOLD2_STYLE; | |
extern struct table_border_style TABLE_FRAME_STYLE; | |
struct table_entire_table_properties { | |
unsigned int left_margin; | |
unsigned int top_margin; | |
unsigned int right_margin; | |
unsigned int bottom_margin; | |
}; | |
typedef struct table_entire_table_properties table_entire_table_properties_t; | |
extern table_entire_table_properties_t g_entire_table_properties; | |
TB_INTERNAL | |
table_status_t set_entire_table_property(table_table_properties_t *table_properties, uint32_t property, int value); | |
TB_INTERNAL | |
table_status_t set_default_entire_table_property(uint32_t property, int value); | |
struct table_table_properties { | |
struct table_border_style border_style; | |
table_cell_prop_container_t *cell_properties; | |
table_entire_table_properties_t entire_table_properties; | |
}; | |
extern table_table_properties_t g_table_properties; | |
TB_INTERNAL | |
size_t max_border_elem_strlen(struct table_table_properties *); | |
TB_INTERNAL | |
table_table_properties_t *create_table_properties(void); | |
TB_INTERNAL | |
void destroy_table_properties(table_table_properties_t *properties); | |
TB_INTERNAL | |
table_table_properties_t *copy_table_properties(const table_table_properties_t *property); | |
#endif /* PROPERTIES_H */ | |
/******************************************************** | |
End of file "properties.h" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "cell.h" | |
********************************************************/ | |
#ifndef CELL_H | |
#define CELL_H | |
/* #include "table_utils.h" */ /* Commented by amalgamation script */ | |
TB_INTERNAL | |
table_cell_t *create_cell(void); | |
TB_INTERNAL | |
void destroy_cell(table_cell_t *cell); | |
TB_INTERNAL | |
table_cell_t *copy_cell(table_cell_t *cell); | |
TB_INTERNAL | |
size_t hint_width_cell(const table_cell_t *cell, const context_t *context, enum request_geom_type geom); | |
TB_INTERNAL | |
size_t hint_height_cell(const table_cell_t *cell, const context_t *context); | |
TB_INTERNAL | |
void set_cell_type(table_cell_t *cell, enum CellType type); | |
TB_INTERNAL | |
enum CellType get_cell_type(const table_cell_t *cell); | |
TB_INTERNAL | |
int cell_printf(table_cell_t *cell, size_t row, char *buf, size_t buf_len, const context_t *context); | |
TB_INTERNAL | |
table_status_t fill_cell_from_string(table_cell_t *cell, const char *str); | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
int cell_wprintf(table_cell_t *cell, size_t row, wchar_t *buf, size_t buf_len, const context_t *context); | |
TB_INTERNAL | |
table_status_t fill_cell_from_wstring(table_cell_t *cell, const wchar_t *str); | |
#endif | |
TB_INTERNAL | |
string_buffer_t *cell_get_string_buffer(table_cell_t *cell); | |
#endif /* CELL_H */ | |
/******************************************************** | |
End of file "cell.h" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "row.h" | |
********************************************************/ | |
#ifndef ROW_H | |
#define ROW_H | |
/* #include "table_utils.h" */ /* Commented by amalgamation script */ | |
#include "table.h" | |
#include <stdarg.h> | |
/* #include "properties.h" */ /* Commented by amalgamation script */ | |
#ifdef TB_HAVE_WCHAR | |
#include <wchar.h> | |
#endif | |
TB_INTERNAL | |
table_row_t *create_row(void); | |
TB_INTERNAL | |
void destroy_row(table_row_t *row); | |
TB_INTERNAL | |
table_row_t *copy_row(table_row_t *row); | |
TB_INTERNAL | |
table_row_t *create_row_from_string(const char *str); | |
TB_PRINTF_ATTRIBUTE_FORMAT(1, 0) | |
TB_INTERNAL | |
table_row_t *create_row_from_fmt_string(const char *fmt, va_list *va_args); | |
TB_INTERNAL | |
size_t columns_in_row(const table_row_t *row); | |
TB_INTERNAL | |
table_cell_t *get_cell(table_row_t *row, size_t col); | |
TB_INTERNAL | |
const table_cell_t *get_cell_c(const table_row_t *row, size_t col); | |
TB_INTERNAL | |
table_cell_t *get_cell_and_create_if_not_exists(table_row_t *row, size_t col); | |
TB_INTERNAL | |
table_status_t swap_row(table_row_t *cur_row, table_row_t *ins_row, size_t pos); | |
TB_INTERNAL | |
size_t group_cell_number(const table_row_t *row, size_t master_cell_col); | |
TB_INTERNAL | |
int get_row_cell_types(const table_row_t *row, enum CellType *types, size_t types_sz); | |
TB_INTERNAL | |
table_status_t row_set_cell_span(table_row_t *row, size_t cell_column, size_t hor_span); | |
TB_INTERNAL | |
int print_row_separator(char *buffer, size_t buffer_sz, | |
const size_t *col_width_arr, size_t cols, | |
const table_row_t *upper_row, const table_row_t *lower_row, | |
enum HorSeparatorPos separatorPos, const separator_t *sep, | |
const context_t *context); | |
TB_INTERNAL | |
int snprintf_row(const table_row_t *row, char *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, | |
size_t row_height, const context_t *context); | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
table_row_t *create_row_from_wstring(const wchar_t *str); | |
TB_INTERNAL | |
table_row_t *create_row_from_fmt_wstring(const wchar_t *fmt, va_list *va_args); | |
TB_INTERNAL | |
int wprint_row_separator(wchar_t *buffer, size_t buffer_sz, | |
const size_t *col_width_arr, size_t cols, | |
const table_row_t *upper_row, const table_row_t *lower_row, | |
enum HorSeparatorPos separatorPos, const separator_t *sep, | |
const context_t *context); | |
TB_INTERNAL | |
int wsnprintf_row(const table_row_t *row, wchar_t *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, | |
size_t row_height, const context_t *context); | |
#endif | |
#endif /* ROW_H */ | |
/******************************************************** | |
End of file "row.h" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "table.h" | |
********************************************************/ | |
#ifndef TABLE_H | |
#define TABLE_H | |
/* #include "table_utils.h" */ /* Commented by amalgamation script */ | |
struct ft_table { | |
vector_t *rows; | |
table_table_properties_t *properties; | |
string_buffer_t *conv_buffer; | |
size_t cur_row; | |
size_t cur_col; | |
vector_t *separators; | |
}; | |
TB_INTERNAL | |
separator_t *create_separator(int enabled); | |
TB_INTERNAL | |
void destroy_separator(separator_t *sep); | |
TB_INTERNAL | |
separator_t *copy_separator(separator_t *sep); | |
TB_INTERNAL | |
table_status_t get_table_sizes(const ft_table_t *table, size_t *rows, size_t *cols); | |
TB_INTERNAL | |
table_row_t *get_row(ft_table_t *table, size_t row); | |
TB_INTERNAL | |
const table_row_t *get_row_c(const ft_table_t *table, size_t row); | |
TB_INTERNAL | |
table_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row); | |
TB_INTERNAL | |
string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table); | |
TB_INTERNAL | |
table_status_t table_rows_and_cols_geometry(const ft_table_t *table, | |
size_t **col_width_arr_p, size_t *col_width_arr_sz, | |
size_t **row_height_arr_p, size_t *row_height_arr_sz, | |
enum request_geom_type geom); | |
TB_INTERNAL | |
table_status_t table_geometry(const ft_table_t *table, size_t *height, size_t *width); | |
#endif /* TABLE_H */ | |
/******************************************************** | |
End of file "table.h" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "cell.c" | |
********************************************************/ | |
/* #include "cell.h" */ /* Commented by amalgamation script */ | |
/* #include "properties.h" */ /* Commented by amalgamation script */ | |
/* #include "string_buffer.h" */ /* Commented by amalgamation script */ | |
#include <assert.h> | |
struct table_cell { | |
string_buffer_t *str_buffer; | |
enum CellType cell_type; | |
}; | |
TB_INTERNAL | |
table_cell_t *create_cell(void) | |
{ | |
table_cell_t *cell = (table_cell_t *)F_CALLOC(sizeof(table_cell_t), 1); | |
if (cell == NULL) | |
return NULL; | |
cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CharBuf); | |
if (cell->str_buffer == NULL) { | |
F_FREE(cell); | |
return NULL; | |
} | |
cell->cell_type = CommonCell; | |
return cell; | |
} | |
TB_INTERNAL | |
void destroy_cell(table_cell_t *cell) | |
{ | |
if (cell == NULL) | |
return; | |
destroy_string_buffer(cell->str_buffer); | |
F_FREE(cell); | |
} | |
TB_INTERNAL | |
table_cell_t *copy_cell(table_cell_t *cell) | |
{ | |
assert(cell); | |
table_cell_t *result = create_cell(); | |
destroy_string_buffer(result->str_buffer); | |
result->str_buffer = copy_string_buffer(cell->str_buffer); | |
if (result->str_buffer == NULL) { | |
destroy_cell(result); | |
return NULL; | |
} | |
result->cell_type = cell->cell_type; | |
return result; | |
} | |
TB_INTERNAL | |
void set_cell_type(table_cell_t *cell, enum CellType type) | |
{ | |
assert(cell); | |
cell->cell_type = type; | |
} | |
TB_INTERNAL | |
enum CellType get_cell_type(const table_cell_t *cell) | |
{ | |
assert(cell); | |
return cell->cell_type; | |
} | |
TB_INTERNAL | |
size_t hint_width_cell(const table_cell_t *cell, const context_t *context, enum request_geom_type geom) | |
{ | |
/* todo: | |
* At the moment min width includes paddings. Maybe it is better that min width weren't include | |
* paddings but be min width of the cell content without padding | |
*/ | |
assert(cell); | |
assert(context); | |
size_t cell_padding_left = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_LETB_PADDING); | |
size_t cell_padding_right = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_RIGHT_PADDING); | |
size_t result = cell_padding_left + cell_padding_right; | |
if (cell->str_buffer && cell->str_buffer->str.data) { | |
result += buffer_text_width(cell->str_buffer); | |
} | |
result = MAX(result, (size_t)get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_MIN_WIDTH)); | |
if (geom == INTERN_REPR_GEOMETRY) { | |
char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_style_tag_for_cell(context->table_properties, context->row, context->column, cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
result += strlen(cell_style_tag); | |
char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_reset_style_tag_for_cell(context->table_properties, context->row, context->column, reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
result += strlen(reset_cell_style_tag); | |
char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_style_tag_for_content(context->table_properties, context->row, context->column, content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
result += strlen(content_style_tag); | |
char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_reset_style_tag_for_content(context->table_properties, context->row, context->column, reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
result += strlen(reset_content_style_tag); | |
} | |
return result; | |
} | |
TB_INTERNAL | |
size_t hint_height_cell(const table_cell_t *cell, const context_t *context) | |
{ | |
assert(cell); | |
assert(context); | |
size_t cell_padding_top = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_TOP_PADDING); | |
size_t cell_padding_bottom = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_BOTTOM_PADDING); | |
size_t cell_empty_string_height = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_EMPTY_STR_HEIGHT); | |
size_t result = cell_padding_top + cell_padding_bottom; | |
if (cell->str_buffer && cell->str_buffer->str.data) { | |
size_t text_height = buffer_text_height(cell->str_buffer); | |
result += text_height == 0 ? cell_empty_string_height : text_height; | |
} | |
return result; | |
} | |
TB_INTERNAL | |
int cell_printf(table_cell_t *cell, size_t row, char *buf, size_t buf_len, const context_t *context) | |
{ | |
const char *space_char = " "; | |
int (*buffer_printf_)(string_buffer_t *, size_t, char *, size_t, const context_t *, const char *, const char *) = buffer_printf; | |
// int (*snprint_n_chars_)(char *, size_t, size_t, char) = snprint_n_chars; | |
int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; | |
if (cell == NULL || buf_len == 0 | |
|| (buf_len <= hint_width_cell(cell, context, VISIBLE_GEOMETRY))) { | |
return -1; | |
} | |
unsigned int cell_padding_top = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_TOP_PADDING); | |
unsigned int cell_padding_left = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_LETB_PADDING); | |
unsigned int cell_padding_right = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_RIGHT_PADDING); | |
int written = 0; | |
int invisible_written = 0; | |
int tmp = 0; | |
// int left = cell_padding_left; | |
// int right = cell_padding_right; | |
/* todo: Dirty hack with changing buf_len! need refactoring. */ | |
/* Also maybe it is better to move all struff with colors to buffers? */ | |
char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_style_tag_for_cell(context->table_properties, context->row, context->column, cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
buf_len += strlen(cell_style_tag); | |
char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_reset_style_tag_for_cell(context->table_properties, context->row, context->column, reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
buf_len += strlen(reset_cell_style_tag); | |
char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_style_tag_for_content(context->table_properties, context->row, context->column, content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
buf_len += strlen(content_style_tag); | |
char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_reset_style_tag_for_content(context->table_properties, context->row, context->column, reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
buf_len += strlen(reset_content_style_tag); | |
/* CELL_STYLE_T LETB_PADDING CONTENT_STYLE_T CONTENT RESET_CONTENT_STYLE_T RIGHT_PADDING RESET_CELL_STYLE_T | |
* | | | | | | | | | |
* L1 R1 | |
* L2 R2 | |
* L3 R3 | |
*/ | |
size_t L2 = cell_padding_left; | |
size_t R2 = cell_padding_right; | |
size_t R3 = strlen(reset_cell_style_tag); | |
#define TOTAL_WRITTEN (written + invisible_written) | |
#define RIGHT (cell_padding_right + extra_right) | |
#define WRITE_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, cell_style_tag)) | |
#define WRITE_RESET_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_cell_style_tag)) | |
#define WRITE_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, content_style_tag)) | |
#define WRITE_RESET_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_content_style_tag)) | |
if (row >= hint_height_cell(cell, context) | |
|| row < cell_padding_top | |
|| row >= (cell_padding_top + buffer_text_height(cell->str_buffer))) { | |
WRITE_CELL_STYLE_TAG; | |
WRITE_CONTENT_STYLE_TAG; | |
WRITE_RESET_CONTENT_STYLE_TAG; | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len, buf_len - 1 - TOTAL_WRITTEN - R3, space_char)); | |
WRITE_RESET_CELL_STYLE_TAG; | |
return TOTAL_WRITTEN; | |
} | |
WRITE_CELL_STYLE_TAG; | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, L2, space_char)); | |
if (cell->str_buffer) { | |
CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf_(cell->str_buffer, row - cell_padding_top, buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, context, content_style_tag, reset_content_style_tag)); | |
} else { | |
WRITE_CONTENT_STYLE_TAG; | |
WRITE_RESET_CONTENT_STYLE_TAG; | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, space_char)); | |
} | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, R2, space_char)); | |
WRITE_RESET_CELL_STYLE_TAG; | |
return TOTAL_WRITTEN; | |
clear: | |
return -1; | |
#undef WRITE_CELL_STYLE_TAG | |
#undef WRITE_RESET_CELL_STYLE_TAG | |
#undef WRITE_CONTENT_STYLE_TAG | |
#undef WRITE_RESET_CONTENT_STYLE_TAG | |
#undef TOTAL_WRITTEN | |
#undef RIGHT | |
} | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
int cell_wprintf(table_cell_t *cell, size_t row, wchar_t *buf, size_t buf_len, const context_t *context) | |
{ | |
const char *space_char = " "; | |
int (*buffer_printf_)(string_buffer_t *, size_t, wchar_t *, size_t, const context_t *, const char *, const char *) = buffer_wprintf; | |
// int (*snprint_n_chars_)(wchar_t *, size_t, size_t, wchar_t) = wsnprint_n_chars; | |
int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; | |
if (cell == NULL || buf_len == 0 | |
|| (buf_len <= hint_width_cell(cell, context, VISIBLE_GEOMETRY))) { | |
return -1; | |
} | |
unsigned int cell_padding_top = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_TOP_PADDING); | |
unsigned int cell_padding_left = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_LETB_PADDING); | |
unsigned int cell_padding_right = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_RIGHT_PADDING); | |
int written = 0; | |
int invisible_written = 0; | |
int tmp = 0; | |
/* todo: Dirty hack with changing buf_len! need refactoring. */ | |
/* Also maybe it is better to move all struff with colors to buffers? */ | |
char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_style_tag_for_cell(context->table_properties, context->row, context->column, cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
buf_len += strlen(cell_style_tag); | |
char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_reset_style_tag_for_cell(context->table_properties, context->row, context->column, reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
buf_len += strlen(reset_cell_style_tag); | |
char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_style_tag_for_content(context->table_properties, context->row, context->column, content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
buf_len += strlen(content_style_tag); | |
char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; | |
get_reset_style_tag_for_content(context->table_properties, context->row, context->column, reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); | |
buf_len += strlen(reset_content_style_tag); | |
/* CELL_STYLE_T LETB_PADDING CONTENT_STYLE_T CONTENT RESET_CONTENT_STYLE_T RIGHT_PADDING RESET_CELL_STYLE_T | |
* | | | | | | | | | |
* L1 R1 | |
* L2 R2 | |
* L3 R3 | |
*/ | |
size_t L2 = cell_padding_left; | |
size_t R2 = cell_padding_right; | |
size_t R3 = strlen(reset_cell_style_tag); | |
#define TOTAL_WRITTEN (written + invisible_written) | |
#define RIGHT (right + extra_right) | |
#define WRITE_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, cell_style_tag)) | |
#define WRITE_RESET_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_cell_style_tag)) | |
#define WRITE_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, content_style_tag)) | |
#define WRITE_RESET_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_content_style_tag)) | |
if (row >= hint_height_cell(cell, context) | |
|| row < cell_padding_top | |
|| row >= (cell_padding_top + buffer_text_height(cell->str_buffer))) { | |
WRITE_CELL_STYLE_TAG; | |
WRITE_CONTENT_STYLE_TAG; | |
WRITE_RESET_CONTENT_STYLE_TAG; | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len, buf_len - 1 - TOTAL_WRITTEN - R3, space_char)); | |
WRITE_RESET_CELL_STYLE_TAG; | |
return TOTAL_WRITTEN; | |
} | |
WRITE_CELL_STYLE_TAG; | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, L2, space_char)); | |
if (cell->str_buffer) { | |
CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf_(cell->str_buffer, row - cell_padding_top, buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, context, content_style_tag, reset_content_style_tag)); | |
} else { | |
WRITE_CONTENT_STYLE_TAG; | |
WRITE_RESET_CONTENT_STYLE_TAG; | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, space_char)); | |
} | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, R2, space_char)); | |
WRITE_RESET_CELL_STYLE_TAG; | |
return TOTAL_WRITTEN; | |
clear: | |
return -1; | |
#undef WRITE_CELL_STYLE_TAG | |
#undef WRITE_RESET_CELL_STYLE_TAG | |
#undef WRITE_CONTENT_STYLE_TAG | |
#undef WRITE_RESET_CONTENT_STYLE_TAG | |
#undef TOTAL_WRITTEN | |
#undef RIGHT | |
} | |
#endif | |
TB_INTERNAL | |
table_status_t fill_cell_from_string(table_cell_t *cell, const char *str) | |
{ | |
assert(str); | |
assert(cell); | |
return fill_buffer_from_string(cell->str_buffer, str); | |
} | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
table_status_t fill_cell_from_wstring(table_cell_t *cell, const wchar_t *str) | |
{ | |
assert(str); | |
assert(cell); | |
return fill_buffer_from_wstring(cell->str_buffer, str); | |
} | |
#endif | |
TB_INTERNAL | |
string_buffer_t *cell_get_string_buffer(table_cell_t *cell) | |
{ | |
assert(cell); | |
assert(cell->str_buffer); | |
return cell->str_buffer; | |
} | |
/******************************************************** | |
End of file "cell.c" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "table_impl.c" | |
********************************************************/ | |
/* | |
libfort | |
MIT License | |
Copyright (c) 2017 - 2018 Seleznev Anton | |
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. | |
*/ | |
#include <stdlib.h> | |
#include <stdarg.h> | |
#include <stdio.h> | |
#include "table.h" | |
#include <assert.h> | |
#include <string.h> | |
#include <wchar.h> | |
#include <ctype.h> | |
/* #include "vector.h" */ /* Commented by amalgamation script */ | |
/* #include "table_utils.h" */ /* Commented by amalgamation script */ | |
/* #include "string_buffer.h" */ /* Commented by amalgamation script */ | |
/* #include "table.h" */ /* Commented by amalgamation script */ | |
/* #include "row.h" */ /* Commented by amalgamation script */ | |
/* #include "properties.h" */ /* Commented by amalgamation script */ | |
ft_table_t *ft_create_table(void) | |
{ | |
ft_table_t *result = (ft_table_t *)F_CALLOC(1, sizeof(ft_table_t)); | |
if (result == NULL) | |
return NULL; | |
result->rows = create_vector(sizeof(table_row_t *), DEFAULT_VECTOR_CAPACITY); | |
if (result->rows == NULL) { | |
F_FREE(result); | |
return NULL; | |
} | |
result->separators = create_vector(sizeof(separator_t *), DEFAULT_VECTOR_CAPACITY); | |
if (result->separators == NULL) { | |
destroy_vector(result->rows); | |
F_FREE(result); | |
return NULL; | |
} | |
result->properties = NULL; | |
result->conv_buffer = NULL; | |
result->cur_row = 0; | |
result->cur_col = 0; | |
return result; | |
} | |
void ft_destroy_table(ft_table_t *table) | |
{ | |
size_t i = 0; | |
if (table == NULL) | |
return; | |
if (table->rows) { | |
size_t row_n = vector_size(table->rows); | |
for (i = 0; i < row_n; ++i) { | |
destroy_row(*(table_row_t **)vector_at(table->rows, i)); | |
} | |
destroy_vector(table->rows); | |
} | |
if (table->separators) { | |
size_t row_n = vector_size(table->separators); | |
for (i = 0; i < row_n; ++i) { | |
destroy_separator(*(separator_t **)vector_at(table->separators, i)); | |
} | |
destroy_vector(table->separators); | |
} | |
destroy_table_properties(table->properties); | |
destroy_string_buffer(table->conv_buffer); | |
F_FREE(table); | |
} | |
ft_table_t *ft_copy_table(ft_table_t *table) | |
{ | |
if (table == NULL) | |
return NULL; | |
ft_table_t *result = ft_create_table(); | |
if (result == NULL) | |
return NULL; | |
size_t rows_n = vector_size(table->rows); | |
for (size_t i = 0; i < rows_n; ++i) { | |
table_row_t *row = *(table_row_t **)vector_at(table->rows, i); | |
table_row_t *new_row = copy_row(row); | |
if (new_row == NULL) { | |
ft_destroy_table(result); | |
return NULL; | |
} | |
vector_push(result->rows, &new_row); | |
} | |
size_t sep_sz = vector_size(table->separators); | |
for (size_t i = 0; i < sep_sz; ++i) { | |
separator_t *sep = *(separator_t **)vector_at(table->separators, i); | |
separator_t *new_sep = copy_separator(sep); | |
if (new_sep == NULL) { | |
ft_destroy_table(result); | |
return NULL; | |
} | |
vector_push(result->separators, &new_sep); | |
} | |
result->properties = copy_table_properties(table->properties); | |
if (result->properties == NULL) { | |
ft_destroy_table(result); | |
return NULL; | |
} | |
/* todo: copy conv_buffer ?? */ | |
result->cur_row = table->cur_row; | |
result->cur_col = table->cur_col; | |
return result; | |
} | |
void ft_ln(ft_table_t *table) | |
{ | |
assert(table); | |
table->cur_col = 0; | |
table->cur_row++; | |
} | |
size_t ft_cur_row(ft_table_t *table) | |
{ | |
assert(table); | |
return table->cur_row; | |
} | |
size_t ft_cur_col(ft_table_t *table) | |
{ | |
assert(table); | |
return table->cur_col; | |
} | |
void ft_set_cur_cell(ft_table_t *table, size_t row, size_t col) | |
{ | |
assert(table); | |
table->cur_row = row; | |
table->cur_col = col; | |
} | |
TB_PRINTF_ATTRIBUTE_FORMAT(3, 0) | |
static int ft_row_printf_impl(ft_table_t *table, size_t row, const char *fmt, va_list *va) | |
{ | |
#define CREATE_ROW_FROM_FMT_STRING create_row_from_fmt_string | |
size_t i = 0; | |
size_t new_cols = 0; | |
if (table == NULL) | |
return -1; | |
table_row_t *new_row = CREATE_ROW_FROM_FMT_STRING(fmt, va); | |
if (new_row == NULL) { | |
return -1; | |
} | |
table_row_t **cur_row_p = NULL; | |
size_t sz = vector_size(table->rows); | |
if (row >= sz) { | |
size_t push_n = row - sz + 1; | |
for (i = 0; i < push_n; ++i) { | |
table_row_t *padding_row = create_row(); | |
if (padding_row == NULL) | |
goto clear; | |
if (TB_IS_ERROR(vector_push(table->rows, &padding_row))) { | |
destroy_row(padding_row); | |
goto clear; | |
} | |
} | |
} | |
/* todo: clearing pushed items in case of error ?? */ | |
new_cols = columns_in_row(new_row); | |
cur_row_p = (table_row_t **)vector_at(table->rows, row); | |
swap_row(*cur_row_p, new_row, table->cur_col); | |
table->cur_col += new_cols; | |
destroy_row(new_row); | |
return (int)new_cols; | |
clear: | |
destroy_row(new_row); | |
return -1; | |
#undef CREATE_ROW_FROM_FMT_STRING | |
} | |
#ifdef TB_HAVE_WCHAR | |
static int ft_row_wprintf_impl(ft_table_t *table, size_t row, const wchar_t *fmt, va_list *va) | |
{ | |
#define CREATE_ROW_FROM_FMT_STRING create_row_from_fmt_wstring | |
size_t i = 0; | |
size_t new_cols = 0; | |
if (table == NULL) | |
return -1; | |
table_row_t *new_row = CREATE_ROW_FROM_FMT_STRING(fmt, va); | |
if (new_row == NULL) { | |
return -1; | |
} | |
table_row_t **cur_row_p = NULL; | |
size_t sz = vector_size(table->rows); | |
if (row >= sz) { | |
size_t push_n = row - sz + 1; | |
for (i = 0; i < push_n; ++i) { | |
table_row_t *padding_row = create_row(); | |
if (padding_row == NULL) | |
goto clear; | |
if (TB_IS_ERROR(vector_push(table->rows, &padding_row))) { | |
destroy_row(padding_row); | |
goto clear; | |
} | |
} | |
} | |
/* todo: clearing pushed items in case of error ?? */ | |
new_cols = columns_in_row(new_row); | |
cur_row_p = (table_row_t **)vector_at(table->rows, row); | |
swap_row(*cur_row_p, new_row, table->cur_col); | |
table->cur_col += new_cols; | |
destroy_row(new_row); | |
return (int)new_cols; | |
clear: | |
destroy_row(new_row); | |
return -1; | |
#undef CREATE_ROW_FROM_FMT_STRING | |
} | |
#endif | |
#if defined(TB_CLANG_COMPILER) || defined(TB_GCC_COMPILER) | |
#define TB_PRINTF ft_printf | |
#define TB_PRINTF_LN ft_printf_ln | |
#else | |
#define TB_PRINTF ft_printf_impl | |
#define TB_PRINTF_LN ft_printf_ln_impl | |
#endif | |
int TB_PRINTF(ft_table_t *table, const char *fmt, ...) | |
{ | |
assert(table); | |
va_list va; | |
va_start(va, fmt); | |
int result = ft_row_printf_impl(table, table->cur_row, fmt, &va); | |
va_end(va); | |
return result; | |
} | |
int TB_PRINTF_LN(ft_table_t *table, const char *fmt, ...) | |
{ | |
assert(table); | |
va_list va; | |
va_start(va, fmt); | |
int result = ft_row_printf_impl(table, table->cur_row, fmt, &va); | |
if (result >= 0) { | |
ft_ln(table); | |
} | |
va_end(va); | |
return result; | |
} | |
#undef TB_PRINTF | |
#undef TB_PRINTF_LN | |
#undef TB_HDR_PRINTF | |
#undef TB_HDR_PRINTF_LN | |
#ifdef TB_HAVE_WCHAR | |
int ft_wprintf(ft_table_t *table, const wchar_t *fmt, ...) | |
{ | |
assert(table); | |
va_list va; | |
va_start(va, fmt); | |
int result = ft_row_wprintf_impl(table, table->cur_row, fmt, &va); | |
va_end(va); | |
return result; | |
} | |
int ft_wprintf_ln(ft_table_t *table, const wchar_t *fmt, ...) | |
{ | |
assert(table); | |
va_list va; | |
va_start(va, fmt); | |
int result = ft_row_wprintf_impl(table, table->cur_row, fmt, &va); | |
if (result >= 0) { | |
ft_ln(table); | |
} | |
va_end(va); | |
return result; | |
} | |
#endif | |
static int ft_write_impl(ft_table_t *table, const char *cell_content) | |
{ | |
assert(table); | |
string_buffer_t *str_buffer = get_cur_str_buffer_and_create_if_not_exists(table); | |
if (str_buffer == NULL) | |
return TB_ERROR; | |
int status = fill_buffer_from_string(str_buffer, cell_content); | |
if (TB_IS_SUCCESS(status)) { | |
table->cur_col++; | |
} | |
return status; | |
} | |
#ifdef TB_HAVE_WCHAR | |
static int ft_wwrite_impl(ft_table_t *table, const wchar_t *cell_content) | |
{ | |
assert(table); | |
string_buffer_t *str_buffer = get_cur_str_buffer_and_create_if_not_exists(table); | |
if (str_buffer == NULL) | |
return TB_ERROR; | |
int status = fill_buffer_from_wstring(str_buffer, cell_content); | |
if (TB_IS_SUCCESS(status)) { | |
table->cur_col++; | |
} | |
return status; | |
} | |
#endif | |
int ft_nwrite(ft_table_t *table, size_t count, const char *cell_content, ...) | |
{ | |
size_t i = 0; | |
assert(table); | |
int status = ft_write_impl(table, cell_content); | |
if (TB_IS_ERROR(status)) | |
return status; | |
va_list va; | |
va_start(va, cell_content); | |
--count; | |
for (i = 0; i < count; ++i) { | |
const char *cell = va_arg(va, const char *); | |
status = ft_write_impl(table, cell); | |
if (TB_IS_ERROR(status)) { | |
va_end(va); | |
return status; | |
} | |
} | |
va_end(va); | |
return status; | |
} | |
int ft_nwrite_ln(ft_table_t *table, size_t count, const char *cell_content, ...) | |
{ | |
size_t i = 0; | |
assert(table); | |
int status = ft_write_impl(table, cell_content); | |
if (TB_IS_ERROR(status)) | |
return status; | |
va_list va; | |
va_start(va, cell_content); | |
--count; | |
for (i = 0; i < count; ++i) { | |
const char *cell = va_arg(va, const char *); | |
status = ft_write_impl(table, cell); | |
if (TB_IS_ERROR(status)) { | |
va_end(va); | |
return status; | |
} | |
} | |
va_end(va); | |
ft_ln(table); | |
return status; | |
} | |
#ifdef TB_HAVE_WCHAR | |
int ft_nwwrite(ft_table_t *table, size_t n, const wchar_t *cell_content, ...) | |
{ | |
size_t i = 0; | |
assert(table); | |
int status = ft_wwrite_impl(table, cell_content); | |
if (TB_IS_ERROR(status)) | |
return status; | |
va_list va; | |
va_start(va, cell_content); | |
--n; | |
for (i = 0; i < n; ++i) { | |
const wchar_t *cell = va_arg(va, const wchar_t *); | |
status = ft_wwrite_impl(table, cell); | |
if (TB_IS_ERROR(status)) { | |
va_end(va); | |
return status; | |
} | |
} | |
va_end(va); | |
return status; | |
} | |
int ft_nwwrite_ln(ft_table_t *table, size_t n, const wchar_t *cell_content, ...) | |
{ | |
size_t i = 0; | |
assert(table); | |
int status = ft_wwrite_impl(table, cell_content); | |
if (TB_IS_ERROR(status)) | |
return status; | |
va_list va; | |
va_start(va, cell_content); | |
--n; | |
for (i = 0; i < n; ++i) { | |
const wchar_t *cell = va_arg(va, const wchar_t *); | |
status = ft_wwrite_impl(table, cell); | |
if (TB_IS_ERROR(status)) { | |
va_end(va); | |
return status; | |
} | |
} | |
va_end(va); | |
ft_ln(table); | |
return status; | |
} | |
#endif | |
int ft_row_write(ft_table_t *table, size_t cols, const char *cells[]) | |
{ | |
size_t i = 0; | |
assert(table); | |
for (i = 0; i < cols; ++i) { | |
int status = ft_write_impl(table, cells[i]); | |
if (TB_IS_ERROR(status)) { | |
/* todo: maybe current pos in case of error should be equal to the one before function call? */ | |
return status; | |
} | |
} | |
return TB_SUCCESS; | |
} | |
int ft_row_write_ln(ft_table_t *table, size_t cols, const char *cells[]) | |
{ | |
assert(table); | |
int status = ft_row_write(table, cols, cells); | |
if (TB_IS_SUCCESS(status)) { | |
ft_ln(table); | |
} | |
return status; | |
} | |
#ifdef TB_HAVE_WCHAR | |
int ft_row_wwrite(ft_table_t *table, size_t cols, const wchar_t *cells[]) | |
{ | |
size_t i = 0; | |
assert(table); | |
for (i = 0; i < cols; ++i) { | |
int status = ft_wwrite_impl(table, cells[i]); | |
if (TB_IS_ERROR(status)) { | |
/* todo: maybe current pos in case of error should be equal | |
* to the one before function call? | |
*/ | |
return status; | |
} | |
} | |
return TB_SUCCESS; | |
} | |
int ft_row_wwrite_ln(ft_table_t *table, size_t cols, const wchar_t *cells[]) | |
{ | |
assert(table); | |
int status = ft_row_wwrite(table, cols, cells); | |
if (TB_IS_SUCCESS(status)) { | |
ft_ln(table); | |
} | |
return status; | |
} | |
#endif | |
int ft_table_write(ft_table_t *table, size_t rows, size_t cols, const char *table_cells[]) | |
{ | |
size_t i = 0; | |
assert(table); | |
for (i = 0; i < rows; ++i) { | |
int status = ft_row_write(table, cols, (const char **)&table_cells[i * cols]); | |
if (TB_IS_ERROR(status)) { | |
/* todo: maybe current pos in case of error should be equal | |
* to the one before function call? | |
*/ | |
return status; | |
} | |
if (i != rows - 1) | |
ft_ln(table); | |
} | |
return TB_SUCCESS; | |
} | |
int ft_table_write_ln(ft_table_t *table, size_t rows, size_t cols, const char *table_cells[]) | |
{ | |
assert(table); | |
int status = ft_table_write(table, rows, cols, table_cells); | |
if (TB_IS_SUCCESS(status)) { | |
ft_ln(table); | |
} | |
return status; | |
} | |
#ifdef TB_HAVE_WCHAR | |
int ft_table_wwrite(ft_table_t *table, size_t rows, size_t cols, const wchar_t *table_cells[]) | |
{ | |
size_t i = 0; | |
assert(table); | |
for (i = 0; i < rows; ++i) { | |
int status = ft_row_wwrite(table, cols, (const wchar_t **)&table_cells[i * cols]); | |
if (TB_IS_ERROR(status)) { | |
/* todo: maybe current pos in case of error should be equal | |
* to the one before function call? | |
*/ | |
return status; | |
} | |
if (i != rows - 1) | |
ft_ln(table); | |
} | |
return TB_SUCCESS; | |
} | |
int ft_table_wwrite_ln(ft_table_t *table, size_t rows, size_t cols, const wchar_t *table_cells[]) | |
{ | |
assert(table); | |
int status = ft_table_wwrite(table, rows, cols, table_cells); | |
if (TB_IS_SUCCESS(status)) { | |
ft_ln(table); | |
} | |
return status; | |
} | |
#endif | |
const char *ft_to_string(const ft_table_t *table) | |
{ | |
typedef char char_type; | |
const enum str_buf_type buf_type = CharBuf; | |
const char *space_char = " "; | |
const char *new_line_char = "\n"; | |
#define EMPTY_STRING "" | |
int (*snprintf_row_)(const table_row_t *, char *, size_t, size_t *, size_t, size_t, const context_t *) = snprintf_row; | |
int (*print_row_separator_)(char *, size_t, | |
const size_t *, size_t, | |
const table_row_t *, const table_row_t *, | |
enum HorSeparatorPos, const separator_t *, | |
const context_t *) = print_row_separator; | |
// int (*snprint_n_chars_)(char *, size_t, size_t, char) = snprint_n_chars; | |
int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; | |
assert(table); | |
/* Determing size of table string representation */ | |
size_t height = 0; | |
size_t width = 0; | |
int status = table_geometry(table, &height, &width); | |
if (TB_IS_ERROR(status)) { | |
return NULL; | |
} | |
size_t sz = height * width + 1; | |
/* Allocate string buffer for string representation */ | |
if (table->conv_buffer == NULL) { | |
((ft_table_t *)table)->conv_buffer = create_string_buffer(sz, buf_type); | |
if (table->conv_buffer == NULL) | |
return NULL; | |
} | |
while (string_buffer_capacity(table->conv_buffer) < sz) { | |
if (TB_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { | |
return NULL; | |
} | |
} | |
char_type *buffer = (char_type *)buffer_get_data(table->conv_buffer); | |
size_t cols = 0; | |
size_t rows = 0; | |
size_t *col_width_arr = NULL; | |
size_t *row_height_arr = NULL; | |
status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows, VISIBLE_GEOMETRY); | |
if (TB_IS_ERROR(status)) | |
return NULL; | |
if (rows == 0) | |
return EMPTY_STRING; | |
int written = 0; | |
int tmp = 0; | |
size_t i = 0; | |
context_t context; | |
context.table_properties = (table->properties ? table->properties : &g_table_properties); | |
table_row_t *prev_row = NULL; | |
table_row_t *cur_row = NULL; | |
separator_t *cur_sep = NULL; | |
size_t sep_size = vector_size(table->separators); | |
/* Print top margin */ | |
for (i = 0; i < context.table_properties->entire_table_properties.top_margin; ++i) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); | |
} | |
for (i = 0; i < rows; ++i) { | |
cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; | |
cur_row = *(table_row_t **)vector_at(table->rows, i); | |
enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator; | |
context.row = i; | |
CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep, &context)); | |
CHCK_RSLT_ADD_TO_WRITTEN(snprintf_row_(cur_row, buffer + written, sz - written, col_width_arr, cols, row_height_arr[i], &context)); | |
prev_row = cur_row; | |
} | |
cur_row = NULL; | |
cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; | |
CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, BottomSeparator, cur_sep, &context)); | |
/* Print bottom margin */ | |
for (i = 0; i < context.table_properties->entire_table_properties.bottom_margin; ++i) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); | |
} | |
F_FREE(col_width_arr); | |
F_FREE(row_height_arr); | |
return buffer; | |
clear: | |
F_FREE(col_width_arr); | |
F_FREE(row_height_arr); | |
// F_FREE(buffer); | |
return NULL; | |
#undef EMPTY_STRING | |
} | |
#ifdef TB_HAVE_WCHAR | |
const wchar_t *ft_to_wstring(const ft_table_t *table) | |
{ | |
typedef wchar_t char_type; | |
const enum str_buf_type buf_type = WCharBuf; | |
const char *space_char = " "; | |
const char *new_line_char = "\n"; | |
#define EMPTY_STRING L"" | |
int (*snprintf_row_)(const table_row_t *, wchar_t *, size_t, size_t *, size_t, size_t, const context_t *) = wsnprintf_row; | |
int (*print_row_separator_)(wchar_t *, size_t, | |
const size_t *, size_t, | |
const table_row_t *, const table_row_t *, | |
enum HorSeparatorPos, const separator_t *, | |
const context_t *) = wprint_row_separator; | |
// int (*snprint_n_chars_)(wchar_t *, size_t, size_t, wchar_t) = wsnprint_n_chars; | |
int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; | |
assert(table); | |
/* Determing size of table string representation */ | |
size_t height = 0; | |
size_t width = 0; | |
int status = table_geometry(table, &height, &width); | |
if (TB_IS_ERROR(status)) { | |
return NULL; | |
} | |
size_t sz = height * width + 1; | |
/* Allocate string buffer for string representation */ | |
if (table->conv_buffer == NULL) { | |
((ft_table_t *)table)->conv_buffer = create_string_buffer(sz, buf_type); | |
if (table->conv_buffer == NULL) | |
return NULL; | |
} | |
while (string_buffer_capacity(table->conv_buffer) < sz) { | |
if (TB_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { | |
return NULL; | |
} | |
} | |
char_type *buffer = (char_type *)buffer_get_data(table->conv_buffer); | |
size_t cols = 0; | |
size_t rows = 0; | |
size_t *col_width_arr = NULL; | |
size_t *row_height_arr = NULL; | |
status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows, VISIBLE_GEOMETRY); | |
if (rows == 0) | |
return EMPTY_STRING; | |
if (TB_IS_ERROR(status)) | |
return NULL; | |
int written = 0; | |
int tmp = 0; | |
size_t i = 0; | |
context_t context; | |
context.table_properties = (table->properties ? table->properties : &g_table_properties); | |
table_row_t *prev_row = NULL; | |
table_row_t *cur_row = NULL; | |
separator_t *cur_sep = NULL; | |
size_t sep_size = vector_size(table->separators); | |
/* Print top margin */ | |
for (i = 0; i < context.table_properties->entire_table_properties.top_margin; ++i) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); | |
} | |
for (i = 0; i < rows; ++i) { | |
cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; | |
cur_row = *(table_row_t **)vector_at(table->rows, i); | |
enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator; | |
context.row = i; | |
CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep, &context)); | |
CHCK_RSLT_ADD_TO_WRITTEN(snprintf_row_(cur_row, buffer + written, sz - written, col_width_arr, cols, row_height_arr[i], &context)); | |
prev_row = cur_row; | |
} | |
cur_row = NULL; | |
cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; | |
CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, BottomSeparator, cur_sep, &context)); | |
/* Print bottom margin */ | |
for (i = 0; i < context.table_properties->entire_table_properties.bottom_margin; ++i) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); | |
} | |
F_FREE(col_width_arr); | |
F_FREE(row_height_arr); | |
return buffer; | |
clear: | |
F_FREE(col_width_arr); | |
F_FREE(row_height_arr); | |
// F_FREE(buffer); | |
return NULL; | |
#undef EMPTY_STRING | |
} | |
#endif | |
int ft_add_separator(ft_table_t *table) | |
{ | |
assert(table); | |
assert(table->separators); | |
while (vector_size(table->separators) <= table->cur_row) { | |
separator_t *sep_p = create_separator(F_FALSE); | |
if (sep_p == NULL) | |
return TB_MEMORY_ERROR; | |
int status = vector_push(table->separators, &sep_p); | |
if (TB_IS_ERROR(status)) | |
return status; | |
} | |
separator_t **sep_p = (separator_t **)vector_at(table->separators, table->cur_row); | |
if (*sep_p == NULL) | |
*sep_p = create_separator(F_TRUE); | |
else | |
(*sep_p)->enabled = F_TRUE; | |
if (*sep_p == NULL) | |
return TB_ERROR; | |
return TB_SUCCESS; | |
} | |
struct ft_border_style *TB_BASIC_STYLE = (struct ft_border_style *) &TABLE_BASIC_STYLE; | |
struct ft_border_style *TB_BASIC2_STYLE = (struct ft_border_style *) &TABLE_BASIC2_STYLE; | |
struct ft_border_style *TB_SIMPLE_STYLE = (struct ft_border_style *) &TABLE_SIMPLE_STYLE; | |
struct ft_border_style *TB_PLAIN_STYLE = (struct ft_border_style *) &TABLE_PLAIN_STYLE; | |
struct ft_border_style *TB_DOT_STYLE = (struct ft_border_style *) &TABLE_DOT_STYLE; | |
struct ft_border_style *TB_EMPTY_STYLE = (struct ft_border_style *) &TABLE_EMPTY_STYLE; | |
struct ft_border_style *TB_SOLID_STYLE = (struct ft_border_style *) &TABLE_SOLID_STYLE; | |
struct ft_border_style *TB_SOLID_ROUND_STYLE = (struct ft_border_style *) &TABLE_SOLID_ROUND_STYLE; | |
struct ft_border_style *TB_NICE_STYLE = (struct ft_border_style *) &TABLE_NICE_STYLE; | |
struct ft_border_style *TB_DOUBLE_STYLE = (struct ft_border_style *) &TABLE_DOUBLE_STYLE; | |
struct ft_border_style *TB_DOUBLE2_STYLE = (struct ft_border_style *) &TABLE_DOUBLE2_STYLE; | |
struct ft_border_style *TB_BOLD_STYLE = (struct ft_border_style *) &TABLE_BOLD_STYLE; | |
struct ft_border_style *TB_BOLD2_STYLE = (struct ft_border_style *) &TABLE_BOLD2_STYLE; | |
struct ft_border_style *TB_FRAME_STYLE = (struct ft_border_style *) &TABLE_FRAME_STYLE; | |
static void set_border_props_for_props(table_table_properties_t *properties, const struct ft_border_style *style) | |
{ | |
if ((const struct table_border_style *)style == &TABLE_BASIC_STYLE | |
|| (const struct table_border_style *)style == &TABLE_BASIC2_STYLE | |
|| (const struct table_border_style *)style == &TABLE_SIMPLE_STYLE | |
|| (const struct table_border_style *)style == &TABLE_DOT_STYLE | |
|| (const struct table_border_style *)style == &TABLE_PLAIN_STYLE | |
|| (const struct table_border_style *)style == &TABLE_EMPTY_STYLE | |
|| (const struct table_border_style *)style == &TABLE_SOLID_STYLE | |
|| (const struct table_border_style *)style == &TABLE_SOLID_ROUND_STYLE | |
|| (const struct table_border_style *)style == &TABLE_NICE_STYLE | |
|| (const struct table_border_style *)style == &TABLE_DOUBLE_STYLE | |
|| (const struct table_border_style *)style == &TABLE_DOUBLE2_STYLE | |
|| (const struct table_border_style *)style == &TABLE_BOLD_STYLE | |
|| (const struct table_border_style *)style == &TABLE_BOLD2_STYLE | |
|| (const struct table_border_style *)style == &TABLE_FRAME_STYLE) { | |
memcpy(&(properties->border_style), (struct table_border_style *)style, sizeof(struct table_border_style)); | |
return; | |
} | |
const struct ft_border_chars *border_chs = &(style->border_chs); | |
const struct ft_border_chars *header_border_chs = &(style->header_border_chs); | |
#define BOR_CHARS properties->border_style.border_chars | |
#define H_BOR_CHARS properties->border_style.header_border_chars | |
#define SEP_CHARS properties->border_style.separator_chars | |
/* | |
BOR_CHARS[TL_bip] = BOR_CHARS[TT_bip] = BOR_CHARS[TV_bip] = BOR_CHARS[TR_bip] = border_chs->top_border_ch; | |
BOR_CHARS[LH_bip] = BOR_CHARS[IH_bip] = BOR_CHARS[II_bip] = BOR_CHARS[RH_bip] = border_chs->separator_ch; | |
BOR_CHARS[BL_bip] = BOR_CHARS[BB_bip] = BOR_CHARS[BV_bip] = BOR_CHARS[BR_bip] = border_chs->bottom_border_ch; | |
BOR_CHARS[LL_bip] = BOR_CHARS[IV_bip] = BOR_CHARS[RR_bip] = border_chs->side_border_ch; | |
H_BOR_CHARS[TL_bip] = H_BOR_CHARS[TT_bip] = H_BOR_CHARS[TV_bip] = H_BOR_CHARS[TR_bip] = header_border_chs->top_border_ch; | |
H_BOR_CHARS[LH_bip] = H_BOR_CHARS[IH_bip] = H_BOR_CHARS[II_bip] = H_BOR_CHARS[RH_bip] = header_border_chs->separator_ch; | |
H_BOR_CHARS[BL_bip] = H_BOR_CHARS[BB_bip] = H_BOR_CHARS[BV_bip] = H_BOR_CHARS[BR_bip] = header_border_chs->bottom_border_ch; | |
H_BOR_CHARS[LL_bip] = H_BOR_CHARS[IV_bip] = H_BOR_CHARS[RR_bip] = header_border_chs->side_border_ch; | |
*/ | |
BOR_CHARS[TT_bip] = border_chs->top_border_ch; | |
BOR_CHARS[IH_bip] = border_chs->separator_ch; | |
BOR_CHARS[BB_bip] = border_chs->bottom_border_ch; | |
BOR_CHARS[LL_bip] = BOR_CHARS[IV_bip] = BOR_CHARS[RR_bip] = border_chs->side_border_ch; | |
BOR_CHARS[TL_bip] = BOR_CHARS[TV_bip] = BOR_CHARS[TR_bip] = border_chs->out_intersect_ch; | |
BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = border_chs->out_intersect_ch; | |
BOR_CHARS[BL_bip] = BOR_CHARS[BV_bip] = BOR_CHARS[BR_bip] = border_chs->out_intersect_ch; | |
BOR_CHARS[II_bip] = border_chs->in_intersect_ch; | |
BOR_CHARS[LI_bip] = BOR_CHARS[TI_bip] = BOR_CHARS[RI_bip] = BOR_CHARS[BI_bip] = border_chs->in_intersect_ch; | |
// if (border_chs->separator_ch == '\0' && border_chs->in_intersect_ch == '\0') { | |
// BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = '\0'; | |
// } | |
if (strlen(border_chs->separator_ch) == 0 && strlen(border_chs->in_intersect_ch) == 0) { | |
BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = "\0"; | |
} | |
H_BOR_CHARS[TT_bip] = header_border_chs->top_border_ch; | |
H_BOR_CHARS[IH_bip] = header_border_chs->separator_ch; | |
H_BOR_CHARS[BB_bip] = header_border_chs->bottom_border_ch; | |
H_BOR_CHARS[LL_bip] = H_BOR_CHARS[IV_bip] = H_BOR_CHARS[RR_bip] = header_border_chs->side_border_ch; | |
H_BOR_CHARS[TL_bip] = H_BOR_CHARS[TV_bip] = H_BOR_CHARS[TR_bip] = header_border_chs->out_intersect_ch; | |
H_BOR_CHARS[LH_bip] = H_BOR_CHARS[RH_bip] = header_border_chs->out_intersect_ch; | |
H_BOR_CHARS[BL_bip] = H_BOR_CHARS[BV_bip] = H_BOR_CHARS[BR_bip] = header_border_chs->out_intersect_ch; | |
H_BOR_CHARS[II_bip] = header_border_chs->in_intersect_ch; | |
H_BOR_CHARS[LI_bip] = H_BOR_CHARS[TI_bip] = H_BOR_CHARS[RI_bip] = H_BOR_CHARS[BI_bip] = header_border_chs->in_intersect_ch; | |
// if (header_border_chs->separator_ch == '\0' && header_border_chs->in_intersect_ch == '\0') { | |
// H_BOR_CHARS[LH_bip] = H_BOR_CHARS[RH_bip] = '\0'; | |
// } | |
if (strlen(header_border_chs->separator_ch) == 0 && strlen(header_border_chs->in_intersect_ch) == 0) { | |
BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = "\0"; | |
} | |
SEP_CHARS[LH_sip] = SEP_CHARS[RH_sip] = SEP_CHARS[II_sip] = header_border_chs->out_intersect_ch; | |
SEP_CHARS[TI_sip] = SEP_CHARS[BI_sip] = header_border_chs->out_intersect_ch; | |
SEP_CHARS[IH_sip] = style->hor_separator_char; | |
#undef BOR_CHARS | |
#undef H_BOR_CHARS | |
#undef SEP_CHARS | |
} | |
int ft_set_default_border_style(const struct ft_border_style *style) | |
{ | |
set_border_props_for_props(&g_table_properties, style); | |
return TB_SUCCESS; | |
} | |
int ft_set_border_style(ft_table_t *table, const struct ft_border_style *style) | |
{ | |
assert(table); | |
if (table->properties == NULL) { | |
table->properties = create_table_properties(); | |
if (table->properties == NULL) | |
return TB_MEMORY_ERROR; | |
} | |
set_border_props_for_props(table->properties, style); | |
return TB_SUCCESS; | |
} | |
int ft_set_cell_prop(ft_table_t *table, size_t row, size_t col, uint32_t property, int value) | |
{ | |
assert(table); | |
if (table->properties == NULL) { | |
table->properties = create_table_properties(); | |
if (table->properties == NULL) | |
return TB_MEMORY_ERROR; | |
} | |
if (table->properties->cell_properties == NULL) { | |
table->properties->cell_properties = create_cell_prop_container(); | |
if (table->properties->cell_properties == NULL) { | |
return TB_ERROR; | |
} | |
} | |
if (row == TB_CUR_ROW) | |
row = table->cur_row; | |
if (row == TB_CUR_COLUMN) | |
col = table->cur_col; | |
return set_cell_property(table->properties->cell_properties, row, col, property, value); | |
} | |
int ft_set_default_cell_prop(uint32_t property, int value) | |
{ | |
return set_default_cell_property(property, value); | |
} | |
int ft_set_default_tbl_prop(uint32_t property, int value) | |
{ | |
return set_default_entire_table_property(property, value); | |
} | |
int ft_set_tbl_prop(ft_table_t *table, uint32_t property, int value) | |
{ | |
assert(table); | |
if (table->properties == NULL) { | |
table->properties = create_table_properties(); | |
if (table->properties == NULL) | |
return TB_MEMORY_ERROR; | |
} | |
return set_entire_table_property(table->properties, property, value); | |
} | |
void ft_set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr)) | |
{ | |
set_memory_funcs(f_malloc, f_free); | |
} | |
int ft_set_cell_span(ft_table_t *table, size_t row, size_t col, size_t hor_span) | |
{ | |
assert(table); | |
if (hor_span < 2) | |
return TB_EINVAL; | |
if (row == TB_CUR_ROW) | |
row = table->cur_row; | |
if (row == TB_CUR_COLUMN) | |
col = table->cur_col; | |
table_row_t *row_p = get_row_and_create_if_not_exists(table, row); | |
if (row_p == NULL) | |
return TB_ERROR; | |
return row_set_cell_span(row_p, col, hor_span); | |
} | |
/******************************************************** | |
End of file "table_impl.c" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "table_utils.c" | |
********************************************************/ | |
/* #include "table_utils.h" */ /* Commented by amalgamation script */ | |
#ifdef TB_HAVE_WCHAR | |
#include <wchar.h> | |
#endif | |
/***************************************************************************** | |
* LIBFORT helpers | |
*****************************************************************************/ | |
#ifndef TB_MICROSOTB_COMPILER | |
void *(*table_malloc)(size_t size) = &malloc; | |
void (*table_free)(void *ptr) = &free; | |
void *(*table_calloc)(size_t nmemb, size_t size) = &calloc; | |
void *(*table_realloc)(void *ptr, size_t size) = &realloc; | |
#else | |
static void *local_malloc(size_t size) | |
{ | |
return malloc(size); | |
} | |
static void local_free(void *ptr) | |
{ | |
free(ptr); | |
} | |
static void *local_calloc(size_t nmemb, size_t size) | |
{ | |
return calloc(nmemb, size); | |
} | |
static void *local_realloc(void *ptr, size_t size) | |
{ | |
return realloc(ptr, size); | |
} | |
void *(*table_malloc)(size_t size) = &local_malloc; | |
void (*table_free)(void *ptr) = &local_free; | |
void *(*table_calloc)(size_t nmemb, size_t size) = &local_calloc; | |
void *(*table_realloc)(void *ptr, size_t size) = &local_realloc; | |
#endif | |
static void *custom_table_calloc(size_t nmemb, size_t size) | |
{ | |
size_t total_size = nmemb * size; | |
void *result = F_MALLOC(total_size); | |
if (result != NULL) | |
memset(result, 0, total_size); | |
return result; | |
} | |
static void *custom_table_realloc(void *ptr, size_t size) | |
{ | |
if (ptr == NULL) | |
return F_MALLOC(size); | |
if (size == 0) { | |
F_FREE(ptr); | |
return NULL; | |
} | |
void *new_chunk = F_MALLOC(size); | |
if (new_chunk == NULL) | |
return NULL; | |
/* | |
* In theory we should copy MIN(size, size allocated for ptr) bytes, | |
* but this is rather dummy implementation so we don't care about it | |
*/ | |
memcpy(new_chunk, ptr, size); | |
F_FREE(ptr); | |
return new_chunk; | |
} | |
void set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr)) | |
{ | |
assert((f_malloc == NULL && f_free == NULL) /* Use std functions */ | |
|| (f_malloc != NULL && f_free != NULL) /* Use custom functions */); | |
if (f_malloc == NULL && f_free == NULL) { | |
#ifndef TB_MICROSOTB_COMPILER | |
table_malloc = &malloc; | |
table_free = &free; | |
table_calloc = &calloc; | |
table_realloc = &realloc; | |
#else | |
table_malloc = &local_malloc; | |
table_free = &local_free; | |
table_calloc = &local_calloc; | |
table_realloc = &local_realloc; | |
#endif | |
} else { | |
table_malloc = f_malloc; | |
table_free = f_free; | |
table_calloc = &custom_table_calloc; | |
table_realloc = &custom_table_realloc; | |
} | |
} | |
char *table_strdup(const char *str) | |
{ | |
if (str == NULL) | |
return NULL; | |
size_t sz = strlen(str); | |
char *str_copy = (char *)F_MALLOC((sz + 1) * sizeof(char)); | |
if (str_copy == NULL) | |
return NULL; | |
strcpy(str_copy, str); | |
return str_copy; | |
} | |
#if defined(TB_HAVE_WCHAR) | |
wchar_t *table_wcsdup(const wchar_t *str) | |
{ | |
if (str == NULL) | |
return NULL; | |
size_t sz = wcslen(str); | |
wchar_t *str_copy = (wchar_t *)F_MALLOC((sz + 1) * sizeof(wchar_t)); | |
if (str_copy == NULL) | |
return NULL; | |
wcscpy(str_copy, str); | |
return str_copy; | |
} | |
#endif | |
size_t number_of_columns_in_format_string(const char *fmt) | |
{ | |
int separator_counter = 0; | |
const char *pos = fmt; | |
while (1) { | |
pos = strchr(pos, TABLE_COL_SEPARATOR); | |
if (pos == NULL) | |
break; | |
separator_counter++; | |
++pos; | |
} | |
return separator_counter + 1; | |
} | |
#if defined(TB_HAVE_WCHAR) | |
size_t number_of_columns_in_format_wstring(const wchar_t *fmt) | |
{ | |
int separator_counter = 0; | |
const wchar_t *pos = fmt; | |
while (1) { | |
pos = wcschr(pos, TABLE_COL_SEPARATOR); | |
if (pos == NULL) | |
break; | |
separator_counter++; | |
++pos; | |
} | |
return separator_counter + 1; | |
} | |
#endif | |
//int snprint_n_chars(char *buf, size_t length, size_t n, char ch) | |
//{ | |
// if (length <= n) | |
// return -1; | |
// if (n == 0) | |
// return 0; | |
// /* To ensure valid return value it is safely not print such big strings */ | |
// if (n > INT_MAX) | |
// return -1; | |
// int status = snprintf(buf, length, "%0*d", (int)n, 0); | |
// if (status < 0) | |
// return status; | |
// size_t i = 0; | |
// for (i = 0; i < n; ++i) { | |
// *buf = ch; | |
// buf++; | |
// } | |
// return (int)n; | |
//} | |
int snprint_n_strings(char *buf, size_t length, size_t n, const char *str) | |
{ | |
size_t str_len = strlen(str); | |
if (length <= n * str_len) | |
return -1; | |
if (n == 0) | |
return 0; | |
/* To ensure valid return value it is safely not print such big strings */ | |
if (n * str_len > INT_MAX) | |
return -1; | |
if (str_len == 0) | |
return 0; | |
int status = snprintf(buf, length, "%0*d", (int)(n * str_len), 0); | |
if (status < 0) | |
return status; | |
size_t i = 0; | |
for (i = 0; i < n; ++i) { | |
const char *str_p = str; | |
while (*str_p) | |
*(buf++) = *(str_p++); | |
} | |
return (int)(n * str_len); | |
} | |
//int wsnprint_n_chars(wchar_t *buf, size_t length, size_t n, wchar_t ch) | |
//{ | |
// if (length <= n) | |
// return -1; | |
// if (n == 0) | |
// return 0; | |
// /* To ensure valid return value it is safely not print such big strings */ | |
// if (n > INT_MAX) | |
// return -1; | |
// int status = swprintf(buf, length, L"%0*d", (int)n, 0); | |
// if (status < 0) | |
// return status; | |
// size_t i = 0; | |
// for (i = 0; i < n; ++i) { | |
// *buf = ch; | |
// buf++; | |
// } | |
// return (int)n; | |
//} | |
#if defined(TB_HAVE_WCHAR) | |
#define WCS_SIZE 64 | |
int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str) | |
{ | |
size_t str_len = strlen(str); | |
/* note: baybe it's, better to return -1 in case of multibyte character | |
* strings (not sure this case is done correctly). | |
*/ | |
if (str_len > 1) { | |
const unsigned char *p = (const unsigned char *)str; | |
while (*p) { | |
if (*p <= 127) | |
p++; | |
else { | |
wchar_t wcs[WCS_SIZE]; | |
const char *ptr = str; | |
size_t wcs_len; | |
mbstate_t mbst; | |
memset(&mbst, 0, sizeof(mbst)); | |
wcs_len = mbsrtowcs(wcs, (const char **)&ptr, WCS_SIZE, &mbst); | |
/* for simplicity */ | |
if ((wcs_len == (size_t) - 1) || wcs_len > 1) { | |
return -1; | |
} else { | |
wcs[wcs_len] = L'\0'; | |
size_t k = n; | |
while (k) { | |
*buf = *wcs; | |
++buf; | |
--k; | |
} | |
buf[n] = L'\0'; | |
return (int)n; | |
} | |
} | |
} | |
} | |
if (length <= n * str_len) | |
return -1; | |
if (n == 0) | |
return 0; | |
/* To ensure valid return value it is safely not print such big strings */ | |
if (n * str_len > INT_MAX) | |
return -1; | |
if (str_len == 0) | |
return 0; | |
int status = swprintf(buf, length, L"%0*d", (int)(n * str_len), 0); | |
if (status < 0) | |
return status; | |
size_t i = 0; | |
for (i = 0; i < n; ++i) { | |
const char *str_p = str; | |
while (*str_p) | |
*(buf++) = (wchar_t) * (str_p++); | |
} | |
return (int)(n * str_len); | |
} | |
#endif | |
/******************************************************** | |
End of file "table_utils.c" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "properties.c" | |
********************************************************/ | |
/* #include "table_utils.h" */ /* Commented by amalgamation script */ | |
#include <assert.h> | |
/* #include "properties.h" */ /* Commented by amalgamation script */ | |
/* #include "vector.h" */ /* Commented by amalgamation script */ | |
#define TB_RESET_COLOR "\033[0m" | |
const char *fg_colors[] = { | |
"", | |
"\033[30m", | |
"\033[31m", | |
"\033[32m", | |
"\033[33m", | |
"\033[34m", | |
"\033[35m", | |
"\033[36m", | |
"\033[37m", | |
"\033[90m", | |
"\033[91m", | |
"\033[92m", | |
"\033[93m", | |
"\033[94m", | |
"\033[95m", | |
"\033[96m", | |
"\033[97m", | |
}; | |
const char *reset_fg_colors[] = { | |
"", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
"\033[39m", | |
}; | |
const char *bg_colors[] = { | |
"", | |
"\033[40m", | |
"\033[41m", | |
"\033[42m", | |
"\033[43m", | |
"\033[44m", | |
"\033[45m", | |
"\033[46m", | |
"\033[47m", | |
"\033[100m", | |
"\033[101m", | |
"\033[102m", | |
"\033[103m", | |
"\033[104m", | |
"\033[105m", | |
"\033[106m", | |
"\033[107m", | |
}; | |
const char *reset_bg_colors[] = { | |
"", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
"\033[49m", | |
}; | |
const char *text_styles[] = { | |
"", | |
"\033[1m", | |
"\033[2m", | |
"\033[3m", | |
"\033[4m", | |
"\033[5m", | |
"\033[7m", | |
"\033[8m", | |
}; | |
const char *reset_text_styles[] = { | |
"", | |
"\033[21m", | |
"\033[22m", | |
"\033[23m", | |
"\033[24m", | |
"\033[25m", | |
"\033[27m", | |
"\033[28m", | |
}; | |
static const size_t n_fg_colors = sizeof(fg_colors) / sizeof(fg_colors[0]); | |
static const size_t n_bg_colors = sizeof(bg_colors) / sizeof(bg_colors[0]); | |
static const size_t n_styles = sizeof(text_styles) / sizeof(text_styles[0]); | |
void get_style_tag_for_cell(const table_table_properties_t *props, | |
size_t row, size_t col, char *style_tag, size_t sz) | |
{ | |
(void)sz; | |
unsigned bg_color_number = get_cell_property_value_hierarcial(props, row, col, TB_CPROP_CELL_BG_COLOR); | |
unsigned text_style = get_cell_property_value_hierarcial(props, row, col, TB_CPROP_CELL_TEXT_STYLE); | |
style_tag[0] = '\0'; | |
if (text_style < (1U << n_styles)) { | |
for (size_t i = 0; i < n_styles; ++i) { | |
if (text_style & (1 << i)) { | |
strcat(style_tag, text_styles[i]); | |
} | |
} | |
} else { | |
goto error; | |
} | |
if (bg_color_number < n_bg_colors) { | |
strcat(style_tag, bg_colors[bg_color_number]); | |
} else { | |
goto error; | |
} | |
return; | |
error: | |
// shouldn't be here | |
assert(0); | |
style_tag[0] = '\0'; | |
return; | |
} | |
void get_reset_style_tag_for_cell(const table_table_properties_t *props, | |
size_t row, size_t col, char *reset_style_tag, size_t sz) | |
{ | |
(void)sz; | |
unsigned bg_color_number = get_cell_property_value_hierarcial(props, row, col, TB_CPROP_CELL_BG_COLOR); | |
unsigned text_style = get_cell_property_value_hierarcial(props, row, col, TB_CPROP_CELL_TEXT_STYLE); | |
reset_style_tag[0] = '\0'; | |
if (text_style < (1U << n_styles)) { | |
for (size_t i = 0; i < n_styles; ++i) { | |
if (text_style & (1 << i)) { | |
strcat(reset_style_tag, reset_text_styles[i]); | |
} | |
} | |
} else { | |
goto error; | |
} | |
if (bg_color_number < n_bg_colors) { | |
strcat(reset_style_tag, reset_bg_colors[bg_color_number]); | |
} else { | |
goto error; | |
} | |
return; | |
error: | |
// shouldn't be here | |
assert(0); | |
reset_style_tag[0] = '\0'; | |
return; | |
} | |
void get_style_tag_for_content(const table_table_properties_t *props, | |
size_t row, size_t col, char *style_tag, size_t sz) | |
{ | |
(void)sz; | |
unsigned text_style = get_cell_property_value_hierarcial(props, row, col, TB_CPROP_CONT_TEXT_STYLE); | |
unsigned fg_color_number = get_cell_property_value_hierarcial(props, row, col, TB_CPROP_CONT_FG_COLOR); | |
unsigned bg_color_number = get_cell_property_value_hierarcial(props, row, col, TB_CPROP_CONT_BG_COLOR); | |
style_tag[0] = '\0'; | |
if (text_style < (1U << n_styles)) { | |
for (size_t i = 0; i < n_styles; ++i) { | |
if (text_style & (1 << i)) { | |
strcat(style_tag, text_styles[i]); | |
} | |
} | |
} else { | |
goto error; | |
} | |
if (fg_color_number < n_fg_colors) { | |
strcat(style_tag, fg_colors[fg_color_number]); | |
} else { | |
goto error; | |
} | |
if (bg_color_number < n_bg_colors) { | |
strcat(style_tag, bg_colors[bg_color_number]); | |
} else { | |
goto error; | |
} | |
return; | |
error: | |
// shouldn't be here | |
assert(0); | |
style_tag[0] = '\0'; | |
return; | |
} | |
void get_reset_style_tag_for_content(const table_table_properties_t *props, | |
size_t row, size_t col, char *reset_style_tag, size_t sz) | |
{ | |
(void)sz; | |
unsigned text_style = get_cell_property_value_hierarcial(props, row, col, TB_CPROP_CONT_TEXT_STYLE); | |
unsigned fg_color_number = get_cell_property_value_hierarcial(props, row, col, TB_CPROP_CONT_FG_COLOR); | |
unsigned bg_color_number = get_cell_property_value_hierarcial(props, row, col, TB_CPROP_CONT_BG_COLOR); | |
reset_style_tag[0] = '\0'; | |
if (text_style < (1U << n_styles)) { | |
for (size_t i = 0; i < n_styles; ++i) { | |
if (text_style & (1 << i)) { | |
strcat(reset_style_tag, reset_text_styles[i]); | |
} | |
} | |
} else { | |
goto error; | |
} | |
if (fg_color_number < n_fg_colors) { | |
strcat(reset_style_tag, reset_fg_colors[fg_color_number]); | |
} else { | |
goto error; | |
} | |
if (bg_color_number < n_bg_colors) { | |
strcat(reset_style_tag, reset_bg_colors[bg_color_number]); | |
} else { | |
goto error; | |
} | |
return; | |
error: | |
// shouldn't be here | |
assert(0); | |
reset_style_tag[0] = '\0'; | |
return; | |
} | |
struct table_cell_props g_default_cell_properties = { | |
TB_ANY_ROW, /* cell_row */ | |
TB_ANY_COLUMN, /* cell_col */ | |
/* properties */ | |
TB_CPROP_MIN_WIDTH | TB_CPROP_TEXT_ALIGN | TB_CPROP_TOP_PADDING | |
| TB_CPROP_BOTTOM_PADDING | TB_CPROP_LETB_PADDING | TB_CPROP_RIGHT_PADDING | |
| TB_CPROP_EMPTY_STR_HEIGHT | TB_CPROP_CONT_FG_COLOR | TB_CPROP_CELL_BG_COLOR | |
| TB_CPROP_CONT_BG_COLOR | TB_CPROP_CELL_TEXT_STYLE | TB_CPROP_CONT_TEXT_STYLE, | |
0, /* col_min_width */ | |
TB_ALIGNED_LEFT, /* align */ | |
0, /* cell_padding_top */ | |
0, /* cell_padding_bottom */ | |
1, /* cell_padding_left */ | |
1, /* cell_padding_right */ | |
1, /* cell_empty_string_height */ | |
TB_ROW_COMMON, /* row_type */ | |
TB_COLOR_DEFAULT, /* content_fg_color_number */ | |
TB_COLOR_DEFAULT, /* content_bg_color_number */ | |
TB_COLOR_DEFAULT, /* cell_bg_color_number */ | |
TB_TSTYLE_DEFAULT, /* cell_text_style */ | |
TB_TSTYLE_DEFAULT, /* content_text_style */ | |
}; | |
static int get_prop_value_if_exists_otherwise_default(const struct table_cell_props *cell_opts, uint32_t property) | |
{ | |
if (cell_opts == NULL || !PROP_IS_SET(cell_opts->properties, property)) { | |
cell_opts = &g_default_cell_properties; | |
} | |
switch (property) { | |
case TB_CPROP_MIN_WIDTH: | |
return cell_opts->col_min_width; | |
case TB_CPROP_TEXT_ALIGN: | |
return cell_opts->align; | |
case TB_CPROP_TOP_PADDING: | |
return cell_opts->cell_padding_top; | |
case TB_CPROP_BOTTOM_PADDING: | |
return cell_opts->cell_padding_bottom; | |
case TB_CPROP_LETB_PADDING: | |
return cell_opts->cell_padding_left; | |
case TB_CPROP_RIGHT_PADDING: | |
return cell_opts->cell_padding_right; | |
case TB_CPROP_EMPTY_STR_HEIGHT: | |
return cell_opts->cell_empty_string_height; | |
case TB_CPROP_ROW_TYPE: | |
return cell_opts->row_type; | |
case TB_CPROP_CONT_FG_COLOR: | |
return cell_opts->content_fg_color_number; | |
case TB_CPROP_CONT_BG_COLOR: | |
return cell_opts->content_bg_color_number; | |
case TB_CPROP_CELL_BG_COLOR: | |
return cell_opts->cell_bg_color_number; | |
case TB_CPROP_CELL_TEXT_STYLE: | |
return cell_opts->cell_text_style; | |
case TB_CPROP_CONT_TEXT_STYLE: | |
return cell_opts->content_text_style; | |
default: | |
/* todo: implement later */ | |
exit(333); | |
} | |
} | |
TB_INTERNAL | |
table_cell_prop_container_t *create_cell_prop_container(void) | |
{ | |
table_cell_prop_container_t *ret = create_vector(sizeof(table_cell_props_t), DEFAULT_VECTOR_CAPACITY); | |
return ret; | |
} | |
TB_INTERNAL | |
void destroy_cell_prop_container(table_cell_prop_container_t *cont) | |
{ | |
if (cont) | |
destroy_vector(cont); | |
} | |
TB_INTERNAL | |
const table_cell_props_t *cget_cell_prop(const table_cell_prop_container_t *cont, size_t row, size_t col) | |
{ | |
assert(cont); | |
size_t sz = vector_size(cont); | |
size_t i = 0; | |
for (i = 0; i < sz; ++i) { | |
const table_cell_props_t *opt = (const table_cell_props_t *)vector_at_c(cont, i); | |
if (opt->cell_row == row && opt->cell_col == col) | |
return opt; | |
} | |
return NULL; | |
} | |
TB_INTERNAL | |
table_cell_props_t *get_cell_prop_and_create_if_not_exists(table_cell_prop_container_t *cont, size_t row, size_t col) | |
{ | |
assert(cont); | |
size_t sz = vector_size(cont); | |
size_t i = 0; | |
for (i = 0; i < sz; ++i) { | |
table_cell_props_t *opt = (table_cell_props_t *)vector_at(cont, i); | |
if (opt->cell_row == row && opt->cell_col == col) | |
return opt; | |
} | |
table_cell_props_t opt; | |
if (row == TB_ANY_ROW && col == TB_ANY_COLUMN) | |
memcpy(&opt, &g_default_cell_properties, sizeof(table_cell_props_t)); | |
else | |
memset(&opt, 0, sizeof(table_cell_props_t)); | |
opt.cell_row = row; | |
opt.cell_col = col; | |
if (TB_IS_SUCCESS(vector_push(cont, &opt))) { | |
return (table_cell_props_t *)vector_at(cont, sz); | |
} | |
return NULL; | |
} | |
TB_INTERNAL | |
int get_cell_property_value_hierarcial(const table_table_properties_t *propertiess, size_t row, size_t column, uint32_t property) | |
{ | |
assert(propertiess); | |
size_t row_origin = row; | |
const table_cell_props_t *opt = NULL; | |
if (propertiess->cell_properties != NULL) { | |
while (1) { | |
opt = cget_cell_prop(propertiess->cell_properties, row, column); | |
if (opt != NULL && PROP_IS_SET(opt->properties, property)) | |
break; | |
if (row != TB_ANY_ROW && column != TB_ANY_COLUMN) { | |
row = TB_ANY_ROW; | |
continue; | |
} else if (row == TB_ANY_ROW && column != TB_ANY_COLUMN) { | |
row = row_origin; | |
column = TB_ANY_COLUMN; | |
continue; | |
} else if (row != TB_ANY_ROW && column == TB_ANY_COLUMN) { | |
row = TB_ANY_ROW; | |
column = TB_ANY_COLUMN; | |
continue; | |
} | |
opt = NULL; | |
break; | |
} | |
} | |
return get_prop_value_if_exists_otherwise_default(opt, property); | |
} | |
static table_status_t set_cell_property_impl(table_cell_props_t *opt, uint32_t property, int value) | |
{ | |
assert(opt); | |
PROP_SET(opt->properties, property); | |
if (PROP_IS_SET(property, TB_CPROP_MIN_WIDTH)) { | |
CHECK_NOT_NEGATIVE(value); | |
opt->col_min_width = value; | |
} else if (PROP_IS_SET(property, TB_CPROP_TEXT_ALIGN)) { | |
opt->align = (enum ft_text_alignment)value; | |
} else if (PROP_IS_SET(property, TB_CPROP_TOP_PADDING)) { | |
CHECK_NOT_NEGATIVE(value); | |
opt->cell_padding_top = value; | |
} else if (PROP_IS_SET(property, TB_CPROP_BOTTOM_PADDING)) { | |
CHECK_NOT_NEGATIVE(value); | |
opt->cell_padding_bottom = value; | |
} else if (PROP_IS_SET(property, TB_CPROP_LETB_PADDING)) { | |
CHECK_NOT_NEGATIVE(value); | |
opt->cell_padding_left = value; | |
} else if (PROP_IS_SET(property, TB_CPROP_RIGHT_PADDING)) { | |
CHECK_NOT_NEGATIVE(value); | |
opt->cell_padding_right = value; | |
} else if (PROP_IS_SET(property, TB_CPROP_EMPTY_STR_HEIGHT)) { | |
CHECK_NOT_NEGATIVE(value); | |
opt->cell_empty_string_height = value; | |
} else if (PROP_IS_SET(property, TB_CPROP_ROW_TYPE)) { | |
opt->row_type = (enum ft_row_type)value; | |
} else if (PROP_IS_SET(property, TB_CPROP_CONT_FG_COLOR)) { | |
opt->content_fg_color_number = value; | |
} else if (PROP_IS_SET(property, TB_CPROP_CONT_BG_COLOR)) { | |
opt->content_bg_color_number = value; | |
} else if (PROP_IS_SET(property, TB_CPROP_CELL_BG_COLOR)) { | |
opt->cell_bg_color_number = value; | |
} else if (PROP_IS_SET(property, TB_CPROP_CELL_TEXT_STYLE)) { | |
enum ft_text_style v = (enum ft_text_style)value; | |
if (v == TB_TSTYLE_DEFAULT) { | |
opt->cell_text_style = TB_TSTYLE_DEFAULT; | |
} else { | |
opt->cell_text_style |= v; | |
} | |
} else if (PROP_IS_SET(property, TB_CPROP_CONT_TEXT_STYLE)) { | |
enum ft_text_style v = (enum ft_text_style)value; | |
if (v == TB_TSTYLE_DEFAULT) { | |
opt->content_text_style = v; | |
} else { | |
opt->content_text_style |= v; | |
} | |
} | |
return TB_SUCCESS; | |
table_fail: | |
return TB_EINVAL; | |
} | |
TB_INTERNAL | |
table_status_t set_cell_property(table_cell_prop_container_t *cont, size_t row, size_t col, uint32_t property, int value) | |
{ | |
table_cell_props_t *opt = get_cell_prop_and_create_if_not_exists(cont, row, col); | |
if (opt == NULL) | |
return TB_ERROR; | |
return set_cell_property_impl(opt, property, value); | |
/* | |
PROP_SET(opt->propertiess, property); | |
if (PROP_IS_SET(property, TB_CPROP_MIN_WIDTH)) { | |
opt->col_min_width = value; | |
} else if (PROP_IS_SET(property, TB_CPROP_TEXT_ALIGN)) { | |
opt->align = value; | |
} | |
return TB_SUCCESS; | |
*/ | |
} | |
TB_INTERNAL | |
table_status_t set_default_cell_property(uint32_t property, int value) | |
{ | |
return set_cell_property_impl(&g_default_cell_properties, property, value); | |
} | |
#define BASIC_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
"+", "-", "+", "+", \ | |
"|", "|", "|", \ | |
"\0", "\0", "\0", "\0", \ | |
"+", "-", "+", "+", \ | |
"+", "+", "+", "+", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
"+", "-", "+", "+", \ | |
"|", "|", "|", \ | |
"+", "-", "+", "+", \ | |
"+", "-", "+", "+", \ | |
"+", "+", "+", "+", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
"+", "-", "+", "+", \ | |
"+", "+", \ | |
}, \ | |
} | |
#define BASIC2_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
"+", "-", "+", "+", \ | |
"|", "|", "|", \ | |
"+", "-", "+", "+", \ | |
"+", "-", "+", "+", \ | |
"+", "+", "+", "+", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
"+", "-", "+", "+", \ | |
"|", "|", "|", \ | |
"+", "-", "+", "+", \ | |
"+", "-", "+", "+", \ | |
"+", "+", "+", "+", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
"+", "-", "+", "+", \ | |
"+", "+", \ | |
}, \ | |
} | |
#define SIMPLE_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
" ", " ", " ", " ", \ | |
" ", " ", " ", \ | |
"\0", "\0", "\0", "\0", \ | |
" ", " ", " ", " ", \ | |
" ", " ", " ", " ", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
" ", " ", " ", " ", \ | |
" ", " ", " ", \ | |
" ", "-", " ", " ", \ | |
" ", " ", " ", " ", \ | |
" ", "-", " ", "-", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
" ", "-", " ", " ", \ | |
" ", " ", \ | |
}, \ | |
} | |
#define PLAIN_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
" ", " ", " ", " ", \ | |
" ", " ", " ", \ | |
"\0", "\0", "\0", "\0", \ | |
" ", " ", " ", " ", \ | |
" ", " ", " ", " ", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
" ", "-", "-", " ", \ | |
" ", " ", " ", \ | |
" ", "-", "-", " ", \ | |
" ", "-", "-", " ", \ | |
" ", "-", " ", "-", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
" ", "-", "-", " ", \ | |
"-", "-", \ | |
}, \ | |
} | |
#define DOT_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
".", ".", ".", ".", \ | |
":", ":", ":", \ | |
"\0", "\0", "\0", "\0", \ | |
":", ".", ":", ":", \ | |
"+", ":", "+", ":", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
".", ".", ".", ".", \ | |
":", ":", ":", \ | |
":", ".", ":", ":", \ | |
":", ".", ":", ":", \ | |
"+", ".", "+", ".", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
":", ".", ":", ":", \ | |
":", ":", \ | |
}, \ | |
} | |
#define EMPTY_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
" ", " ", " ", " ", \ | |
" ", " ", " ", \ | |
"\0", "\0", "\0", "\0", \ | |
" ", " ", " ", " ", \ | |
" ", " ", " ", " ", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
" ", " ", " ", " ", \ | |
" ", " ", " ", \ | |
"\0", "\0", "\0", "\0", \ | |
" ", " ", " ", " ", \ | |
" ", " ", " ", " ", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
" ", " ", " ", " ", \ | |
" ", " ", \ | |
}, \ | |
} | |
#define SOLID_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
"┌", "─", "┬", "┐", \ | |
"│", "│", "│", \ | |
"", "", "", "", \ | |
"└", "─", "┴", "╯", \ | |
"│", "─", "│", "─", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
"┌", "─", "┬", "┐", \ | |
"│", "│", "│", \ | |
"├", "─", "┼", "┤", \ | |
"└", "─", "┴", "┘", \ | |
"┼", "┬", "┼", "┴", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
"├", "─", "┼", "┤", \ | |
"┬", "┴", \ | |
}, \ | |
} | |
#define SOLID_ROUND_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
"╭", "─", "┬", "╮", \ | |
"│", "│", "│", \ | |
"", "", "", "", \ | |
"╰", "─", "┴", "╯", \ | |
"│", "─", "│", "─", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
"╭", "─", "┬", "╮", \ | |
"│", "│", "│", \ | |
"├", "─", "┼", "┤", \ | |
"╰", "─", "┴", "╯", \ | |
"┼", "┬", "┼", "┴", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
"├", "─", "┼", "┤", \ | |
"┬", "┴", \ | |
}, \ | |
} | |
#define NICE_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
"╔", "═", "╦", "╗", \ | |
"║", "║", "║", \ | |
"", "", "", "", \ | |
"╚", "═", "╩", "╝", \ | |
"┣", "┻", "┣", "┳", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
"╔", "═", "╦", "╗", \ | |
"║", "║", "║", \ | |
"╠", "═", "╬", "╣", \ | |
"╚", "═", "╩", "╝", \ | |
"┣", "╦", "┣", "╩", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
"╟", "─", "╫", "╢", \ | |
"╥", "╨", \ | |
}, \ | |
} | |
#define DOUBLE_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
"╔", "═", "╦", "╗", \ | |
"║", "║", "║", \ | |
"", "", "", "", \ | |
"╚", "═", "╩", "╝", \ | |
"┣", "┻", "┣", "┳", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
"╔", "═", "╦", "╗", \ | |
"║", "║", "║", \ | |
"╠", "═", "╬", "╣", \ | |
"╚", "═", "╩", "╝", \ | |
"┣", "╦", "┣", "╩", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
"╠", "═", "╬", "╣", \ | |
"╦", "╩", \ | |
}, \ | |
} | |
#define DOUBLE2_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
"╔", "═", "╤", "╗", \ | |
"║", "│", "║", \ | |
"╟", "─", "┼", "╢", \ | |
"╚", "═", "╧", "╝", \ | |
"├", "┬", "┤", "┴", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
"╔", "═", "╤", "╗", \ | |
"║", "│", "║", \ | |
"╠", "═", "╪", "╣", \ | |
"╚", "═", "╧", "╝", \ | |
"├", "╤", "┤", "╧", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
"╠", "═", "╪", "╣", \ | |
"╤", "╧", \ | |
}, \ | |
} | |
#define BOLD_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
"┏", "━", "┳", "┓", \ | |
"┃", "┃", "┃", \ | |
"", "", "", "", \ | |
"┗", "━", "┻", "┛", \ | |
"┣", "┻", "┣", "┳", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
"┏", "━", "┳", "┓", \ | |
"┃", "┃", "┃", \ | |
"┣", "━", "╋", "┫", \ | |
"┗", "━", "┻", "┛", \ | |
"┣", "┳", "┣", "┻", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
"┣", "━", "╋", "┫", \ | |
"┳", "┻", \ | |
}, \ | |
} | |
#define BOLD2_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
"┏", "━", "┯", "┓", \ | |
"┃", "│", "┃", \ | |
"┠", "─", "┼", "┨", \ | |
"┗", "━", "┷", "┛", \ | |
"┣", "┬", "┣", "┴", \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
"┏", "━", "┯", "┓", \ | |
"┃", "│", "┃", \ | |
"┣", "━", "┿", "┫", \ | |
"┗", "━", "┷", "┛", \ | |
"┣", "┯", "┣", "┷", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
"┣", "━", "┿", "┫", \ | |
"┯", "┷", \ | |
}, \ | |
} | |
#define FRAME_STYLE { \ | |
/* border_chars */ \ | |
{ \ | |
"▛", "▀", "▀", "▜", \ | |
"▌", "┃", "▐", \ | |
"", "", "", "", \ | |
"▙", "▄", "▄", "▟", \ | |
"┣", "━", "┣", "━" \ | |
}, \ | |
/* header_border_chars */ \ | |
{ \ | |
"▛", "▀", "▀", "▜", \ | |
"▌", "┃", "▐", \ | |
"▌", "━", "╋", "▐", \ | |
"▙", "▄", "▄", "▟", \ | |
"┣", "━", "┣", "━", \ | |
}, \ | |
/* separator_chars */ \ | |
{ \ | |
"▌", "━", "╋", "▐", \ | |
"╋", "╋", \ | |
}, \ | |
} | |
struct table_border_style TABLE_BASIC_STYLE = BASIC_STYLE; | |
struct table_border_style TABLE_BASIC2_STYLE = BASIC2_STYLE; | |
struct table_border_style TABLE_SIMPLE_STYLE = SIMPLE_STYLE; | |
struct table_border_style TABLE_PLAIN_STYLE = PLAIN_STYLE; | |
struct table_border_style TABLE_DOT_STYLE = DOT_STYLE; | |
struct table_border_style TABLE_EMPTY_STYLE = EMPTY_STYLE; | |
struct table_border_style TABLE_SOLID_STYLE = SOLID_STYLE; | |
struct table_border_style TABLE_SOLID_ROUND_STYLE = SOLID_ROUND_STYLE; | |
struct table_border_style TABLE_NICE_STYLE = NICE_STYLE; | |
struct table_border_style TABLE_DOUBLE_STYLE = DOUBLE_STYLE; | |
struct table_border_style TABLE_DOUBLE2_STYLE = DOUBLE2_STYLE; | |
struct table_border_style TABLE_BOLD_STYLE = BOLD_STYLE; | |
struct table_border_style TABLE_BOLD2_STYLE = BOLD2_STYLE; | |
struct table_border_style TABLE_FRAME_STYLE = FRAME_STYLE; | |
table_entire_table_properties_t g_entire_table_properties = { | |
0, /* left_margin */ | |
0, /* top_margin */ | |
0, /* right_margin */ | |
0, /* bottom_margin */ | |
}; | |
static table_status_t set_entire_table_property_internal(table_entire_table_properties_t *properties, uint32_t property, int value) | |
{ | |
assert(properties); | |
CHECK_NOT_NEGATIVE(value); | |
if (PROP_IS_SET(property, TB_TPROP_LETB_MARGIN)) { | |
properties->left_margin = value; | |
} else if (PROP_IS_SET(property, TB_TPROP_TOP_MARGIN)) { | |
properties->top_margin = value; | |
} else if (PROP_IS_SET(property, TB_TPROP_RIGHT_MARGIN)) { | |
properties->right_margin = value; | |
} else if (PROP_IS_SET(property, TB_TPROP_BOTTOM_MARGIN)) { | |
properties->bottom_margin = value; | |
} else { | |
return TB_EINVAL; | |
} | |
return TB_SUCCESS; | |
table_fail: | |
return TB_EINVAL; | |
} | |
TB_INTERNAL | |
table_status_t set_entire_table_property(table_table_properties_t *table_properties, uint32_t property, int value) | |
{ | |
assert(table_properties); | |
return set_entire_table_property_internal(&table_properties->entire_table_properties, property, value); | |
} | |
TB_INTERNAL | |
table_status_t set_default_entire_table_property(uint32_t property, int value) | |
{ | |
return set_entire_table_property_internal(&g_entire_table_properties, property, value); | |
} | |
TB_INTERNAL | |
size_t max_border_elem_strlen(struct table_table_properties *properties) | |
{ | |
assert(properties); | |
size_t result = 1; | |
int i = 0; | |
for (i = 0; i < BorderItemPosSize; ++i) { | |
result = MAX(result, strlen(properties->border_style.border_chars[i])); | |
} | |
i = 0; | |
for (i = 0; i < BorderItemPosSize; ++i) { | |
result = MAX(result, strlen(properties->border_style.header_border_chars[i])); | |
} | |
i = 0; | |
for (i = 0; i < SepratorItemPosSize; ++i) { | |
result = MAX(result, strlen(properties->border_style.separator_chars[i])); | |
} | |
return result; | |
} | |
table_table_properties_t g_table_properties = { | |
/* border_style */ | |
BASIC_STYLE, | |
NULL, /* cell_properties */ | |
/* entire_table_properties */ | |
{ | |
0, /* left_margin */ | |
0, /* top_margin */ | |
0, /* right_margin */ | |
0 /* bottom_margin */ | |
} | |
}; | |
TB_INTERNAL | |
table_table_properties_t *create_table_properties(void) | |
{ | |
table_table_properties_t *properties = (table_table_properties_t *)F_CALLOC(sizeof(table_table_properties_t), 1); | |
if (properties == NULL) { | |
return NULL; | |
} | |
memcpy(properties, &g_table_properties, sizeof(table_table_properties_t)); | |
properties->cell_properties = create_cell_prop_container(); | |
if (properties->cell_properties == NULL) { | |
destroy_table_properties(properties); | |
return NULL; | |
} | |
memcpy(&properties->entire_table_properties, &g_entire_table_properties, sizeof(table_entire_table_properties_t)); | |
return properties; | |
} | |
TB_INTERNAL | |
void destroy_table_properties(table_table_properties_t *properties) | |
{ | |
if (properties == NULL) | |
return; | |
if (properties->cell_properties != NULL) { | |
destroy_cell_prop_container(properties->cell_properties); | |
} | |
F_FREE(properties); | |
} | |
static | |
table_cell_prop_container_t *copy_cell_properties(table_cell_prop_container_t *cont) | |
{ | |
table_cell_prop_container_t *result = create_cell_prop_container(); | |
if (result == NULL) | |
return NULL; | |
size_t sz = vector_size(cont); | |
for (size_t i = 0; i < sz; ++i) { | |
table_cell_props_t *opt = (table_cell_props_t *)vector_at(cont, i); | |
if (TB_IS_ERROR(vector_push(result, opt))) { | |
destroy_cell_prop_container(result); | |
return NULL; | |
} | |
} | |
return result; | |
} | |
TB_INTERNAL | |
table_table_properties_t *copy_table_properties(const table_table_properties_t *properties) | |
{ | |
table_table_properties_t *new_opt = create_table_properties(); | |
if (new_opt == NULL) | |
return NULL; | |
destroy_vector(new_opt->cell_properties); | |
new_opt->cell_properties = copy_cell_properties(properties->cell_properties); | |
if (new_opt->cell_properties == NULL) { | |
destroy_table_properties(new_opt); | |
return NULL; | |
} | |
memcpy(&new_opt->border_style, &properties->border_style, sizeof(struct table_border_style)); | |
memcpy(&new_opt->entire_table_properties, | |
&properties->entire_table_properties, sizeof(table_entire_table_properties_t)); | |
return new_opt; | |
} | |
/******************************************************** | |
End of file "properties.c" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "row.c" | |
********************************************************/ | |
#include <assert.h> | |
#include <ctype.h> | |
/* #include "row.h" */ /* Commented by amalgamation script */ | |
/* #include "cell.h" */ /* Commented by amalgamation script */ | |
/* #include "string_buffer.h" */ /* Commented by amalgamation script */ | |
/* #include "vector.h" */ /* Commented by amalgamation script */ | |
struct table_row { | |
vector_t *cells; | |
/*enum ft_row_type type;*/ | |
}; | |
TB_INTERNAL | |
table_row_t *create_row(void) | |
{ | |
table_row_t *row = (table_row_t *)F_CALLOC(sizeof(table_row_t), 1); | |
if (row == NULL) | |
return NULL; | |
row->cells = create_vector(sizeof(table_cell_t *), DEFAULT_VECTOR_CAPACITY); | |
if (row->cells == NULL) { | |
F_FREE(row); | |
return NULL; | |
} | |
/* | |
row->is_header = F_FALSE; | |
row->type = TB_ROW_COMMON; | |
*/ | |
return row; | |
} | |
TB_INTERNAL | |
void destroy_row(table_row_t *row) | |
{ | |
if (row == NULL) | |
return; | |
if (row->cells) { | |
size_t cells_n = vector_size(row->cells); | |
for (size_t i = 0; i < cells_n; ++i) { | |
table_cell_t *cell = *(table_cell_t **)vector_at(row->cells, i); | |
destroy_cell(cell); | |
} | |
destroy_vector(row->cells); | |
} | |
F_FREE(row); | |
} | |
TB_INTERNAL | |
table_row_t *copy_row(table_row_t *row) | |
{ | |
assert(row); | |
table_row_t *result = create_row(); | |
if (result == NULL) | |
return NULL; | |
size_t cols_n = vector_size(row->cells); | |
for (size_t i = 0; i < cols_n; ++i) { | |
table_cell_t *cell = *(table_cell_t **)vector_at(row->cells, i); | |
table_cell_t *new_cell = copy_cell(cell); | |
if (new_cell == NULL) { | |
destroy_row(result); | |
return NULL; | |
} | |
vector_push(result->cells, &new_cell); | |
} | |
return result; | |
} | |
TB_INTERNAL | |
size_t columns_in_row(const table_row_t *row) | |
{ | |
if (row == NULL || row->cells == NULL) | |
return 0; | |
return vector_size(row->cells); | |
} | |
static | |
table_cell_t *get_cell_implementation(table_row_t *row, size_t col, enum PolicyOnNull policy) | |
{ | |
if (row == NULL || row->cells == NULL) { | |
return NULL; | |
} | |
switch (policy) { | |
case DoNotCreate: | |
if (col < columns_in_row(row)) { | |
return *(table_cell_t **)vector_at(row->cells, col); | |
} | |
return NULL; | |
break; | |
case Create: | |
while (col >= columns_in_row(row)) { | |
table_cell_t *new_cell = create_cell(); | |
if (new_cell == NULL) | |
return NULL; | |
if (TB_IS_ERROR(vector_push(row->cells, &new_cell))) { | |
destroy_cell(new_cell); | |
return NULL; | |
} | |
} | |
return *(table_cell_t **)vector_at(row->cells, col); | |
break; | |
} | |
return NULL; | |
} | |
TB_INTERNAL | |
table_cell_t *get_cell(table_row_t *row, size_t col) | |
{ | |
return get_cell_implementation(row, col, DoNotCreate); | |
} | |
TB_INTERNAL | |
const table_cell_t *get_cell_c(const table_row_t *row, size_t col) | |
{ | |
return get_cell((table_row_t *)row, col); | |
} | |
TB_INTERNAL | |
table_cell_t *get_cell_and_create_if_not_exists(table_row_t *row, size_t col) | |
{ | |
return get_cell_implementation(row, col, Create); | |
} | |
TB_INTERNAL | |
table_status_t swap_row(table_row_t *cur_row, table_row_t *ins_row, size_t pos) | |
{ | |
assert(cur_row); | |
assert(ins_row); | |
size_t cur_sz = vector_size(cur_row->cells); | |
if (cur_sz == 0 && pos == 0) { | |
table_row_t tmp; | |
memcpy(&tmp, cur_row, sizeof(table_row_t)); | |
memcpy(cur_row, ins_row, sizeof(table_row_t)); | |
memcpy(ins_row, &tmp, sizeof(table_row_t)); | |
return TB_SUCCESS; | |
} | |
return vector_swap(cur_row->cells, ins_row->cells, pos); | |
} | |
TB_INTERNAL | |
size_t group_cell_number(const table_row_t *row, size_t master_cell_col) | |
{ | |
assert(row); | |
const table_cell_t *master_cell = get_cell_c(row, master_cell_col); | |
if (master_cell == NULL) | |
return 0; | |
if (get_cell_type(master_cell) != GroupMasterCell) | |
return 1; | |
size_t total_cols = vector_size(row->cells); | |
size_t slave_col = master_cell_col + 1; | |
while (slave_col < total_cols) { | |
const table_cell_t *cell = get_cell_c(row, slave_col); | |
if (cell && get_cell_type(cell) == GroupSlaveCell) { | |
++slave_col; | |
} else { | |
break; | |
} | |
} | |
return slave_col - master_cell_col; | |
} | |
TB_INTERNAL | |
int get_row_cell_types(const table_row_t *row, enum CellType *types, size_t types_sz) | |
{ | |
assert(row); | |
assert(types); | |
size_t i = 0; | |
for (i = 0; i < types_sz; ++i) { | |
const table_cell_t *cell = get_cell_c(row, i); | |
if (cell) { | |
types[i] = get_cell_type(cell); | |
} else { | |
types[i] = CommonCell; | |
} | |
} | |
return TB_SUCCESS; | |
} | |
TB_INTERNAL | |
table_status_t row_set_cell_span(table_row_t *row, size_t cell_column, size_t hor_span) | |
{ | |
assert(row); | |
if (hor_span < 2) | |
return TB_EINVAL; | |
table_cell_t *main_cell = get_cell_and_create_if_not_exists(row, cell_column); | |
if (main_cell == NULL) { | |
return TB_ERROR; | |
} | |
set_cell_type(main_cell, GroupMasterCell); | |
--hor_span; | |
++cell_column; | |
while (hor_span) { | |
table_cell_t *slave_cell = get_cell_and_create_if_not_exists(row, cell_column); | |
if (slave_cell == NULL) { | |
return TB_ERROR; | |
} | |
set_cell_type(slave_cell, GroupSlaveCell); | |
--hor_span; | |
++cell_column; | |
} | |
return TB_SUCCESS; | |
} | |
TB_INTERNAL | |
int print_row_separator(char *buffer, size_t buffer_sz, | |
const size_t *col_width_arr, size_t cols, | |
const table_row_t *upper_row, const table_row_t *lower_row, | |
enum HorSeparatorPos separatorPos, | |
const separator_t *sep, const context_t *context) | |
{ | |
// int (*snprint_n_chars_)(char *, size_t, size_t, char) = snprint_n_chars; | |
int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; | |
assert(buffer); | |
assert(context); | |
const char *space_char = " "; | |
int status = -1; | |
/* Get cell types | |
* | |
* Regions above top row and below bottom row areconsidered full of virtual | |
* GroupSlaveCell cells | |
*/ | |
enum CellType *top_row_types = (enum CellType *)F_MALLOC(sizeof(enum CellType) * cols * 2); | |
if (top_row_types == NULL) { | |
return TB_MEMORY_ERROR; | |
} | |
enum CellType *bottom_row_types = top_row_types + cols; | |
if (upper_row) { | |
get_row_cell_types(upper_row, top_row_types, cols); | |
} else { | |
size_t i = 0; | |
for (i = 0; i < cols; ++i) | |
top_row_types[i] = GroupSlaveCell; | |
} | |
if (lower_row) { | |
get_row_cell_types(lower_row, bottom_row_types, cols); | |
} else { | |
size_t i = 0; | |
for (i = 0; i < cols; ++i) | |
bottom_row_types[i] = GroupSlaveCell; | |
} | |
int written = 0; | |
int tmp = 0; | |
enum ft_row_type lower_row_type = TB_ROW_COMMON; | |
if (lower_row != NULL) { | |
lower_row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row, TB_ANY_COLUMN, TB_CPROP_ROW_TYPE); | |
} | |
enum ft_row_type upper_row_type = TB_ROW_COMMON; | |
if (upper_row != NULL) { | |
upper_row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row - 1, TB_ANY_COLUMN, TB_CPROP_ROW_TYPE); | |
} | |
/* Row separator anatomy | |
* | |
* | C11 | C12 C13 | C14 C15 | | |
* L I I I IV I I IT I I I IB I I II I I R | |
* | C21 | C22 | C23 C24 C25 | | |
*/ | |
const char **L = NULL; | |
const char **I = NULL; | |
const char **IV = NULL; | |
const char **R = NULL; | |
const char **IT = NULL; | |
const char **IB = NULL; | |
const char **II = NULL; | |
typedef const char *(*border_chars_point_t)[BorderItemPosSize]; | |
const char *(*border_chars)[BorderItemPosSize] = NULL; | |
border_chars = (border_chars_point_t)&context->table_properties->border_style.border_chars; | |
if (upper_row_type == TB_ROW_HEADER || lower_row_type == TB_ROW_HEADER) { | |
border_chars = (border_chars_point_t)&context->table_properties->border_style.header_border_chars; | |
} | |
if (sep && sep->enabled) { | |
L = &(context->table_properties->border_style.separator_chars[LH_sip]); | |
I = &(context->table_properties->border_style.separator_chars[IH_sip]); | |
IV = &(context->table_properties->border_style.separator_chars[II_sip]); | |
R = &(context->table_properties->border_style.separator_chars[RH_sip]); | |
IT = &(context->table_properties->border_style.separator_chars[TI_sip]); | |
IB = &(context->table_properties->border_style.separator_chars[BI_sip]); | |
II = &(context->table_properties->border_style.separator_chars[IH_sip]); | |
if (lower_row == NULL) { | |
L = &(*border_chars)[BL_bip]; | |
R = &(*border_chars)[BR_bip]; | |
} else if (upper_row == NULL) { | |
L = &(*border_chars)[TL_bip]; | |
R = &(*border_chars)[TR_bip]; | |
} | |
} else { | |
switch (separatorPos) { | |
case TopSeparator: | |
L = &(*border_chars)[TL_bip]; | |
I = &(*border_chars)[TT_bip]; | |
IV = &(*border_chars)[TV_bip]; | |
R = &(*border_chars)[TR_bip]; | |
IT = &(*border_chars)[TV_bip]; | |
IB = &(*border_chars)[TV_bip]; | |
II = &(*border_chars)[TT_bip]; | |
break; | |
case InsideSeparator: | |
L = &(*border_chars)[LH_bip]; | |
I = &(*border_chars)[IH_bip]; | |
IV = &(*border_chars)[II_bip]; | |
R = &(*border_chars)[RH_bip]; | |
// IT = &(*border_chars)[TV_bip]; | |
// IB = &(*border_chars)[BV_bip]; | |
IT = &(*border_chars)[TI_bip]; | |
IB = &(*border_chars)[BI_bip]; | |
II = &(*border_chars)[IH_bip]; | |
break; | |
case BottomSeparator: | |
L = &(*border_chars)[BL_bip]; | |
I = &(*border_chars)[BB_bip]; | |
IV = &(*border_chars)[BV_bip]; | |
R = &(*border_chars)[BR_bip]; | |
IT = &(*border_chars)[BV_bip]; | |
IB = &(*border_chars)[BV_bip]; | |
II = &(*border_chars)[BB_bip]; | |
break; | |
default: | |
break; | |
} | |
} | |
size_t i = 0; | |
/* If all chars are not printable, skip line separator */ /* todo: add processing for wchar_t */ | |
// if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) { | |
// status = 0; | |
// goto clear; | |
// } | |
if ((strlen(*L) == 0 || (strlen(*L) == 1 && !isprint(**L))) | |
&& (strlen(*I) == 0 || (strlen(*I) == 1 && !isprint(**I))) | |
&& (strlen(*IV) == 0 || (strlen(*IV) == 1 && !isprint(**IV))) | |
&& (strlen(*R) == 0 || (strlen(*R) == 1 && !isprint(**R)))) { | |
status = 0; | |
goto clear; | |
} | |
/* Print left margin */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); | |
for (i = 0; i < cols; ++i) { | |
if (i == 0) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *L)); | |
} else { | |
if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell) | |
&& (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IV)); | |
} else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *II)); | |
} else if (top_row_types[i] == GroupSlaveCell) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IT)); | |
} else { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IB)); | |
} | |
} | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, col_width_arr[i], *I)); | |
} | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *R)); | |
/* Print right margin */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, "\n")); | |
status = written; | |
clear: | |
F_FREE(top_row_types); | |
return status; | |
} | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
int wprint_row_separator(wchar_t *buffer, size_t buffer_sz, | |
const size_t *col_width_arr, size_t cols, | |
const table_row_t *upper_row, const table_row_t *lower_row, | |
enum HorSeparatorPos separatorPos, const separator_t *sep, | |
const context_t *context) | |
{ | |
// int (*snprint_n_chars_)(wchar_t *, size_t, size_t, wchar_t) = wsnprint_n_chars; | |
int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; | |
assert(buffer); | |
assert(context); | |
const char *space_char = " "; | |
int status = -1; | |
/* Get cell types | |
* | |
* Regions above top row and below bottom row areconsidered full of virtual | |
* GroupSlaveCell cells | |
*/ | |
enum CellType *top_row_types = (enum CellType *)F_MALLOC(sizeof(enum CellType) * cols * 2); | |
if (top_row_types == NULL) { | |
return TB_MEMORY_ERROR; | |
} | |
enum CellType *bottom_row_types = top_row_types + cols; | |
if (upper_row) { | |
get_row_cell_types(upper_row, top_row_types, cols); | |
} else { | |
size_t i = 0; | |
for (i = 0; i < cols; ++i) | |
top_row_types[i] = GroupSlaveCell; | |
} | |
if (lower_row) { | |
get_row_cell_types(lower_row, bottom_row_types, cols); | |
} else { | |
size_t i = 0; | |
for (i = 0; i < cols; ++i) | |
bottom_row_types[i] = GroupSlaveCell; | |
} | |
int written = 0; | |
int tmp = 0; | |
enum ft_row_type lower_row_type = TB_ROW_COMMON; | |
if (lower_row != NULL) { | |
lower_row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row, TB_ANY_COLUMN, TB_CPROP_ROW_TYPE); | |
} | |
enum ft_row_type upper_row_type = TB_ROW_COMMON; | |
if (upper_row != NULL) { | |
upper_row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row - 1, TB_ANY_COLUMN, TB_CPROP_ROW_TYPE); | |
} | |
/* Row separator anatomy | |
* | |
* | C11 | C12 C13 | C14 C15 | | |
* L I I I IV I I IT I I I IB I I II I I R | |
* | C21 | C22 | C23 C24 C25 | | |
*/ | |
const char **L = NULL; | |
const char **I = NULL; | |
const char **IV = NULL; | |
const char **R = NULL; | |
const char **IT = NULL; | |
const char **IB = NULL; | |
const char **II = NULL; | |
typedef const char *(*border_chars_point_t)[BorderItemPosSize]; | |
const char *(*border_chars)[BorderItemPosSize] = NULL; | |
border_chars = (border_chars_point_t)&context->table_properties->border_style.border_chars; | |
if (upper_row_type == TB_ROW_HEADER || lower_row_type == TB_ROW_HEADER) { | |
border_chars = (border_chars_point_t)&context->table_properties->border_style.header_border_chars; | |
} | |
if (sep && sep->enabled) { | |
L = &(context->table_properties->border_style.separator_chars[LH_sip]); | |
I = &(context->table_properties->border_style.separator_chars[IH_sip]); | |
IV = &(context->table_properties->border_style.separator_chars[II_sip]); | |
R = &(context->table_properties->border_style.separator_chars[RH_sip]); | |
IT = &(context->table_properties->border_style.separator_chars[TI_sip]); | |
IB = &(context->table_properties->border_style.separator_chars[BI_sip]); | |
II = &(context->table_properties->border_style.separator_chars[IH_sip]); | |
if (lower_row == NULL) { | |
L = &(*border_chars)[BL_bip]; | |
R = &(*border_chars)[BR_bip]; | |
} else if (upper_row == NULL) { | |
L = &(*border_chars)[TL_bip]; | |
R = &(*border_chars)[TR_bip]; | |
} | |
} else { | |
switch (separatorPos) { | |
case TopSeparator: | |
L = &(*border_chars)[TL_bip]; | |
I = &(*border_chars)[TT_bip]; | |
IV = &(*border_chars)[TV_bip]; | |
R = &(*border_chars)[TR_bip]; | |
IT = &(*border_chars)[TV_bip]; | |
IB = &(*border_chars)[TV_bip]; | |
II = &(*border_chars)[TT_bip]; | |
break; | |
case InsideSeparator: | |
L = &(*border_chars)[LH_bip]; | |
I = &(*border_chars)[IH_bip]; | |
IV = &(*border_chars)[II_bip]; | |
R = &(*border_chars)[RH_bip]; | |
// IT = &(*border_chars)[TV_bip]; | |
// IB = &(*border_chars)[BV_bip]; | |
IT = &(*border_chars)[TI_bip]; | |
IB = &(*border_chars)[BI_bip]; | |
II = &(*border_chars)[IH_bip]; | |
break; | |
case BottomSeparator: | |
L = &(*border_chars)[BL_bip]; | |
I = &(*border_chars)[BB_bip]; | |
IV = &(*border_chars)[BV_bip]; | |
R = &(*border_chars)[BR_bip]; | |
IT = &(*border_chars)[BV_bip]; | |
IB = &(*border_chars)[BV_bip]; | |
II = &(*border_chars)[BB_bip]; | |
break; | |
default: | |
break; | |
} | |
} | |
size_t i = 0; | |
/* If all chars are not printable, skip line separator */ /* todo: add processing for wchar_t */ | |
// if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) { | |
// status = 0; | |
// goto clear; | |
// } | |
if ((strlen(*L) == 0 || (strlen(*L) == 1 && !isprint(**L))) | |
&& (strlen(*I) == 0 || (strlen(*I) == 1 && !isprint(**I))) | |
&& (strlen(*IV) == 0 || (strlen(*IV) == 1 && !isprint(**IV))) | |
&& (strlen(*R) == 0 || (strlen(*R) == 1 && !isprint(**R)))) { | |
status = 0; | |
goto clear; | |
} | |
/* Print left margin */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); | |
for (i = 0; i < cols; ++i) { | |
if (i == 0) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *L)); | |
} else { | |
if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell) | |
&& (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IV)); | |
} else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *II)); | |
} else if (top_row_types[i] == GroupSlaveCell) { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IT)); | |
} else { | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IB)); | |
} | |
} | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, col_width_arr[i], *I)); | |
} | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *R)); | |
/* Print right margin */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, "\n")); | |
status = written; | |
clear: | |
F_FREE(top_row_types); | |
return status; | |
} | |
#endif | |
TB_INTERNAL | |
table_row_t *create_row_from_string(const char *str) | |
{ | |
typedef char char_type; | |
char_type *(*strdup_)(const char_type * str) = F_STRDUP; | |
const char_type zero_char = '\0'; | |
table_status_t (*fill_cell_from_string_)(table_cell_t *cell, const char *str) = fill_cell_from_string; | |
const char_type *const zero_string = ""; | |
#define STRCHR strchr | |
char_type *pos = NULL; | |
char_type *base_pos = NULL; | |
unsigned int number_of_separators = 0; | |
table_row_t *row = create_row(); | |
if (row == NULL) | |
return NULL; | |
if (str == NULL) | |
return row; | |
char_type *str_copy = strdup_(str); | |
if (str_copy == NULL) | |
goto clear; | |
pos = str_copy; | |
base_pos = str_copy; | |
number_of_separators = 0; | |
while (*pos) { | |
pos = STRCHR(pos, TABLE_COL_SEPARATOR); | |
if (pos != NULL) { | |
*(pos) = zero_char; | |
++pos; | |
number_of_separators++; | |
} | |
table_cell_t *cell = create_cell(); | |
if (cell == NULL) | |
goto clear; | |
int status = fill_cell_from_string_(cell, base_pos); | |
if (TB_IS_ERROR(status)) { | |
destroy_cell(cell); | |
goto clear; | |
} | |
status = vector_push(row->cells, &cell); | |
if (TB_IS_ERROR(status)) { | |
destroy_cell(cell); | |
goto clear; | |
} | |
if (pos == NULL) | |
break; | |
base_pos = pos; | |
} | |
/* special case if in format string last cell is empty */ | |
while (vector_size(row->cells) < (number_of_separators + 1)) { | |
table_cell_t *cell = create_cell(); | |
if (cell == NULL) | |
goto clear; | |
int status = fill_cell_from_string_(cell, zero_string); | |
if (TB_IS_ERROR(status)) { | |
destroy_cell(cell); | |
goto clear; | |
} | |
status = vector_push(row->cells, &cell); | |
if (TB_IS_ERROR(status)) { | |
destroy_cell(cell); | |
goto clear; | |
} | |
} | |
F_FREE(str_copy); | |
return row; | |
clear: | |
destroy_row(row); | |
F_FREE(str_copy); | |
return NULL; | |
#undef STRCHR | |
} | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
table_row_t *create_row_from_wstring(const wchar_t *str) | |
{ | |
typedef wchar_t char_type; | |
char_type *(*strdup_)(const char_type * str) = F_WCSDUP; | |
const char_type zero_char = L'\0'; | |
table_status_t (*fill_cell_from_string_)(table_cell_t *cell, const wchar_t *str) = fill_cell_from_wstring; | |
const char_type *const zero_string = L""; | |
#define STRCHR wcschr | |
char_type *pos = NULL; | |
char_type *base_pos = NULL; | |
unsigned int number_of_separators = 0; | |
table_row_t *row = create_row(); | |
if (row == NULL) | |
return NULL; | |
if (str == NULL) | |
return row; | |
char_type *str_copy = strdup_(str); | |
if (str_copy == NULL) | |
goto clear; | |
pos = str_copy; | |
base_pos = str_copy; | |
number_of_separators = 0; | |
while (*pos) { | |
pos = STRCHR(pos, TABLE_COL_SEPARATOR); | |
if (pos != NULL) { | |
*(pos) = zero_char; | |
++pos; | |
number_of_separators++; | |
} | |
table_cell_t *cell = create_cell(); | |
if (cell == NULL) | |
goto clear; | |
int status = fill_cell_from_string_(cell, base_pos); | |
if (TB_IS_ERROR(status)) { | |
destroy_cell(cell); | |
goto clear; | |
} | |
status = vector_push(row->cells, &cell); | |
if (TB_IS_ERROR(status)) { | |
destroy_cell(cell); | |
goto clear; | |
} | |
if (pos == NULL) | |
break; | |
base_pos = pos; | |
} | |
/* special case if in format string last cell is empty */ | |
while (vector_size(row->cells) < (number_of_separators + 1)) { | |
table_cell_t *cell = create_cell(); | |
if (cell == NULL) | |
goto clear; | |
int status = fill_cell_from_string_(cell, zero_string); | |
if (TB_IS_ERROR(status)) { | |
destroy_cell(cell); | |
goto clear; | |
} | |
status = vector_push(row->cells, &cell); | |
if (TB_IS_ERROR(status)) { | |
destroy_cell(cell); | |
goto clear; | |
} | |
} | |
F_FREE(str_copy); | |
return row; | |
clear: | |
destroy_row(row); | |
F_FREE(str_copy); | |
return NULL; | |
#undef STRCHR | |
} | |
#endif | |
TB_INTERNAL | |
table_row_t *create_row_from_fmt_string(const char *fmt, va_list *va_args) | |
{ | |
#define VSNPRINTF vsnprintf | |
#define STR_FILED cstr | |
#define CREATE_ROW_FROM_STRING create_row_from_string | |
#define NUMBER_OF_COLUMNS_IN_FORMAT_STRING number_of_columns_in_format_string | |
#define FILL_CELL_FROM_STRING fill_cell_from_string | |
string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CharBuf); | |
if (buffer == NULL) | |
return NULL; | |
size_t cols_origin = NUMBER_OF_COLUMNS_IN_FORMAT_STRING(fmt); | |
size_t cols = 0; | |
while (1) { | |
va_list va; | |
va_copy(va, *va_args); | |
int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_capacity(buffer), fmt, va); | |
va_end(va); | |
/* If error encountered */ | |
if (virtual_sz < 0) | |
goto clear; | |
/* Successful write */ | |
if ((size_t)virtual_sz < string_buffer_capacity(buffer)) | |
break; | |
/* Otherwise buffer was too small, so incr. buffer size ant try again. */ | |
if (!TB_IS_SUCCESS(realloc_string_buffer_without_copy(buffer))) | |
goto clear; | |
} | |
cols = NUMBER_OF_COLUMNS_IN_FORMAT_STRING(buffer->str.STR_FILED); | |
if (cols == cols_origin) { | |
table_row_t *row = CREATE_ROW_FROM_STRING(buffer->str.STR_FILED); | |
if (row == NULL) { | |
goto clear; | |
} | |
destroy_string_buffer(buffer); | |
return row; | |
} | |
if (cols_origin == 1) { | |
table_row_t *row = create_row(); | |
if (row == NULL) { | |
goto clear; | |
} | |
table_cell_t *cell = get_cell_and_create_if_not_exists(row, 0); | |
if (cell == NULL) { | |
destroy_row(row); | |
goto clear; | |
} | |
table_status_t result = FILL_CELL_FROM_STRING(cell, buffer->str.STR_FILED); | |
if (TB_IS_ERROR(result)) { | |
destroy_row(row); | |
goto clear; | |
} | |
destroy_string_buffer(buffer); | |
return row; | |
} | |
/* | |
* todo: add processing of cols != cols_origin in a general way | |
* (when cols_origin != 1). | |
*/ | |
clear: | |
destroy_string_buffer(buffer); | |
return NULL; | |
#undef VSNPRINTF | |
#undef STR_FILED | |
#undef CREATE_ROW_FROM_STRING | |
#undef NUMBER_OF_COLUMNS_IN_FORMAT_STRING | |
#undef FILL_CELL_FROM_STRING | |
} | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
table_row_t *create_row_from_fmt_wstring(const wchar_t *fmt, va_list *va_args) | |
{ | |
#define VSNPRINTF vswprintf | |
#define STR_FILED wstr | |
#define CREATE_ROW_FROM_STRING create_row_from_wstring | |
#define NUMBER_OF_COLUMNS_IN_FORMAT_STRING number_of_columns_in_format_wstring | |
#define FILL_CELL_FROM_STRING fill_cell_from_wstring | |
string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CharBuf); | |
if (buffer == NULL) | |
return NULL; | |
size_t cols_origin = NUMBER_OF_COLUMNS_IN_FORMAT_STRING(fmt); | |
size_t cols = 0; | |
while (1) { | |
va_list va; | |
va_copy(va, *va_args); | |
int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_capacity(buffer), fmt, va); | |
va_end(va); | |
/* If error encountered */ | |
if (virtual_sz < 0) | |
goto clear; | |
/* Successful write */ | |
if ((size_t)virtual_sz < string_buffer_capacity(buffer)) | |
break; | |
/* Otherwise buffer was too small, so incr. buffer size ant try again. */ | |
if (!TB_IS_SUCCESS(realloc_string_buffer_without_copy(buffer))) | |
goto clear; | |
} | |
cols = NUMBER_OF_COLUMNS_IN_FORMAT_STRING(buffer->str.STR_FILED); | |
if (cols == cols_origin) { | |
table_row_t *row = CREATE_ROW_FROM_STRING(buffer->str.STR_FILED); | |
if (row == NULL) { | |
goto clear; | |
} | |
destroy_string_buffer(buffer); | |
return row; | |
} | |
if (cols_origin == 1) { | |
table_row_t *row = create_row(); | |
if (row == NULL) { | |
goto clear; | |
} | |
table_cell_t *cell = get_cell_and_create_if_not_exists(row, 0); | |
if (cell == NULL) { | |
destroy_row(row); | |
goto clear; | |
} | |
table_status_t result = FILL_CELL_FROM_STRING(cell, buffer->str.STR_FILED); | |
if (TB_IS_ERROR(result)) { | |
destroy_row(row); | |
goto clear; | |
} | |
destroy_string_buffer(buffer); | |
return row; | |
} | |
/* | |
* todo: add processing of cols != cols_origin in a general way | |
* (when cols_origin != 1). | |
*/ | |
clear: | |
destroy_string_buffer(buffer); | |
return NULL; | |
#undef VSNPRINTF | |
#undef STR_FILED | |
#undef CREATE_ROW_FROM_STRING | |
#undef NUMBER_OF_COLUMNS_IN_FORMAT_STRING | |
#undef FILL_CELL_FROM_STRING | |
} | |
#endif | |
TB_INTERNAL | |
int snprintf_row(const table_row_t *row, char *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, | |
size_t row_height, const context_t *context) | |
{ | |
int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; | |
int (*cell_printf_)(table_cell_t *, size_t, char *, size_t, const context_t *) = cell_printf; | |
assert(context); | |
const char *space_char = " "; | |
const char *new_line_char = "\n"; | |
if (row == NULL) | |
return -1; | |
size_t cols_in_row = columns_in_row(row); | |
if (cols_in_row > col_width_arr_sz) | |
return -1; | |
/* Row separator anatomy | |
* | |
* L data IV data IV data R | |
*/ | |
typedef const char *(*border_chars_point_t)[BorderItemPosSize]; | |
enum ft_row_type row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row, TB_ANY_COLUMN, TB_CPROP_ROW_TYPE); | |
const char *(*bord_chars)[BorderItemPosSize] = (row_type == TB_ROW_HEADER) | |
? (border_chars_point_t)(&context->table_properties->border_style.header_border_chars) | |
: (border_chars_point_t)(&context->table_properties->border_style.border_chars); | |
const char **L = &(*bord_chars)[LL_bip]; | |
const char **IV = &(*bord_chars)[IV_bip]; | |
const char **R = &(*bord_chars)[RR_bip]; | |
int written = 0; | |
int tmp = 0; | |
size_t i = 0; | |
for (i = 0; i < row_height; ++i) { | |
/* Print left margin */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); | |
/* Print left table boundary */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *L)); | |
size_t j = 0; | |
while (j < col_width_arr_sz) { | |
if (j < cols_in_row) { | |
((context_t *)context)->column = j; | |
table_cell_t *cell = *(table_cell_t **)vector_at(row->cells, j); | |
size_t cell_width = 0; | |
size_t group_slave_sz = group_cell_number(row, j); | |
cell_width = col_width_arr[j]; | |
size_t slave_j = 0; | |
size_t master_j = j; | |
for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) { | |
cell_width += col_width_arr[slave_j] + TABLE_COL_SEPARATOR_LENGTH; | |
++j; | |
} | |
CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context)); | |
} else { | |
/* Print empty cell */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, col_width_arr[j], space_char)); | |
} | |
/* Print boundary between cells */ | |
if (j < col_width_arr_sz - 1) | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *IV)); | |
++j; | |
} | |
/* Print right table boundary */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *R)); | |
/* Print right margin */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); | |
/* Print new line character */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, new_line_char)); | |
} | |
return written; | |
clear: | |
return -1; | |
} | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
int wsnprintf_row(const table_row_t *row, wchar_t *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, | |
size_t row_height, const context_t *context) | |
{ | |
int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; | |
int (*cell_printf_)(table_cell_t *, size_t, wchar_t *, size_t, const context_t *) = cell_wprintf; | |
assert(context); | |
const char *space_char = " "; | |
const char *new_line_char = "\n"; | |
if (row == NULL) | |
return -1; | |
size_t cols_in_row = columns_in_row(row); | |
if (cols_in_row > col_width_arr_sz) | |
return -1; | |
/* Row separator anatomy | |
* | |
* L data IV data IV data R | |
*/ | |
typedef const char *(*border_chars_point_t)[BorderItemPosSize]; | |
enum ft_row_type row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row, TB_ANY_COLUMN, TB_CPROP_ROW_TYPE); | |
const char *(*bord_chars)[BorderItemPosSize] = (row_type == TB_ROW_HEADER) | |
? (border_chars_point_t)(&context->table_properties->border_style.header_border_chars) | |
: (border_chars_point_t)(&context->table_properties->border_style.border_chars); | |
const char **L = &(*bord_chars)[LL_bip]; | |
const char **IV = &(*bord_chars)[IV_bip]; | |
const char **R = &(*bord_chars)[RR_bip]; | |
int written = 0; | |
int tmp = 0; | |
size_t i = 0; | |
for (i = 0; i < row_height; ++i) { | |
/* Print left margin */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); | |
/* Print left table boundary */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *L)); | |
size_t j = 0; | |
while (j < col_width_arr_sz) { | |
if (j < cols_in_row) { | |
((context_t *)context)->column = j; | |
table_cell_t *cell = *(table_cell_t **)vector_at(row->cells, j); | |
size_t cell_width = 0; | |
size_t group_slave_sz = group_cell_number(row, j); | |
cell_width = col_width_arr[j]; | |
size_t slave_j = 0; | |
size_t master_j = j; | |
for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) { | |
cell_width += col_width_arr[slave_j] + TABLE_COL_SEPARATOR_LENGTH; | |
++j; | |
} | |
CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context)); | |
} else { | |
/* Print empty cell */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, col_width_arr[j], space_char)); | |
} | |
/* Print boundary between cells */ | |
if (j < col_width_arr_sz - 1) | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *IV)); | |
++j; | |
} | |
/* Print right table boundary */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *R)); | |
/* Print right margin */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); | |
/* Print new line character */ | |
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, new_line_char)); | |
} | |
return written; | |
clear: | |
return -1; | |
} | |
#endif | |
/******************************************************** | |
End of file "row.c" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "string_buffer.c" | |
********************************************************/ | |
/* #include "string_buffer.h" */ /* Commented by amalgamation script */ | |
/* #include "properties.h" */ /* Commented by amalgamation script */ | |
/* #include "wcwidth.h" */ /* Commented by amalgamation script */ | |
#include <assert.h> | |
#include <stddef.h> | |
#include <wchar.h> | |
static ptrdiff_t str_iter_width(const char *beg, const char *end) | |
{ | |
assert(end >= beg); | |
return (end - beg); | |
} | |
#ifdef TB_HAVE_WCHAR | |
static ptrdiff_t wcs_iter_width(const wchar_t *beg, const wchar_t *end) | |
{ | |
assert(end >= beg); | |
return mk_wcswidth(beg, (end - beg)); | |
} | |
#endif /* TB_HAVE_WCHAR */ | |
static size_t buf_str_len(const string_buffer_t *buf) | |
{ | |
assert(buf); | |
if (buf->type == CharBuf) { | |
return strlen(buf->str.cstr); | |
} else { | |
return wcslen(buf->str.wstr); | |
} | |
} | |
TB_INTERNAL | |
size_t strchr_count(const char *str, char ch) | |
{ | |
if (str == NULL) | |
return 0; | |
size_t count = 0; | |
str = strchr(str, ch); | |
while (str) { | |
count++; | |
str++; | |
str = strchr(str, ch); | |
} | |
return count; | |
} | |
TB_INTERNAL | |
size_t wstrchr_count(const wchar_t *str, wchar_t ch) | |
{ | |
if (str == NULL) | |
return 0; | |
size_t count = 0; | |
str = wcschr(str, ch); | |
while (str) { | |
count++; | |
str++; | |
str = wcschr(str, ch); | |
} | |
return count; | |
} | |
TB_INTERNAL | |
const char *str_n_substring_beg(const char *str, char ch_separator, size_t n) | |
{ | |
if (str == NULL) | |
return NULL; | |
if (n == 0) | |
return str; | |
str = strchr(str, ch_separator); | |
--n; | |
while (n > 0) { | |
if (str == NULL) | |
return NULL; | |
--n; | |
str++; | |
str = strchr(str, ch_separator); | |
} | |
return str ? (str + 1) : NULL; | |
} | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
const wchar_t *wstr_n_substring_beg(const wchar_t *str, wchar_t ch_separator, size_t n) | |
{ | |
if (str == NULL) | |
return NULL; | |
if (n == 0) | |
return str; | |
str = wcschr(str, ch_separator); | |
--n; | |
while (n > 0) { | |
if (str == NULL) | |
return NULL; | |
--n; | |
str++; | |
str = wcschr(str, ch_separator); | |
} | |
return str ? (str + 1) : NULL; | |
} | |
#endif /* TB_HAVE_WCHAR */ | |
TB_INTERNAL | |
void str_n_substring(const char *str, char ch_separator, size_t n, const char **begin, const char **end) | |
{ | |
const char *beg = str_n_substring_beg(str, ch_separator, n); | |
if (beg == NULL) { | |
*begin = NULL; | |
*end = NULL; | |
return; | |
} | |
const char *en = strchr(beg, ch_separator); | |
if (en == NULL) { | |
en = str + strlen(str); | |
} | |
*begin = beg; | |
*end = en; | |
return; | |
} | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
void wstr_n_substring(const wchar_t *str, wchar_t ch_separator, size_t n, const wchar_t **begin, const wchar_t **end) | |
{ | |
const wchar_t *beg = wstr_n_substring_beg(str, ch_separator, n); | |
if (beg == NULL) { | |
*begin = NULL; | |
*end = NULL; | |
return; | |
} | |
const wchar_t *en = wcschr(beg, ch_separator); | |
if (en == NULL) { | |
en = str + wcslen(str); | |
} | |
*begin = beg; | |
*end = en; | |
return; | |
} | |
#endif /* TB_HAVE_WCHAR */ | |
TB_INTERNAL | |
string_buffer_t *create_string_buffer(size_t number_of_chars, enum str_buf_type type) | |
{ | |
size_t sz = (number_of_chars) * (type == CharBuf ? sizeof(char) : sizeof(wchar_t)); | |
string_buffer_t *result = (string_buffer_t *)F_MALLOC(sizeof(string_buffer_t)); | |
if (result == NULL) | |
return NULL; | |
result->str.data = F_MALLOC(sz); | |
if (result->str.data == NULL) { | |
F_FREE(result); | |
return NULL; | |
} | |
result->data_sz = sz; | |
result->type = type; | |
if (sz && type == CharBuf) { | |
result->str.cstr[0] = '\0'; | |
#ifdef TB_HAVE_WCHAR | |
} else if (sz && type == WCharBuf) { | |
result->str.wstr[0] = L'\0'; | |
#endif /* TB_HAVE_WCHAR */ | |
} | |
return result; | |
} | |
TB_INTERNAL | |
void destroy_string_buffer(string_buffer_t *buffer) | |
{ | |
if (buffer == NULL) | |
return; | |
F_FREE(buffer->str.data); | |
buffer->str.data = NULL; | |
F_FREE(buffer); | |
} | |
TB_INTERNAL | |
string_buffer_t *copy_string_buffer(string_buffer_t *buffer) | |
{ | |
assert(buffer); | |
string_buffer_t *result = create_string_buffer(buffer->data_sz, buffer->type); | |
if (result == NULL) | |
return NULL; | |
switch (buffer->type) { | |
case CharBuf: | |
if (TB_IS_ERROR(fill_buffer_from_string(result, buffer->str.cstr))) { | |
destroy_string_buffer(buffer); | |
return NULL; | |
} | |
break; | |
#ifdef TB_HAVE_WCHAR | |
case WCharBuf: | |
if (TB_IS_ERROR(fill_buffer_from_wstring(result, buffer->str.wstr))) { | |
destroy_string_buffer(buffer); | |
return NULL; | |
} | |
break; | |
#endif /* TB_HAVE_WCHAR */ | |
default: | |
destroy_string_buffer(result); | |
return NULL; | |
} | |
return result; | |
} | |
TB_INTERNAL | |
table_status_t realloc_string_buffer_without_copy(string_buffer_t *buffer) | |
{ | |
assert(buffer); | |
char *new_str = (char *)F_MALLOC(buffer->data_sz * 2); | |
if (new_str == NULL) { | |
return TB_MEMORY_ERROR; | |
} | |
F_FREE(buffer->str.data); | |
buffer->str.data = new_str; | |
buffer->data_sz *= 2; | |
return TB_SUCCESS; | |
} | |
TB_INTERNAL | |
table_status_t fill_buffer_from_string(string_buffer_t *buffer, const char *str) | |
{ | |
assert(buffer); | |
assert(str); | |
// size_t sz = strlen(str); | |
char *copy = F_STRDUP(str); | |
if (copy == NULL) | |
return TB_MEMORY_ERROR; | |
// while (sz >= string_buffer_capacity(buffer)) { | |
// int status = realloc_string_buffer_without_copy(buffer); | |
// if (!TB_IS_SUCCESS(status)) { | |
// return status; | |
// } | |
// } | |
F_FREE(buffer->str.data); | |
buffer->str.cstr = copy; | |
buffer->type = CharBuf; | |
return TB_SUCCESS; | |
} | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
table_status_t fill_buffer_from_wstring(string_buffer_t *buffer, const wchar_t *str) | |
{ | |
assert(buffer); | |
assert(str); | |
// size_t sz = wcslen(str); | |
wchar_t *copy = F_WCSDUP(str); | |
if (copy == NULL) | |
return TB_MEMORY_ERROR; | |
// while (sz >= string_buffer_capacity(buffer)) { | |
// int status = realloc_string_buffer_without_copy(buffer); | |
// if (!TB_IS_SUCCESS(status)) { | |
// return status; | |
// } | |
// } | |
F_FREE(buffer->str.data); | |
buffer->str.wstr = copy; | |
buffer->type = WCharBuf; | |
return TB_SUCCESS; | |
} | |
#endif /* TB_HAVE_WCHAR */ | |
TB_INTERNAL | |
size_t buffer_text_height(string_buffer_t *buffer) | |
{ | |
if (buffer == NULL || buffer->str.data == NULL || buf_str_len(buffer) == 0) { | |
return 0; | |
} | |
if (buffer->type == CharBuf) | |
return 1 + strchr_count(buffer->str.cstr, '\n'); | |
else | |
return 1 + wstrchr_count(buffer->str.wstr, L'\n'); | |
} | |
TB_INTERNAL | |
size_t buffer_text_width(string_buffer_t *buffer) | |
{ | |
size_t max_length = 0; | |
if (buffer->type == CharBuf) { | |
int n = 0; | |
while (1) { | |
const char *beg = NULL; | |
const char *end = NULL; | |
str_n_substring(buffer->str.cstr, '\n', n, &beg, &end); | |
if (beg == NULL || end == NULL) | |
return max_length; | |
max_length = MAX(max_length, (size_t)(end - beg)); | |
++n; | |
} | |
#ifdef TB_HAVE_WCHAR | |
} else { | |
int n = 0; | |
while (1) { | |
const wchar_t *beg = NULL; | |
const wchar_t *end = NULL; | |
wstr_n_substring(buffer->str.wstr, L'\n', n, &beg, &end); | |
if (beg == NULL || end == NULL) | |
return max_length; | |
int line_width = mk_wcswidth(beg, end - beg); | |
if (line_width < 0) /* For safety */ | |
line_width = 0; | |
max_length = MAX(max_length, (size_t)line_width); | |
++n; | |
} | |
#endif /* TB_HAVE_WCHAR */ | |
} | |
return max_length; /* shouldn't be here */ | |
} | |
TB_INTERNAL | |
int buffer_printf(string_buffer_t *buffer, size_t buffer_row, char *buf, size_t total_buf_len, | |
const context_t *context, const char *content_style_tag, const char *reset_content_style_tag) | |
{ | |
#define CHAR_TYPE char | |
#define NULL_CHAR '\0' | |
#define NEWLINE_CHAR '\n' | |
#define SPACE_CHAR " " | |
#define SNPRINTF_FMT_STR "%*s" | |
#define SNPRINTF snprintf | |
#define BUFFER_STR str.cstr | |
//#define SNPRINT_N_CHARS snprint_n_chars | |
#define SNPRINT_N_STRINGS snprint_n_strings | |
#define STR_N_SUBSTRING str_n_substring | |
#define STR_ITER_WIDTH str_iter_width | |
size_t buf_len = total_buf_len - strlen(content_style_tag) - strlen(reset_content_style_tag); | |
if (buffer == NULL || buffer->str.data == NULL | |
|| buffer_row >= buffer_text_height(buffer) || buf_len == 0) { | |
return -1; | |
} | |
size_t content_width = buffer_text_width(buffer); | |
if ((buf_len - 1) < content_width) | |
return -1; | |
size_t left = 0; | |
size_t right = 0; | |
switch (get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_TEXT_ALIGN)) { | |
case TB_ALIGNED_LEFT: | |
left = 0; | |
right = (buf_len - 1) - content_width; | |
break; | |
case TB_ALIGNED_CENTER: | |
left = ((buf_len - 1) - content_width) / 2; | |
right = ((buf_len - 1) - content_width) - left; | |
break; | |
case TB_ALIGNED_RIGHT: | |
left = (buf_len - 1) - content_width; | |
right = 0; | |
break; | |
default: | |
assert(0); | |
break; | |
} | |
int written = 0; | |
int tmp = 0; | |
ptrdiff_t str_it_width = 0; | |
const CHAR_TYPE *beg = NULL; | |
const CHAR_TYPE *end = NULL; | |
CHAR_TYPE old_value; | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, left, SPACE_CHAR)); | |
STR_N_SUBSTRING(buffer->BUFFER_STR, NEWLINE_CHAR, buffer_row, &beg, &end); | |
if (beg == NULL || end == NULL) | |
return -1; | |
old_value = *end; | |
*(CHAR_TYPE *)end = NULL_CHAR; | |
str_it_width = STR_ITER_WIDTH(beg, end); | |
if (str_it_width < 0 || content_width < (size_t)str_it_width) | |
return - 1; | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, content_style_tag)); | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINTF(buf + written, total_buf_len - written, SNPRINTF_FMT_STR, (int)(end - beg), beg)); | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, reset_content_style_tag)); | |
*(CHAR_TYPE *)end = old_value; | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, (content_width - (size_t)str_it_width), SPACE_CHAR)); | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, right, SPACE_CHAR)); | |
return written; | |
clear: | |
return -1; | |
#undef CHAR_TYPE | |
#undef NULL_CHAR | |
#undef NEWLINE_CHAR | |
#undef SPACE_CHAR | |
#undef SNPRINTF_FMT_STR | |
#undef SNPRINTF | |
#undef BUFFER_STR | |
//#undef SNPRINT_N_CHARS | |
#undef SNPRINT_N_STRINGS | |
#undef STR_N_SUBSTRING | |
#undef STR_ITER_WIDTH | |
} | |
#ifdef TB_HAVE_WCHAR | |
TB_INTERNAL | |
int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, size_t total_buf_len, | |
const context_t *context, const char *content_style_tag, const char *reset_content_style_tag) | |
{ | |
#define CHAR_TYPE wchar_t | |
#define NULL_CHAR L'\0' | |
#define NEWLINE_CHAR L'\n' | |
#define SPACE_CHAR " " | |
#define SNPRINTF_FMT_STR L"%*ls" | |
#define SNPRINTF swprintf | |
#define BUFFER_STR str.wstr | |
//#define SNPRINT_N_CHARS wsnprint_n_chars | |
#define SNPRINT_N_STRINGS wsnprint_n_string | |
#define STR_N_SUBSTRING wstr_n_substring | |
#define STR_ITER_WIDTH wcs_iter_width | |
size_t buf_len = total_buf_len - strlen(content_style_tag) - strlen(reset_content_style_tag); | |
if (buffer == NULL || buffer->str.data == NULL | |
|| buffer_row >= buffer_text_height(buffer) || buf_len == 0) { | |
return -1; | |
} | |
size_t content_width = buffer_text_width(buffer); | |
if ((buf_len - 1) < content_width) | |
return -1; | |
size_t left = 0; | |
size_t right = 0; | |
switch (get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, TB_CPROP_TEXT_ALIGN)) { | |
case TB_ALIGNED_LEFT: | |
left = 0; | |
right = (buf_len - 1) - content_width; | |
break; | |
case TB_ALIGNED_CENTER: | |
left = ((buf_len - 1) - content_width) / 2; | |
right = ((buf_len - 1) - content_width) - left; | |
break; | |
case TB_ALIGNED_RIGHT: | |
left = (buf_len - 1) - content_width; | |
right = 0; | |
break; | |
default: | |
assert(0); | |
break; | |
} | |
int written = 0; | |
int tmp = 0; | |
ptrdiff_t str_it_width = 0; | |
const CHAR_TYPE *beg = NULL; | |
const CHAR_TYPE *end = NULL; | |
CHAR_TYPE old_value; | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, left, SPACE_CHAR)); | |
STR_N_SUBSTRING(buffer->BUFFER_STR, NEWLINE_CHAR, buffer_row, &beg, &end); | |
if (beg == NULL || end == NULL) | |
return -1; | |
old_value = *end; | |
*(CHAR_TYPE *)end = NULL_CHAR; | |
str_it_width = STR_ITER_WIDTH(beg, end); | |
if (str_it_width < 0 || content_width < (size_t)str_it_width) | |
return - 1; | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, content_style_tag)); | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINTF(buf + written, total_buf_len - written, SNPRINTF_FMT_STR, (int)(end - beg), beg)); | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, reset_content_style_tag)); | |
*(CHAR_TYPE *)end = old_value; | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, (content_width - (size_t)str_it_width), SPACE_CHAR)); | |
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, right, SPACE_CHAR)); | |
return written; | |
clear: | |
return -1; | |
#undef CHAR_TYPE | |
#undef NULL_CHAR | |
#undef NEWLINE_CHAR | |
#undef SPACE_CHAR | |
#undef SNPRINTF_FMT_STR | |
#undef SNPRINTF | |
#undef BUFFER_STR | |
//#undef SNPRINT_N_CHARS | |
#undef SNPRINT_N_STRINGS | |
#undef STR_N_SUBSTRING | |
#undef STR_ITER_WIDTH | |
} | |
#endif /* TB_HAVE_WCHAR */ | |
TB_INTERNAL | |
size_t string_buffer_capacity(const string_buffer_t *buffer) | |
{ | |
assert(buffer); | |
if (buffer->type == CharBuf) | |
return buffer->data_sz; | |
else | |
return buffer->data_sz / sizeof(wchar_t); | |
} | |
TB_INTERNAL | |
void *buffer_get_data(string_buffer_t *buffer) | |
{ | |
assert(buffer); | |
return buffer->str.data; | |
} | |
/******************************************************** | |
End of file "string_buffer.c" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "table.c" | |
********************************************************/ | |
/* #include "table.h" */ /* Commented by amalgamation script */ | |
/* #include "string_buffer.h" */ /* Commented by amalgamation script */ | |
/* #include "cell.h" */ /* Commented by amalgamation script */ | |
/* #include "vector.h" */ /* Commented by amalgamation script */ | |
/* #include "row.h" */ /* Commented by amalgamation script */ | |
TB_INTERNAL | |
separator_t *create_separator(int enabled) | |
{ | |
separator_t *res = (separator_t *)F_CALLOC(1, sizeof(separator_t)); | |
if (res == NULL) | |
return NULL; | |
res->enabled = enabled; | |
return res; | |
} | |
TB_INTERNAL | |
void destroy_separator(separator_t *sep) | |
{ | |
F_FREE(sep); | |
} | |
TB_INTERNAL | |
separator_t *copy_separator(separator_t *sep) | |
{ | |
assert(sep); | |
return create_separator(sep->enabled); | |
} | |
static | |
table_row_t *get_row_implementation(ft_table_t *table, size_t row, enum PolicyOnNull policy) | |
{ | |
if (table == NULL || table->rows == NULL) { | |
return NULL; | |
} | |
switch (policy) { | |
case DoNotCreate: | |
if (row < vector_size(table->rows)) { | |
return *(table_row_t **)vector_at(table->rows, row); | |
} | |
return NULL; | |
break; | |
case Create: | |
while (row >= vector_size(table->rows)) { | |
table_row_t *new_row = create_row(); | |
if (new_row == NULL) | |
return NULL; | |
if (TB_IS_ERROR(vector_push(table->rows, &new_row))) { | |
destroy_row(new_row); | |
return NULL; | |
} | |
} | |
return *(table_row_t **)vector_at(table->rows, row); | |
break; | |
} | |
return NULL; | |
} | |
TB_INTERNAL | |
table_row_t *get_row(ft_table_t *table, size_t row) | |
{ | |
return get_row_implementation(table, row, DoNotCreate); | |
} | |
TB_INTERNAL | |
const table_row_t *get_row_c(const ft_table_t *table, size_t row) | |
{ | |
return get_row((ft_table_t *)table, row); | |
} | |
TB_INTERNAL | |
table_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row) | |
{ | |
return get_row_implementation(table, row, Create); | |
} | |
TB_INTERNAL | |
string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table) | |
{ | |
assert(table); | |
table_row_t *row = get_row_and_create_if_not_exists(table, table->cur_row); | |
if (row == NULL) | |
return NULL; | |
table_cell_t *cell = get_cell_and_create_if_not_exists(row, table->cur_col); | |
if (cell == NULL) | |
return NULL; | |
return cell_get_string_buffer(cell); | |
} | |
/* | |
* Returns number of cells (rows * cols) | |
*/ | |
TB_INTERNAL | |
table_status_t get_table_sizes(const ft_table_t *table, size_t *rows, size_t *cols) | |
{ | |
*rows = 0; | |
*cols = 0; | |
if (table && table->rows) { | |
*rows = vector_size(table->rows); | |
table_row_t *row = NULL; | |
FOR_EACH(table_row_t *, row, table->rows) { | |
(void)i0; | |
size_t cols_in_row = columns_in_row(row); | |
if (cols_in_row > *cols) | |
*cols = cols_in_row; | |
} | |
} | |
return TB_SUCCESS; | |
} | |
TB_INTERNAL | |
table_status_t table_rows_and_cols_geometry(const ft_table_t *table, | |
size_t **col_width_arr_p, size_t *col_width_arr_sz, | |
size_t **row_height_arr_p, size_t *row_height_arr_sz, | |
enum request_geom_type geom) | |
{ | |
if (table == NULL) { | |
return TB_ERROR; | |
} | |
size_t cols = 0; | |
size_t rows = 0; | |
int status = get_table_sizes(table, &rows, &cols); | |
if (TB_IS_ERROR(status)) | |
return status; | |
size_t *col_width_arr = (size_t *)F_CALLOC(sizeof(size_t), cols); | |
size_t *row_height_arr = (size_t *)F_CALLOC(sizeof(size_t), rows); | |
if (col_width_arr == NULL || row_height_arr == NULL) { | |
F_FREE(col_width_arr); | |
F_FREE(row_height_arr); | |
return TB_ERROR; | |
} | |
int combined_cells_found = 0; | |
context_t context; | |
context.table_properties = (table->properties ? table->properties : &g_table_properties); | |
size_t col = 0; | |
for (col = 0; col < cols; ++col) { | |
col_width_arr[col] = 0; | |
size_t row = 0; | |
for (row = 0; row < rows; ++row) { | |
const table_row_t *row_p = get_row_c(table, row); | |
const table_cell_t *cell = get_cell_c(row_p, col); | |
context.column = col; | |
context.row = row; | |
if (cell) { | |
switch (get_cell_type(cell)) { | |
case CommonCell: | |
col_width_arr[col] = MAX(col_width_arr[col], hint_width_cell(cell, &context, geom)); | |
break; | |
case GroupMasterCell: | |
combined_cells_found = 1; | |
break; | |
case GroupSlaveCell: | |
; /* Do nothing */ | |
break; | |
} | |
row_height_arr[row] = MAX(row_height_arr[row], hint_height_cell(cell, &context)); | |
} | |
} | |
} | |
if (combined_cells_found) { | |
col = 0; | |
for (col = 0; col < cols; ++col) { | |
size_t row = 0; | |
for (row = 0; row < rows; ++row) { | |
const table_row_t *row_p = get_row_c(table, row); | |
const table_cell_t *cell = get_cell_c(row_p, col); | |
context.column = col; | |
context.row = row; | |
if (cell) { | |
if (get_cell_type(cell) == GroupMasterCell) { | |
size_t hint_width = hint_width_cell(cell, &context, geom); | |
size_t slave_col = col + group_cell_number(row_p, col); | |
size_t cur_adj_col = col; | |
size_t group_width = col_width_arr[col]; | |
size_t i; | |
for (i = col + 1; i < slave_col; ++i) | |
group_width += col_width_arr[i] + TABLE_COL_SEPARATOR_LENGTH; | |
/* adjust col. widths */ | |
while (1) { | |
if (group_width >= hint_width) | |
break; | |
col_width_arr[cur_adj_col] += 1; | |
group_width++; | |
cur_adj_col++; | |
if (cur_adj_col == slave_col) | |
cur_adj_col = col; | |
} | |
} | |
} | |
} | |
} | |
} | |
/* todo: Maybe it is better to move min width checking to a particular cell width checking. | |
* At the moment min width includes paddings. Maybe it is better that min width weren't include | |
* paddings but be min width of the cell content without padding | |
*/ | |
/* | |
if (table->properties) { | |
for (size_t i = 0; i < cols; ++i) { | |
col_width_arr[i] = MAX((int)col_width_arr[i], table_props_column_width(table->properties, i)); | |
} | |
} | |
*/ | |
*col_width_arr_p = col_width_arr; | |
*col_width_arr_sz = cols; | |
*row_height_arr_p = row_height_arr; | |
*row_height_arr_sz = rows; | |
return TB_SUCCESS; | |
} | |
/* | |
* Returns geometry in characters | |
*/ | |
TB_INTERNAL | |
table_status_t table_geometry(const ft_table_t *table, size_t *height, size_t *width) | |
{ | |
if (table == NULL) | |
return TB_ERROR; | |
*height = 0; | |
*width = 0; | |
size_t cols = 0; | |
size_t rows = 0; | |
size_t *col_width_arr = NULL; | |
size_t *row_height_arr = NULL; | |
int status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows, INTERN_REPR_GEOMETRY); | |
if (TB_IS_ERROR(status)) | |
return status; | |
*width = 1 + (cols == 0 ? 1 : cols) + 1; /* for boundaries (that take 1 symbol) + newline */ | |
size_t i = 0; | |
for (i = 0; i < cols; ++i) { | |
*width += col_width_arr[i]; | |
} | |
/* todo: add check for non printable horizontal row separators */ | |
*height = 1 + (rows == 0 ? 1 : rows); /* for boundaries (that take 1 symbol) */ | |
for (i = 0; i < rows; ++i) { | |
*height += row_height_arr[i]; | |
} | |
F_FREE(col_width_arr); | |
F_FREE(row_height_arr); | |
if (table->properties) { | |
*height += table->properties->entire_table_properties.top_margin; | |
*height += table->properties->entire_table_properties.bottom_margin; | |
*width += table->properties->entire_table_properties.left_margin; | |
*width += table->properties->entire_table_properties.right_margin; | |
} | |
/* Take into account that border elements can be more than one byte long */ | |
table_table_properties_t *table_properties = table->properties ? table->properties : &g_table_properties; | |
size_t max_border_elem_len = max_border_elem_strlen(table_properties); | |
*width *= max_border_elem_len; | |
return TB_SUCCESS; | |
} | |
/******************************************************** | |
End of file "table.c" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "vector.c" | |
********************************************************/ | |
/* #include "vector.h" */ /* Commented by amalgamation script */ | |
#include <assert.h> | |
#include <string.h> | |
struct vector { | |
size_t m_size; | |
void *m_data; | |
size_t m_capacity; | |
size_t m_item_size; | |
}; | |
static int vector_reallocate_(vector_t *vector, size_t new_capacity) | |
{ | |
assert(vector); | |
assert(new_capacity > vector->m_capacity); | |
size_t new_size = new_capacity * vector->m_item_size; | |
vector->m_data = F_REALLOC(vector->m_data, new_size); | |
if (vector->m_data == NULL) | |
return -1; | |
return 0; | |
} | |
TB_INTERNAL | |
vector_t *create_vector(size_t item_size, size_t capacity) | |
{ | |
vector_t *vector = (vector_t *)F_MALLOC(sizeof(vector_t)); | |
if (vector == NULL) { | |
SYS_LOG_ERROR("Failed to allocate memory for asock vector"); | |
return NULL; | |
} | |
size_t init_size = MAX(item_size * capacity, 1); | |
vector->m_data = F_MALLOC(init_size); | |
if (vector->m_data == NULL) { | |
SYS_LOG_ERROR("Failed to allocate memory for asock vector inern. buffer"); | |
F_FREE(vector); | |
return NULL; | |
} | |
vector->m_size = 0; | |
vector->m_capacity = capacity; | |
vector->m_item_size = item_size; | |
return vector; | |
} | |
TB_INTERNAL | |
void destroy_vector(vector_t *vector) | |
{ | |
assert(vector); | |
F_FREE(vector->m_data); | |
F_FREE(vector); | |
} | |
TB_INTERNAL | |
size_t vector_size(const vector_t *vector) | |
{ | |
assert(vector); | |
return vector->m_size; | |
} | |
TB_INTERNAL | |
size_t vector_capacity(const vector_t *vector) | |
{ | |
assert(vector); | |
return vector->m_capacity; | |
} | |
TB_INTERNAL | |
int vector_push(vector_t *vector, const void *item) | |
{ | |
assert(vector); | |
assert(item); | |
if (vector->m_size == vector->m_capacity) { | |
if (vector_reallocate_(vector, vector->m_capacity * 2) == -1) | |
return TB_ERROR; | |
vector->m_capacity = vector->m_capacity * 2; | |
} | |
ptrdiff_t deviation = vector->m_size * vector->m_item_size; | |
memcpy((char *)vector->m_data + deviation, item, vector->m_item_size); | |
++(vector->m_size); | |
return TB_SUCCESS; | |
} | |
TB_INTERNAL | |
const void *vector_at_c(const vector_t *vector, size_t index) | |
{ | |
if (index >= vector->m_size) | |
return NULL; | |
return (char *)vector->m_data + index * vector->m_item_size; | |
} | |
TB_INTERNAL | |
void *vector_at(vector_t *vector, size_t index) | |
{ | |
if (index >= vector->m_size) | |
return NULL; | |
return (char *)vector->m_data + index * vector->m_item_size; | |
} | |
TB_INTERNAL | |
table_status_t vector_swap(vector_t *cur_vec, vector_t *mv_vec, size_t pos) | |
{ | |
assert(cur_vec); | |
assert(mv_vec); | |
assert(cur_vec != mv_vec); | |
assert(cur_vec->m_item_size == mv_vec->m_item_size); | |
size_t cur_sz = vector_size(cur_vec); | |
size_t mv_sz = vector_size(mv_vec); | |
if (mv_sz == 0) { | |
return TB_SUCCESS; | |
} | |
size_t min_targ_size = pos + mv_sz; | |
if (vector_capacity(cur_vec) < min_targ_size) { | |
if (vector_reallocate_(cur_vec, min_targ_size) == -1) | |
return TB_ERROR; | |
cur_vec->m_capacity = min_targ_size; | |
} | |
ptrdiff_t deviation = pos * cur_vec->m_item_size; | |
void *tmp = NULL; | |
size_t new_mv_sz = 0; | |
if (cur_sz > pos) { | |
new_mv_sz = MIN(cur_sz - pos, mv_sz); | |
tmp = F_MALLOC(cur_vec->m_item_size * new_mv_sz); | |
if (tmp == NULL) { | |
return TB_MEMORY_ERROR; | |
} | |
} | |
if (tmp) { | |
memcpy(tmp, | |
(char *)cur_vec->m_data + deviation, | |
cur_vec->m_item_size * new_mv_sz); | |
} | |
memcpy((char *)cur_vec->m_data + deviation, | |
mv_vec->m_data, | |
cur_vec->m_item_size * mv_sz); | |
if (tmp) { | |
memcpy(mv_vec->m_data, | |
tmp, | |
cur_vec->m_item_size * new_mv_sz); | |
} | |
cur_vec->m_size = MAX(cur_vec->m_size, min_targ_size); | |
mv_vec->m_size = new_mv_sz; | |
F_FREE(tmp); | |
return TB_SUCCESS; | |
} | |
#ifdef TB_TEST_BUILD | |
vector_t *copy_vector(vector_t *v) | |
{ | |
if (v == NULL) | |
return NULL; | |
vector_t *new_vector = create_vector(v->m_item_size, v->m_capacity); | |
if (new_vector == NULL) | |
return NULL; | |
memcpy(new_vector->m_data, v->m_data, v->m_item_size * v->m_size); | |
new_vector->m_size = v->m_size ; | |
new_vector->m_item_size = v->m_item_size ; | |
return new_vector; | |
} | |
size_t vector_index_of(const vector_t *vector, const void *item) | |
{ | |
assert(vector); | |
assert(item); | |
size_t i = 0; | |
for (i = 0; i < vector->m_size; ++i) { | |
void *data_pos = (char *)vector->m_data + i * vector->m_item_size; | |
if (memcmp(data_pos, item, vector->m_item_size) == 0) { | |
return i; | |
} | |
} | |
return INVALID_VEC_INDEX; | |
} | |
int vector_erase(vector_t *vector, size_t index) | |
{ | |
assert(vector); | |
if (vector->m_size == 0 || index >= vector->m_size) | |
return TB_ERROR; | |
memmove((char *)vector->m_data + vector->m_item_size * index, | |
(char *)vector->m_data + vector->m_item_size * (index + 1), | |
(vector->m_size - 1 - index) * vector->m_item_size); | |
vector->m_size--; | |
return TB_SUCCESS; | |
} | |
void vector_clear(vector_t *vector) | |
{ | |
vector->m_size = 0; | |
} | |
#endif | |
/******************************************************** | |
End of file "vector.c" | |
********************************************************/ | |
/******************************************************** | |
Begin of file "wcwidth.c" | |
********************************************************/ | |
/* | |
* 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 | |
*/ | |
/* #include "wcwidth.h" */ /* Commented by amalgamation script */ | |
#ifdef TB_HAVE_WCHAR | |
struct interval { | |
int first; | |
int last; | |
}; | |
/* auxiliary function for binary search in interval table */ | |
static int bisearch(wchar_t ucs, const struct interval *table, int max) | |
{ | |
int min = 0; | |
if (ucs < table[0].first || ucs > table[max].last) | |
return 0; | |
while (max >= min) { | |
int 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 wchar_t characters are encoded | |
* in ISO 10646. | |
*/ | |
static int mk_wcwidth(wchar_t ucs) | |
{ | |
/* 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 } | |
}; | |
/* 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))); | |
} | |
TB_INTERNAL | |
int mk_wcswidth(const wchar_t *pwcs, size_t n) | |
{ | |
int width = 0; | |
for (; *pwcs && n-- > 0; pwcs++) { | |
int w; | |
if ((w = mk_wcwidth(*pwcs)) < 0) | |
return -1; | |
else | |
width += w; | |
} | |
return width; | |
} | |
#endif /* TB_HAVE_WCHAR */ | |
/******************************************************** | |
End of file "wcwidth.c" | |
********************************************************/ | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
libfort | |
MIT License | |
Copyright (c) 2017 - 2018 Seleznev Anton | |
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. | |
*/ | |
/** | |
* @file fort.h | |
* @brief Main header file describing libfort API. | |
* | |
* This file contains declarations of all libfort functions and macro | |
* definitions. | |
*/ | |
#ifndef LIBTABLE_H | |
#define LIBTABLE_H | |
#include <stddef.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <limits.h> | |
/***************************************************************************** | |
* Configuration | |
*****************************************************************************/ | |
/** | |
* libfort configuration macros | |
* (to enable/disable some options this macros should be defined/undefined) | |
*/ | |
/** #define TB_CONGIG_HAVE_WCHAR */ | |
#if defined(TB_CONGIG_HAVE_WCHAR) | |
#define TB_HAVE_WCHAR | |
#endif | |
/***************************************************************************** | |
* RETURN CODES | |
*****************************************************************************/ | |
typedef int table_status_t; | |
#define TB_SUCCESS 0 | |
#define TB_MEMORY_ERROR -1 | |
#define TB_ERROR -2 | |
#define TB_EINVAL -3 | |
#define TB_IS_SUCCESS(arg) ((arg) >= 0) | |
#define TB_IS_ERROR(arg) ((arg) < 0) | |
/** | |
* @cond HELPER_MACROS | |
*/ | |
/***************************************************************************** | |
* Determine compiler | |
*****************************************************************************/ | |
#if defined(__clang__) | |
#define TB_CLANG_COMPILER | |
#elif defined(__GNUC__) | |
#define TB_GCC_COMPILER | |
#elif defined(_MSC_VER) | |
#define TB_MICROSOTB_COMPILER | |
#else | |
#define TB_UNDEFINED_COMPILER | |
#endif | |
/***************************************************************************** | |
* Declare inline | |
*****************************************************************************/ | |
#if defined(__cplusplus) | |
#define TB_INLINE inline | |
#else | |
#define TB_INLINE __inline | |
#endif /* if defined(__cplusplus) */ | |
/***************************************************************************** | |
* C++ needs to know that types and declarations are C, not C++. | |
*****************************************************************************/ | |
#ifdef __cplusplus | |
# define TB_BEGIN_DECLS extern "C" { | |
# define TB_END_DECLS } | |
#else | |
# define TB_BEGIN_DECLS | |
# define TB_END_DECLS | |
#endif | |
/***************************************************************************** | |
* Helper macros | |
*****************************************************************************/ | |
#define TB_STR_2_CAT_(arg1, arg2) \ | |
arg1##arg2 | |
#define TB_STR_2_CAT(arg1, arg2) \ | |
TB_STR_2_CAT_(arg1, arg2) | |
/** | |
* @interanl | |
*/ | |
static TB_INLINE int ft_check_if_string_helper(const char *str) | |
{ | |
(void)str; | |
return 0; | |
} | |
/** | |
* @interanl | |
*/ | |
static TB_INLINE int ft_check_if_wstring_helper(const wchar_t *str) | |
{ | |
(void)str; | |
return 0; | |
} | |
#define TB_NARGS_IMPL_(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,N,...) N | |
#define TB_EXPAND_(x) x | |
#define TB_PP_NARG_(...) \ | |
TB_EXPAND_(TB_NARGS_IMPL_(__VA_ARGS__,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)) | |
#define TB_CHECK_IF_STR_32(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_31(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_31(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_30(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_30(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_29(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_29(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_28(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_28(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_27(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_27(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_26(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_26(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_25(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_25(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_24(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_24(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_23(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_23(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_22(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_22(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_21(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_21(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_20(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_20(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_19(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_19(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_18(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_18(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_17(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_17(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_16(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_16(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_15(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_15(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_14(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_14(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_13(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_13(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_12(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_12(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_11(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_11(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_10(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_10(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_9(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_9(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_8(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_8(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_7(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_7(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_6(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_6(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_5(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_5(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_4(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_4(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_3(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_3(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_2(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_2(checker,arg,...) (checker(arg),TB_EXPAND_(TB_CHECK_IF_STR_1(checker,__VA_ARGS__))) | |
#define TB_CHECK_IF_STR_1(checker,arg) (checker(arg)) | |
#define TB_CHECK_IF_ARGS_ARE_STRINGS__(checker,func, ...) \ | |
TB_EXPAND_(func(checker,__VA_ARGS__)) | |
#define TB_CHECK_IF_ARGS_ARE_STRINGS_(checker,basis, n, ...) \ | |
TB_CHECK_IF_ARGS_ARE_STRINGS__(checker,TB_STR_2_CAT_(basis, n), __VA_ARGS__) | |
#define TB_CHECK_IF_ARGS_ARE_STRINGS(...) \ | |
TB_CHECK_IF_ARGS_ARE_STRINGS_(ft_check_if_string_helper,TB_CHECK_IF_STR_,TB_PP_NARG_(__VA_ARGS__), __VA_ARGS__) | |
#ifdef TB_HAVE_WCHAR | |
#define CHECK_IF_ARGS_ARE_WSTRINGS(...) \ | |
TB_CHECK_IF_ARGS_ARE_STRINGS_(ft_check_if_wstring_helper,TB_CHECK_IF_STR_,TB_PP_NARG_(__VA_ARGS__), __VA_ARGS__) | |
#endif | |
/** | |
* @endcond | |
*/ | |
/***************************************************************************** | |
* Attribute format for argument checking | |
*****************************************************************************/ | |
#if defined(TB_CLANG_COMPILER) || defined(TB_GCC_COMPILER) | |
#define TB_PRINTF_ATTRIBUTE_FORMAT(string_index, first_to_check) \ | |
__attribute__ ((format (printf, string_index, first_to_check))) | |
#else | |
#define TB_PRINTF_ATTRIBUTE_FORMAT(string_index, first_to_check) | |
#endif /* defined(TB_CLANG_COMPILER) || defined(TB_GCC_COMPILER) */ | |
/***************************************************************************** | |
* libfort API | |
*****************************************************************************/ | |
TB_BEGIN_DECLS | |
/** | |
* The main structure of libfort containing information about formatted table. | |
*/ | |
struct ft_table; | |
/** | |
* The main structure of libfort containing information about formatted table. | |
* | |
* ft_table_t objects should be created by a call to ft_create_table and | |
* destroyed with ft_destroy_table. | |
*/ | |
typedef struct ft_table ft_table_t; | |
/** | |
* Create formatted table. | |
* | |
* @return | |
* The pointer to the new allocated ft_table_t, on success. NULL on error. | |
*/ | |
ft_table_t *ft_create_table(void); | |
/** | |
* Destroy formatted table. | |
* | |
* Destroy formatted table and free all resources allocated during table creation | |
* and work with it. | |
* | |
* @param table | |
* Pointer to formatted table previousley created with ft_create_table. If | |
* table is a null pointer, the function does nothing. | |
*/ | |
void ft_destroy_table(ft_table_t *table); | |
/** | |
* Copy formatted table. | |
* | |
* @param table | |
* Pointer to formatted table previousley created with ft_create_table. If | |
* table is a null pointer, the function returns null. | |
* @return | |
* The pointer to the new allocated ft_table_t, on success. NULL on error. | |
*/ | |
ft_table_t *ft_copy_table(ft_table_t *table); | |
/** | |
* Move current position to the first cell of the next line(row). | |
* | |
* @param table | |
* Pointer to formatted table. | |
*/ | |
void ft_ln(ft_table_t *table); | |
/** | |
* Get row number of the current cell. | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @return | |
* Row number of the current cell. | |
*/ | |
size_t ft_cur_row(ft_table_t *table); | |
/** | |
* Get column number of the current cell. | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @return | |
* Column number of the current cell. | |
*/ | |
size_t ft_cur_col(ft_table_t *table); | |
/** | |
* Set current cell position. | |
* | |
* Current cell - cell that will be edited with all modifiing functions | |
* (ft_printf, ft_write ...). | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param row | |
* New row number for the current cell. | |
* @param col | |
* New row number for the current cell. | |
*/ | |
void ft_set_cur_cell(ft_table_t *table, size_t row, size_t col); | |
#if defined(TB_CLANG_COMPILER) || defined(TB_GCC_COMPILER) | |
/** | |
* Write data formatted acording to the format string to a variety of table | |
* cells. | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param fmt | |
* Pointer to a null-terminated multibyte string specifying how to interpret | |
* the data. The format string consists of ordinary characters (except % and |), | |
* which are copied unchanged into the output stream, and conversion | |
* specifications. Conversion specifications are the same as for standard | |
* printf function. Character '|' in the format string is treated as a cell | |
* separator. | |
* @param ... | |
* Arguments specifying data to print. Similarly to standard printf-like | |
* functions if any argument after default conversions is not the type | |
* expected by the corresponding conversion specifier, or if there are fewer | |
* arguments than required by format, the behavior is undefined. If there are | |
* more arguments than required by format, the extraneous arguments are | |
* evaluated and ignored. | |
* @return | |
* - Number of printed cells | |
* - (<0): In case of error | |
*/ | |
int ft_printf(ft_table_t *table, const char *fmt, ...) TB_PRINTF_ATTRIBUTE_FORMAT(2, 3); | |
/** | |
* Write data formatted acording to the format string to a variety of table | |
* cells and move current position to the first cell of the next line(row). | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param fmt | |
* Pointer to a null-terminated multibyte string specifying how to interpret | |
* the data. The format string consists of ordinary characters (except % and |), | |
* which are copied unchanged into the output stream, and conversion | |
* specifications. Conversion specifications are the same as for standard | |
* printf function. Character '|' in the format string is treated as a cell | |
* separator. | |
* @param ... | |
* Arguments specifying data to print. Similarly to standard printf-like | |
* functions if any argument after default conversions is not the type | |
* expected by the corresponding conversion specifier, or if there are fewer | |
* arguments than required by format, the behavior is undefined. If there are | |
* more arguments than required by format, the extraneous arguments are | |
* evaluated and ignored. | |
* @return | |
* - Number of printed cells. | |
* - (<0): In case of error. | |
*/ | |
int ft_printf_ln(ft_table_t *table, const char *fmt, ...) TB_PRINTF_ATTRIBUTE_FORMAT(2, 3); | |
#else | |
/** | |
* @cond IGNORE_DOC | |
*/ | |
int ft_printf_impl(ft_table_t *table, const char *fmt, ...) TB_PRINTF_ATTRIBUTE_FORMAT(2, 3); | |
int ft_printf_ln_impl(ft_table_t *table, const char *fmt, ...) TB_PRINTF_ATTRIBUTE_FORMAT(2, 3); | |
#define ft_printf(table, ...) \ | |
(( 0 ? fprintf(stderr, __VA_ARGS__) : 1), ft_printf_impl(table, __VA_ARGS__)) | |
#define ft_printf_ln(table, ...) \ | |
(( 0 ? fprintf(stderr, __VA_ARGS__) : 1), ft_printf_ln_impl(table, __VA_ARGS__)) | |
/** | |
* @endcond | |
*/ | |
#endif | |
/** | |
* Write strings to the the table. | |
* | |
* Write specified strings to the same number of consecutive cells in the | |
* current row. | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param ... | |
* Strings to write. | |
* @return | |
* - 0: Success; data were written | |
* - (<0): In case of error | |
*/ | |
#define ft_write(table, ...)\ | |
(0 ? TB_CHECK_IF_ARGS_ARE_STRINGS(__VA_ARGS__) : ft_nwrite(table, TB_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) | |
/** | |
* Write strings to the the table and go to the next line. | |
* | |
* Write specified strings to the same number of consecutive cells in the | |
* current row and move current position to the first cell of the next | |
* line(row). | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param ... | |
* Strings to write. | |
* @return | |
* - 0: Success; data were written | |
* - (<0): In case of error | |
*/ | |
#define ft_write_ln(table, ...)\ | |
(0 ? TB_CHECK_IF_ARGS_ARE_STRINGS(__VA_ARGS__) : ft_nwrite_ln(table, TB_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) | |
/** | |
* Write specified number of strings to the the table. | |
* | |
* Write specified number of strings to the same number of consecutive cells in | |
* the current row. | |
* | |
* @note In most cases it is more preferable to use MACRO @ref ft_write instead | |
* of @ref ft_nwrite, which is more safe (@ref ft_write automatically counts the | |
* number of string arguments and at compile check that all passed arguments are | |
* strings). | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param count | |
* Number of strings to write. | |
* @param cell_content | |
* First string to write. | |
* @param ... | |
* Other strings to write. | |
* @return | |
* - 0: Success; data were written | |
* - (<0): In case of error | |
*/ | |
int ft_nwrite(ft_table_t *table, size_t count, const char *cell_content, ...); | |
/** | |
* Write specified number of strings to the the table and go to the next line. | |
* | |
* Write specified number of strings to the same number of consecutive cells | |
* in the current row and move current position to the first cell of the next | |
* line(row). | |
* | |
* @note In most cases it is more preferable to use MACRO @ref ft_write instead | |
* of @ref ft_nwrite, which is more safe (@ref ft_write automatically counts the | |
* number of string arguments and at compile check that all passed arguments are | |
* strings). | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param count | |
* Number of strings to write. | |
* @param cell_content | |
* First string to write. | |
* @param ... | |
* Other strings to write. | |
* @return | |
* - 0: Success; data were written | |
* - (<0): In case of error | |
*/ | |
int ft_nwrite_ln(ft_table_t *table, size_t count, const char *cell_content, ...); | |
/** | |
* Write strings from the array to the table. | |
* | |
* Write specified number of strings from the array to the same number of | |
* consecutive cells in the current row. | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param cols | |
* Number of elements in row_cells. | |
* @param row_cells | |
* Array of strings to write. | |
* @return | |
* - 0: Success; data were written | |
* - (<0): In case of error | |
*/ | |
int ft_row_write(ft_table_t *table, size_t cols, const char *row_cells[]); | |
/** | |
* Write strings from the array to the table and go to the next line. | |
* | |
* Write specified number of strings from the array to the same number of | |
* consecutive cells in the current row and move current position to the first | |
* cell of the next line(row). | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param cols | |
* Number of elements in row_cells. | |
* @param row_cells | |
* Array of strings to write. | |
* @return | |
* - 0: Success; data were written | |
* - (<0): In case of error | |
*/ | |
int ft_row_write_ln(ft_table_t *table, size_t cols, const char *row_cells[]); | |
/** | |
* Write strings from the 2D array to the table. | |
* | |
* Write specified number of strings from the 2D array to the formatted table. | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param rows | |
* Number of rows in the 2D array. | |
* @param cols | |
* Number of columns in the 2D array. | |
* @param table_cells | |
* 2D array of strings to write. | |
* @return | |
* - 0: Success; data were written | |
* - (<0): In case of error | |
*/ | |
int ft_table_write(ft_table_t *table, size_t rows, size_t cols, const char *table_cells[]); | |
/** | |
* Write strings from the 2D array to the table and go to the next line. | |
* | |
* Write specified number of strings from the 2D array to the formatted table | |
* and move current position to the first cell of the next line(row). | |
* | |
* @param table | |
* Pointer to formatted table. | |
* @param rows | |
* Number of rows in the 2D array. | |
* @param cols | |
* Number of columns in the 2D array. | |
* @param table_cells | |
* 2D array of strings to write. | |
* @return | |
* - 0: Success; data were written | |
* - (<0): In case of error | |
*/ | |
int ft_table_write_ln(ft_table_t *table, size_t rows, size_t cols, const char *table_cells[]); | |
/** | |
* Add separator after the current row. | |
* | |
* @param table | |
* Formatted table. | |
* @return | |
* - 0: Success; separator was added. | |
* - (<0): In case of error | |
*/ | |
int ft_add_separator(ft_table_t *table); | |
/** | |
* Convert table to string representation. | |
* | |
* ft_table_t has ownership of the returned pointer. So there is no need to | |
* free it. To take ownership user should explicitly copy the returned | |
* string with strdup or similar functions. | |
* | |
* Returned pointer may be later invalidated by: | |
* - Calling ft_destroy_table; | |
* - Other invocations of ft_to_string. | |
* | |
* @param table | |
* Formatted table. | |
* @return | |
* - The pointer to the string representation of formatted table, on success. | |
* - NULL on error. | |
*/ | |
const char *ft_to_string(const ft_table_t *table); | |
/** | |
* Structure describing border appearance. | |
*/ | |
struct ft_border_chars { | |
const char *top_border_ch; | |
const char *separator_ch; | |
const char *bottom_border_ch; | |
const char *side_border_ch; | |
const char *out_intersect_ch; | |
const char *in_intersect_ch; | |
}; | |
/** | |
* Structure describing border style. | |
*/ | |
struct ft_border_style { | |
struct ft_border_chars border_chs; | |
struct ft_border_chars header_border_chs; | |
const char *hor_separator_char; | |
}; | |
/** | |
* @defgroup BasicStyles | |
* @name Built-in table border styles. | |
* @{ | |
*/ | |
extern struct ft_border_style *TB_BASIC_STYLE; | |
extern struct ft_border_style *TB_BASIC2_STYLE; | |
extern struct ft_border_style *TB_SIMPLE_STYLE; | |
extern struct ft_border_style *TB_PLAIN_STYLE; | |
extern struct ft_border_style *TB_DOT_STYLE; | |
extern struct ft_border_style *TB_EMPTY_STYLE; | |
extern struct ft_border_style *TB_SOLID_STYLE; | |
extern struct ft_border_style *TB_SOLID_ROUND_STYLE; | |
extern struct ft_border_style *TB_NICE_STYLE; | |
extern struct ft_border_style *TB_DOUBLE_STYLE; | |
extern struct ft_border_style *TB_DOUBLE2_STYLE; | |
extern struct ft_border_style *TB_BOLD_STYLE; | |
extern struct ft_border_style *TB_BOLD2_STYLE; | |
extern struct ft_border_style *TB_FRAME_STYLE; | |
/** @} */ | |
/** | |
* Set default border style for all new formatted tables. | |
* | |
* @param style | |
* Pointer to border style. | |
* @return | |
* - 0: Success; default border style was changed. | |
* - (<0): In case of error | |
*/ | |
int ft_set_default_border_style(const struct ft_border_style *style); | |
/** | |
* Set border style for the table. | |
* | |
* @param table | |
* A pointer to the ft_table_t structure. | |
* @param style | |
* Pointer to border style. | |
* @return | |
* - 0: Success; table border style was changed. | |
* - (<0): In case of error | |
*/ | |
int ft_set_border_style(ft_table_t *table, const struct ft_border_style *style); | |
/** | |
* @name Special macros to define cell position (row and column). | |
* @{ | |
*/ | |
#define TB_ANY_COLUMN (UINT_MAX) /**< Any column (can be used to refer to all cells in a row)*/ | |
#define TB_CUR_COLUMN (UINT_MAX - 1) /**< Current column */ | |
#define TB_ANY_ROW (UINT_MAX) /**< Any row (can be used to refer to all cells in a column)*/ | |
#define TB_CUR_ROW (UINT_MAX - 1) /**< Current row */ | |
/** @} */ | |
/** | |
* @name Cell properties identifiers. | |
* @{ | |
*/ | |
#define TB_CPROP_MIN_WIDTH (0x01U << 0) /**< Minimum width */ | |
#define TB_CPROP_TEXT_ALIGN (0x01U << 1) /**< Text alignment */ | |
#define TB_CPROP_TOP_PADDING (0x01U << 2) /**< Top padding for cell content */ | |
#define TB_CPROP_BOTTOM_PADDING (0x01U << 3) /**< Bottom padding for cell content */ | |
#define TB_CPROP_LETB_PADDING (0x01U << 4) /**< Left padding for cell content */ | |
#define TB_CPROP_RIGHT_PADDING (0x01U << 5) /**< Right padding for cell content */ | |
#define TB_CPROP_EMPTY_STR_HEIGHT (0x01U << 6) /**< Height of empty cell */ | |
#define TB_CPROP_ROW_TYPE (0x01U << 7) /**< Row type */ | |
#define TB_CPROP_CONT_FG_COLOR (0x01U << 8) /**< Cell content foreground text color */ | |
#define TB_CPROP_CELL_BG_COLOR (0x01U << 9) /**< Cell background color */ | |
#define TB_CPROP_CONT_BG_COLOR (0x01U << 10) /**< Cell content background color */ | |
#define TB_CPROP_CELL_TEXT_STYLE (0x01U << 11) /**< Cell text style */ | |
#define TB_CPROP_CONT_TEXT_STYLE (0x01U << 12) /**< Cell content text style */ | |
/** @} */ | |
/** | |
* Colors. | |
*/ | |
enum ft_color { | |
TB_COLOR_DEFAULT = 0, | |
TB_COLOR_BLACK = 1, | |
TB_COLOR_RED = 2, | |
TB_COLOR_GREEN = 3, | |
TB_COLOR_YELLOW = 4, | |
TB_COLOR_BLUE = 5, | |
TB_COLOR_MAGENTA = 6, | |
TB_COLOR_CYAN = 7, | |
TB_COLOR_LIGHT_GRAY = 8, | |
TB_COLOR_DARK_GRAY = 9, | |
TB_COLOR_LIGHT_RED = 10, | |
TB_COLOR_LIGHT_GREEN = 11, | |
TB_COLOR_LIGHT_YELLOW = 12, | |
TB_COLOR_LIGHT_BLUE = 13, | |
TB_COLOR_LIGHT_MAGENTA = 15, | |
TB_COLOR_LIGHT_CYAN = 16, | |
TB_COLOR_LIGHT_WHYTE = 17 | |
}; | |
/** | |
* Text styles. | |
*/ | |
enum ft_text_style { | |
TB_TSTYLE_DEFAULT = (1U << 0), | |
TB_TSTYLE_BOLD = (1U << 1), | |
TB_TSTYLE_DIM = (1U << 2), | |
TB_TSTYLE_ITALIC = (1U << 3), | |
TB_TSTYLE_UNDERLINED = (1U << 4), | |
TB_TSTYLE_BLINK = (1U << 5), | |
TB_TSTYLE_INVERTED = (1U << 6), | |
TB_TSTYLE_HIDDEN = (1U << 7) | |
}; | |
/** | |
* Alignment of cell content. | |
*/ | |
enum ft_text_alignment { | |
TB_ALIGNED_LEFT = 0, /**< Align left */ | |
TB_ALIGNED_CENTER, /**< Align center */ | |
TB_ALIGNED_RIGHT /**< Align right */ | |
}; | |
/** | |
* Type of table row. Determines appearance of row. | |
*/ | |
enum ft_row_type { | |
TB_ROW_COMMON = 0, /**< Common row */ | |
TB_ROW_HEADER /**< Header row */ | |
}; | |
/** | |
* Set default cell property for all new formatted tables. | |
* | |
* @param property | |
* Cell property identifier. | |
* @param value | |
* Cell property value. | |
* @return | |
* - 0: Success; default cell property was changed. | |
* - (<0): In case of error | |
*/ | |
int ft_set_default_cell_prop(uint32_t property, int value); | |
/** | |
* Set property for the specified cell of the table. | |
* | |
* @param table | |
* A pointer to the ft_table_t structure. | |
* @param row | |
* Cell row. | |
* @param col | |
* Cell column. | |
* @param property | |
* Cell property identifier. | |
* @param value | |
* Cell property value. | |
* @return | |
* - 0: Success; cell property was changed. | |
* - (<0): In case of error | |
*/ | |
int ft_set_cell_prop(ft_table_t *table, size_t row, size_t col, uint32_t property, int value); | |
/** | |
* @name Table properties identifiers. | |
* @{ | |
*/ | |
#define TB_TPROP_LETB_MARGIN (0x01U << 0) | |
#define TB_TPROP_TOP_MARGIN (0x01U << 1) | |
#define TB_TPROP_RIGHT_MARGIN (0x01U << 2) | |
#define TB_TPROP_BOTTOM_MARGIN (0x01U << 3) | |
/** @} */ | |
/** | |
* Set default table property. | |
* | |
* @param property | |
* Table property identifier. | |
* @param value | |
* Table property value. | |
* @return | |
* - 0: Success; default table property was changed. | |
* - (<0): In case of error | |
*/ | |
int ft_set_default_tbl_prop(uint32_t property, int value); | |
/** | |
* Set table property. | |
* | |
* @param table | |
* A pointer to the ft_table_t structure. | |
* @param property | |
* Table property identifier. | |
* @param value | |
* Table property value. | |
* @return | |
* - 0: Success; default table property was changed. | |
* - (<0): In case of error | |
*/ | |
int ft_set_tbl_prop(ft_table_t *table, uint32_t property, int value); | |
/** | |
* Set column span for the specified cell of the table. | |
* | |
* @param table | |
* A pointer to the ft_table_t structure. | |
* @param row | |
* Cell row. | |
* @param col | |
* Cell column. | |
* @param hor_span | |
* Column span. | |
* @return | |
* - 0: Success; cell span was changed. | |
* - (<0): In case of error | |
*/ | |
int ft_set_cell_span(ft_table_t *table, size_t row, size_t col, size_t hor_span); | |
/** | |
* Set functions for memory allocation and deallocation to be used instead of | |
* standard ones. | |
* | |
* @param f_malloc | |
* Pointer to a function for memory allocation that should be used instead of | |
* malloc. | |
* @param f_free | |
* Pointer to a function for memory deallocation that should be used instead | |
* of free. | |
* @note | |
* To return memory allocation/deallocation functions to their standard values | |
* set f_malloc and f_free to NULL. | |
*/ | |
void ft_set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr)); | |
#ifdef TB_HAVE_WCHAR | |
int ft_wprintf(ft_table_t *table, const wchar_t *fmt, ...); | |
int ft_wprintf_ln(ft_table_t *table, const wchar_t *fmt, ...); | |
#define ft_wwrite(table, ...)\ | |
(0 ? CHECK_IF_ARGS_ARE_WSTRINGS(__VA_ARGS__) : ft_nwwrite(table, TB_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) | |
#define ft_wwrite_ln(table, ...)\ | |
(0 ? CHECK_IF_ARGS_ARE_WSTRINGS(__VA_ARGS__) : ft_nwwrite_ln(table, TB_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) | |
int ft_nwwrite(ft_table_t *table, size_t n, const wchar_t *cell_content, ...); | |
int ft_nwwrite_ln(ft_table_t *table, size_t n, const wchar_t *cell_content, ...); | |
int ft_row_wwrite(ft_table_t *table, size_t cols, const wchar_t *row_cells[]); | |
int ft_row_wwrite_ln(ft_table_t *table, size_t cols, const wchar_t *row_cells[]); | |
int ft_table_wwrite(ft_table_t *table, size_t rows, size_t cols, const wchar_t *table_cells[]); | |
int ft_table_wwrite_ln(ft_table_t *table, size_t rows, size_t cols, const wchar_t *table_cells[]); | |
const wchar_t *ft_to_wstring(const ft_table_t *table); | |
#endif | |
TB_END_DECLS | |
#endif /* LIBTABLE_H */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment