Chandra
view release on metacpan or search on metacpan
include/chandra/chandra_store.h view on Meta::CPAN
#ifdef CHANDRA_XS_IMPLEMENTATION
/* ============================================================================
* JSON singleton
* ============================================================================ */
static SV *_store_json_obj = NULL;
static SV *
chandra_store_get_json(pTHX)
{
if (!_store_json_obj || !SvOK(_store_json_obj)) {
_store_json_obj = eval_pv(
"require Cpanel::JSON::XS;"
"Cpanel::JSON::XS->new->utf8->pretty(1)->canonical(1)->allow_nonref",
TRUE
);
SvREFCNT_inc_simple_void(_store_json_obj);
}
return _store_json_obj;
}
static SV *
chandra_store_encode(pTHX_ SV *val)
{
dSP;
SV *json = chandra_store_get_json(aTHX);
int count;
SV *result;
ENTER; SAVETMPS;
PUSHMARK(SP);
XPUSHs(json);
XPUSHs(val);
PUTBACK;
count = call_method("encode", G_SCALAR | G_EVAL);
SPAGAIN;
result = (count > 0 && !SvTRUE(ERRSV))
? newSVsv(POPs) : newSVpvs("{}");
if (SvTRUE(ERRSV)) sv_setpvs(ERRSV, "");
PUTBACK; FREETMPS; LEAVE;
return result;
}
/* Returns new HV* on success, NULL on corrupt (emits warn) */
static HV *
chandra_store_decode(pTHX_ SV *json_sv, const char *path)
{
dSP;
SV *json = chandra_store_get_json(aTHX);
int count;
HV *result = NULL;
ENTER; SAVETMPS;
PUSHMARK(SP);
XPUSHs(json);
XPUSHs(json_sv);
PUTBACK;
count = call_method("decode", G_SCALAR | G_EVAL);
SPAGAIN;
if (count > 0 && !SvTRUE(ERRSV)) {
SV *decoded = POPs;
if (SvOK(decoded) && SvROK(decoded)
&& SvTYPE(SvRV(decoded)) == SVt_PVHV) {
result = (HV *)SvRV(decoded);
SvREFCNT_inc_simple_void((SV *)result);
}
}
if (SvTRUE(ERRSV)) sv_setpvs(ERRSV, "");
PUTBACK; FREETMPS; LEAVE;
if (!result)
warn("Chandra::Store: corrupt store '%s', starting fresh\n",
path ? path : "unknown");
return result;
}
/* ============================================================================
* mkdir -p in C
* ============================================================================ */
static int
chandra_store_mkdirp(const char *path, int mode)
{
char tmp[4096];
char *p;
size_t len;
#ifdef _WIN32
/* Use Win32 API to avoid Perl's stat/mkdir macro redefinitions */
DWORD attr = GetFileAttributesA(path);
if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
return 0;
#else
{
struct stat st;
if (stat(path, &st) == 0) return 0;
}
#endif
strncpy(tmp, path, sizeof(tmp) - 1);
tmp[sizeof(tmp) - 1] = '\0';
len = strlen(tmp);
if (len > 0 && tmp[len - 1] == '/') tmp[len - 1] = '\0';
#ifdef _WIN32
/* Also handle backslash separators */
if (len > 0 && tmp[len - 1] == '\\') tmp[len - 1] = '\0';
#endif
for (p = tmp + 1; *p; p++) {
#ifdef _WIN32
if (*p == '/' || *p == '\\') {
#else
if (*p == '/') {
#endif
*p = '\0';
#ifdef _WIN32
{
DWORD a = GetFileAttributesA(tmp);
if (a == INVALID_FILE_ATTRIBUTES || !(a & FILE_ATTRIBUTE_DIRECTORY))
if (!CreateDirectoryA(tmp, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
return -1;
}
#else
{
struct stat st;
include/chandra/chandra_store.h view on Meta::CPAN
fclose(fh);
SvREFCNT_dec(json_sv);
#ifdef _WIN32
if (!MoveFileExA(tmp_path, path, MOVEFILE_REPLACE_EXISTING))
croak("Chandra::Store: cannot rename '%s' to '%s': error %lu\n",
tmp_path, path, (unsigned long)GetLastError());
#else
if (rename(tmp_path, path) != 0)
croak("Chandra::Store: cannot rename '%s' to '%s': %s\n",
tmp_path, path, strerror(errno));
#endif
}
static void
chandra_store_reload_c(pTHX_ HV *self_hv)
{
SV **path_svp = hv_fetchs(self_hv, "_path", 0);
const char *path;
FILE *fh;
int fd;
long size;
char *buf;
SV *json_sv;
HV *new_data;
if (!path_svp || !SvOK(*path_svp)) return;
path = SvPV_nolen(*path_svp);
fh = fopen(path, "rb");
if (!fh) return; /* File doesn't exist yet - start fresh */
fd = fileno(fh);
flock(fd, LOCK_SH);
fseek(fh, 0, SEEK_END);
size = ftell(fh);
fseek(fh, 0, SEEK_SET);
if (size <= 0) {
flock(fd, LOCK_UN);
fclose(fh);
return;
}
Newx(buf, size + 1, char);
fread(buf, 1, (size_t)size, fh);
buf[size] = '\0';
flock(fd, LOCK_UN);
fclose(fh);
json_sv = sv_2mortal(newSVpvn(buf, (STRLEN)size));
Safefree(buf);
new_data = chandra_store_decode(aTHX_ json_sv, path);
if (new_data) {
SV **old_svp = hv_fetchs(self_hv, "_data", 0);
if (old_svp && SvROK(*old_svp)) {
/* Replace _data with freshly decoded HV */
(void)hv_stores(self_hv, "_data", newRV_noinc((SV *)new_data));
} else {
(void)hv_stores(self_hv, "_data", newRV_noinc((SV *)new_data));
}
}
/* If new_data is NULL, we leave _data as-is (start fresh, already set in new) */
}
/* ============================================================================
* Constructor helper - build default path from name
* ============================================================================ */
static SV *
chandra_store_default_path(pTHX_ const char *name)
{
const char *home = getenv("HOME");
SV *path_sv;
#ifdef _WIN32
if (!home || !*home) {
home = getenv("USERPROFILE");
}
if (!home || !*home) {
home = getenv("APPDATA");
}
if (!home || !*home) {
home = "C:\\\\";
}
#else
if (!home || !*home) {
/* Fall back to getpwuid */
struct passwd *pw = getpwuid(getuid());
home = pw ? pw->pw_dir : "/tmp";
}
#endif
path_sv = newSVpvf("%s/.chandra/%s/store.json", home, name);
return path_sv;
}
#endif /* CHANDRA_XS_IMPLEMENTATION */
#endif /* CHANDRA_STORE_H */
( run in 0.626 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )