/* SPDX-License-Identifier: Apache-2.0 * (c) 2025, Konstantin Demin */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include extern "C" { #include } #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] [..]\n" // TODO: shell-escape mode /* " overlaydirs --list [--no-sort|--zero|--escape] [..]\n" */ " list entries\n" " overlaydirs --merge [..]\n" " symlinks entries into \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(); 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(); 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; }