~ubuntu-branches/debian/experimental/thunar/experimental

« back to all changes in this revision

Viewing changes to thunar/thunar-metafile.c

  • Committer: Bazaar Package Importer
  • Author(s): Yves-Alexis Perez
  • Date: 2006-01-02 23:42:32 UTC
  • Revision ID: james.westby@ubuntu.com-20060102234232-8xeq0lqhyn70syr0
Tags: upstream-0.1.4svn+r18850
ImportĀ upstreamĀ versionĀ 0.1.4svn+r18850

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: thunar-metafile.c 18843 2005-11-14 14:25:58Z benny $ */
 
2
/*-
 
3
 * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify it
 
6
 * under the terms of the GNU General Public License as published by the Free
 
7
 * Software Foundation; either version 2 of the License, or (at your option)
 
8
 * any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful, but WITHOUT
 
11
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
12
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 
13
 * more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License along with
 
16
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 
17
 * Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 */
 
19
 
 
20
#ifdef HAVE_CONFIG_H
 
21
#include <config.h>
 
22
#endif
 
23
 
 
24
#ifdef HAVE_ERRNO_H
 
25
#include <errno.h>
 
26
#endif
 
27
#ifdef HAVE_FCNTL_H
 
28
#include <fcntl.h>
 
29
#endif
 
30
#ifdef HAVE_MEMORY_H
 
31
#include <memory.h>
 
32
#endif
 
33
#ifdef HAVE_STDLIB_H
 
34
#include <stdlib.h>
 
35
#endif
 
36
#ifdef HAVE_STRING_H
 
37
#include <string.h>
 
38
#endif
 
39
 
 
40
#include <tdb/tdb.h>
 
41
 
 
42
#include <thunar/thunar-metafile.h>
 
43
 
 
44
 
 
45
 
 
46
static void     thunar_metafile_class_init  (ThunarMetafileClass  *klass);
 
47
static void     thunar_metafile_init        (ThunarMetafile       *metafile);
 
48
static void     thunar_metafile_finalize    (GObject              *object);
 
49
static TDB_DATA thunar_metafile_read        (ThunarMetafile       *metafile,
 
50
                                             TDB_DATA              data);
 
51
 
 
52
 
 
53
 
 
54
struct _ThunarMetafileClass
 
55
{
 
56
  GObjectClass __parent__;
 
57
};
 
58
 
 
59
struct _ThunarMetafile
 
60
{
 
61
  GObject __parent__;
 
62
 
 
63
  TDB_CONTEXT *context;
 
64
  TDB_DATA     data;
 
65
};
 
66
 
 
67
 
 
68
 
 
69
G_DEFINE_TYPE (ThunarMetafile, thunar_metafile, G_TYPE_OBJECT);
 
70
 
 
71
 
 
72
 
 
73
static void
 
74
thunar_metafile_class_init (ThunarMetafileClass *klass)
 
75
{
 
76
  GObjectClass *gobject_class;
 
77
 
 
78
  gobject_class = G_OBJECT_CLASS (klass);
 
79
  gobject_class->finalize = thunar_metafile_finalize;
 
80
}
 
81
 
 
82
 
 
83
 
 
84
static void
 
85
thunar_metafile_init (ThunarMetafile *metafile)
 
86
{
 
87
  gchar *path;
 
88
 
 
89
  /* determine the path to the metafile database (create directories as required) */
 
90
  path = xfce_resource_save_location (XFCE_RESOURCE_CACHE, "Thunar/metafile.tdb", TRUE);
 
91
  if (G_UNLIKELY (path == NULL))
 
92
    {
 
93
      path = xfce_resource_save_location (XFCE_RESOURCE_CACHE, "Thunar/", FALSE);
 
94
      g_warning ("Failed to create the Thunar cache directory in %s", path);
 
95
      g_free (path);
 
96
      return;
 
97
    }
 
98
 
 
99
  /* try to open the metafile database file */
 
100
  metafile->context = tdb_open (path, 0, TDB_DEFAULT, O_CREAT | O_RDWR, 0600);
 
101
  if (G_UNLIKELY (metafile->context == NULL))
 
102
    g_warning ("Failed to open metafile database in %s: %s.", path, g_strerror (errno));
 
103
 
 
104
  /* release the path */
 
105
  g_free (path);
 
106
}
 
107
 
 
108
 
 
109
 
 
110
static void
 
111
thunar_metafile_finalize (GObject *object)
 
112
{
 
113
  ThunarMetafile *metafile = THUNAR_METAFILE (object);
 
114
 
 
115
  /* close the database (if open) */
 
116
  if (G_LIKELY (metafile->context != NULL))
 
117
    tdb_close (metafile->context);
 
118
 
 
119
  /* release any pending data */
 
120
  if (G_LIKELY (metafile->data.dptr != NULL))
 
121
    free (metafile->data.dptr);
 
122
 
 
123
  (*G_OBJECT_CLASS (thunar_metafile_parent_class)->finalize) (object);
 
124
}
 
125
 
 
126
 
 
127
 
 
128
static TDB_DATA
 
129
thunar_metafile_read (ThunarMetafile *metafile,
 
130
                      TDB_DATA        data)
 
131
{
 
132
  /* perform the fetch operation on the database */
 
133
  data = tdb_fetch (metafile->context, data);
 
134
  if (G_UNLIKELY (data.dptr != NULL))
 
135
    {
 
136
      /* validate the result */
 
137
      if (data.dsize < sizeof (guint32)
 
138
          || (data.dsize % sizeof (guint32)) != 0
 
139
          || data.dptr[data.dsize - 1] != '\0')
 
140
        {
 
141
          free (data.dptr);
 
142
          data.dptr = NULL;
 
143
          data.dsize = 0;
 
144
        }
 
145
    }
 
146
 
 
147
  return data;
 
148
}
 
149
 
 
150
 
 
151
 
 
152
/**
 
153
 * thunar_metafile_get_default:
 
154
 *
 
155
 * Returns a reference to the default #ThunarMetafile
 
156
 * instance. There can be only one #ThunarMetafile
 
157
 * instance at any time.
 
158
 *
 
159
 * The caller is responsible to free the returned
 
160
 * object using g_object_unref() when no longer
 
161
 * needed.
 
162
 *
 
163
 * Return value: a reference to the default #ThunarMetafile
 
164
 *               instance.
 
165
 **/
 
166
ThunarMetafile*
 
167
thunar_metafile_get_default (void)
 
168
{
 
169
  static ThunarMetafile *metafile = NULL;
 
170
 
 
171
  if (G_UNLIKELY (metafile == NULL))
 
172
    {
 
173
      /* allocate a new metafile instance. */
 
174
      metafile = g_object_new (THUNAR_TYPE_METAFILE, NULL);
 
175
      g_object_add_weak_pointer (G_OBJECT (metafile), (gpointer) &metafile);
 
176
    }
 
177
  else
 
178
    {
 
179
      /* take a reference for the caller */
 
180
      g_object_ref (G_OBJECT (metafile));
 
181
    }
 
182
 
 
183
  return metafile;
 
184
}
 
185
 
 
186
 
 
187
 
 
188
/**
 
189
 * thunar_metafile_fetch:
 
190
 * @metafile      : a #ThunarMetafile.
 
191
 * @path          : a #ThunarVfsPath.
 
192
 * @key           : a #ThunarMetafileKey.
 
193
 * @default_value : the default value for @key,
 
194
 *                  which may be %NULL.
 
195
 *
 
196
 * Fetches the value for @key on @path in
 
197
 * @metafile. Returns a pointer to the
 
198
 * value if found, or the default value
 
199
 * if the @key is explicitly not set for
 
200
 * @path in @metafile, as specified in
 
201
 * @default_value.
 
202
 *
 
203
 * The returned string is owned by @metafile
 
204
 * and is only valid until the next call to
 
205
 * thunar_metafile_fetch(), so you might need
 
206
 * to take a copy of the value if you need to
 
207
 * keep for a longer period.
 
208
 *
 
209
 * Return value: the value for @key on @path
 
210
 *               in @metafile or the default
 
211
 *               value for @key, as specified
 
212
 *               by @default_value.
 
213
 **/
 
214
const gchar*
 
215
thunar_metafile_fetch (ThunarMetafile   *metafile,
 
216
                       ThunarVfsPath    *path,
 
217
                       ThunarMetafileKey key,
 
218
                       const gchar      *default_value)
 
219
{
 
220
  const guchar *dend;
 
221
  const guchar *dp;
 
222
  TDB_DATA      key_data;
 
223
  gssize        key_size;
 
224
  gchar         key_path[THUNAR_VFS_PATH_MAXSTRLEN];
 
225
 
 
226
  g_return_val_if_fail (THUNAR_IS_METAFILE (metafile), NULL);
 
227
  g_return_val_if_fail (key < THUNAR_METAFILE_N_KEYS, NULL);
 
228
  g_return_val_if_fail (path != NULL, NULL);
 
229
 
 
230
  /* check if the database handle is available */
 
231
  if (G_UNLIKELY (metafile->context == NULL))
 
232
    goto use_default_value;
 
233
 
 
234
  /* determine the string representation of the path */
 
235
  key_size = thunar_vfs_path_to_string (path, key_path, sizeof (key_path), NULL);
 
236
  if (G_UNLIKELY (key_size <= 0))
 
237
    goto use_default_value;
 
238
 
 
239
  /* generate the key data */
 
240
  key_data.dptr = key_path;
 
241
  key_data.dsize = key_size - 1;
 
242
 
 
243
  /* release any earlier result data */
 
244
  if (G_LIKELY (metafile->data.dptr != NULL))
 
245
    free (metafile->data.dptr);
 
246
 
 
247
  /* perform the fetch operation on the database */
 
248
  metafile->data = thunar_metafile_read (metafile, key_data);
 
249
  if (G_LIKELY (metafile->data.dptr == NULL))
 
250
    goto use_default_value;
 
251
 
 
252
  /* lookup the value for the given key */
 
253
  dp = (const guchar *) metafile->data.dptr;
 
254
  dend = dp + metafile->data.dsize;
 
255
  for (;;)
 
256
    {
 
257
      /* check if we have a match */
 
258
      if (*dp == (guint) key)
 
259
        return (const gchar *) (dp + 1);
 
260
 
 
261
      /* lookup the next entry */
 
262
      do
 
263
        {
 
264
          /* skip another 4 bytes */
 
265
          dp += sizeof (guint32);
 
266
          if (G_UNLIKELY (dp == dend))
 
267
            goto use_default_value;
 
268
        }
 
269
      while (*(dp - 1) != '\0');
 
270
    }
 
271
 
 
272
  /* use the default value */
 
273
use_default_value:
 
274
  return default_value;
 
275
}
 
276
 
 
277
 
 
278
 
 
279
/**
 
280
 * thunar_metafile_store:
 
281
 * @metafile      : a #ThunarMetafile.
 
282
 * @path          : a #ThunarVfsPath.
 
283
 * @key           : a #ThunarMetafileKey.
 
284
 * @value         : the new value for @key on @path.
 
285
 * @default_value : the default value for @key on @path.
 
286
 *
 
287
 * Stores the given @value for @key on @path in
 
288
 * @metafile.
 
289
 *
 
290
 * No error is returned from this method, but
 
291
 * the store operation may nevertheless fail,
 
292
 * so don't depend on the success of the operation.
 
293
 *
 
294
 * Note that if @value equals the @default_value
 
295
 * for @key, it isn't stored in the @metafile to
 
296
 * save memory.
 
297
 **/
 
298
void
 
299
thunar_metafile_store (ThunarMetafile   *metafile,
 
300
                       ThunarVfsPath    *path,
 
301
                       ThunarMetafileKey key,
 
302
                       const gchar      *value,
 
303
                       const gchar      *default_value)
 
304
{
 
305
  TDB_DATA value_data;
 
306
  TDB_DATA key_data;
 
307
  gssize   value_size;
 
308
  gssize   key_size;
 
309
  gchar   *buffer;
 
310
  gchar   *bp;
 
311
  gchar    key_path[THUNAR_VFS_PATH_MAXSTRLEN];
 
312
 
 
313
  g_return_if_fail (THUNAR_IS_METAFILE (metafile));
 
314
  g_return_if_fail (key < THUNAR_METAFILE_N_KEYS);
 
315
  g_return_if_fail (default_value != NULL);
 
316
  g_return_if_fail (value != NULL);
 
317
  g_return_if_fail (path != NULL);
 
318
 
 
319
  /* check if the database handle is available */
 
320
  if (G_UNLIKELY (metafile->context == NULL))
 
321
    return;
 
322
 
 
323
  /* determine the string representation of the path */
 
324
  key_size = thunar_vfs_path_to_string (path, key_path, sizeof (key_path), NULL);
 
325
  if (G_UNLIKELY (key_size <= 0))
 
326
    return;
 
327
 
 
328
  /* generate the key data */
 
329
  key_data.dptr = key_path;
 
330
  key_data.dsize = key_size - 1;
 
331
 
 
332
  /* fetch the current value for the key */
 
333
  value_data = thunar_metafile_read (metafile, key_data);
 
334
 
 
335
  /* determine the size required for the new value */
 
336
  value_size = strlen (value) + 2;
 
337
  value_size = ((value_size + sizeof (guint32) - 1) / sizeof (guint32)) * sizeof (guint32);
 
338
 
 
339
  /* allocate a buffer to merge the existing value set with the new value */
 
340
  buffer = g_new0 (gchar, value_data.dsize + value_size);
 
341
 
 
342
  /* copy the new value to the buffer if it's not equal to the default value */
 
343
  if (G_LIKELY (strcmp (value, default_value) != 0))
 
344
    {
 
345
      buffer[0] = key;
 
346
      strcpy (buffer + 1, value);
 
347
      bp = buffer + value_size;
 
348
    }
 
349
  else
 
350
    {
 
351
      bp = buffer;
 
352
    }
 
353
 
 
354
  /* copy the existing entries (if any) */
 
355
  if (G_LIKELY (value_data.dptr != NULL))
 
356
    {
 
357
      const guchar *vp = (const guchar *) value_data.dptr;
 
358
      const guchar *vend = vp + value_data.dsize;
 
359
      const guchar *vx;
 
360
 
 
361
      for (; vp < vend; vp = vx)
 
362
        {
 
363
          /* grab a pointer to the next entry (thereby calc
 
364
           * the length of this entry).
 
365
           */
 
366
          for (vx = vp + sizeof (guint32); *(vx - 1) != '\0'; vx += sizeof (guint32))
 
367
            ;
 
368
 
 
369
          /* verify the vx pointer */
 
370
          g_assert (vx <= vend);
 
371
          g_assert (vx > vp);
 
372
 
 
373
          /* check if we should copy the entry */
 
374
          if (*vp != key)
 
375
            {
 
376
              memcpy (bp, vp, vx - vp);
 
377
              bp += (vx - vp);
 
378
            }
 
379
        }
 
380
 
 
381
      /* verify the buffer space */
 
382
      g_assert (bp <= buffer + value_data.dsize + value_size);
 
383
      g_assert ((bp - buffer) % sizeof (guint32) == 0);
 
384
 
 
385
      /* release the previous value set */
 
386
      free (value_data.dptr);
 
387
    }
 
388
 
 
389
  /* delete the key from the database if the new
 
390
   * value set is the same as the default value set.
 
391
   */
 
392
  if (G_UNLIKELY (bp == buffer))
 
393
    {
 
394
      tdb_delete (metafile->context, key_data);
 
395
    }
 
396
  else
 
397
    {
 
398
      /* setup the new value set */
 
399
      value_data.dptr = buffer;
 
400
      value_data.dsize = bp - buffer;
 
401
 
 
402
      /* execute the store operation */
 
403
      tdb_store (metafile->context, key_data, value_data, TDB_REPLACE);
 
404
    }
 
405
 
 
406
  /* free the buffer space */
 
407
  g_free (buffer);
 
408
}
 
409