~ubuntu-branches/debian/sid/openbox/sid

« back to all changes in this revision

Viewing changes to obt/ddparse.c

  • Committer: Bazaar Package Importer
  • Author(s): Nico Golde, Nico Golde, Eugenio Paolantonio
  • Date: 2011-10-03 22:59:30 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20111003225930-tdvyax5tx63dyoez
Tags: 3.5.0-1
[Nico Golde]
* New upstream release (Closes: #638783).
  - Fix crashes in the menu code (Closes: #563891).
* Add Brazilian translation to openbox.desktop,
  thanks Sérgio Cipolla (Closes: #627912).
* Remove 06_fix_swap_byte_order.patch, applied upstream.
* Bump debhelper dependency to >= 7.0.50~ due to override.
* Remove CHANGELOG from openbox.docs to prevent double installation.
* Add 02_fix_freedesktop_compliance.dpatch desktop file to
  /usr/share/applications.

[Eugenio Paolantonio]
* debian/patches:
  - Disabled 03_place_windows_in_quadrants.patch
  - Updated 01_rc.xml.patch and 06_fix_swap_byte_order.patch
  - Removed 04_fix_ftbfs_no-add-needed.patch and 20_24bits_support.patch
* debian/control:
  - Added myself to the Uploaders.
  - Build-Depends: removed libxau-dev, libxft-dev and python-xdg;
    added libimlib2-dev
  - openbox Suggests: added python-xdg
  - libobrender21 renamed to libobrender27
  - libobparser21 renamed to libobt0
* debian/rules:
  - Rewrote using a simpler debhelper syntax
  - Moved the install pass to openbox.install
* debian/*.{install,links,dirs}:
  - Updated.
* debian/openbox.xsession:
  - Removed. Openbox now ships it by default.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
 
2
 
 
3
   obt/ddparse.c for the Openbox window manager
 
4
   Copyright (c) 2009        Dana Jansens
 
5
 
 
6
   This program is free software; you can redistribute it and/or modify
 
7
   it under the terms of the GNU General Public License as published by
 
8
   the Free Software Foundation; either version 2 of the License, or
 
9
   (at your option) any later version.
 
10
 
 
11
   This program is distributed in the hope that it will be useful,
 
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
   GNU General Public License for more details.
 
15
 
 
16
   See the COPYING file for a copy of the GNU General Public License.
 
17
*/
 
18
 
 
19
#include "obt/ddparse.h"
 
20
#include "obt/link.h"
 
21
#ifdef HAVE_STRING_H
 
22
#include <string.h>
 
23
#endif
 
24
#ifdef HAVE_STDIO_H
 
25
#include <stdio.h>
 
26
#endif
 
27
 
 
28
typedef struct _ObtDDParse ObtDDParse;
 
29
 
 
30
/* Parses the value and adds it to the group's key_hash, with the given
 
31
   key
 
32
   Return TRUE if it is added to the hash table, and FALSE if not.
 
33
*/
 
34
typedef gboolean (*ObtDDParseValueFunc)(gchar *key, const gchar *val,
 
35
                                        ObtDDParse *parse, gboolean *error);
 
36
 
 
37
 
 
38
enum {
 
39
    DE_TYPE             = 1 << 0,
 
40
    DE_TYPE_APPLICATION = 1 << 1,
 
41
    DE_TYPE_LINK        = 1 << 2,
 
42
    DE_NAME             = 1 << 3,
 
43
    DE_EXEC             = 1 << 4,
 
44
    DE_URL              = 1 << 5
 
45
};
 
46
 
 
47
struct _ObtDDParse {
 
48
    gchar *filename;
 
49
    gulong lineno;
 
50
    gulong flags;
 
51
    ObtDDParseGroup *group;
 
52
    /* the key is a group name, the value is a ObtDDParseGroup */
 
53
    GHashTable *group_hash;
 
54
};
 
55
 
 
56
struct _ObtDDParseGroup {
 
57
    gchar *name;
 
58
    gboolean seen;
 
59
    ObtDDParseValueFunc value_func;
 
60
    /* the key is a string (a key inside the group in the .desktop).
 
61
       the value is an ObtDDParseValue */
 
62
    GHashTable *key_hash;
 
63
};
 
64
 
 
65
/* Displays a warning message including the file name and line number, and
 
66
   sets the boolean @error to true if it points to a non-NULL address.
 
67
*/
 
68
static void parse_error(const gchar *m, const ObtDDParse *const parse,
 
69
                        gboolean *error)
 
70
{
 
71
    if (!parse->filename)
 
72
        g_warning("%s at line %lu of input", m, parse->lineno);
 
73
    else
 
74
        g_warning("%s at line %lu of file %s",
 
75
                  m, parse->lineno, parse->filename);
 
76
    if (error) *error = TRUE;
 
77
}
 
78
 
 
79
static void parse_value_free(ObtDDParseValue *v)
 
80
{
 
81
    switch (v->type) {
 
82
    case OBT_DDPARSE_EXEC:
 
83
    case OBT_DDPARSE_STRING:
 
84
    case OBT_DDPARSE_LOCALESTRING:
 
85
        g_free(v->value.string); break;
 
86
    case OBT_DDPARSE_STRINGS:
 
87
    case OBT_DDPARSE_LOCALESTRINGS:
 
88
        g_strfreev(v->value.strings.a);
 
89
        v->value.strings.n = 0;
 
90
        break;
 
91
    case OBT_DDPARSE_BOOLEAN:
 
92
    case OBT_DDPARSE_NUMERIC:
 
93
    case OBT_DDPARSE_ENUM_TYPE:
 
94
    case OBT_DDPARSE_ENVIRONMENTS:
 
95
        break;
 
96
    default:
 
97
        g_assert_not_reached();
 
98
    }
 
99
    g_slice_free(ObtDDParseValue, v);
 
100
}
 
101
 
 
102
static ObtDDParseGroup* parse_group_new(gchar *name, ObtDDParseValueFunc f)
 
103
{
 
104
    ObtDDParseGroup *g = g_slice_new(ObtDDParseGroup);
 
105
    g->name = name;
 
106
    g->value_func = f;
 
107
    g->seen = FALSE;
 
108
    g->key_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
 
109
                                        g_free,
 
110
                                        (GDestroyNotify)parse_value_free);
 
111
    return g;
 
112
}
 
113
 
 
114
static void parse_group_free(ObtDDParseGroup *g)
 
115
{
 
116
    g_free(g->name);
 
117
    g_hash_table_destroy(g->key_hash);
 
118
    g_slice_free(ObtDDParseGroup, g);
 
119
}
 
120
 
 
121
/*! Reads an input string, strips out invalid stuff, and parses
 
122
    backslash-stuff.
 
123
 */
 
124
static gchar* parse_value_string(const gchar *in,
 
125
                                 gboolean locale,
 
126
                                 gboolean semicolonterminate,
 
127
                                 gulong *len,
 
128
                                 const ObtDDParse *const parse,
 
129
                                 gboolean *error)
 
130
{
 
131
    gint bytes;
 
132
    gboolean backslash;
 
133
    gchar *out, *o;
 
134
    const gchar *end, *i;
 
135
 
 
136
    /* find the end/size of the string */
 
137
    backslash = FALSE;
 
138
    for (end = in; *end; ++end) {
 
139
        if (semicolonterminate) {
 
140
            if (backslash) backslash = FALSE;
 
141
            else if (*end == '\\') backslash = TRUE;
 
142
            else if (*end == ';') break;
 
143
        }
 
144
    }
 
145
    bytes = end - in;
 
146
 
 
147
    g_return_val_if_fail(in != NULL, NULL);
 
148
 
 
149
    if (locale && !g_utf8_validate(in, bytes, &end)) {
 
150
        parse_error("Invalid bytes in localestring", parse, error);
 
151
        bytes = end - in;
 
152
    }
 
153
 
 
154
    out = g_new(char, bytes + 1);
 
155
    if (len) *len = 0;
 
156
    i = in; o = out;
 
157
    backslash = FALSE;
 
158
    while (i < end) {
 
159
        const gchar *next;
 
160
 
 
161
        /* find the next character in the string */
 
162
        if (!locale) next = i+1;
 
163
        else if (!(next = g_utf8_find_next_char(i, end))) next = end;
 
164
 
 
165
        if (backslash) {
 
166
            switch(*i) {
 
167
            case 's': *o++ = ' '; break;
 
168
            case 'n': *o++ = '\n'; break;
 
169
            case 't': *o++ = '\t'; break;
 
170
            case 'r': *o++ = '\r'; break;
 
171
            case ';': *o++ = ';'; break;
 
172
            case '\\': *o++ = '\\'; break;
 
173
            default:
 
174
                parse_error((locale ?
 
175
                             "Invalid escape sequence in localestring" :
 
176
                             "Invalid escape sequence in string"),
 
177
                            parse, error);
 
178
            }
 
179
            backslash = FALSE;
 
180
        }
 
181
        else if (*i == '\\')
 
182
            backslash = TRUE;
 
183
        else if ((guchar)*i >= 127 || (guchar)*i < 32) {
 
184
            /* avoid ascii control characters */
 
185
            parse_error("Found control character in string", parse, error);
 
186
            break;
 
187
        }
 
188
        else {
 
189
            const gulong s = next-i;
 
190
            memcpy(o, i, s);
 
191
            o += s;
 
192
            if (len) *len += s;
 
193
        }
 
194
        i = next;
 
195
    }
 
196
    *o = '\0';
 
197
    return out;
 
198
}
 
199
 
 
200
 
 
201
/*! Reads a list of input strings, strips out invalid stuff, and parses
 
202
    backslash-stuff.
 
203
 */
 
204
static gchar** parse_value_strings(const gchar *in,
 
205
                                   gboolean locale,
 
206
                                   gulong *nstrings,
 
207
                                   const ObtDDParse *const parse,
 
208
                                   gboolean *error)
 
209
{
 
210
    gchar **out;
 
211
    const gchar *i;
 
212
 
 
213
    out = g_new(gchar*, 1);
 
214
    out[0] = NULL;
 
215
    *nstrings = 0;
 
216
 
 
217
    i = in;
 
218
    while (TRUE) {
 
219
        gchar *a;
 
220
        gulong len;
 
221
 
 
222
        a = parse_value_string(i, locale, TRUE, &len, parse, error);
 
223
        i += len;
 
224
 
 
225
        if (len) {
 
226
            (*nstrings)++;
 
227
            out = g_renew(gchar*, out, *nstrings+1);
 
228
            out[*nstrings-1] = a;
 
229
            out[*nstrings] = NULL;
 
230
        }
 
231
 
 
232
        if (!*i) break; /* no more strings */
 
233
        ++i;
 
234
    }
 
235
    return out;
 
236
}
 
237
 
 
238
static guint parse_value_environments(const gchar *in,
 
239
                                      const ObtDDParse *const parse,
 
240
                                      gboolean *error)
 
241
{
 
242
    const gchar *s;
 
243
    guint mask = 0;
 
244
 
 
245
    s = in;
 
246
    while (*s) {
 
247
        switch (*(s++)) {
 
248
        case 'G':
 
249
            if (strcmp(s, "NOME") == 0) {
 
250
                mask |= OBT_LINK_ENV_GNOME;
 
251
                s += 4;
 
252
            }
 
253
            break;
 
254
        case 'K':
 
255
            if (strcmp(s, "DE") == 0) {
 
256
                mask |= OBT_LINK_ENV_KDE;
 
257
                s += 2;
 
258
            }
 
259
            break;
 
260
        case 'L':
 
261
            if (strcmp(s, "XDE") == 0) {
 
262
                mask |= OBT_LINK_ENV_LXDE;
 
263
                s += 3;
 
264
            }
 
265
            break;
 
266
        case 'R':
 
267
            if (strcmp(s, "OX") == 0) {
 
268
                mask |= OBT_LINK_ENV_ROX;
 
269
                s += 2;
 
270
            }
 
271
            break;
 
272
        case 'X':
 
273
            if (strcmp(s, "FCE") == 0) {
 
274
                mask |= OBT_LINK_ENV_XFCE;
 
275
                s += 3;
 
276
            }
 
277
            break;
 
278
        case 'O':
 
279
            switch (*(s++)) {
 
280
            case 'l':
 
281
                if (strcmp(s, "d") == 0) {
 
282
                    mask |= OBT_LINK_ENV_OLD;
 
283
                    s += 1;
 
284
                }
 
285
                break;
 
286
            case 'P':
 
287
                if (strcmp(s, "ENBOX") == 0) {
 
288
                    mask |= OBT_LINK_ENV_OPENBOX;
 
289
                    s += 5;
 
290
                }
 
291
                break;
 
292
            }
 
293
        }
 
294
        /* find the next string, or the end of the sequence */
 
295
        while (*s && *s != ';') ++s;
 
296
    }
 
297
    return mask;
 
298
}
 
299
 
 
300
static gboolean parse_value_boolean(const gchar *in,
 
301
                                    const ObtDDParse *const parse,
 
302
                                    gboolean *error)
 
303
{
 
304
    if (strcmp(in, "true") == 0)
 
305
        return TRUE;
 
306
    else if (strcmp(in, "false") != 0)
 
307
        parse_error("Invalid boolean value", parse, error);
 
308
    return FALSE;
 
309
}
 
310
 
 
311
static gfloat parse_value_numeric(const gchar *in,
 
312
                                  const ObtDDParse *const parse,
 
313
                                  gboolean *error)
 
314
{
 
315
    gfloat out = 0;
 
316
    if (sscanf(in, "%f", &out) == 0)
 
317
        parse_error("Invalid numeric value", parse, error);
 
318
    return out;
 
319
}
 
320
 
 
321
static gboolean parse_file_line(FILE *f, gchar **buf,
 
322
                                gulong *size, gulong *read,
 
323
                                ObtDDParse *parse, gboolean *error)
 
324
{
 
325
    const gulong BUFMUL = 80;
 
326
    size_t ret;
 
327
    gulong i, null;
 
328
 
 
329
    if (*size == 0) {
 
330
        g_assert(*read == 0);
 
331
        *size = BUFMUL;
 
332
        *buf = g_new(char, *size);
 
333
    }
 
334
 
 
335
    /* remove everything up to a null zero already in the buffer and shift
 
336
       the rest to the front */
 
337
    null = *size;
 
338
    for (i = 0; i < *read; ++i) {
 
339
        if (null < *size)
 
340
            (*buf)[i-null-1] = (*buf)[i];
 
341
        else if ((*buf)[i] == '\0')
 
342
            null = i;
 
343
    }
 
344
    if (null < *size)
 
345
        *read -= null + 1;
 
346
 
 
347
    /* is there already a newline in the buffer? */
 
348
    for (i = 0; i < *read; ++i)
 
349
        if ((*buf)[i] == '\n') {
 
350
            /* turn it into a null zero and done */
 
351
            (*buf)[i] = '\0';
 
352
            return TRUE;
 
353
        }
 
354
 
 
355
    /* we need to read some more to find a newline */
 
356
    while (TRUE) {
 
357
        gulong eol;
 
358
        gchar *newread;
 
359
 
 
360
        newread = *buf + *read;
 
361
        ret = fread(newread, sizeof(char), *size-*read, f);
 
362
        if (ret < *size - *read && !feof(f)) {
 
363
            parse_error("Error reading", parse, error);
 
364
            return FALSE;
 
365
        }
 
366
        *read += ret;
 
367
 
 
368
        /* strip out null zeros in the input and look for an endofline */
 
369
        null = 0;
 
370
        eol = *size;
 
371
        for (i = newread-*buf; i < *read; ++i) {
 
372
            if (null > 0)
 
373
                (*buf)[i] = (*buf)[i+null];
 
374
            if ((*buf)[i] == '\0') {
 
375
                ++null;
 
376
                --(*read);
 
377
                --i; /* try again */
 
378
            }
 
379
            else if ((*buf)[i] == '\n' && eol == *size) {
 
380
                eol = i;
 
381
                /* turn it into a null zero */
 
382
                (*buf)[i] = '\0';
 
383
            }
 
384
        }
 
385
 
 
386
        if (eol != *size)
 
387
            /* found an endofline, done */
 
388
            break;
 
389
        else if (feof(f) && *read < *size) {
 
390
            /* found the endoffile, done (if there is space) */
 
391
            if (*read > 0) {
 
392
                /* stick a null zero on if there is test on the last line */
 
393
                (*buf)[(*read)++] = '\0';
 
394
            }
 
395
            break;
 
396
        }
 
397
        else {
 
398
            /* read more */
 
399
            size += BUFMUL;
 
400
            *buf = g_renew(char, *buf, *size);
 
401
        }
 
402
    }
 
403
    return *read > 0;
 
404
}
 
405
 
 
406
static void parse_group(const gchar *buf, gulong len,
 
407
                        ObtDDParse *parse, gboolean *error)
 
408
{
 
409
    ObtDDParseGroup *g;
 
410
    gchar *group;
 
411
    gulong i;
 
412
 
 
413
    /* get the group name */
 
414
    group = g_strndup(buf+1, len-2);
 
415
    for (i = 0; i < len-2; ++i)
 
416
        if ((guchar)group[i] < 32 || (guchar)group[i] >= 127) {
 
417
            /* valid ASCII only */
 
418
            parse_error("Invalid character found", parse, NULL);
 
419
            group[i] = '\0'; /* stopping before this character */
 
420
            break;
 
421
        }
 
422
 
 
423
    /* make sure it's a new group */
 
424
    g = g_hash_table_lookup(parse->group_hash, group);
 
425
    if (g && g->seen) {
 
426
        parse_error("Duplicate group found", parse, error);
 
427
        g_free(group);
 
428
        return;
 
429
    }
 
430
    /* if it's the first group, make sure it's named Desktop Entry */
 
431
    else if (!parse->group && strcmp(group, "Desktop Entry") != 0)
 
432
    {
 
433
        parse_error("Incorrect group found, "
 
434
                    "expected [Desktop Entry]",
 
435
                    parse, error);
 
436
        g_free(group);
 
437
        return;
 
438
    }
 
439
    else {
 
440
        if (!g) {
 
441
            g = parse_group_new(group, NULL);
 
442
            g_hash_table_insert(parse->group_hash, g->name, g);
 
443
        }
 
444
        else
 
445
            g_free(group);
 
446
 
 
447
        g->seen = TRUE;
 
448
        parse->group = g;
 
449
        g_print("Found group %s\n", g->name);
 
450
    }
 
451
}
 
452
 
 
453
static void parse_key_value(const gchar *buf, gulong len,
 
454
                            ObtDDParse *parse, gboolean *error)
 
455
{
 
456
    gulong i, keyend, valstart, eq;
 
457
    char *key;
 
458
 
 
459
    /* find the end of the key */
 
460
    for (i = 0; i < len; ++i)
 
461
        if (!(((guchar)buf[i] >= 'A' && (guchar)buf[i] <= 'Z') ||
 
462
              ((guchar)buf[i] >= 'a' && (guchar)buf[i] <= 'z') ||
 
463
              ((guchar)buf[i] >= '0' && (guchar)buf[i] <= '9') ||
 
464
              ((guchar)buf[i] == '-'))) {
 
465
            /* not part of the key */
 
466
            break;
 
467
        }
 
468
    keyend = i;
 
469
 
 
470
    if (keyend < 1) {
 
471
        parse_error("Empty key", parse, error);
 
472
        return;
 
473
    }
 
474
    /* find the = character */
 
475
    for (i = keyend; i < len; ++i) {
 
476
        if (buf[i] == '=') {
 
477
            eq = i;
 
478
            break;
 
479
        }
 
480
        else if (buf[i] != ' ') {
 
481
            parse_error("Invalid character in key name", parse, error);
 
482
            return ;
 
483
        }
 
484
    }
 
485
    if (i == len) {
 
486
        parse_error("Key without value found", parse, error);
 
487
        return;
 
488
    }
 
489
    /* find the start of the value */
 
490
    for (i = eq+1; i < len; ++i)
 
491
        if (buf[i] != ' ') {
 
492
            valstart = i;
 
493
            break;
 
494
        }
 
495
    if (i == len) {
 
496
        parse_error("Empty value found", parse, error);
 
497
        return;
 
498
    }
 
499
 
 
500
    key = g_strndup(buf, keyend);
 
501
    if (g_hash_table_lookup(parse->group->key_hash, key)) {
 
502
        parse_error("Duplicate key found", parse, error);
 
503
        g_free(key);
 
504
        return;
 
505
    }
 
506
    g_print("Found key/value %s=%s.\n", key, buf+valstart);
 
507
    if (parse->group->value_func)
 
508
        if (!parse->group->value_func(key, buf+valstart, parse, error)) {
 
509
            parse_error("Unknown key", parse, error);
 
510
            g_free(key);
 
511
        }
 
512
}
 
513
 
 
514
static gboolean parse_file(FILE *f, ObtDDParse *parse)
 
515
{
 
516
    gchar *buf = NULL;
 
517
    gulong bytes = 0, read = 0;
 
518
    gboolean error = FALSE;
 
519
 
 
520
    while (!error && parse_file_line(f, &buf, &bytes, &read, parse, &error)) {
 
521
        gulong len = strlen(buf);
 
522
        if (buf[0] == '#' || buf[0] == '\0')
 
523
            ; /* ignore comment lines */
 
524
        else if (buf[0] == '[' && buf[len-1] == ']')
 
525
            parse_group(buf, len, parse, &error);
 
526
        else if (!parse->group)
 
527
            /* just ignore keys outside of groups */
 
528
            parse_error("Key found before group", parse, NULL);
 
529
        else
 
530
            /* ignore errors in key-value pairs and continue */
 
531
            parse_key_value(buf, len, parse, NULL);
 
532
        ++parse->lineno;
 
533
    }
 
534
 
 
535
    if (buf) g_free(buf);
 
536
    return !error;
 
537
}
 
538
 
 
539
static gboolean parse_desktop_entry_value(gchar *key, const gchar *val,
 
540
                                          ObtDDParse *parse, gboolean *error)
 
541
{
 
542
    ObtDDParseValue v, *pv;
 
543
 
 
544
    switch (key[0]) {
 
545
    case 'C':
 
546
        switch (key[1]) {
 
547
        case 'a': /* Categories */
 
548
            if (strcmp(key+2, "tegories")) return FALSE;
 
549
            v.type = OBT_DDPARSE_STRINGS; break;
 
550
        case 'o': /* Comment */
 
551
            if (strcmp(key+2, "mment")) return FALSE;
 
552
            v.type = OBT_DDPARSE_LOCALESTRING; break;
 
553
        default:
 
554
            return FALSE;
 
555
        }
 
556
        break;
 
557
    case 'E': /* Exec */
 
558
        if (strcmp(key+1, "xec")) return FALSE;
 
559
        v.type = OBT_DDPARSE_EXEC; parse->flags |= DE_EXEC; break;
 
560
    case 'G': /* GenericName */
 
561
        if (strcmp(key+1, "enericName")) return FALSE;
 
562
        v.type = OBT_DDPARSE_LOCALESTRING; break;
 
563
    case 'I': /* Icon */
 
564
        if (strcmp(key+1, "con")) return FALSE;
 
565
        v.type = OBT_DDPARSE_LOCALESTRING; break;
 
566
    case 'H': /* Hidden */
 
567
        if (strcmp(key+1, "idden")) return FALSE;
 
568
        v.type = OBT_DDPARSE_BOOLEAN; break;
 
569
    case 'M': /* MimeType */
 
570
        if (strcmp(key+1, "imeType")) return FALSE;
 
571
        v.type = OBT_DDPARSE_STRINGS; break;
 
572
    case 'N':
 
573
        switch (key[1]) {
 
574
        case 'a': /* Name */
 
575
            if (strcmp(key+2, "me")) return FALSE;
 
576
            v.type = OBT_DDPARSE_LOCALESTRING; parse->flags |= DE_NAME; break;
 
577
        case 'o':
 
578
            switch (key[2]) {
 
579
            case 'D': /* NoDisplay */
 
580
                if (strcmp(key+3, "isplay")) return FALSE;
 
581
                v.type = OBT_DDPARSE_BOOLEAN; break;
 
582
            case 't': /* NotShowIn */
 
583
                if (strcmp(key+3, "ShowIn")) return FALSE;
 
584
                v.type = OBT_DDPARSE_STRINGS; break;
 
585
            default:
 
586
                return FALSE;
 
587
            }
 
588
            break;
 
589
        default:
 
590
            return FALSE;
 
591
        }
 
592
        break;
 
593
    case 'P': /* Path */
 
594
        if (strcmp(key+1, "ath")) return FALSE;
 
595
        v.type = OBT_DDPARSE_STRING; break;
 
596
    case 'S': /* Path */
 
597
        if (key[1] == 't' && key[2] == 'a' && key[3] == 'r' &&
 
598
            key[4] == 't' && key[5] == 'u' && key[6] == 'p')
 
599
            switch (key[7]) {
 
600
            case 'N': /* StartupNotify */
 
601
                if (strcmp(key+8, "otify")) return FALSE;
 
602
                v.type = OBT_DDPARSE_BOOLEAN; break;
 
603
            case 'W': /* StartupWMClass */
 
604
                if (strcmp(key+8, "MClass")) return FALSE;
 
605
                v.type = OBT_DDPARSE_STRING; break;
 
606
            default:
 
607
                return FALSE;
 
608
            }
 
609
        else
 
610
            return FALSE;
 
611
        break;
 
612
    case 'T':
 
613
        switch (key[1]) {
 
614
        case 'e': /* Terminal */
 
615
            if (strcmp(key+2, "rminal")) return FALSE;
 
616
            v.type = OBT_DDPARSE_BOOLEAN; break;
 
617
        case 'r': /* TryExec */
 
618
            if (strcmp(key+2, "yExec")) return FALSE;
 
619
            v.type = OBT_DDPARSE_STRING; break;
 
620
        case 'y': /* Type */
 
621
            if (strcmp(key+2, "pe")) return FALSE;
 
622
            v.type = OBT_DDPARSE_ENUM_TYPE; parse->flags |= DE_TYPE; break;
 
623
        default:
 
624
            return FALSE;
 
625
        }
 
626
        break;
 
627
    case 'U': /* URL */
 
628
        if (strcmp(key+1, "RL")) return FALSE;
 
629
        v.type = OBT_DDPARSE_STRING; parse->flags |= DE_URL; break;
 
630
    case 'V': /* MimeType */
 
631
        if (strcmp(key+1, "ersion")) return FALSE;
 
632
        v.type = OBT_DDPARSE_STRING; break;
 
633
    default:
 
634
        return FALSE;
 
635
    }
 
636
 
 
637
    /* parse the value */
 
638
    switch (v.type) {
 
639
    case OBT_DDPARSE_EXEC: {
 
640
        gchar *c, *m;
 
641
        gboolean percent;
 
642
        gboolean found;
 
643
 
 
644
        v.value.string = parse_value_string(val, FALSE, FALSE, NULL,
 
645
                                            parse, error);
 
646
        g_assert(v.value.string);
 
647
 
 
648
        /* an exec string can only contain one of the file/url-opening %'s */
 
649
        percent = found = FALSE;
 
650
        for (c = v.value.string; *c; ++c) {
 
651
            if (percent) {
 
652
                switch (*c) {
 
653
                case 'f':
 
654
                case 'F':
 
655
                case 'u':
 
656
                case 'U':
 
657
                    if (found) {
 
658
                        m = g_strdup_printf("Malformed Exec key, "
 
659
                                            "extraneous %%%c", *c);
 
660
                        parse_error(m, parse, error);
 
661
                        g_free(m);
 
662
                    }
 
663
                    found = TRUE;
 
664
                    break;
 
665
                case 'd':
 
666
                case 'D':
 
667
                case 'n':
 
668
                case 'N':
 
669
                case 'v':
 
670
                case 'm':
 
671
                    m = g_strdup_printf("Malformed Exec key, "
 
672
                                        "uses deprecated %%%c", *c);
 
673
                    parse_error(m, parse, NULL); /* just a warning */
 
674
                    g_free(m);
 
675
                    break;
 
676
                case 'i':
 
677
                case 'c':
 
678
                case 'k':
 
679
                case '%':
 
680
                    break;
 
681
                default:
 
682
                    m = g_strdup_printf("Malformed Exec key, "
 
683
                                        "uses unknown %%%c", *c);
 
684
                    parse_error(m, parse, NULL); /* just a warning */
 
685
                    g_free(m);
 
686
                }
 
687
                percent = FALSE;
 
688
            }
 
689
            else if (*c == '%') percent = TRUE;
 
690
        }
 
691
        break;
 
692
    }
 
693
    case OBT_DDPARSE_STRING:
 
694
        v.value.string = parse_value_string(val, FALSE, FALSE, NULL,
 
695
                                            parse, error);
 
696
        g_assert(v.value.string);
 
697
        break;
 
698
    case OBT_DDPARSE_LOCALESTRING:
 
699
        v.value.string = parse_value_string(val, TRUE, FALSE, NULL,
 
700
                                            parse, error);
 
701
        g_assert(v.value.string);
 
702
        break;
 
703
    case OBT_DDPARSE_STRINGS:
 
704
        v.value.strings.a = parse_value_strings(val, FALSE, &v.value.strings.n,
 
705
                                                parse, error);
 
706
        g_assert(v.value.strings.a);
 
707
        g_assert(v.value.strings.n);
 
708
        break;
 
709
    case OBT_DDPARSE_LOCALESTRINGS:
 
710
        v.value.strings.a = parse_value_strings(val, TRUE, &v.value.strings.n,
 
711
                                                parse, error);
 
712
        g_assert(v.value.strings.a);
 
713
        g_assert(v.value.strings.n);
 
714
        break;
 
715
    case OBT_DDPARSE_BOOLEAN:
 
716
        v.value.boolean = parse_value_boolean(val, parse, error);
 
717
        break;
 
718
    case OBT_DDPARSE_NUMERIC:
 
719
        v.value.numeric = parse_value_numeric(val, parse, error);
 
720
        break;
 
721
    case OBT_DDPARSE_ENUM_TYPE:
 
722
        if (val[0] == 'A' && strcmp(val+1, "pplication") == 0) {
 
723
            v.value.enumerable = OBT_LINK_TYPE_APPLICATION;
 
724
            parse->flags |= DE_TYPE_APPLICATION;
 
725
        }
 
726
        else if (val[0] == 'L' && strcmp(val+1, "ink") == 0) {
 
727
            v.value.enumerable = OBT_LINK_TYPE_URL;
 
728
            parse->flags |= DE_TYPE_LINK;
 
729
        }
 
730
        else if (val[0] == 'D' && strcmp(val+1, "irectory") == 0)
 
731
            v.value.enumerable = OBT_LINK_TYPE_DIRECTORY;
 
732
        else {
 
733
            parse_error("Unknown Type", parse, error);
 
734
            return FALSE;
 
735
        }
 
736
        break;
 
737
    case OBT_DDPARSE_ENVIRONMENTS:
 
738
        v.value.environments = parse_value_environments(val, parse, error);
 
739
        break;
 
740
    default:
 
741
        g_assert_not_reached();
 
742
    }
 
743
 
 
744
    pv = g_slice_new(ObtDDParseValue);
 
745
    *pv = v;
 
746
    g_hash_table_insert(parse->group->key_hash, key, pv);
 
747
    return TRUE;
 
748
}
 
749
 
 
750
GHashTable* obt_ddparse_file(const gchar *name, GSList *paths)
 
751
{
 
752
    ObtDDParse parse;
 
753
    ObtDDParseGroup *desktop_entry;
 
754
    GSList *it;
 
755
    FILE *f;
 
756
    gboolean success;
 
757
 
 
758
    parse.filename = NULL;
 
759
    parse.lineno = 0;
 
760
    parse.group = NULL;
 
761
    parse.group_hash = g_hash_table_new_full(g_str_hash,
 
762
                                             g_str_equal,
 
763
                                             NULL,
 
764
                                             (GDestroyNotify)parse_group_free);
 
765
 
 
766
    /* set up the groups (there's only one right now) */
 
767
    desktop_entry = parse_group_new(g_strdup("Desktop Entry"),
 
768
                                    parse_desktop_entry_value);
 
769
    g_hash_table_insert(parse.group_hash, desktop_entry->name, desktop_entry);
 
770
 
 
771
    success = FALSE;
 
772
    for (it = paths; it && !success; it = g_slist_next(it)) {
 
773
        gchar *path = g_strdup_printf("%s/%s", (char*)it->data, name);
 
774
        if ((f = fopen(path, "r"))) {
 
775
            parse.filename = path;
 
776
            parse.lineno = 1;
 
777
            parse.flags = 0;
 
778
            if ((success = parse_file(f, &parse))) {
 
779
                /* check that required keys exist */
 
780
 
 
781
                if (!(parse.flags & DE_TYPE)) {
 
782
                    g_warning("Missing Type key in %s", path);
 
783
                    success = FALSE;
 
784
                }
 
785
                if (!(parse.flags & DE_NAME)) {
 
786
                    g_warning("Missing Name key in %s", path);
 
787
                    success = FALSE;
 
788
                }
 
789
                if (parse.flags & DE_TYPE_APPLICATION &&
 
790
                    !(parse.flags & DE_EXEC))
 
791
                {
 
792
                    g_warning("Missing Exec key for Application in %s",
 
793
                              path);
 
794
                    success = FALSE;
 
795
                }
 
796
                else if (parse.flags & DE_TYPE_LINK && !(parse.flags & DE_URL))
 
797
                {
 
798
                    g_warning("Missing URL key for Link in %s", path);
 
799
                    success = FALSE;
 
800
                }
 
801
            }
 
802
            fclose(f);
 
803
        }
 
804
        g_free(path);
 
805
    }
 
806
    if (!success) {
 
807
        g_hash_table_destroy(parse.group_hash);
 
808
        parse.group_hash = NULL;
 
809
    }
 
810
    return parse.group_hash;
 
811
}
 
812
 
 
813
GHashTable* obt_ddparse_group_keys(ObtDDParseGroup *g)
 
814
{
 
815
    return g->key_hash;
 
816
}