1b79e139e31ecfa4a895fd6494e0a5031796fa7c
[framework/uifw/edje.git] / src / bin / edje_decc.c
1 /* ugly ugly. avert your eyes. */
2
3 #ifdef HAVE_CONFIG_H
4 # include <config.h>
5 #endif
6
7 #include <string.h>
8 #include <ctype.h>
9 #include <unistd.h>
10 #include <locale.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <errno.h>
14
15
16 #include <Ecore_File.h>
17 #include <Ecore_Evas.h>
18
19 #include "edje_decc.h"
20
21 int _edje_cc_log_dom = -1;
22 char *progname = NULL;
23 char *file_in = NULL;
24 char *file_out = NULL;
25
26 Edje_File *edje_file = NULL;
27 SrcFile_List *srcfiles = NULL;
28 Font_List *fontlist = NULL;
29
30 int line = 0;
31 int build_sh = 1;
32 int new_dir = 1;
33
34 int        decomp(void);
35 void       output(void);
36 static int compiler_cmd_is_sane();
37 static int root_filename_is_sane();
38
39 static void
40 main_help(void)
41 {
42    printf
43      ("Usage:\n"
44       "\t%s input_file.edj [-main-out file.edc] [-no-build-sh] [-current-dir]\n"
45       "\n"
46       " -main-out\tCreate a symbolic link to the main edc \n"
47       " -no-build-sh\tDon't output build.sh \n"
48       " -current-dir\tOutput to current directory \n"
49       "\n"
50       ,progname);
51 }
52
53 Eet_File *ef;
54 Eet_Dictionary *ed;
55
56 int
57 main(int argc, char **argv)
58 {
59    int i;
60
61    setlocale(LC_NUMERIC, "C");
62    if (!eina_init())
63      exit(-1);
64    _edje_cc_log_dom = eina_log_domain_register
65      ("edje_decc", EDJE_CC_DEFAULT_LOG_COLOR);
66    if (_edje_cc_log_dom < 0)
67      {
68        EINA_LOG_ERR("Impossible to create a log domain.");
69        eina_shutdown();
70        exit(-1);
71      }
72    eina_log_level_set(EINA_LOG_LEVEL_INFO);
73    progname = argv[0];
74    for (i = 1; i < argc; i++)
75      {
76         if (!strcmp(argv[i], "-h"))
77           {
78              main_help();
79              exit(0);
80           }
81         if (!file_in)
82           file_in = argv[i];
83         else if ((!strcmp(argv[i], "-main-out")) && (i < (argc - 1)))
84           {
85              i++;
86              file_out = argv[i];
87           }
88         else if (!strcmp(argv[i], "-no-build-sh"))
89           build_sh = 0;
90         else if (!strcmp(argv[i], "-current-dir"))
91           new_dir = 0;
92      }
93    if (!file_in)
94      {
95         ERR("%s: Error: no input file specified.", progname);
96         main_help();
97         exit(-1);
98      }
99
100    if (!edje_init())
101      exit(-1);
102    source_edd();
103
104    if (!decomp()) return -1;
105    output();
106
107    printf("WARNING! If any Image or audio data was encoded in a LOSSY way, then\n"
108           "re-encoding will drop quality even more. You need access to the original\n"
109           "data to ensure no loss of quality.\n");
110    eet_close(ef);
111    edje_shutdown();
112    eina_log_domain_unregister(_edje_cc_log_dom);
113    _edje_cc_log_dom = -1;
114    eina_shutdown();
115    return 0;
116 }
117
118 int
119 decomp(void)
120 {
121    ef = eet_open(file_in, EET_FILE_MODE_READ);
122    if (!ef)
123      {
124         ERR("ERROR: cannot open %s", file_in);
125         return 0;
126      }
127
128    srcfiles = source_load(ef);
129    if (!srcfiles || !srcfiles->list)
130      {
131         ERR("ERROR: %s has no decompile information", file_in);
132         eet_close(ef);
133         return 0;
134      }
135    if (!eina_list_data_get(srcfiles->list) || !root_filename_is_sane())
136      {
137         ERR("ERROR: Invalid root filename: '%s'", (char *) eina_list_data_get(srcfiles->list));
138         eet_close(ef);
139         return 0;
140      }
141    edje_file = eet_data_read(ef, _edje_edd_edje_file, "edje/file");
142    if (!edje_file)
143      {
144         ERR("ERROR: %s does not appear to be an edje file", file_in);
145         eet_close(ef);
146         return 0;
147      }
148    /* force compiler to be edje_cc */
149    edje_file->compiler = strdup("edje_cc");
150    if (!edje_file->compiler)
151      {
152         edje_file->compiler = strdup("edje_cc");
153      }
154    else if (!compiler_cmd_is_sane())
155      {
156         ERR("ERROR: invalid compiler executable: '%s'", edje_file->compiler);
157         eet_close(ef);
158         return 0;
159      }
160    fontlist = source_fontmap_load(ef);
161    return 1;
162 }
163
164 void
165 output(void)
166 {
167    Eina_List *l;
168    Eet_File *tef;
169    SrcFile *sf;
170    char *outdir, *p;
171
172    if (!new_dir)
173      outdir = strdup(".");
174    else
175      {
176         p = strrchr(file_in, '/');
177         if (p)
178           outdir = strdup(p + 1);
179         else
180           outdir = strdup(file_in);
181         p = strrchr(outdir, '.');
182         if (p) *p = 0;
183         ecore_file_mkpath(outdir);
184      }
185
186
187    tef = eet_open(file_in, EET_FILE_MODE_READ);
188
189    if (edje_file->image_dir)
190      {
191         Edje_Image_Directory_Entry *ei;
192         unsigned int i;
193
194         for (i = 0; i < edje_file->image_dir->entries_count; ++i)
195           {
196              ei = &edje_file->image_dir->entries[i];
197
198              if ((ei->source_type > EDJE_IMAGE_SOURCE_TYPE_NONE) &&
199                  (ei->source_type < EDJE_IMAGE_SOURCE_TYPE_LAST) &&
200                  (ei->source_type != EDJE_IMAGE_SOURCE_TYPE_EXTERNAL) &&
201                  (ei->entry))
202                {
203                   Ecore_Evas *ee;
204                   Evas *evas;
205                   Evas_Object *im;
206                   char buf[4096];
207                   char out[4096];
208                   char *pp;
209
210                   ecore_init();
211                   ecore_evas_init();
212                   ee = ecore_evas_buffer_new(1, 1);
213                   if (!ee)
214                     {
215                        ERR("Cannot create buffer engine canvas for image save.");
216                        exit(-1);
217                     }
218                   evas = ecore_evas_get(ee);
219                   im = evas_object_image_add(evas);
220                   if (!im)
221                     {
222                        ERR("Cannot create image object for save.");
223                        exit(-1);
224                     }
225                   snprintf(buf, sizeof(buf), "edje/images/%i", ei->id);
226                   evas_object_image_file_set(im, file_in, buf);
227                   snprintf(out, sizeof(out), "%s/%s", outdir, ei->entry);
228                   printf("Output Image: %s\n", out);
229                   pp = strdup(out);
230                   p = strrchr(pp, '/');
231                   *p = 0;
232                   if (strstr(pp, "../"))
233                     {
234                        ERR("Potential security violation. attempt to write in parent dir.");
235                        exit(-1);
236                     }
237                   ecore_file_mkpath(pp);
238                   free(pp);
239                   if (!evas_object_image_save(im, out, NULL, "quality=100 compress=9"))
240                     {
241                        ERR("Cannot write file %s. Perhaps missing JPEG or PNG saver modules for Evas.", out);
242                        exit(-1);
243                     }
244                   evas_object_del(im);
245                   ecore_evas_free(ee);
246                   ecore_evas_shutdown();
247                   ecore_shutdown();
248                }
249           }
250      }
251
252    EINA_LIST_FOREACH(srcfiles->list, l, sf)
253      {
254         char out[4096];
255         FILE *f;
256         char *pp;
257
258         snprintf(out, sizeof(out), "%s/%s", outdir, sf->name);
259         INF("Output Source File: %s\n", out);
260         pp = strdup(out);
261         p = strrchr(pp, '/');
262         *p = 0;
263         if (strstr(pp, "../"))
264           {
265              ERR("Potential security violation. attempt to write in parent dir.");
266              exit (-1);
267           }
268         ecore_file_mkpath(pp);
269         free(pp);
270         if (strstr(out, "../"))
271           {
272              ERR("Potential security violation. attempt to write in parent dir.");
273              exit (-1);
274           }
275         f = fopen(out, "wb");
276         if (!f)
277           {
278              ERR("Unable to write file (%s).", out);
279              exit (-1);
280           }
281
282         /* if the file is empty, sf->file will be NULL.
283          * note that that's not an error
284          */
285         if (sf->file) fputs(sf->file, f);
286         fclose(f);
287      }
288    if (edje_file->fonts)
289      {
290         Edje_Font_Directory_Entry *fn;
291         Eina_Iterator *it;
292
293         it = eina_hash_iterator_data_new(edje_file->fonts);
294         EINA_ITERATOR_FOREACH(it, fn)
295           {
296              void *font;
297              int fontsize;
298              char out[4096];
299              /* FIXME!!!! */
300                                          /* should be fn->entry -v */
301              snprintf(out, sizeof(out), "edje/fonts/%s", fn->file);
302              font = eet_read(tef, out, &fontsize);
303              if (font)
304                {
305                   FILE *f;
306                   char *pp;
307
308                                          /* should be fn->file -v */
309                   snprintf(out, sizeof(out), "%s/%s", outdir, fn->entry);
310                   INF("Output Font: %s", out);
311                   pp = strdup(out);
312                   p = strrchr(pp, '/');
313                   *p = 0;
314                   if (strstr(pp, "../"))
315                     {
316                        ERR("Potential security violation. attempt to write in parent dir.");
317                        exit (-1);
318                     }
319                   ecore_file_mkpath(pp);
320                   free(pp);
321                   if (strstr(out, "../"))
322                     {
323                        ERR("Potential security violation. attempt to write in parent dir.");
324                        exit (-1);
325                     }
326                   if (!(f = fopen(out, "wb")))
327                     {
328                        ERR("Could not open file: %s", out);
329                        exit (-1);
330                     }
331                   if (fwrite(font, fontsize, 1, f) != 1)
332                     ERR("Could not write font: %s", strerror(errno));
333                   if (f) fclose(f);
334                   free(font);
335                }
336           }
337         eina_iterator_free(it);
338      }
339      {
340         char out[4096];
341         FILE *f;
342         sf = eina_list_data_get(srcfiles->list);
343
344
345         if (build_sh)
346           {
347              snprintf(out, sizeof(out), "%s/build.sh", outdir);
348              printf("Output Build Script: %s\n", out);
349              if (strstr(out, "../"))
350                {
351                   ERR("potential security violation. attempt to write in parent dir.\n");
352                   exit (-1);
353                }
354              f = fopen(out, "wb");
355              fprintf(f, "#!/bin/sh\n");
356              fprintf(f, "%s $@ -id . -fd . %s -o %s.edj\n", edje_file->compiler, sf->name, outdir);
357              fclose(f);
358
359              WRN("\n*** CAUTION ***\n"
360                  "Please check the build script for anything malicious "
361                  "before running it!\n\n");
362           }
363
364         if (file_out)
365           {
366              snprintf(out, sizeof(out), "%s/%s", outdir, file_out);
367              if (ecore_file_symlink(sf->name, out) != EINA_TRUE)
368                {
369                   ERR("symlink %s -> %s failed\n", sf->name, out);
370                }
371           }
372
373         chmod(out, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP);
374
375      }
376
377    if (edje_file->sound_dir)
378      {
379         Edje_Sound_Sample *sample;
380         void *sound_data;
381         char out[PATH_MAX];
382         char out1[PATH_MAX];
383         char *pp;
384         int sound_data_size;
385         FILE *f;
386         int i;
387
388         for (i = 0; i < (int)edje_file->sound_dir->samples_count; i++)
389           {
390              sample = &edje_file->sound_dir->samples[i];
391              if ((!sample) || (!sample->name)) continue;
392              snprintf(out, sizeof(out), "edje/sounds/%i", sample->id);
393              sound_data = (void *)eet_read_direct(tef, out, &sound_data_size);
394              if (sound_data)
395                {
396                   snprintf(out1, sizeof(out1), "%s/%s", outdir, sample->name);
397                   pp = strdup(out1);
398                   p = strrchr(pp, '/');
399                   *p = 0;
400                   if (strstr(pp, "../"))
401                     {
402                        ERR("Potential security violation. attempt to write in parent dir.");
403                        exit(-1);
404                     }
405                   ecore_file_mkpath(pp);
406                   free(pp);
407                   if (strstr(out, "../"))
408                     {
409                        ERR("Potential security violation. attempt to write in parent dir.");
410                        exit(-1);
411                     }
412                   f = fopen(out1, "wb");
413                   if (fwrite(sound_data, sound_data_size, 1, f) != 1)
414                     ERR("Could not write sound: %s", strerror(errno));
415                   fclose(f);
416                   free(sound_data);
417               }
418           }
419
420      }
421    eet_close(tef);
422 }
423
424 static int
425 compiler_cmd_is_sane()
426 {
427    const char *c = edje_file->compiler, *ptr;
428
429    if ((!c) || (!*c))
430      {
431         return 0;
432      }
433
434    for (ptr = c; ptr && *ptr; ptr++)
435      {
436         /* only allow [a-z][A-Z][0-9]_- */
437         if ((!isalnum(*ptr)) && (*ptr != '_') && (*ptr != '-'))
438           {
439              return 0;
440           }
441      }
442
443    return 1;
444 }
445
446 static int
447 root_filename_is_sane()
448 {
449    SrcFile *sf = eina_list_data_get(srcfiles->list);
450    char *f = sf->name, *ptr;
451
452    if (!f || !*f)
453      {
454         return 0;
455      }
456
457    for (ptr = f; ptr && *ptr; ptr++)
458      {
459         /* only allow [a-z][A-Z][0-9]_-./ */
460         switch (*ptr)
461           {
462            case '_': case '-':  case '.': case '/':
463               break;
464            default:
465               if (!isalnum(*ptr))
466                 {
467                    return 0;
468                 }
469           }
470      }
471    return 1;
472 }