Rev 18 | Go to most recent revision | Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 16 | pmbaty | 1 | #ifndef UTILITY_H |
| 2 | #define UTILITY_H |
||
| 3 | |||
| 4 | |||
| 5 | #ifdef __cplusplus |
||
| 6 | extern "C" { |
||
| 7 | #endif |
||
| 8 | |||
| 9 | |||
| 10 | // standard C includes |
||
| 11 | #include <stdint.h> |
||
| 12 | #include <time.h> |
||
| 13 | |||
| 14 | |||
| 15 | // bring TLS support to antique compilers |
||
| 16 | #ifndef thread_local |
||
| 17 | #ifdef _MSC_VER |
||
| 18 | #define thread_local __declspec(thread) // the thread_local keyword wasn't defined before C++11 and C23 |
||
| 19 | #else // a saner compiler |
||
| 20 | #define thread_local _Thread_local // the thread_local keyword wasn't defined before C++11 and C23 |
||
| 21 | #endif // _MSC_VER |
||
| 22 | #endif // !thread_local |
||
| 23 | |||
| 24 | |||
| 25 | // Microsoft-specific shims |
||
| 26 | #ifdef _MSC_VER |
||
| 27 | #if defined(_M_AMD64) |
||
| 28 | #define __x86_64__ 1 |
||
| 29 | #elif defined(_M_ARM64) || defined(_M_ARM64EC) |
||
| 30 | #define __aarch64__ 1 |
||
| 31 | #endif // native platform identification |
||
| 32 | static inline char *sane_strerror (int errnum) { thread_local static char errorstr[256]; strerror_s (errorstr, sizeof (errorstr), errnum); return (errorstr); } // non-complaining strerror() implementation for MSVC. Note that strerror() is unsafe IF AND ONLY IF the locale is changed during program execution AND a string pointer returned by strerror is saved and reused by the program. Guess the odds it will happen here... |
||
| 33 | #define strerror(n) sane_strerror ((n)) // STFU Microsoft |
||
| 34 | static inline char *sane_getenv (const char *varname) { extern char **environ; size_t i, len = strlen (varname); for (i = 0; environ[i] != NULL; i++) if ((strncmp (environ[i], varname, len) == 0) && ((environ[i])[len] == '=')) return (&(environ[i])[len + 1]); return (NULL); } // non-complaining getenv() implementation for MSVC. Note that getenv_s() access _environ too as per Microsoft docs, so this is perfectly legit. Nuff said. |
||
| 35 | #define getenv(v) sane_getenv ((v)) // STFU Microsoft |
||
| 36 | static struct tm *sane_localtime (const time_t *clock) { thread_local static struct tm localtime_tm; if (localtime_s (&localtime_tm, clock) != 0) return (NULL); return (&localtime_tm); } // non-complaining localtime() implementation for MSVC. Uses TLS. |
||
| 37 | #define localtime(t) sane_localtime ((t)) // STFU Microsoft |
||
| 38 | static inline char *sane_asctime (const struct tm *tm_time) { thread_local static char asctimestr[64]; if (asctime_s (asctimestr, sizeof (asctimestr), tm_time) != 0) return (NULL); return (asctimestr); } // non-complaining asctime() implementation for MSVC. Uses TLS. |
||
| 39 | #define asctime(t) sane_asctime ((t)) // STFU Microsoft |
||
| 40 | #define S_IFIFO 0x1000 |
||
| 41 | #define S_IFLNK 0xa000 |
||
| 42 | #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) |
||
| 43 | #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) |
||
| 44 | #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) |
||
| 45 | #define strdup(s) _strdup ((s)) |
||
| 46 | #define strcasecmp(s1,s2) _stricmp ((s1), (s2)) |
||
| 47 | #define strtok_r(s,delim,ctx) strtok_s ((s), (delim), (ctx)) |
||
| 48 | #define mkdir(p,m) _mkdir ((p)) |
||
| 49 | #define utimbuf __utimbuf32 |
||
| 50 | #define utime(p,t) _utime32 ((p), (t)) |
||
| 51 | #define access(p,m) _access ((p), (m)) |
||
| 52 | #define MAXPATHLEN 1024 |
||
| 53 | #else // !_MSC_VER |
||
| 54 | #define strcpy_s(s1,s1size,s2) strcpy ((s1), (s2)) |
||
| 55 | #define strcat_s(s1,s1size,s2) strcat ((s1), (s2)) |
||
| 56 | #define sprintf_s(s1,s1size,...) sprintf ((s1), __VA_ARGS__) |
||
| 57 | #define fopen_s(fp,pathname,mode) *(fp) = fopen ((pathname), (mode)) |
||
| 58 | #endif // _MSC_VER |
||
| 59 | |||
| 60 | |||
| 61 | // handy macros that generate a version number in the format "YYYYMMDD" corresponding to the build date. Usage: printf ("version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD); |
||
| 62 | #ifndef VERSION_ARG_YYYYMMDD |
||
| 63 | #define BUILDDATE_YEAR (&__DATE__[7]) // compiler will optimize this into a const string, e.g. "2021" |
||
| 64 | #define BUILDDATE_MONTH ( \ |
||
| 65 | *((uint32_t *) __DATE__) == *((uint32_t *) "Jan ") ? "01" : \ |
||
| 66 | *((uint32_t *) __DATE__) == *((uint32_t *) "Feb ") ? "02" : \ |
||
| 67 | *((uint32_t *) __DATE__) == *((uint32_t *) "Mar ") ? "03" : \ |
||
| 68 | *((uint32_t *) __DATE__) == *((uint32_t *) "Apr ") ? "04" : \ |
||
| 69 | *((uint32_t *) __DATE__) == *((uint32_t *) "May ") ? "05" : \ |
||
| 70 | *((uint32_t *) __DATE__) == *((uint32_t *) "Jun ") ? "06" : \ |
||
| 71 | *((uint32_t *) __DATE__) == *((uint32_t *) "Jul ") ? "07" : \ |
||
| 72 | *((uint32_t *) __DATE__) == *((uint32_t *) "Aug ") ? "08" : \ |
||
| 73 | *((uint32_t *) __DATE__) == *((uint32_t *) "Sep ") ? "09" : \ |
||
| 74 | *((uint32_t *) __DATE__) == *((uint32_t *) "Oct ") ? "10" : \ |
||
| 75 | *((uint32_t *) __DATE__) == *((uint32_t *) "Nov ") ? "11" : \ |
||
| 76 | *((uint32_t *) __DATE__) == *((uint32_t *) "Dec ") ? "12" : \ |
||
| 77 | "XX" \ |
||
| 78 | ) // compiler will optimize this into a const string, e.g. "11" |
||
| 79 | #define BUILDDATE_DAY ( \ |
||
| 80 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 1 ") ? "01" : \ |
||
| 81 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 2 ") ? "02" : \ |
||
| 82 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 3 ") ? "03" : \ |
||
| 83 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 4 ") ? "04" : \ |
||
| 84 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 5 ") ? "05" : \ |
||
| 85 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 6 ") ? "06" : \ |
||
| 86 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 7 ") ? "07" : \ |
||
| 87 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 8 ") ? "08" : \ |
||
| 88 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 9 ") ? "09" : \ |
||
| 89 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 10 ") ? "10" : \ |
||
| 90 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 11 ") ? "11" : \ |
||
| 91 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 12 ") ? "12" : \ |
||
| 92 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 13 ") ? "13" : \ |
||
| 93 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 14 ") ? "14" : \ |
||
| 94 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 15 ") ? "15" : \ |
||
| 95 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 16 ") ? "16" : \ |
||
| 96 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 17 ") ? "17" : \ |
||
| 97 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 18 ") ? "18" : \ |
||
| 98 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 19 ") ? "19" : \ |
||
| 99 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 20 ") ? "20" : \ |
||
| 100 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 21 ") ? "21" : \ |
||
| 101 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 22 ") ? "22" : \ |
||
| 102 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 23 ") ? "23" : \ |
||
| 103 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 24 ") ? "24" : \ |
||
| 104 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 25 ") ? "25" : \ |
||
| 105 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 26 ") ? "26" : \ |
||
| 106 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 27 ") ? "27" : \ |
||
| 107 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 28 ") ? "28" : \ |
||
| 108 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 29 ") ? "29" : \ |
||
| 109 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 30 ") ? "30" : \ |
||
| 110 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 31 ") ? "31" : \ |
||
| 111 | "XX" \ |
||
| 112 | ) // compiler will optimize this into a const string, e.g. "14" |
||
| 113 | #define VERSION_FMT_YYYYMMDD "%s%s%s" |
||
| 114 | #define VERSION_ARG_YYYYMMDD BUILDDATE_YEAR, BUILDDATE_MONTH, BUILDDATE_DAY |
||
| 115 | #endif // !VERSION_ARG_YYYYMMDD |
||
| 116 | |||
| 117 | |||
| 118 | // macro to bring __FILE_NAME__ support to moronic compilers |
||
| 119 | #ifndef __FILE_NAME__ // Clang 9+ has the macro, GCC 12+ added it too in 2021, MSVC obviously won't do it. Heh. |
||
| 120 | #define __FILE_NAME__ ( \ |
||
| 121 | (sizeof (__FILE__) > 2) && ((__FILE__[sizeof (__FILE__) - 2] == '/') || (__FILE__[sizeof (__FILE__) - 2] == '\\')) ? &__FILE__[sizeof (__FILE__) - 1] : \ |
||
| 122 | (sizeof (__FILE__) > 3) && ((__FILE__[sizeof (__FILE__) - 3] == '/') || (__FILE__[sizeof (__FILE__) - 3] == '\\')) ? &__FILE__[sizeof (__FILE__) - 2] : \ |
||
| 123 | (sizeof (__FILE__) > 4) && ((__FILE__[sizeof (__FILE__) - 4] == '/') || (__FILE__[sizeof (__FILE__) - 4] == '\\')) ? &__FILE__[sizeof (__FILE__) - 3] : \ |
||
| 124 | (sizeof (__FILE__) > 5) && ((__FILE__[sizeof (__FILE__) - 5] == '/') || (__FILE__[sizeof (__FILE__) - 5] == '\\')) ? &__FILE__[sizeof (__FILE__) - 4] : \ |
||
| 125 | (sizeof (__FILE__) > 6) && ((__FILE__[sizeof (__FILE__) - 6] == '/') || (__FILE__[sizeof (__FILE__) - 6] == '\\')) ? &__FILE__[sizeof (__FILE__) - 5] : \ |
||
| 126 | (sizeof (__FILE__) > 7) && ((__FILE__[sizeof (__FILE__) - 7] == '/') || (__FILE__[sizeof (__FILE__) - 7] == '\\')) ? &__FILE__[sizeof (__FILE__) - 6] : \ |
||
| 127 | (sizeof (__FILE__) > 8) && ((__FILE__[sizeof (__FILE__) - 8] == '/') || (__FILE__[sizeof (__FILE__) - 8] == '\\')) ? &__FILE__[sizeof (__FILE__) - 7] : \ |
||
| 128 | (sizeof (__FILE__) > 9) && ((__FILE__[sizeof (__FILE__) - 9] == '/') || (__FILE__[sizeof (__FILE__) - 9] == '\\')) ? &__FILE__[sizeof (__FILE__) - 8] : \ |
||
| 129 | (sizeof (__FILE__) > 10) && ((__FILE__[sizeof (__FILE__) - 10] == '/') || (__FILE__[sizeof (__FILE__) - 10] == '\\')) ? &__FILE__[sizeof (__FILE__) - 9] : \ |
||
| 130 | (sizeof (__FILE__) > 11) && ((__FILE__[sizeof (__FILE__) - 11] == '/') || (__FILE__[sizeof (__FILE__) - 11] == '\\')) ? &__FILE__[sizeof (__FILE__) - 10] : \ |
||
| 131 | (sizeof (__FILE__) > 12) && ((__FILE__[sizeof (__FILE__) - 12] == '/') || (__FILE__[sizeof (__FILE__) - 12] == '\\')) ? &__FILE__[sizeof (__FILE__) - 11] : \ |
||
| 132 | (sizeof (__FILE__) > 13) && ((__FILE__[sizeof (__FILE__) - 13] == '/') || (__FILE__[sizeof (__FILE__) - 13] == '\\')) ? &__FILE__[sizeof (__FILE__) - 12] : \ |
||
| 133 | (sizeof (__FILE__) > 14) && ((__FILE__[sizeof (__FILE__) - 14] == '/') || (__FILE__[sizeof (__FILE__) - 14] == '\\')) ? &__FILE__[sizeof (__FILE__) - 13] : \ |
||
| 134 | (sizeof (__FILE__) > 15) && ((__FILE__[sizeof (__FILE__) - 15] == '/') || (__FILE__[sizeof (__FILE__) - 15] == '\\')) ? &__FILE__[sizeof (__FILE__) - 14] : \ |
||
| 135 | (sizeof (__FILE__) > 16) && ((__FILE__[sizeof (__FILE__) - 16] == '/') || (__FILE__[sizeof (__FILE__) - 16] == '\\')) ? &__FILE__[sizeof (__FILE__) - 15] : \ |
||
| 136 | (sizeof (__FILE__) > 17) && ((__FILE__[sizeof (__FILE__) - 17] == '/') || (__FILE__[sizeof (__FILE__) - 17] == '\\')) ? &__FILE__[sizeof (__FILE__) - 16] : \ |
||
| 137 | (sizeof (__FILE__) > 18) && ((__FILE__[sizeof (__FILE__) - 18] == '/') || (__FILE__[sizeof (__FILE__) - 18] == '\\')) ? &__FILE__[sizeof (__FILE__) - 17] : \ |
||
| 138 | (sizeof (__FILE__) > 19) && ((__FILE__[sizeof (__FILE__) - 19] == '/') || (__FILE__[sizeof (__FILE__) - 19] == '\\')) ? &__FILE__[sizeof (__FILE__) - 18] : \ |
||
| 139 | (sizeof (__FILE__) > 20) && ((__FILE__[sizeof (__FILE__) - 20] == '/') || (__FILE__[sizeof (__FILE__) - 20] == '\\')) ? &__FILE__[sizeof (__FILE__) - 19] : \ |
||
| 140 | (sizeof (__FILE__) > 21) && ((__FILE__[sizeof (__FILE__) - 21] == '/') || (__FILE__[sizeof (__FILE__) - 21] == '\\')) ? &__FILE__[sizeof (__FILE__) - 20] : \ |
||
| 141 | (sizeof (__FILE__) > 22) && ((__FILE__[sizeof (__FILE__) - 22] == '/') || (__FILE__[sizeof (__FILE__) - 22] == '\\')) ? &__FILE__[sizeof (__FILE__) - 21] : \ |
||
| 142 | (sizeof (__FILE__) > 23) && ((__FILE__[sizeof (__FILE__) - 23] == '/') || (__FILE__[sizeof (__FILE__) - 23] == '\\')) ? &__FILE__[sizeof (__FILE__) - 22] : \ |
||
| 143 | (sizeof (__FILE__) > 24) && ((__FILE__[sizeof (__FILE__) - 24] == '/') || (__FILE__[sizeof (__FILE__) - 24] == '\\')) ? &__FILE__[sizeof (__FILE__) - 23] : \ |
||
| 144 | (sizeof (__FILE__) > 25) && ((__FILE__[sizeof (__FILE__) - 25] == '/') || (__FILE__[sizeof (__FILE__) - 25] == '\\')) ? &__FILE__[sizeof (__FILE__) - 24] : \ |
||
| 145 | (sizeof (__FILE__) > 26) && ((__FILE__[sizeof (__FILE__) - 26] == '/') || (__FILE__[sizeof (__FILE__) - 26] == '\\')) ? &__FILE__[sizeof (__FILE__) - 25] : \ |
||
| 146 | (sizeof (__FILE__) > 27) && ((__FILE__[sizeof (__FILE__) - 27] == '/') || (__FILE__[sizeof (__FILE__) - 27] == '\\')) ? &__FILE__[sizeof (__FILE__) - 26] : \ |
||
| 147 | (sizeof (__FILE__) > 28) && ((__FILE__[sizeof (__FILE__) - 28] == '/') || (__FILE__[sizeof (__FILE__) - 28] == '\\')) ? &__FILE__[sizeof (__FILE__) - 27] : \ |
||
| 148 | (sizeof (__FILE__) > 29) && ((__FILE__[sizeof (__FILE__) - 29] == '/') || (__FILE__[sizeof (__FILE__) - 29] == '\\')) ? &__FILE__[sizeof (__FILE__) - 28] : \ |
||
| 149 | (sizeof (__FILE__) > 30) && ((__FILE__[sizeof (__FILE__) - 30] == '/') || (__FILE__[sizeof (__FILE__) - 30] == '\\')) ? &__FILE__[sizeof (__FILE__) - 29] : \ |
||
| 150 | (sizeof (__FILE__) > 31) && ((__FILE__[sizeof (__FILE__) - 31] == '/') || (__FILE__[sizeof (__FILE__) - 31] == '\\')) ? &__FILE__[sizeof (__FILE__) - 30] : \ |
||
| 151 | (sizeof (__FILE__) > 32) && ((__FILE__[sizeof (__FILE__) - 32] == '/') || (__FILE__[sizeof (__FILE__) - 32] == '\\')) ? &__FILE__[sizeof (__FILE__) - 31] : \ |
||
| 152 | (sizeof (__FILE__) > 33) && ((__FILE__[sizeof (__FILE__) - 33] == '/') || (__FILE__[sizeof (__FILE__) - 33] == '\\')) ? &__FILE__[sizeof (__FILE__) - 32] : \ |
||
| 153 | __FILE__) // this *COMPILE-TIME* macro complements the __FILE__ macro defined by the C standard by returning just the filename portion of the full path. Supports filenames up to 32 chars. Expand as necessary. |
||
| 154 | #endif // !__FILE_NAME__ |
||
| 155 | |||
| 156 | |||
| 157 | // logging macros |
||
| 158 | #define LOG(type,lvl,...) do { if ((lvl) <= verbose_level) { fprintf (stderr, "ifstool: %s: ", (type)); if (verbose_level > 1) fprintf (stderr, "%s:%d:%s(): ", __FILE_NAME__, __LINE__, __func__); fprintf (stderr, __VA_ARGS__); fputc ('\n', stderr); } } while (0) |
||
| 159 | #define LOG_ERROR(...) LOG ("error", 0, __VA_ARGS__) |
||
| 160 | #define LOG_WARNING(...) LOG ("warning", 1, __VA_ARGS__) |
||
| 161 | #define LOG_INFO(...) LOG ("info", 2, __VA_ARGS__) |
||
| 162 | #define LOG_DEBUG(...) LOG ("debug", 3, __VA_ARGS__) |
||
| 163 | |||
| 164 | // macro to gently exit with an error message |
||
| 165 | #define DIE_WITH_EXITCODE(exitcode,...) do { LOG_ERROR (__VA_ARGS__); exit ((exitcode)); } while (0) |
||
| 166 | |||
| 167 | // macro to exit less brutally than with abort() if something doesn't go the way we'd like to |
||
| 168 | #define ASSERT(is_it_true,...) do { if (!(is_it_true)) { LOG ("fatal error", 0, "consistency check failed:"); LOG ("fatal error", 0, #is_it_true); LOG ("fatal error", 0, __VA_ARGS__); exit (1); } } while (0) |
||
| 169 | #define ASSERT_WITH_ERRNO(is_it_true) ASSERT ((is_it_true), "%s", strerror (errno)) |
||
| 170 | |||
| 171 | |||
| 172 | #ifdef __cplusplus |
||
| 173 | } |
||
| 174 | #endif |
||
| 175 | |||
| 176 | |||
| 177 | #endif // UTILITY_H |