New function, suggested by Havoc earlier this month. (g_mkstemp): Use only
authorTor Lillqvist <tml@iki.fi>
Fri, 10 Nov 2000 23:43:33 +0000 (23:43 +0000)
committerTor Lillqvist <tml@src.gnome.org>
Fri, 10 Nov 2000 23:43:33 +0000 (23:43 +0000)
2000-11-11  Tor Lillqvist  <tml@iki.fi>

* gfileutils.c (g_file_open_tmp): New function, suggested by Havoc
earlier this month.
(g_mkstemp): Use only one case for letters in temp file name, as
this will be used on systems with case-insensitive file systems.

* testglib.c (main): Test g_mkstemp() and g_file_open_tmp().

14 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gfileutils.c
glib.def
glib/gfileutils.c
glib/glib.def
testglib.c
tests/testglib.c

index 57964146d58a9956a3d19cb62e1f6d2870d73eac..acaf5bc5d7decce19c10ecddfb06732a59be858b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2000-11-11  Tor Lillqvist  <tml@iki.fi>
+
+       * gfileutils.c (g_file_open_tmp): New function, suggested by Havoc
+       earlier this month.
+       (g_mkstemp): Use only one case for letters in temp file name, as
+       this will be used on systems with case-insensitive file systems.
+
+       * testglib.c (main): Test g_mkstemp() and g_file_open_tmp().
+
 2000-11-09  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>
 
        * gthreadpool.c: Don't take other threads with other priorities
index 57964146d58a9956a3d19cb62e1f6d2870d73eac..acaf5bc5d7decce19c10ecddfb06732a59be858b 100644 (file)
@@ -1,3 +1,12 @@
+2000-11-11  Tor Lillqvist  <tml@iki.fi>
+
+       * gfileutils.c (g_file_open_tmp): New function, suggested by Havoc
+       earlier this month.
+       (g_mkstemp): Use only one case for letters in temp file name, as
+       this will be used on systems with case-insensitive file systems.
+
+       * testglib.c (main): Test g_mkstemp() and g_file_open_tmp().
+
 2000-11-09  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>
 
        * gthreadpool.c: Don't take other threads with other priorities
index 57964146d58a9956a3d19cb62e1f6d2870d73eac..acaf5bc5d7decce19c10ecddfb06732a59be858b 100644 (file)
@@ -1,3 +1,12 @@
+2000-11-11  Tor Lillqvist  <tml@iki.fi>
+
+       * gfileutils.c (g_file_open_tmp): New function, suggested by Havoc
+       earlier this month.
+       (g_mkstemp): Use only one case for letters in temp file name, as
+       this will be used on systems with case-insensitive file systems.
+
+       * testglib.c (main): Test g_mkstemp() and g_file_open_tmp().
+
 2000-11-09  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>
 
        * gthreadpool.c: Don't take other threads with other priorities
index 57964146d58a9956a3d19cb62e1f6d2870d73eac..acaf5bc5d7decce19c10ecddfb06732a59be858b 100644 (file)
@@ -1,3 +1,12 @@
+2000-11-11  Tor Lillqvist  <tml@iki.fi>
+
+       * gfileutils.c (g_file_open_tmp): New function, suggested by Havoc
+       earlier this month.
+       (g_mkstemp): Use only one case for letters in temp file name, as
+       this will be used on systems with case-insensitive file systems.
+
+       * testglib.c (main): Test g_mkstemp() and g_file_open_tmp().
+
 2000-11-09  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>
 
        * gthreadpool.c: Don't take other threads with other priorities
index 57964146d58a9956a3d19cb62e1f6d2870d73eac..acaf5bc5d7decce19c10ecddfb06732a59be858b 100644 (file)
@@ -1,3 +1,12 @@
+2000-11-11  Tor Lillqvist  <tml@iki.fi>
+
+       * gfileutils.c (g_file_open_tmp): New function, suggested by Havoc
+       earlier this month.
+       (g_mkstemp): Use only one case for letters in temp file name, as
+       this will be used on systems with case-insensitive file systems.
+
+       * testglib.c (main): Test g_mkstemp() and g_file_open_tmp().
+
 2000-11-09  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>
 
        * gthreadpool.c: Don't take other threads with other priorities
index 57964146d58a9956a3d19cb62e1f6d2870d73eac..acaf5bc5d7decce19c10ecddfb06732a59be858b 100644 (file)
@@ -1,3 +1,12 @@
+2000-11-11  Tor Lillqvist  <tml@iki.fi>
+
+       * gfileutils.c (g_file_open_tmp): New function, suggested by Havoc
+       earlier this month.
+       (g_mkstemp): Use only one case for letters in temp file name, as
+       this will be used on systems with case-insensitive file systems.
+
+       * testglib.c (main): Test g_mkstemp() and g_file_open_tmp().
+
 2000-11-09  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>
 
        * gthreadpool.c: Don't take other threads with other priorities
index 57964146d58a9956a3d19cb62e1f6d2870d73eac..acaf5bc5d7decce19c10ecddfb06732a59be858b 100644 (file)
@@ -1,3 +1,12 @@
+2000-11-11  Tor Lillqvist  <tml@iki.fi>
+
+       * gfileutils.c (g_file_open_tmp): New function, suggested by Havoc
+       earlier this month.
+       (g_mkstemp): Use only one case for letters in temp file name, as
+       this will be used on systems with case-insensitive file systems.
+
+       * testglib.c (main): Test g_mkstemp() and g_file_open_tmp().
+
 2000-11-09  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>
 
        * gthreadpool.c: Don't take other threads with other priorities
index 57964146d58a9956a3d19cb62e1f6d2870d73eac..acaf5bc5d7decce19c10ecddfb06732a59be858b 100644 (file)
@@ -1,3 +1,12 @@
+2000-11-11  Tor Lillqvist  <tml@iki.fi>
+
+       * gfileutils.c (g_file_open_tmp): New function, suggested by Havoc
+       earlier this month.
+       (g_mkstemp): Use only one case for letters in temp file name, as
+       this will be used on systems with case-insensitive file systems.
+
+       * testglib.c (main): Test g_mkstemp() and g_file_open_tmp().
+
 2000-11-09  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>
 
        * gthreadpool.c: Don't take other threads with other priorities
index c9c0d9e1992d6c3501340dc54e7ae7a01760c88d..e5cc56aed13c230a2f6cbe301777e70c7e0ce2fd 100644 (file)
@@ -518,7 +518,7 @@ g_file_get_contents (const gchar *filename,
  * end in "XXXXXX". The X string will be modified to form the name
  * of a file that didn't exist.
  *
- * Return value: A file handle (as from open()) to the file file
+ * Return value: A file handle (as from open()) to the file
  * opened for reading and writing. The file is opened in binary mode
  * on platforms where there is a difference. The file handle should be
  * closed with close(). In case of errors, -1 is returned.
@@ -534,7 +534,8 @@ g_mkstemp (char *tmpl)
   char *XXXXXX;
   int count, fd;
   static const char letters[] =
-    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+  static const int NLETTERS = sizeof (letters) - 1;
   glong value;
   GTimeVal tv;
 
@@ -554,17 +555,17 @@ g_mkstemp (char *tmpl)
       glong v = value;
 
       /* Fill in the random bits.  */
-      XXXXXX[0] = letters[v % 62];
-      v /= 62;
-      XXXXXX[1] = letters[v % 62];
-      v /= 62;
-      XXXXXX[2] = letters[v % 62];
-      v /= 62;
-      XXXXXX[3] = letters[v % 62];
-      v /= 62;
-      XXXXXX[4] = letters[v % 62];
-      v /= 62;
-      XXXXXX[5] = letters[v % 62];
+      XXXXXX[0] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[1] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[2] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[3] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[4] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[5] = letters[v % NLETTERS];
 
       fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
 
@@ -581,3 +582,94 @@ g_mkstemp (char *tmpl)
   return -1;
 #endif
 }
+
+/**
+ * g_file_open_tmp:
+ * @template: Template for file name, as in g_mkstemp, basename only
+ * @name_used: location to store actual name used
+ * @error: return location for a #GError
+ *
+ * Opens a file for writing in the preferred directory for temporary
+ * files (as returned by g_get_tmp_dir()). 
+ *
+ * @template should be a string ending with six 'X' characters, as the
+ * parameter to g_mkstemp() (or mktemp()). However, unlike these
+ * functions, the template should only be a basename, no directory
+ * components are allowed. If template is NULL, a default template is
+ * used.
+ *
+ * The actual name used is returned in @name_used if non-NULL. This
+ * string should be freed with g_free when not needed any longer.
+ *
+ * If some error occurs, @error is set, and -1 is returned. Otherwise,
+ * the file descriptor to a file opened for reading and writing with
+ * g_mkstemp() is returned.
+ **/
+int
+g_file_open_tmp (const char *template,
+                char      **name_used,
+                GError    **error)
+{
+  int retval;
+  char mytemplate[10];
+  char *tmpdir;
+  char *sep;
+  char *fulltemplate;
+
+  if (template == NULL)
+    {
+      strcpy (mytemplate, ".XXXXXX");
+      template = mytemplate;
+    }
+
+  if (strchr (template, G_DIR_SEPARATOR))
+    {
+      g_set_error (error,
+                  G_FILE_ERROR,
+                  G_FILE_ERROR_FAILED,
+                  _("Template '%s' illegal, should not contain a '%s'"),
+                  template, G_DIR_SEPARATOR_S);
+
+      return -1;
+    }
+  
+  if (strlen (template) < 6 ||
+      strcmp (template + strlen (template) - 6, "XXXXXX") != 0)
+    {
+      g_set_error (error,
+                  G_FILE_ERROR,
+                  G_FILE_ERROR_FAILED,
+                  _("Template '%s' doesn end with XXXXXX"),
+                  template);
+      return -1;
+    }
+
+  tmpdir = g_get_tmp_dir ();
+
+  if (tmpdir [strlen (tmpdir) - 1] == G_DIR_SEPARATOR)
+    sep = "";
+  else
+    sep = G_DIR_SEPARATOR_S;
+
+  fulltemplate = g_strconcat (tmpdir, sep, template, NULL);
+
+  retval = g_mkstemp (fulltemplate);
+
+  if (retval == -1)
+    {
+      g_set_error (error,
+                  G_FILE_ERROR,
+                  g_file_error_from_errno (errno),
+                  _("Failed to create file '%s': %s"),
+                  fulltemplate, strerror (errno));
+      g_free (fulltemplate);
+      return -1;
+    }
+
+  if (name_used)
+    *name_used = fulltemplate;
+  else
+    g_free (fulltemplate);
+
+  return retval;
+}
index 818485869315574be978a8338c8c46aeba15ed7d..82642e2ddb4163ac94aff0f423d4dc99c415d303 100644 (file)
--- a/glib.def
+++ b/glib.def
@@ -103,6 +103,7 @@ EXPORTS
        g_file_error_quark
        g_file_get_contents
        g_file_test
+       g_file_open_tmp
        g_filename_from_utf8
        g_filename_to_utf8
        g_free
index c9c0d9e1992d6c3501340dc54e7ae7a01760c88d..e5cc56aed13c230a2f6cbe301777e70c7e0ce2fd 100644 (file)
@@ -518,7 +518,7 @@ g_file_get_contents (const gchar *filename,
  * end in "XXXXXX". The X string will be modified to form the name
  * of a file that didn't exist.
  *
- * Return value: A file handle (as from open()) to the file file
+ * Return value: A file handle (as from open()) to the file
  * opened for reading and writing. The file is opened in binary mode
  * on platforms where there is a difference. The file handle should be
  * closed with close(). In case of errors, -1 is returned.
@@ -534,7 +534,8 @@ g_mkstemp (char *tmpl)
   char *XXXXXX;
   int count, fd;
   static const char letters[] =
-    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+  static const int NLETTERS = sizeof (letters) - 1;
   glong value;
   GTimeVal tv;
 
@@ -554,17 +555,17 @@ g_mkstemp (char *tmpl)
       glong v = value;
 
       /* Fill in the random bits.  */
-      XXXXXX[0] = letters[v % 62];
-      v /= 62;
-      XXXXXX[1] = letters[v % 62];
-      v /= 62;
-      XXXXXX[2] = letters[v % 62];
-      v /= 62;
-      XXXXXX[3] = letters[v % 62];
-      v /= 62;
-      XXXXXX[4] = letters[v % 62];
-      v /= 62;
-      XXXXXX[5] = letters[v % 62];
+      XXXXXX[0] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[1] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[2] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[3] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[4] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[5] = letters[v % NLETTERS];
 
       fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
 
@@ -581,3 +582,94 @@ g_mkstemp (char *tmpl)
   return -1;
 #endif
 }
+
+/**
+ * g_file_open_tmp:
+ * @template: Template for file name, as in g_mkstemp, basename only
+ * @name_used: location to store actual name used
+ * @error: return location for a #GError
+ *
+ * Opens a file for writing in the preferred directory for temporary
+ * files (as returned by g_get_tmp_dir()). 
+ *
+ * @template should be a string ending with six 'X' characters, as the
+ * parameter to g_mkstemp() (or mktemp()). However, unlike these
+ * functions, the template should only be a basename, no directory
+ * components are allowed. If template is NULL, a default template is
+ * used.
+ *
+ * The actual name used is returned in @name_used if non-NULL. This
+ * string should be freed with g_free when not needed any longer.
+ *
+ * If some error occurs, @error is set, and -1 is returned. Otherwise,
+ * the file descriptor to a file opened for reading and writing with
+ * g_mkstemp() is returned.
+ **/
+int
+g_file_open_tmp (const char *template,
+                char      **name_used,
+                GError    **error)
+{
+  int retval;
+  char mytemplate[10];
+  char *tmpdir;
+  char *sep;
+  char *fulltemplate;
+
+  if (template == NULL)
+    {
+      strcpy (mytemplate, ".XXXXXX");
+      template = mytemplate;
+    }
+
+  if (strchr (template, G_DIR_SEPARATOR))
+    {
+      g_set_error (error,
+                  G_FILE_ERROR,
+                  G_FILE_ERROR_FAILED,
+                  _("Template '%s' illegal, should not contain a '%s'"),
+                  template, G_DIR_SEPARATOR_S);
+
+      return -1;
+    }
+  
+  if (strlen (template) < 6 ||
+      strcmp (template + strlen (template) - 6, "XXXXXX") != 0)
+    {
+      g_set_error (error,
+                  G_FILE_ERROR,
+                  G_FILE_ERROR_FAILED,
+                  _("Template '%s' doesn end with XXXXXX"),
+                  template);
+      return -1;
+    }
+
+  tmpdir = g_get_tmp_dir ();
+
+  if (tmpdir [strlen (tmpdir) - 1] == G_DIR_SEPARATOR)
+    sep = "";
+  else
+    sep = G_DIR_SEPARATOR_S;
+
+  fulltemplate = g_strconcat (tmpdir, sep, template, NULL);
+
+  retval = g_mkstemp (fulltemplate);
+
+  if (retval == -1)
+    {
+      g_set_error (error,
+                  G_FILE_ERROR,
+                  g_file_error_from_errno (errno),
+                  _("Failed to create file '%s': %s"),
+                  fulltemplate, strerror (errno));
+      g_free (fulltemplate);
+      return -1;
+    }
+
+  if (name_used)
+    *name_used = fulltemplate;
+  else
+    g_free (fulltemplate);
+
+  return retval;
+}
index 818485869315574be978a8338c8c46aeba15ed7d..82642e2ddb4163ac94aff0f423d4dc99c415d303 100644 (file)
@@ -103,6 +103,7 @@ EXPORTS
        g_file_error_quark
        g_file_get_contents
        g_file_test
+       g_file_open_tmp
        g_filename_from_utf8
        g_filename_to_utf8
        g_free
index cb8f50f2e95d824414a0c048d8e814be9069a312..3eedc81dd5b298fa0ac4570a956d703b4a80e651 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <errno.h>
 #include "glib.h"
 
 int array[10000];
@@ -349,6 +350,12 @@ main (int   argc,
   guint64 gu64t1 = G_GINT64_CONSTANT(0x1d636b02300a7aa7U),
          gu64t2 = G_GINT64_CONSTANT(0xa77a0a30026b631dU);
 #endif
+  const char hello[] = "Hello, World";
+  const int hellolen = sizeof (hello) - 1;
+  int fd;
+  char template[10];
+  GError *error;
+  char *name_used;
 
   g_print ("TestGLib v%u.%u.%u (i:%u b:%u)\n",
           glib_major_version,
@@ -1085,6 +1092,71 @@ main (int   argc,
   g_print ("current locale: %s\n", g_win32_getlocale ());
 #endif
 
+  g_print ("checking file functions...\n");
+
+  strcpy (template, "foobar");
+  fd = g_mkstemp (template);
+  if (fd != -1)
+    g_print ("g_mkstemp works even if template doesn't end in XXXXXX\n");
+  close (fd);
+  strcpy (template, "fooXXXXXX");
+  fd = g_mkstemp (template);
+  if (fd == -1)
+    g_print ("g_mkstemp didn't work for template %s\n", template);
+  i = write (fd, hello, hellolen);
+  if (i == -1)
+    g_print ("write() failed: %s\n", g_strerror (errno));
+  else if (i != hellolen)
+    g_print ("write() should have written %d bytes, wrote %d\n", hellolen, i);
+
+  lseek (fd, 0, 0);
+  i = read (fd, chars, sizeof (chars));
+  if (i == -1)
+    g_print ("read() failed: %s\n", g_strerror (errno));
+  else if (i != hellolen)
+    g_print ("read() should have read %d bytes, got %d\n", hellolen, i);
+
+  chars[i] = 0;
+  if (strcmp (chars, hello) != 0)
+    g_print ("wrote '%s', but got '%s'\n", hello, chars);
+
+  close (fd);
+  remove (template);
+
+  strcpy (template, "zap" G_DIR_SEPARATOR_S "barXXXXXX");
+  fd = g_file_open_tmp (template, &name_used, &error);
+  if (fd != -1)
+    g_print ("g_file_open_tmp works even if template contains '%s'\n",
+            G_DIR_SEPARATOR_S);
+  else
+    g_print ("g_file_open_tmp correctly returns error: %s\n",
+            error->message);
+  close (fd);
+  g_clear_error (&error);
+
+  strcpy (template, "zapXXXXXX");
+  fd = g_file_open_tmp (template, &name_used, &error);
+  if (fd == -1)
+    g_print ("g_file_open_tmp didn't work for template '%s': %s\n",
+            template, error->message);
+  else
+    g_print ("g_file_open_tmp for template '%s' used name '%s'\n",
+            template, name_used);
+  close (fd);
+  g_clear_error (&error);
+  remove (name_used);
+
+  fd = g_file_open_tmp (NULL, &name_used, &error);
+  if (fd == -1)
+    g_print ("g_file_open_tmp didn't work for a NULL template: %s\n",
+            error->message);
+  else
+    g_print ("g_file_open_tmp for NULL template used name '%s'\n",
+            name_used);
+  close (fd);
+  g_clear_error (&error);
+  remove (name_used);
+  
   return 0;
 }
 
index cb8f50f2e95d824414a0c048d8e814be9069a312..3eedc81dd5b298fa0ac4570a956d703b4a80e651 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <errno.h>
 #include "glib.h"
 
 int array[10000];
@@ -349,6 +350,12 @@ main (int   argc,
   guint64 gu64t1 = G_GINT64_CONSTANT(0x1d636b02300a7aa7U),
          gu64t2 = G_GINT64_CONSTANT(0xa77a0a30026b631dU);
 #endif
+  const char hello[] = "Hello, World";
+  const int hellolen = sizeof (hello) - 1;
+  int fd;
+  char template[10];
+  GError *error;
+  char *name_used;
 
   g_print ("TestGLib v%u.%u.%u (i:%u b:%u)\n",
           glib_major_version,
@@ -1085,6 +1092,71 @@ main (int   argc,
   g_print ("current locale: %s\n", g_win32_getlocale ());
 #endif
 
+  g_print ("checking file functions...\n");
+
+  strcpy (template, "foobar");
+  fd = g_mkstemp (template);
+  if (fd != -1)
+    g_print ("g_mkstemp works even if template doesn't end in XXXXXX\n");
+  close (fd);
+  strcpy (template, "fooXXXXXX");
+  fd = g_mkstemp (template);
+  if (fd == -1)
+    g_print ("g_mkstemp didn't work for template %s\n", template);
+  i = write (fd, hello, hellolen);
+  if (i == -1)
+    g_print ("write() failed: %s\n", g_strerror (errno));
+  else if (i != hellolen)
+    g_print ("write() should have written %d bytes, wrote %d\n", hellolen, i);
+
+  lseek (fd, 0, 0);
+  i = read (fd, chars, sizeof (chars));
+  if (i == -1)
+    g_print ("read() failed: %s\n", g_strerror (errno));
+  else if (i != hellolen)
+    g_print ("read() should have read %d bytes, got %d\n", hellolen, i);
+
+  chars[i] = 0;
+  if (strcmp (chars, hello) != 0)
+    g_print ("wrote '%s', but got '%s'\n", hello, chars);
+
+  close (fd);
+  remove (template);
+
+  strcpy (template, "zap" G_DIR_SEPARATOR_S "barXXXXXX");
+  fd = g_file_open_tmp (template, &name_used, &error);
+  if (fd != -1)
+    g_print ("g_file_open_tmp works even if template contains '%s'\n",
+            G_DIR_SEPARATOR_S);
+  else
+    g_print ("g_file_open_tmp correctly returns error: %s\n",
+            error->message);
+  close (fd);
+  g_clear_error (&error);
+
+  strcpy (template, "zapXXXXXX");
+  fd = g_file_open_tmp (template, &name_used, &error);
+  if (fd == -1)
+    g_print ("g_file_open_tmp didn't work for template '%s': %s\n",
+            template, error->message);
+  else
+    g_print ("g_file_open_tmp for template '%s' used name '%s'\n",
+            template, name_used);
+  close (fd);
+  g_clear_error (&error);
+  remove (name_used);
+
+  fd = g_file_open_tmp (NULL, &name_used, &error);
+  if (fd == -1)
+    g_print ("g_file_open_tmp didn't work for a NULL template: %s\n",
+            error->message);
+  else
+    g_print ("g_file_open_tmp for NULL template used name '%s'\n",
+            name_used);
+  close (fd);
+  g_clear_error (&error);
+  remove (name_used);
+  
   return 0;
 }