1
/* $Id: thunar-metafile.c 18843 2005-11-14 14:25:58Z benny $ */
3
* Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
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)
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
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
42
#include <thunar/thunar-metafile.h>
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,
54
struct _ThunarMetafileClass
56
GObjectClass __parent__;
59
struct _ThunarMetafile
69
G_DEFINE_TYPE (ThunarMetafile, thunar_metafile, G_TYPE_OBJECT);
74
thunar_metafile_class_init (ThunarMetafileClass *klass)
76
GObjectClass *gobject_class;
78
gobject_class = G_OBJECT_CLASS (klass);
79
gobject_class->finalize = thunar_metafile_finalize;
85
thunar_metafile_init (ThunarMetafile *metafile)
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))
93
path = xfce_resource_save_location (XFCE_RESOURCE_CACHE, "Thunar/", FALSE);
94
g_warning ("Failed to create the Thunar cache directory in %s", path);
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));
104
/* release the path */
111
thunar_metafile_finalize (GObject *object)
113
ThunarMetafile *metafile = THUNAR_METAFILE (object);
115
/* close the database (if open) */
116
if (G_LIKELY (metafile->context != NULL))
117
tdb_close (metafile->context);
119
/* release any pending data */
120
if (G_LIKELY (metafile->data.dptr != NULL))
121
free (metafile->data.dptr);
123
(*G_OBJECT_CLASS (thunar_metafile_parent_class)->finalize) (object);
129
thunar_metafile_read (ThunarMetafile *metafile,
132
/* perform the fetch operation on the database */
133
data = tdb_fetch (metafile->context, data);
134
if (G_UNLIKELY (data.dptr != NULL))
136
/* validate the result */
137
if (data.dsize < sizeof (guint32)
138
|| (data.dsize % sizeof (guint32)) != 0
139
|| data.dptr[data.dsize - 1] != '\0')
153
* thunar_metafile_get_default:
155
* Returns a reference to the default #ThunarMetafile
156
* instance. There can be only one #ThunarMetafile
157
* instance at any time.
159
* The caller is responsible to free the returned
160
* object using g_object_unref() when no longer
163
* Return value: a reference to the default #ThunarMetafile
167
thunar_metafile_get_default (void)
169
static ThunarMetafile *metafile = NULL;
171
if (G_UNLIKELY (metafile == NULL))
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);
179
/* take a reference for the caller */
180
g_object_ref (G_OBJECT (metafile));
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.
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
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.
209
* Return value: the value for @key on @path
210
* in @metafile or the default
211
* value for @key, as specified
215
thunar_metafile_fetch (ThunarMetafile *metafile,
217
ThunarMetafileKey key,
218
const gchar *default_value)
224
gchar key_path[THUNAR_VFS_PATH_MAXSTRLEN];
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);
230
/* check if the database handle is available */
231
if (G_UNLIKELY (metafile->context == NULL))
232
goto use_default_value;
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;
239
/* generate the key data */
240
key_data.dptr = key_path;
241
key_data.dsize = key_size - 1;
243
/* release any earlier result data */
244
if (G_LIKELY (metafile->data.dptr != NULL))
245
free (metafile->data.dptr);
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;
252
/* lookup the value for the given key */
253
dp = (const guchar *) metafile->data.dptr;
254
dend = dp + metafile->data.dsize;
257
/* check if we have a match */
258
if (*dp == (guint) key)
259
return (const gchar *) (dp + 1);
261
/* lookup the next entry */
264
/* skip another 4 bytes */
265
dp += sizeof (guint32);
266
if (G_UNLIKELY (dp == dend))
267
goto use_default_value;
269
while (*(dp - 1) != '\0');
272
/* use the default value */
274
return default_value;
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.
287
* Stores the given @value for @key on @path in
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.
294
* Note that if @value equals the @default_value
295
* for @key, it isn't stored in the @metafile to
299
thunar_metafile_store (ThunarMetafile *metafile,
301
ThunarMetafileKey key,
303
const gchar *default_value)
311
gchar key_path[THUNAR_VFS_PATH_MAXSTRLEN];
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);
319
/* check if the database handle is available */
320
if (G_UNLIKELY (metafile->context == NULL))
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))
328
/* generate the key data */
329
key_data.dptr = key_path;
330
key_data.dsize = key_size - 1;
332
/* fetch the current value for the key */
333
value_data = thunar_metafile_read (metafile, key_data);
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);
339
/* allocate a buffer to merge the existing value set with the new value */
340
buffer = g_new0 (gchar, value_data.dsize + value_size);
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))
346
strcpy (buffer + 1, value);
347
bp = buffer + value_size;
354
/* copy the existing entries (if any) */
355
if (G_LIKELY (value_data.dptr != NULL))
357
const guchar *vp = (const guchar *) value_data.dptr;
358
const guchar *vend = vp + value_data.dsize;
361
for (; vp < vend; vp = vx)
363
/* grab a pointer to the next entry (thereby calc
364
* the length of this entry).
366
for (vx = vp + sizeof (guint32); *(vx - 1) != '\0'; vx += sizeof (guint32))
369
/* verify the vx pointer */
370
g_assert (vx <= vend);
373
/* check if we should copy the entry */
376
memcpy (bp, vp, vx - vp);
381
/* verify the buffer space */
382
g_assert (bp <= buffer + value_data.dsize + value_size);
383
g_assert ((bp - buffer) % sizeof (guint32) == 0);
385
/* release the previous value set */
386
free (value_data.dptr);
389
/* delete the key from the database if the new
390
* value set is the same as the default value set.
392
if (G_UNLIKELY (bp == buffer))
394
tdb_delete (metafile->context, key_data);
398
/* setup the new value set */
399
value_data.dptr = buffer;
400
value_data.dsize = bp - buffer;
402
/* execute the store operation */
403
tdb_store (metafile->context, key_data, value_data, TDB_REPLACE);
406
/* free the buffer space */