Add Bocfel interpreter
[projects/chimara/chimara.git] / interpreters / bocfel / osdep.c
1 /*-
2  * Copyright 2010-2012 Chris Spiegel.
3  *
4  * This file is part of Bocfel.
5  *
6  * Bocfel is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version
8  * 2 or 3, as published by the Free Software Foundation.
9  *
10  * Bocfel is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Bocfel.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef ZTERP_UNIX
20 #define _XOPEN_SOURCE   600
21 #endif
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <limits.h>
26
27 #include "osdep.h"
28 #include "screen.h"
29
30 /* OS-specific functions should all be collected in this file for
31  * convenience.  A sort of poor-man’s “reverse” inheritance is used: for
32  * each function that a particular operating system provides, it should
33  * #define a macro of the same name.  At the end of the file, a generic
34  * function is provided for each function that has no associated macro
35  * definition.
36  *
37  * The functions required are as follows:
38  *
39  * long zterp_os_filesize(FILE *fp)
40  *
41  * Return the size of the file referred to by fp.  It is safe to assume
42  * that the file is opened in binary mode.  The file position indicator
43  * need not be maintained.  If the size of the file is larger than
44  * LONG_MAX, -1 should be returned.
45  *
46  * int zterp_os_have_unicode(void)
47  *
48  * The main purpose behind this function is to indicate whether
49  * transcripts will be written in UTF-8 or Latin-1.  This is, of course,
50  * not necessarily an OS matter, but I’ve run into some issues with
51  * UTF-8 and Windows (at least through Wine), so I want to be as
52  * sensible as I can with the defaults.  The user is able to override
53  * this value if he so desires.
54  * If a Glk build is not being used, this function also serves to
55  * indicate whether all I/O, not just transcripts, should be UTF-8 or
56  * not.  Glk libraries are able to be queried as to their support for
57  * Unicode so there is no need to make assumptions in that case.
58  *
59  * void zterp_os_rcfile(char *s, size_t n)
60  *
61  * Different operating systems have different ideas about where
62  * configuration data should be stored; this function will copy a
63  * suitable value for the bocfel configuration file into the buffer s
64  * which is n bytes long.
65  *
66  * void zterp_os_reopen_binary(FILE *fp)
67  *
68  * Writing UTF-8 requires that no mangling be done, such as might happen
69  * when a stream is opened in text mode.  This function should, if
70  * necessary, set the mode on the file pointer in fp to be binary.
71  *
72  * The following functions are useful for non-Glk builds only.  They
73  * provide for some handling of screen functions that is normally taken
74  * care of by Glk.
75  *
76  * void zterp_os_get_screen_size(unsigned *w, unsigned *h)
77  *
78  * The size of the terminal, if known, is written into *w (width) and *h
79  * (height).  If terminal size is unavalable, nothing should be written.
80  *
81  * void zterp_os_init_term(void)
82  *
83  * If something special needs to be done to prepare the terminal for
84  * output, it should be done here.  This function is called once at
85  * program startup.
86  *
87  * int zterp_os_have_style(int style)
88  *
89  * This should return true if the provided style (see style.h for valid
90  * STYLE_ values) is available.  It is safe to assume that styles will
91  * not be combined; e.g. this will not be called as:
92  * zterp_os_have_style(STYLE_BOLD | STYLE_ITALIC);
93  *
94  * int zterp_os_have_colors(void)
95  *
96  * Returns true if the terminal supports colors.
97  *
98  * void zterp_os_set_style(int style, int fg, int bg)
99  *
100  * Set both a style and foreground/background color.  Any previous
101  * settings should be ignored; for example, if the last call to
102  * zterp_os_set_style() turned on italics and the current call sets
103  * bold, the result should be bold, not bold italic.
104  * Unlike in zterp_os_have_style(), here styles may be combined.  See
105  * the Unix implementation for a reference.
106  * The colors are Z-machine colors (see §8.3.1), with the following
107  * note: the only color values that will ever be passed in are 1–9.
108  */
109
110 /******************
111  * Unix functions *
112  ******************/
113 #ifdef ZTERP_UNIX
114 #include <sys/stat.h>
115
116 long zterp_os_filesize(FILE *fp)
117 {
118   struct stat buf;
119   int fd = fileno(fp);
120
121   if(fd == -1 || fstat(fd, &buf) == -1 || !S_ISREG(buf.st_mode) || buf.st_size > LONG_MAX) return -1;
122
123   return buf.st_size;
124 }
125 #define zterp_os_filesize
126
127 int zterp_os_have_unicode(void)
128 {
129   return 1;
130 }
131 #define zterp_os_have_unicode
132
133 void zterp_os_rcfile(char *s, size_t n)
134 {
135   snprintf(s, n, "%s/.bocfelrc", getenv("HOME") != NULL ? getenv("HOME") : ".");
136 }
137 #define zterp_os_rcfile
138
139 #ifndef ZTERP_GLK
140 #include <unistd.h>
141 #include <sys/ioctl.h>
142 #include <curses.h>
143 #include <term.h>
144 #ifdef TIOCGWINSZ
145 void zterp_os_get_screen_size(unsigned *w, unsigned *h)
146 {
147   struct winsize winsize;
148
149   if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) == 0)
150   {
151     *w = winsize.ws_col;
152     *h = winsize.ws_row;
153   }
154 }
155 #define zterp_os_get_screen_size
156 #endif
157
158 static const char *ital = NULL, *rev = NULL, *bold = NULL, *none = NULL;
159 static char *fg_string = NULL, *bg_string = NULL;
160 static int have_colors = 0;
161 void zterp_os_init_term(void)
162 {
163   if(setupterm(NULL, STDIN_FILENO, NULL) != OK) return;
164
165   /* prefer italics over underline for emphasized text */
166   ital = tgetstr("ZH", NULL);
167   if(ital == NULL) ital = tgetstr("us", NULL);
168   rev  = tgetstr("mr", NULL);
169   bold = tgetstr("md", NULL);
170   none = tgetstr("me", NULL);
171
172   fg_string = tgetstr("AF", NULL);
173   bg_string = tgetstr("AB", NULL);
174
175   have_colors = none != NULL && fg_string != NULL && bg_string != NULL;
176 }
177 #define zterp_os_init_term
178
179 int zterp_os_have_style(int style)
180 {
181   if(none == NULL) return 0;
182
183   if     (style == STYLE_ITALIC)  return ital != NULL;
184   else if(style == STYLE_REVERSE) return rev  != NULL;
185   else if(style == STYLE_BOLD)    return bold != NULL;
186   else if(style == STYLE_NONE)    return none != NULL;
187
188   return 0;
189 }
190 #define zterp_os_have_style
191
192 int zterp_os_have_colors(void)
193 {
194   return have_colors;
195 }
196 #define zterp_os_have_colors
197
198 void zterp_os_set_style(int style, int fg, int bg)
199 {
200   /* If the terminal cannot be reset, nothing can be used. */
201   if(none == NULL) return;
202
203   putp(none);
204
205   if((style & STYLE_ITALIC)  && ital != NULL) putp(ital);
206   if((style & STYLE_REVERSE) && rev  != NULL) putp(rev);
207   if((style & STYLE_BOLD)    && bold != NULL) putp(bold);
208
209   if(have_colors)
210   {
211     if(fg > 1) putp(tparm(fg_string, fg - 2, 0, 0, 0, 0, 0, 0, 0, 0));
212     if(bg > 1) putp(tparm(bg_string, bg - 2, 0, 0, 0, 0, 0, 0, 0, 0));
213   }
214 }
215 #define zterp_os_set_style
216 #endif
217
218 /*********************
219  * Windows functions *
220  *********************/
221 #elif defined(ZTERP_WIN32)
222 void zterp_os_rcfile(char *s, size_t n)
223 {
224   char *p;
225
226   p = getenv("APPDATA");
227   if(p == NULL) p = getenv("LOCALAPPDATA");
228   if(p == NULL) p = ".";
229
230   snprintf(s, n, "%s\\bocfel.ini", p);
231 }
232 #define zterp_os_rcfile
233
234 #endif
235
236 /*********************
237  * Generic functions *
238  *********************/
239 #ifndef zterp_os_filesize
240 long zterp_os_filesize(FILE *fp)
241 {
242   /* Assume fseek() can seek to the end of binary streams. */
243   if(fseek(fp, 0, SEEK_END) == -1) return -1;
244
245   return ftell(fp);
246 }
247 #endif
248
249 #ifndef zterp_os_have_unicode
250 int zterp_os_have_unicode(void)
251 {
252   return 0;
253 }
254 #endif
255
256 #ifndef zterp_os_rcfile
257 void zterp_os_rcfile(char *s, size_t n)
258 {
259   snprintf(s, n, "bocfelrc");
260 }
261 #endif
262
263 /* When UTF-8 output is enabled, special translation of characters (e.g.
264  * newline) should not be done.  Theoretically this function exists to
265  * set stdin/stdout to binary mode, if necessary.  Unix makes no
266  * text/binary distinction, but Windows does.  I’m under the impression
267  * that there is a setmode() function that should be able to do this,
268  * but my knowledge of Windows is so small that I do not want to do much
269  * more than I have, lest I completely break Windows support—assuming it
270  * even works.
271  * freopen() should be able to do this, but with my testing under Wine,
272  * no text gets output in such a case.
273  */
274 #ifndef zterp_os_reopen_binary
275 void zterp_os_reopen_binary(FILE *fp)
276 {
277 }
278 #endif
279
280 #ifndef ZTERP_GLK
281 #ifndef zterp_os_get_screen_size
282 void zterp_os_get_screen_size(unsigned *w, unsigned *h)
283 {
284 }
285 #endif
286
287 #ifndef zterp_os_init_term
288 void zterp_os_init_term(void)
289 {
290 }
291 #endif
292
293 #ifndef zterp_os_have_style
294 int zterp_os_have_style(int style)
295 {
296   return 0;
297 }
298 #endif
299
300 #ifndef zterp_os_have_colors
301 int zterp_os_have_colors(void)
302 {
303   return 0;
304 }
305 #endif
306
307 #ifndef zterp_os_set_style
308 void zterp_os_set_style(int style, int fg, int bg)
309 {
310 }
311 #endif
312 #endif