lexer: Reimplement for better testability and internationalization.
[pspp-builds.git] / src / data / file-handle-def.c
index 872559154469cceebbbf3272aab1702a319b80e8..2b8e40ce347b7935b68589083dcda03030c07a74 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include <config.h>
 
 
 #include <config.h>
 
-#include "file-handle-def.h"
+#include "data/file-handle-def.h"
 
 #include <assert.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 
 
 #include <assert.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include <libpspp/compiler.h>
-#include <libpspp/hash.h>
-#include <libpspp/ll.h>
-#include <libpspp/message.h>
-#include <libpspp/str.h>
-#include <data/file-name.h>
-#include <data/variable.h>
-#include <data/scratch-handle.h>
+#include "libpspp/compiler.h"
+#include "libpspp/hmap.h"
+#include "libpspp/i18n.h"
+#include "libpspp/message.h"
+#include "libpspp/str.h"
+#include "libpspp/hash-functions.h"
+#include "data/file-name.h"
+#include "data/variable.h"
+#include "data/scratch-handle.h"
 
 
-#include "xalloc.h"
+#include "gl/xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -40,7 +41,7 @@
 /* File handle. */
 struct file_handle
   {
 /* File handle. */
 struct file_handle
   {
-    struct ll ll;               /* Element in global list. */
+    struct hmap_node name_node; /* Element in named_handles hmap. */
     size_t ref_cnt;             /* Number of references. */
     char *id;                   /* Identifier token, NULL if none. */
     char *name;                 /* User-friendly identifying name. */
     size_t ref_cnt;             /* Number of references. */
     char *id;                   /* Identifier token, NULL if none. */
     char *name;                 /* User-friendly identifying name. */
@@ -49,6 +50,7 @@ struct file_handle
     /* FH_REF_FILE only. */
     char *file_name;           /* File name as provided by user. */
     enum fh_mode mode;         /* File mode. */
     /* FH_REF_FILE only. */
     char *file_name;           /* File name as provided by user. */
     enum fh_mode mode;         /* File mode. */
+    const char *encoding;       /* File encoding. */
 
     /* FH_REF_FILE and FH_REF_INLINE only. */
     size_t record_width;        /* Length of fixed-format records. */
 
     /* FH_REF_FILE and FH_REF_INLINE only. */
     size_t record_width;        /* Length of fixed-format records. */
@@ -58,14 +60,8 @@ struct file_handle
     struct scratch_handle *sh;  /* Scratch file data. */
   };
 
     struct scratch_handle *sh;  /* Scratch file data. */
   };
 
-static struct file_handle *
-file_handle_from_ll (struct ll *ll)
-{
-  return ll_data (ll, struct file_handle, ll);
-}
-
-/* List of all named handles. */
-static struct ll_list named_handles;
+/* All "struct file_handle"s with nonnull 'id' member. */
+static struct hmap named_handles = HMAP_INITIALIZER (named_handles);
 
 /* Default file handle for DATA LIST, REREAD, REPEATING DATA
    commands. */
 
 /* Default file handle for DATA LIST, REREAD, REPEATING DATA
    commands. */
@@ -79,11 +75,13 @@ static struct file_handle *create_handle (const char *id,
 static void free_handle (struct file_handle *);
 static void unname_handle (struct file_handle *);
 
 static void free_handle (struct file_handle *);
 static void unname_handle (struct file_handle *);
 
+/* Hash table of all active locks. */
+static struct hmap locks = HMAP_INITIALIZER (locks);
+
 /* File handle initialization routine. */
 void
 fh_init (void)
 {
 /* File handle initialization routine. */
 void
 fh_init (void)
 {
-  ll_init (&named_handles);
   inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE);
   inline_file->record_width = 80;
   inline_file->tab_width = 8;
   inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE);
   inline_file->record_width = 80;
   inline_file->tab_width = 8;
@@ -93,8 +91,11 @@ fh_init (void)
 void
 fh_done (void)
 {
 void
 fh_done (void)
 {
-  while (!ll_is_empty (&named_handles))
-    unname_handle (file_handle_from_ll (ll_head (&named_handles)));
+  struct file_handle *handle, *next;
+
+  HMAP_FOR_EACH_SAFE (handle, next,
+                      struct file_handle, name_node, &named_handles)
+    unname_handle (handle);
 }
 
 /* Free HANDLE and remove it from the global list. */
 }
 
 /* Free HANDLE and remove it from the global list. */
@@ -103,7 +104,7 @@ free_handle (struct file_handle *handle)
 {
   /* Remove handle from global list. */
   if (handle->id != NULL)
 {
   /* Remove handle from global list. */
   if (handle->id != NULL)
-    ll_remove (&handle->ll);
+    hmap_delete (&named_handles, &handle->name_node);
 
   /* Free data. */
   free (handle->id);
 
   /* Free data. */
   free (handle->id);
@@ -122,7 +123,7 @@ unname_handle (struct file_handle *handle)
   assert (handle->id != NULL);
   free (handle->id);
   handle->id = NULL;
   assert (handle->id != NULL);
   free (handle->id);
   handle->id = NULL;
-  ll_remove (&handle->ll);
+  hmap_delete (&named_handles, &handle->name_node);
 
   /* Drop the reference held by the named_handles table. */
   fh_unref (handle);
 
   /* Drop the reference held by the named_handles table. */
   fh_unref (handle);
@@ -171,7 +172,8 @@ fh_from_id (const char *id)
 {
   struct file_handle *handle;
 
 {
   struct file_handle *handle;
 
-  ll_for_each (handle, struct file_handle, ll, &named_handles)
+  HMAP_FOR_EACH_WITH_HASH (handle, struct file_handle, name_node,
+                           hash_case_string (id, 0), &named_handles)
     if (!strcasecmp (id, handle->id))
       {
         handle->ref_cnt++;
     if (!strcasecmp (id, handle->id))
       {
         handle->ref_cnt++;
@@ -200,7 +202,8 @@ create_handle (const char *id, char *handle_name, enum fh_referent referent)
   if (id != NULL)
     {
       assert (fh_from_id (id) == NULL);
   if (id != NULL)
     {
       assert (fh_from_id (id) == NULL);
-      ll_push_tail (&named_handles, &handle->ll);
+      hmap_insert (&named_handles, &handle->name_node,
+                   hash_case_string (handle->id, 0));
       handle->ref_cnt++;
     }
 
       handle->ref_cnt++;
     }
 
@@ -217,10 +220,10 @@ fh_inline_file (void)
   return inline_file;
 }
 
   return inline_file;
 }
 
-/* Creates and returns a new file handle with the given ID, which
-   may be null.  If it is non-null, it must be unique among
-   existing file identifiers.  The new handle is associated with
-   file FILE_NAME and the given PROPERTIES. */
+/* Creates and returns a new file handle with the given ID, which may be null.
+   If it is non-null, it must be a UTF-8 encoded string that is unique among
+   existing file identifiers.  The new handle is associated with file FILE_NAME
+   and the given PROPERTIES. */
 struct file_handle *
 fh_create_file (const char *id, const char *file_name,
                 const struct fh_properties *properties)
 struct file_handle *
 fh_create_file (const char *id, const char *file_name,
                 const struct fh_properties *properties)
@@ -228,12 +231,13 @@ fh_create_file (const char *id, const char *file_name,
   char *handle_name;
   struct file_handle *handle;
 
   char *handle_name;
   struct file_handle *handle;
 
-  handle_name = id != NULL ? xstrdup (id) : xasprintf ("\"%s\"", file_name);
+  handle_name = id != NULL ? xstrdup (id) : xasprintf ("`%s'", file_name);
   handle = create_handle (id, handle_name, FH_REF_FILE);
   handle->file_name = xstrdup (file_name);
   handle->mode = properties->mode;
   handle->record_width = properties->record_width;
   handle->tab_width = properties->tab_width;
   handle = create_handle (id, handle_name, FH_REF_FILE);
   handle->file_name = xstrdup (file_name);
   handle->mode = properties->mode;
   handle->record_width = properties->record_width;
   handle->tab_width = properties->tab_width;
+  handle->encoding = properties->encoding;
   return handle;
 }
 
   return handle;
 }
 
@@ -254,7 +258,7 @@ const struct fh_properties *
 fh_default_properties (void)
 {
   static const struct fh_properties default_properties
 fh_default_properties (void)
 {
   static const struct fh_properties default_properties
-    = {FH_MODE_TEXT, 1024, 4};
+    = {FH_MODE_TEXT, 1024, 4, C_ENCODING};
   return &default_properties;
 }
 
   return &default_properties;
 }
 
@@ -322,6 +326,14 @@ fh_get_tab_width (const struct file_handle *handle)
   return handle->tab_width;
 }
 
   return handle->tab_width;
 }
 
+/* Returns the encoding of characters read from HANDLE. */
+const char *
+fh_get_legacy_encoding (const struct file_handle *handle)
+{
+  assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
+  return (handle->referent == FH_REF_FILE ? handle->encoding : C_ENCODING);
+}
+
 /* Returns the scratch file handle associated with HANDLE.
    Applicable to only FH_REF_SCRATCH files. */
 struct scratch_handle *
 /* Returns the scratch file handle associated with HANDLE.
    Applicable to only FH_REF_SCRATCH files. */
 struct scratch_handle *
@@ -363,6 +375,8 @@ fh_set_default_handle (struct file_handle *new_default_handle)
 /* Information about a file handle's readers or writers. */
 struct fh_lock
   {
 /* Information about a file handle's readers or writers. */
 struct fh_lock
   {
+    struct hmap_node node;      /* hmap_node member. */
+
     /* Hash key. */
     enum fh_referent referent;  /* Type of underlying file. */
     union
     /* Hash key. */
     enum fh_referent referent;  /* Type of underlying file. */
     union
@@ -382,14 +396,12 @@ struct fh_lock
     void *aux;                  /* Owner's auxiliary data. */
   };
 
     void *aux;                  /* Owner's auxiliary data. */
   };
 
-/* Hash table of all active locks. */
-static struct hsh_table *locks;
 
 static void make_key (struct fh_lock *, const struct file_handle *,
                       enum fh_access);
 static void free_key (struct fh_lock *);
 
 static void make_key (struct fh_lock *, const struct file_handle *,
                       enum fh_access);
 static void free_key (struct fh_lock *);
-static int compare_fh_locks (const void *, const void *, const void *);
-static unsigned int hash_fh_lock (const void *, const void *);
+static int compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b);
+static unsigned int hash_fh_lock (const struct fh_lock *lock);
 
 /* Tries to lock handle H for the given kind of ACCESS and TYPE
    of file.  Returns a pointer to a struct fh_lock if successful,
 
 /* Tries to lock handle H for the given kind of ACCESS and TYPE
    of file.  Returns a pointer to a struct fh_lock if successful,
@@ -403,6 +415,10 @@ static unsigned int hash_fh_lock (const void *, const void *);
    and similarly for writing.  If successful, a reference to TYPE
    is retained, so it should probably be a string literal.
 
    and similarly for writing.  If successful, a reference to TYPE
    is retained, so it should probably be a string literal.
 
+   TYPE should be marked with N_() in the caller: that is, the
+   caller should not translate it with gettext, but fh_lock will
+   do so.
+
    ACCESS specifies whether the lock is for reading or writing.
    EXCLUSIVE is true to require exclusive access, false to allow
    sharing with other accessors.  Exclusive read access precludes
    ACCESS specifies whether the lock is for reading or writing.
    EXCLUSIVE is true to require exclusive access, false to allow
    sharing with other accessors.  Exclusive read access precludes
@@ -417,31 +433,36 @@ struct fh_lock *
 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
          const char *type, enum fh_access access, bool exclusive)
 {
 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
          const char *type, enum fh_access access, bool exclusive)
 {
-  struct fh_lock key, *lock;
-  void **lockp;
+  struct fh_lock *key = NULL;
+  size_t hash ;
+  struct fh_lock *lock = NULL;
+  bool found_lock = false;
 
   assert ((fh_get_referent (h) & mask) != 0);
   assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
 
 
   assert ((fh_get_referent (h) & mask) != 0);
   assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
 
-  if (locks == NULL)
-    locks = hsh_create (0, compare_fh_locks, hash_fh_lock, NULL, NULL);
+  key = xmalloc (sizeof *key);
+
+  make_key (key, h, access);
+
+  key->open_cnt = 1;
+  key->exclusive = exclusive;
+  key->type = type;
+  key->aux = NULL;
 
 
-  make_key (&key, h, access);
-  lockp = hsh_probe (locks, &key);
-  if (*lockp == NULL)
+  hash = hash_fh_lock (key);
+
+  HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
     {
     {
-      lock = *lockp = xmalloc (sizeof *lock);
-      *lock = key;
-      lock->open_cnt = 1;
-      lock->exclusive = exclusive;
-      lock->type = type;
-      lock->aux = NULL;
+      if ( 0 == compare_fh_locks (lock, key))
+       {
+         found_lock = true;
+         break;
+       }
     }
     }
-  else
-    {
-      free_key (&key);
 
 
-      lock = *lockp;
+  if ( found_lock )
+    {
       if (strcmp (lock->type, type))
         {
           if (access == FH_ACC_READ)
       if (strcmp (lock->type, type))
         {
           if (access == FH_ACC_READ)
@@ -461,9 +482,27 @@ fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
           return NULL;
         }
       lock->open_cnt++;
           return NULL;
         }
       lock->open_cnt++;
+      
+      free_key (key);
+      free (key);
+
+      return lock;
     }
 
     }
 
-  return lock;
+  hmap_insert (&locks, &key->node, hash);
+  found_lock = false;
+  HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
+    {
+      if ( 0 == compare_fh_locks (lock, key))
+       {
+         found_lock = true;
+         break;
+       }
+    }
+
+  assert (found_lock);
+
+  return key;
 }
 
 /* Releases LOCK that was acquired with fh_lock.
 }
 
 /* Releases LOCK that was acquired with fh_lock.
@@ -485,7 +524,7 @@ fh_unlock (struct fh_lock *lock)
       assert (lock->open_cnt > 0);
       if (--lock->open_cnt == 0)
         {
       assert (lock->open_cnt > 0);
       if (--lock->open_cnt == 0)
         {
-          hsh_delete (locks, lock);
+         hmap_delete (&locks, &lock->node);
           free_key (lock);
           free (lock);
           return false;
           free_key (lock);
           free (lock);
           return false;
@@ -518,10 +557,24 @@ bool
 fh_is_locked (const struct file_handle *handle, enum fh_access access)
 {
   struct fh_lock key;
 fh_is_locked (const struct file_handle *handle, enum fh_access access)
 {
   struct fh_lock key;
-  bool is_locked;
+  const struct fh_lock *k = NULL;
+  bool is_locked = false;
+  size_t hash ;
 
   make_key (&key, handle, access);
 
   make_key (&key, handle, access);
-  is_locked = hsh_find (locks, &key) != NULL;
+
+  hash = hash_fh_lock (&key);
+
+
+  HMAP_FOR_EACH_WITH_HASH (k, struct fh_lock, node, hash, &locks)
+    {
+      if ( 0 == compare_fh_locks (k, &key))
+       {
+         is_locked = true;
+         break;
+       }
+    }
+
   free_key (&key);
 
   return is_locked;
   free_key (&key);
 
   return is_locked;
@@ -555,11 +608,8 @@ free_key (struct fh_lock *lock)
 /* Compares the key fields in struct fh_lock objects A and B and
    returns a strcmp()-type result. */
 static int
 /* Compares the key fields in struct fh_lock objects A and B and
    returns a strcmp()-type result. */
 static int
-compare_fh_locks (const void *a_, const void *b_, const void *aux UNUSED)
+compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b)
 {
 {
-  const struct fh_lock *a = a_;
-  const struct fh_lock *b = b_;
-
   if (a->referent != b->referent)
     return a->referent < b->referent ? -1 : 1;
   else if (a->access != b->access)
   if (a->referent != b->referent)
     return a->referent < b->referent ? -1 : 1;
   else if (a->access != b->access)
@@ -575,13 +625,14 @@ compare_fh_locks (const void *a_, const void *b_, const void *aux UNUSED)
 
 /* Returns a hash value for LOCK. */
 static unsigned int
 
 /* Returns a hash value for LOCK. */
 static unsigned int
-hash_fh_lock (const void *lock_, const void *aux UNUSED)
+hash_fh_lock (const struct fh_lock *lock)
 {
 {
-  const struct fh_lock *lock = lock_;
-  unsigned int hash = hsh_hash_int ((lock->referent << 3) | lock->access);
+  unsigned int basis;
   if (lock->referent == FH_REF_FILE)
   if (lock->referent == FH_REF_FILE)
-    hash ^= fn_hash_identity (lock->u.file);
+    basis = fn_hash_identity (lock->u.file);
   else if (lock->referent == FH_REF_SCRATCH)
   else if (lock->referent == FH_REF_SCRATCH)
-    hash ^= hsh_hash_int (lock->u.unique_id);
-  return hash;
+    basis = lock->u.unique_id;
+  else
+    basis = 0;
+  return hash_int ((lock->referent << 3) | lock->access, basis);
 }
 }