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