genwrap.c 21.2 KB
Newer Older
1
2
3
/* General cross-platform development wrappers */

/* $Id$ */
4
// vi: tabstop=4
5
6
7
8
9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11
 *																			*
12
13
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
14
15
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
16
17
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 *																			*
 * 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() */
deuce's avatar
deuce committed
38
#include <stdarg.h>	/* vsnprintf() */
39
40
41
#include <stdlib.h>		/* RAND_MAX */
#include <fcntl.h>		/* O_NOCTTY */
#include <time.h>		/* clock() */
42
#include <errno.h>		/* errno */
43
#include <ctype.h>		/* toupper/tolower */
44
#include <limits.h>		/* CHAR_BIT */
45
#include <math.h>		/* fmod */
rswindell's avatar
rswindell committed
46

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

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

59
60
61
/****************************************************************************/
/* Used to replace snprintf()  guarantees to terminate.			  			*/
/****************************************************************************/
62
int DLLCALL safe_snprintf(char *dst, size_t size, const char *fmt, ...)
63
64
65
66
67
68
69
{
	va_list argptr;
	int     numchars;

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

81
/****************************************************************************/
82
83
/* Return last character of string											*/
/****************************************************************************/
84
char* DLLCALL lastchar(const char* str)
85
86
87
88
89
90
{
	size_t	len;

	len = strlen(str);

	if(len)
91
		return((char*)&str[len-1]);
92

93
	return((char*)str);
94
95
96
}

/****************************************************************************/
97
98
/* Return character value of C-escaped (\) character						*/
/****************************************************************************/
99
char DLLCALL c_unescape_char(char ch)
100
101
{
	switch(ch) {
102
		case 'e':	return(ESC);	/* non-standard */
103
104
105
106
107
108
109
110
111
112
113
114
		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);
}

/****************************************************************************/
115
/* Return character value of C-escaped (\) character literal sequence		*/
116
/* supports \x## (hex) and \### sequences (for octal or decimal)			*/
117
/****************************************************************************/
118
char DLLCALL c_unescape_char_ptr(const char* str, char** endptr)
119
120
121
{
	char	ch;

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
	if(*str == 'x') {
		int digits = 0;		// \x## for hexadecimal character literals (only 2 digits supported)
		++str;
		ch = 0;
		while(digits < 2 && isxdigit(*str)) {
			ch *= 0x10;	
			ch += HEX_CHAR_TO_INT(*str);
			str++;
			digits++;
		}
#ifdef C_UNESCAPE_OCTAL_SUPPORT
	} else if(isodigit(*str)) {
		int digits = 0;		// \### for octal character literals (only 3 digits supported)
		ch = 0;
		while(digits < 3 && isodigit(*str)) {
			ch *= 8;
			ch += OCT_CHAR_TO_INT(*str);
			str++;
			digits++;
		}
#else
	} else if(isdigit(*str)) {
		int digits = 0;		// \### for decimal charater literals (only 3 digits supported)
		ch = 0;
		while(digits < 3 && isdigit(*str)) {
			ch *= 10;
			ch += DEC_CHAR_TO_INT(*str);
			str++;
			digits++;
		}
#endif
	 } else
154
		ch=c_unescape_char(*(str++));
155

156
157
158
159
	if(endptr!=NULL)
		*endptr=(char*)str;

	return ch;
160
161
162
163
164
}

/****************************************************************************/
/* Unescape a C string, in place											*/
/****************************************************************************/
165
char* DLLCALL c_unescape_str(char* str)
166
167
168
169
170
171
172
173
174
175
176
177
178
{
	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 */
179
			ch=c_unescape_char_ptr(src,&src);
180
181
182
183
184
185
186
		*(dst++)=ch;
	}
	*dst=0;
	free(buf);
	return(str);
}

187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
char* DLLCALL c_escape_char(char ch)
{
	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);
}

char* DLLCALL c_escape_str(const char* src, char* dst, size_t maxlen, BOOL ctrl_only)
{
	const char*	s;
210
211
	char*		d;
	const char*	e;
212

213
	for(s=src,d=dst;*s && (size_t)(d-dst)<maxlen;s++) {
214
		if((!ctrl_only || (uchar)*s < ' ') && (e=c_escape_char(*s))!=NULL) {
215
216
			strncpy(d,e,maxlen-(d-dst));
			d+=strlen(d);
217
218
		} else if((uchar)*s < ' ') {
			d += snprintf(d, maxlen-(d-dst), "\\x%02X", *s);
219
		} else *d++=*s;
220
221
222
223
224
225
	}
	*d=0;

	return(dst);
}

226
227
228
229
230
231
232
233
234
235
236
237
238
/* 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()
 */
int64_t DLLCALL parse_byte_count(const char* str, ulong unit)
{
	char*	p=NULL;
	double	bytes;

	bytes=strtod(str,&p);
	if(p!=NULL) {
239
		SKIP_WHITESPACE(p);
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
		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));
}

264
265
266
267
268
269
/* Convert a byte count to a string with a floating point value
   and a single letter multiplier/suffix.
   For values evenly divisible by 1024, no suffix otherwise.
*/
char* DLLCALL byte_count_to_str(int64_t bytes, char* str, size_t size)
{
270
	if(bytes && fmod((double)bytes,1024.0*1024.0*1024.0*1024.0)==0)
271
		safe_snprintf(str, size, "%gT",bytes/(1024.0*1024.0*1024.0*1024.0));
272
	else if(bytes && fmod((double)bytes,1024.0*1024.0*1024.0)==0)
273
		safe_snprintf(str, size, "%gG",bytes/(1024.0*1024.0*1024.0));
274
	else if(bytes && fmod((double)bytes,1024.0*1024.0)==0)
275
		safe_snprintf(str, size, "%gM",bytes/(1024.0*1024.0));
276
	else if(bytes && fmod((double)bytes,1024.0)==0)
277
278
279
280
281
282
283
		safe_snprintf(str, size, "%gK",bytes/1024.0);
	else
		safe_snprintf(str, size, "%"PRIi64, bytes);

	return str;
}

284
285
/* 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
286
/* suffixes/multipliers are supported. */
287
288
289
290
291
292
293
294
/* Return value is in seconds */
double DLLCALL parse_duration(const char* str)
{
	char*	p=NULL;
	double	t;

	t=strtod(str,&p);
	if(p!=NULL) {
295
		SKIP_WHITESPACE(p);
296
297
298
299
300
301
302
303
304
305
306
		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;
}

307
308
/* Convert a duration (in seconds) to a string
 * with a single letter multiplier/suffix: 
309
 * (Y)ears, (W)eeks, (D)ays, (H)ours, (M)inutes, or (S)econds
310
311
312
 */
char* DLLCALL duration_to_str(double value, char* str, size_t size)
{
313
	if(value && fmod(value,365.0*24.0*60.0*60.0)==0)
314
		safe_snprintf(str, size, "%gY",value/(365.0*24.0*60.0*60.0));
315
	else if(value && fmod(value,7.0*24.0*60.0*60.0)==0)
316
		safe_snprintf(str, size, "%gW",value/(7.0*24.0*60.0*60.0));
317
	else if(value && fmod(value,24.0*60.0*60.0)==0)
318
		safe_snprintf(str, size, "%gD",value/(24.0*60.0*60.0));
319
	else if(value && fmod(value,60.0*60.0)==0)
320
		safe_snprintf(str, size, "%gH",value/(60.0*60.0));
321
	else if(value && fmod(value,60.0)==0)
322
323
324
325
326
327
328
		safe_snprintf(str, size, "%gM",value/60.0);
	else
		safe_snprintf(str, size, "%gS",value);

	return str;
}

329
330
331
332
333
334
/* 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]
 */
char* DLLCALL duration_to_vstr(double value, char* str, size_t size)
{
335
	if(value && fmod(value,365.0*24.0*60.0*60.0)==0) {
336
		value /= (365.0*24.0*60.0*60.0);
337
		safe_snprintf(str, size, "%g year%s", value, value == 1 ? "":"s");
338
	}
339
	else if(value && fmod(value,7.0*24.0*60.0*60.0)==0) {
340
		value /= (7.0*24.0*60.0*60.0);
341
		safe_snprintf(str, size, "%g week%s", value, value == 1 ? "":"s");
342
	}
343
	else if(value && fmod(value,24.0*60.0*60.0)==0) {
344
		value /= (24.0*60.0*60.0);
345
		safe_snprintf(str, size, "%g day%s", value, value == 1 ? "":"s");
346
	}
347
	else if(value && fmod(value,60.0*60.0)==0) {
348
		value /= (60.0*60.0);
349
		safe_snprintf(str, size, "%g hour%s", value, value == 1 ? "":"s");
350
	}
351
	else if(value && fmod(value,60.0)==0) {
352
		value /= 60.0;
353
		safe_snprintf(str, size, "%g minute%s", value, value == 1 ? "":"s");
354
355
	}
	else
356
		safe_snprintf(str, size, "%g second%s", value, value == 1 ? "":"s");
357
358
359
360
361

	return str;
}


362
/****************************************************************************/
363
364
/* Convert ASCIIZ string to upper case										*/
/****************************************************************************/
365
#if defined(__unix__)
366
char* DLLCALL strupr(char* str)
367
368
369
370
371
372
373
374
375
376
377
378
{
	char*	p=str;

	while(*p) {
		*p=toupper(*p);
		p++;
	}
	return(str);
}
/****************************************************************************/
/* Convert ASCIIZ string to lower case										*/
/****************************************************************************/
379
char* DLLCALL strlwr(char* str)
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
{
	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

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
#if !defined(__unix__)

/****************************************************************************/
/* Implementations of the recursive (thread-safe) version of strtok			*/
/* Thanks to Apache Portable Runtime (APR)									*/
/****************************************************************************/
char* DLLCALL strtok_r(char *str, const char *delim, char **last)
{
    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;

420
421
    if(!*str) {         /* no more tokens */
		*last = str;
422
        return NULL;
423
	}
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443

    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
444
445
446
/****************************************************************************/
/* Initialize (seed) the random number generator							*/
/****************************************************************************/
447
void DLLCALL xp_randomize(void)
rswindell's avatar
rswindell committed
448
{
449
#if !(defined(HAS_SRANDOMDEV_FUNC) && defined(HAS_RANDOM_FUNC))
rswindell's avatar
rswindell committed
450
	unsigned seed=~0;
451
#if defined(HAS_DEV_URANDOM) && defined(URANDOM_DEV)
452
453
	int		rf;
#endif
454
#endif
rswindell's avatar
rswindell committed
455

456
457
458
#if defined(HAS_SRANDOMDEV_FUNC) && defined(HAS_RANDOM_FUNC)
	srandomdev();
	return;
459
#else
460
461

#if defined(HAS_DEV_URANDOM) && defined(URANDOM_DEV)
462
	if((rf=open(URANDOM_DEV, O_RDONLY))!=-1) {
rswindell's avatar
rswindell committed
463
464
465
		read(rf, &seed, sizeof(seed));
		close(rf);
	}
466
467
468
469
	else {
#endif
		unsigned curtime	= (unsigned)time(NULL);
		unsigned process_id = (unsigned)GetCurrentProcessId();
rswindell's avatar
rswindell committed
470

471
		seed = curtime ^ BYTE_SWAP_INT(process_id);
rswindell's avatar
rswindell committed
472

473
474
475
		#if defined(_WIN32) || defined(GetCurrentThreadId)
			seed ^= (unsigned)GetCurrentThreadId();
		#endif
rswindell's avatar
rswindell committed
476

477
478
#if defined(HAS_DEV_URANDOM) && defined(URANDOM_DEV)
	}
rswindell's avatar
rswindell committed
479
480
#endif

481
482
483
#ifdef HAS_RANDOM_FUNC
 	srandom(seed);
#else
rswindell's avatar
rswindell committed
484
 	srand(seed);
485
#endif
486
#endif
rswindell's avatar
rswindell committed
487
488
}

489
490
491
/****************************************************************************/
/* Return random number between 0 and n-1									*/
/****************************************************************************/
492
long DLLCALL xp_random(int n)
493
{
494
#ifdef HAS_RANDOM_FUNC
495
496
	long			curr;
	unsigned long	limit;
497

498
499
	if(n<2)
		return(0);
500

501
	limit = ((1UL<<((sizeof(long)*CHAR_BIT)-1)) / n) * n - 1;
502

503
504
	while(1) {
		curr=random();
505
		if(curr <= limit)
506
507
			return(curr % n);
	}
508
#else
509
510
	double f=0;
	int ret;
511
512
513

	if(n<2)
		return(0);
514
	do {
515
		f=(double)rand()/(double)(RAND_MAX+1);
516
517
		ret=(int)(n*f);
	} while(ret==n);
518

519
	return(ret);
520
#endif
521
522
}

523
524
525
526
/****************************************************************************/
/* Return ASCII string representation of ulong								*/
/* There may be a native GNU C Library function to this...					*/
/****************************************************************************/
rswindell's avatar
rswindell committed
527
#if !defined(_MSC_VER) && !defined(__BORLANDC__) && !defined(__WATCOMC__)
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
char* DLLCALL ultoa(ulong val, char* str, int radix)
{
	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

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
/****************************************************************************/
/* Write the version details of the current operating system into str		*/
/****************************************************************************/
char* DLLCALL os_version(char *str)
{
#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);
	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;
	}

578
579
580
581
582
583
584
585
586
587
588
	/* 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;
		}
	}

	sprintf(str,"Windows %sVersion %u.%u"
589
			,winflavor
590
591
592
593
594
			,winver.dwMajorVersion, winver.dwMinorVersion);
	if(winver.dwBuildNumber)
		sprintf(str+strlen(str), " (Build %u)", winver.dwBuildNumber);
	if(winver.szCSDVersion[0])
		sprintf(str+strlen(str), " %s", winver.szCSDVersion);
595
596
597
598
599

#elif defined(__unix__)

	struct utsname unixver;

600
	if(uname(&unixver)<0)
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
		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);
}
617

618
619
620
621
char* DLLCALL os_cmdshell(void)
{
	char*	shell=getenv(OS_CMD_SHELL_ENV_VAR);

deuce's avatar
deuce committed
622
#if defined(__unix__)
623
624
625
626
627
628
629
630
631
632
633
	if(shell==NULL)
#ifdef _PATH_BSHELL
		shell=_PATH_BSHELL;
#else
		shell="/bin/sh";
#endif
#endif

	return(shell);
}

rswindell's avatar
rswindell committed
634
635
636
/****************************************************************/
/* Microsoft (DOS/Win32) real-time system clock implementation.	*/
/****************************************************************/
637
638
639
640
641
642
643
#ifdef __unix__
clock_t DLLCALL msclock(void)
{
	long long int usecs;
	struct timeval tv;
	if(gettimeofday(&tv,NULL)==1)
		return(-1);
644
	usecs=((long long int)tv.tv_sec)*((long long int)1000000)+tv.tv_usec;
645
646
647
	return((clock_t)(usecs/(1000000/MSCLOCKS_PER_SEC)));
}
#endif
648

649
650
651
652
653
654
655
656
657
/****************************************************************************/
/* Skips all white-space chars at beginning of 'str'						*/
/****************************************************************************/
char* DLLCALL skipsp(char* str)
{
	SKIP_WHITESPACE(str);
	return(str);
}

658
659
660
661
662
/****************************************************************************/
/* Truncates all white-space chars off end of 'str'	(needed by STRERRROR)	*/
/****************************************************************************/
char* DLLCALL truncsp(char* str)
{
663
	size_t i,len;
664

665
666
	if(str!=NULL) {
		i=len=strlen(str);
667
		while(i && isspace((unsigned char)str[i-1]))
668
669
670
671
			i--;
		if(i!=len)
			str[i]=0;	/* truncate */
	}
672
673
674
	return(str);
}

675
/****************************************************************************/
676
677
/* 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)			*/
678
679
680
681
682
683
684
685
686
687
688
/****************************************************************************/
char* DLLCALL truncsp_lines(char* dst)
{
	char* sp;
	char* dp;
	char* src;

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

	for(sp=src, dp=dst; *sp!=0; sp++) {
689
		if(*sp=='\n') {
690
			while(dp!=dst
691
				&& (*(dp-1)==' ' || *(dp-1)=='\t' || *(dp-1)=='\r'))
692
					dp--;
693
694
695
			if(sp!=src && *(sp-1)=='\r')
				*(dp++)='\r';
		}
696
697
698
699
700
701
702
703
		*(dp++)=*sp;
	}
	*dp=0;

	free(src);
	return(dst);
}

704
705
706
707
708
/****************************************************************************/
/* Truncates carriage-return and line-feed chars off end of 'str'			*/
/****************************************************************************/
char* DLLCALL truncnl(char* str)
{
709
	size_t i,len;
710

711
712
	if(str!=NULL) {
		i=len=strlen(str);
713
		while(i && (str[i-1]=='\r' || str[i-1]=='\n'))
714
715
716
717
			i--;
		if(i!=len)
			str[i]=0;	/* truncate */
	}
718
719
	return(str);
}
720
721
722
723
724
725
726
727

/****************************************************************************/
/* Return errno from the proper C Library implementation					*/
/* (single/multi-threaded)													*/
/****************************************************************************/
int DLLCALL	get_errno(void)
{
	return(errno);
728
}
729
730
731
732
733

/****************************************************************************/
/* Returns the current value of the systems best timer (in SECONDS)			*/
/* Any value < 0 indicates an error											*/
/****************************************************************************/
734
long double	DLLCALL	xp_timer(void)
735
{
736
	long double ret;
737
#if defined(__unix__)
738
739
740
741
742
743
	struct timeval tv;
	if(gettimeofday(&tv,NULL)==1)
		return(-1);
	ret=tv.tv_usec;
	ret /= 1000000;
	ret += tv.tv_sec;
744
#elif defined(_WIN32)
745
	LARGE_INTEGER	freq;
deuce's avatar
deuce committed
746
747
	LARGE_INTEGER	tick;
	if(QueryPerformanceFrequency(&freq) && QueryPerformanceCounter(&tick)) {
748
#if 0
749
750
		ret=((long double)tick.HighPart*4294967296)+((long double)tick.LowPart);
		ret /= ((long double)freq.HighPart*4294967296)+((long double)freq.LowPart);
751
#else
752
		/* In MSVC, a long double does NOT have 19 decimals of precision */
753
		ret=(((long double)(tick.QuadPart%freq.QuadPart))/freq.QuadPart);
754
		ret+=tick.QuadPart/freq.QuadPart;
755
#endif
756
757
758
759
760
761
	}
	else {
		ret=GetTickCount();
		ret /= 1000;
	}
#else
762
	ret=time(NULL);	/* Weak implementation */
763
764
765
#endif
	return(ret);
}
766
767
768
769
770
771
772
773
774
775
776
777

/* Returns TRUE if specified process is running */
BOOL DLLCALL check_pid(pid_t pid)
{
#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;
778
		if(GetExitCodeProcess(h,(PDWORD)&code)==TRUE && code==STILL_ACTIVE)
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
			result=TRUE;
		CloseHandle(h);
	}
	return result;
#else
	return FALSE;	/* Need check_pid() definition! */
#endif
}

/* Terminate (unconditionally) the specified process */
BOOL DLLCALL terminate_pid(pid_t pid)
{
#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
}