diff --git a/examples/demo/greet.c b/examples/demo/greet.c new file mode 100644 index 0000000..7144ab2 --- /dev/null +++ b/examples/demo/greet.c @@ -0,0 +1,9 @@ +/* greet.c */ +#include +#include "greet.h" + +int calc(int a, int b) { return a * b + a - b; } + +void greet(const char *name) { + printf("Hello, %s! Welcome to PCC.\n", name); +} diff --git a/examples/demo/greet.h b/examples/demo/greet.h new file mode 100644 index 0000000..decd130 --- /dev/null +++ b/examples/demo/greet.h @@ -0,0 +1,6 @@ +/* greet.h */ +#ifndef GREET_H +#define GREET_H +int calc(int a, int b); +void greet(const char *name); +#endif diff --git a/examples/demo/main.c b/examples/demo/main.c new file mode 100644 index 0000000..3d2d259 --- /dev/null +++ b/examples/demo/main.c @@ -0,0 +1,9 @@ +/* main.c - demo project */ +#include +#include "greet.h" + +int main(void) { + printf("calc(5,3) = %d\n", calc(5, 3)); + greet("PCC user"); + return 0; +} diff --git a/examples/demo/pcmake.conf b/examples/demo/pcmake.conf new file mode 100644 index 0000000..c7d7d15 --- /dev/null +++ b/examples/demo/pcmake.conf @@ -0,0 +1,5 @@ +# pcmake.conf - demo project config +sources = main.c greet.c +include = . +output = demo +flags = -O2 diff --git a/libpcc.c b/libpcc.c index adb612e..3a3e7da 100644 --- a/libpcc.c +++ b/libpcc.c @@ -880,7 +880,7 @@ LIBPCCAPI PCCState *pcc_new(void) s->pcc_ext = 1; s->nocommon = 1; s->dollars_in_identifiers = 1; /*on by default like in gcc/clang*/ - s->cversion = 199901; /* default unless -std=c11 is supplied */ + s->cversion = 201112; /* default C11 (C17 is a defect-fix of C11) */ s->warn_implicit_function_declaration = 1; s->warn_discarded_qualifiers = 1; s->ms_extensions = 1; @@ -1992,8 +1992,18 @@ PUB_FUNC int pcc_parse_args(PCCState *s, int *pargc, char ***pargv) s->static_link = 1; break; case PCC_OPTION_std: - if (strcmp(optarg, "=c11") == 0 || strcmp(optarg, "=gnu11") == 0) + /* C17 is a defect-fix of C11, same __STDC_VERSION__ (201112L) */ + if (strcmp(optarg, "=c11") == 0 || strcmp(optarg, "=gnu11") == 0 + || strcmp(optarg, "=c17") == 0 || strcmp(optarg, "=gnu17") == 0 + || strcmp(optarg, "=c18") == 0 || strcmp(optarg, "=gnu18") == 0) s->cversion = 201112; + else if (strcmp(optarg, "=c99") == 0 || strcmp(optarg, "=gnu99") == 0) + s->cversion = 199901; + else if (strcmp(optarg, "=c89") == 0 || strcmp(optarg, "=c90") == 0 + || strcmp(optarg, "=gnu89") == 0 || strcmp(optarg, "=gnu90") == 0) + s->cversion = 199001; + else + pcc_warning("unsupported -std%s", optarg); break; case PCC_OPTION_shared: x = PCC_OUTPUT_DLL; diff --git a/tools/pcmake.bat b/tools/pcmake.bat new file mode 100644 index 0000000..3b9a7dd --- /dev/null +++ b/tools/pcmake.bat @@ -0,0 +1,24 @@ +@echo off +rem ============================================================ +rem pcmake.bat - build C projects with PCC +rem +rem Reads pcmake.conf in the current directory and builds +rem the project using the pcc compiler. +rem +rem Usage: +rem pcmake build (uses pcmake.conf in cwd) +rem pcmake clean remove build artifacts +rem ============================================================ +setlocal +set "TOOLSDIR=%~dp0" +for %%I in ("%TOOLSDIR%..") do set "ROOT=%%~fI" +set "WIN32=%ROOT%\win32" +set "PCCEXE=%WIN32%\pcc.exe" + +if not exist "%PCCEXE%" ( + echo pcmake.bat: cannot find pcc.exe at "%PCCEXE%" + exit /b 1 +) + +"%TOOLSDIR%pcmake.exe" -pcc "%PCCEXE%" -B "%WIN32%" %* +exit /b %errorlevel% diff --git a/tools/pcmake.c b/tools/pcmake.c new file mode 100644 index 0000000..dfd2d53 --- /dev/null +++ b/tools/pcmake.c @@ -0,0 +1,348 @@ +/* + * pcmake.c - a tiny build tool for PCC + * + * Reads a "pcmake.conf" file and builds a C project. + * Written in pure C, compiles with pcc itself. + * + * pcmake.conf format: + * sources = main.c util.c + * include = include + * libs = m + * output = myprog + * flags = -O2 -Wall + * + * Usage: + * pcmake # build using pcmake.conf in current dir + * pcmake -f file # use a different config file + * pcmake clean # remove build artifacts + * + * License: MIT + */ + +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +# include +# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\') +#else +# include +# define IS_DIRSEP(c) ((c) == '/') +#endif + +#define MAX_TOKENS 256 +#define MAX_LINE 1024 +#define MAX_STR 4096 + +typedef struct { + char *tokens[MAX_TOKENS]; + int count; +} TokenList; + +static char *g_sources[MAX_TOKENS]; +static char *g_include[MAX_TOKENS]; +static char *g_libs[MAX_TOKENS]; +static char g_output_buf[128] = "a"; +static char *g_output = g_output_buf; /* never free() this */ +static char *g_flags[MAX_TOKENS]; +static int n_sources, n_include, n_libs, n_flags; + +static void die(const char *msg) { + fprintf(stderr, "pcmake: %s\n", msg); + exit(1); +} + +static char *xstrdup(const char *s) { + char *p = (char*)malloc(strlen(s) + 1); + if (!p) die("out of memory"); + strcpy(p, s); + return p; +} + +/* trim whitespace */ +static char *trim(char *s) { + char *end; + while (isspace((unsigned char)*s)) s++; + if (*s == 0) return s; + end = s + strlen(s) - 1; + while (end > s && isspace((unsigned char)*end)) end--; + end[1] = 0; + return s; +} + +/* split a line into tokens on whitespace */ +static TokenList tokenize(const char *line) { + TokenList tl; + char buf[MAX_LINE]; + char *p, *tok; + tl.count = 0; + strncpy(buf, line, sizeof buf - 1); + buf[sizeof buf - 1] = 0; + p = buf; + while (*p) { + while (*p && isspace((unsigned char)*p)) p++; + if (!*p) break; + tok = p; + while (*p && !isspace((unsigned char)*p)) p++; + if (*p) *p++ = 0; + if (tl.count < MAX_TOKENS) + tl.tokens[tl.count++] = xstrdup(tok); + } + return tl; +} + +static void add_token(char **arr, int *n, const char *tok) { + if (*n < MAX_TOKENS) + arr[(*n)++] = xstrdup(tok); +} + +/* parse one config line: key = value tokens */ +static void parse_line(const char *line) { + char buf[MAX_LINE]; + char *eq, *key, *val; + TokenList tl; + int i; + + strncpy(buf, line, sizeof buf - 1); + buf[sizeof buf - 1] = 0; + key = trim(buf); + if (*key == 0 || *key == '#') /* empty or comment */ + return; + eq = strchr(key, '='); + if (!eq) + return; /* ignore lines without '=' */ + *eq = 0; + val = trim(eq + 1); + key = trim(key); + + tl = tokenize(val); + if (strcmp(key, "sources") == 0) { + for (i = 0; i < tl.count; i++) add_token(g_sources, &n_sources, tl.tokens[i]); + } else if (strcmp(key, "include") == 0) { + for (i = 0; i < tl.count; i++) add_token(g_include, &n_include, tl.tokens[i]); + } else if (strcmp(key, "libs") == 0) { + for (i = 0; i < tl.count; i++) add_token(g_libs, &n_libs, tl.tokens[i]); + } else if (strcmp(key, "output") == 0) { + if (tl.count > 0) { + strncpy(g_output_buf, tl.tokens[0], sizeof g_output_buf - 1); + g_output_buf[sizeof g_output_buf - 1] = 0; + g_output = g_output_buf; + } + } else if (strcmp(key, "flags") == 0) { + for (i = 0; i < tl.count; i++) add_token(g_flags, &n_flags, tl.tokens[i]); + } + /* unknown keys ignored */ + for (i = 0; i < tl.count; i++) free(tl.tokens[i]); +} + +static void read_config(const char *fname) { + FILE *f = fopen(fname, "r"); + char line[MAX_LINE]; + if (!f) die("cannot open config file (expected 'pcmake.conf')"); + while (fgets(line, sizeof line, f)) + parse_line(line); + fclose(f); +} + +/* compile one source file into an object file */ +static int run_command(const char *cmd); /* fwd decl */ + +static int compile_file(const char *pcc, const char *bopt, const char *src, const char *obj) { + char cmd[MAX_STR]; + int len = 0, i; + len += snprintf(cmd + len, sizeof cmd - len, "\"%s\" %s \"%s\" -c -o \"%s\"", + pcc, bopt, src, obj); + for (i = 0; i < n_include; i++) + len += snprintf(cmd + len, sizeof cmd - len, " -I \"%s\"", g_include[i]); + for (i = 0; i < n_flags; i++) + len += snprintf(cmd + len, sizeof cmd - len, " %s", g_flags[i]); + printf(" pcc -c %s\n", src); + return run_command(cmd); +} + +/* replace .c extension with .o */ +static void objname(const char *src, char *out, int outsize) { + const char *slash = NULL, *p; + const char *base; + const char *dot; + int n; + /* find last slash of either kind */ + for (p = src; *p; p++) { + if (*p == '/' || *p == '\\') + slash = p; + } + base = slash ? slash + 1 : src; + dot = strrchr(base, '.'); + n = (dot && dot > base) ? (int)(dot - base) : (int)strlen(base); + if (n >= outsize) n = outsize - 1; + memcpy(out, base, n); + out[n] = 0; + strncat(out, ".o", outsize - n - 1); +} + +static int file_exists(const char *f) { + FILE *p = fopen(f, "r"); + if (p) { fclose(p); return 1; } + return 0; +} + +/* run a command line; Windows uses CreateProcess to handle + quoted paths with spaces correctly (system() misbehaves) */ +static int run_command(const char *cmd) { +#ifdef _WIN32 + STARTUPINFOA si; + PROCESS_INFORMATION pi; + DWORD code = 1; + char *cmdline = (char*)malloc(strlen(cmd) + 1); + if (!cmdline) return -1; + strcpy(cmdline, cmd); + memset(&si, 0, sizeof si); + si.cb = sizeof si; + if (CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, &code); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } else { + fprintf(stderr, "pcmake: CreateProcess failed (%lu): %s\n", + (unsigned long)GetLastError(), cmd); + code = 1; + } + free(cmdline); + return (int)code; +#else + return system(cmd); +#endif +} + +int main(int argc, char **argv) { + const char *cfg = "pcmake.conf"; + const char *pcc = "pcc"; + char bopt[600] = ""; /* "-B " or empty when pcc.bat used */ + char objbuf[512]; + char cmd[MAX_STR]; + int i, len, rc; + int is_clean = 0; + int any_failed = 0; + + /* parse args */ + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) { + cfg = argv[++i]; + } else if (strcmp(argv[i], "-pcc") == 0 && i + 1 < argc) { + pcc = argv[++i]; + } else if (strcmp(argv[i], "-B") == 0 && i + 1 < argc) { + snprintf(bopt, sizeof bopt, "-B \"%s\"", argv[++i]); + } else if (strcmp(argv[i], "clean") == 0) { + is_clean = 1; + } + } + + read_config(cfg); + + /* locate pcc: + 1. if -pcc was given, use it + 2. else if pcc.bat wrapper exists in cwd, use it (it sets -B) + 3. else search common install locations for pcc.exe */ + if (strcmp(pcc, "pcc") == 0) { + const char *cands[] = { + "pcc.bat", + "pcc.exe", + "C:\\pcc\\pcc.exe", + "C:\\Program Files\\pcc\\pcc.exe", + "C:\\Program Files (x86)\\pcc\\pcc.exe", + NULL + }; + int k; + for (k = 0; cands[k]; k++) { + if (file_exists(cands[k])) { + pcc = cands[k]; + break; + } + } + } + + if (is_clean) { + /* remove object files and output */ + for (i = 0; i < n_sources; i++) { + objname(g_sources[i], objbuf, sizeof objbuf); + printf(" rm %s\n", objbuf); + remove(objbuf); + } + { + char outbuf[512]; + strncpy(outbuf, g_output, sizeof outbuf - 1); + outbuf[sizeof outbuf - 1] = 0; +#ifdef _WIN32 + strncat(outbuf, ".exe", sizeof outbuf - strlen(outbuf) - 1); +#endif + printf(" rm %s\n", outbuf); + remove(outbuf); + } + printf("pcmake: cleaned\n"); + return 0; + } + + if (n_sources == 0) { + fprintf(stderr, "pcmake: no sources specified in %s\n", cfg); + return 1; + } + + printf("pcmake: building %s (%d source files)\n", g_output, n_sources); + + /* compile each source */ + for (i = 0; i < n_sources; i++) { + objname(g_sources[i], objbuf, sizeof objbuf); + rc = compile_file(pcc, bopt, g_sources[i], objbuf); + if (rc != 0) { + fprintf(stderr, "pcmake: compile failed: %s\n", g_sources[i]); + any_failed = 1; + } + } + if (any_failed) { + fprintf(stderr, "pcmake: build failed\n"); + return 1; + } + + /* link */ + len = 0; + len += snprintf(cmd + len, sizeof cmd - len, "\"%s\" %s", pcc, bopt); + for (i = 0; i < n_include; i++) + len += snprintf(cmd + len, sizeof cmd - len, " -I \"%s\"", g_include[i]); + for (i = 0; i < n_flags; i++) + len += snprintf(cmd + len, sizeof cmd - len, " %s", g_flags[i]); + for (i = 0; i < n_sources; i++) { + objname(g_sources[i], objbuf, sizeof objbuf); + len += snprintf(cmd + len, sizeof cmd - len, " \"%s\"", objbuf); + } + for (i = 0; i < n_libs; i++) + len += snprintf(cmd + len, sizeof cmd - len, " -l%s", g_libs[i]); + { + char outbuf[512]; + strncpy(outbuf, g_output, sizeof outbuf - 1); + outbuf[sizeof outbuf - 1] = 0; +#ifdef _WIN32 + strncat(outbuf, ".exe", sizeof outbuf - strlen(outbuf) - 1); +#endif + len += snprintf(cmd + len, sizeof cmd - len, " -o \"%s\"", outbuf); + } + printf(" link %s\n", g_output); + rc = run_command(cmd); + if (rc != 0) { + fprintf(stderr, "pcmake: link failed\n"); + return 1; + } + + printf("pcmake: build OK -> %s%s\n", g_output, +#ifdef _WIN32 + ".exe" +#else + "" +#endif + ); + return 0; +} diff --git a/win32/include/pthread.h b/win32/include/pthread.h new file mode 100644 index 0000000..5f65815 --- /dev/null +++ b/win32/include/pthread.h @@ -0,0 +1,307 @@ +/* + * pthread.h - POSIX threads for Windows (PCC) + * + * A lightweight pthread implementation on top of Win32 + * thread APIs. Provides the commonly used subset of POSIX + * threads sufficient for most C programs. + * + * Condition variables are implemented with Win32 events, + * so no modern kernel32 APIs are required. + * + * License: MIT + */ + +#ifndef _PCC_PTHREAD_H +#define _PCC_PTHREAD_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---------- types ---------- */ + +typedef HANDLE pthread_t; +typedef DWORD pthread_attr_t; + +typedef struct { + CRITICAL_SECTION cs; +} pthread_mutex_t; + +typedef struct { + int dummy; +} pthread_mutexattr_t; + +typedef struct { + HANDLE event; /* auto-reset event used for signaling */ + CRITICAL_SECTION cs; /* protects waiters count */ + int waiters; +} pthread_cond_t; + +typedef struct { + int dummy; +} pthread_condattr_t; + +typedef struct { + int done; +} pthread_once_control_t; + +#define PTHREAD_ONCE_INIT { 0 } +/* Zero-init is valid for our event-based cond; for mutex we + lazily initialize the CRITICAL_SECTION on first use. */ +#define PTHREAD_MUTEX_INITIALIZER { { 0 } } +#define PTHREAD_COND_INITIALIZER { NULL, { 0 }, 0 } + +typedef DWORD pthread_key_t; + +/* ---------- thread creation ---------- */ + +static DWORD WINAPI pthread_wrapper(LPVOID arg) +{ + void *(*fn)(void *) = ((void **)arg)[0]; + void *data = ((void **)arg)[1]; + void *result; + free(arg); + result = fn(data); + /* return value stored in thread exit code */ + return (DWORD)(uintptr_t)result; +} + +static int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) +{ + void **box = (void **)malloc(2 * sizeof(void *)); + DWORD tid; + HANDLE h; + (void)attr; + if (!box) return -1; + box[0] = (void *)start_routine; + box[1] = arg; + h = CreateThread(NULL, 0, pthread_wrapper, box, 0, &tid); + if (!h) { + free(box); + return -1; + } + *thread = h; + return 0; +} + +static int pthread_join(pthread_t thread, void **retval) +{ + DWORD code = 0; + if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) + return -1; + GetExitCodeThread(thread, &code); + CloseHandle(thread); + if (retval) + *retval = (void *)(uintptr_t)code; + return 0; +} + +static pthread_t pthread_self(void) +{ + return GetCurrentThread(); +} + +static int pthread_equal(pthread_t t1, pthread_t t2) +{ + return t1 == t2; +} + +static void pthread_exit(void *retval) +{ + ExitThread((DWORD)(uintptr_t)retval); +} + +static int pthread_detach(pthread_t thread) +{ + CloseHandle(thread); + return 0; +} + +/* ---------- mutex (with lazy init for static initializers) ---------- */ + +static void pthread_mutex_ensure(pthread_mutex_t *m) +{ + /* CRITICAL_SECTION is initialized when DebugInfo != NULL */ + if (m->cs.DebugInfo == NULL) + InitializeCriticalSection(&m->cs); +} + +static int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a) +{ + (void)a; + memset(&m->cs, 0, sizeof m->cs); + InitializeCriticalSection(&m->cs); + return 0; +} + +static int pthread_mutex_destroy(pthread_mutex_t *m) +{ + if (m->cs.DebugInfo) + DeleteCriticalSection(&m->cs); + memset(&m->cs, 0, sizeof m->cs); + return 0; +} + +static int pthread_mutex_lock(pthread_mutex_t *m) +{ + pthread_mutex_ensure(m); + EnterCriticalSection(&m->cs); + return 0; +} + +static int pthread_mutex_trylock(pthread_mutex_t *m) +{ + pthread_mutex_ensure(m); + return TryEnterCriticalSection(&m->cs) ? 0 : 1; +} + +static int pthread_mutex_unlock(pthread_mutex_t *m) +{ + LeaveCriticalSection(&m->cs); + return 0; +} + +/* ---------- condition variable (event-based) ---------- */ + +static int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a) +{ + (void)a; + c->event = CreateEventA(NULL, FALSE, FALSE, NULL); /* auto-reset */ + if (!c->event) return -1; + InitializeCriticalSection(&c->cs); + c->waiters = 0; + return 0; +} + +static int pthread_cond_destroy(pthread_cond_t *c) +{ + if (c->event) CloseHandle(c->event); + DeleteCriticalSection(&c->cs); + return 0; +} + +static int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) +{ + EnterCriticalSection(&c->cs); + c->waiters++; + LeaveCriticalSection(&c->cs); + + LeaveCriticalSection(&m->cs); + /* wait for signal; spurious wakeups possible, caller must re-check */ + WaitForSingleObject(c->event, INFINITE); + EnterCriticalSection(&m->cs); + + EnterCriticalSection(&c->cs); + if (c->waiters > 0) c->waiters--; + LeaveCriticalSection(&c->cs); + return 0; +} + +static int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, + const struct timespec *abstime) +{ + DWORD ms = INFINITE; + if (abstime) { + FILETIME ft; + ULARGE_INTEGER ul; + unsigned __int64 target, now; + GetSystemTimeAsFileTime(&ft); + ul.LowPart = ft.dwLowDateTime; + ul.HighPart = ft.dwHighDateTime; + now = ul.QuadPart; /* 100ns since 1601 */ + target = ((unsigned __int64)abstime->tv_sec + 11644473600ULL) + * 10000000ULL + abstime->tv_nsec / 100; + if (target <= now) + return 110; /* ETIMEDOUT */ + ms = (DWORD)((target - now) / 10000); + } + + EnterCriticalSection(&c->cs); + c->waiters++; + LeaveCriticalSection(&c->cs); + + LeaveCriticalSection(&m->cs); + { + DWORD r = WaitForSingleObject(c->event, ms); + EnterCriticalSection(&m->cs); + EnterCriticalSection(&c->cs); + if (c->waiters > 0) c->waiters--; + LeaveCriticalSection(&c->cs); + if (r == WAIT_TIMEOUT) return 110; + } + return 0; +} + +static int pthread_cond_signal(pthread_cond_t *c) +{ + EnterCriticalSection(&c->cs); + if (c->waiters > 0) + SetEvent(c->event); + LeaveCriticalSection(&c->cs); + return 0; +} + +static int pthread_cond_broadcast(pthread_cond_t *c) +{ + int n; + EnterCriticalSection(&c->cs); + n = c->waiters; + LeaveCriticalSection(&c->cs); + /* set event multiple times; auto-reset wakes one waiter per set */ + while (n-- > 0) + SetEvent(c->event); + return 0; +} + +/* ---------- once ---------- */ + +static int pthread_once(pthread_once_control_t *once, + void (*init_routine)(void)) +{ + if (!once->done) { + once->done = 1; + init_routine(); + } + return 0; +} + +/* ---------- keys (TLS) ---------- */ + +static int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) +{ + (void)destructor; + *key = TlsAlloc(); + return (*key == TLS_OUT_OF_INDEXES) ? -1 : 0; +} + +static int pthread_key_delete(pthread_key_t key) +{ + return TlsFree(key) ? 0 : -1; +} + +static void *pthread_getspecific(pthread_key_t key) +{ + return TlsGetValue(key); +} + +static int pthread_setspecific(pthread_key_t key, const void *value) +{ + return TlsSetValue(key, (LPVOID)value) ? 0 : -1; +} + +/* ---------- attributes (minimal) ---------- */ + +static int pthread_attr_init(pthread_attr_t *a) { (void)a; return 0; } +static int pthread_attr_destroy(pthread_attr_t *a) { (void)a; return 0; } + +#ifdef __cplusplus +} +#endif + +#endif /* _PCC_PTHREAD_H */ diff --git a/win32/include/threads.h b/win32/include/threads.h new file mode 100644 index 0000000..9f873b8 --- /dev/null +++ b/win32/include/threads.h @@ -0,0 +1,150 @@ +/* + * threads.h - C11 threads for Windows (PCC) + * + * Implements the C11 interface on top of the + * Win32 thread APIs. C11 threads are a thin wrapper around + * threads, mutexes, condition variables and TLS. + * + * License: MIT + */ + +#ifndef _PCC_THREADS_H +#define _PCC_THREADS_H + +#include +#include "pthread.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---------- types ---------- */ + +typedef pthread_t thrd_t; +typedef pthread_mutex_t mtx_t; +typedef pthread_cond_t cnd_t; +typedef pthread_key_t tss_t; +typedef int (*thrd_start_t)(void *); +typedef void (*tss_dtor_t)(void *); + +typedef struct { + int type; /* unused */ +} mtx_t_attr; + +/* thread start wrapper: convert int(*)(void*) to void*(*)(void*) */ +static void *thrd_wrapper(void *arg) +{ + thrd_start_t fn = (thrd_start_t)((void **)arg)[0]; + void *data = ((void **)arg)[1]; + int result = fn(data); + free(arg); + return (void *)(intptr_t)result; +} + +/* ---------- constants ---------- */ + +enum { + thrd_success = 0, + thrd_nomem = 1, + thrd_timedout = 2, + thrd_busy = 3, + thrd_error = 4 +}; + +enum { + mtx_plain = 0, + mtx_timed = 1, + mtx_recursive = 2 +}; + +#define TSS_DTOR_ITERATIONS 4 + +/* ---------- thread functions ---------- */ + +static int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + void **box = (void **)malloc(2 * sizeof(void *)); + if (!box) return thrd_nomem; + box[0] = (void *)func; + box[1] = arg; + if (pthread_create(thr, NULL, thrd_wrapper, box) != 0) { + free(box); + return thrd_error; + } + return thrd_success; +} + +static int thrd_equal(thrd_t a, thrd_t b) { return pthread_equal(a, b); } +static thrd_t thrd_current(void) { return pthread_self(); } +static int thrd_sleep(const struct timespec *duration, struct timespec *remaining) +{ + DWORD ms = (DWORD)(duration->tv_sec * 1000 + duration->tv_nsec / 1000000); + (void)remaining; + Sleep(ms); + return 0; +} +static void thrd_yield(void) { Sleep(0); } +static void thrd_exit(int res) { pthread_exit((void *)(intptr_t)res); } +static int thrd_detach(thrd_t t) { return pthread_detach(t) ? thrd_error : thrd_success; } + +static int thrd_join(thrd_t t, int *res) +{ + void *ret; + if (pthread_join(t, &ret) != 0) return thrd_error; + if (res) *res = (int)(intptr_t)ret; + return thrd_success; +} + +/* ---------- mutex functions ---------- */ + +static int mtx_init(mtx_t *mtx, int type) +{ + (void)type; + return pthread_mutex_init(mtx, NULL) ? thrd_error : thrd_success; +} +static void mtx_destroy(mtx_t *mtx) { pthread_mutex_destroy(mtx); } +static int mtx_lock(mtx_t *mtx) { return pthread_mutex_lock(mtx) ? thrd_error : thrd_success; } +static int mtx_trylock(mtx_t *mtx) { return pthread_mutex_trylock(mtx) ? thrd_busy : thrd_success; } +static int mtx_unlock(mtx_t *mtx) { return pthread_mutex_unlock(mtx) ? thrd_error : thrd_success; } +static int mtx_timedlock(mtx_t *mtx, const struct timespec *at) +{ + (void)mtx; (void)at; + return mtx_lock(mtx); +} + +/* ---------- condition variable functions ---------- */ + +static int cnd_init(cnd_t *cond) { return pthread_cond_init(cond, NULL) ? thrd_error : thrd_success; } +static void cnd_destroy(cnd_t *cond) { pthread_cond_destroy(cond); } +static int cnd_signal(cnd_t *cond) { return pthread_cond_signal(cond) ? thrd_error : thrd_success; } +static int cnd_broadcast(cnd_t *cond) { return pthread_cond_broadcast(cond) ? thrd_error : thrd_success; } +static int cnd_wait(cnd_t *cond, mtx_t *mtx) { return pthread_cond_wait(cond, mtx) ? thrd_error : thrd_success; } +static int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *at) +{ + int r = pthread_cond_timedwait(cond, mtx, at); + if (r == 110) return thrd_timedout; + return r ? thrd_error : thrd_success; +} + +/* ---------- TSS (thread-specific storage) ---------- */ + +static int tss_create(tss_t *key, tss_dtor_t dtor) +{ + (void)dtor; + return pthread_key_create(key, NULL) ? thrd_error : thrd_success; +} +static void tss_delete(tss_t key) { pthread_key_delete(key); } +static void *tss_get(tss_t key) { return pthread_getspecific(key); } +static int tss_set(tss_t key, void *val) { return pthread_setspecific(key, val) ? thrd_error : thrd_success; } + +/* ---------- call_once ---------- */ + +typedef pthread_once_control_t once_flag; +#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT +static void call_once(once_flag *flag, void (*func)(void)) { pthread_once(flag, func); } + +#ifdef __cplusplus +} +#endif + +#endif /* _PCC_THREADS_H */ diff --git a/win32/pcc-run.cmd b/win32/pcc-run.cmd new file mode 100644 index 0000000..c68346d --- /dev/null +++ b/win32/pcc-run.cmd @@ -0,0 +1,21 @@ +@echo off +rem ============================================================ +rem pcc-run.cmd - run a C source file directly (script mode) +rem +rem Usage: +rem pcc-run hello.c [args...] +rem pcc-run myscript [args...] (no extension works too) +rem +rem This compiles the C file in memory and runs it immediately, +rem just like a script. No .exe file is produced. +rem ============================================================ +setlocal +set "PCCDIR=%~dp0" + +if "%~1"=="" ( + echo Usage: pcc-run script.c [arguments...] + exit /b 1 +) + +"%PCCDIR%pcc.exe" -B "%PCCDIR%win32" -I "%PCCDIR%include" -L "%PCCDIR%lib" -run %* +exit /b %errorlevel%