This source file includes following definitions.
- potion_load_code
- potion_initializer_name
- potion_load_dylib
- find_extension
- potion_find_file
- potion_load
- potion_loader_add
- potion_loader_init
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include "potion.h"
#include "internal.h"
#include "table.h"
static PN potion_load_code(Potion *P, const char *filename) {
PN buf, code;
struct stat stats;
int fd = -1;
PN result = PN_NIL;
if (stat(filename, &stats) == -1) {
fprintf(stderr, "** %s does not exist.", filename);
return PN_NIL;
}
fd = open(filename, O_RDONLY | O_BINARY);
if (fd == -1) {
fprintf(stderr, "** could not open %s. check permissions.", filename);
return PN_NIL;
}
buf = potion_bytes(P, stats.st_size);
if (read(fd, PN_STR_PTR(buf), stats.st_size) == stats.st_size) {
PN_STR_PTR(buf)[stats.st_size] = '\0';
code = potion_source_load(P, PN_NIL, buf);
if (!PN_IS_PROTO(code)) {
result = potion_run(P, potion_send(
potion_parse(P, buf, (char *)filename),
PN_compile, potion_str(P, filename), PN_NIL),
POTION_JIT);
}
} else {
fprintf(stderr, "** could not read entire file: %s.", filename);
}
close(fd);
return result;
}
static char *potion_initializer_name(Potion *P, const char *filename, PN_SIZE len) {
PN_SIZE ext_name_len = 0;
char *allocated_str, *ext_name, *func_name;
while (*(filename + ++ext_name_len) != '.' && ext_name_len <= len);
allocated_str = ext_name = malloc(ext_name_len + 1);
if (allocated_str == NULL) potion_allocation_error();
strncpy(ext_name, filename, ext_name_len);
ext_name[ext_name_len] = '\0';
ext_name += ext_name_len;
while (*--ext_name != '/' && ext_name >= allocated_str);
ext_name++;
if (asprintf(&func_name, "Potion_Init_%s", ext_name) == -1)
potion_allocation_error();
free(allocated_str);
return func_name;
}
static PN potion_load_dylib(Potion *P, const char *filename) {
void *handle = dlopen(filename, RTLD_LAZY);
void (*func)(Potion *);
char *err, *init_func_name;
if (handle == NULL) {
fprintf(stderr, "** error loading %s: %s\n", filename, dlerror());
return PN_NIL;
}
init_func_name = potion_initializer_name(P, filename, strlen(filename));
func = (void (*)(Potion *))dlsym(handle, init_func_name);
err = (char *)dlerror();
free(init_func_name);
if (err != NULL) {
fprintf(stderr, "** error loading %s in %s: %s\n", init_func_name, filename, err);
dlclose(handle);
return PN_NIL;
}
func(P);
return PN_TRUE;
}
static PN pn_loader_path;
static const char *pn_loader_extensions[] = {
".pnb"
, ".pn"
, POTION_LOADEXT
};
static const char *find_extension(char *str) {
int i;
PN_SIZE str_len = strlen(str);
struct stat st;
for (i = 0;
i < sizeof(pn_loader_extensions) / sizeof(void *);
i++) {
PN_SIZE len = strlen(pn_loader_extensions[i]);
char buf[str_len + len + 1];
strncpy(buf, str, str_len);
strncpy(buf + str_len, pn_loader_extensions[i], len);
buf[str_len + len] = '\0';
if (stat(buf, &st) == 0 && S_ISREG(st.st_mode))
return pn_loader_extensions[i];
}
return NULL;
}
char *potion_find_file(Potion *P, char *str, PN_SIZE str_len) {
char *r = NULL;
struct stat st;
if (!str_len) str_len = strlen(str);
PN_TUPLE_EACH(pn_loader_path, i, prefix, {
PN_SIZE prefix_len = PN_STR_LEN(prefix);
char dirname[prefix_len + 1 + str_len + 1];
char *str_pos = dirname + prefix_len + 1;
char *dot;
const char *ext;
memcpy(str_pos, str, str_len);
dot = memchr(str, '.', str_len);
if (dot == NULL)
dirname[sizeof(dirname) - 1] = '\0';
else
*dot = '\0';
memcpy(dirname, PN_STR_PTR(prefix), prefix_len);
dirname[prefix_len] = '/';
if (stat(dirname, &st) == 0 && S_ISREG(st.st_mode)) {
if (asprintf(&r, "%s", dirname) == -1) potion_allocation_error();
break;
} else if ((ext = find_extension(dirname)) != NULL) {
if (asprintf(&r, "%s%s", dirname, ext) == -1) potion_allocation_error();
break;
} else {
char *file;
if ((file = strrchr(str, '/')) == NULL)
file = str;
else
file++;
if (asprintf(&r, "%s/%s", dirname, file) == -1) potion_allocation_error();
if (stat(r, &st) != 0 || !S_ISREG(st.st_mode)) {
int r_len = prefix_len + 1 + str_len * 2 + 1;
if ((ext = find_extension(r)) == NULL) { free(r); r = NULL; continue; }
r = realloc(r, r_len + strlen(ext));
if (r == NULL) potion_allocation_error();
strcpy(r + r_len, ext);
}
break;
}
});
return r;
}
PN potion_load(Potion *P, PN cl, PN self, PN file) {
if (!file && PN_IS_STR(self))
file = self;
char *filename = potion_find_file(P, PN_STR_PTR(file), PN_STR_LEN(file));
char *file_ext;
PN result = PN_NIL;
if (filename == NULL) {
fprintf(stderr, "** can't find %s\n", PN_STR_PTR(file));
return PN_NIL;
}
file_ext = filename + strlen(filename);
while (*--file_ext != '.' && file_ext >= filename);
if (file_ext++ != filename) {
if (strcmp(file_ext, "pn") == 0)
result = potion_load_code(P, filename);
else if (strcmp(file_ext, "pnb") == 0)
result = potion_load_code(P, filename);
else if (strcmp(file_ext, POTION_LOADEXT+1) == 0)
result = potion_load_dylib(P, filename);
else
fprintf(stderr, "** unrecognized file extension: %s\n", file_ext);
} else {
fprintf(stderr, "** no file extension: %s\n", filename);
}
free(filename);
return result;
}
void potion_loader_add(Potion *P, PN path) {
PN_PUSH(pn_loader_path, path);
}
void potion_loader_init(Potion *P) {
pn_loader_path = PN_TUP0();
PN_PUSH(pn_loader_path, potion_str(P, "lib/potion"));
PN_PUSH(pn_loader_path, potion_str(P, POTION_PREFIX"/lib/potion"));
PN_PUSH(pn_loader_path, potion_str(P, "."));
potion_define_global(P, potion_str(P, "LOADER_PATH"), pn_loader_path);
potion_method(P->lobby, "load", potion_load, "file=S");
}