175 lines
4.0 KiB
C++
175 lines
4.0 KiB
C++
/* SPDX-License-Identifier: Apache-2.0
|
|
* (c) 2025, Konstantin Demin
|
|
*/
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
|
|
extern "C" {
|
|
#include <unistd.h>
|
|
}
|
|
|
|
#include "overlay.hh"
|
|
|
|
static
|
|
void usage(int retcode = 0)
|
|
{
|
|
static const char usage_msg[] =
|
|
"overlaydirs 0.0.1\n"
|
|
"Usage:\n"
|
|
" overlaydirs --help\n"
|
|
" show this message\n"
|
|
" overlaydirs --list [--no-sort|--zero] <source directory> [..<source directory>]\n"
|
|
// TODO: shell-escape mode
|
|
/* " overlaydirs --list [--no-sort|--zero|--escape] <source directory> [..<source directory>]\n" */
|
|
" list entries\n"
|
|
" overlaydirs --merge <target directory> <source directory> [..<source directory>]\n"
|
|
" symlinks entries into <target directory>\n"
|
|
"\n"
|
|
" --no-sort - don't sort entries\n"
|
|
" --zero - separate entries with NUL instead of LF\n"
|
|
// TODO: shell-escape mode
|
|
/*
|
|
" --escape - shell-escape strings\n"
|
|
"\n"
|
|
"Notes:\n"
|
|
" - flags \"--zero\" and \"--escape\" are mutually exclusive.\n"
|
|
*/
|
|
;
|
|
|
|
(void) write(STDERR_FILENO, usage_msg, sizeof(usage_msg));
|
|
|
|
exit(retcode);
|
|
}
|
|
|
|
static int main_list(int argc, char * argv[]);
|
|
static int main_merge(int argc, char * argv[]);
|
|
|
|
int main(int argc, char * argv[])
|
|
{
|
|
if (argc < 2) usage(0);
|
|
|
|
if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0))
|
|
usage(0);
|
|
else if (strcmp(argv[1], "--list") == 0)
|
|
return main_list(argc, argv);
|
|
else if (strcmp(argv[1], "--merge") == 0)
|
|
return main_merge(argc, argv);
|
|
else
|
|
usage(EINVAL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int main_list(int argc, char * argv[])
|
|
{
|
|
if (argc < 3) usage(EINVAL);
|
|
|
|
print_mode print_m = print_mode::_default;
|
|
sort_mode sort_m = sort_mode::_default;
|
|
|
|
int arg_start = 2;
|
|
while (arg_start < argc) {
|
|
char * _arg = argv[arg_start];
|
|
|
|
if (strncmp(_arg, "--", 2) != 0) break;
|
|
|
|
if (strcmp(_arg, "--") == 0) {
|
|
arg_start++;
|
|
break;
|
|
}
|
|
else if (strcmp(_arg, "--no-sort") == 0) {
|
|
arg_start++;
|
|
if (sort_m != sort_mode::_default) {
|
|
(void) fprintf(stderr, "overlaydirs: no-sort mode already set\n");
|
|
}
|
|
sort_m = sort_mode::none;
|
|
}
|
|
else if (strcmp(_arg, "--zero") == 0) {
|
|
arg_start++;
|
|
if (print_m != print_mode::_default) {
|
|
(void) fprintf(stderr, "overlaydirs: output mode already set\n");
|
|
}
|
|
print_m = print_mode::zero;
|
|
}
|
|
// TODO
|
|
/*
|
|
else if (strcmp(_arg, "--escape") == 0) {
|
|
arg_start++;
|
|
if (print_m != print_mode::_default) {
|
|
(void) fprintf(stderr, "overlaydirs: output mode already set\n");
|
|
}
|
|
print_m = print_mode::shell_escape;
|
|
}
|
|
*/
|
|
else {
|
|
(void) fprintf(stderr, "overlaydirs: unknown option \"%s\"\n", _arg);
|
|
// nevertheless, continue and try argument as a directory
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto roots = std::vector<ovl_dirspec_t>();
|
|
for (int i = arg_start; i < argc; i++) {
|
|
ovl_dirspec_t t;
|
|
if (!arg_to_rootspec(argv[i], &t, ovl_rootspec_flags::relative)) continue;
|
|
roots.push_back(t);
|
|
}
|
|
if (roots.empty()) {
|
|
(void) fprintf(stderr, "overlaydirs: no usable sources found\n");
|
|
return EINVAL;
|
|
}
|
|
|
|
auto ovl = process_overlay(roots, sort_m);
|
|
// not needed anymore
|
|
roots.clear();
|
|
roots.shrink_to_fit();
|
|
|
|
list_overlay(ovl, print_m);
|
|
return 0;
|
|
}
|
|
|
|
static int main_merge(int argc, char * argv[])
|
|
{
|
|
if (argc < 4) usage(EINVAL);
|
|
|
|
ovl_dirspec_t target;
|
|
if (!arg_to_rootspec(argv[2], &target, ovl_rootspec_flags::relative)) {
|
|
(void) fprintf(stderr, "overlaydirs: not a directory: \"%s\"\n", argv[2]);
|
|
return EINVAL;
|
|
}
|
|
if (!is_empty_dir(target.fd)) {
|
|
(void) fprintf(stderr, "overlaydirs: target directory is not empty\n");
|
|
return EEXIST;
|
|
}
|
|
|
|
auto roots = std::vector<ovl_dirspec_t>();
|
|
for (int i = 3; i < argc; i++) {
|
|
ovl_dirspec_t t;
|
|
if (!arg_to_rootspec(argv[i], &t)) continue;
|
|
roots.push_back(t);
|
|
}
|
|
if (roots.empty()) {
|
|
(void) fprintf(stderr, "overlaydirs: no usable sources found\n");
|
|
return EINVAL;
|
|
}
|
|
|
|
auto ovl = process_overlay(roots);
|
|
// not needed anymore
|
|
roots.clear();
|
|
roots.shrink_to_fit();
|
|
|
|
int rv = merge_overlay(ovl, target);
|
|
if (rv == -1) {
|
|
(void) fprintf(stderr, "overlaydirs: target directory is not empty\n");
|
|
return EEXIST;
|
|
}
|
|
|
|
return rv;
|
|
}
|