From cb61fa1ecdff22772ddf350ec0bbcf99b01552bc Mon Sep 17 00:00:00 2001 From: rxi Date: Sat, 1 Oct 2016 00:35:22 +0100 Subject: [PATCH] Added packaging functionality, added packaged exe support to filesystem Adds package.c and package.h. Adds support for the `--pack` argument for packing a project directory into an exe or tar file. --- src/filesystem.c | 84 +++++++++++++++++-- src/main.c | 6 ++ src/package.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++ src/package.h | 19 +++++ 4 files changed, 310 insertions(+), 6 deletions(-) create mode 100644 src/package.c create mode 100644 src/package.h diff --git a/src/filesystem.c b/src/filesystem.c index 5e345dd..7ad5c5e 100644 --- a/src/filesystem.c +++ b/src/filesystem.c @@ -242,14 +242,80 @@ static void* tar_read(Mount *mnt, const char *filename, int *size) { } -static int tar_mount(Mount *mnt, const char *path) { - mtar_t *tar = dmt_malloc( sizeof(*tar) ); +typedef struct { FILE *fp; int offset; } TarStream; - /* Try to init tar */ - int err = mtar_open(tar, path, "r"); +static int tar_stream_read(mtar_t *tar, void *data, unsigned size) { + TarStream *t = tar->stream; + unsigned res = fread(data, 1, size, t->fp); + return (res == size) ? MTAR_ESUCCESS : MTAR_EREADFAIL; +} + +static int tar_stream_seek(mtar_t *tar, unsigned offset) { + TarStream *t = tar->stream; + int res = fseek(t->fp, t->offset + offset, SEEK_SET); + return (res == 0) ? MTAR_ESUCCESS : MTAR_ESEEKFAIL; +} + +static int tar_stream_close(mtar_t *tar) { + TarStream *t = tar->stream; + fclose(t->fp); + dmt_free(t); + return MTAR_ESUCCESS; +} + + +static int tar_mount(Mount *mnt, const char *path) { + FILE *fp = NULL; + TarStream *stream = NULL; + mtar_t *tar = NULL; + + + /* Try to open file */ + fp = fopen(path, "rb"); + if (!fp) { + goto fail; + } + + /* Init tar stream */ + stream = dmt_calloc(1, sizeof(*stream)); + if (!stream) { + goto fail; + } + stream->fp = fp; + + /* Init tar */ + tar = dmt_calloc(1, sizeof(*tar)); + if (!tar) { + goto fail; + } + tar->read = tar_stream_read; + tar->seek = tar_stream_seek; + tar->close = tar_stream_close; + tar->stream = stream; + + /* Check start of file for valid tar header */ + mtar_header_t h; + int err = mtar_read_header(tar, &h); + + /* If checking the start of the file failed then check the end of file for a + * "TAR\0" tag and offset, this would have been added when packaging (see + * `package.c`) to indicate the offset of the tar archive's beginning from the + * file's end */ if (err) { - dmt_free(tar); - return FILESYSTEM_EFAILURE; + int offset; + char buf[4] = ""; + fseek(fp, -8, SEEK_END); + fread(buf, 1, 4, fp); + fread(&offset, 1, 4, fp); + if ( !memcmp(buf, "TAR\0", 4) ) { + fseek(fp, -offset, SEEK_END); + stream->offset = ftell(fp); + } + mtar_rewind(tar); + err = mtar_read_header(tar, &h); + if (err) { + goto fail; + } } /* Init mount */ @@ -262,6 +328,12 @@ static int tar_mount(Mount *mnt, const char *path) { /* Return ok */ return FILESYSTEM_ESUCCESS; + +fail: + if (fp) fclose(fp); + dmt_free(tar); + dmt_free(stream); + return FILESYSTEM_EFAILURE; } diff --git a/src/main.c b/src/main.c index 63dc8b9..9bb2ec5 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,7 @@ #include "mouse.h" #include "image.h" #include "palette.h" +#include "package.h" static lua_State *L; @@ -45,6 +46,11 @@ int luaopen_love(lua_State *L); int main(int argc, char **argv) { + /* Handle package command */ + if ( package_run(argc, argv) == PACKAGE_ESUCCESS ) { + exit(EXIT_SUCCESS); + } + /* Init everything */ atexit(deinit); vga_init(); diff --git a/src/package.c b/src/package.c new file mode 100644 index 0000000..86f4160 --- /dev/null +++ b/src/package.c @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2016 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include +#include +#include +#include + +#include "lib/microtar/microtar.h" + +#include "package.h" + + +static void error(const char *fmt, ...) { + va_list argp; + printf("Package error: "); + va_start(argp, fmt); + vprintf(fmt, argp); + va_end(argp); + printf("\n"); + exit(EXIT_FAILURE); +} + + +static int tar_stream_write(mtar_t *tar, const void *data, unsigned size) { + unsigned res = fwrite(data, 1, size, tar->stream); + if (res != size) { + error("failed when writing to output file"); + } + return MTAR_ESUCCESS; +} + + +static void concat(char *dst, int dstsz, ...) { + const char *s; + va_list argp; + int i = 0; + va_start(argp, dstsz); + while ( (s = va_arg(argp, const char*)) ) { + while (*s) { + dst[i++] = *s++; + if (i == dstsz) { + error("string length exceeds destination buffer"); + } + } + } + dst[i] = '\0'; + va_end(argp); +} + + +static +void concat_path(char *dst, int dstsz, const char *dir, const char *filename) { + int dirlen = strlen(dir); + if ( dir[dirlen - 1] == '/' || *dir == '\0' ) { + concat(dst, dstsz, dir, filename, NULL); + } else { + concat(dst, dstsz, dir, "/", filename, NULL); + } +} + + +static void write_file(mtar_t *tar, const char *inname, const char *outname) { + FILE *fp = fopen(inname, "rb"); + if (!fp) { + error("couldn't open input file '%s'", inname); + } + + /* Get size */ + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + /* Write header */ + mtar_header_t h; + memset(&h, 0, sizeof(h)); + concat(h.name, sizeof(h.name), outname, NULL); + h.size = size; + h.mode = 0664; + mtar_write_header(tar, &h); + + /* Write file */ + int chr; + while ( (chr = fgetc(fp)) != EOF ) { + unsigned char byte = chr; + mtar_write_data(tar, &byte, 1); + } + + /* Close file and return ok */ + fclose(fp); +} + + +static void write_dir(mtar_t *tar, const char *indir, const char *outdir) { + char inbuf[256]; + char outbuf[256]; + struct dirent *ep; + + DIR *dir = opendir(indir); + if (!dir) { + error("couldn't open input dir '%s'", indir); + } + + /* Write dir */ + mtar_header_t h; + memset(&h, 0, sizeof(h)); + concat(h.name, sizeof(h.name), outdir, NULL); + h.type = MTAR_TDIR; + h.mode = 0664; + mtar_write_header(tar, &h); + + /* Write files */ + while ( (ep = readdir(dir)) ) { + /* Skip `.` and `..` */ + if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, "..")) { + continue; + } + /* Get full input name and full output name */ + concat_path(inbuf, sizeof(inbuf), indir, ep->d_name); + concat_path(outbuf, sizeof(outbuf), outdir, ep->d_name); + /* Write */ + DIR *d = opendir(inbuf); + if (d) { + closedir(d); + write_dir(tar, inbuf, outbuf); + } else { + write_file(tar, inbuf, outbuf); + } + } + + closedir(dir); +} + + +void package_make(const char *indir, const char *outfile, const char *exefile, int type) { + /* Open file */ + FILE *fp = fopen(outfile, "wb"); + if (!fp) { + error("couldn't open output file"); + } + + /* Copy .exe to file if exe type is set */ + if (type == PACKAGE_TEXE) { + FILE *exefp = fopen(exefile, "rb"); + if (!exefp) { + error("couldn't open .exe file"); + } + int chr; + while ( (chr = fgetc(exefp)) != EOF ) { + fputc(chr, fp); + } + fclose(exefp); + } + + /* Get start of file (used for offset) */ + int start = ftell(fp); + + /* Init tar writer */ + mtar_t tar; + memset(&tar, 0, sizeof(tar)); + tar.write = tar_stream_write; + tar.stream = fp; + + /* Write package data to file and finalize tar */ + write_dir(&tar, indir, ""); + mtar_finalize(&tar); + + /* Write "TAR\0" and offset int so we can find the .tar if it was appended + * onto the end of the .exe */ + int offset = ftell(fp) - start + 8; + fwrite("TAR\0", 1, 4, fp); + fwrite(&offset, 1, 4, fp); + + /* Done */ + fclose(fp); +} + + +int package_run(int argc, char **argv) { + /* Check for `--pack` argument; return failure if it isn't present */ + if (argc < 2) { + return PACKAGE_EFAILURE; + } + if ( strcmp(argv[1], "--pack") != 0 && strcmp(argv[1], "--PACK") != 0 ) { + return PACKAGE_EFAILURE; + } + + /* Check arguments */ + if (argc < 4) { + error("expected arguments: %s DIRNAME OUTFILE", argv[1]); + } + + /* Set package type based on file extension */ + int type = PACKAGE_TTAR; + if ( strstr(argv[3], ".exe") || strstr(argv[3], ".EXE") ) { + type = PACKAGE_TEXE; + } + + /* Make package and return success*/ + package_make(argv[2], argv[3], argv[0], type); + return PACKAGE_ESUCCESS; +} diff --git a/src/package.h b/src/package.h new file mode 100644 index 0000000..0d2d90a --- /dev/null +++ b/src/package.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2016 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +enum { + PACKAGE_TTAR, + PACKAGE_TEXE +}; + +enum { + PACKAGE_ESUCCESS = 0, + PACKAGE_EFAILURE = -1 +}; + +void package_make(const char *indir, const char *outfile, const char *exefile, int type); +int package_run(int argc, char **argv);