1 /*
2 ** os_glk.c - Definitions which are specific to the Glk 'platform'
3 **
4 ** Notes:
5 **   10 Jan 99: SRG Initial creation
6 **    3 Dec 01: SRG Added os_build_full_path
7 **   20 Dec 01: SRG Added os_get_exe_filename and os_get_special_path
8 **   10 Mar 02: SRG Added os_more_prompt routine (thanks to D.J. Picton)
9 **   13 Jul 04: Nikos Added os_nonstop_mode dummy implementation
10 */
11 
12 #include "run.h"
13 #include "os_glk.h"
14 #include "oss_glk.h"
15 #include <unistd.h>
16 #include <stdlib.h>
17 
18 
19 /* ------------------------------------------------------------------------ */
20 /*
21 ** Some machines are missing memmove, so we use our own memcpy/memmove
22 ** routine instead.
23 */
our_memcpy(void * destp,const void * srcp,size_t size)24 void *our_memcpy(void *destp, const void *srcp, size_t size)
25 {
26     char *dest = (char *)destp;
27     char *src = (char *)srcp;
28     size_t n;
29 
30     n = size;
31     if (dest < src) {
32         while (n > 0) {
33             *dest = *src;
34             dest++;
35             src++;
36         n--;
37         }
38     }
39     else if (dest > src) {
40         for (src += n, dest += n; n > 0; n--) {
41             dest--;
42             src--;
43             *dest = *src;
44         }
45     }
46 
47     return destp;
48 }
49 
50 
51 /* ------------------------------------------------------------------------ */
52 /*
53 ** File handling
54 */
55 
56 /* Ask the user for a filename. Return 0 if successful, non-zero otherwise */
os_askfile(const char * prompt,char * fname_buf,int fname_buf_len,int prompt_type,int file_type)57 int os_askfile(const char *prompt, char *fname_buf, int fname_buf_len,
58                int prompt_type, int file_type)
59 {
60     frefid_t fileref;
61     glui32 glk_prompt_type, glk_file_type;
62 
63     glk_prompt_type = oss_convert_prompt_type(prompt_type);
64     glk_file_type = oss_convert_file_type(file_type) |
65     fileusage_TextMode;
66 
67     fileref = glk_fileref_create_by_prompt(glk_file_type, glk_prompt_type, 0);
68     if (fileref == NULL)
69         return OS_AFE_CANCEL;
70     return (oss_convert_fileref_to_string(fileref, fname_buf, fname_buf_len)
71             == FALSE ? OS_AFE_FAILURE : OS_AFE_SUCCESS);
72 }
73 
74 /* Create and open a temporary file. Theoretically I should use the
75  filename in swapname or generate my own and put it in buf */
os_create_tempfile(const char * swapname,char * buf)76 osfildef *os_create_tempfile(const char *swapname, char *buf)
77 {
78     frefid_t fileref;
79     strid_t  stream;
80 
81     fileref = glk_fileref_create_temp(fileusage_Data | fileusage_BinaryMode,
82                                       0);
83     stream = glk_stream_open_file(fileref, filemode_ReadWrite, 0);
84     glk_fileref_destroy(fileref);
85     return stream;
86 }
87 
88 /* Delete a temp file that was created by os_create_tempfile. Since
89    Glk automatically deletes temporary files, I have no need of this */
osfdel_temp(const char * fname)90 int osfdel_temp(const char *fname)
91 {
92     return TRUE;
93 }
94 
95 /* Get a path to put a temp file--too bad we don't use paths...most of
96    the time */
os_get_tmp_path(char * buf)97 void os_get_tmp_path(char *buf)
98 {
99     buf[0] = 0;
100 }
101 
102 /* Locate a file and, if the search is successful, store the resulting
103    name in the given buffer. Return TRUE if the file was found, FALSE
104    otherwise. Since Glk doesn't concern itself with paths, this is an
105    easy check */
os_locate(const char * fname,int flen,const char * arg0,char * buf,size_t bufsiz)106 int os_locate(const char *fname, int flen, const char *arg0, char *buf,
107           size_t bufsiz)
108 {
109     if (!osfacc(fname)) {
110         memcpy(buf, fname, flen);
111         buf[flen] = 0;
112         return TRUE;
113     }
114     return FALSE;
115 }
116 
117 /* open text file for reading; returns NULL on error */
osfoprt(char * fname,glui32 typ)118 osfildef *osfoprt(char *fname, glui32 typ)
119 {
120     return (oss_open_stream(fname, typ, fileusage_TextMode,
121                             filemode_Read, 0));
122 }
123 
124 /* open text file for writing; returns NULL on error */
osfopwt(char * fname,glui32 typ)125 osfildef *osfopwt(char *fname, glui32 typ)
126 {
127     return (oss_open_stream(fname, typ, fileusage_TextMode,
128                             filemode_Write, 0));
129 }
130 
131 /* open text file for reading and writing; returns NULL on error */
osfoprwt(char * fname,glui32 typ)132 osfildef *osfoprwt(char *fname, glui32 typ)
133 {
134     return (oss_open_stream(fname, typ, fileusage_TextMode,
135                             filemode_ReadWrite, 0));
136 }
137 
138 /* open text file for reading/writing; truncate; returns NULL on error */
osfoprwtt(char * fname,glui32 typ)139 osfildef *osfoprwtt(char *fname, glui32 typ)
140 {
141     return (oss_open_stream(fname, typ, fileusage_TextMode,
142                             filemode_ReadWrite, 0));
143 }
144 
145 /* open binary file for writing; returns NULL on error */
osfopwb(char * fname,glui32 typ)146 osfildef *osfopwb(char *fname, glui32 typ)
147 {
148     return (oss_open_stream(fname, typ, fileusage_BinaryMode,
149                             filemode_Write, 0));
150 }
151 
152 /* open source file for reading */
osfoprs(char * fname,glui32 typ)153 osfildef *osfoprs(char *fname, glui32 typ)
154 {
155     return (oss_open_stream(fname, typ, fileusage_BinaryMode,
156                             filemode_Read, 0));
157 }
158 
159 /* open binary file for reading; returns NULL on erorr */
osfoprb(char * fname,glui32 typ)160 osfildef *osfoprb(char *fname, glui32 typ)
161 {
162     return (oss_open_stream(fname, typ, fileusage_BinaryMode,
163                             filemode_Read, 0));
164 }
165 
166 /* open binary file for reading/writing; don't truncate */
osfoprwb(char * fname,glui32 typ)167 osfildef *osfoprwb(char *fname, glui32 typ)
168 {
169     return (oss_open_stream(fname, typ, fileusage_BinaryMode,
170                             filemode_ReadWrite, 0));
171 }
172 
173 /* open binary file for reading/writing; truncate; returns NULL on error */
osfoprwtb(char * fname,glui32 typ)174 osfildef *osfoprwtb(char *fname, glui32 typ)
175 {
176     return (oss_open_stream(fname, typ, fileusage_BinaryMode,
177                             filemode_ReadWrite, 0));
178 }
179 
180 /* delete a file - TRUE if error */
osfdel(char * fname)181 int osfdel(char *fname)
182 {
183     frefid_t fileref;
184     int changed_dirs;
185 
186     fileref = oss_convert_string_to_fileref(fname, fileusage_BinaryMode);
187     changed_dirs = oss_check_path(fname);
188     glk_fileref_delete_file(fileref);
189     glk_fileref_destroy(fileref);
190     if (changed_dirs)
191         oss_revert_path();
192     return FALSE;
193 }
194 
195 /* access a file - 0 if file exists */
osfacc(const char * fname)196 int osfacc(const char *fname)
197 {
198     frefid_t fileref;
199     int      result, changed_dirs;
200 
201     fileref = oss_convert_string_to_fileref(fname, fileusage_BinaryMode);
202     changed_dirs = oss_check_path(fname);
203     result = (int)glk_fileref_does_file_exist(fileref);
204     if (changed_dirs)
205         oss_revert_path();
206     return (result != TRUE);
207 }
208 
209 
210 /* build a full path name given a path and a filename */
211 /* (NB much of this comes from osnoui.c) */
os_build_full_path(char * fullpathbuf,size_t fullpathbuflen,const char * path,const char * filename)212 void os_build_full_path(char *fullpathbuf, size_t fullpathbuflen,
213                         const char *path, const char *filename)
214 {
215     size_t plen, flen;
216     int add_sep;
217 
218     /* I'm defining this just for the Unix version of GLK */
219 #ifdef GLKUNIX
220 
221     /*
222      *   Note whether we need to add a separator.  If the path prefix ends
223      *   in a separator, don't add another; otherwise, add the standard
224      *   system separator character.
225      *
226      *   Do not add a separator if the path is completely empty, since this
227      *   simply means that we want to use the current directory.
228      */
229     plen = strlen(path);
230     add_sep = (plen != 0
231                && path[plen-1] != OSPATHCHAR
232                && strchr(OSPATHALT, path[plen-1]) == 0);
233 
234     /* copy the path to the full path buffer, limiting to the buffer length */
235     if (plen > fullpathbuflen - 1)
236         plen = fullpathbuflen - 1;
237     memcpy(fullpathbuf, path, plen);
238 
239     /* add the path separator if necessary (and if there's room) */
240     if (add_sep && plen + 2 < fullpathbuflen)
241         fullpathbuf[plen++] = OSPATHCHAR;
242 
243     /* add the filename after the path, if there's room */
244     flen = strlen(filename);
245     if (flen > fullpathbuflen - plen - 1)
246         flen = fullpathbuflen - plen - 1;
247     memcpy(fullpathbuf + plen, filename, flen);
248 
249     /* add a null terminator */
250     fullpathbuf[plen + flen] = '\0';
251 #endif /* GLKUNIX */
252 }
253 
254 /*
255  *   Given argv[0], what's the executable's full pathname? The problem under
256  *   Unix is that we don't necessarily know. If the interpreter is invoked
257  *   using an alias, argv[0] will (under many shells) contain that alias. So
258  *   we're going to return failure regardless. The good news is that we only
259  *   lose the ability to create bound games, which I don't expect to be an
260  *   issue under Unix. If you port Glk TADS to another platform where this
261  *   is not the case, feel free to adjust this routine appropriately.
262  */
os_get_exe_filename(char * buf,size_t buflen,const char * argv0)263 int os_get_exe_filename(char *buf, size_t buflen, const char *argv0)
264 {
265     buf[0] = 0;
266     return FALSE;
267 }
268 
269 /*
270  *   Get a special path (e.g. path to standard include files or libraries).
271  *   Valid id values include OS_GSP_T3_RES, OS_GSP_T3_LIB, and
272  *   OS_GSP_T3_INC. If certain compiler variables are set, use those
273  *   hardcoded paths. Otherwise, check for environment variables; if those
274  *   aren't set, return argv0's path. Note that this is only for Unix Glk
275  */
os_get_special_path(char * buf,size_t buflen,const char * argv0,int id)276 void os_get_special_path(char *buf, size_t buflen, const char *argv0, int id)
277 {
278     const char *str;
279     char *p;
280 
281 #ifdef GLKUNIX
282     switch(id) {
283 #ifdef DEFINEDIRS
284     case OS_GSP_T3_RES:
285         str = RESDIR;
286         break;
287     case OS_GSP_T3_LIB:
288         str = LIBDIR;
289         break;
290     case OS_GSP_T3_INC:
291         str = INCDIR;
292         break;
293 #else /* DEFINEDIRS */
294     case OS_GSP_T3_RES:
295         str = getenv("TADS3_RESDIR");
296         break;
297     case OS_GSP_T3_LIB:
298         str = getenv("TADS3_LIBDIR");
299         break;
300     case OS_GSP_T3_INC:
301         str = getenv("TADS3_INCLUDEDIR");
302         break;
303 #endif /* DEFINEDIRS */
304     default:
305         /*
306          *   If we're called with another identifier, it must mean that
307          *   we're out of date.  Fail with an assertion.
308          */
309         assert(FALSE);
310     }
311 
312 #ifndef DEFINEDIRS
313     if (str == NULL) {
314         strcpy(buf, argv0);
315         p = buf + strlen(argv0) - 1;
316                 /* Move backwards until we find a slash in argv0 or its beginning */
317         while (p != buf && *p != '/') {
318             p--;
319         }
320         if (*p == '/' && p != buf) {
321             *p = (char)NULL;
322         }
323     }
324 #endif /* DEFINEDIRS */
325 
326     if (strlen(str) >= buflen)
327         assert(FALSE);
328     strcpy(buf, str);
329 #endif /* GLKUNIX */
330 }
331 
332 
333 /* ------------------------------------------------------------------------ */
334 /*
335 ** File extension and path fiddling
336 */
337 
338 /* I'm defining these functions for the Unix Glk libraries. Your port may
339    not need them at all */
340 
os_defext(char * fname,const char * ext)341 void os_defext(char *fname, const char *ext)
342 {
343 #ifdef GLKUNIX
344     os_addext(fname, ext);
345 #endif /* GLKUNIX */
346 }
347 
os_addext(char * fname,const char * ext)348 void os_addext(char *fname, const char *ext)
349 {
350 #ifdef GLKUNIX
351     char buf[5], *p;
352 
353     /* Don't do any fiddling if the passed string is a hashed fileref or
354        if there is already an extension on the file */
355     if (oss_is_string_a_fileref(fname))
356         return;
357     p = fname + strlen(fname) - 1;
358     while (p > fname && *p != '.' && *p != '/')
359         p--;
360     if (*p == '.')
361         return;
362 
363     strcat(fname, ".");                   /* Append a dot and the extension */
364     strcpy(buf, ext);           /* Make the extension lower-case by default */
365     os_strlwr(buf);
366     strcat(fname, buf);
367 #endif /* GLKUNIX */
368 }
369 
os_remext(char * fname)370 void os_remext(char *fname)
371 {
372 #ifdef GLKUNIX
373     char *p;
374 
375     /* Don't do any fiddling if the passed string is a hashed fileref */
376     if (oss_is_string_a_fileref(fname))
377         return;
378 
379     p = fname + strlen(fname);
380     while (p != fname) {
381         p--;
382         if (*p == '.') {
383             *p = 0;
384             return;
385         }
386         /* If we run into a directory separator, return */
387         if (*p == '/' || *p == '\\' || *p == ':')
388             return;
389     }
390 #endif /* GLKUNIX */
391 }
392 
393 /* Get a pointer to the root name portion of a filename */
os_get_root_name(char * buf)394 char *os_get_root_name(char *buf)
395 {
396     char *p = buf;
397 
398 #ifdef GLKUNIX
399     p += strlen(buf) - 1;
400     while (*p != '/' && p > buf)
401         p--;
402     if (p != buf) p++;
403 #endif /* GLKUNIX */
404     return p;
405 }
406 
407 
408 /* ------------------------------------------------------------------------ */
409 /*
410 ** Text handling and I/O
411 */
412 
413 /*
414 ** The main text area print routines
415 */
os_printz(const char * str)416 void os_printz(const char *str)
417 {
418     /* print the string through the base counted-length print routine */
419     os_print(str, strlen(str));
420 }
421 
os_print(const char * str,size_t len)422 void os_print(const char *str, size_t len)
423 {
424     int current_status_mode;
425 
426     /* Decide what to do based on our status mode */
427     current_status_mode = os_get_status();
428     if (current_status_mode == OSS_STATUS_MODE_STORY) {
429         oss_put_string_with_hilite(story_win, str, len);
430     }
431     else if (current_status_mode == OSS_STATUS_MODE_STATUS) {
432         const char *p;
433         size_t      rem;
434 
435         /* The string requires some fiddling for the status window */
436         for (p = str, rem = len ; rem != 0 && *p == '\n'; p++, --rem) ;
437         if (rem != 0 && p[rem-1] == '\n')
438             --rem;
439 
440         /* if that leaves anything, update the statusline */
441         if (rem != 0)
442             oss_change_status_left(p, rem);
443     }
444 }
445 
os_fprintz(osfildef * fp,const char * str)446 void os_fprintz(osfildef *fp, const char *str)
447 {
448     glk_put_string_stream(fp, str);
449 }
450 
os_fprint(osfildef * fp,const char * str,size_t len)451 void os_fprint(osfildef *fp, const char *str, size_t len)
452 {
453     glk_put_buffer_stream(fp, str, len);
454 }
455 
456 /* Convert a string to all-lowercase */
os_strlwr(char * s)457 char *os_strlwr(char *s)
458 {
459     char *sptr;
460 
461     sptr = s;
462     while (*sptr != 0) {
463         *sptr = glk_char_to_lower((unsigned char)*sptr);
464         sptr++;
465     }
466     return s;
467 }
468 
469 /* Show a [MORE] prompt */
os_more_prompt(void)470 void os_more_prompt(void)
471 {
472     int done;
473 
474     /* display the "MORE" prompt */
475     os_printz("[More]");
476     os_flush();
477 
478     /* wait for a keystroke */
479     for (done = FALSE; !done; )
480     {
481         os_event_info_t evt;
482 
483         /* get an event */
484         switch(os_get_event(0, FALSE, &evt))
485         {
486         case OS_EVT_KEY:
487             /* stop waiting, show one page */
488             done = TRUE;
489             break;
490 
491         case OS_EVT_EOF:
492             /* end of file - there's nothing to wait for now */
493             done = TRUE;
494             break;
495 
496         default:
497             /* ignore other events */
498             break;
499         }
500     }
501     os_printz("\n");
502     os_flush();
503 }
504 
505 /* Set non-stop mode */
os_nonstop_mode(int flag)506 void os_nonstop_mode(int flag)
507 {
508     /* Dummy; someone please implement it. */
509 }
510 
511 /* ------------------------------------------------------------------------ */
512 /*
513 ** Keyboard I/O
514 */
515 
516 /* Read in a line of text from the keyboard */
os_gets(unsigned char * buf,size_t bufl)517 unsigned char *os_gets(unsigned char *buf, size_t bufl)
518 {
519     event_t ev;
520 
521     glk_request_line_event(story_win, buf, (glui32) bufl - 1, 0);
522     do {
523         glk_select(&ev);
524         if (ev.type == evtype_Arrange)
525             oss_draw_status_line();
526     } while (ev.type != evtype_LineInput);
527     buf[ev.val1] = 0;                     /* Don't forget the trailing NULL */
528 
529     return buf;
530 }
531 
532 /* Get a character from the keyboard. For extended characters, return 0,
533    then return the extended key at the next call to this function */
os_getc(void)534 int os_getc(void)
535 {
536     return (oss_getc_from_window(story_win));
537 }
538 
539 /* Get a character from the keyboard, returning low-level, untranslated
540    key codes. Since we don't deal with anything but low-level codes,
541    this is exactly the same as os_getc. */
os_getc_raw(void)542 int os_getc_raw(void)
543 {
544     return os_getc();
545 }
546 
547 /* Wait for a key to be hit */
os_waitc(void)548 void os_waitc(void)
549 {
550     os_getc();
551 }
552 
553 /* Here's the biggie. Get an input event, which may or may not be timed
554    out. timeout is in milliseconds */
os_get_event(unsigned long timeout,int use_timeout,os_event_info_t * info)555 int os_get_event(unsigned long timeout, int use_timeout,
556          os_event_info_t *info)
557 {
558     event_t ev;
559 
560     glk_request_char_event(story_win);
561     if (flag_timer_supported && use_timeout)
562         glk_request_timer_events((glui32)timeout);
563     do {
564         glk_select(&ev);
565         if (ev.type == evtype_Arrange)
566             oss_draw_status_line();
567     } while (ev.type != evtype_Timer && ev.type != evtype_CharInput);
568     glk_cancel_char_event(story_win);                       /* Just in case */
569     if (flag_timer_supported)             /* If we support timers, stop 'em */
570         glk_request_timer_events(0);
571     if (ev.type == evtype_Timer)
572         return OS_EVT_TIMEOUT;
573     if (ev.val1 == keycode_Return)
574         ev.val1 = '\n';
575     else if (ev.val1 == keycode_Tab)
576         ev.val1 = '\t';
577     if (ev.val1 <= 255)
578         info->key[0] = (int)(ev.val1);
579     else {
580         info->key[0] = 0;
581         info->key[1] = (int)(oss_convert_keystroke_to_tads(ev.val1));
582     }
583     return OS_EVT_KEY;
584 }
585 
586 
587 /* ------------------------------------------------------------------------ */
588 /*
589 ** Status line functions
590 */
591 
592 /* Set the status mode */
os_status(int stat)593 void os_status(int stat)
594 {
595     status_mode = stat;
596 }
597 
598 /* Query the status mode */
os_get_status()599 int os_get_status()
600 {
601     return status_mode;
602 }
603 
604 /* Display a string in the score area (rightmost) of the status line */
os_strsc(const char * p)605 void os_strsc(const char *p)
606 {
607     if (p == NULL)              /* NULL means simply refresh the status */
608         oss_draw_status_line();
609     else oss_change_status_right(p);
610 }
611 
612 /* Set the score. If score == -1, use the last score */
os_score(int cur,int turncount)613 void os_score(int cur, int turncount)
614 {
615     char buf[20];
616 
617     if (turncount == -1)       /* -1 means simply refresh the status */
618         oss_draw_status_line();
619     else {
620         sprintf(buf, "%d/%d", cur, turncount);
621         oss_change_status_right(buf);
622     }
623 }
624 
625 
626 /* ------------------------------------------------------------------------ */
627 /*
628 ** Other misc functions
629 */
630 
os_rand(long * seed)631 void os_rand(long *seed)
632 {
633     time_t t;
634 
635     time(&t);
636     *seed = (long)t;
637 }
638 
os_get_sys_clock_ms(void)639 long os_get_sys_clock_ms(void)
640 {
641     if (time_zero == 0)
642         time_zero = time(0);
643     return ((time(0) - time_zero) * 1000);
644 }
645 
os_sleep_ms(long delay_in_ms)646 void os_sleep_ms(long delay_in_ms)
647 {
648     glk_tick();
649     usleep(delay_in_ms);
650     glk_tick();
651 }
652 
os_set_text_attr(int attr)653 void os_set_text_attr(int attr)
654 {
655     /* ensure that we are acting on the story window */
656     glk_set_window(story_win);
657 
658     /* map highlighting/bold/emphasized to "emphasized" style */
659     if ((attr & (OS_ATTR_HILITE | OS_ATTR_BOLD | OS_ATTR_EM)) != 0)
660         glk_set_style(style_Emphasized);
661     else
662         glk_set_style(style_Normal);
663 }
664 
os_set_text_color(os_color_t fg,os_color_t bg)665 void os_set_text_color(os_color_t fg, os_color_t bg)
666 {
667     /* glk does not have a way to set colors explicitly */
668 }
669 
oscls(void)670 void oscls(void)
671 {
672     glk_window_clear(story_win);
673 }
674 
675 /* Flush the output */
os_flush(void)676 void os_flush(void)
677 {
678     glk_tick();
679 }
680 
681 /* update the display - process any painting events immediately */
os_update_display()682 void os_update_display()
683 {
684     glk_tick();
685 }
686 
os_yield(void)687 int os_yield(void)
688 {
689     glk_tick();
690     return FALSE;
691 }
692 
os_expause(void)693 void os_expause(void)
694 {
695 #ifdef USE_EXPAUSE
696     os_printz("(Strike any key to exit...)");
697     os_flush();
698     os_waitc();
699 #endif /* USE_EXPAUSE */
700 }
701 
os_get_sysinfo(int code,void * parm,long * result)702 int os_get_sysinfo(int code, void *parm, long *result)
703 {
704     switch(code) {
705     case SYSINFO_TEXT_HILITE:
706         /* we do support text highlighting */
707         *result = 1;
708         return TRUE;
709 
710     case SYSINFO_HTML:
711     case SYSINFO_JPEG:
712     case SYSINFO_PNG:
713     case SYSINFO_WAV:
714     case SYSINFO_MIDI:
715     case SYSINFO_WAV_MIDI_OVL:
716     case SYSINFO_WAV_OVL:
717     case SYSINFO_PREF_IMAGES:
718     case SYSINFO_PREF_SOUNDS:
719     case SYSINFO_PREF_MUSIC:
720     case SYSINFO_PREF_LINKS:
721     case SYSINFO_MPEG:
722     case SYSINFO_MPEG1:
723     case SYSINFO_MPEG2:
724     case SYSINFO_MPEG3:
725     case SYSINFO_LINKS_HTTP:
726     case SYSINFO_LINKS_FTP:
727     case SYSINFO_LINKS_NEWS:
728     case SYSINFO_LINKS_MAILTO:
729     case SYSINFO_LINKS_TELNET:
730     case SYSINFO_PNG_TRANS:
731     case SYSINFO_PNG_ALPHA:
732     case SYSINFO_OGG:
733     case SYSINFO_BANNERS:
734         /* Since we support none of these, set result to 0 */
735         *result = 0;
736         return TRUE;                              /* We recognized the code */
737     case SYSINFO_INTERP_CLASS:
738         /* we're a text-only character-mode interpreter */
739         /*
740          *   $$$ we might want to be more specific: if it's possible to
741          *   determine whether we're running on a character-mode or GUI
742          *   platform, we should indicate type TEXT or TEXTGUI as
743          *   appropriate.  There's no practical difference between these
744          *   classes at the moment, though, so it's not very important to
745          *   distinguish them.
746          */
747         *result = SYSINFO_ICLASS_TEXT;
748         return TRUE;
749     default:
750         return FALSE;
751     }
752 }
753 
os_xlat_html4(unsigned int html4_char,char * result,size_t result_len)754 void os_xlat_html4(unsigned int html4_char, char *result, size_t result_len)
755 {
756     /* Return all standard Latin-1 characters as-is */
757     if (html4_char <= 128 || (html4_char >= 160 && html4_char <= 255))
758         result[0] = (unsigned char)html4_char;
759     else {
760         switch (html4_char) {
761         case 130:                                      /* single back quote */
762             result[0] = '`'; break;
763         case 132:                                      /* double back quote */
764             result[0] = '\"'; break;
765         case 153:                                             /* trade mark */
766             strcpy(result, "(tm)"); return;
767         case 140:                                            /* OE ligature */
768         case 338:                                            /* OE ligature */
769             strcpy(result, "OE"); return;
770         case 339:                                            /* oe ligature */
771             strcpy(result, "oe"); return;
772         case 159:                                                   /* Yuml */
773             result[0] = 255;
774         case 376:                                        /* Y with diaresis */
775             result[0] = 'Y'; break;
776         case 352:                                           /* S with caron */
777             result[0] = 'S'; break;
778         case 353:                                           /* s with caron */
779             result[0] = 's'; break;
780         case 150:                                                /* en dash */
781         case 8211:                                               /* en dash */
782             result[0] = '-'; break;
783         case 151:                                                /* em dash */
784         case 8212:                                               /* em dash */
785             strcpy(result, "--"); return;
786         case 145:                                      /* left single quote */
787         case 8216:                                     /* left single quote */
788             result[0] = '`'; break;
789         case 146:                                     /* right single quote */
790         case 8217:                                    /* right single quote */
791         case 8218:                                    /* single low-9 quote */
792             result[0] = '\''; break;
793         case 147:                                      /* left double quote */
794         case 148:                                     /* right double quote */
795         case 8220:                                     /* left double quote */
796         case 8221:                                    /* right double quote */
797         case 8222:                                    /* double low-9 quote */
798             result[0] = '\"'; break;
799         case 8224:                                                /* dagger */
800         case 8225:                                         /* double dagger */
801         case 8240:                                        /* per mille sign */
802             result[0] = ' '; break;
803         case 139:                       /* single left-pointing angle quote */
804         case 8249:                      /* single left-pointing angle quote */
805             result[0] = '<'; break;
806         case 155:                      /* single right-pointing angle quote */
807         case 8250:                     /* single right-pointing angle quote */
808             result[0] = '>'; break;
809         case 8482:                                           /* small tilde */
810             result[0] = '~'; break;
811 
812         default:
813             /* unmappable character - return space */
814             result[0] = (unsigned char)' ';
815         }
816     }
817     result[1] = 0;
818 }
819 
os_gen_charmap_filename(char * filename,char * internal_id,char * argv0)820 void os_gen_charmap_filename(char *filename, char *internal_id, char *argv0)
821 {
822     filename[0] = 0;
823 }
824 
825 /* ------------------------------------------------------------------------ */
826 /*
827 ** Some empty routines that we have to have just because
828 */
829 /* Set the title of the story window */
os_set_title(const char * title)830 void os_set_title(const char *title) {}
831 
832 /* Seek to the game file embedded in the given executable file */
os_exeseek(const char * exefile,const char * typ)833 osfildef *os_exeseek(const char *exefile, const char *typ) { return NULL; }
834 
835 /* Load an external function from a file, given the name of the file */
os_exfil(const char * name)836 int (*os_exfil(const char *name))(void *) { return (int (*)(void *))NULL; }
837 
838 /* Load an external function from an open file */
os_exfld(osfildef * fp,unsigned len)839 int (*os_exfld(osfildef *fp, unsigned len))(void *)
840 {
841     return (int (*)(void *))NULL;
842 }
843 
844 /* Call an external function */
os_excall(int (* extfn)(void *),void * arg)845 int os_excall(int (*extfn)(void *), void *arg) { return 0; }
846 
847 /* Check for user break */
os_break(void)848 int os_break(void) { return FALSE; }
849 
850 /* Get a filename from a startup parameter, if possible. Which it isn't */
os_paramfile(char * buf)851 int os_paramfile(char *buf) { return FALSE; }
852 
853 /* Set the terminal into "plain" mode */
os_plain(void)854 void os_plain(void) {}
855 
856 /* Set the saved game extension. Sha, as if. */
os_set_save_ext(const char * ext)857 void os_set_save_ext(const char *ext) {}
858 
859 /* Set a file's filetype */
os_settype(const char * f,int t)860 void os_settype(const char *f, int t) {}
861 
862 /* Find the first file in a directory, given a wildcard pattern */
os_find_first_file(const char * dir,const char * pattern,char * outbuf,size_t outbufsiz,int * isdir,char * outpathbuf,size_t outpathbufsiz)863 void *os_find_first_file(const char *dir, const char *pattern,
864                          char *outbuf, size_t outbufsiz, int *isdir,
865                          char *outpathbuf, size_t outpathbufsiz) {}
866 
867 /* Find the next file */
os_find_next_file(void * ctx0,char * outbuf,size_t outbufsize,int * isdir,char * outpathbuf,size_t outpathbufsiz)868 void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsize,
869         int *isdir, char *outpathbuf, size_t outpathbufsiz) {}
870 
871 /* Cancel a search */
os_find_close(void * ctx0)872 void os_find_close(void *ctx0) {}
873 
874 /* Character map loading */
os_advise_load_charmap(char * id,char * ldesc,char * sysinfo)875 void os_advise_load_charmap(char *id, char *ldesc, char *sysinfo) {}
876 
877 /* TK I should be able to remove these eventually, when Glk is updated */
osfwb(osfildef * fp,unsigned char * buf,int bufl)878 int osfwb(osfildef *fp, unsigned char *buf, int bufl)
879 {
880     glk_put_buffer_stream(fp, buf, (glui32)bufl);
881     return FALSE;
882 }
osfseek(osfildef * fp,long pos,int mode)883 int osfseek(osfildef *fp, long pos, int mode)
884 {
885     glk_stream_set_position(fp, (glsi32)pos, mode);
886     return FALSE;
887 }
osfputs(char * buf,osfildef * fp)888 int osfputs(char *buf, osfildef *fp)
889 {
890     glk_put_string_stream(fp, buf);
891     return 1;
892 }
893