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