gtkfilesystemunix.c 45.5 KB
Newer Older
Owen Taylor's avatar
Owen Taylor committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* GTK - The GIMP Toolkit
 * gtkfilesystemunix.c: Default implementation of GtkFileSystem for UNIX-like systems
 * Copyright (C) 2003, 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

21
22
#include <config.h>

Owen Taylor's avatar
Owen Taylor committed
23
24
#include "gtkfilesystem.h"
#include "gtkfilesystemunix.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
25
#include "gtkicontheme.h"
26
#include "gtkintl.h"
Owen Taylor's avatar
Owen Taylor committed
27

28
#define XDG_PREFIX _gtk_xdg
Owen Taylor's avatar
Owen Taylor committed
29
30
#include "xdgmime/xdgmime.h"

Owen Taylor's avatar
Owen Taylor committed
31
32
33
34
35
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
36
#include <stdio.h>
37
#include <time.h>
38
39
40

#define BOOKMARKS_FILENAME ".gtk-bookmarks"
#define BOOKMARKS_TMP_FILENAME ".gtk-bookmarks-XXXXXX"
Owen Taylor's avatar
Owen Taylor committed
41

42
43
#define FOLDER_CACHE_LIFETIME 2 /* seconds */

Owen Taylor's avatar
Owen Taylor committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;

#define GTK_FILE_SYSTEM_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
#define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
#define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))

struct _GtkFileSystemUnixClass
{
  GObjectClass parent_class;
};

struct _GtkFileSystemUnix
{
  GObject parent_instance;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
58
59

  GHashTable *folder_hash;
Owen Taylor's avatar
Owen Taylor committed
60
61
};

Federico Mena Quintero's avatar
Federico Mena Quintero committed
62
63
64
/* Icon type, supplemented by MIME type
 */
typedef enum {
65
  ICON_UNDECIDED,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
66
67
68
69
70
71
72
73
74
75
76
  ICON_NONE,
  ICON_REGULAR,	/* Use mime type for icon */
  ICON_BLOCK_DEVICE,
  ICON_BROKEN_SYMBOLIC_LINK,
  ICON_CHARACTER_DEVICE,
  ICON_DIRECTORY,
  ICON_EXECUTABLE,
  ICON_FIFO,
  ICON_SOCKET
} IconType;

77

Owen Taylor's avatar
Owen Taylor committed
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#define GTK_TYPE_FILE_FOLDER_UNIX             (gtk_file_folder_unix_get_type ())
#define GTK_FILE_FOLDER_UNIX(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
#define GTK_IS_FILE_FOLDER_UNIX(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
#define GTK_FILE_FOLDER_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
#define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
#define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))

typedef struct _GtkFileFolderUnix      GtkFileFolderUnix;
typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;

struct _GtkFileFolderUnixClass
{
  GObjectClass parent_class;
};

struct _GtkFileFolderUnix
{
  GObject parent_instance;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
97
  GtkFileSystemUnix *system_unix;
Owen Taylor's avatar
Owen Taylor committed
98
99
  GtkFileInfoType types;
  gchar *filename;
100
101
102
103
104
105
106
107
108
109
  GHashTable *stat_info;
  unsigned int have_stat : 1;
  unsigned int have_mime_type : 1;
  time_t asof;
};

struct stat_info_entry {
  struct stat statbuf;
  char *mime_type;
  IconType icon_type;
Owen Taylor's avatar
Owen Taylor committed
110
111
};

112
113
114
115
116
static const GtkFileInfoType STAT_NEEDED_MASK = (GTK_FILE_INFO_IS_FOLDER |
						 GTK_FILE_INFO_IS_HIDDEN |
						 GTK_FILE_INFO_MODIFICATION_TIME |
						 GTK_FILE_INFO_SIZE);

Owen Taylor's avatar
Owen Taylor committed
117
118
static GObjectClass *system_parent_class;
static GObjectClass *folder_parent_class;
119

Owen Taylor's avatar
Owen Taylor committed
120
121
122
123
124
static void gtk_file_system_unix_class_init   (GtkFileSystemUnixClass *class);
static void gtk_file_system_unix_iface_init   (GtkFileSystemIface     *iface);
static void gtk_file_system_unix_init         (GtkFileSystemUnix      *impl);
static void gtk_file_system_unix_finalize     (GObject                *object);

125
126
127
128
static GSList *             gtk_file_system_unix_list_volumes        (GtkFileSystem     *file_system);
static GtkFileSystemVolume *gtk_file_system_unix_get_volume_for_path (GtkFileSystem     *file_system,
								      const GtkFilePath *path);

129
130
131
132
133
134
135
static GtkFileFolder *gtk_file_system_unix_get_folder    (GtkFileSystem      *file_system,
							  const GtkFilePath  *path,
							  GtkFileInfoType     types,
							  GError            **error);
static gboolean       gtk_file_system_unix_create_folder (GtkFileSystem      *file_system,
							  const GtkFilePath  *path,
							  GError            **error);
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

static void         gtk_file_system_unix_volume_free             (GtkFileSystem       *file_system,
								  GtkFileSystemVolume *volume);
static GtkFilePath *gtk_file_system_unix_volume_get_base_path    (GtkFileSystem       *file_system,
								  GtkFileSystemVolume *volume);
static gboolean     gtk_file_system_unix_volume_get_is_mounted   (GtkFileSystem       *file_system,
								  GtkFileSystemVolume *volume);
static gboolean     gtk_file_system_unix_volume_mount            (GtkFileSystem       *file_system,
								  GtkFileSystemVolume *volume,
								  GError             **error);
static gchar *      gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
								  GtkFileSystemVolume *volume);
static GdkPixbuf *  gtk_file_system_unix_volume_render_icon      (GtkFileSystem        *file_system,
								  GtkFileSystemVolume  *volume,
								  GtkWidget            *widget,
								  gint                  pixel_size,
								  GError              **error);

154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
static gboolean       gtk_file_system_unix_get_parent    (GtkFileSystem      *file_system,
							  const GtkFilePath  *path,
							  GtkFilePath       **parent,
							  GError            **error);
static GtkFilePath *  gtk_file_system_unix_make_path     (GtkFileSystem      *file_system,
							  const GtkFilePath  *base_path,
							  const gchar        *display_name,
							  GError            **error);
static gboolean       gtk_file_system_unix_parse         (GtkFileSystem      *file_system,
							  const GtkFilePath  *base_path,
							  const gchar        *str,
							  GtkFilePath       **folder,
							  gchar             **file_part,
							  GError            **error);

static gchar *      gtk_file_system_unix_path_to_uri      (GtkFileSystem     *file_system,
							   const GtkFilePath *path);
static gchar *      gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
							   const GtkFilePath *path);
static GtkFilePath *gtk_file_system_unix_uri_to_path      (GtkFileSystem     *file_system,
							   const gchar       *uri);
static GtkFilePath *gtk_file_system_unix_filename_to_path (GtkFileSystem     *file_system,
							   const gchar       *filename);
Owen Taylor's avatar
Owen Taylor committed
177

178
179
180
181
182
183
static GdkPixbuf *gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
						    const GtkFilePath *path,
						    GtkWidget         *widget,
						    gint               pixel_size,
						    GError           **error);

184
static gboolean gtk_file_system_unix_insert_bookmark (GtkFileSystem     *file_system,
185
						      const GtkFilePath *path,
186
						      gint               position,
187
188
189
190
191
						      GError           **error);
static gboolean gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
						      const GtkFilePath *path,
						      GError           **error);
static GSList * gtk_file_system_unix_list_bookmarks  (GtkFileSystem *file_system);
192

Owen Taylor's avatar
Owen Taylor committed
193
194
195
196
197
198
199
static GType gtk_file_folder_unix_get_type   (void);
static void  gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
static void  gtk_file_folder_unix_iface_init (GtkFileFolderIface     *iface);
static void  gtk_file_folder_unix_init       (GtkFileFolderUnix      *impl);
static void  gtk_file_folder_unix_finalize   (GObject                *object);

static GtkFileInfo *gtk_file_folder_unix_get_info      (GtkFileFolder  *folder,
200
							const GtkFilePath    *path,
Owen Taylor's avatar
Owen Taylor committed
201
202
203
204
205
							GError        **error);
static gboolean     gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
							GSList        **children,
							GError        **error);

206
207
static GtkFilePath *filename_to_path   (const gchar       *filename);

Owen Taylor's avatar
Owen Taylor committed
208
static gboolean     filename_is_root  (const char       *filename);
209
210
211
212
213

static gboolean fill_in_names (GtkFileFolderUnix *folder_unix, GError **error);
static gboolean fill_in_stats (GtkFileFolderUnix *folder_unix, GError **error);
static gboolean fill_in_mime_type (GtkFileFolderUnix *folder_unix, GError **error);

214
static char *       get_parent_dir    (const char       *filename);
Owen Taylor's avatar
Owen Taylor committed
215
216
217
218
219

/*
 * GtkFileSystemUnix
 */
GType
Owen Taylor's avatar
Owen Taylor committed
220
gtk_file_system_unix_get_type (void)
Owen Taylor's avatar
Owen Taylor committed
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
{
  static GType file_system_unix_type = 0;

  if (!file_system_unix_type)
    {
      static const GTypeInfo file_system_unix_info =
      {
	sizeof (GtkFileSystemUnixClass),
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_file_system_unix_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkFileSystemUnix),
	0,		/* n_preallocs */
	(GInstanceInitFunc) gtk_file_system_unix_init,
      };
238

Owen Taylor's avatar
Owen Taylor committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
      static const GInterfaceInfo file_system_info =
      {
	(GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
	NULL,			                              /* interface_finalize */
	NULL			                              /* interface_data */
      };

      file_system_unix_type = g_type_register_static (G_TYPE_OBJECT,
						      "GtkFileSystemUnix",
						      &file_system_unix_info, 0);
      g_type_add_interface_static (file_system_unix_type,
				   GTK_TYPE_FILE_SYSTEM,
				   &file_system_info);
    }

  return file_system_unix_type;
}

257
/**
Owen Taylor's avatar
Owen Taylor committed
258
 * gtk_file_system_unix_new:
259
 *
260
261
262
 * Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
 * implements the #GtkFileSystem interface with direct access to
 * the filesystem using Unix/Linux API calls
263
 *
264
265
266
 * Return value: the new #GtkFileSystemUnix object
 **/
GtkFileSystem *
Owen Taylor's avatar
Owen Taylor committed
267
gtk_file_system_unix_new (void)
268
269
270
271
{
  return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
}

Owen Taylor's avatar
Owen Taylor committed
272
273
274
275
static void
gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
276
277

  system_parent_class = g_type_class_peek_parent (class);
278

Owen Taylor's avatar
Owen Taylor committed
279
280
281
282
283
284
  gobject_class->finalize = gtk_file_system_unix_finalize;
}

static void
gtk_file_system_unix_iface_init   (GtkFileSystemIface *iface)
{
285
  iface->list_volumes = gtk_file_system_unix_list_volumes;
286
  iface->get_volume_for_path = gtk_file_system_unix_get_volume_for_path;
Owen Taylor's avatar
Owen Taylor committed
287
288
  iface->get_folder = gtk_file_system_unix_get_folder;
  iface->create_folder = gtk_file_system_unix_create_folder;
289
290
291
292
293
294
  iface->volume_free = gtk_file_system_unix_volume_free;
  iface->volume_get_base_path = gtk_file_system_unix_volume_get_base_path;
  iface->volume_get_is_mounted = gtk_file_system_unix_volume_get_is_mounted;
  iface->volume_mount = gtk_file_system_unix_volume_mount;
  iface->volume_get_display_name = gtk_file_system_unix_volume_get_display_name;
  iface->volume_render_icon = gtk_file_system_unix_volume_render_icon;
Owen Taylor's avatar
Owen Taylor committed
295
  iface->get_parent = gtk_file_system_unix_get_parent;
296
  iface->make_path = gtk_file_system_unix_make_path;
297
  iface->parse = gtk_file_system_unix_parse;
298
299
300
301
  iface->path_to_uri = gtk_file_system_unix_path_to_uri;
  iface->path_to_filename = gtk_file_system_unix_path_to_filename;
  iface->uri_to_path = gtk_file_system_unix_uri_to_path;
  iface->filename_to_path = gtk_file_system_unix_filename_to_path;
302
  iface->render_icon = gtk_file_system_unix_render_icon;
303
  iface->insert_bookmark = gtk_file_system_unix_insert_bookmark;
304
  iface->remove_bookmark = gtk_file_system_unix_remove_bookmark;
305
  iface->list_bookmarks = gtk_file_system_unix_list_bookmarks;
Owen Taylor's avatar
Owen Taylor committed
306
307
308
309
310
}

static void
gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
{
Federico Mena Quintero's avatar
Federico Mena Quintero committed
311
  system_unix->folder_hash = g_hash_table_new (g_str_hash, g_str_equal);
Owen Taylor's avatar
Owen Taylor committed
312
313
314
315
316
}

static void
gtk_file_system_unix_finalize (GObject *object)
{
Federico Mena Quintero's avatar
Federico Mena Quintero committed
317
318
319
320
321
322
323
  GtkFileSystemUnix *system_unix;

  system_unix = GTK_FILE_SYSTEM_UNIX (object);

  /* FIXME: assert that the hash is empty? */
  g_hash_table_destroy (system_unix->folder_hash);

324
  system_parent_class->finalize (object);
Owen Taylor's avatar
Owen Taylor committed
325
326
}

327
328
329
/* Returns our single root volume */
static GtkFileSystemVolume *
get_root_volume (void)
330
{
331
  return (GtkFileSystemVolume *) gtk_file_path_new_dup ("/");
332
333
}

Owen Taylor's avatar
Owen Taylor committed
334
static GSList *
335
gtk_file_system_unix_list_volumes (GtkFileSystem *file_system)
Owen Taylor's avatar
Owen Taylor committed
336
{
337
  return g_slist_append (NULL, get_root_volume ());
Owen Taylor's avatar
Owen Taylor committed
338
339
}

340
341
342
static GtkFileSystemVolume *
gtk_file_system_unix_get_volume_for_path (GtkFileSystem     *file_system,
					  const GtkFilePath *path)
Owen Taylor's avatar
Owen Taylor committed
343
{
344
  return get_root_volume ();
Owen Taylor's avatar
Owen Taylor committed
345
346
}

347
348
349
350
351
352
353
354
355
356
357
358
359
static char *
remove_trailing_slash (const char *filename)
{
  int len;

  len = strlen (filename);

  if (len > 1 && filename[len - 1] == '/')
    return g_strndup (filename, len - 1);
  else
    return g_memdup (filename, len + 1);
}

Owen Taylor's avatar
Owen Taylor committed
360
static GtkFileFolder *
361
362
363
364
gtk_file_system_unix_get_folder (GtkFileSystem     *file_system,
				 const GtkFilePath *path,
				 GtkFileInfoType    types,
				 GError           **error)
Owen Taylor's avatar
Owen Taylor committed
365
{
Federico Mena Quintero's avatar
Federico Mena Quintero committed
366
  GtkFileSystemUnix *system_unix;
Owen Taylor's avatar
Owen Taylor committed
367
  GtkFileFolderUnix *folder_unix;
368
  const char *filename;
369
  char *filename_copy;
370
  time_t now = time (NULL);
Owen Taylor's avatar
Owen Taylor committed
371

Federico Mena Quintero's avatar
Federico Mena Quintero committed
372
373
  system_unix = GTK_FILE_SYSTEM_UNIX (file_system);

374
  filename = gtk_file_path_get_string (path);
375
  g_return_val_if_fail (filename != NULL, NULL);
376
  g_return_val_if_fail (g_path_is_absolute (filename), NULL);
Owen Taylor's avatar
Owen Taylor committed
377

378
379
  filename_copy = remove_trailing_slash (filename);
  folder_unix = g_hash_table_lookup (system_unix->folder_hash, filename_copy);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
380
381

  if (folder_unix)
382
    {
383
      g_free (filename_copy);
384
385
386
      if (now - folder_unix->asof >= FOLDER_CACHE_LIFETIME &&
	  folder_unix->stat_info)
	{
Morten Welinder's avatar
Morten Welinder committed
387
#if 0
388
	  g_print ("Cleaning out cached directory %s\n", filename);
Morten Welinder's avatar
Morten Welinder committed
389
#endif
390
391
392
393
394
395
396
	  g_hash_table_destroy (folder_unix->stat_info);
	  folder_unix->stat_info = NULL;
	  folder_unix->have_mime_type = FALSE;
	  folder_unix->have_stat = FALSE;
	}

      g_object_ref (folder_unix);
397
      folder_unix->types |= types;
398
      types = folder_unix->types;
399
    }
Federico Mena Quintero's avatar
Federico Mena Quintero committed
400
401
  else
    {
402
403
404
405
      if (!g_file_test (filename, G_FILE_TEST_IS_DIR))
	{
	  int save_errno = errno;
	  gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425

	  /* If g_file_test() returned FALSE but not due to an error, it means
	   * that the filename is not a directory.
	   */
	  if (save_errno == 0)
	    /* ENOTDIR */
	    g_set_error (error,
			 GTK_FILE_SYSTEM_ERROR,
			 GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
			 _("%s: %s"),
			 filename_utf8 ? filename_utf8 : "???",
			 g_strerror (ENOTDIR));
	  else
	    g_set_error (error,
			 GTK_FILE_SYSTEM_ERROR,
			 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
			 _("error getting information for '%s': %s"),
			 filename_utf8 ? filename_utf8 : "???",
			 g_strerror (save_errno));

426
427
428
429
430
	  g_free (filename_utf8);
	  g_free (filename_copy);
	  return NULL;
	}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
431
432
      folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
      folder_unix->system_unix = system_unix;
433
      folder_unix->filename = filename_copy;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
434
      folder_unix->types = types;
435
436
437
438
439
440
441
442
443
      folder_unix->stat_info = NULL;
      folder_unix->asof = now;
      folder_unix->have_mime_type = FALSE;
      folder_unix->have_stat = FALSE;

      g_hash_table_insert (system_unix->folder_hash,
			   folder_unix->filename,
			   folder_unix);
    }
Federico Mena Quintero's avatar
Federico Mena Quintero committed
444

445
446
447
448
  if ((types & STAT_NEEDED_MASK) && !fill_in_stats (folder_unix, error))
    {
      g_object_unref (folder_unix);
      return NULL;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
449
    }
450
451
452
453
454
455
456
  if ((types & GTK_FILE_INFO_MIME_TYPE) && !fill_in_mime_type (folder_unix, error))
    {
      g_object_unref (folder_unix);
      return NULL;
    }

  return GTK_FILE_FOLDER (folder_unix);
Owen Taylor's avatar
Owen Taylor committed
457
458
459
}

static gboolean
460
461
462
gtk_file_system_unix_create_folder (GtkFileSystem     *file_system,
				    const GtkFilePath *path,
				    GError           **error)
Owen Taylor's avatar
Owen Taylor committed
463
{
Federico Mena Quintero's avatar
Federico Mena Quintero committed
464
  GtkFileSystemUnix *system_unix;
465
  const char *filename;
Owen Taylor's avatar
Owen Taylor committed
466
  gboolean result;
467
  char *parent, *tmp;
468

Federico Mena Quintero's avatar
Federico Mena Quintero committed
469
  system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
Owen Taylor's avatar
Owen Taylor committed
470

471
  filename = gtk_file_path_get_string (path);
472
  g_return_val_if_fail (filename != NULL, FALSE);
473
  g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
474

475
476
477
  tmp = remove_trailing_slash (filename);
  result = mkdir (tmp, 0777) == 0;
  g_free (tmp);
478

Owen Taylor's avatar
Owen Taylor committed
479
480
  if (!result)
    {
Morten Welinder's avatar
Morten Welinder committed
481
      int save_errno = errno;
Owen Taylor's avatar
Owen Taylor committed
482
483
484
      gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
      g_set_error (error,
		   GTK_FILE_SYSTEM_ERROR,
485
		   GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
486
		   _("error creating directory '%s': %s"),
Owen Taylor's avatar
Owen Taylor committed
487
		   filename_utf8 ? filename_utf8 : "???",
Morten Welinder's avatar
Morten Welinder committed
488
		   g_strerror (save_errno));
Owen Taylor's avatar
Owen Taylor committed
489
      g_free (filename_utf8);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
490
      return FALSE;
Owen Taylor's avatar
Owen Taylor committed
491
    }
492

Federico Mena Quintero's avatar
Federico Mena Quintero committed
493
494
495
  if (filename_is_root (filename))
    return TRUE; /* hmmm, but with no notification */

496
  parent = get_parent_dir (filename);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
497
498
499
500
501
502
503
  if (parent)
    {
      GtkFileFolderUnix *folder_unix;

      folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
      if (folder_unix)
	{
504
505
	  GtkFileInfoType types;
	  GtkFilePath *parent_path;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
506
	  GSList *paths;
507
	  GtkFileFolder *folder;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
508

509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
	  /* This is sort of a hack.  We re-get the folder, to ensure that the
	   * newly-created directory gets read into the folder's info hash table.
	   */

	  types = folder_unix->types;

	  parent_path = gtk_file_path_new_dup (parent);
	  folder = gtk_file_system_get_folder (file_system, parent_path, types, NULL);
	  gtk_file_path_free (parent_path);

	  if (folder)
	    {
	      paths = g_slist_append (NULL, (GtkFilePath *) path);
	      g_signal_emit_by_name (folder, "files-added", paths);
	      g_slist_free (paths);
	      g_object_unref (folder);
	    }
Federico Mena Quintero's avatar
Federico Mena Quintero committed
526
	}
527
528

      g_free (parent);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
529
530
531
    }

  return TRUE;
Owen Taylor's avatar
Owen Taylor committed
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
static void
gtk_file_system_unix_volume_free (GtkFileSystem        *file_system,
				  GtkFileSystemVolume  *volume)
{
  GtkFilePath *path;

  path = (GtkFilePath *) volume;
  gtk_file_path_free (path);
}

static GtkFilePath *
gtk_file_system_unix_volume_get_base_path (GtkFileSystem        *file_system,
					   GtkFileSystemVolume  *volume)
{
  return gtk_file_path_new_dup ("/");
}

static gboolean
gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem        *file_system,
					    GtkFileSystemVolume  *volume)
{
  return TRUE;
}

static gboolean
559
gtk_file_system_unix_volume_mount (GtkFileSystem        *file_system,
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
				   GtkFileSystemVolume  *volume,
				   GError              **error)
{
  g_set_error (error,
	       GTK_FILE_SYSTEM_ERROR,
	       GTK_FILE_SYSTEM_ERROR_FAILED,
	       _("This file system does not support mounting"));
  return FALSE;
}

static gchar *
gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
					      GtkFileSystemVolume *volume)
{
  return g_strdup (_("Filesystem")); /* Same as Nautilus */
}

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
static IconType
get_icon_type_from_stat (struct stat *statp)
{
  if (S_ISBLK (statp->st_mode))
    return ICON_BLOCK_DEVICE;
  else if (S_ISLNK (statp->st_mode))
    return ICON_BROKEN_SYMBOLIC_LINK; /* See get_icon_type */
  else if (S_ISCHR (statp->st_mode))
    return ICON_CHARACTER_DEVICE;
  else if (S_ISDIR (statp->st_mode))
    return ICON_DIRECTORY;
  else if (S_ISFIFO (statp->st_mode))
    return  ICON_FIFO;
  else if (S_ISSOCK (statp->st_mode))
    return ICON_SOCKET;
  else
    return ICON_REGULAR;
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
596
597
598
599
600
601
602
603
static IconType
get_icon_type (const char *filename,
	       GError    **error)
{
  struct stat statbuf;

  /* If stat fails, try to fall back to lstat to catch broken links
   */
Federico Mena Quintero's avatar
Federico Mena Quintero committed
604
  if (stat (filename, &statbuf) != 0)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
605
    {
Federico Mena Quintero's avatar
Federico Mena Quintero committed
606
607
608
609
610
611
612
613
614
615
616
      if (errno != ENOENT || lstat (filename, &statbuf) != 0)
	{
	  int save_errno = errno;
	  gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
	  g_set_error (error,
		       GTK_FILE_SYSTEM_ERROR,
		       GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
		       _("error getting information for '%s': %s"),
		       filename_utf8 ? filename_utf8 : "???",
		       g_strerror (save_errno));
	  g_free (filename_utf8);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
617

Federico Mena Quintero's avatar
Federico Mena Quintero committed
618
619
	  return ICON_NONE;
	}
Federico Mena Quintero's avatar
Federico Mena Quintero committed
620
621
    }

622
  return get_icon_type_from_stat (&statbuf);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
}

typedef struct
{
  gint size;
  GdkPixbuf *pixbuf;
} IconCacheElement;

static void
icon_cache_element_free (IconCacheElement *element)
{
  if (element->pixbuf)
    g_object_unref (element->pixbuf);
  g_free (element);
}

static void
icon_theme_changed (GtkIconTheme *icon_theme)
{
  GHashTable *cache;
643

Federico Mena Quintero's avatar
Federico Mena Quintero committed
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
  /* Difference from the initial creation is that we don't
   * reconnect the signal
   */
  cache = g_hash_table_new_full (g_str_hash, g_str_equal,
				 (GDestroyNotify)g_free,
				 (GDestroyNotify)icon_cache_element_free);
  g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
			  cache, (GDestroyNotify)g_hash_table_destroy);
}

static GdkPixbuf *
get_cached_icon (GtkWidget   *widget,
		 const gchar *name,
		 gint         pixel_size)
{
  GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
  GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
  IconCacheElement *element;
662

Federico Mena Quintero's avatar
Federico Mena Quintero committed
663
664
665
666
667
  if (!cache)
    {
      cache = g_hash_table_new_full (g_str_hash, g_str_equal,
				     (GDestroyNotify)g_free,
				     (GDestroyNotify)icon_cache_element_free);
668

Federico Mena Quintero's avatar
Federico Mena Quintero committed
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
      g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
			      cache, (GDestroyNotify)g_hash_table_destroy);
      g_signal_connect (icon_theme, "changed",
			G_CALLBACK (icon_theme_changed), NULL);
    }

  element = g_hash_table_lookup (cache, name);
  if (!element)
    {
      element = g_new0 (IconCacheElement, 1);
      g_hash_table_insert (cache, g_strdup (name), element);
    }

  if (element->size != pixel_size)
    {
      if (element->pixbuf)
	g_object_unref (element->pixbuf);
      element->size = pixel_size;
      element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
						  pixel_size, 0, NULL);
    }

  return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
}

694
695
696
697
698
699
700
static GdkPixbuf *
gtk_file_system_unix_volume_render_icon (GtkFileSystem        *file_system,
					 GtkFileSystemVolume  *volume,
					 GtkWidget            *widget,
					 gint                  pixel_size,
					 GError              **error)
{
Federico Mena Quintero's avatar
Federico Mena Quintero committed
701
702
  /* FIXME: set the GError if we can't load the icon */
  return get_cached_icon (widget, "gnome-fs-blockdev", pixel_size);
703
704
}

705
706
707
708
709
710
static char *
get_parent_dir (const char *filename)
{
  int len;

  len = strlen (filename);
711

712
  /* Ignore trailing slashes */
713
  if (len > 1 && filename[len - 1] == '/')
714
715
    {
      char *tmp, *parent;
716

717
718
719
720
721
722
723
724
725
726
727
      tmp = g_strndup (filename, len - 1);

      parent = g_path_get_dirname (tmp);
      g_free (tmp);

      return parent;
    }
  else
    return g_path_get_dirname (filename);
}

Owen Taylor's avatar
Owen Taylor committed
728
static gboolean
729
730
731
732
gtk_file_system_unix_get_parent (GtkFileSystem     *file_system,
				 const GtkFilePath *path,
				 GtkFilePath      **parent,
				 GError           **error)
Owen Taylor's avatar
Owen Taylor committed
733
{
734
  const char *filename;
735

736
  filename = gtk_file_path_get_string (path);
737
  g_return_val_if_fail (filename != NULL, FALSE);
738
  g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
Owen Taylor's avatar
Owen Taylor committed
739
740
741
742
743
744
745

  if (filename_is_root (filename))
    {
      *parent = NULL;
    }
  else
    {
746
      gchar *parent_filename = get_parent_dir (filename);
747
      *parent = filename_to_path (parent_filename);
Owen Taylor's avatar
Owen Taylor committed
748
749
750
751
752
753
      g_free (parent_filename);
    }

  return TRUE;
}

754
755
756
757
758
static GtkFilePath *
gtk_file_system_unix_make_path (GtkFileSystem    *file_system,
			       const GtkFilePath *base_path,
			       const gchar       *display_name,
			       GError           **error)
Owen Taylor's avatar
Owen Taylor committed
759
{
760
  const char *base_filename;
Owen Taylor's avatar
Owen Taylor committed
761
762
763
  gchar *filename;
  gchar *full_filename;
  GError *tmp_error = NULL;
764
  GtkFilePath *result;
765

766
  base_filename = gtk_file_path_get_string (base_path);
767
  g_return_val_if_fail (base_filename != NULL, NULL);
768
  g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
Owen Taylor's avatar
Owen Taylor committed
769
770
771
772
773
774
775
776
777

  filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
  if (!filename)
    {
      g_set_error (error,
		   GTK_FILE_SYSTEM_ERROR,
		   GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
		   "%s",
		   tmp_error->message);
778

Owen Taylor's avatar
Owen Taylor committed
779
780
      g_error_free (tmp_error);

781
      return NULL;
Owen Taylor's avatar
Owen Taylor committed
782
    }
783

Owen Taylor's avatar
Owen Taylor committed
784
  full_filename = g_build_filename (base_filename, filename, NULL);
785
  result = filename_to_path (full_filename);
Owen Taylor's avatar
Owen Taylor committed
786
787
  g_free (filename);
  g_free (full_filename);
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
  return result;
}

/* If this was a publically exported function, it should return
 * a dup'ed result, but we make it modify-in-place for efficiency
 * here, and because it works for us.
 */
static void
canonicalize_filename (gchar *filename)
{
  gchar *p, *q;
  gboolean last_was_slash = FALSE;

  p = filename;
  q = filename;

  while (*p)
    {
      if (*p == G_DIR_SEPARATOR)
	{
	  if (!last_was_slash)
	    *q++ = G_DIR_SEPARATOR;

	  last_was_slash = TRUE;
	}
      else
	{
	  if (last_was_slash && *p == '.')
	    {
	      if (*(p + 1) == G_DIR_SEPARATOR ||
		  *(p + 1) == '\0')
		{
		  if (*(p + 1) == '\0')
		    break;
823

824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
		  p += 1;
		}
	      else if (*(p + 1) == '.' &&
		       (*(p + 2) == G_DIR_SEPARATOR ||
			*(p + 2) == '\0'))
		{
		  if (q > filename + 1)
		    {
		      q--;
		      while (q > filename + 1 &&
			     *(q - 1) != G_DIR_SEPARATOR)
			q--;
		    }

		  if (*(p + 2) == '\0')
		    break;
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
		  p += 2;
		}
	      else
		{
		  *q++ = *p;
		  last_was_slash = FALSE;
		}
	    }
	  else
	    {
	      *q++ = *p;
	      last_was_slash = FALSE;
	    }
	}

      p++;
    }

  if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
    q--;

  *q = '\0';
}

static gboolean
866
867
868
869
870
871
gtk_file_system_unix_parse (GtkFileSystem     *file_system,
			    const GtkFilePath *base_path,
			    const gchar       *str,
			    GtkFilePath      **folder,
			    gchar            **file_part,
			    GError           **error)
872
{
873
  const char *base_filename;
874
875
876
  gchar *last_slash;
  gboolean result = FALSE;

877
  base_filename = gtk_file_path_get_string (base_path);
878
  g_return_val_if_fail (base_filename != NULL, FALSE);
879
  g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
880

881
882
883
  last_slash = strrchr (str, G_DIR_SEPARATOR);
  if (!last_slash)
    {
884
      *folder = gtk_file_path_copy (base_path);
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
      *file_part = g_strdup (str);
      result = TRUE;
    }
  else
    {
      gchar *folder_part;
      gchar *folder_path;
      GError *tmp_error = NULL;

      if (last_slash == str)
	folder_part = g_strdup ("/");
      else
	folder_part = g_filename_from_utf8 (str, last_slash - str,
					    NULL, NULL, &tmp_error);

      if (!folder_part)
	{
	  g_set_error (error,
		       GTK_FILE_SYSTEM_ERROR,
		       GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
		       "%s",
		       tmp_error->message);
	  g_error_free (tmp_error);
	}
      else
	{
	  if (folder_part[0] == G_DIR_SEPARATOR)
	    folder_path = folder_part;
	  else
	    {
	      folder_path = g_build_filename (base_filename, folder_part, NULL);
	      g_free (folder_part);
	    }

	  canonicalize_filename (folder_path);
920

921
	  *folder = filename_to_path (folder_path);
922
923
924
925
926
927
928
929
	  *file_part = g_strdup (last_slash + 1);

	  g_free (folder_path);

	  result = TRUE;
	}
    }

Owen Taylor's avatar
Owen Taylor committed
930
931
932
  return result;
}

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
static gchar *
gtk_file_system_unix_path_to_uri (GtkFileSystem     *file_system,
				  const GtkFilePath *path)
{
  return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
}

static gchar *
gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
				       const GtkFilePath *path)
{
  return g_strdup (gtk_file_path_get_string (path));
}

static GtkFilePath *
gtk_file_system_unix_uri_to_path (GtkFileSystem     *file_system,
				  const gchar       *uri)
{
  gchar *filename = g_filename_from_uri (uri, NULL, NULL);
  if (filename)
    return gtk_file_path_new_steal (filename);
  else
    return NULL;
}

static GtkFilePath *
gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
				       const gchar   *filename)
{
  return gtk_file_path_new_dup (filename);
}

965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
static const char *
get_icon_for_directory (const char *path)
{
  static char *desktop_path = NULL;

  if (!g_get_home_dir ())
    return "gnome-fs-directory";

  if (!desktop_path)
      desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);

  if (strcmp (g_get_home_dir (), path) == 0)
    return "gnome-fs-home";
  else if (strcmp (desktop_path, path) == 0)
    return "gnome-fs-desktop";
  else
    return "gnome-fs-directory";
}

984
985
986
987
988
989
990
static GdkPixbuf *
gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
				  const GtkFilePath *path,
				  GtkWidget         *widget,
				  gint               pixel_size,
				  GError           **error)
{
Federico Mena Quintero's avatar
Federico Mena Quintero committed
991
992
  const char *filename;
  IconType icon_type;
993
994
995
996
  const char *mime_type = NULL;
  char *dirname;
  GtkFileSystemUnix *system_unix;
  GtkFileFolderUnix *folder_unix;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
997

998
  system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
999
  filename = gtk_file_path_get_string (path);
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
  dirname = g_path_get_dirname (filename);
  folder_unix = g_hash_table_lookup (system_unix->folder_hash, dirname);
  g_free (dirname);

  if (folder_unix)
    {
      char *basename;
      struct stat_info_entry *entry;

      if (!fill_in_stats (folder_unix, error))
	return NULL;

      basename = g_path_get_basename (filename);
      entry = g_hash_table_lookup (folder_unix->stat_info, basename);
      g_free (basename);
      if (entry)
	{
	  if (entry->icon_type == ICON_UNDECIDED)
	    entry->icon_type = get_icon_type_from_stat (&entry->statbuf);
	  icon_type = entry->icon_type;
	  if (icon_type == ICON_REGULAR)
	    {
	      (void)fill_in_mime_type (folder_unix, NULL);
	      mime_type = entry->mime_type;
	    }
	}
      else
	icon_type = ICON_NONE;
    }
  else
    {
Morten Welinder's avatar
Morten Welinder committed
1031
#if 0
1032
      g_print ("No folder open for %s\n", filename);
Morten Welinder's avatar
Morten Welinder committed
1033
#endif
1034
1035
1036
1037
1038
1039

      icon_type = get_icon_type (filename, error);
      if (icon_type == ICON_REGULAR)
	mime_type = xdg_mime_get_mime_type_for_file (filename);
    }

Federico Mena Quintero's avatar
Federico Mena Quintero committed
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051

  /* FIXME: this function should not return NULL without setting the GError; we
   * should perhaps provide a "never fails" generic stock icon for when all else
   * fails.
   */

  if (icon_type == ICON_NONE)
    return NULL;

  if (icon_type != ICON_REGULAR)
    {
      const char *name;
1052

Federico Mena Quintero's avatar
Federico Mena Quintero committed
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
      switch (icon_type)
	{
	case ICON_BLOCK_DEVICE:
          name = "gnome-fs-blockdev";
	  break;
	case ICON_BROKEN_SYMBOLIC_LINK:
	  name = "gnome-fs-symlink";
	  break;
	case ICON_CHARACTER_DEVICE:
	  name = "gnome-fs-chardev";
	  break;
	case ICON_DIRECTORY:
1065
	  name = get_icon_for_directory (filename);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
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
	  break;
	case ICON_EXECUTABLE:
	  name ="gnome-fs-executable";
	  break;
	case ICON_FIFO:
	  name = "gnome-fs-fifo";
	  break;
	case ICON_SOCKET:
	  name = "gnome-fs-socket";
	  break;
	default:
	  g_assert_not_reached ();
	  return NULL;
	}

      return get_cached_icon (widget, name, pixel_size);
    }

  if (mime_type)
    {
      const char *separator;
      GString *icon_name;
      GdkPixbuf *pixbuf;

      separator = strchr (mime_type, '/');
      if (!separator)
	return NULL;

      icon_name = g_string_new ("gnome-mime-");
      g_string_append_len (icon_name, mime_type, separator - mime_type);
      g_string_append_c (icon_name, '-');
      g_string_append (icon_name, separator + 1);
      pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
      g_string_free (icon_name, TRUE);
      if (pixbuf)
	return pixbuf;

      icon_name = g_string_new ("gnome-mime-");
      g_string_append_len (icon_name, mime_type, separator - mime_type);
      pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
      g_string_free (icon_name, TRUE);
      if (pixbuf)
	return pixbuf;
    }

  return get_cached_icon (widget, "gnome-fs-regular", pixel_size);
1112
1113
}

1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
static void
bookmark_list_free (GSList *list)
{
  GSList *l;

  for (l = list; l; l = l->next)
    g_free (l->data);

  g_slist_free (list);
}

/* Returns whether a URI is a local file:// */
1126
static gboolean
1127
is_local_uri (const char *uri)
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
  char *filename;
  char *hostname;
  gboolean result;

  /* This is rather crude, but hey */
  filename = g_filename_from_uri (uri, &hostname, NULL);

  result = (filename && !hostname);

  g_free (filename);
  g_free (hostname);

  return result;
}

static char *
bookmark_get_filename (gboolean tmp_file)
{
  char *filename;

  filename = g_build_filename (g_get_home_dir (),
			       tmp_file ? BOOKMARKS_TMP_FILENAME : BOOKMARKS_FILENAME,
			       NULL);
  g_assert (filename != NULL);
  return filename;
}

static gboolean
bookmark_list_read (GSList **bookmarks, GError **error)
{
  gchar *filename;
  gchar *contents;
1161
  gboolean result = FALSE;
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

  filename = bookmark_get_filename (FALSE);
  *bookmarks = NULL;

  if (g_file_get_contents (filename, &contents, NULL, error))
    {
      gchar **lines = g_strsplit (contents, "\n", -1);
      int i;
      GHashTable *table;

      table = g_hash_table_new (g_str_hash, g_str_equal);

      for (i = 0; lines[i]; i++)
	{
	  if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
	    {
	      *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
	      g_hash_table_insert (table, lines[i], lines[i]);
	    }
	}

      g_free (contents);
      g_hash_table_destroy (table);
      g_strfreev (lines);

      *bookmarks = g_slist_reverse (*bookmarks);
      result = TRUE;
    }

  g_free (filename);

  return result;
}

static gboolean
bookmark_list_write (GSList *bookmarks, GError **error)
{
  char *tmp_filename;
  char *filename;
  gboolean result = TRUE;
  FILE *file;
  int fd;
  int saved_errno;

  /* First, write a temporary file */

  tmp_filename = bookmark_get_filename (TRUE);
  filename = bookmark_get_filename (FALSE);

  fd = g_mkstemp (tmp_filename);
  if (fd == -1)
    {
      saved_errno = errno;
      goto io_error;
    }

  if ((file = fdopen (fd, "w")) != NULL)
    {
      GSList *l;

      for (l = bookmarks; l; l = l->next)
	if (fputs (l->data, file) == EOF
	    || fputs ("\n", file) == EOF)
	  {
	    saved_errno = errno;
	    goto io_error;
	  }

      if (fclose (file) == EOF)
	{
	  saved_errno = errno;
	  goto io_error;
	}

      if (rename (tmp_filename, filename) == -1)
	{
	  saved_errno = errno;
	  goto io_error;
	}

      result = TRUE;
      goto out;
    }
  else
    {
      saved_errno = errno;

      /* fdopen() failed, so we can't do much error checking here anyway */
      close (fd);
    }

 io_error:

1255
1256
1257
  g_set_error (error,
	       GTK_FILE_SYSTEM_ERROR,
	       GTK_FILE_SYSTEM_ERROR_FAILED,
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
	       _("Bookmark saving failed (%s)"),
	       g_strerror (saved_errno));
  result = FALSE;

  if (fd != -1)
    unlink (tmp_filename); /* again, not much error checking we can do here */

 out:

  g_free (filename);
  g_free (tmp_filename);

  return result;
}

static gboolean
1274
1275
1276
1277
gtk_file_system_unix_insert_bookmark (GtkFileSystem     *file_system,
				      const GtkFilePath *path,
				      gint               position,
				      GError           **error)
1278
1279
{
  GSList *bookmarks;
1280
  int num_bookmarks;
1281
1282
1283
  GSList *l;
  char *uri;
  gboolean result;
1284
  GError *err;
1285

1286
1287
1288
1289
1290
1291
  err = NULL;
  if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
    {
      g_propagate_error (error, err);
      return FALSE;
    }
1292

1293
1294
1295
  num_bookmarks = g_slist_length (bookmarks);
  g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);

1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
  result = FALSE;

  uri = gtk_file_system_unix_path_to_uri (file_system, path);

  for (l = bookmarks; l; l = l->next)
    {
      const char *bookmark;

      bookmark = l->data;
      if (strcmp (bookmark, uri) == 0)
1306
1307
1308
1309
1310
1311
1312
1313
	{
	  g_set_error (error,
		       GTK_FILE_SYSTEM_ERROR,
		       GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
		       "%s already exists in the bookmarks list",
		       uri);
	  goto out;
	}
1314
1315
    }

1316
1317
  bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
  if (bookmark_list_write (bookmarks, error))
1318
    {
1319
1320
      result = TRUE;
      g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1321
1322
    }

1323
1324
 out:

1325
1326
1327
1328
  g_free (uri);
  bookmark_list_free (bookmarks);

  return result;
1329
1330
}

1331
1332
1333
1334
static gboolean
gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
				      const GtkFilePath *path,
				      GError           **error)
1335
{
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
  GSList *bookmarks;
  char *uri;
  GSList *l;
  gboolean result;

  if (!bookmark_list_read (&bookmarks, error))
    return FALSE;

  result = FALSE;

  uri = gtk_file_system_path_to_uri (file_system, path);

  for (l = bookmarks; l; l = l->next)
    {
      const char *bookmark;

      bookmark = l->data;
      if (strcmp (bookmark, uri) == 0)
1354
1355
1356
1357
	{
	  g_free (l->data);
	  bookmarks = g_slist_remove_link (bookmarks, l);
	  g_slist_free_1 (l);
1358

1359
1360
1361
1362
1363
	  if (bookmark_list_write (bookmarks, error))
	    {
	      result = TRUE;
	      g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
	    }
1364

1365
1366
	  goto out;
	}
1367
    }
1368
1369
1370
1371
1372
1373
1374
1375

  g_set_error (error,
	       GTK_FILE_SYSTEM_ERROR,
	       GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
	       "%s does not exist in the bookmarks list",
	       uri);

 out:
1376
1377
1378
1379
1380

  g_free (uri);
  bookmark_list_free (bookmarks);

  return result;
1381
1382
1383
1384
1385
}

static GSList *
gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
{
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
  GSList *bookmarks;
  GSList *result;
  GSList *l;

  if (!bookmark_list_read (&bookmarks, NULL))
    return NULL;

  result = NULL;

  for (l = bookmarks; l; l = l->next)
    {
      const char *name;

      name = l->data;

      if (is_local_uri (name))
	result = g_slist_prepend (result, gtk_file_system_unix_uri_to_path (file_system, name));
    }

  bookmark_list_free (bookmarks);

  result = g_slist_reverse (result);
  return result;
1409
1410
}

Owen Taylor's avatar
Owen Taylor committed
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
/*
 * GtkFileFolderUnix
 */
static GType
gtk_file_folder_unix_get_type (void)
{
  static GType file_folder_unix_type = 0;

  if (!file_folder_unix_type)
    {
      static const GTypeInfo file_folder_unix_info =
      {
	sizeof (GtkFileFolderUnixClass),
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_file_folder_unix_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkFileFolderUnix),
	0,		/* n_preallocs */
	(GInstanceInitFunc) gtk_file_folder_unix_init,
      };
1433

Owen Taylor's avatar
Owen Taylor committed
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
      static const GInterfaceInfo file_folder_info =
      {
	(GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
	NULL,			                              /* interface_finalize */
	NULL			                              /* interface_data */
      };

      file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
						      "GtkFileFolderUnix",
						      &file_folder_unix_info, 0);
      g_type_add_interface_static (file_folder_unix_type,
				   GTK_TYPE_FILE_FOLDER,
				   &file_folder_info);
    }

  return file_folder_unix_type;
}

static void
gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);

1457
1458
  folder_parent_class = g_type_class_peek_parent (class);

Owen Taylor's avatar
Owen Taylor committed
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
  gobject_class->finalize = gtk_file_folder_unix_finalize;
}

static void
gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
{
  iface->get_info = gtk_file_folder_unix_get_info;
  iface->list_children = gtk_file_folder_unix_list_children;
}

static void
gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
{
}

static void
gtk_file_folder_unix_finalize (GObject *object)
{
  GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
1479
1480
  g_hash_table_remove (folder_unix->system_unix->folder_hash, folder_unix->filename);

1481
1482
  if (folder_unix->stat_info)
    {
Morten Welinder's avatar
Morten Welinder committed
1483
#if 0
1484
      g_print ("Releasing information for directory %s\n", folder_unix->filename);
Morten Welinder's avatar
Morten Welinder committed
1485
#endif
1486
1487
1488
      g_hash_table_destroy (folder_unix->stat_info);
    }

Owen Taylor's avatar
Owen Taylor committed
1489
  g_free (folder_unix->filename);
1490

1491
  folder_parent_class->finalize (object);
Owen Taylor's avatar
Owen Taylor committed
1492
1493
1494
}

static GtkFileInfo *
1495
1496
1497
gtk_file_folder_unix_get_info (GtkFileFolder      *folder,
			       const GtkFilePath  *path,
			       GError            **error)
Owen Taylor's avatar
Owen Taylor committed
1498
1499
1500
{
  GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
  GtkFileInfo *info;
1501
  gchar *dirname, *basename;
1502
  const char *filename;
1503
1504
1505
  struct stat_info_entry *entry;
  gboolean file_must_exist;
  GtkFileInfoType types;
1506

1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
  if (!path)
    {
      struct stat buf;

      g_return_val_if_fail (filename_is_root (folder_unix->filename), NULL);

      if (stat (folder_unix->filename, &buf) != 0)
	return NULL;

      info = gtk_file_info_new ();
      gtk_file_info_set_display_name (info, "/");
      gtk_file_info_set_is_folder (info, TRUE);
      gtk_file_info_set_is_hidden (info, FALSE);
      gtk_file_info_set_mime_type (info, "x-directory/normal");
      gtk_file_info_set_modification_time (info, buf.st_mtime);
      gtk_file_info_set_size (info, buf.st_size);

      return info;
    }

1527
  filename = gtk_file_path_get_string (path);
1528
  g_return_val_if_fail (filename != NULL, NULL);
1529
  g_return_val_if_fail (g_path_is_absolute (