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 "strwrap.h" /* strdup */
#include "ini_file.h"
#include <sys/ioctl.h> /* ioctl() */
#include <sys/utsname.h> /* uname() */
#elif defined(_WIN32)
#include <lm.h> /* NetWkstaGetInfo() */
#if WINVER >= 0x0600 // _WIN32_WINNT_VISTA
#define GetTickCount() GetTickCount64()
#endif
#else
#endif
#include "genwrap.h" /* Verify prototypes */
#include "xpendian.h" /* BYTE_SWAP */
/****************************************************************************/
/* 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);
if (numchars == -1)
numchars = strlen(dst);
if ((size_t)numchars >= size && numchars > 0) {
if (size == 0)
numchars = 0;
else
numchars = size - 1;
}
#ifdef NEEDS_STRLCPY
size_t strlcpy(char *dst, const char *src, size_t 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 */
}
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 */
/****************************************************************************/
len = strlen(str);
return (char*)&str[len - 1];
}
/****************************************************************************/
/* Return character value of C-escaped (\) character */
/****************************************************************************/
{
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 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)
{
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)
src = buf;
dst = str;
while ((ch = *(src++)) != 0) {
if (ch == '\\') /* escape */
ch = c_unescape_char_ptr(src, &src);
*(dst++) = ch;
}
free(buf);
}
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 "\\'";
char* c_escape_str(const char* src, char* dst, size_t maxlen, bool ctrl_only)
const char* s;
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) {
strlcpy(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);
/* 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':
break;
}
}
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) {
safe_snprintf(str, size, "%g year%s", value, value == 1 ? "":"s");
}
else if (value && fmod(value, one_week) == 0) {
safe_snprintf(str, size, "%g week%s", value, value == 1 ? "":"s");
}
else if (value && fmod(value, one_day) == 0) {
safe_snprintf(str, size, "%g day%s", value, value == 1 ? "":"s");
}
else if (value && fmod(value, one_hour) == 0) {
safe_snprintf(str, size, "%g hour%s", value, value == 1 ? "":"s");
}
else if (value && fmod(value, one_minute) == 0) {
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) {
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) {
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) {
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 */
/****************************************************************************/
while (*p) {
*p = toupper(*p);
p++;
}
}
/****************************************************************************/
/* Convert ASCIIZ string to lower case */
/****************************************************************************/
while (*p) {
*p = tolower(*p);
p++;
}
}
/****************************************************************************/
/* 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)
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;
/* 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;
}
}
#endif
/****************************************************************************/
/* Initialize (seed) the random number generator */
/****************************************************************************/
#if !(defined(HAS_SRANDOMDEV_FUNC) && defined(HAS_RANDOM_FUNC))
#if defined(HAS_DEV_URANDOM) && defined(URANDOM_DEV)
#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)
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
/****************************************************************************/
/* Return random number between 0 and n-1 */
/****************************************************************************/
#ifdef HAS_RANDOM_FUNC
long curr;
unsigned long limit;
limit = ((1UL << ((sizeof(long) * CHAR_BIT) - 1)) / n) * n - 1;
curr = random();
if (curr <= limit)
double f = 0;
int ret;
f = (double)rand() / (double)(RAND_MAX + 1);
ret = (int)(n * f);
} while (ret == n);
/****************************************************************************/
/* 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)
sprintf(str, "%lo", val);
sprintf(str, "%lu", val);
sprintf(str, "%lx", val);
sprintf(str, "bad radix: %d", radix);
#if (defined(__GNUC__) && (__GNUC__ < 5)) || !defined(__MINGW32__)
char* _i64toa(int64_t val, char* str, int radix)
{
sprintf(str, "%" PRIo64, val);
sprintf(str, "%" PRId64, val);
sprintf(str, "%" PRIx64, val);
sprintf(str, "bad radix: %d", radix);
break;
}
return str;
}
char* _ui64toa(uint64_t val, char* str, int radix)
{
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:
break;
case VER_PLATFORM_WIN32s:
winflavor = "Win32s ";
break;
case VER_PLATFORM_WIN32_WINDOWS:
winver.dwBuildNumber &= 0xffff;
if (winver.dwMajorVersion == 6 && winver.dwMinorVersion == 1) {
winver.dwMajorVersion = 7;
winver.dwMinorVersion = 0;
}
else {
static NTSTATUS (WINAPI * pRtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation) = NULL;
if (pRtlGetVersion == NULL) {
HINSTANCE ntdll = LoadLibrary("ntdll.dll");
pRtlGetVersion = (NTSTATUS (WINAPI *)(PRTL_OSVERSIONINFOW))GetProcAddress(ntdll, "RtlGetVersion");
}
if (pRtlGetVersion != NULL) {
pRtlGetVersion((PRTL_OSVERSIONINFOW)&winver);
if (winver.dwMajorVersion == 10 && winver.dwMinorVersion == 0 && winver.dwBuildNumber >= 22000)
winver.dwMajorVersion = 11;
}
}
safe_snprintf(str, size, "Windows %sVersion %lu.%lu"
, winflavor
, 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");
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);
*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);
/****************************************************************************/
/* 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);
#ifdef _PATH_BSHELL
#endif
#endif
/********************************************************/
/* Stupid real-time system clock implementation. */
/********************************************************/
return (clock_t)(t & 0xffffffff);
/****************************************************************************/
/* Skips all white-space chars at beginning of 'str' */
/****************************************************************************/
{
SKIP_WHITESPACE(str);
}
/****************************************************************************/
/* Truncates all white-space chars off end of 'str' (needed by STRERRROR) */
/****************************************************************************/
if (str != NULL) {
i = len = strlen(str);
while (i && IS_WHITESPACE(str[i - 1]))
if (i != len)
str[i] = 0; /* truncate */
}
/****************************************************************************/
/* 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)
for (sp = src, dp = dst; *sp != 0; sp++) {
if (*sp == '\n') {
while (dp != dst
&& (*(dp - 1) == ' ' || *(dp - 1) == '\t' || *(dp - 1) == '\r'))
dp--;
if (sp != src && *(sp - 1) == '\r')
*(dp++) = '\r';
}
free(src);