1 /*
2  *   Copyright (c) 1991, 2002 Michael J. Roberts.  All Rights Reserved.
3  *
4  *   Please see the accompanying license file, LICENSE.TXT, for information
5  *   on using and copying this software.
6  */
7 /*
8 Name
9   t3std.h - standard definitions
10 Function
11   Various standard definitions
12 Notes
13   None
14 Modified
15   10/17/98 MJRoberts  - creation (from TADS 2 lib.h)
16 */
17 
18 #ifndef T3_STD_INCLUDED
19 #define T3_STD_INCLUDED
20 
21 #include <stddef.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <ctype.h>
26 
27 #include "os.h"
28 
29 
30 /* short-hand for various types */
31 #ifndef OS_UCHAR_DEFINED
32 typedef unsigned char  uchar;
33 #endif
34 
35 #ifndef OS_USHORT_DEFINED
36 typedef unsigned short ushort;
37 #endif
38 
39 #ifndef OS_UINT_DEFINED
40 typedef unsigned int   uint;
41 #endif
42 
43 #ifndef OS_ULONG_DEFINED
44 typedef unsigned long  ulong;
45 #endif
46 
47 /* maximum/minimum portable values for various types */
48 #define ULONGMAXVAL   0xffffffffUL
49 #define USHORTMAXVAL  0xffffU
50 #define UCHARMAXVAL   0xffU
51 #define SLONGMAXVAL   0x7fffffffL
52 #define SSHORTMAXVAL  0x7fff
53 #define SCHARMAXVAL   0x7f
54 #define SLONGMINVAL   (-(0x7fffffff)-1)
55 #define SSHORTMINVAL  (-(0x7fff)-1)
56 #define SCHARMINVAL   (-(0x7f)-1)
57 
58 
59 /*
60  *   Text character
61  */
62 typedef char textchar_t;
63 
64 /*
65  *   16-bit signed/unsigned integer types
66  */
67 typedef short int16;
68 typedef unsigned short uint16;
69 
70 /*
71  *   32-bit signed/unsigned integer types
72  */
73 typedef long int32;
74 typedef unsigned long uint32;
75 
76 /* unsigned byte */
77 typedef unsigned char uchar;
78 
79 
80 /* clear a struture */
81 #define CLRSTRUCT(x) memset(&(x), 0, (size_t)sizeof(x))
82 #define CPSTRUCT(dst,src) memcpy(&(dst), &(src), (size_t)sizeof(dst))
83 
84 
85 /* TRUE and FALSE */
86 #ifndef TRUE
87 # define TRUE 1
88 #endif /* TRUE */
89 #ifndef FALSE
90 # define FALSE 0
91 #endif /* FALSE */
92 
93 
94 /* bitwise operations */
95 #define bit(va, bt) ((va) & (bt))
96 #define bis(va, bt) ((va) |= (bt))
97 #define bic(va, bt) ((va) &= ~(bt))
98 
99 
100 /* conditionally compile code if debugging is enabled */
101 #ifdef DEBUG
102 # define IF_DEBUG(x) x
103 #else /* DEBUG */
104 # define IF_DEBUG(x)
105 #endif /* DEBUG */
106 
107 /* offset within a structure of a member of the structure */
108 #ifndef offsetof
109 # define offsetof(s_name, m_name) (size_t)&(((s_name *)0)->m_name)
110 #endif /* offsetof */
111 
112 /* ------------------------------------------------------------------------ */
113 /*
114  *   Allocate space for a null-terminated string and save a copy of the
115  *   string
116  */
117 char *lib_copy_str(const char *str);
118 char *lib_copy_str(const char *str, size_t len);
119 
120 /*
121  *   allocate space for a string of a given length; we'll add in space for
122  *   a null terminator
123  */
124 char *lib_alloc_str(size_t len);
125 
126 /*
127  *   Free a string previously allocated with lib_copy_str() or
128  *   lib_alloc_str()
129  */
130 void lib_free_str(char *buf);
131 
132 /* ------------------------------------------------------------------------ */
133 /*
134  *   Compare two strings, ignoring differences in whitespace between the
135  *   strings.  Returns true if the strings are equal (other than
136  *   whitespace, false if not.
137  *
138  *   Note that we do not ignore the *presence* of whitespace; we only
139  *   ignore differences in the amount of whitespace.  For example, "login"
140  *   does not equal "log_in" (underscore = whitespace for these examples
141  *   only, to emphasize the spacing), because the first lacks whitespace
142  *   where the second has it; but "log_in" equals "log___in", because both
143  *   strings have whitespace, albeit in different amounts, in the same
144  *   place, and are otherwise the same.
145  */
146 int lib_strequal_collapse_spaces(const char *a, size_t a_len,
147                                  const char *b, size_t b_len);
148 
149 
150 /* ------------------------------------------------------------------------ */
151 /*
152  *   Find a version suffix in an identifier string.  A version suffix
153  *   starts with the given character.  If we don't find the character,
154  *   we'll return the default version suffix.  In any case, we'll set
155  *   name_len to the length of the name portion, excluding the version
156  *   suffix and its leading separator.
157  *
158  *   For example, with a '/' suffix, a versioned name string would look
159  *   like "tads-gen/030000" - the name is "tads_gen" and the version is
160  *   "030000".
161  */
162 const char *lib_find_vsn_suffix(const char *name_string, char suffix_char,
163                                 const char *default_vsn, size_t *name_len);
164 
165 
166 /* ------------------------------------------------------------------------ */
167 /*
168  *   Unicode-compatible character classification functions.  These
169  *   functions accept any Unicode character, but classify all non-ASCII
170  *   characters in the Unicode character set as unknown; hence,
171  *   is_digit(ch) will always return false for any non-ASCII character,
172  *   even if the character is considered a digit in the Unicode character
173  *   set, and to_upper(ch) will return ch for any non-ASCII character,
174  *   even if the character has a case conversion defined in the Unicode
175  *   set.
176  *
177  *   Use the t3_is_xxx() and t3_to_xxx() functions defined vmuni.h for
178  *   classifications and conversions that operate over the entire Unicode
179  *   character set.
180  */
181 
182 /* determine if a character is an ASCII character */
is_ascii(wchar_t c)183 inline int is_ascii(wchar_t c) { return (((unsigned int)c) <= 127); }
184 
185 /* determine if a character is an ASCII space */
is_space(wchar_t c)186 inline int is_space(wchar_t c) { return (is_ascii(c) && isspace((char)c)); }
187 
188 /* determine if a character is an ASCII alphabetic character */
is_alpha(wchar_t c)189 inline int is_alpha(wchar_t c) { return (is_ascii(c) && isalpha((char)c)); }
190 
191 /* determine if a character is an ASCII numeric character */
is_digit(wchar_t c)192 inline int is_digit(wchar_t c) { return (is_ascii(c) && isdigit((char)c)); }
193 
194 /* determine if a character is an ASCII octal numeric character */
is_odigit(wchar_t c)195 inline int is_odigit(wchar_t c)
196     { return (is_ascii(c) && isdigit((char)c) && c <= '7'); }
197 
198 /* determine if a character is an ASCII hex numeric character */
is_xdigit(wchar_t c)199 inline int is_xdigit(wchar_t c)
200 {
201     return (is_ascii(c)
202             && (isdigit((char)c)
203                 || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')));
204 }
205 
206 /* get the numeric value of a decimal digit character */
value_of_digit(wchar_t c)207 inline int value_of_digit(wchar_t c)
208 {
209     return (int)(c - '0');
210 }
211 
212 /* get the numeric value of an octal numeric character */
value_of_odigit(wchar_t c)213 inline int value_of_odigit(wchar_t c)
214 {
215     return (int)(c - '0');
216 }
217 
218 /* get the numeric value of a hex numeric character */
value_of_xdigit(wchar_t c)219 inline int value_of_xdigit(wchar_t c)
220 {
221     /*
222      *   since our internal characters are always in unicode, we can take
223      *   advantage of the order of unicode characters to reduce the number
224      *   of comparisons we must make here
225      */
226     return (int)(c >= 'a'
227                  ? c - 'a' + 10
228                  : c >= 'A' ? c - 'A' + 10
229                             : c - '0');
230 }
231 
232 /* determine if a character is a symbol initial character */
is_syminit(wchar_t c)233 inline int is_syminit(wchar_t c)
234 {
235     /* underscores and alphabetic characters can start symbols */
236     return (is_ascii(c) && (c == '_' || isalpha((char)c)));
237 }
238 
239 /* determine if a character is a symbol non-initial character */
is_sym(wchar_t c)240 inline int is_sym(wchar_t c)
241 {
242     /* underscores, alphabetics, and digits can be in symbols */
243     return (is_ascii(c) && (c == '_' || isalpha((char)c)
244                             || isdigit((char)c)));
245 }
246 
247 /* determine if a character is ASCII lower-case */
is_lower(wchar_t c)248 inline int is_lower(wchar_t c) { return (is_ascii(c) && islower((char)c)); }
249 
250 /* convert ASCII lower-case to upper-case */
to_upper(wchar_t c)251 inline wchar_t to_upper(wchar_t c)
252 {
253     return (is_ascii(c) ? toupper((char)c) : c);
254 }
255 
256 /* ------------------------------------------------------------------------ */
257 /*
258  *   sprintf and vsprintf replacements.  These versions provide subsets of
259  *   the full 'printf' format capabilities, but check for buffer overflow,
260  *   which the standard library's sprintf functions do not.
261  */
262 void t3sprintf(char *buf, size_t buflen, const char *fmt, ...);
263 void t3vsprintf(char *buf, size_t buflen, const char *fmt, va_list args);
264 
265 
266 /* ------------------------------------------------------------------------ */
267 /*
268  *   Basic heap allocation functions.  We don't call malloc and free
269  *   directly, but use our own cover functions; when we compile the system
270  *   for debugging, we use diagnostic memory allocators so that we can more
271  *   easily find memory mismanagement errors (such as leaks, multiple
272  *   deletes, and use after deletion).
273  */
274 
275 #ifdef T3_DEBUG
276 
277 /*
278  *   Compiling in debug mode - use our diagnostic heap functions.
279  *   Override C++ operators new, new[], delete, and delete[] as well, so
280  *   that we can handle those allocations through our diagnostic heap
281  *   manager, too.
282  */
283 
284 void *t3malloc(size_t siz);
285 void *t3realloc(void *oldptr, size_t siz);
286 void  t3free(void *ptr);
287 
288 void *operator new(size_t siz);
289 void *operator new[](size_t siz);
290 void operator delete(void *ptr);
291 void operator delete[](void *ptr);
292 
293 /*
294  *   List all allocated memory blocks - displays heap information on stdout.
295  *   This can be called at program termination to detect un-freed memory
296  *   blocks, the existence of which could indicate a memory leak.
297  *
298  *   If cb is provided, we'll display output through the given callback
299  *   function; otherwise we'll display the output directly on stderr.
300  */
301 void t3_list_memory_blocks(void (*cb)(const char *msg));
302 
303 #else /* T3_DEBUG */
304 
305 /*
306  *   Compiling in production mode - use the system memory allocators
307  *   directly.  Note that we go through the osmalloc() et. al. functions
308  *   rather than calling malloc() directly, so that individual ports can
309  *   use customized memory management where necessary or desirable.
310  */
311 #define t3malloc(siz)          (::osmalloc(siz))
312 #define t3realloc(ptr, siz)    (::osrealloc(ptr, siz))
313 #define t3free(ptr)            (::osfree(ptr))
314 
315 #define t3_list_memory_blocks(cb)
316 
317 #endif /* T3_DEBUG */
318 
319 
320 /* ------------------------------------------------------------------------ */
321 /*
322  *   A simple array list type.  We keep an underlying array of elements,
323  *   automatically expanding the underlying array as needed to accomodate new
324  *   elements.
325  */
326 
327 /* array list element type codes */
328 #define ARRAY_LIST_ELE_INT   1
329 #define ARRAY_LIST_ELE_LONG  2
330 #define ARRAY_LIST_ELE_PTR   3
331 
332 /* array list element - we can store various types here */
333 union array_list_ele_t
334 {
array_list_ele_t(int i)335     array_list_ele_t(int i) { intval = i; }
array_list_ele_t(long l)336     array_list_ele_t(long l) { longval = l; }
array_list_ele_t(void * p)337     array_list_ele_t(void *p) { ptrval = p; }
338 
339     int intval;
340     long longval;
341     void *ptrval;
342 
343     /* compare to a given value for equality */
equals(array_list_ele_t other,int typ)344     int equals(array_list_ele_t other, int typ)
345     {
346         return ((typ == ARRAY_LIST_ELE_INT && intval == other.intval)
347                 || (typ == ARRAY_LIST_ELE_LONG && longval == other.longval)
348                 || (typ == ARRAY_LIST_ELE_PTR && ptrval == other.ptrval));
349     }
350 };
351 
352 /*
353  *   The array list type
354  */
355 class CArrayList
356 {
357 public:
CArrayList()358     CArrayList()
359     {
360         /* we have nothing allocated yet */
361         arr_ = 0;
362         cnt_ = 0;
363 
364         /* use default initial size and increment */
365         alloc_ = 16;
366         inc_siz_ = 16;
367     }
CArrayList(size_t init_cnt,size_t inc_siz)368     CArrayList(size_t init_cnt, size_t inc_siz)
369     {
370         /* we have nothing allocated yet */
371         arr_ = 0;
372         cnt_ = 0;
373 
374         /* remember the initial size and increment */
375         alloc_ = init_cnt;
376         inc_siz_ = inc_siz;
377     }
378 
~CArrayList()379     virtual ~CArrayList()
380     {
381         /* delete our underlying array */
382         free_mem(arr_);
383     }
384 
385     /* get the number of elements in the array */
get_count()386     size_t get_count() const { return cnt_; }
387 
388     /* get the element at the given index (no error checking) */
get_ele_int(size_t idx)389     int get_ele_int(size_t idx) const { return arr_[idx].intval; }
get_ele_long(size_t idx)390     long get_ele_long(size_t idx) const { return arr_[idx].longval; }
get_ele_ptr(size_t idx)391     void *get_ele_ptr(size_t idx) const { return arr_[idx].ptrval; }
392 
393     /* find an element's index; returns -1 if not found */
find_ele(int i)394     int find_ele(int i) const
395         { return find_ele(array_list_ele_t(i), ARRAY_LIST_ELE_INT); }
find_ele(long l)396     int find_ele(long l) const
397         { return find_ele(array_list_ele_t(l), ARRAY_LIST_ELE_LONG); }
find_ele(void * p)398     int find_ele(void *p) const
399         { return find_ele(array_list_ele_t(p), ARRAY_LIST_ELE_PTR); }
400 
401     /* find an element's index; returns -1 if not found */
find_ele(array_list_ele_t ele,int typ)402     int find_ele(array_list_ele_t ele, int typ) const
403     {
404         size_t i;
405         array_list_ele_t *p;
406 
407         /* scan for the element */
408         for (i = 0, p = arr_ ; i < cnt_ ; ++i, ++p)
409         {
410             /* if this is the element, return the index */
411             if (p->equals(ele, typ))
412                 return (int)i;
413         }
414 
415         /* didn't find it */
416         return -1;
417     }
418 
419     /* add a new element */
add_ele(int i)420     void add_ele(int i) { add_ele(array_list_ele_t(i)); }
add_ele(long l)421     void add_ele(long l) { add_ele(array_list_ele_t(l)); }
add_ele(void * p)422     void add_ele(void *p) { add_ele(array_list_ele_t(p)); }
423 
424     /* add a new element */
add_ele(array_list_ele_t ele)425     void add_ele(array_list_ele_t ele)
426     {
427         /* expand the array if necessary */
428         if (arr_ == 0)
429         {
430             /* we don't have an array yet, so allocate at the initial size */
431             init();
432         }
433         if (cnt_ >= alloc_)
434         {
435             /* allocate at the new size */
436             arr_ = (array_list_ele_t *)
437                    realloc_mem(arr_, alloc_ * sizeof(arr_[0]),
438                                (alloc_ + inc_siz_) * sizeof(arr_[0]));
439 
440             /* remember the new size */
441             alloc_ += inc_siz_;
442         }
443 
444         /* add the new element */
445         arr_[cnt_++] = ele;
446     }
447 
448     /* remove one element by value; returns true if found, false if not */
remove_ele(int i)449     void remove_ele(int i)
450         { remove_ele(array_list_ele_t(i), ARRAY_LIST_ELE_INT); }
remove_ele(long l)451     void remove_ele(long l)
452         { remove_ele(array_list_ele_t(l), ARRAY_LIST_ELE_LONG); }
remove_ele(void * p)453     void remove_ele(void *p)
454         { remove_ele(array_list_ele_t(p), ARRAY_LIST_ELE_PTR); }
455 
456     /* remove one element by value; returns true if found, false if not */
remove_ele(array_list_ele_t ele,int typ)457     int remove_ele(array_list_ele_t ele, int typ)
458     {
459         size_t i;
460         array_list_ele_t *p;
461 
462         /* scan for the element */
463         for (i = 0, p = arr_ ; i < cnt_ ; ++i, ++p)
464         {
465             /* if this is the element, remove it */
466             if (p->equals(ele, typ))
467             {
468                 /* remove the element at this index */
469                 remove_ele(i);
470 
471                 /* indicate that we found the element */
472                 return TRUE;
473             }
474         }
475 
476         /* we didn't find the element */
477         return FALSE;
478     }
479 
480     /* remove the element at the given index */
remove_ele(size_t idx)481     void remove_ele(size_t idx)
482     {
483         array_list_ele_t *p;
484 
485         /* move each following element down one slot */
486         for (p = arr_ + idx, ++idx ; idx < cnt_ ; ++idx, ++p)
487             *p = *(p + 1);
488 
489         /* reduce the in-use count */
490         --cnt_;
491     }
492 
493     /* clear the entire list */
clear()494     void clear() { cnt_ = 0; }
495 
496 protected:
497     /*
498      *   Initialize.  This is called to set up the array at the initial size,
499      *   stored in alloc_, when we first need memory.  Note that we defer
500      *   this until we actually need the memory for two reasons.  First, we
501      *   can't call it from the constructor, because the vtable won't be
502      *   built at construction, and we need to call the virtual alloc_mem().
503      *   Second, by waiting, we ensure that we won't allocate any memory if
504      *   our list is never actually needed.
505      */
init()506     void init()
507     {
508         /* allocate the array */
509         arr_ = (array_list_ele_t *)alloc_mem(alloc_ * sizeof(arr_[0]));
510     }
511 
512     /* memory management */
alloc_mem(size_t siz)513     virtual void *alloc_mem(size_t siz)
514         { return t3malloc(siz); }
realloc_mem(void * p,size_t oldsiz,size_t newsiz)515     virtual void *realloc_mem(void *p, size_t oldsiz, size_t newsiz)
516         { return t3realloc(p, newsiz); }
free_mem(void * p)517     virtual void free_mem(void *p)
518         { t3free(p); }
519 
520     /* our array of elements */
521     array_list_ele_t *arr_;
522 
523     /* number of elements allocated */
524     size_t alloc_;
525 
526     /* number of elements currently in use */
527     size_t cnt_;
528 
529     /* increment size */
530     size_t inc_siz_;
531 };
532 
533 #endif /* T3_STD_INCLUDED */
534 
535