Use statically-allocated thread private data
[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 GPrivate glk_data_key;
11
12 /**
13  * glkunix_stream_open_pathname_gen:
14  * @pathname: A path to a file, in the system filename encoding.
15  * @writemode: 0 for read-only mode, 1 for write mode.
16  * @textmode: 0 for binary mode, 1 for text mode.
17  * @rock: The new stream's rock value.
18  *
19  * Opens an arbitrary file for reading or writing. (You cannot open a file for
20  * appending using this call.) Note that this function is
21  * <emphasis>only</emphasis> available during glkunix_startup_code(). It is 
22  * inherently non-portable; it should not and cannot be called from inside 
23  * glk_main().
24  *
25  * Returns: A new stream, or %NULL if the file operation failed.
26  */
27 strid_t
28 glkunix_stream_open_pathname_gen(char *pathname, glui32 writemode, glui32 textmode, glui32 rock)
29 {
30         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
31
32         if(!glk_data->in_startup)
33                 ILLEGAL("glkunix_stream_open_pathname_gen() may only be called from "
34                                 "glkunix_startup_code().");
35
36         g_return_val_if_fail(pathname, NULL);
37         g_return_val_if_fail(strlen(pathname) > 0, NULL);
38
39         frefid_t fileref = fileref_new(pathname, NULL, rock,
40                 textmode? fileusage_TextMode : fileusage_BinaryMode,
41                 writemode? filemode_Write : filemode_Read);
42         return file_stream_new(fileref, writemode? filemode_Write : filemode_Read, rock, FALSE);
43 }
44
45 /**
46  * glkunix_stream_open_pathname:
47  * @pathname: A path to a file, in the system filename encoding. 
48  * @textmode: 0 for binary mode, 1 for text mode.
49  * @rock: The new stream's rock value.
50  *
51  * This opens a file for reading. It is a less-general form of
52  * glkunix_stream_open_pathname_gen(), preserved for backwards compatibility.
53  *
54  * This should be used only by glkunix_startup_code().
55  * 
56  * Returns: A new stream, or %NULL if the file operation failed.
57  */
58 strid_t
59 glkunix_stream_open_pathname(char *pathname, glui32 textmode, glui32 rock)
60 {
61         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
62
63         if(!glk_data->in_startup)
64                 ILLEGAL("glkunix_stream_open_pathname() may only be called from "
65                                 "glkunix_startup_code().");
66
67         g_return_val_if_fail(pathname, NULL);
68         g_return_val_if_fail(strlen(pathname) > 0, NULL);
69
70         frefid_t fileref = fileref_new(pathname, NULL, rock, textmode? fileusage_TextMode : fileusage_BinaryMode, filemode_Read);
71         return file_stream_new(fileref, filemode_Read, rock, FALSE);
72 }
73
74 /**
75  * glkunix_set_base_file:
76  * @filename: A path to a file, in the system filename encoding.
77  *
78  * Sets the library's idea of the <quote>current directory</quote> for the 
79  * executing program. The argument should be the name of a file (not a 
80  * directory). When this is set, glk_fileref_create_by_name() will create files 
81  * in the same directory as that file, and glk_fileref_create_by_prompt() will 
82  * base default filenames off of the file. If this is not called, the library 
83  * works in the Unix current working directory, and picks reasonable default 
84  * defaults.
85  */
86 void 
87 glkunix_set_base_file(char *filename)
88 {
89         g_return_if_fail(filename);
90         g_return_if_fail(strlen(filename) > 0);
91
92         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
93
94         gchar *dirname = g_path_get_dirname(filename);
95         if(!g_file_test(dirname, G_FILE_TEST_IS_DIR))
96         {
97                 WARNING_S("Not a directory", dirname);
98                 g_free(dirname);
99                 return;
100         }
101         
102         glk_data->current_dir = dirname;
103 }
104
105 /* Internal function: parse the command line, getting only the arguments
106  requested by the plugin in its glkunix_arguments structure. Algorithm copied
107  from CheapGlk by Andrew Plotkin. */
108 gboolean
109 parse_command_line(glkunix_argumentlist_t glkunix_arguments[], int argc, char *argv[], glkunix_startup_t *data)
110 {
111         GSList *arglist = NULL, *iter;
112         int arg;
113         
114         /* Now some argument-parsing. This is probably going to hurt. */
115     for(arg = 1; arg < argc; arg++) 
116         {
117         glkunix_argumentlist_t *argform;
118         char *numptr;
119         
120         for(argform = glkunix_arguments; argform->argtype != glkunix_arg_End; argform++) 
121                 {
122             if(argform->name[0] == '\0') 
123                         {
124                 if(((argform->argtype == glkunix_arg_ValueFollows ||
125                                     argform->argtype == glkunix_arg_ValueCanFollow) && 
126                                     argv[arg][0] != '-') ||
127                                     (argform->argtype == glkunix_arg_NumberValue &&
128                                         (atoi(argv[arg]) != 0 || argv[arg][0] == '0')))
129                                 {
130                     arglist = g_slist_prepend(arglist, argv[arg]);
131                                 }
132                                 else
133                                         continue;
134             }
135                         
136             else if((argform->argtype == glkunix_arg_NumberValue)
137                 && !strncmp(argv[arg], argform->name, strlen(argform->name))
138                 && (numptr = argv[arg] + strlen(argform->name))
139                 && (atoi(numptr) != 0 || numptr[0] == '0')) 
140                         {
141                 arglist = g_slist_prepend(arglist, argv[arg]);
142                         }
143                         
144             else if(strcmp(argv[arg], argform->name) == 0) 
145                         {
146                 if(argform->argtype == glkunix_arg_ValueFollows) 
147                                 {
148                     if(arg + 1 >= argc) {
149                                                 g_slist_free(arglist);
150                                                 return FALSE; /* No more arguments, and this one is invalid */
151                                         }
152                     arglist = g_slist_prepend(arglist, argv[arg++]);
153                                         arglist = g_slist_prepend(arglist, argv[arg]);
154                 }
155
156                                 else if(argform->argtype == glkunix_arg_NoValue) 
157                     arglist = g_slist_prepend(arglist, argv[arg]);
158
159                 else if(argform->argtype == glkunix_arg_ValueCanFollow) 
160                                 {
161                                         arglist = g_slist_prepend(arglist, argv[arg]);
162                     if(arg + 1 < argc && argv[arg + 1][0] != '-') 
163                         arglist = g_slist_prepend(arglist, argv[++arg]);
164                 }
165                 
166                                 else if(argform->argtype == glkunix_arg_NumberValue) 
167                                 {
168                     if(arg + 1 >= argc || (atoi(argv[arg + 1]) == 0 && argv[arg + 1][0] != '0')) 
169                                         {
170                                                 g_slist_free(arglist);
171                                                 return FALSE;
172                                         }
173                     arglist = g_slist_prepend(arglist, argv[arg++]);
174                                         arglist = g_slist_prepend(arglist, argv[arg]);
175                 }
176                 else 
177                                 {
178                                         g_slist_free(arglist);
179                                         return FALSE;
180                                 }
181             }
182                 }
183         }
184         
185         data->argc = g_slist_length(arglist) + 1;
186         data->argv = g_new0(char *, data->argc);
187         arglist = g_slist_reverse(arglist);
188         for(iter = arglist, arg = 1; iter; iter = g_slist_next(iter), arg++)
189                 data->argv[arg] = g_strdup(iter->data);
190         g_slist_free(arglist);
191         
192         return TRUE;
193 }