
It's now a sensible source flie containing a set of routines that do something coherently connected to each other, so it doesn't deserve that 'fake-' prefix that I used for the previous monolithic files full of tangled-together stuff. While I'm here, I've also made up nicer (i.e. more distinguishable) random magic numbers for the structure-type disambiguation.
220 lines
6.2 KiB
C
220 lines
6.2 KiB
C
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <uchar.h>
|
|
|
|
#include <err.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "memory.h"
|
|
#include "dupstr.h"
|
|
#include "subproc.h"
|
|
#include "uchars.h"
|
|
|
|
/*
|
|
* The same routine MsiCloseHandle is used by our client to dispose of
|
|
* three kinds of structure that _we_ think of as completely different
|
|
* types. So we must ensure all three start with distinct magic number
|
|
* fields, so as to know what kind we're being asked to close.
|
|
*/
|
|
typedef struct MsiTypePrefix {
|
|
enum {
|
|
MAIN = 0x2B7FB8B8,
|
|
VIEW = 0x1570B0E3,
|
|
RECORD = 0x62365065
|
|
} 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,
|
|
MsiMainCtx **out_ctx)
|
|
{
|
|
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", cNULL);
|
|
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++] = dupstr("sh");
|
|
ctx->args[ctx->nargs++] = dupstr("-c");
|
|
ctx->args[ctx->nargs++] = dupstr("cd \"$0\" && \"$@\"");
|
|
ctx->args[ctx->nargs++] = dupstr(ctx->tempdir);
|
|
ctx->args[ctx->nargs++] = dupstr("msibuild");
|
|
ctx->args[ctx->nargs++] = dupstr(ctx->outfile);
|
|
*out_ctx = ctx;
|
|
return 0;
|
|
}
|
|
|
|
uint32_t MsiDatabaseImportW(MsiMainCtx *ctx, const char16_t *folder,
|
|
const char16_t *file)
|
|
{
|
|
assert(ctx->t.type == MAIN);
|
|
system_argv("sh", "-c", "cd \"$0\" && cp \"$1\" \"$2\"",
|
|
ascii(folder, true), ascii(file, true), ctx->tempdir, cNULL);
|
|
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++] = dupstr("-i");
|
|
ctx->args[ctx->nargs++] = dupcat(ctx->tempdir, "/", ascii(file, true),
|
|
cNULL);
|
|
return 0;
|
|
}
|
|
|
|
typedef struct MsiView {
|
|
MsiTypePrefix t;
|
|
|
|
FILE *fp;
|
|
char *targetdir;
|
|
MsiMainCtx *ctx;
|
|
} MsiView;
|
|
|
|
uint32_t MsiDatabaseOpenViewW(MsiMainCtx *ctx, const char16_t *query,
|
|
MsiView **outview)
|
|
{
|
|
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", cNULL),
|
|
"a");
|
|
view->targetdir = dupcat(ctx->tempdir, "/", "Binary", cNULL);
|
|
} else if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `Icon`")) {
|
|
view->fp = fopen(dupcat(ctx->tempdir, "/", "Icon.idt", cNULL),
|
|
"a");
|
|
view->targetdir = dupcat(ctx->tempdir, "/", "Icon", cNULL);
|
|
} 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 MsiViewExecute(MsiView *view, void *params)
|
|
{
|
|
assert(view->t.type == VIEW);
|
|
return 0;
|
|
}
|
|
|
|
typedef struct MsiRecord {
|
|
MsiTypePrefix t;
|
|
|
|
char *name, *data;
|
|
} MsiRecord;
|
|
|
|
MsiRecord *MsiCreateRecord(uint32_t nparams)
|
|
{
|
|
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;
|
|
}
|
|
|
|
uint32_t MsiRecordSetStringW(MsiRecord *rec, uint32_t field, char16_t *value)
|
|
{
|
|
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 MsiRecordSetStreamW(MsiRecord *rec, uint32_t field, char16_t *path)
|
|
{
|
|
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 MsiViewModify(MsiView *view, uint32_t mode, MsiRecord *rec)
|
|
{
|
|
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, cNULL);
|
|
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++] = dupstr("-a");
|
|
ctx->args[ctx->nargs++] = dupstr(rec->name);
|
|
ctx->args[ctx->nargs++] = dupstr(rec->data);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint32_t MsiCloseHandle(MsiTypePrefix *t)
|
|
{
|
|
if (t->type == VIEW) {
|
|
MsiView *view = (MsiView *)t;
|
|
if (view->fp)
|
|
fclose(view->fp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint32_t MsiDatabaseCommit(MsiMainCtx *ctx)
|
|
{
|
|
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;
|
|
}
|