1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
|
/* Jody Bruchon's helpful code library header
* Copyright (C) 2014-2025 by Jody Bruchon <jody@jodybruchon.com>
* Licensed under The MIT License
* Source code: https://codeberg.org/jbruchon/libjodycode
*/
#ifndef LIBJODYCODE_H
#define LIBJODYCODE_H
#ifdef __cplusplus
extern "C" {
#endif
/* libjodycode version information
* The major/minor version number and API version/revision MUST match!
* Major version must change whenever an interface incompatibly changes
* Minor version must change when new interfaces are added
* Revision version must not change interfaces in any way
* Revision is optional in version string, so "2.0" is identical to 2.0.0
* Feature level is incremented whenever the available interfaces change
* regardless of compatibility; the lowest feature level possible that
* supports the used interfaces should be chosen by programs that check
* version information for compatibility. See README for more information. */
#define LIBJODYCODE_API_VERSION 4
#define LIBJODYCODE_API_FEATURE_LEVEL 5
#define LIBJODYCODE_VER "4.1"
#define LIBJODYCODE_VERDATE "2025-09-25"
#ifdef UNICODE
#define LIBJODYCODE_WINDOWS_UNICODE 1
#else
#define LIBJODYCODE_WINDOWS_UNICODE 0
#endif
/* Define ON_WINDOWS if not otherwise defined and building on Windows */
#if defined _WIN32 || defined __WIN32 || defined WIN64 || defined __WIN64
#ifndef ON_WINDOWS
#define ON_WINDOWS
#endif
#endif
/* Silence a MSVC++ warning */
#ifdef _MSC_VER
#pragma warning(disable : 4200)
#endif
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#define JC_PATHBUF_SIZE 32768
#ifdef ON_WINDOWS
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MAN
#endif
#include <windows.h>
#include <direct.h>
/* Unicode conversion on Windows */
#ifndef M2W
#define M2W(a,b) MultiByteToWideChar(CP_UTF8, 0, a, -1, (LPWSTR)b, JC_PATHBUF_SIZE)
#endif
#ifndef M2W_SIZED
#define M2W_SIZED(a,b,c) MultiByteToWideChar(CP_UTF8, 0, a, -1, (LPWSTR)b, c)
#endif
#ifndef W2M
#define W2M(a,b) WideCharToMultiByte(CP_UTF8, 0, a, -1, (LPSTR)b, JC_PATHBUF_SIZE, NULL, FALSE)
#endif
#ifndef W2M_SIZED
#define W2M_SIZED(a,b,c) WideCharToMultiByte(CP_UTF8, 0, a, -1, (LPSTR)b, c, NULL, FALSE)
#endif
#define jc_GetLastError() (int32_t)GetLastError()
#else
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#endif /* ON_WINDOWS */
/* Extend an allocation length to the next 64-bit (8-byte) boundary */
#define JC_EXTEND64(a) (((a) & 0x7) > 0 ? (((a) & (~0x7)) + 8) : (a))
/* File modes and wchar_t type */
/* Uther things depend on this, keep it at the top */
#if defined _WIN32 || defined __WIN32 || defined ON_WINDOWS
#ifdef UNICODE
#define JC_WCHAR_T wchar_t
#define JC_FILE_MODE_RDONLY L"rb"
#define JC_FILE_MODE_WRONLY L"wb"
#define JC_FILE_MODE_RW L"w+b"
#define JC_FILE_MODE_RW_EXISTING L"r+b"
#define JC_FILE_MODE_WRONLY_APPEND L"ab"
#define JC_FILE_MODE_RW_APPEND L"a+b"
#define JC_FILE_MODE_RDONLY_SEQ L"rbS"
#define JC_FILE_MODE_WRONLY_SEQ L"wbS"
#define JC_FILE_MODE_RW_SEQ L"w+bS"
#define JC_FILE_MODE_RW_EXISTING_SEQ L"r+bS"
#define JC_FILE_MODE_WRONLY_APPEND_SEQ L"abS"
#define JC_FILE_MODE_RW_APPEND_SEQ L"a+bS"
#else /* Windows, not UNICODE */
#define JC_WCHAR_T char
#define JC_FILE_MODE_RDONLY "rb"
#define JC_FILE_MODE_WRONLY "wb"
#define JC_FILE_MODE_RW "w+b"
#define JC_FILE_MODE_RW_EXISTING "r+b"
#define JC_FILE_MODE_WRONLY_APPEND "ab"
#define JC_FILE_MODE_RW_APPEND "a+b"
#define JC_FILE_MODE_RDONLY_SEQ "rbS"
#define JC_FILE_MODE_WRONLY_SEQ "wbS"
#define JC_FILE_MODE_RW_SEQ "w+bS"
#define JC_FILE_MODE_RW_EXISTING_SEQ "r+bS"
#define JC_FILE_MODE_WRONLY_APPEND_SEQ "abS"
#define JC_FILE_MODE_RW_APPEND_SEQ "a+bS"
#endif
#define JC_F_OK 0
#define JC_R_OK 4
#define JC_W_OK 2
#define JC_X_OK 6
#else /* Not Windows */
#define JC_WCHAR_T char
#define JC_FILE_MODE_RDONLY "rb"
#define JC_FILE_MODE_WRONLY "wb"
#define JC_FILE_MODE_RW "w+b"
#define JC_FILE_MODE_RW_EXISTING "r+b"
#define JC_FILE_MODE_WRONLY_APPEND "ab"
#define JC_FILE_MODE_RW_APPEND "a+b"
#define JC_FILE_MODE_RDONLY_SEQ "rb"
#define JC_FILE_MODE_WRONLY_SEQ "wb"
#define JC_FILE_MODE_RW_SEQ "w+b"
#define JC_FILE_MODE_RW_EXISTING_SEQ "r+b"
#define JC_FILE_MODE_WRONLY_APPEND_SEQ "ab"
#define JC_FILE_MODE_RW_APPEND_SEQ "a+b"
#define JC_F_OK F_OK
#define JC_R_OK R_OK
#define JC_W_OK W_OK
#define JC_X_OK X_OK
#endif /* Windows */
/*** time ***/
/* This is out of order because other things depend on it */
#ifdef ON_WINDOWS
struct JC_TIMESPEC {
time_t tv_sec;
long tv_nsec;
};
extern int jc_nttime_to_unixtime(const FILETIME * const restrict filetime, struct JC_TIMESPEC * const restrict unixtime);
extern int jc_unixtime_to_nttime(const struct JC_TIMESPEC * const restrict unixtime, FILETIME * const restrict filetime);
#else
#define JC_TIMESPEC timespec
#endif /* ON_WINDOWS */
/*** stat ***/
/* This is out of order because other things depend on it */
#ifdef ON_WINDOWS
struct JC_STAT {
uint64_t st_ino;
int64_t st_size;
uint32_t st_dev;
uint32_t st_nlink;
uint32_t st_mode;
struct JC_TIMESPEC st_atim;
struct JC_TIMESPEC st_mtim;
struct JC_TIMESPEC st_ctim;
/* legacy st_*time should be avoided. Use st_*tim.tv_sec instead. */
};
/* stat() macros for Windows "mode" flags (file attributes) */
#define JC_S_IFMT FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_READONLY | \
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | \
FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_DIRECTORY | \
FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_REPARSE_POINT | \
FILE_ATTRIBUTE_SPARSE | FILE_ATTRIBUTE_TEMPORARY
#define JC_S_IFARCHIVE FILE_ATTRIBUTE_ARCHIVE
#define JC_S_IFRO FILE_ATTRIBUTE_READONLY
#define JC_S_IFHIDDEN FILE_ATTRIBUTE_HIDDEN
#define JC_S_IFSYSTEM FILE_ATTRIBUTE_SYSTEM
#define JC_S_IFCRYPT FILE_ATTRIBUTE_ENCRYPTED
#define JC_S_IFDIR FILE_ATTRIBUTE_DIRECTORY
#define JC_S_IFCOMPR FILE_ATTRIBUTE_COMPRESSED
#define JC_S_IFREPARSE FILE_ATTRIBUTE_REPARSE_POINT
#define JC_S_IFSPARSE FILE_ATTRIBUTE_SPARSE
#define JC_S_IFTEMP FILE_ATTRIBUTE_TEMPORARY
#define JC_S_IFREG !(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)
#define JC_S_IFLNK FILE_ATTRIBUTE_REPARSE_POINT
#define JC_S_ISARCHIVE(st_mode) ((st_mode & FILE_ATTRIBUTE_ARCHIVE) ? 1 : 0)
#define JC_S_ISRO(st_mode) ((st_mode & FILE_ATTRIBUTE_READONLY) ? 1 : 0)
#define JC_S_ISHIDDEN(st_mode) ((st_mode & FILE_ATTRIBUTE_HIDDEN) ? 1 : 0)
#define JC_S_ISSYSTEM(st_mode) ((st_mode & FILE_ATTRIBUTE_SYSTEM) ? 1 : 0)
#define JC_S_ISCRYPT(st_mode) ((st_mode & FILE_ATTRIBUTE_ENCRYPTED) ? 1 : 0)
#define JC_S_ISDIR(st_mode) ((st_mode & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0)
#define JC_S_ISCOMPR(st_mode) ((st_mode & FILE_ATTRIBUTE_COMPRESSED) ? 1 : 0)
#define JC_S_ISREPARSE(st_mode) ((st_mode & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0)
#define JC_S_ISSPARSE(st_mode) ((st_mode & FILE_ATTRIBUTE_SPARSE) ? 1 : 0)
#define JC_S_ISTEMP(st_mode) ((st_mode & FILE_ATTRIBUTE_TEMPORARY) ? 1 : 0)
#define JC_S_ISREG(st_mode) ((st_mode & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) ? 0 : 1)
#define JC_S_ISLNK(st_mode) ((st_mode & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0)
#else
#include <sys/stat.h>
#define JC_STAT stat
#define JC_S_IFMT S_IFMT
#define JC_S_IFARCHIVE 0
#define JC_S_IFRO 0
#define JC_S_IFHIDDEN 0
#define JC_S_IFSYSTEM 0
#define JC_S_IFCRYPT 0
#define JC_S_IFDIR S_IFDIR
#define JC_S_IFCOMPR 0
#define JC_S_IFREPARSE 0
#define JC_S_IFSPARSE 0
#define JC_S_IFTEMP 0
#define JC_S_IFREG S_IFREG
#define JC_S_IFBLK S_IFBLK
#define JC_S_IFCHR S_IFCHR
#define JC_S_IFIFO S_IFIFO
#define JC_S_IFSOCK S_IFSOCK
#define JC_S_ISARCHIVE(st_mode) 0
#define JC_S_ISRO(st_mode) 0
#define JC_S_ISHIDDEN(st_mode) 0
#define JC_S_ISSYSTEM(st_mode) 0
#define JC_S_ISCRYPT(st_mode) 0
#define JC_S_ISDIR(st_mode) S_ISDIR(st_mode)
#define JC_S_ISCOMPR(st_mode) 0
#define JC_S_ISREPARSE(st_mode) 0
#define JC_S_ISSPARSE(st_mode) 0
#define JC_S_ISTEMP(st_mode) 0
#define JC_S_ISREG(st_mode) S_ISREG(st_mode)
#define JC_S_ISLNK(st_mode) S_ISLNK(st_mode)
#endif /* ON_WINDOWS */
extern int jc_stat(const char * const filename, struct JC_STAT * const restrict buf);
/*** access ***/
extern int jc_access(const char *pathname, int mode);
/*** alarm ***/
extern int jc_alarm_ring;
extern int jc_start_alarm(const unsigned int seconds, const int repeat);
extern int jc_stop_alarm(void);
/*** cacheinfo ***/
/* Cache information structure
* Split caches populate i/d, unified caches populate non-i/d */
struct jc_proc_cacheinfo {
size_t l1;
size_t l1i;
size_t l1d;
size_t l2;
size_t l2i;
size_t l2d;
size_t l3;
size_t l3i;
size_t l3d;
};
extern struct jc_proc_cacheinfo *jc_get_proc_cacheinfo(int cleanup);
/*** dir ***/
/* Directory stream type
* Must be hijacked because FindFirstFileW() does one readdir() equivalent too
* When the first file is returned, this entry is removed from the linked list */
#ifdef ON_WINDOWS
struct JC_DIRENT {
uint64_t d_ino;
uint32_t d_namlen; /* we already do a strlen() so may as well pass it on */
unsigned char d_type;
char d_name[];
};
typedef struct _JC_DIR_T {
struct _JC_DIR_T *next;
int cached;
HANDLE hFind;
WIN32_FIND_DATA ffd;
struct JC_DIRENT dirent;
} JC_DIR;
#define JC_DIRENT_HAVE_D_NAMLEN
#define JC_DIRENT_HAVE_D_TYPE
#define JC_DT_BLK 6
#define JC_DT_CHR 2
#define JC_DT_DIR 4
#define JC_DT_FIFO 1
#define JC_DT_LNK 10
#define JC_DT_REG 8
#define JC_DT_SOCK 12
#define JC_DT_UNKNOWN 0
#define JC_DT_WHT 14
#else
#define JC_DIR DIR
#define JC_DIRENT dirent
#ifdef _DIRENT_HAVE_D_NAMLEN
#define JC_DIRENT_HAVE_D_NAMLEN
#endif
#ifdef _DIRENT_HAVE_D_TYPE
#define JC_DIRENT_HAVE_D_TYPE
#endif
#ifdef _DIRENT_HAVE_D_RECLEN
#define JC_DIRENT_HAVE_D_RECLEN
#endif
#ifdef _DIRENT_HAVE_D_OFF
#define JC_DIRENT_HAVE_D_OFF
#endif
#ifdef DT_UNKNOWN /* Cheap way to detect d_type support in the preprocessor */
#define JC_DT_BLK DT_BLK
#define JC_DT_CHR DT_CHR
#define JC_DT_DIR DT_DIR
#define JC_DT_FIFO DT_FIFO
#define JC_DT_LNK DT_LNK
#define JC_DT_REG DT_REG
#define JC_DT_SOCK DT_SOCK
#define JC_DT_UNKNOWN DT_UNKNOWN
#define JC_DT_WHT DT_WHT
#endif /* DT_UNKNOWN */
#endif /* ON_WINDOWS */
extern JC_DIR *jc_opendir(const char * restrict path);
extern size_t jc_get_d_namlen(const struct JC_DIRENT * const restrict dirent);
extern struct JC_DIRENT *jc_readdir(JC_DIR * const restrict dirp);
extern int jc_closedir(JC_DIR * const restrict dirp);
/*** batch ***/
/* This is out of order because it depends on the dir definitions */
struct jc_fileinfo {
struct JC_STAT *stat;
struct JC_DIRENT *dirent;
int status;
};
struct jc_fileinfo_batch {
int count;
struct jc_fileinfo files[];
};
extern struct jc_fileinfo_batch *jc_fileinfo_batch_alloc(const int filecnt, const int stat, const int namlen);
extern void jc_fileinfo_batch_free(struct jc_fileinfo_batch * const restrict batch);
/*** error ***/
extern int32_t jc_errno;
extern const char *jc_get_errname(int errnum);
extern const char *jc_get_errdesc(int errnum);
extern int jc_print_error(int errnum);
#define JC_ERRORCODE_START 1024
#define JC_ENOERROR 1024 // 0
#define JC_ENULL 1025
#define JC_ECDOTDOT 1026
#define JC_EGRNEND 1027
#define JC_EBADERR 1028
#define JC_EBADARGV 1029
#define JC_EMBWC 1030
#define JC_EALARM 1031
#define JC_EALLOC 1032
#define JC_ENUMSTRCMP 1033
#define JC_EDATETIME 1034 // 10
#define JC_EWIN32API 1035
#define JC_EKERNVER 1036
#define JC_ENOMEM 1037
#define JC_ESETVBUF 1038
/*** fopen ***/
extern FILE *jc_fopen(const char *pathname, const JC_WCHAR_T *mode);
extern int jc_fclose(FILE *stream);
/*** jc_fwprint ***/
extern int jc_fwprint(FILE * const restrict stream, const char * const restrict str, const int cr);
/*** getcwd ***/
extern char *jc_getcwd(char * const restrict pathname, const size_t size);
/*** jody_hash ***/
#ifndef JODY_HASH_H
#define JODY_HASH_VERSION 7
#define JODY_HASH_WIDTH 64
typedef uint64_t jodyhash_t;
#endif
enum jc_e_hash { NORMAL = 0, ROLLING = 1 };
extern int jc_block_hash(const enum jc_e_hash type, jodyhash_t *data, jodyhash_t *hash, const size_t count);
/*** link ***/
extern int jc_link(const char *path1, const char *path2);
/*** linkfiles ***/
enum jc_e_link { SYMLINK = 0, HARDLINK = 1, REFLINK = 2 };
extern int jc_linkfiles(struct jc_fileinfo_batch * const restrict batch, const enum jc_e_link linktype);
/*** oom ***/
/* Out-of-memory and null pointer error-exit functions */
extern void jc_oom(const char * restrict msg);
extern void jc_nullptr(const char * restrict func);
/*** paths ***/
/* Remove "middle" '..' components in a path: 'foo/../bar/baz' => 'bar/baz' */
extern int jc_collapse_dotdot(char * const path);
/* Given a src and dest path, create a relative path name from src to dest */
extern int jc_make_relative_link_name(const char * const src, const char * const dest, char * rel_path);
/*** rename ***/
extern int jc_rename(const char *oldpath, const char *newpath);
/*** remove ***/
extern int jc_remove(const char *pathname);
/*** size_suffix ***/
/* Suffix definitions (treat as case-insensitive) */
struct jc_size_suffix {
const char * const suffix;
const int64_t multiplier;
const int shift;
};
extern const struct jc_size_suffix jc_size_suffix[];
/*** sort ***/
/* Numerically-correct string sort with a little extra intelligence
insensitive: 0 = case-sensitive, 1 = case-insensitive */
extern int jc_numeric_strcmp(const char * restrict c1, const char * restrict c2, const int insensitive);
/*** string ***/
extern const char *jc_emptystring;
/* string type with length prefix */
typedef struct _JC_STR_T {
uint64_t len;
char str[1];
} JC_STR_T;
extern JC_STR_T *jc_str_init(const char *string, uint32_t len);
extern int jc_strncaseeq(const char *s1, const char *s2, const size_t len);
extern int jc_strcaseeq(const char *s1, const char *s2);
extern int jc_strneq(const char *s1, const char *s2, const size_t len);
extern int jc_streq(const char *s1, const char *s2);
extern int jc_strteq(const JC_STR_T *s1, const JC_STR_T *s2);
extern int jc_strtneq(const JC_STR_T *s1, const JC_STR_T *s2, const size_t len);
extern int jc_strtcaseeq(const JC_STR_T *s1, const JC_STR_T *s2);
extern int jc_strtncaseeq(const JC_STR_T *s1, const JC_STR_T *s2, const size_t len);
extern int jc_numeric_strtcmp(const JC_STR_T *c1, const JC_STR_T *c2, const int insensitive);
/*** strtoepoch ***/
/* Convert a date/time string to seconds since the epoch
* Format must be "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS" */
extern time_t jc_strtoepoch(const char * const datetime);
/*** version ***/
/* libjodycode version information */
extern const char *jc_version;
extern const char *jc_verdate;
extern const int jc_api_version;
extern const int jc_api_featurelevel;
extern const int jc_jodyhash_version;
extern const int jc_windows_unicode;
#ifdef __linux__
extern int jc_get_kernel_version(void);
#endif
/*** win_unicode ***/
/* Cross-platform help for strings in Unicode mode on Windows
* On non-Windows platforms a lot of these are just wrappers */
#ifdef ON_WINDOWS
#define JC_MODE_NO_CHANGE 0 /* Don't change the output mode */
#define JC_MODE_TEXT 1 /* Set output mode to _O_TEXT */
#define JC_MODE_BINARY 2 /* Set output mode to _O_BINARY (UTF-8) */
#define JC_MODE_UTF16 3 /* Set output mode to _O_U16TEXT (UTF-16) */
#define JC_MODE_UTF16_TTY 4 /* Set non-_O_TEXT output mode based on if it's a terminal or not */
extern JC_DIR *dirp_head;
extern int jc_ffd_to_dirent(JC_DIR **dirp, HANDLE hFind, WIN32_FIND_DATA *ffd);
extern void jc_slash_convert(char *path);
extern void jc_set_output_modes(const int out, const int err);
/* These are used for Unicode output and string work on Windows only */
#ifdef UNICODE
extern int jc_string_to_wstring(const char * const restrict string, JC_WCHAR_T **wstring);
extern int jc_widearg_to_argv(int argc, JC_WCHAR_T **wargv, char ***cargv);
#endif /* UNICODE */
#else
#define jc_slash_convert(a)
#define jc_set_output_modes(a,b)
#endif
extern int jc_setup_unicode_terminal(int argc, JC_WCHAR_T **wargv, char ***argv, int *stdout_tty);
#ifdef __cplusplus
}
#endif
#endif /* LIBJODYCODE_H */
|