Logo Search packages:      
Sourcecode: gnome-vfs2 version File versions  Download package

gzip-method.c

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gzip-method.c - GZIP access method for the GNOME Virtual File
   System.

   Copyright (C) 1999 Free Software Foundation

   The Gnome Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The Gnome 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Author: Ettore Perazzoli <ettore@comm2000.it> */

#include <config.h>

#include <glib/galloca.h>
#include <glib/gmessages.h>
#include <glib/gstrfuncs.h>
#include <libgnomevfs/gnome-vfs-mime.h>
#include <libgnomevfs/gnome-vfs-module.h>
#include <libgnomevfs/gnome-vfs-ops.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <zlib.h>

struct _GZipMethodHandle {
      GnomeVFSURI *uri;
      GnomeVFSHandle *parent_handle;
      GnomeVFSOpenMode open_mode;
      time_t modification_time;

      GnomeVFSResult last_vfs_result;
      gint last_z_result;
      z_stream zstream;
      guchar *buffer;
      guint32 crc;
};
typedef struct _GZipMethodHandle GZipMethodHandle;


#define GZIP_MAGIC_1 0x1f
#define GZIP_MAGIC_2 0x8b

#define GZIP_FLAG_ASCII        0x01 /* bit 0 set: file probably ascii text */
#define GZIP_FLAG_HEAD_CRC     0x02 /* bit 1 set: header CRC present */
#define GZIP_FLAG_EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
#define GZIP_FLAG_ORIG_NAME    0x08 /* bit 3 set: original file name present */
#define GZIP_FLAG_COMMENT      0x10 /* bit 4 set: file comment present */
#define GZIP_FLAG_RESERVED     0xE0 /* bits 5..7: reserved */

#define GZIP_HEADER_SIZE 10
#define GZIP_FOOTER_SIZE 8

#define Z_BUFSIZE 16384


static GnomeVFSResult   do_open           (GnomeVFSMethod *method,
                               GnomeVFSMethodHandle **method_handle,
                               GnomeVFSURI *uri,
                               GnomeVFSOpenMode mode,
                               GnomeVFSContext *context);

static GnomeVFSResult   do_create   (GnomeVFSMethod *method,
                               GnomeVFSMethodHandle **method_handle,
                               GnomeVFSURI *uri,
                               GnomeVFSOpenMode mode,
                               gboolean exclusive,
                               guint perm,
                               GnomeVFSContext *context);

static GnomeVFSResult   do_close    (GnomeVFSMethod *method,
                               GnomeVFSMethodHandle *method_handle,
                               GnomeVFSContext *context);

static GnomeVFSResult   do_read           (GnomeVFSMethod *method,
                               GnomeVFSMethodHandle *method_handle,
                               gpointer buffer,
                               GnomeVFSFileSize num_bytes,
                               GnomeVFSFileSize *bytes_read,
                               GnomeVFSContext *context);

static GnomeVFSResult   do_write    (GnomeVFSMethod *method,
                               GnomeVFSMethodHandle *method_handle,
                               gconstpointer buffer,
                               GnomeVFSFileSize num_bytes,
                               GnomeVFSFileSize *bytes_written,
                               GnomeVFSContext *context);

static GnomeVFSResult   do_get_file_info(GnomeVFSMethod *method,
                               GnomeVFSURI *uri,
                               GnomeVFSFileInfo *file_info,
                               GnomeVFSFileInfoOptions options,
                               GnomeVFSContext *context);

static gboolean         do_is_local (GnomeVFSMethod *method,
                               const GnomeVFSURI *uri);

static GnomeVFSMethod method = {
      sizeof (GnomeVFSMethod),
      do_open,
      do_create,
      do_close,
      do_read,
      do_write,
      NULL,             /* seek */
      NULL,             /* tell */
      NULL,             /* truncate_handle FIXME bugzilla.eazel.com 1175 */
      NULL,             /* open_directory */
      NULL,             /* close_directory */
      NULL,             /* read_directory */
      do_get_file_info,
      NULL,             /* get_file_info_from_handle */
      do_is_local,
      NULL,             /* make_directory */
      NULL,             /* remove_directory */
      NULL,             /* move */
      NULL,                   /* unlink */
      NULL,             /* check_same_fs */
      NULL,             /* set_file_info */
      NULL,                   /* truncate */
      NULL,             /* find_directory */
      NULL                    /* create_symbolic_link */
};

#define RETURN_IF_FAIL(action)                  \
G_STMT_START{                             \
      GnomeVFSResult __tmp_result;        \
                                    \
      __tmp_result = (action);            \
      if (__tmp_result != GNOME_VFS_OK)   \
            return __tmp_result;          \
}G_STMT_END

#define VALID_URI(u) ((u)->parent!=NULL&&(((u)->text==NULL)||((u)->text[0]=='\0')||(((u)->text[0]=='/')&&((u)->text[1]=='\0'))))


/* GZip handle creation/destruction.  */

static GZipMethodHandle *
gzip_method_handle_new (GnomeVFSHandle *parent_handle,
                        time_t modification_time,
                        GnomeVFSURI *uri,
                  GnomeVFSOpenMode open_mode)
{
      GZipMethodHandle *new;

      new = g_new (GZipMethodHandle, 1);

      new->parent_handle = parent_handle;
      new->modification_time = modification_time;
      new->uri = gnome_vfs_uri_ref (uri);
      new->open_mode = open_mode;

      new->buffer = NULL;
      new->crc = crc32 (0, Z_NULL, 0);

      return new;
}

static void
gzip_method_handle_destroy (GZipMethodHandle *handle)
{
      gnome_vfs_uri_unref (handle->uri);
      g_free (handle->buffer);
      g_free (handle);
}


/* GZip method initialization for compression/decompression.  */

static gboolean
gzip_method_handle_init_for_inflate (GZipMethodHandle *handle)
{
      handle->zstream.zalloc = NULL;
      handle->zstream.zfree = NULL;
      handle->zstream.opaque = NULL;

      g_free (handle->buffer);

      handle->buffer = g_malloc (Z_BUFSIZE);
      handle->zstream.next_in = handle->buffer;
      handle->zstream.avail_in = 0;

      if (inflateInit2 (&handle->zstream, -MAX_WBITS) != Z_OK) {
            g_free (handle->buffer);
            return FALSE;
      }

      handle->last_z_result = Z_OK;
      handle->last_vfs_result = GNOME_VFS_OK;

      return TRUE;
}

static gboolean
gzip_method_handle_init_for_deflate (GZipMethodHandle *handle)
{
      handle->zstream.zalloc = NULL;
      handle->zstream.zfree = NULL;
      handle->zstream.opaque = NULL;

      g_free (handle->buffer);

      handle->buffer = g_malloc (Z_BUFSIZE);
      handle->zstream.next_out = handle->buffer;
      handle->zstream.avail_out = Z_BUFSIZE;

      /* FIXME bugzilla.eazel.com 1174: We want this to be user-configurable.  */
      if (deflateInit2 (&handle->zstream, Z_DEFAULT_COMPRESSION,
                    Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL,
                    Z_DEFAULT_STRATEGY) != Z_OK) {
            g_free (handle->buffer);
            return FALSE;
      }

      handle->last_z_result = Z_OK;
      handle->last_vfs_result = GNOME_VFS_OK;

      return TRUE;
}


static GnomeVFSResult
result_from_z_result (gint z_result)
{
      switch (z_result) {
      case Z_OK:
      case Z_STREAM_END: /* FIXME bugzilla.eazel.com 1173: Is this right? */
            return GNOME_VFS_OK;
      case Z_DATA_ERROR:
            return GNOME_VFS_ERROR_CORRUPTED_DATA;
      default:
            return GNOME_VFS_ERROR_INTERNAL;
      }
}


/* Functions to skip data in the file.  */

static GnomeVFSResult
skip_string (GnomeVFSHandle *handle)
{
      GnomeVFSResult result;
      guchar c;
      GnomeVFSFileSize bytes_read;

      do {
            result = gnome_vfs_read (handle, &c, 1, &bytes_read);
            RETURN_IF_FAIL (result);

            if (bytes_read != 1)
                  return GNOME_VFS_ERROR_WRONG_FORMAT;
      } while (c != 0);

      return GNOME_VFS_OK;
}

static gboolean
skip (GnomeVFSHandle *handle,
      GnomeVFSFileSize num_bytes)
{
      GnomeVFSResult result;
      guchar *tmp;
      GnomeVFSFileSize bytes_read;

      tmp = g_alloca (num_bytes);

      result = gnome_vfs_read (handle, tmp, num_bytes, &bytes_read);
      RETURN_IF_FAIL (result);

      if (bytes_read != num_bytes)
            return GNOME_VFS_ERROR_WRONG_FORMAT;

      return TRUE;
}


/* Utility function to write a gulong value.  */

static GnomeVFSResult
write_guint32 (GnomeVFSHandle *handle,
               guint32 value)
{
      guint i;
      guchar buffer[4];
      GnomeVFSFileSize bytes_written;

      for (i = 0; i < 4; i++) {
            buffer[i] = value & 0xff;
            value >>= 8;
      }

      return gnome_vfs_write (handle, buffer, 4, &bytes_written);
}


/* GZIP Header functions.  */

static GnomeVFSResult
read_gzip_header (GnomeVFSHandle *handle,
                  time_t *modification_time)
{
      GnomeVFSResult result;
      guchar buffer[GZIP_HEADER_SIZE];
      GnomeVFSFileSize bytes_read;
      guint mode;
      guint flags;

      result = gnome_vfs_read (handle, buffer, GZIP_HEADER_SIZE,
                         &bytes_read);
      RETURN_IF_FAIL (result);

      if (bytes_read != GZIP_HEADER_SIZE)
            return GNOME_VFS_ERROR_WRONG_FORMAT;

      if (buffer[0] != GZIP_MAGIC_1 || buffer[1] != GZIP_MAGIC_2)
            return GNOME_VFS_ERROR_WRONG_FORMAT;

      mode = buffer[2];
      if (mode != 8) /* Mode: deflate */
            return GNOME_VFS_ERROR_WRONG_FORMAT;

      flags = buffer[3];

      if (flags & GZIP_FLAG_RESERVED)
            return GNOME_VFS_ERROR_WRONG_FORMAT;

      if (flags & GZIP_FLAG_EXTRA_FIELD) {
            guchar tmp[2];
            GnomeVFSFileSize bytes_read;

            if (gnome_vfs_read (handle, tmp, 2, &bytes_read)
                || bytes_read != 2)
                  return GNOME_VFS_ERROR_WRONG_FORMAT;
            if (! skip (handle, tmp[0] | (tmp[0] << 8)))
                  return GNOME_VFS_ERROR_WRONG_FORMAT;
      }

      if (flags & GZIP_FLAG_ORIG_NAME)
            RETURN_IF_FAIL (skip_string (handle));

      if (flags & GZIP_FLAG_COMMENT)
            RETURN_IF_FAIL (skip_string (handle));

      if (flags & GZIP_FLAG_HEAD_CRC)
            RETURN_IF_FAIL (skip (handle, 2));

      *modification_time = (buffer[4] | (buffer[5] << 8)
                        | (buffer[6] << 16) | (buffer[7] << 24));
      return GNOME_VFS_OK;
}

static GnomeVFSResult
write_gzip_header (GnomeVFSHandle *handle, time_t modification_time)
{
      GnomeVFSResult result;
      guchar buffer[GZIP_HEADER_SIZE];
      GnomeVFSFileSize bytes_written;

      buffer[0] = GZIP_MAGIC_1;     /* magic 1 */
      buffer[1] = GZIP_MAGIC_2;     /* magic 2 */
      buffer[2] = Z_DEFLATED;       /* method */
      buffer[3] = 0;                /* flags */
      buffer[4] = (guchar) ((modification_time >>  0) & 0xFF);   /* time 1 */
      buffer[5] = (guchar) ((modification_time >>  8) & 0xFF);   /* time 2 */
      buffer[6] = (guchar) ((modification_time >> 16) & 0xFF);   /* time 3 */
      buffer[7] = (guchar) ((modification_time >> 24) & 0xFF);   /* time 4 */
      buffer[8] = 0;                /* xflags */
      buffer[9] = 3;                /* OS (Unix) */

      result = gnome_vfs_write (handle, buffer, GZIP_HEADER_SIZE,
                          &bytes_written);
      RETURN_IF_FAIL (result);

      if (bytes_written != GZIP_HEADER_SIZE)
            return GNOME_VFS_ERROR_IO;

      return GNOME_VFS_OK;
}

static GnomeVFSResult
flush_write (GZipMethodHandle *gzip_handle)
{
      GnomeVFSHandle *parent_handle;
      GnomeVFSResult result;
      gboolean done;
      z_stream *zstream;
      gint z_result;

      zstream = &gzip_handle->zstream;
      zstream->avail_in = 0;         /* (Should be zero already anyway.)  */

      parent_handle = gzip_handle->parent_handle;

      done = FALSE;
      z_result = Z_OK;
      while (z_result == Z_OK || z_result == Z_STREAM_END) {
            GnomeVFSFileSize bytes_written;
            GnomeVFSFileSize len;

            len = Z_BUFSIZE - zstream->avail_out;

            result = gnome_vfs_write (parent_handle, gzip_handle->buffer,
                                len, &bytes_written);
            RETURN_IF_FAIL (result);

            zstream->next_out = gzip_handle->buffer;
            zstream->avail_out = Z_BUFSIZE;

            if (done)
                  break;

            z_result = deflate(zstream, Z_FINISH);

            /* Ignore the second of two consecutive flushes.  */
            if (z_result == Z_BUF_ERROR)
                  z_result = Z_OK;

            /* Deflate has finished flushing only when it hasn't used up
               all the available space in the output buffer.  */
            done = (zstream->avail_out != 0 || z_result == Z_STREAM_END);
      }

      result = write_guint32 (parent_handle, gzip_handle->crc);
      RETURN_IF_FAIL (result);

      result = write_guint32 (parent_handle, zstream->total_in);
      RETURN_IF_FAIL (result);

      if (z_result == Z_OK || z_result == Z_STREAM_END)
            return GNOME_VFS_OK;
      else
            return result_from_z_result (z_result);
}


/* Open.  */

/* TODO: 
   - Check that there is no subpath.  */
static GnomeVFSResult
do_open (GnomeVFSMethod *method,
       GnomeVFSMethodHandle **method_handle,
       GnomeVFSURI *uri,
       GnomeVFSOpenMode open_mode,
       GnomeVFSContext *context)
{
      GnomeVFSHandle *parent_handle;
      GnomeVFSURI *parent_uri;
      GnomeVFSResult result;
      GZipMethodHandle *gzip_handle;
      time_t modification_time;

      _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
      _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);

      /* Check that the URI is valid.  */
      if (!VALID_URI(uri)) return GNOME_VFS_ERROR_INVALID_URI;

      parent_uri = uri->parent;

      if (open_mode & GNOME_VFS_OPEN_RANDOM)
            return GNOME_VFS_ERROR_NOT_SUPPORTED;

      result = gnome_vfs_open_uri (&parent_handle, parent_uri, open_mode);
      RETURN_IF_FAIL (result);

      if (open_mode & GNOME_VFS_OPEN_READ) {
            result = read_gzip_header (parent_handle, &modification_time);
            if (result != GNOME_VFS_OK) {
                  gnome_vfs_close (parent_handle);
                  return result;
            }

            gzip_handle = gzip_method_handle_new (parent_handle,
                                          modification_time,
                                          uri,
                                          open_mode);

            if (! gzip_method_handle_init_for_inflate (gzip_handle)) {
                  gnome_vfs_close (parent_handle);
                  gzip_method_handle_destroy (gzip_handle);
                  return GNOME_VFS_ERROR_INTERNAL;
            }
      } else {                          /* GNOME_VFS_OPEN_WRITE */
            modification_time = time (NULL);
            result = write_gzip_header (parent_handle, modification_time);
            RETURN_IF_FAIL (result);

            /* FIXME bugzilla.eazel.com 1172: need to set modification_time */
            gzip_handle = gzip_method_handle_new (parent_handle,
                                          modification_time,
                                          uri,
                                          open_mode);

            if (! gzip_method_handle_init_for_deflate (gzip_handle)) {
                  gnome_vfs_close (parent_handle);
                  gzip_method_handle_destroy (gzip_handle);
                  return GNOME_VFS_ERROR_INTERNAL;
            }
      }

      *method_handle = (GnomeVFSMethodHandle *) gzip_handle;

      return GNOME_VFS_OK;
}


/* Create.  */

static GnomeVFSResult
do_create (GnomeVFSMethod *method,
         GnomeVFSMethodHandle **method_handle,
         GnomeVFSURI *uri,
         GnomeVFSOpenMode mode,
         gboolean exclusive,
         guint perm,
         GnomeVFSContext *context)
{
      _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
      _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);

      return GNOME_VFS_ERROR_NOT_SUPPORTED; /* FIXME bugzilla.eazel.com 1170 */
}


/* Close.  */

static GnomeVFSResult
do_close (GnomeVFSMethod *method,
        GnomeVFSMethodHandle *method_handle,
        GnomeVFSContext *context)
{
      GZipMethodHandle *gzip_handle;
      GnomeVFSResult result;

      _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);

      gzip_handle = (GZipMethodHandle *) method_handle;

      if (gzip_handle->open_mode & GNOME_VFS_OPEN_WRITE)
            result = flush_write (gzip_handle);
      else
            result = GNOME_VFS_OK;

      if (result == GNOME_VFS_OK)
            result = gnome_vfs_close (gzip_handle->parent_handle);

      gzip_method_handle_destroy (gzip_handle);

      return result;
}


/* Read. */
static GnomeVFSResult
fill_buffer (GZipMethodHandle *gzip_handle,
           GnomeVFSFileSize num_bytes)
{
      GnomeVFSResult result;
      GnomeVFSFileSize count;
      z_stream *zstream;

      zstream = &gzip_handle->zstream;

      if (zstream->avail_in > 0)
            return GNOME_VFS_OK;

      result = gnome_vfs_read (gzip_handle->parent_handle,
                         gzip_handle->buffer,
                         Z_BUFSIZE,
                         &count);

      if (result != GNOME_VFS_OK) {
            if (zstream->avail_out == num_bytes)
                  return result;
            gzip_handle->last_vfs_result = result;
      } else {
            zstream->next_in = gzip_handle->buffer;
            zstream->avail_in = count;
      }

      return GNOME_VFS_OK;
}

/* FIXME bugzilla.eazel.com 1165: TODO:
   - Concatenated GZIP file handling.  */
static GnomeVFSResult
do_read (GnomeVFSMethod *method,
       GnomeVFSMethodHandle *method_handle,
       gpointer buffer,
       GnomeVFSFileSize num_bytes,
       GnomeVFSFileSize *bytes_read,
       GnomeVFSContext *context)
{
      GZipMethodHandle *gzip_handle;
      GnomeVFSResult result;
      z_stream *zstream;
      int z_result;
      guchar *crc_start;

      *bytes_read = 0;

      crc_start = buffer;

      gzip_handle = (GZipMethodHandle *) method_handle;

      zstream = &gzip_handle->zstream;

      if (gzip_handle->last_z_result != Z_OK) {
            if (gzip_handle->last_z_result == Z_STREAM_END) {
                  *bytes_read = 0;
                  return GNOME_VFS_ERROR_EOF;
            } else
                  return result_from_z_result (gzip_handle->last_z_result);
      } else if (gzip_handle->last_vfs_result != GNOME_VFS_OK) {
            return gzip_handle->last_vfs_result;
      }

      zstream->next_out = buffer;
      zstream->avail_out = num_bytes;

      while (zstream->avail_out != 0) {
            result = fill_buffer (gzip_handle, num_bytes);
            RETURN_IF_FAIL (result);

            z_result = inflate (&gzip_handle->zstream, Z_NO_FLUSH);
            if (z_result == Z_STREAM_END) {
                  gzip_handle->last_z_result = z_result;
                  break;
            } else if (z_result != Z_OK) {      
                  /* FIXME bugzilla.eazel.com 1165: Concatenated GZIP files?  */
                  gzip_handle->last_z_result = z_result;
            }

            if (gzip_handle->last_z_result != Z_OK
                && zstream->avail_out == num_bytes)
                  return result_from_z_result (gzip_handle->last_z_result);
      }

      gzip_handle->crc = crc32 (gzip_handle->crc,
                          crc_start,
                          (guint) (zstream->next_out - crc_start));

      *bytes_read = num_bytes - zstream->avail_out;

      return GNOME_VFS_OK;
}


/* Write.  */

static GnomeVFSResult
do_write (GnomeVFSMethod *method,
        GnomeVFSMethodHandle *method_handle,
        gconstpointer buffer,
        GnomeVFSFileSize num_bytes,
        GnomeVFSFileSize *bytes_written,
        GnomeVFSContext *context)
{
      GZipMethodHandle *gzip_handle;
      GnomeVFSResult result;
      z_stream *zstream;
      gint z_result;

      gzip_handle = (GZipMethodHandle *) method_handle;
      zstream = &gzip_handle->zstream;

      /* This cast sucks.  It is not my fault, though.  :-)  */
      zstream->next_in = (gpointer) buffer;
      zstream->avail_in = num_bytes;

      result = GNOME_VFS_OK;

      while (zstream->avail_in != 0 && result == GNOME_VFS_OK) {
            if (zstream->avail_out == 0) {
                  GnomeVFSFileSize written;

                  zstream->next_out = gzip_handle->buffer;
                  result = gnome_vfs_write (gzip_handle->parent_handle,
                                      gzip_handle->buffer,
                                      Z_BUFSIZE, &written);

                  if (result != GNOME_VFS_OK)
                        break;

                  zstream->avail_out += written;
            }

            z_result = deflate (zstream, Z_NO_FLUSH);
            result = result_from_z_result (z_result);
      }

      gzip_handle->crc = crc32 (gzip_handle->crc, buffer, num_bytes);

      *bytes_written = num_bytes - zstream->avail_in;

      return result;
}


static GnomeVFSResult 
do_get_file_info  (GnomeVFSMethod *method,
                 GnomeVFSURI *uri,
               GnomeVFSFileInfo *file_info,
               GnomeVFSFileInfoOptions options,
               GnomeVFSContext *context) {
      GnomeVFSResult result;

      if (!VALID_URI(uri)) return GNOME_VFS_ERROR_INVALID_URI;

      result = gnome_vfs_get_file_info_uri(uri->parent, file_info, options);
      if(result == GNOME_VFS_OK) {
            gint namelen = strlen(file_info->name);
            
            /* work out the name */
            /* FIXME bugzilla.eazel.com 2790: handle uppercase */
            if(namelen > 3 &&
                        file_info->name[namelen-1] == 'z' &&
                        file_info->name[namelen-2] == 'g' &&
                        file_info->name[namelen-3] == '.')
                  file_info->name[namelen-3] = '\0';

            /* we can't tell the size without uncompressing it */
            /*file_info->valid_fields &= ~GNOME_VFS_FILE_INFO_FIELDS_SIZE;*/

            /* guess the mime type of the file inside */
            /* FIXME bugzilla.eazel.com 2791: guess mime based on contents */
            file_info->mime_type = g_strdup(gnome_vfs_mime_type_from_name(file_info->name));
      }

      return result;
}



static gboolean
do_is_local (GnomeVFSMethod *method,
           const GnomeVFSURI *uri)
{
      g_return_val_if_fail (uri != NULL, FALSE);

      return gnome_vfs_uri_is_local (uri->parent);
}


/* Init.  */

GnomeVFSMethod *
vfs_module_init (const char *method_name, const char *args)
{
      return &method;
}

void
vfs_module_shutdown (GnomeVFSMethod *method)
{
}


Generated by  Doxygen 1.6.0   Back to index