-
Notifications
You must be signed in to change notification settings - Fork 1
/
osifc.h
5182 lines (4779 loc) · 238 KB
/
osifc.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* $Header: d:/cvsroot/tads/TADS2/osifc.h,v 1.3 1999/07/11 00:46:34 MJRoberts Exp $ */
/*
* Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
osifc.h - portable interfaces to OS-specific functions
Function
This file defines interfaces to certain functions that must be called
from portable code, but which must have system-specific implementations.
Notes
DO NOT MODIFY THIS FILE WITH PLATFORM-SPECIFIC DEFINITIONS. If you
wish to add definitions for your platform, add them to the osxxx.h
file specific to your platform. Note that your osxxx.h file is always
included *before* this file, so items in your osxxx.h file are already
defined by the time this file is seen by the compiler.
To port TADS to a new platform, you should go through this entire file
and provide a definition for each documented macro, typedef, and
function, and you should provide an implementation for each function
prototype. Each prototype provides a portable interface, so it is
the same on all platforms, but each platform requires its own custom
implementation. Put your definitions in your osxxx.h header file; put
your function implementations in your osxxx.c file or files.
You should not change any macro or typedef that is actually #define'd
in this file. Those definitions are part of the portable interface,
not part of the platform-specific implementation.
Certain functions are merely documented here, but no prototypes are
provided. For these functions, most platforms use #define macros to
implement the functions in terms of standard C library functions or
OS API functions; we do not provide prototypes for these functions so
that the OS implementation can call the C library or OS API functions
directly through a macro, avoiding an unnecessary extra function call.
However, if you must provide an implementation for these functions,
you can provide your own prototypes for them in your osxxx.h header
file.
SEE ALSO osifctyp.h, which contains definitions for some of the
interface datatypes.
Modified
04/04/99 CNebel - Improve const-ness; use new appctx.h header.
12/05/97 MJRoberts - Creation
*/
#ifndef OSIFC_H
#define OSIFC_H
#include <stdlib.h>
#include <stdarg.h>
#include "appctx.h"
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------------------------------------------------------------ */
/*
* A note on character sets:
*
* Except where noted, all character strings passed to and from the
* osxxx functions defined herein use the local operating system
* representation. On a Windows machine localized to Eastern Europe,
* for example, the character strings passed to and from the osxxx
* functions would use single-byte characters in the Windows code page
* 1250 representation.
*
* Callers that use multiple character sets must implement mappings to
* and from the local character set when calling the osxxx functions.
* The osxxx implementations are thus free to ignore any issues related
* to character set conversion or mapping.
*
* The osxxx implementations are specifically not permitted to use
* double-byte Unicode as the native character set, nor any other
* character set where a null byte could appear as part of a non-null
* character. In particular, callers may assume that null-terminated
* strings passed to and from the osxxx functions contain no embedded
* null bytes. Multi-byte character sets (i.e., character sets with
* mixed single-byte and double-byte characters) may be used as long as
* a null byte is never part of any multi-byte character, since this
* would guarantee that a null byte could always be taken as a null
* character without knowledge of the encoding or context.
*/
/* ------------------------------------------------------------------------ */
/*
* "Far" Pointers. Most platforms can ignore this. For platforms with
* mixed-mode addressing models, where pointers of different sizes can
* be used within a single program and hence some pointers require
* qualification to indicate that they use a non-default addressing
* model, the keyword OSFAR should be defined to the appropriate
* compiler-specific extension keyword.
*
* If you don't know what I'm talking about here, you should just ignore
* it, because your platform probably doesn't have anything this
* sinister. As of this writing, this applies only to MS-DOS, and then
* only to 16-bit implementations that must interact with other 16-bit
* programs via dynamic linking or other mechanisms.
*/
/* ------------------------------------------------------------------------ */
/*
* ANSI C99 exact-size integer types.
*
* C99 defines a set of integer types with exact bit sizes, named intXX_t
* for a signed integer with XX bits, and uintXX_t for unsigned. XX can be
* 8, 16, 32, or 64. TADS uses the 16- and 32-bit sizes, so each platform
* is responsible for defining the following types:
*
*. int16_t - a signed integer type storing EXACTLY 16 bits
*. uint16_t - an unsigned integer type storing EXACTLY 16 bits
*. int32_t - a signed integer type storing EXACTLY 32 bits
*. uint32_t - an unsigned integer type storing EXACTLY 32 bits
*
* Many modern compilers provide definitions for these types via the
* standard header stdint.h. Where stdint.h is provided, the platform code
* can merely #include <stdint.h>.
*
* For compilers where stdint.h isn't available, you must provide suitable
* typedefs. Note that the types must be defined with the exact bit sizes
* specified; it's not sufficient to use a bigger type, because we depend
* in some cases on overflow and sign extension behavior at the specific
* bit size.
*/
/* ------------------------------------------------------------------------ */
/*
* Thread-local storage (TLS).
*
* When TADS is compiled with threading support, it requires some variables
* to be "thread-local". This means that the variables have global scope
* (so they're not stored in "auto" variables on the stack), but each
* thread has a private copy of each such variable.
*
* Nearly all systems that support threads also support thread-local
* storage. Like threading support itself, though, TLS support is at
* present implemented only in non-portable OS APIs rather than standard C
* language features. TLS is a requirement if TADS is compiled with
* threading, but it's not needed for non-threaded builds. TADS only
* requires threading at present (version 3.1) for its network features;
* since these features are optional, systems that don't have threading and
* TLS support will simply need to disable the network features, which will
* allow all of the threading and TLS definitions in osifc to be omitted.
*
* There appear to be two common styles of TLS programming models. The
* first provides non-standard compiler syntax for declarative creation of
* thread-local variables. The Microsoft (on Windows) and Gnu compilers
* (on Linux and Unix) do this: they provide custom storage class modifiers
* for declaring thread locals (__declspec(thread) for MSVC, __thread for
* gcc). Compilers that support declarative thread locals handle the
* implementation details through code generation, so the program merely
* needs to add the special TLS storage class qualifier to an otherwise
* ordinary global variable declaration, and then can access the thread
* local as though it were an ordinary global.
*
* The second programming model is via explicit OS API calls to create,
* initialize, and access thread locals. pthreads provides such an API, as
* does Win32. In fact, when you use the declarative syntax with MSVC or
* gcc, the compiler generates the appropriate API calls, but the details
* are transparent to the program; in contrast, when using pthreads
* directly, the program must actively call the relevant APIs.
*
* It's probably the case that every system that has compiler-level support
* for declarative thread local creation also has procedural APIs, so the
* simplest way to abstract the platform differences would be to do
* everything in terms of APIs. However, it seems likely that compilers
* with declarative syntax might be able to generate more efficient code,
* since optimizers always benefit from declarative information. So we'd
* like to use declarative syntax whenever it's available, but fall back on
* explicit API calls when it's not. So our programming model is a union
* of the two styles:
*
* 1. For each thread local, declare the thread local:
*. OS_DECL_TLS(char *, my_local);
*
* 2. At main program startup (for the main thread only), initialize each
* thread local:
*. os_tls_create(my_local);
*
* 3. Never get or set the value of a thread local directly; instead, use
* the get/set functions:
*. char *x = os_tls_get(char *, my_local);
*. os_tls_set(my_local, "hello");
*
* One key feature of this implementation is that each thread local is
* stored as a (void *) value. We do it this way to allow a simple direct
* mapping to the pthreads APIs, since that's going to be the most common
* non-declarative implementation. This means that a thread local variable
* can contain any pointer type, but *only* a pointer type. The standard
* pattern for dealing with anything more ocmplex is the same as in
* pthreads: gather up the data into a structure, malloc() an instance of
* that structure at entry to each thread (including the main thread), and
* os_tls_set() the variable to contain a pointer to that structure. From
* then on, use os_tls_set(my_struct *, my_local)->member to access the
* member variables in the structure. And finally, each thread must delete
* the structure at thread exit.
*/
/*
*
* Declare a thread local.
*
* - For compilers that support declarative TLS variables, the local OS
* headers should use the compiler support by #defining OS_DECL_TLS to the
* appropriate local declarative keyword.
*
* - For systems without declarative TLS support but with TLS APIs, the
* global declared by this macro actually stores the slot ID (what pthreads
* calls the "key") for the variable. This macro should therefore expand
* to a declaration of the appropriate API type for a slot ID; for example,
* on pthreads, #define OS_DECL_TLS(t, v) pthread_key_t v.
*
* - For builds with no thread support, simply #define this to declare the
* variable as an ordinary global: #define OS_DECL_TLS(t, v) t v.
*/
/* #define OS_DECL_TLS(typ, varname) __thread typ varname */
/*
* For API-based systems without declarative support in the compiler, the
* main program startup code must explicitly create a slot for each thread-
* local variable by calling os_tls_create(). The API returns a slot ID,
* which is shared among threads and therefore can be stored in an ordinary
* global variable. OS_DECL_TLS will have declared the global variable
* name in this case as an ordinary global of the slot ID type. The
* os_tls_create() macro should therefore expand to a call to the slot
* creation API, storing the new slot ID in the global.
*
* Correspondingly, before the main thread exits, it should delete each
* slot it created, b calling os_tls_delete().
*
* For declarative systems, there's no action required here, so these
* macros can be defined to empty.
*/
/* #define os_tls_create(varname) pthread_key_create(&varname, NULL) */
/* #define os_tls_delete(varname) pthread_key_delete(varname) */
/*
* On API-based systems, each access to get or set the thread local
* requires an API call, using the slot ID stored in the actual global to
* get the per-thread instance of the variable's storage.
*. #define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname))
*. #define os_tls_set(varname, val) pthread_setspecific(varname, val)
*
* On declarative systems, the global variable itself is the thread local,
* so get/set can be implemented as direct access to the variable.
*. #define os_tls_get(typ, varname) varname
*. #define os_tls_set(varname, val) varname = (val)
*/
/*
* Common TLS definitions - declarative thread locals
*
* For systems with declarative TLS support in the compiler, the OS header
* can #define OS_DECLARATIVE_TLS to pick up suitable definitions for the
* os_tls_xxx() macros. The OS header must separately define OS_DECL_TLS
* as appropriate for the local system.
*/
#ifdef OS_DECLARATIVE_TLS
#define os_tls_create(varname)
#define os_tls_delete(varname)
#define os_tls_get(typ, varname) varname
#define os_tls_set(varname, val) varname = (val)
#endif
/*
* Common TLS definitions - pthreads
*
* For pthreads systems without declarative TLS support in the compiler,
* the OS header can simply #define OS_PTHREAD_TLS to pick up the standard
* definitions below.
*/
#ifdef OS_PTHREAD_TLS
#include <pthread.h>
#define OS_DECL_TLS(typ, varname) pthread_key_t varname
#define os_tls_create(varname) pthread_key_create(&varname, NULL)
#define os_tls_delete(varname) pthread_key_delete(varname)
#define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname))
#define os_tls_set(varname, val) pthread_setspecific(varname, val)
#endif
/* ------------------------------------------------------------------------ */
/*
* <time.h> definitions.
*
* os_time() should act like Unix time(), returning the number of seconds
* elapsed since January 1, 1970 at midnight UTC.
*
* The original Unix <time.h> package defined time_t as a 32-bit signed
* int, and many subsequent C compilers on other platforms followed suit.
* A signed 32-bit time_t has the well-known year-2038 problem; some later
* C compilers tried to improve matters by using an unsigned 32-bit time_t
* instead, but for many purposes this is even worse since it can't
* represent any date before 1/1/1970. *Most* modern compilers solve the
* problem once and for all (for 300 billion years in either direction of
* 1/1/1970, anyway - enough to represent literally all of eternity in most
* current cosmological models) by defining time_t as a signed 64-bit int.
* But some compilers stubbornly stick to the old 32-bit time_t even in
* newer versions, for the sake of compatibility with older code that might
* be lax about mixing time_t's with ordinary int's. E.g., MSVC2003 does
* this. Fortunately, some of these compilers (such as MSVC2003 again)
* also define a parallel, transitional set of 64-bit time functions that
* you can use by replacing all references to the standard time_t and
* related names with the corresponding 64-bit names.
*
* We'd really like to use a 64-bit time_t wherever we can - the TADS
* release cycle can be a bit slow, and we don't want 2038 to sneak up on
* us and catch us unawares. So for those compilers that offer a choice of
* 32 or 64 bits, we'd like to select the 64 bit version. To facilitate
* this, we define covers here for the time.h types and functions that we
* use. On platforms where the regular time_t is already 64 bits, or where
* there's no 64-bit option at all, you can simply do nothing - the
* defaults defined here use the standard time_t typedef and functions, so
* that's what you'll get if you don't define these in the OS-specific
* headers for your platform. For compilers that provide both a 32-bit
* time_t and a 64-bit other_time_t, the OS headers should #define these
* macros in terms of those compiler-specific 64-bit names.
*/
#ifndef os_time_t
# define os_time_t time_t
# define os_gmtime(t) gmtime(t)
# define os_localtime(t) localtime(t)
# define os_time(t) time(t)
#endif
/*
* Initialize the time zone. This routine is meant to take care of any
* work that needs to be done prior to calling localtime() and other
* time-zone-dependent routines in the run-time library. For DOS and
* Windows, we need to call the run-time library routine tzset() to set up
* the time zone from the environment; most systems shouldn't need to do
* anything in this routine. It's sufficient to call this once during the
* process lifetime, since it's meant to perform static initialization that
* lasts as long as the process is running.
*/
#ifndef os_tzset
void os_tzset(void);
#endif
/*
* Higher-precision time. This retrieves the same time information as
* os_time() (i.e., the elapsed time since the standard Unix Epoch, January
* 1, 1970 at midnight UTC), but retrieves it with the highest precision
* available on the local system, up to nanosecond precision. If less
* precision is available, that's fine; just return the time to the best
* precision available, but expressed in terms of the number of
* nanoseconds. For example, if you can retrieve milliseconds, you can
* convert that to nanoseconds by multiplying by 1,000,000.
*
* On return, fills in '*seconds' with the number of whole seconds since
* the Epoch, and fills in '*nanoseconds' with the fractional portion,
* expressed in nanosceconds. Note that '*nanoseconds' is merely the
* fractional portion of the time, so 0 <= *nanoseconds < 1000000000.
*/
void os_time_ns(os_time_t *seconds, long *nanoseconds);
/*
* Get the local time zone name, as a location name in the IANA zoneinfo
* database. For example, locations using US Pacific Time should return
* "America/Los_Angeles".
*
* Returns true if successful, false if not. If the local operating system
* doesn't have a way to obtain this information, or if it's not available
* in the local machine's configuration, this returns false.
*
* The zoneinfo database is also known as the Olson or TZ (timezone)
* database; it's widely used on Unix systems as the definitive source of
* local time zone settings. See http://www.iana.org/time-zones for more
* information.
*
* On many Unix systems, the TZ environment variable contains the zoneinfo
* zone name when its first character is ':'. Windows uses a proprietary
* list of time zone names that can be mapped to zoneinfo names via a
* hand-coded list (such a list is maintained in the Unicode CLDR; our
* Windows implementation uses the CLDR list to generate the mapping).
* MacOS X uses zoneinfo keys directly; /etc/localtime is a link to the
* zoneinfo file for the local zone as set via the system preferences.
*
* os_tzset() must be invoked at some point before this routine is called.
*/
int os_get_zoneinfo_key(char *buf, size_t buflen);
/*
* Get a description of the local time zone. Fills in '*info' with the
* available information. Returns true on success, false on failure.
*
* See osstzprs.h/.c for a portable implementation of a parser for
* POSIX-style TZ strings. That can serve as a full implementation of this
* function for systems that use the POSIX TZ environment variable syntax
* to specify the timezone. (That routine simply parses a string from any
* source, so it can be used to parse the TZ syntax even on systems where
* the string comes from somewhere other than the TZ environment variable.)
*
* os_tzset() must be invoked at some point before this routine is called.
*
* The following two structures are used for the return information:
*
* os_tzrule_t - Timezone Rule structure. This describes a rule for an
* annual transition between daylight savings time and standard time in a
* time zone. Most timezones that have recurring standard/daylight changes
* require two of these rules, one for switching to daylight time in the
* spring and one for switching to standard time in the fall.
*
* os_tzinfo_t - Timezone Information structure. This describes a
* timezone's clock settings, name(s), and rules for recurring annual
* changes between standard time and daylight time, if applicable.
*/
struct os_tzrule_t
{
/*
* Day of year, 1-365, NEVER counting Feb 29; set to 0 if not used.
* Corresponds to the "J" format in Unix TZ strings. (Called "Julian
* day" in the POSIX docs, thus the "J", even though it's a bit of a
* misnomer.)(Because of the invariance of the mapping from J-number to
* date, this is just an obtuse way of specifying a month/day date.
* But even so, we'll let the OS layer relay this back to us in
* J-number format and count on the portable caller to work out the
* date, rather than foisting that work on each platform
* implementation.)
*/
int jday;
/*
* Day of year, 1-366, counting Feb 29 on leap years; set to 0 if not
* used; ignored if 'jday' is nonzero. This corresponds to the Julian
* day sans "J" in TZ strings (almost - that TZ format uses 0-365 as
* its range, so bump it up by one when parsing a TZ string). This
* format is even more obtuse than the J-day format, in that it doesn't
* even have an invariant month/day mapping (not after day 59, anyway -
* day 60 is either February 29 or March 1, depending on the leapness
* of the year, and every day after that is similarly conditional). As
* far as I can tell, no one uses this option, so I'm not sure why it
* exists. The zoneinfo source format doesn't have a way to represent
* it, which says to me that no one has ever used it in a statutory DST
* start/end date definition in the whole history of time zones around
* the world, since the whole history of time zones around the world is
* exactly what the zoneinfo database captures in exhaustive and
* painstaking detail. If anyone had ever used it in defining a time
* zone, zoneinfo would have an option for it. My guess is that it's a
* fossilized bug from some early C RTL that's been retained out of an
* abundance of caution vis-a-vis compatibility, and was entirely
* replaced in practice by the J-number format as soon as someone
* noticed the fiddly leap year behavior. But for the sake of
* completeness...
*/
int yday;
/*
* The month (1-12), week of the month, and day of the week (1-7 for
* Sunday to Saturday). Week 1 is the first week in which 'day'
* occurs, week 2 is the second, etc.; week 5 is the last occurrence of
* 'day' in the month. These fields are used for "second Sunday in
* March" types of rules. Set these to zero if they're not used;
* they're ignored in any case if 'jday' or 'yday' are non-zero.
*/
int month;
int week;
int day;
/* time of day, in seconds after midnight (e.g., 2AM is 120 == 2*60*60) */
int time;
};
struct os_tzinfo_t
{
/*
* The local offset from GMT, in seconds, for standard time and
* daylight time in this zone. These values are positive for zones
* east of GMT and negative for zones west: New York standard time
* (EST) is 5 hours west of GMT, so its offset is -5*60*60.
*
* Set both of these fields (if possible) regardless of whether
* standard or daylight time is currently in effect in the zone. The
* caller will select which offset to use based on the start/end rules,
* or based on the 'is_dst' flag if no rules are available.
*
* If it's only possible to determine the current wall clock offset, be
* it standard or daylight time, and it's not possible to determine the
* time difference between the two, simply set both of these to the
* current offset. This information isn't available from the standard
* C library, and many OS APIs also lack it.
*/
int32_t std_ofs;
int32_t dst_ofs;
/*
* The abbreviations for the local zone's standard time and daylight
* time, respectively, when displaying date/time values. E.g., "EST"
* and "EDT" for US Eastern Time. If the zone doesn't observe daylight
* time (it's on standard time year round), set dst_abbr to an empty
* string.
*
* As with std_ofs and dst_ofs, you can set both of these to the same
* string if it's only possible to determine the one that's currently
* in effect.
*/
char std_abbr[16];
char dst_abbr[16];
/*
* The ongoing rules for switching between daylight and standard time
* in this zone, if available. 'dst_start' is the date when daylight
* savings starts, 'dst_end' is the date when standard time resumes.
* Set all fields to 0 if the start/stop dates aren't available, or the
* zone is on standard time year round.
*/
struct os_tzrule_t dst_start;
struct os_tzrule_t dst_end;
/*
* True -> the zone is CURRENTLY on daylight savings time; false means
* it's currently on standard time.
*
* This is only used if the start/end rules aren't specified. In the
* absence of start/end rules, there's no way to know when the current
* standard/daylight phase ends, so we'll have to assume that the
* current mode is in effect permanently. In this case, the caller
* will use only be able to use the offset and abbreviation for the
* current mode and will have to ignore the other one.
*/
int is_dst;
};
int os_get_timezone_info(struct os_tzinfo_t *info);
/*
* Get the current system high-precision timer. This function returns a
* value giving the wall-clock ("real") time in milliseconds, relative to
* any arbitrary zero point. It doesn't matter what this value is relative
* to -- the only important thing is that the values returned by two
* different calls should differ by the number of actual milliseconds that
* have elapsed between the two calls. This might be the number of
* milliseconds since the computer was booted, since the current user
* logged in, since midnight of the previous night, since the program
* started running, since 1-1-1970, etc - it doesn't matter what the epoch
* is, so the implementation can use whatever's convenient on the local
* system.
*
* True millisecond precision isn't required. Each implementation should
* simply use the best precision available on the system. If your system
* doesn't have any kind of high-precision clock, you can simply use the
* time() function and multiply the result by 1000 (but see the note below
* about exceeding 32-bit precision).
*
* However, it *is* required that the return value be in *units* of
* milliseconds, even if your system clock doesn't have that much
* precision; so on a system that uses its own internal clock units, this
* routine must multiply the clock units by the appropriate factor to yield
* milliseconds for the return value.
*
* It is also required that the values returned by this function be
* monotonically increasing. In other words, each subsequent call must
* return a value that is equal to or greater than the value returned from
* the last call. On some systems, you must be careful of two special
* situations.
*
* First, the system clock may "roll over" to zero at some point; for
* example, on some systems, the internal clock is reset to zero at
* midnight every night. If this happens, you should make sure that you
* apply a bias after a roll-over to make sure that the value returned from
* this return continues to increase despite the reset of the system clock.
*
* Second, a 32-bit signed number can only hold about twenty-three days
* worth of milliseconds. While it seems unlikely that a TADS game would
* run for 23 days without a break, it's certainly reasonable to expect
* that the computer itself may run this long without being rebooted. So,
* if your system uses some large type (a 64-bit number, for example) for
* its high-precision timer, you may want to store a zero point the very
* first time this function is called, and then always subtract this zero
* point from the large value returned by the system clock. If you're
* using time(0)*1000, you should use this technique, since the result of
* time(0)*1000 will almost certainly not fit in 32 bits in most cases.
*/
long os_get_sys_clock_ms(void);
/* ------------------------------------------------------------------------ */
/*
* Hardware Configuration. Define the following functions appropriately
* for your hardware. For efficiency, these functions should be defined
* as macros if possible.
*
* Note that these hardware definitions are independent of the OS, at
* least to the extent that your OS can run on multiple types of
* hardware. So, rather than combining these definitions into your
* osxxx.h header file, we recommend that you put these definitions in a
* separate h_yyy.h header file, which can be configured into os.h with
* an appropriate "_M_yyy" preprocessor symbol. Refer to os.h for
* details of configuring the hardware include file.
*/
/*
* Round a size up to worst-case alignment boundary. For example, on a
* platform where the largest type must be aligned on a 4-byte boundary,
* this should round the value up to the next higher mutliple of 4 and
* return the result.
*/
/* size_t osrndsz(size_t siz); */
/*
* Round a pointer up to worst-case alignment boundary.
*/
/* void *osrndpt(void *ptr); */
/*
* Read an unaligned portable unsigned 2-byte value, returning an int
* value. The portable representation has the least significant byte
* first, so the value 0x1234 is represented as the byte 0x34, followed
* by the byte 0x12.
*
* The source value must be treated as unsigned, but the result is
* signed. This is significant on 32- and 64-bit platforms, because it
* means that the source value should never be sign-extended to 32-bits.
* For example, if the source value is 0xffff, the result is 65535, not
* -1.
*/
/* int osrp2(unsigned char *p); */
/*
* Read an unaligned portable signed 2-byte value, returning int. This
* differs from osrp2() in that this function treats the source value as
* signed, and returns a signed result; hence, on 32- and 64-bit
* platforms, the result must be sign-extended to the int size. For
* example, if the source value is 0xffff, the result is -1.
*/
/* int osrp2s(unsigned char *p); */
/*
* Write unsigned int to unaligned portable 2-byte value. The portable
* representation stores the low-order byte first in memory, so
* oswp2(0x1234) should result in storing a byte value 0x34 in the first
* byte, and 0x12 in the second byte.
*/
/* void oswp2(unsigned char *p, unsigned int i); */
/*
* Write signed int to unaligned portable 2-byte value. Negative values
* must be stored in two's complement notation. E.g., -1 is stored as
* FF.FF, -32768 is stored as 00.80 (little-endian).
*
* Virtually all modern hardware uses two's complement notation as the
* native representation, which makes this routine a trivial synonym of
* osrp2() (i.e., #define oswp2s(p,i) oswp2(p,i)). We distinguish the
* signed version on the extremely off chance that TADS is ever ported to
* wacky hardware with a different representation for negative integers
* (one's complement, sign bit, etc).
*/
/* void oswp2s(unsigned char *p, int i); */
/*
* Read an unaligned unsigned portable 4-byte value, returning long. The
* underlying value should be considered signed, and the result is signed.
* The portable representation stores the bytes starting with the least
* significant: the value 0x12345678 is stored with 0x78 in the first byte,
* 0x56 in the second byte, 0x34 in the third byte, and 0x12 in the fourth
* byte.
*/
/* unsigned long osrp4(unsigned char *p); */
/*
* Read an unaligned signed portable 4-byte value, returning long.
*/
/* long osrp4s(unsigned char *p); */
/*
* Write an unsigned long to an unaligned portable 4-byte value. The
* portable representation stores the low-order byte first in memory, so
* 0x12345678 is written to memory as 0x78, 0x56, 0x34, 0x12.
*/
/* void oswp4(unsigned char *p, unsigned long l); */
/*
* Write a signed long, using little-endian byte order and two's complement
* notation for negative numbers. This is a trivial synonym for oswp4()
* for all platforms with native two's complement arithmetic (which is
* virtually all modern platforms). See oswp2s() for more discussion.
*/
/* void oswp4s(unsigned char *p, long l); */
/*
* For convenience and readability, the 1-byte integer (signed and
* unsigned) equivalents of the above.
*/
#define osrp1(p) (*(unsigned char *)(p))
#define osrp1s(p) (*(signed char *)(p))
#define oswp1(p, b) (*(unsigned char *)(p) = (b))
#define oswp1s(p, b) (*(signed char *)(p) = (b))
/* ------------------------------------------------------------------------ */
/*
* varargs va_copy() extension.
*
* On some compilers, va_list is a reference type. This means that if a
* va_list value is passed to a function that uses va_arg() to step through
* the referenced arguments, the caller's copy of the va_list might be
* updated on return. This is problematic in cases where the caller needs
* to use the va_list again in another function call, since the va_list is
* no longer pointing to the first argument for the second call. C99 has a
* solution in the form of the va_copy() macro. Unfortunately, this isn't
* typically available in pre-C99 compilers, and isn't standard in *any*
* C++ version. We thus virtualize it here in a macro.
*
* os_va_copy() has identical semantics to C99 va_copy(). A matching call
* to os_va_copy_end() must be made for each call to os_va_copy() before
* the calling function returns; this has identical semantics to C99
* va_end().
*
* Because our semantics are identical to the C99 version, we provide a
* default definition here for compilers that define va_copy(). Platform
* headers must provide suitable definitions only if their compilers don't
* have va_copy(). We also provide a definition for GCC compilers that
* define the private __va_copy macro, which also has the same semantics.
*/
#ifdef va_copy
# define os_va_copy(dst, src) va_copy(dst, src)
# define os_va_copy_end(dst) va_end(dst)
#else
# if defined(__GNUC__) && defined(__va_copy)
# define os_va_copy(dst, src) __va_copy(dst, src)
# define os_va_copy_end(dst) va_end(dst)
# endif
#endif
/* ------------------------------------------------------------------------ */
/*
* Platform Identifiers. You must define the following macros in your
* osxxx.h header file:
*
* OS_SYSTEM_NAME - a string giving the system identifier. This string
* must contain only characters that are valid in a TADS identifier:
* letters, numbers, and underscores; and must start with a letter or
* underscore. For example, on MS-DOS, this string is "MSDOS".
*
* OS_SYSTEM_LDESC - a string giving the system descriptive name. This
* is used in messages displayed to the user. For example, on MS-DOS,
* this string is "MS-DOS".
*/
/* ------------------------------------------------------------------------ */
/*
* Message Linking Configuration. You should #define ERR_LINK_MESSAGES
* in your osxxx.h header file if you want error messages linked into
* the application. Leave this symbol undefined if you want an external
* message file.
*/
/* ------------------------------------------------------------------------ */
/*
* Program Exit Codes. These values are used for the argument to exit()
* to conform to local conventions. Define the following values in your
* OS-specific header:
*
* OSEXSUCC - successful completion. Usually defined to 0.
*. OSEXFAIL - failure. Usually defined to 1.
*/
/* ------------------------------------------------------------------------ */
/*
* Basic memory management interface. These functions are merely
* documented here, but no prototypes are defined, because most
* platforms #define macros for these functions and types, mapping them
* to malloc or other system interfaces.
*/
/*
* Theoretical maximum osmalloc() size. This may be less than the
* capacity of the argument to osmalloc() on some systems. For example,
* on segmented architectures (such as 16-bit x86), memory is divided into
* segments, so a single memory allocation can allocate only a subset of
* the total addressable memory in the system. This value thus specifies
* the maximum amount of memory that can be allocated in one chunk.
*
* Note that this is an architectural maximum for the hardware and
* operating system. It doesn't have anything to do with the total amount
* of memory actually available at run-time.
*
* #define OSMALMAX to a constant long value with theoretical maximum
* osmalloc() argument value. For a platform with a flat (unsegmented)
* 32-bit memory space, this is usually 0xffffffff; for 16-bit platforms,
* this is usually 0xffff.
*/
/* #define OSMALMAX 0xffffffff */
/*
* Allocate a block of memory of the given size in bytes. The actual
* allocation may be larger, but may be no smaller. The block returned
* should be worst-case aligned (i.e., suitably aligned for any type).
* Return null if the given amount of memory is not available.
*/
/* void *osmalloc(size_t siz); */
/*
* Free memory previously allocated with osmalloc().
*/
/* void osfree(void *block); */
/*
* Reallocate memory previously allocated with osmalloc() or
* osrealloc(), changing the block's size to the given number of bytes.
* If necessary, a new block at a different address can be allocated, in
* which case the data from the original block is copied (the lesser of
* the old block size and the new size is copied) to the new block, and
* the original block is freed. If the new size is less than the old
* size, this need not do anything at all, since the returned block can
* be larger than the new requested size. If the block cannot be
* enlarged to the requested size, return null.
*/
/* void *osrealloc(void *block, size_t siz); */
/* ------------------------------------------------------------------------ */
/*
* Basic file I/O interface. These functions are merely documented here,
* but no prototypes are defined, because most platforms #define macros for
* these functions and types, mapping them to stdio or other system I/O
* interfaces.
*
* When writing a file, writes might or might not be buffered in
* application memory; this is up to the OS implementation, which can
* perform buffering according to local conventions and what's most
* efficient. However, it shouldn't make any difference to the caller
* whether writes are buffered or not - the OS implementation must take
* care that any buffering is invisible to the app. (Porters: note that
* the basic C stdio package has the proper behavior here, so you'll get
* the correct semantics if you use a simple stdio implementation.)
*
* Write buffering might be visible to *other* apps, though. In
* particular, another process might not see data written to a file (with
* osfwb(), os_fprint(), etc) immediately, since the write functions might
* hold the written bytes in an internal memory buffer rather than sending
* them to the OS. Any internal buffers are guaranteed to be flushed to
* the OS upon calling osfcls() or osfflush(). Note that it's never
* *necessary* to call osfflush(), because buffered data will always be
* flushed on closing the file with osfcls(). However, if you want other
* apps to be able to see updates immediately, you can use osfflush() to
* ensure that buffers are flushed to a file before you close it.
*
* You can also use osfflush() to check for buffered write errors. When
* you use osfwb() or other write functions to write data, they will return
* a success indication even if the data was only copied into a buffer.
* This means that a write that appeared to succeed might actually fail
* later, when the buffer is flushed. The only way to know for sure is to
* explicitly flush buffers using osfflush(), and check the result code.
* If the original write function and a subsequent osfflush() *both* return
* success indications, then the write has definitely succeeded.
*/
/*
* Define the following values in your OS header to indicate local
* file/path syntax conventions:
*
* OSFNMAX - integer indicating maximum length of a filename
*
* OSPATHCHAR - character giving the normal path separator character
*. OSPATHALT - string giving other path separator characters
*. OSPATHURL - string giving path separator characters for URL conversions
*. OSPATHSEP - directory separator for PATH-style environment variables
*. OSPATHPWD - string giving the special path representing the current
*. working directory; for Unix or Windows, this is "."
*
* OSPATHURL is a little different: this specifies the characters that
* should be converted to URL-style separators when converting a path from
* local notation to URL notation. This is usually the same as the union
* of OSPATHCHAR and OSPATHALT, but need not be; for example, on DOS, the
* colon (':') is a path separator for most purposes, but is NOT a path
* character for URL conversions.
*/
/*
* Define the type osfildef as the appropriate file handle structure for
* your osfxxx functions. This type is always used as a pointer, but
* the value is always obtained from an osfopxxx call, and is never
* synthesized by portable code, so you can use essentially any type
* here that you want.
*
* For platforms that use C stdio functions to implement the osfxxx
* functions, osfildef can simply be defined as FILE.
*/
/* typedef FILE osfildef; */
/*
* File types.
*
* These are symbols of the form OSFTxxxx defining various content types,
* somewhat aking to MIME types. These were mainly designed for the old
* Mac OS (versions up to OS 9), where the file system stored a type tag
* with each file's metadata. The type tags were used for things like
* filtering file selector dialogs and setting file-to-app associations in
* the desktop shell.
*
* Our OSFTxxx symbols are abstract file types that we define, for types
* used within the TADS family of applications. They give us a common,
* cross-platform reference point for each type we use. Each port where
* file types are meaningful then maps our abstract type IDs to the
* corresponding port-specific type IDs. In practice, this has never been
* used anywhere other than the old Mac OS ports; in fact, it's not even
* used in the modern Mac OS (OS X and later), since Apple decided to stop
* fighting the tide and start using filename suffixes for this sort of
* tagging, like everyone else always has.
*
* For the list of file types, see osifctyp.h
*/
/*
* Local newline convention.
*
* Because of the pernicious NIH ("Not Invented Here") cultures of the
* major technology vendors, basically every platform out there has its own
* unique way of expressing newlines in text files. Unix uses LF (ASCII
* 10); Mac uses CR (ASCII 13); DOS and Windows use CR-LF pairs. In the
* past there were heaven-only-knows how many other conventions in use, but
* fortunately these three have the market pretty well locked up at this
* point. But we do still have to worry about these three.
*
* Our strategy on input is to be open to just about anything whenever
* possible. So, when we're reading something that we believe to be a text
* file, we'll treat all of these as line endings: CR, LF, CR-LF, and
* LF-CR. It's pretty safe to do this; if we have a CR and LF occurring
* adjacently, it's almost certain that they're intended to be taken
* together as a single newline sequence. Likewise, if there's a lone CR
* or LF, it's rare for it to mean anything other than a newline.
*
* On output, though, we can't be as loose. The problem is that other
* applications on our big three platforms *don't* tend to aim for the same
* flexibility we do on input: other apps usually expect exactly the local
* conventions on input, and don't always work well if they don't get it.
* So it's important that when we're writing a text file, we write newlines
* in the local convention. This means that we sometimes need to know what
* the local convention actually is. That's where this definition comes
* in.
*
* Each port must define OS_NEWLINE_SEQ as an ASCII string giving the local
* newline sequence to write on output. For example, DOS defines it as
* "\r\n" (CR-LF). Always define it as a STRING (not a character
* constant), even if it's only one character long.
*
* (Note that some compilers use wacky mappings for \r and \n. Some older
* Mac compilers, for example, defined \n as CR and \r as LF, because of
* the Mac convention where newline is represented as CR in a text file.
* If there's any such variability on your platform, you can always use the
* octal codes to be unambiguous: \012 for LF and \015 for CR.)
*/
/* #define OS_NEWLINE_SEQ "\r\n" */
/*
* Open text file for reading. This opens the file with read-only access;
* we're not allowed to write to the file using this handle. Returns NULL
* on error.
*
* A text file differs from a binary file in that some systems perform
* translations to map between C conventions and local file system
* conventions; for example, on DOS, the stdio library maps the DOS CR-LF
* newline convention to the C-style '\n' newline format. On many systems
* (Unix, for example), there is no distinction between text and binary
* files.
*
* On systems that support file sharing and locking, this should open the
* file in "shared read" mode - this means that other processes are allowed
* to simultaneously read from the file, but no other processs should be
* allowed to write to the file as long as we have it open. If another
* process already has the file open with write access, this routine should
* return failure, since we can't take away the write privileges the other
* process already has and thus we can't guarantee that other processes
* won't write to the file while we have it open.
*/
/* osfildef *osfoprt(const char *fname, os_filetype_t typ); */
/*
* Open a text file for "volatile" reading: we open the file with read-only
* access, and we explicitly accept instability in the file's contents due
* to other processes simultaneously writing to the file. On systems that
* support file sharing and locking, the file should be opened in "deny
* none" mode, meaning that other processes can simultaneously open the
* file for reading and/or writing even while have the file open.
*/
/* osfildef *osfoprtv(const char *fname, os_filetype_t typ); */
/*
* Open text file for writing; returns NULL on error. If the file already
* exists, this truncates the file to zero length, deleting any existing
* contents.