table

Помоћни програм из командне линије за форматирање и приказ CSV-а
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 23e9021005c75757e2bdd28d1949eaeec25ea92a
родитељ 71387db6bdac2bb8dafd73fbe37dc5ace75d0e74
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Tue, 20 Jul 2021 13:48:06 +0200

Removed libunistring as a dependency

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
M.gitignore | 6++++--
MCHANGES | 5+++++
MTODO | 2+-
Mall.do | 3+--
Adefault.h.do | 13+++++++++++++
Mdefs.h | 106++++++++++++++++++++++++++++---------------------------------------------------
Mtable.c | 1375+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mtable.do | 10++++++++--
Autf8.c | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autf8.h | 40++++++++++++++++++++++++++++++++++++++++
Aversion.h.in | 4++++
измењених датотека: 11, додавања: 981(+), брисања: 744(-)

diff --git a/.gitignore b/.gitignore @@ -14,11 +14,13 @@ *.swp *.did nohup.out -highlight.css +*.gz +*.xz *.png +*.1 table table.1 -*.gz date version +version.h diff --git a/CHANGES b/CHANGES @@ -1,5 +1,9 @@ Changelog ========= +* v0.3 + + - Removed libunistring as a dependency + * v0.2 @@ -35,3 +39,4 @@ - Better UTF-8 substring code based on screen columns + diff --git a/TODO b/TODO @@ -1,7 +1,7 @@ TODO ==== - [ ] Move away from libunistring + [x] Move away from libunistring Legend diff --git a/all.do b/all.do @@ -1,3 +1,2 @@ -redo-ifchange table -redo-ifchange table.1.gz table.pdf +redo-ifchange version.h table table.1.gz table.pdf diff --git a/default.h.do b/default.h.do @@ -0,0 +1,13 @@ +if [ -r $1.in ]; then + redo-ifchange $1.in version date + read VERSION <version + read DATE <date + sed -e "s/%VERSION%/$VERSION/g" \ + -e "s/%DATE%/$DATE/g" <$1.in +else + if [ -f $2.def.h ]; then + redo-ifchange $2.def.h + cat $2.def.h > $3 + fi +fi + diff --git a/defs.h b/defs.h @@ -1,38 +1,8 @@ -/* - * table - Command line utility to format and display CSV. - * Copyright (C) 2020 Страхиња Радић - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <https://www.gnu.org/licenses/>. - * - */ - -#ifndef __DEFS_H -#define __DEFS_H +/* See the file LICENSE for copyright and license details. */ #define _POSIX_C_SOURCE 200809L -#include <errno.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistr.h> -#include <unistdio.h> -#include <uniwidth.h> - -#define PROGRAMNAME "table" -#define VERSION "0.2.2" +#include "utf8.h" #define BUFSIZE 4096 #define SMALL_BUFSIZE 256 @@ -77,65 +47,63 @@ enum TABLE_INNER_DOUBLE_DOUBLE }; -static const uint8_t* table_symbols[][9] = +static const u32* table_symbols[][9] = { - [TABLE_SYMBOLS_ASCII] = { - // ascii - (uint8_t*)"+", (uint8_t*)"-", (uint8_t*)"+", - (uint8_t*)"|", (uint8_t*)" ", (uint8_t*)"|", - (uint8_t*)"+", (uint8_t*)"-", (uint8_t*)"+", - }, - [TABLE_SYMBOLS_SINGLE] = { - // single - (uint8_t*)"\u250c", (uint8_t*)"\u2500", (uint8_t*)"\u2510", - (uint8_t*)"\u2502", (uint8_t*)" ", (uint8_t*)"\u2502", - (uint8_t*)"\u2514", (uint8_t*)"\u2500", (uint8_t*)"\u2518", - }, - [TABLE_SYMBOLS_DOUBLE] = { - // double - (uint8_t*)"\u2554", (uint8_t*)"\u2550", (uint8_t*)"\u2557", - (uint8_t*)"\u2551", (uint8_t*)" ", (uint8_t*)"\u2551", - (uint8_t*)"\u255a", (uint8_t*)"\u2550", (uint8_t*)"\u255d", - } + [TABLE_SYMBOLS_ASCII] = { + // ascii + (u32*)L"+", (u32*)L"-", (u32*)L"+", + (u32*)L"|", (u32*)L" ", (u32*)L"|", + (u32*)L"+", (u32*)L"-", (u32*)L"+", + }, + [TABLE_SYMBOLS_SINGLE] = { + // single + (u32*)L"\u250c", (u32*)L"\u2500", (u32*)L"\u2510", + (u32*)L"\u2502", (u32*)L" ", (u32*)L"\u2502", + (u32*)L"\u2514", (u32*)L"\u2500", (u32*)L"\u2518", + }, + [TABLE_SYMBOLS_DOUBLE] = { + // double + (u32*)L"\u2554", (u32*)L"\u2550", (u32*)L"\u2557", + (u32*)L"\u2551", (u32*)L" ", (u32*)L"\u2551", + (u32*)L"\u255a", (u32*)L"\u2550", (u32*)L"\u255d", + } }; -static const uint8_t* table_inner_symbols[][3] = +static const u32* table_inner_symbols[][3] = { [TABLE_INNER_ASCII_ASCII] = { // ascii -> ascii - (uint8_t*)"+", - (uint8_t*)"|", - (uint8_t*)"+", + (u32*)L"+", + (u32*)L"|", + (u32*)L"+", }, [TABLE_INNER_SINGLE_SINGLE] = { // single -> single - (uint8_t*)"\u252c", - (uint8_t*)"\u2502", - (uint8_t*)"\u2534", + (u32*)L"\u252c", + (u32*)L"\u2502", + (u32*)L"\u2534", }, [TABLE_INNER_SINGLE_DOUBLE] = { // single -> double - (uint8_t*)"\u2565", - (uint8_t*)"\u2551", - (uint8_t*)"\u2568", + (u32*)L"\u2565", + (u32*)L"\u2551", + (u32*)L"\u2568", }, [TABLE_INNER_DOUBLE_SINGLE] = { // double -> single - (uint8_t*)"\u2564", - (uint8_t*)"\u2502", - (uint8_t*)"\u2567", + (u32*)L"\u2564", + (u32*)L"\u2502", + (u32*)L"\u2567", }, [TABLE_INNER_DOUBLE_DOUBLE] = { // double -> double - (uint8_t*)"\u2566", - (uint8_t*)"\u2551", - (uint8_t*)"\u2569", + (u32*)L"\u2566", + (u32*)L"\u2551", + (u32*)L"\u2569", } }; -#endif - diff --git a/table.c b/table.c @@ -1,748 +1,787 @@ -/* - * table - Command line utility to format and display CSV. - * Copyright (C) 2020 Страхиња Радић - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <https://www.gnu.org/licenses/>. - * - */ +/* See the file LICENSE for copyright and license details. */ + +#define _POSIX_C_SOURCE 200809L + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include "defs.h" +#include "version.h" #define CHECKEXITNOMEM(ptr) { if (!ptr) exit(error(ENOMEM, \ - (uint8_t*)"Memory allocation failed (out of memory?)")); } + "Memory allocation failed (out of memory?)")); } #define CALLOC(ptr, ptrtype, nmemb) { ptr = calloc(nmemb, sizeof(ptrtype)); \ - CHECKEXITNOMEM(ptr) } + CHECKEXITNOMEM(ptr) } #define REALLOC(ptr, ptrtype, newsize) { ptrtype* newptr = realloc(ptr, newsize); \ - CHECKEXITNOMEM(newptr) \ - ptr = newptr; } + CHECKEXITNOMEM(newptr) \ + ptr = newptr; } #define REALLOCARRAY(ptr, membtype, newcount) \ - REALLOC(ptr, membtype, sizeof(membtype) * newcount) + REALLOC(ptr, membtype, sizeof(membtype) * newcount) #define CHECKCOPY(token, ptoken, token_size, parg) { \ - if (ptoken + 2 > token + token_size) \ - { \ - size_t old_size = token_size; \ - token_size += BUFSIZE; \ - REALLOC(token, char, token_size) \ - ptoken = token + old_size - 1; \ - } \ - *ptoken++ = *parg++; } + if (ptoken + 2 > token + token_size) \ + { \ + size_t old_size = token_size; \ + token_size += BUFSIZE; \ + REALLOC(token, char, token_size) \ + ptoken = token + old_size - 1; \ + } \ + *ptoken++ = *parg++; } #define CHECKSET(format, pformat, format_size, num) { \ - if (pformat + 2 > format + format_size) \ - { \ - size_t old_size = format_size; \ - format_size += BUFSIZE; \ - REALLOC(format, ULONG, format_size) \ - pformat = format + old_size - 1; \ - } \ - *pformat++ = num; } - -size_t colno = 0; -size_t lineno = 0; -int current_symbol_set = TABLE_SYMBOLS_DOUBLE; -int current_inner_symbol_set = TABLE_INNER_DOUBLE_SINGLE; -size_t current_table_column = 0; -size_t current_rune_column = 0; -size_t table_columns = 0; -size_t rune_columns = 80; -size_t tab_length = 8; -ucs4_t delimiter = ','; -ULONG* format = NULL; -size_t format_size = 0; -ULONG format_value = 0; -BOOL border_mode = FALSE; -BOOL handle_ansi = TRUE; -BOOL msdos = FALSE; -BOOL expand_tabs = FALSE; + if (pformat + 2 > format + format_size) \ + { \ + size_t old_size = format_size; \ + format_size += BUFSIZE; \ + REALLOC(format, ULONG, format_size) \ + pformat = format + old_size - 1; \ + } \ + *pformat++ = num; } + +size_t colno = 0; +size_t lineno = 0; +int current_symbol_set = TABLE_SYMBOLS_DOUBLE; +int current_inner_symbol_set = TABLE_INNER_DOUBLE_SINGLE; +size_t current_table_column = 0; +size_t current_rune_column = 0; +size_t table_columns = 0; +size_t rune_columns = 80; +size_t tab_length = 8; +u32 delimiter = L','; +ULONG* format = NULL; +size_t format_size = 0; +ULONG format_value = 0; +BOOL border_mode = FALSE; +BOOL handle_ansi = TRUE; +BOOL msdos = FALSE; +BOOL expand_tabs = FALSE; int version() { - printf("%s v%s\n", PROGRAMNAME, VERSION); - return 0; + printf("%s v%s\n", PROGRAMNAME, VERSION); + return 0; } int usage() { - printf("Usage: %s [-b|--border-mode] [-c <cols>|--columns=<cols>] [-d" - " <delim>|--delimiter=<delim>] [-f <format>|--format=<format>]" - " [-h|--help] [-m|--msdos] [-n|--no-ansi]" - " [-s <set>|--symbols=<set>] [-t|--expand-tabs] [-v|--version]\n", - PROGRAMNAME); - return 0; + printf("Usage: %s [-b|--border-mode] [-c <cols>|--columns=<cols>] [-d" + " <delim>|--delimiter=<delim>] [-f <format>|--format=<format>]" + " [-h|--help] [-m|--msdos] [-n|--no-ansi]" + " [-s <set>|--symbols=<set>] [-t|--expand-tabs] [-v|--version]\n", + PROGRAMNAME); + return 0; } int -error(int code, uint8_t* fmt, ...) +error(int code, char* fmt, ...) { - uint8_t buf[BUFSIZE]; - va_list args; - va_start(args, fmt); - u8_vsnprintf(buf, sizeof(buf), (const char*)fmt, args); - va_end(args); - fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf); - return code; + char buf[BUFSIZE]; + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), (const char*)fmt, args); + va_end(args); + fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf); + return code; } char* substr(const char* src, int start, int finish) { - int len = strlen(src); - if (finish > len) - finish = len; - int substr_len = finish-start; - if (substr_len < 0) - substr_len = 0; - char* result = NULL; - CALLOC(result, char, substr_len+1) - char* presult = result; - for (int i = start; i < finish && *(src+i) != '\0'; i++) - *presult++ = *(src+i); - *presult = 0; - - return result; + int len = strlen(src); + if (finish > len) + finish = len; + int substr_len = finish-start; + if (substr_len < 0) + substr_len = 0; + char* result = NULL; + CALLOC(result, char, substr_len+1) + char* presult = result; + for (int i = start; i < finish && *(src+i) != '\0'; i++) + *presult++ = *(src+i); + *presult = 0; + + return result; } BOOL startswith(const char* s, const char* what) { - if (!s || !what) - return 0; + if (!s || !what) + return 0; - char* subs = substr(s, 0, strlen(what)); - BOOL result = !strcmp(subs, what); + char* subs = substr(s, 0, strlen(what)); + BOOL result = !strcmp(subs, what); - free(subs); + free(subs); - return result; + return result; } int set_symbol_set(char* arg, int* current_symbol_set, - int* current_inner_symbol_set) + int* current_inner_symbol_set) { - if (!strcmp(arg, "aa")) - { - *current_symbol_set = TABLE_SYMBOLS_ASCII; - *current_inner_symbol_set = TABLE_INNER_ASCII_ASCII; - } - else if (!strcmp(arg, "ss")) - { - *current_symbol_set = TABLE_SYMBOLS_SINGLE; - *current_inner_symbol_set = TABLE_INNER_SINGLE_SINGLE; - } - else if (!strcmp(arg, "sd")) - { - *current_symbol_set = TABLE_SYMBOLS_SINGLE; - *current_inner_symbol_set = TABLE_INNER_SINGLE_DOUBLE; - } - else if (!strcmp(arg, "ds")) - { - *current_symbol_set = TABLE_SYMBOLS_DOUBLE; - *current_inner_symbol_set = TABLE_INNER_DOUBLE_SINGLE; - } - else if (!strcmp(arg, "dd")) - { - *current_symbol_set = TABLE_SYMBOLS_DOUBLE; - *current_inner_symbol_set = TABLE_INNER_DOUBLE_DOUBLE; - } - else - { - return error(1, (uint8_t*)"Invalid symbol set: %s", arg); - } - return 0; + if (!strcmp(arg, "aa")) + { + *current_symbol_set = TABLE_SYMBOLS_ASCII; + *current_inner_symbol_set = TABLE_INNER_ASCII_ASCII; + } + else if (!strcmp(arg, "ss")) + { + *current_symbol_set = TABLE_SYMBOLS_SINGLE; + *current_inner_symbol_set = TABLE_INNER_SINGLE_SINGLE; + } + else if (!strcmp(arg, "sd")) + { + *current_symbol_set = TABLE_SYMBOLS_SINGLE; + *current_inner_symbol_set = TABLE_INNER_SINGLE_DOUBLE; + } + else if (!strcmp(arg, "ds")) + { + *current_symbol_set = TABLE_SYMBOLS_DOUBLE; + *current_inner_symbol_set = TABLE_INNER_DOUBLE_SINGLE; + } + else if (!strcmp(arg, "dd")) + { + *current_symbol_set = TABLE_SYMBOLS_DOUBLE; + *current_inner_symbol_set = TABLE_INNER_DOUBLE_DOUBLE; + } + else + { + return error(1, "Invalid symbol set: %s", arg); + } + return 0; } int set_columns(const char* arg, size_t* cols) { - size_t c = strtol(arg, NULL, 10); - if (errno == EINVAL || errno == ERANGE) - return error(1, (uint8_t*)"Invalid numeric value: %s", arg); - else - *cols = c; - return 0; + size_t c = strtol(arg, NULL, 10); + if (errno == EINVAL || errno == ERANGE) + return error(1, "Invalid numeric value: %s", arg); + else + *cols = c; + return 0; } int -set_delimiter(uint8_t* arg, ucs4_t* delimiter) +set_delimiter(u8* arg, u32* delimiter) { - size_t arg_len = u8_strlen(arg); - size_t delimiter_len = 0; - uint8_t* parg = arg; - ucs4_t uch; - - while (parg && *parg) - { - delimiter_len = u8_mbtouc(&uch, parg, arg_len); - if (uch != (ucs4_t)'"' && uch != (ucs4_t)'\'') - { - *delimiter = uch; - return 0; - } - parg += delimiter_len; - } - - return -1; + size_t delimiter_len = 0; + u8* parg = arg; + u32 uch; + + while (parg && *parg) + { + u8_char_to_u32(&uch, parg, &delimiter_len); + if (uch != (u32)L'"' && uch != (u32)L'\'') + { + *delimiter = uch; + return 0; + } + parg += delimiter_len; + } + + return -1; } int set_format(const char* arg, ULONG** format, size_t* format_size) { - const char* parg = arg; - ULONG* pformat = NULL; - char* token = NULL; - size_t token_size = BUFSIZE; - char* ptoken = NULL; - ULONG num; - - CALLOC(token, char, token_size) - ptoken = token; - *format_size = SMALL_BUFSIZE; - CALLOC(*format, ULONG, *format_size) - pformat = *format; - - while (*parg) - { - if (!((*parg >= '0' && *parg <= '9') || *parg == ':')) - { - free(token); - free(*format); - *format = NULL; - return 1; - } - if (*parg == ':') - { - if (parg == arg || !*(parg+1)) - { - free(token); - free(*format); - *format = NULL; - return 2; - } - *ptoken = 0; - num = strtol(token, NULL, 10); - if (errno) - { - perror("strtol"); - free(token); - free(*format); - *format = NULL; - return 3; - } - CHECKSET(*format, pformat, *format_size, num) - *token = 0; - ptoken = token; - parg++; - } - else - CHECKCOPY(token, ptoken, token_size, parg) - } - if (*token) - { - *ptoken = 0; - num = strtol(token, NULL, 10); - if (errno) - { - perror("strtol"); - free(token); - free(*format); - *format = NULL; - return 3; - } - CHECKSET(*format, pformat, *format_size, num) - } - - free(token); - - return 0; + const char* parg = arg; + ULONG* pformat = NULL; + char* token = NULL; + size_t token_size = BUFSIZE; + char* ptoken = NULL; + ULONG num; + + CALLOC(token, char, token_size) + ptoken = token; + *format_size = SMALL_BUFSIZE; + CALLOC(*format, ULONG, *format_size) + pformat = *format; + + while (*parg) + { + if (!((*parg >= '0' && *parg <= '9') || *parg == ':')) + { + free(token); + free(*format); + *format = NULL; + return 1; + } + if (*parg == ':') + { + if (parg == arg || !*(parg+1)) + { + free(token); + free(*format); + *format = NULL; + return 2; + } + *ptoken = 0; + num = strtol(token, NULL, 10); + if (errno) + { + perror("strtol"); + free(token); + free(*format); + *format = NULL; + return 3; + } + CHECKSET(*format, pformat, *format_size, num) + *token = 0; + ptoken = token; + parg++; + } + else + CHECKCOPY(token, ptoken, token_size, parg) + } + if (*token) + { + *ptoken = 0; + num = strtol(token, NULL, 10); + if (errno) + { + perror("strtol"); + free(token); + free(*format); + *format = NULL; + return 3; + } + CHECKSET(*format, pformat, *format_size, num) + } + + free(token); + + return 0; } /* Return number of columns based on the input line */ size_t -number_of_columns(const uint8_t* input, ucs4_t delimiter) +number_of_columns(const u8* input, u32 delimiter) { - ucs4_t uch; - size_t ch_len = 0; - size_t result = 1; - const uint8_t* pinput = NULL; - size_t input_len = 0; - - if (!input) - return result; - - input_len = u8_strlen(input); - pinput = input; - - while (pinput && *pinput) - { - ch_len = u8_mbtouc(&uch, pinput, input_len); - if (uch == delimiter) - result++; - pinput += ch_len; - } - - return result; + u32 uch; + size_t ch_len = 0; + size_t result = 1; + const u8* pinput = NULL; + + pinput = input; + + if (!input) + return result; + + while (pinput && *pinput) + { + u8_char_to_u32(&uch, pinput, &ch_len); + if (uch == delimiter) + result++; + pinput += ch_len; + } + + return result; } UINT round_div(UINT a, UINT b) { - return (a + (b/2)) / b; + return (a + (b/2)) / b; } BOOL within_column(size_t column_start) { - return format - ? (current_rune_column < - column_start + *(format+current_table_column)) - : (current_rune_column < - column_start + format_value); + return format + ? (current_rune_column < + column_start + *(format+current_table_column)) + : (current_rune_column < + column_start + format_value); } int main(int argc, char** argv) { - char* arg; - Command cmd = CMD_NONE; - char* filename = NULL; - - while ((arg = *++argv)) - { - if (*arg == '-') - { - arg++; - char c = *arg++; - if (c == '-') - { - if (!strcmp(arg, "version")) - cmd = CMD_VERSION; - else if (startswith(arg, "border-mode")) - { - arg += strlen("border-mode"); - border_mode = TRUE; - } - else if (startswith(arg, "columns=")) - { - arg += strlen("columns="); - if (set_columns(arg, &rune_columns)) - return error(EINVAL, (uint8_t*)"Invalid argument: '%s'", - arg); - } - else if (startswith(arg, "delimiter=")) - { - arg += strlen("delimiter="); - if (set_delimiter((uint8_t*)arg, &delimiter)) - return error(EINVAL, (uint8_t*)"Invalid argument: '%s'", - arg); - } - else if (startswith(arg, "expand-tabs")) - { - arg += strlen("expand-tabs"); - expand_tabs = TRUE; - } - else if (startswith(arg, "format=")) - { - arg += strlen("format="); - set_format(arg, &format, &format_size); - } - else if (startswith(arg, "msdos")) - { - arg += strlen("msdos"); - msdos = TRUE; - } - else if (startswith(arg, "no-ansi")) - { - arg += strlen("no-ansi"); - handle_ansi = FALSE; - } - else if (startswith(arg, "symbols=")) - { - arg += strlen("symbols="); - if (set_symbol_set(arg, &current_symbol_set, - &current_inner_symbol_set)) - return error(EINVAL, (uint8_t*)"Invalid argument: '%s'", - arg); - } - else if (!strcmp(arg, "help")) - return usage(); - else - { - error(EINVAL, (uint8_t*)"Invalid argument: --%s", arg); - return usage(); - } - } - else - { - switch (c) - { - case 'b': - border_mode = TRUE; - break; - case 'c': - cmd = CMD_COLUMNS; - break; - case 'd': - cmd = CMD_DELIMITER; - break; - case 'f': - cmd = CMD_FORMAT; - break; - case 'h': - return usage(); - break; - case 'm': - msdos = TRUE; - break; - case 'n': - handle_ansi = FALSE; - break; - case 's': - cmd = CMD_SYMBOLS; - break; - case 't': - expand_tabs = TRUE; - break; - case 'v': - cmd = CMD_VERSION; - break; - default: - error(EINVAL, (uint8_t*)"Invalid argument: -%c", c); - return usage(); - } - } - } - else - { - if (cmd == CMD_COLUMNS) - { - if (set_columns(arg, &rune_columns)) - return error(EINVAL, (uint8_t*)"Invalid argument: '%s'", arg); - } - else if (cmd == CMD_DELIMITER) - { - if (set_delimiter((uint8_t*)arg, &delimiter)) - return error(EINVAL, (uint8_t*)"Invalid argument: '%s'", arg); - } - else if (cmd == CMD_FORMAT) - { - if (set_format(arg, &format, &format_size)) - return error(EINVAL, (uint8_t*)"Invalid argument: '%s'", arg); - } - else if (cmd == CMD_SYMBOLS) - { - if (set_symbol_set(arg, &current_symbol_set, - &current_inner_symbol_set)) - return error(EINVAL, (uint8_t*)"Invalid argument: '%s'", arg); - } - else - filename = arg; - cmd = CMD_NONE; - } - } - - if (cmd == CMD_VERSION) - return version(); - - FILE* input = NULL; - if (filename) - { - input = fopen(filename, "r"); - if (!input) - return error(ENOENT, (uint8_t*)"File not found: %s", filename); - } - else - input = stdin; - - uint8_t* line = NULL; - size_t pline_len = 0; - uint8_t* pline = NULL; - BOOL quote = FALSE; - ucs4_t uch; - size_t ch_len = 0; - size_t output_lines = 0; - size_t column_start = 0; - - CALLOC(line, uint8_t, BUFSIZE) - - while (!feof(input)) - { - char* eol = NULL; - if (!fgets((char*)line, sizeof(uint8_t) * BUFSIZE-1, input)) - break; - - eol = strchr((char*)line, '\n'); - if (eol) - { - if (msdos && *line != '\n' && *(eol-1) == '\r') - *(eol-1) = 0; - *eol = 0; - } - - if (!*line) - continue; - - quote = FALSE; - pline = line; - colno = 0; - current_rune_column = 0; - - /* Top border */ - if (lineno == 0) - { - if (!border_mode) - table_columns = number_of_columns(line, delimiter); - else - table_columns = 1; - - /* Sanity check */ - if (rune_columns < table_columns+2) - rune_columns = table_columns+2; - - if (format && !border_mode) - { - ULONG* pformat = format; - ULONG format_sum = 0; - while (*pformat) - format_sum += *pformat++; - pformat = format; - while (*pformat && format_sum) - { - *pformat = round_div((rune_columns-table_columns-2) - * (*pformat), format_sum); - pformat++; - } - } - else - format_value = (rune_columns-table_columns-2) / table_columns; - - current_table_column = 0; - column_start = 0; - while (current_table_column < table_columns) - { - if (current_rune_column == 0) - { - printf("%s", table_symbols[current_symbol_set][0]); - column_start++; - } - else if (!within_column(column_start)) - { - if (current_table_column == table_columns-1) - { - printf("%s", table_symbols[current_symbol_set][2]); - current_table_column++; - } - else - { - printf("%s", table_inner_symbols[current_inner_symbol_set][0]); - column_start++; - if (format) - column_start += *(format+current_table_column); - else - column_start += format_value; - current_table_column++; - } - } - else - printf("%s", table_symbols[current_symbol_set][1]); - current_rune_column++; - } - printf("\n"); - output_lines++; - } - - /* Inner rows */ - current_table_column = 0; - current_rune_column = 0; - column_start = 0; - - printf("%s", table_symbols[current_symbol_set][3]); - - if (handle_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_ON); - - while (pline && *pline) - { - if (*pline == '"') - { - quote = !quote; - pline++; - colno++; - } - else - { - uint8_t* pch_end; - pline_len = u8_strlen(line); - ch_len = u8_mbtouc(&uch, pline, pline_len); - if (border_mode) - { - pch_end = pline + ch_len; - if (within_column(column_start)) - { - while (pline != pch_end) - printf("%c", *pline++); - current_rune_column++; - } - pline = pch_end; - colno += ch_len; - } - else if (ch_len <= 0) - { - if (within_column(column_start)) - { - printf("%c", *pline++); - current_rune_column++; - } - colno++; - } - else if (uch == '\t' && expand_tabs) - { - while (within_column(column_start)) - { - printf(" "); - current_rune_column++; - if (current_rune_column % tab_length == 0) - break; - } - pline++; - colno++; - } - else if (uch == delimiter && !quote - && current_table_column < table_columns-1) - { - if (handle_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_OFF); - - while (within_column(column_start)) - { - printf(" "); - current_rune_column++; - } - column_start++; - if (format) - column_start += *(format+current_table_column); - else - column_start += format_value; - if (current_table_column != table_columns-1) - { - printf("%s", table_inner_symbols[current_inner_symbol_set][1]); - current_rune_column++; - current_table_column++; - } - - if (handle_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_ON); - - pline += ch_len; - colno += ch_len; - } - else - { - pch_end = pline + ch_len; - if (within_column(column_start)) - { - while (pline != pch_end) - printf("%c", *pline++); - current_rune_column++; - } - pline = pch_end; - colno += ch_len; - } - } - } - - if (handle_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_OFF); - - while (current_table_column < table_columns-1) - { - while (within_column(column_start)) - { - printf(" "); - current_rune_column++; - } - printf("%s", table_inner_symbols[current_inner_symbol_set][1]); - column_start++; - if (format) - column_start += *(format+current_table_column); - else - column_start += format_value; - current_table_column++; - current_rune_column++; - } - while (within_column(column_start)) - { - printf(" "); - current_rune_column++; - } - - printf("%s\n", table_symbols[current_symbol_set][5]); - - output_lines++; - lineno++; - } - - fclose(input); - free(line); - - /* Bottom border */ - if (output_lines) - { - current_table_column = 0; - current_rune_column = 0; - column_start = 0; - while (current_table_column < table_columns) - { - if (current_rune_column == 0) - { - printf("%s", table_symbols[current_symbol_set][6]); - column_start++; - } - else if (!within_column(column_start)) - { - if (current_table_column == table_columns-1) - { - printf("%s", table_symbols[current_symbol_set][8]); - current_table_column++; - } - else - { - printf("%s", table_inner_symbols[current_inner_symbol_set][2]); - column_start++; - if (format) - column_start += *(format+current_table_column); - else - column_start += format_value; - current_table_column++; - } - } - else - printf("%s", table_symbols[current_symbol_set][7]); - current_rune_column++; - } - printf("\n"); - output_lines++; - } - - if (format) - free(format); - - return 0; + char* arg; + Command cmd = CMD_NONE; + char* filename = NULL; + + while ((arg = *++argv)) + { + if (*arg == '-') + { + arg++; + char c = *arg++; + if (c == '-') + { + if (!strcmp(arg, "version")) + cmd = CMD_VERSION; + else if (startswith(arg, "border-mode")) + { + arg += strlen("border-mode"); + border_mode = TRUE; + } + else if (startswith(arg, "columns=")) + { + arg += strlen("columns="); + if (set_columns(arg, &rune_columns)) + return error(EINVAL, "Invalid argument: '%s'", + arg); + } + else if (startswith(arg, "delimiter=")) + { + arg += strlen("delimiter="); + if (set_delimiter((u8*)arg, &delimiter)) + return error(EINVAL, "Invalid argument: '%s'", + arg); + } + else if (startswith(arg, "expand-tabs")) + { + arg += strlen("expand-tabs"); + expand_tabs = TRUE; + } + else if (startswith(arg, "format=")) + { + arg += strlen("format="); + set_format(arg, &format, &format_size); + } + else if (startswith(arg, "msdos")) + { + arg += strlen("msdos"); + msdos = TRUE; + } + else if (startswith(arg, "no-ansi")) + { + arg += strlen("no-ansi"); + handle_ansi = FALSE; + } + else if (startswith(arg, "symbols=")) + { + arg += strlen("symbols="); + if (set_symbol_set(arg, &current_symbol_set, + &current_inner_symbol_set)) + return error(EINVAL, "Invalid argument: '%s'", + arg); + } + else if (!strcmp(arg, "help")) + return usage(); + else + { + error(EINVAL, "Invalid argument: --%s", arg); + return usage(); + } + } + else + { + switch (c) + { + case 'b': + border_mode = TRUE; + break; + case 'c': + cmd = CMD_COLUMNS; + break; + case 'd': + cmd = CMD_DELIMITER; + break; + case 'f': + cmd = CMD_FORMAT; + break; + case 'h': + return usage(); + break; + case 'm': + msdos = TRUE; + break; + case 'n': + handle_ansi = FALSE; + break; + case 's': + cmd = CMD_SYMBOLS; + break; + case 't': + expand_tabs = TRUE; + break; + case 'v': + cmd = CMD_VERSION; + break; + default: + error(EINVAL, "Invalid argument: -%c", c); + return usage(); + } + } + } + else + { + if (cmd == CMD_COLUMNS) + { + if (set_columns(arg, &rune_columns)) + return error(EINVAL, "Invalid argument: '%s'", arg); + } + else if (cmd == CMD_DELIMITER) + { + if (set_delimiter((u8*)arg, &delimiter)) + return error(EINVAL, "Invalid argument: '%s'", arg); + } + else if (cmd == CMD_FORMAT) + { + if (set_format(arg, &format, &format_size)) + return error(EINVAL, "Invalid argument: '%s'", arg); + } + else if (cmd == CMD_SYMBOLS) + { + if (set_symbol_set(arg, &current_symbol_set, + &current_inner_symbol_set)) + return error(EINVAL, "Invalid argument: '%s'", arg); + } + else + filename = arg; + cmd = CMD_NONE; + } + } + + if (cmd == CMD_VERSION) + return version(); + + FILE* input = NULL; + if (filename) + { + input = fopen(filename, "r"); + if (!input) + return error(ENOENT, "File not found: %s", filename); + } + else + input = stdin; + + u8* line = NULL; + u8* pline = NULL; + BOOL quote = FALSE; + u32 uch; + u8 u8ch[7]; + size_t u8len = 0; + size_t ch_len = 0; + size_t output_lines = 0; + size_t column_start = 0; + + CALLOC(line, u8, BUFSIZE) + + while (!feof(input)) + { + char* eol = NULL; + if (!fgets((char*)line, BUFSIZE-1, input)) + break; + + eol = strchr((char*)line, '\n'); + if (eol) + { + if (msdos && *line != '\n' && *(eol-1) == '\r') + *(eol-1) = 0; + *eol = 0; + } + + if (!*line) + continue; + + quote = FALSE; + pline = line; + colno = 0; + current_rune_column = 0; + + /* Top border */ + if (lineno == 0) + { + if (!border_mode) + table_columns = number_of_columns(line, delimiter); + else + table_columns = 1; + + /* Sanity check */ + if (rune_columns < table_columns+2) + rune_columns = table_columns+2; + + if (format && !border_mode) + { + ULONG* pformat = format; + ULONG format_sum = 0; + while (*pformat) + format_sum += *pformat++; + pformat = format; + while (*pformat && format_sum) + { + *pformat = round_div((rune_columns-table_columns-2) + * (*pformat), format_sum); + pformat++; + } + } + else + format_value = (rune_columns-table_columns-2) / table_columns; + + current_table_column = 0; + column_start = 0; + while (current_table_column < table_columns) + { + if (current_rune_column == 0) + { + u8len = u32_char_to_u8(u8ch, + *table_symbols[ + current_symbol_set][0]); + u8ch[u8len] = 0; + printf("%s", u8ch); + column_start++; + } + else if (!within_column(column_start)) + { + if (current_table_column == table_columns-1) + { + u8len = u32_char_to_u8(u8ch, + *table_symbols[ + current_symbol_set][2]); + u8ch[u8len] = 0; + printf("%s", u8ch); + current_table_column++; + } + else + { + u8len = u32_char_to_u8(u8ch, + *table_inner_symbols[ + current_inner_symbol_set][0]); + u8ch[u8len] = 0; + printf("%s", u8ch); + column_start++; + if (format) + column_start += *(format+current_table_column); + else + column_start += format_value; + current_table_column++; + } + } + else + { + u8len = u32_char_to_u8(u8ch, + *table_symbols[ + current_symbol_set][1]); + u8ch[u8len] = 0; + printf("%s", u8ch); + } + current_rune_column++; + } + printf("\n"); + output_lines++; + } + + /* Inner rows */ + current_table_column = 0; + current_rune_column = 0; + column_start = 0; + + u8len = u32_char_to_u8(u8ch, + *table_symbols[current_symbol_set][3]); + u8ch[u8len] = 0; + printf("%s", u8ch); + + if (handle_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_ON); + + while (pline && *pline) + { + if (*pline == '"') + { + quote = !quote; + pline++; + colno++; + } + else + { + u8* pch_end; + u8_char_to_u32(&uch, pline, &ch_len); + if (border_mode) + { + pch_end = pline + ch_len; + if (within_column(column_start)) + { + while (pline != pch_end) + printf("%c", *pline++); + current_rune_column++; + } + pline = pch_end; + colno += ch_len; + } + else if (ch_len <= 0) + { + if (within_column(column_start)) + { + printf("%c", *pline++); + current_rune_column++; + } + colno++; + } + else if (uch == (u32)L'\t' && expand_tabs) + { + while (within_column(column_start)) + { + printf(" "); + current_rune_column++; + if (current_rune_column % tab_length == 0) + break; + } + pline++; + colno++; + } + else if (uch == delimiter && !quote + && current_table_column < table_columns-1) + { + if (handle_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_OFF); + + while (within_column(column_start)) + { + printf(" "); + current_rune_column++; + } + column_start++; + if (format) + column_start += *(format+current_table_column); + else + column_start += format_value; + if (current_table_column != table_columns-1) + { + u8len = u32_char_to_u8(u8ch, + *table_inner_symbols[ + current_inner_symbol_set][1]); + u8ch[u8len] = 0; + printf("%s", u8ch); + current_rune_column++; + current_table_column++; + } + + if (handle_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_ON); + + pline += ch_len; + colno += ch_len; + } + else + { + pch_end = pline + ch_len; + if (within_column(column_start)) + { + while (pline != pch_end) + printf("%c", *pline++); + current_rune_column++; + } + pline = pch_end; + colno += ch_len; + } + } + } + + if (handle_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_OFF); + + while (current_table_column < table_columns-1) + { + while (within_column(column_start)) + { + printf(" "); + current_rune_column++; + } + u8len = u32_char_to_u8(u8ch, + *table_inner_symbols[ + current_inner_symbol_set][1]); + u8ch[u8len] = 0; + printf("%s", u8ch); + column_start++; + if (format) + column_start += *(format+current_table_column); + else + column_start += format_value; + current_table_column++; + current_rune_column++; + } + while (within_column(column_start)) + { + printf(" "); + current_rune_column++; + } + + u8len = u32_char_to_u8(u8ch, + *table_symbols[current_symbol_set][5]); + u8ch[u8len] = 0; + printf("%s\n", u8ch); + + output_lines++; + lineno++; + } + + fclose(input); + free(line); + + /* Bottom border */ + if (output_lines) + { + current_table_column = 0; + current_rune_column = 0; + column_start = 0; + while (current_table_column < table_columns) + { + if (current_rune_column == 0) + { + u8len = u32_char_to_u8(u8ch, + *table_symbols[ + current_symbol_set][6]); + u8ch[u8len] = 0; + printf("%s", u8ch); + column_start++; + } + else if (!within_column(column_start)) + { + if (current_table_column == table_columns-1) + { + u8len = u32_char_to_u8(u8ch, + *table_symbols[ + current_symbol_set][8]); + u8ch[u8len] = 0; + printf("%s", u8ch); + current_table_column++; + } + else + { + u8len = u32_char_to_u8(u8ch, + *table_inner_symbols[ + current_inner_symbol_set][2]); + u8ch[u8len] = 0; + printf("%s", u8ch); + column_start++; + if (format) + column_start += *(format+current_table_column); + else + column_start += format_value; + current_table_column++; + } + } + else + { + u8len = u32_char_to_u8(u8ch, + *table_symbols[ + current_symbol_set][7]); + u8ch[u8len] = 0; + printf("%s", u8ch); + } + current_rune_column++; + } + printf("\n"); + output_lines++; + } + + if (format) + free(format); + + return 0; } diff --git a/table.do b/table.do @@ -1,3 +1,9 @@ -redo-ifchange table.o table.c defs.h -${TABLE_CC:-gcc} -g -Wall -std=c99 -o $3 table.o -lunistring +for f in *.h.in; do + echo $f | sed -e's/\.in$//g' +done | xargs redo-ifchange +for f in *.h *.c; do + echo $f + echo $f | sed -e's/\.c$/.o/g' +done | xargs redo-ifchange +${TABLE_CC:-gcc} -g -Wall -std=c99 -o $3 table.o utf8.o diff --git a/utf8.c b/utf8.c @@ -0,0 +1,161 @@ +/* See the file LICENSE for copyright and license details. */ + +#include "utf8.h" + +/* + * 00000000 -- 0000007F: 0xxxxxxx + * (2^7 = 128 chars) + * 00000080 -- 000007FF: 110xxxxx 10xxxxxx + * (2^5 = 32 chars) + * 00000800 -- 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx + * (2^4 = 16 chars) + * 00010000 -- 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * (2^3 = 8 chars) + * 00200000 -- 007FFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * (2^2 = 4 chars) + * 00800000 -- 00FFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * 10xxxxxx + * (2^1 = 2 chars) + */ + +const int +utf_length_table[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 32 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 128 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 160 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 192 */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 224 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1, /* 256 */ +}; + +const int +extract_masks[6] = { + XMASK1, XMASK2, XMASK3, XMASK4, XMASK5, XMASK6 +}; + +int +u8_char_to_u32(u32* to, const u8 *from, size_t* from_delta) +{ + const u8* pfrom = from; + if (!from) + return 1; + *from_delta = 0; + int len = utf_length_table[*from]; + *to = *from & extract_masks[len-1]; + for (int i = 1; i < len; i++) + { + pfrom = from + i; + if (!*pfrom) + return 1; + *to <<= 6; + *to |= *pfrom & XMASKR; + } + *from_delta = len; + + return 0; +} + +size_t +u32_char_to_u8(u8 *to, const u32 from) +{ + size_t len = 0; + u8 start = 0; + u32 cfrom = from; + if (from >= BOUND6) + { + start = START6; + len = 6; + } + else if (from >= BOUND5) + { + start = START5; + len = 5; + } + else if (from >= BOUND4) + { + start = START4; + len = 4; + } + else if (from >= BOUND3) + { + start = START3; + len = 3; + } + else if (from >= BOUND2) + { + start = START2; + len = 2; + } + else + { + start = START1; + len = 1; + } + for (int i = len-1; i > 0; i--) + { + to[i] = STARTR | (cfrom & XMASKR); + cfrom >>= 6; + } + to[0] = start | cfrom; + return len; +} + +int +u8_to_u32(u32 *to, const u8 *from, size_t* from_delta) +{ + const u8* pfrom = from; + u32* pto = to; + if (!from) + return 1; + size_t delta = 0; + *from_delta = 0; + while (*pfrom) + { + int result = u8_char_to_u32(pto, pfrom, &delta); + if (result) + return result; + pto++; + pfrom += delta; + *from_delta += delta; + } + *pto = 0; + return 0; +} + +size_t +u32_to_u8(u8 *to, const u32* from) +{ + u8* pto = to; + const u32* pfrom = from; + size_t len = 0; + if (!from) + return 0; + while (*pfrom) + { + size_t delta = u32_char_to_u8(pto, *pfrom); + pto += delta; + len += delta; + pfrom++; + } + return len; +} + +size_t +u32_strlen(const u32* s) +{ + const u32* ps = s; + while (ps && *ps) + ps++; + return ps - s; +} + diff --git a/utf8.h b/utf8.h @@ -0,0 +1,40 @@ +/* See the file LICENSE for copyright and license details. */ + +#include <stdint.h> +#include <sys/types.h> + +#define XMASK1 0x7F /* b01111111 */ +#define XMASK2 0x1F /* b00011111 */ +#define XMASK3 0x0F /* b00001111 */ +#define XMASK4 0x07 /* b00000111 */ +#define XMASK5 0x03 /* b00000011 */ +#define XMASK6 0x01 /* b00000001 */ + +#define XMASKR 0x3F /* b00111111 */ + +/* +#define BOUND1 0x0*/ +#define BOUND2 0x80 +#define BOUND3 0x800 +#define BOUND4 0x10000 +#define BOUND5 0x200000 +#define BOUND6 0x800000 + +#define START1 0x00 /* b0xxxxxxx */ +#define START2 0xC0 /* b110xxxxx */ +#define START3 0xE0 /* b1110xxxx */ +#define START4 0xF0 /* b11110xxx */ +#define START5 0xF8 /* b111110xx */ +#define START6 0xFC /* b1111110x */ + +#define STARTR 0x80 /* b10xxxxxx */ + +typedef uint8_t u8; +typedef uint32_t u32; + +int u8_char_to_u32(u32* to, const u8* from, size_t* from_delta); +size_t u32_char_to_u8(u8* to, const u32 from); +int u8_to_u32(u32* to, const u8* from, size_t* from_delta); +size_t u32_to_u8(u8* to, const u32* from); +size_t u32_strlen(const u32* s); + diff --git a/version.h.in b/version.h.in @@ -0,0 +1,4 @@ +#define PROGRAMNAME "table" +#define DATE "%DATE%" +#define VERSION "%VERSION%" +