travis: update
[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 signed__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
47 static bool BASE_FILE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
48 static bool COUNTER__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
49 static bool DATE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
50 static bool FILE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
51 static bool INCLUDE_LEVEL__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
52 static bool LINE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
53 static bool TIME__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
54 static bool TIMESTAMP__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
55
56 static inline struct psi_plist *builtin_sig(token_t typ, ...)
57 {
58 struct psi_plist *sig;
59 size_t n = 0;
60 va_list args;
61
62 if (typ == (token_t) - 1) {
63 return NULL;
64 }
65
66 sig = psi_plist_init((psi_plist_dtor) psi_token_free);
67 va_start(args, typ);
68 while (typ) {
69 char a = 'a' + n++;
70 struct psi_token *arg;
71
72 arg = psi_token_init(typ, &a, 1, 0, 0, zend_empty_string);
73 sig = psi_plist_add(sig, &arg);
74 typ = va_arg(args, token_t);
75 }
76 va_end(args);
77
78 return sig;
79 }
80
81 static void free_builtin(zval *p)
82 {
83 struct psi_builtin *b = Z_PTR_P(p);
84
85 if (b) {
86 zend_string_release(b->name);
87 psi_cpp_macro_decl_free(&b->decl);
88 pefree(b, 1);
89 }
90 }
91
92 PHP_MINIT_FUNCTION(psi_builtin);
93 PHP_MINIT_FUNCTION(psi_builtin)
94 {
95 #define PSI_BUILTIN(builtin, ...) do { \
96 struct psi_builtin entry; \
97 struct psi_plist *sig = builtin_sig(__VA_ARGS__, 0); \
98 struct psi_cpp_macro_decl *decl = psi_cpp_macro_decl_init(sig, NULL, NULL); \
99 decl->token = psi_token_init(PSI_T_NAME, "__" #builtin, sizeof("__" #builtin)-1, \
100 0, 0, zend_empty_string); \
101 entry.name = zend_string_copy(decl->token->text); \
102 entry.func = &builtin; \
103 entry.decl = decl; \
104 zend_hash_add_mem(&psi_builtins, entry.name, &entry, sizeof(entry)); \
105 } while(0)
106
107 zend_hash_init(&psi_builtins, 0, NULL, free_builtin, 1);
108 PSI_BUILTIN(has_include, PSI_T_CPP_HEADER);
109 PSI_BUILTIN(has_include_next, PSI_T_CPP_HEADER);
110 PSI_BUILTIN(has_feature, PSI_T_NAME);
111 PSI_BUILTIN(builtin_constant_p, PSI_T_NAME);
112
113 PSI_BUILTIN(signed__, -1);
114
115 PSI_BUILTIN(BASE_FILE__, -1);
116 PSI_BUILTIN(COUNTER__, -1);
117 PSI_BUILTIN(DATE__, -1);
118 PSI_BUILTIN(FILE__, -1);
119 PSI_BUILTIN(INCLUDE_LEVEL__, -1);
120 PSI_BUILTIN(LINE__, -1);
121 PSI_BUILTIN(TIME__, -1);
122 PSI_BUILTIN(TIMESTAMP__, -1);
123
124 return SUCCESS;
125 }
126
127 PHP_MSHUTDOWN_FUNCTION(psi_builtin);
128 PHP_MSHUTDOWN_FUNCTION(psi_builtin)
129 {
130 zend_hash_destroy(&psi_builtins);
131 return SUCCESS;
132 }
133
134 bool psi_builtin_exists(zend_string *name)
135 {
136 return zend_hash_exists(&psi_builtins, name);
137 }
138
139 struct psi_builtin *psi_builtin_get(zend_string *name)
140 {
141 return zend_hash_find_ptr(&psi_builtins, name);
142 }
143
144 static bool has_include(struct psi_cpp *cpp, struct psi_token *target,
145 struct psi_plist **args, struct psi_plist **res)
146 {
147 struct psi_plist *arg = args[0];
148 struct psi_token *tok;
149
150 switch (psi_plist_count(arg)) {
151 case 1:
152 if (psi_plist_get(arg, 0, &tok)) {
153 const char *cpp_search = cpp->search;
154 bool has = psi_cpp_has_include(cpp, tok, 0, NULL);
155 cpp->search = cpp_search;
156 return has;
157 }
158 /* no break */
159 default:
160 cpp->parser->error(PSI_DATA(cpp->parser), target, PSI_WARNING,
161 "Erroneous usage of builtin __%s", __FUNCTION__);
162 }
163 return false;
164 }
165
166 static bool has_include_next(struct psi_cpp *cpp, struct psi_token *target,
167 struct psi_plist **args, struct psi_plist **res)
168 {
169 struct psi_plist *arg = args[0];
170 struct psi_token *tok;
171
172 switch (psi_plist_count(arg)) {
173 case 1:
174 if (psi_plist_get(arg, 0, &tok)) {
175 const char *cpp_search = cpp->search;
176 bool has = psi_cpp_has_include(cpp, tok, PSI_CPP_INCLUDE_NEXT, NULL);
177 cpp->search = cpp_search;
178 return has;
179 }
180 /* no break */
181 default:
182 cpp->parser->error(PSI_DATA(cpp->parser), target, PSI_WARNING,
183 "Erroneous usage of builtin __%s", __FUNCTION__);
184 }
185 return false;
186 }
187
188 static bool has_feature(struct psi_cpp *cpp, struct psi_token *target,
189 struct psi_plist **args, struct psi_plist **res_ptr)
190 {
191 return false;
192 }
193
194 static bool builtin_constant_p(struct psi_cpp *cpp, struct psi_token *target,
195 struct psi_plist **args, struct psi_plist **res_ptr)
196 {
197 /* we want functions, not macros for e.g. htonl() */
198 return false;
199 }
200
201 #define NEW_TOKEN(typ, str, len) \
202 psi_token_init((typ), (str), (len), (target)->col, (target)->line, (target)->file)
203
204 #define ADD_TOKEN(tok) do { \
205 struct psi_token *tok__ = tok; \
206 if (!*res) { \
207 *res = psi_plist_init((psi_plist_dtor) psi_token_free); \
208 } \
209 *res = psi_plist_add(*res, &tok__); \
210 } while (0)
211
212 #define ADD_QUOTED_STRING(buf, len) do { \
213 ADD_TOKEN(NEW_TOKEN(PSI_T_QUOTED_STRING, buf, len)); \
214 } while(0)
215
216 #define ADD_QUOTED_ZSTRING(zs) do { \
217 zend_string *zs_ = zs; \
218 ADD_QUOTED_STRING(zs_->val, zs_->len); \
219 } while (0)
220
221 #define ADD_UNSIGNED_NUMBER(u) do { \
222 char buf[0x20]; \
223 unsigned u_ = u; \
224 size_t len = snprintf(buf, sizeof(buf), "%u", u_); \
225 struct psi_token *tok_ = NEW_TOKEN(PSI_T_NUMBER, buf, len); \
226 tok_->flags |= PSI_NUMBER_INT | PSI_NUMBER_U; \
227 ADD_TOKEN(tok_); \
228 } while (0)
229
230 static bool signed__(struct psi_cpp *cpp, struct psi_token *target,
231 struct psi_plist **args, struct psi_plist **res)
232 {
233 ADD_TOKEN(NEW_TOKEN(PSI_T_SIGNED, "signed", 8));
234 return true;
235 }
236
237 static bool BASE_FILE__(struct psi_cpp *cpp, struct psi_token *target,
238 struct psi_plist **args, struct psi_plist **res)
239 {
240 ADD_QUOTED_ZSTRING(cpp->parser->input->file);
241 return true;
242 }
243
244 static bool COUNTER__(struct psi_cpp *cpp, struct psi_token *target,
245 struct psi_plist **args, struct psi_plist **res)
246 {
247 ADD_UNSIGNED_NUMBER(cpp->counter++);
248 return true;
249 }
250
251 static bool DATE__(struct psi_cpp *cpp, struct psi_token *target,
252 struct psi_plist **args, struct psi_plist **res)
253 {
254 char buf[12];
255 struct tm *tp;
256 time_t t = time(NULL);
257 #if HAVE_LOCALTIME_R
258 struct tm tm = {0};
259 tp = localtime_r(&t, &tm);
260 #else
261 tp = localtime(&t);
262 #endif
263
264 strftime(buf, sizeof(buf), "%b %e %Y", tp);
265 ADD_QUOTED_STRING(buf, 11);
266 return true;
267 }
268
269 static bool FILE__(struct psi_cpp *cpp, struct psi_token *target,
270 struct psi_plist **args, struct psi_plist **res)
271 {
272 ADD_QUOTED_ZSTRING(target->file);
273 return true;
274 }
275
276 static bool INCLUDE_LEVEL__(struct psi_cpp *cpp, struct psi_token *target,
277 struct psi_plist **args, struct psi_plist **res)
278 {
279 ADD_UNSIGNED_NUMBER(cpp->include_level);
280 return true;
281 }
282
283 static bool LINE__(struct psi_cpp *cpp, struct psi_token *target,
284 struct psi_plist **args, struct psi_plist **res)
285 {
286 ADD_UNSIGNED_NUMBER(target->line);
287 return true;
288 }
289
290 static bool TIME__(struct psi_cpp *cpp, struct psi_token *target,
291 struct psi_plist **args, struct psi_plist **res)
292 {
293 char buf[9];
294 struct tm *tp;
295 time_t t = time(NULL);
296 #if HAVE_LOCALTIME_R
297 struct tm tm = {0};
298 tp = localtime_r(&t, &tm);
299 #else
300 tp = localtime(&t);
301 #endif
302
303 strftime(buf, sizeof(buf), "%H:%M:%S", tp);
304 ADD_QUOTED_STRING(buf, 8);
305 return true;
306 }
307
308 static bool TIMESTAMP__(struct psi_cpp *cpp, struct psi_token *target,
309 struct psi_plist **args, struct psi_plist **res)
310 {
311 char *str;
312 #ifdef HAVE_CTIME_R
313 char buf[26];
314 str = ctime_r(&cpp->parser->input->lmod, buf);
315 #else
316 str = ctime(&cpp->parser->input->lmod);
317 #endif
318
319 /* kill EOL */
320 ADD_QUOTED_STRING(str, 24);
321 return true;
322 }
323
324
325 #ifdef __APPLE__
326 #include <libkern/OSByteOrder.h>
327 # define bswap_16(u) _OSSwapInt16(u)
328 # define bswap_32(u) _OSSwapInt32(u)
329 # define bswap_64(u) _OSSwapInt64(u)
330 #elif defined(__FreeBSD__)
331 # include <sys/endian.h>
332 # define bswap_16(u) bswap16(u)
333 # define bswap_32(u) bswap32(u)
334 # define bswap_64(u) bswap64(u)
335 #elif defined(__OpenBSD__)
336 # include <sys/types.h>
337 # define bswap_16(u) swap16(u)
338 # define bswap_32(u) swap32(u)
339 # define bswap_64(u) swap64(u)
340 #elif defined(__NetBSD__)
341 # include <sys/types.h>
342 # include <machine/bswap.h>
343 # define bswap_16(u) bswap16(u)
344 # define bswap_32(u) bswap32(u)
345 # define bswap_64(u) bswap64(u)
346 #else
347 # include <byteswap.h>
348 #endif
349
350 uint16_t psi_swap16(uint16_t u)
351 {
352 return bswap_16(u);
353 }
354
355 uint32_t psi_swap32(uint32_t u)
356 {
357 return bswap_32(u);
358 }
359
360 uint64_t psi_swap64(uint64_t u)
361 {
362 return bswap_64(u);
363 }