/* Copyright (c) 2021 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "utilities/FileBundle.h" #include "utilities/IOUtilities.h" #include #include #include #include #include #include #include static void printUsage(void) { fprintf(stderr, "Usage: filebundler create [-f] [-add ] [...]\n" " filebundler update [-delete ] [-add ] [...]\n" " filebundler unpack [-f] [-o ]\n" " filebundler list \n"); } int main(int argc, char ** argv) { if (argc < 3) { printUsage(); return EXIT_FAILURE; } if (!strcmp(argv[1], "create")) { const char * outFilePath = NULL; bool force = false; FileBundle * bundle = FileBundle_create(); for (int argIndex = 2; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "-f")) { force = true; } else if (outFilePath == NULL) { outFilePath = argv[argIndex]; } else if (!strcmp(argv[argIndex], "-add")) { if (argc < argIndex + 4) { fprintf(stderr, "-add specified at end of arguments\n"); printUsage(); return EXIT_FAILURE; } FileBundle_fileType type = FILE_TYPE_INVALID; if (!sscanf(argv[argIndex + 2], "%d", &type)) { fprintf(stderr, "Couldn't understand \"%s\" as a file type integer\n", argv[argIndex + 2]); return EXIT_FAILURE; } size_t size = 0; void * data = readFileSimple(argv[argIndex + 3], &size); if (data == NULL) { fprintf(stderr, "Couldn't read file \"%s\" to bundle it (errno = %d)\n", argv[argIndex + 3], errno); return EXIT_FAILURE; } FileBundle_addFile(bundle, argv[argIndex + 1], type, data, size, true, false); argIndex += 3; } else { fprintf(stderr, "Unrecognized option \"%s\"\n", argv[argIndex]); printUsage(); return EXIT_FAILURE; } } if (outFilePath == NULL) { fprintf(stderr, "Bundle name not specified\n"); printUsage(); return EXIT_FAILURE; } struct stat statbuf; if (!force && !stat(outFilePath, &statbuf)) { if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { printf("Overwrite \"%s\"? [y/n]\n", outFilePath); char input = getchar(); while (getchar() != '\n'); if (input != 'y' && input != 'Y') { fprintf(stderr, "Not confirmed; operation canceled\n"); return EXIT_FAILURE; } } else { fprintf(stderr, "File \"%s\" exists; pass -f to overwrite\n", outFilePath); return EXIT_FAILURE; } } if (!FileBundle_writeFile(bundle, outFilePath)) { fprintf(stderr, "Couldn't write bundle to \"%s\" (errno = %d)\n", outFilePath, errno); return EXIT_FAILURE; } return EXIT_SUCCESS; } if (!strcmp(argv[1], "update")) { const char * bundlePath = NULL; FileBundle * bundle = NULL; for (int argIndex = 2; argIndex < argc; argIndex++) { if (bundlePath == NULL) { bundlePath = argv[argIndex]; bundle = FileBundle_loadFile(bundlePath); if (bundle == NULL) { fprintf(stderr, "Couldn't load \"%s\" as a FileBundle (errno = %d)\n", bundlePath, errno); return EXIT_FAILURE; } } else if (!strcmp(argv[argIndex], "-add")) { if (bundle == NULL) { fprintf(stderr, "Bundle file must be specified before items to add\n"); printUsage(); return EXIT_FAILURE; } if (argc < argIndex + 4) { fprintf(stderr, "-add specified at end of arguments\n"); printUsage(); return EXIT_FAILURE; } FileBundle_fileType type = FILE_TYPE_INVALID; if (!sscanf(argv[argIndex + 2], "%d", &type)) { fprintf(stderr, "Couldn't understand \"%s\" as a file type integer\n", argv[argIndex + 2]); return EXIT_FAILURE; } size_t size = 0; void * data = readFileSimple(argv[argIndex + 3], &size); if (data == NULL) { fprintf(stderr, "Couldn't read file \"%s\" to bundle it (errno = %d)\n", argv[argIndex + 3], errno); return EXIT_FAILURE; } FileBundle_addFile(bundle, argv[argIndex + 1], type, data, size, true, false); argIndex += 3; } else if (!strcmp(argv[argIndex], "-delete")) { if (bundle == NULL) { fprintf(stderr, "Bundle file must be specified before items to delete\n"); printUsage(); return EXIT_FAILURE; } if (argc < argIndex + 2) { fprintf(stderr, "-delete specified at end of arguments\n"); printUsage(); return EXIT_FAILURE; } if (!FileBundle_removeFile(bundle, argv[argIndex + 1])) { fprintf(stderr, "Warning: Identifier \"%s\" to be deleted doesn't exist in specified bundle\n", argv[argIndex + 1]); } argIndex++; } else { fprintf(stderr, "Unrecognized option \"%s\"\n", argv[argIndex]); printUsage(); return EXIT_FAILURE; } } if (bundle == NULL) { fprintf(stderr, "Bundle name not specified\n"); printUsage(); return EXIT_FAILURE; } if (!FileBundle_writeFile(bundle, bundlePath)) { fprintf(stderr, "Couldn't write bundle to \"%s\" (errno = %d)\n", bundlePath, errno); } return EXIT_SUCCESS; } if (!strcmp(argv[1], "unpack")) { FileBundle * bundle = NULL; const char * outDirectory = NULL; bool force = false; for (int argIndex = 2; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "-f")) { force = true; } else if (!strcmp(argv[argIndex], "-o")) { if (argc < argIndex + 2) { fprintf(stderr, "-o specified at end of arguments\n"); printUsage(); return EXIT_FAILURE; } outDirectory = argv[argIndex + 1]; argIndex++; } else if (bundle == NULL) { bundle = FileBundle_loadFile(argv[argIndex]); if (bundle == NULL) { fprintf(stderr, "Couldn't load \"%s\" as a FileBundle (errno = %d)\n", argv[argIndex], errno); return EXIT_FAILURE; } } else { fprintf(stderr, "Unrecognized option \"%s\"\n", argv[argIndex]); printUsage(); return EXIT_FAILURE; } } if (bundle == NULL) { fprintf(stderr, "Bundle not specified\n"); printUsage(); return EXIT_FAILURE; } unsigned int fileCount = FileBundle_getFileCount(bundle); for (unsigned int fileIndex = 0; fileIndex < fileCount; fileIndex++) { const char * fileIdentifier = FileBundle_getFileIdentifier(bundle, fileIndex); char outFilePath[2048]; if (outDirectory == NULL) { strncpy_safe(outFilePath, fileIdentifier, sizeof(outFilePath)); } else { snprintf_safe(outFilePath, sizeof(outFilePath), "%s/%s", outDirectory, fileIdentifier); } struct stat statbuf; if (!force && !stat(outFilePath, &statbuf)) { if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { printf("Overwrite \"%s\"? [y/n]\n", outFilePath); char input = getchar(); while (getchar() != '\n'); if (input != 'y' && input != 'Y') { continue; } } else { fprintf(stderr, "File \"%s\" exists; pass -f to overwrite\n", outFilePath); return EXIT_FAILURE; } } uint32_t size = 0; const void * data = FileBundle_getFileAtIndex(bundle, fileIndex, &size); if (!writeFileSimple(outFilePath, data, size)) { fprintf(stderr, "Couldn't write \"%s\" (errno = %d)\n", outFilePath, errno); return EXIT_FAILURE; } } return EXIT_SUCCESS; } if (!strcmp(argv[1], "list")) { FileBundle * bundle = FileBundle_loadFile(argv[2]); if (bundle == NULL) { fprintf(stderr, "Error: Couldn't load \"%s\" as a FileBundle (errno = %d)\n", argv[2], errno); return EXIT_FAILURE; } unsigned int fileCount = FileBundle_getFileCount(bundle); for (unsigned int fileIndex = 0; fileIndex < fileCount; fileIndex++) { printf("%s\n", FileBundle_getFileIdentifier(bundle, fileIndex)); } return EXIT_SUCCESS; } if (!strcmp(argv[1], "--help")) { printUsage(); return EXIT_SUCCESS; } fprintf(stderr, "Couldn't understand argument \"%s\"\n", argv[1]); printUsage(); return EXIT_FAILURE; }