1 /* gtmisc.c: Miscellaneous functions
2 for GlkTerm, curses.h implementation of the Glk API.
3 Designed by Andrew Plotkin <erkyrath@eblong.com>
4 http://www.eblong.com/zarf/glk/index.html
5 */
6
7 #include "gtoption.h"
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <curses.h>
12 #include "glk.h"
13 #include "glkterm.h"
14
15 static unsigned char char_tolower_table[256];
16 static unsigned char char_toupper_table[256];
17 unsigned char char_printable_table[256];
18 unsigned char char_typable_table[256];
19 static unsigned char char_A0_FF_typable[6*16] = OPT_AO_FF_TYPABLE;
20 #ifndef OPT_NATIVE_LATIN_1
21 static unsigned char char_A0_FF_output[6*16] = OPT_AO_FF_OUTPUT;
22 unsigned char char_from_native_table[256];
23 unsigned char char_to_native_table[256];
24 #endif /* OPT_NATIVE_LATIN_1 */
25
26 gidispatch_rock_t (*gli_register_obj)(void *obj, glui32 objclass) = NULL;
27 void (*gli_unregister_obj)(void *obj, glui32 objclass, gidispatch_rock_t objrock) = NULL;
28 gidispatch_rock_t (*gli_register_arr)(void *array, glui32 len, char *typecode) = NULL;
29 void (*gli_unregister_arr)(void *array, glui32 len, char *typecode,
30 gidispatch_rock_t objrock) = NULL;
31
32 static char *char_A0_FF_to_ascii[6*16] = {
33 " ", "!", "c", "Lb", NULL, "Y", "|", NULL,
34 NULL, "(C)", NULL, "<<", NULL, "-", "(R)", NULL,
35 NULL, NULL, NULL, NULL, NULL, NULL, NULL, "*",
36 NULL, NULL, NULL, ">>", "1/4", "1/2", "3/4", "?",
37 "A", "A", "A", "A", "A", "A", "AE", "C",
38 "E", "E", "E", "E", "I", "I", "I", "I",
39 NULL, "N", "O", "O", "O", "O", "O", "x",
40 "O", "U", "U", "U", "U", "Y", NULL, "ss",
41 "a", "a", "a", "a", "a", "a", "ae", "c",
42 "e", "e", "e", "e", "i", "i", "i", "i",
43 NULL, "n", "o", "o", "o", "o", "o", "/",
44 "o", "u", "u", "u", "u", "y", NULL, "y",
45 };
46
47 /* Set up things. This is called from main(). */
gli_initialize_misc()48 void gli_initialize_misc()
49 {
50 int ix;
51
52 /* Initialize the to-uppercase and to-lowercase tables. These should
53 *not* be localized to a platform-native character set! They are
54 intended to work on Latin-1 data, and the code below correctly
55 sets up the tables for that character set. */
56
57 for (ix=0; ix<256; ix++) {
58 char_toupper_table[ix] = ix;
59 char_tolower_table[ix] = ix;
60 }
61 for (ix=0; ix<256; ix++) {
62 int lower_equiv;
63 if (ix >= 'A' && ix <= 'Z') {
64 lower_equiv = ix + ('a' - 'A');
65 }
66 else if (ix >= 0xC0 && ix <= 0xDE && ix != 0xD7) {
67 lower_equiv = ix + 0x20;
68 }
69 else {
70 lower_equiv = 0;
71 }
72 if (lower_equiv) {
73 char_tolower_table[ix] = lower_equiv;
74 char_toupper_table[lower_equiv] = ix;
75 }
76 }
77
78 #ifndef OPT_NATIVE_LATIN_1
79 for (ix=0; ix<256; ix++) {
80 if (ix <= 0x7E)
81 char_from_native_table[ix] = ix;
82 else
83 char_from_native_table[ix] = 0;
84 }
85 #endif /* OPT_NATIVE_LATIN_1 */
86
87 for (ix=0; ix<256; ix++) {
88 unsigned char native_equiv;
89 int cantype, canprint;
90 native_equiv = ix;
91 if (ix < 0x20) {
92 /* Many control characters are untypable, for many reasons. */
93 if (ix == '\t' || ix == '\014' /* reserved by the input system */
94 #ifdef OPT_USE_SIGNALS
95 || ix == '\003' || ix == '\032' /* interrupt/suspend signals */
96 #endif
97 || ix == '\010' /* parsed as keycode_Delete */
98 || ix == '\012' || ix == '\015' /* parsed as keycode_Return */
99 || ix == '\033') /* parsed as keycode_Escape */
100 cantype = FALSE;
101 else
102 cantype = TRUE;
103 /* The newline is printable, but no other control characters. */
104 if (ix == '\012')
105 canprint = TRUE;
106 else
107 canprint = FALSE;
108 }
109 else if (ix <= 0x7E) {
110 cantype = TRUE;
111 canprint = TRUE;
112 }
113 else if (ix < 0xA0) {
114 cantype = FALSE;
115 canprint = FALSE;
116 }
117 else {
118 cantype = char_A0_FF_typable[ix - 0xA0];
119 #ifdef OPT_NATIVE_LATIN_1
120 canprint = TRUE;
121 #else /* OPT_NATIVE_LATIN_1 */
122 native_equiv = char_A0_FF_output[ix - 0xA0];
123 cantype = cantype && native_equiv; /* If it can't be printed exactly, it certainly
124 can't be typed. */
125 canprint = (native_equiv != 0);
126 #endif /* OPT_NATIVE_LATIN_1 */
127 }
128 char_typable_table[ix] = cantype;
129 char_printable_table[ix] = canprint;
130 #ifndef OPT_NATIVE_LATIN_1
131 char_to_native_table[ix] = native_equiv;
132 if (native_equiv)
133 char_from_native_table[native_equiv] = ix;
134 #endif /* OPT_NATIVE_LATIN_1 */
135 }
136
137 #ifndef OPT_NATIVE_LATIN_1
138 char_from_native_table[0] = '\0'; /* The little dance above misses this
139 entry, for dull reasons. */
140 #endif /* OPT_NATIVE_LATIN_1 */
141 }
142
glk_exit()143 void glk_exit()
144 {
145 gli_msgin_getchar("Hit any key to exit.", TRUE);
146
147 gli_streams_close_all();
148
149 endwin();
150 putchar('\n');
151 exit(0);
152 }
153
glk_set_interrupt_handler(void (* func)(void))154 void glk_set_interrupt_handler(void (*func)(void))
155 {
156 gli_interrupt_handler = func;
157 }
158
glk_tick()159 void glk_tick()
160 {
161 /* Nothing to do here. */
162 }
163
gidispatch_set_object_registry(gidispatch_rock_t (* regi)(void * obj,glui32 objclass),void (* unregi)(void * obj,glui32 objclass,gidispatch_rock_t objrock))164 void gidispatch_set_object_registry(
165 gidispatch_rock_t (*regi)(void *obj, glui32 objclass),
166 void (*unregi)(void *obj, glui32 objclass, gidispatch_rock_t objrock))
167 {
168 window_t *win;
169 stream_t *str;
170 fileref_t *fref;
171
172 gli_register_obj = regi;
173 gli_unregister_obj = unregi;
174
175 if (gli_register_obj) {
176 /* It's now necessary to go through all existing objects, and register
177 them. */
178 for (win = glk_window_iterate(NULL, NULL);
179 win;
180 win = glk_window_iterate(win, NULL)) {
181 win->disprock = (*gli_register_obj)(win, gidisp_Class_Window);
182 }
183 for (str = glk_stream_iterate(NULL, NULL);
184 str;
185 str = glk_stream_iterate(str, NULL)) {
186 str->disprock = (*gli_register_obj)(str, gidisp_Class_Stream);
187 }
188 for (fref = glk_fileref_iterate(NULL, NULL);
189 fref;
190 fref = glk_fileref_iterate(fref, NULL)) {
191 fref->disprock = (*gli_register_obj)(fref, gidisp_Class_Fileref);
192 }
193 }
194 }
195
gidispatch_set_retained_registry(gidispatch_rock_t (* regi)(void * array,glui32 len,char * typecode),void (* unregi)(void * array,glui32 len,char * typecode,gidispatch_rock_t objrock))196 void gidispatch_set_retained_registry(
197 gidispatch_rock_t (*regi)(void *array, glui32 len, char *typecode),
198 void (*unregi)(void *array, glui32 len, char *typecode,
199 gidispatch_rock_t objrock))
200 {
201 gli_register_arr = regi;
202 gli_unregister_arr = unregi;
203 }
204
gidispatch_get_objrock(void * obj,glui32 objclass)205 gidispatch_rock_t gidispatch_get_objrock(void *obj, glui32 objclass)
206 {
207 switch (objclass) {
208 case gidisp_Class_Window:
209 return ((window_t *)obj)->disprock;
210 case gidisp_Class_Stream:
211 return ((stream_t *)obj)->disprock;
212 case gidisp_Class_Fileref:
213 return ((fileref_t *)obj)->disprock;
214 default: {
215 gidispatch_rock_t dummy;
216 dummy.num = 0;
217 return dummy;
218 }
219 }
220 }
221
gidispatch_set_autorestore_registry(long (* locatearr)(void * array,glui32 len,char * typecode,gidispatch_rock_t objrock,int * elemsizeref),gidispatch_rock_t (* restorearr)(long bufkey,glui32 len,char * typecode,void ** arrayref))222 void gidispatch_set_autorestore_registry(
223 long (*locatearr)(void *array, glui32 len, char *typecode,
224 gidispatch_rock_t objrock, int *elemsizeref),
225 gidispatch_rock_t (*restorearr)(long bufkey, glui32 len,
226 char *typecode, void **arrayref))
227 {
228 /* GlkTerm is not able to serialize its UI state. Therefore, it
229 does not have the capability of autosaving and autorestoring.
230 Therefore, it will never call these hooks. Therefore, we ignore
231 them and do nothing here. */
232 }
233
glk_char_to_lower(unsigned char ch)234 unsigned char glk_char_to_lower(unsigned char ch)
235 {
236 return char_tolower_table[ch];
237 }
238
glk_char_to_upper(unsigned char ch)239 unsigned char glk_char_to_upper(unsigned char ch)
240 {
241 return char_toupper_table[ch];
242 }
243
gli_ascii_equivalent(unsigned char ch)244 char *gli_ascii_equivalent(unsigned char ch)
245 {
246 static char buf[5];
247
248 if (ch >= 0xA0 && char_A0_FF_to_ascii[ch - 0xA0]) {
249 return char_A0_FF_to_ascii[ch - 0xA0];
250 }
251
252 buf[0] = '\\';
253 buf[1] = '0' + ((ch >> 6) & 7);
254 buf[2] = '0' + ((ch >> 3) & 7);
255 buf[3] = '0' + ((ch) & 7);
256 buf[4] = '\0';
257
258 return buf;
259 }
260
261 #ifdef NO_MEMMOVE
262
memmove(void * destp,void * srcp,int n)263 void *memmove(void *destp, void *srcp, int n)
264 {
265 char *dest = (char *)destp;
266 char *src = (char *)srcp;
267
268 if (dest < src) {
269 for (; n > 0; n--) {
270 *dest = *src;
271 dest++;
272 src++;
273 }
274 }
275 else if (dest > src) {
276 src += n;
277 dest += n;
278 for (; n > 0; n--) {
279 dest--;
280 src--;
281 *dest = *src;
282 }
283 }
284
285 return destp;
286 }
287
288 #endif /* NO_MEMMOVE */
289