implemented main function

This commit is contained in:
Timerix 2025-08-10 21:39:51 +03:00
parent b08ec27629
commit c008d759ae
15 changed files with 359 additions and 224 deletions

2
dependencies/tlibc vendored

@ -1 +1 @@
Subproject commit d04aac567f105cc566db7b9b8e201c1a04e4ecea
Subproject commit bf56984482d83d1a178f4da6483fbd350457e438

View File

@ -34,13 +34,13 @@ case "$OS" in
EXEC_FILE="$PROJECT.exe"
SHARED_LIB_FILE="$PROJECT.dll"
INCLUDE="$INCLUDE "
LINKER_LIBS="-lpthread -lws2_32"
LINKER_LIBS="-lpthread -lws2_32 -lreadline"
;;
LINUX)
EXEC_FILE="$PROJECT"
SHARED_LIB_FILE="$PROJECT.so"
INCLUDE="$INCLUDE "
LINKER_LIBS=""
LINKER_LIBS="-lreadline"
;;
*)
error "operating system $OS has no configuration variants"

101
src/client.c Normal file
View File

@ -0,0 +1,101 @@
#include "client.h"
#include <readline/readline.h>
#include <readline/history.h>
#include "term.h"
#define inp_eq(LITERAL) str_equals(input, STR(LITERAL))
static const str greeting_art = STR(
" ^,,^ |\n"
" ( •·•) Meum! (o.o`7\n"
" / ` | Meum... |`˜ \\\n"
"\\(_,J J L l`,)/\n"
);
static const str farewell_art = STR(
" ^,,^ |\n"
" ( -.-) (>,<`7\n"
" / ` | Goodbye! |`˜ \\\n"
"\\(_,J J L l`,)/\n"
);
Result(void) client_run() {
Deferral(128);
if(!term_init()){
Return RESULT_ERROR("can't init terminal", false);
}
using_history();
fputs(greeting_art.data, stdout);
char* input_prev = NULL;
char* input_raw = NULL;
Defer(rl_free(input_prev));
str input = str_null;
while((input_raw = readline("> "))){
rl_free(input_prev);
input_prev = input_raw;
input = str_from_cstr(input_raw);
line_trim(&input, true);
add_history(input.data);
if(input.size == 0){
continue;
}
else if(inp_eq("q") || inp_eq("quit") || inp_eq("exit")){
fputs(farewell_art.data, stdout);
break;
}
else if(inp_eq("clear")){
term_clear();
}
else if(inp_eq("h") || inp_eq("help")){
}
else if(inp_eq("c") || inp_eq("connect")){
}
else {
printf("ERROR: unknown kommand: '%s'\n"
"Use 'h' to see list of avaliable commands\n",
input.data);
}
}
Return RESULT_VOID;
}
void line_trim(str* line, bool set_zero_at_end){
bool stop = false;
// loop forward
while(line->size > 0 && !stop){
char first_char = line->data[line->size - 1];
switch(first_char){
case '\0': case '\r': case '\n':
case '\t': case ' ':
line->data++;
line->size--;
break;
default:
stop = true;
break;
}
}
// loop backward
while(line->size > 0 && !stop)
{
char last_char = line->data[line->size - 1];
switch(last_char){
case '\0': case '\r': case '\n':
case '\t': case ' ':
line->size--;
break;
default:
stop = true;
break;
}
}
if(set_zero_at_end){
line->data[line->size] = '\0';
line->isZeroTerminated = true;
}
}

7
src/client.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "tlibc/errors.h"
Result(void) client_run();
/// @brief removes blank characters from start and end of the line
void line_trim(str* line, bool set_zero_at_end);

View File

@ -34,14 +34,6 @@ typedef struct IncrementalDB {
} IncrementalDB;
static const Magic32 TABLE_FILE_MAGIC = { .bytes = { 'I', 'D', 'B', 't' } };
#define IDB_VERSION 0x01
#define try_pthread(CALL) do {\
int r = CALL;\
if(r != 0){\
Return RESULT_ERROR(strerror(r), false);\
}\
} while(0)
void Table_close(Table* t){
@ -169,7 +161,7 @@ Result(IncrementalDB*) idb_open(str db_dir){
db->db_dir = str_copy(db_dir);
try_void(dir_create(db->db_dir.data));
HashMap_construct(&db->tables_map, Table*, TablePtr_destroy);
try_pthread(pthread_mutex_init(&db->mutex, NULL));
try_stderrcode(pthread_mutex_init(&db->mutex, NULL));
success = true;
Return RESULT_VALUE(p, db);
@ -185,7 +177,7 @@ void idb_close(IncrementalDB* db){
Result(Table*) idb_getOrCreateTable(IncrementalDB* db, str _table_name, u32 row_size){
Deferral(64);
// db lock
try_pthread(pthread_mutex_lock(&db->mutex));
try_stderrcode(pthread_mutex_lock(&db->mutex));
Defer(pthread_mutex_unlock(&db->mutex));
Table** tpp = HashMap_tryGetPtr(&db->tables_map, _table_name);
@ -208,7 +200,7 @@ Result(Table*) idb_getOrCreateTable(IncrementalDB* db, str _table_name, u32 row_
// value of *t must be set to zero or behavior of Table_close will be undefined
memset(t, 0, sizeof(Table));
t->db = db;
try_pthread(pthread_mutex_init(&t->mutex, NULL));
try_stderrcode(pthread_mutex_init(&t->mutex, NULL));
t->name = str_copy(_table_name);
t->table_file_path = str_from_cstr(
strcat_malloc(db->db_dir.data, path_seps, t->name.data, ".idb-table"));
@ -251,7 +243,7 @@ Result(Table*) idb_getOrCreateTable(IncrementalDB* db, str _table_name, u32 row_
Result(void) idb_getRows(Table* t, u64 id, void* dst, u64 count){
Deferral(16);
// table lock
try_pthread(pthread_mutex_lock(&t->mutex));
try_stderrcode(pthread_mutex_lock(&t->mutex));
Defer(pthread_mutex_unlock(&t->mutex));
if(id + count > t->row_count){
@ -274,7 +266,7 @@ Result(void) idb_getRows(Table* t, u64 id, void* dst, u64 count){
Result(void) idb_updateRows(Table* t, u64 id, const void* src, u64 count){
Deferral(16);
// table lock
try_pthread(pthread_mutex_lock(&t->mutex));
try_stderrcode(pthread_mutex_lock(&t->mutex));
Defer(pthread_mutex_unlock(&t->mutex));
if(id + count >= t->row_count){
@ -303,7 +295,7 @@ Result(void) idb_updateRows(Table* t, u64 id, const void* src, u64 count){
Result(u64) idb_pushRows(Table* t, const void* src, u64 count){
Deferral(16);
// table lock
try_pthread(pthread_mutex_lock(&t->mutex));
try_stderrcode(pthread_mutex_lock(&t->mutex));
Defer(pthread_mutex_unlock(&t->mutex));
try_void(Table_setDirtyBit(t, true));
@ -323,7 +315,7 @@ Result(u64) idb_pushRows(Table* t, const void* src, u64 count){
Result(u64) idb_getRowCount(Table* t){
Deferral(8);
// table lock
try_pthread(pthread_mutex_lock(&t->mutex));
try_stderrcode(pthread_mutex_lock(&t->mutex));
Defer(pthread_mutex_unlock(&t->mutex));
u64 count = t->row_count;
Return RESULT_VALUE(u, count);

View File

@ -1,7 +1,8 @@
#pragma once
#include "tlibc/errors.h"
#include "tlibc/string/str.h"
#define IDB_VERSION 1
typedef struct IncrementalDB IncrementalDB;
typedef struct Table Table;

View File

@ -1,197 +1,66 @@
#include "cryptography/cryptography.h"
#include "network/network.h"
#include "network/socket.h"
#include "tlibc/time.h"
#include "db/idb.h"
#include <pthread.h>
#include <assert.h>
#include "client.h"
#include "server.h"
Result(void) test_aes(){
Deferral(64);
const str password = STR("abobus");
const Array(const char) data = str_castTo_Array(STR("0123456789_hii_"));
u8 hash_buffer[hash_password_out_size];
// SHA256 accepts keys with size 16, 24 or 32
const u32 key_size = 32;
assert(key_size <= hash_password_out_size);
const Array(u8) key = Array_construct_size(hash_buffer, key_size);
hash_password(password, hash_buffer, 1e5);
str hash_str = hex_to_str(key, true);
printf("key [%i] %s\n", key.size, hash_str.data);
free(hash_str.data);
typedef enum ProgramMode {
Client,
Server,
} ProgramMode;
EncryptorAES encr;
EncryptorAES_create(&encr, key);
Array(u8) buffer = Array_alloc_size(EncryptorAES_calcDstSize(data.size));
EncryptorAES_encrypt(&encr, data, buffer);
str encrypted_str = hex_to_str(buffer, true);
printf("data encrypted (hex): %s\n", encrypted_str.data);
free(encrypted_str.data);
#define arg_is(LITERAL) str_equals(arg_str, STR(LITERAL))
DecryptorAES decr;
DecryptorAES_create(&decr, key);
u32 decrypted_size = 0;
DecryptorAES_decrypt(&decr, buffer, buffer, &decrypted_size);
str decrypted_str = str_copy(str_construct(buffer.data, decrypted_size, false));
printf("data decrypted (utf8): %s\n", decrypted_str.data);
free(decrypted_str.data);
free(buffer.data);
Return RESULT_VOID;
}
static pthread_mutex_t stdout_mutex = {0};
/// thread-safe print
#define printf_safe(ARGS...) {\
pthread_mutex_lock(&stdout_mutex);\
printf(ARGS);\
fflush(stdout);\
pthread_mutex_unlock(&stdout_mutex);\
}
/// removes blank characters from line end
void line_trim(str* line, bool set_zero_at_end){
bool stop = false;
while(line->size > 0 && !stop)
{
char last_char = line->data[line->size - 1];
switch(last_char){
case '\0': case '\r': case '\n':
case '\t': case ' ':
line->size--;
break;
default:
stop = true;
break;
}
}
if(set_zero_at_end){
line->data[line->size] = '\0';
line->isZeroTerminated = true;
}
}
void* test_server(void* data){
Deferral(64);
printf_safe("[server]: opening main socket\n");
try_fatal(Socket main_socket, i, socket_open_TCP());
Defer(socket_close(main_socket));
EndpointIPv4 server_end = EndpointIPv4_create(AddressIPv4_LOOPBACK, 24500);
try_fatal_void(socket_bind(main_socket, server_end));
try_fatal_void(socket_listen(main_socket, 64));
printf_safe("[server]: accepting client connection\n");
try_fatal(Socket client_socket, i, socket_accept(main_socket, NULL));
Defer(socket_close(client_socket));
// last byte is reserved for '\0'
Array(u8) buf = Array_construct(malloc(1024), u8, 1023);
printf_safe("[server]: receiving data from client\n");
while(true){
try_fatal(i32 read_n, i, socket_recv(client_socket, buf));
if(read_n == 0){
sleepMsec(20);
continue;
}
str message = str_construct(buf.data, read_n, false);
line_trim(&message, true);
printf_safe("[server]: received '%s'\n", message.data);
}
printf_safe("[server]: client socket closed\n");
perror("errno:");
Return NULL;
}
void* test_client(void* data){
Deferral(64);
printf_safe("[client]: opening socket\n");
try_fatal(Socket client_socket, i, socket_open_TCP());
Defer(socket_close(client_socket));
printf_safe("[client]: connecting to server\n");
EndpointIPv4 server_end = EndpointIPv4_create(AddressIPv4_LOOPBACK, 24500);
try_fatal_void(socket_connect(client_socket, server_end));
Array(u8) buf = Array_alloc(u8, 1024);
printf_safe("[client]: reading stdin\n");
while(fgets(buf.data, buf.size, stdin) != NULL){
str line = str_construct(buf.data, strlen(buf.data), true);
line_trim(&line, true);
if(str_equals(line, STR("/q"))){
printf_safe("[client]: quit\n");
break;
}
try_fatal_void(socket_send(client_socket, str_castTo_Array(line)));
}
printf_safe("[client]: closing connection\n");
Return NULL;
}
Result(void) test_network(){
Deferral(64);
if(pthread_mutex_init(&stdout_mutex, NULL) != 0){
Return RESULT_ERROR("can't init mutex", false);
}
pthread_t server_thread = {0};
if(pthread_create(&server_thread, NULL, test_server, NULL) != 0){
Return RESULT_ERROR("can't create server thread", false);
}
sleepMsec(100);
test_client(NULL);
printf_safe("[main]: joining server thread\n");
if(pthread_join(server_thread, NULL) != 0){
Return RESULT_ERROR("can't join server thread", false);
}
if(pthread_mutex_destroy(&stdout_mutex) != 0){
Return RESULT_ERROR("can't destroy mutex", false);
}
printf_safe("[main]: completed\n");
Return RESULT_VOID;
}
Result(void) test_db(){
Deferral(64);
try(IncrementalDB* db, p, idb_open(STR("idb")));
Defer(idb_close(db));
const u32 row_size = 8;
const u32 rows_count = 5;
const char const_rows[5][8] = {
"0123456", "bebra", "abobus", "q", "DIMOOON"
};
char buffer[512];
memset(buffer, 0, 512);
try(Table* t0, p, idb_getOrCreateTable(db, STR("test0"), row_size));
printf("table 'test0' created\n");
try_void(idb_pushRows(t0, const_rows, rows_count));
const u32 indices[] = { 0, 1, 4, 3, 4, 0 };
for(u32 i = 0; i < ARRAY_LEN(indices); i++){
try_void(idb_getRow(t0, indices[i], buffer));
printf("row %u: %s\n", indices[i], buffer);
}
Return RESULT_VOID;
}
int main(){
int main(const int argc, cstr const* argv){
Deferral(32);
ProgramMode mode = Client;
cstr server_endpoint_cstr;
for(int argi = 1; argi < argc; argi++){
str arg_str = str_from_cstr(argv[argi]);
if(arg_is("-h") || arg_is("--help")){
printf(
"USAGE:\n"
"no arguments Interactive client mode.\n"
"-h, --help Show this message.\n"
"-l, --listen [addr:port] Start server.\n"
);
Return 0;
}
if(arg_is("-l") || arg_is("--listen")){
if(mode != Client){
printf("program mode is set already\n");
Return 1;
}
mode = Server;
if(++argi >= argc){
printfe("ERROR: no endpoint specified\n");
Return 1;
}
server_endpoint_cstr = argv[argi];
}
else {
printfe("ERROR: unknown argument '%s'\n"
"Use '-h' to see list of avaliable arguments\n",
argv[argi]);
Return 1;
}
}
try_fatal_void(network_init());
Defer(network_deinit());
// try_fatal_void(test_aes());
// try_fatal_void(test_network());
try_fatal_void(test_db());
switch(mode){
case Client:
try_fatal_void(client_run());
break;
case Server:
try_fatal_void(server_run(server_endpoint_cstr));
break;
default:
printfe("ERROR: invalid program mode %i\n", mode);
Return 1;
}
Return 0;
}

View File

@ -19,11 +19,19 @@ EndpointIPv4 EndpointIPv4_fromSockaddr(struct sockaddr_in saddr){
}
//TODO Endpoint functions
AddressIPv4 AddressIPv4_fromStr(cstr);
AddressIPv4 AddressIPv4_fromStr(cstr s);
str AddressIPv4_toStr(AddressIPv4 address);
EndpointIPv4 EndpointIPv4_fromStr(cstr s);
EndpointIPv4 EndpointIPv4_fromStr(cstr s){
u32 a, b, c, d, p;
sscanf(s, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &p);
EndpointIPv4 e = (EndpointIPv4){
.address = AddressIPv4_fromBytes(a, b, c, d),
.port = p
};
return e;
}
str EndpointIPv4_toStr(EndpointIPv4 end);

View File

@ -16,9 +16,9 @@ typedef union AddressIPv4 {
#define AddressIPv4_INVALID AddressIPv4_fromBytes(255,255,255,255)
#define AddressIPv4_is_invalid(ADDR) (ADDR.UintBigEndian == (u32)~0)
#define AddressIPv4_fromBytes(A, B, C, D) ((AddressIPv4){.bytes={A,B,C,D}})
#define AddressIPv4_fromU32(N) ((AddressIPv4){.UintBigEndian=N})
AddressIPv4 AddressIPv4_fromStr(cstr);
#define AddressIPv4_fromBytes(A, B, C, D) ((AddressIPv4){ .bytes = {A,B,C,D} })
#define AddressIPv4_fromU32(N) ((AddressIPv4){ .UintBigEndian = N })
AddressIPv4 AddressIPv4_fromStr(cstr s);
str AddressIPv4_toStr(AddressIPv4 address);

View File

@ -1,4 +1,5 @@
#pragma once
#include "tlibc/errors.h"
#if !defined(KN_USE_WINSOCK)
#if defined(_WIN64) || defined(_WIN32)
@ -10,6 +11,9 @@
#if KN_USE_WINSOCK
#include <winsock2.h>
// There you can see what error codes mean.
#include <winerror.h>
#define RESULT_ERROR_SOCKET() RESULT_ERROR(sprintf_malloc(64, "Winsock error %i", WSAGetLastError()), true)
#else
#include <sys/types.h>
#include <sys/socket.h>
@ -17,4 +21,6 @@
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#define RESULT_ERROR_SOCKET() RESULT_ERROR(strerror(errno), false)
#endif

View File

@ -5,7 +5,7 @@
Result(Socket) socket_open_TCP(){
Socket s = socket(AF_INET, SOCK_STREAM, 0);
if(s == -1){
return RESULT_ERROR_ERRNO();
return RESULT_ERROR_SOCKET();
}
return RESULT_VALUE(i, s);
@ -21,20 +21,20 @@ void socket_close(Socket s){
Result(void) socket_shutdown(Socket s, SocketShutdownType direction){
if(shutdown(s, (int)direction) == -1)
return RESULT_ERROR_ERRNO();
return RESULT_ERROR_SOCKET();
return RESULT_VOID;
}
Result(void) socket_bind(Socket s, EndpointIPv4 local_end){
struct sockaddr_in sockaddr = EndpointIPv4_toSockaddr(local_end);
if(bind(s, (void*)&sockaddr, sizeof(sockaddr)) != 0)
return RESULT_ERROR_ERRNO();
return RESULT_ERROR_SOCKET();
return RESULT_VOID;
}
Result(void) socket_listen(Socket s, i32 backlog){
if(listen(s, backlog) != 0)
return RESULT_ERROR_ERRNO();
return RESULT_ERROR_SOCKET();
return RESULT_VOID;
}
@ -43,7 +43,7 @@ Result(Socket) socket_accept(Socket main_socket, NULLABLE(EndpointIPv4*) remote_
i32 sockaddr_size = sizeof(remote_addr);
Socket user_connection = accept(main_socket, (void*)&remote_addr, (void*)&sockaddr_size);
if(user_connection == -1)
return RESULT_ERROR_ERRNO();
return RESULT_ERROR_SOCKET();
//TODO: add IPV6 support (struct sockaddr_in6)
assert(sockaddr_size == sizeof(remote_addr));
@ -55,14 +55,14 @@ Result(Socket) socket_accept(Socket main_socket, NULLABLE(EndpointIPv4*) remote_
Result(void) socket_connect(Socket s, EndpointIPv4 remote_end){
struct sockaddr_in sockaddr = EndpointIPv4_toSockaddr(remote_end);
if(connect(s, (void*)&sockaddr, sizeof(sockaddr)) != 0)
return RESULT_ERROR_ERRNO();
return RESULT_ERROR_SOCKET();
return RESULT_VOID;
}
Result(void) socket_send(Socket s, Array(u8) buffer){
i32 r = send(s, buffer.data, buffer.size, 0);
if(r < 0)
return RESULT_ERROR_ERRNO();
return RESULT_ERROR_SOCKET();
return RESULT_VOID;
}
@ -70,25 +70,24 @@ Result(void) socket_sendto(Socket s, Array(u8) buffer, EndpointIPv4 dst){
struct sockaddr_in sockaddr = EndpointIPv4_toSockaddr(dst);
i32 r = sendto(s, buffer.data, buffer.size, 0, (void*)&sockaddr, sizeof(sockaddr));
if(r < 0)
return RESULT_ERROR_ERRNO();
return RESULT_ERROR_SOCKET();
return RESULT_VOID;
}
Result(i32) socket_recv(Socket s, Array(u8) buffer){
i32 r = recv(s, buffer.data, buffer.size, 0);
if(r < 0)
return RESULT_ERROR_ERRNO();
return RESULT_ERROR_SOCKET();
return RESULT_VALUE(i, r);
}
Result(i32) socket_recvfrom(Socket s, Array(u8) buffer, NULLABLE(EndpointIPv4*) remote_end){
struct sockaddr_in remote_addr = {0};
i32 sockaddr_size = sizeof(remote_addr);
i32 r = recvfrom(s, buffer.data, buffer.size, 0,
(struct sockaddr*)&remote_addr, (void*)&sockaddr_size);
if(r < 0)
return RESULT_ERROR_ERRNO();
return RESULT_ERROR_SOCKET();
//TODO: add IPV6 support (struct sockaddr_in6)
assert(sockaddr_size == sizeof(remote_addr));
@ -96,4 +95,4 @@ Result(i32) socket_recvfrom(Socket s, Array(u8) buffer, NULLABLE(EndpointIPv4*)
if(remote_end)
*remote_end = EndpointIPv4_fromSockaddr(remote_addr);
return RESULT_VALUE(i, r);
}
}

37
src/server.c Normal file
View File

@ -0,0 +1,37 @@
#include "server.h"
#include "network/socket.h"
#include "db/idb.h"
#include <pthread.h>
typedef struct AcceptedConnection {
Socket sock;
EndpointIPv4 client_end;
} AcceptedConnection;
static void* handle_connection(void* _args);
Result(void) server_run(cstr server_endpoint_str){
Deferral(32);
EndpointIPv4 server_end = EndpointIPv4_fromStr(server_endpoint_str);
//TODO: add log
try(Socket main_socket, i, socket_open_TCP());
try_void(socket_bind(main_socket, server_end));
try_void(socket_listen(main_socket, 512));
while(true){
AcceptedConnection* args = malloc(sizeof(AcceptedConnection));
try(args->sock, i, socket_accept(main_socket, &args->client_end));
pthread_t conn_thread = {0};
try_stderrcode(pthread_create(&conn_thread, NULL, handle_connection, args));
}
Return RESULT_VOID;
}
static void* handle_connection(void* _args){
Deferral(64);
AcceptedConnection* conn = (AcceptedConnection*)_args;
Defer(free(conn));
Return NULL;
}

4
src/server.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include "tlibc/errors.h"
Result(void) server_run(cstr server_endpoint_str);

95
src/term.c Normal file
View File

@ -0,0 +1,95 @@
#include "term.h"
#include <unistd.h>
#include IFWIN("windows.h", "sys/ioctl.h")
bool term_init(){
#if defined(_WIN64) || defined(_WIN32)
DWORD mode=0;
HANDLE h;
// configure stdout
h = GetStdHandle(STD_OUTPUT_HANDLE);
if(h == INVALID_HANDLE_VALUE)
return false;
GetConsoleMode(h, &mode);
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
mode |= ENABLE_PROCESSED_OUTPUT;
SetConsoleMode(h, mode);
// configure stdin
h = GetStdHandle(STD_INPUT_HANDLE);
if(h == INVALID_HANDLE_VALUE)
return false;
GetConsoleMode(h, &mode);
mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
mode |= ENABLE_PROCESSED_INPUT;
SetConsoleMode(h, mode);
#endif
return true;
}
int getenv_int(const char* var_name){
char* str=getenv(var_name);
if(str==NULL)
return -1;
return strtol(str, NULL, 0);
}
bool term_getSize(TerminalSize* out) {
#if defined(_WIN64) || defined(_WIN32)
// helps when STD_OUT is redirected to a file
HANDLE hConsoleErr = GetStdHandle(STD_ERROR_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
if(!GetConsoleScreenBufferInfo(hConsoleErr, &consoleInfo))
return false;
out->cols = consoleInfo.srWindow.Right - consoleInfo.srWindow.Left + 1;
out->rows = consoleInfo.srWindow.Bottom - consoleInfo.srWindow.Top + 1;
#else
struct winsize ws = {0};
// try to get terminal size from stdin, stdout, stderr
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)==0 ||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)==0 ||
ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)==0 ){
out->cols=ws.ws_col;
out->rows=ws.ws_row;
}
// try to get size from environtent variables
else {
out->cols=getenv_int("COLUMNS");
out->rows=getenv_int("LINES");
}
#endif
return out->cols > 0 && out->rows > 0;
}
/*
Most of escape sequences can be found there
https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
*/
void term_resetCursor() {
printf("\e[H");
}
void term_resetColors() {
printf("\e[0m");
}
void term_clear() {
printf("\e[0m\e[H\e[2J");
}
void term_cursorMove(u16 row, u16 column) {
printf("\e[%u;%uH",row,column);
}
void term_cursorHide() {
printf("\e[?25l");
}
void term_cursorShow() {
printf("\e[?25h");
}

16
src/term.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "tlibc/std.h"
typedef struct TerminalSize {
i16 cols;
i16 rows;
} TerminalSize;
bool term_init();
bool term_getSize(TerminalSize* out);
void term_resetCursor();
void term_resetColors();
void term_clear();
void term_cursorMove(u16 row, u16 column);
void term_cursorHide();
void term_cursorShow();