cfad47cfa3/src/osportable.cc

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* This file implements some of the functions described in
2
 * tads2/osifc.h.  We don't need to implement them all, as most of them
3
 * are provided by tads2/osnoui.c and tads2/osgen3.c.
4
 *
5
 * This file implements the "portable" subset of these functions;
6
 * functions that depend upon curses/ncurses are defined in oscurses.cc.
7
 */
8
#include "common.h"
9
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <ctype.h>
13
#include <string.h>
14
#include <sys/stat.h>
15
#ifdef HAVE_LANGINFO_CODESET
16
#include <langinfo.h>
17
#endif
18
19
20
#include "os.h"
21
#ifdef RUNTIME
22
// We utilize the Tads 3 Unicode character mapping facility even for
23
// Tads 2 (only in the interpreter).
24
#include "charmap.h"
25
// Need access to command line options: globalApp->options. Only at
26
// runtime - compiler uses different mechanism for command line
27
// options.
28
#include "frobtadsapp.h"
29
#endif // RUNTIME
30
31
32
/* Service routine.
33
 * Try to bring returned by system charset name to a name of one of
34
 * the encodings in cmaplib.t3r.  The proper way to do it is to
35
 * maintain an external database of charset aliases, which user can
36
 * edit.  But this adds unnecessary complexity, which we really don't
37
 * need.  So, the "database" is internal.
38
 *
39
 * TODO: Restore previous bahavior when we fix out curses interface.
40
 * For now, we always return "us-ascii".
41
 */
42
static const char*
43
get_charset_alias( const char* charset )
44
{
45
	const char* p;
46
	const char* c;
47
48
	// Special case.
49
	if (!stricmp(charset, "ANSI_X3.4-1968")) return "us-ascii";
50
51
	// If it's in ru_RU.KOI8-R form, try to extract charset part
52
	// (everything after last point).
53
54
	// Find the last point in the charset name.
55
	for (p = charset, c = charset ; *p != '\0' ; ++p) {
56
		if (*p == '.') c = p + 1;
57
	}
58
59
	// '.' was the last symbol in charset name?
60
	if (*c == '\0') c = charset;
61
62
	if (!stricmp(c, "KOI8-R")) return "koi8-r";
63
64
	if (!stricmp(c, "ISO-8859-1"))  return "iso1";
65
	if (!stricmp(c, "ISO-8859-2"))  return "iso2";
66
	if (!stricmp(c, "ISO-8859-3"))  return "iso3";
67
	if (!stricmp(c, "ISO-8859-4"))  return "iso4";
68
	if (!stricmp(c, "ISO-8859-5"))  return "iso5";
69
	if (!stricmp(c, "ISO-8859-6"))  return "iso6";
70
	if (!stricmp(c, "ISO-8859-7"))  return "iso7";
71
	if (!stricmp(c, "ISO-8859-8"))  return "iso8";
72
	if (!stricmp(c, "ISO-8859-9"))  return "iso9";
73
	if (!stricmp(c, "ISO-8859-10")) return "iso10";
74
	if (!stricmp(c, "8859-1"))      return "iso1";
75
	if (!stricmp(c, "8859-2"))      return "iso2";
76
	if (!stricmp(c, "8859-3"))      return "iso3";
77
	if (!stricmp(c, "8859-4"))      return "iso4";
78
	if (!stricmp(c, "8859-5"))      return "iso5";
79
	if (!stricmp(c, "8859-6"))      return "iso6";
80
	if (!stricmp(c, "8859-7"))      return "iso7";
81
	if (!stricmp(c, "8859-8"))      return "iso8";
82
	if (!stricmp(c, "8859-9"))      return "iso9";
83
	if (!stricmp(c, "8859-10"))     return "iso10";
84
85
	if (!stricmp(c, "CP1250"))  return "cp1250";
86
	if (!stricmp(c, "CP-1250")) return "cp1250";
87
	if (!stricmp(c, "CP_1250")) return "cp1250";
88
	if (!stricmp(c, "CP1251"))  return "cp1251";
89
	if (!stricmp(c, "CP-1251")) return "cp1251";
90
	if (!stricmp(c, "CP_1251")) return "cp1251";
91
	if (!stricmp(c, "CP1252"))  return "cp1252";
92
	if (!stricmp(c, "CP-1252")) return "cp1252";
93
	if (!stricmp(c, "CP_1252")) return "cp1252";
94
	if (!stricmp(c, "CP1253"))  return "cp1253";
95
	if (!stricmp(c, "CP-1253")) return "cp1253";
96
	if (!stricmp(c, "CP_1253")) return "cp1253";
97
	if (!stricmp(c, "CP1254"))  return "cp1254";
98
	if (!stricmp(c, "CP-1254")) return "cp1254";
99
	if (!stricmp(c, "CP_1254")) return "cp1254";
100
	if (!stricmp(c, "CP1255"))  return "cp1255";
101
	if (!stricmp(c, "CP-1255")) return "cp1255";
102
	if (!stricmp(c, "CP_1255")) return "cp1255";
103
	if (!stricmp(c, "CP1256"))  return "cp1256";
104
	if (!stricmp(c, "CP-1256")) return "cp1256";
105
	if (!stricmp(c, "CP_1256")) return "cp1256";
106
	if (!stricmp(c, "CP1257"))  return "cp1257";
107
	if (!stricmp(c, "CP-1257")) return "cp1257";
108
	if (!stricmp(c, "CP_1257")) return "cp1257";
109
	if (!stricmp(c, "CP1258"))  return "cp1258";
110
	if (!stricmp(c, "CP-1258")) return "cp1258";
111
	if (!stricmp(c, "CP_1258")) return "cp1258";
112
113
	if (!stricmp(c, "CP437"))  return "cp437";
114
	if (!stricmp(c, "CP-437")) return "cp437";
115
	if (!stricmp(c, "CP_437")) return "cp437";
116
	if (!stricmp(c, "PC437"))  return "cp437";
117
	if (!stricmp(c, "PC-437")) return "cp437";
118
	if (!stricmp(c, "PC_437")) return "cp437";
119
	if (!stricmp(c, "CP737"))  return "cp737";
120
	if (!stricmp(c, "CP-737")) return "cp737";
121
	if (!stricmp(c, "CP_737")) return "cp737";
122
	if (!stricmp(c, "PC737"))  return "cp737";
123
	if (!stricmp(c, "PC-737")) return "cp737";
124
	if (!stricmp(c, "PC_737")) return "cp737";
125
	if (!stricmp(c, "CP775"))  return "cp775";
126
	if (!stricmp(c, "CP-775")) return "cp775";
127
	if (!stricmp(c, "CP_775")) return "cp775";
128
	if (!stricmp(c, "PC775"))  return "cp775";
129
	if (!stricmp(c, "PC-775")) return "cp775";
130
	if (!stricmp(c, "PC_775")) return "cp775";
131
	if (!stricmp(c, "CP850"))  return "cp850";
132
	if (!stricmp(c, "CP-850")) return "cp850";
133
	if (!stricmp(c, "CP_850")) return "cp850";
134
	if (!stricmp(c, "PC850"))  return "cp850";
135
	if (!stricmp(c, "PC-850")) return "cp850";
136
	if (!stricmp(c, "PC_850")) return "cp850";
137
	if (!stricmp(c, "CP852"))  return "cp852";
138
	if (!stricmp(c, "CP-852")) return "cp852";
139
	if (!stricmp(c, "CP_852")) return "cp852";
140
	if (!stricmp(c, "PC852"))  return "cp852";
141
	if (!stricmp(c, "PC-852")) return "cp852";
142
	if (!stricmp(c, "PC_852")) return "cp852";
143
	if (!stricmp(c, "CP855"))  return "cp855";
144
	if (!stricmp(c, "CP-855")) return "cp855";
145
	if (!stricmp(c, "CP_855")) return "cp855";
146
	if (!stricmp(c, "PC855"))  return "cp855";
147
	if (!stricmp(c, "PC-855")) return "cp855";
148
	if (!stricmp(c, "PC_855")) return "cp855";
149
	if (!stricmp(c, "CP857"))  return "cp857";
150
	if (!stricmp(c, "CP-857")) return "cp857";
151
	if (!stricmp(c, "CP_857")) return "cp857";
152
	if (!stricmp(c, "PC857"))  return "cp857";
153
	if (!stricmp(c, "PC-857")) return "cp857";
154
	if (!stricmp(c, "PC_857")) return "cp857";
155
	if (!stricmp(c, "CP860"))  return "cp860";
156
	if (!stricmp(c, "CP-860")) return "cp860";
157
	if (!stricmp(c, "CP_860")) return "cp860";
158
	if (!stricmp(c, "PC860"))  return "cp860";
159
	if (!stricmp(c, "PC-860")) return "cp860";
160
	if (!stricmp(c, "PC_860")) return "cp860";
161
	if (!stricmp(c, "CP861"))  return "cp861";
162
	if (!stricmp(c, "CP-861")) return "cp861";
163
	if (!stricmp(c, "CP_861")) return "cp861";
164
	if (!stricmp(c, "PC861"))  return "cp861";
165
	if (!stricmp(c, "PC-861")) return "cp861";
166
	if (!stricmp(c, "PC_861")) return "cp861";
167
	if (!stricmp(c, "CP862"))  return "cp862";
168
	if (!stricmp(c, "CP-862")) return "cp862";
169
	if (!stricmp(c, "CP_862")) return "cp862";
170
	if (!stricmp(c, "PC862"))  return "cp862";
171
	if (!stricmp(c, "PC-862")) return "cp862";
172
	if (!stricmp(c, "PC_862")) return "cp862";
173
	if (!stricmp(c, "CP863"))  return "cp863";
174
	if (!stricmp(c, "CP-863")) return "cp863";
175
	if (!stricmp(c, "CP_863")) return "cp863";
176
	if (!stricmp(c, "PC863"))  return "cp863";
177
	if (!stricmp(c, "PC-863")) return "cp863";
178
	if (!stricmp(c, "PC_863")) return "cp863";
179
	if (!stricmp(c, "CP864"))  return "cp864";
180
	if (!stricmp(c, "CP-864")) return "cp864";
181
	if (!stricmp(c, "CP_864")) return "cp864";
182
	if (!stricmp(c, "PC864"))  return "cp864";
183
	if (!stricmp(c, "PC-864")) return "cp864";
184
	if (!stricmp(c, "PC_864")) return "cp864";
185
	if (!stricmp(c, "CP865"))  return "cp865";
186
	if (!stricmp(c, "CP-865")) return "cp865";
187
	if (!stricmp(c, "CP_865")) return "cp865";
188
	if (!stricmp(c, "PC865"))  return "cp865";
189
	if (!stricmp(c, "PC-865")) return "cp865";
190
	if (!stricmp(c, "PC_865")) return "cp865";
191
	if (!stricmp(c, "CP866"))  return "cp866";
192
	if (!stricmp(c, "CP-866")) return "cp866";
193
	if (!stricmp(c, "CP_866")) return "cp866";
194
	if (!stricmp(c, "PC866"))  return "cp866";
195
	if (!stricmp(c, "PC-866")) return "cp866";
196
	if (!stricmp(c, "PC_866")) return "cp866";
197
	if (!stricmp(c, "CP869"))  return "cp869";
198
	if (!stricmp(c, "CP-869")) return "cp869";
199
	if (!stricmp(c, "CP_869")) return "cp869";
200
	if (!stricmp(c, "PC869"))  return "cp869";
201
	if (!stricmp(c, "PC-869")) return "cp869";
202
	if (!stricmp(c, "PC_869")) return "cp869";
203
	if (!stricmp(c, "CP874"))  return "cp874";
204
	if (!stricmp(c, "CP-874")) return "cp874";
205
	if (!stricmp(c, "CP_874")) return "cp874";
206
	if (!stricmp(c, "PC874"))  return "cp874";
207
	if (!stricmp(c, "PC-874")) return "cp874";
208
	if (!stricmp(c, "PC_874")) return "cp874";
209
210
	// There are Mac OS Roman, Central European, Cyrillic, Greek,
211
	// Icelandic and Turkish charmaps in cmaplib.t3r. But what codes
212
	// system supposed to return?
213
214
	// Unrecognized.
215
	return charset;
216
}
217
218
219
/* Open text file for reading and writing.
220
 */
221
osfildef*
222
osfoprwt( const char* fname, os_filetype_t )
223
{
224
//	assert(fname != 0);
225
	// Try opening the file in read/write mode.
226
	osfildef* fp = fopen(fname, "r+");
227
	// If opening the file failed, it probably means that it doesn't
228
	// exist.  In that case, create a new file in read/write mode.
229
	if (fp == 0) fp = fopen(fname, "w+");
230
	return fp;
231
}
232
233
234
/* Open binary file for reading/writing.
235
 */
236
osfildef*
237
osfoprwb( const char* fname, os_filetype_t )
238
{
239
//	assert(fname != 0);
240
	osfildef* fp = fopen(fname, "r+b");
241
	if (fp == 0) fp = fopen(fname, "w+b");
242
	return fp;
243
}
244
245
246
/* Convert string to all-lowercase.
247
 */
248
char*
249
os_strlwr( char* s )
250
{
251
//	assert(s != 0);
252
	for (int i = 0; s[i] != '\0'; ++i) {
253
		s[i] = tolower(s[i]);
254
	}
255
	return s;
256
}
257
258
259
/* Seek to the resource file embedded in the current executable file.
260
 *
261
 * We don't support this (and probably never will).
262
 */
263
osfildef*
264
os_exeseek( const char*, const char* )
265
{
266
	return static_cast(osfildef*)(0);
267
}
268
269
270
/* Create and open a temporary file.
271
 */
272
osfildef*
273
os_create_tempfile( const char* fname, char* buf )
274
{
275
	if (fname != 0 and fname[0] != '\0') {
276
		// A filename has been specified; use it.
277
		return fopen(fname, "w+b");
278
	}
279
280
	//ASSERT(buf != 0);
281
282
	// No filename needed; create a nameless tempfile.
283
	buf[0] = '\0';
284
	return tmpfile();
285
}
286
287
288
/* Delete a temporary file created with os_create_tempfile().
289
 */
290
int
291
osfdel_temp( const char* fname )
292
{
293
	//ASSERT(fname != 0);
294
295
	if (fname[0] == '\0' or remove(fname) == 0) {
296
		// Either it was a nameless temp-file and has been
297
		// already deleted by the system, or deleting it
298
		// succeeded.  In either case, the operation was
299
		// successful.
300
		return 0;
301
	}
302
	// It was not a nameless tempfile and remove() failed.
303
	return 1;
304
}
305
306
307
/* Get the temporary file path.
308
 *
309
 * tads2/osnoui.c defines a DOS version of this routine when MSDOS is
310
 * defined.
311
 */
312
#ifndef MSDOS
313
void
314
os_get_tmp_path( char* buf )
315
{
316
	// Try the usual env. variable first.
317
	const char* tmpDir = getenv("TMPDIR");
318
319
	// If no such variable exists, try P_tmpdir (if defined in
320
	// <stdio.h>).
321
#ifdef P_tmpdir
322
	if (tmpDir == 0 or tmpDir[0] == '\0') {
323
		tmpDir = P_tmpdir;
324
	}
325
#endif
326
327
	// If we still lack a path, use "/tmp".
328
	if (tmpDir == 0 or tmpDir[0] == '\0') {
329
		tmpDir = "/tmp";
330
	}
331
332
	// If the directory doesn't exist or is not a directory, don't
333
	// provide anything at all (which means that the current
334
	// directory will be used).
335
	struct stat inf;
336
	int statRet = stat(tmpDir, &inf);
337
	if (statRet != 0 or (inf.st_mode & S_IFMT) != S_IFDIR) {
338
		buf[0] = '\0';
339
		return;
340
	}
341
342
	strcpy(buf, tmpDir);
343
344
	// Append a slash if necessary.
345
	size_t len = strlen(buf);
346
	if (buf[len - 1] != '/') {
347
		buf[len] = '/';
348
		buf[len + 1] = '\0';
349
	}
350
}
351
#endif // not MSDOS
352
353
354
/* Get a suitable seed for a random number generator.
355
 *
356
 * We don't just use the system-clock, but build a number as random as
357
 * the mechanisms of the standard C-library allow.  This is somewhat of
358
 * an overkill, but it's simple enough so here goes.  Someone has to
359
 * write a nuclear war simulator in TADS to test this thoroughly.
360
 */
361
void
362
os_rand( long* val )
363
{
364
	//assert(val != 0);
365
	// Use the current time as the first seed.
366
	srand(static_cast(unsigned int)(time(0)));
367
368
	// Create the final seed by generating a random number using
369
	// high-order bits, because on some systems the low-order bits
370
	// aren't very random.
371
	*val = 1 + static_cast(long)(static_cast(long double)(65535) * rand() / (RAND_MAX + 1.0));
372
}
373
374
375
/* Get the current system high-precision timer.
376
 *
377
 * We provide four (4) implementations of this function:
378
 *
379
 *   1. clock_gettime() is available
380
 *   2. gettimeofday() is available
381
 *   3. ftime() is available
382
 *   4. Neither is available - fall back to time()
383
 *
384
 * Note that HAVE_CLOCK_GETTIME, HAVE_GETTIMEOFDAY and HAVE_FTIME are
385
 * mutually exclusive; if one of them is defined, the others aren't.  No
386
 * need for #else here.
387
 *
388
 * Although not required by the TADS VM, these implementations will
389
 * always return 0 on the first call.
390
 */
391
#ifdef HAVE_CLOCK_GETTIME
392
// The system has the clock_gettime() function.
393
long
394
os_get_sys_clock_ms( void )
395
{
396
	// We need to remember the exact time this function has been
397
	// called for the first time, and use that time as our
398
	// zero-point.  On each call, we simply return the difference
399
	// in milliseconds between the current time and our zero point.
400
	static struct timespec zeroPoint;
401
402
	// Did we get the zero point yet?
403
	static bool initialized = false;
404
405
	// Not all systems provide a monotonic clock; check if it's
406
	// available before falling back to the global system-clock.  A
407
	// monotonic clock is guaranteed not to change while the system
408
	// is running, so we prefer it over the global clock.
409
	static const clockid_t clockType =
410
#ifdef HAVE_CLOCK_MONOTONIC
411
		CLOCK_MONOTONIC;
412
#else
413
		CLOCK_REALTIME;
414
#endif
415
	// We must get the current time in each call.
416
	struct timespec currTime;
417
418
	// Initialize our zero-point, if not already done so.
419
	if (not initialized) {
420
		clock_gettime(clockType, &zeroPoint);
421
		initialized = true;
422
	}
423
424
	// Get the current time.
425
	clock_gettime(clockType, &currTime);
426
427
	// Note that tv_nsec contains *nano*seconds, not milliseconds,
428
	// so we need to convert it; a millisec is 1.000.000 nanosecs.
429
	return (currTime.tv_sec - zeroPoint.tv_sec) * 1000
430
	     + (currTime.tv_nsec - zeroPoint.tv_nsec) / 1000000;
431
}
432
#endif // HAVE_CLOCK_GETTIME
433
434
#ifdef HAVE_GETTIMEOFDAY
435
// The system has the gettimeofday() function.
436
long
437
os_get_sys_clock_ms( void )
438
{
439
	static struct timeval zeroPoint;
440
	static bool initialized = false;
441
	// gettimeofday() needs the timezone as a second argument.  This
442
	// is obsolete today, but we don't care anyway; we just pass
443
	// something.
444
	struct timezone bogus = {0, 0};
445
	struct timeval currTime;
446
447
	if (not initialized) {
448
		gettimeofday(&zeroPoint, &bogus);
449
		initialized = true;
450
	}
451
452
	gettimeofday(&currTime, &bogus);
453
454
	// 'tv_usec' contains *micro*seconds, not milliseconds.  A
455
	// millisec is 1.000 microsecs.
456
	return (currTime.tv_sec - zeroPoint.tv_sec) * 1000 + (currTime.tv_usec - zeroPoint.tv_usec) / 1000;
457
}
458
#endif // HAVE_GETTIMEOFDAY
459
460
461
#ifdef HAVE_FTIME
462
// The system has the ftime() function.  ftime() is in <sys/timeb.h>.
463
#include <sys/timeb.h>
464
long
465
os_get_sys_clock_ms( void )
466
{
467
	static timeb zeroPoint;
468
	static bool initialized = false;
469
	struct timeb currTime;
470
471
	if (not initialized) {
472
		ftime(&zeroPoint);
473
		initialized = true;
474
	}
475
476
	ftime(&currTime);
477
	return (currTime.time - zeroPoint.time) * 1000 + (currTime.millitm - zeroPoint.millitm);
478
}
479
#endif // HAVE_FTIME
480
481
482
#ifndef HAVE_CLOCK_GETTIME
483
#ifndef HAVE_GETTIMEOFDAY
484
#ifndef HAVE_FTIME
485
// Use the standard time() function from the C library.  We lose
486
// millisecond-precision, but that's the best we can do with time().
487
long
488
os_get_sys_clock_ms( void )
489
{
490
	static time_t zeroPoint = time(0);
491
	return (time(0) - zeroPoint) * 1000;
492
}
493
#endif
494
#endif
495
#endif
496
497
498
/* Get filename from startup parameter, if possible.
499
 *
500
 * TODO: Find out what this is supposed to do.
501
 */
502
int
503
os_paramfile( char* )
504
{
505
	return 0;
506
}
507
508
509
/* Get a special directory path.
510
 *
511
 * If env. variables exist, they always override the compile-time
512
 * defaults.
513
 *
514
 * We ignore the argv parameter, since on Unix binaries aren't stored
515
 * together with data on disk.
516
 */
517
void
518
os_get_special_path( char* buf, size_t buflen, const char*, int id )
519
{
520
	const char* res;
521
522
	switch (id) {
523
	  case OS_GSP_T3_RES:
524
		res = getenv("T3_RESDIR");
525
		if (res == 0 or res[0] == '\0') {
526
			res = T3_RES_DIR;
527
		}
528
		break;
529
	  case OS_GSP_T3_INC:
530
		res = getenv("T3_INCDIR");
531
		if (res == 0 or res[0] == '\0') {
532
			res = T3_INC_DIR;
533
		}
534
		break;
535
	  case OS_GSP_T3_LIB:
536
		res = getenv("T3_LIBDIR");
537
		if (res == 0 or res[0] == '\0') {
538
			res = T3_LIB_DIR;
539
		}
540
		break;
541
	  case OS_GSP_T3_USER_LIBS:
542
		// There's no compile-time default for user libs.
543
		res = getenv("T3_USERLIBDIR");
544
		break;
545
	  default:
546
		// TODO: We could print a warning here to inform the
547
		// user that we're outdated.
548
		res = 0;
549
	}
550
551
	if (res != 0) {
552
		// Only use the detected path if it exists and is a
553
		// directory.
554
		struct stat inf;
555
		int statRet = stat(res, &inf);
556
		if (statRet == 0 and (inf.st_mode & S_IFMT) == S_IFDIR) {
557
			strncpy(buf, res, buflen - 1);
558
			return;
559
		}
560
	}
561
	// Indicate failure.
562
	buf[0] = '\0';
563
}
564
565
566
/* Generate a filename for a character-set mapping file.
567
 *
568
 * Follow DOS convention: start with the current local charset
569
 * identifier, then the internal ID, and the suffix ".tcp".  No path
570
 * prefix, which means look in current directory.  This is what we
571
 * want, because mapping files are supposed to be distributed with a
572
 * game, not with the interpreter.
573
 */
574
void
575
os_gen_charmap_filename( char* filename, char* internal_id, char* /*argv0*/ )
576
{
577
	char mapname[32];
578
579
	os_get_charmap(mapname, OS_CHARMAP_DISPLAY);
580
581
	// Theoretically, we can get mapname so long that with 4-letter
582
	// internal id and 4-letter extension '.tcp' it will be longer
583
	// than OSFNMAX. Highly unlikely, but...
584
	size_t len = strlen(mapname);
585
	if (len > OSFNMAX - 9) len = OSFNMAX - 9;
586
587
	memcpy(filename, mapname, len);
588
	strcat(filename, internal_id);
589
	strcat(filename, ".tcp");
590
}
591
592
593
/* Receive notification that a character mapping file has been loaded.
594
 */
595
void
596
os_advise_load_charmap( char* /*id*/, char* /*ldesc*/, char* /*sysinfo*/ )
597
{
598
}
599
600
601
/* Get the full filename (including directory path) to the executable
602
 * file, given the argv[0] parameter passed into the main program.
603
 *
604
 * On Unix, you can't tell what the executable's name is by just looking
605
 * at argv, so we always indicate failure.  No big deal though; this
606
 * information is only used when the interpreter's executable is bundled
607
 * with a game, and we don't support this at all.
608
 */
609
int
610
os_get_exe_filename( char*, size_t, const char* )
611
{
612
	return false;
613
}
614
615
616
/* Generate the name of the character set mapping table for Unicode
617
 * characters to and from the given local character set.
618
 */
619
void
620
os_get_charmap( char* mapname, int charmap_id )
621
{
622
	const char* charset = 0;  // Character set name.
623
624
#ifdef RUNTIME
625
	// If there was a command line option, it takes precedence.
626
	// User knows better, so do not try to modify his setting.
627
	if (globalApp->options.characterSet[0] != '\0') {
628
		// One charset for all.
629
		strncpy(mapname, globalApp->options.characterSet, 32);
630
		return;
631
	}
632
#endif
633
634
	// There is absolutely no robust way to determine the local
635
	// character set.  Roughly speaking, we have three options:
636
	//
637
	// Use nl_langinfo() function.  Not always available.
638
	//
639
	// Use setlocale(LC_CTYPE, NULL).  This only works if user set
640
	// locale which is actually installed on the machine, or else
641
	// it will return NULL.  But we don't need locale, we just need
642
	// to know what the user wants.
643
	//
644
	// Manually look up environment variables LC_ALL, LC_CTYPE and
645
	// LANG.
646
	//
647
	// However, not a single one will provide us with a reliable
648
	// name of local character set.  There is no standard way to
649
	// name charsets.  For a single set we can get almost anything:
650
	// Windows-1251, Win-1251, CP1251, CP-1251, ru_RU.CP1251,
651
	// ru_RU.CP-1251, ru_RU.CP_1251...  And the only way is to
652
	// maintain a database of aliases.
653
654
#if HAVE_LANGINFO_CODESET
655
	charset = nl_langinfo(CODESET);
656
#else
657
	charset = getenv("LC_CTYPE");
658
	if (charset == 0 or charset[0] == '\0') {
659
		charset = getenv("LC_ALL");
660
		if (charset == 0 or charset[0] == '\0') {
661
			charset = getenv("LANG");
662
		}
663
	}
664
#endif
665
666
	if (charset == 0) {
667
		strcpy(mapname, "us-ascii");
668
	} else {
669
		strcpy(mapname, get_charset_alias(charset));
670
	}
671
	return;
672
}
673
674
675
/* Translate a character from the HTML 4 Unicode character set to the
676
 * current character set used for display.
677
 *
678
 * Note that this function is used only by Tads 2.  Tads 3 does mappings
679
 * automatically.
680
 *
681
 * We omit this implementation when not compiling the interpreter (in
682
 * which case os_xlat_html4 will have been defined as an empty macro in
683
 * osfrobtads.h).  We do this because we don't want to introduce any
684
 * TADS 3 dependencies upon the TADS 2 compiler, which should compile
685
 * regardless of whether the TADS 3 sources are available or not.
686
 */
687
#ifndef os_xlat_html4
688
void
689
os_xlat_html4( unsigned int html4_char, char* result, size_t result_buf_len )
690
{
691
	// HTML 4 characters are Unicode.  Tads 3 provides just the
692
	// right mapper: Unicode to ASCII.  We make it static in order
693
	// not to create a mapper on each call and save CPU cycles.
694
	static CCharmapToLocalASCII mapper;
695
	result[mapper.map_char(html4_char, result, result_buf_len)] = '\0';
696
}
697
#endif
698
699
700
/* =====================================================================
701
 *
702
 * The functions defined below are not needed by the interpreter, or
703
 * have a curses-specific implementation and are therefore only used
704
 * when building the compiler (the compiler doesn't use curses, just
705
 * plain stdio).
706
 */
707
#ifndef RUNTIME
708
709
/* Wait for a character to become available from the keyboard.
710
 */
711
void
712
os_waitc( void )
713
{
714
	getchar();
715
}
716
717
718
/* Read a character from the keyboard.
719
 */
720
int
721
os_getc( void )
722
{
723
	return getchar();
724
}
725
726
727
/* Read a character from the keyboard and return the low-level,
728
 * untranslated key code whenever possible.
729
 */
730
int
731
os_getc_raw( void )
732
{
733
	return getchar();
734
}
735
736
737
/* Check for user break.
738
 *
739
 * We only provide a dummy here; we might do something more useful in
740
 * the future though.
741
 */
742
int
743
os_break( void )
744
{
745
	return false;
746
}
747
748
749
/* Yield CPU.
750
 *
751
 * We don't need this.  It's only useful for Windows 3.x and maybe pre-X Mac OS
752
 * and other systems with brain damaged multitasking.
753
 */
754
int
755
os_yield( void )
756
{
757
	return false;
758
}
759
760
761
/* Sleep for a while.
762
 *
763
 * The compiler doesn't need this.
764
 */
765
void
766
os_sleep_ms( long )
767
{
768
}
769
770
#endif /* ! RUNTIME */