7670462cd255e8769f0d01caa7ffa3ae803d8729
[m6w6/ext-psi] / src / context.c
1 #include <sys/param.h>
2 #include <dirent.h>
3 #include <fnmatch.h>
4 #include <errno.h>
5
6 #include "php.h"
7 #include "php_scandir.h"
8 #include "context.h"
9 #include "parser.h"
10 #include "validator.h"
11
12 PSI_Context *PSI_ContextInit(PSI_Context *C, PSI_ContextOps *ops, PSI_ContextErrorFunc error)
13 {
14 if (!C) {
15 C = malloc(sizeof(*C));
16 }
17 memset(C, 0, sizeof(*C));
18
19 C->error = error;
20 C->ops = ops;
21 ops->init(C);
22
23 return C;
24 }
25
26 static int psi_select_dirent(const struct dirent *entry)
27 {
28 #ifndef FNM_CASEFOLD
29 #define FNM_CASEFOLD 0
30 #endif
31 return 0 == fnmatch("*.psi", entry->d_name, FNM_CASEFOLD);
32 }
33
34
35 void PSI_ContextBuild(PSI_Context *C, const char *path)
36 {
37 int i, n;
38 struct dirent **entries = NULL;
39
40 n = php_scandir(path, &entries, psi_select_dirent, alphasort);
41
42 if (n < 0) {
43 return;
44 C->error(PSI_WARNING, "Failed to scan PSI directory '%s'", path);
45 } else for (i = 0; i < n; ++i) {
46 char psi[MAXPATHLEN];
47 PSI_Parser P;
48 PSI_Validator V;
49
50 if (MAXPATHLEN <= slprintf(psi, MAXPATHLEN, "%s/%s", path, entries[i]->d_name)) {
51 C->error(PSI_WARNING, "Path to PSI file too long: %s/%s",
52 path, entries[i]->d_name);
53 }
54 if (!PSI_ParserInit(&P, psi, C->error, 0)) {
55 C->error(PSI_WARNING, "Failed to init PSI parser (%s): %s",
56 psi, strerror(errno));
57 continue;
58 }
59
60 while (-1 != PSI_ParserScan(&P)) {
61 PSI_ParserParse(&P, PSI_TokenAlloc(&P));
62 };
63 PSI_ParserParse(&P, NULL);
64
65 if (!PSI_ValidatorInit(&V, &P)) {
66 C->error(PSI_WARNING, "Failed to init PSI validator");
67 break;
68 }
69 PSI_ParserDtor(&P);
70
71 if (PSI_ValidatorValidate(&V)) {
72 zend_function_entry *closures;
73
74 if (V.consts) {
75 zend_constant zc;
76
77 zc.flags = CONST_PERSISTENT|CONST_CS;
78 zc.module_number = EG(current_module)->module_number;
79
80 for (i = 0; i < V.consts->count; ++i) {
81 constant *c = V.consts->list[i];
82
83 zc.name = zend_string_init(c->name + (c->name[0] == '\\'), strlen(c->name) - (c->name[0] == '\\'), 1);
84 ZVAL_NEW_STR(&zc.value, zend_string_init(c->val->text, strlen(c->val->text), 1));
85
86 switch (c->type->type) {
87 case PSI_T_BOOL:
88 convert_to_boolean(&zc.value);
89 break;
90 case PSI_T_INT:
91 convert_to_long(&zc.value);
92 break;
93 case PSI_T_FLOAT:
94 convert_to_double(&zc.value);
95 break;
96 }
97 zend_register_constant(&zc);
98 }
99 }
100
101 closures = PSI_ContextCompile(C, (PSI_Data *) &V);
102 if (closures && SUCCESS != zend_register_functions(NULL, closures, NULL, MODULE_PERSISTENT)) {
103 C->error(PSI_WARNING, "Failed to register functions!");
104 }
105 }
106 PSI_ValidatorDtor(&V);
107 }
108 if (entries) {
109 for (i = 0; i < n; ++i) {
110 free(entries[i]);
111 }
112 free(entries);
113 }
114
115 }
116
117 zend_function_entry *PSI_ContextCompile(PSI_Context *C, PSI_Data *D)
118 {
119 size_t count = C->count++;
120 zend_function_entry *zfe;
121
122 C->data = realloc(C->data, C->count * sizeof(*C->data));
123 PSI_DataExchange(&C->data[count], D);
124
125 zfe = C->ops->compile(C, &C->data[count]);
126
127 C->closures = realloc(C->closures, C->count * sizeof(*C->closures));
128 C->closures[count] = zfe;
129
130 return zfe;
131 }
132
133 void PSI_ContextDtor(PSI_Context *C)
134 {
135 size_t i;
136
137 C->ops->dtor(C);
138
139 for (i = 0; i < C->count; ++i) {
140 PSI_DataDtor(&C->data[i]);
141 if (C->closures[i]){
142 free(C->closures[i]->arg_info);
143 free(C->closures[i]);
144 }
145 }
146 free(C->data);
147 free(C->closures);
148
149 memset(C, 0, sizeof(*C));
150 }
151
152 void PSI_ContextFree(PSI_Context **C)
153 {
154 if (*C) {
155 PSI_ContextDtor(*C);
156 free(*C);
157 *C = NULL;
158 }
159 }
160