Skip to content

Commit c5ef352

Browse files
committed
feat: Add lws_dir_du_cb for du-like functionality
Co-developed-by: Gemini 2.5 Pro
1 parent eda3381 commit c5ef352

File tree

5 files changed

+195
-0
lines changed

5 files changed

+195
-0
lines changed

include/libwebsockets/lws-misc.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,34 @@ lws_dir(const char *dirpath, void *user, lws_dir_callback_function cb);
822822
LWS_VISIBLE LWS_EXTERN int
823823
lws_dir_rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde);
824824

825+
826+
/**
827+
* lws_dir_du_t: context for lws_dir_du_cb()
828+
*
829+
* It's zeroed before starting the lws_dir() walk.
830+
*/
831+
832+
typedef struct lws_dir_du {
833+
uint64_t size_in_bytes;
834+
uint32_t count_files;
835+
} lws_dir_du_t;
836+
837+
/**
838+
* lws_dir_du_cb() - callback for lws_dir that performs recursive du
839+
*
840+
* \param dirpath: directory we are at in lws_dir
841+
* \param user: pointer to a lws_dir_du_t to collate the results in
842+
* \param lde: lws_dir info on the file or directory we are at
843+
*
844+
* This is a readymade du -b callback for use with lws_dir. It recursively
845+
* sums the sizes of all files it finds and the count of files. The user
846+
an
847+
* lws_dir_du_t struct should be zeroed before starting the walk.
848+
*/
849+
LWS_VISIBLE LWS_EXTERN int
850+
lws_dir_du_cb(const char *dirpath, void *user, struct lws_dir_entry *lde);
851+
852+
825853
/*
826854
* We pass every file in the base dir through a filter, and call back on the
827855
* ones that match. Directories are ignored.

lib/misc/dir.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,37 @@ lws_dir_glob_check(const char *nm, const char *filt)
209209
return 0;
210210
}
211211

212+
int
213+
lws_dir_du_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
214+
{
215+
lws_dir_du_t *du = (lws_dir_du_t *)user;
216+
char path[384];
217+
struct stat s;
218+
219+
if (!strcmp(lde->name, ".") || !strcmp(lde->name, ".."))
220+
return 0;
221+
222+
lws_snprintf(path, sizeof(path), "%s%c%s", dirpath, csep, lde->name);
223+
224+
if (lde->type == LDOT_DIR) {
225+
lws_dir(path, user, lws_dir_du_cb);
226+
227+
return 0;
228+
}
229+
230+
if (lde->type == LDOT_FILE) {
231+
if (stat(path, &s))
232+
lwsl_warn("%s: stat %s failed %d\n", __func__,
233+
path, errno);
234+
else {
235+
du->size_in_bytes += (uint64_t)s.st_size;
236+
du->count_files++;
237+
}
238+
}
239+
240+
return 0;
241+
}
242+
212243
/*
213244
* We get passed a single filter string, like "*.txt" or "mydir/\*.rpm" or so.
214245
*/
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
project(lws-api-test-dir C)
2+
cmake_minimum_required(VERSION 3.10)
3+
find_package(libwebsockets CONFIG REQUIRED)
4+
list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})
5+
include(CheckCSourceCompiles)
6+
include(LwsCheckRequirements)
7+
8+
set(SAMP lws-api-test-dir)
9+
set(SRCS main.c )
10+
11+
set(requirements 1)
12+
require_lws_config(LWS_WITH_DIR 1 requirements)
13+
14+
if (requirements)
15+
16+
add_executable(${SAMP} ${SRCS})
17+
add_test(NAME api-test-dir COMMAND ${SAMP})
18+
19+
if (websockets_shared)
20+
target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})
21+
add_dependencies(${SAMP} websockets_shared)
22+
else()
23+
target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS})
24+
endif()
25+
endif()
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* lws-api-test-dir
3+
*
4+
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
5+
*
6+
* This file is made available under the Creative Commons CC0 1.0
7+
* Universal Public Domain Dedication.
8+
*/
9+
10+
#include <libwebsockets.h>
11+
#include <sys/stat.h>
12+
#include <fcntl.h>
13+
14+
#if defined(WIN32)
15+
#include <direct.h>
16+
#define mkdir(x,y) _mkdir(x)
17+
#define rmdir _rmdir
18+
#endif
19+
20+
static int
21+
create_file(const char *path, size_t size)
22+
{
23+
int fd = lws_open(path, O_CREAT | O_WRONLY | O_TRUNC, 0600);
24+
char buf[1024];
25+
size_t s = size;
26+
27+
if (fd < 0)
28+
return 1;
29+
30+
memset(buf, 'A', sizeof(buf));
31+
32+
while (s) {
33+
size_t w = sizeof(buf);
34+
if (w > s)
35+
w = s;
36+
if (write(fd, buf, w) != (ssize_t)w) {
37+
close(fd);
38+
return 1;
39+
}
40+
s -= w;
41+
}
42+
43+
close(fd);
44+
45+
return 0;
46+
}
47+
48+
int main(int argc, const char **argv)
49+
{
50+
int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
51+
lws_dir_du_t du;
52+
int result = 0;
53+
54+
lwsl_user("lws-api-test-dir\n");
55+
lws_set_log_level(logs, NULL);
56+
lwsl_user("LWS API selftest: lws_dir du\n");
57+
58+
/* Create test directory structure */
59+
mkdir("./test-dir", 0700);
60+
mkdir("./test-dir/subdir", 0700);
61+
62+
if (create_file("./test-dir/file1", 10)) {
63+
lwsl_err("Failed to create file1\n");
64+
result = 1;
65+
goto cleanup;
66+
}
67+
if (create_file("./test-dir/file2", 20)) {
68+
lwsl_err("Failed to create file2\n");
69+
result = 1;
70+
goto cleanup;
71+
}
72+
if (create_file("./test-dir/subdir/file3", 30)) {
73+
lwsl_err("Failed to create file3\n");
74+
result = 1;
75+
goto cleanup;
76+
}
77+
78+
memset(&du, 0, sizeof(du));
79+
if (!lws_dir("./test-dir", &du, lws_dir_du_cb)) {
80+
lwsl_err("lws_dir failed\n");
81+
result = 1;
82+
goto cleanup;
83+
}
84+
85+
lwsl_user("Total size: %llu, total files: %u\n",
86+
(unsigned long long)du.size_in_bytes, du.count_files);
87+
88+
if (du.size_in_bytes != 60) {
89+
lwsl_err("size_in_bytes is %llu, expected 60\n",
90+
(unsigned long long)du.size_in_bytes);
91+
result = 1;
92+
}
93+
94+
if (du.count_files != 3) {
95+
lwsl_err("count_files is %u, expected 3\n", du.count_files);
96+
result = 1;
97+
}
98+
99+
cleanup:
100+
/* Clean up test directory structure */
101+
lws_dir("./test-dir", NULL, lws_dir_rm_rf_cb);
102+
rmdir("./test-dir");
103+
104+
if (!result)
105+
lwsl_user("Completed successfully\n");
106+
else
107+
lwsl_err("Failed\n");
108+
109+
return result;
110+
}

minimal-examples-lowlevel/api-tests/api-test-lws_spawn/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ find_package(libwebsockets CONFIG REQUIRED)
44
list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})
55
include(CheckCSourceCompiles)
66
include(LwsCheckRequirements)
7+
include(CTest)
78

89
set(SAMP lws-api-test-lws_spawn)
910
set(SRCS main.c)

0 commit comments

Comments
 (0)