Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317#ifndef PROGRESS_H#define PROGRESS_H
#include <stdio.h>#include <stdbool.h>#include <stdint.h>#include <stdatomic.h>#include <stdlib.h>#include <string.h>#include <time.h>
#ifdef _WIN32#define WIN32_LEAN_AND_MEAN#include <windows.h>#include <io.h>#define PROGRESS_ISATTY(fd) _isatty(fd)#define PROGRESS_FILENO(f) _fileno(f)#else#include <unistd.h>#include <sys/ioctl.h>#define PROGRESS_ISATTY(fd) isatty(fd)#define PROGRESS_FILENO(f) fileno(f)#endif
#define PROGRESS_MSG_SIZE 256
#ifdef __has_include#if __has_include(<pthread.h>)#include <pthread.h>#define PROGRESS_HAS_PTHREADS 1#endif#endif
typedef struct {#if defined(PROGRESS_HAS_PTHREADS) pthread_mutex_t mtx;#elif defined(_WIN32) CRITICAL_SECTION cs;#else int dummy;#endif} progress_mutex_t;
typedef struct { FILE *terminal; bool is_windows_terminal; bool supports_ansi; bool dont_print_on_dumb; uint64_t start_time_ns; uint64_t prev_refresh_ns; uint64_t refresh_rate_ns; uint64_t initial_delay_ns; bool done; bool timer_valid; size_t columns_written; progress_mutex_t mutex; char buffer[PROGRESS_MSG_SIZE]; char msg_buffer[PROGRESS_MSG_SIZE];} progress_t;
static inline void progress_mutex_init(progress_mutex_t *m) {#if defined(PROGRESS_HAS_PTHREADS) pthread_mutex_init(&m->mtx, NULL);#elif defined(_WIN32) InitializeCriticalSection(&m->cs);#else (void)m;#endif}
static inline void progress_mutex_destroy(progress_mutex_t *m) {#if defined(PROGRESS_HAS_PTHREADS) pthread_mutex_destroy(&m->mtx);#elif defined(_WIN32) DeleteCriticalSection(&m->cs);#else (void)m;#endif}
static inline void progress_mutex_lock(progress_mutex_t *m) {#if defined(PROGRESS_HAS_PTHREADS) pthread_mutex_lock(&m->mtx);#elif defined(_WIN32) EnterCriticalSection(&m->cs);#else (void)m;#endif}
static inline void progress_mutex_unlock(progress_mutex_t *m) {#if defined(PROGRESS_HAS_PTHREADS) pthread_mutex_unlock(&m->mtx);#elif defined(_WIN32) LeaveCriticalSection(&m->cs);#else (void)m;#endif}
static inline bool progress_mutex_trylock(progress_mutex_t *m) {#if defined(PROGRESS_HAS_PTHREADS) return pthread_mutex_trylock(&m->mtx) == 0;#elif defined(_WIN32) return TryEnterCriticalSection(&m->cs) != 0;#else (void)m; return true;#endif}
static inline uint64_t progress_now_ns(void) { struct timespec ts;#if defined(CLOCK_MONOTONIC) if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec; }#elif defined(_WIN32) static LARGE_INTEGER freq = {0}; if (freq.QuadPart == 0) { QueryPerformanceFrequency(&freq); } LARGE_INTEGER counter; QueryPerformanceCounter(&counter); return (uint64_t)(counter.QuadPart * 1000000000ULL / freq.QuadPart);#endif if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec; } return 0;}
static inline bool progress_detect_ansi(FILE *f) { if (!f) return false; int fd = PROGRESS_FILENO(f); if (!PROGRESS_ISATTY(fd)) return false; #ifdef _WIN32 HANDLE h = (HANDLE)_get_osfhandle(fd); DWORD mode; if (GetConsoleMode(h, &mode)) { if (SetConsoleMode(h, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) return true; } const char *term = getenv("TERM"); return (term && strstr(term, "xterm")) || (term && strstr(term, "vt100")) || (term && strstr(term, "color")) || (term && strstr(term, "ansi"));#else const char *term = getenv("TERM"); if (!term) return false; if (strcmp(term, "dumb") == 0) return false; return true;#endif}
static void progress_refresh_locked(progress_t *p);static void progress_clear_locked(progress_t *p, size_t *end);
static inline void progress_start(progress_t *p, const char *message) { memset(p, 0, sizeof(*p)); progress_mutex_init(&p->mutex); FILE *f = stderr; int fd = PROGRESS_FILENO(f); if (PROGRESS_ISATTY(fd)) { p->terminal = f; p->supports_ansi = progress_detect_ansi(f);#ifdef _WIN32 if (!p->supports_ansi) p->is_windows_terminal = true;#endif } else { p->terminal = f; p->supports_ansi = false; p->is_windows_terminal = false; } if (message) { strncpy(p->msg_buffer, message, PROGRESS_MSG_SIZE - 1); p->msg_buffer[PROGRESS_MSG_SIZE - 1] = '\0'; } else p->msg_buffer[0] = '\0'; p->refresh_rate_ns = 50 * 1000000ULL; p->initial_delay_ns = 500 * 1000000ULL; p->start_time_ns = progress_now_ns(); p->prev_refresh_ns = 0; p->timer_valid = (p->start_time_ns != 0); p->columns_written = 0; p->done = false; progress_refresh_locked(p);}
static inline void progress_maybe_refresh(progress_t *p) { if (!p->timer_valid) return; if (!progress_mutex_trylock(&p->mutex)) return; uint64_t now = progress_now_ns(); uint64_t elapsed = now - p->start_time_ns; if (elapsed < p->initial_delay_ns) { progress_mutex_unlock(&p->mutex); return; } if (now < p->prev_refresh_ns || (now - p->prev_refresh_ns) < p->refresh_rate_ns) { progress_mutex_unlock(&p->mutex); return; } progress_refresh_locked(p); progress_mutex_unlock(&p->mutex);}
static inline void progress_update(progress_t *p, const char *message) { progress_mutex_lock(&p->mutex); if (message) { strncpy(p->msg_buffer, message, PROGRESS_MSG_SIZE - 1); p->msg_buffer[PROGRESS_MSG_SIZE - 1] = '\0'; } else p->msg_buffer[0] = '\0';
progress_mutex_unlock(&p->mutex); progress_maybe_refresh(p);}
static inline void progress_refresh(progress_t *p) { if (!progress_mutex_trylock(&p->mutex)) return; progress_refresh_locked(p); progress_mutex_unlock(&p->mutex);}
static void progress_clear_locked(progress_t *p, size_t *end) { if (!p->terminal) return; size_t pos = *end; if (p->columns_written > 0) { if (p->supports_ansi) { int written = snprintf(p->buffer + pos, sizeof(p->buffer) - pos, "\x1b[%zuD\x1b[0K", p->columns_written); if (written > 0) pos += (size_t)written; } else if (p->is_windows_terminal) {#ifdef _WIN32 HANDLE h = (HANDLE)_get_osfhandle(PROGRESS_FILENO(p->terminal)); CONSOLE_SCREEN_BUFFER_INFO info; if (GetConsoleScreenBufferInfo(h, &info)) { COORD cursor = { .X = (SHORT)(info.dwCursorPosition.X - (SHORT)p->columns_written), .Y = info.dwCursorPosition.Y }; if (cursor.X < 0) cursor.X = 0; DWORD fill_len = (DWORD)(info.dwSize.X - cursor.X); DWORD written; FillConsoleOutputAttribute(h, info.wAttributes, fill_len, cursor, &written); FillConsoleOutputCharacterA(h, ' ', fill_len, cursor, &written); SetConsoleCursorPosition(h, cursor); }#endif } else { if (pos < sizeof(p->buffer)) p->buffer[pos++] = '\n'; } p->columns_written = 0; } *end = pos;}
static void progress_refresh_locked(progress_t *p) { bool is_dumb = !p->supports_ansi && !p->is_windows_terminal; if (is_dumb && p->dont_print_on_dumb) return; if (!p->terminal) return; size_t end = 0; progress_clear_locked(p, &end); if (!p->done) { if (p->msg_buffer[0]) { int written = snprintf(p->buffer + end, sizeof(p->buffer) - end, " %s", p->msg_buffer); if (written > 0) { size_t amt = ( (size_t)written < sizeof(p->buffer) - end) ? (size_t)written : sizeof(p->buffer) - end - 1; end += amt; p->columns_written = amt; } } } if (end > 0) { fwrite(p->buffer, 1, end, p->terminal); fflush(p->terminal); } p->prev_refresh_ns = progress_now_ns();}
static inline void progress_stop(progress_t *p) { progress_mutex_lock(&p->mutex); p->done = true; size_t end = 0; progress_clear_locked(p, &end); if (end > 0 && p->terminal) { fwrite(p->buffer, 1, end, p->terminal); fflush(p->terminal); } progress_mutex_unlock(&p->mutex); progress_mutex_destroy(&p->mutex);}
#endif