9ed930c042eb4ca19b07165346cf5072c0c10f50
[m6w6/ext-psi] / src / builtin.c
1 /*******************************************************************************
2 Copyright (c) 2018, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #else
29 # include "php_config.h"
30 #endif
31
32 #include <stdbool.h>
33 #include <stdarg.h>
34
35 #include "php_psi.h"
36 #include "builtin.h"
37 #include "parser.h"
38 #include "cpp.h"
39
40 HashTable psi_builtins;
41
42 static bool has_include(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
43 static bool has_include_next(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
44 static bool has_feature(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
45 static bool builtin_constant_p(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
46 static bool BASE_FILE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
47 static bool COUNTER__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
48 static bool DATE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
49 static bool FILE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
50 static bool INCLUDE_LEVEL__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
51 static bool LINE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
52 static bool TIME__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
53 static bool TIMESTAMP__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
54
55 static inline struct psi_plist *builtin_sig(token_t typ, ...)
56 {
57 struct psi_plist *sig;
58 size_t n = 0;
59 va_list args;
60
61 if (typ == (token_t) - 1) {
62 return NULL;
63 }
64
65 sig = psi_plist_init((psi_plist_dtor) psi_token_free);
66 va_start(args, typ);
67 while (typ) {
68 char a = 'a' + n++;
69 struct psi_token *arg;
70
71 arg = psi_token_init(typ, &a, 1, 0, 0, zend_empty_string);
72 sig = psi_plist_add(sig, &arg);
73 typ = va_arg(args, token_t);
74 }
75 va_end(args);
76
77 return sig;
78 }
79
80 static void free_builtin(zval *p)
81 {
82 struct psi_builtin *b = Z_PTR_P(p);
83
84 if (b) {
85 zend_string_release(b->name);
86 psi_cpp_macro_decl_free(&b->decl);
87 pefree(b, 1);
88 }
89 }
90
91 PHP_MINIT_FUNCTION(psi_builtin);
92 PHP_MINIT_FUNCTION(psi_builtin)
93 {
94 #define PSI_BUILTIN(builtin, ...) do { \
95 struct psi_builtin entry; \
96 struct psi_plist *sig = builtin_sig(__VA_ARGS__, 0); \
97 struct psi_cpp_macro_decl *decl = psi_cpp_macro_decl_init(sig, NULL, NULL); \
98 decl->token = psi_token_init(PSI_T_NAME, "__" #builtin, sizeof("__" #builtin)-1, \
99 0, 0, zend_empty_string); \
100 entry.name = zend_string_copy(decl->token->text); \
101 entry.func = &builtin; \
102 entry.decl = decl; \
103 zend_hash_add_mem(&psi_builtins, entry.name, &entry, sizeof(entry)); \
104 } while(0)
105
106 zend_hash_init(&psi_builtins, 0, NULL, free_builtin, 1);
107 PSI_BUILTIN(has_include, PSI_T_CPP_HEADER);
108 PSI_BUILTIN(has_include_next, PSI_T_CPP_HEADER);
109 PSI_BUILTIN(has_feature, PSI_T_NAME);
110 PSI_BUILTIN(builtin_constant_p, PSI_T_NAME);
111
112 PSI_BUILTIN(BASE_FILE__, -1);
113 PSI_BUILTIN(COUNTER__, -1);
114 PSI_BUILTIN(DATE__, -1);
115 PSI_BUILTIN(FILE__, -1);
116 PSI_BUILTIN(INCLUDE_LEVEL__, -1);
117 PSI_BUILTIN(LINE__, -1);
118 PSI_BUILTIN(TIME__, -1);
119 PSI_BUILTIN(TIMESTAMP__, -1);
120
121 return SUCCESS;
122 }
123
124 PHP_MSHUTDOWN_FUNCTION(psi_builtin);
125 PHP_MSHUTDOWN_FUNCTION(psi_builtin)
126 {
127 zend_hash_destroy(&psi_builtins);
128 return SUCCESS;
129 }
130
131 bool psi_builtin_exists(zend_string *name)
132 {
133 return zend_hash_exists(&psi_builtins, name);
134 }
135
136 struct psi_builtin *psi_builtin_get(zend_string *name)
137 {
138 return zend_hash_find_ptr(&psi_builtins, name);
139 }
140
141 static bool has_include(struct psi_cpp *cpp, struct psi_token *target,
142 struct psi_plist **args, struct psi_plist **res)
143 {
144 struct psi_plist *arg = args[0];
145 struct psi_token *tok;
146
147 switch (psi_plist_count(arg)) {
148 case 1:
149 if (psi_plist_get(arg, 0, &tok)) {
150 const char *cpp_search = cpp->search;
151 bool has = psi_cpp_has_include(cpp, tok, 0, NULL);
152 cpp->search = cpp_search;
153 return has;
154 }
155 /* no break */
156 default:
157 cpp->parser->error(PSI_DATA(cpp->parser), target, PSI_WARNING,
158 "Erroneous usage of builtin __%s", __FUNCTION__);
159 }
160 return false;
161 }
162
163 static bool has_include_next(struct psi_cpp *cpp, struct psi_token *target,
164 struct psi_plist **args, struct psi_plist **res)
165 {
166 struct psi_plist *arg = args[0];
167 struct psi_token *tok;
168
169 switch (psi_plist_count(arg)) {
170 case 1:
171 if (psi_plist_get(arg, 0, &tok)) {
172 const char *cpp_search = cpp->search;
173 bool has = psi_cpp_has_include(cpp, tok, PSI_CPP_INCLUDE_NEXT, NULL);
174 cpp->search = cpp_search;
175 return has;
176 }
177 /* no break */
178 default:
179 cpp->parser->error(PSI_DATA(cpp->parser), target, PSI_WARNING,
180 "Erroneous usage of builtin __%s", __FUNCTION__);
181 }
182 return false;
183 }
184
185 static bool has_feature(struct psi_cpp *cpp, struct psi_token *target,
186 struct psi_plist **args, struct psi_plist **res_ptr)
187 {
188 return false;
189 }
190
191 static bool builtin_constant_p(struct psi_cpp *cpp, struct psi_token *target,
192 struct psi_plist **args, struct psi_plist **res_ptr)
193 {
194 /* we want functions, not macros for e.g. htonl() */
195 return false;
196 }
197
198 #define NEW_TOKEN(typ, str, len) \
199 psi_token_init((typ), (str), (len), (target)->col, (target)->line, (target)->file)
200
201 #define ADD_TOKEN(tok) do { \
202 struct psi_token *tok__ = tok; \
203 if (!*res) { \
204 *res = psi_plist_init((psi_plist_dtor) psi_token_free); \
205 } \
206 *res = psi_plist_add(*res, &tok__); \
207 } while (0)
208
209 #define ADD_QUOTED_STRING(buf, len) do { \
210 ADD_TOKEN(NEW_TOKEN(PSI_T_QUOTED_STRING, buf, len)); \
211 } while(0)
212
213 #define ADD_QUOTED_ZSTRING(zs) do { \
214 zend_string *zs_ = zs; \
215 ADD_QUOTED_STRING(zs_->val, zs_->len); \
216 } while (0)
217
218 #define ADD_UNSIGNED_NUMBER(u) do { \
219 char buf[0x20]; \
220 unsigned u_ = u; \
221 size_t len = snprintf(buf, sizeof(buf), "%u", u_); \
222 struct psi_token *tok_ = NEW_TOKEN(PSI_T_NUMBER, buf, len); \
223 tok_->flags |= PSI_NUMBER_INT | PSI_NUMBER_U; \
224 ADD_TOKEN(tok_); \
225 } while (0)
226
227 static bool BASE_FILE__(struct psi_cpp *cpp, struct psi_token *target,
228 struct psi_plist **args, struct psi_plist **res)
229 {
230 ADD_QUOTED_ZSTRING(cpp->parser->input->file);
231 return true;
232 }
233
234 static bool COUNTER__(struct psi_cpp *cpp, struct psi_token *target,
235 struct psi_plist **args, struct psi_plist **res)
236 {
237 ADD_UNSIGNED_NUMBER(cpp->counter++);
238 return true;
239 }
240
241 static bool DATE__(struct psi_cpp *cpp, struct psi_token *target,
242 struct psi_plist **args, struct psi_plist **res)
243 {
244 char buf[12];
245 struct tm *tp;
246 time_t t = time(NULL);
247 #if HAVE_LOCALTIME_R
248 struct tm tm = {0};
249 tp = localtime_r(&t, &tm);
250 #else
251 tp = localtime(&t);
252 #endif
253
254 strftime(buf, sizeof(buf), "%b %e %Y", tp);
255 ADD_QUOTED_STRING(buf, 11);
256 return true;
257 }
258
259 static bool FILE__(struct psi_cpp *cpp, struct psi_token *target,
260 struct psi_plist **args, struct psi_plist **res)
261 {
262 ADD_QUOTED_ZSTRING(target->file);
263 return true;
264 }
265
266 static bool INCLUDE_LEVEL__(struct psi_cpp *cpp, struct psi_token *target,
267 struct psi_plist **args, struct psi_plist **res)
268 {
269 ADD_UNSIGNED_NUMBER(cpp->include_level);
270 return true;
271 }
272
273 static bool LINE__(struct psi_cpp *cpp, struct psi_token *target,
274 struct psi_plist **args, struct psi_plist **res)
275 {
276 ADD_UNSIGNED_NUMBER(target->line);
277 return true;
278 }
279
280 static bool TIME__(struct psi_cpp *cpp, struct psi_token *target,
281 struct psi_plist **args, struct psi_plist **res)
282 {
283 char buf[9];
284 struct tm *tp;
285 time_t t = time(NULL);
286 #if HAVE_LOCALTIME_R
287 struct tm tm = {0};
288 tp = localtime_r(&t, &tm);
289 #else
290 tp = localtime(&t);
291 #endif
292
293 strftime(buf, sizeof(buf), "%H:%M:%S", tp);
294 ADD_QUOTED_STRING(buf, 8);
295 return true;
296 }
297
298 static bool TIMESTAMP__(struct psi_cpp *cpp, struct psi_token *target,
299 struct psi_plist **args, struct psi_plist **res)
300 {
301 char *str;
302 #ifdef HAVE_CTIME_R
303 char buf[26];
304 str = ctime_r(&cpp->parser->input->lmod, buf);
305 #else
306 str = ctime(&cpp->parser->input->lmod);
307 #endif
308
309 /* kill EOL */
310 ADD_QUOTED_STRING(str, 24);
311 return true;
312 }
313
314
315 #ifdef __APPLE__
316 #include <libkern/OSByteOrder.h>
317 # define bswap_16(u) _OSSwapInt16(u)
318 # define bswap_32(u) _OSSwapInt32(u)
319 # define bswap_64(u) _OSSwapInt64(u)
320 #elif defined(__FreeBSD__)
321 # include <sys/endian.h>
322 # define bswap_16(u) bswap16(u)
323 # define bswap_32(u) bswap32(u)
324 # define bswap_64(u) bswap64(u)
325 #elif defined(__OpenBSD__)
326 # include <sys/types.h>
327 # define bswap_16(u) swap16(u)
328 # define bswap_32(u) swap32(u)
329 # define bswap_64(u) swap64(u)
330 #elif defined(__NetBSD__)
331 # include <sys/types.h>
332 # include <machine/bswap.h>
333 # define bswap_16(u) bswap16(u)
334 # define bswap_32(u) bswap32(u)
335 # define bswap_64(u) bswap64(u)
336 #else
337 # include <byteswap.h>
338 #endif
339
340 uint16_t psi_swap16(uint16_t u)
341 {
342 return bswap_16(u);
343 }
344
345 uint32_t psi_swap32(uint32_t u)
346 {
347 return bswap_32(u);
348 }
349
350 uint64_t psi_swap64(uint64_t u)
351 {
352 return bswap_64(u);
353 }