/*************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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<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 }