/* * Copyright (C) 2023 Patrick McDermott * * This file is part of opkg-opk. * * opkg-opk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * opkg-opk is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with opkg-opk. If not, see . */ #include "config.h" #ifdef ENABLE_NLS #include #endif #include #include #include "defs.h" #include "i18n.h" #include "opk.h" #ifdef HAVE_GETOPT_LONG #include #else #include #endif #include "error.h" extern const char *PACKAGE_VERSION_GIT; /* TODO: Options -o, -u, -g, and -r */ static const char *_OPTSTRING = "bc:d:s:f:F:lLhV"; #ifdef HAVE_GETOPT_LONG static const struct option _LONGOPTS[] = { { .name = "build", .has_arg = 0, .flag = NULL, .val = 'b', }, { .name = "control-dir", .has_arg = 1, .flag = NULL, .val = 'c', }, { .name = "data-dir", .has_arg = 1, .flag = NULL, .val = 'd', }, { .name = "special", .has_arg = 1, .flag = NULL, .val = 's', }, { .name = "control-file", .has_arg = 1, .flag = NULL, .val = 'f', }, { .name = "data-file", .has_arg = 1, .flag = NULL, .val = 'F', }, { .name = "list-control", .has_arg = 0, .flag = NULL, .val = 'l', }, { .name = "list-data", .has_arg = 0, .flag = NULL, .val = 'L', }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h', }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V', }, }; #endif static void _help(const char *program_name) { printf(_("Usage: %s [-c CONTROL-DIR] [-f CONTROL-FILE] [-F DATA-FILE] " "[-l] [-L] PACKAGE\n"), program_name); printf(_(" or: %s -b -c CONTROL-DIR -d DATA-DIR [-s SPECIAL-FILE] " "PACKAGE\n"), program_name); #ifdef HAVE_GETOPT_LONG puts(_("Options:\n" " -b, --build Create a package from the specified\n" " CONTROL-DIR and DATA-DIR.\n" " -c, --control-dir=CONTROL-DIR In build mode, read control files from\n" " CONTROL-DIR. Otherwise, extract control\n" " files into CONTROL-DIR.\n" " -d, --data-dir=DATA-DIR In build mode, read data files from " "DATA-DIR\n" " -s, --special=SPECIAL-FILE Read device special files list from\n" " SPECIAL-FILE.\n" " -f, --control-file=CONTROL-FILE Print the contents of the named control " "file.\n" " If this option is given multiple times, the" "\n" " named control files will be printed in the" "\n" " order they appear in the package.\n" " -F, --data-file=DATA-FILE Print the contents of the named data file." "\n" " If this option is given multiple times, the" "\n" " named data files will be printed in the " "order\n" " they appear in the package.\n" " -l, --list-control List the control files.\n" " -L, --list-data List the data files. The list is currently" "\n" " produced in a format similar to that\n" " generated by GNU and BusyBox tar's verbose" "\n" " listing. User and group IDs and names are" "\n" " not checked against those on the host " "system,\n" " since they may differ.\n" " -h, --help Show this help information and exit.\n" " -V, --version Show version information and exit.")); #else puts(_("Options:\n" " -b Create a package from the specified CONTROL-DIR and DATA-DIR.\n" " -c In build mode, read control files from CONTROL-DIR. Otherwise, extract" "\n" " control files into CONTROL-DIR.\n" " -d In build mode, read data files from DATA-DIR\n" " -s Read device special files list from SPECIAL-FILE.\n" " -f Print the contents of the named control file. If this option is given\n" " multiple times, the named control files will be printed in the order " "they\n" " appear in the package.\n" " -F Print the contents of the named data file. If this option is given\n" " multiple times, the named data files will be printed in the order they\n" " appear in the package.\n" " -l List the control files.\n" " -L List the data files. The list is currently produced in a format " "similar\n" " to that generated by GNU and BusyBox tar's verbose listing. User and\n" " group IDs and names are not checked against those on the host system,\n" " since they may differ.\n" " -h Show this help information and exit.\n" " -V Show version information and exit.")); #endif } static void _version(void) { printf("%s %s%s\n", PACKAGE_NAME, PACKAGE_VERSION, PACKAGE_VERSION_GIT); /* TRANSLATORS: The "%s" conversion specifications are the copyright * year(s) and copyright holder(s), respectively. */ printf(_("Copyright (C) %s %s\n"), "2023", "Patrick McDermott"); puts(_("License GPLv3+: GNU GPL version 3 or later " ".\n" "This is free software: you are free to change and " "redistribute it.\n" "There is NO WARRANTY, to the extent permitted by " "law.\n")); printf(_("Please report bugs to <%s>.\n"), PACKAGE_BUGREPORT); } static void _help_tip(const char *program_name) { /* TRANSLATORS: The "%s" conversion specifications are the program name * and help option, respectively. */ fprintf(stderr, _("Try \"%s %s\" for more information\n"), program_name, #ifdef HAVE_GETOPT_LONG "--help" #else "-h" #endif ); } static void _opt_mutex(const char *program_name, char opt1, char opt2) { opkg_opk_error(_("Options -%c and -%c are mutually exclusive"), opt1, opt2); _help_tip(program_name); } int main(int argc, char *argv[]) { struct opkg_opk_opk *opk; int want_build; int build; int build_dirs; int opt_f; int opt_F; int opt_l; int opt_L; int opt; #ifdef ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); #ifdef HAVE_BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset(PACKAGE, "UTF-8"); #endif textdomain(PACKAGE); setlocale(LC_ALL, ""); #endif opkg_opk_set_program_name(argv[0]); /* Initialize package. */ opk = opkg_opk_opk_init(); if (opk == NULL) { return EXIT_FAILURE; } opterr = 0; want_build = 0; build = 0; build_dirs = 0; opt_f = 0; opt_F = 0; opt_l = 0; opt_L = 0; #ifdef HAVE_GETOPT_LONG while ((opt = getopt_long(argc, argv, _OPTSTRING, _LONGOPTS, NULL)) != -1) { #else while ((opt = getopt(argc, argv, _OPTSTRING)) != -1) { #endif switch(opt) { case 'b': build = 1; break; case 'c': ++build_dirs; opkg_opk_opk_control_dir(opk, optarg); break; case 'd': ++build_dirs; want_build = 1; opkg_opk_opk_data_dir(opk, optarg); break; case 's': want_build = 1; if (opkg_opk_opk_specials_read(opk, optarg) != OPKG_OPK_OK) { opkg_opk_error(_("Failed to read " "specials file") ); opkg_opk_opk_free(opk); return EXIT_FAILURE; } break; case 'f': want_build = -1; if (opt_l > 0) { _opt_mutex(argv[0], 'f', 'l'); opkg_opk_opk_free(opk); return EXIT_FAILURE; } opt_f = 1; if (opkg_opk_opk_print_control(opk, optarg) != OPKG_OPK_OK) { opkg_opk_opk_free(opk); return EXIT_FAILURE; } break; case 'F': want_build = -1; if (opt_L > 0) { _opt_mutex(argv[0], 'F', 'L'); opkg_opk_opk_free(opk); return EXIT_FAILURE; } opt_F = 1; if (opkg_opk_opk_print_data(opk, optarg) != OPKG_OPK_OK) { opkg_opk_opk_free(opk); return EXIT_FAILURE; } break; case 'l': want_build = -1; if (opt_f > 0) { _opt_mutex(argv[0], 'f', 'l'); opkg_opk_opk_free(opk); return EXIT_FAILURE; } opt_l = 1; opkg_opk_opk_list_control(opk); break; case 'L': want_build = -1; if (opt_F > 0) { _opt_mutex(argv[0], 'F', 'L'); opkg_opk_opk_free(opk); return EXIT_FAILURE; } opt_L = 1; opkg_opk_opk_list_data(opk); break; case 'h': _help(argv[0]); opkg_opk_opk_free(opk); return EXIT_SUCCESS; case 'V': _version(); opkg_opk_opk_free(opk); return EXIT_SUCCESS; default: opkg_opk_error(_("Invalid option: \"%c\""), optopt); _help_tip(argv[0]); opkg_opk_opk_free(opk); return EXIT_FAILURE; } } if (want_build < 0 && build > 0) { opkg_opk_error(_("Options -f, -F, -l, and -L " "are invalid with -b")); _help_tip(argv[0]); opkg_opk_opk_free(opk); return EXIT_FAILURE; } else if (want_build > 0 && build <= 0) { opkg_opk_error(_("Options -d and -s are only valid with -b")); /* TODO: opkg_opk_error(_("Options -d, -s, -o, -u, -g, and -r " "are only valid with -b")); */ _help_tip(argv[0]); opkg_opk_opk_free(opk); return EXIT_FAILURE; } else if (build > 0 && build_dirs < 2) { opkg_opk_error(_("Options -c and -d are required with -b")); _help_tip(argv[0]); opkg_opk_opk_free(opk); return EXIT_FAILURE; } /* TODO: Options -u and -g are invalid with -r */ argc -= optind; argv += optind; if (argc < 1) { opkg_opk_error(_("Missing package file operand")); _help_tip(argv[0]); opkg_opk_opk_free(opk); return EXIT_FAILURE; } else if (argc > 1) { opkg_opk_error(_("Too many package file operands")); _help_tip(argv[0]); opkg_opk_opk_free(opk); return EXIT_FAILURE; } /* Read or build package. */ if (build == 1) { if (opkg_opk_opk_write(opk, argv[0]) != OPKG_OPK_OK) { opkg_opk_opk_free(opk); return EXIT_FAILURE; } } else { if (opkg_opk_opk_read(opk, argv[0]) != OPKG_OPK_OK) { opkg_opk_opk_free(opk); return EXIT_FAILURE; } } opkg_opk_opk_free(opk); return EXIT_SUCCESS; }