#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#define LIB_HANDLE HMODULE
#define LIB_LOAD(name) LoadLibraryA(name)
#define LIB_SYM(handle,n) GetProcAddress(handle, n)
#define LIB_CLOSE(handle) FreeLibrary(handle)
#define LIB_ERROR() win32_dlerror()
static const char *win32_dlerror(void)
{
static char buf[256];
DWORD err = GetLastError();
if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0,
buf, sizeof(buf), NULL))
return buf;
snprintf(buf, sizeof(buf), "error code %lu", (unsigned long)err);
return buf;
}
#else
#include <dlfcn.h>
#define LIB_HANDLE void*
#define LIB_LOAD(name) dlopen(name, RTLD_LAZY)
#define LIB_SYM(handle,n) dlsym(handle, n)
#define LIB_CLOSE(handle) dlclose(handle)
#define LIB_ERROR() dlerror()
#endif
typedef struct thaiid_card_data thaiid_card_data;
typedef thaiid_card_data *(*read_fn)(const char *reader, int face, int nhso, int laser);
typedef void (*free_fn)(thaiid_card_data *data);
typedef const char *(*get_last_error_fn)(void);
typedef const char *(*get_str_fn)(const thaiid_card_data *data);
static read_fn fn_read = NULL;
static free_fn fn_free = NULL;
static get_last_error_fn fn_get_last_error = NULL;
static get_str_fn fn_get_cid = NULL;
static get_str_fn fn_get_name_thai = NULL;
static get_str_fn fn_get_name_en = NULL;
static get_str_fn fn_get_dob = NULL;
static get_str_fn fn_get_gender = NULL;
static get_str_fn fn_get_card_issuer = NULL;
static get_str_fn fn_get_issue_date = NULL;
static get_str_fn fn_get_expire_date = NULL;
static get_str_fn fn_get_address = NULL;
static get_str_fn fn_get_face_image = NULL;
static get_str_fn fn_get_laser_id = NULL;
static get_str_fn fn_get_main_inscl = NULL;
static get_str_fn fn_get_sub_inscl = NULL;
static get_str_fn fn_get_main_hospital = NULL;
static get_str_fn fn_get_sub_hospital = NULL;
static get_str_fn fn_get_paid_type = NULL;
static get_str_fn fn_get_nhso_issue_date = NULL;
static get_str_fn fn_get_nhso_expire_date = NULL;
static get_str_fn fn_get_nhso_update_date = NULL;
static get_str_fn fn_get_change_hospital_amount = NULL;
static LIB_HANDLE g_lib = NULL;
static void *resolve(const char *sym_name)
{
void *p = LIB_SYM(g_lib, sym_name);
if (!p)
{
fprintf(stderr, "Missing symbol: %s (%s)\n", sym_name, LIB_ERROR());
}
return p;
}
static int load_library(const char *path)
{
g_lib = LIB_LOAD(path);
if (!g_lib)
{
fprintf(stderr, "Failed to load %s: %s\n", path, LIB_ERROR());
return -1;
}
#define RESOLVE(var, name) \
do \
{ \
*(void **)(&var) = resolve(name); \
if (!var) \
{ \
LIB_CLOSE(g_lib); \
g_lib = NULL; \
return -1; \
} \
} while (0)
RESOLVE(fn_read, "thaiid_read");
RESOLVE(fn_free, "thaiid_free");
RESOLVE(fn_get_last_error, "thaiid_get_last_error");
RESOLVE(fn_get_cid, "thaiid_get_cid");
RESOLVE(fn_get_name_thai, "thaiid_get_name_thai");
RESOLVE(fn_get_name_en, "thaiid_get_name_en");
RESOLVE(fn_get_dob, "thaiid_get_dob");
RESOLVE(fn_get_gender, "thaiid_get_gender");
RESOLVE(fn_get_card_issuer, "thaiid_get_card_issuer");
RESOLVE(fn_get_issue_date, "thaiid_get_issue_date");
RESOLVE(fn_get_expire_date, "thaiid_get_expire_date");
RESOLVE(fn_get_address, "thaiid_get_address");
RESOLVE(fn_get_face_image, "thaiid_get_face_image");
RESOLVE(fn_get_laser_id, "thaiid_get_laser_id");
RESOLVE(fn_get_main_inscl, "thaiid_get_main_inscl");
RESOLVE(fn_get_sub_inscl, "thaiid_get_sub_inscl");
RESOLVE(fn_get_main_hospital, "thaiid_get_main_hospital");
RESOLVE(fn_get_sub_hospital, "thaiid_get_sub_hospital");
RESOLVE(fn_get_paid_type, "thaiid_get_paid_type");
RESOLVE(fn_get_nhso_issue_date, "thaiid_get_nhso_issue_date");
RESOLVE(fn_get_nhso_expire_date, "thaiid_get_nhso_expire_date");
RESOLVE(fn_get_nhso_update_date, "thaiid_get_nhso_update_date");
RESOLVE(fn_get_change_hospital_amount, "thaiid_get_change_hospital_amount");
#undef RESOLVE
return 0;
}
static void unload_library(void)
{
if (g_lib)
LIB_CLOSE(g_lib);
}
static const char *library_filename(void)
{
#if defined(_WIN32)
return "thaiidcard.dll";
#elif defined(__APPLE__)
return "libthaiidcard.dylib";
#else
return "libthaiidcard.so";
#endif
}
static const char *find_library(void)
{
static char buf[1024];
const char *name = library_filename();
const char *relative[] = {
"target/debug/",
"target/release/",
NULL
};
for (int i = 0; relative[i]; i++)
{
snprintf(buf, sizeof(buf), "%s%s", relative[i], name);
FILE *f = fopen(buf, "r");
if (f) { fclose(f); return buf; }
}
#ifdef __APPLE__
const char *syspaths[] = {
"/usr/local/lib/",
"/opt/homebrew/lib/",
NULL
};
#elif defined(_WIN32)
(void)0;
#else
const char *syspaths[] = {
"/usr/local/lib/",
"/usr/lib/",
"/usr/lib/x86_64-linux-gnu/",
"/usr/lib/aarch64-linux-gnu/",
NULL
};
#endif
#ifndef _WIN32
for (int i = 0; syspaths[i]; i++)
{
snprintf(buf, sizeof(buf), "%s%s", syspaths[i], name);
FILE *f = fopen(buf, "r");
if (f) { fclose(f); return buf; }
}
#endif
return NULL;
}
static void print_card_data(const thaiid_card_data *data)
{
#define FIELD(label, getter) \
do \
{ \
const char *val = getter(data); \
printf(" %-20s %s\n", label, val ? val : "(null)"); \
} while (0)
printf("\n=== Personal Information ===\n");
FIELD("CID:", fn_get_cid);
FIELD("Name (TH):", fn_get_name_thai);
FIELD("Name (EN):", fn_get_name_en);
FIELD("DOB:", fn_get_dob);
FIELD("Gender:", fn_get_gender);
FIELD("Card Issuer:", fn_get_card_issuer);
FIELD("Issue Date:", fn_get_issue_date);
FIELD("Expire Date:", fn_get_expire_date);
FIELD("Address:", fn_get_address);
const char *face = fn_get_face_image(data);
if (face && strlen(face) > 0)
{
printf(" %-20s [%zu bytes base64]\n", "Face Image:", strlen(face));
}
const char *laser = fn_get_laser_id(data);
if (laser && strlen(laser) > 0)
{
printf("\n=== Card Info ===\n");
FIELD("Laser ID:", fn_get_laser_id);
}
const char *inscl = fn_get_main_inscl(data);
if (inscl && strlen(inscl) > 0)
{
printf("\n=== NHSO Information ===\n");
FIELD("Main Inscl:", fn_get_main_inscl);
FIELD("Sub Inscl:", fn_get_sub_inscl);
FIELD("Main Hosp:", fn_get_main_hospital);
FIELD("Sub Hosp:", fn_get_sub_hospital);
FIELD("Paid Type:", fn_get_paid_type);
FIELD("NHSO Issue:", fn_get_nhso_issue_date);
FIELD("NHSO Expire:", fn_get_nhso_expire_date);
FIELD("NHSO Update:", fn_get_nhso_update_date);
FIELD("Change Hosp:", fn_get_change_hospital_amount);
}
#undef FIELD
}
int main(int argc, char **argv)
{
const char *libpath = NULL;
const char *reader = NULL;
if (argc > 1)
libpath = argv[1];
if (argc > 2)
reader = argv[2];
if (!libpath)
{
libpath = find_library();
if (!libpath)
{
fprintf(stderr, "libthaiidcard not found. "
"Pass the path as the first argument, or build with: make shared\n");
return 1;
}
}
if (load_library(libpath) != 0)
{
return 1;
}
atexit(unload_library);
printf("Using library: %s\n", libpath);
printf("Reader: %s\n", reader ? reader : "(auto-detect)");
printf("Waiting for card...\n");
thaiid_card_data *data = fn_read(reader, 1, 1, 0);
if (data)
{
print_card_data(data);
fn_free(data);
printf("\nDone.\n");
}
else
{
const char *err = fn_get_last_error();
fprintf(stderr, "Error: %s\n", err ? err : "unknown error");
return 1;
}
return 0;
}