Re: glib outstanding stuff



Hi,

I rewrote g_file_get_contents, appended. Thanks for the comments so
far.

Havoc

/* gfileutils.h - File utility functions
 *
 *  Copyright 2000 Red Hat, Inc.
 *
 * GLib 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.
 *
 * GLib 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 GLib; see the file COPYING.LIB.  If not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *   Boston, MA 02111-1307, USA.
 */

#ifndef __GFILEUTILS_H__
#define __GFILEUTILS_H__

#ifdef __cplusplus
extern "C"
{
#endif

#define G_FILE_ERROR g_file_error_quark ()

typedef enum
{
  G_FILE_ERROR_EXIST,
  G_FILE_ERROR_ISDIR,
  G_FILE_ERROR_ACCES,
  G_FILE_ERROR_NAMETOOLONG,
  G_FILE_ERROR_NOENT,
  G_FILE_ERROR_NOTDIR,
  G_FILE_ERROR_NXIO,
  G_FILE_ERROR_NODEV,
  G_FILE_ERROR_ROFS,
  G_FILE_ERROR_TXTBSY,
  G_FILE_ERROR_FAULT,
  G_FILE_ERROR_LOOP,
  G_FILE_ERROR_NOSPC,
  G_FILE_ERROR_NOMEM,
  G_FILE_ERROR_MFILE,
  G_FILE_ERROR_NFILE,
  G_FILE_ERROR_FAILED
} GFileError;

typedef enum
{
  G_FILE_TEST_IS_DIR        = 1 << 1,
  G_FILE_TEST_EXISTS        = 1 << 2,
  G_FILE_TEST_IS_EXECUTABLE = 1 << 3,
  G_FILE_TEST_IS_SYMLINK    = 1 << 4
} GFileTest;

GQuark g_file_error_quark ();

gboolean g_file_test         (const gchar  *filename,
                              GFileTest     test);
gboolean g_file_get_contents (const gchar  *filename,
                              gchar       **contents,
                              guint        *length,
                              GError      **error);


#ifdef __cplusplus
}
#endif

#endif /* __GFILEUTILS_H__ */


/* gfileutils.c - File utility functions
 *
 *  Copyright 2000 Red Hat, Inc.
 *
 * GLib 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.
 *
 * GLib 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 GLib; see the file COPYING.LIB.  If not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *   Boston, MA 02111-1307, USA.
 */

#include "glib.h"

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#define _(x) x

/**
 * g_file_test:
 * @filename: a filename to test
 * @test: bitfield of #GFileTest flags
 * 
 * Returns TRUE if any of the tests in the bitfield @test are
 * TRUE. For example, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)
 * will return TRUE if the file exists; the check whether it's
 * a directory doesn't matter since the existence test is TRUE.
 * With the current set of available tests, there's no point
 * passing in more than one test at a time.
 *
 * Return value: whether a test was TRUE
 **/
gboolean
g_file_test (const gchar *filename,
             GFileTest    test)
{
  struct stat s;

  if (test & G_FILE_TEST_EXISTS)
    return (access (filename, F_OK) == 0);
  else if (test & G_FILE_TEST_IS_EXECUTABLE)
    return (access (filename, X_OK) == 0);
  else
    {
      if (stat (filename, &s) < 0)
        return FALSE;
      
      if ((test & G_FILE_TEST_IS_DIR) &&
          S_ISDIR (s.st_mode))
        return TRUE;
      else if ((test & G_FILE_TEST_IS_SYMLINK) &&
               S_ISLNK (s.st_mode))
        return TRUE;
      else
        return FALSE;
    }
}

GQuark
g_file_error_quark ()
{
  static GQuark q = 0;
  if (q == 0)
    q = g_quark_from_static_string ("g-file-error-quark");

  return q;
}

static GFileError
errno_to_g_file_error (gint en)
{
  switch (en)
    {
#ifdef EEXIST
    case EEXIST:
      return G_FILE_ERROR_EXIST;
      break;
#endif

#ifdef EISDIR
    case EISDIR:
      return G_FILE_ERROR_ISDIR;
      break;
#endif

#ifdef EACCES
    case EACCES:
      return G_FILE_ERROR_ACCES;
      break;
#endif

#ifdef ENAMETOOLONG
    case ENAMETOOLONG:
      return G_FILE_ERROR_NAMETOOLONG;
      break;
#endif

#ifdef ENOENT
    case ENOENT:
      return G_FILE_ERROR_NOENT;
      break;
#endif

#ifdef ENOTDIR
    case ENOTDIR:
      return G_FILE_ERROR_NOTDIR;
      break;
#endif

#ifdef ENXIO
    case ENXIO:
      return G_FILE_ERROR_NXIO;
      break;
#endif

#ifdef ENODEV
    case ENODEV:
      return G_FILE_ERROR_NODEV;
      break;
#endif

#ifdef EROFS
    case EROFS:
      return G_FILE_ERROR_ROFS;
      break;
#endif

#ifdef ETXTBSY
    case ETXTBSY:
      return G_FILE_ERROR_TXTBSY;
      break;
#endif

#ifdef EFAULT
    case EFAULT:
      return G_FILE_ERROR_FAULT;
      break;
#endif

#ifdef ELOOP
    case ELOOP:
      return G_FILE_ERROR_LOOP;
      break;
#endif

#ifdef ENOSPC
    case ENOSPC:
      return G_FILE_ERROR_NOSPC;
      break;
#endif

#ifdef ENOMEM
    case ENOMEM:
      return G_FILE_ERROR_NOMEM;
      break;
#endif

#ifdef EMFILE
    case EMFILE:
      return G_FILE_ERROR_MFILE;
      break;
#endif

#ifdef ENFILE
    case ENFILE:
      return G_FILE_ERROR_NFILE;
      break;
#endif

    default:
      return G_FILE_ERROR_FAILED;
      break;
    }

  return G_FILE_ERROR_FAILED;
}

static gboolean
get_contents_stdio (const gchar *filename,
                    FILE        *f,
                    gchar      **contents,
                    guint       *length,
                    GError     **error)
{
  gchar buf[2048];
  size_t bytes;
  GString *str;

  g_assert (f != NULL);
  
  str = g_string_new ("");
  
  while (!feof (f))
    {
      bytes = fread (buf, 1, 2048, f);
      
      if (ferror (f))
        {
          g_set_error (error,
                       G_FILE_ERROR,
                       errno_to_g_file_error (errno),
                       _("Error reading file '%s': %s"),
                       filename, strerror (errno));

          g_string_free (str, TRUE);
          
          return FALSE;
        }

      g_string_append_len (str, buf, bytes);
    }

  fclose (f);

  if (length)
    *length = str->len;
  
  *contents = g_string_free (str, FALSE);

  return TRUE;  
}

static gboolean
get_contents_regfile (const gchar *filename,
                      struct stat *stat_buf,
                      gint         fd,
                      gchar      **contents,
                      guint       *length,
                      GError     **error)
{
  gchar *buf;
  size_t bytes_read;
  size_t size;
      
  size = stat_buf->st_size;

  buf = g_new (gchar, size + 1);
      
  bytes_read = 0;
  while (bytes_read < size)
    {
      gint rc;
          
      rc = read (fd, buf + bytes_read, size - bytes_read);

      if (rc < 0)
        {
          if (errno != EINTR) 
            {
              close (fd);

              g_free (buf);
                  
              g_set_error (error,
                           G_FILE_ERROR,
                           errno_to_g_file_error (errno),
                           _("Failed to read from file '%s': %s"),
                           filename, strerror (errno));

              return FALSE;
            }
        }
      else if (rc == 0)
        break;
      else
        bytes_read += rc;
    }
      
  buf[bytes_read] = '\0';

  if (length)
    *length = bytes_read;
  
  *contents = buf;

  return TRUE;
}

static gboolean
get_contents_posix (const gchar *filename,
                    gchar      **contents,
                    guint       *length,
                    GError     **error)
{
  struct stat stat_buf;
  gint fd;
  
  fd = open (filename, O_RDONLY);

  if (fd < 0)
    {
      g_set_error (error,
                   G_FILE_ERROR,
                   errno_to_g_file_error (errno),
                   _("Failed to open file '%s': %s"),
                   filename, strerror (errno));

      return FALSE;
    }

  /* I don't think this will ever fail, aside from ENOMEM, but. */
  if (fstat (fd, &stat_buf) < 0)
    {
      close (fd);
      
      g_set_error (error,
                   G_FILE_ERROR,
                   errno_to_g_file_error (errno),
                   _("Failed to get attributes of file '%s': fstat() failed: %s"),
                   filename, strerror (errno));

      return FALSE;
    }

  if (S_ISREG (stat_buf.st_mode))
    {
      return get_contents_regfile (filename,
                                   &stat_buf,
                                   fd,
                                   contents,
                                   length,
                                   error);
    }
  else
    {
      FILE *f;

      f = fdopen (fd, "r");
      
      if (f == NULL)
        {
          g_set_error (error,
                       G_FILE_ERROR,
                       errno_to_g_file_error (errno),
                       _("Failed to open file '%s': fdopen() failed: %s"),
                       filename, strerror (errno));
          
          return FALSE;
        }
  
      return get_contents_stdio (filename, f, contents, length, error);
    }
}

#ifdef G_OS_WIN32
static gboolean
get_contents_win32 (const gchar *filename,
                    gchar      **contents,
                    guint       *length,
                    GError     **error)
{
  FILE *f;

  /* I guess you want binary mode; maybe you want text sometimes? */
  f = fopen (filename, "rb");

  if (f == NULL)
    {
      g_set_error (error,
                   G_FILE_ERROR,
                   errno_to_g_file_error (errno),
                   _("Failed to open file '%s': %s"),
                   filename, strerror (errno));
      
      return FALSE;
    }
  
  return get_contents_stdio (filename, f, contents, length, error);
}
#endif

/**
 * g_file_get_contents:
 * @filename: a file to read contents from
 * @contents: location to store an allocated string
 * @length: location to store length in bytes of the contents
 * @error: return location for a #GError
 * 
 * Reads an entire file into allocated memory, with good error
 * checking. If @error is set, FALSE is returned, and
 * @contents is set to NULL. If TRUE is returned, @error will
 * not be set, and @contents will be set to the file contents.
 * The string stored in @contents will be nul-terminated,
 * so for text files you can pass NULL for the @length argument.
 *
 * FIXME currently crashes if the file is too big to fit in memory;
 * should probably use g_try_malloc() when we have that function.
 * 
 * Return value: TRUE on success, FALSE if error is set
 **/
gboolean
g_file_get_contents (const gchar *filename,
                     gchar      **contents,
                     guint       *length,
                     GError     **error)
{  
  g_return_val_if_fail (filename != NULL, FALSE);
  g_return_val_if_fail (contents != NULL, FALSE);

  *contents = NULL;
  if (length)
    *length = 0;

#ifdef G_OS_WIN32
  return get_contents_win32 (filename, contents, length, error);
#else
  return get_contents_posix (filename, contents, length, error);
#endif
}





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]