add prep tool and common
[ginge.git] / prep / main.c
CommitLineData
3adc9ccb 1// vim:shiftwidth=2:expandtab
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <stdarg.h>
6#include <unistd.h>
7#include <elf.h>
8#include <sys/stat.h>
9#include <ctype.h>
10
11#include "../common/host_fb.h"
12#include "../common/cmn.h"
13
14#define PFX "ging_prep: "
15#define LOADER_STATIC "ginge_sloader"
16#define LOADER_DYNAMIC "ginge_dyn.sh"
17#define LAUNCHER "gp2xmenu"
18
19#include "font.c"
20
21static void *fb_mem;
22static int fb_stride;
23static int fb_x, fb_y;
24
25static char *sskip(char *p)
26{
27 while (p && *p && isspace(*p))
28 p++;
29 return p;
30}
31
32static char *cskip(char *p)
33{
34 while (p && *p && !isspace(*p))
35 p++;
36 return p;
37}
38
39static void fb_syms_out(void *fbi, int x, int y, int dotsz, int stride, const char *text, int count)
40{
41 int v = -1, n = 0, *p;
42 int i, l;
43 char *fb;
44
45 fb = (char *)fbi + x * dotsz + y * stride;
46
47 for (i = 0; i < count; i++)
48 {
49 for (l = 0; l < 8; l++)
50 {
51 #define pix(fdmask,add) \
52 p = (fontdata8x8[((text[i])*8)+l] & fdmask) ? &v : &n; \
53 memcpy(fb + l*stride + add*dotsz, p, dotsz)
54 pix(0x80, 0);
55 pix(0x40, 1);
56 pix(0x20, 2);
57 pix(0x10, 3);
58 pix(0x08, 4);
59 pix(0x04, 5);
60 pix(0x02, 6);
61 pix(0x01, 7);
62 #undef pix
63 }
64 fb += dotsz * 8;
65 }
66}
67
68// FIXME: y overrun
69static void fb_text_out(char *text)
70{
71 int dotsz = 2, w = 320; // hardcoded for now
72 char *p, *pe;
73 int l;
74
75 if (fb_mem == NULL)
76 return;
77
78 p = text;
79 while (*p) {
80 for (; *p && isspace(*p); p++) {
81 if (*p == '\n' || fb_x + dotsz * 8 > w) {
82 fb_x = 4;
83 fb_y += 8;
84 }
85 if (*p >= 0x20)
86 fb_x += 8;
87 }
88
89 pe = cskip(p);
90 l = pe - p;
91 if (fb_x + 8 * l > w) {
92 fb_x = 4;
93 fb_y += 8;
94 }
95 fb_syms_out(fb_mem, fb_x, fb_y, dotsz, fb_stride, p, l);
96 fb_x += 8 * l;
97 p = pe;
98 }
99}
100
101static void fb_text_init(void)
102{
103 int ret = host_video_init(&fb_stride, 1);
104 if (ret == 0)
105 fb_mem = host_video_flip();
106 fb_x = 4;
107 fb_y = 4;
108}
109
110static void fbprintf(int is_err, const char *format, ...)
111{
112 va_list ap;
113 char buff[512];
114
115 va_start(ap, format);
116 vsnprintf(buff, sizeof(buff), format, ap);
117 va_end(ap);
118 fputs(buff, is_err ? stderr : stdout);
119
120 fb_text_out(buff);
121}
122
123#define msg(fmt, ...) fbprintf(0, fmt, #__VA_ARGS__)
124#define err(fmt, ...) fbprintf(1, fmt, #__VA_ARGS__)
125
126static int id_elf(const char *fname)
127{
128 Elf32_Ehdr hdr;
129 Elf32_Phdr *phdr = NULL;
130 FILE *fi;
131 int i, ret = 0;
132
133 fi = fopen(fname, "rb");
134 if (fi == NULL) {
135 err("open %s: ", fname);
136 perror("");
137 return -1;
138 }
139
140 if (fread(&hdr, 1, sizeof(hdr), fi) != sizeof(hdr))
141 goto out;
142
143 if (memcmp(hdr.e_ident, ELFMAG "\x01\x01", SELFMAG + 2) != 0)
144 goto out;
145
146 if (hdr.e_phentsize != sizeof(Elf32_Phdr) || hdr.e_phnum == 0)
147 goto out;
148
149 phdr = malloc(hdr.e_phnum * hdr.e_phentsize);
150 if (phdr == NULL)
151 goto out;
152
153 if (fread(phdr, hdr.e_phentsize, hdr.e_phnum, fi) != hdr.e_phnum)
154 goto out;
155
156 ret = 1;
157
158 // do what 'file' does - check for PT_INTERP in program headers
159 for (i = 0; i < hdr.e_phnum; i++) {
160 if (phdr[i].p_type == PT_INTERP) {
161 ret = 2;
162 break;
163 }
164 }
165
166out:
167 fclose(fi);
168 free(phdr);
169 return ret;
170}
171
172static void dump_args(FILE *fout, int argc, char * const argv[])
173{
174 const char *p;
175 int i;
176
177 for (i = 0; i < argc; i++) {
178 if (i != 0)
179 fputc(' ', fout);
180 fputc('"', fout);
181
182 for (p = argv[i]; *p != 0; p++) {
183 if (*p == '"')
184 fputc('\\', fout);
185 fputc(*p, fout);
186 }
187
188 fputc('"', fout);
189 }
190}
191
192static char *get_arg(char *d, size_t size, char *p)
193{
194 char *pe;
195 int len;
196
197 p = sskip(p);
198 pe = cskip(p);
199 len = pe - p;
200
201 if (len > size - 1) {
202 err(PFX "get_arg: buff to small: %d/%d\n", len, size);
203 len = size - 1;
204 }
205 strncpy(d, p, len);
206 d[len] = 0;
207
208 return sskip(pe);
209}
210
211#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
212
213#define CB_ENTRY(x) { x, sizeof(x) - 1 }
214const struct {
215 const char *name;
216 int len;
217} conv_blacklist[] = {
218 CB_ENTRY("insmod"),
219 CB_ENTRY("modprobe"),
220 CB_ENTRY("umount"),
221 CB_ENTRY("./cpuctrl_tiny"),
222};
223
224static int cmd_in_blacklist(char *cmd)
225{
226 int i;
227
228 cmd = sskip(cmd);
229 for (i = 0; i < ARRAY_SIZE(conv_blacklist); i++)
230 if (strncmp(cmd, conv_blacklist[i].name, conv_blacklist[i].len) == 0)
231 return 1;
232
233 return 0;
234}
235
236int main(int argc, char *argv[])
237{
238 static const char out_script[] = "/tmp/ginge_conv.sh";
239 char root_path[512], cwd[512];
240 int have_cramfs = 0;
241 int rerun_gp2xmenu = 1;
242 FILE *fin, *fout;
243 int ret;
244
245 fb_text_init();
246
247 if (argc < 2) {
248 err("usage: %s <script|program> [args]\n", argv[0]);
249 return 1;
250 }
251
252 if (getcwd(cwd, sizeof(cwd)) == NULL) {
253 err(PFX "failed to get cwd\n");
254 return 1;
255 }
256
257 ret = make_local_path(root_path, sizeof(root_path), "");
258 if (ret != 0) {
259 err(PFX "failed to generate root path\n");
260 return 1;
261 }
262
263 fout = fopen(out_script, "w");
264 if (fout == NULL) {
265 perror("can't open output script");
266 return 1;
267 }
268
269 fprintf(fout, "#!/bin/sh\n");
270
271 ret = id_elf(argv[1]);
272 if (ret == 1 || ret == 2) {
273 if (cmd_in_blacklist(argv[1])) {
274 fprintf(stderr, "blacklisted: %s\n", argv[1]);
275 goto no_in_script;
276 }
277 }
278
279 switch (ret) {
280 case 0:
281 break;
282
283 case 1:
284 fprintf(fout, "op_runfbapp %s%s ", root_path, LOADER_STATIC);
285 dump_args(fout, argc - 1, &argv[1]);
286 fprintf(fout, "\n");
287 goto no_in_script;
288
289 case 2:
290 fprintf(fout, "op_runfbapp %s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path);
291 dump_args(fout, argc - 1, &argv[1]);
292 fprintf(fout, "\n");
293 goto no_in_script;
294
295 default:
296 return 1;
297 }
298
299 // assume script
300 fin = fopen(argv[1], "r");
301 if (fin == NULL)
302 return 1;
303
304 while (1) {
305 char buff[512], fname[512], *p, *p2;
306
307 p = fgets(buff, sizeof(buff), fin);
308 if (p == NULL)
309 break;
310 p = sskip(p);
311
312 if (p[0] == '#' && p[1] == '!')
313 continue;
314
315 if (*p == 0) {
316 fputs("\n", fout);
317 continue;
318 }
319
320 // things we are sure we want to pass
321 if (*p == '#' || strncmp(p, "export ", 7) == 0)
322 goto pass;
323
324 // hmh..
325 if (strncmp(p, "exec ", 5) == 0)
326 p = sskip(p + 5);
327
328 // blacklist some stuff
329 if (strncmp(p, "/sbin/", 6) == 0)
330 p2 = p + 6;
331 else if (strncmp(p, "/bin/", 5) == 0)
332 p2 = p + 5;
333 else
334 p2 = p;
335 if (strncmp(p2, "mount ", 6) == 0) {
336 p2 = sskip(p2 + 6);
337 // cramfs stuff?
338 if (strstr(p2, "cramfs")) {
339 while (*p2 == '-') {
340 // skip option
341 p2 = sskip(cskip(p2));
342 p2 = sskip(cskip(p2));
343 }
344 if (*p2 == 0) {
345 err(PFX "cramfs: missing mount file in \"%s\"?\n", p);
346 continue;
347 }
348 p2 = get_arg(fname, sizeof(fname), p2);
349 if (*p2 == 0) {
350 err(PFX "cramfs: missing mount point in \"%s\"?\n", p);
351 continue;
352 }
353 get_arg(buff, sizeof(buff), p2);
354 fprintf(fout, "if [ `ls %s | wc -l` -eq 0 ]; then\n", buff);
355 fprintf(fout, " rmdir \"%s\"\n", buff); // cramfsck doesn't want it
356 fprintf(fout, " %stools/cramfsck -x \"%s\" \"%s\"\n", root_path, buff, fname);
357 fprintf(fout, "fi\n");
358 have_cramfs = 1;
359 }
360 continue;
361 }
362 if (cmd_in_blacklist(p2))
363 continue;
364
365 // cd?
366 if (strncmp(p, "cd ", 3) == 0) {
367 get_arg(fname, sizeof(fname), p + 3);
368 if (strncmp(fname, "/usr/gp2x", 9) == 0)
369 continue;
370 ret = chdir(fname);
371 if (ret != 0) {
372 err("%s: ", fname);
373 perror("");
374 }
375 }
376
377 // trying to run something from cwd?
378 if ((p[0] == '.' && p[1] == '/') || *p == '/') {
379 get_arg(fname, sizeof(fname), p);
380 p2 = strrchr(fname, '/');
381 if (p2 != NULL && strcmp(p2 + 1, "gp2xmenu") == 0)
382 continue;
383
384 ret = id_elf(fname);
385 switch (ret) {
386 case 1:
387 printf(PFX "prefixing as static: %s", p);
388 fprintf(fout, "op_runfbapp %s%s ", root_path, LOADER_STATIC);
389 break;
390
391 case 2:
392 printf(PFX "prefixing as dynamic: %s", p);
393 fprintf(fout, "op_runfbapp %s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path);
394 break;
395
396 default:
397 break;
398 }
399 }
400
401pass:
402 fputs(p, fout);
403 }
404
405 fclose(fin);
406
407no_in_script:
408 if (rerun_gp2xmenu) {
409 fprintf(fout, "cd %s\n", root_path);
410 fprintf(fout, "exec %s%s\n", root_path, LAUNCHER);
411 }
412
413 fclose(fout);
414
415 msg("starting script..\n");
416 if (have_cramfs)
417 msg("\nsome files need to be unpacked, this may tike a few minutes.\n"
418 "Please wait at least while SD LED is active.\n");
419 system("echo ---; cat /tmp/ginge_conv.sh; echo ---");
420 chmod(out_script, S_IRWXU|S_IRWXG|S_IRWXO);
421 chdir(cwd);
422 execlp(out_script, out_script, NULL);
423 perror("run out_script");
424
425 return 1;
426}
427