Newer
Older
/* 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) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU Lesser General Public License for more details: lgpl.txt or *
* http://www.fsf.org/copyleft/lesser.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include <string.h> /* strlen() */
#include <stdarg.h> /* vsnprintf() */
#include <stdlib.h> /* RAND_MAX */
#include <fcntl.h> /* O_NOCTTY */
#include <time.h> /* clock() */
#include <errno.h> /* errno */
#include <ctype.h> /* toupper/tolower */
#include <limits.h> /* CHAR_BIT */
#include <math.h> /* fmod */
#include "ini_file.h"
#include <sys/utsname.h> /* uname() */
#elif defined(_WIN32)
#include <windows.h>
#include <lm.h> /* NetWkstaGetInfo() */
#endif
#include "genwrap.h" /* Verify prototypes */
/****************************************************************************/
/* Used to replace snprintf() guarantees to terminate. */
/****************************************************************************/
int safe_snprintf(char *dst, size_t size, const char *fmt, ...)
{
va_list argptr;
int numchars;
va_start(argptr,fmt);
numchars= vsnprintf(dst,size,fmt,argptr);
va_end(argptr);
#ifdef _MSC_VER
if(numchars==-1)
numchars=strlen(dst);
if(numchars>=(int)size && numchars>0)
numchars=size-1;
return(numchars);
}
#ifdef NEEDS_STRLCPY
size_t strlcpy(char *dst, const char *src, size_t dsize)
{
const char *osrc = src;
size_t nleft = dsize;
/* Copy as many bytes as will fit. */
if (nleft != 0) {
while (--nleft != 0) {
if ((*dst++ = *src++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src. */
if (nleft == 0) {
if (dsize != 0)
*dst = '\0'; /* NUL-terminate dst */
while (*src++)
;
}
return(src - osrc - 1); /* count does not include NUL */
}
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
size_t
strlcat(char *dst, const char *src, size_t dsize)
{
const char *odst = dst;
const char *osrc = src;
size_t n = dsize;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end. */
while (n-- != 0 && *dst != '\0')
dst++;
dlen = dst - odst;
n = dsize - dlen;
if (n-- == 0)
return(dlen + strlen(src));
while (*src != '\0') {
if (n != 0) {
*dst++ = *src;
n--;
}
src++;
}
*dst = '\0';
return(dlen + (src - osrc)); /* count does not include NUL */
}
#endif
/****************************************************************************/
/* Case insensitive version of strstr() - currently heavy-handed */
/****************************************************************************/
char* strcasestr(const char* haystack, const char* needle)
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;
}
#endif
/****************************************************************************/
/* Return last character of string */
/****************************************************************************/
{
size_t len;
len = strlen(str);
if(len)
}
/****************************************************************************/
/* Return character value of C-escaped (\) character */
/****************************************************************************/
{
switch(ch) {
case 'e': return(ESC); /* 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');
}
return(ch);
}
/****************************************************************************/
/* Return character value of C-escaped (\) character literal sequence */
/* supports \x## (hex) and \### sequences (for octal or decimal) */
/****************************************************************************/
char c_unescape_char_ptr(const char* str, char** endptr)
{
char ch;
if(*str == 'x') {
int digits = 0; // \x## for hexadecimal character literals (only 2 digits supported)
++str;
ch = 0;
while(digits < 2 && IS_HEXDIGIT(*str)) {
ch *= 0x10;
ch += HEX_CHAR_TO_INT(*str);
str++;
digits++;
}
#ifdef C_UNESCAPE_OCTAL_SUPPORT
} else if(IS_OCTDIGIT(*str)) {
int digits = 0; // \### for octal character literals (only 3 digits supported)
ch = 0;
while(digits < 3 && IS_OCTDIGIT(*str)) {
ch *= 8;
ch += OCT_CHAR_TO_INT(*str);
str++;
digits++;
}
#else
} else if(IS_DIGIT(*str)) {
int digits = 0; // \### for decimal character literals (only 3 digits supported)
ch = 0;
while(digits < 3 && IS_DIGIT(*str)) {
ch *= 10;
ch += DEC_CHAR_TO_INT(*str);
str++;
digits++;
}
#endif
} else
ch=c_unescape_char(*(str++));
if(endptr!=NULL)
*endptr=(char*)str;
return ch;
}
/****************************************************************************/
/* Unescape a C string, in place */
/****************************************************************************/
{
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 */
ch=c_unescape_char_ptr(src,&src);
*(dst++)=ch;
}
*dst=0;
free(buf);
return(str);
}
{
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* c_escape_str(const char* src, char* dst, size_t maxlen, bool ctrl_only)
char* d;
const char* e;
for(s=src,d=dst;*s && (size_t)(d-dst)<maxlen;s++) {
if((!ctrl_only || (uchar)*s < ' ') && (e=c_escape_char(*s))!=NULL) {
strncpy(d,e,maxlen-(d-dst));
d+=strlen(d);
} else if((uchar)*s < ' ' || (uchar)*s >= '\x7f') {
d += safe_snprintf(d, maxlen-(d-dst), "\\x%02X", (uchar)*s);
} else *d++=*s;
}
*d=0;
return(dst);
}
/* 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 parse_byte_count(const char* str, uint64_t unit)
{
char* p=NULL;
double bytes;
bytes=strtod(str,&p);
if(p!=NULL) {
SKIP_WHITESPACE(p);
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;
}
}
if(unit > 1)
bytes /= unit;
if(bytes < 0 || bytes > (double)INT64_MAX)
return INT64_MAX;
return (int64_t)bytes;
}
static const double one_pebibyte = 1024.0*1024.0*1024.0*1024.0*1024.0;
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
and a single letter multiplier/suffix.
For values evenly divisible by 1024, no suffix otherwise.
*/
char* byte_count_to_str(uint64_t bytes, char* str, size_t size)
{
if(bytes && fmod((double)bytes,one_pebibyte)==0)
safe_snprintf(str, size, "%gP",bytes/one_pebibyte);
else 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);
else
safe_snprintf(str, size, "%"PRIi64, bytes);
return str;
}
/* Convert a rounded byte count to a string with a floating point value
with a single decimal place and a single letter multiplier/suffix.
'unit' is the smallest divisor used.
*/
char* byte_estimate_to_str(uint64_t bytes, char* str, size_t size, uint64_t unit, int precision)
if(bytes >= one_pebibyte)
safe_snprintf(str, size, "%1.*fP", precision, bytes/one_pebibyte);
else if(bytes >= one_tebibyte || unit == one_tebibyte)
safe_snprintf(str, size, "%1.*fT", precision, bytes/one_tebibyte);
else if(bytes >= one_gibibyte || unit == one_gibibyte)
safe_snprintf(str, size, "%1.*fG", precision, bytes/one_gibibyte);
else if(bytes >= one_mebibyte || unit == one_mebibyte)
safe_snprintf(str, size, "%1.*fM", precision, bytes/one_mebibyte);
else if(bytes >= one_kibibyte || unit == one_kibibyte)
safe_snprintf(str, size, "%1.*fK", precision, bytes/one_kibibyte);
safe_snprintf(str, size, "%"PRIi64, bytes);
static const double one_year = 365.0*24.0*60.0*60.0; // Actually, 365.2425
static const double one_week = 7.0*24.0*60.0*60.0;
static const double one_day = 24.0*60.0*60.0;
static const double one_hour = 60.0*60.0;
static const double one_minute = 60.0;
/* Parse a duration string, default unit is in seconds */
/* (Y)ears, (W)eeks, (D)ays, (H)ours, and (M)inutes */
/* Return value is in seconds */
{
char* p=NULL;
double t;
t=strtod(str,&p);
if(p!=NULL) {
SKIP_WHITESPACE(p);
switch(toupper(*p)) {
case 'Y': t*= one_year; break;
case 'W': t*= one_week; break;
case 'D': t*= one_day; break;
case 'H': t*= one_hour; break;
case 'M': t*= one_minute; break;
}
}
return t;
}
/* Convert a duration (in seconds) to a string
* with a single letter multiplier/suffix:
* (y)ears, (w)eeks, (d)ays, (h)ours, (m)inutes, or (s)econds
*/
char* duration_to_str(double value, char* str, size_t size)
{
if(value && fmod(value, one_year)==0)
safe_snprintf(str, size, "%gy",value/one_year);
else if(value && fmod(value, one_week)==0)
safe_snprintf(str, size, "%gw",value/one_week);
else if(value && fmod(value, one_day)==0)
safe_snprintf(str, size, "%gd",value/one_day);
else if(value && fmod(value, one_hour)==0)
safe_snprintf(str, size, "%gh",value/one_hour);
else if(value && fmod(value, one_minute)==0)
safe_snprintf(str, size, "%gm",value/one_minute);
else
safe_snprintf(str, size, "%gs",value);
return str;
}
/* 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* duration_to_vstr(double value, char* str, size_t size)
{
if(value && fmod(value, one_year)==0) {
value /= one_year;
safe_snprintf(str, size, "%g year%s", value, value == 1 ? "":"s");
}
else if(value && fmod(value, one_week)==0) {
value /= one_week;
safe_snprintf(str, size, "%g week%s", value, value == 1 ? "":"s");
}
else if(value && fmod(value, one_day)==0) {
value /= one_day;
safe_snprintf(str, size, "%g day%s", value, value == 1 ? "":"s");
}
else if(value && fmod(value, one_hour)==0) {
value /= one_hour;
safe_snprintf(str, size, "%g hour%s", value, value == 1 ? "":"s");
}
else if(value && fmod(value, one_minute)==0) {
value /= one_minute;
safe_snprintf(str, size, "%g minute%s", value, value == 1 ? "":"s");
}
else
safe_snprintf(str, size, "%g second%s", value, value == 1 ? "":"s");
return str;
}
/* Convert a duration estimate (in seconds) to a string
* with a single letter multiplier/suffix:
* (y)ears, (w)eeks, (d)ays, (h)ours, (m)inutes, or (s)econds
*/
char* duration_estimate_to_str(double value, char* str, size_t size, double unit, int precision)
{
if(value && fmod(value, one_year) == 0)
safe_snprintf(str, size, "%gy", value/one_year);
else if(value >= one_year || unit == one_year)
safe_snprintf(str, size, "%1.*fy", precision, value/one_year);
else if(value && fmod(value, one_week) == 0)
safe_snprintf(str, size, "%gw", value/one_week);
else if(unit == one_week) // prefer "90 days" over "12.9 weeks"
safe_snprintf(str, size, "%1.*fw", precision, value/one_week);
else if(value && fmod(value, one_day) == 0)
safe_snprintf(str, size, "%gd", value/one_day);
else if(value >= one_day || unit == one_day)
safe_snprintf(str, size, "%1.*fd", precision, value/one_day);
else if(value && fmod(value, one_hour) == 0)
safe_snprintf(str, size, "%gh", value/one_hour);
else if(value >= one_hour || unit == one_hour)
safe_snprintf(str, size, "%1.*fh", precision, value/one_hour);
else if(value && fmod(value, one_minute) == 0)
safe_snprintf(str, size, "%gm", value/one_minute);
else if(value >= one_minute || unit == one_minute)
safe_snprintf(str, size, "%1.*fm", precision, value/one_minute);
safe_snprintf(str, size, "%gs",value);
return str;
}
/* Convert a duration estimate (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* duration_estimate_to_vstr(double value, char* str, size_t size, double unit, int precision)
{
if(value && fmod(value, one_year) == 0) {
value /= one_year;
safe_snprintf(str, size, "%g year%s", value, value == 1 ? "":"s");
}
else if(value >= one_year || unit == one_year) {
safe_snprintf(str, size, "%1.*f year%s", precision, value, value == 1 ? "":"s");
else if(value && fmod(value, one_week) == 0) {
value /= one_week;
safe_snprintf(str, size, "%g week%s", value, value == 1 ? "":"s");
}
else if(unit == one_week) { // prefer "90 days" over "12.9 weeks"
safe_snprintf(str, size, "%1.*f week%s", precision, value, value == 1 ? "":"s");
else if(value && fmod(value, one_day) == 0) {
value /= one_day;
safe_snprintf(str, size, "%g day%s", value, value == 1 ? "":"s");
}
else if(value >= one_day || unit == one_day) {
value /= one_day;
safe_snprintf(str, size, "%1.*f day%s", precision, value, value == 1 ? "":"s");
else if(value && fmod(value, one_hour) == 0) {
value /= one_hour;
safe_snprintf(str, size, "%g hour%s", value, value == 1 ? "":"s");
}
else if(value >= one_hour || unit == one_hour) {
value /= one_hour;
safe_snprintf(str, size, "%1.*f hour%s", precision, value, value == 1 ? "":"s");
else if(value && fmod(value, one_minute) == 0) {
value /= one_minute;
safe_snprintf(str, size, "%g minute%s", value, value == 1 ? "":"s");
}
else if(value >= one_minute || unit == one_minute) {
value /= one_minute;
safe_snprintf(str, size, "%1.*f minute%s", precision, value, value == 1 ? "":"s");
}
else
safe_snprintf(str, size, "%g second%s", value, value == 1 ? "":"s");
return str;
}
/****************************************************************************/
/* Convert ASCIIZ string to upper case */
/****************************************************************************/
{
char* p=str;
while(*p) {
*p=toupper(*p);
p++;
}
return(str);
}
/****************************************************************************/
/* Convert ASCIIZ string to lower case */
/****************************************************************************/
{
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
#if !defined(__unix__)
/****************************************************************************/
/* Implementations of the recursive (thread-safe) version of strtok */
/* Thanks to Apache Portable Runtime (APR) */
/****************************************************************************/
char* 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;
if(!*str) { /* no more tokens */
*last = str;
return NULL;
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
/****************************************************************************/
/* Initialize (seed) the random number generator */
/****************************************************************************/
#if !(defined(HAS_SRANDOMDEV_FUNC) && defined(HAS_RANDOM_FUNC))
#if defined(HAS_DEV_URANDOM) && defined(URANDOM_DEV)
int rf;
#endif
#if defined(HAS_SRANDOMDEV_FUNC) && defined(HAS_RANDOM_FUNC)
srandomdev();
return;
#if defined(HAS_DEV_URANDOM) && defined(URANDOM_DEV)
if((rf=open(URANDOM_DEV, O_RDONLY))!=-1) {
if(read(rf, &seed, sizeof(seed)) != sizeof seed)
seed = UINT_MAX;
else {
#endif
unsigned curtime = (unsigned)time(NULL);
unsigned process_id = (unsigned)GetCurrentProcessId();
seed = curtime ^ BYTE_SWAP_INT(process_id);
#if defined(_WIN32) || defined(GetCurrentThreadId)
seed ^= (unsigned)(uintptr_t)GetCurrentThreadId();
#if defined(HAS_DEV_URANDOM) && defined(URANDOM_DEV)
}
#ifdef HAS_RANDOM_FUNC
srandom(seed);
#else
/****************************************************************************/
/* Return random number between 0 and n-1 */
/****************************************************************************/
#ifdef HAS_RANDOM_FUNC
long curr;
unsigned long limit;
if(n<2)
return(0);
limit = ((1UL<<((sizeof(long)*CHAR_BIT)-1)) / n) * n - 1;
while(1) {
curr=random();
if(curr <= limit)
return(curr % n);
}
double f=0;
int ret;
if(n<2)
return(0);
f=(double)rand()/(double)(RAND_MAX+1);
ret=(int)(n*f);
} while(ret==n);
return(ret);
/****************************************************************************/
/* Return ASCII string representation of ulong */
/* There may be a native GNU C Library function to this... */
/****************************************************************************/
#if !defined(_MSC_VER) && !defined(__BORLANDC__) && !defined(__WATCOMC__)
char* 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);
}
#if (defined(__GNUC__) && (__GNUC__ < 5)) || !defined(__MINGW32__)
char* _i64toa(int64_t val, char* str, int radix)
{
switch(radix) {
case 8:
sprintf(str,"%"PRIo64,val);
break;
case 10:
sprintf(str,"%"PRId64,val);
break;
case 16:
sprintf(str,"%"PRIx64,val);
break;
default:
sprintf(str,"bad radix: %d",radix);
break;
}
return str;
}
char* _ui64toa(uint64_t val, char* str, int radix)
{
switch(radix) {
case 8:
sprintf(str,"%"PRIo64,val);
break;
case 10:
sprintf(str,"%"PRIu64,val);
break;
case 16:
sprintf(str,"%"PRIx64,val);
break;
default:
sprintf(str,"bad radix: %d",radix);
break;
}
return str;
}
#endif
/****************************************************************************/
/* Write the version details of the current operating system into str */
/****************************************************************************/
char* os_version(char *str, size_t size)
{
#if defined(__OS2__) && defined(__BORLANDC__)
safe_snprintf(str, size, "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);
#pragma warning(suppress : 4996) // error C4996: 'GetVersionExA': was declared deprecated
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;
}
if(winver.dwMajorVersion == 10 && winver.dwMinorVersion == 0) {
static NTSTATUS (WINAPI *pRtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation) = NULL;
if(pRtlGetVersion == NULL) {
HINSTANCE ntdll = LoadLibrary("ntdll.dll");
if(ntdll != NULL)
pRtlGetVersion = (NTSTATUS (WINAPI *)(PRTL_OSVERSIONINFOW))GetProcAddress(ntdll, "RtlGetVersion");
}
if(pRtlGetVersion != NULL)
pRtlGetVersion((PRTL_OSVERSIONINFOW)&winver);
if(winver.dwBuildNumber >= 22000)
winver.dwMajorVersion = 11;
}
else if(winver.dwMajorVersion == 6 && winver.dwMinorVersion == 2) {
/* Work-around Microsoft Windows 8.1 stupidity where GetVersionEx() lies about the current OS version */
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;
}
}
else if(winver.dwMajorVersion == 6 && winver.dwMinorVersion == 1) {
winver.dwMajorVersion = 7;
winver.dwMinorVersion = 0;
}
safe_snprintf(str, size, "Windows %sVersion %lu.%lu"
,winver.dwMajorVersion, winver.dwMinorVersion);
if(winver.dwBuildNumber)
sprintf(str+strlen(str), " (Build %lu)", winver.dwBuildNumber);
if(winver.szCSDVersion[0])
sprintf(str+strlen(str), " %s", winver.szCSDVersion);
#elif defined(__unix__)
FILE* fp = fopen("/etc/os-release", "r");
if(fp == NULL)
fp = fopen("/usr/lib/os-release", "r");
if(fp != NULL) {
char value[INI_MAX_VALUE_LEN];
char* p = iniReadString(fp, NULL, "PRETTY_NAME", "Unix", value);
fclose(fp);
SKIP_CHAR(p, '"');
strncpy(str, p, size);
p = lastchar(str);
if(*p == '"')
*p = '\0';
} else {
struct utsname unixver;
if(uname(&unixver) != 0)
safe_snprintf(str, size, "Unix (uname errno: %d)",errno);
safe_snprintf(str, size, "%s %s"
,unixver.sysname /* e.g. "Linux" */
,unixver.release /* e.g. "2.2.14-5.0" */
);
}
safe_snprintf(str, size, "DOS %u.%02u",_osmajor,_osminor);
#endif
return(str);
}
/****************************************************************************/
/* Write the CPU architecture according to the Operating System into str */
/****************************************************************************/
char* os_cpuarch(char *str, size_t size)
{
#if defined(_WIN32)
SYSTEM_INFO sysinfo;
#if _WIN32_WINNT < 0x0501
GetSystemInfo(&sysinfo);
#else
GetNativeSystemInfo(&sysinfo);
switch(sysinfo.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_AMD64:
safe_snprintf(str, size, "x64");
break;
case PROCESSOR_ARCHITECTURE_ARM:
safe_snprintf(str, size, "ARM");
break;
#if defined PROCESSOR_ARCHITECTURE_ARM64
case PROCESSOR_ARCHITECTURE_ARM64:
safe_snprintf(str, size, "ARM64");
break;
#endif
case PROCESSOR_ARCHITECTURE_IA64:
safe_snprintf(str, size, "IA-64");
break;
case PROCESSOR_ARCHITECTURE_INTEL:
safe_snprintf(str, size, "x86");
break;
default:
safe_snprintf(str, size, "unknown");
break;
}
#elif defined(__unix__)
struct utsname unixver;
if(uname(&unixver) == 0)
safe_snprintf(str, size, "%s", unixver.machine);
else
safe_snprintf(str, size, "unknown");
#endif
return str;
}
{
char* shell=getenv(OS_CMD_SHELL_ENV_VAR);
if(shell==NULL)
#ifdef _PATH_BSHELL
shell=_PATH_BSHELL;
#else
shell="/bin/sh";
#endif
#endif
return(shell);
}
/********************************************************/
/* Stupid real-time system clock implementation. */
/********************************************************/
return (clock_t)(t&0xffffffff);
/****************************************************************************/
/* Skips all white-space chars at beginning of 'str' */
/****************************************************************************/
{
SKIP_WHITESPACE(str);
return(str);
}
/****************************************************************************/
/* Truncates all white-space chars off end of 'str' (needed by STRERRROR) */
/****************************************************************************/
size_t i,len;
if(str!=NULL) {
i=len=strlen(str);
while(i && IS_WHITESPACE(str[i-1]))
i--;
if(i!=len)
str[i]=0; /* truncate */
}
return(str);
}
/****************************************************************************/
/* 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) */
/****************************************************************************/
{
char* sp;
char* dp;
char* src;
if((src=strdup(dst))==NULL)
return(dst);
for(sp=src, dp=dst; *sp!=0; sp++) {
if(*sp=='\n') {
&& (*(dp-1)==' ' || *(dp-1)=='\t' || *(dp-1)=='\r'))
dp--;
if(sp!=src && *(sp-1)=='\r')
*(dp++)='\r';
}