mirror of
https://github.com/jrbrtsn/ban2fail
synced 2024-06-16 11:58:01 +00:00
982 lines
24 KiB
C
982 lines
24 KiB
C
/***************************************************************************
|
|
* Copyright (C) 2018 by John D. Robertson *
|
|
* john@rrci.com *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 3 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* 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. See the *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
/***************************************************************************
|
|
util.h - description
|
|
Common utility routines needed by most c and c++ applications.
|
|
|
|
-------------------
|
|
begin : Fri Oct 19 10:09:38 EDT 2018
|
|
email : john@rrci.com
|
|
***************************************************************************/
|
|
#define _GNU_SOURCE
|
|
#include <arpa/inet.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "ez_libc.h"
|
|
#include "util.h"
|
|
|
|
#define MSGBUF_SZ 4096
|
|
|
|
static void
|
|
dflt_eprintf_line (const char *fmt, va_list ap)
|
|
/***************************************************************
|
|
* default line vprintf()'ing function for eprintf() and friends.
|
|
*/
|
|
{
|
|
vfprintf(stderr, fmt, ap);
|
|
fputc ('\n', stderr);
|
|
fflush (stderr);
|
|
}
|
|
|
|
|
|
/* Static data for all threads */
|
|
static struct {
|
|
eprintf_line_f prt_f;
|
|
} S = {
|
|
.prt_f= dflt_eprintf_line
|
|
};
|
|
|
|
eprintf_line_f
|
|
set_eprintf_line (eprintf_line_f newFunc)
|
|
/****************************************************
|
|
* Set the global eprintf line vprintf()'ing function.
|
|
*/
|
|
{
|
|
eprintf_line_f rtn= S.prt_f;
|
|
S.prt_f= newFunc;
|
|
return rtn;
|
|
}
|
|
|
|
/* Function to print out an error message. Usualy called by eprintf() macro. */
|
|
void _eprintf(
|
|
#ifdef DEBUG
|
|
const char* filename,
|
|
int lineno,
|
|
const char *func,
|
|
#endif
|
|
const char *fmt,
|
|
...
|
|
)
|
|
/*********************************************************************************
|
|
* Function to simplify printing error messages.
|
|
*/
|
|
{
|
|
va_list args;
|
|
char buf[MSGBUF_SZ];
|
|
|
|
#ifdef DEBUG
|
|
/* Put the filename, lineno, and function name into a new format string */
|
|
snprintf(buf, MSGBUF_SZ-1, "%s line %d, %s(): %s", filename, lineno, func, fmt);
|
|
#else
|
|
snprintf(buf, MSGBUF_SZ-1, "%s", fmt);
|
|
#endif
|
|
|
|
/* make sure buffer is null terminated */
|
|
buf[MSGBUF_SZ-1]= '\0';
|
|
|
|
|
|
/* Pass on doctored format string and varargs to vfprintf() */
|
|
va_start(args, fmt);
|
|
(* S.prt_f) (buf, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* Function to print out an error message. Usualy called by sys_eprintf() macro. */
|
|
void _sys_eprintf(
|
|
const char* (*strerror_f)(int errnum),
|
|
#ifdef DEBUG
|
|
const char* filename,
|
|
int lineno,
|
|
const char *func,
|
|
#endif
|
|
const char *fmt,
|
|
...
|
|
)
|
|
/*********************************************************************************
|
|
* Function to simplify printing error messages.
|
|
*/
|
|
{
|
|
va_list args;
|
|
char buf[MSGBUF_SZ];
|
|
|
|
/* Put the filename, lineno, and function name into a new format string */
|
|
#ifdef DEBUG
|
|
snprintf(buf, MSGBUF_SZ-1, "%s line %d, %s(): %s [ %s ]", filename, lineno, func, fmt, (*strerror_f)(errno));
|
|
#else
|
|
snprintf(buf, MSGBUF_SZ-1, "%s [ %s ]", fmt, (*strerror_f)(errno));
|
|
#endif
|
|
|
|
/* make sure buffer is null terminated */
|
|
buf[MSGBUF_SZ-1]= '\0';
|
|
|
|
|
|
/* Pass on doctored format string and varargs to vfprintf() */
|
|
va_start(args, fmt);
|
|
(* S.prt_f) (buf, args);
|
|
va_end(args);
|
|
}
|
|
|
|
int64_t
|
|
timespec2ms(const struct timespec *ts)
|
|
/**********************************************************************
|
|
* Convert a timespec structure to integer milliseconds.
|
|
*/
|
|
{
|
|
return ts->tv_sec*1000 + ts->tv_nsec/1000000;
|
|
}
|
|
|
|
struct timespec*
|
|
ms2timespec(struct timespec *rtnBuf, int64_t ms)
|
|
/**********************************************************************
|
|
* Load up a timespec struct given number of milliseconds.
|
|
*/
|
|
{
|
|
rtnBuf->tv_sec= ms/1000;
|
|
rtnBuf->tv_nsec= (ms%1000)*1000000;
|
|
return rtnBuf;
|
|
}
|
|
|
|
const char*
|
|
bits2str(int64_t bits, const struct bitTuple *btArr)
|
|
/**********************************************************************
|
|
* Returns a null terminated buffer with OR'd set bits
|
|
* printed out.
|
|
*/
|
|
{
|
|
/* Rotating buffers so this can be used multiple times as arg to printf() */
|
|
#define N_BUFS 10
|
|
static _Thread_local char bufArr[N_BUFS][1024];
|
|
static _Thread_local unsigned count;
|
|
const struct bitTuple *t;
|
|
unsigned offset;
|
|
char *buf= bufArr[++count%N_BUFS];
|
|
|
|
/* Need this in case no bits are set */
|
|
buf[0]= '\0';
|
|
|
|
for(offset= 0, t= btArr; t->name; ++t) {
|
|
if(!(bits & t->bit)) continue;
|
|
if(offset) offset += sprintf(buf+offset, "|");
|
|
offset += sprintf(buf+offset, "%s", t->name);
|
|
}
|
|
return buf;
|
|
#undef N_BUFS
|
|
}
|
|
|
|
static const struct bitTuple*
|
|
findBitTuple(const char *symbol, const struct bitTuple *btArr)
|
|
/**********************************************************************
|
|
* brute force search for matching symbol.
|
|
*/
|
|
{
|
|
const struct bitTuple *bt;
|
|
for(bt= btArr; bt->name; ++bt) {
|
|
if(!strcmp(symbol, bt->name)) return bt;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
str2bits(int64_t *rtnBuf, const char *str, const struct bitTuple *btArr)
|
|
/**********************************************************************
|
|
* Convert all OR'd symbolic bits in str into the return value.
|
|
*
|
|
* RETURNS: 0 for success, -1 for error.
|
|
*/
|
|
{
|
|
*rtnBuf= 0;
|
|
if(!strlen(str)) return 0;
|
|
|
|
int rtn= -1;
|
|
char symbolArr[20][60];
|
|
memset(symbolArr, 0, sizeof(symbolArr));
|
|
|
|
int rc= sscanf(str,
|
|
"%59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
"| %59[^|] "
|
|
,symbolArr[0]
|
|
,symbolArr[1]
|
|
,symbolArr[2]
|
|
,symbolArr[3]
|
|
,symbolArr[4]
|
|
,symbolArr[5]
|
|
,symbolArr[6]
|
|
,symbolArr[7]
|
|
,symbolArr[8]
|
|
,symbolArr[9]
|
|
,symbolArr[10]
|
|
,symbolArr[11]
|
|
,symbolArr[12]
|
|
,symbolArr[13]
|
|
,symbolArr[14]
|
|
,symbolArr[15]
|
|
,symbolArr[16]
|
|
,symbolArr[17]
|
|
,symbolArr[18]
|
|
,symbolArr[19]
|
|
);
|
|
|
|
if(-1 == rc) {
|
|
sys_eprintf("ERROR: sscanf() failed");
|
|
goto abort;
|
|
}
|
|
|
|
for(int i= 0; i < rc; ++i) {
|
|
const struct bitTuple *bt= findBitTuple(symbolArr[i], btArr);
|
|
if(!bt) {
|
|
eprintf("ERROR: \"%s\" not found in bitTuple array.", symbolArr[i]);
|
|
goto abort;
|
|
}
|
|
*rtnBuf |= bt->bit;
|
|
}
|
|
|
|
|
|
rtn= 0;
|
|
|
|
abort:
|
|
return rtn;
|
|
}
|
|
|
|
void
|
|
printBuffer(FILE *fh, const char *buf)
|
|
/************************************************************************************
|
|
* Print out the supplied buffer, replacing unprintable characters with the corresponding
|
|
* hex value in pointy brackets, e.g. <0x01>
|
|
*/
|
|
{
|
|
const char *pc;
|
|
/* Print something for each character */
|
|
for(pc= buf; pc && *pc; ++pc) {
|
|
|
|
if(isprint(*pc)) { /* Character is printable */
|
|
|
|
fputc(*pc, fh);
|
|
|
|
} else { /* Character is NOT printable, show hex number instead */
|
|
|
|
fprintf(fh, "<0x%02X>", *pc);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
int64_t
|
|
clock_gettime_ms(clockid_t whichClock)
|
|
/**********************************************************************
|
|
* Returns current value of whichClock in milliseconds, avoiding the
|
|
* need for struct timespec.
|
|
* See man clock_gettime for more information.
|
|
*/
|
|
{
|
|
struct timespec ts;
|
|
if(-1 == clock_gettime(whichClock, &ts)) {
|
|
sys_eprintf("\tclock_gettime() failed");
|
|
abort();
|
|
}
|
|
return timespec2ms(&ts);
|
|
}
|
|
|
|
const char*
|
|
local_strftime (const time_t *pWhen, const char *fmt)
|
|
/***************************************************
|
|
* Get local time in a static string buffer
|
|
*/
|
|
{
|
|
/* Rotating buffers so this can be used multiple times as arg to printf() */
|
|
#define N_BUFS 5
|
|
#define BUF_SZ 64
|
|
static _Thread_local char bufArr[N_BUFS][BUF_SZ];
|
|
static _Thread_local unsigned count;
|
|
char *buf= bufArr[++count%N_BUFS];
|
|
|
|
/* Print the local time to a buffer */
|
|
struct tm *tm= localtime (pWhen);
|
|
if (!tm) {
|
|
sys_eprintf ("localtime() failed");
|
|
return NULL;
|
|
}
|
|
|
|
if (!strftime (buf, BUF_SZ-1, fmt, tm)) {
|
|
sys_eprintf ("strftime(\"%s\") failed", fmt);
|
|
return NULL;
|
|
}
|
|
|
|
return buf;
|
|
#undef BUF_SZ
|
|
#undef N_BUFS
|
|
}
|
|
|
|
const char*
|
|
strbits(int64_t bits, unsigned nBytes)
|
|
/**********************************************************************
|
|
* Convert a bits to a null terminated string of '1' and '0' characters.
|
|
* Uses rotating per-thread static buffer for return, so it is safe
|
|
* to call multiple times in a single printf()ish invocation.
|
|
*
|
|
* bits: bit field of interest.
|
|
* nBytes: Number of bytes to consider.
|
|
*
|
|
* RETURNS:
|
|
* null terminated buffer with a string representing bits as '0' or '1'
|
|
*/
|
|
{
|
|
/* Rotating buffers so this can be used multiple times as arg to printf() */
|
|
#define N_BUFS 8
|
|
#define BUF_SZ 65
|
|
static _Thread_local char bufArr[N_BUFS][BUF_SZ];
|
|
static _Thread_local unsigned count;
|
|
char *buf= bufArr[++count%N_BUFS];
|
|
unsigned pos= 0;
|
|
|
|
/* order bits from MSbit -> LSbit */
|
|
for(unsigned n= nBytes; n--;) {
|
|
unsigned char byte= (bits >>(8*n)) << 56 >> 56;
|
|
for(unsigned bit= 7; bit--;) {
|
|
buf[pos]= byte & 1<<bit ? '1' : '0';
|
|
++pos;
|
|
}
|
|
}
|
|
|
|
buf[pos]= '\0';
|
|
|
|
return buf;
|
|
#undef BUF_SZ
|
|
#undef N_BUFS
|
|
}
|
|
|
|
int
|
|
fd_setNONBLOCK (int fd)
|
|
/***************************************************
|
|
* Set a file descriptor to non-blocking mode.
|
|
*/
|
|
{
|
|
int rtn= -1;
|
|
|
|
/* Set for non-blocking I/O */
|
|
int fcntlFlgs;
|
|
/* This sets 'status' flags */
|
|
if((fcntlFlgs= fcntl(fd, F_GETFL)) == -1 ||
|
|
fcntl(fd, F_SETFL, fcntlFlgs | O_NONBLOCK) == -1 ||
|
|
(fcntlFlgs= fcntl(fd, F_GETFD)) == -1 ||
|
|
fcntl(fd, F_SETFD, fcntlFlgs | FD_CLOEXEC) == -1) {
|
|
|
|
sys_eprintf("fcntl() failed");
|
|
abort();
|
|
}
|
|
|
|
/* This sets 'descriptor' flags */
|
|
fcntlFlgs= fcntl(fd, F_GETFD);
|
|
if(fcntl(fd, F_SETFD, fcntlFlgs | FD_CLOEXEC) < 0) {
|
|
sys_eprintf("fcntl() failed");
|
|
abort();
|
|
}
|
|
|
|
rtn= 0;
|
|
|
|
abort:
|
|
return rtn;
|
|
|
|
}
|
|
|
|
const struct enumTuple*
|
|
str2enum(const char *str, const struct enumTuple *etArr)
|
|
/**********************************************************************
|
|
* Try to match str with an entry in the enumTupeArr.
|
|
*/
|
|
{
|
|
const struct enumTuple *et;
|
|
for(et= etArr; et->name; ++et) {
|
|
if(!strcmp(str, et->name)) return et;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const char*
|
|
enum2str(int enumVal, const struct enumTuple *etArr)
|
|
/**********************************************************************
|
|
* Return a the string representing enumVal, or NULL.
|
|
*/
|
|
{
|
|
const struct enumTuple *et;
|
|
for(et= etArr; et->name; ++et) {
|
|
if(enumVal == et->enumVal) return et->name;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
sleep_ms(unsigned msec)
|
|
/***************************************************
|
|
* Sleep for the specified number of milliseconds.
|
|
*/
|
|
{
|
|
struct timespec ts;
|
|
ts.tv_sec= msec/1000;
|
|
ts.tv_nsec= (msec%1000)*1000000;
|
|
return nanosleep(&ts, NULL);
|
|
}
|
|
|
|
int
|
|
tcp_connect(const char *hostName, unsigned short port)
|
|
/***************************************************
|
|
* Blocking TCP connect for convenience.
|
|
*/
|
|
{
|
|
int rtn= -1, sock;
|
|
struct hostent *hp;
|
|
struct sockaddr_in sin;
|
|
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if(-1 == sock) {
|
|
sys_eprintf("ERROR: socket() failed.");
|
|
goto abort;
|
|
}
|
|
|
|
// Initialize socket address structure
|
|
hp = gethostbyname(hostName);
|
|
if(!hp) {
|
|
sys_eprintf("ERROR: gethostbyname(\"%s\") failed.", hostName);
|
|
goto abort;
|
|
}
|
|
|
|
memset(&sin, 0, sizeof(sin));
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = htons(port);
|
|
memcpy(&(sin.sin_addr.s_addr), hp->h_addr, hp->h_length);
|
|
|
|
int err= connect(sock, (struct sockaddr *)&sin, sizeof(sin));
|
|
if(-1 == err) {
|
|
sys_eprintf("ERROR: connect(%s:%hu) failed.", hostName, port);
|
|
goto abort;
|
|
}
|
|
|
|
rtn= sock;
|
|
|
|
abort:
|
|
if(-1 == rtn) {
|
|
if(-1 != sock) {
|
|
ez_close(sock);
|
|
}
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
int
|
|
tm_normalize(struct tm *buf)
|
|
/***************************************************
|
|
* Normalize buf so member values are corrected.
|
|
* Returns 0 for success, -1 for failure.
|
|
*/
|
|
{
|
|
buf->tm_isdst= -1;
|
|
time_t tt= mktime(buf);
|
|
if(!localtime_r(&tt, buf)) {
|
|
sys_eprintf ("localtime() failed");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
secs2tod(const time_t *pFrom, int tod_sec)
|
|
/***************************************************
|
|
* Return the smallest number of seconds in the future
|
|
* remaining between from and tod_sec time-of-day, without
|
|
* respect to which day of the week/month/year.
|
|
*/
|
|
{
|
|
assert(0 <= tod_sec && (23*3600+59*60+59) >= tod_sec);
|
|
|
|
if(0 > tod_sec || (23*3600+59*60+59) < tod_sec) return -1;
|
|
|
|
/* Get the local time breakdown in a buffer */
|
|
struct tm tm_from;
|
|
if(!localtime_r(pFrom, &tm_from)) {
|
|
sys_eprintf ("localtime_r() failed");
|
|
abort();
|
|
}
|
|
|
|
/* We'll look at today and tomorrow */
|
|
for(int day= 0; day <= 1; ++day) {
|
|
|
|
struct tm tm= tm_from;
|
|
|
|
tm.tm_mday += day;
|
|
if(-1 == tm_normalize(&tm)) assert(0);
|
|
|
|
tm.tm_hour= tod_sec / 3600;
|
|
tm.tm_min= (tod_sec % 3600) / 60;
|
|
tm.tm_sec= (tod_sec % 3600) % 60;
|
|
|
|
time_t dptime= timelocal(&tm);
|
|
if(dptime > *pFrom) return (int)(dptime - *pFrom);
|
|
}
|
|
|
|
assert(0);
|
|
|
|
/* Can't find a time (should be impossible to get here) */
|
|
return -1;
|
|
}
|
|
|
|
const char*
|
|
indentStr(unsigned lvl, const char *pfix)
|
|
/***************************************************
|
|
* Return a string with lvl concatenated pfix strings.
|
|
*/
|
|
{
|
|
#define N_BUFS 10
|
|
#define BUF_SZ 128
|
|
static _Thread_local char bufArr[N_BUFS][BUF_SZ];
|
|
static _Thread_local unsigned count;
|
|
char *buf= bufArr[++count%N_BUFS];
|
|
size_t len= strlen(pfix);
|
|
|
|
buf[0]= '\0';
|
|
for(unsigned i= 0; (i+1)*len < BUF_SZ && i < lvl; ++i) {
|
|
strncpy(buf+i*len, pfix, len+1);
|
|
}
|
|
|
|
return buf;
|
|
|
|
#undef BUF_SZ
|
|
#undef N_BUFS
|
|
}
|
|
|
|
int
|
|
regex_compile(regex_t *preg, const char *pattern, int cflags)
|
|
/***************************************************
|
|
* Regular expression compile with error reporting.
|
|
*/
|
|
{
|
|
int rc, rtn= -1;
|
|
|
|
rc= regcomp(preg, pattern, cflags);
|
|
if(rc) {
|
|
#define BUF_SZ 1024
|
|
char buf[BUF_SZ];
|
|
regerror(rc, preg, buf, BUF_SZ);
|
|
eprintf("ERROR: %s", buf);
|
|
goto abort;
|
|
#undef BUF_SZ
|
|
}
|
|
|
|
rtn= 0;
|
|
|
|
abort:
|
|
return rtn;
|
|
}
|
|
|
|
FILE*
|
|
pager_open(void)
|
|
/***************************************************
|
|
* popen() the caller's pager.
|
|
*/
|
|
{
|
|
char *cmd= getenv("PAGER");
|
|
if(!cmd)
|
|
cmd= "/bin/more";
|
|
|
|
return ez_popen(cmd, "w");
|
|
}
|
|
|
|
const char*
|
|
prefix_home(const char *fname)
|
|
/***************************************************
|
|
* return $HOME/fname in a static buffer.
|
|
*/
|
|
{
|
|
#define N_BUFS 4
|
|
#define BUF_SZ PATH_MAX+1
|
|
static _Thread_local char bufArr[N_BUFS][BUF_SZ];
|
|
static _Thread_local unsigned count;
|
|
char *buf = bufArr[++count % N_BUFS];
|
|
|
|
const char *home_env= getenv("HOME");
|
|
|
|
if(home_env) {
|
|
snprintf(buf, BUF_SZ-1, "%s/%s", home_env, fname);
|
|
} else {
|
|
snprintf(buf, BUF_SZ-1, "%s", fname);
|
|
}
|
|
|
|
return buf;
|
|
|
|
#undef BUF_SZ
|
|
#undef N_BUFS
|
|
}
|
|
|
|
const char*
|
|
pthread_t_str(pthread_t tid)
|
|
/***************************************************
|
|
* return a hexidecimal representation of tid in
|
|
* a rotating static buffer.
|
|
*/
|
|
{
|
|
#define N_BUFS 10
|
|
#define BUF_SZ (sizeof(tid)*2+3)
|
|
static _Thread_local char bufArr[N_BUFS][BUF_SZ];
|
|
static _Thread_local unsigned count;
|
|
char *buf = bufArr[++count % N_BUFS];
|
|
|
|
strcpy(buf, "0x");
|
|
char *dst= buf+2;
|
|
|
|
unsigned i, byte;
|
|
size_t sz= sizeof(tid);
|
|
for(i= 0; i < sz; ++i) {
|
|
byte= *((unsigned char *)(&tid) + sz - i - 1);
|
|
dst += sprintf(dst, "%02x", byte);
|
|
}
|
|
*dst= '\0';
|
|
|
|
return buf;
|
|
|
|
#undef BUF_SZ
|
|
#undef N_BUFS
|
|
}
|
|
|
|
|
|
char*
|
|
skipspace(char *str)
|
|
/***************************************************
|
|
* return first character in str which is not
|
|
* whitespace, which could be the terminating null.
|
|
*/
|
|
{
|
|
for(; *str && isspace(*str); ++str);
|
|
return str;
|
|
}
|
|
|
|
const char*
|
|
skipspacec(const char *str)
|
|
/***************************************************
|
|
* return first character in str which is not
|
|
* whitespace, which could be the terminating null.
|
|
*/
|
|
{
|
|
for(; *str && isspace(*str); ++str);
|
|
return str;
|
|
}
|
|
|
|
char*
|
|
trimend(char *str)
|
|
/***************************************************
|
|
* Trim space on end of string by replacing it with
|
|
* null bytes.
|
|
*/
|
|
{
|
|
size_t len= strlen(str);
|
|
for(; len && isspace(str[len-1]); str[--len]= '\0');
|
|
return str;
|
|
}
|
|
|
|
void
|
|
julian_2_gregorian(int *year, int *month, int *day, uint32_t julianDay)
|
|
/************************************************************************
|
|
* Gregorian calendar starting from October 15, 1582
|
|
* This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern
|
|
*/
|
|
{
|
|
uint64_t ell, n, i, j;
|
|
|
|
ell = (uint64_t)julianDay + 68569;
|
|
n = (4 * ell) / 146097;
|
|
ell = ell - (146097 * n + 3) / 4;
|
|
i = (4000 * (ell + 1)) / 1461001;
|
|
ell = ell - (1461 * i) / 4 + 31;
|
|
j = (80 * ell) / 2447;
|
|
*day = ell - (2447 * j) / 80;
|
|
ell = j / 11;
|
|
*month = j + 2 - (12 * ell);
|
|
*year = 100 * (n - 49) + i + ell;
|
|
}
|
|
|
|
int32_t
|
|
gregorian_2_julian(int year, int month, int day)
|
|
/* Gregorian calendar starting from October 15, 1582
|
|
* Algorithm from Henry F. Fliegel and Thomas C. Van Flandern
|
|
*/
|
|
{
|
|
return (1461 * (year + 4800 + (month - 14) / 12)) / 4
|
|
+ (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12
|
|
- (3 * ((year + 4900 + (month - 14) / 12) / 100)) / 4
|
|
+ day - 32075;
|
|
}
|
|
|
|
const char*
|
|
bytes_2_hexStr(
|
|
char *rtn_sbuf,
|
|
unsigned rtn_sz,
|
|
const unsigned char *src,
|
|
unsigned src_len
|
|
)
|
|
/***************************************************************
|
|
* Convert bytes to hex characters, place in rtn_sbuf.
|
|
*/
|
|
{
|
|
if(src_len*2 >= rtn_sz)
|
|
return NULL;
|
|
|
|
unsigned i;
|
|
for(i= 0; i < src_len; ++i) {
|
|
snprintf(rtn_sbuf+i*2, rtn_sz-i*2, "%02x", (int)src[i]);
|
|
}
|
|
/* Null terminate the result */
|
|
rtn_sbuf[i*2]= '\0';
|
|
return rtn_sbuf;
|
|
}
|
|
|
|
int
|
|
rmdir_recursive(const char *path)
|
|
/***************************************************************
|
|
* recursively remove a directory and all of it's contents.
|
|
* RETURNS:
|
|
* 0 for success
|
|
* -1 for error
|
|
*/
|
|
{
|
|
int rtn= -1;
|
|
|
|
struct stat st;
|
|
DIR *dir= ez_opendir(path);
|
|
struct dirent *entry;
|
|
STR sb;
|
|
STR_constructor(&sb, PATH_MAX);
|
|
|
|
while((entry= ez_readdir(dir))) {
|
|
/* Skip uninteresting entries */
|
|
if(!strcmp(".", entry->d_name) ||
|
|
!strcmp("..", entry->d_name)) continue;
|
|
|
|
STR_reset(&sb);
|
|
STR_sprintf(&sb, "%s/%s", path, entry->d_name);
|
|
|
|
ez_stat(STR_str(&sb), &st);
|
|
if (S_ISDIR(st.st_mode)) {
|
|
int rc= rmdir_recursive(STR_str(&sb));
|
|
if(rc)
|
|
goto abort;
|
|
} else {
|
|
ez_unlink(STR_str(&sb));
|
|
}
|
|
}
|
|
|
|
rtn= 0;
|
|
|
|
abort:
|
|
if(!rtn)
|
|
ez_rmdir(path);
|
|
|
|
STR_destructor(&sb);
|
|
return rtn;
|
|
}
|
|
|
|
int _ez_rmdir_recursive (
|
|
const char *fileName,
|
|
int lineNo,
|
|
const char *funcName,
|
|
const char *pathname
|
|
)
|
|
/***************************************************************
|
|
* ez version of rmdir_recursive().
|
|
*/
|
|
{
|
|
int rtn= rmdir_recursive (pathname);
|
|
if (-1 == rtn) {
|
|
_eprintf(fileName, lineNo, funcName, "rmdir_recursive(\"%s\") failed", pathname);
|
|
abort();
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
/*============================================================*/
|
|
/*================ struct addrinfo ===========================*/
|
|
/*============================================================*/
|
|
|
|
const static struct bitTuple ai_flags_btArr[]= {
|
|
{.name= "AI_ADDRCONFIG", .bit= AI_ADDRCONFIG},
|
|
{.name= "AI_ALL", .bit= AI_ALL},
|
|
{.name= "AI_CANONNAME", .bit= AI_CANONNAME},
|
|
{.name= "AI_NUMERICHOST", .bit= AI_NUMERICHOST},
|
|
{.name= "AI_NUMERICSERV", .bit= AI_NUMERICSERV},
|
|
{.name= "AI_PASSIVE", .bit= AI_PASSIVE},
|
|
{.name= "AI_V4MAPPED", .bit= AI_V4MAPPED},
|
|
{}
|
|
};
|
|
|
|
const static struct enumTuple ai_family_etArr[]= {
|
|
{.name= "AF_INET", .enumVal= AF_INET},
|
|
{.name= "AF_INET6", .enumVal= AF_INET6},
|
|
{.name= "AF_UNSPEC", .enumVal= AF_UNSPEC},
|
|
{}
|
|
};
|
|
|
|
const static struct enumTuple ai_socktype_etArr[]= {
|
|
{.name= "SOCK_DGRAM", .enumVal= SOCK_DGRAM},
|
|
{.name= "SOCK_RAW", .enumVal= SOCK_RAW},
|
|
{.name= "SOCK_STREAM", .enumVal= SOCK_STREAM},
|
|
{}
|
|
};
|
|
|
|
const static struct enumTuple ai_protocol_etArr[]= {
|
|
{.name= "IPPROTO_TCP", .enumVal= IPPROTO_TCP},
|
|
{.name= "IPPROTO_UDP", .enumVal= IPPROTO_UDP},
|
|
{}
|
|
};
|
|
|
|
|
|
int
|
|
addrinfo_print(struct addrinfo *ai, FILE *fh)
|
|
/*************************************************************
|
|
* Print a legible rendition of all struct addrinfo in the
|
|
* linked-list chain.
|
|
*
|
|
* ai: pinter to a struct addrinfo
|
|
* fh: stream for output
|
|
*
|
|
* RETURNS:
|
|
* 0 for success
|
|
* -1 for error
|
|
*/
|
|
{
|
|
for(; ai; ai= ai->ai_next) {
|
|
const char *addr= addrinfo_2_addr(ai);
|
|
ez_fprintf(fh,
|
|
"struct addressinfo {\n"
|
|
"\tai_flags= %s\n"
|
|
"\tai_family= %s\n"
|
|
"\tai_socktype= %s\n"
|
|
"\tai_protocol= %s\n"
|
|
"\tai_addrlen= %d\n"
|
|
"\tai_addr= %s\n"
|
|
"\tai_cannonname= %s\n"
|
|
"}\n"
|
|
, bits2str(ai->ai_flags, ai_flags_btArr)
|
|
, enum2str(ai->ai_family, ai_family_etArr)
|
|
, enum2str(ai->ai_socktype, ai_socktype_etArr)
|
|
, enum2str(ai->ai_protocol, ai_protocol_etArr)
|
|
, (int)ai->ai_addrlen
|
|
, addr ? addr : "NULL"
|
|
, ai->ai_canonname ? ai->ai_canonname : "NULL"
|
|
);
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
addrinfo_is_match(const struct addrinfo *ai, const char *addr)
|
|
/***********************************************************************
|
|
* Search all members in addrinfo linked list for a match.
|
|
*
|
|
* ai: pinter to a struct addrinfo
|
|
* addr: NULL terminated numeric address for strcmp() comparison
|
|
*
|
|
* RETURNS:
|
|
* 1 a match was found
|
|
* 0 no match was found.
|
|
*/
|
|
{
|
|
for(; ai; ai= ai->ai_next) {
|
|
const char *this_addr= addrinfo_2_addr(ai);
|
|
if(!strcmp(this_addr, addr))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char*
|
|
addrinfo_2_addr(const struct addrinfo *ai)
|
|
/***********************************************************************
|
|
* Return in a static buffer a sting version of the numeric address found
|
|
* in a single addrinfo struct.
|
|
*
|
|
* ai: pinter to a struct addrinfo
|
|
*
|
|
* RETURNS:
|
|
* NULL for failure
|
|
* address of the static buffer containing address in null terminated string form.
|
|
*/
|
|
{
|
|
/* Rotating buffers so this can be used multiple times as arg to printf() */
|
|
#define N_BUFS 5
|
|
#define BUF_SZ 43
|
|
const char *rtn= NULL;
|
|
if(!ai->ai_addr) goto abort;
|
|
|
|
static _Thread_local char bufArr[N_BUFS][BUF_SZ];
|
|
static _Thread_local unsigned count;
|
|
char *buf= bufArr[++count%N_BUFS];
|
|
|
|
memset(buf, 0, BUF_SZ);
|
|
|
|
switch(ai->ai_family) {
|
|
case AF_INET: {
|
|
struct sockaddr_in *sin= (struct sockaddr_in*)ai->ai_addr;
|
|
rtn= inet_ntop(AF_INET, &sin->sin_addr, buf, BUF_SZ-1);
|
|
} break;
|
|
|
|
case AF_INET6: {
|
|
struct sockaddr_in6 *sin6= (struct sockaddr_in6*)ai->ai_addr;
|
|
rtn= inet_ntop(AF_INET6, &sin6->sin6_addr, buf, BUF_SZ-1);
|
|
} break;
|
|
|
|
default:
|
|
assert(0);
|
|
|
|
}
|
|
|
|
abort:
|
|
return rtn;
|
|
#undef BUF_SZ
|
|
#undef N_BUFS
|
|
}
|
|
|