21 Commits
Author SHA1 Message Date
zheiling 5021f61c80 fixes file search 2026-08-20 18:33:07 +03:00
zheiling 0ee52bc2ea dlist: fixes dlist_clear_list when dlist->start == NULL 2026-08-20 18:32:21 +03:00
zheiling d681f11bef updates file list->fl_clear, gets rid of **current and **start pointers in w_ui_file_list_t 2026-08-17 19:43:47 +03:00
zheiling 274442105e updates TODO.md 2026-08-17 19:42:41 +03:00
zheiling e8d95f14b9 file_list: double list integration 2 [not finished] 2026-08-17 18:22:15 +03:00
zheiling 3d9b6ccac9 file_list: double list integration [not finished] 2026-08-14 18:58:33 +03:00
zheiling 247419fc93 fl_fs: fixes return path from the linked directory 2026-08-11 15:47:51 +03:00
zheiling 58d23fbf61 fs_file_list: uses wchar_t for displaying the name. Improves sorting. 2026-08-10 16:51:14 +03:00
zheiling c563c71b5f fs_file_list: integration of double-linked in file lists 2026-08-07 18:02:58 +03:00
zheiling 6b20f2f179 fs_file_list: fixes for new dual linked list implementation 2026-08-06 18:58:31 +03:00
zheiling 01b078c8d9 dlist integration in fs_file_list.c [not finished] 2026-08-05 19:00:10 +03:00
zheiling 769aab6d1f Dual-linked list: clear list 2026-08-04 19:08:30 +03:00
zheiling 43a1da856f tests and fixes for dlist_remove_by_ptr 2026-08-03 17:50:09 +03:00
zheiling 55994e9cb3 Dual-linked list: tests and sort and delete function. 2026-07-31 16:31:20 +03:00
zheiling a1e1a0a0d3 dlist: insert sort [not finished] 2026-07-30 18:38:02 +03:00
zheiling fc33d521a2 test dlist 2026-07-30 17:04:07 +03:00
zheiling 7711749d03 adds cmocka submodule 2026-07-29 19:06:59 +03:00
zheiling 5b42ea6a73 activates CTest and adds Cmocka as a dependency 2026-07-29 18:46:27 +03:00
zheiling 92115c0dd4 changes file structure 2026-07-29 18:40:04 +03:00
zheiling 62626aaea9 Correct cancel editing the file description after file upload 2026-07-28 15:48:47 +03:00
zheiling a2568443e0 Merge pull request 'Description edit on save' (#1) from text_edit_window into main
Reviewed-on: #1
2026-07-27 19:45:06 +03:00
60 changed files with 1211 additions and 643 deletions
+6
View File
@@ -0,0 +1,6 @@
# [submodule "external/Unity"]
# path = external/Unity
# url = https://github.com/ThrowTheSwitch/Unity.git
[submodule "external/cmocka"]
path = external/cmocka
url = https://git.cryptomilk.org/projects/cmocka.git
+7
View File
@@ -23,9 +23,16 @@ add_subdirectory(src/modals)
add_subdirectory(src/lib) add_subdirectory(src/lib)
add_subdirectory(src/utils) add_subdirectory(src/utils)
add_subdirectory(src/windows) add_subdirectory(src/windows)
add_subdirectory(src/core)
add_subdirectory(src) add_subdirectory(src)
# Testing
include(CTest)
include(cmake/OptionsCMocka.cmake)
add_subdirectory(external/cmocka)
add_subdirectory(test)
install( install(
TARGETS zbbs-client-tui TARGETS zbbs-client-tui
) )
+6 -3
View File
@@ -1,13 +1,16 @@
# TODO: # TODO:
## Current: ## Current:
- [ ] Sorting in the file list (local files)
- [ ] Resume download ## Future:
- [ ] Separate common structures in the common.h - [ ] File catalogs (or tags)
- [ ] Store only unique files (check MD5 sums)
## Global tasks: ## Global tasks:
- [ ] Testing - [ ] Testing
- [ ] Documentation - [ ] Documentation
- [ ] Resume download
- [ ] Separate common structures in the common.h
## Features: ## Features:
- [ ] Files removal - [ ] Files removal
+7
View File
@@ -0,0 +1,7 @@
# Disable things we don't need from CMocka
set(WITH_EXAMPLES OFF CACHE BOOL "Build examples" FORCE)
set(UNIT_TESTING OFF CACHE BOOL "Build CMocka's own tests" FORCE)
set(WITH_CMOCKERY_SUPPORT OFF CACHE BOOL "" FORCE)
# Force static library (easier for most projects)
set(WITH_STATIC_LIB ON CACHE BOOL "Build static library" FORCE)
Vendored Submodule
+1
Submodule external/cmocka added at 7f736f65dc
+12 -7
View File
@@ -20,17 +20,21 @@ elseif(
endif() endif()
endif() endif()
file(GLOB M_SOURCES "*.c")
if(DEV_WIDGET) if(DEV_WIDGET)
list(FILTER M_SOURCES EXCLUDE REGEX "main\\.c$") target_sources(zbbs-client-tui
PRIVATE
main_dev_widget.c
)
else() else()
list(FILTER M_SOURCES EXCLUDE REGEX "main_dev_widget\\.c$") target_sources(zbbs-client-tui
PRIVATE
main.c
)
endif() endif()
target_sources(zbbs-client-tui add_library(common INTERFACE)
PRIVATE target_sources(common
${M_SOURCES} PUBLIC FILE_SET HEADERS FILES common.h
) )
target_link_libraries(zbbs-client-tui target_link_libraries(zbbs-client-tui
@@ -41,5 +45,6 @@ target_link_libraries(zbbs-client-tui
bstring bstring
windows windows
utils utils
common
${CURSES_LIBRARIES} ${CURSES_LIBRARIES}
) )
+279
View File
@@ -4,8 +4,23 @@
#ifndef COMMON_H #ifndef COMMON_H
#define COMMON_H #define COMMON_H
#include "dlist.h"
#include <d_array.h>
#include <ncursesw/ncurses.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#define LOCAL_PORT 1999
#define SERVER_PORT 2000
#define LISTEN_QLEN 32
#define INBUFSIZE 1024
#define DOWNLOADS_DIR "./Downloads"
#define DIALOGUE_TITLE 64
#define DIALOGUE_TEXT 4096
#define INPUT_TEXT DIALOGUE_TITLE
#define PACKAGE_SIZE INBUFSIZE * 10
typedef struct { typedef struct {
int32_t cur_y, cur_x; int32_t cur_y, cur_x;
int32_t max_y, max_x; int32_t max_y, max_x;
@@ -25,4 +40,268 @@ struct action_key {
int (*callback)(void *app, void *d_args); int (*callback)(void *app, void *d_args);
}; };
typedef struct fl_item {
size_t size;
char *name;
char *description;
char *owner;
} fl_item_t;
enum main_window_type {
mw_fl_server, /* file list on the server */
mw_fl_local, /* local file list */
mw_f_desc, /* file description */
};
typedef void(draw_f_t)(void *fui);
typedef struct main_window {
enum main_window_type type;
void *ui; /* structure which describes window UI */
int32_t (*cb_b_press)(void *app, void *args); /* callback on button press */
void (*cb_ui_refresh)(void *app); /* refresh callback */
void (*reset)(void *app); /* function to reset the state of the window */
draw_f_t *draw;
struct action_key *b_keys;
int32_t b_keys_len;
} main_window_t;
typedef struct file_args {
int file_d;
fl_item_t f_selected;
fl_item_t *l_start;
fl_item_t *l_current;
dlist_t *f_list;
} file_args_t;
typedef struct params {
unsigned short port;
unsigned addr;
char *uname;
char *pass;
char privileges;
int32_t sd;
int32_t is_connected : 1;
} params_t;
enum state {
/* TUI-based */
S_N_D,
S_ASK_SEVER_IP,
S_ASK_LOGIN_TYPE,
S_ASK_LOGIN_USER,
S_ASK_REGISTER,
S_WAIT_SERVER,
S_PREP_SERVER_MESSAGE,
S_PRINT_SERVER_MESSAGE,
S_FILE_LIST,
S_WAIT_PASS,
/* S_FILE_SELECT, */
S_DOWNLOAD_FILE,
S_UPLOAD_PARAMS,
S_UPLOAD_FILE,
/* S_UPLOAD_FILE_SELECT, */
S_UPLOAD_REQUESTED,
S_UPLOAD_SERVER_FINISHES,
S_ASK_USER_BEFORE_LOGIN,
S_ERR,
S_NEXT_ACTION,
S_WAIT_REGISTER_CONFIRMATION,
S_WAIT_USER_DESCRIPTION,
/* Legacy */
WAIT_SERVER_INIT,
WAIT_SERVER,
WAIT_REGISTER,
WAIT_REGISTER_CONFIRMATION,
WAIT_CLIENT,
};
enum package_signal { sig_continue, sig_cancel, sig_finish, sig_pause };
typedef struct {
enum package_signal signal;
size_t package_size;
} s_file_pd_t; /* file package descriptor */
typedef struct p_file {
char *name;
char *path;
char *description;
size_t size;
size_t rest;
size_t package_rest;
int32_t it_count;
size_t it_interval;
enum package_signal signal;
int fd;
} p_file_t;
typedef struct query_args {
int32_t sd;
int32_t buf_used;
p_file_t *file;
enum state state;
char buf[INBUFSIZE];
bool from_server;
params_t *params;
struct {
char *text;
int32_t capacity;
int32_t size;
} server_message;
main_window_t *main_ui;
void *progress_bar;
void *active_dialogue;
void *notification;
} query_args_t;
enum w_dialogue_color_scheme { dc_normal, dc_alert };
enum w_g_type { g_content, g_action };
enum w_g_dir {
horizontal,
vertical,
};
enum w_type {
w_end,
w_button,
w_box,
w_group,
w_input,
w_checkbox,
w_progress,
};
enum w_val_type {
val_num,
val_ptr,
val_nul,
};
struct w_val_t {
enum w_val_type type;
union {
void *ptr;
int32_t num;
} val;
};
typedef struct {
int32_t id;
int32_t idx; /* element index in the group */
void *element;
enum w_type type;
enum w_g_type g_type;
bool is_default;
struct w_val_t val;
} w_g_el_t;
/* widget callback response code */
enum w_cbrp_code {
cbrc_none, /* default */
cbrp_val,
cbrp_g_el, /* element of type group_el_t */
cbrp_err
};
enum w_pmt_attrs {
PMT_POS_CENTER = 01,
PMT_ALIGN_CENTER = 03,
};
/* widget callback response data */
typedef struct {
enum w_cbrp_code code;
struct w_val_t val;
} w_cbrp_data;
typedef struct {
void *app;
void *element;
void *data;
w_cbrp_data resp_data;
void *active_el;
} w_cb_args_t;
typedef int32_t (*w_cb_press_t)(void *app, void *data);
typedef void (*w_cb_t)(w_cb_args_t *args);
typedef struct w_t {
int32_t id;
char title[DIALOGUE_TITLE];
struct {
int32_t y;
int32_t x;
} cur; /* cursor position */
struct {
int32_t y;
int32_t x;
} sz; /* size */
struct {
int32_t y;
int32_t x;
} m; /* margin (relative to the parent) */
struct {
int32_t y;
int32_t x;
} ps; /* position (relative to the window) */
WINDOW *const *parent_win;
struct w_t *w_parent;
w_cb_t callback;
} w_t;
typedef struct group_el_init_t {
enum w_type type;
bool is_default;
bool is_hidden_value;
char label[DIALOGUE_TITLE];
int32_t length;
enum w_g_dir direction;
union {
int32_t num;
} val;
struct group_el_init_t *children;
} w_group_el_init_t;
typedef struct group_t {
w_t w;
w_g_el_t *elements;
enum w_g_dir direction;
int32_t count;
int32_t first_id, last_id;
struct group_t *parent_group;
} w_group_t;
typedef struct {
w_t w;
WINDOW *win;
w_group_t *g_content;
w_group_t *g_action;
w_g_el_t *active_el;
char text[DIALOGUE_TEXT];
bool is_initiated;
bool needs_update;
bool needs_destroy;
coordinates_t *p_coordinates;
enum w_dialogue_color_scheme color_scheme;
d_array_ptr_t id_map;
} w_dialogue_t;
typedef struct app_t {
WINDOW *win;
w_dialogue_t modal;
params_t *params;
coordinates_t coordinates;
query_args_t *query_args;
file_args_t *file_args;
void (*active_callback)(w_cb_args_t *args);
void (*callback_after_notification)(struct app_t *);
void *active_widget;
wchar_t *top_text;
main_window_t main_ui;
} app_t;
#endif #endif
+23
View File
@@ -0,0 +1,23 @@
add_library(core STATIC)
file(GLOB M_SOURCES "*.c")
target_sources(core
PRIVATE
${M_SOURCES}
PUBLIC
FILE_SET HEADERS
FILES
core.h
)
target_link_libraries(core
PRIVATE
common
widget_core
widget
bstring
utils
modals
windows
)
+1 -1
View File
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
/* Copyright (c) 2026 Oleksandr Zhylin */ /* Copyright (c) 2026 Oleksandr Zhylin */
#include "../app.h" #include "app.h"
#include <locale.h> #include <locale.h>
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <netinet/in.h> #include <netinet/in.h>
+1 -17
View File
@@ -4,9 +4,7 @@
#ifndef APP_H #ifndef APP_H
#define APP_H #define APP_H
#include "../main.h" #include <common.h>
#include "../common.h"
#include "dialogue.h"
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <widget_core.h> #include <widget_core.h>
#include <bstrlib.h> #include <bstrlib.h>
@@ -15,20 +13,6 @@ typedef enum {
modal_color_pair = 3, modal_color_pair = 3,
} app_color; } app_color;
typedef struct app_t {
WINDOW *win;
w_dialogue_t modal;
params_t *params;
coordinates_t coordinates;
query_args_t *query_args;
file_args_t *file_args;
void (*active_callback)(w_cb_args_t *args);
void (*callback_after_notification)(struct app_t *);
void *active_widget;
wchar_t *top_text;
main_window_t main_ui;
} app_t;
app_t *app_init(app_t *app); app_t *app_init(app_t *app);
void app_init_nc(void); void app_init_nc(void);
void app_destroy(app_t *app, int32_t exit_code); void app_destroy(app_t *app, int32_t exit_code);
+2 -2
View File
@@ -1,8 +1,8 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
/* Copyright (c) 2026 Oleksandr Zhylin */ /* Copyright (c) 2026 Oleksandr Zhylin */
#include "alert.h" #include "app.h"
#include "main.h" #include <widget.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
+1 -1
View File
@@ -3,7 +3,7 @@
#ifndef CONNECTION_H #ifndef CONNECTION_H
#define CONNECTION_H #define CONNECTION_H
#include "main.h"
#include <widget.h> #include <widget.h>
bool connect_to_server(app_t *params); bool connect_to_server(app_t *params);
+12
View File
@@ -0,0 +1,12 @@
#ifndef CORE_H
#define CORE_H
#include "app.h"
#include "connection.h"
#include "draw_modal.h"
#include "file_processor.h"
#include "fs.h"
#include "query.h"
#include "server.h"
#endif
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
/* Copyright (c) 2026 Oleksandr Zhylin */ /* Copyright (c) 2026 Oleksandr Zhylin */
#include "main.h"
#include <modals.h> #include <modals.h>
#include <widget.h> #include <widget.h>
@@ -3,6 +3,8 @@
#ifndef DRAW_MODAL_H #ifndef DRAW_MODAL_H
#define DRAW_MODAL_H #define DRAW_MODAL_H
#include <widget.h> #include <widget.h>
void app_draw_modal(app_t *app); void app_draw_modal(app_t *app);
#endif #endif
@@ -2,9 +2,10 @@
/* Copyright (c) 2026 Oleksandr Zhylin */ /* Copyright (c) 2026 Oleksandr Zhylin */
#include "alert.h" #include "alert.h"
#include "dialogue.h" #include "dlist.h"
#include "server.h" #include "server.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <common.h>
#include <fcntl.h> #include <fcntl.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stddef.h> #include <stddef.h>
@@ -20,28 +21,27 @@
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <widget.h> #include <widget.h>
#include <windows.h> #include <windows/file_list.h>
#include "file_processor.h" #include "file_processor.h"
#include "main.h"
#include "query.h" #include "query.h"
static void fl_add(fl_item_t **cur, fl_item_t **start, char *fname); static void fl_add(dlist_t *fl_list, char *q_line);
fl_item_t *fl_select(fl_item_t *start, int num); static bool fl_rm_cb(void *_ptr);
void fl_clear(fl_item_t **start, fl_item_t **current);
void fl_clear(dlist_t *dlist) { dlist_clear_list(dlist, fl_rm_cb); }
void file_list(file_args_t *f_args, query_args_t *q_args) { void file_list(file_args_t *f_args, query_args_t *q_args) {
int32_t qlen; int32_t qlen;
char *query = NULL; char *query = NULL;
static char qbuf[INBUFSIZE * 2]; static char q_buf[INBUFSIZE * 2];
static int32_t qbuf_used = 0; static int32_t qbuf_used = 0;
w_ui_file_list_t *fui = (w_ui_file_list_t *)q_args->main_ui->ui; w_ui_file_list_t *fui = (w_ui_file_list_t *)q_args->main_ui->ui;
w_dialogue_t *d = (w_dialogue_t *)q_args->active_dialogue; w_dialogue_t *d = (w_dialogue_t *)q_args->active_dialogue;
fui->start = &(f_args->l_start);
fui->current = &(f_args->l_current);
if (qbuf_used == 0) if (qbuf_used == 0)
qbuf[0] = 0; q_buf[0] = 0;
while ((qlen = query_extract_from_buf(q_args->buf, &(q_args->buf_used), while ((qlen = query_extract_from_buf(q_args->buf, &(q_args->buf_used),
&query))) { &query))) {
@@ -56,33 +56,35 @@ void file_list(file_args_t *f_args, query_args_t *q_args) {
q_args->state = WAIT_CLIENT; q_args->state = WAIT_CLIENT;
break; break;
} }
strcat(qbuf, query); strcat(q_buf, query);
qbuf_used += strlen(qbuf); qbuf_used += strlen(q_buf);
if (strchr(qbuf, '\n') == NULL) if (strchr(q_buf, '\n') == NULL)
continue; continue;
fl_add(&(f_args->l_current), &(f_args->l_start), qbuf); fl_add(f_args->f_list, q_buf);
fui->current_count++; fui->current_count++;
free(query); free(query);
query = NULL; query = NULL;
qbuf_used = 0; qbuf_used = 0;
qbuf[0] = 0; q_buf[0] = 0;
} }
} }
int32_t ui_file_select(file_args_t *f_args, query_args_t *q_args, int32_t idx) { static bool fl_add_cb(void *_dst, void *_src);
fl_item_t *l_selected; /* from the list */
int32_t ui_file_select(file_args_t *f_args, query_args_t *q_args,
fl_item_t *l_selected) {
fl_item_t *f_selected = fl_item_t *f_selected =
&(f_args->f_selected); /* new copy of file struct (list be cleared) */ &(f_args->f_selected); /* new copy of file struct (list be cleared) */
struct stat st = {0}; struct stat st = {0};
l_selected = fl_select(f_args->l_start, idx);
if (l_selected == NULL) { if (l_selected == NULL) {
return -1; return -1;
} }
memcpy(f_selected, l_selected, sizeof(fl_item_t)); memcpy(f_selected, l_selected, sizeof(fl_item_t));
fl_add_cb(f_selected, l_selected);
if (stat(DOWNLOADS_DIR, &st) == -1) { if (stat(DOWNLOADS_DIR, &st) == -1) {
mkdir(DOWNLOADS_DIR, 0700); mkdir(DOWNLOADS_DIR, 0700);
} }
@@ -91,7 +93,7 @@ int32_t ui_file_select(file_args_t *f_args, query_args_t *q_args, int32_t idx) {
f_selected->name = NULL; f_selected->name = NULL;
sprintf(file_path, "%s/%s", DOWNLOADS_DIR, l_selected->name); sprintf(file_path, "%s/%s", DOWNLOADS_DIR, l_selected->name);
f_selected->name = strdup(l_selected->name); f_selected->name = strdup(l_selected->name);
fl_clear(&f_args->l_start, &f_args->l_current); fl_clear(f_args->f_list);
f_args->file_d = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); f_args->file_d = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (f_args->file_d == -1) { if (f_args->file_d == -1) {
server_send_string(q_args, "error: %s\n", f_selected->name); server_send_string(q_args, "error: %s\n", f_selected->name);
@@ -174,8 +176,8 @@ void file_download(file_args_t *f_args, query_args_t *q_args) {
if (q_args->file->package_rest <= q_args->buf_used) { if (q_args->file->package_rest <= q_args->buf_used) {
write_len = q_args->file->package_rest; write_len = q_args->file->package_rest;
qlen = write(f_args->file_d, q_args->buf, write_len); qlen = write(f_args->file_d, q_args->buf, write_len);
file_download_receive_after(qlen, q_args, f_args, d, pb) file_download_receive_after(qlen, q_args, f_args, d,
if (q_args->file->signal == sig_continue) { pb) if (q_args->file->signal == sig_continue) {
server_send_string(q_args, "continue %d\n", PACKAGE_SIZE * 10); server_send_string(q_args, "continue %d\n", PACKAGE_SIZE * 10);
if (q_args->file->package_rest == 0) { if (q_args->file->package_rest == 0) {
q_args->file->package_rest = PACKAGE_SIZE * 10; q_args->file->package_rest = PACKAGE_SIZE * 10;
@@ -317,80 +319,93 @@ void clear_file_in_query(query_args_t *q_args) {
q_args->file = NULL; q_args->file = NULL;
} }
void init_file_args(file_args_t *f_args) { void init_file_args(file_args_t *f_args, app_t *app) {
w_ui_file_list_t *fui = app->main_ui.ui;
f_args->file_d = 0; f_args->file_d = 0;
f_args->f_selected.name = NULL; f_args->f_selected.name = NULL;
f_args->f_selected.next = NULL;
f_args->f_selected.size = 0; f_args->f_selected.size = 0;
f_args->l_current = NULL; f_args->l_current = NULL;
f_args->l_start = NULL; f_args->l_start = NULL;
if (app->main_ui.type == mw_fl_server) {
fui = app->main_ui.ui;
if (fui != NULL) {
f_args->f_list = fui->f_list;
}
}
f_args->f_list = fui->f_list;
app->file_args = f_args;
} }
/* work with file list */ static void fl_q_extract(fl_item_t *f, char *line) {
static void fl_add(fl_item_t **cur, fl_item_t **start, char *line) {
char fowner[32]; char fowner[32];
char *descr_begin = NULL; char *descr_begin = NULL;
int h_len; int h_len;
char *name_begin = strchr(line, '[') + 1; char *name_begin = strchr(line, '[') + 1;
char *name_end = strchr(line, ']'); char *name_end = strchr(line, ']');
int name_len = name_end - name_begin; int name_len = name_end - name_begin;
fl_item_t *fitem = malloc(sizeof(fl_item_t)); sscanf(name_end, "] %zu %s%n", &(f->size), fowner, &h_len);
sscanf(name_end, "] %zu %s%n", &(fitem->size), fowner, &h_len); strncpy(f->name, name_begin, name_len);
/* file name */ f->name[name_len] = 0;
fitem->name = malloc((sizeof(char)) * (name_len + 1)); strcpy(f->owner, fowner);
strncpy(fitem->name, name_begin, name_len);
fitem->name[name_len] = 0;
/* file owner */
fitem->owner = malloc((sizeof(char)) * (strlen(fowner) + 1));
strcpy(fitem->owner, fowner);
/* file description */
descr_begin = name_end + h_len + 1; descr_begin = name_end + h_len + 1;
if (strcmp(descr_begin, "\n")) { if (strcmp(descr_begin, "\n")) {
int d_len = strlen(line) - h_len; int d_len = strlen(line) - h_len;
fitem->description = malloc((sizeof(char)) * (d_len)); strcpy(f->description, descr_begin); // +1 to escape the dividing \32
strcpy(fitem->description, descr_begin); // +1 to escape the dividing \32
for (int i = 0; i < d_len; i++) { for (int i = 0; i < d_len; i++) {
if (fitem->description[i] == '\a') { if (f->description[i] == '\a') {
fitem->description[i] = '\n'; f->description[i] = '\n';
} }
} }
} else { } else {
fitem->description = NULL; f->description = NULL;
} }
/* fitem->description[strlen(line + line_pos)] = 0; */ }
fitem->next = NULL;
if (*cur != NULL) { static bool fl_add_cb(void *_dst, void *_src) {
(*cur)->next = fitem; fl_item_t *dst = _dst;
fl_item_t *src = _src;
dst->size = src->size;
if (src->description != NULL) {
dst->description = malloc(sizeof(char) * (strlen(src->description) + 1));
strcpy(dst->description, src->description);
} else { } else {
*start = fitem; dst->description = NULL;
} }
*cur = fitem;
dst->name = malloc(sizeof(char) * (strlen(src->name) + 1));
strcpy(dst->name, src->name);
dst->owner = malloc(sizeof(char) * (strlen(src->owner) + 1));
strcpy(dst->owner, src->owner);
return true;
} }
fl_item_t *fl_select(fl_item_t *start, int num) { static bool fl_rm_cb(void *_ptr) {
fl_item_t *current = start; fl_item_t *ptr = _ptr;
for (; num != 1; num--) { if (ptr->description != NULL)
if (current->next != NULL) { free(ptr->description);
current = current->next; if (ptr->name != NULL)
} else { free(ptr->name);
return NULL; if (ptr->owner != NULL)
} free(ptr->owner);
} free(ptr);
return current; return true;
} }
void fl_clear(fl_item_t **start, fl_item_t **arg_current) { /* work with file list */
if (*start == NULL) static void fl_add(dlist_t *fl_list, char *q_line) {
return; fl_item_t f_item;
fl_item_t *next, *current; char f_description[INBUFSIZE * 100];
current = *start; char f_owner[256];
char f_name[256];
f_item.description = f_description;
f_item.owner = f_owner;
f_item.name = f_name;
f_item.size = 0;
do { fl_q_extract(&f_item, q_line);
next = current->next;
free(current->description); dlist_add(fl_list, &f_item, fl_item_t, fl_add_cb, false);
free(current->name);
free(current);
} while ((current = next) != NULL);
*start = NULL;
*arg_current = NULL;
} }
@@ -4,16 +4,17 @@
#ifndef FILE_PROCESSOR_H #ifndef FILE_PROCESSOR_H
#define FILE_PROCESSOR_H #define FILE_PROCESSOR_H
#include "main.h" #include "common.h"
void file_list(file_args_t *f_args, query_args_t *q_args); void file_list(file_args_t *f_args, query_args_t *q_args);
void file_download(file_args_t *f_args, query_args_t *q_args); void file_download(file_args_t *f_args, query_args_t *q_args);
void init_file_args(file_args_t *fargs); void init_file_args(file_args_t *fargs, app_t *app);
int file_upload_request(char *, query_args_t *q_args); int file_upload_request(char *, query_args_t *q_args);
int file_upload_start(query_args_t *q_args); int file_upload_start(query_args_t *q_args);
int file_upload(query_args_t *q_args); int file_upload(query_args_t *q_args);
int32_t file_upload_open(char *dpath, char *fname, query_args_t *q_args); int32_t file_upload_open(char *dpath, char *fname, query_args_t *q_args);
void fl_clear(fl_item_t **start, fl_item_t **arg_current); void fl_clear(dlist_t *dlist);
int32_t ui_file_select(file_args_t *f_args, query_args_t *q_args, int32_t idx); int32_t ui_file_select(file_args_t *f_args, query_args_t *q_args, fl_item_t *ptr);
void clear_file_in_query(query_args_t *q_args); void clear_file_in_query(query_args_t *q_args);
#endif #endif
View File
View File
+2 -7
View File
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
/* Copyright (c) 2026 Oleksandr Zhylin */ /* Copyright (c) 2026 Oleksandr Zhylin */
#include "query.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <fcntl.h> #include <fcntl.h>
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
@@ -23,13 +22,12 @@
#include <modals.h> #include <modals.h>
#include <widget.h> #include <widget.h>
#include <widget_core.h> #include <widget_core.h>
#include <windows.h>
#include "app.h"
#include "query.h"
#include "alert.h" #include "alert.h"
#include "connection.h" #include "connection.h"
#include "dialogue.h"
#include "file_processor.h" #include "file_processor.h"
#include "main.h"
#include "main_window.h" #include "main_window.h"
#include "server.h" #include "server.h"
#include "types.h" #include "types.h"
@@ -52,13 +50,10 @@ void query_loop(app_t *app) {
int32_t sd = app->params->sd; int32_t sd = app->params->sd;
size_t qlen; size_t qlen;
int sr; int sr;
static file_args_t file_args;
w_cb_args_t d_args = { w_cb_args_t d_args = {
.app = app, .element = NULL, .data = NULL, .resp_data.code = cbrc_none}; .app = app, .element = NULL, .data = NULL, .resp_data.code = cbrc_none};
init_file_args(&file_args);
query_args->sd = sd; query_args->sd = sd;
app->file_args = &file_args;
for (;;) { for (;;) {
/* update screen */ /* update screen */
-2
View File
@@ -4,8 +4,6 @@
#ifndef QUERY_H #ifndef QUERY_H
#define QUERY_H #define QUERY_H
#include "../app.h"
#include "main.h"
#include <widget.h> #include <widget.h>
void query_loop(app_t *); void query_loop(app_t *);
+2 -3
View File
@@ -1,9 +1,8 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
/* Copyright (c) 2026 Oleksandr Zhylin */ /* Copyright (c) 2026 Oleksandr Zhylin */
#include "../app.h" #include "app.h"
#include "dialogue.h" #include "dialogue.h"
#include "main.h"
#include "query.h" #include "query.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <fcntl.h> #include <fcntl.h>
@@ -18,7 +17,7 @@
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <widget.h> #include <widget.h>
#include <windows.h> #include <windows/file_list.h>
void ask_uname_and_password(params_t *params); void ask_uname_and_password(params_t *params);
void ask_register(params_t *params, char *email); void ask_register(params_t *params, char *email);
+2 -2
View File
@@ -4,8 +4,8 @@
#ifndef SERVER_H #ifndef SERVER_H
#define SERVER_H #define SERVER_H
#include "../app.h" #include <common.h>
#include "main.h"
int process_server_command(char *line, int l_len, app_t *app); int process_server_command(char *line, int l_len, app_t *app);
void ask_register(params_t *params, char *email); void ask_register(params_t *params, char *email);
void server_send_string(query_args_t *q, const char *fmt, ...); void server_send_string(query_args_t *q, const char *fmt, ...);
+6 -5
View File
@@ -14,12 +14,11 @@
#include <widget.h> #include <widget.h>
#include <windows.h> #include <windows.h>
#include "../app.h"
#include "alert.h" #include "alert.h"
#include "connection.h" #include "core/file_processor.h"
// #include "file_list.h" #include <core/app.h>
#include "main.h" #include <core/connection.h>
#include "query.h" #include <core/query.h>
int32_t m_id = 0; int32_t m_id = 0;
@@ -29,6 +28,7 @@ int main(int argc, char **argv) {
app_t app; app_t app;
params_t params; params_t params;
query_args_t q_args; query_args_t q_args;
file_args_t file_args;
app_init_nc(); app_init_nc();
@@ -44,6 +44,7 @@ int main(int argc, char **argv) {
app.query_args = &q_args; app.query_args = &q_args;
main_window_set(&app, mw_fl_server); main_window_set(&app, mw_fl_server);
init_file_args(&file_args, &app);
/* init client to connect to the server */ /* init client to connect to the server */
app.params->sd = init_client(); app.params->sd = init_client();
-125
View File
@@ -9,129 +9,4 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#define LOCAL_PORT 1999
#define SERVER_PORT 2000
#define LISTEN_QLEN 32
#define INBUFSIZE 1024
#define DOWNLOADS_DIR "./Downloads"
#define DIALOGUE_TITLE 64
#define DIALOGUE_TEXT 4096
#define INPUT_TEXT DIALOGUE_TITLE
#define PACKAGE_SIZE INBUFSIZE * 10
typedef struct params {
unsigned short port;
unsigned addr;
char *uname;
char *pass;
char privileges;
int32_t sd;
int32_t is_connected : 1;
} params_t;
enum state {
/* TUI-based */
S_N_D,
S_ASK_SEVER_IP,
S_ASK_LOGIN_TYPE,
S_ASK_LOGIN_USER,
S_ASK_REGISTER,
S_WAIT_SERVER,
S_PREP_SERVER_MESSAGE,
S_PRINT_SERVER_MESSAGE,
S_FILE_LIST,
S_WAIT_PASS,
/* S_FILE_SELECT, */
S_DOWNLOAD_FILE,
S_UPLOAD_PARAMS,
S_UPLOAD_FILE,
/* S_UPLOAD_FILE_SELECT, */
S_UPLOAD_REQUESTED,
S_UPLOAD_SERVER_FINISHES,
S_ASK_USER_BEFORE_LOGIN,
S_ERR,
S_NEXT_ACTION,
S_WAIT_REGISTER_CONFIRMATION,
S_WAIT_USER_DESCRIPTION,
/* Legacy */
WAIT_SERVER_INIT,
WAIT_SERVER,
WAIT_REGISTER,
WAIT_REGISTER_CONFIRMATION,
WAIT_CLIENT,
};
enum package_signal { sig_continue, sig_cancel, sig_finish, sig_pause };
typedef struct {
enum package_signal signal;
size_t package_size;
} s_file_pd_t; /* file package descriptor */
typedef struct p_file {
char *name;
char *path;
char *description;
size_t size;
size_t rest;
size_t package_rest;
int32_t it_count;
size_t it_interval;
enum package_signal signal;
int fd;
} p_file_t;
typedef struct fl_item {
size_t size;
char *name;
char *description;
char *owner;
struct fl_item *next;
} fl_item_t;
typedef struct file_args {
int file_d;
fl_item_t f_selected;
fl_item_t *l_start;
fl_item_t *l_current;
} file_args_t;
enum main_window_type {
mw_fl_server, /* file list on the server */
mw_fl_local, /* local file list */
mw_f_desc, /* file description */
};
typedef void(draw_f_t)(void *fui);
typedef struct main_window {
enum main_window_type type;
void *ui; /* structure which describes window UI */
int32_t (*cb_b_press)(void *app, void *args); /* callback on button press */
void (*cb_ui_refresh)(void *app); /* refresh callback */
void (*reset)(void *app); /* function to reset the state of the window */
draw_f_t *draw;
struct action_key *b_keys;
int32_t b_keys_len;
} main_window_t;
typedef struct query_args {
int32_t sd;
int32_t buf_used;
p_file_t *file;
enum state state;
char buf[INBUFSIZE];
bool from_server;
params_t *params;
struct {
char *text;
int32_t capacity;
int32_t size;
} server_message;
main_window_t *main_ui;
void *progress_bar;
void *active_dialogue;
void *notification;
} query_args_t;
#endif #endif
+9 -7
View File
@@ -2,13 +2,13 @@ add_library(modals STATIC)
file(GLOB M_SOURCES "*.c") file(GLOB M_SOURCES "*.c")
target_link_directories(modals # target_link_directories(modals
PRIVATE # PRIVATE
"../widget/" # "../widget/"
"../widget_core/" # "../widget_core/"
"../lib/" # "../lib/"
"../utils/" # "../utils/"
) # )
target_link_libraries(modals target_link_libraries(modals
PRIVATE PRIVATE
@@ -16,6 +16,8 @@ target_link_libraries(modals
widget_core widget_core
bstring bstring
utils utils
common
windows
) )
target_sources(modals target_sources(modals
+2 -1
View File
@@ -4,7 +4,8 @@
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <widget.h> #include <widget.h>
#include <widget_core.h> #include <widget_core.h>
#include "../connection.h" #include <core/connection.h>
#include <core/app.h>
#include "group.h" #include "group.h"
typedef struct { typedef struct {
+6 -6
View File
@@ -1,15 +1,15 @@
#include "../server.h" #include <core/server.h>
#include "../windows/file_list.h" #include <windows/file_list.h>
#include "alert.h"
#include "dialogue.h"
#include "group.h"
#include "utils.h"
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <widget.h> #include <widget.h>
#include "alert.h"
#include "dialogue.h"
#include "group.h"
void dwn_pr_modal_cb(w_cb_args_t *args) { void dwn_pr_modal_cb(w_cb_args_t *args) {
/* only cancel action */ /* only cancel action */
int32_t key = *((int32_t *)args->data); int32_t key = *((int32_t *)args->data);
+2 -1
View File
@@ -6,7 +6,8 @@
#include <unistd.h> #include <unistd.h>
#include <widget.h> #include <widget.h>
#include "../server.h" #include <core/server.h>
#include <core/app.h>
typedef struct { typedef struct {
w_button_t *element; w_button_t *element;
+2 -1
View File
@@ -6,7 +6,8 @@
#include <unistd.h> #include <unistd.h>
#include <widget.h> #include <widget.h>
#include "../server.h" #include <core/app.h>
#include <core/server.h>
typedef struct { typedef struct {
w_button_t *element; w_button_t *element;
+1 -1
View File
@@ -8,7 +8,7 @@
#include "alert.h" #include "alert.h"
#include "group.h" #include "group.h"
#include "../server.h" #include <core/server.h>
typedef struct { typedef struct {
w_button_t *element; w_button_t *element;
+5 -6
View File
@@ -1,15 +1,14 @@
#include "checkbox.h"
#include "group.h"
#include "widget_core.h"
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h>
#include <utils.h> #include <utils.h>
#include <widget.h> #include <widget.h>
#include <core/server.h>
#include "../server.h" #include "checkbox.h"
#include "group.h"
void upload_props_dialogue_modal_cb(w_cb_args_t *args) { void upload_props_dialogue_modal_cb(w_cb_args_t *args) {
w_cb_args_t d_args; w_cb_args_t d_args;
+174 -35
View File
@@ -4,60 +4,199 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
typedef struct dlist_el_t { #include "dlist.h"
struct dlist_el_t *next;
struct dlist_el_t *previous;
void *el_ptr;
} dlist_el_t;
typedef struct dlist_t { /*
dlist_el_t *start; Uses *add* when needs to make a full copy of the element.
dlist_el_t *current; Uses *insert* when operates with nodes.
size_t len; */
} dlist_t;
void dlist_add(dlist_t *dlist, void *el_ptr, int el_siz, bool prepend); dlist_t *_dlist_init(void *el_ptr, int el_siz, dblist_add_cb_t add_cb) {
dlist_t *dlist_init(void *el_ptr, int el_siz) {
dlist_t *dlist = malloc(sizeof(dlist_t)); dlist_t *dlist = malloc(sizeof(dlist_t));
dlist->len = 0; dlist->len = 0;
dlist->current = NULL; dlist->current = NULL;
dlist->start = NULL; dlist->start = NULL;
if (el_ptr != NULL) { if (el_ptr != NULL) {
dlist_add(dlist, el_ptr, el_siz, false); _dlist_add(dlist, el_ptr, el_siz, add_cb, false);
} }
return dlist; return dlist;
} }
void dlist_add(dlist_t *dlist, void *el_ptr, int el_siz, bool prepend) { void _dlist_insert(dlist_t *dlist, dlist_node_t *new_el,
dlist_el_t *d_el = malloc(sizeof(dlist_el_t)); dlist_node_t *prev_node) {
d_el->el_ptr = malloc(el_siz); if (prev_node != NULL) {
memcpy(d_el->el_ptr, el_ptr, el_siz); new_el->next = prev_node->next;
prev_node->next->previous = new_el;
if (dlist->start == NULL) { new_el->previous = prev_node;
d_el->previous = NULL; prev_node->next = new_el;
d_el->next = NULL; if (new_el->next == NULL) {
dlist->start = d_el; dlist->current = new_el;
dlist->current = d_el; dlist->work_pointer = new_el;
} else if (prepend) { }
d_el->previous = NULL;
d_el->next = dlist->start;
dlist->start = d_el;
} else { } else {
d_el->next = NULL; new_el->next = dlist->start;
dlist->current->next = d_el; new_el->previous = NULL;
dlist->current = d_el; dlist->start->previous = new_el;
dlist->start = new_el;
} }
dlist->len++; dlist->len++;
} }
void *dlist_get_current(dlist_t *dlist) { void _dlist_insert_end(dlist_t *dlist, dlist_node_t *new_node) {
return dlist->current->el_ptr; dlist->current->next = new_node;
new_node->previous = dlist->current;
new_node->next = NULL;
dlist->current = new_node;
dlist->work_pointer = new_node;
dlist->len++;
} }
void _dlist_add_sort(dlist_t *dlist, void *el_ptr, int el_siz,
dblist_sort_cb_t cb, dblist_add_cb_t add_cb) {
dlist_node_t *node = malloc(sizeof(dlist_node_t));
node->el_ptr = malloc(el_siz);
add_cb(node->el_ptr, el_ptr);
dlist_node_t *dlist_item = dlist->start;
bool res = true;
if (dlist->start == NULL) {
dlist->start = node;
dlist->current = node;
node->next = NULL;
node->previous = NULL;
dlist->work_pointer = node;
dlist->len++;
return;
}
for (int i = 0; i < dlist->len; i++) {
res = cb(el_ptr, dlist_item->el_ptr);
if (!res)
break;
dlist_item = dlist_item->next;
}
if (!res) {
dlist_item = dlist_item->previous;
_dlist_insert(dlist, node, dlist_item);
} else {
_dlist_insert_end(dlist, node);
}
}
void _dlist_add(dlist_t *dlist, void *el_ptr, int el_siz,
dblist_add_cb_t *add_cb, bool prepend) {
dlist_node_t *node = malloc(sizeof(dlist_node_t));
node->el_ptr = malloc(el_siz);
add_cb(node->el_ptr, el_ptr);
if (dlist->start == NULL) {
node->previous = NULL;
node->next = NULL;
dlist->start = node;
dlist->current = node;
dlist->work_pointer = node;
} else if (prepend) {
node->previous = NULL;
node->next = dlist->start;
dlist->start->previous = node;
dlist->start = node;
} else {
node->previous = dlist->current;
node->next = NULL;
dlist->current->next = node;
dlist->current = node;
dlist->work_pointer = node;
}
dlist->len++;
}
dlist_node_t *find_node_by_ptr(dlist_t *dlist, void *el_ptr) {
dlist_node_t *node = dlist->start;
do {
if (node->el_ptr == el_ptr)
break;
} while ((node = node->next) != NULL);
if (node->el_ptr == el_ptr)
return node;
return NULL;
}
int dlist_remove_by_ptr(dlist_t *dlist, void *el_ptr, dblist_rm_cb_t cb) {
dlist_node_t *node = find_node_by_ptr(dlist, el_ptr);
if (node == NULL)
return -1;
node->next->previous = node->previous;
node->previous->next = node->next;
dlist->len--;
if (node->next == NULL)
dlist->current = node->previous;
if (node->previous == NULL)
dlist->start = node->next;
cb(node->el_ptr);
free(node);
return 1;
}
int dlist_clear_list(dlist_t *dlist, dblist_rm_cb_t cb) {
dlist_node_t *current = dlist->start;
dlist_node_t *next = NULL;
if (current == NULL) return 1;
do {
cb(current->el_ptr);
next = current->next;
free(current);
current = next;
} while (current != NULL);
dlist->len = 0;
dlist->current = NULL;
dlist->start = NULL;
dlist->work_pointer = NULL;
return 1;
}
/* Iterators */
void *dlist_it_prev(dlist_t *dlist) { void *dlist_it_prev(dlist_t *dlist) {
dlist->current = dlist->current->previous; dlist_node_t *cur_ptr = dlist->work_pointer;
return dlist->current; if (cur_ptr != NULL) {
dlist->work_pointer = dlist->work_pointer->previous;
return cur_ptr->el_ptr;
}
return NULL;
}
void *dlist_it_next(dlist_t *dlist) {
dlist_node_t *cur_ptr = dlist->work_pointer;
if (cur_ptr != NULL) {
dlist->work_pointer = dlist->work_pointer->next;
return cur_ptr->el_ptr;
}
return NULL;
}
dlist_node_t *dlist_wind_fwd(dlist_t *dlist, dlist_node_t *start, int count) {
dlist_node_t *new_ptr = start;
for (int i = 0; i <= count && new_ptr != NULL; i++) {
new_ptr = new_ptr->next;
}
dlist->work_pointer = new_ptr;
return new_ptr;
}
dlist_node_t *dlist_wind_bwd(dlist_t *dlist, dlist_node_t *start, int count) {
dlist_node_t *new_ptr = start;
for (int i = 0; i <= count && new_ptr != NULL; i++) {
new_ptr = new_ptr->previous;
}
dlist->work_pointer = new_ptr;
return new_ptr;
} }
+51
View File
@@ -0,0 +1,51 @@
#ifndef DLIST_H
#define DLIST_H
#include <stdbool.h>
#include <stddef.h>
typedef struct dlist_node_t {
struct dlist_node_t *next;
struct dlist_node_t *previous;
void *el_ptr;
} dlist_node_t;
typedef struct dlist_t {
dlist_node_t *start;
dlist_node_t *current;
dlist_node_t *work_pointer;
size_t len;
} dlist_t;
/* Returns if a > b */
typedef bool dblist_sort_cb_t(void *a, void *b);
typedef bool dblist_add_cb_t(void *dst, void *src);
typedef bool dblist_rm_cb_t(void *el_ptr);
dlist_t *_dlist_init(void *el_ptr, int el_siz, dblist_add_cb_t);
void *dlist_get_current(dlist_t *dlist);
void *dlist_it_prev(dlist_t *dlist);
void _dlist_add(dlist_t *dlist, void *el_ptr, int el_siz,
dblist_add_cb_t add_cb, bool prepend);
void *dlist_it_prev(dlist_t *dlist);
void *dlist_it_next(dlist_t *dlist);
void _dlist_add_sort(dlist_t *dlist, void *el_ptr, int el_siz,
dblist_sort_cb_t cb, dblist_add_cb_t add_cb);
int dlist_remove_by_ptr(dlist_t *dlist, void *el_ptr, dblist_rm_cb_t cb);
int dlist_clear_list(dlist_t *dlist, dblist_rm_cb_t cb);
dlist_node_t *dlist_wind_fwd(dlist_t *dlist, dlist_node_t *start, int count);
dlist_node_t *dlist_wind_bwd(dlist_t *dlist, dlist_node_t *start, int count);
#define dlist_init(el_ptr, el_def, add_cb) \
_dlist_init(el_ptr, sizeof(el_def), add_cb)
#define dlist_add(dlist, el_ptr, el_def, add_cb, prepend) \
_dlist_add(dlist, el_ptr, sizeof(el_def), add_cb, prepend)
#define dlist_get_current(dlist) dlist->current->el_ptr
#define dlist_get_working_point(dlist) dlist->current->el_ptr
#define dlist_add_sort(dlist, el_ptr, el_typ, sort_cb, add_cb) \
_dlist_add_sort(dlist, el_ptr, sizeof(el_typ), sort_cb, add_cb)
#define dlist_get_ptr(node_ptr) node_ptr == NULL ? NULL : node_ptr->el_ptr
#define dlist_set_wp(dlist, node_ptr) dlist->work_pointer = node_ptr
#define dlist_get_wp(dlist) dlist->work_pointer
#endif
+2
View File
@@ -7,6 +7,8 @@ target_link_libraries(widget
bstring bstring
widget_core widget_core
utils utils
common
core
) )
target_sources(widget target_sources(widget
+1 -1
View File
@@ -1,6 +1,6 @@
#include "alert.h" #include "alert.h"
#include "dialogue.h" #include "dialogue.h"
#include "widget_core.h" #include <core/app.h>
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
+2 -1
View File
@@ -1,8 +1,9 @@
#ifndef W_ALERT_H #ifndef W_ALERT_H
#define W_ALERT_H #define W_ALERT_H
#include "../app.h" #include <common.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h>
void w_alert_init(app_t *app); void w_alert_init(app_t *app);
void w_alert(const char *message); void w_alert(const char *message);
+2 -1
View File
@@ -8,7 +8,8 @@
#include <widget_core.h> #include <widget_core.h>
#include "button.h" #include "button.h"
#include "../app.h"
#include <core/app.h>
w_button_t *init_button(WINDOW **win, w_t *w_parent, char *label) { w_button_t *init_button(WINDOW **win, w_t *w_parent, char *label) {
w_button_t *btn = malloc(sizeof(w_button_t)); w_button_t *btn = malloc(sizeof(w_button_t));
+1 -1
View File
@@ -6,7 +6,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "../app.h" #include <core/app.h>
#include "checkbox.h" #include "checkbox.h"
#include <widget_core.h> #include <widget_core.h>
+1
View File
@@ -11,6 +11,7 @@
#include <wchar.h> #include <wchar.h>
#include <widget.h> #include <widget.h>
#include <widget_core.h> #include <widget_core.h>
#include <core/app.h>
static void (*active_callback_backup)(w_cb_args_t *) = NULL; static void (*active_callback_backup)(w_cb_args_t *) = NULL;
+2 -19
View File
@@ -4,31 +4,14 @@
#ifndef W_DIALOGUE_H #ifndef W_DIALOGUE_H
#define W_DIALOGUE_H #define W_DIALOGUE_H
#include "../common.h" #include <common.h>
#include "group.h"
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stdint.h> #include <stdint.h>
#include <utils.h> #include <utils.h>
#include <widget_core.h> #include <widget_core.h>
#define MAX_IDS 64 #define MAX_IDS 64
enum w_dialogue_color_scheme { dc_normal, dc_alert };
typedef struct {
w_t w;
WINDOW *win;
w_group_t *g_content;
w_group_t *g_action;
w_g_el_t *active_el;
char text[DIALOGUE_TEXT];
bool is_initiated;
bool needs_update;
bool needs_destroy;
coordinates_t *p_coordinates;
enum w_dialogue_color_scheme color_scheme;
d_array_ptr_t id_map;
} w_dialogue_t;
void w_dialogue_init(w_dialogue_t *d, const char title[], const char text[], void w_dialogue_init(w_dialogue_t *d, const char title[], const char text[],
coordinates_t *p_coordinates); coordinates_t *p_coordinates);
void w_dialogue_vinit(w_dialogue_t *dialogue, const char title[], void w_dialogue_vinit(w_dialogue_t *dialogue, const char title[],
+1 -22
View File
@@ -7,28 +7,7 @@
#include <stdint.h> #include <stdint.h>
#include <utils.h> #include <utils.h>
#include <widget_core.h> #include <widget_core.h>
#include <common.h>
typedef struct group_el_init_t {
enum w_type type;
bool is_default;
bool is_hidden_value;
char label[DIALOGUE_TITLE];
int32_t length;
enum w_g_dir direction;
union {
int32_t num;
} val;
struct group_el_init_t *children;
} w_group_el_init_t;
typedef struct group_t {
w_t w;
w_g_el_t *elements;
enum w_g_dir direction;
int32_t count;
int32_t first_id, last_id;
struct group_t *parent_group;
} w_group_t;
w_group_t *w_group_init(WINDOW **win, w_t *w_parent, w_group_el_init_t *children, w_group_t *w_group_init(WINDOW **win, w_t *w_parent, w_group_el_init_t *children,
d_array_ptr_t *id_map, enum w_g_dir dir, d_array_ptr_t *id_map, enum w_g_dir dir,
+2 -1
View File
@@ -6,7 +6,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "../app.h" #include <core/app.h>
#include <utils.h>
#include "input.h" #include "input.h"
#include <wchar.h> #include <wchar.h>
#include <widget_core.h> #include <widget_core.h>
-1
View File
@@ -4,7 +4,6 @@
#ifndef W_INPUT_H #ifndef W_INPUT_H
#define W_INPUT_H #define W_INPUT_H
#include "../main.h"
#include <stdint.h> #include <stdint.h>
#include <widget_core.h> #include <widget_core.h>
+1 -1
View File
@@ -2,7 +2,7 @@
/* Copyright (c) 2026 Oleksandr Zhylin */ /* Copyright (c) 2026 Oleksandr Zhylin */
#include "progress_bar.h" #include "progress_bar.h"
#include "../app.h" #include <core/app.h>
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
+1
View File
@@ -5,6 +5,7 @@ file(GLOB M_SOURCES "*.c")
target_link_libraries(widget_core target_link_libraries(widget_core
PRIVATE PRIVATE
utils utils
common
) )
target_sources(widget_core target_sources(widget_core
-1
View File
@@ -6,7 +6,6 @@
so it is separated from the widget directory */ so it is separated from the widget directory */
#include "widget_core.h" #include "widget_core.h"
#include "../main.h"
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
+1 -96
View File
@@ -4,104 +4,9 @@
#ifndef WIDGET_CORE_H #ifndef WIDGET_CORE_H
#define WIDGET_CORE_H #define WIDGET_CORE_H
#include "../main.h"
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stdint.h> #include <stdint.h>
#include <common.h>
enum w_g_type { g_content, g_action };
enum w_g_dir {
horizontal,
vertical,
};
enum w_type {
w_end,
w_button,
w_box,
w_group,
w_input,
w_checkbox,
w_progress,
};
enum w_val_type {
val_num,
val_ptr,
val_nul,
};
struct w_val_t {
enum w_val_type type;
union {
void *ptr;
int32_t num;
} val;
};
typedef struct {
int32_t id;
int32_t idx; /* element index in the group */
void *element;
enum w_type type;
enum w_g_type g_type;
bool is_default;
struct w_val_t val;
} w_g_el_t;
/* widget callback response code */
enum w_cbrp_code {
cbrc_none, /* default */
cbrp_val,
cbrp_g_el, /* element of type group_el_t */
cbrp_err
};
/* widget callback response data */
typedef struct {
enum w_cbrp_code code;
struct w_val_t val;
} w_cbrp_data;
typedef struct {
void *app;
void *element;
void *data;
w_cbrp_data resp_data;
void *active_el;
} w_cb_args_t;
typedef void (*w_cb_t)(w_cb_args_t *args);
typedef int32_t (*w_cb_press_t)(void *app, void *data);
typedef struct w_t {
int32_t id;
char title[DIALOGUE_TITLE];
struct {
int32_t y;
int32_t x;
} cur; /* cursor position */
struct {
int32_t y;
int32_t x;
} sz; /* size */
struct {
int32_t y;
int32_t x;
} m; /* margin (relative to the parent) */
struct {
int32_t y;
int32_t x;
} ps; /* position (relative to the window) */
WINDOW *const *parent_win;
struct w_t *w_parent;
w_cb_t callback;
} w_t;
enum w_pmt_attrs {
PMT_POS_CENTER = 01,
PMT_ALIGN_CENTER = 03,
};
void w_init(w_t *w, w_t *w_parent, WINDOW **win, char *title); void w_init(w_t *w, w_t *w_parent, WINDOW **win, char *title);
int32_t w_get_max_line_len(const char *text, int32_t *line_count); int32_t w_get_max_line_len(const char *text, int32_t *line_count);
+9 -7
View File
@@ -2,13 +2,13 @@ add_library(windows STATIC)
file(GLOB M_SOURCES "*.c") file(GLOB M_SOURCES "*.c")
target_link_directories(windows # target_link_directories(windows
PRIVATE # PRIVATE
"../widget/" # "../widget/"
"../widget_core/" # "../widget_core/"
"../lib/" # "../lib/"
"../utils/" # "../utils/"
) # )
target_link_libraries(windows target_link_libraries(windows
PRIVATE PRIVATE
@@ -16,6 +16,8 @@ target_link_libraries(windows
widget_core widget_core
bstring bstring
utils utils
core
common
) )
target_sources(windows target_sources(windows
+25 -20
View File
@@ -2,11 +2,10 @@
/* Copyright (c) 2026 Oleksandr Zhylin */ /* Copyright (c) 2026 Oleksandr Zhylin */
#include "file_list.h" #include "file_list.h"
#include "../app.h"
#include "../file_processor.h"
#include "../fs.h"
#include "../server.h"
#include "main_window.h" #include "main_window.h"
#include "dlist.h"
#include <common.h>
#include <core.h>
#include <bstrlib.h> #include <bstrlib.h>
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stdbool.h> #include <stdbool.h>
@@ -46,7 +45,7 @@ static void w_fl_cb(w_cb_args_t *args) {
} }
server_send_string(app->query_args, "%s %d %d\n", q_prefix, server_send_string(app->query_args, "%s %d %d\n", q_prefix,
fui->max_lines, 1); fui->max_lines, 1);
w_fl_reset(fui); fl_clear(fui->f_list);
} else { } else {
bconchar(fui->search_key, key); bconchar(fui->search_key, key);
w_fl_draw(fui); w_fl_draw(fui);
@@ -58,6 +57,8 @@ static void w_fl_cb(w_cb_args_t *args) {
case KEY_DOWN: case KEY_DOWN:
if (fui->current_idx < fui->current_count - 1) { if (fui->current_idx < fui->current_count - 1) {
fui->current_idx++; fui->current_idx++;
if (fui->f_selected == NULL) fui->f_selected = fui->f_list->start;
fui->f_selected = fui->f_selected->next;
} else if (fui->current_page < fui->pages) { } else if (fui->current_page < fui->pages) {
app->query_args->state = S_FILE_LIST; app->query_args->state = S_FILE_LIST;
server_send_string(app->query_args, "%s %d %d\n", q_prefix, server_send_string(app->query_args, "%s %d %d\n", q_prefix,
@@ -70,6 +71,7 @@ static void w_fl_cb(w_cb_args_t *args) {
case KEY_UP: case KEY_UP:
if (fui->current_idx > 0) { if (fui->current_idx > 0) {
fui->current_idx--; fui->current_idx--;
fui->f_selected = fui->f_selected->previous;
} else if (fui->current_page > 1) { } else if (fui->current_page > 1) {
app->query_args->state = S_FILE_LIST; app->query_args->state = S_FILE_LIST;
server_send_string(app->query_args, "%s %d %d\n", q_prefix, server_send_string(app->query_args, "%s %d %d\n", q_prefix,
@@ -111,7 +113,7 @@ static void w_fl_cb(w_cb_args_t *args) {
1); 1);
w_fl_reset(fui); w_fl_reset(fui);
case '\n': case '\n':
ui_file_select(app->file_args, app->query_args, fui->current_idx + 1); ui_file_select(app->file_args, app->query_args, dlist_get_ptr(fui->f_selected));
break; break;
} }
} }
@@ -188,11 +190,15 @@ void *w_fl_init(app_t *app) {
fui->activate_last = false; fui->activate_last = false;
fui->active_search = false; fui->active_search = false;
fui->search_key = bfromcstrrangealloc(12, 64, ""); fui->search_key = bfromcstrrangealloc(12, 64, "");
fui->start = NULL;
fui->current = NULL;
fui->w.sz.x = getmaxx(app->win); fui->w.sz.x = getmaxx(app->win);
fui->w.sz.y = getmaxy(app->win); fui->w.sz.y = getmaxy(app->win);
fui->f_list = dlist_init(NULL, fl_item_t, NULL);
fui->f_selected = NULL;
if (app->file_args != NULL) {
app->file_args->f_list = fui->f_list;
}
/* * INIT UI * */ /* * INIT UI * */
/* define the width for each sub window */ /* define the width for each sub window */
@@ -210,13 +216,13 @@ void *w_fl_init(app_t *app) {
} }
void w_fl_destroy(w_ui_file_list_t **fui) { void w_fl_destroy(w_ui_file_list_t **fui) {
fl_clear((*fui)->start, (*fui)->current); fl_clear((*fui)->f_list);
free(*fui); free(*fui);
*fui = NULL; *fui = NULL;
} }
void w_fl_reset(w_ui_file_list_t *fl_ui) { void w_fl_reset(w_ui_file_list_t *fl_ui) {
fl_clear(fl_ui->start, fl_ui->current); fl_clear(fl_ui->f_list);
fl_ui->current_idx = 0; fl_ui->current_idx = 0;
fl_ui->current_page = 0; fl_ui->current_page = 0;
fl_ui->pages = 0; fl_ui->pages = 0;
@@ -242,17 +248,14 @@ void w_fl_draw(w_ui_file_list_t *fui) {
sz_x -= 1; /* do not count the borders */ sz_x -= 1; /* do not count the borders */
p_y = 1; p_y = 1;
p_x = 1; p_x = 1;
fl_item_t *el = NULL;
fl_item_t *active_el = NULL; dlist_node_t *wp_bac = dlist_get_wp(fui->f_list);
dlist_set_wp(fui->f_list, fui->f_list->start);
fl_item_t *el = dlist_it_next(fui->f_list);
fl_item_t *active_el = dlist_get_ptr(fui->f_selected);
int32_t cur_el_idx = 0; int32_t cur_el_idx = 0;
box(fui->win_list, 0, 0); box(fui->win_list, 0, 0);
if (fui->start != NULL) {
el = *(fui->start);
active_el = el;
}
if (fui->activate_last) { if (fui->activate_last) {
fui->current_idx = fui->max_lines - 1; fui->current_idx = fui->max_lines - 1;
fui->activate_last = false; fui->activate_last = false;
@@ -281,13 +284,15 @@ void w_fl_draw(w_ui_file_list_t *fui) {
} }
p_y++; p_y++;
cur_el_idx++; cur_el_idx++;
} while ((el = el->next) != NULL && p_y < sz_y_f); } while ((el = dlist_it_next(fui->f_list)) != NULL && p_y < sz_y_f);
dlist_set_wp(fui->f_list, wp_bac);
p_x = 1; p_x = 1;
int32_t p_len; int32_t p_len;
if (!fui->active_search && fui->search_key->slen > 0 && if (!fui->active_search && fui->search_key->slen > 0 &&
*(fui->start) == NULL) { fui->f_list->start == NULL) {
bstring text = bfromStatic("[No data to show]"); bstring text = bfromStatic("[No data to show]");
for (; p_y < (sz_y_f / 2); p_y++) { for (; p_y < (sz_y_f / 2); p_y++) {
mvwprintw(win, p_y, p_x, "%*s", sz_x - 1, ""); mvwprintw(win, p_y, p_x, "%*s", sz_x - 1, "");
+3 -3
View File
@@ -4,15 +4,15 @@
#ifndef W_FILE_LIST_H #ifndef W_FILE_LIST_H
#define W_FILE_LIST_H #define W_FILE_LIST_H
#include "../app.h" #include "dlist.h"
#include <bstrlib.h> #include <bstrlib.h>
#include <stdint.h> #include <stdint.h>
#include <widget_core.h> #include <widget_core.h>
typedef struct { typedef struct {
w_t w; w_t w;
fl_item_t **current; dlist_t *f_list;
fl_item_t **start; dlist_node_t *f_selected;
int32_t current_idx; int32_t current_idx;
int32_t current_count; int32_t current_count;
int32_t full_count; int32_t full_count;
+178 -122
View File
@@ -15,14 +15,24 @@
#include <unistd.h> #include <unistd.h>
#include <utils.h> #include <utils.h>
#include "../file_processor.h" #include <core/app.h>
#include "../fs.h" #include <core/file_processor.h>
#include "../server.h" #include <core/fs.h>
#include <core/server.h>
#include <wchar.h>
#include "alert.h" #include "alert.h"
#include "dlist.h"
#include "fs_file_list.h" #include "fs_file_list.h"
#include "main_window.h" #include "main_window.h"
#include "widget_core.h" #include "widget_core.h"
#define fl_add(dlist, el_ptr, prepend) \
dlist_add(dlist, el_ptr, w_lfl_item_t, fl_add_cb, prepend)
#define fl_add_sort(dlist, el_ptr) \
dlist_add_sort(dlist, el_ptr, w_lfl_item_t, fl_add_sort_cb, fl_add_cb)
#define fl_clear(f_list) dlist_clear_list(f_list, fl_clear_cb)
// void w_fl_reset(w_lfl_ui_t *fl_ui); // void w_fl_reset(w_lfl_ui_t *fl_ui);
static size_t get_file_size(char *path) { static size_t get_file_size(char *path) {
@@ -44,124 +54,163 @@ struct fl_args {
u_char d_type; u_char d_type;
}; };
static void fl_add(w_lfl_item_t **f_start, w_lfl_item_t **f_current, static bool fl_add_cb(void *_dst, void *_src) {
struct fl_args *fargs) { w_lfl_item_t *dst = _dst;
w_lfl_item_t *c_item = malloc(sizeof(w_lfl_item_t)); w_lfl_item_t *src = _src;
c_item->name = malloc(strlen(fargs->name) + 1); memcpy(dst, src, sizeof(w_lfl_item_t));
strcpy(c_item->name, fargs->name); if (src->name != NULL) {
c_item->size = fargs->size; dst->name = malloc(strlen(src->name) + 1);
c_item->next = NULL; strcpy(dst->name, src->name);
c_item->d_type = fargs->d_type;
if (fargs->path != NULL) {
c_item->path = malloc(strlen(fargs->path) + 1);
strcpy(c_item->path, fargs->path);
} }
if (*f_start == NULL) {
c_item->prev = NULL; if (src->w_name != NULL) {
*f_start = c_item; dst->w_name = malloc((wcslen(src->w_name) + 1) * sizeof(wchar_t));
*f_current = c_item; wcscpy(dst->w_name, src->w_name);
}
if (src->path != NULL) {
dst->path = malloc(strlen(src->path) + 1);
strcpy(dst->path, src->path);
} else { } else {
c_item->prev = *f_current; dst->path = NULL;
(*f_current)->next = c_item;
*f_current = c_item;
} }
return 1;
} }
static void lfl_clear(w_lfl_item_t **start, w_lfl_item_t **arg_current) { static int _strcmp(wchar_t *a, wchar_t *b) {
if (*start == NULL) wchar_t abuf[256];
return; wchar_t bbuf[256];
w_lfl_item_t *next, *current;
current = *start;
do { int c = 0;
next = current->next;
free(current->name); for (int i = 0; a[i] != '\0'; i++) {
free(current); abuf[i] = a[i] & ~32; /* case non-sensitive for Latin characters */
} while ((current = next) != NULL); }
*start = NULL;
*arg_current = NULL; for (int i = 0; b[i] != '\0'; i++) {
bbuf[i] = b[i] & ~32;
}
return wcscmp(abuf, bbuf);
}
static bool fl_add_sort_cb(void *_a, void *_b) {
w_lfl_item_t *a = _a;
w_lfl_item_t *b = _b;
wchar_t *a_w_name = a->w_name;
wchar_t *b_w_name = b->w_name;
int a_m = 0, b_m = 0;
int res = 0;
if (a->d_type == DT_DIR || (a->d_type == DT_LNK && a->w_name[2] == '/'))
a_m |= 1;
if (b->d_type == DT_DIR || (b->d_type == DT_LNK && b->w_name[2] == '/'))
b_m |= 1;
if (a_m != b_m)
return b_m > a_m;
if (a->d_type == DT_LNK)
a_w_name += 2;
if (b->d_type == DT_LNK)
b_w_name += 2;
res = _strcmp(a_w_name, b_w_name);
return res > 0;
}
static bool fl_clear_cb(void *el_ptr) {
w_lfl_item_t *el = el_ptr;
free(el->path);
free(el->name);
free(el->w_name);
free(el);
return 1;
} }
static int get_files_from_fs(w_lfl_ui_t *fui, char *path) { static int get_files_from_fs(w_lfl_ui_t *fui, char *path) {
DIR *dir; DIR *dir;
struct dirent *dent; struct dirent *dent;
char name[257]; char name[257];
char d_name[257]; wchar_t w_d_name[257];
char l_path[INBUFSIZE]; char l_path[INBUFSIZE];
struct fl_args f_args; w_lfl_item_t f_args;
ssize_t r = 0; ssize_t r = 0;
fui->files_num = 0; fui->files_num = 0;
w_lfl_item_t *d_start = NULL, *d_current = NULL; /* directories */
w_lfl_item_t *f_start = NULL, *f_current = NULL; /* files */
dir = opendir(path); dir = opendir(path);
if (!dir) if (!dir)
return 1; return 1;
if (fui->d_path != NULL) { if (fui->d_path != NULL) {
free(fui->d_path); free(fui->d_path);
lfl_clear(&(fui->start), &(fui->current)); fl_clear(fui->f_list);
} }
fui->d_path = malloc(strlen(path) + 1); fui->d_path = malloc(strlen(path) + 1);
strcpy(fui->d_path, path); strcpy(fui->d_path, path);
while ((dent = readdir(dir)) != NULL) { while ((dent = readdir(dir)) != NULL) {
f_args.path = NULL; f_args.path = NULL;
if ((f_args.d_type = dent->d_type) == DT_DIR) { if ((f_args.d_type = dent->d_type) == DT_DIR) {
/* -> DIRECTORIES */
if (!strcmp(dent->d_name, "..")) { if (!strcmp(dent->d_name, "..")) {
w_lfl_item_t *t_st = NULL, *t_cur = NULL; w_lfl_item_t *t_st = NULL, *t_cur = NULL;
sprintf(name, "/%s", dent->d_name); wcscpy(w_d_name, L"/..");
f_args.name = name; f_args.name = dent->d_name;
f_args.w_name = w_d_name;
f_args.size = 0; f_args.size = 0;
fl_add(&t_st, &t_cur, &f_args); f_args.d_type = dent->d_type;
fl_add(fui->f_list, &f_args, true);
fui->files_num++; fui->files_num++;
/* Always put .. at the beginning */
t_st->next = d_start;
d_start = t_st;
if (d_current == NULL)
d_current = t_cur;
continue; continue;
} }
/* Hide directories with dot-beginnings */ /* Hide directories with dot-beginnings */
if (!strncmp(dent->d_name, ".", 1)) if (!strncmp(dent->d_name, ".", 1))
continue; continue;
sprintf(name, "/%s", dent->d_name); mbstowcs(w_d_name + 1, dent->d_name, 255);
f_args.name = name; w_d_name[0] = '/';
fl_add(&d_start, &d_current, &f_args); f_args.name = dent->d_name;
f_args.w_name = w_d_name;
f_args.size = 0; f_args.size = 0;
f_args.d_type = dent->d_type;
fl_add_sort(fui->f_list, &f_args);
fui->files_num++; fui->files_num++;
/* <- DIRECTORIES */
} else if (f_args.d_type == DT_LNK) { } else if (f_args.d_type == DT_LNK) {
/* -> LINKS */
sprintf(name, "%s/%s", fui->d_path, dent->d_name); sprintf(name, "%s/%s", fui->d_path, dent->d_name);
r = readlink(name, l_path, INBUFSIZE); r = readlink(name, l_path, INBUFSIZE);
sprintf(name, "->/%s", dent->d_name); for (int i = 1; l_path[r - 1] == '/'; i++) {
f_args.name = name; l_path[r - 1] = '\0';
}
wcsncpy(w_d_name, L"->/", 3);
mbstowcs(w_d_name + 3, dent->d_name, 253);
f_args.name = dent->d_name;
f_args.w_name = w_d_name;
f_args.path = l_path; f_args.path = l_path;
f_args.size = 0; f_args.size = 0;
fl_add(&d_start, &d_current, &f_args); f_args.d_type = dent->d_type;
fl_add_sort(fui->f_list, &f_args);
fui->files_num++; fui->files_num++;
/* <- LINKS */
} else { } else {
/* -> FILES */
/* Hide files with dot-beginnings */ /* Hide files with dot-beginnings */
if (!strncmp(dent->d_name, ".", 1)) if (!strncmp(dent->d_name, ".", 1))
continue; continue;
f_args.name = dent->d_name; f_args.name = dent->d_name;
mbstowcs(w_d_name, dent->d_name, 256);
f_args.w_name = w_d_name;
f_args.size = get_file_size(dent->d_name); f_args.size = get_file_size(dent->d_name);
fl_add(&f_start, &f_current, &f_args); f_args.d_type = dent->d_type;
fl_add_sort(fui->f_list, &f_args);
fui->files_num++; fui->files_num++;
sprintf(name, "%s", dent->d_name); /* <- FILES */
} }
} }
if (d_current != NULL)
d_current->next = f_start;
if (f_start != NULL)
f_start->prev = d_current;
fui->start = d_start;
fui->page_start = d_start;
fui->current = d_start;
fui->cur_page = 1; fui->cur_page = 1;
fui->pages_num = fui->files_num / fui->max_lines; fui->pages_num = fui->files_num / fui->max_lines;
if (fui->files_num % fui->max_lines) { if (fui->files_num % fui->max_lines) {
fui->pages_num++; fui->pages_num++;
} }
fui->page_start = fui->f_list->start;
fui->fl_selected = fui->f_list->start;
return 0; return 0;
} }
@@ -170,9 +219,18 @@ static void open_selected_item(w_lfl_ui_t *fui, w_cbrp_data *resp_data) {
char *selection = NULL; char *selection = NULL;
size_t dp_current; size_t dp_current;
char n_path[INBUFSIZE]; char n_path[INBUFSIZE];
if (fui->current->d_type == DT_DIR) { w_lfl_item_t *selected_item = fui->fl_selected->el_ptr;
if (!strcmp("/..", fui->current->name)) { if (selected_item->d_type == DT_DIR) {
if (!strcmp("..", selected_item->name)) {
strcpy(n_path, fui->d_path); strcpy(n_path, fui->d_path);
if (fui->d_path_previous != NULL) {
if (get_files_from_fs(fui, fui->d_path_previous)) {
w_alert("Can't open the folder!");
return;
}
free(fui->d_path_previous);
fui->d_path_previous = NULL;
} else {
bash_case = strrchr(n_path, '/'); bash_case = strrchr(n_path, '/');
if (bash_case != NULL) { if (bash_case != NULL) {
*bash_case = '\0'; *bash_case = '\0';
@@ -181,63 +239,55 @@ static void open_selected_item(w_lfl_ui_t *fui, w_cbrp_data *resp_data) {
return; return;
} }
} }
}
} else { } else {
dp_current = strlen(fui->d_path); dp_current = strlen(fui->d_path);
selection = fui->current->name + 1; selection = selected_item->name + 1;
sprintf(n_path, "%s/%s", fui->d_path, selection); sprintf(n_path, "%s/%s", fui->d_path, selection);
if (get_files_from_fs(fui, n_path)) { if (get_files_from_fs(fui, n_path)) {
w_alert("Can't open the folder!"); w_alert("Can't open the folder!");
return; return;
} }
} }
fui->current = fui->start;
fui->current_idx = 0; fui->current_idx = 0;
resp_data->code = cbrc_none; resp_data->code = cbrc_none;
w_lfl_draw(fui); w_lfl_draw(fui);
} else if (fui->current->d_type == DT_REG) { } else if (selected_item->d_type == DT_REG) {
resp_data->code = cbrp_val; resp_data->code = cbrp_val;
resp_data->val.type = val_num; resp_data->val.type = val_num;
resp_data->val.val.num = 1; resp_data->val.val.num = 1;
} else if (fui->current->d_type == DT_LNK) { } else if (selected_item->d_type == DT_LNK) {
strcpy(n_path, fui->current->path); strcpy(n_path, selected_item->path);
fui->d_path_previous = malloc(sizeof(char) * (strlen(fui->d_path) + 1));
strcpy(fui->d_path_previous, fui->d_path);
if (get_files_from_fs(fui, n_path)) { if (get_files_from_fs(fui, n_path)) {
w_alert("Can't open the link!"); w_alert("Can't open the link!");
return; return;
} }
fui->current = fui->start;
fui->current_idx = 0; fui->current_idx = 0;
resp_data->code = cbrc_none; resp_data->code = cbrc_none;
w_lfl_draw(fui); w_lfl_draw(fui);
} }
} }
/* next page */
static void page_next(w_lfl_ui_t *fui) { static void page_next(w_lfl_ui_t *fui) {
int count = fui->max_lines;
w_lfl_item_t *n_page_item = fui->page_start;
if (fui->cur_page < fui->pages_num) { if (fui->cur_page < fui->pages_num) {
for (; count >= 0; count--) { dlist_node_t *n_page_item =
n_page_item = n_page_item->next; dlist_wind_fwd(fui->f_list, fui->page_start, fui->max_lines);
}
fui->cur_page++; fui->cur_page++;
fui->page_start = n_page_item; fui->page_start = n_page_item;
fui->fl_selected = n_page_item;
fui->current_idx = 0; fui->current_idx = 0;
fui->current = n_page_item;
} }
} }
/* previous page */
static void page_previous(w_lfl_ui_t *fui) { static void page_previous(w_lfl_ui_t *fui) {
int count = fui->max_lines; dlist_node_t *last_node = fui->page_start->previous;
w_lfl_item_t *n_page_item = fui->page_start;
if (fui->cur_page > 1) { if (fui->cur_page > 1) {
for (; count >= 0; count--) { dlist_node_t *n_page_item =
n_page_item = n_page_item->prev; dlist_wind_bwd(fui->f_list, fui->page_start, fui->max_lines);
}
fui->cur_page--; fui->cur_page--;
fui->current =
fui->page_start->prev; /* the last element of the previous page */
fui->page_start = n_page_item; fui->page_start = n_page_item;
fui->fl_selected = last_node;
fui->current_idx = fui->max_lines; fui->current_idx = fui->max_lines;
} }
} }
@@ -247,32 +297,30 @@ static void w_lfl_cb(w_cb_args_t *args) {
int32_t key = *((int32_t *)args->data); int32_t key = *((int32_t *)args->data);
bool is_cur_p = 0; /* current page */ bool is_cur_p = 0; /* current page */
app_t *app = (app_t *)args->app; app_t *app = (app_t *)args->app;
dlist_node_t *flwp = dlist_get_wp(fui->f_list);
dlist_set_wp(fui->f_list, fui->fl_selected);
switch (key) { switch (key) {
case KEY_DOWN: case KEY_DOWN:
is_cur_p = (fui->current_idx % fui->max_lines || fui->current_idx == 0); is_cur_p = (fui->current_idx % fui->max_lines || fui->current_idx == 0);
if (fui->current->next != NULL) {
if (is_cur_p) { if (is_cur_p) {
if (fui->current_idx < fui->max_lines) { if (fui->fl_selected->next != NULL) {
fui->current = fui->current->next; fui->fl_selected = fui->fl_selected->next;
fui->current_idx++; fui->current_idx++;
} }
} else { /* is_cur_p */ } else { /* is_cur_p */
page_next(fui); page_next(fui);
} }
} else
break; /* fui->current->next != NULL */
w_lfl_draw(fui); w_lfl_draw(fui);
break; break;
case KEY_UP: case KEY_UP:
if (fui->current->prev != NULL) {
if (fui->current_idx > 0) { if (fui->current_idx > 0) {
fui->current = fui->current->prev; if (fui->fl_selected->previous != NULL) {
fui->fl_selected = fui->fl_selected->previous;
fui->current_idx--; fui->current_idx--;
}
} else { } else {
page_previous(fui); page_previous(fui);
} }
} else
break;
w_lfl_draw(fui); w_lfl_draw(fui);
break; break;
case KEY_NPAGE: case KEY_NPAGE:
@@ -285,24 +333,26 @@ static void w_lfl_cb(w_cb_args_t *args) {
open_selected_item(fui, &(args->resp_data)); open_selected_item(fui, &(args->resp_data));
break; break;
} }
dlist_set_wp(fui->f_list, flwp);
} }
w_lfl_ui_t *w_lfl_init(WINDOW **win, w_t *w_parent) { w_lfl_ui_t *w_lfl_init(WINDOW **win, w_t *w_parent) {
w_lfl_ui_t *fl_ui = malloc(sizeof(w_lfl_ui_t)); w_lfl_ui_t *fui = malloc(sizeof(w_lfl_ui_t));
WINDOW *win_par = *(w_parent->w_parent->parent_win); WINDOW *win_par = *(w_parent->w_parent->parent_win);
w_init(&(fl_ui->w), w_parent, win, ""); w_init(&(fui->w), w_parent, win, "");
fl_ui->current_idx = 0; fui->current_idx = 0;
fl_ui->win_info = NULL; fui->win_info = NULL;
fl_ui->d_path = NULL; fui->d_path = NULL;
char *path = get_current_dir_name(); char *path = get_current_dir_name();
get_files_from_fs(fl_ui, path); get_files_from_fs(fui, path);
fl_ui->current = fl_ui->start; fui->f_list = dlist_init(NULL, w_lfl_item_t, NULL);
fl_ui->w.callback = w_lfl_cb; fui->w.callback = w_lfl_cb;
fl_ui->w.sz.x = getmaxx(win_par) / 10 * 8; fui->w.sz.x = getmaxx(win_par) / 10 * 8;
fl_ui->w.sz.y = getmaxy(win_par) / 10 * 8; fui->w.sz.y = getmaxy(win_par) / 10 * 8;
fl_ui->max_lines = 0; /* detects on the first draw */ fui->max_lines = 0; /* detects on the first draw */
fl_ui->cur_page = 1; fui->cur_page = 1;
return fl_ui; fui->d_path_previous = NULL;
return fui;
} }
static void w_lfl_cb_refresh(void *data) { static void w_lfl_cb_refresh(void *data) {
@@ -347,7 +397,8 @@ static int32_t process_user_input(app_t *app, w_cb_args_t *d_args) {
if (d_args->resp_data.code == cbrp_val && if (d_args->resp_data.code == cbrp_val &&
d_args->resp_data.val.type == val_num && d_args->resp_data.val.type == val_num &&
d_args->resp_data.val.val.num == 1) { d_args->resp_data.val.val.num == 1) {
file_upload_open(fui->d_path, fui->current->name, app->query_args); w_lfl_item_t *file = dlist_get_ptr(fui->fl_selected);
file_upload_open(fui->d_path, file->name, app->query_args);
app->query_args->state = S_UPLOAD_PARAMS; app->query_args->state = S_UPLOAD_PARAMS;
} }
return OK; return OK;
@@ -364,7 +415,7 @@ w_lfl_ui_t *w_lfl_init_win(app_t *app) {
app->main_ui.b_keys_len = 2; app->main_ui.b_keys_len = 2;
app->main_ui.cb_b_press = (w_cb_press_t)process_user_input; app->main_ui.cb_b_press = (w_cb_press_t)process_user_input;
app->main_ui.reset = w_lfl_reset_app; app->main_ui.reset = w_lfl_reset_app;
app->main_ui.draw = (draw_f_t *) w_lfl_draw; app->main_ui.draw = (draw_f_t *)w_lfl_draw;
app->active_callback = w_lfl_cb; app->active_callback = w_lfl_cb;
fui->w.callback = w_lfl_cb; fui->w.callback = w_lfl_cb;
w_init(&(fui->w), NULL, &(app->win), ""); w_init(&(fui->w), NULL, &(app->win), "");
@@ -375,6 +426,7 @@ w_lfl_ui_t *w_lfl_init_win(app_t *app) {
fui->w.sz.y = getmaxy(app->win); fui->w.sz.y = getmaxy(app->win);
fui->cur_page = 1; fui->cur_page = 1;
fui->w.parent_win = &(app->win); fui->w.parent_win = &(app->win);
fui->f_list = dlist_init(NULL, w_lfl_item_t, NULL);
/* * INIT UI * */ /* * INIT UI * */
/* define the width for each sub window */ /* define the width for each sub window */
@@ -392,18 +444,18 @@ w_lfl_ui_t *w_lfl_init_win(app_t *app) {
/* get files */ /* get files */
char *path = get_current_dir_name(); char *path = get_current_dir_name();
get_files_from_fs(fui, path); get_files_from_fs(fui, path);
fui->current = fui->start; fui->d_path_previous = NULL;
return fui; return fui;
} }
void w_lfl_reset(w_lfl_ui_t *fl_ui) { void w_lfl_reset(w_lfl_ui_t *fui) {
fl_ui->current_idx = 0; fui->current_idx = 0;
lfl_clear(&(fl_ui->start), &(fl_ui->current)); fl_clear(fui->f_list);
} }
void w_lfl_reset_app(void *app) { void w_lfl_reset_app(void *app) {
app_t *_app = app; app_t *_app = app;
w_lfl_ui_t *fl_ui = (w_lfl_ui_t *) _app->main_ui.ui; w_lfl_ui_t *fl_ui = (w_lfl_ui_t *)_app->main_ui.ui;
w_lfl_reset(fl_ui); w_lfl_reset(fl_ui);
} }
@@ -417,8 +469,9 @@ void w_lfl_draw(w_lfl_ui_t *fui) {
p_y = 1; p_y = 1;
p_x = 1; p_x = 1;
w_lfl_item_t *el = fui->page_start; dlist_set_wp(fui->f_list, fui->page_start);
w_lfl_item_t *active_el = fui->page_start; w_lfl_item_t *el = dlist_it_next(fui->f_list);
w_lfl_item_t *active_el = dlist_get_ptr(fui->fl_selected);
box(fui->win_list, 0, 0); box(fui->win_list, 0, 0);
box(fui->win_info, 0, 0); box(fui->win_info, 0, 0);
@@ -428,21 +481,24 @@ void w_lfl_draw(w_lfl_ui_t *fui) {
p_y++; p_y++;
continue; continue;
} }
if (el == fui->current) { if (el == fui->fl_selected->el_ptr) {
wattrset(win, A_BOLD | A_REVERSE); wattrset(win, A_BOLD | A_REVERSE);
active_el = el; active_el = el;
} }
p_x = 1; p_x = 1;
p_x += u_utf8_curs_printw(win, &p_y, &p_x, el->name, sz_x_f - 1, false); mvwaddnwstr(win, p_y, p_x, el->w_name, sz_x_f - 1);
p_x += wcslen(el->w_name);
/* p_x += u_utf8_curs_printw(win, &p_y, &p_x, el->name, sz_x_f - 1, false);
*/
int pad = sz_x_f - p_x; int pad = sz_x_f - p_x;
if (pad >= 0) { if (pad >= 0) {
mvwprintw(win, p_y, p_x, "%*s", pad, ""); mvwprintw(win, p_y, p_x, "%*s", pad, "");
} }
if (el == fui->current) { if (el == fui->fl_selected->el_ptr) {
wattroff(win, A_BOLD | A_REVERSE); wattroff(win, A_BOLD | A_REVERSE);
} }
p_y++; p_y++;
} while ((el = el->next) != NULL && p_y < sz_y_f); } while ((el = dlist_it_next(fui->f_list)) != NULL && p_y < sz_y_f);
p_x = 1; p_x = 1;
@@ -482,7 +538,7 @@ void w_lfl_draw(w_lfl_ui_t *fui) {
void w_lfl_destroy(w_lfl_ui_t **fui) { void w_lfl_destroy(w_lfl_ui_t **fui) {
w_lfl_ui_t *f = *fui; w_lfl_ui_t *f = *fui;
lfl_clear(&(f->start), &(f->current)); fl_clear(f->f_list);
free(*fui); free(*fui);
*fui = NULL; *fui = NULL;
} }
+7 -7
View File
@@ -4,8 +4,9 @@
#ifndef W_LFL_H /* LOCAL FILE LIST */ #ifndef W_LFL_H /* LOCAL FILE LIST */
#define W_LFL_H #define W_LFL_H
#include "../app.h" #include "dlist.h"
#include <stddef.h> #include <stddef.h>
#include <wchar.h>
#include <widget_core.h> #include <widget_core.h>
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stdint.h> #include <stdint.h>
@@ -14,18 +15,13 @@
typedef struct fs_fl_item { typedef struct fs_fl_item {
char *name; char *name;
char *path; char *path;
char *d_name; wchar_t *w_name;
size_t size; size_t size;
struct fs_fl_item *next;
struct fs_fl_item *prev;
u_char d_type; u_char d_type;
} w_lfl_item_t; } w_lfl_item_t;
typedef struct { typedef struct {
w_t w; w_t w;
w_lfl_item_t *current;
w_lfl_item_t *start;
w_lfl_item_t *page_start;
int32_t current_idx; int32_t current_idx;
int32_t max_lines; int32_t max_lines;
int32_t cur_page; int32_t cur_page;
@@ -34,6 +30,10 @@ typedef struct {
WINDOW *win_info; WINDOW *win_info;
WINDOW *win_list; WINDOW *win_list;
char *d_path; char *d_path;
char *d_path_previous;
dlist_t *f_list;
dlist_node_t *page_start;
dlist_node_t *fl_selected;
} w_lfl_ui_t; } w_lfl_ui_t;
void w_lfl_draw(w_lfl_ui_t *fl_ui); void w_lfl_draw(w_lfl_ui_t *fl_ui);
+3
View File
@@ -6,6 +6,9 @@
#include "fs_file_list.h" #include "fs_file_list.h"
#include "text_edit.h" #include "text_edit.h"
#include <core/app.h>
#include <widget/dialogue.h>
void main_window_set(app_t *app, enum main_window_type type) { void main_window_set(app_t *app, enum main_window_type type) {
if (app->main_ui.ui != NULL) { if (app->main_ui.ui != NULL) {
main_window_destroy(app); main_window_destroy(app);
+1 -1
View File
@@ -4,7 +4,7 @@
#ifndef MAIN_WINDOW_H #ifndef MAIN_WINDOW_H
#define MAIN_WINDOW_H #define MAIN_WINDOW_H
#include "../app.h" #include <common.h>
void main_window_set(app_t *app, enum main_window_type type); void main_window_set(app_t *app, enum main_window_type type);
void main_window_draw(app_t *app); void main_window_draw(app_t *app);
+5 -3
View File
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
/* Copyright (c) 2026 Oleksandr Zhylin */ /* Copyright (c) 2026 Oleksandr Zhylin */
#include "../file_processor.h"
#include "../server.h"
#include "../windows/main_window.h"
#include <dirent.h> #include <dirent.h>
#include <fcntl.h> #include <fcntl.h>
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
@@ -17,10 +14,15 @@
#include <utils.h> #include <utils.h>
#include <wchar.h> #include <wchar.h>
#include <core/file_processor.h>
#include <core/server.h>
#include <core/app.h>
#include "d_array.h" #include "d_array.h"
#include "file_list.h" #include "file_list.h"
#include "text_edit.h" #include "text_edit.h"
#include "widget_core.h" #include "widget_core.h"
#include "main_window.h"
/* TODO: расширять capacity линии при достижении лимита */ /* TODO: расширять capacity линии при достижении лимита */
-1
View File
@@ -4,7 +4,6 @@
#ifndef W_TEXT_EDIT_H /* LOCAL FILE LIST */ #ifndef W_TEXT_EDIT_H /* LOCAL FILE LIST */
#define W_TEXT_EDIT_H #define W_TEXT_EDIT_H
#include "../app.h"
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
+1 -13
View File
@@ -1,13 +1 @@
project("My Awesome Tested Project" C) add_subdirectory(utils)
cmake_minimum_required(VERSION 3.0) # Let's tell it a bit about our environment
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) # We declare a variable to select what to build
set (TARGET_GROUP production CACHE STRING "What Should I Build?") # Modules that exist in both our release and our unit tests, are added now
add_subdirectory(src) if(TARGET_GROUP STREQUAL release) # If we had any files that ONLY were compiled into our release, for example startup code,
# it's really handy to put it here.
elseif(TARGET_GROUP STREQUAL test) # Our tests need to let CTest know we're running tests
include(CTest) # It also needs to understand we have code in source and test directories
add_subdirectory(unity)
add_subdirectory(test)
else()
message(FATAL_ERROR "I don't know the TARGET_GROUP you gave me!")
endif()
+13
View File
@@ -0,0 +1,13 @@
# ---/ process_client_command /--------------------------------------
add_executable(test_dlist
dlist.c
)
target_link_libraries(test_dlist
cmocka
common
utils
)
add_test(NAME utils_dlist COMMAND test_dlist)
+222
View File
@@ -0,0 +1,222 @@
#include <cmocka.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <utils/dlist.h>
typedef struct {
int num;
char *text;
char buf[128];
} test_t;
bool dblist_add_cb(void *dst, void *src) {
// test_t *dst = _dst;
// test_t *src = _src;
memcpy(dst, src, sizeof(test_t));
return true;
}
#define tlist_add(dlist, item, prepend) \
dlist_add(dlist, item, test_t, dblist_add_cb, prepend);
#define LOREM_IPSUM \
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod " \
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim " \
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea " \
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate " \
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint " \
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt " \
"mollit anim id est laborum."
void test__init(void **state) {
test_t t = {.buf = "TEST BUF", .num = 123, .text = LOREM_IPSUM};
dlist_t *dlist = dlist_init(&t, test_t, dblist_add_cb);
assert_int_equal(dlist->len, 1);
test_t *dt_ptr = dlist->current->el_ptr;
assert_int_equal(dt_ptr->num, t.num);
assert_string_equal(dt_ptr->buf, t.buf);
assert_ptr_equal(dt_ptr->text, LOREM_IPSUM);
}
void test__add_several_times(void **state) {
test_t t1 = {.buf = "TEST BUF", .num = 123, .text = LOREM_IPSUM};
test_t t2 = {.buf = "TEST 1 BUF", .num = 11, .text = "RAND 1 TEXT"};
test_t t3 = {.buf = "TEST 2 BUF", .num = 22, .text = "RAND 2 TEXT"};
dlist_t *dlist = dlist_init(NULL, test_t, NULL);
dlist_add(dlist, &t1, test_t, dblist_add_cb, false);
dlist_add(dlist, &t2, test_t, dblist_add_cb, false);
dlist_add(dlist, &t3, test_t, dblist_add_cb, false);
assert_int_equal(dlist->len, 3);
test_t *dl_t1 = dlist->start->el_ptr;
test_t *dl_t2 = dlist->start->next->el_ptr;
test_t *dl_t3 = dlist->current->el_ptr;
assert_memory_equal(dl_t1, &t1, sizeof(test_t));
assert_memory_equal(dl_t2, &t2, sizeof(test_t));
assert_memory_equal(dl_t3, &t3, sizeof(test_t));
}
void test__add_several_times_prepend(void **state) {
test_t t1 = {.buf = "TEST BUF", .num = 123, .text = LOREM_IPSUM};
test_t t2 = {.buf = "TEST 1 BUF", .num = 11, .text = "RAND 1 TEXT"};
test_t t3 = {.buf = "TEST 2 BUF", .num = 22, .text = "RAND 2 TEXT"};
dlist_t *dlist = dlist_init(NULL, test_t, NULL);
dlist_add(dlist, &t1, test_t, dblist_add_cb, true);
dlist_add(dlist, &t2, test_t, dblist_add_cb, true);
dlist_add(dlist, &t3, test_t, dblist_add_cb, true);
assert_int_equal(dlist->len, 3);
test_t *dl_t1 = dlist->current->el_ptr;
test_t *dl_t2 = dlist->current->previous->el_ptr;
test_t *dl_t3 = dlist->start->el_ptr;
assert_memory_equal(dl_t1, &t1, sizeof(test_t));
assert_memory_equal(dl_t2, &t2, sizeof(test_t));
assert_memory_equal(dl_t3, &t3, sizeof(test_t));
}
void test__add_and_iterate(void **state) {
test_t t1 = {.buf = "TEST BUF", .num = 123, .text = LOREM_IPSUM};
test_t t2 = {.buf = "TEST 1 BUF", .num = 11, .text = "RAND 1 TEXT"};
test_t t3 = {.buf = "TEST 2 BUF", .num = 22, .text = "RAND 2 TEXT"};
dlist_t *dlist = dlist_init(NULL, test_t, NULL);
dlist_add(dlist, &t1, test_t, dblist_add_cb, false);
dlist_add(dlist, &t2, test_t, dblist_add_cb, false);
dlist_add(dlist, &t3, test_t, dblist_add_cb, false);
test_t *dl_t3 = dlist_it_prev(dlist);
test_t *dl_t2 = dlist_it_prev(dlist);
test_t *dl_t1 = dlist_it_prev(dlist);
assert_memory_equal(dl_t1, &t1, sizeof(test_t));
assert_memory_equal(dl_t2, &t2, sizeof(test_t));
assert_memory_equal(dl_t3, &t3, sizeof(test_t));
}
bool _sort(void *_a, void *_b) {
test_t *a = _a;
test_t *b = _b;
return a->num > b->num;
}
#define T_BUF "TEST BUF "
#define T_TEXT "RAND TEXT "
#define T_IT_NUM 120
void test__insert_sort(void **state) {
srand(time(NULL));
test_t t = {.text = T_TEXT};
dlist_t *dlist = dlist_init(NULL, test_t, NULL);
for (int i = 0; i < T_IT_NUM; i++) {
sprintf(t.buf, "%s %d", T_BUF, i);
t.num = rand();
dlist_add_sort(dlist, &t, test_t, _sort, dblist_add_cb);
}
test_t *t1, *t2;
t2 = dlist_it_prev(dlist);
t1 = dlist_it_prev(dlist);
assert_int_in_range(t2->num, t1->num, INT_MAX);
for (int i = T_IT_NUM - 2; i > 0; i--) {
t2 = t1;
t1 = dlist_it_prev(dlist);
assert_int_in_range(t2->num, t1->num, INT_MAX);
}
}
int delete_times = 0;
test_t *b_ptr = NULL;
bool delete_cb(void *el_ptr) {
assert_int_equal(b_ptr, el_ptr);
delete_times++;
return 1;
}
void test__remove_by_ptr(void **state) {
test_t ta = {.buf = "TEST A", .num = 123, .text = "TEXT A"};
test_t tb = {.buf = "TEST B", .num = 456, .text = "TEXT B"};
test_t tc = {.buf = "TEST C", .num = 789, .text = "TEXT C"};
dlist_t *dlist = dlist_init(NULL, int, NULL);
tlist_add(dlist, &ta, false);
tlist_add(dlist, &tb, false);
tlist_add(dlist, &tc, false);
b_ptr = dlist->start->next->el_ptr;
assert_int_equal(tb.num, 456);
dlist_remove_by_ptr(dlist, b_ptr, delete_cb);
assert_ptr_equal(dlist->start->next, dlist->current);
assert_ptr_equal(dlist->current->previous, dlist->start);
assert_int_equal(dlist->len, 2);
assert_int_equal(delete_times, 1);
test_t *a_ptr = dlist->start->el_ptr;
test_t *c_ptr = dlist->start->next->el_ptr;
assert_int_equal(a_ptr->num, 123);
assert_int_equal(c_ptr->num, 789);
}
int clean_db_call_times = 0;
bool clean_db(void *ptr) {
free(ptr);
clean_db_call_times++;
return 1;
}
void test__clear_list(void **state) {
test_t ta = {.buf = "TEST A", .num = 123, .text = "TEXT A"};
test_t tb = {.buf = "TEST B", .num = 456, .text = "TEXT B"};
test_t tc = {.buf = "TEST C", .num = 789, .text = "TEXT C"};
dlist_t *dlist = dlist_init(NULL, int, NULL);
tlist_add(dlist, &ta, false);
tlist_add(dlist, &tb, false);
tlist_add(dlist, &tc, false);
assert_int_equal(dlist->len, 3);
dlist_clear_list(dlist, clean_db);
assert_int_equal(dlist->len, 0);
assert_ptr_equal(dlist->current, NULL);
assert_ptr_equal(dlist->start, NULL);
assert_int_equal(clean_db_call_times, 3);
clean_db_call_times = 0;
}
int setup(void **state) { return 0; }
int tear_down(void **state) { return 0; }
int main(int argc, char **argv) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test__init),
cmocka_unit_test(test__add_several_times),
cmocka_unit_test(test__add_several_times_prepend),
cmocka_unit_test(test__add_and_iterate),
cmocka_unit_test(test__insert_sort),
cmocka_unit_test(test__remove_by_ptr),
cmocka_unit_test(test__clear_list),
};
return cmocka_run_group_tests(tests, setup, tear_down);
}