~ubuntu-branches/ubuntu/lucid/desktop-file-utils/lucid

« back to all changes in this revision

Viewing changes to src/desktop_file.c

  • Committer: Bazaar Package Importer
  • Author(s): Baptiste Mille-Mathias
  • Date: 2007-06-11 08:58:58 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20070611085858-o940g2g5h9telh2c
Tags: 0.13-0ubuntu1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#include <config.h>
2
 
 
3
 
#include <string.h>
4
 
#include <errno.h>
5
 
#include <stdio.h>
6
 
#include <unistd.h>
7
 
#include <sys/types.h>
8
 
#include <sys/stat.h>
9
 
#include <locale.h>
10
 
#include "desktop_file.h"
11
 
 
12
 
 
13
 
#include <libintl.h>
14
 
#define _(x) gettext ((x))
15
 
#define N_(x) x
16
 
 
17
 
 
18
 
typedef struct _GnomeDesktopFileSection GnomeDesktopFileSection;
19
 
typedef struct _GnomeDesktopFileLine GnomeDesktopFileLine;
20
 
typedef struct _GnomeDesktopFileParser GnomeDesktopFileParser;
21
 
 
22
 
struct _GnomeDesktopFileSection {
23
 
  GQuark section_name; /* 0 means just a comment block (before any section) */
24
 
  gint n_lines;
25
 
  GnomeDesktopFileLine *lines;
26
 
  gint n_allocated_lines;
27
 
};
28
 
 
29
 
struct _GnomeDesktopFileLine {
30
 
  GQuark key; /* 0 means comment or blank line in value */
31
 
  char *locale;
32
 
  gchar *value;
33
 
};
34
 
 
35
 
struct _GnomeDesktopFile {
36
 
  gint n_sections;
37
 
  GnomeDesktopFileSection *sections;
38
 
  gint n_allocated_sections;
39
 
  gint main_section;
40
 
  GnomeDesktopFileEncoding encoding;
41
 
};
42
 
 
43
 
struct _GnomeDesktopFileParser {
44
 
  GnomeDesktopFile *df;
45
 
  gint current_section;
46
 
  gint line_nr;
47
 
  char *line;
48
 
};
49
 
 
50
 
#define VALID_KEY_CHAR 1
51
 
#define VALID_LOCALE_CHAR 2
52
 
guchar valid[256] = { 
53
 
  /* 0   */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
54
 
  /* 16  */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
55
 
  /* 32  */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x3 , 0x2 , 0x0 , 
56
 
  /* 48  */ 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
57
 
  /* 64  */ 0x2 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 
58
 
  /* 80  */ 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x2 , 
59
 
  /* 96  */ 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 
60
 
  /* 112 */ 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
61
 
  /* 128 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
62
 
  /* 144 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
63
 
  /* 160 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
64
 
  /* 176 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
65
 
  /* 192 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
66
 
  /* 208 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
67
 
  /* 224 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 
68
 
  /* 240 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0
69
 
};
70
 
 
71
 
static void                     report_error   (GnomeDesktopFileParser   *parser,
72
 
                                                char                     *message,
73
 
                                                GnomeDesktopParseError    error_code,
74
 
                                                GError                  **error);
75
 
static GnomeDesktopFileSection *lookup_section (GnomeDesktopFile         *df,
76
 
                                                const char               *section);
77
 
static GnomeDesktopFileLine *   lookup_line    (GnomeDesktopFile         *df,
78
 
                                                GnomeDesktopFileSection  *section,
79
 
                                                const char               *keyname,
80
 
                                                const char               *locale);
81
 
 
82
 
 
83
 
 
84
 
 
85
 
GQuark
86
 
gnome_desktop_parse_error_quark (void)
87
 
{
88
 
  static GQuark quark;
89
 
  if (!quark)
90
 
    quark = g_quark_from_static_string ("g_desktop_parse_error");
91
 
 
92
 
  return quark;
93
 
}
94
 
 
95
 
static void
96
 
parser_free (GnomeDesktopFileParser *parser)
97
 
{
98
 
  gnome_desktop_file_free (parser->df);
99
 
}
100
 
 
101
 
static void
102
 
gnome_desktop_file_line_free (GnomeDesktopFileLine *line)
103
 
{
104
 
  g_free (line->locale);
105
 
  g_free (line->value);
106
 
}
107
 
 
108
 
static void
109
 
gnome_desktop_file_section_free (GnomeDesktopFileSection *section)
110
 
{
111
 
  int i;
112
 
 
113
 
  for (i = 0; i < section->n_lines; i++)
114
 
    gnome_desktop_file_line_free (&section->lines[i]);
115
 
  
116
 
  g_free (section->lines);
117
 
}
118
 
 
119
 
void
120
 
gnome_desktop_file_free (GnomeDesktopFile *df)
121
 
{
122
 
  int i;
123
 
 
124
 
  for (i = 0; i < df->n_sections; i++)
125
 
    gnome_desktop_file_section_free (&df->sections[i]);
126
 
  g_free (df->sections);
127
 
 
128
 
  g_free (df);
129
 
}
130
 
 
131
 
static void
132
 
grow_lines_in_section (GnomeDesktopFileSection *section)
133
 
{
134
 
  int new_n_lines;
135
 
 
136
 
  if (section->n_allocated_lines == 0)
137
 
    new_n_lines = 1;
138
 
  else
139
 
    new_n_lines = section->n_allocated_lines*2;
140
 
 
141
 
  section->lines = g_realloc (section->lines,
142
 
                              sizeof (GnomeDesktopFileLine) * new_n_lines);
143
 
  section->n_allocated_lines = new_n_lines;
144
 
}
145
 
 
146
 
static void
147
 
grow_sections (GnomeDesktopFile *df)
148
 
{
149
 
  int new_n_sections;
150
 
 
151
 
  if (df->n_allocated_sections == 0)
152
 
    new_n_sections = 1;
153
 
  else
154
 
    new_n_sections = df->n_allocated_sections*2;
155
 
 
156
 
  df->sections = g_realloc (df->sections,
157
 
                            sizeof (GnomeDesktopFileSection) * new_n_sections);
158
 
  df->n_allocated_sections = new_n_sections;
159
 
}
160
 
 
161
 
static gchar *
162
 
unescape_string (gchar *str, gint len)
163
 
{
164
 
  gchar *res;
165
 
  gchar *p, *q;
166
 
  gchar *end;
167
 
 
168
 
  /* len + 1 is enough, because unescaping never makes the
169
 
   * string longer */
170
 
  res = g_new (gchar, len + 1);
171
 
  p = str;
172
 
  q = res;
173
 
  end = str + len;
174
 
 
175
 
  while (p < end)
176
 
    {
177
 
      if (*p == 0)
178
 
        {
179
 
          /* Found an embedded null */
180
 
          g_free (res);
181
 
          return NULL;
182
 
        }
183
 
      if (*p == '\\')
184
 
        {
185
 
          p++;
186
 
          if (p >= end)
187
 
            {
188
 
              /* Escape at end of string */
189
 
              g_free (res);
190
 
              return NULL;
191
 
            }
192
 
          
193
 
          switch (*p)
194
 
            {
195
 
            case 's':
196
 
              *q++ = ' ';
197
 
              break;
198
 
           case 't':
199
 
              *q++ = '\t';
200
 
              break;
201
 
           case 'n':
202
 
              *q++ = '\n';
203
 
              break;
204
 
           case 'r':
205
 
              *q++ = '\r';
206
 
              break;
207
 
           case '\\':
208
 
              *q++ = '\\';
209
 
              break;
210
 
           default:
211
 
             /* Invalid escape code */
212
 
             g_free (res);
213
 
             return NULL;
214
 
            }
215
 
          p++;
216
 
        }
217
 
      else
218
 
        *q++ = *p++;
219
 
    }
220
 
  *q = 0;
221
 
 
222
 
  return res;
223
 
}
224
 
 
225
 
static gchar *
226
 
escape_string (const gchar *str, gboolean escape_first_space)
227
 
{
228
 
  gchar *res;
229
 
  char *q;
230
 
  const gchar *p;
231
 
  const gchar *end;
232
 
 
233
 
  /* len + 1 is enough, because unescaping never makes the
234
 
   * string longer */
235
 
  res = g_new (gchar, strlen (str)*2 + 1);
236
 
  
237
 
  p = str;
238
 
  q = res;
239
 
  end = str + strlen (str);
240
 
 
241
 
  while (*p)
242
 
    {
243
 
      if (*p == ' ')
244
 
        {
245
 
          if (escape_first_space && p == str)
246
 
            {
247
 
              *q++ = '\\';
248
 
              *q++ = 's';
249
 
            }
250
 
          else
251
 
            *q++ = ' ';
252
 
        }
253
 
      else if (*p == '\\')
254
 
        {
255
 
          *q++ = '\\';
256
 
          *q++ = '\\';
257
 
        }
258
 
      else if (*p == '\t')
259
 
        {
260
 
          *q++ = '\\';
261
 
          *q++ = 't';
262
 
        }
263
 
      else if (*p == '\n')
264
 
        {
265
 
          *q++ = '\\';
266
 
          *q++ = 'n';
267
 
        }
268
 
      else if (*p == '\r')
269
 
        {
270
 
          *q++ = '\\';
271
 
          *q++ = 'r';
272
 
        }
273
 
      else
274
 
        *q++ = *p;
275
 
      p++;
276
 
    }
277
 
  *q = 0;
278
 
 
279
 
  return res;
280
 
}
281
 
 
282
 
 
283
 
static GnomeDesktopFileSection* 
284
 
new_section (GnomeDesktopFile       *df,
285
 
             const char             *name,
286
 
             GError                **error)
287
 
{
288
 
  int n;
289
 
  gboolean is_main = FALSE;
290
 
 
291
 
  if (name &&
292
 
      (strcmp (name, "Desktop Entry") == 0 ||
293
 
       strcmp (name, "KDE Desktop Entry") == 0))
294
 
    is_main = TRUE;
295
 
 
296
 
  if (is_main &&
297
 
      df->main_section >= 0)
298
 
    {
299
 
      g_set_error (error,
300
 
                   GNOME_DESKTOP_PARSE_ERROR,
301
 
                   GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX,                   
302
 
                   "Two [Desktop Entry] or [KDE Desktop Entry] sections seen");
303
 
 
304
 
      return NULL;
305
 
    }
306
 
  
307
 
  if (df->n_allocated_sections == df->n_sections)
308
 
    grow_sections (df);
309
 
 
310
 
  if (df->n_sections == 1 &&
311
 
      df->sections[0].section_name == 0 &&
312
 
      df->sections[0].n_lines == 0)
313
 
    {
314
 
      if (!name)
315
 
        g_warning ("non-initial NULL section\n");
316
 
      
317
 
      /* The initial section was empty. Piggyback on it. */
318
 
      df->sections[0].section_name = g_quark_from_string (name);
319
 
 
320
 
      if (is_main)
321
 
        df->main_section = 0;
322
 
      
323
 
      return &df->sections[0];
324
 
    }
325
 
  
326
 
  n = df->n_sections++;
327
 
 
328
 
  if (is_main)
329
 
    df->main_section = n;
330
 
  
331
 
  if (name)
332
 
    df->sections[n].section_name = g_quark_from_string (name);
333
 
  else
334
 
    df->sections[n].section_name = 0;
335
 
 
336
 
  df->sections[n].n_lines = 0;
337
 
  df->sections[n].lines = NULL;
338
 
  df->sections[n].n_allocated_lines = 0;
339
 
 
340
 
  grow_lines_in_section (&df->sections[n]);
341
 
 
342
 
  return &df->sections[n];
343
 
}
344
 
 
345
 
static GnomeDesktopFileSection* 
346
 
open_section (GnomeDesktopFileParser *parser,
347
 
              char                   *name,
348
 
              GError                **error)
349
 
{  
350
 
  GnomeDesktopFileSection *section;
351
 
 
352
 
  section = new_section (parser->df, name, error);
353
 
  if (section == NULL)
354
 
    return NULL;
355
 
  
356
 
  parser->current_section = parser->df->n_sections - 1;
357
 
  g_assert (&parser->df->sections[parser->current_section] == section);
358
 
  
359
 
  return section;
360
 
}
361
 
 
362
 
static GnomeDesktopFileLine*
363
 
new_line_in_section (GnomeDesktopFileSection *section)
364
 
{
365
 
  GnomeDesktopFileLine *line;
366
 
  
367
 
  if (section->n_allocated_lines == section->n_lines)
368
 
    grow_lines_in_section (section);
369
 
 
370
 
  line = &section->lines[section->n_lines++];
371
 
 
372
 
  memset (line, 0, sizeof (GnomeDesktopFileLine));
373
 
  
374
 
  return line;
375
 
}
376
 
 
377
 
static GnomeDesktopFileLine *
378
 
new_line (GnomeDesktopFileParser *parser)
379
 
{
380
 
  GnomeDesktopFileSection *section;
381
 
 
382
 
  section = &parser->df->sections[parser->current_section];
383
 
 
384
 
  return new_line_in_section (section);
385
 
}
386
 
 
387
 
static gboolean
388
 
is_blank_line (GnomeDesktopFileParser *parser)
389
 
{
390
 
  gchar *p;
391
 
 
392
 
  p = parser->line;
393
 
 
394
 
  while (*p && *p != '\n')
395
 
    {
396
 
      if (!g_ascii_isspace (*p))
397
 
        return FALSE;
398
 
 
399
 
      p++;
400
 
    }
401
 
  return TRUE;
402
 
}
403
 
 
404
 
static void
405
 
parse_comment_or_blank (GnomeDesktopFileParser *parser)
406
 
{
407
 
  GnomeDesktopFileLine *line;
408
 
  gchar *line_end;
409
 
 
410
 
  line_end = strchr (parser->line, '\n');
411
 
  if (line_end == NULL)
412
 
    line_end = parser->line + strlen (parser->line);
413
 
 
414
 
  line = new_line (parser);
415
 
  
416
 
  line->value = g_strndup (parser->line, line_end - parser->line);
417
 
 
418
 
    if (*line_end == '\n')
419
 
    ++line_end;
420
 
  else if (*line_end == '\0')
421
 
    line_end = NULL;
422
 
  
423
 
  parser->line = line_end;
424
 
 
425
 
  parser->line_nr++;
426
 
}
427
 
 
428
 
static gboolean
429
 
is_valid_section_name (const char *name)
430
 
{
431
 
  /* 5. Group names may contain all ASCII characters except for control characters and '[' and ']'. */
432
 
  
433
 
  while (*name)
434
 
    {
435
 
      if (!(g_ascii_isprint (*name) || *name == '\n' || *name == '\t'))
436
 
        return FALSE;
437
 
      
438
 
      name++;
439
 
    }
440
 
  
441
 
  return TRUE;
442
 
}
443
 
 
444
 
static gboolean
445
 
parse_section_start (GnomeDesktopFileParser *parser, GError **error)
446
 
{
447
 
  gchar *line_end;
448
 
  gchar *section_name;
449
 
 
450
 
  line_end = strchr (parser->line, '\n');
451
 
  if (line_end == NULL)
452
 
    line_end = parser->line + strlen (parser->line);
453
 
 
454
 
  if (line_end - parser->line <= 2 ||
455
 
      line_end[-1] != ']')
456
 
    {
457
 
      report_error (parser, "Invalid syntax for section header", GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
458
 
      parser_free (parser);
459
 
      return FALSE;
460
 
    }
461
 
 
462
 
  section_name = unescape_string (parser->line + 1, line_end - parser->line - 2);
463
 
 
464
 
  if (section_name == NULL)
465
 
    {
466
 
      report_error (parser, "Invalid escaping in section name", GNOME_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
467
 
      parser_free (parser);
468
 
      return FALSE;
469
 
    }
470
 
 
471
 
  if (!is_valid_section_name (section_name))
472
 
    {
473
 
      report_error (parser, "Invalid characters in section name", GNOME_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
474
 
      parser_free (parser);
475
 
      g_free (section_name);
476
 
      return FALSE;
477
 
    }
478
 
  
479
 
  if (open_section (parser, section_name, error) == NULL)
480
 
    {
481
 
      g_free (section_name);
482
 
      return FALSE;
483
 
    }
484
 
 
485
 
  if (*line_end == '\n')
486
 
    ++line_end;
487
 
  else if (*line_end == '\0')
488
 
    line_end = NULL;
489
 
  
490
 
  parser->line = line_end;
491
 
  
492
 
  parser->line_nr++;
493
 
 
494
 
  g_free (section_name);
495
 
  
496
 
  return TRUE;
497
 
}
498
 
 
499
 
static gboolean
500
 
parse_key_value (GnomeDesktopFileParser *parser, GError **error)
501
 
{
502
 
  GnomeDesktopFileLine *line;
503
 
  gchar *line_end;
504
 
  gchar *key_start;
505
 
  gchar *key_end;
506
 
  gchar *locale_start = NULL;
507
 
  gchar *locale_end = NULL;
508
 
  gchar *value_start;
509
 
  gchar *value;
510
 
  gchar *p;
511
 
  char *key;
512
 
  
513
 
  line_end = strchr (parser->line, '\n');
514
 
  if (line_end == NULL)
515
 
    line_end = parser->line + strlen (parser->line);
516
 
 
517
 
  p = parser->line;
518
 
  key_start = p;
519
 
  while (p < line_end &&
520
 
         (valid[(guchar)*p] & VALID_KEY_CHAR)) 
521
 
    p++;
522
 
  key_end = p;
523
 
 
524
 
  if (key_start == key_end)
525
 
    {
526
 
      report_error (parser, "Empty key name", GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
527
 
      parser_free (parser);
528
 
      return FALSE;
529
 
    }
530
 
 
531
 
  if (p < line_end && *p == '[')
532
 
    {
533
 
      p++;
534
 
      locale_start = p;
535
 
      while (p < line_end &&
536
 
             (valid[(guchar)*p] & VALID_LOCALE_CHAR)) 
537
 
        p++;
538
 
      locale_end = p;
539
 
 
540
 
      if (p == line_end)
541
 
        {
542
 
          report_error (parser, "Unterminated locale specification in key", GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
543
 
          parser_free (parser);
544
 
          return FALSE;
545
 
        }
546
 
      
547
 
      if (*p != ']')
548
 
        {
549
 
          report_error (parser, "Invalid characters in locale name", GNOME_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
550
 
          parser_free (parser);
551
 
          return FALSE;
552
 
        }
553
 
      
554
 
      if (locale_start == locale_end)
555
 
        {
556
 
          report_error (parser, "Empty locale name", GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
557
 
          parser_free (parser);
558
 
          return FALSE;
559
 
        }
560
 
      p++;
561
 
    }
562
 
  
563
 
  /* Skip space before '=' */
564
 
  while (p < line_end && *p == ' ')
565
 
    p++;
566
 
 
567
 
  if (p < line_end && *p != '=')
568
 
    {
569
 
      report_error (parser, "Invalid characters in key name", GNOME_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
570
 
      parser_free (parser);
571
 
      return FALSE;
572
 
    }
573
 
 
574
 
  if (p == line_end)
575
 
    {
576
 
      report_error (parser, "No '=' in key/value pair", GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
577
 
      parser_free (parser);
578
 
      return FALSE;
579
 
    }
580
 
 
581
 
  /* Skip the '=' */
582
 
  p++;
583
 
 
584
 
  /* Skip space after '=' */
585
 
  while (p < line_end && *p == ' ')
586
 
    p++;
587
 
 
588
 
  value_start = p;
589
 
 
590
 
  value = unescape_string (value_start, line_end - value_start);
591
 
  if (value == NULL)
592
 
    {
593
 
      report_error (parser, "Invalid escaping in value", GNOME_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
594
 
      parser_free (parser);
595
 
      return FALSE;
596
 
    }
597
 
 
598
 
  line = new_line (parser);
599
 
  key = g_strndup (key_start, key_end - key_start);
600
 
  line->key = g_quark_try_string (key);
601
 
  if (line->key == 0)
602
 
    line->key = g_quark_from_static_string (key);
603
 
  else
604
 
    g_free (key);
605
 
  if (locale_start)
606
 
    line->locale = g_strndup (locale_start, locale_end - locale_start);
607
 
  line->value = value;
608
 
 
609
 
  if (*line_end == '\n')
610
 
    ++line_end;
611
 
  else if (*line_end == '\0')
612
 
    line_end = NULL;
613
 
  
614
 
  parser->line = line_end;
615
 
  parser->line_nr++;
616
 
  
617
 
  return TRUE;
618
 
}
619
 
 
620
 
 
621
 
static void
622
 
report_error (GnomeDesktopFileParser *parser,
623
 
              char                   *message,
624
 
              GnomeDesktopParseError  error_code,
625
 
              GError                **error)
626
 
{
627
 
  GnomeDesktopFileSection *section;
628
 
  const gchar *section_name = NULL;
629
 
 
630
 
  section = &parser->df->sections[parser->current_section];
631
 
 
632
 
  if (section->section_name)
633
 
    section_name = g_quark_to_string (section->section_name);
634
 
  
635
 
  if (error)
636
 
    {
637
 
      if (section_name)
638
 
        *error = g_error_new (GNOME_DESKTOP_PARSE_ERROR,
639
 
                              error_code,
640
 
                              "Error in section %s at line %d: %s", section_name, parser->line_nr, message);
641
 
      else
642
 
        *error = g_error_new (GNOME_DESKTOP_PARSE_ERROR,
643
 
                              error_code,
644
 
                              "Error at line %d: %s", parser->line_nr, message);
645
 
    }
646
 
}
647
 
 
648
 
 
649
 
GnomeDesktopFile *
650
 
gnome_desktop_file_new_from_string (char                       *data,
651
 
                                    GError                    **error)
652
 
{
653
 
  GnomeDesktopFileParser parser;
654
 
  GnomeDesktopFileLine *line;
655
 
 
656
 
  parser.df = g_new0 (GnomeDesktopFile, 1);
657
 
  parser.df->main_section = -1;
658
 
  parser.current_section = -1;
659
 
 
660
 
  parser.line_nr = 1;
661
 
 
662
 
  parser.line = data;
663
 
 
664
 
  /* Put any initial comments in a NULL segment */
665
 
  open_section (&parser, NULL, NULL);
666
 
  
667
 
  while (parser.line && *parser.line)
668
 
    {
669
 
      if (*parser.line == '[') {
670
 
        if (!parse_section_start (&parser, error))
671
 
          return NULL;
672
 
      } else if (is_blank_line (&parser) ||
673
 
                 *parser.line == '#')
674
 
        parse_comment_or_blank (&parser);
675
 
      else
676
 
        {
677
 
          if (!parse_key_value (&parser, error))
678
 
            return NULL;
679
 
        }
680
 
    }
681
 
 
682
 
  if (parser.df->main_section >= 0)
683
 
    {
684
 
      line = lookup_line (parser.df,
685
 
                          &parser.df->sections[parser.df->main_section],
686
 
                          "Encoding", NULL);
687
 
      if (line)
688
 
        {
689
 
          if (strcmp (line->value, "UTF-8") == 0)
690
 
            parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_UTF8;
691
 
          else if (strcmp (line->value, "Legacy-Mixed") == 0)
692
 
            parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_LEGACY;
693
 
          else
694
 
            parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_UNKNOWN;
695
 
        }
696
 
      else
697
 
        {
698
 
          /* No encoding specified. We have to guess
699
 
           * If the whole file validates as UTF-8 it's probably UTF-8.
700
 
           * Otherwise we guess it's a Legacy-Mixed
701
 
           */
702
 
          if (g_utf8_validate (data, -1, NULL))
703
 
            parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_UTF8;
704
 
          else
705
 
            parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_LEGACY;
706
 
        }
707
 
        
708
 
    }
709
 
  else
710
 
    parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_UNKNOWN;
711
 
  
712
 
  return parser.df;
713
 
}
714
 
 
715
 
GnomeDesktopFileEncoding
716
 
gnome_desktop_file_get_encoding (GnomeDesktopFile *df)
717
 
{
718
 
  return df->encoding;
719
 
}
720
 
 
721
 
char *
722
 
gnome_desktop_file_to_string (GnomeDesktopFile *df)
723
 
{
724
 
  GnomeDesktopFileSection *section;
725
 
  GnomeDesktopFileLine *line;
726
 
  GString *str;
727
 
  char *s;
728
 
  int i, j;
729
 
  
730
 
  str = g_string_sized_new (800);
731
 
 
732
 
  for (i = 0; i < df->n_sections; i ++)
733
 
    {
734
 
      section = &df->sections[i];
735
 
 
736
 
      if (section->section_name)
737
 
        {
738
 
          g_string_append_c (str, '[');
739
 
          s = escape_string (g_quark_to_string (section->section_name), FALSE);
740
 
          g_string_append (str, s);
741
 
          g_free (s);
742
 
          g_string_append (str, "]\n");
743
 
        }
744
 
      
745
 
      for (j = 0; j < section->n_lines; j++)
746
 
        {
747
 
          line = &section->lines[j];
748
 
          
749
 
          if (line->key == 0)
750
 
            {
751
 
              g_string_append (str, line->value);
752
 
              g_string_append_c (str, '\n');
753
 
            }
754
 
          else
755
 
            {
756
 
              g_string_append (str, g_quark_to_string (line->key));
757
 
              if (line->locale)
758
 
                {
759
 
                  g_string_append_c (str, '[');
760
 
                  g_string_append (str, line->locale);
761
 
                  g_string_append_c (str, ']');
762
 
                }
763
 
              g_string_append_c (str, '=');
764
 
              s = escape_string (line->value, TRUE);
765
 
              g_string_append (str, s);
766
 
              g_free (s);
767
 
              g_string_append_c (str, '\n');
768
 
            }
769
 
        }
770
 
    }
771
 
  
772
 
  return g_string_free (str, FALSE);
773
 
}
774
 
 
775
 
static GnomeDesktopFileSection *
776
 
lookup_section (GnomeDesktopFile  *df,
777
 
                const char        *section_name)
778
 
{
779
 
  GnomeDesktopFileSection *section;
780
 
  GQuark section_quark;
781
 
  int i;
782
 
  
783
 
  if (section_name == NULL)
784
 
    {
785
 
      if (df->main_section < 0)
786
 
        return NULL;
787
 
      else
788
 
        return &df->sections[df->main_section];
789
 
    }
790
 
  
791
 
  section_quark = g_quark_try_string (section_name);
792
 
  if (section_quark == 0)
793
 
    return NULL;
794
 
  
795
 
  for (i = 0; i < df->n_sections; i ++)
796
 
    {
797
 
      section = &df->sections[i];
798
 
 
799
 
      if (section->section_name == section_quark)
800
 
        return section;
801
 
    }
802
 
  return NULL;
803
 
}
804
 
 
805
 
static GnomeDesktopFileLine *
806
 
lookup_line (GnomeDesktopFile        *df,
807
 
             GnomeDesktopFileSection *section,
808
 
             const char              *keyname,
809
 
             const char              *locale)
810
 
{
811
 
  GnomeDesktopFileLine *line;
812
 
  GQuark key_quark;
813
 
  int i;
814
 
 
815
 
  key_quark = g_quark_try_string (keyname);
816
 
  if (key_quark == 0)
817
 
    return NULL;
818
 
  
819
 
  for (i = 0; i < section->n_lines; i++)
820
 
    {
821
 
      line = &section->lines[i];
822
 
      
823
 
      if (line->key == key_quark &&
824
 
          ((locale == NULL && line->locale == NULL) ||
825
 
           (locale != NULL && line->locale != NULL && strcmp (locale, line->locale) == 0)))
826
 
        return line;
827
 
    }
828
 
  
829
 
  return NULL;
830
 
}
831
 
 
832
 
gboolean
833
 
gnome_desktop_file_get_raw (GnomeDesktopFile  *df,
834
 
                            const char        *section_name,
835
 
                            const char        *keyname,
836
 
                            const char        *locale,
837
 
                            const char       **val)
838
 
{
839
 
  GnomeDesktopFileSection *section;
840
 
  GnomeDesktopFileLine *line;
841
 
 
842
 
  *val = NULL;
843
 
 
844
 
  section = lookup_section (df, section_name);
845
 
  if (!section)
846
 
    return FALSE;
847
 
 
848
 
  line = lookup_line (df,
849
 
                      section,
850
 
                      keyname,
851
 
                      locale);
852
 
 
853
 
  if (!line)
854
 
    return FALSE;
855
 
  
856
 
  *val = line->value;
857
 
  
858
 
  return TRUE;
859
 
}
860
 
 
861
 
void
862
 
gnome_desktop_file_foreach_section (GnomeDesktopFile            *df,
863
 
                                    GnomeDesktopFileSectionFunc  func,
864
 
                                    gpointer                     user_data)
865
 
{
866
 
  GnomeDesktopFileSection *section;
867
 
  int i;
868
 
 
869
 
  for (i = 0; i < df->n_sections; i ++)
870
 
    {
871
 
      section = &df->sections[i];
872
 
 
873
 
      (*func) (df, g_quark_to_string (section->section_name),  user_data);
874
 
    }
875
 
  return;
876
 
}
877
 
 
878
 
void
879
 
gnome_desktop_file_foreach_key (GnomeDesktopFile            *df,
880
 
                                const char                  *section_name,
881
 
                                gboolean                     include_localized,
882
 
                                GnomeDesktopFileLineFunc     func,
883
 
                                gpointer                     user_data)
884
 
{
885
 
  GnomeDesktopFileSection *section;
886
 
  GnomeDesktopFileLine *line;
887
 
  int i;
888
 
  
889
 
  section = lookup_section (df, section_name);
890
 
  if (!section)
891
 
    return;
892
 
  
893
 
  for (i = 0; i < section->n_lines; i++)
894
 
    {
895
 
      line = &section->lines[i];
896
 
 
897
 
      (*func) (df, g_quark_to_string (line->key), line->locale, line->value, user_data);
898
 
    }
899
 
  
900
 
  return;
901
 
}
902
 
 
903
 
void
904
 
gnome_desktop_file_rename_section  (GnomeDesktopFile            *df,
905
 
                                    const char                  *old_name,
906
 
                                    const char                  *new_name)
907
 
{
908
 
  GnomeDesktopFileSection *section;  
909
 
 
910
 
  g_return_if_fail (new_name != NULL);
911
 
  
912
 
  if (old_name == NULL &&
913
 
      df->main_section < 0)
914
 
    old_name = "Desktop Entry";
915
 
 
916
 
  section = lookup_section (df, old_name);
917
 
  if (section == NULL)
918
 
    return;
919
 
 
920
 
  section->section_name = g_quark_from_string (new_name);
921
 
}
922
 
 
923
 
gboolean
924
 
gnome_desktop_file_has_section (GnomeDesktopFile *df,
925
 
                                const char       *name)
926
 
{
927
 
  GnomeDesktopFileSection *section;  
928
 
 
929
 
  g_return_val_if_fail (name != NULL, FALSE);  
930
 
 
931
 
  section = lookup_section (df, name);
932
 
 
933
 
  return section != NULL;
934
 
}
935
 
 
936
 
GnomeDesktopFile*
937
 
gnome_desktop_file_load (const char    *filename,
938
 
                         GError       **error)
939
 
{
940
 
  char *contents;
941
 
  GnomeDesktopFile *df;
942
 
  
943
 
  if (!g_file_get_contents (filename, &contents,
944
 
                            NULL, error))
945
 
    return NULL;
946
 
 
947
 
  df = gnome_desktop_file_new_from_string (contents, error);
948
 
 
949
 
  g_free (contents);
950
 
 
951
 
  return df;
952
 
}
953
 
 
954
 
 
955
 
gboolean
956
 
gnome_desktop_file_save (GnomeDesktopFile *df,
957
 
                         const char       *path,
958
 
                         int               mode,
959
 
                         GError          **error)
960
 
{
961
 
  char *str;
962
 
  FILE *f;
963
 
  int fd;
964
 
  char *tmp_filename;
965
 
 
966
 
  tmp_filename = g_strconcat (path, ".new-tmp", NULL);
967
 
  
968
 
  f = fopen (tmp_filename, "w");
969
 
  if (f == NULL)
970
 
    {
971
 
      g_set_error (error, 
972
 
                   G_FILE_ERROR,
973
 
                   g_file_error_from_errno (errno),
974
 
                   _("Failed to open \"%s\": %s"),
975
 
                   tmp_filename, g_strerror (errno));
976
 
      g_free (tmp_filename);
977
 
      return FALSE;
978
 
    }
979
 
  
980
 
  fd = fileno (f);
981
 
  
982
 
  if (fchmod (fd, mode) < 0)
983
 
    {
984
 
      g_set_error (error, G_FILE_ERROR,
985
 
                   g_file_error_from_errno (errno),
986
 
                   _("Failed to set permissions %o on \"%s\": %s"),
987
 
                   mode, tmp_filename, g_strerror (errno));
988
 
 
989
 
      fclose (f);
990
 
      unlink (tmp_filename);
991
 
      g_free (tmp_filename);
992
 
      
993
 
      return FALSE;
994
 
    }
995
 
 
996
 
  str = gnome_desktop_file_to_string (df);
997
 
  
998
 
  if (fputs (str, f) < 0)
999
 
    {
1000
 
      g_set_error (error,
1001
 
                   G_FILE_ERROR,
1002
 
                   g_file_error_from_errno (errno),
1003
 
                   _("Failed to write to \"%s\": %s"),
1004
 
                   tmp_filename, g_strerror (errno));
1005
 
 
1006
 
      fclose (f);
1007
 
      unlink (tmp_filename);
1008
 
      g_free (str);
1009
 
      g_free (tmp_filename);
1010
 
      
1011
 
      return FALSE;
1012
 
    }
1013
 
 
1014
 
  g_free (str);
1015
 
  
1016
 
  if (fclose (f) < 0)
1017
 
    {
1018
 
      g_set_error (error,
1019
 
                   G_FILE_ERROR,
1020
 
                   g_file_error_from_errno (errno),
1021
 
                   _("Failed to close \"%s\": %s"),
1022
 
                   tmp_filename, g_strerror (errno));
1023
 
 
1024
 
      unlink (tmp_filename);
1025
 
      g_free (tmp_filename);
1026
 
      
1027
 
      return FALSE;
1028
 
    }
1029
 
 
1030
 
  if (rename (tmp_filename, path) < 0)
1031
 
    {
1032
 
      g_set_error (error,
1033
 
                   G_FILE_ERROR,
1034
 
                   g_file_error_from_errno (errno),
1035
 
                   _("Failed to rename \"%s\" to \"%s\": %s"),
1036
 
                   tmp_filename, path, g_strerror (errno));
1037
 
      unlink (tmp_filename);
1038
 
      g_free (tmp_filename);
1039
 
 
1040
 
      return FALSE;
1041
 
    }
1042
 
 
1043
 
  g_free (tmp_filename);
1044
 
  
1045
 
  return TRUE;
1046
 
}
1047
 
 
1048
 
/* Mask for components of locale spec. The ordering here is from
1049
 
 * least significant to most significant
1050
 
 */
1051
 
enum
1052
 
{
1053
 
  COMPONENT_CODESET =   1 << 0,
1054
 
  COMPONENT_TERRITORY = 1 << 1,
1055
 
  COMPONENT_MODIFIER =  1 << 2
1056
 
};
1057
 
 
1058
 
/* Break an X/Open style locale specification into components
1059
 
 */
1060
 
static guint
1061
 
explode_locale (const gchar *locale,
1062
 
                gchar      **language, 
1063
 
                gchar      **territory, 
1064
 
                gchar      **codeset, 
1065
 
                gchar      **modifier)
1066
 
{
1067
 
  const gchar *uscore_pos;
1068
 
  const gchar *at_pos;
1069
 
  const gchar *dot_pos;
1070
 
 
1071
 
  guint mask = 0;
1072
 
 
1073
 
  uscore_pos = strchr (locale, '_');
1074
 
  dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.');
1075
 
  at_pos = strchr (dot_pos ? dot_pos : (uscore_pos ? uscore_pos : locale), '@');
1076
 
 
1077
 
  if (at_pos)
1078
 
    {
1079
 
      mask |= COMPONENT_MODIFIER;
1080
 
      *modifier = g_strdup (at_pos);
1081
 
    }
1082
 
  else
1083
 
    at_pos = locale + strlen (locale);
1084
 
 
1085
 
  if (dot_pos)
1086
 
    {
1087
 
      mask |= COMPONENT_CODESET;
1088
 
      *codeset = g_new (gchar, 1 + at_pos - dot_pos);
1089
 
      strncpy (*codeset, dot_pos, at_pos - dot_pos);
1090
 
      (*codeset)[at_pos - dot_pos] = '\0';
1091
 
    }
1092
 
  else
1093
 
    dot_pos = at_pos;
1094
 
 
1095
 
  if (uscore_pos)
1096
 
    {
1097
 
      mask |= COMPONENT_TERRITORY;
1098
 
      *territory = g_new (gchar, 1 + dot_pos - uscore_pos);
1099
 
      strncpy (*territory, uscore_pos, dot_pos - uscore_pos);
1100
 
      (*territory)[dot_pos - uscore_pos] = '\0';
1101
 
    }
1102
 
  else
1103
 
    uscore_pos = dot_pos;
1104
 
 
1105
 
  *language = g_new (gchar, 1 + uscore_pos - locale);
1106
 
  strncpy (*language, locale, uscore_pos - locale);
1107
 
  (*language)[uscore_pos - locale] = '\0';
1108
 
 
1109
 
  return mask;
1110
 
}
1111
 
 
1112
 
gboolean
1113
 
gnome_desktop_file_get_locale_string (GnomeDesktopFile  *df,
1114
 
                                      const char        *section,
1115
 
                                      const char        *keyname,
1116
 
                                      char             **val)
1117
 
{
1118
 
  const char *raw;
1119
 
  char *lang, *territory, *codeset, *modifier;
1120
 
  const char *locale;
1121
 
  char *with_territory;
1122
 
  char *used_locale;
1123
 
  GError *error;
1124
 
  
1125
 
  *val = NULL;
1126
 
  
1127
 
  lang = NULL;
1128
 
  territory = NULL;
1129
 
  codeset = NULL;
1130
 
  modifier = NULL;
1131
 
  used_locale = NULL;
1132
 
  
1133
 
  locale = setlocale (LC_MESSAGES, NULL);
1134
 
  if (locale != NULL)
1135
 
    explode_locale (locale, &lang, &territory, &codeset, &modifier);
1136
 
 
1137
 
  if (territory)
1138
 
    with_territory = g_strconcat (lang, "_", territory, NULL);
1139
 
  else
1140
 
    with_territory = NULL;
1141
 
 
1142
 
  if (with_territory == NULL ||
1143
 
      !gnome_desktop_file_get_raw (df, section, keyname, with_territory, &raw))
1144
 
    {
1145
 
      if (lang == NULL ||
1146
 
          !gnome_desktop_file_get_raw (df, section, keyname, lang, &raw))
1147
 
        {
1148
 
          gnome_desktop_file_get_raw (df, section, keyname, NULL, &raw);
1149
 
        }
1150
 
      else
1151
 
        {
1152
 
          used_locale = lang;
1153
 
          lang = NULL;
1154
 
        }
1155
 
    }
1156
 
  else
1157
 
    {
1158
 
      used_locale = with_territory;
1159
 
      with_territory = NULL;
1160
 
    }
1161
 
 
1162
 
  g_free (lang);
1163
 
  g_free (territory);
1164
 
  g_free (codeset);
1165
 
  g_free (modifier);
1166
 
  g_free (with_territory);
1167
 
 
1168
 
  if (raw == NULL)
1169
 
    {
1170
 
      g_free (used_locale);
1171
 
      return FALSE;
1172
 
    }
1173
 
  
1174
 
  if (gnome_desktop_file_get_encoding (df) == GNOME_DESKTOP_FILE_ENCODING_UTF8)
1175
 
    {
1176
 
      g_free (used_locale);
1177
 
      *val = g_strdup (raw);
1178
 
      return TRUE;      
1179
 
    }
1180
 
  else if (gnome_desktop_file_get_encoding (df) == GNOME_DESKTOP_FILE_ENCODING_LEGACY)
1181
 
    {
1182
 
      if (used_locale)
1183
 
        {
1184
 
          const char *encoding;
1185
 
          
1186
 
          encoding = desktop_file_get_encoding_for_locale (used_locale);
1187
 
          
1188
 
          if (encoding)
1189
 
            {
1190
 
              char *res;
1191
 
              
1192
 
              g_free (used_locale);
1193
 
              error = NULL;
1194
 
              res = g_convert (raw, -1,            
1195
 
                               "UTF-8",
1196
 
                               encoding,
1197
 
                               NULL,     
1198
 
                               NULL,  
1199
 
                               &error);
1200
 
 
1201
 
              if (res == NULL)
1202
 
                {
1203
 
                  g_printerr ("Error converting from UTF-8 to %s for key %s: %s\n",
1204
 
                              encoding, keyname, error->message);
1205
 
                  g_error_free (error);
1206
 
                }
1207
 
 
1208
 
              *val = res;
1209
 
 
1210
 
              return *val != NULL;
1211
 
            }
1212
 
          else
1213
 
            {
1214
 
              g_printerr ("Don't know encoding for desktop file field %s with locale \"%s\"\n",
1215
 
                          keyname, used_locale);
1216
 
              g_free (used_locale);
1217
 
              return FALSE;
1218
 
            }
1219
 
        }
1220
 
      else
1221
 
        {
1222
 
          /* this is just ASCII, hopefully OK, though it's really a bit
1223
 
           * broken to return it
1224
 
           */
1225
 
          *val = g_strdup (raw);
1226
 
          return TRUE;
1227
 
        }
1228
 
    }
1229
 
  else
1230
 
    {
1231
 
      g_printerr ("Desktop file doesn't have its encoding marked, can't parse it.\n");
1232
 
      g_free (used_locale);
1233
 
      return FALSE;
1234
 
    }
1235
 
}
1236
 
 
1237
 
gboolean
1238
 
gnome_desktop_file_get_string (GnomeDesktopFile   *df,
1239
 
                               const char         *section,
1240
 
                               const char         *keyname,
1241
 
                               char              **val)
1242
 
{
1243
 
  const char *raw;
1244
 
  
1245
 
  *val = NULL;
1246
 
  
1247
 
  if (!gnome_desktop_file_get_raw (df, section, keyname, NULL, &raw))
1248
 
    return FALSE;
1249
 
 
1250
 
  *val = g_strdup (raw);
1251
 
  
1252
 
  return TRUE;
1253
 
}
1254
 
 
1255
 
gboolean
1256
 
gnome_desktop_file_get_strings (GnomeDesktopFile   *df,
1257
 
                                const char         *section,
1258
 
                                const char         *keyname,
1259
 
                                const char         *locale,
1260
 
                                char             ***vals,
1261
 
                                int                *len)
1262
 
{
1263
 
  const char *raw;
1264
 
  char **retval;
1265
 
  int i;
1266
 
  
1267
 
  if (vals)
1268
 
    *vals = NULL;
1269
 
  if (len)
1270
 
    *len = 0;
1271
 
  
1272
 
  if (!gnome_desktop_file_get_raw (df, section, keyname, locale, &raw))
1273
 
    return FALSE;
1274
 
 
1275
 
  retval = g_strsplit (raw, ";", G_MAXINT);
1276
 
 
1277
 
  i = 0;
1278
 
  while (retval[i])
1279
 
    ++i;
1280
 
 
1281
 
  /* Drop the empty string g_strsplit leaves in the vector since
1282
 
   * our list of strings ends in ";"
1283
 
   */
1284
 
  --i;
1285
 
  g_free (retval[i]);
1286
 
  retval[i] = NULL;
1287
 
  
1288
 
  if (vals)
1289
 
    *vals = retval;
1290
 
  else
1291
 
    g_strfreev (retval);
1292
 
 
1293
 
  if (len)
1294
 
    *len = i;
1295
 
 
1296
 
  return TRUE;
1297
 
}
1298
 
 
1299
 
void
1300
 
gnome_desktop_file_set_raw (GnomeDesktopFile  *df,
1301
 
                            const char        *section_name,
1302
 
                            const char        *keyname,
1303
 
                            const char        *locale,
1304
 
                            const char        *value)
1305
 
{
1306
 
  GnomeDesktopFileSection *section;
1307
 
  GnomeDesktopFileLine *line;
1308
 
  
1309
 
 
1310
 
  if (section_name == NULL &&
1311
 
      df->main_section < 0)
1312
 
    section_name = "Desktop Entry";
1313
 
 
1314
 
  section = lookup_section (df, section_name);
1315
 
  if (section == NULL)
1316
 
    {
1317
 
      section = new_section (df, section_name, NULL);
1318
 
      g_assert (section);
1319
 
    }
1320
 
 
1321
 
  line = lookup_line (df,
1322
 
                      section,
1323
 
                      keyname,
1324
 
                      locale);
1325
 
 
1326
 
  if (line == NULL)
1327
 
    line = new_line_in_section (section);
1328
 
 
1329
 
  line->key = g_quark_from_string (keyname);
1330
 
  g_free (line->value);
1331
 
  g_free (line->locale);
1332
 
  line->value = g_strdup (value);
1333
 
  line->locale = g_strdup (locale);
1334
 
}
1335
 
 
1336
 
void
1337
 
gnome_desktop_file_set_strings (GnomeDesktopFile  *df,
1338
 
                                const char        *section_name,
1339
 
                                const char        *keyname,
1340
 
                                const char        *locale,
1341
 
                                const char       **value)
1342
 
{
1343
 
  char *str;
1344
 
  char *tmp;  
1345
 
  
1346
 
  tmp = g_strjoinv (";", (char**)value);
1347
 
  str = g_strconcat (tmp, ";", NULL);
1348
 
  g_free (tmp);
1349
 
  gnome_desktop_file_set_raw (df, section_name, keyname, locale, str);
1350
 
  g_free (str);
1351
 
}
1352
 
 
1353
 
 
1354
 
void
1355
 
gnome_desktop_file_merge_string_into_list (GnomeDesktopFile *df,
1356
 
                                           const char       *section,
1357
 
                                           const char       *keyname,
1358
 
                                           const char       *locale,
1359
 
                                           const char       *value)
1360
 
{
1361
 
  char **values;
1362
 
  int n_values;
1363
 
  const char *raw;
1364
 
  
1365
 
  if (gnome_desktop_file_get_strings (df, section, keyname, locale,
1366
 
                                      &values, &n_values))
1367
 
    {
1368
 
      /* Look for a duplicate */
1369
 
      int i;
1370
 
      gboolean found;
1371
 
 
1372
 
      found = FALSE;
1373
 
      
1374
 
      i = 0;
1375
 
      while (i < n_values)
1376
 
        {
1377
 
          if (strcmp (values[i], value) == 0)
1378
 
            {
1379
 
              found = TRUE;
1380
 
              break;
1381
 
            }
1382
 
 
1383
 
          ++i;
1384
 
        }
1385
 
 
1386
 
      g_strfreev (values);
1387
 
      
1388
 
      if (found)
1389
 
        return; /* nothing to do */
1390
 
    }
1391
 
 
1392
 
  gnome_desktop_file_get_raw (df, section, keyname, locale, &raw);
1393
 
  
1394
 
  {
1395
 
    /* Append to current list */
1396
 
    char *str;
1397
 
    if (raw)
1398
 
      {
1399
 
        int len;
1400
 
        len = strlen (raw);
1401
 
        /* handle broken file with no ';' after the list */
1402
 
        if (len > 0 && raw[len-1] != ';')
1403
 
          str = g_strconcat (raw, ";", value, ";", NULL);
1404
 
        else
1405
 
          str = g_strconcat (raw, value, ";", NULL);
1406
 
      }
1407
 
    else
1408
 
      str = g_strconcat (value, ";", NULL);
1409
 
    
1410
 
    gnome_desktop_file_set_raw (df, section, keyname, locale, str);
1411
 
    g_free (str);
1412
 
  }
1413
 
}
1414
 
 
1415
 
void
1416
 
gnome_desktop_file_remove_string_from_list (GnomeDesktopFile *df,
1417
 
                                            const char        *section,
1418
 
                                            const char        *keyname,
1419
 
                                            const char        *locale,
1420
 
                                            const char        *value)
1421
 
{
1422
 
  char **values;
1423
 
  int n_values;
1424
 
  
1425
 
  if (gnome_desktop_file_get_strings (df, section, keyname, locale,
1426
 
                                      &values, &n_values))
1427
 
    {
1428
 
      int i, j;
1429
 
      int n_found;
1430
 
 
1431
 
      n_found = 0;
1432
 
      
1433
 
      i = 0;
1434
 
      while (i < n_values)
1435
 
        {
1436
 
          if (values[i] == NULL)
1437
 
            continue;
1438
 
 
1439
 
          if (strcmp (values[i], value) == 0)
1440
 
            {
1441
 
              g_free (values[i]);
1442
 
              values[i] = NULL;
1443
 
              ++n_found;
1444
 
            }
1445
 
 
1446
 
          ++i;
1447
 
        }
1448
 
 
1449
 
      i = j = 0;
1450
 
      while (i < n_values)
1451
 
        {
1452
 
          j = MAX (i + 1, j);
1453
 
          if (values[i] == NULL)
1454
 
            {
1455
 
              while (j < n_values && values[j] == NULL)
1456
 
                j++;
1457
 
 
1458
 
              if (j < n_values)
1459
 
                {
1460
 
                  values[i] = values[j];
1461
 
                  values[j] = NULL;
1462
 
                }
1463
 
            }
1464
 
 
1465
 
          ++i;
1466
 
        }
1467
 
 
1468
 
      if (n_found == n_values)
1469
 
        gnome_desktop_file_unset (df, section, keyname);
1470
 
      else if (n_found > 0)
1471
 
        gnome_desktop_file_set_strings (df, section, keyname, locale,
1472
 
                                        (const char**) values);
1473
 
      
1474
 
      g_strfreev (values);
1475
 
    }
1476
 
}
1477
 
 
1478
 
#define N_LANG 30
1479
 
 
1480
 
/* #define VERIFY_CANONICAL_ENCODING_NAME */
1481
 
 
1482
 
struct {
1483
 
  const char *encoding;
1484
 
  const char *langs[N_LANG];
1485
 
} known_encodings[] = {
1486
 
  {"ARMSCII-8", {"hy"}},
1487
 
  {"BIG5", {"zh_TW"}},
1488
 
  {"CP1251", {"be", "bg"}},
1489
 
  {"EUC-CN", {"zh_CN"}},
1490
 
  {"EUC-JP", {"ja"}},
1491
 
  {"EUC-KR", {"ko"}},
1492
 
  {"GEORGIAN-ACADEMY", {}},
1493
 
  {"GEORGIAN-PS", {"ka"}},
1494
 
  {"ISO-8859-1", {"br", "ca", "da", "de", "en", "es", "eu", "fi", "fr", "gl", "it", "nl", "no", "pt", "sv", "wa" }},
1495
 
  {"ISO-8859-2", {"cs", "hr", "hu", "pl", "ro", "sk", "sl", "sq", "sr"}},
1496
 
  {"ISO-8859-3", {"eo"}},
1497
 
  {"ISO-8859-5", {"mk", "sp"}},
1498
 
  {"ISO-8859-7", {"el"}},
1499
 
  {"ISO-8859-9", {"tr"}},
1500
 
  {"ISO-8859-13", {"lv", "lt", "mi"}},
1501
 
  {"ISO-8859-14", {"ga", "cy"}},
1502
 
  {"ISO-8859-15", {"et"}},
1503
 
  {"KOI8-R", {"ru"}},
1504
 
  {"KOI8-U", {"uk"}},
1505
 
  {"TCVN-5712", {"vi"}},
1506
 
  {"TIS-620", {"th"}},
1507
 
  {"VISCII", {}},
1508
 
};
1509
 
 
1510
 
struct {
1511
 
  const char *alias;
1512
 
  const char *value;
1513
 
} enc_aliases[] = {
1514
 
  {"GB2312", "EUC-CN"},
1515
 
  {"TCVN", "TCVN-5712" }
1516
 
};
1517
 
 
1518
 
static gboolean
1519
 
aliases_equal (const char *enc1, const char *enc2)
1520
 
{
1521
 
  while (*enc1 && *enc2)
1522
 
    {
1523
 
      while (*enc1 == '-' ||
1524
 
             *enc1 == '.' ||
1525
 
             *enc1 == '_')
1526
 
        enc1++;
1527
 
      
1528
 
      while (*enc2 == '-' ||
1529
 
             *enc2 == '.' ||
1530
 
             *enc2 == '_')
1531
 
        enc2++;
1532
 
 
1533
 
      if (g_ascii_tolower (*enc1) != g_ascii_tolower (*enc2))
1534
 
        return FALSE;
1535
 
      enc1++;
1536
 
      enc2++;
1537
 
    }
1538
 
 
1539
 
  while (*enc1 == '-' ||
1540
 
         *enc1 == '.' ||
1541
 
         *enc1 == '_')
1542
 
    enc1++;
1543
 
  
1544
 
  while (*enc2 == '-' ||
1545
 
         *enc2 == '.' ||
1546
 
         *enc2 == '_')
1547
 
    enc2++;
1548
 
 
1549
 
  if (*enc1 || *enc2)
1550
 
    return FALSE;
1551
 
  
1552
 
  return TRUE;
1553
 
}
1554
 
 
1555
 
static const char *
1556
 
get_canonical_encoding (const char *encoding)
1557
 
{
1558
 
  int i;
1559
 
  for (i = 0; i < (int) G_N_ELEMENTS (enc_aliases); i++)
1560
 
    {
1561
 
      if (aliases_equal (enc_aliases[i].alias, encoding))
1562
 
        return enc_aliases[i].value;
1563
 
    }
1564
 
 
1565
 
  for (i = 0; i < (int) G_N_ELEMENTS (known_encodings); i++)
1566
 
    {
1567
 
      if (aliases_equal (known_encodings[i].encoding, encoding))
1568
 
        return known_encodings[i].encoding;
1569
 
    }
1570
 
  
1571
 
  return encoding;
1572
 
}
1573
 
 
1574
 
static gboolean
1575
 
lang_tag_matches (const char *l, const char *spec)
1576
 
{
1577
 
  char *l2;
1578
 
  
1579
 
  if (strcmp (l, spec) == 0)
1580
 
    return TRUE;
1581
 
 
1582
 
  l2 = strchr (l, '_');
1583
 
 
1584
 
  if (l2 && strchr (spec, '_') == NULL &&
1585
 
      strncmp (l, spec, l2 - l) == 0)
1586
 
    return TRUE;
1587
 
  
1588
 
  return FALSE;
1589
 
}
1590
 
 
1591
 
static const char *
1592
 
get_encoding_from_lang (const char *lang)
1593
 
{
1594
 
  int i, j;
1595
 
  
1596
 
  for (i = 0; i < (int) G_N_ELEMENTS (known_encodings); i++)
1597
 
    {
1598
 
      for (j = 0; j < N_LANG; j++)
1599
 
        {
1600
 
          if (known_encodings[i].langs[j] && lang_tag_matches (lang, known_encodings[i].langs[j]))
1601
 
            return known_encodings[i].encoding;
1602
 
        }
1603
 
    }
1604
 
  return NULL;
1605
 
}
1606
 
 
1607
 
const char*
1608
 
desktop_file_get_encoding_for_locale (const char *locale)
1609
 
{
1610
 
  char *encoding;
1611
 
 
1612
 
  encoding = strchr(locale, '.');
1613
 
 
1614
 
  if (encoding)
1615
 
    {
1616
 
      encoding++;
1617
 
 
1618
 
      return get_canonical_encoding (encoding);
1619
 
    }
1620
 
  
1621
 
  return get_encoding_from_lang (locale);
1622
 
}
1623
 
 
1624
 
static void
1625
 
gnome_desktop_file_unset_internal (GnomeDesktopFile *df,
1626
 
                                   const char       *section_name,
1627
 
                                   const char       *keyname,
1628
 
                                   const char       *locale,
1629
 
                                   gboolean          all_locales)
1630
 
{
1631
 
  GnomeDesktopFileLine *line;
1632
 
  GnomeDesktopFileSection *section;
1633
 
  GQuark key_quark;
1634
 
  int i;
1635
 
 
1636
 
  section = lookup_section (df, section_name);
1637
 
  if (section == NULL)
1638
 
    return;
1639
 
  
1640
 
  key_quark = g_quark_try_string (keyname);
1641
 
  if (key_quark == 0)
1642
 
    return;
1643
 
  
1644
 
  i = 0;
1645
 
  while (i < section->n_lines)
1646
 
    {
1647
 
      line = &section->lines[i];
1648
 
      
1649
 
      if (line->key == key_quark &&
1650
 
          (all_locales ||
1651
 
           ((locale == NULL && line->locale == NULL) ||
1652
 
            (locale != NULL && line->locale != NULL && strcmp (locale, line->locale) == 0))))
1653
 
        {
1654
 
          g_free (line->locale);
1655
 
          g_free (line->value);
1656
 
 
1657
 
          if ((i+1) < section->n_lines)
1658
 
            g_memmove (&section->lines[i], &section->lines[i+1],
1659
 
                       (section->n_lines - i - 1) * sizeof (section->lines[0]));
1660
 
 
1661
 
          section->n_lines -= 1;
1662
 
        }
1663
 
      else
1664
 
        {
1665
 
          ++i;
1666
 
        }
1667
 
    }
1668
 
}
1669
 
 
1670
 
void
1671
 
gnome_desktop_file_unset (GnomeDesktopFile *df,
1672
 
                          const char       *section,
1673
 
                          const char       *keyname)
1674
 
{
1675
 
  gnome_desktop_file_unset_internal (df, section, keyname, NULL, TRUE);
1676
 
}
1677
 
 
1678
 
void
1679
 
gnome_desktop_file_unset_for_locale (GnomeDesktopFile *df,
1680
 
                                     const char       *section,
1681
 
                                     const char       *keyname,
1682
 
                                     const char       *locale)
1683
 
{
1684
 
  gnome_desktop_file_unset_internal (df, section, keyname, locale, FALSE);
1685
 
}
1686
 
 
1687
 
void
1688
 
gnome_desktop_file_copy_key (GnomeDesktopFile *df,
1689
 
                             const char       *section_name,
1690
 
                             const char       *source_key,
1691
 
                             const char       *dest_key)
1692
 
{
1693
 
  GnomeDesktopFileLine *line;
1694
 
  GnomeDesktopFileSection *section;
1695
 
  GQuark key_quark;
1696
 
  int i;
1697
 
 
1698
 
  g_return_if_fail (source_key != NULL);
1699
 
  g_return_if_fail (dest_key != NULL);
1700
 
  g_return_if_fail (strcmp (source_key, dest_key) != 0);
1701
 
  
1702
 
  gnome_desktop_file_unset (df, section_name, dest_key);
1703
 
 
1704
 
  section = lookup_section (df, section_name);
1705
 
  if (section == NULL)
1706
 
    return;
1707
 
  
1708
 
  key_quark = g_quark_try_string (source_key);
1709
 
  if (key_quark == 0)
1710
 
    return;
1711
 
 
1712
 
  i = 0;
1713
 
  while (i < section->n_lines)
1714
 
    {
1715
 
      line = &section->lines[i];
1716
 
      
1717
 
      if (line->key == key_quark)
1718
 
        {
1719
 
          /* Mmmm, modify the array we're iterating over...
1720
 
           * should be safe as it just appends.
1721
 
           */
1722
 
          
1723
 
          gnome_desktop_file_set_raw (df, section_name, dest_key,
1724
 
                                      line->locale, line->value); 
1725
 
        }
1726
 
 
1727
 
      ++i;
1728
 
    }
1729
 
}