Implemented Unix startup code, argument parsing, and platform-dependent functions
[rodin/chimara.git] / libchimara / glkunix.c
diff --git a/libchimara/glkunix.c b/libchimara/glkunix.c
new file mode 100644 (file)
index 0000000..ee11943
--- /dev/null
@@ -0,0 +1,156 @@
+#include <string.h>
+#include <stdlib.h>
+#include <libchimara/glk.h>
+#include <libchimara/glkstart.h>
+#include "chimara-glk-private.h"
+#include "magic.h"
+#include "fileref.h"
+#include "stream.h"
+
+extern ChimaraGlkPrivate *glk_data;
+
+/**
+ * glkunix_stream_open_pathname:
+ * @pathname: A path to a file, in the system filename encoding. 
+ * @usage: Bitfield with one or more of the <code>fileusage_</code> constants.
+ * @rock: The new stream's rock value.
+ *
+ * Opens an arbitrary file, in read-only mode. Note that this function is
+ * <emphasis>only</emphasis> available during glkunix_startup_code(). It is 
+ * inherently non-portable; it should not and cannot be called from inside 
+ * glk_main().
+ * 
+ * Returns: A new stream, or %NULL if the file operation failed.
+ */
+strid_t
+glkunix_stream_open_pathname(char *pathname, glui32 usage, glui32 rock)
+{
+       if(!glk_data->in_startup)
+               ILLEGAL("glkunix_stream_open_pathname() may only be called from "
+                               "glkunix_startup_code().");
+       
+       g_return_val_if_fail(pathname, NULL);
+       g_return_val_if_fail(strlen(pathname) > 0, NULL);
+       
+       frefid_t fileref = fileref_new(pathname, rock, usage, filemode_Read);
+       return file_stream_new(fileref, filemode_Read, rock, FALSE);
+}
+
+/**
+ * glkunix_set_base_file:
+ * @filename: A path to a file, in the system filename encoding.
+ *
+ * Sets the library's idea of the <quote>current directory</quote> for the 
+ * executing program. The argument should be the name of a file (not a 
+ * directory). When this is set, glk_fileref_create_by_name() will create files 
+ * in the same directory as that file, and glk_fileref_create_by_prompt() will 
+ * base default filenames off of the file. If this is not called, the library 
+ * works in the Unix current working directory, and picks reasonable default 
+ * defaults.
+ */
+void 
+glkunix_set_base_file(char *filename)
+{
+       g_return_if_fail(filename);
+       g_return_if_fail(strlen(filename) > 0);
+       
+       gchar *dirname = g_path_get_dirname(filename);
+       if(!g_file_test(dirname, G_FILE_TEST_IS_DIR))
+       {
+               WARNING_S("Not a directory", dirname);
+               g_free(dirname);
+               return;
+       }
+       
+       glk_data->current_dir = dirname;
+}
+
+/* Internal function: parse the command line, getting only the arguments
+ requested by the plugin in its glkunix_arguments structure. Algorithm copied
+ from CheapGlk by Andrew Plotkin. */
+gboolean
+parse_command_line(glkunix_argumentlist_t glkunix_arguments[], int argc, char *argv[], glkunix_startup_t *data)
+{
+       GSList *arglist = NULL, *iter;
+       int arg;
+       
+       /* Now some argument-parsing. This is probably going to hurt. */
+    for(arg = 1; arg < argc; arg++) 
+       {
+        glkunix_argumentlist_t *argform;
+        char *numptr;
+        
+        for(argform = glkunix_arguments; argform->argtype != glkunix_arg_End; argform++) 
+               {
+            if(argform->name[0] == '\0') 
+                       {
+                if(((argform->argtype == glkunix_arg_ValueFollows ||
+                                   argform->argtype == glkunix_arg_ValueCanFollow) && 
+                                   argv[arg][0] != '-') ||
+                                   (argform->argtype == glkunix_arg_NumberValue &&
+                                       (atoi(argv[arg]) != 0 || argv[arg][0] == '0')))
+                               {
+                    arglist = g_slist_prepend(arglist, argv[arg]);
+                               }
+                               else
+                                       continue;
+            }
+                       
+            else if((argform->argtype == glkunix_arg_NumberValue)
+                && !strncmp(argv[arg], argform->name, strlen(argform->name))
+                && (numptr = argv[arg] + strlen(argform->name))
+                && (atoi(numptr) != 0 || numptr[0] == '0')) 
+                       {
+                arglist = g_slist_prepend(arglist, argv[arg]);
+                       }
+                       
+            else if(strcmp(argv[arg], argform->name) == 0) 
+                       {
+                if(argform->argtype == glkunix_arg_ValueFollows) 
+                               {
+                    if(arg + 1 >= argc) {
+                                               g_slist_free(arglist);
+                                               return FALSE; /* No more arguments, and this one is invalid */
+                                       }
+                    arglist = g_slist_prepend(arglist, argv[arg++]);
+                                       arglist = g_slist_prepend(arglist, argv[arg]);
+                }
+
+                               else if(argform->argtype == glkunix_arg_NoValue) 
+                    arglist = g_slist_prepend(arglist, argv[arg]);
+
+                else if(argform->argtype == glkunix_arg_ValueCanFollow) 
+                               {
+                                       arglist = g_slist_prepend(arglist, argv[arg]);
+                    if(arg + 1 < argc && argv[arg + 1][0] != '-') 
+                        arglist = g_slist_prepend(arglist, argv[++arg]);
+                }
+                
+                               else if(argform->argtype == glkunix_arg_NumberValue) 
+                               {
+                    if(arg + 1 >= argc || (atoi(argv[arg + 1]) == 0 && argv[arg + 1][0] != '0')) 
+                                       {
+                                               g_slist_free(arglist);
+                                               return FALSE;
+                                       }
+                    arglist = g_slist_prepend(arglist, argv[arg++]);
+                                       arglist = g_slist_prepend(arglist, argv[arg]);
+                }
+                else 
+                               {
+                                       g_slist_free(arglist);
+                                       return FALSE;
+                               }
+            }
+               }
+       }
+       
+       data->argc = g_slist_length(arglist) + 1;
+       data->argv = g_new0(char *, data->argc);
+       arglist = g_slist_reverse(arglist);
+       for(iter = arglist, arg = 1; iter; iter = g_slist_next(iter), arg++)
+               data->argv[arg] = g_strdup(iter->data);
+       g_slist_free(arglist);
+       
+       return TRUE;
+}
\ No newline at end of file