From c63a6f2822a66b3ca78d98f4a49a9a751c1fa390 Mon Sep 17 00:00:00 2001 From: "raveit65 (via Travis CI)" Date: Mon, 21 Jun 2021 21:10:08 +0000 Subject: Deploy mate-desktop/mate-menus to github.com/mate-desktop/mate-menus.git:gh-pages --- .../0.html | 1956 ++++++++ .../1.html | 5148 ++++++++++++++++++++ .../index.html | 126 + .../stats.html | 109 + .../style.css | 137 + 5 files changed, 7476 insertions(+) create mode 100644 2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/0.html create mode 100644 2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/1.html create mode 100644 2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/index.html create mode 100644 2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/stats.html create mode 100644 2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/style.css (limited to '2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support') diff --git a/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/0.html b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/0.html new file mode 100644 index 0000000..cea5456 --- /dev/null +++ b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/0.html @@ -0,0 +1,1956 @@ + + + + + + Cppcheck - HTML report - mate-menus + + + + + + +
+ +
+
  1
+  2
+  3
+  4
+  5
+  6
+  7
+  8
+  9
+ 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ 26
+ 27
+ 28
+ 29
+ 30
+ 31
+ 32
+ 33
+ 34
+ 35
+ 36
+ 37
+ 38
+ 39
+ 40
+ 41
+ 42
+ 43
+ 44
+ 45
+ 46
+ 47
+ 48
+ 49
+ 50
+ 51
+ 52
+ 53
+ 54
+ 55
+ 56
+ 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
/*
+ * Copyright (C) 2002 - 2004 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include "desktop-entries.h"
+#include <gio/gdesktopappinfo.h>
+
+#include <string.h>
+
+#include "menu-util.h"
+
+#define DESKTOP_ENTRY_GROUP     "Desktop Entry"
+
+struct DesktopEntry
+{
+  guint       refcount;
+
+  char       *path;
+  const char *basename;
+
+  guint       type      : 2;
+  guint       reserved  : 30;
+};
+
+typedef struct
+{
+  DesktopEntry     base;
+
+  GDesktopAppInfo *appinfo;
+  GQuark          *categories;
+} DesktopEntryDesktop;
+
+typedef struct
+{
+  DesktopEntry base;
+
+  char     *name;
+  char     *generic_name;
+  char     *comment;
+  GIcon    *icon;
+	char* full_name;
+	char* exec;
+
+  guint     nodisplay   : 1;
+  guint     hidden      : 1;
+  guint     showin      : 1;
+	guint terminal:1;
+} DesktopEntryDirectory;
+
+struct DesktopEntrySet {
+	int refcount;
+	GHashTable* hash;
+};
+
+/*
+ * Desktop entries
+ */
+
+/**
+ * unix_basename_from_path:
+ * @path: Path string
+ *
+ * Returns: A constant pointer into the basename of @path
+ */
+static const char *
+unix_basename_from_path (const char *path)
+{
+  const char *basename = g_strrstr (path, "/");
+  if (basename)
+    return basename + 1;
+  else
+    return path;
+}
+
+static const char *
+get_current_desktop (void)
+{
+  static char *current_desktop = NULL;
+
+  /* Support XDG_CURRENT_DESKTOP environment variable; this can be used
+   * to abuse mate-menus in non-MATE desktops. */
+  if (!current_desktop)
+    {
+      const char *desktop;
+
+      desktop = g_getenv ("XDG_CURRENT_DESKTOP");
+
+      /* Note: if XDG_CURRENT_DESKTOP is set but empty, do as if it
+       * was not set */
+      if (!desktop || desktop[0] == '\0')
+        current_desktop = g_strdup ("MATE");
+      else
+        current_desktop = g_strdup (desktop);
+    }
+
+  /* Using "*" means skipping desktop-related checks */
+  if (g_strcmp0 (current_desktop, "*") == 0)
+    return NULL;
+
+  return current_desktop;
+}
+
+static GIcon *
+key_file_get_icon (GKeyFile *key_file)
+{
+  GIcon *icon = NULL;
+  gchar *icon_name;
+
+  icon_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP,
+                                            "Icon", NULL, NULL);
+  if (!icon_name)
+    return NULL;
+
+  if (g_path_is_absolute (icon_name)) {
+    GFile *file;
+
+    file = g_file_new_for_path (icon_name);
+    icon = g_file_icon_new (file);
+    g_object_unref (file);
+  } else {
+    char *p;
+
+    /* Work around a common mistake in desktop files */
+    if ((p = strrchr (icon_name, '.')) != NULL &&
+        (strcmp (p, ".png") == 0 ||
+         strcmp (p, ".xpm") == 0 ||
+         strcmp (p, ".svg") == 0))
+      *p = 0;
+
+    icon = g_themed_icon_new (icon_name);
+  }
+
+  g_free (icon_name);
+
+  return icon;
+}
+
+static gboolean
+key_file_get_show_in (GKeyFile *key_file)
+{
+  const gchar *current_desktop;
+  gchar **strv;
+  gboolean show_in = TRUE;
+  int i;
+
+  current_desktop = get_current_desktop ();
+  if (!current_desktop)
+    return TRUE;
+
+  strv = g_key_file_get_string_list (key_file,
+                                     DESKTOP_ENTRY_GROUP,
+                                     "OnlyShowIn",
+                                     NULL,
+                                     NULL);
+  if (strv)
+    {
+      show_in = FALSE;
+      for (i = 0; strv[i]; i++)
+        {
+          if (!strcmp (strv[i], current_desktop))
+            {
+              show_in = TRUE;
+              break;
+            }
+        }
+    }
+  else
+    {
+      strv = g_key_file_get_string_list (key_file,
+                                         DESKTOP_ENTRY_GROUP,
+                                         "NotShowIn",
+                                         NULL,
+                                         NULL);
+      if (strv)
+        {
+          show_in = TRUE;
+          for (i = 0; strv[i]; i++)
+            {
+              if (!strcmp (strv[i], current_desktop))
+                {
+                  show_in = FALSE;
+                }
+            }
+        }
+    }
+  g_strfreev (strv);
+
+  return show_in;
+}
+
+static gboolean
+desktop_entry_load_directory (DesktopEntry  *entry,
+                              GKeyFile      *key_file,
+                              GError       **error)
+{
+  DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry;
+  char *type_str;
+
+  type_str = g_key_file_get_string (key_file, DESKTOP_ENTRY_GROUP, "Type", error);
+  if (!type_str)
+    return FALSE;
+
+  if (strcmp (type_str, "Directory") != 0)
+    {
+      g_set_error (error,
+                   G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_INVALID_VALUE,
+                   "\"%s\" does not contain the correct \"Type\" value\n", entry->path);
+      g_free (type_str);
+      return FALSE;
+    }
+
+  g_free (type_str);
+
+  entry_directory->name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Name", NULL, error);
+  if (entry_directory->name == NULL)
+    return FALSE;
+
+  entry_directory->generic_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "GenericName", NULL, NULL);
+  entry_directory->comment      = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL);
+  entry_directory->icon         = key_file_get_icon (key_file);
+  entry_directory->nodisplay    = g_key_file_get_boolean (key_file,
+                                                          DESKTOP_ENTRY_GROUP,
+                                                          "NoDisplay",
+                                                          NULL);
+  entry_directory->hidden       = g_key_file_get_boolean (key_file,
+                                                          DESKTOP_ENTRY_GROUP,
+                                                          "Hidden",
+                                                          NULL);
+  entry_directory->showin       = key_file_get_show_in (key_file);
+
+  return TRUE;
+}
+
+static gboolean
+desktop_entry_load (DesktopEntry *entry)
+{
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    {
+      DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop*)entry;
+      const char *categories_str;
+
+      entry_desktop->appinfo = g_desktop_app_info_new_from_filename (entry->path);
+      if (!entry_desktop->appinfo ||
+          !g_app_info_get_name (G_APP_INFO (entry_desktop->appinfo)) ||
+          !g_app_info_get_executable (G_APP_INFO (entry_desktop->appinfo)))
+        {
+          menu_verbose ("Failed to load \"%s\"\n", entry->path);
+          return FALSE;
+        }
+
+      categories_str = g_desktop_app_info_get_categories (entry_desktop->appinfo);
+      if (categories_str)
+        {
+          char **categories;
+          int i;
+
+          categories = g_strsplit (categories_str, ";", -1);
+          entry_desktop->categories = g_new0 (GQuark, g_strv_length (categories) + 1);
+
+          for (i = 0; categories[i]; i++)
+            entry_desktop->categories[i] = g_quark_from_string (categories[i]);
+
+          g_strfreev (categories);
+        }
+
+      return TRUE;
+    }
+  else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
+    {
+      GKeyFile *key_file = NULL;
+      GError   *error = NULL;
+      gboolean  retval = FALSE;
+
+      key_file = g_key_file_new ();
+
+      if (!g_key_file_load_from_file (key_file, entry->path, 0, &error))
+        goto out;
+
+      if (!desktop_entry_load_directory (entry, key_file, &error))
+        goto out;
+
+      retval = TRUE;
+
+    out:
+      g_key_file_free (key_file);
+
+      if (!retval)
+        {
+          if (error)
+            {
+              menu_verbose ("Failed to load \"%s\": %s\n", entry->path, error->message);
+              g_error_free (error);
+            }
+          else
+            {
+              menu_verbose ("Failed to load \"%s\"\n", entry->path);
+            }
+        }
+
+      return retval;
+    }
+  else
+    g_assert_not_reached ();
+
+  return FALSE;
+}
+
+DesktopEntry* desktop_entry_new(const char* path)
+{
+  DesktopEntryType  type;
+  DesktopEntry     *retval;
+
+  menu_verbose ("Loading desktop entry \"%s\"\n", path);
+
+  if (g_str_has_suffix (path, ".desktop"))
+    {
+      type = DESKTOP_ENTRY_DESKTOP;
+      retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1);
+    }
+  else if (g_str_has_suffix (path, ".directory"))
+    {
+      type = DESKTOP_ENTRY_DIRECTORY;
+      retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1);
+    }
+  else
+    {
+      menu_verbose ("Unknown desktop entry suffix in \"%s\"\n",
+                    path);
+      return NULL;
+    }
+
+  retval->refcount = 1;
+  retval->type     = type;
+  retval->path     = g_strdup (path);
+  retval->basename = unix_basename_from_path (retval->path);
+
+  if (!desktop_entry_load (retval))
+    {
+      desktop_entry_unref (retval);
+      return NULL;
+    }
+
+  return retval;
+}
+
+DesktopEntry* desktop_entry_reload(DesktopEntry* entry)
+{
+  g_return_val_if_fail (entry != NULL, NULL);
+
+  menu_verbose ("Re-loading desktop entry \"%s\"\n", entry->path);
+
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    {
+      DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop *) entry;
+
+      g_object_unref (entry_desktop->appinfo);
+      entry_desktop->appinfo = NULL;
+
+      g_free (entry_desktop->categories);
+      entry_desktop->categories = NULL;
+    }
+  else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
+    {
+      DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry;
+
+      g_free (entry_directory->name);
+      entry_directory->name = NULL;
+
+      g_free (entry_directory->comment);
+      entry_directory->comment = NULL;
+
+      g_object_unref (entry_directory->icon);
+      entry_directory->icon = NULL;
+    }
+  else
+    g_assert_not_reached ();
+
+  if (!desktop_entry_load (entry))
+    {
+      desktop_entry_unref (entry);
+      return NULL;
+    }
+
+  return entry;
+}
+
+DesktopEntry* desktop_entry_ref(DesktopEntry* entry)
+{
+  g_return_val_if_fail (entry != NULL, NULL);
+  g_return_val_if_fail (entry->refcount > 0, NULL);
+
+  entry->refcount += 1;
+
+  return entry;
+}
+
+DesktopEntry* desktop_entry_copy(DesktopEntry* entry)
+{
+  DesktopEntry *retval;
+
+  menu_verbose ("Copying desktop entry \"%s\"\n",
+                entry->basename);
+
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1);
+  else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
+    retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1);
+  else
+    g_assert_not_reached ();
+
+  retval->refcount     = 1;
+  retval->type         = entry->type;
+  retval->path         = g_strdup (entry->path);
+  retval->basename     = unix_basename_from_path (retval->path);
+
+  if (retval->type == DESKTOP_ENTRY_DESKTOP)
+    {
+      DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry;
+      DesktopEntryDesktop *retval_desktop_entry = (DesktopEntryDesktop*) retval;
+      int i;
+
+      retval_desktop_entry->appinfo = g_object_ref (desktop_entry->appinfo);
+
+      if (desktop_entry->categories != NULL)
+        {
+          i = 0;
+          for (; desktop_entry->categories[i]; i++);
+
+          retval_desktop_entry->categories = g_new0 (GQuark, i + 1);
+
+          i = 0;
+          for (; desktop_entry->categories[i]; i++)
+            retval_desktop_entry->categories[i] = desktop_entry->categories[i];
+        }
+      else
+        retval_desktop_entry->categories = NULL;
+    }
+  else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
+    {
+      DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry;
+      DesktopEntryDirectory *retval_directory = (DesktopEntryDirectory*)retval;
+
+      retval_directory->name         = g_strdup (entry_directory->name);
+      retval_directory->comment      = g_strdup (entry_directory->comment);
+      retval_directory->icon         = g_object_ref (entry_directory->icon);
+      retval_directory->nodisplay    = entry_directory->nodisplay;
+      retval_directory->hidden       = entry_directory->hidden;
+      retval_directory->showin       = entry_directory->showin;
+    }
+
+  return retval;
+}
+
+void desktop_entry_unref(DesktopEntry* entry)
+{
+  g_return_if_fail (entry != NULL);
+  g_return_if_fail (entry->refcount > 0);
+
+  entry->refcount -= 1;
+  if (entry->refcount != 0)
+    return;
+
+  g_free (entry->path);
+  entry->path = NULL;
+
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    {
+      DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry;
+      g_free (desktop_entry->categories);
+      if (desktop_entry->appinfo)
+        g_object_unref (desktop_entry->appinfo);
+    }
+  else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
+    {
+      DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry;
+
+      g_free (entry_directory->name);
+      entry_directory->name = NULL;
+
+      g_free (entry_directory->comment);
+      entry_directory->comment = NULL;
+
+      if (entry_directory->icon != NULL)
+        {
+          g_object_unref (entry_directory->icon);
+          entry_directory->icon = NULL;
+        }
+    }
+  else
+    g_assert_not_reached ();
+
+  g_free (entry);
+}
+
+DesktopEntryType desktop_entry_get_type(DesktopEntry* entry)
+{
+	return entry->type;
+}
+
+const char* desktop_entry_get_path(DesktopEntry* entry)
+{
+	return entry->path;
+}
+
+const char *
+desktop_entry_get_basename (DesktopEntry *entry)
+{
+	return entry->basename;
+}
+
+const char* desktop_entry_get_name(DesktopEntry* entry)
+{
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_app_info_get_name (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo));
+  return ((DesktopEntryDirectory*)entry)->name;
+}
+
+const char* desktop_entry_get_generic_name(DesktopEntry* entry)
+{
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_desktop_app_info_get_generic_name (((DesktopEntryDesktop*)entry)->appinfo);
+  return ((DesktopEntryDirectory*)entry)->generic_name;
+}
+
+const char* desktop_entry_get_comment(DesktopEntry* entry)
+{
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_app_info_get_description (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo));
+  return ((DesktopEntryDirectory*)entry)->comment;
+}
+
+GIcon *
+desktop_entry_get_icon (DesktopEntry *entry)
+{
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_app_info_get_icon (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo));
+  return ((DesktopEntryDirectory*)entry)->icon;
+}
+
+gboolean desktop_entry_get_no_display (DesktopEntry *entry)
+{
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_desktop_app_info_get_nodisplay (((DesktopEntryDesktop*)entry)->appinfo);
+  return ((DesktopEntryDirectory*)entry)->nodisplay;
+}
+
+gboolean desktop_entry_get_hidden(DesktopEntry* entry)
+{
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_desktop_app_info_get_is_hidden (((DesktopEntryDesktop*)entry)->appinfo);
+  return ((DesktopEntryDirectory*)entry)->hidden;
+}
+
+gboolean
+desktop_entry_get_show_in (DesktopEntry *entry)
+{
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    {
+      const char *current_desktop = get_current_desktop ();
+
+      if (current_desktop == NULL)
+        return TRUE;
+      else
+        return g_desktop_app_info_get_show_in (((DesktopEntryDesktop*)entry)->appinfo, current_desktop);
+    }
+  return ((DesktopEntryDirectory*)entry)->showin;
+}
+
+GDesktopAppInfo  *
+desktop_entry_get_app_info (DesktopEntry *entry)
+{
+  g_return_val_if_fail (entry->type == DESKTOP_ENTRY_DESKTOP, NULL);
+  return ((DesktopEntryDesktop*)entry)->appinfo;
+}
+
+gboolean desktop_entry_has_categories(DesktopEntry* entry)
+{
+  DesktopEntryDesktop *desktop_entry;
+  if (entry->type != DESKTOP_ENTRY_DESKTOP)
+    return FALSE;
+
+  desktop_entry = (DesktopEntryDesktop*) entry;
+  return (desktop_entry->categories != NULL && desktop_entry->categories[0] != 0);
+}
+
+gboolean desktop_entry_has_category(DesktopEntry* entry, const char* category)
+{
+  GQuark quark;
+  int    i;
+  DesktopEntryDesktop *desktop_entry;
+
+  if (entry->type != DESKTOP_ENTRY_DESKTOP)
+    return FALSE;
+
+  desktop_entry = (DesktopEntryDesktop*) entry;
+
+  if (desktop_entry->categories == NULL)
+    return FALSE;
+
+  if (!(quark = g_quark_try_string (category)))
+    return FALSE;
+
+  for (i = 0; desktop_entry->categories[i]; i++)
+    {
+      if (quark == desktop_entry->categories[i])
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+void desktop_entry_add_legacy_category(DesktopEntry* entry)
+{
+  GQuark *categories;
+  int     i;
+  DesktopEntryDesktop *desktop_entry;
+
+  g_return_if_fail (entry->type == DESKTOP_ENTRY_DESKTOP);
+
+  desktop_entry = (DesktopEntryDesktop*) entry;
+
+  menu_verbose ("Adding Legacy category to \"%s\"\n",
+                entry->basename);
+
+  if (desktop_entry->categories != NULL)
+    {
+      i = 0;
+      for (; desktop_entry->categories[i]; i++);
+
+      categories = g_new0 (GQuark, i + 2);
+
+      i = 0;
+      for (; desktop_entry->categories[i]; i++)
+        categories[i] = desktop_entry->categories[i];
+    }
+  else
+    {
+      categories = g_new0 (GQuark, 2);
+      i = 0;
+    }
+
+  categories[i] = g_quark_from_string ("Legacy");
+
+  g_free (desktop_entry->categories);
+  desktop_entry->categories = categories;
+}
+
+/*
+ * Entry sets
+ */
+
+DesktopEntrySet* desktop_entry_set_new(void)
+{
+  DesktopEntrySet *set;
+
+  set = g_new0 (DesktopEntrySet, 1);
+  set->refcount = 1;
+
+  menu_verbose (" New entry set %p\n", set);
+
+  return set;
+}
+
+DesktopEntrySet* desktop_entry_set_ref(DesktopEntrySet* set)
+{
+  g_return_val_if_fail (set != NULL, NULL);
+  g_return_val_if_fail (set->refcount > 0, NULL);
+
+  set->refcount += 1;
+
+  return set;
+}
+
+void desktop_entry_set_unref(DesktopEntrySet* set)
+{
+  g_return_if_fail (set != NULL);
+  g_return_if_fail (set->refcount > 0);
+
+  set->refcount -= 1;
+  if (set->refcount == 0)
+    {
+      menu_verbose (" Deleting entry set %p\n", set);
+
+      if (set->hash)
+        g_hash_table_destroy (set->hash);
+      set->hash = NULL;
+
+      g_free (set);
+    }
+}
+
+void desktop_entry_set_add_entry(DesktopEntrySet* set, DesktopEntry* entry, const char* file_id)
+{
+  menu_verbose (" Adding to set %p entry %s\n", set, file_id);
+
+  if (set->hash == NULL)
+    {
+      set->hash = g_hash_table_new_full (g_str_hash,
+                                         g_str_equal,
+                                         g_free,
+                                         (GDestroyNotify) desktop_entry_unref);
+    }
+
+  g_hash_table_replace (set->hash,
+                        g_strdup (file_id),
+                        desktop_entry_ref (entry));
+}
+
+DesktopEntry* desktop_entry_set_lookup(DesktopEntrySet* set, const char* file_id)
+{
+  if (set->hash == NULL)
+    return NULL;
+
+  return g_hash_table_lookup (set->hash, file_id);
+}
+
+typedef struct {
+	DesktopEntrySetForeachFunc func;
+	gpointer user_data;
+} EntryHashForeachData;
+
+static void entry_hash_foreach(const char* file_id, DesktopEntry* entry, EntryHashForeachData* fd)
+{
+	fd->func(file_id, entry, fd->user_data);
+}
+
+void desktop_entry_set_foreach(DesktopEntrySet* set, DesktopEntrySetForeachFunc func, gpointer user_data)
+{
+  g_return_if_fail (set != NULL);
+  g_return_if_fail (func != NULL);
+
+  if (set->hash != NULL)
+    {
+      EntryHashForeachData fd;
+
+      fd.func      = func;
+      fd.user_data = user_data;
+
+      g_hash_table_foreach (set->hash,
+                            (GHFunc) entry_hash_foreach,
+                            &fd);
+    }
+}
+
+static void desktop_entry_set_clear(DesktopEntrySet* set)
+{
+  menu_verbose (" Clearing set %p\n", set);
+
+  if (set->hash != NULL)
+    {
+      g_hash_table_destroy (set->hash);
+      set->hash = NULL;
+    }
+}
+
+int desktop_entry_set_get_count(DesktopEntrySet* set)
+{
+  if (set->hash == NULL)
+    return 0;
+
+  return g_hash_table_size (set->hash);
+}
+
+static void union_foreach(const char* file_id, DesktopEntry* entry, DesktopEntrySet* set)
+{
+	/* we are iterating over "with" adding anything not
+	 * already in "set". We unconditionally overwrite
+	 * the stuff in "set" because we can assume
+	 * two entries with the same name are equivalent.
+	 */
+	desktop_entry_set_add_entry(set, entry, file_id);
+}
+
+void desktop_entry_set_union(DesktopEntrySet* set, DesktopEntrySet* with)
+{
+  menu_verbose (" Union of %p and %p\n", set, with);
+
+  if (desktop_entry_set_get_count (with) == 0)
+    return; /* A fast simple case */
+
+  g_hash_table_foreach (with->hash,
+                        (GHFunc) union_foreach,
+                        set);
+}
+
+typedef struct {
+	DesktopEntrySet *set;
+	DesktopEntrySet *with;
+} IntersectData;
+
+static gboolean intersect_foreach_remove(const char* file_id, DesktopEntry* entry, IntersectData* id)
+{
+  /* Remove everything in "set" which is not in "with" */
+
+  if (g_hash_table_lookup (id->with->hash, file_id) != NULL)
+    return FALSE;
+
+  menu_verbose (" Removing from %p entry %s\n", id->set, file_id);
+
+  return TRUE; /* return TRUE to remove */
+}
+
+void desktop_entry_set_intersection(DesktopEntrySet* set, DesktopEntrySet* with)
+{
+  IntersectData id;
+
+  menu_verbose (" Intersection of %p and %p\n", set, with);
+
+  if (desktop_entry_set_get_count (set) == 0 ||
+      desktop_entry_set_get_count (with) == 0)
+    {
+      /* A fast simple case */
+      desktop_entry_set_clear (set);
+      return;
+    }
+
+  id.set  = set;
+  id.with = with;
+
+  g_hash_table_foreach_remove (set->hash,
+                               (GHRFunc) intersect_foreach_remove,
+                               &id);
+}
+
+typedef struct {
+	DesktopEntrySet *set;
+	DesktopEntrySet *other;
+} SubtractData;
+
+static gboolean subtract_foreach_remove(const char* file_id, DesktopEntry* entry, SubtractData* sd)
+{
+  /* Remove everything in "set" which is not in "other" */
+
+  if (g_hash_table_lookup (sd->other->hash, file_id) == NULL)
+    return FALSE;
+
+  menu_verbose (" Removing from %p entry %s\n", sd->set, file_id);
+
+  return TRUE; /* return TRUE to remove */
+}
+
+void desktop_entry_set_subtract(DesktopEntrySet* set, DesktopEntrySet* other)
+{
+  SubtractData sd;
+
+  menu_verbose (" Subtract from %p set %p\n", set, other);
+
+  if (desktop_entry_set_get_count (set) == 0 ||
+      desktop_entry_set_get_count (other) == 0)
+    return; /* A fast simple case */
+
+  sd.set   = set;
+  sd.other = other;
+
+  g_hash_table_foreach_remove (set->hash,
+                               (GHRFunc) subtract_foreach_remove,
+                               &sd);
+}
+
+void desktop_entry_set_swap_contents(DesktopEntrySet* a, DesktopEntrySet* b)
+{
+	GHashTable *tmp;
+
+	menu_verbose (" Swap contents of %p and %p\n", a, b);
+
+	tmp = a->hash;
+	 a->hash = b->hash;
+	b->hash = tmp;
+}
+
+
+
+
+ + + diff --git a/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/1.html b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/1.html new file mode 100644 index 0000000..dc03e80 --- /dev/null +++ b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/1.html @@ -0,0 +1,5148 @@ + + + + + + Cppcheck - HTML report - mate-menus + + + + + + +
+ +
+
   1
+   2
+   3
+   4
+   5
+   6
+   7
+   8
+   9
+  10
+  11
+  12
+  13
+  14
+  15
+  16
+  17
+  18
+  19
+  20
+  21
+  22
+  23
+  24
+  25
+  26
+  27
+  28
+  29
+  30
+  31
+  32
+  33
+  34
+  35
+  36
+  37
+  38
+  39
+  40
+  41
+  42
+  43
+  44
+  45
+  46
+  47
+  48
+  49
+  50
+  51
+  52
+  53
+  54
+  55
+  56
+  57
+  58
+  59
+  60
+  61
+  62
+  63
+  64
+  65
+  66
+  67
+  68
+  69
+  70
+  71
+  72
+  73
+  74
+  75
+  76
+  77
+  78
+  79
+  80
+  81
+  82
+  83
+  84
+  85
+  86
+  87
+  88
+  89
+  90
+  91
+  92
+  93
+  94
+  95
+  96
+  97
+  98
+  99
+ 100
+ 101
+ 102
+ 103
+ 104
+ 105
+ 106
+ 107
+ 108
+ 109
+ 110
+ 111
+ 112
+ 113
+ 114
+ 115
+ 116
+ 117
+ 118
+ 119
+ 120
+ 121
+ 122
+ 123
+ 124
+ 125
+ 126
+ 127
+ 128
+ 129
+ 130
+ 131
+ 132
+ 133
+ 134
+ 135
+ 136
+ 137
+ 138
+ 139
+ 140
+ 141
+ 142
+ 143
+ 144
+ 145
+ 146
+ 147
+ 148
+ 149
+ 150
+ 151
+ 152
+ 153
+ 154
+ 155
+ 156
+ 157
+ 158
+ 159
+ 160
+ 161
+ 162
+ 163
+ 164
+ 165
+ 166
+ 167
+ 168
+ 169
+ 170
+ 171
+ 172
+ 173
+ 174
+ 175
+ 176
+ 177
+ 178
+ 179
+ 180
+ 181
+ 182
+ 183
+ 184
+ 185
+ 186
+ 187
+ 188
+ 189
+ 190
+ 191
+ 192
+ 193
+ 194
+ 195
+ 196
+ 197
+ 198
+ 199
+ 200
+ 201
+ 202
+ 203
+ 204
+ 205
+ 206
+ 207
+ 208
+ 209
+ 210
+ 211
+ 212
+ 213
+ 214
+ 215
+ 216
+ 217
+ 218
+ 219
+ 220
+ 221
+ 222
+ 223
+ 224
+ 225
+ 226
+ 227
+ 228
+ 229
+ 230
+ 231
+ 232
+ 233
+ 234
+ 235
+ 236
+ 237
+ 238
+ 239
+ 240
+ 241
+ 242
+ 243
+ 244
+ 245
+ 246
+ 247
+ 248
+ 249
+ 250
+ 251
+ 252
+ 253
+ 254
+ 255
+ 256
+ 257
+ 258
+ 259
+ 260
+ 261
+ 262
+ 263
+ 264
+ 265
+ 266
+ 267
+ 268
+ 269
+ 270
+ 271
+ 272
+ 273
+ 274
+ 275
+ 276
+ 277
+ 278
+ 279
+ 280
+ 281
+ 282
+ 283
+ 284
+ 285
+ 286
+ 287
+ 288
+ 289
+ 290
+ 291
+ 292
+ 293
+ 294
+ 295
+ 296
+ 297
+ 298
+ 299
+ 300
+ 301
+ 302
+ 303
+ 304
+ 305
+ 306
+ 307
+ 308
+ 309
+ 310
+ 311
+ 312
+ 313
+ 314
+ 315
+ 316
+ 317
+ 318
+ 319
+ 320
+ 321
+ 322
+ 323
+ 324
+ 325
+ 326
+ 327
+ 328
+ 329
+ 330
+ 331
+ 332
+ 333
+ 334
+ 335
+ 336
+ 337
+ 338
+ 339
+ 340
+ 341
+ 342
+ 343
+ 344
+ 345
+ 346
+ 347
+ 348
+ 349
+ 350
+ 351
+ 352
+ 353
+ 354
+ 355
+ 356
+ 357
+ 358
+ 359
+ 360
+ 361
+ 362
+ 363
+ 364
+ 365
+ 366
+ 367
+ 368
+ 369
+ 370
+ 371
+ 372
+ 373
+ 374
+ 375
+ 376
+ 377
+ 378
+ 379
+ 380
+ 381
+ 382
+ 383
+ 384
+ 385
+ 386
+ 387
+ 388
+ 389
+ 390
+ 391
+ 392
+ 393
+ 394
+ 395
+ 396
+ 397
+ 398
+ 399
+ 400
+ 401
+ 402
+ 403
+ 404
+ 405
+ 406
+ 407
+ 408
+ 409
+ 410
+ 411
+ 412
+ 413
+ 414
+ 415
+ 416
+ 417
+ 418
+ 419
+ 420
+ 421
+ 422
+ 423
+ 424
+ 425
+ 426
+ 427
+ 428
+ 429
+ 430
+ 431
+ 432
+ 433
+ 434
+ 435
+ 436
+ 437
+ 438
+ 439
+ 440
+ 441
+ 442
+ 443
+ 444
+ 445
+ 446
+ 447
+ 448
+ 449
+ 450
+ 451
+ 452
+ 453
+ 454
+ 455
+ 456
+ 457
+ 458
+ 459
+ 460
+ 461
+ 462
+ 463
+ 464
+ 465
+ 466
+ 467
+ 468
+ 469
+ 470
+ 471
+ 472
+ 473
+ 474
+ 475
+ 476
+ 477
+ 478
+ 479
+ 480
+ 481
+ 482
+ 483
+ 484
+ 485
+ 486
+ 487
+ 488
+ 489
+ 490
+ 491
+ 492
+ 493
+ 494
+ 495
+ 496
+ 497
+ 498
+ 499
+ 500
+ 501
+ 502
+ 503
+ 504
+ 505
+ 506
+ 507
+ 508
+ 509
+ 510
+ 511
+ 512
+ 513
+ 514
+ 515
+ 516
+ 517
+ 518
+ 519
+ 520
+ 521
+ 522
+ 523
+ 524
+ 525
+ 526
+ 527
+ 528
+ 529
+ 530
+ 531
+ 532
+ 533
+ 534
+ 535
+ 536
+ 537
+ 538
+ 539
+ 540
+ 541
+ 542
+ 543
+ 544
+ 545
+ 546
+ 547
+ 548
+ 549
+ 550
+ 551
+ 552
+ 553
+ 554
+ 555
+ 556
+ 557
+ 558
+ 559
+ 560
+ 561
+ 562
+ 563
+ 564
+ 565
+ 566
+ 567
+ 568
+ 569
+ 570
+ 571
+ 572
+ 573
+ 574
+ 575
+ 576
+ 577
+ 578
+ 579
+ 580
+ 581
+ 582
+ 583
+ 584
+ 585
+ 586
+ 587
+ 588
+ 589
+ 590
+ 591
+ 592
+ 593
+ 594
+ 595
+ 596
+ 597
+ 598
+ 599
+ 600
+ 601
+ 602
+ 603
+ 604
+ 605
+ 606
+ 607
+ 608
+ 609
+ 610
+ 611
+ 612
+ 613
+ 614
+ 615
+ 616
+ 617
+ 618
+ 619
+ 620
+ 621
+ 622
+ 623
+ 624
+ 625
+ 626
+ 627
+ 628
+ 629
+ 630
+ 631
+ 632
+ 633
+ 634
+ 635
+ 636
+ 637
+ 638
+ 639
+ 640
+ 641
+ 642
+ 643
+ 644
+ 645
+ 646
+ 647
+ 648
+ 649
+ 650
+ 651
+ 652
+ 653
+ 654
+ 655
+ 656
+ 657
+ 658
+ 659
+ 660
+ 661
+ 662
+ 663
+ 664
+ 665
+ 666
+ 667
+ 668
+ 669
+ 670
+ 671
+ 672
+ 673
+ 674
+ 675
+ 676
+ 677
+ 678
+ 679
+ 680
+ 681
+ 682
+ 683
+ 684
+ 685
+ 686
+ 687
+ 688
+ 689
+ 690
+ 691
+ 692
+ 693
+ 694
+ 695
+ 696
+ 697
+ 698
+ 699
+ 700
+ 701
+ 702
+ 703
+ 704
+ 705
+ 706
+ 707
+ 708
+ 709
+ 710
+ 711
+ 712
+ 713
+ 714
+ 715
+ 716
+ 717
+ 718
+ 719
+ 720
+ 721
+ 722
+ 723
+ 724
+ 725
+ 726
+ 727
+ 728
+ 729
+ 730
+ 731
+ 732
+ 733
+ 734
+ 735
+ 736
+ 737
+ 738
+ 739
+ 740
+ 741
+ 742
+ 743
+ 744
+ 745
+ 746
+ 747
+ 748
+ 749
+ 750
+ 751
+ 752
+ 753
+ 754
+ 755
+ 756
+ 757
+ 758
+ 759
+ 760
+ 761
+ 762
+ 763
+ 764
+ 765
+ 766
+ 767
+ 768
+ 769
+ 770
+ 771
+ 772
+ 773
+ 774
+ 775
+ 776
+ 777
+ 778
+ 779
+ 780
+ 781
+ 782
+ 783
+ 784
+ 785
+ 786
+ 787
+ 788
+ 789
+ 790
+ 791
+ 792
+ 793
+ 794
+ 795
+ 796
+ 797
+ 798
+ 799
+ 800
+ 801
+ 802
+ 803
+ 804
+ 805
+ 806
+ 807
+ 808
+ 809
+ 810
+ 811
+ 812
+ 813
+ 814
+ 815
+ 816
+ 817
+ 818
+ 819
+ 820
+ 821
+ 822
+ 823
+ 824
+ 825
+ 826
+ 827
+ 828
+ 829
+ 830
+ 831
+ 832
+ 833
+ 834
+ 835
+ 836
+ 837
+ 838
+ 839
+ 840
+ 841
+ 842
+ 843
+ 844
+ 845
+ 846
+ 847
+ 848
+ 849
+ 850
+ 851
+ 852
+ 853
+ 854
+ 855
+ 856
+ 857
+ 858
+ 859
+ 860
+ 861
+ 862
+ 863
+ 864
+ 865
+ 866
+ 867
+ 868
+ 869
+ 870
+ 871
+ 872
+ 873
+ 874
+ 875
+ 876
+ 877
+ 878
+ 879
+ 880
+ 881
+ 882
+ 883
+ 884
+ 885
+ 886
+ 887
+ 888
+ 889
+ 890
+ 891
+ 892
+ 893
+ 894
+ 895
+ 896
+ 897
+ 898
+ 899
+ 900
+ 901
+ 902
+ 903
+ 904
+ 905
+ 906
+ 907
+ 908
+ 909
+ 910
+ 911
+ 912
+ 913
+ 914
+ 915
+ 916
+ 917
+ 918
+ 919
+ 920
+ 921
+ 922
+ 923
+ 924
+ 925
+ 926
+ 927
+ 928
+ 929
+ 930
+ 931
+ 932
+ 933
+ 934
+ 935
+ 936
+ 937
+ 938
+ 939
+ 940
+ 941
+ 942
+ 943
+ 944
+ 945
+ 946
+ 947
+ 948
+ 949
+ 950
+ 951
+ 952
+ 953
+ 954
+ 955
+ 956
+ 957
+ 958
+ 959
+ 960
+ 961
+ 962
+ 963
+ 964
+ 965
+ 966
+ 967
+ 968
+ 969
+ 970
+ 971
+ 972
+ 973
+ 974
+ 975
+ 976
+ 977
+ 978
+ 979
+ 980
+ 981
+ 982
+ 983
+ 984
+ 985
+ 986
+ 987
+ 988
+ 989
+ 990
+ 991
+ 992
+ 993
+ 994
+ 995
+ 996
+ 997
+ 998
+ 999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
+1722
+1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
+1733
+1734
+1735
+1736
+1737
+1738
+1739
+1740
+1741
+1742
+1743
+1744
+1745
+1746
+1747
+1748
+1749
+1750
+1751
+1752
+1753
+1754
+1755
+1756
+1757
+1758
+1759
+1760
+1761
+1762
+1763
+1764
+1765
+1766
+1767
+1768
+1769
+1770
+1771
+1772
+1773
+1774
+1775
+1776
+1777
+1778
+1779
+1780
+1781
+1782
+1783
+1784
+1785
+1786
+1787
+1788
+1789
+1790
+1791
+1792
+1793
+1794
+1795
+1796
+1797
+1798
+1799
+1800
+1801
+1802
+1803
+1804
+1805
+1806
+1807
+1808
+1809
+1810
+1811
+1812
+1813
+1814
+1815
+1816
+1817
+1818
+1819
+1820
+1821
+1822
+1823
+1824
+1825
+1826
+1827
+1828
+1829
+1830
+1831
+1832
+1833
+1834
+1835
+1836
+1837
+1838
+1839
+1840
+1841
+1842
+1843
+1844
+1845
+1846
+1847
+1848
+1849
+1850
+1851
+1852
+1853
+1854
+1855
+1856
+1857
+1858
+1859
+1860
+1861
+1862
+1863
+1864
+1865
+1866
+1867
+1868
+1869
+1870
+1871
+1872
+1873
+1874
+1875
+1876
+1877
+1878
+1879
+1880
+1881
+1882
+1883
+1884
+1885
+1886
+1887
+1888
+1889
+1890
+1891
+1892
+1893
+1894
+1895
+1896
+1897
+1898
+1899
+1900
+1901
+1902
+1903
+1904
+1905
+1906
+1907
+1908
+1909
+1910
+1911
+1912
+1913
+1914
+1915
+1916
+1917
+1918
+1919
+1920
+1921
+1922
+1923
+1924
+1925
+1926
+1927
+1928
+1929
+1930
+1931
+1932
+1933
+1934
+1935
+1936
+1937
+1938
+1939
+1940
+1941
+1942
+1943
+1944
+1945
+1946
+1947
+1948
+1949
+1950
+1951
+1952
+1953
+1954
+1955
+1956
+1957
+1958
+1959
+1960
+1961
+1962
+1963
+1964
+1965
+1966
+1967
+1968
+1969
+1970
+1971
+1972
+1973
+1974
+1975
+1976
+1977
+1978
+1979
+1980
+1981
+1982
+1983
+1984
+1985
+1986
+1987
+1988
+1989
+1990
+1991
+1992
+1993
+1994
+1995
+1996
+1997
+1998
+1999
+2000
+2001
+2002
+2003
+2004
+2005
+2006
+2007
+2008
+2009
+2010
+2011
+2012
+2013
+2014
+2015
+2016
+2017
+2018
+2019
+2020
+2021
+2022
+2023
+2024
+2025
+2026
+2027
+2028
+2029
+2030
+2031
+2032
+2033
+2034
+2035
+2036
+2037
+2038
+2039
+2040
+2041
+2042
+2043
+2044
+2045
+2046
+2047
+2048
+2049
+2050
+2051
+2052
+2053
+2054
+2055
+2056
+2057
+2058
+2059
+2060
+2061
+2062
+2063
+2064
+2065
+2066
+2067
+2068
+2069
+2070
+2071
+2072
+2073
+2074
+2075
+2076
+2077
+2078
+2079
+2080
+2081
+2082
+2083
+2084
+2085
+2086
+2087
+2088
+2089
+2090
+2091
+2092
+2093
+2094
+2095
+2096
+2097
+2098
+2099
+2100
+2101
+2102
+2103
+2104
+2105
+2106
+2107
+2108
+2109
+2110
+2111
+2112
+2113
+2114
+2115
+2116
+2117
+2118
+2119
+2120
+2121
+2122
+2123
+2124
+2125
+2126
+2127
+2128
+2129
+2130
+2131
+2132
+2133
+2134
+2135
+2136
+2137
+2138
+2139
+2140
+2141
+2142
+2143
+2144
+2145
+2146
+2147
+2148
+2149
+2150
+2151
+2152
+2153
+2154
+2155
+2156
+2157
+2158
+2159
+2160
+2161
+2162
+2163
+2164
+2165
+2166
+2167
+2168
+2169
+2170
+2171
+2172
+2173
+2174
+2175
+2176
+2177
+2178
+2179
+2180
+2181
+2182
+2183
+2184
+2185
+2186
+2187
+2188
+2189
+2190
+2191
+2192
+2193
+2194
+2195
+2196
+2197
+2198
+2199
+2200
+2201
+2202
+2203
+2204
+2205
+2206
+2207
+2208
+2209
+2210
+2211
+2212
+2213
+2214
+2215
+2216
+2217
+2218
+2219
+2220
+2221
+2222
+2223
+2224
+2225
+2226
+2227
+2228
+2229
+2230
+2231
+2232
+2233
+2234
+2235
+2236
+2237
+2238
+2239
+2240
+2241
+2242
+2243
+2244
+2245
+2246
+2247
+2248
+2249
+2250
+2251
+2252
+2253
+2254
+2255
+2256
+2257
+2258
+2259
+2260
+2261
+2262
+2263
+2264
+2265
+2266
+2267
+2268
+2269
+2270
+2271
+2272
+2273
+2274
+2275
+2276
+2277
+2278
+2279
+2280
+2281
+2282
+2283
+2284
+2285
+2286
+2287
+2288
+2289
+2290
+2291
+2292
+2293
+2294
+2295
+2296
+2297
+2298
+2299
+2300
+2301
+2302
+2303
+2304
+2305
+2306
+2307
+2308
+2309
+2310
+2311
+2312
+2313
+2314
+2315
+2316
+2317
+2318
+2319
+2320
+2321
+2322
+2323
+2324
+2325
+2326
+2327
+2328
+2329
+2330
+2331
+2332
+2333
+2334
+2335
+2336
+2337
+2338
+2339
+2340
+2341
+2342
+2343
+2344
+2345
+2346
+2347
+2348
+2349
+2350
+2351
+2352
+2353
+2354
+2355
+2356
+2357
+2358
+2359
+2360
+2361
+2362
+2363
+2364
+2365
+2366
+2367
+2368
+2369
+2370
+2371
+2372
+2373
+2374
+2375
+2376
+2377
+2378
+2379
+2380
+2381
+2382
+2383
+2384
+2385
+2386
+2387
+2388
+2389
+2390
+2391
+2392
+2393
+2394
/* Menu layout in-memory data structure (a custom "DOM tree") */
+
+/*
+ * Copyright (C) 2002 - 2004 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include "menu-layout.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "entry-directories.h"
+#include "menu-util.h"
+
+typedef struct MenuLayoutNodeMenu          MenuLayoutNodeMenu;
+typedef struct MenuLayoutNodeRoot          MenuLayoutNodeRoot;
+typedef struct MenuLayoutNodeLegacyDir     MenuLayoutNodeLegacyDir;
+typedef struct MenuLayoutNodeMergeFile     MenuLayoutNodeMergeFile;
+typedef struct MenuLayoutNodeDefaultLayout MenuLayoutNodeDefaultLayout;
+typedef struct MenuLayoutNodeMenuname      MenuLayoutNodeMenuname;
+typedef struct MenuLayoutNodeMerge         MenuLayoutNodeMerge;
+
+struct MenuLayoutNode
+{
+  /* Node lists are circular, for length-one lists
+   * prev/next point back to the node itself.
+   */
+  MenuLayoutNode *prev;
+  MenuLayoutNode *next;
+  MenuLayoutNode *parent;
+  MenuLayoutNode *children;
+
+  char *content;
+
+  guint refcount : 20;
+  guint type : 7;
+};
+
+struct MenuLayoutNodeRoot
+{
+  MenuLayoutNode node;
+
+  char *basedir;
+  char *name;
+
+  GMainContext *main_context;
+
+  GSList *monitors;
+  GSource *monitors_idle_handler;
+};
+
+struct MenuLayoutNodeMenu
+{
+  MenuLayoutNode node;
+
+  MenuLayoutNode *name_node; /* cache of the <Name> node */
+
+  EntryDirectoryList *app_dirs;
+  EntryDirectoryList *dir_dirs;
+};
+
+struct MenuLayoutNodeLegacyDir
+{
+  MenuLayoutNode node;
+
+  char *prefix;
+};
+
+struct MenuLayoutNodeMergeFile
+{
+  MenuLayoutNode node;
+
+  MenuMergeFileType type;
+};
+
+struct MenuLayoutNodeDefaultLayout
+{
+  MenuLayoutNode node;
+
+  MenuLayoutValues layout_values;
+};
+
+struct MenuLayoutNodeMenuname
+{
+  MenuLayoutNode node;
+
+  MenuLayoutValues layout_values;
+};
+
+struct MenuLayoutNodeMerge
+{
+  MenuLayoutNode node;
+
+  MenuLayoutMergeType merge_type;
+};
+
+typedef struct
+{
+  MenuLayoutNodeEntriesChangedFunc callback;
+  gpointer                         user_data;
+} MenuLayoutNodeEntriesMonitor;
+
+
+static inline MenuLayoutNode *
+node_next (MenuLayoutNode *node)
+{
+  /* root nodes (no parent) never have siblings */
+  if (node->parent == NULL)
+    return NULL;
+
+  /* circular list */
+  if (node->next == node->parent->children)
+    return NULL;
+
+  return node->next;
+}
+
+static gboolean
+menu_layout_invoke_monitors (MenuLayoutNodeRoot *nr)
+{
+  GSList *tmp;
+
+  g_assert (nr->node.type == MENU_LAYOUT_NODE_ROOT);
+
+  nr->monitors_idle_handler = NULL;
+
+  tmp = nr->monitors;
+  while (tmp != NULL)
+    {
+      MenuLayoutNodeEntriesMonitor *monitor = tmp->data;
+      GSList                       *next    = tmp->next;
+
+      monitor->callback ((MenuLayoutNode *) nr, monitor->user_data);
+
+      tmp = next;
+    }
+
+  return FALSE;
+}
+
+static void
+handle_entry_directory_changed (EntryDirectory *dir,
+                                MenuLayoutNode *node)
+{
+  MenuLayoutNodeRoot *nr;
+
+  g_assert (node->type == MENU_LAYOUT_NODE_MENU);
+
+  nr = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node);
+
+  if (nr->monitors_idle_handler == NULL)
+    {
+      nr->monitors_idle_handler = g_idle_source_new ();
+      g_source_set_callback (nr->monitors_idle_handler,
+                             (GSourceFunc) menu_layout_invoke_monitors, nr, NULL);
+      g_source_attach (nr->monitors_idle_handler, nr->main_context);
+      g_source_unref (nr->monitors_idle_handler);
+    }
+}
+
+static void
+remove_entry_directory_list (MenuLayoutNodeMenu  *nm,
+                             EntryDirectoryList **dirs)
+{
+  if (*dirs)
+    {
+      entry_directory_list_remove_monitors (*dirs,
+                                            (EntryDirectoryChangedFunc) handle_entry_directory_changed,
+                                            nm);
+      entry_directory_list_unref (*dirs);
+      *dirs = NULL;
+    }
+}
+
+MenuLayoutNode *
+menu_layout_node_ref (MenuLayoutNode *node)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+
+  node->refcount += 1;
+
+  return node;
+}
+
+void
+menu_layout_node_unref (MenuLayoutNode *node)
+{
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (node->refcount > 0);
+
+  node->refcount -= 1;
+  if (node->refcount == 0)
+    {
+      MenuLayoutNode *iter;
+
+      iter = node->children;
+      while (iter != NULL)
+        {
+          MenuLayoutNode *next = node_next (iter);
+
+          menu_layout_node_unref (iter);
+
+          iter = next;
+        }
+
+      if (node->type == MENU_LAYOUT_NODE_MENU)
+        {
+          MenuLayoutNodeMenu *nm = (MenuLayoutNodeMenu *) node;
+
+          if (nm->name_node)
+            menu_layout_node_unref (nm->name_node);
+
+          remove_entry_directory_list (nm, &nm->app_dirs);
+          remove_entry_directory_list (nm, &nm->dir_dirs);
+        }
+      else if (node->type == MENU_LAYOUT_NODE_LEGACY_DIR)
+        {
+          MenuLayoutNodeLegacyDir *legacy = (MenuLayoutNodeLegacyDir *) node;
+
+          g_free (legacy->prefix);
+        }
+      else if (node->type == MENU_LAYOUT_NODE_ROOT)
+        {
+          MenuLayoutNodeRoot *nr = (MenuLayoutNodeRoot*) node;
+
+          g_slist_foreach (nr->monitors, (GFunc) g_free, NULL);
+          g_slist_free (nr->monitors);
+
+          if (nr->monitors_idle_handler != NULL)
+            g_source_destroy (nr->monitors_idle_handler);
+          nr->monitors_idle_handler = NULL;
+
+          if (nr->main_context != NULL)
+            g_main_context_unref (nr->main_context);
+          nr->main_context = NULL;
+
+          g_free (nr->basedir);
+          g_free (nr->name);
+        }
+
+      g_free (node->content);
+      g_free (node);
+    }
+}
+
+MenuLayoutNode *
+menu_layout_node_new (MenuLayoutNodeType type)
+{
+  MenuLayoutNode *node;
+
+  switch (type)
+    {
+    case MENU_LAYOUT_NODE_MENU:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMenu, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_LEGACY_DIR:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeLegacyDir, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_ROOT:
+      node = (MenuLayoutNode*) g_new0 (MenuLayoutNodeRoot, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_MERGE_FILE:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMergeFile, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeDefaultLayout, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_MENUNAME:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMenuname, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_MERGE:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMerge, 1);
+      break;
+
+    default:
+      node = g_new0 (MenuLayoutNode, 1);
+      break;
+    }
+
+  node->type = type;
+
+  node->refcount = 1;
+
+  /* we're in a list of one node */
+  node->next = node;
+  node->prev = node;
+
+  return node;
+}
+
+MenuLayoutNode *
+menu_layout_node_get_next (MenuLayoutNode *node)
+{
+  return node_next (node);
+}
+
+MenuLayoutNode *
+menu_layout_node_get_parent (MenuLayoutNode *node)
+{
+  return node->parent;
+}
+
+MenuLayoutNode *
+menu_layout_node_get_children (MenuLayoutNode *node)
+{
+  return node->children;
+}
+
+MenuLayoutNode *
+menu_layout_node_get_root (MenuLayoutNode *node)
+{
+  MenuLayoutNode *parent;
+
+  parent = node;
+  while (parent->parent != NULL)
+    parent = parent->parent;
+
+  g_assert (parent->type == MENU_LAYOUT_NODE_ROOT);
+
+  return parent;
+}
+
+char *
+menu_layout_node_get_content_as_path (MenuLayoutNode *node)
+{
+  if (node->content == NULL)
+    {
+      menu_verbose ("  (node has no content to get as a path)\n");
+      return NULL;
+    }
+
+  if (g_path_is_absolute (node->content))
+    {
+      return g_strdup (node->content);
+    }
+  else
+    {
+      MenuLayoutNodeRoot *root;
+
+      root = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node);
+
+      if (root->basedir == NULL)
+        {
+          menu_verbose ("No basedir available, using \"%s\" as-is\n",
+                        node->content);
+          return g_strdup (node->content);
+        }
+      else
+        {
+          menu_verbose ("Using basedir \"%s\" filename \"%s\"\n",
+                        root->basedir, node->content);
+          return g_build_filename (root->basedir, node->content, NULL);
+        }
+    }
+}
+
+#define RETURN_IF_NO_PARENT(node) G_STMT_START {                  \
+    if ((node)->parent == NULL)                                   \
+      {                                                           \
+        g_warning ("To add siblings to a menu node, "             \
+                   "it must not be the root node, "               \
+                   "and must be linked in below some root node\n" \
+                   "node parent = %p and type = %d",              \
+                   (node)->parent, (node)->type);                 \
+        return;                                                   \
+      }                                                           \
+  } G_STMT_END
+
+#define RETURN_IF_HAS_ENTRY_DIRS(node) G_STMT_START {             \
+    if ((node)->type == MENU_LAYOUT_NODE_MENU &&                  \
+        (((MenuLayoutNodeMenu*)(node))->app_dirs != NULL ||       \
+         ((MenuLayoutNodeMenu*)(node))->dir_dirs != NULL))        \
+      {                                                           \
+        g_warning ("node acquired ->app_dirs or ->dir_dirs "      \
+                   "while not rooted in a tree\n");               \
+        return;                                                   \
+      }                                                           \
+  } G_STMT_END                                                    \
+
+void
+menu_layout_node_insert_before (MenuLayoutNode *node,
+                                MenuLayoutNode *new_sibling)
+{
+  g_return_if_fail (new_sibling != NULL);
+  g_return_if_fail (new_sibling->parent == NULL);
+
+  RETURN_IF_NO_PARENT (node);
+  RETURN_IF_HAS_ENTRY_DIRS (new_sibling);
+
+  new_sibling->next = node;
+  new_sibling->prev = node->prev;
+
+  node->prev = new_sibling;
+  new_sibling->prev->next = new_sibling;
+
+  new_sibling->parent = node->parent;
+
+  if (node == node->parent->children)
+    node->parent->children = new_sibling;
+
+  menu_layout_node_ref (new_sibling);
+}
+
+void
+menu_layout_node_insert_after (MenuLayoutNode *node,
+                               MenuLayoutNode *new_sibling)
+{
+  g_return_if_fail (new_sibling != NULL);
+  g_return_if_fail (new_sibling->parent == NULL);
+
+  RETURN_IF_NO_PARENT (node);
+  RETURN_IF_HAS_ENTRY_DIRS (new_sibling);
+
+  new_sibling->prev = node;
+  new_sibling->next = node->next;
+
+  node->next = new_sibling;
+  new_sibling->next->prev = new_sibling;
+
+  new_sibling->parent = node->parent;
+
+  menu_layout_node_ref (new_sibling);
+}
+
+void
+menu_layout_node_prepend_child (MenuLayoutNode *parent,
+                                MenuLayoutNode *new_child)
+{
+  RETURN_IF_HAS_ENTRY_DIRS (new_child);
+
+  if (parent->children)
+    {
+      menu_layout_node_insert_before (parent->children, new_child);
+    }
+  else
+    {
+      parent->children = menu_layout_node_ref (new_child);
+      new_child->parent = parent;
+    }
+}
+
+void
+menu_layout_node_append_child (MenuLayoutNode *parent,
+                               MenuLayoutNode *new_child)
+{
+  RETURN_IF_HAS_ENTRY_DIRS (new_child);
+
+  if (parent->children)
+    {
+      menu_layout_node_insert_after (parent->children->prev, new_child);
+    }
+  else
+    {
+      parent->children = menu_layout_node_ref (new_child);
+      new_child->parent = parent;
+    }
+}
+
+void
+menu_layout_node_unlink (MenuLayoutNode *node)
+{
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (node->parent != NULL);
+
+  menu_layout_node_steal (node);
+  menu_layout_node_unref (node);
+}
+
+static void
+recursive_clean_entry_directory_lists (MenuLayoutNode *node,
+                                       gboolean        apps)
+{
+  EntryDirectoryList **dirs;
+  MenuLayoutNodeMenu  *nm;
+  MenuLayoutNode      *iter;
+
+  if (node->type != MENU_LAYOUT_NODE_MENU)
+    return;
+
+  nm = (MenuLayoutNodeMenu *) node;
+
+  dirs = apps ? &nm->app_dirs : &nm->dir_dirs;
+
+  if (*dirs == NULL || entry_directory_list_get_length (*dirs) == 0)
+    return; /* child menus continue to have valid lists */
+
+  remove_entry_directory_list (nm, dirs);
+
+  iter = node->children;
+  while (iter != NULL)
+    {
+      if (iter->type == MENU_LAYOUT_NODE_MENU)
+        recursive_clean_entry_directory_lists (iter, apps);
+
+      iter = node_next (iter);
+    }
+}
+
+void
+menu_layout_node_steal (MenuLayoutNode *node)
+{
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (node->parent != NULL);
+
+  switch (node->type)
+    {
+    case MENU_LAYOUT_NODE_NAME:
+      {
+        MenuLayoutNodeMenu *nm = (MenuLayoutNodeMenu *) node->parent;
+
+        if (nm->name_node == node)
+          {
+            menu_layout_node_unref (nm->name_node);
+            nm->name_node = NULL;
+          }
+      }
+      break;
+
+    case MENU_LAYOUT_NODE_APP_DIR:
+      recursive_clean_entry_directory_lists (node->parent, TRUE);
+      break;
+
+    case MENU_LAYOUT_NODE_DIRECTORY_DIR:
+      recursive_clean_entry_directory_lists (node->parent, FALSE);
+      break;
+
+    default:
+      break;
+    }
+
+  if (node->parent && node->parent->children == node)
+    {
+      if (node->next != node)
+        node->parent->children = node->next;
+      else
+        node->parent->children = NULL;
+    }
+
+  /* these are no-ops for length-one node lists */
+  node->prev->next = node->next;
+  node->next->prev = node->prev;
+
+  node->parent = NULL;
+
+  /* point to ourselves, now we're length one */
+  node->next = node;
+  node->prev = node;
+}
+
+MenuLayoutNodeType
+menu_layout_node_get_type (MenuLayoutNode *node)
+{
+  return node->type;
+}
+
+const char *
+menu_layout_node_get_content (MenuLayoutNode *node)
+{
+  return node->content;
+}
+
+void
+menu_layout_node_set_content (MenuLayoutNode *node,
+                              const char     *content)
+{
+  if (node->content == content)
+    return;
+
+  g_free (node->content);
+  node->content = g_strdup (content);
+}
+
+const char *
+menu_layout_node_root_get_name (MenuLayoutNode *node)
+{
+  MenuLayoutNodeRoot *nr;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_ROOT, NULL);
+
+  nr = (MenuLayoutNodeRoot*) node;
+
+  return nr->name;
+}
+
+const char *
+menu_layout_node_root_get_basedir (MenuLayoutNode *node)
+{
+  MenuLayoutNodeRoot *nr;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_ROOT, NULL);
+
+  nr = (MenuLayoutNodeRoot*) node;
+
+  return nr->basedir;
+}
+
+const char *
+menu_layout_node_menu_get_name (MenuLayoutNode *node)
+{
+  MenuLayoutNodeMenu *nm;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL);
+
+  nm = (MenuLayoutNodeMenu*) node;
+
+  if (nm->name_node == NULL)
+    {
+      MenuLayoutNode *iter;
+
+      iter = node->children;
+      while (iter != NULL)
+        {
+          if (iter->type == MENU_LAYOUT_NODE_NAME)
+            {
+              nm->name_node = menu_layout_node_ref (iter);
+              break;
+            }
+
+          iter = node_next (iter);
+        }
+    }
+
+  if (nm->name_node == NULL)
+    return NULL;
+
+  return menu_layout_node_get_content (nm->name_node);
+}
+
+static void
+ensure_dir_lists (MenuLayoutNodeMenu *nm)
+{
+  MenuLayoutNode     *node;
+  MenuLayoutNode     *iter;
+  EntryDirectoryList *app_dirs;
+  EntryDirectoryList *dir_dirs;
+
+  node = (MenuLayoutNode *) nm;
+
+  if (nm->app_dirs && nm->dir_dirs)
+    return;
+
+  app_dirs = NULL;
+  dir_dirs = NULL;
+
+  if (nm->app_dirs == NULL)
+    {
+      app_dirs = entry_directory_list_new ();
+
+      if (node->parent && node->parent->type == MENU_LAYOUT_NODE_MENU)
+        {
+          EntryDirectoryList *dirs;
+
+          if ((dirs = menu_layout_node_menu_get_app_dirs (node->parent)))
+            entry_directory_list_append_list (app_dirs, dirs);
+        }
+    }
+
+  if (nm->dir_dirs == NULL)
+    {
+      dir_dirs = entry_directory_list_new ();
+
+      if (node->parent && node->parent->type == MENU_LAYOUT_NODE_MENU)
+        {
+          EntryDirectoryList *dirs;
+
+          if ((dirs = menu_layout_node_menu_get_directory_dirs (node->parent)))
+            entry_directory_list_append_list (dir_dirs, dirs);
+        }
+    }
+
+  iter = node->children;
+  while (iter != NULL)
+    {
+      EntryDirectory *ed;
+
+      if (app_dirs != NULL && iter->type == MENU_LAYOUT_NODE_APP_DIR)
+        {
+          char *path;
+
+          path = menu_layout_node_get_content_as_path (iter);
+
+          ed = entry_directory_new (DESKTOP_ENTRY_DESKTOP, path);
+          if (ed != NULL)
+            {
+              entry_directory_list_prepend (app_dirs, ed);
+              entry_directory_unref (ed);
+            }
+
+          g_free (path);
+        }
+
+      if (dir_dirs != NULL && iter->type == MENU_LAYOUT_NODE_DIRECTORY_DIR)
+        {
+          char *path;
+
+          path = menu_layout_node_get_content_as_path (iter);
+
+          ed = entry_directory_new (DESKTOP_ENTRY_DIRECTORY, path);
+          if (ed != NULL)
+            {
+              entry_directory_list_prepend (dir_dirs, ed);
+              entry_directory_unref (ed);
+            }
+
+          g_free (path);
+        }
+
+      if (iter->type == MENU_LAYOUT_NODE_LEGACY_DIR)
+        {
+          MenuLayoutNodeLegacyDir *legacy = (MenuLayoutNodeLegacyDir *) iter;
+          char                    *path;
+
+          path = menu_layout_node_get_content_as_path (iter);
+
+          if (app_dirs != NULL) /* we're loading app dirs */
+            {
+              ed = entry_directory_new_legacy (DESKTOP_ENTRY_DESKTOP,
+                                               path,
+                                               legacy->prefix);
+              if (ed != NULL)
+                {
+                  entry_directory_list_prepend (app_dirs, ed);
+                  entry_directory_unref (ed);
+                }
+            }
+
+          if (dir_dirs != NULL) /* we're loading dir dirs */
+            {
+              ed = entry_directory_new_legacy (DESKTOP_ENTRY_DIRECTORY,
+                                               path,
+                                               legacy->prefix);
+              if (ed != NULL)
+                {
+                  entry_directory_list_prepend (dir_dirs, ed);
+                  entry_directory_unref (ed);
+                }
+            }
+
+          g_free (path);
+        }
+
+      iter = node_next (iter);
+    }
+
+  if (app_dirs)
+    {
+      g_assert (nm->app_dirs == NULL);
+
+      nm->app_dirs = app_dirs;
+      entry_directory_list_add_monitors (nm->app_dirs,
+                                         (EntryDirectoryChangedFunc) handle_entry_directory_changed,
+                                         nm);
+    }
+
+  if (dir_dirs)
+    {
+      g_assert (nm->dir_dirs == NULL);
+
+      nm->dir_dirs = dir_dirs;
+      entry_directory_list_add_monitors (nm->dir_dirs,
+                                         (EntryDirectoryChangedFunc) handle_entry_directory_changed,
+                                         nm);
+    }
+}
+
+EntryDirectoryList *
+menu_layout_node_menu_get_app_dirs (MenuLayoutNode *node)
+{
+  MenuLayoutNodeMenu *nm;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL);
+
+  nm = (MenuLayoutNodeMenu *) node;
+
+  ensure_dir_lists (nm);
+
+  return nm->app_dirs;
+}
+
+EntryDirectoryList *
+menu_layout_node_menu_get_directory_dirs (MenuLayoutNode *node)
+{
+  MenuLayoutNodeMenu *nm;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL);
+
+  nm = (MenuLayoutNodeMenu *) node;
+
+  ensure_dir_lists (nm);
+
+  return nm->dir_dirs;
+}
+
+const char *
+menu_layout_node_move_get_old (MenuLayoutNode *node)
+{
+  MenuLayoutNode *iter;
+
+  iter = node->children;
+  while (iter != NULL)
+    {
+      if (iter->type == MENU_LAYOUT_NODE_OLD)
+        return iter->content;
+
+      iter = node_next (iter);
+    }
+
+  return NULL;
+}
+
+const char *
+menu_layout_node_move_get_new (MenuLayoutNode *node)
+{
+  MenuLayoutNode *iter;
+
+  iter = node->children;
+  while (iter != NULL)
+    {
+      if (iter->type == MENU_LAYOUT_NODE_NEW)
+        return iter->content;
+
+      iter = node_next (iter);
+    }
+
+  return NULL;
+}
+
+const char *
+menu_layout_node_legacy_dir_get_prefix (MenuLayoutNode *node)
+{
+  MenuLayoutNodeLegacyDir *legacy;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_LEGACY_DIR, NULL);
+
+  legacy = (MenuLayoutNodeLegacyDir *) node;
+
+  return legacy->prefix;
+}
+
+void
+menu_layout_node_legacy_dir_set_prefix (MenuLayoutNode *node,
+                                        const char     *prefix)
+{
+  MenuLayoutNodeLegacyDir *legacy;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_LEGACY_DIR);
+
+  legacy = (MenuLayoutNodeLegacyDir *) node;
+
+  g_free (legacy->prefix);
+  legacy->prefix = g_strdup (prefix);
+}
+
+MenuMergeFileType
+menu_layout_node_merge_file_get_type (MenuLayoutNode *node)
+{
+  MenuLayoutNodeMergeFile *merge_file;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MERGE_FILE, FALSE);
+
+  merge_file = (MenuLayoutNodeMergeFile *) node;
+
+  return merge_file->type;
+}
+
+void
+menu_layout_node_merge_file_set_type (MenuLayoutNode    *node,
+				      MenuMergeFileType  type)
+{
+  MenuLayoutNodeMergeFile *merge_file;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_MERGE_FILE);
+
+  merge_file = (MenuLayoutNodeMergeFile *) node;
+
+  merge_file->type = type;
+}
+
+MenuLayoutMergeType
+menu_layout_node_merge_get_type (MenuLayoutNode *node)
+{
+  MenuLayoutNodeMerge *merge;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MERGE, 0);
+
+  merge = (MenuLayoutNodeMerge *) node;
+
+  return merge->merge_type;
+}
+
+static void
+menu_layout_node_merge_set_type (MenuLayoutNode *node,
+                                 const char     *merge_type)
+{
+  MenuLayoutNodeMerge *merge;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_MERGE);
+
+  merge = (MenuLayoutNodeMerge *) node;
+
+  merge->merge_type = MENU_LAYOUT_MERGE_NONE;
+
+  if (strcmp (merge_type, "menus") == 0)
+    {
+      merge->merge_type = MENU_LAYOUT_MERGE_MENUS;
+    }
+  else if (strcmp (merge_type, "files") == 0)
+    {
+      merge->merge_type = MENU_LAYOUT_MERGE_FILES;
+    }
+  else if (strcmp (merge_type, "all") == 0)
+    {
+      merge->merge_type = MENU_LAYOUT_MERGE_ALL;
+    }
+}
+
+void
+menu_layout_node_default_layout_get_values (MenuLayoutNode   *node,
+					    MenuLayoutValues *values)
+{
+  MenuLayoutNodeDefaultLayout *default_layout;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT);
+  g_return_if_fail (values != NULL);
+
+  default_layout = (MenuLayoutNodeDefaultLayout *) node;
+
+  *values = default_layout->layout_values;
+}
+
+void
+menu_layout_node_menuname_get_values (MenuLayoutNode   *node,
+				      MenuLayoutValues *values)
+{
+  MenuLayoutNodeMenuname *menuname;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_MENUNAME);
+  g_return_if_fail (values != NULL);
+
+  menuname = (MenuLayoutNodeMenuname *) node;
+
+  *values = menuname->layout_values;
+}
+
+static void
+menu_layout_values_set (MenuLayoutValues *values,
+			const char       *show_empty,
+			const char       *inline_menus,
+			const char       *inline_limit,
+			const char       *inline_header,
+			const char       *inline_alias)
+{
+  values->mask          = MENU_LAYOUT_VALUES_NONE;
+  values->show_empty    = FALSE;
+  values->inline_menus  = FALSE;
+  values->inline_limit  = 4;
+  values->inline_header = FALSE;
+  values->inline_alias  = FALSE;
+
+  if (show_empty != NULL)
+    {
+      values->show_empty = strcmp (show_empty, "true") == 0;
+      values->mask |= MENU_LAYOUT_VALUES_SHOW_EMPTY;
+    }
+
+  if (inline_menus != NULL)
+    {
+      values->inline_menus = strcmp (inline_menus, "true") == 0;
+      values->mask |= MENU_LAYOUT_VALUES_INLINE_MENUS;
+    }
+
+  if (inline_limit != NULL)
+    {
+      char *end;
+      int   limit;
+
+      limit = strtol (inline_limit, &end, 10);
+      if (*end == '\0')
+	{
+	  values->inline_limit = limit;
+	  values->mask |= MENU_LAYOUT_VALUES_INLINE_LIMIT;
+	}
+    }
+
+  if (inline_header != NULL)
+    {
+      values->inline_header = strcmp (inline_header, "true") == 0;
+      values->mask |= MENU_LAYOUT_VALUES_INLINE_HEADER;
+    }
+
+  if (inline_alias != NULL)
+    {
+      values->inline_alias = strcmp (inline_alias, "true") == 0;
+      values->mask |= MENU_LAYOUT_VALUES_INLINE_ALIAS;
+    }
+}
+
+static void
+menu_layout_node_default_layout_set_values (MenuLayoutNode *node,
+					    const char     *show_empty,
+					    const char     *inline_menus,
+					    const char     *inline_limit,
+					    const char     *inline_header,
+					    const char     *inline_alias)
+{
+  MenuLayoutNodeDefaultLayout *default_layout;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT);
+
+  default_layout = (MenuLayoutNodeDefaultLayout *) node;
+
+  menu_layout_values_set (&default_layout->layout_values,
+			  show_empty,
+			  inline_menus,
+			  inline_limit,
+			  inline_header,
+			  inline_alias);
+}
+
+static void
+menu_layout_node_menuname_set_values (MenuLayoutNode *node,
+				      const char     *show_empty,
+				      const char     *inline_menus,
+				      const char     *inline_limit,
+				      const char     *inline_header,
+				      const char     *inline_alias)
+{
+  MenuLayoutNodeMenuname *menuname;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_MENUNAME);
+
+  menuname = (MenuLayoutNodeMenuname *) node;
+
+  menu_layout_values_set (&menuname->layout_values,
+			  show_empty,
+			  inline_menus,
+			  inline_limit,
+			  inline_header,
+			  inline_alias);
+}
+
+void
+menu_layout_node_root_add_entries_monitor (MenuLayoutNode                   *node,
+                                           MenuLayoutNodeEntriesChangedFunc  callback,
+                                           gpointer                          user_data)
+{
+  MenuLayoutNodeEntriesMonitor *monitor;
+  MenuLayoutNodeRoot           *nr;
+  GSList                       *tmp;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_ROOT);
+
+  nr = (MenuLayoutNodeRoot *) node;
+
+  tmp = nr->monitors;
+  while (tmp != NULL)
+    {
+      monitor = tmp->data;
+
+      if (monitor->callback  == callback &&
+          monitor->user_data == user_data)
+        break;
+
+      tmp = tmp->next;
+    }
+
+  if (tmp == NULL)
+    {
+      monitor            = g_new0 (MenuLayoutNodeEntriesMonitor, 1);
+      monitor->callback  = callback;
+      monitor->user_data = user_data;
+
+      nr->monitors = g_slist_append (nr->monitors, monitor);
+    }
+}
+
+void
+menu_layout_node_root_remove_entries_monitor (MenuLayoutNode                   *node,
+                                              MenuLayoutNodeEntriesChangedFunc  callback,
+                                              gpointer                          user_data)
+{
+  MenuLayoutNodeRoot *nr;
+  GSList             *tmp;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_ROOT);
+
+  nr = (MenuLayoutNodeRoot *) node;
+
+  tmp = nr->monitors;
+  while (tmp != NULL)
+    {
+      MenuLayoutNodeEntriesMonitor *monitor = tmp->data;
+      GSList                       *next = tmp->next;
+
+      if (monitor->callback == callback &&
+          monitor->user_data == user_data)
+        {
+          nr->monitors = g_slist_delete_link (nr->monitors, tmp);
+          g_free (monitor);
+        }
+
+      tmp = next;
+    }
+}
+
+
+/*
+ * Menu file parsing
+ */
+
+typedef struct
+{
+  MenuLayoutNode *root;
+  MenuLayoutNode *stack_top;
+} MenuParser;
+
+static void set_error (GError             **err,
+                       GMarkupParseContext *context,
+                       int                  error_domain,
+                       int                  error_code,
+                       const char          *format,
+                       ...) G_GNUC_PRINTF (5, 6);
+
+static void add_context_to_error (GError             **err,
+                                  GMarkupParseContext *context);
+
+static void start_element_handler (GMarkupParseContext  *context,
+                                   const char           *element_name,
+                                   const char          **attribute_names,
+                                   const char          **attribute_values,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void end_element_handler   (GMarkupParseContext  *context,
+                                   const char           *element_name,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void text_handler          (GMarkupParseContext  *context,
+                                   const char           *text,
+                                   gsize                 text_len,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void passthrough_handler   (GMarkupParseContext  *context,
+                                   const char           *passthrough_text,
+                                   gsize                 text_len,
+                                   gpointer              user_data,
+                                   GError              **error);
+
+
+static GMarkupParser menu_funcs = {
+  start_element_handler,
+  end_element_handler,
+  text_handler,
+  passthrough_handler,
+  NULL
+};
+
+static void
+set_error (GError              **err,
+           GMarkupParseContext  *context,
+           int                   error_domain,
+           int                   error_code,
+           const char           *format,
+           ...)
+{
+  int      line, ch;
+  va_list  args;
+  char    *str;
+
+  g_markup_parse_context_get_position (context, &line, &ch);
+
+  va_start (args, format);
+  str = g_strdup_vprintf (format, args);
+  va_end (args);
+
+  g_set_error (err, error_domain, error_code,
+               "Line %d character %d: %s",
+               line, ch, str);
+
+  g_free (str);
+}
+
+static void
+add_context_to_error (GError             **err,
+                      GMarkupParseContext *context)
+{
+  int   line, ch;
+  char *str;
+
+  if (err == NULL || *err == NULL)
+    return;
+
+  g_markup_parse_context_get_position (context, &line, &ch);
+
+  str = g_strdup_printf ("Line %d character %d: %s",
+                         line, ch, (*err)->message);
+  g_free ((*err)->message);
+  (*err)->message = str;
+}
+
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
+
+typedef struct
+{
+  const char  *name;
+  const char **retloc;
+} LocateAttr;
+
+static gboolean
+locate_attributes (GMarkupParseContext  *context,
+                   const char           *element_name,
+                   const char          **attribute_names,
+                   const char          **attribute_values,
+                   GError              **error,
+                   const char           *first_attribute_name,
+                   const char          **first_attribute_retloc,
+                   ...)
+{
+#define MAX_ATTRS 24
+  LocateAttr   attrs[MAX_ATTRS];
+  int          n_attrs;
+  va_list      args;
+  const char  *name;
+  const char **retloc;
+  gboolean     retval;
+  int          i;
+
+  g_return_val_if_fail (first_attribute_name != NULL, FALSE);
+  g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
+
+  retval = TRUE;
+
+  n_attrs = 1;
+  attrs[0].name = first_attribute_name;
+  attrs[0].retloc = first_attribute_retloc;
+  *first_attribute_retloc = NULL;
+
+  va_start (args, first_attribute_retloc);
+
+  name = va_arg (args, const char *);
+  retloc = va_arg (args, const char **);
+
+  while (name != NULL)
+    {
+      g_return_val_if_fail (retloc != NULL, FALSE);
+
+      g_assert (n_attrs < MAX_ATTRS);
+
+      attrs[n_attrs].name = name;
+      attrs[n_attrs].retloc = retloc;
+      n_attrs += 1;
+      *retloc = NULL;
+
+      name = va_arg (args, const char *);
+      retloc = va_arg (args, const char **);
+    }
+
+  va_end (args);
+
+  i = 0;
+  while (attribute_names[i])
+    {
+      int j;
+
+      j = 0;
+      while (j < n_attrs)
+        {
+          if (strcmp (attrs[j].name, attribute_names[i]) == 0)
+            {
+              retloc = attrs[j].retloc;
+
+              if (*retloc != NULL)
+                {
+                  set_error (error, context,
+                             G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                             "Attribute \"%s\" repeated twice on the same <%s> element",
+                             attrs[j].name, element_name);
+                  retval = FALSE;
+                  goto out;
+                }
+
+              *retloc = attribute_values[i];
+              break;
+            }
+
+          ++j;
+        }
+
+      if (j == n_attrs)
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                     "Attribute \"%s\" is invalid on <%s> element in this context",
+                     attribute_names[i], element_name);
+          retval = FALSE;
+          goto out;
+        }
+
+      ++i;
+    }
+
+ out:
+  return retval;
+
+#undef MAX_ATTRS
+}
+
+static gboolean
+check_no_attributes (GMarkupParseContext  *context,
+                     const char           *element_name,
+                     const char          **attribute_names,
+                     const char          **attribute_values,
+                     GError              **error)
+{
+  if (attribute_names[0] != NULL)
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                 "Attribute \"%s\" is invalid on <%s> element in this context",
+                 attribute_names[0], element_name);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static int
+has_child_of_type (MenuLayoutNode     *node,
+                   MenuLayoutNodeType  type)
+{
+  MenuLayoutNode *iter;
+
+  iter = node->children;
+  while (iter)
+    {
+      if (iter->type == type)
+        return TRUE;
+
+      iter = node_next (iter);
+    }
+
+  return FALSE;
+}
+
+static void
+push_node (MenuParser         *parser,
+           MenuLayoutNodeType  type)
+{
+  MenuLayoutNode *node;
+
+  node = menu_layout_node_new (type);
+  menu_layout_node_append_child (parser->stack_top, node);
+  menu_layout_node_unref (node);
+
+  parser->stack_top = node;
+}
+
+static void
+start_menu_element (MenuParser           *parser,
+                    GMarkupParseContext  *context,
+                    const char           *element_name,
+                    const char          **attribute_names,
+                    const char          **attribute_values,
+                    GError              **error)
+{
+  if (!check_no_attributes (context, element_name,
+                            attribute_names, attribute_values,
+                            error))
+    return;
+
+  if (!(parser->stack_top->type == MENU_LAYOUT_NODE_ROOT ||
+        parser->stack_top->type == MENU_LAYOUT_NODE_MENU))
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                 "<Menu> element can only appear below other <Menu> elements or at toplevel\n");
+    }
+  else
+    {
+      push_node (parser, MENU_LAYOUT_NODE_MENU);
+    }
+}
+
+static void
+start_menu_child_element (MenuParser           *parser,
+                          GMarkupParseContext  *context,
+                          const char           *element_name,
+                          const char          **attribute_names,
+                          const char          **attribute_values,
+                          GError              **error)
+{
+  if (ELEMENT_IS ("LegacyDir"))
+    {
+      const char *prefix;
+
+      push_node (parser, MENU_LAYOUT_NODE_LEGACY_DIR);
+
+      if (!locate_attributes (context, element_name,
+                              attribute_names, attribute_values,
+                              error,
+                              "prefix", &prefix,
+                              NULL))
+        return;
+
+      menu_layout_node_legacy_dir_set_prefix (parser->stack_top, prefix);
+    }
+  else if (ELEMENT_IS ("MergeFile"))
+    {
+      const char *type;
+
+      push_node (parser, MENU_LAYOUT_NODE_MERGE_FILE);
+
+      if (!locate_attributes (context, element_name,
+                              attribute_names, attribute_values,
+                              error,
+                              "type", &type,
+                              NULL))
+        return;
+
+      if (type != NULL && strcmp (type, "parent") == 0)
+	{
+	  menu_layout_node_merge_file_set_type (parser->stack_top,
+						MENU_MERGE_FILE_TYPE_PARENT);
+	}
+    }
+  else if (ELEMENT_IS ("DefaultLayout"))
+    {
+      const char *show_empty;
+      const char *inline_menus;
+      const char *inline_limit;
+      const char *inline_header;
+      const char *inline_alias;
+
+      push_node (parser, MENU_LAYOUT_NODE_DEFAULT_LAYOUT);
+
+      locate_attributes (context, element_name,
+                         attribute_names, attribute_values,
+                         error,
+                         "show_empty",    &show_empty,
+                         "inline",        &inline_menus,
+                         "inline_limit",  &inline_limit,
+                         "inline_header", &inline_header,
+                         "inline_alias",  &inline_alias,
+                         NULL);
+
+      menu_layout_node_default_layout_set_values (parser->stack_top,
+						  show_empty,
+						  inline_menus,
+						  inline_limit,
+						  inline_header,
+						  inline_alias);
+    }
+  else
+    {
+      if (!check_no_attributes (context, element_name,
+                                attribute_names, attribute_values,
+                                error))
+        return;
+
+      if (ELEMENT_IS ("AppDir"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_APP_DIR);
+        }
+      else if (ELEMENT_IS ("DefaultAppDirs"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DEFAULT_APP_DIRS);
+        }
+      else if (ELEMENT_IS ("DirectoryDir"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DIRECTORY_DIR);
+        }
+      else if (ELEMENT_IS ("DefaultDirectoryDirs"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS);
+        }
+      else if (ELEMENT_IS ("DefaultMergeDirs"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS);
+        }
+      else if (ELEMENT_IS ("Name"))
+        {
+          if (has_child_of_type (parser->stack_top, MENU_LAYOUT_NODE_NAME))
+            {
+              set_error (error, context,
+                         G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         "Multiple <Name> elements in a <Menu> element is not allowed\n");
+              return;
+            }
+
+          push_node (parser, MENU_LAYOUT_NODE_NAME);
+        }
+      else if (ELEMENT_IS ("Directory"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DIRECTORY);
+        }
+      else if (ELEMENT_IS ("OnlyUnallocated"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_ONLY_UNALLOCATED);
+        }
+      else if (ELEMENT_IS ("NotOnlyUnallocated"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED);
+        }
+      else if (ELEMENT_IS ("Include"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_INCLUDE);
+        }
+      else if (ELEMENT_IS ("Exclude"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_EXCLUDE);
+        }
+      else if (ELEMENT_IS ("MergeDir"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_MERGE_DIR);
+        }
+      else if (ELEMENT_IS ("KDELegacyDirs"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_KDE_LEGACY_DIRS);
+        }
+      else if (ELEMENT_IS ("Move"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_MOVE);
+        }
+      else if (ELEMENT_IS ("Deleted"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DELETED);
+
+        }
+      else if (ELEMENT_IS ("NotDeleted"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_NOT_DELETED);
+        }
+      else if (ELEMENT_IS ("Layout"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_LAYOUT);
+        }
+      else
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                     "Element <%s> may not appear below <%s>\n",
+                     element_name, "Menu");
+        }
+    }
+}
+
+static void
+start_matching_rule_element (MenuParser           *parser,
+                             GMarkupParseContext  *context,
+                             const char           *element_name,
+                             const char          **attribute_names,
+                             const char          **attribute_values,
+                             GError              **error)
+{
+  if (!check_no_attributes (context, element_name,
+                            attribute_names, attribute_values,
+                            error))
+    return;
+
+
+  if (ELEMENT_IS ("Filename"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_FILENAME);
+    }
+  else if (ELEMENT_IS ("Category"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_CATEGORY);
+    }
+  else if (ELEMENT_IS ("All"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_ALL);
+    }
+  else if (ELEMENT_IS ("And"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_AND);
+    }
+  else if (ELEMENT_IS ("Or"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_OR);
+    }
+  else if (ELEMENT_IS ("Not"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_NOT);
+    }
+  else
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                 "Element <%s> may not appear in this context\n",
+                 element_name);
+    }
+}
+
+static void
+start_move_child_element (MenuParser           *parser,
+                          GMarkupParseContext  *context,
+                          const char           *element_name,
+                          const char          **attribute_names,
+                          const char          **attribute_values,
+                          GError              **error)
+{
+  if (!check_no_attributes (context, element_name,
+                            attribute_names, attribute_values,
+                            error))
+    return;
+
+  if (ELEMENT_IS ("Old"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_OLD);
+    }
+  else if (ELEMENT_IS ("New"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_NEW);
+    }
+  else
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                 "Element <%s> may not appear below <%s>\n",
+                 element_name, "Move");
+    }
+}
+
+static void
+start_layout_child_element (MenuParser           *parser,
+                            GMarkupParseContext  *context,
+                            const char           *element_name,
+                            const char          **attribute_names,
+                            const char          **attribute_values,
+                            GError              **error)
+{
+  if (ELEMENT_IS ("Menuname"))
+    {
+      const char *show_empty;
+      const char *inline_menus;
+      const char *inline_limit;
+      const char *inline_header;
+      const char *inline_alias;
+
+      push_node (parser, MENU_LAYOUT_NODE_MENUNAME);
+
+      locate_attributes (context, element_name,
+                         attribute_names, attribute_values,
+                         error,
+                         "show_empty",    &show_empty,
+                         "inline",        &inline_menus,
+                         "inline_limit",  &inline_limit,
+                         "inline_header", &inline_header,
+                         "inline_alias",  &inline_alias,
+                         NULL);
+
+       menu_layout_node_menuname_set_values (parser->stack_top,
+					     show_empty,
+					     inline_menus,
+					     inline_limit,
+					     inline_header,
+					     inline_alias);
+    }
+  else if (ELEMENT_IS ("Merge"))
+    {
+        const char *type;
+
+        push_node (parser, MENU_LAYOUT_NODE_MERGE);
+
+        locate_attributes (context, element_name,
+                           attribute_names, attribute_values,
+                           error,
+                           "type", &type,
+                           NULL);
+
+	menu_layout_node_merge_set_type (parser->stack_top, type);
+    }
+  else
+    {
+      if (!check_no_attributes (context, element_name,
+                                attribute_names, attribute_values,
+                                error))
+        return;
+
+      if (ELEMENT_IS ("Filename"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_FILENAME);
+        }
+      else if (ELEMENT_IS ("Separator"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_SEPARATOR);
+        }
+      else
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                     "Element <%s> may not appear below <%s>\n",
+                     element_name, "Move");
+        }
+    }
+}
+
+static void
+start_element_handler (GMarkupParseContext   *context,
+                       const char            *element_name,
+                       const char           **attribute_names,
+                       const char           **attribute_values,
+                       gpointer               user_data,
+                       GError               **error)
+{
+  MenuParser *parser = user_data;
+
+  if (ELEMENT_IS ("Menu"))
+    {
+      if (parser->stack_top == parser->root &&
+          has_child_of_type (parser->root, MENU_LAYOUT_NODE_MENU))
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                     "Multiple root elements in menu file, only one toplevel <Menu> is allowed\n");
+          return;
+        }
+
+      start_menu_element (parser, context, element_name,
+                          attribute_names, attribute_values,
+                          error);
+    }
+  else if (parser->stack_top == parser->root)
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                 "Root element in a menu file must be <Menu>, not <%s>\n",
+                 element_name);
+    }
+  else if (parser->stack_top->type == MENU_LAYOUT_NODE_MENU)
+    {
+      start_menu_child_element (parser, context, element_name,
+                                attribute_names, attribute_values,
+                                error);
+    }
+  else if (parser->stack_top->type == MENU_LAYOUT_NODE_INCLUDE ||
+           parser->stack_top->type == MENU_LAYOUT_NODE_EXCLUDE ||
+           parser->stack_top->type == MENU_LAYOUT_NODE_AND     ||
+           parser->stack_top->type == MENU_LAYOUT_NODE_OR      ||
+           parser->stack_top->type == MENU_LAYOUT_NODE_NOT)
+    {
+      start_matching_rule_element (parser, context, element_name,
+                                   attribute_names, attribute_values,
+                                   error);
+    }
+  else if (parser->stack_top->type == MENU_LAYOUT_NODE_MOVE)
+    {
+      start_move_child_element (parser, context, element_name,
+                                attribute_names, attribute_values,
+                                error);
+    }
+  else if (parser->stack_top->type == MENU_LAYOUT_NODE_LAYOUT ||
+           parser->stack_top->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT)
+    {
+      start_layout_child_element (parser, context, element_name,
+                                  attribute_names, attribute_values,
+                                  error);
+    }
+  else
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                 "Element <%s> may not appear in this context\n",
+                 element_name);
+    }
+
+  add_context_to_error (error, context);
+}
+
+/* we want to make sure that the <Layout> or <DefaultLayout> is either empty,
+ * or contain one <Merge> of type "all", or contain one <Merge> of type "menus"
+ * and one <Merge> of type "files". If this is not the case, we try to clean up
+ * things:
+ *  + if there is at least one <Merge> of type "all", then we only keep the
+ *    last <Merge> of type "all" and remove all others <Merge>
+ *  + if there is no <Merge> with type "all", we keep only the last <Merge> of
+ *    type "menus" and the last <Merge> of type "files". If there's no <Merge>
+ *    of type "menus" we append one, and then if there's no <Merge> of type
+ *    "files", we append one. (So menus are before files)
+ */
+static gboolean
+fixup_layout_node (GMarkupParseContext   *context,
+                   MenuParser            *parser,
+                   MenuLayoutNode        *node,
+                   GError              **error)
+{
+  MenuLayoutNode *child;
+  MenuLayoutNode *last_all;
+  MenuLayoutNode *last_menus;
+  MenuLayoutNode *last_files;
+  int             n_all;
+  int             n_menus;
+  int             n_files;
+
+  if (!node->children)
+    {
+      return TRUE;
+    }
+
+  last_all   = NULL;
+  last_menus = NULL;
+  last_files = NULL;
+  n_all   = 0;
+  n_menus = 0;
+  n_files = 0;
+
+  child = node->children;
+  while (child != NULL)
+    {
+      switch (child->type)
+        {
+        case MENU_LAYOUT_NODE_MERGE:
+	  switch (menu_layout_node_merge_get_type (child))
+	    {
+	    case MENU_LAYOUT_MERGE_NONE:
+	      break;
+
+	    case MENU_LAYOUT_MERGE_MENUS:
+	      last_menus = child;
+	      n_menus++;
+	      break;
+
+	    case MENU_LAYOUT_MERGE_FILES:
+	      last_files = child;
+	      n_files++;
+	      break;
+
+	    case MENU_LAYOUT_MERGE_ALL:
+	      last_all = child;
+	      n_all++;
+	      break;
+
+	    default:
+	      g_assert_not_reached ();
+	      break;
+	    }
+          break;
+
+	  default:
+	    break;
+	}
+
+      child = node_next (child);
+    }
+
+  if ((n_all == 1 && n_menus == 0 && n_files == 0) ||
+      (n_all == 0 && n_menus == 1 && n_files == 1))
+    {
+      return TRUE;
+    }
+  else if (n_all > 1 || n_menus > 1 || n_files > 1 ||
+           (n_all == 1 && (n_menus != 0 || n_files != 0)))
+    {
+      child = node->children;
+      while (child != NULL)
+	{
+	  MenuLayoutNode *next;
+
+	  next = node_next (child);
+
+	  switch (child->type)
+	    {
+	    case MENU_LAYOUT_NODE_MERGE:
+	      switch (menu_layout_node_merge_get_type (child))
+		{
+		case MENU_LAYOUT_MERGE_NONE:
+		  break;
+
+		case MENU_LAYOUT_MERGE_MENUS:
+		  if (n_all || last_menus != child)
+		    {
+		      menu_verbose ("removing duplicated merge menus element\n");
+		      menu_layout_node_unlink (child);
+		    }
+		  break;
+
+		case MENU_LAYOUT_MERGE_FILES:
+		  if (n_all || last_files != child)
+		    {
+		      menu_verbose ("removing duplicated merge files element\n");
+		      menu_layout_node_unlink (child);
+		    }
+		  break;
+
+		case MENU_LAYOUT_MERGE_ALL:
+		  if (last_all != child)
+		    {
+		      menu_verbose ("removing duplicated merge all element\n");
+		      menu_layout_node_unlink (child);
+		    }
+		  break;
+
+		default:
+		  g_assert_not_reached ();
+		  break;
+		}
+	      break;
+
+	      default:
+		break;
+	    }
+
+	  child = next;
+	}
+    }
+
+  if (n_all == 0 && n_menus == 0)
+    {
+      last_menus = menu_layout_node_new (MENU_LAYOUT_NODE_MERGE);
+      menu_layout_node_merge_set_type (last_menus, "menus");
+      menu_verbose ("appending missing merge menus element\n");
+      menu_layout_node_append_child (node, last_menus);
+    }
+
+  if (n_all == 0 && n_files == 0)
+    {
+      last_files = menu_layout_node_new (MENU_LAYOUT_NODE_MERGE);
+      menu_layout_node_merge_set_type (last_files, "files");
+      menu_verbose ("appending missing merge files element\n");
+      menu_layout_node_append_child (node, last_files);
+    }
+
+  return TRUE;
+}
+
+/* we want to a) check that we have old-new pairs and b) canonicalize
+ * such that each <Move> has exactly one old-new pair
+ */
+static gboolean
+fixup_move_node (GMarkupParseContext   *context,
+                 MenuParser            *parser,
+                 MenuLayoutNode        *node,
+                 GError              **error)
+{
+  MenuLayoutNode *child;
+  int             n_old;
+  int             n_new;
+
+  n_old = 0;
+  n_new = 0;
+
+  child = node->children;
+  while (child != NULL)
+    {
+      switch (child->type)
+        {
+        case MENU_LAYOUT_NODE_OLD:
+          if (n_new != n_old)
+            {
+              set_error (error, context,
+                         G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         "<Old>/<New> elements not paired properly\n");
+              return FALSE;
+            }
+
+          n_old += 1;
+
+          break;
+
+        case MENU_LAYOUT_NODE_NEW:
+          n_new += 1;
+
+          if (n_new != n_old)
+            {
+              set_error (error, context,
+                         G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         "<Old>/<New> elements not paired properly\n");
+              return FALSE;
+            }
+
+          break;
+
+        default:
+          g_assert_not_reached ();
+          break;
+        }
+
+      child = node_next (child);
+    }
+
+  if (n_new == 0 || n_old == 0)
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                 "<Old>/<New> elements missing under <Move>\n");
+      return FALSE;
+    }
+
+  g_assert (n_new == n_old);
+  g_assert ((n_new + n_old) % 2 == 0);
+
+  if (n_new > 1)
+    {
+      MenuLayoutNode *prev;
+      MenuLayoutNode *append_after;
+
+      /* Need to split the <Move> into multiple <Move> */
+
+      n_old = 0;
+      n_new = 0;
+      prev = NULL;
+      append_after = node;
+
+      child = node->children;
+      while (child != NULL)
+        {
+          MenuLayoutNode *next;
+
+          next = node_next (child);
+
+          switch (child->type)
+            {
+            case MENU_LAYOUT_NODE_OLD:
+              n_old += 1;
+              break;
+
+            case MENU_LAYOUT_NODE_NEW:
+              n_new += 1;
+              break;
+
+            default:
+              g_assert_not_reached ();
+              break;
+            }
+
+          if (n_old == n_new &&
+              n_old > 1)
+            {
+              /* Move the just-completed pair */
+              MenuLayoutNode *new_move;
+
+              g_assert (prev != NULL);
+
+              new_move = menu_layout_node_new (MENU_LAYOUT_NODE_MOVE);
+              menu_verbose ("inserting new_move after append_after\n");
+              menu_layout_node_insert_after (append_after, new_move);
+              append_after = new_move;
+
+              menu_layout_node_steal (prev);
+              menu_layout_node_steal (child);
+
+              menu_verbose ("appending prev to new_move\n");
+              menu_layout_node_append_child (new_move, prev);
+              menu_verbose ("appending child to new_move\n");
+              menu_layout_node_append_child (new_move, child);
+
+              menu_verbose ("Created new move element old = %s new = %s\n",
+                            menu_layout_node_move_get_old (new_move),
+                            menu_layout_node_move_get_new (new_move));
+
+              menu_layout_node_unref (new_move);
+              menu_layout_node_unref (prev);
+              menu_layout_node_unref (child);
+
+              prev = NULL;
+            }
+          else
+            {
+              prev = child;<--- prev is assigned
+            }
+
+          prev = child;<--- prev is overwritten
+          child = next;
+        }
+    }
+
+  return TRUE;
+}
+
+static void
+end_element_handler (GMarkupParseContext  *context,
+                     const char           *element_name,
+                     gpointer              user_data,
+                     GError              **error)
+{
+  MenuParser *parser = user_data;
+
+  g_assert (parser->stack_top != NULL);
+
+  switch (parser->stack_top->type)
+    {
+    case MENU_LAYOUT_NODE_APP_DIR:
+    case MENU_LAYOUT_NODE_DIRECTORY_DIR:
+    case MENU_LAYOUT_NODE_NAME:
+    case MENU_LAYOUT_NODE_DIRECTORY:
+    case MENU_LAYOUT_NODE_FILENAME:
+    case MENU_LAYOUT_NODE_CATEGORY:
+    case MENU_LAYOUT_NODE_MERGE_DIR:
+    case MENU_LAYOUT_NODE_LEGACY_DIR:
+    case MENU_LAYOUT_NODE_OLD:
+    case MENU_LAYOUT_NODE_NEW:
+    case MENU_LAYOUT_NODE_MENUNAME:
+      if (menu_layout_node_get_content (parser->stack_top) == NULL)
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                     "Element <%s> is required to contain text and was empty\n",
+                     element_name);
+          goto out;
+        }
+      break;
+
+    case MENU_LAYOUT_NODE_MENU:
+      if (!has_child_of_type (parser->stack_top, MENU_LAYOUT_NODE_NAME))
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                     "<Menu> elements are required to contain a <Name> element\n");
+          goto out;
+        }
+      break;
+
+    case MENU_LAYOUT_NODE_ROOT:
+    case MENU_LAYOUT_NODE_PASSTHROUGH:
+    case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
+    case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
+    case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
+    case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
+    case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
+    case MENU_LAYOUT_NODE_INCLUDE:
+    case MENU_LAYOUT_NODE_EXCLUDE:
+    case MENU_LAYOUT_NODE_ALL:
+    case MENU_LAYOUT_NODE_AND:
+    case MENU_LAYOUT_NODE_OR:
+    case MENU_LAYOUT_NODE_NOT:
+    case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
+    case MENU_LAYOUT_NODE_DELETED:
+    case MENU_LAYOUT_NODE_NOT_DELETED:
+    case MENU_LAYOUT_NODE_SEPARATOR:
+    case MENU_LAYOUT_NODE_MERGE:
+    case MENU_LAYOUT_NODE_MERGE_FILE:
+      break;
+
+    case MENU_LAYOUT_NODE_LAYOUT:
+    case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
+      if (!fixup_layout_node (context, parser, parser->stack_top, error))
+        goto out;
+      break;
+
+    case MENU_LAYOUT_NODE_MOVE:
+      if (!fixup_move_node (context, parser, parser->stack_top, error))
+        goto out;
+      break;
+    }
+
+ out:
+  parser->stack_top = parser->stack_top->parent;
+}
+
+static gboolean
+all_whitespace (const char *text,
+                int         text_len)
+{
+  const char *p;
+  const char *end;
+
+  p = text;
+  end = text + text_len;
+
+  while (p != end)
+    {
+      if (!g_ascii_isspace (*p))
+        return FALSE;
+
+      p = g_utf8_next_char (p);
+    }
+
+  return TRUE;
+}
+
+static void
+text_handler (GMarkupParseContext  *context,
+              const char           *text,
+              gsize                 text_len,
+              gpointer              user_data,
+              GError              **error)
+{
+  MenuParser *parser = user_data;
+
+  switch (parser->stack_top->type)
+    {
+    case MENU_LAYOUT_NODE_APP_DIR:
+    case MENU_LAYOUT_NODE_DIRECTORY_DIR:
+    case MENU_LAYOUT_NODE_NAME:
+    case MENU_LAYOUT_NODE_DIRECTORY:
+    case MENU_LAYOUT_NODE_FILENAME:
+    case MENU_LAYOUT_NODE_CATEGORY:
+    case MENU_LAYOUT_NODE_MERGE_FILE:
+    case MENU_LAYOUT_NODE_MERGE_DIR:
+    case MENU_LAYOUT_NODE_LEGACY_DIR:
+    case MENU_LAYOUT_NODE_OLD:
+    case MENU_LAYOUT_NODE_NEW:
+    case MENU_LAYOUT_NODE_MENUNAME:
+      g_assert (menu_layout_node_get_content (parser->stack_top) == NULL);
+
+      menu_layout_node_set_content (parser->stack_top, text);
+      break;
+
+    case MENU_LAYOUT_NODE_ROOT:
+    case MENU_LAYOUT_NODE_PASSTHROUGH:
+    case MENU_LAYOUT_NODE_MENU:
+    case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
+    case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
+    case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
+    case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
+    case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
+    case MENU_LAYOUT_NODE_INCLUDE:
+    case MENU_LAYOUT_NODE_EXCLUDE:
+    case MENU_LAYOUT_NODE_ALL:
+    case MENU_LAYOUT_NODE_AND:
+    case MENU_LAYOUT_NODE_OR:
+    case MENU_LAYOUT_NODE_NOT:
+    case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
+    case MENU_LAYOUT_NODE_MOVE:
+    case MENU_LAYOUT_NODE_DELETED:
+    case MENU_LAYOUT_NODE_NOT_DELETED:
+    case MENU_LAYOUT_NODE_LAYOUT:
+    case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
+    case MENU_LAYOUT_NODE_SEPARATOR:
+    case MENU_LAYOUT_NODE_MERGE:
+      if (!all_whitespace (text, text_len))
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                     "No text is allowed inside element <%s>",
+                     g_markup_parse_context_get_element (context));
+        }
+      break;
+    }
+
+  add_context_to_error (error, context);
+}
+
+static void
+passthrough_handler (GMarkupParseContext  *context,
+                     const char           *passthrough_text,
+                     gsize                 text_len,
+                     gpointer              user_data,
+                     GError              **error)
+{
+  MenuParser *parser = user_data;
+  MenuLayoutNode *node;
+
+  /* don't push passthrough on the stack, it's not an element */
+
+  node = menu_layout_node_new (MENU_LAYOUT_NODE_PASSTHROUGH);
+  menu_layout_node_set_content (node, passthrough_text);
+
+  menu_layout_node_append_child (parser->stack_top, node);
+  menu_layout_node_unref (node);
+
+  add_context_to_error (error, context);
+}
+
+static void
+menu_parser_init (MenuParser *parser)
+{
+  parser->root = menu_layout_node_new (MENU_LAYOUT_NODE_ROOT);
+  parser->stack_top = parser->root;
+}
+
+static void
+menu_parser_free (MenuParser *parser)
+{
+  if (parser->root)
+    menu_layout_node_unref (parser->root);
+}
+
+MenuLayoutNode *
+menu_layout_load (const char  *filename,
+                  const char  *non_prefixed_basename,
+                  GError     **err)
+{
+  GMainContext        *main_context;
+  GMarkupParseContext *context;
+  MenuLayoutNodeRoot  *root;
+  MenuLayoutNode      *retval;
+  MenuParser           parser;
+  GError              *error;
+  GString             *str;
+  char                *text;
+  char                *s;
+  gsize                length;
+
+  text = NULL;
+  length = 0;
+  retval = NULL;
+  context = NULL;
+
+  main_context = g_main_context_get_thread_default ();
+
+  menu_verbose ("Loading \"%s\" from disk\n", filename);
+
+  if (!g_file_get_contents (filename,
+                            &text,
+                            &length,
+                            err))
+    {
+      menu_verbose ("Failed to load \"%s\"\n",
+                    filename);
+      return NULL;
+    }
+
+  g_assert (text != NULL);
+
+  menu_parser_init (&parser);
+
+  root = (MenuLayoutNodeRoot *) parser.root;
+
+  root->basedir = g_path_get_dirname (filename);
+  menu_verbose ("Set basedir \"%s\"\n", root->basedir);
+
+  if (non_prefixed_basename)
+    s = g_strdup (non_prefixed_basename);
+  else
+    s = g_path_get_basename (filename);
+  str = g_string_new (s);
+  if (g_str_has_suffix (str->str, ".menu"))
+    g_string_truncate (str, str->len - strlen (".menu"));
+
+  root->name = str->str;
+  menu_verbose ("Set menu name \"%s\"\n", root->name);
+
+  g_string_free (str, FALSE);
+  g_free (s);
+
+  context = g_markup_parse_context_new (&menu_funcs, 0, &parser, NULL);
+
+  error = NULL;
+  if (!g_markup_parse_context_parse (context,
+                                     text,
+                                     length,
+                                     &error))
+    goto out;
+
+  error = NULL;
+  g_markup_parse_context_end_parse (context, &error);
+
+  root->main_context = main_context ? g_main_context_ref (main_context) : NULL;
+
+ out:
+  if (context)
+    g_markup_parse_context_free (context);
+  g_free (text);
+
+  if (error)
+    {
+      menu_verbose ("Error \"%s\" loading \"%s\"\n",
+                    error->message, filename);
+      g_propagate_error (err, error);
+    }
+  else if (has_child_of_type (parser.root, MENU_LAYOUT_NODE_MENU))
+    {
+      menu_verbose ("File loaded OK\n");
+      retval = parser.root;
+      parser.root = NULL;
+    }
+  else
+    {
+      menu_verbose ("Did not have a root element in file\n");
+      g_set_error (err, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                   "Menu file %s did not contain a root <Menu> element",
+                   filename);
+    }
+
+  menu_parser_free (&parser);
+
+  return retval;
+}
+
+
+
+
+ + + diff --git a/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/index.html b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/index.html new file mode 100644 index 0000000..ce345f4 --- /dev/null +++ b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/index.html @@ -0,0 +1,126 @@ + + + + + + Cppcheck - HTML report - mate-menus + + + + + + +
+ +
+ + + + + + + + + + + + + + +
LineIdCWESeverityMessage
missingIncludeinformationCppcheck cannot find all the include files (use --check-config for details)
libmenu/desktop-entries.c
438variableScope398styleThe scope of the variable 'i' can be reduced.
libmenu/menu-layout.c
1426varFuncNullUB475portabilityPassing NULL after the last typed argument to a variadic function leads to undefined behaviour.
1441varFuncNullUB475portabilityPassing NULL after the last typed argument to a variadic function leads to undefined behaviour.
1468varFuncNullUB475portabilityPassing NULL after the last typed argument to a variadic function leads to undefined behaviour.
1674varFuncNullUB475portabilityPassing NULL after the last typed argument to a variadic function leads to undefined behaviour.
1693varFuncNullUB475portabilityPassing NULL after the last typed argument to a variadic function leads to undefined behaviour.
2087redundantAssignment563styleVariable 'prev' is reassigned a value before the old one has been used.
+
+
+ + + diff --git a/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/stats.html b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/stats.html new file mode 100644 index 0000000..3583182 --- /dev/null +++ b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/stats.html @@ -0,0 +1,109 @@ + + + + + + Cppcheck - HTML report - mate-menus + + + + + + +
+ +
+

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 2
+   1  libmenu/menu-layout.c
+   1  libmenu/desktop-entries.c
+

+ +
+
+ + + diff --git a/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/style.css b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} -- cgit v1.2.1