/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/*
Copyright (c) 2013 D. Richard Hipp
This program is free software; you can redistribute it and/or
modify it under the terms of the Simplified BSD License (also
known as the "2-Clause License" or "FreeBSD License".)
This program is distributed in the hope that it will be useful,
but without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
Author contact information:
drh@hwaci.com
http://www.hwaci.com/drh/
*****************************************************************************
This file houses some context-independent API routines as well as
some of the generic helper functions and types.
*/
#include "fossil-scm/fossil-internal.h"
#include <assert.h>
#include <stdlib.h> /* malloc() and friends, qsort() */
#include <memory.h> /* memset() */
#include <time.h> /* strftime() and gmtime() */
#if defined(_WIN32) || defined(WIN32)
# include <io.h>
#define isatty(h) _isatty(h)
#else
# include <unistd.h> /* isatty() */
#endif
/* extern int isatty(int); */
#define MARKER(pfexp) \
do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \
printf pfexp; \
} while(0)
/*
Please keep all fsl_XXX_empty initializers in one place (here)
and lexically sorted.
*/
const fsl_acache fsl_acache_empty = fsl_acache_empty_m;
const fsl_branch_opt fsl_branch_opt_empty = fsl_branch_opt_empty_m;
const fsl_buffer fsl_buffer_empty = fsl_buffer_empty_m;
const fsl_card_F fsl_card_F_empty = fsl_card_F_empty_m;
const fsl_card_J fsl_card_J_empty = fsl_card_J_empty_m;
const fsl_card_Q fsl_card_Q_empty = fsl_card_Q_empty_m;
const fsl_card_T fsl_card_T_empty = fsl_card_T_empty_m;
const fsl_checkin_opt fsl_checkin_opt_empty = fsl_checkin_opt_empty_m;
const fsl_confirmer fsl_confirmer_empty = fsl_confirmer_empty_m;
const fsl_cx fsl_cx_empty = fsl_cx_empty_m;
const fsl_cx_config fsl_cx_config_empty = fsl_cx_config_empty_m;
const fsl_db fsl_db_empty = fsl_db_empty_m;
const fsl_deck fsl_cardset_empty = fsl_deck_empty_m;
const fsl_deck fsl_deck_empty = fsl_deck_empty_m;
const fsl_error fsl_error_empty = fsl_error_empty_m;
const fsl_fstat fsl_fstat_empty = fsl_fstat_empty_m;
const fsl_cx_init_opt fsl_cx_init_opt_default = fsl_cx_init_opt_default_m;
const fsl_cx_init_opt fsl_cx_init_opt_empty = fsl_cx_init_opt_empty_m;
const fsl_list fsl_list_empty = fsl_list_empty_m;
const fsl_outputer fsl_outputer_FILE = fsl_outputer_FILE_m;
const fsl_outputer fsl_outputer_empty = fsl_outputer_empty_m;
const fsl_pathfinder fsl_pathfinder_empty = fsl_pathfinder_empty_m;
const fsl_pq fsl_pq_empty = fsl_pq_empty_m;
const fsl_repo_create_opt fsl_repo_create_opt_empty = fsl_repo_create_opt_empty_m;
const fsl_sha1_cx fsl_sha1_cx_empty = fsl_sha1_cx_empty_m;
const fsl_state fsl_state_empty = fsl_state_empty_m;
const fsl_stmt fsl_stmt_empty = fsl_stmt_empty_m;
const fsl_timer_state fsl_timer_state_empty = fsl_timer_state_empty_m;
const fsl_xlinker fsl_xlinker_empty = fsl_xlinker_empty_m;
const fsl_xlinker_list fsl_xlinker_list_empty = fsl_xlinker_list_empty_m;
const fsl_zip_writer fsl_zip_writer_empty = fsl_zip_writer_empty_m;
const fsl_allocator fsl_allocator_stdalloc = {
fsl_realloc_f_stdalloc,
NULL
};
fsl_lib_configurable_t fsl_lib_configurable = {
{/*allocator*/ fsl_realloc_f_stdalloc, NULL}
};
void * fsl_malloc( fsl_size_t n ){
return n
? fsl_realloc(NULL, n)
: NULL;
}
void fsl_free( void * mem ){
if(mem) fsl_realloc(mem, 0);
}
void * fsl_realloc( void * mem, fsl_size_t n ){
#define FLCA fsl_lib_configurable.allocator
if(!mem){
/* malloc() */
return n
? FLCA.f(FLCA.state, NULL, n)
: NULL;
}else if(!n){
/* free() */
FLCA.f(FLCA.state, mem, 0);
return NULL;
}else{
/* realloc() */
return FLCA.f(FLCA.state, mem, n);
}
#undef FLC
}
#if 0
/* highly arguable */
/**
A convenience form of fsl_free() which frees multiple memory
blocks of memory. It expects n to reflect the number of
arugments after n, and it passes each argument to fsl_free().
Results are undefined (but guaranteed to be poor!) if passed
fewer than n additional arguments. It will leak if passed more
than n additional arguments.
This is a no-op if n is 0.
Example:
@code
fsl_free_multiple( 3, foo, bar, baz );
@endcode
Design note: we "could" use a trailing NULL entry to signal end
of list (and the original implementation did), but for the cases
where this utility helps, it could lead to leaks because not all
values need to have been allocated at the time this would be
called.
*/
void fsl_free_multiple( fsl_size_t n, ... );
void fsl_free_multiple( fsl_size_t n, ... ){
fsl_size_t i;
va_list va;
va_start(va, n);
for( i = 0; i < n; ++i ){
fsl_free(va_arg(va,void *));
}
va_end(va);
}
/**
A convenience form of fsl_free() which frees multiple
memory blocks. It calls fsl_free() on mem, then on
each subsequent (void*) argument until an argument with
the value 0/NULL is reached, at which point it stops.
If !mem then this is a no-op.
Results are undefined if the argument list has no trailing 0
entry.
Example:
@code
fsl_free_n( foo, bar, baz, 0 );
@endcode
*/
void fsl_free_n( void * mem, ... ){
va_list va;
if(!mem) return;
fsl_free(mem);
va_start(va, mem);
while( (mem = va_arg(va,void *)) ){
fsl_free(mem);
}
va_end(va);
}
#endif
void * fsl_realloc_f_stdalloc(void * state, void * mem, fsl_size_t n){
if(!mem){
return malloc(n);
}else if(!n){
free(mem);
return NULL;
}else{
return realloc(mem, n);
}
}
char fsl_is_uuid(char const * str){
fsl_size_t const len = fsl_strlen(str);
return (FSL_UUID_STRLEN==len)
&& fsl_validate16(str, FSL_UUID_STRLEN);
}
void fsl_error_clear( fsl_error * err ){
if(err){
fsl_buffer_clear(&err->msg);
*err = fsl_error_empty;
}
}
void fsl_error_reset( fsl_error * err ){
if(err){
err->code = 0;
err->msg.used = 0;
if(err->msg.mem) err->msg.mem[0] = 0;
}
}
int fsl_error_copy( fsl_error const * src, fsl_error * dest ){
if(!src || !dest || (src==dest)) return FSL_RC_MISUSE;
else {
int rc = 0;
dest->msg.used = 0;
dest->code = src->code;
if(FSL_RC_OOM!=src->code){
rc = fsl_buffer_append( &dest->msg, src->msg.mem, src->msg.used );
}
return rc;
}
}
void fsl_error_move( fsl_error * lower, fsl_error * higher ){
fsl_error const err = *lower;
*lower = *higher;
lower->code = 0;
lower->msg.used = 0;
*higher = err;
}
int fsl_error_setv( fsl_error * err, int code, char const * fmt,
va_list args ){
if(!err) return FSL_RC_MISUSE;
else if(!code){ /* clear error state */
err->code = 0;
err->msg.used = 0;
if(err->msg.mem){
err->msg.mem[0] = 0;
}
return 0;
}else{
int rc = 0;
err->msg.used = 0;
err->code = code;
if(FSL_RC_OOM!=code){
rc = fmt
? fsl_buffer_appendfv(&err->msg, fmt, args)
: fsl_buffer_append(&err->msg, fsl_rc_cstr(code), -1);
}
return rc ? rc : code;
}
}
int fsl_error_set( fsl_error * err, int code, char const * fmt,
... ){
int rc;
va_list args;
va_start(args,fmt);
rc = fsl_error_setv(err, code, fmt, args);
va_end(args);
return rc;
}
int fsl_error_get( fsl_error const * err, char const ** str, fsl_size_t * len ){
if(!err) return FSL_RC_MISUSE;
else{
if(str) *str = err->msg.used
? (char const *)err->msg.mem
: NULL;
if(len) *len = err->msg.used;
return err->code;
}
}
char const * fsl_rc_cstr(int rc){
fsl_rc_t const RC = (fsl_rc_t)rc
/* we do this so that gcc will warn if the switch() below is
missing any fsl_rc_t entries. */
;
switch(RC){
#define STR(T) case FSL_RC_##T: return "FSL_RC_" #T
STR(ACCESS);
STR(ALREADY_EXISTS);
STR(AMBIGUOUS);
STR(BREAK);
STR(CA_SYNTAX);
STR(CHECKSUM_MISMATCH);
STR(CONSISTENCY);
STR(DB);
STR(DELTA_INVALID_OPERATOR);
STR(DELTA_INVALID_SEPARATOR);
STR(DELTA_INVALID_SIZE);
STR(DELTA_INVALID_TERMINATOR);
STR(ERROR);
STR(IO);
STR(MISUSE);
STR(NOT_A_CHECKOUT);
STR(NOT_A_REPO);
STR(NOT_FOUND);
STR(NYI);
STR(OK);
STR(OOM);
STR(RANGE);
STR(REPO_MISMATCH);
STR(REPO_NEEDS_REBUILD);
STR(REPO_VERSION);
STR(SIZE_MISMATCH);
STR(STEP_DONE);
STR(STEP_ERROR);
STR(STEP_ROW);
STR(TYPE);
#undef STR
}
return "Unknown result code";
}
char const * fsl_library_version(){
return FSL_LIBRARY_VERSION;
}
char fsl_library_version_matches(char const * yourLibVersion){
return 0 == fsl_strcmp(FSL_LIBRARY_VERSION, yourLibVersion);
}
fsl_double_t fsl_unix_to_julian( fsl_time_t unix_ ){
return (unix_ * 1.0 / 86400.0 ) + 2440587.5;
}
fsl_double_t fsl_julian_now(){
return fsl_unix_to_julian( time(0) );
}
int fsl_strcmp(const char *zA, const char *zB){
if( zA==0 ){
if( zB==0 ) return 0;
return -1;
}else if( zB==0 ){
return +1;
}else{
int a, b;
do{
a = *zA++;
b = *zB++;
}while( a==b && a!=0 );
return ((unsigned char)a) - (unsigned char)b;
}
}
int fsl_strcmp_cmp( void const * lhs, void const * rhs ){
return fsl_strcmp((char const *)lhs, (char const *)rhs);
}
int fsl_strncmp(const char *zA, const char *zB, fsl_size_t nByte){
if( !zA ) return zB ? -1 : 0;
else if( !zB ) return +1;
else if(!nByte) return 0;
else{
int a, b;
do{
a = *zA++;
b = *zB++;
}while( a==b && a!=0 && (--nByte)>0 );
return (nByte>0) ? (((unsigned char)a) - (unsigned char)b) : 0;
}
}
int fsl_uuidcmp( fsl_uuid_cstr lhs, fsl_uuid_cstr rhs ){
return fsl_strncmp( lhs, rhs, FSL_UUID_STRLEN );
}
int fsl_strnicmp(const char *zA, const char *zB, fsl_int_t nByte){
if( zA==0 ){
if( zB==0 ) return 0;
return -1;
}else if( zB==0 ){
return +1;
}
if( nByte<0 ) nByte = (fsl_int_t)fsl_strlen(zB);
return sqlite3_strnicmp(zA, zB, nByte);
}
int fsl_stricmp(const char *zA, const char *zB){
fsl_int_t nByte;
int rc;
if( zA==0 ){
if( zB==0 ) return 0;
return -1;
}else if( zB==0 ){
return +1;
}
nByte = (fsl_int_t)fsl_strlen(zB);
rc = sqlite3_strnicmp(zA, zB, nByte);
if( rc==0 && zA[nByte] ) rc = 1;
return rc;
}
int fsl_stricmp_cmp( void const * lhs, void const * rhs ){
return fsl_stricmp((char const *)lhs, (char const *)rhs);
}
fsl_size_t fsl_strlen( char const * src ){
fsl_size_t i = 0;
if(src) for( ; *src; ++i, ++src ){}
return i;
}
char * fsl_strndup( char const * src, fsl_int_t len ){
if(!src) return NULL;
else{
fsl_buffer b = fsl_buffer_empty;
if(len<0) len = (fsl_int_t)fsl_strlen(src);
fsl_buffer_append( &b, src, len );
return (char*)b.mem;
}
}
char * fsl_strdup( char const * src ){
return fsl_strndup(src, -1);
}
/*
Return TRUE if the string begins with something that looks roughly
like an ISO date/time string. The SQLite date/time functions will
have the final say-so about whether or not the date/time string is
well-formed.
*/
char fsl_str_is_date(const char *z){
if(!z || !*z) return 0;
if( !fsl_isdigit(z[0]) ) return 0;
if( !fsl_isdigit(z[1]) ) return 0;
if( !fsl_isdigit(z[2]) ) return 0;
if( !fsl_isdigit(z[3]) ) return 0;
if( z[4]!='-') return 0;
if( !fsl_isdigit(z[5]) ) return 0;
if( !fsl_isdigit(z[6]) ) return 0;
if( z[7]!='-') return 0;
if( !fsl_isdigit(z[8]) ) return 0;
if( !fsl_isdigit(z[9]) ) return 0;
return 1;
}
char fsl_str_bool( char const * s ){
switch(s ? *s : 0){
case 0: case '0':
case 'f': case 'F':
case 'n': case 'N':
return 0;
case '1':
case 't': case 'T':
case 'y': case 'Y':
return 1;
default: {
char buf[5] = {0,0,0,0,0};
int i;
for( i = 0; (i<5) && *s; ++i, ++s ){
buf[i] = fsl_tolower(*s);
}
if(0==fsl_strncmp(buf, "off", 3)) return 0;
return 1;
}
}
}
char * fsl_guess_user_name(){
char const ** e;
static char const * list[] = {
"FOSSIL_USER",
#if defined(_WIN32)
"USERNAME",
#else
"USER",
"LOGNAME",
#endif
NULL /* sentinel */
};
char * rv = NULL;
for( e = list; *e; ++e ){
rv = fsl_getenv(*e);
if(rv){
/*
Because fsl_getenv() has the odd requirement of needing
fsl_filename_free(), and we want strings returned from this
function to be safe for passing to fsl_free(), we have to dupe
the string. We "could" block this off to happen only on the
platforms for which fsl_getenv() requires an extra encoding
step, but that would likely eventually lead to a bug.
*/
char * kludge = fsl_strdup(rv);
fsl_filename_free(rv);
rv = kludge;
break;
}
}
return rv;
}
void fsl_fatal( int code, char const * fmt, ... ){
static char inFatal = 0;
if(inFatal){
/* This can only happen if the fsl_appendv() bits
call this AND trigger it via fsl_fprintf() below,
neither of which is currently the case.
*/
assert(!"fsl_fatal() called recursively.");
abort();
}else{
va_list args;
inFatal = 1;
fsl_fprintf(stderr, "FATAL ERROR: code=%d (%s)\n",
code, fsl_rc_cstr(code));
va_start(args,fmt);
fsl_fprintfv(stderr, fmt, args);
va_end(args);
fwrite("\n", 1, 1, stderr);
exit(EXIT_FAILURE);
}
}
#if 0
char * fsl_unix_to_iso8601( fsl_time_t u ){
enum { BufSize = 20 };
char buf[BufSize]= {0,};
time_t const tt = (time_t)u;
fsl_strftime( buf, BufSize, "%Y-%m-%dT%H:%M:%S", gmtime(&tt) );
return fsl_strdup(buf);
}
#endif
char fsl_iso8601_to_julian( char const * zDate, fsl_double_t * out ){
/* Adapted from this article:
http://quasar.as.utexas.edu/BillInfo/JulianDatesG.html
*/
char const * p = zDate;
int y = 0, m = 0, d = 0;
int h = 0, mi = 0, s = 0, f = 0;
double j = 0;
if(!zDate || !*zDate){
return 0;
}
#define DIG(NUM) if(!fsl_isdigit(*p)) return 0; \
NUM=(NUM*10)+(*(p++)-'0')
DIG(y);DIG(y);DIG(y);DIG(y);
if('-'!=*p++) return 0;
DIG(m);DIG(m);
if('-'!=*p++) return 0;
DIG(d);DIG(d);
if('T' != *p++) return 0;
DIG(h);DIG(h);
if(':'!=*p++) return 0;
DIG(mi);DIG(mi);
if(':'!=*p++) return 0;
DIG(s);DIG(s);
if('.'==*p++){
DIG(f);DIG(f);DIG(f);
}
if(out){
typedef fsl_int64_t TI;
TI A, B, C, E, F;
if(m<3){
--y;
m += 12;
}
A = y/100;
B = A/4;
C = 2-A+B;
E = (TI)(365.25*(y+4716));
F = (TI)(30.6001*(m+1));
j = C + d + E + F - 1524.5;
j += ((1.0*h)/24) + ((1.0*mi)/1440) + ((1.0*s)/86400);
if(0 != f){
j += (1.0*f)/86400000;
}
*out = j;
}
return 1;
#undef DIG
}
fsl_time_t fsl_julian_to_unix( fsl_double_t JD ){
return (JD - 2440587.5) * 86400;
}
char fsl_julian_to_iso8601( fsl_double_t J, char * out, char addMs ){
/* Adapted from this article:
http://quasar.as.utexas.edu/BillInfo/JulianDatesG.html
*/
typedef fsl_int64_t TI;
int Y, M, D, H, MI, S, F;
TI ms;
char * z = out;
if(!out || (J<=0)) return 0;
else{
double Z;
TI W, X;
TI A, B;
TI C, DD, E, F;
Z = J + 0.5;
W = (TI)((Z-1867216.25)/36524.25);
X = W/4;
A = (TI)(Z+1+W-X);
B = A+1524;
C = (TI)((B-122.1)/365.25);
DD = (TI)(365.25 * C);
E = (TI)((B-DD)/30.6001);
F = (TI)(30.6001 * E);
D = (int)(B - DD - F);
M = (E<=13) ? (E-1) : (E-13);
Y = (M<3) ? (C-4715) : (C-4716);
}
if(Y<0 || Y>9999) return 0;
else if(M<1 || M>12) return 0;
else if(D<1 || D>31) return 0;
ms = (TI)((J-(TI)J) * 86400001.0)
/* number of milliseconds in the fraction part of the JDay. The
non-0 at the end works around a problem where SS.000 converts
to (SS-1).999. This will only hide the bug for the cases i've
seen it, and might introduce other inaccuracies
elsewhere. Testing it against the current libfossil event table
produces good results - at most a 1ms round-trip fidelity loss
for the (currently ~1157) records being checked. The suffix of
1.0 was found to be a decent value via much testing with the
libfossil and fossil(1) source repos.
*/;
if( (H = ms / 3600000) ){
ms -= H * 3600000;
H = (H + 12) % 24;
}else{
H = 12 /* astronomers start their day at noon. */;
}
if( (MI = ms / 60000) ) ms -= MI * 60000;
if( (S = ms / 1000) ) ms -= S * 1000;
assert(ms<1000);
F = (int)(ms);
assert(H>=0 && H<24);
assert(MI>=0 && MI<60);
assert(S>=0 && S<60);
assert(F>=0 && F<1000);
if(H<0 || H>23) return 0;
else if(MI<0 || MI>59) return 0;
else if(S<0 || S>59) return 0;
else if(F<0 || F>999) return 0;
#define UGLY_999_KLUDGE 1
/* The fossil(1) repo has 27 of 10041 records which exhibit the
SS.999 behaviour commented on above. With this kludge, that
number drops to 0. But it's still an ugly, ugly kludge.
OTOH, the chance of the .999 being correct is 1 in 1000,
whereas we see "correct" behaviour more often (2.7 in 1000)
with this workaround.
*/
#if UGLY_999_KLUDGE
if(999==F){
char oflow = 0;
int s2 = S, mi2 = MI, h2 = H;
if(++s2 == 60){ /* Overflow minute */
s2 = 0;
if(++mi2 == 60){ /* Overflow hour */
mi2 = 0;
if(++h2 == 24){ /* Overflow day */
/* leave this corner-corner case in place */
oflow = 1;
}
}
}
/* MARKER(("UGLY 999 KLUDGE (A): H=%d MI=%d S=%d F=%d\n", H, MI, S, F)); */
if(!oflow){
F = 0;
S = s2;
MI = mi2;
H = h2;
/* MARKER(("UGLY 999 KLUDGE (B): H=%d MI=%d S=%d F=%d\n", H, MI, S, F)); */
}
}
#endif
#undef UGLY_999_KLUDGE
*(z++) = '0'+(Y/1000);
*(z++) = '0'+(Y%1000/100);
*(z++) = '0'+(Y%100/10);
*(z++) = '0'+(Y%10);
*(z++) = '-';
*(z++) = '0'+(M/10);
*(z++) = '0'+(M%10);
*(z++) = '-';
*(z++) = '0'+(D/10);
*(z++) = '0'+(D%10);
*(z++) = 'T';
*(z++) = '0'+(H/10);
*(z++) = '0'+(H%10);
*(z++) = ':';
*(z++) = '0'+(MI/10);
*(z++) = '0'+(MI%10);
*(z++) = ':';
*(z++) = '0'+(S/10);
*(z++) = '0'+(S%10);
if(addMs){
*(z++) = '.';
*(z++) = '0'+(F%1000/100);
*(z++) = '0'+(F%100/10);
*(z++) = '0'+(F%10);
}
*z = 0;
return 1;
}
int fsl_confirmv( fsl_confirmation_f f, void * state, char const * fmt, va_list va ){
int rc;
fsl_buffer msg = fsl_buffer_empty;
rc = fsl_buffer_appendfv(&msg, fmt, va);
if(!rc){
rc = f(state, (char const *)msg.mem, msg.used);
}else{
rc = 1;
}
fsl_buffer_clear(&msg);
return rc;
}
int fsl_confirm( fsl_confirmation_f f, void * state, char const * fmt, ... ){
int rc;
va_list va;
va_start(va, fmt);
rc = fsl_confirmv( f, state, fmt, va );
va_end(va);
return rc;
}
int fsl_confirmation_f_int(void * state, char const * msg, fsl_size_t msgLen){
int const rc = state ? *((int*)state) : FSL_CONFIRM_ERROR;
switch(rc){
case FSL_CONFIRM_ERROR:
case FSL_CONFIRM_YES:
case FSL_CONFIRM_YES_ALL:
case FSL_CONFIRM_NO:
case FSL_CONFIRM_NO_ALL:
return rc;
default:
return FSL_CONFIRM_ERROR;
}
}
#if FSL_CONFIG_ENABLE_TIMER
/**
For the fsl_timer_xxx() family of functions...
*/
#ifdef _WIN32
# include <windows.h>
#else
# include <sys/time.h>
# include <sys/resource.h>
# include <unistd.h>
# include <fcntl.h>
# include <errno.h>
#endif
#endif
/* FSL_CONFIG_ENABLE_TIMER */
/**
Get user and kernel times in microseconds.
*/
static void fsl_cpu_times(fsl_uint64_t *piUser, fsl_uint64_t *piKernel){
#if !FSL_CONFIG_ENABLE_TIMER
*piUser = *piKernel = 0U;
#else
#ifdef _WIN32
FILETIME not_used;
FILETIME kernel_time;
FILETIME user_time;
GetProcessTimes(GetCurrentProcess(), ¬_used, ¬_used,
&kernel_time, &user_time);
if( piUser ){
*piUser = ((((fsl_uint64_t)user_time.dwHighDateTime)<<32) +
(fsl_uint64_t)user_time.dwLowDateTime + 5)/10;
}
if( piKernel ){
*piKernel = ((((fsl_uint64_t)kernel_time.dwHighDateTime)<<32) +
(fsl_uint64_t)kernel_time.dwLowDateTime + 5)/10;
}
#else
struct rusage s;
getrusage(RUSAGE_SELF, &s);
if( piUser ){
*piUser = ((fsl_uint64_t)s.ru_utime.tv_sec)*1000000 + s.ru_utime.tv_usec;
}
if( piKernel ){
*piKernel =
((fsl_uint64_t)s.ru_stime.tv_sec)*1000000 + s.ru_stime.tv_usec;
}
#endif
#endif
/* FSL_CONFIG_ENABLE_TIMER */
}
void fsl_timer_start(fsl_timer_state * ft){
fsl_cpu_times( &ft->user, &ft->system );
}
fsl_uint64_t fsl_timer_fetch(fsl_timer_state const * t){
fsl_uint64_t eu = 0, es = 0;
fsl_cpu_times( &eu, &es );
return (eu - t->user) + (es - t->system);
}
fsl_uint64_t fsl_timer_reset(fsl_timer_state * t){
fsl_uint64_t const rc = fsl_timer_fetch(t);
fsl_cpu_times( &t->user, &t->system );
return rc;
}
fsl_uint64_t fsl_timer_stop(fsl_timer_state *t){
fsl_uint64_t const rc = fsl_timer_fetch(t);
*t = fsl_timer_state_empty;
return rc;
}
unsigned int fsl_rgb_encode( int r, int g, int b ){
return (unsigned int)(((r&0xFF)<<16) + ((g&0xFF)<<8) + (b&0xFF));
}
void fsl_rgb_decode( unsigned int src, int *r, int *g, int *b ){
if(r) *r = (src&0xFF0000)>>16;
if(g) *g = (src&0xFF00)>>8;
if(b) *b = src&0xFF;
}
unsigned fsl_gradient_color(unsigned c1, unsigned c2, unsigned int n, unsigned int i){
unsigned c; /* Result color */
unsigned x1, x2;
if( i==0 || n==0 ) return c1;
else if(i>=n) return c2;
x1 = (c1>>16)&0xff;
x2 = (c2>>16)&0xff;
c = (x1*(n-i) + x2*i)/n<<16 & 0xff0000;
x1 = (c1>>8)&0xff;
x2 = (c2>>8)&0xff;
c |= (x1*(n-i) + x2*i)/n<<8 & 0xff00;
x1 = c1&0xff;
x2 = c2&0xff;
c |= (x1*(n-i) + x2*i)/n & 0xff;
return c;
}
fsl_size_t fsl_simplify_sql( char * sql, fsl_int_t len ){
char * wat = sql /* write pos */;
char * rat = sql /* read pos */;
char const * end /* one-past-the-end */;
char inStr = 0 /* in an SQL string? */;
char prev = 0 /* previous character. Sometimes. */;
if(!sql || !*sql) return 0;
else if(len < 0) len = fsl_strlen(sql);
if(!len) return 0;
end = sql + len;
while( *rat && (rat < end) ){
switch(*rat){
case 0: break;
case '\r':
case '\n':
/* Bug: we don't handle \r\n pairs. Because nobody
should never have to :/. */
if(inStr || (prev!=*rat)){
/* Keep them as-is */
prev = *wat++ = *rat++;
}else{
/* Collapse multiples into one. */
++rat;
}
continue;
case ' ':
case '\t':
case '\v':
case '\f':
if(inStr){
/* Keep them as-is */
prev = *wat++ = *rat++;
}else{
/* Reduce to a single space. */
/* f_out("prev=[%c] rat=[%c]\n", prev, *rat); */
if(prev != *rat){
*wat++ = ' ';
prev = *rat;
}
++rat;
}
continue;
case '\'': /* SQL strings */
prev = *wat++ = *rat++;
if(!inStr){
inStr = 1;
}else if('\'' == *rat){
/* Escaped quote */
*wat++ = *rat++;
}else{
/* End of '...' string. */
inStr = 0;
}
continue;
default:
prev = *wat++ = *rat++;
continue;
}
}
*wat = 0;
return (fsl_size_t)(wat - sql);
}
/**
Convenience form of fsl_simplify_sql() which assumes b holds an SQL
string. It gets processed by fsl_simplify_sql() and its 'used'
length potentially gets adjusted to match the adjusted SQL string.
*/
fsl_size_t fsl_simplify_sql_buffer( fsl_buffer * b ){
return b->used = fsl_simplify_sql( (char *)b->mem, (fsl_int_t)b->used );
}
char fsl_isatty(int fd){
return isatty(fd) ? 1 : 0;
}
#undef MARKER
#if defined(_WIN32) || defined(WIN32)
#undef isatty
#endif