diff options
-rw-r--r-- | include/spm.h | 27 | ||||
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/fs.c | 25 | ||||
-rw-r--r-- | src/install.c | 45 | ||||
-rw-r--r-- | src/internal_cmd.c | 92 | ||||
-rw-r--r-- | src/mime.c | 97 | ||||
-rw-r--r-- | src/relocation.c | 51 | ||||
-rw-r--r-- | src/spm.c | 19 | ||||
-rw-r--r-- | src/spm_build.c | 14 |
9 files changed, 364 insertions, 8 deletions
diff --git a/include/spm.h b/include/spm.h index f7b7b60..85af4d0 100644 --- a/include/spm.h +++ b/include/spm.h @@ -34,6 +34,14 @@ #define NOT_DIRSEP DIRSEP_WIN32 #endif +#define SPM_META_DEPENDS ".SPM_DEPENDS" +#define SPM_META_PREFIX_BIN ".SPM_PREFIX_BIN" +#define SPM_META_PREFIX_TEXT ".SPM_PREFIX_TEXT" +#define SPM_META_MANIFEST ".SPM_MANIFEST" // TODO: Implement + +#define PREFIX_WRITE_BIN 0 +#define PREFIX_WRITE_TEXT 1 + #define SPM_PACKAGE_EXTENSION ".tar.gz" #define PKG_DIR SPM_GLOBAL.package_dir #define TMP_DIR SPM_GLOBAL.tmp_dir @@ -110,6 +118,12 @@ typedef struct { char *path; } RelocationEntry; +typedef struct { + char *origin; + char *type; + char *charset; +} Mime; + // GLOBALS spm_vars SPM_GLOBAL; @@ -127,6 +141,7 @@ int replace_text(char *data, const char *_spattern, const char *_sreplacement); int file_replace_text(char *filename, const char *spattern, const char *sreplacement); RelocationEntry **prefixes_read(const char *filename); void prefixes_free(RelocationEntry **entry); +int prefixes_write(const char *output_file, int mode, char **prefix, const char *tree); // strings.c int num_chars(const char *sptr, int ch); @@ -186,6 +201,7 @@ void show_global_config(void); void check_runtime_environment(void); // install.c +int metadata_remove(const char *_path); int install(const char *destroot, const char *_package); // config.c @@ -231,4 +247,15 @@ int version_spec_from(const char *op); static int _find_by_spec_compare(const void *a, const void *b); ManifestPackage **find_by_spec(Manifest *manifest, const char *name, const char *op, const char *version_str); +// build.c +Process *file_command(const char *_filename); +Mime *file_mimetype(const char *filename); +void mime_free(Mime *m); +int build(int bargc, char **bargv); +int file_is_binary(const char *filename); +int file_is_text(const char *filename); + +// internal_cmd.c +int internal_cmd(int argc, char **argv); + #endif //SPM_SPM_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d866ef0..80fb9d9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,7 @@ include_directories( ${CMAKE_BINARY_DIR}/include ) -add_executable(spm spm.c config.c compat.c deps.c fs.c rpath.c find.c shell.c archive.c strings.c relocation.c install.c config_global.c manifest.c checksum.c extern/url.c version_spec.c) +add_executable(spm spm.c config.c compat.c deps.c fs.c rpath.c find.c shell.c archive.c strings.c relocation.c install.c config_global.c manifest.c checksum.c extern/url.c version_spec.c spm_build.c mime.c internal_cmd.c) target_link_libraries(spm rt crypto ssl curl) install( TARGETS spm @@ -299,6 +299,11 @@ int rsync(const char *_args, const char *_source, const char *_destination) { return returncode; } +/** + * Return the size of a file + * @param filename + * @return + */ long int get_file_size(const char *filename) { long int result = 0; FILE *fp = fopen(filename, "rb"); @@ -337,6 +342,26 @@ int mkdirs(const char *_path, mode_t mode) { return result; } +/** + * Convert size in bytes to the closest human-readable unit + * + * NOTE: Caller is responsible for freeing memory + * + * Example: + * ~~~{.c} + * char *output; + * output = human_readable_size(1); // "1B" + * free(output); + * output = human_readable_size(1024) // "1.0K" + * free(output); + * output = human_readable_size(1024000) // "1.0MB" + * free(output); + * // and so on + * ~~~ + * + * @param n size to convert + * @return string + */ char *human_readable_size(uint64_t n) { int i; double result = (double)n; diff --git a/src/install.c b/src/install.c index e647d31..7ecb401 100644 --- a/src/install.c +++ b/src/install.c @@ -1,5 +1,40 @@ #include "spm.h" +/** + * + * @param _path + * @return + */ +int metadata_remove(const char *_path) { + char *metadata[] = { + SPM_META_DEPENDS, + SPM_META_PREFIX_BIN, + SPM_META_PREFIX_TEXT, + SPM_META_MANIFEST, + NULL, + }; + + if (exists(_path) != 0) { + perror(_path); + fprintf(SYSERROR); + return -1; + } + + for (int i = 0; metadata[i] != NULL; i++) { + char path[PATH_MAX]; + sprintf(path, "%s%c%s", _path, DIRSEP, metadata[i]); + if (exists(path) != 0) { + continue; + } + if (unlink(path) < 0) { + perror(path); + fprintf(SYSERROR); + return -1; + } + } + return 0; +} + int install(const char *destroot, const char *_package) { char *package = find_package(_package); if (!package) { @@ -31,7 +66,7 @@ int install(const char *destroot, const char *_package) { chdir(tmpdir); { // Rewrite binary prefixes - RelocationEntry **b_record = prefixes_read(".SPM_PREFIX_BIN"); + RelocationEntry **b_record = prefixes_read(SPM_META_PREFIX_BIN); if (b_record) { for (int i = 0; b_record[i] != NULL; i++) { relocate(b_record[i]->path, b_record[i]->prefix, destroot); @@ -39,7 +74,7 @@ int install(const char *destroot, const char *_package) { } // Rewrite text prefixes - RelocationEntry **t_record = prefixes_read(".SPM_PREFIX_TEXT"); + RelocationEntry **t_record = prefixes_read(SPM_META_PREFIX_TEXT); if (t_record) { for (int i = 0; t_record[i] != NULL; i++) { file_replace_text(t_record[i]->path, t_record[i]->prefix, destroot); @@ -51,9 +86,13 @@ int install(const char *destroot, const char *_package) { } chdir(cwd); - // Append a trailing slash to tmpdir to direct rsync to copy files, not the directory, into destroot sprintf(source, "%s%c", tmpdir, DIRSEP); + + // Remove metadata files before copying + metadata_remove(source); + + // Copy temporary directory to destination if (rsync(NULL, source, destroot) != 0) { exit(1); } diff --git a/src/internal_cmd.c b/src/internal_cmd.c new file mode 100644 index 0000000..3f4d05d --- /dev/null +++ b/src/internal_cmd.c @@ -0,0 +1,92 @@ +#include "spm.h" + +static char *internal_commands[] = { + "mkprefixbin", "generate prefix manifest (binary)", + "mkprefixtext", "generate prefix manifest (text)", + NULL, NULL, +}; + +void internal_command_list(void) { + printf("possible commands:\n"); + for (int i = 0; internal_commands[i] != NULL; i++) { + printf(" %-20s - %-20s\n", internal_commands[i], internal_commands[i + 1]); + i++; + } +} + +void mkprefix_usage(void) { + printf("usage: mkprefix[bin|text] {output_file} {dir} {prefix ...}\n"); +} + +int internal_cmd(int argc, char **argv) { + int command_valid = 0; + char *command = argv[1]; + if (argc < 2) { + internal_command_list(); + return 1; + } + + for (int i = 0; internal_commands[i] != NULL; i++) { + if (strcmp(internal_commands[i], command) == 0) { + command_valid = 1; + break; + } + } + + if (!command_valid) { + fprintf(stderr, "error: '%s' is not a valid command\n", command); + internal_command_list(); + return 1; + } + + if (strcmp(command, "mkprefixbin") == 0 || strcmp(command, "mkprefixtext") == 0) { + char *outfile = argv[2]; + char *tree = argv[3]; + + int prefix_start = 4; + int prefixes; + for (int i = prefix_start; i < argc; i++) { + prefixes = i; + } + + // Check arguments + if (!outfile) { + fprintf(stderr, "error: missing output file name\n"); + mkprefix_usage(); + return -1; + } + if (!tree) { + fprintf(stderr, "error: missing directory path\n"); + mkprefix_usage(); + return -1; + } + if (!prefixes) { + fprintf(stderr, "error: missing prefix string(s) (%d, %d)\n", prefix_start, prefixes); + mkprefix_usage(); + return -1; + } + + char **prefix = (char **) calloc(prefixes + 1, sizeof(char *)); + if (!prefix) { + perror("prefix array"); + fprintf(SYSERROR); + return -1; + } + + // Populate array of prefixes; reusing pointers from argv + for (int i = 0; (i + prefix_start) < argc; i++) { + prefix[i] = argv[(i + prefix_start)]; + } + + if (SPM_GLOBAL.verbose) { + printf("Generating prefix manifest: %s\n", outfile); + } + + if (strcmp(command, "mkprefixbin") == 0) { + prefixes_write(outfile, PREFIX_WRITE_BIN, prefix, tree); + } else if (strcmp(command, "mkprefixtext") == 0) { + prefixes_write(outfile, PREFIX_WRITE_TEXT, prefix, tree); + } + } + return 0; +}
\ No newline at end of file diff --git a/src/mime.c b/src/mime.c new file mode 100644 index 0000000..d90cc3f --- /dev/null +++ b/src/mime.c @@ -0,0 +1,97 @@ +#include "spm.h" + +Process *file_command(const char *_filename) { + char *filename = strdup(_filename); + Process *proc_info = NULL; + char sh_cmd[PATH_MAX]; + sh_cmd[0] = '\0'; +#ifdef __APPLE__ + const char *fmt_cmd = "file -I \"%s\""; +#else // GNU + const char *fmt_cmd = "file -E -i \"%s\""; +#endif + + strchrdel(filename, "&;|"); + sprintf(sh_cmd, fmt_cmd, filename); + shell(&proc_info, SHELL_OUTPUT, sh_cmd); + +#ifdef __APPLE__ + // Force BSD command to return non-zero when a file can't be found + const char *failmsg = ": cannot open"; + if (strstr(proc_info->output, failmsg) != NULL) { + proc_info->returncode = 1; + } +#endif + free(filename); + return proc_info; +} + +Mime *file_mimetype(const char *filename) { + char **output = NULL; + char **parts = NULL; + Mime *type = NULL; + Process *proc = file_command(filename); + + if (proc->returncode != 0) { + return NULL; + } + output = split(proc->output, ":"); + if (!output || output[1] == NULL) { + return NULL; + } + parts = split(output[1], ";"); + if (!parts || !parts[0] || !parts[1]) { + return NULL; + } + + char *what = strdup(parts[0]); + what = lstrip(what); + + char *charset = strdup(strchr(parts[1], '=') + 1); + charset = lstrip(charset); + charset[strlen(charset) - 1] = '\0'; + + char *origin = strdup(realpath(filename, NULL)); + + type = (Mime *)calloc(1, sizeof(Mime)); + type->origin = origin; + type->type = strdup(what); + type->charset = strdup(charset); + + split_free(output); + split_free(parts); + return type; +} + +void mime_free(Mime *m) { + if (m != NULL) { + free(m->origin); + free(m->type); + free(m->charset); + free(m); + } +} + +int file_is_text(const char *filename) { + int result = 0; + char *path = normpath(filename); + Mime *type = file_mimetype(path); + if (startswith(type->type, "text/") == 0) { + result = 1; + } + free(path); + mime_free(type); + return result; +} + +int file_is_binary(const char *filename) { + int result = 0; + char *path = normpath(filename); + Mime *type = file_mimetype(path); + if (startswith(type->type, "application/") == 0 && strcmp(type->charset, "binary") == 0) { + result = 1; + } + free(path); + mime_free(type); + return result; +}
\ No newline at end of file diff --git a/src/relocation.c b/src/relocation.c index 13ae799..87b204e 100644 --- a/src/relocation.c +++ b/src/relocation.c @@ -201,6 +201,57 @@ RelocationEntry **prefixes_read(const char *filename) { return entry; } +/** + * Generate a prefix manifest + * + * Example: + * ~~~{.c} + * char **prefixes = {"/usr", "/var", NULL}; + * prefixes_write("binary.manifest", PREFIX_WRITE_BIN, prefixes, "/usr/bin"); + * prefixes_write("text.manifest", PREFIX_WRITE_TEXT, prefixes, "/usr/share/man/7"); + * ~~~ + * + * @param output_file file path to create + * @param mode `PREFIX_WRITE_BIN`, `PREFIX_WRITE_TEXT` + * @param prefix array of prefix strings + * @param tree directory to scan + * @return success=0, failure=1, error=-1 + */ +int prefixes_write(const char *output_file, int mode, char **prefix, const char *tree) { + FILE *fp = fopen(output_file, "w+"); + if (!fp) { + perror(output_file); + fprintf(SYSERROR); + return -1; + } + + FSTree *fsdata = fstree(tree); + if (!fsdata) { + fprintf(SYSERROR); + return -1; + } + for (int i = 0; i < fsdata->files_length; i++) { + for (int p = 0; prefix[p] != NULL; p++) { + int proceed = 0; + if (find_in_file(fsdata->files[i], prefix[p]) == 0) { + if (mode == PREFIX_WRITE_BIN) { + proceed = file_is_binary(fsdata->files[i]); + } else if (mode == PREFIX_WRITE_TEXT) { + proceed = file_is_text(fsdata->files[i]); + } + + if (!proceed) { + continue; + } + + fprintf(fp, "#%s\n%s\n", prefix[p], fsdata->files[i]); + } + } + } + fclose(fp); + return 0; +} + int relocate(const char *_filename, const char *_oldstr, const char *_newstr) { int returncode; Process *proc = NULL; @@ -13,14 +13,15 @@ const int PACKAGE_MAX = 0xff; void usage(const char *program_name) { printf( - "usage: %s [-hVv] [-I|--install {package ...}]\n" + "usage: %s [-hVvBIrLS]\n" " -h, --help show this help message\n" " -V, --version show version\n" " -v, --verbose show more information\n" + " -B, --build build package(s)\n" " -I, --install install package(s)\n" - " -S --search search for a package\n" - " -L --list list available packages\n" - " -r --root installation prefix (requires --install)\n" + " -r, --root installation prefix (requires --install)\n" + " -L, --list list available packages\n" + " -S, --search search for a package\n" , program_name ); } @@ -77,6 +78,16 @@ int main(int argc, char *argv[]) { manifest_free(info); exit(0); } + else if (strcmp(arg, "--cmd") == 0) { + int c = argc - i; + char **a = &argv[i]; + exit(internal_cmd(c, a)); + } + else if (strcmp(arg, "-B") == 0 || strcmp(arg, "--build") == 0) { + int c = argc - i; + char **a = &argv[i]; + exit(build(c, a)); + } else if (strcmp(arg, "-L") == 0 || strcmp(arg, "--list") == 0) { RUNTIME_LIST = 1; } diff --git a/src/spm_build.c b/src/spm_build.c new file mode 100644 index 0000000..4cc4c47 --- /dev/null +++ b/src/spm_build.c @@ -0,0 +1,14 @@ +#include "spm.h" + +int build(int argc, char **argv) { + printf("build:\n"); + printf("argc: %d\n", argc); + printf("argv:\n"); + for (int i = 0; i < argc; i++) { + printf("%d: %s\n", i, argv[i]); + } + + return 0; +} + + |