Работа с папками
Чтение файлов в директории
В стандарте си отсутствует реализация работы с папками – ни создания, ни чтения директории, ничего. Для работы с папками в кроссплатформенной библиотеке libuv есть набор методов. Но сначала рассмотрим особенности файловой системы. Библиотека libuv выделяет следующие типы файлов, описанные с помощью перечисляемого типа uv_dirent_type_t:
- UV_DIRENT_UNKNOWN – тип не известен
- UV_DIRENT_FILE – обыкновенный файл
- UV_DIRENT_DIR – папка
- UV_DIRENT_LINK – ссылка (ярлык)
- UV_DIRENT_FIFO – специализированный файл для межпроцессорного взаимодействия
- UV_DIRENT_SOCKET – сокет
- UV_DIRENT_CHAR - Специальный файл для передачи данных посимвольно, например, от клавиатуры, мыши, последовательного порта. Примеры файлов в ОС Linux это /dev/autofs, /dev/console, /dev/crash, /dev/lp0, /dev/null, /dev/ppp, /dev/random, /dev/tty
- UV_DIRENT_BLOCK - Специальный файл для передачи данных блочно, например, от клавиатуры, мыши, последовательного порта. Примеры файлов в ОС Linux это /dev/loop0, /dev/ram0, /dev/sda1, /dev/sr0
Для чтения содержимого папки сначала производим запрос uv_fs_scandir
int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb)
Все аргументы функции нам уже знакомы. Результатом выполнения (напомню, результат – это поле result запроса, который будет передан колбэку) будет число считанных объектов в папке. Для прохода по всем элементам используется функция uv_fs_scandir_next
int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent)
где req – это полученный колбэком запрос, а второй аргумент – это структура типа uv_dirent_t. Эта структура после каждой итерации будет иметь интересующие нас значения – поле name с именем объекта, и поле type типа uv_dirent_type_t, которое определяет тип объекта (о нём говорилось в самом начале).
Для тестирования создадим папку с тремя файлами, папкой и ссылкой.

Код программы для чтения списка файлов в каталоге
#include <uv.h> #include <stdio.h> #include <fcntl.h> #ifdef _WIN32 #include <conio.h> #include <Windows.h> #define Sleep(x) Sleep(x) #define wait() _getch() #else #include <unistd.h> #define Sleep(x) sleep(x) #define wait() scanf("1"); #endif uv_loop_t* loop; uv_fs_t scan_req; uv_fs_t scan_next_req; uv_fs_t close_req; void scan_cb(uv_fs_t*); void close_cb(uv_fs_t*); const char* name_by_type(uv_dirent_type_t); const char *dirpath = "C:/c/test_scan"; int main(int argc, char **argv) { int r; loop = uv_loop_new(); r = uv_fs_scandir(loop, &scan_req, dirpath, O_RDONLY, scan_cb); if (r < 0) { printf("Error at opening file: %s\n", uv_strerror(r)); } uv_run(loop, UV_RUN_DEFAULT); uv_loop_close(loop); return 0; } void scan_cb(uv_fs_t *req) { int r, i; uv_dirent_t dent; while (UV__EOF != uv_fs_scandir_next(req, &dent)) { printf("%s\t[%s]\n", dent.name, name_by_type(dent.type)); } r = uv_fs_close(loop, &close_req, req->file.fd, close_cb); uv_fs_req_cleanup(req); } void close_cb(uv_fs_t* req) { wait(); } const char* name_by_type(uv_dirent_type_t type) { switch (type) { case UV_DIRENT_UNKNOWN: return "Unknown"; case UV_DIRENT_FILE: return "File"; case UV_DIRENT_DIR: return "Directory"; case UV_DIRENT_LINK: return "Link"; case UV_DIRENT_FIFO: return "FIFO special file"; case UV_DIRENT_SOCKET: return "Socket"; case UV_DIRENT_CHAR: return "Char file"; case UV_DIRENT_BLOCK: return "Block file"; } }
Создание новой директории
Для создания новой директории есть две функции
int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb)
создаёт новый каталог. Стоит заметить, что аргумент mode не используется в ОС Windows. Также можно использовать функцию
int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb)
которая создаёт каталог с уникальным именем. При этом аргумент tpl – это шаблон имени, в котором последние 6 символов буду заменены на случайный набор символов.
Пример создания папки. Далее все директивы пропущены, чтобы уменьшить размер программы
uv_loop_t* loop; uv_fs_t mkdir_req; uv_fs_t close_req; void mkdir_cb(uv_fs_t*); void close_cb(uv_fs_t*); const char *dirpath = "C:/c/test_dir"; int main(int argc, char **argv) { int r; loop = uv_loop_new(); r = uv_fs_mkdir(loop, &mkdir_req, dirpath, O_RDONLY, mkdir_cb); if (r < 0) { printf("Error at making directory: %s\n", uv_strerror(r)); } uv_run(loop, UV_RUN_DEFAULT); uv_loop_close(loop); return 0; } void mkdir_cb(uv_fs_t* req) { int r = req->result; uv_fs_close(loop, &close_req, req->file.fd, close_cb); uv_fs_req_cleanup(req); } void close_cb(uv_fs_t* req) { printf("wait"); wait(); }
При создании папки с уникальным именем её имя будет в поле path запроса
uv_loop_t* loop; uv_fs_t mkdir_req; uv_fs_t close_req; void mkdir_cb(uv_fs_t*); void close_cb(uv_fs_t*); const char *template = "C:/c/my-folder-template-XXXXXX"; int main(int argc, char **argv) { int r; loop = uv_loop_new(); r = uv_fs_mkdtemp(loop, &mkdir_req, template, mkdir_cb); if (r < 0) { printf("Error at making directory: %s\n", uv_strerror(r)); } uv_run(loop, UV_RUN_DEFAULT); uv_loop_close(loop); return 0; } void mkdir_cb(uv_fs_t* req) { int r = req->result; printf("tmp folder name = %s", req->path); uv_fs_close(loop, &close_req, req->file.fd, close_cb); uv_fs_req_cleanup(req); } void close_cb(uv_fs_t* req) { wait(); }
Удаление директории
Для удаления папки в кроссплатформенной библиотеке libuv используется функция
int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb)
Важно, что директория должна быть пустой.
Пример программы для удаления пустой директории
#include <uv.h> #include <stdio.h> #include <fcntl.h> #ifdef _WIN32 #include <conio.h> #define wait() _getch() #else #define wait() scanf("1"); #endif uv_loop_t* loop; uv_fs_t remove_dir_req; void remove_dir_cb(uv_fs_t*); const char *dirname_remove = "C:/c/toremove"; int main(int argc, char **argv) { int r; loop = uv_default_loop(); r = uv_fs_rmdir(loop, &remove_dir_req, dirname_remove, remove_dir_cb); if (r) { printf("Error when remove directory: %s\n", uv_strerror(r)); } uv_run(loop, UV_RUN_DEFAULT); return 0; } void remove_dir_cb(uv_fs_t* req) { int r = req->result; printf("remove callback with status %d", r); uv_fs_req_cleanup(&remove_dir_req); wait(); }
