From 29795aed5b2c81c264eab087e480d204ec7df8f8 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 15 May 2017 20:04:18 +0100 Subject: We now get as far as building a non-empty MSI file! But I haven't tested it yet, so it's probably got a zillion things wrong inside it. --- fake-lib.c | 43 ++++++++----- fake-lib.h | 3 + fake-msi.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++---------- fake-winterop.c | 3 +- 4 files changed, 190 insertions(+), 49 deletions(-) diff --git a/fake-lib.c b/fake-lib.c index 9255005..2207ce3 100644 --- a/fake-lib.c +++ b/fake-lib.c @@ -28,6 +28,25 @@ char *ascii(const char16_t *wstr, bool translate_slashes) return ret; } +void system_argv_array(char **args) +{ + pid_t pid = fork(); + if (pid < 0) + err(1, "fork"); + + if (pid == 0) { + execvp(args[0], args); + warn("execvp"); + _exit(127); + } + + int status; + if (waitpid(pid, &status, 0) != pid) + err(1, "waitpid"); + if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) + errx(1, "subcommand failed"); +} + void system_argv(const char *cmd, ...) { int nargs, nchars; @@ -54,21 +73,7 @@ void system_argv(const char *cmd, ...) va_end(ap); *argp++ = NULL; - pid_t pid = fork(); - if (pid < 0) - err(1, "fork"); - - if (pid == 0) { - execvp(args[0], args); - warn("execvp"); - _exit(127); - } - - int status; - if (waitpid(pid, &status, 0) != pid) - err(1, "waitpid"); - if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) - errx(1, "subcommand failed"); + system_argv_array(args); } void c16cpy(char16_t *out, uint32_t *outsize, char *s) @@ -91,6 +96,14 @@ void *smalloc(size_t size) return toret; } +void *srealloc(void *ptr, size_t size) +{ + void *toret = realloc(ptr, size); + if (!toret) + errx(1, "out of memory"); + return toret; +} + char *dupcat(const char *str, ...) { va_list ap; diff --git a/fake-lib.h b/fake-lib.h index 65fed63..c95169a 100644 --- a/fake-lib.h +++ b/fake-lib.h @@ -1,9 +1,12 @@ char *ascii(const char16_t *wstr, bool translate_slashes); void system_argv(const char *cmd, ...); +void system_argv_array(char **args); void c16cpy(char16_t *out, uint32_t *outsize, char *s); void *smalloc(size_t size); +void *srealloc(void *ptr, size_t size); char *dupcat(const char *str, ...); #define snew(type) ((type *)smalloc(sizeof(type))) #define snewn(n,type) ((type *)smalloc((n)*sizeof(type))) +#define sresize(ptr,n,type) ((type *)srealloc(ptr,(n)*sizeof(type))) #define sfree(ptr) free(ptr) diff --git a/fake-msi.c b/fake-msi.c index 59e0a4b..3f82f74 100644 --- a/fake-msi.c +++ b/fake-msi.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -25,74 +26,199 @@ uint32_t MsiGetFileVersionW(const char16_t *filename, return 0; } +typedef struct MsiTypePrefix { + enum { MAIN, VIEW, RECORD } type; +} MsiTypePrefix; + +typedef struct MsiMainCtx { + MsiTypePrefix t; + + char *tempdir; + char *outfile; + + char **args; + int nargs, argsize; +} MsiMainCtx; + + uint32_t MsiOpenDatabaseW(const char16_t *filename, const char16_t *persist, - void **handle) + MsiMainCtx **out_ctx) { - char *file = ascii(filename, true); - warnx("FIXME: MsiOpenDatabaseW(%s,%p)", file, persist); - close(open(file, O_WRONLY | O_CREAT, 0666)); - *handle = (void *)1; + MsiMainCtx *ctx = snew(MsiMainCtx); + ctx->t.type = MAIN; + ctx->outfile = ascii(filename, true); + close(open(ctx->outfile, O_CREAT | O_WRONLY, 0666)); + ctx->outfile = realpath(ctx->outfile, NULL); + unlink(ctx->outfile); + ctx->tempdir = dupcat(ctx->outfile, "-msiXXXXXX", (const char *)NULL); + if (!mkdtemp(ctx->tempdir)) + err(1, "%s: mkdtemp", ctx->tempdir); + ctx->nargs = 0; + ctx->argsize = 16; + ctx->args = snewn(ctx->argsize, char *); + ctx->args[ctx->nargs++] = dupcat("sh", (const char *)NULL); + ctx->args[ctx->nargs++] = dupcat("-c", (const char *)NULL); + ctx->args[ctx->nargs++] = dupcat("cd \"$0\" && \"$@\"", + (const char *)NULL); + ctx->args[ctx->nargs++] = dupcat(ctx->tempdir, (const char *)NULL); + ctx->args[ctx->nargs++] = dupcat("msibuild", (const char *)NULL); + ctx->args[ctx->nargs++] = dupcat(ctx->outfile, (const char *)NULL); + *out_ctx = ctx; return 0; } -uint32_t MsiCloseHandle(void *handle) +uint32_t MsiDatabaseImportW(MsiMainCtx *ctx, const char16_t *folder, + const char16_t *file) { - warnx("FIXME: MsiCloseHandle(%p)", handle); + assert(ctx->t.type == MAIN); + system_argv("sh", "-c", "cd \"$0\" && cp \"$1\" \"$2\"", + ascii(folder, true), ascii(file, true), ctx->tempdir, + (const char *)NULL); + if (ctx->nargs + 2 >= ctx->argsize) { + ctx->argsize = ctx->nargs * 5 / 4 + 16; + ctx->args = sresize(ctx->args, ctx->argsize, char *); + } + ctx->args[ctx->nargs++] = dupcat("-i", (const char *)NULL); + ctx->args[ctx->nargs++] = dupcat(ctx->tempdir, "/", ascii(file, true), + (const char *)NULL); return 0; } -uint32_t MsiDatabaseImportW(void *handle, const char16_t *folder, - const char16_t *file) +typedef struct MsiView { + MsiTypePrefix t; + + FILE *fp; + char *targetdir; + MsiMainCtx *ctx; +} MsiView; + +uint32_t MsiDatabaseOpenViewW(MsiMainCtx *ctx, const char16_t *query, + MsiView **outview) { - warnx("FIXME: MsiDatabaseImport(%p,%s,%s)", handle, ascii(folder, true), - ascii(file, true)); + assert(ctx->t.type == MAIN); + MsiView *view = snew(MsiView); + view->t.type = VIEW; + view->ctx = ctx; + char *cquery = ascii(query, false); + if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `_Streams`")) + view->fp = NULL; /* special case */ + else { + if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `Binary`")) { + view->fp = fopen(dupcat(ctx->tempdir, "/", "Binary.idt", + (const char *)NULL), "a"); + view->targetdir = dupcat(ctx->tempdir, "/", "Binary", + (const char *)NULL); + } else if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `Icon`")) { + view->fp = fopen(dupcat(ctx->tempdir, "/", "Icon.idt", + (const char *)NULL), "a"); + view->targetdir = dupcat(ctx->tempdir, "/", "Icon", + (const char *)NULL); + } else + errx(1, "unrecognised query: %s", cquery); + if (!view->fp) + err(1, "open"); + if (mkdir(view->targetdir, 0777) < 0) + err(1, "%s: mkdir", view->targetdir); + } + *outview = view; return 0; } -uint32_t MsiDatabaseOpenViewW(void *handle, const char16_t *query, - void **outhandle) +uint32_t MsiViewExecute(MsiView *view, void *params) { - warnx("FIXME: MsiDatabaseOpenView(%p,%s)", handle, ascii(query, false)); - *outhandle = (void *)2; + assert(view->t.type == VIEW); return 0; } -uint32_t MsiViewExecute(void *handle, void *params) +typedef struct MsiRecord { + MsiTypePrefix t; + + char *name, *data; +} MsiRecord; + +MsiRecord *MsiCreateRecord(uint32_t nparams) { - warnx("FIXME: MsiViewExecute(%p)", handle); - return 0; + MsiRecord *rec = snew(MsiRecord); + rec->t.type = RECORD; + + if (nparams != 2) + errx(1, "bad MsiCreateRecord param count %u", (unsigned)nparams); + rec->name = rec->data = NULL; + return rec; } -void *MsiCreateRecord(uint32_t nparams) +uint32_t MsiRecordSetStringW(MsiRecord *rec, uint32_t field, char16_t *value) { - warnx("FIXME: MsiCreateRecord(%u)", (unsigned)nparams); - return (void *)3; + assert(rec->t.type == RECORD); + if (field != 1) + errx(1, "bad MsiRecordSetString param index %u", (unsigned)field); + rec->name = ascii(value, false); + return 0; } -uint32_t MsiRecordSetStringW(void *handle, uint32_t field, char16_t *value) +uint32_t MsiRecordSetStreamW(MsiRecord *rec, uint32_t field, char16_t *path) { - warnx("FIXME: MsiRecordSetString(%p,%u,%s)", handle, (unsigned)field, - ascii(value, false)); + assert(rec->t.type == RECORD); + if (field != 2) + errx(1, "bad MsiRecordSetStream param index %u", (unsigned)field); + rec->data = ascii(path, true); return 0; } -uint32_t MsiRecordSetStreamW(void *handle, uint32_t field, char16_t *path) +uint32_t MsiViewModify(MsiView *view, uint32_t mode, MsiRecord *rec) { - warnx("FIXME: MsiRecordSetStream(%p,%u,%s)", handle, (unsigned)field, - ascii(path, true)); + assert(view->t.type == VIEW); + assert(rec->t.type == RECORD); + if (view->fp) { + system_argv("sh", "-c", "cp \"$0\" \"$1\"/\"$2\"", + rec->data, view->targetdir, rec->name, + (const char *)NULL); + fprintf(view->fp, "%s\t%s\r\n", rec->name, rec->name); + } else { + MsiMainCtx *ctx = view->ctx; + if (ctx->nargs + 3 >= ctx->argsize) { + ctx->argsize = ctx->nargs * 5 / 4 + 16; + ctx->args = sresize(ctx->args, ctx->argsize, char *); + } + ctx->args[ctx->nargs++] = dupcat("-s", (const char *)NULL); + ctx->args[ctx->nargs++] = dupcat(rec->name, (const char *)NULL); + ctx->args[ctx->nargs++] = dupcat(rec->data, (const char *)NULL); + } return 0; } -uint32_t MsiViewModify(void *vhandle, uint32_t mode, void *rechandle) +uint32_t MsiCloseHandle(MsiTypePrefix *t) { - warnx("FIXME: MsiViewModify(%p,%u,%p)", - vhandle, (unsigned)mode, rechandle); + if (t->type == VIEW) { + MsiView *view = (MsiView *)t; + if (view->fp) + fclose(view->fp); + } return 0; } -uint32_t MsiDatabaseCommit(void *handle) +uint32_t MsiDatabaseCommit(MsiMainCtx *ctx) { - warnx("FIXME: MsiDatabaseCommit(%p)", handle); + assert(ctx->t.type == MAIN); + printf("commit:"); + for (int i = 0; i < ctx->nargs; i++) { + printf(" '"); + for (const char *p = ctx->args[i]; *p; p++) { + if (*p == '\'') + printf("'\\''"); + else + putchar(*p); + } + printf("'"); + } + printf("\n"); + + if (ctx->nargs + 1 >= ctx->argsize) { + ctx->argsize = ctx->nargs * 5 / 4 + 16; + ctx->args = sresize(ctx->args, ctx->argsize, char *); + } + ctx->args[ctx->nargs++] = NULL; + system_argv_array(ctx->args); return 0; } diff --git a/fake-winterop.c b/fake-winterop.c index d792553..c302f12 100644 --- a/fake-winterop.c +++ b/fake-winterop.c @@ -40,8 +40,7 @@ uint32_t CreateCabBegin(const char16_t *wzCab, const char16_t *wzCabDir, ctx->outdir = ascii(wzCabDir, true); ctx->outfile = dupcat(ctx->outdir, "/", ascii(wzCab, true), (const char *)NULL); - ctx->tempdir = snewn(20 + strlen(ctx->outdir), char); - sprintf(ctx->tempdir, "%s/cabXXXXXX", ctx->outdir); + ctx->tempdir = dupcat(ctx->outdir, "/cabXXXXXX", (const char *)NULL); if (!mkdtemp(ctx->tempdir)) err(1, "mkdtemp"); *out_ctx = ctx; -- cgit v1.2.3-55-g6feb