potion  0.2
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
load.c
Go to the documentation of this file.
1 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <string.h>
10 #include <dlfcn.h>
11 #include <sys/stat.h>
12 #include "potion.h"
13 #include "internal.h"
14 #include "table.h"
15 
16 static PN potion_load_code(Potion *P, const char *filename) {
17  PN buf, code;
18  struct stat stats;
19  int fd = -1;
20  PN result = PN_NIL;
21  if (stat(filename, &stats) == -1) {
22  fprintf(stderr, "** %s does not exist.", filename);
23  return PN_NIL;
24  }
25  fd = open(filename, O_RDONLY | O_BINARY);
26  if (fd == -1) {
27  fprintf(stderr, "** could not open %s. check permissions.", filename);
28  return PN_NIL;
29  }
30  buf = potion_bytes(P, stats.st_size);
31  if (read(fd, PN_STR_PTR(buf), stats.st_size) == stats.st_size) {
32  PN_STR_PTR(buf)[stats.st_size] = '\0';
33  code = potion_source_load(P, PN_NIL, buf);
34  if (!PN_IS_PROTO(code)) {
35  result = potion_run(P, potion_send(
36  potion_parse(P, buf, (char *)filename),
37  PN_compile, potion_str(P, filename), PN_NIL),
38  POTION_JIT);
39  }
40  } else {
41  fprintf(stderr, "** could not read entire file: %s.", filename);
42  }
43  close(fd);
44  return result;
45 }
46 
47 static char *potion_initializer_name(Potion *P, const char *filename, PN_SIZE len) {
48  PN_SIZE ext_name_len = 0;
49  char *allocated_str, *ext_name, *func_name;
50  while (*(filename + ++ext_name_len) != '.' && ext_name_len <= len);
51  allocated_str = ext_name = malloc(ext_name_len + 1);
52  if (allocated_str == NULL) potion_allocation_error();
53  strncpy(ext_name, filename, ext_name_len);
54  ext_name[ext_name_len] = '\0';
55  ext_name += ext_name_len;
56  while (*--ext_name != '/' && ext_name >= allocated_str);
57  ext_name++;
58  if (asprintf(&func_name, "Potion_Init_%s", ext_name) == -1)
60  free(allocated_str);
61  return func_name;
62 }
63 
64 static PN potion_load_dylib(Potion *P, const char *filename) {
65  void *handle = dlopen(filename, RTLD_LAZY);
66  void (*func)(Potion *);
67  char *err, *init_func_name;
68  if (handle == NULL) {
69  // TODO: error
70  fprintf(stderr, "** error loading %s: %s\n", filename, dlerror());
71  return PN_NIL;
72  }
73  init_func_name = potion_initializer_name(P, filename, strlen(filename));
74  func = (void (*)(Potion *))dlsym(handle, init_func_name);
75  err = (char *)dlerror();
76  free(init_func_name);
77  if (err != NULL) {
78  fprintf(stderr, "** error loading %s in %s: %s\n", init_func_name, filename, err);
79  dlclose(handle);
80  return PN_NIL;
81  }
82  func(P);
83  return PN_TRUE;
84 }
85 
87 static const char *pn_loader_extensions[] = {
88  ".pnb"
89  , ".pn"
91 };
92 
93 static const char *find_extension(char *str) {
94  int i;
95  PN_SIZE str_len = strlen(str);
96  struct stat st;
97  for (i = 0;
98  i < sizeof(pn_loader_extensions) / sizeof(void *);
99  i++) {
100  PN_SIZE len = strlen(pn_loader_extensions[i]);
101  char buf[str_len + len + 1];
102  strncpy(buf, str, str_len);
103  strncpy(buf + str_len, pn_loader_extensions[i], len);
104  buf[str_len + len] = '\0';
105  if (stat(buf, &st) == 0 && S_ISREG(st.st_mode))
106  return pn_loader_extensions[i];
107  }
108  return NULL;
109 }
110 
111 char *potion_find_file(Potion *P, char *str, PN_SIZE str_len) {
112  char *r = NULL;
113  struct stat st;
114  if (!str_len) str_len = strlen(str);
115  PN_TUPLE_EACH(pn_loader_path, i, prefix, {
116  PN_SIZE prefix_len = PN_STR_LEN(prefix);
117  char dirname[prefix_len + 1 + str_len + 1];
118  char *str_pos = dirname + prefix_len + 1;
119  char *dot;
120  const char *ext;
121  memcpy(str_pos, str, str_len);
122  dot = memchr(str, '.', str_len);
123  if (dot == NULL)
124  dirname[sizeof(dirname) - 1] = '\0';
125  else
126  *dot = '\0';
127  memcpy(dirname, PN_STR_PTR(prefix), prefix_len);
128  dirname[prefix_len] = '/';
129  if (stat(dirname, &st) == 0 && S_ISREG(st.st_mode)) {
130  if (asprintf(&r, "%s", dirname) == -1) potion_allocation_error();
131  break;
132  } else if ((ext = find_extension(dirname)) != NULL) {
133  if (asprintf(&r, "%s%s", dirname, ext) == -1) potion_allocation_error();
134  break;
135  } else {
136  char *file;
137  if ((file = strrchr(str, '/')) == NULL)
138  file = str;
139  else
140  file++;
141  if (asprintf(&r, "%s/%s", dirname, file) == -1) potion_allocation_error();
142  if (stat(r, &st) != 0 || !S_ISREG(st.st_mode)) {
143  int r_len = prefix_len + 1 + str_len * 2 + 1;
144  if ((ext = find_extension(r)) == NULL) { free(r); r = NULL; continue; }
145  r = realloc(r, r_len + strlen(ext));
146  if (r == NULL) potion_allocation_error();
147  strcpy(r + r_len, ext);
148  }
149  break;
150  }
151  });
152  return r;
153 }
154 
155 PN potion_load(Potion *P, PN cl, PN self, PN file) {
156  if (!file && PN_IS_STR(self))
157  file = self;
158  char *filename = potion_find_file(P, PN_STR_PTR(file), PN_STR_LEN(file));
159  char *file_ext;
160  PN result = PN_NIL;
161  if (filename == NULL) {
162  fprintf(stderr, "** can't find %s\n", PN_STR_PTR(file));
163  return PN_NIL;
164  }
165  file_ext = filename + strlen(filename);
166  while (*--file_ext != '.' && file_ext >= filename);
167  if (file_ext++ != filename) {
168  if (strcmp(file_ext, "pn") == 0)
169  result = potion_load_code(P, filename);
170  else if (strcmp(file_ext, "pnb") == 0)
171  result = potion_load_code(P, filename);
172  else if (strcmp(file_ext, POTION_LOADEXT+1) == 0)
173  result = potion_load_dylib(P, filename);
174  else
175  fprintf(stderr, "** unrecognized file extension: %s\n", file_ext);
176  } else {
177  fprintf(stderr, "** no file extension: %s\n", filename);
178  }
179  free(filename);
180  return result;
181 }
182 
183 void potion_loader_add(Potion *P, PN path) {
184  PN_PUSH(pn_loader_path, path);
185 }
186 
189  // relocatable path - relative to exe in argv[0]
190  //PN arg0 = potion_send(potion_str(P, "$^X")); // but too early for argv[0]
191  //if (arg0) PN_PUSH(pn_loader_path, potion_strcat(P, basename(PN_STR_PTR(arg0)), "../lib/potion"));
192  PN_PUSH(pn_loader_path, potion_str(P, "lib/potion"));
193  PN_PUSH(pn_loader_path, potion_str(P, POTION_PREFIX"/lib/potion"));
195 
196  potion_define_global(P, potion_str(P, "LOADER_PATH"), pn_loader_path);
197  potion_method(P->lobby, "load", potion_load, "file=S");
198 }
PN PN_compile
Definition: internal.c:14
#define PN_IS_STR(v)
Definition: potion.h:166
PN potion_bytes(Potion *, size_t)
Definition: string.c:342
PN potion_str(Potion *, const char *)
Definition: string.c:33
static char * potion_initializer_name(Potion *P, const char *filename, PN_SIZE len)
Definition: load.c:47
#define potion_method(RCV, MSG, FN, SIG)
Definition: potion.h:780
#define PN_PUSH(T, X)
Definition: potion.h:263
#define PN_STR_LEN(x)
Definition: potion.h:214
PN potion_source_load(Potion *P, PN cl, PN buf)
Definition: compile.c:1171
#define POTION_PREFIX
Definition: config.h:12
#define POTION_JIT
Definition: config.h:17
unsigned int PN_SIZE
Definition: potion.h:79
PN potion_load(Potion *P, PN cl, PN self, PN file)
Definition: load.c:155
PN lobby
root namespace
Definition: potion.h:648
PN potion_run(Potion *P, PN code, int jit)
Definition: compile.c:1283
void potion_define_global(Potion *P, PN name, PN val)
Definition: objmodel.c:622
PN potion_parse(Potion *, PN, char *)
static PN potion_load_dylib(Potion *P, const char *filename)
Definition: load.c:64
static PN potion_load_code(Potion *P, const char *filename)
Definition: load.c:16
#define PN_TRUE
Definition: potion.h:142
#define POTION_LOADEXT
Definition: config.h:15
non-API internal parts
#define PN_IS_PROTO(v)
Definition: potion.h:169
static const char * find_extension(char *str)
Definition: load.c:93
static const char * pn_loader_extensions[]
Definition: load.c:87
the global interpreter state P. currently singleton (not threads yet)
Definition: potion.h:644
The potion API.
void potion_loader_init(Potion *P)
Definition: load.c:187
the central table type, based on core/khash.h
static PN pn_loader_path
Definition: load.c:86
#define O_BINARY
Definition: internal.h:128
#define potion_send(RCV, MSG, ARGS...)
method caches (more great stuff from ian piumarta)
Definition: potion.h:772
void potion_allocation_error(void)
Definition: internal.c:301
volatile _PN PN
Definition: potion.h:81
#define PN_NIL
Definition: potion.h:139
void potion_loader_add(Potion *P, PN path)
Definition: load.c:183
#define PN_TUPLE_EACH(T, I, V, B)
Definition: potion.h:279
#define PN_STR_PTR(x)
Definition: potion.h:213
#define PN_TUP0()
Definition: potion.h:261
char * potion_find_file(Potion *P, char *str, PN_SIZE str_len)
Definition: load.c:111