chiark / gitweb /
Imported Debian patch 1.0.0-5
[e16] / src / config.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "aclass.h"
26 #include "backgrounds.h"
27 #include "borders.h"
28 #include "buttons.h"
29 #include "conf.h"
30 #include "file.h"
31 #include "iclass.h"
32 #include "menus.h"
33 #include "progress.h"
34 #include "session.h"
35 #include "tclass.h"
36 #include "tooltips.h"
37 #include "user.h"
38 #include "windowmatch.h"
39
40 #define MAX_E_CFG_VERSION 2     /* Max. supported configuration version */
41
42 void
43 SkipTillEnd(FILE * fs)
44 {
45    char                s[FILEPATH_LEN_MAX];
46    int                 i1, i2, fields;
47
48    while (GetLine(s, sizeof(s), fs))
49      {
50         i1 = i2 = 0;
51         fields = sscanf(s, "%i %i", &i1, &i2);
52         if (i1 == CONFIG_CLOSE)
53            return;
54         if (i2 == CONFIG_OPEN)
55            SkipTillEnd(fs);
56      }
57 }
58
59 #define LINE_BUFFER_SIZE 1024
60 /*
61  * This function will get a single line from the file
62  * The string will be null terminated.
63  * Size must be >= 2.
64  */
65 char               *
66 GetLine(char *s, int size, FILE * f)
67 {
68    static char        *buffer = NULL;
69    static const char  *bufptr = NULL;
70    char               *so, ch, quote, escape;
71    const char         *si;
72    size_t              nr;
73
74    if (buffer == NULL)
75      {
76         buffer = EMALLOC(char, LINE_BUFFER_SIZE);
77
78         if (buffer == NULL)
79            return NULL;
80         buffer[LINE_BUFFER_SIZE - 1] = '\0';
81      }
82
83    si = bufptr;
84    so = s;
85    quote = '\0';
86    escape = '\0';
87    for (;;)
88      {
89         /* Get a line from the input file */
90         if (si == NULL)
91           {
92              nr = fread(buffer, 1, LINE_BUFFER_SIZE - 1, f);
93              if (nr == 0)
94                 break;
95              buffer[nr] = '\0';
96              si = buffer;
97           }
98
99         /* Split on ';' or '\n', handle quoting */
100         ch = *si++;
101         switch (ch)
102           {
103           case '\0':
104              si = NULL;
105              break;
106           case ';':             /* Line separator */
107              if (escape || quote)
108                 goto case_char;
109           case '\n':
110              if (so == s)       /* Skip empty lines */
111                 break;
112              *so = '\0';        /* Terminate and return */
113              goto done;
114           case '\r':            /* Ignore */
115              break;
116           case '\\':            /* Escape */
117              if (escape)
118                 goto case_char;
119              escape = ch;
120              break;
121           case '"':             /* Quoting */
122 /*             case '\'': */
123              if (escape)
124                 goto case_char;
125              if (quote == '\0')
126                 quote = ch;
127              else if (quote == ch)
128                 quote = '\0';
129              else
130                 goto case_char;
131              break;
132           case ' ':             /* Whitespace */
133           case '\t':
134              if (so == s)       /* Skip leading whitespace */
135                 break;
136            case_char:           /* Normal character */
137           default:
138              *so++ = ch;
139              escape = '\0';
140              if (--size > 1)
141                 break;
142              *so = '\0';
143              goto done;
144           }
145      }
146
147  done:
148    bufptr = si;
149    if (si == NULL)
150      {
151         /* EOF */
152         Efree(buffer);
153         buffer = NULL;
154         if (so == s)
155            return NULL;
156      }
157
158    /* Strip trailing whitespace */
159    si = so;
160    for (; so > s; so--)
161      {
162         ch = so[-1];
163         if (ch != ' ' && ch != '\t')
164            break;
165      }
166    if (so != si)
167       *so = '\0';
168
169    if (EDebug(EDBUG_TYPE_CONFIG) > 1)
170       Eprintf("GetLine %s\n", s);
171
172    return s;
173 }
174
175 int
176 ConfigParseline1(char *str, char *s2, char **p2, char **p3)
177 {
178    int                 i1, len1, len2, fields;
179
180    i1 = CONFIG_INVALID;
181    len1 = len2 = 0;
182    s2[0] = '\0';
183    fields = sscanf(str, "%i %n%4000s %n", &i1, &len1, s2, &len2);
184    if (p2)
185       *p2 = (len1) ? str + len1 : NULL;
186    if (p3)
187       *p3 = (len2) ? str + len2 : NULL;
188
189    if (fields <= 0)
190      {
191         i1 = CONFIG_INVALID;
192      }
193    else if (i1 == CONFIG_CLOSE || i1 == CONFIG_NEXT)
194      {
195         if (fields != 1)
196           {
197              Alert(_("CONFIG: ignoring extra data in \"%s\"\n"), str);
198           }
199      }
200    else if (i1 != CONFIG_INVALID)
201      {
202         if (fields != 2)
203           {
204              i1 = CONFIG_INVALID;
205              Alert(_("CONFIG: missing required data in \"%s\"\n"), str);
206           }
207      }
208
209    return i1;
210 }
211
212 void
213 ConfigParseError(const char *where, const char *line)
214 {
215    Alert(_("Warning: unable to determine what to do with\n"
216            "the following text in the middle of current %s definition:\n"
217            "%s\nWill ignore and continue...\n"), where, line);
218 }
219
220 void
221 ConfigAlertLoad(const char *txt)
222 {
223    Alert(_("Warning:  Configuration error in %s block.\n"
224            "Outcome is likely not good.\n"), txt);
225 }
226
227 static int
228 ConfigFilePreparse(const char *src, const char *dst, const char *themepath)
229 {
230    char                execline[FILEPATH_LEN_MAX];
231    const char         *epp_path = ENLIGHTENMENT_BIN "/epp";
232    char               *def_home, *def_user, *def_shell;
233
234    if (EDebug(EDBUG_TYPE_CONFIG))
235       Eprintf("ConfigFilePreparse %s -> %s\n", src, dst);
236
237    def_home = homedir(getuid());
238    def_user = username(getuid());
239    def_shell = usershell(getuid());
240
241    /* When themepath is NULL it shouldn't be used, but this is consistent
242     * with old behavior */
243    if (!themepath)
244       themepath = Mode.theme.path;
245
246    Esnprintf(execline, sizeof(execline), "%s " "-P " "-nostdinc " "-undef "
247              "-include %s/config/definitions " "-I%s " "-I%s/config "
248              "-D ENLIGHTENMENT_VERSION=%s " "-D ENLIGHTENMENT_ROOT=%s "
249              "-D ENLIGHTENMENT_BIN=%s "
250              "-D ENLIGHTENMENT_THEME=%s " "-D ECONFDIR=%s "
251              "-D ECACHEDIR=%s " "-D SCREEN_RESOLUTION_%ix%i=1 "
252              "-D SCREEN_WIDTH_%i=1 " "-D SCREEN_HEIGHT_%i=1 "
253              "-D SCREEN_DEPTH_%i=1 " "-D USER_NAME=%s " "-D HOME_DIR=%s "
254              "-D USER_SHELL=%s "
255              "%s %s",
256              epp_path, EDirRoot(), themepath, EDirRoot(),
257              e_wm_version, EDirRoot(), EDirBin(),
258              themepath, EDirUser(), EDirUserCache(),
259              WinGetW(VROOT), WinGetH(VROOT), WinGetW(VROOT), WinGetH(VROOT),
260              WinGetDepth(VROOT), def_user, def_home, def_shell, src, dst);
261    system(execline);
262
263    Efree(def_user);
264    Efree(def_shell);
265    Efree(def_home);
266
267    return exists(dst) ? 0 : 1;
268 }
269
270 /* Split the process of finding the file from the process of loading it */
271 int
272 ConfigFileRead(FILE * fs)
273 {
274    int                 err;
275    int                 i1, i2, fields;
276    char                s[FILEPATH_LEN_MAX];
277    int                 e_cfg_ver = 0;
278
279    while (GetLine(s, sizeof(s), fs))
280      {
281         i1 = i2 = CONFIG_INVALID;
282         fields = sscanf(s, "%i %i", &i1, &i2);
283
284         if (fields < 1)
285           {
286              i1 = CONFIG_INVALID;
287           }
288         else if (i1 == CONFIG_VERSION)
289           {
290              if (fields == 2)
291                 e_cfg_ver = i2;
292           }
293         else if (i1 == CONFIG_CLOSE)
294           {
295              if (fields != 1)
296                {
297                   Alert(_("CONFIG: ignoring extra data in \"%s\"\n"), s);
298                }
299           }
300         else if (i1 != CONFIG_INVALID)
301           {
302              if (fields != 2)
303                {
304                   Alert(_("CONFIG: missing required data in \"%s\"\n"), s);
305                   i1 = CONFIG_INVALID;
306                }
307           }
308
309         if (i2 == CONFIG_OPEN)
310           {
311              if (e_cfg_ver > MAX_E_CFG_VERSION)
312                {
313                   AlertX(_("Theme versioning ERROR"),
314                          _("Restart with Defaults"), " ",
315                          _("Abort and Exit"),
316                          _("ERROR:\n" "\n"
317                            "The configuration for the theme you are running is\n"
318                            "incompatible. It's config revision is %i.\n"
319                            "It needs to be marked as being revision <= %i\n"
320                            "\n"
321                            "Please contact the theme author or maintainer and\n"
322                            "inform them that in order for their theme to function\n"
323                            "with this version of Enlightenment, they have to\n"
324                            "update it to the current settings, and then match\n"
325                            "the revision number.\n" "\n"
326                            "If the theme revision is higher than Enlightenment's\n"
327                            "it may be that you haven't upgraded Enlightenment for\n"
328                            "a while and this theme takes advantages of new\n"
329                            "features in Enlightenment in new versions.\n"),
330                          e_cfg_ver, MAX_E_CFG_VERSION);
331                   SessionExit(EEXIT_THEME, "DEFAULT");
332                }
333              else
334                {
335                   switch (i1)
336                     {
337                     case CONFIG_CLOSE:
338                        goto done;
339
340                     case CONFIG_IMAGECLASS:
341                        err = ImageclassConfigLoad(fs);
342                        if (err)
343                           ConfigAlertLoad("Image class");
344                        break;
345                     case CONFIG_TOOLTIP:
346                        err = TooltipConfigLoad(fs);
347                        if (err)
348                           ConfigAlertLoad("Tooltip");
349                        break;
350                     case CONFIG_TEXT:
351                        err = TextclassConfigLoad(fs);
352                        if (err)
353                           ConfigAlertLoad("Text class");
354                        break;
355                     case MENU_STYLE:
356                        err = MenuStyleConfigLoad(fs);
357                        if (err)
358                           ConfigAlertLoad("Menu style");
359                        break;
360                     case CONFIG_MENU:
361                        err = MenuConfigLoad(fs);
362                        if (err)
363                           ConfigAlertLoad("Menu");
364                        break;
365                     case CONFIG_BORDER:
366                        err = BorderConfigLoad(fs);
367                        if (err)
368                           ConfigAlertLoad("Border");
369                        break;
370                     case CONFIG_BUTTON:
371                        err = ButtonsConfigLoad(fs);
372                        if (err)
373                           ConfigAlertLoad("Button");
374                        break;
375                     case CONFIG_DESKTOP:
376                        err = BackgroundsConfigLoad(fs);
377                        if (err)
378                           ConfigAlertLoad("Background");
379                        break;
380                     case CONFIG_WINDOWMATCH:
381                        err = WindowMatchConfigLoad(fs);
382                        if (err)
383                           ConfigAlertLoad("Window match");
384                        break;
385                     case CONFIG_COLORMOD:
386                        break;
387                     case CONFIG_ACTIONCLASS:
388                        err = AclassConfigLoad(fs);
389                        if (err)
390                           ConfigAlertLoad("Action class");
391                        break;
392                     case CONFIG_SLIDEOUT:
393                        err = SlideoutsConfigLoad(fs);
394                        if (err)
395                           ConfigAlertLoad("Slideout");
396                        break;
397                     default:
398                        break;
399                     }
400                }
401           }
402      }
403
404  done:
405    return 0;
406 }
407
408 static char        *
409 FindFilePath(const char *name, const char *path)
410 {
411    char                s[FILEPATH_LEN_MAX];
412    int                 len;
413
414    if (path)
415      {
416         len = Esnprintf(s, sizeof(s), "%s/%s", path, name);
417         name = s;
418      }
419    else
420      {
421         len = strlen(name);
422      }
423    if (len <= 0)
424       return NULL;
425
426    if (canread(name))
427       return Estrdup(name);
428    else
429       return NULL;
430 }
431
432 char               *
433 FindFile(const char *file, const char *themepath)
434 {
435    char                s[FILEPATH_LEN_MAX];
436    char               *p;
437
438    /* if absolute path - and file exists - return it */
439    if (isabspath(file))
440      {
441         p = FindFilePath(file, NULL);
442         if (p)
443            return p;
444      }
445
446    /* look in ~/.e16 first */
447    p = FindFilePath(file, EDirUser());
448    if (p)
449       return p;
450
451    if (themepath)
452      {
453         /* look in theme dir */
454         p = FindFilePath(file, themepath);
455         if (p)
456            return p;
457      }
458
459    /* look in system config dir */
460    Esnprintf(s, sizeof(s), "%s/config", EDirRoot());
461    p = FindFilePath(file, s);
462    if (p)
463       return p;
464
465    /* not found.... NULL */
466    return NULL;
467 }
468
469 char               *
470 ThemeFileFind(const char *file)
471 {
472    return FindFile(file, Mode.theme.path);
473 }
474
475 char               *
476 ConfigFileFind(const char *name, const char *themepath, int pp)
477 {
478    char                s[FILEPATH_LEN_MAX];
479    char               *fullname, *file, *ppfile;
480    int                 i, err;
481
482    fullname = FindFile(name, themepath);
483    if (!fullname)
484       return NULL;
485
486    /* Quit if not preparsing */
487    if (!pp)
488       return fullname;
489
490    /* The file exists. Now check the preparsed one. */
491    file = Estrdup(fullname);
492    for (i = 0; file[i]; i++)
493       if (file[i] == '/')
494          file[i] = '.';
495
496    Esnprintf(s, sizeof(s), "%s/cached/cfg/%s.preparsed", EDirUserCache(), file);
497    ppfile = Estrdup(s);
498    if (exists(s) && moddate(s) > moddate(fullname))
499       goto done;
500
501    /* No preparesd file or source is newer. Do preparsing. */
502    err = ConfigFilePreparse(fullname, ppfile, themepath);
503    if (err)
504      {
505         Efree(ppfile);
506         ppfile = NULL;
507      }
508
509  done:
510    Efree(fullname);
511    Efree(file);
512    return ppfile;
513 }
514
515 int
516 ConfigFileLoad(const char *name, const char *themepath,
517                int (*parse) (FILE * fs), int preparse)
518 {
519    int                 err = -1;
520    char               *file;
521    FILE               *fs;
522
523    if (EDebug(EDBUG_TYPE_CONFIG))
524       Eprintf("ConfigFileLoad %s\n", name);
525
526    file = ConfigFileFind(name, themepath, preparse);
527    if (!file)
528       goto done;
529
530    fs = fopen(file, "r");
531    Efree(file);
532    if (!fs)
533       goto done;
534
535    err = parse(fs);
536
537    fclose(fs);
538
539  done:
540    return err;
541 }
542
543 int
544 ThemeConfigLoad(void)
545 {
546    static const char  *const config_files[] = {
547       "init.cfg",
548       "textclasses.cfg",
549       "imageclasses.cfg",
550       "desktops.cfg",
551       "actionclasses.cfg",
552       "buttons.cfg",
553       "slideouts.cfg",
554       "borders.cfg",
555       "windowmatches.cfg",
556       "tooltips.cfg",
557       "menustyles.cfg",
558    };
559    char                s[FILEPATH_LEN_MAX];
560    Progressbar        *p = NULL;
561    int                 i;
562
563    /* Font mappings */
564    FontConfigLoad();
565
566    Esnprintf(s, sizeof(s), "%s/", Mode.theme.path);
567
568    for (i = 0; i < (int)(sizeof(config_files) / sizeof(char *)); i++)
569
570      {
571         if (!Mode.wm.restart && Conf.startup.animate)
572           {
573              if (i == 2)
574                 StartupWindowsCreate();
575
576              if ((i > 1) && (!p))
577                {
578                   p = ProgressbarCreate(_("Enlightenment Starting..."), 400,
579                                         16);
580                   if (p)
581                      ProgressbarShow(p);
582                }
583           }
584
585         ConfigFileLoad(config_files[i], Mode.theme.path, ConfigFileRead, 1);
586
587         if (p)
588            ProgressbarSet(p, (i * 100) /
589                           (int)(sizeof(config_files) / sizeof(char *)));
590      }
591
592    if (p)
593       ProgressbarDestroy(p);
594
595    /* Font mappings no longer needed */
596    FontConfigUnload();
597
598    return 0;
599 }