Compare commits
18
Commits
92115c0dd4
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5021f61c80
|
||
|
|
0ee52bc2ea
|
||
|
|
d681f11bef
|
||
|
|
274442105e
|
||
|
|
e8d95f14b9
|
||
|
|
3d9b6ccac9
|
||
|
|
247419fc93
|
||
|
|
58d23fbf61
|
||
|
|
c563c71b5f
|
||
|
|
6b20f2f179
|
||
|
|
01b078c8d9
|
||
|
|
769aab6d1f
|
||
|
|
43a1da856f
|
||
|
|
55994e9cb3
|
||
|
|
a1e1a0a0d3
|
||
|
|
fc33d521a2
|
||
|
|
7711749d03
|
||
|
|
5b42ea6a73
|
@@ -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
|
||||||
@@ -27,6 +27,12 @@ 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
|
||||||
)
|
)
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# TODO:
|
# TODO:
|
||||||
|
|
||||||
## Current:
|
## Current:
|
||||||
- [ ] Sorting in the file list (local files):
|
|
||||||
- [ ] Testing env
|
## Future:
|
||||||
- [ ] Test dlist
|
- [ ] File catalogs (or tags)
|
||||||
- [ ] Dlist sort
|
- [ ] Store only unique files (check MD5 sums)
|
||||||
|
|
||||||
## Global tasks:
|
## Global tasks:
|
||||||
- [ ] Testing
|
- [ ] Testing
|
||||||
|
|||||||
@@ -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)
|
||||||
+1
Submodule external/cmocka added at 7f736f65dc
+2
-3
@@ -4,6 +4,7 @@
|
|||||||
#ifndef COMMON_H
|
#ifndef COMMON_H
|
||||||
#define COMMON_H
|
#define COMMON_H
|
||||||
|
|
||||||
|
#include "dlist.h"
|
||||||
#include <d_array.h>
|
#include <d_array.h>
|
||||||
#include <ncursesw/ncurses.h>
|
#include <ncursesw/ncurses.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -44,7 +45,6 @@ typedef struct fl_item {
|
|||||||
char *name;
|
char *name;
|
||||||
char *description;
|
char *description;
|
||||||
char *owner;
|
char *owner;
|
||||||
struct fl_item *next;
|
|
||||||
} fl_item_t;
|
} fl_item_t;
|
||||||
|
|
||||||
enum main_window_type {
|
enum main_window_type {
|
||||||
@@ -66,15 +66,14 @@ typedef struct main_window {
|
|||||||
int32_t b_keys_len;
|
int32_t b_keys_len;
|
||||||
} main_window_t;
|
} main_window_t;
|
||||||
|
|
||||||
|
|
||||||
typedef struct file_args {
|
typedef struct file_args {
|
||||||
int file_d;
|
int file_d;
|
||||||
fl_item_t f_selected;
|
fl_item_t f_selected;
|
||||||
fl_item_t *l_start;
|
fl_item_t *l_start;
|
||||||
fl_item_t *l_current;
|
fl_item_t *l_current;
|
||||||
|
dlist_t *f_list;
|
||||||
} file_args_t;
|
} file_args_t;
|
||||||
|
|
||||||
|
|
||||||
typedef struct params {
|
typedef struct params {
|
||||||
unsigned short port;
|
unsigned short port;
|
||||||
unsigned addr;
|
unsigned addr;
|
||||||
|
|||||||
+85
-69
@@ -2,8 +2,10 @@
|
|||||||
/* Copyright (c) 2026 Oleksandr Zhylin */
|
/* Copyright (c) 2026 Oleksandr Zhylin */
|
||||||
|
|
||||||
#include "alert.h"
|
#include "alert.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>
|
||||||
@@ -19,28 +21,27 @@
|
|||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <widget.h>
|
#include <widget.h>
|
||||||
#include <common.h>
|
|
||||||
#include <windows/file_list.h>
|
#include <windows/file_list.h>
|
||||||
|
|
||||||
#include "file_processor.h"
|
#include "file_processor.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))) {
|
||||||
@@ -55,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);
|
||||||
}
|
}
|
||||||
@@ -90,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);
|
||||||
@@ -173,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;
|
||||||
@@ -316,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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fl_item_t *fl_select(fl_item_t *start, int num) {
|
dst->name = malloc(sizeof(char) * (strlen(src->name) + 1));
|
||||||
fl_item_t *current = start;
|
strcpy(dst->name, src->name);
|
||||||
for (; num != 1; num--) {
|
|
||||||
if (current->next != NULL) {
|
dst->owner = malloc(sizeof(char) * (strlen(src->owner) + 1));
|
||||||
current = current->next;
|
strcpy(dst->owner, src->owner);
|
||||||
} else {
|
return true;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fl_clear(fl_item_t **start, fl_item_t **arg_current) {
|
static bool fl_rm_cb(void *_ptr) {
|
||||||
if (*start == NULL)
|
fl_item_t *ptr = _ptr;
|
||||||
return;
|
if (ptr->description != NULL)
|
||||||
fl_item_t *next, *current;
|
free(ptr->description);
|
||||||
current = *start;
|
if (ptr->name != NULL)
|
||||||
|
free(ptr->name);
|
||||||
|
if (ptr->owner != NULL)
|
||||||
|
free(ptr->owner);
|
||||||
|
free(ptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
/* work with file list */
|
||||||
next = current->next;
|
static void fl_add(dlist_t *fl_list, char *q_line) {
|
||||||
free(current->description);
|
fl_item_t f_item;
|
||||||
free(current->name);
|
char f_description[INBUFSIZE * 100];
|
||||||
free(current);
|
char f_owner[256];
|
||||||
} while ((current = next) != NULL);
|
char f_name[256];
|
||||||
*start = NULL;
|
f_item.description = f_description;
|
||||||
*arg_current = NULL;
|
f_item.owner = f_owner;
|
||||||
|
f_item.name = f_name;
|
||||||
|
f_item.size = 0;
|
||||||
|
|
||||||
|
fl_q_extract(&f_item, q_line);
|
||||||
|
|
||||||
|
dlist_add(fl_list, &f_item, fl_item_t, fl_add_cb, false);
|
||||||
}
|
}
|
||||||
@@ -8,13 +8,13 @@
|
|||||||
|
|
||||||
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
|
||||||
@@ -50,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 */
|
||||||
|
|||||||
+4
-1
@@ -14,8 +14,9 @@
|
|||||||
#include <widget.h>
|
#include <widget.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <core/app.h>
|
|
||||||
#include "alert.h"
|
#include "alert.h"
|
||||||
|
#include "core/file_processor.h"
|
||||||
|
#include <core/app.h>
|
||||||
#include <core/connection.h>
|
#include <core/connection.h>
|
||||||
#include <core/query.h>
|
#include <core/query.h>
|
||||||
|
|
||||||
@@ -27,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();
|
||||||
|
|
||||||
@@ -42,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();
|
||||||
|
|
||||||
|
|||||||
+174
-35
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
+24
-17
@@ -2,9 +2,10 @@
|
|||||||
/* Copyright (c) 2026 Oleksandr Zhylin */
|
/* Copyright (c) 2026 Oleksandr Zhylin */
|
||||||
|
|
||||||
#include "file_list.h"
|
#include "file_list.h"
|
||||||
|
#include "main_window.h"
|
||||||
|
#include "dlist.h"
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
#include "main_window.h"
|
|
||||||
#include <bstrlib.h>
|
#include <bstrlib.h>
|
||||||
#include <ncursesw/ncurses.h>
|
#include <ncursesw/ncurses.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -44,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);
|
||||||
@@ -56,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,
|
||||||
@@ -68,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,
|
||||||
@@ -109,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,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 */
|
||||||
@@ -208,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;
|
||||||
@@ -240,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;
|
||||||
@@ -279,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, "");
|
||||||
|
|||||||
@@ -4,14 +4,15 @@
|
|||||||
#ifndef W_FILE_LIST_H
|
#ifndef W_FILE_LIST_H
|
||||||
#define W_FILE_LIST_H
|
#define W_FILE_LIST_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;
|
||||||
|
|||||||
+172
-118
@@ -15,16 +15,24 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <utils.h>
|
#include <utils.h>
|
||||||
|
|
||||||
|
#include <core/app.h>
|
||||||
#include <core/file_processor.h>
|
#include <core/file_processor.h>
|
||||||
#include <core/fs.h>
|
#include <core/fs.h>
|
||||||
#include <core/server.h>
|
#include <core/server.h>
|
||||||
#include <core/app.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) {
|
||||||
@@ -46,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,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';
|
||||||
@@ -183,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,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:
|
||||||
@@ -287,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) {
|
||||||
@@ -349,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;
|
||||||
@@ -377,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 */
|
||||||
@@ -394,13 +444,13 @@ 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) {
|
||||||
@@ -419,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);
|
||||||
@@ -430,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;
|
||||||
|
|
||||||
@@ -484,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;
|
||||||
}
|
}
|
||||||
@@ -4,7 +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 "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>
|
||||||
@@ -13,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;
|
||||||
@@ -33,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);
|
||||||
|
|||||||
+1
-13
@@ -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()
|
|
||||||
@@ -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)
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user