Implemented Unix startup code, argument parsing, and platform-dependent functions
[projects/chimara/chimara.git] / libchimara / glkunix.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <libchimara/glk.h>
4 #include <libchimara/glkstart.h>
5 #include "chimara-glk-private.h"
6 #include "magic.h"
7 #include "fileref.h"
8 #include "stream.h"
9
10 extern ChimaraGlkPrivate *glk_data;
11
12 /**
13  * glkunix_stream_open_pathname:
14  * @pathname: A path to a file, in the system filename encoding. 
15  * @usage: Bitfield with one or more of the <code>fileusage_</code> constants.
16  * @rock: The new stream's rock value.
17  *
18  * Opens an arbitrary file, in read-only mode. Note that this function is
19  * <emphasis>only</emphasis> available during glkunix_startup_code(). It is 
20  * inherently non-portable; it should not and cannot be called from inside 
21  * glk_main().
22  * 
23  * Returns: A new stream, or %NULL if the file operation failed.
24  */
25 strid_t
26 glkunix_stream_open_pathname(char *pathname, glui32 usage, glui32 rock)
27 {
28         if(!glk_data->in_startup)
29                 ILLEGAL("glkunix_stream_open_pathname() may only be called from "
30                                 "glkunix_startup_code().");
31         
32         g_return_val_if_fail(pathname, NULL);
33         g_return_val_if_fail(strlen(pathname) > 0, NULL);
34         
35         frefid_t fileref = fileref_new(pathname, rock, usage, filemode_Read);
36         return file_stream_new(fileref, filemode_Read, rock, FALSE);
37 }
38
39 /**
40  * glkunix_set_base_file:
41  * @filename: A path to a file, in the system filename encoding.
42  *
43  * Sets the library's idea of the <quote>current directory</quote> for the 
44  * executing program. The argument should be the name of a file (not a 
45  * directory). When this is set, glk_fileref_create_by_name() will create files 
46  * in the same directory as that file, and glk_fileref_create_by_prompt() will 
47  * base default filenames off of the file. If this is not called, the library 
48  * works in the Unix current working directory, and picks reasonable default 
49  * defaults.
50  */
51 void 
52 glkunix_set_base_file(char *filename)
53 {
54         g_return_if_fail(filename);
55         g_return_if_fail(strlen(filename) > 0);
56         
57         gchar *dirname = g_path_get_dirname(filename);
58         if(!g_file_test(dirname, G_FILE_TEST_IS_DIR))
59         {
60                 WARNING_S("Not a directory", dirname);
61                 g_free(dirname);
62                 return;
63         }
64         
65         glk_data->current_dir = dirname;
66 }
67
68 /* Internal function: parse the command line, getting only the arguments
69  requested by the plugin in its glkunix_arguments structure. Algorithm copied
70  from CheapGlk by Andrew Plotkin. */
71 gboolean
72 parse_command_line(glkunix_argumentlist_t glkunix_arguments[], int argc, char *argv[], glkunix_startup_t *data)
73 {
74         GSList *arglist = NULL, *iter;
75         int arg;
76         
77         /* Now some argument-parsing. This is probably going to hurt. */
78     for(arg = 1; arg < argc; arg++) 
79         {
80         glkunix_argumentlist_t *argform;
81         char *numptr;
82         
83         for(argform = glkunix_arguments; argform->argtype != glkunix_arg_End; argform++) 
84                 {
85             if(argform->name[0] == '\0') 
86                         {
87                 if(((argform->argtype == glkunix_arg_ValueFollows ||
88                                     argform->argtype == glkunix_arg_ValueCanFollow) && 
89                                     argv[arg][0] != '-') ||
90                                     (argform->argtype == glkunix_arg_NumberValue &&
91                                         (atoi(argv[arg]) != 0 || argv[arg][0] == '0')))
92                                 {
93                     arglist = g_slist_prepend(arglist, argv[arg]);
94                                 }
95                                 else
96                                         continue;
97             }
98                         
99             else if((argform->argtype == glkunix_arg_NumberValue)
100                 && !strncmp(argv[arg], argform->name, strlen(argform->name))
101                 && (numptr = argv[arg] + strlen(argform->name))
102                 && (atoi(numptr) != 0 || numptr[0] == '0')) 
103                         {
104                 arglist = g_slist_prepend(arglist, argv[arg]);
105                         }
106                         
107             else if(strcmp(argv[arg], argform->name) == 0) 
108                         {
109                 if(argform->argtype == glkunix_arg_ValueFollows) 
110                                 {
111                     if(arg + 1 >= argc) {
112                                                 g_slist_free(arglist);
113                                                 return FALSE; /* No more arguments, and this one is invalid */
114                                         }
115                     arglist = g_slist_prepend(arglist, argv[arg++]);
116                                         arglist = g_slist_prepend(arglist, argv[arg]);
117                 }
118
119                                 else if(argform->argtype == glkunix_arg_NoValue) 
120                     arglist = g_slist_prepend(arglist, argv[arg]);
121
122                 else if(argform->argtype == glkunix_arg_ValueCanFollow) 
123                                 {
124                                         arglist = g_slist_prepend(arglist, argv[arg]);
125                     if(arg + 1 < argc && argv[arg + 1][0] != '-') 
126                         arglist = g_slist_prepend(arglist, argv[++arg]);
127                 }
128                 
129                                 else if(argform->argtype == glkunix_arg_NumberValue) 
130                                 {
131                     if(arg + 1 >= argc || (atoi(argv[arg + 1]) == 0 && argv[arg + 1][0] != '0')) 
132                                         {
133                                                 g_slist_free(arglist);
134                                                 return FALSE;
135                                         }
136                     arglist = g_slist_prepend(arglist, argv[arg++]);
137                                         arglist = g_slist_prepend(arglist, argv[arg]);
138                 }
139                 else 
140                                 {
141                                         g_slist_free(arglist);
142                                         return FALSE;
143                                 }
144             }
145                 }
146         }
147         
148         data->argc = g_slist_length(arglist) + 1;
149         data->argv = g_new0(char *, data->argc);
150         arglist = g_slist_reverse(arglist);
151         for(iter = arglist, arg = 1; iter; iter = g_slist_next(iter), arg++)
152                 data->argv[arg] = g_strdup(iter->data);
153         g_slist_free(arglist);
154         
155         return TRUE;
156 }