diff options
author | Simon Tatham <anakin@pobox.com> | 2017-05-15 20:04:18 +0100 |
---|---|---|
committer | Simon Tatham <anakin@pobox.com> | 2017-05-15 20:04:18 +0100 |
commit | 29795aed5b2c81c264eab087e480d204ec7df8f8 (patch) | |
tree | d3349d53478a998b1354d6ca4a9c32819f1592e5 | |
parent | b19fd4ee7798a4948f5f47fafd89d67576a4642a (diff) | |
download | wix-on-linux-29795aed5b2c81c264eab087e480d204ec7df8f8.tar.gz wix-on-linux-29795aed5b2c81c264eab087e480d204ec7df8f8.tar.bz2 wix-on-linux-29795aed5b2c81c264eab087e480d204ec7df8f8.zip |
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.
-rw-r--r-- | fake-lib.c | 43 | ||||
-rw-r--r-- | fake-lib.h | 3 | ||||
-rw-r--r-- | fake-msi.c | 190 | ||||
-rw-r--r-- | fake-winterop.c | 3 |
4 files changed, 190 insertions, 49 deletions
@@ -28,6 +28,25 @@ char *ascii(const char16_t *wstr, bool translate_slashes) | |||
28 | return ret; | 28 | return ret; |
29 | } | 29 | } |
30 | 30 | ||
31 | void system_argv_array(char **args) | ||
32 | { | ||
33 | pid_t pid = fork(); | ||
34 | if (pid < 0) | ||
35 | err(1, "fork"); | ||
36 | |||
37 | if (pid == 0) { | ||
38 | execvp(args[0], args); | ||
39 | warn("execvp"); | ||
40 | _exit(127); | ||
41 | } | ||
42 | |||
43 | int status; | ||
44 | if (waitpid(pid, &status, 0) != pid) | ||
45 | err(1, "waitpid"); | ||
46 | if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) | ||
47 | errx(1, "subcommand failed"); | ||
48 | } | ||
49 | |||
31 | void system_argv(const char *cmd, ...) | 50 | void system_argv(const char *cmd, ...) |
32 | { | 51 | { |
33 | int nargs, nchars; | 52 | int nargs, nchars; |
@@ -54,21 +73,7 @@ void system_argv(const char *cmd, ...) | |||
54 | va_end(ap); | 73 | va_end(ap); |
55 | *argp++ = NULL; | 74 | *argp++ = NULL; |
56 | 75 | ||
57 | pid_t pid = fork(); | 76 | system_argv_array(args); |
58 | if (pid < 0) | ||
59 | err(1, "fork"); | ||
60 | |||
61 | if (pid == 0) { | ||
62 | execvp(args[0], args); | ||
63 | warn("execvp"); | ||
64 | _exit(127); | ||
65 | } | ||
66 | |||
67 | int status; | ||
68 | if (waitpid(pid, &status, 0) != pid) | ||
69 | err(1, "waitpid"); | ||
70 | if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) | ||
71 | errx(1, "subcommand failed"); | ||
72 | } | 77 | } |
73 | 78 | ||
74 | void c16cpy(char16_t *out, uint32_t *outsize, char *s) | 79 | void c16cpy(char16_t *out, uint32_t *outsize, char *s) |
@@ -91,6 +96,14 @@ void *smalloc(size_t size) | |||
91 | return toret; | 96 | return toret; |
92 | } | 97 | } |
93 | 98 | ||
99 | void *srealloc(void *ptr, size_t size) | ||
100 | { | ||
101 | void *toret = realloc(ptr, size); | ||
102 | if (!toret) | ||
103 | errx(1, "out of memory"); | ||
104 | return toret; | ||
105 | } | ||
106 | |||
94 | char *dupcat(const char *str, ...) | 107 | char *dupcat(const char *str, ...) |
95 | { | 108 | { |
96 | va_list ap; | 109 | va_list ap; |
@@ -1,9 +1,12 @@ | |||
1 | char *ascii(const char16_t *wstr, bool translate_slashes); | 1 | char *ascii(const char16_t *wstr, bool translate_slashes); |
2 | void system_argv(const char *cmd, ...); | 2 | void system_argv(const char *cmd, ...); |
3 | void system_argv_array(char **args); | ||
3 | void c16cpy(char16_t *out, uint32_t *outsize, char *s); | 4 | void c16cpy(char16_t *out, uint32_t *outsize, char *s); |
4 | void *smalloc(size_t size); | 5 | void *smalloc(size_t size); |
6 | void *srealloc(void *ptr, size_t size); | ||
5 | char *dupcat(const char *str, ...); | 7 | char *dupcat(const char *str, ...); |
6 | 8 | ||
7 | #define snew(type) ((type *)smalloc(sizeof(type))) | 9 | #define snew(type) ((type *)smalloc(sizeof(type))) |
8 | #define snewn(n,type) ((type *)smalloc((n)*sizeof(type))) | 10 | #define snewn(n,type) ((type *)smalloc((n)*sizeof(type))) |
11 | #define sresize(ptr,n,type) ((type *)srealloc(ptr,(n)*sizeof(type))) | ||
9 | #define sfree(ptr) free(ptr) | 12 | #define sfree(ptr) free(ptr) |
@@ -1,3 +1,4 @@ | |||
1 | #include <assert.h> | ||
1 | #include <stdio.h> | 2 | #include <stdio.h> |
2 | #include <stdlib.h> | 3 | #include <stdlib.h> |
3 | #include <stdint.h> | 4 | #include <stdint.h> |
@@ -25,74 +26,199 @@ uint32_t MsiGetFileVersionW(const char16_t *filename, | |||
25 | return 0; | 26 | return 0; |
26 | } | 27 | } |
27 | 28 | ||
29 | typedef struct MsiTypePrefix { | ||
30 | enum { MAIN, VIEW, RECORD } type; | ||
31 | } MsiTypePrefix; | ||
32 | |||
33 | typedef struct MsiMainCtx { | ||
34 | MsiTypePrefix t; | ||
35 | |||
36 | char *tempdir; | ||
37 | char *outfile; | ||
38 | |||
39 | char **args; | ||
40 | int nargs, argsize; | ||
41 | } MsiMainCtx; | ||
42 | |||
43 | |||
28 | uint32_t MsiOpenDatabaseW(const char16_t *filename, | 44 | uint32_t MsiOpenDatabaseW(const char16_t *filename, |
29 | const char16_t *persist, | 45 | const char16_t *persist, |
30 | void **handle) | 46 | MsiMainCtx **out_ctx) |
31 | { | 47 | { |
32 | char *file = ascii(filename, true); | 48 | MsiMainCtx *ctx = snew(MsiMainCtx); |
33 | warnx("FIXME: MsiOpenDatabaseW(%s,%p)", file, persist); | 49 | ctx->t.type = MAIN; |
34 | close(open(file, O_WRONLY | O_CREAT, 0666)); | 50 | ctx->outfile = ascii(filename, true); |
35 | *handle = (void *)1; | 51 | close(open(ctx->outfile, O_CREAT | O_WRONLY, 0666)); |
52 | ctx->outfile = realpath(ctx->outfile, NULL); | ||
53 | unlink(ctx->outfile); | ||
54 | ctx->tempdir = dupcat(ctx->outfile, "-msiXXXXXX", (const char *)NULL); | ||
55 | if (!mkdtemp(ctx->tempdir)) | ||
56 | err(1, "%s: mkdtemp", ctx->tempdir); | ||
57 | ctx->nargs = 0; | ||
58 | ctx->argsize = 16; | ||
59 | ctx->args = snewn(ctx->argsize, char *); | ||
60 | ctx->args[ctx->nargs++] = dupcat("sh", (const char *)NULL); | ||
61 | ctx->args[ctx->nargs++] = dupcat("-c", (const char *)NULL); | ||
62 | ctx->args[ctx->nargs++] = dupcat("cd \"$0\" && \"$@\"", | ||
63 | (const char *)NULL); | ||
64 | ctx->args[ctx->nargs++] = dupcat(ctx->tempdir, (const char *)NULL); | ||
65 | ctx->args[ctx->nargs++] = dupcat("msibuild", (const char *)NULL); | ||
66 | ctx->args[ctx->nargs++] = dupcat(ctx->outfile, (const char *)NULL); | ||
67 | *out_ctx = ctx; | ||
36 | return 0; | 68 | return 0; |
37 | } | 69 | } |
38 | 70 | ||
39 | uint32_t MsiCloseHandle(void *handle) | 71 | uint32_t MsiDatabaseImportW(MsiMainCtx *ctx, const char16_t *folder, |
72 | const char16_t *file) | ||
40 | { | 73 | { |
41 | warnx("FIXME: MsiCloseHandle(%p)", handle); | 74 | assert(ctx->t.type == MAIN); |
75 | system_argv("sh", "-c", "cd \"$0\" && cp \"$1\" \"$2\"", | ||
76 | ascii(folder, true), ascii(file, true), ctx->tempdir, | ||
77 | (const char *)NULL); | ||
78 | if (ctx->nargs + 2 >= ctx->argsize) { | ||
79 | ctx->argsize = ctx->nargs * 5 / 4 + 16; | ||
80 | ctx->args = sresize(ctx->args, ctx->argsize, char *); | ||
81 | } | ||
82 | ctx->args[ctx->nargs++] = dupcat("-i", (const char *)NULL); | ||
83 | ctx->args[ctx->nargs++] = dupcat(ctx->tempdir, "/", ascii(file, true), | ||
84 | (const char *)NULL); | ||
42 | return 0; | 85 | return 0; |
43 | } | 86 | } |
44 | 87 | ||
45 | uint32_t MsiDatabaseImportW(void *handle, const char16_t *folder, | 88 | typedef struct MsiView { |
46 | const char16_t *file) | 89 | MsiTypePrefix t; |
90 | |||
91 | FILE *fp; | ||
92 | char *targetdir; | ||
93 | MsiMainCtx *ctx; | ||
94 | } MsiView; | ||
95 | |||
96 | uint32_t MsiDatabaseOpenViewW(MsiMainCtx *ctx, const char16_t *query, | ||
97 | MsiView **outview) | ||
47 | { | 98 | { |
48 | warnx("FIXME: MsiDatabaseImport(%p,%s,%s)", handle, ascii(folder, true), | 99 | assert(ctx->t.type == MAIN); |
49 | ascii(file, true)); | 100 | MsiView *view = snew(MsiView); |
101 | view->t.type = VIEW; | ||
102 | view->ctx = ctx; | ||
103 | char *cquery = ascii(query, false); | ||
104 | if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `_Streams`")) | ||
105 | view->fp = NULL; /* special case */ | ||
106 | else { | ||
107 | if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `Binary`")) { | ||
108 | view->fp = fopen(dupcat(ctx->tempdir, "/", "Binary.idt", | ||
109 | (const char *)NULL), "a"); | ||
110 | view->targetdir = dupcat(ctx->tempdir, "/", "Binary", | ||
111 | (const char *)NULL); | ||
112 | } else if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `Icon`")) { | ||
113 | view->fp = fopen(dupcat(ctx->tempdir, "/", "Icon.idt", | ||
114 | (const char *)NULL), "a"); | ||
115 | view->targetdir = dupcat(ctx->tempdir, "/", "Icon", | ||
116 | (const char *)NULL); | ||
117 | } else | ||
118 | errx(1, "unrecognised query: %s", cquery); | ||
119 | if (!view->fp) | ||
120 | err(1, "open"); | ||
121 | if (mkdir(view->targetdir, 0777) < 0) | ||
122 | err(1, "%s: mkdir", view->targetdir); | ||
123 | } | ||
124 | *outview = view; | ||
50 | return 0; | 125 | return 0; |
51 | } | 126 | } |
52 | 127 | ||
53 | uint32_t MsiDatabaseOpenViewW(void *handle, const char16_t *query, | 128 | uint32_t MsiViewExecute(MsiView *view, void *params) |
54 | void **outhandle) | ||
55 | { | 129 | { |
56 | warnx("FIXME: MsiDatabaseOpenView(%p,%s)", handle, ascii(query, false)); | 130 | assert(view->t.type == VIEW); |
57 | *outhandle = (void *)2; | ||
58 | return 0; | 131 | return 0; |
59 | } | 132 | } |
60 | 133 | ||
61 | uint32_t MsiViewExecute(void *handle, void *params) | 134 | typedef struct MsiRecord { |
135 | MsiTypePrefix t; | ||
136 | |||
137 | char *name, *data; | ||
138 | } MsiRecord; | ||
139 | |||
140 | MsiRecord *MsiCreateRecord(uint32_t nparams) | ||
62 | { | 141 | { |
63 | warnx("FIXME: MsiViewExecute(%p)", handle); | 142 | MsiRecord *rec = snew(MsiRecord); |
64 | return 0; | 143 | rec->t.type = RECORD; |
144 | |||
145 | if (nparams != 2) | ||
146 | errx(1, "bad MsiCreateRecord param count %u", (unsigned)nparams); | ||
147 | rec->name = rec->data = NULL; | ||
148 | return rec; | ||
65 | } | 149 | } |
66 | 150 | ||
67 | void *MsiCreateRecord(uint32_t nparams) | 151 | uint32_t MsiRecordSetStringW(MsiRecord *rec, uint32_t field, char16_t *value) |
68 | { | 152 | { |
69 | warnx("FIXME: MsiCreateRecord(%u)", (unsigned)nparams); | 153 | assert(rec->t.type == RECORD); |
70 | return (void *)3; | 154 | if (field != 1) |
155 | errx(1, "bad MsiRecordSetString param index %u", (unsigned)field); | ||
156 | rec->name = ascii(value, false); | ||
157 | return 0; | ||
71 | } | 158 | } |
72 | 159 | ||
73 | uint32_t MsiRecordSetStringW(void *handle, uint32_t field, char16_t *value) | 160 | uint32_t MsiRecordSetStreamW(MsiRecord *rec, uint32_t field, char16_t *path) |
74 | { | 161 | { |
75 | warnx("FIXME: MsiRecordSetString(%p,%u,%s)", handle, (unsigned)field, | 162 | assert(rec->t.type == RECORD); |
76 | ascii(value, false)); | 163 | if (field != 2) |
164 | errx(1, "bad MsiRecordSetStream param index %u", (unsigned)field); | ||
165 | rec->data = ascii(path, true); | ||
77 | return 0; | 166 | return 0; |
78 | } | 167 | } |
79 | 168 | ||
80 | uint32_t MsiRecordSetStreamW(void *handle, uint32_t field, char16_t *path) | 169 | uint32_t MsiViewModify(MsiView *view, uint32_t mode, MsiRecord *rec) |
81 | { | 170 | { |
82 | warnx("FIXME: MsiRecordSetStream(%p,%u,%s)", handle, (unsigned)field, | 171 | assert(view->t.type == VIEW); |
83 | ascii(path, true)); | 172 | assert(rec->t.type == RECORD); |
173 | if (view->fp) { | ||
174 | system_argv("sh", "-c", "cp \"$0\" \"$1\"/\"$2\"", | ||
175 | rec->data, view->targetdir, rec->name, | ||
176 | (const char *)NULL); | ||
177 | fprintf(view->fp, "%s\t%s\r\n", rec->name, rec->name); | ||
178 | } else { | ||
179 | MsiMainCtx *ctx = view->ctx; | ||
180 | if (ctx->nargs + 3 >= ctx->argsize) { | ||
181 | ctx->argsize = ctx->nargs * 5 / 4 + 16; | ||
182 | ctx->args = sresize(ctx->args, ctx->argsize, char *); | ||
183 | } | ||
184 | ctx->args[ctx->nargs++] = dupcat("-s", (const char *)NULL); | ||
185 | ctx->args[ctx->nargs++] = dupcat(rec->name, (const char *)NULL); | ||
186 | ctx->args[ctx->nargs++] = dupcat(rec->data, (const char *)NULL); | ||
187 | } | ||
84 | return 0; | 188 | return 0; |
85 | } | 189 | } |
86 | 190 | ||
87 | uint32_t MsiViewModify(void *vhandle, uint32_t mode, void *rechandle) | 191 | uint32_t MsiCloseHandle(MsiTypePrefix *t) |
88 | { | 192 | { |
89 | warnx("FIXME: MsiViewModify(%p,%u,%p)", | 193 | if (t->type == VIEW) { |
90 | vhandle, (unsigned)mode, rechandle); | 194 | MsiView *view = (MsiView *)t; |
195 | if (view->fp) | ||
196 | fclose(view->fp); | ||
197 | } | ||
91 | return 0; | 198 | return 0; |
92 | } | 199 | } |
93 | 200 | ||
94 | uint32_t MsiDatabaseCommit(void *handle) | 201 | uint32_t MsiDatabaseCommit(MsiMainCtx *ctx) |
95 | { | 202 | { |
96 | warnx("FIXME: MsiDatabaseCommit(%p)", handle); | 203 | assert(ctx->t.type == MAIN); |
204 | printf("commit:"); | ||
205 | for (int i = 0; i < ctx->nargs; i++) { | ||
206 | printf(" '"); | ||
207 | for (const char *p = ctx->args[i]; *p; p++) { | ||
208 | if (*p == '\'') | ||
209 | printf("'\\''"); | ||
210 | else | ||
211 | putchar(*p); | ||
212 | } | ||
213 | printf("'"); | ||
214 | } | ||
215 | printf("\n"); | ||
216 | |||
217 | if (ctx->nargs + 1 >= ctx->argsize) { | ||
218 | ctx->argsize = ctx->nargs * 5 / 4 + 16; | ||
219 | ctx->args = sresize(ctx->args, ctx->argsize, char *); | ||
220 | } | ||
221 | ctx->args[ctx->nargs++] = NULL; | ||
222 | system_argv_array(ctx->args); | ||
97 | return 0; | 223 | return 0; |
98 | } | 224 | } |
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, | |||
40 | ctx->outdir = ascii(wzCabDir, true); | 40 | ctx->outdir = ascii(wzCabDir, true); |
41 | ctx->outfile = dupcat(ctx->outdir, "/", | 41 | ctx->outfile = dupcat(ctx->outdir, "/", |
42 | ascii(wzCab, true), (const char *)NULL); | 42 | ascii(wzCab, true), (const char *)NULL); |
43 | ctx->tempdir = snewn(20 + strlen(ctx->outdir), char); | 43 | ctx->tempdir = dupcat(ctx->outdir, "/cabXXXXXX", (const char *)NULL); |
44 | sprintf(ctx->tempdir, "%s/cabXXXXXX", ctx->outdir); | ||
45 | if (!mkdtemp(ctx->tempdir)) | 44 | if (!mkdtemp(ctx->tempdir)) |
46 | err(1, "mkdtemp"); | 45 | err(1, "mkdtemp"); |
47 | *out_ctx = ctx; | 46 | *out_ctx = ctx; |