genwrap.c 24.6 KB
Newer Older
1
2
3
4
5
6
/* General cross-platform development wrappers */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8
 *																			*
9
10
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
11
12
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
13
14
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include <string.h>     /* strlen() */
35
#include <stdarg.h>		/* vsnprintf() */
36
37
38
#include <stdlib.h>		/* RAND_MAX */
#include <fcntl.h>		/* O_NOCTTY */
#include <time.h>		/* clock() */
39
#include <errno.h>		/* errno */
40
#include <ctype.h>		/* toupper/tolower */
41
#include <limits.h>		/* CHAR_BIT */
42
#include <math.h>		/* fmod */
rswindell's avatar
rswindell committed
43

44
#if defined(__unix__)
45
	#include <sys/ioctl.h>		/* ioctl() */
46
	#include <sys/utsname.h>	/* uname() */
cyan's avatar
cyan committed
47
	#include <signal.h>
48
#elif defined(_WIN32)
deuce's avatar
deuce committed
49
50
	#include <windows.h>
	#include <lm.h>		/* NetWkstaGetInfo() */
51
#endif
52
53

#include "genwrap.h"	/* Verify prototypes */
rswindell's avatar
rswindell committed
54
#include "xpendian.h"	/* BYTE_SWAP */
55

56
57
58
/****************************************************************************/
/* Used to replace snprintf()  guarantees to terminate.			  			*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
59
int safe_snprintf(char *dst, size_t size, const char *fmt, ...)
60
61
62
63
64
65
66
{
	va_list argptr;
	int     numchars;

	va_start(argptr,fmt);
	numchars= vsnprintf(dst,size,fmt,argptr);
	va_end(argptr);
deuce's avatar
deuce committed
67
68
	if (size > 0)
		dst[size-1]=0;
69
70
#ifdef _MSC_VER
	if(numchars==-1)
71
		numchars=strlen(dst);
72
#endif
73
	if(numchars>=(int)size && numchars>0)
74
		numchars=size-1;
75
76
77
	return(numchars);
}

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#ifdef NEEDS_STRLCPY
size_t strlcpy(char *dst, const char *src, size_t size)
{
	size_t i;

	if(size < 1)
		return 0;

	for(i = 0; src[i] != '\0'; i++) {
		if(i < (size - 1))
			*(dst++) = src[i];
	}
	*dst = 0;
	return i;
}
#endif

95
#ifdef _WIN32
96
/****************************************************************************/
97
/* Case insensitive version of strstr()	- currently heavy-handed			*/
98
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
99
char* strcasestr(const char* haystack, const char* needle)
100
{
101
102
103
104
105
106
107
108
	const char* p;
	size_t len = strlen(needle);

	for(p = haystack; *p != '\0'; p++) {
		if(strnicmp(p, needle, len) == 0)
			return (char*)p;
	}
	return NULL;
109
110
111
}
#endif

112
/****************************************************************************/
113
114
/* Return last character of string											*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
115
char* lastchar(const char* str)
116
117
118
119
120
121
{
	size_t	len;

	len = strlen(str);

	if(len)
122
		return((char*)&str[len-1]);
123

124
	return((char*)str);
125
126
127
}

/****************************************************************************/
128
129
/* Return character value of C-escaped (\) character						*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
130
char c_unescape_char(char ch)
131
132
{
	switch(ch) {
133
		case 'e':	return(ESC);	/* non-standard */
134
135
136
137
138
139
140
141
142
143
144
145
		case 'a':	return('\a');
		case 'b':	return('\b');
		case 'f':	return('\f');
		case 'n':	return('\n');
		case 'r':	return('\r');
		case 't':	return('\t');
		case 'v':	return('\v');
	}
	return(ch);
}

/****************************************************************************/
146
/* Return character value of C-escaped (\) character literal sequence		*/
147
/* supports \x## (hex) and \### sequences (for octal or decimal)			*/
148
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
149
char c_unescape_char_ptr(const char* str, char** endptr)
150
151
152
{
	char	ch;

153
154
155
156
	if(*str == 'x') {
		int digits = 0;		// \x## for hexadecimal character literals (only 2 digits supported)
		++str;
		ch = 0;
157
		while(digits < 2 && IS_HEXDIGIT(*str)) {
158
159
160
161
162
163
			ch *= 0x10;	
			ch += HEX_CHAR_TO_INT(*str);
			str++;
			digits++;
		}
#ifdef C_UNESCAPE_OCTAL_SUPPORT
164
	} else if(IS_OCTDIGIT(*str)) {
165
166
		int digits = 0;		// \### for octal character literals (only 3 digits supported)
		ch = 0;
167
		while(digits < 3 && IS_OCTDIGIT(*str)) {
168
169
170
171
172
173
			ch *= 8;
			ch += OCT_CHAR_TO_INT(*str);
			str++;
			digits++;
		}
#else
174
175
	} else if(IS_DIGIT(*str)) {
		int digits = 0;		// \### for decimal character literals (only 3 digits supported)
176
		ch = 0;
177
		while(digits < 3 && IS_DIGIT(*str)) {
178
179
180
181
182
183
184
			ch *= 10;
			ch += DEC_CHAR_TO_INT(*str);
			str++;
			digits++;
		}
#endif
	 } else
185
		ch=c_unescape_char(*(str++));
186

187
188
189
190
	if(endptr!=NULL)
		*endptr=(char*)str;

	return ch;
191
192
193
194
195
}

/****************************************************************************/
/* Unescape a C string, in place											*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
196
char* c_unescape_str(char* str)
197
198
199
200
201
202
203
204
205
206
207
208
209
{
	char	ch;
	char*	buf;
	char*	src;
	char*	dst;

	if(str==NULL || (buf=strdup(str))==NULL)
		return(NULL);

	src=buf;
	dst=str;
	while((ch=*(src++))!=0) {
		if(ch=='\\')	/* escape */
210
			ch=c_unescape_char_ptr(src,&src);
211
212
213
214
215
216
217
		*(dst++)=ch;
	}
	*dst=0;
	free(buf);
	return(str);
}

Rob Swindell's avatar
Rob Swindell committed
218
char* c_escape_char(char ch)
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
{
	switch(ch) {
		case 0:		return("\\x00");
		case 1:		return("\\x01");
		case ESC:	return("\\e");		/* non-standard */
		case '\a':	return("\\a");
		case '\b':	return("\\b");
		case '\f':	return("\\f");
		case '\n':	return("\\n");
		case '\r':	return("\\r");
		case '\t':	return("\\t");
		case '\v':	return("\\v");
		case '\\':	return("\\\\");
		case '\"':	return("\\\"");
		case '\'':	return("\\'");
	}
	return(NULL);
}

Rob Swindell's avatar
Rob Swindell committed
238
char* c_escape_str(const char* src, char* dst, size_t maxlen, BOOL ctrl_only)
239
240
{
	const char*	s;
241
242
	char*		d;
	const char*	e;
243

244
	for(s=src,d=dst;*s && (size_t)(d-dst)<maxlen;s++) {
245
		if((!ctrl_only || (uchar)*s < ' ') && (e=c_escape_char(*s))!=NULL) {
246
247
			strncpy(d,e,maxlen-(d-dst));
			d+=strlen(d);
248
		} else if((uchar)*s < ' ') {
249
			d += safe_snprintf(d, maxlen-(d-dst), "\\x%02X", *s);
250
		} else *d++=*s;
251
252
253
254
255
256
	}
	*d=0;

	return(dst);
}

257
258
259
260
261
262
/* Returns a byte count parsed from the 'str' argument, supporting power-of-2
 * short-hands (e.g. 'K' for kibibytes).
 * If 'unit' is specified (greater than 1), result is divided by this amount.
 * 
 * Moved from ini_file.c/parseBytes()
 */
Rob Swindell's avatar
Rob Swindell committed
263
int64_t parse_byte_count(const char* str, ulong unit)
264
265
266
267
268
269
{
	char*	p=NULL;
	double	bytes;

	bytes=strtod(str,&p);
	if(p!=NULL) {
270
		SKIP_WHITESPACE(p);
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
		switch(toupper(*p)) {
			case 'E':
				bytes*=1024;
				/* fall-through */
			case 'P':
				bytes*=1024;
				/* fall-through */
			case 'T':
				bytes*=1024;
				/* fall-through */
			case 'G':
				bytes*=1024;
				/* fall-through */
			case 'M':
				bytes*=1024;
				/* fall-through */
			case 'K':
				bytes*=1024;
				break;
		}
	}
	return((int64_t)(unit>1 ? (bytes/unit):bytes));
}

295
296
297
298
299
300
static const double one_tebibyte = 1024.0*1024.0*1024.0*1024.0;
static const double one_gibibyte = 1024.0*1024.0*1024.0;
static const double one_mebibyte = 1024.0*1024.0;
static const double one_kibibyte = 1024.0;

/* Convert an exact byte count to a string with a floating point value
301
302
303
   and a single letter multiplier/suffix.
   For values evenly divisible by 1024, no suffix otherwise.
*/
Rob Swindell's avatar
Rob Swindell committed
304
char* byte_count_to_str(int64_t bytes, char* str, size_t size)
305
{
306
307
308
309
310
311
312
313
	if(bytes && fmod((double)bytes,one_tebibyte)==0)
		safe_snprintf(str, size, "%gT",bytes/one_tebibyte);
	else if(bytes && fmod((double)bytes,one_gibibyte)==0)
		safe_snprintf(str, size, "%gG",bytes/one_gibibyte);
	else if(bytes && fmod((double)bytes,one_mebibyte)==0)
		safe_snprintf(str, size, "%gM",bytes/one_mebibyte);
	else if(bytes && fmod((double)bytes,one_kibibyte)==0)
		safe_snprintf(str, size, "%gK",bytes/one_kibibyte);
314
315
316
317
318
319
	else
		safe_snprintf(str, size, "%"PRIi64, bytes);

	return str;
}

320
321
322
323
324
/* Convert a rounded byte count to a string with a floating point value
   with a single decimal place and a single letter multiplier/suffix.
   This function also appends 'B' for exact byte counts (< 1024).
   'unit' is the smallest divisor used.
*/
Rob Swindell's avatar
Rob Swindell committed
325
char* byte_estimate_to_str(int64_t bytes, char* str, size_t size, ulong unit, int precision)
326
327
{
	if(bytes >= one_tebibyte)
328
		safe_snprintf(str, size, "%1.*fT", precision, bytes/one_tebibyte);
329
	else if(bytes >= one_gibibyte || unit == one_gibibyte)
330
		safe_snprintf(str, size, "%1.*fG", precision, bytes/one_gibibyte);
331
	else if(bytes >= one_mebibyte || unit == one_mebibyte)
332
		safe_snprintf(str, size, "%1.*fM", precision, bytes/one_mebibyte);
333
	else if(bytes >= one_kibibyte || unit == one_kibibyte)
334
		safe_snprintf(str, size, "%1.*fK", precision, bytes/one_kibibyte);
335
336
337
338
339
340
341
	else
		safe_snprintf(str, size, "%"PRIi64"B", bytes);

	return str;
}


342
343
/* Parse a duration string, default unit is in seconds */
/* (Y)ears, (W)eeks, (D)ays, (H)ours, and (M)inutes */
deuce's avatar
deuce committed
344
/* suffixes/multipliers are supported. */
345
/* Return value is in seconds */
Rob Swindell's avatar
Rob Swindell committed
346
double parse_duration(const char* str)
347
348
349
350
351
352
{
	char*	p=NULL;
	double	t;

	t=strtod(str,&p);
	if(p!=NULL) {
353
		SKIP_WHITESPACE(p);
354
355
356
357
358
359
360
361
362
363
364
		switch(toupper(*p)) {
			case 'Y':	t*=365.0*24.0*60.0*60.0; break;
			case 'W':	t*=  7.0*24.0*60.0*60.0; break;
			case 'D':	t*=		 24.0*60.0*60.0; break;
			case 'H':	t*=			  60.0*60.0; break;
			case 'M':	t*=				   60.0; break;
		}
	}
	return t;
}

365
366
/* Convert a duration (in seconds) to a string
 * with a single letter multiplier/suffix: 
367
 * (Y)ears, (W)eeks, (D)ays, (H)ours, (M)inutes, or (S)econds
368
 */
Rob Swindell's avatar
Rob Swindell committed
369
char* duration_to_str(double value, char* str, size_t size)
370
{
371
	if(value && fmod(value,365.0*24.0*60.0*60.0)==0)
372
		safe_snprintf(str, size, "%gY",value/(365.0*24.0*60.0*60.0));
373
	else if(value && fmod(value,7.0*24.0*60.0*60.0)==0)
374
		safe_snprintf(str, size, "%gW",value/(7.0*24.0*60.0*60.0));
375
	else if(value && fmod(value,24.0*60.0*60.0)==0)
376
		safe_snprintf(str, size, "%gD",value/(24.0*60.0*60.0));
377
	else if(value && fmod(value,60.0*60.0)==0)
378
		safe_snprintf(str, size, "%gH",value/(60.0*60.0));
379
	else if(value && fmod(value,60.0)==0)
380
381
382
383
384
385
386
		safe_snprintf(str, size, "%gM",value/60.0);
	else
		safe_snprintf(str, size, "%gS",value);

	return str;
}

387
388
389
390
/* Convert a duration (in seconds) to a verbose string
 * with a word clarifier / modifier: 
 * year[s], week[s], day[s], hour[s], minute[s] or second[s]
 */
Rob Swindell's avatar
Rob Swindell committed
391
char* duration_to_vstr(double value, char* str, size_t size)
392
{
393
	if(value && fmod(value,365.0*24.0*60.0*60.0)==0) {
394
		value /= (365.0*24.0*60.0*60.0);
395
		safe_snprintf(str, size, "%g year%s", value, value == 1 ? "":"s");
396
	}
397
	else if(value && fmod(value,7.0*24.0*60.0*60.0)==0) {
398
		value /= (7.0*24.0*60.0*60.0);
399
		safe_snprintf(str, size, "%g week%s", value, value == 1 ? "":"s");
400
	}
401
	else if(value && fmod(value,24.0*60.0*60.0)==0) {
402
		value /= (24.0*60.0*60.0);
403
		safe_snprintf(str, size, "%g day%s", value, value == 1 ? "":"s");
404
	}
405
	else if(value && fmod(value,60.0*60.0)==0) {
406
		value /= (60.0*60.0);
407
		safe_snprintf(str, size, "%g hour%s", value, value == 1 ? "":"s");
408
	}
409
	else if(value && fmod(value,60.0)==0) {
410
		value /= 60.0;
411
		safe_snprintf(str, size, "%g minute%s", value, value == 1 ? "":"s");
412
413
	}
	else
414
		safe_snprintf(str, size, "%g second%s", value, value == 1 ? "":"s");
415
416
417
418
419

	return str;
}


420
/****************************************************************************/
421
422
/* Convert ASCIIZ string to upper case										*/
/****************************************************************************/
423
#if defined(__unix__)
Rob Swindell's avatar
Rob Swindell committed
424
char* strupr(char* str)
425
426
427
428
429
430
431
432
433
434
435
436
{
	char*	p=str;

	while(*p) {
		*p=toupper(*p);
		p++;
	}
	return(str);
}
/****************************************************************************/
/* Convert ASCIIZ string to lower case										*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
437
char* strlwr(char* str)
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
{
	char*	p=str;

	while(*p) {
		*p=tolower(*p);
		p++;
	}
	return(str);
}
/****************************************************************************/
/* Reverse characters of a string (provided by amcleod)						*/
/****************************************************************************/
char* strrev(char* str)
{
    char t, *i=str, *j=str+strlen(str);

    while (i<j) {
        t=*i; *(i++)=*(--j); *j=t;
    }
    return str;
}
#endif

461
462
463
464
465
466
#if !defined(__unix__)

/****************************************************************************/
/* Implementations of the recursive (thread-safe) version of strtok			*/
/* Thanks to Apache Portable Runtime (APR)									*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
467
char* strtok_r(char *str, const char *delim, char **last)
468
469
470
471
472
473
474
475
476
477
{
    char* token;

    if (str==NULL)      /* subsequent call */
        str = *last;    /* start where we left off */

    /* skip characters in delimiter (will terminate at '\0') */
    while(*str && strchr(delim, *str))
        ++str;

478
479
    if(!*str) {         /* no more tokens */
		*last = str;
480
        return NULL;
481
	}
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501

    token = str;

    /* skip valid token characters to terminate token and
     * prepare for the next call (will terminate at '\0)
     */
    *last = token + 1;
    while(**last && !strchr(delim, **last))
        ++*last;

    if (**last) {
        **last = '\0';
        ++*last;
    }

    return token;
}

#endif

rswindell's avatar
rswindell committed
502
503
504
/****************************************************************************/
/* Initialize (seed) the random number generator							*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
505
void xp_randomize(void)
rswindell's avatar
rswindell committed
506
{
507
#if !(defined(HAS_SRANDOMDEV_FUNC) && defined(HAS_RANDOM_FUNC))
rswindell's avatar
rswindell committed
508
	unsigned seed=~0;
509
#if defined(HAS_DEV_URANDOM) && defined(URANDOM_DEV)
510
511
	int		rf;
#endif
512
#endif
rswindell's avatar
rswindell committed
513

514
515
516
#if defined(HAS_SRANDOMDEV_FUNC) && defined(HAS_RANDOM_FUNC)
	srandomdev();
	return;
517
#else
518
519

#if defined(HAS_DEV_URANDOM) && defined(URANDOM_DEV)
520
	if((rf=open(URANDOM_DEV, O_RDONLY))!=-1) {
rswindell's avatar
rswindell committed
521
522
523
		read(rf, &seed, sizeof(seed));
		close(rf);
	}
524
525
526
527
	else {
#endif
		unsigned curtime	= (unsigned)time(NULL);
		unsigned process_id = (unsigned)GetCurrentProcessId();
rswindell's avatar
rswindell committed
528

529
		seed = curtime ^ BYTE_SWAP_INT(process_id);
rswindell's avatar
rswindell committed
530

531
532
533
		#if defined(_WIN32) || defined(GetCurrentThreadId)
			seed ^= (unsigned)GetCurrentThreadId();
		#endif
rswindell's avatar
rswindell committed
534

535
536
#if defined(HAS_DEV_URANDOM) && defined(URANDOM_DEV)
	}
rswindell's avatar
rswindell committed
537
538
#endif

539
540
541
#ifdef HAS_RANDOM_FUNC
 	srandom(seed);
#else
rswindell's avatar
rswindell committed
542
 	srand(seed);
543
#endif
544
#endif
rswindell's avatar
rswindell committed
545
546
}

547
548
549
/****************************************************************************/
/* Return random number between 0 and n-1									*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
550
long xp_random(int n)
551
{
552
#ifdef HAS_RANDOM_FUNC
553
554
	long			curr;
	unsigned long	limit;
555

556
557
	if(n<2)
		return(0);
558

559
	limit = ((1UL<<((sizeof(long)*CHAR_BIT)-1)) / n) * n - 1;
560

561
562
	while(1) {
		curr=random();
563
		if(curr <= limit)
564
565
			return(curr % n);
	}
566
#else
567
568
	double f=0;
	int ret;
569
570
571

	if(n<2)
		return(0);
572
	do {
573
		f=(double)rand()/(double)(RAND_MAX+1);
574
575
		ret=(int)(n*f);
	} while(ret==n);
576

577
	return(ret);
578
#endif
579
580
}

581
582
583
584
/****************************************************************************/
/* Return ASCII string representation of ulong								*/
/* There may be a native GNU C Library function to this...					*/
/****************************************************************************/
rswindell's avatar
rswindell committed
585
#if !defined(_MSC_VER) && !defined(__BORLANDC__) && !defined(__WATCOMC__)
Rob Swindell's avatar
Rob Swindell committed
586
char* ultoa(ulong val, char* str, int radix)
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
{
	switch(radix) {
		case 8:
			sprintf(str,"%lo",val);
			break;
		case 10:
			sprintf(str,"%lu",val);
			break;
		case 16:
			sprintf(str,"%lx",val);
			break;
		default:
			sprintf(str,"bad radix: %d",radix);
			break;
	}
	return(str);
}
#endif

606
607
608
/****************************************************************************/
/* Write the version details of the current operating system into str		*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
609
char* os_version(char *str)
610
611
612
613
614
615
616
617
618
619
620
621
{
#if defined(__OS2__) && defined(__BORLANDC__)

	sprintf(str,"OS/2 %u.%u (%u.%u)",_osmajor/10,_osminor/10,_osmajor,_osminor);

#elif defined(_WIN32)

	/* Windows Version */
	char*			winflavor="";
	OSVERSIONINFO	winver;

	winver.dwOSVersionInfoSize=sizeof(winver);
622
	#pragma warning(suppress : 4996)
623
624
625
626
627
628
629
630
631
632
633
634
635
636
	GetVersionEx(&winver);

	switch(winver.dwPlatformId) {
		case VER_PLATFORM_WIN32_NT:
			winflavor="NT ";
			break;
		case VER_PLATFORM_WIN32s:
			winflavor="Win32s ";
			break;
		case VER_PLATFORM_WIN32_WINDOWS:
			winver.dwBuildNumber&=0xffff;
			break;
	}

637
638
639
640
641
642
643
644
645
646
	/* Work-around Microsoft Windows 8.1 stupidity where GetVersionEx() lies about the current OS version */
	if(winver.dwMajorVersion == 6 && winver.dwMinorVersion == 2) {
		WKSTA_INFO_100* wksta_info;
		if(NetWkstaGetInfo(NULL, 100, (LPBYTE*)&wksta_info) == NERR_Success) {
			winver.dwMajorVersion = wksta_info->wki100_ver_major;
			winver.dwMinorVersion = wksta_info->wki100_ver_minor;
			winver.dwBuildNumber = 0;
		}
	}

647
	sprintf(str,"Windows %sVersion %lu.%lu"
648
			,winflavor
649
650
			,winver.dwMajorVersion, winver.dwMinorVersion);
	if(winver.dwBuildNumber)
651
		sprintf(str+strlen(str), " (Build %lu)", winver.dwBuildNumber);
652
653
	if(winver.szCSDVersion[0])
		sprintf(str+strlen(str), " %s", winver.szCSDVersion);
654
655
656
657
658

#elif defined(__unix__)

	struct utsname unixver;

659
	if(uname(&unixver)<0)
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
		sprintf(str,"Unix (uname errno: %d)",errno);
	else
		sprintf(str,"%s %s %s"
			,unixver.sysname	/* e.g. "Linux" */
			,unixver.release	/* e.g. "2.2.14-5.0" */
			,unixver.machine	/* e.g. "i586" */
			);

#else	/* DOS */

	sprintf(str,"DOS %u.%02u",_osmajor,_osminor);

#endif

	return(str);
}
676

Rob Swindell's avatar
Rob Swindell committed
677
char* os_cmdshell(void)
678
679
680
{
	char*	shell=getenv(OS_CMD_SHELL_ENV_VAR);

deuce's avatar
deuce committed
681
#if defined(__unix__)
682
683
684
685
686
687
688
689
690
691
692
	if(shell==NULL)
#ifdef _PATH_BSHELL
		shell=_PATH_BSHELL;
#else
		shell="/bin/sh";
#endif
#endif

	return(shell);
}

693
694
695
/********************************************************/
/* Stupid real-time system clock implementation.	*/
/********************************************************/
Rob Swindell's avatar
Rob Swindell committed
696
clock_t msclock(void)
697
{
698
699
700
701
702
703
704
705
        long double t = roundl(xp_timer() * 1000);

        if (sizeof(clock_t) < 8) {
                while (t > INT32_MAX)
                        t -= UINT32_MAX;
        }

	return (clock_t)t;
706
}
707

708
709
710
/****************************************************************************/
/* Skips all white-space chars at beginning of 'str'						*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
711
char* skipsp(char* str)
712
713
714
715
716
{
	SKIP_WHITESPACE(str);
	return(str);
}

717
718
719
/****************************************************************************/
/* Truncates all white-space chars off end of 'str'	(needed by STRERRROR)	*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
720
char* truncsp(char* str)
721
{
722
	size_t i,len;
723

724
725
	if(str!=NULL) {
		i=len=strlen(str);
726
		while(i && IS_WHITESPACE(str[i-1]))
727
728
729
730
			i--;
		if(i!=len)
			str[i]=0;	/* truncate */
	}
731
732
733
	return(str);
}

734
/****************************************************************************/
735
736
/* Truncates common white-space chars off end of \n-terminated lines in		*/
/* 'dst' and retains original line break format	(e.g. \r\n or \n)			*/
737
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
738
char* truncsp_lines(char* dst)
739
740
741
742
743
744
745
746
747
{
	char* sp;
	char* dp;
	char* src;

	if((src=strdup(dst))==NULL)
		return(dst);

	for(sp=src, dp=dst; *sp!=0; sp++) {
748
		if(*sp=='\n') {
749
			while(dp!=dst
750
				&& (*(dp-1)==' ' || *(dp-1)=='\t' || *(dp-1)=='\r'))
751
					dp--;
752
753
754
			if(sp!=src && *(sp-1)=='\r')
				*(dp++)='\r';
		}
755
756
757
758
759
760
761
762
		*(dp++)=*sp;
	}
	*dp=0;

	free(src);
	return(dst);
}

763
764
765
/****************************************************************************/
/* Truncates carriage-return and line-feed chars off end of 'str'			*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
766
char* truncnl(char* str)
767
{
768
	size_t i,len;
769

770
771
	if(str!=NULL) {
		i=len=strlen(str);
772
		while(i && (str[i-1]=='\r' || str[i-1]=='\n'))
773
774
775
776
			i--;
		if(i!=len)
			str[i]=0;	/* truncate */
	}
777
778
	return(str);
}
779
780
781
782
783

/****************************************************************************/
/* Return errno from the proper C Library implementation					*/
/* (single/multi-threaded)													*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
784
int get_errno(void)
785
786
{
	return(errno);
787
}
788
789
790
791
792

/****************************************************************************/
/* Returns the current value of the systems best timer (in SECONDS)			*/
/* Any value < 0 indicates an error											*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
793
long double	xp_timer(void)
794
{
795
	long double ret;
796
#if defined(__unix__)
797
798
799
800
801
802
803
804
	struct timespec ts;

	if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
		ret = ts.tv_sec;
		ret += ((long double)ts.tv_nsec) / 1000000000;
	}
	else
		ret = -1;
805
#elif defined(_WIN32)
806
	LARGE_INTEGER	freq;
deuce's avatar
deuce committed
807
808
	LARGE_INTEGER	tick;
	if(QueryPerformanceFrequency(&freq) && QueryPerformanceCounter(&tick)) {
809
#if 0
810
811
		ret=((long double)tick.HighPart*4294967296)+((long double)tick.LowPart);
		ret /= ((long double)freq.HighPart*4294967296)+((long double)freq.LowPart);
812
#else
813
		/* In MSVC, a long double does NOT have 19 decimals of precision */
814
		ret=(((long double)(tick.QuadPart%freq.QuadPart))/freq.QuadPart);
815
		ret+=tick.QuadPart/freq.QuadPart;
816
#endif
817
818
819
820
821
822
	}
	else {
		ret=GetTickCount();
		ret /= 1000;
	}
#else
823
#error no high-resolution time for this platform
824
825
826
#endif
	return(ret);
}
827

Deucе's avatar
Deucе committed
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
/****************************************************************************/
/* Returns the current value of the systems best timer (in MILLISECONDS)	*/
/* Any value < 0 indicates an error											*/
/****************************************************************************/
uint64_t xp_timer64(void)
{
	uint64_t ret;
#if defined(__unix__)
	struct timespec ts;

	if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
		ret = ts.tv_sec * 1000;
		ret += ts.tv_nsec / 1000000;
	}
	else
		ret = -1;
#elif defined(_WIN32)
Deucе's avatar
Deucе committed
845
	static BOOL can_use_QPF = TRUE;
Deucе's avatar
Moar!    
Deucе committed
846
	static BOOL intable = FALSE;
Deucе's avatar
Deucе committed
847
848
849
	static BOOL initialized = FALSE;
	static uint32_t msfreq;
	static double msdfreq;
Deucе's avatar
Deucе committed
850
	LARGE_INTEGER	tick;
Deucе's avatar
Deucе committed
851

Deucе's avatar
Deucе committed
852
	if (!initialized) {
Deucе's avatar
Deucе committed
853
		LARGE_INTEGER	freq;
Deucе's avatar
Deucе committed
854
855
856
		if (!QueryPerformanceFrequency(&freq))
			can_use_QPF = FALSE;
		else
Deucе's avatar
Moar!    
Deucе committed
857
        		intable = (freq.QuadPart % 1000) == 0;
Deucе's avatar
Deucе committed
858
859
860
861
862
863
864
865
866
867
868
869

		if (intable)
			msfreq = freq.QuadPart / 1000;
		else
			msdfreq = ((double)freq.QuadPart) / 1000;

		initialized = TRUE;
	}
	if (can_use_QPF) {
		if (!QueryPerformanceCounter(&tick)) {
			can_use_QPF = FALSE;
			return GetTickCount();
Deucе's avatar
Moar!    
Deucе committed
870
		}
Deucе's avatar
Deucе committed
871
		if (intable)
Deucе's avatar
Deucе committed
872
			ret = tick.QuadPart / msfreq;
Deucе's avatar
Deucе committed
873
		else
Deucе's avatar
Deucе committed
874
			ret = (uint32_t)(((double)tick.QuadPart) / msdfreq);
Deucе's avatar
Deucе committed
875
876
877
878
879
880
881
882
883
884
	}
	else {
		ret=GetTickCount();
	}
#else
#error no high-resolution time for this platform
#endif
	return(ret);
}

885
/* Returns TRUE if specified process is running */
Rob Swindell's avatar
Rob Swindell committed
886
BOOL check_pid(pid_t pid)
887
888
889
890
891
892
893
894
895
{
#if defined(__unix__)
	return(kill(pid,0)==0);
#elif defined(_WIN32)
	HANDLE	h;
	BOOL	result=FALSE;

	if((h=OpenProcess(PROCESS_QUERY_INFORMATION,/* inheritable: */FALSE, pid)) != NULL) {
		DWORD	code;
896
		if(GetExitCodeProcess(h,(PDWORD)&code)==TRUE && code==STILL_ACTIVE)
897
898
899
900
901
902
903
904
905
906
			result=TRUE;
		CloseHandle(h);
	}
	return result;
#else
	return FALSE;	/* Need check_pid() definition! */
#endif
}

/* Terminate (unconditionally) the specified process */
Rob Swindell's avatar
Rob Swindell committed
907
BOOL terminate_pid(pid_t pid)
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
{
#if defined(__unix__)
	return(kill(pid,SIGKILL)==0);
#elif defined(_WIN32)
	HANDLE	h;
	BOOL	result=FALSE;

	if((h=OpenProcess(PROCESS_TERMINATE,/* inheritable: */FALSE, pid)) != NULL) {
		if(TerminateProcess(h,255))
			result=TRUE;
		CloseHandle(h);
	}
	return result;
#else
	return FALSE;	/* Need check_pid() definition! */
#endif
}

926
927
928
929
930
931
/****************************************************************************/
/* Re-entrant (thread-safe) version of strerror()							*/
/* GNU (not POSIX) inspired API											*/
/****************************************************************************/
char* safe_strerror(int errnum, char *buf, size_t buflen)
{
932
933
	strncpy(buf, "Unknown error", buflen);
	buf[buflen - 1] = 0;
934

935
#if defined(_MSC_VER)
936
	strerror_s(buf, buflen, errnum);
937
#elif defined(_WIN32)
938
939
	strncpy(buf, strerror(errnum), buflen);
	buf[buflen - 1] = 0;
940
941
#elif defined(_GNU_SOURCE)
	buf = strerror_r(errnum, buf, buflen);
942
943
944
945
946
#else
	strerror_r(errnum, buf, buflen);
#endif
	return buf;
}