Toevoegen van de sources die ik vorige keer vergeten was, en nog wat
[projects/chimara/chimara.git] / src / stream.c
1 #include "stream.h"
2 #include <string.h>
3
4 /* Global current stream */
5 static strid_t current_stream = NULL;
6 /* List of streams currently in existence */
7 static GList *stream_list = NULL;
8
9 /* Internal function: create a window stream to go with window. */
10 strid_t
11 window_stream_new(winid_t window)
12 {
13         /* Create stream and connect it to window */
14         strid_t s = g_new0(struct glk_stream_struct, 1);
15         s->file_mode = filemode_Write;
16         s->stream_type = STREAM_TYPE_WINDOW;
17         s->window = window;
18         /* Add it to the global stream list */
19         stream_list = g_list_prepend(stream_list, s);
20         s->stream_list = stream_list;
21
22         return s;
23 }
24
25 /**
26  * glk_stream_iterate:
27  * @str: A stream, or #NULL.
28  * @rockptr: Return location for the next window's rock, or #NULL.
29  *
30  * Iterates over the list of streams; if @str is #NULL, it returns the first
31  * stream, otherwise the next stream after @str. If there are no more, it
32  * returns #NULL. The stream's rock is stored in @rockptr. If you don't want
33  * the rocks to be returned, you may set @rockptr to #NULL.
34  *
35  * The order in which streams are returned is arbitrary. The order may change
36  * every time you create or destroy a stream, invalidating the iteration.
37  *
38  * Returns: the next stream, or #NULL if there are no more.
39  */
40 strid_t
41 glk_stream_iterate(strid_t str, glui32 *rockptr)
42 {
43         GList *retnode;
44         
45         if(str == NULL)
46                 retnode = stream_list;
47         else
48                 retnode = str->stream_list->next;
49         strid_t retval = retnode? (strid_t)retnode->data : NULL;
50                 
51         /* Store the stream's rock in rockptr */
52         if(retval && rockptr)
53                 *rockptr = glk_stream_get_rock(retval);
54                 
55         return retval;
56 }
57
58 /**
59  * glk_stream_get_rock:
60  * @str: A stream.
61  * 
62  * Returns the stream @str's rock value.
63  *
64  * Returns: A rock value.
65  */
66 glui32
67 glk_stream_get_rock(strid_t str)
68 {
69         g_return_val_if_fail(str != NULL, 0);
70         return str->rock;
71 }
72
73 /**
74  * glk_stream_set_current:
75  * @str: An output stream, or NULL.
76  *
77  * Sets the current stream to @str, or to nothing if @str is #NULL.
78  */
79 void
80 glk_stream_set_current(strid_t str)
81 {
82         if(str != NULL && str->file_mode != filemode_Write)
83         {
84                 g_warning("glk_stream_set_current: "
85                         "Cannot set current stream to non output stream");
86                 return;
87         }
88
89         current_stream = str;
90 }
91
92 /**
93  * glk_stream_get_current:
94  * 
95  * Returns the current stream, or #NULL if there is none.
96  *
97  * Returns: A stream.
98  */
99 strid_t
100 glk_stream_get_current()
101 {
102         return current_stream;
103 }
104
105 /**
106  * glk_put_char:
107  * @ch: A character in Latin-1 encoding.
108  *
109  * Prints one character @ch to the current stream.
110  */
111 void
112 glk_put_char(unsigned char ch)
113 {
114         /* Illegal to print to the current stream if it is NULL */
115         g_return_if_fail(current_stream != NULL);
116         glk_put_char_stream(current_stream, ch);
117 }
118
119 /**
120  * glk_put_string:
121  * @s: A null-terminated string in Latin-1 encoding.
122  *
123  * Prints @s to the current stream.
124  */
125 void
126 glk_put_string(char *s)
127 {
128         /* Illegal to print to the current stream if it is NULL */
129         g_return_if_fail(current_stream != NULL);
130         glk_put_string_stream(current_stream, s);
131 }
132
133 /**
134  * glk_put_buffer:
135  * @buf: An array of characters in Latin-1 encoding.
136  * @len: Length of @buf.
137  *
138  * Prints @buf to the current stream.
139  */
140 void
141 glk_put_buffer(char *buf, glui32 len)
142 {
143         /* Illegal to print to the current stream if it is NULL */
144         g_return_if_fail(current_stream != NULL);
145         glk_put_buffer_stream(current_stream, buf, len);
146 }
147
148 /**
149  * glk_put_char_stream:
150  * @str: An output stream.
151  * @ch: A character in Latin-1 encoding.
152  *
153  * Prints one character @ch to the stream @str. It is illegal for @str to be
154  * #NULL, or an input-only stream.
155  */
156 void
157 glk_put_char_stream(strid_t str, unsigned char ch)
158 {
159         g_return_if_fail(str != NULL);
160         g_return_if_fail(str->file_mode != filemode_Read);
161         
162         /* Convert ch to a null-terminated string, call glk_put_string_stream() */
163         gchar *s = g_strndup(&ch, 1);
164         glk_put_string_stream(str, s);
165         g_free(s);
166 }
167
168 /* Internal function: change illegal (control) characters in a string to a
169 placeholder character. Must free returned string afterwards. */
170 static gchar *
171 remove_latin1_control_characters(gchar *s)
172 {
173         gchar *retval = g_strdup(s);
174         unsigned char *ptr;
175         for(ptr = (unsigned char *)retval; *ptr != '\0'; ptr++)
176                 if( (*ptr < 32 && *ptr != 10) || (*ptr >= 127 && *ptr <= 159) )
177                         *ptr = '?';
178                         /* Our placeholder character is '?'; other options are possible,
179                         like printing "0x7F" or something */
180         return retval;
181 }
182
183 /* Internal function: convert a Latin-1 string to a UTF-8 string, replacing
184 Latin-1 control characters by a placeholder first. The UTF-8 string must be
185 freed afterwards. Returns NULL on error. */
186 static gchar *
187 convert_latin1_to_utf8(gchar *s)
188 {
189         GError *error = NULL;
190         gchar *canonical = remove_latin1_control_characters(s);
191         utf8 = g_convert(canonical, -1, "UTF-8", "ISO-8859-1", NULL, NULL, &error);
192         g_free(canonical);
193         
194         if(utf8 == NULL)
195         {
196                 error_dialog(NULL, error, "Error during latin1->utf8 conversion: ");
197                 return NULL;
198         }
199         
200         return utf8;
201 }
202
203 /* Internal function: write a UTF-8 string to a window's text buffer. */
204 static void
205 write_utf8_to_window(winid_t win, gchar *s)
206 {
207         GtkTextBuffer *buffer = 
208                 gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
209
210         GtkTextIter iter;
211         gtk_text_buffer_get_end_iter(buffer, &iter);
212         gtk_text_buffer_insert(buffer, &iter, utf8, -1);
213 }
214
215 /**
216  * glk_put_string_stream:
217  * @str: An output stream.
218  * @s: A null-terminated string in Latin-1 encoding.
219  *
220  * Prints @s to the stream @str. It is illegal for @str to be #NULL, or an
221  * input-only stream.
222  */
223 void
224 glk_put_string_stream(strid_t str, char *s)
225 {
226         g_return_if_fail(str != NULL);
227         g_return_if_fail(str->file_mode != filemode_Read);
228
229         switch(str->stream_type)
230         {
231                 case STREAM_TYPE_WINDOW:
232                         /* Each window type has a different way of printing to it */
233                         switch(str->window->window_type)
234                         {
235                                 /* Printing to a these windows' streams does nothing */
236                                 case wintype_Blank:
237                                 case wintype_Pair:
238                                 case wintype_Graphics:
239                                         current_stream->write_count += strlen(s);
240                                         break;
241                                 /* Text buffer window */        
242                                 case wintype_TextBuffer:
243                                 {
244                                         gchar *utf8 = convert_latin1_to_utf8(s);
245                                         write_utf8_to_window(str->window, utf8);
246                                         g_free(utf8);
247                                 }       
248                                         str->write_count += strlen(s);
249                                         break;
250                                 default:
251                                         g_warning("glk_put_string: "
252                                                 "Writing to this kind of window unsupported.");
253                         }
254                         
255                         /* Now write the same buffer to the window's echo stream */
256                         if(str->window->echo_stream != NULL)
257                                 glk_put_string_stream(str->window->echo_stream, s);
258                         
259                         break;
260                 default:
261                         g_warning("glk_put_string: "
262                                 "Writing to this kind of stream unsupported."); 
263         }
264 }
265
266 /**
267  * glk_put_buffer_stream:
268  * @str: An output stream.
269  * @buf: An array of characters in Latin-1 encoding.
270  * @len: Length of @buf.
271  *
272  * Prints @buf to the stream @str. It is illegal for @str to be #NULL, or an
273  * input-only stream.
274  */
275 void
276 glk_put_buffer_stream(strid_t str, char *buf, glui32 len)
277 {
278         g_return_if_fail(str != NULL);
279         g_return_if_fail(str->file_mode != filemode_Read);
280         
281         /* Convert buf to a null-terminated string, call glk_put_string_stream() */
282         gchar *s = g_strndup(buf, len);
283         glk_put_string_stream(str, s);
284         g_free(s);
285 }
286