wip, most of my SD static bins work
[ginge.git] / prep / main.c
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
21 static void *fb_mem;
22 static int fb_stride;
23 static int fb_x, fb_y;
24
25 static char *sskip(char *p)
26 {
27   while (p && *p && isspace(*p))
28     p++;
29   return p;
30 }
31
32 static char *cskip(char *p)
33 {
34   while (p && *p && !isspace(*p))
35     p++;
36   return p;
37 }
38
39 static 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
69 static 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
101 static 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
110 static 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
126 static 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
166 out:
167   fclose(fi);
168   free(phdr);
169   return ret;
170 }
171
172 static 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
192 static 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 }
214 const 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
224 static 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
236 int 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
401 pass:
402     fputs(p, fout);
403   }
404
405   fclose(fin);
406
407 no_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