Eliminated warnings about static functions declared with G_GNUC_INTERNAL
[projects/chimara/chimara.git] / src / fileref.c
index 921997b08238da2315cc8306c5b34eab9226470f..2b5cf53efd65d9f8c04bc32732699e16c6db33a4 100644 (file)
@@ -1,37 +1,34 @@
+#include <errno.h>
 #include <unistd.h>
 #include <string.h>
 #include <gtk/gtk.h>
 #include <glib/gstdio.h>
 #include "fileref.h"
-#include "error.h"
+#include "magic.h"
+#include "chimara-glk-private.h"
 
-/* List of streams currently in existence */
-static GList *fileref_list = NULL;
+extern ChimaraGlkPrivate *glk_data;
 
 /**
  * glk_fileref_iterate:
- * @fref: A file reference, or #NULL.
- * @rockptr: Return location for the next window's rock, or #NULL.
+ * @fref: A file reference, or %NULL.
+ * @rockptr: Return location for the next fileref's rock, or %NULL.
  *
- * Iterates over the list of file references; if @fref is #NULL, it returns the
- * first file reference, otherwise the next file reference after @fref. If 
- * there are no more, it returns #NULL. The file reference's rock is stored in
- * @rockptr. If you don't want the rocks to be returned, you may set @rockptr 
- * to #NULL.
+ * Iterates through all the existing filerefs. See <link
+ * linkend="chimara-Iterating-Through-Opaque-Objects">Iterating Through Opaque
+ * Objects</link>.
  *
- * The order in which file references are returned is arbitrary. The order may
- * change every time you create or destroy a file reference, invalidating the 
- * iteration.
- *
- * Returns: the next file reference, or #NULL if there are no more.
+ * Returns: the next file reference, or %NULL if there are no more.
  */
 frefid_t
 glk_fileref_iterate(frefid_t fref, glui32 *rockptr)
 {
+       VALID_FILEREF_OR_NULL(fref, return NULL);
+       
        GList *retnode;
        
        if(fref == NULL)
-               retnode = fileref_list;
+               retnode = glk_data->fileref_list;
        else
                retnode = fref->fileref_list->next;
        frefid_t retval = retnode? (frefid_t)retnode->data : NULL;
@@ -47,14 +44,15 @@ glk_fileref_iterate(frefid_t fref, glui32 *rockptr)
  * glk_fileref_get_rock:
  * @fref: A file reference.
  * 
- * Returns the file reference @fref's rock value.
+ * Retrieves the file reference @fref's rock value. See <link 
+ * linkend="chimara-Rocks">Rocks</link>.
  *
  * Returns: A rock value.
  */
 glui32
 glk_fileref_get_rock(frefid_t fref)
 {
-       g_return_val_if_fail(fref != NULL, 0);
+       VALID_FILEREF(fref, return 0);
        return fref->rock;
 }
 
@@ -65,27 +63,32 @@ fileref_new(gchar *filename, glui32 rock, glui32 usage, glui32 orig_filemode)
        g_return_val_if_fail(filename != NULL, NULL);
 
        frefid_t f = g_new0(struct glk_fileref_struct, 1);
+       f->magic = MAGIC_FILEREF;
        f->rock = rock;
        f->filename = g_strdup(filename);
        f->usage = usage;
        f->orig_filemode = orig_filemode;
        
        /* Add it to the global fileref list */
-       fileref_list = g_list_prepend(fileref_list, f);
-       f->fileref_list = fileref_list;
+       glk_data->fileref_list = g_list_prepend(glk_data->fileref_list, f);
+       f->fileref_list = glk_data->fileref_list;
        
        return f;
 }
 
 /**
  * glk_fileref_create_temp:
- * @usage: Bitfield with one or more of the #fileusage_ constants.
+ * @usage: Bitfield with one or more of the <code>fileusage_</code> constants.
  * @rock: The new fileref's rock value.
  *
  * Creates a reference to a temporary file. It is always a new file (one which
  * does not yet exist). The file (once created) will be somewhere out of the
  * player's way.
  *
+ * <note><para>
+ *   This is why no name is specified; the player will never need to know it.
+ * </para></note>
+ *
  * A temporary file should never be used for long-term storage. It may be
  * deleted automatically when the program exits, or at some later time, say
  * when the machine is turned off or rebooted. You do not have to worry about
@@ -102,14 +105,14 @@ glk_fileref_create_temp(glui32 usage, glui32 rock)
        gint handle = g_file_open_tmp("glkXXXXXX", &filename, &error);
        if(handle == -1)
        {
-               error_dialog(NULL, error, "Error creating temporary file: ");
+               WARNING_S("Error creating temporary file", error->message);
                if(filename)
                        g_free(filename);
                return NULL;
        }
-       if(close(handle) == -1) /* There is no g_close()? */
+       if(close(handle) == -1) /* There is no g_close() */
        {
-               error_dialog(NULL, NULL, "Error closing temporary file.");
+               IO_WARNING( "Error closing temporary file", filename, g_strerror(errno) );
                if(filename)
                        g_free(filename);
                return NULL;
@@ -122,18 +125,54 @@ glk_fileref_create_temp(glui32 usage, glui32 rock)
 
 /**
  * glk_fileref_create_by_prompt:
- * @usage: Bitfield with one or more of the #fileusage_ constants.
+ * @usage: Bitfield with one or more of the <code>fileusage_</code> constants.
  * @fmode: File mode, contolling the dialog's behavior.
  * @rock: The new fileref's rock value.
  *
- * Creates a reference to a file by opening a file chooser dialog. If @fmode is
- * #filemode_Read, then the file must already exist and the user will be asked
- * to select from existing files. If @fmode is #filemode_Write, then the file
- * should not exist; if the user selects an existing file, he or she will be
- * warned that it will be replaced. If @fmode is #filemode_ReadWrite, then the
- * file may or may not exist; if it already exists, the user will be warned
- * that it will be modified. The @fmode argument should generally match the
- * @fmode which will be used to open the file.
+ * Creates a reference to a file by asking the player to locate it. The library
+ * may simply prompt the player to type a name, or may use a platform-native
+ * file navigation tool. (The prompt, if any, is inferred from the usage
+ * argument.)
+ *
+ * <note><title>Chimara</title>
+ * <para>
+ * Chimara uses a <link 
+ * linkend="gtk-GtkFileChooserDialog">GtkFileChooserDialog</link>.
+ * </para></note>
+ *
+ * @fmode must be one of these values:
+ * <variablelist>
+ * <varlistentry>
+ *   <term>#filemode_Read</term>
+ *   <listitem><para>The file must already exist; and the player will be asked
+ *   to select from existing files which match the usage.</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ *   <term>#filemode_Write</term>
+ *   <listitem><para>The file should not exist; if the player selects an
+ *   existing file, he will be warned that it will be replaced.
+ *   </para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ *   <term>#filemode_ReadWrite</term>
+ *   <listitem><para>The file may or may not exist; if it already exists, the
+ *   player will be warned that it will be modified.</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ *   <term>#filemode_WriteAppend</term>
+ *   <listitem><para>Same behavior as #filemode_ReadWrite.</para></listitem>
+ * </varlistentry>
+ * </variablelist>
+ *
+ * The @fmode argument should generally match the @fmode which will be used to
+ * open the file.
+ *
+ * <note><para>
+ *   It is possible that the prompt or file tool will have a 
+ *   <quote>cancel</quote> option. If the player chooses this,
+ *   glk_fileref_create_by_prompt() will return %NULL. This is a major reason
+ *   why you should make sure the return value is valid before you use it.
+ * </para></note>
  *
  * Returns: A new fileref, or #NULL if the fileref creation failed or the
  * dialog was canceled.
@@ -180,7 +219,7 @@ glk_fileref_create_by_prompt(glui32 usage, glui32 fmode, glui32 rock)
                                GTK_FILE_CHOOSER_ACTION_SAVE);
                        break;
                default:
-                       g_warning("glk_fileref_create_by_prompt: Unsupported mode");
+                       ILLEGAL_PARAM("Unknown file mode: %u", fmode);
                        gdk_threads_leave();
                        return NULL;
        }
@@ -203,14 +242,42 @@ glk_fileref_create_by_prompt(glui32 usage, glui32 fmode, glui32 rock)
 
 /**
  * glk_fileref_create_by_name:
- * @usage: Bitfield with one or more of the #fileusage_ constants.
+ * @usage: Bitfield with one or more of the <code>fileusage_</code> constants.
  * @name: A filename.
  * @rock: The new fileref's rock value.
  *
  * This creates a reference to a file with a specific name. The file will be
- * in the same directory as your program, and visible to the player.
+ * in a fixed location relevant to your program, and visible to the player.
+ *
+ * <note><para>
+ *   This usually means <quote>in the same directory as your program.</quote>
+ * </para></note>
+ * <note><title>Chimara</title>
+ * <para>
+ * In Chimara, the file is created in the current working directory.
+ * </para></note>
+ *
+ * Since filenames are highly platform-specific, you should use
+ * glk_fileref_create_by_name() with care. It is legal to pass any string in the
+ * name argument. However, the library may have to mangle, transform, or
+ * truncate the string to make it a legal native filename. 
  *
- * Returns: A new fileref, or #NULL if the fileref creation failed. 
+ * <note><para>
+ *   For example, if you create two filerefs with the names <quote>File</quote>
+ *   and <quote>FILE</quote>, they may wind up pointing to the same file; the
+ *   platform may not support case distinctions in file names. Another example:
+ *   on a platform where file type is specified by filename suffix, the library
+ *   will add an appropriate suffix based on the usage; any suffix in the string
+ *   will be overwritten or added to. For that matter, remember that the period
+ *   is not a legal character in Acorn filenames...
+ * </para></note>
+ *
+ * The most conservative approach is to pass a string of no more than 8
+ * characters, consisting entirely of upper-case letters and numbers, starting
+ * with a letter. You can then be reasonably sure that the resulting filename
+ * will display all the characters you specify &mdash; in some form. 
+ *
+ * Returns: A new fileref, or %NULL if the fileref creation failed. 
  */
 frefid_t
 glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock)
@@ -227,7 +294,7 @@ glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock)
                &error);
        if(osname == NULL)
        {
-               error_dialog(NULL, error, "Error during latin1->filename conversion: ");
+               WARNING_S("Error during latin1->filename conversion", error->message);
                return NULL;
        }
 
@@ -243,21 +310,39 @@ glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock)
 
 /**
  * glk_fileref_create_from_fileref:
- * @usage: Bitfield with one or more of the #fileusage_ constants.
+ * @usage: Bitfield with one or more of the <code>fileusage_</code> constants.
  * @fref: Fileref to copy.
  * @rock: The new fileref's rock value.
  *
  * This copies an existing file reference @fref, but changes the usage. (The
- * original @fref is not modified.)
+ * original fileref is not modified.)
+ *
+ * The use of this function can be tricky. If you change the type of the fileref
+ * (#fileusage_Data, #fileusage_SavedGame, etc), the new reference may or may
+ * not point to the same actual disk file.
+ *
+ * <note><para>
+ *   This generally depends on whether the platform uses suffixes to indicate
+ *   file type.
+ * </para></note>
  *
- * If you write to a file in text mode and then read from it in binary mode,
- * the results are platform-dependent.
+ * If you do this, and open both file references for writing, the results are
+ * unpredictable. It is safest to change the type of a fileref only if it refers
+ * to a nonexistent file.
  *
- * Returns: A new fileref, or #NULL if the fileref creation failed. 
+ * If you change the mode of a fileref (#fileusage_TextMode,
+ * #fileusage_BinaryMode), but leave the rest of the type unchanged, the new
+ * fileref will definitely point to the same disk file as the old one.
+ * 
+ * Obviously, if you write to a file in text mode and then read from it in
+ * binary mode, the results are platform-dependent. 
+ *
+ * Returns: A new fileref, or %NULL if the fileref creation failed. 
  */
 frefid_t
 glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock)
 {
+       VALID_FILEREF(fref, return NULL);
        return fileref_new(fref->filename, rock, usage, fref->orig_filemode);
 }
 
@@ -265,8 +350,9 @@ glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock)
  * glk_fileref_destroy:
  * @fref: Fileref to destroy.
  * 
- * Destroys a fileref which you have created. This does not affect the disk
- * file.
+ * Destroys a fileref which you have created. This does <emphasis>not</emphasis>
+ * affect the disk file; it just reclaims the resources allocated by the
+ * <code>glk_fileref_create...</code> function.
  *
  * It is legal to destroy a fileref after opening a file with it (while the
  * file is still open.) The fileref is only used for the opening operation,
@@ -275,9 +361,13 @@ glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock)
 void
 glk_fileref_destroy(frefid_t fref)
 {
-       fileref_list = g_list_delete_link(fileref_list, fref->fileref_list);
+       VALID_FILEREF(fref, return);
+       
+       glk_data->fileref_list = g_list_delete_link(glk_data->fileref_list, fref->fileref_list);
        if(fref->filename)
                g_free(fref->filename);
+       
+       fref->magic = MAGIC_FREE;
        g_free(fref);
 }
 
@@ -285,14 +375,15 @@ glk_fileref_destroy(frefid_t fref)
  * glk_fileref_delete_file:
  * @fref: A refrence to the file to delete.
  *
- * Deletes the file referred to by @fref. Does not destroy @fref itself.
+ * Deletes the file referred to by @fref. It does not destroy @fref itself.
  */
 void
 glk_fileref_delete_file(frefid_t fref)
 {
+       VALID_FILEREF(fref, return);
        if( glk_fileref_does_file_exist(fref) )
                if(g_unlink(fref->filename) == -1)
-                       error_dialog(NULL, NULL, "Error deleting file %s", fref->filename);
+                       IO_WARNING( "Error deleting file", fref->filename, g_strerror(errno) );
 }
 
 /**
@@ -301,11 +392,13 @@ glk_fileref_delete_file(frefid_t fref)
  *
  * Checks whether the file referred to by @fref exists.
  *
- * Returns: #TRUE (1) if @fref refers to an existing file, #FALSE (0) if not.
+ * Returns: %TRUE (1) if @fref refers to an existing file, and %FALSE (0) if 
+ * not.
  */
 glui32
 glk_fileref_does_file_exist(frefid_t fref)
 {
+       VALID_FILEREF(fref, return 0);
        if( g_file_test(fref->filename, G_FILE_TEST_EXISTS) )
                return 1;
        return 0;