summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/CMakeLists.txt9
-rw-r--r--common/listener.cpp6
-rw-r--r--common/storage_unqlite.cpp340
-rw-r--r--common/unqlite/unqlite.c59972
-rw-r--r--common/unqlite/unqlite.h949
-rw-r--r--docs/applicationdomaintypes.md8
-rw-r--r--docs/storage.md40
-rw-r--r--examples/client/main.cpp7
-rw-r--r--tests/dummyresourcewritebenchmark.cpp5
9 files changed, 61 insertions, 61275 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 8312f13..f07772a 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -3,13 +3,8 @@ include_directories(domain)
3 3
4project(akonadi2common) 4project(akonadi2common)
5 5
6if (STORAGE_unqlite) 6set(storage_SRCS storage_lmdb.cpp)
7 add_definitions(-DUNQLITE_ENABLE_THREADS -fpermissive) 7set(storage_LIBS lmdb)
8 set(storage_SRCS unqlite/unqlite.c storage_unqlite.cpp)
9else (STORAGE_unqlite)
10 set(storage_SRCS storage_lmdb.cpp)
11 set(storage_LIBS lmdb)
12endif (STORAGE_unqlite)
13 8
14set(command_SRCS 9set(command_SRCS
15 modelresult.cpp 10 modelresult.cpp
diff --git a/common/listener.cpp b/common/listener.cpp
index 5468769..1b78f01 100644
--- a/common/listener.cpp
+++ b/common/listener.cpp
@@ -35,6 +35,7 @@
35#include <QLocalServer> 35#include <QLocalServer>
36#include <QLocalSocket> 36#include <QLocalSocket>
37#include <QTimer> 37#include <QTimer>
38#include <QTime>
38 39
39Listener::Listener(const QByteArray &resourceInstanceIdentifier, QObject *parent) 40Listener::Listener(const QByteArray &resourceInstanceIdentifier, QObject *parent)
40 : QObject(parent), 41 : QObject(parent),
@@ -220,6 +221,8 @@ void Listener::processCommand(int commandId, uint messageId, const QByteArray &c
220 if (Akonadi2::VerifySynchronizeBuffer(verifier)) { 221 if (Akonadi2::VerifySynchronizeBuffer(verifier)) {
221 auto buffer = Akonadi2::GetSynchronize(commandBuffer.constData()); 222 auto buffer = Akonadi2::GetSynchronize(commandBuffer.constData());
222 Log() << QString("\tSynchronize request (id %1) from %2").arg(messageId).arg(client.name); 223 Log() << QString("\tSynchronize request (id %1) from %2").arg(messageId).arg(client.name);
224 auto timer = QSharedPointer<QTime>::create();
225 timer->start();
223 auto job = KAsync::null<void>(); 226 auto job = KAsync::null<void>();
224 if (buffer->sourceSync()) { 227 if (buffer->sourceSync()) {
225 job = loadResource()->synchronizeWithSource(); 228 job = loadResource()->synchronizeWithSource();
@@ -227,7 +230,8 @@ void Listener::processCommand(int commandId, uint messageId, const QByteArray &c
227 if (buffer->localSync()) { 230 if (buffer->localSync()) {
228 job = job.then<void>(loadResource()->processAllMessages()); 231 job = job.then<void>(loadResource()->processAllMessages());
229 } 232 }
230 job.then<void>([callback]() { 233 job.then<void>([callback, timer]() {
234 Trace() << "Sync took " << timer->elapsed();
231 callback(); 235 callback();
232 }).exec(); 236 }).exec();
233 return; 237 return;
diff --git a/common/storage_unqlite.cpp b/common/storage_unqlite.cpp
deleted file mode 100644
index 6950acd..0000000
--- a/common/storage_unqlite.cpp
+++ /dev/null
@@ -1,340 +0,0 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "storage.h"
22
23#include <iostream>
24
25#include <QAtomicInt>
26#include <QDebug>
27#include <QDir>
28#include <QReadWriteLock>
29#include <QString>
30#include <QTime>
31
32extern "C" {
33 #include "unqlite/unqlite.h"
34}
35
36namespace Akonadi2
37{
38
39static const char *s_unqliteDir = "/unqlite/";
40
41class Storage::Private
42{
43public:
44 Private(const QString &s, const QString &name, AccessMode m, bool allowDuplicates);
45 ~Private();
46
47 void reportDbError(const char *functionName);
48 void reportDbError(const char *functionName, int errorCode,
49 const std::function<void(const Storage::Error &error)> &errorHandler);
50
51 QString storageRoot;
52 QString name;
53 AccessMode mode;
54
55 unqlite *db;
56 bool allowDuplicates;
57 bool inTransaction;
58};
59
60Storage::Private::Private(const QString &s, const QString &n, AccessMode m, bool duplicates)
61 : storageRoot(s),
62 name(n),
63 mode(m),
64 db(0),
65 allowDuplicates(duplicates), //FIXME: currently does nothing ... should do what it says
66 inTransaction(false)
67{
68 const QString fullPath(storageRoot + s_unqliteDir + name);
69 QDir dir;
70 dir.mkpath(storageRoot + s_unqliteDir);
71
72 //create file
73 int openFlags = UNQLITE_OPEN_CREATE;
74 if (mode == ReadOnly) {
75 openFlags |= UNQLITE_OPEN_READONLY | UNQLITE_OPEN_MMAP;
76 } else {
77 openFlags |= UNQLITE_OPEN_READWRITE;
78 }
79
80 int rc = unqlite_open(&db, fullPath.toStdString().data(), openFlags);
81
82 if (rc != UNQLITE_OK) {
83 reportDbError("unqlite_open");
84 }
85}
86
87Storage::Private::~Private()
88{
89 unqlite_close(db);
90}
91
92void Storage::Private::reportDbError(const char *functionName)
93{
94 std::cerr << "ERROR: " << functionName;
95 if (db) {
96 const char *errorMessage;
97 int length;
98 /* Something goes wrong, extract database error log */
99 unqlite_config(db, UNQLITE_CONFIG_ERR_LOG, &errorMessage, &length);
100 if (length > 0) {
101 std::cerr << ": " << errorMessage;
102 }
103 }
104 std::cerr << std::endl;
105}
106
107void Storage::Private::reportDbError(const char *functionName, int errorCode,
108 const std::function<void(const Storage::Error &error)> &errorHandler)
109{
110 if (db) {
111 const char *errorMessage;
112 int length;
113 /* Something goes wrong, extract database error log */
114 unqlite_config(db, UNQLITE_CONFIG_ERR_LOG, &errorMessage, &length);
115 if (length > 0) {
116 Error error(name.toStdString(), errorCode, errorMessage);
117 errorHandler(error);
118 return;
119 }
120 }
121
122 Error error(name.toStdString(), errorCode, functionName);
123 errorHandler(error);
124}
125
126Storage::Storage(const QString &storageRoot, const QString &name, AccessMode mode, bool allowDuplicates)
127 : d(new Private(storageRoot, name, mode, allowDuplicates))
128{
129}
130
131Storage::~Storage()
132{
133 if (d->inTransaction) {
134 abortTransaction();
135 }
136
137 delete d;
138}
139
140bool Storage::isInTransaction() const
141{
142 return d->inTransaction;
143}
144
145bool Storage::startTransaction(AccessMode type)
146{
147 if (!d->db) {
148 return false;
149 }
150
151 if (d->inTransaction) {
152 return true;
153 }
154
155 d->inTransaction = unqlite_begin(d->db) == UNQLITE_OK;
156
157 if (!d->inTransaction) {
158 d->reportDbError("unqlite_begin");
159 }
160
161 return d->inTransaction;
162}
163
164bool Storage::commitTransaction()
165{
166 if (!d->db) {
167 return false;
168 }
169
170 if (!d->inTransaction) {
171 return true;
172 }
173
174 int rc = unqlite_commit(d->db);
175 d->inTransaction = false;
176
177 if (rc != UNQLITE_OK) {
178 d->reportDbError("unqlite_commit");
179 }
180
181 return rc == UNQLITE_OK;
182}
183
184void Storage::abortTransaction()
185{
186 if (!d->db || !d->inTransaction) {
187 return;
188 }
189
190 unqlite_rollback(d->db);
191 d->inTransaction = false;
192}
193
194bool Storage::write(const void *key, size_t keySize, const void *value, size_t valueSize)
195{
196 if (!d->db) {
197 return false;
198 }
199
200 int rc = unqlite_kv_store(d->db, key, keySize, value, valueSize);
201
202 if (rc != UNQLITE_OK) {
203 d->reportDbError("unqlite_kv_store");
204 }
205
206 return !rc;
207}
208
209bool Storage::write(const std::string &sKey, const std::string &sValue)
210{
211 return write(sKey.data(), sKey.size(), sValue.data(), sKey.size());
212}
213
214void Storage::read(const std::string &sKey,
215 const std::function<bool(const std::string &value)> &resultHandler,
216 const std::function<void(const Storage::Error &error)> &errorHandler)
217{
218 read(sKey,
219 [&](void *ptr, int size) -> bool {
220 if (ptr) {
221 const std::string resultValue(static_cast<char*>(ptr), size);
222 return resultHandler(resultValue);
223 }
224
225 return true;
226 }, errorHandler);
227}
228
229void Storage::read(const std::string &sKey,
230 const std::function<bool(void *ptr, int size)> &resultHandler,
231 const std::function<void(const Storage::Error &error)> &errorHandler)
232{
233 scan(sKey.data(), sKey.size(), [resultHandler](void *keyPtr, int keySize, void *valuePtr, int valueSize) {
234 return resultHandler(valuePtr, valueSize);
235 }, errorHandler);
236}
237
238void Storage::remove(const void *keyData, uint keySize)
239{
240 remove(keyData, keySize, basicErrorHandler());
241}
242
243void Storage::remove(const void *keyData, uint keySize,
244 const std::function<void(const Storage::Error &error)> &errorHandler)
245{
246 if (!d->db) {
247 Error error(d->name.toStdString(), -1, "Not open");
248 errorHandler(error);
249 return;
250 }
251
252 unqlite_kv_delete(d->db, keyData, keySize);
253}
254
255
256void fetchCursorData(unqlite_kv_cursor *cursor,
257 void **keyBuffer, int *keyBufferLength, void **dataBuffer, unqlite_int64 *dataBufferLength,
258 const std::function<bool(void *keyPtr, int keySize, void *valuePtr, int valueSize)> &resultHandler)
259{
260 int keyLength = 0;
261 unqlite_int64 dataLength = 0;
262 // now fetch the data sizes
263 if (unqlite_kv_cursor_key(cursor, nullptr, &keyLength) == UNQLITE_OK &&
264 unqlite_kv_cursor_data(cursor, nullptr, &dataLength) == UNQLITE_OK) {
265 if (keyLength > *keyBufferLength) {
266 *keyBuffer = realloc(*keyBuffer, keyLength);
267 *keyBufferLength = keyLength;
268 }
269
270 if (dataLength > *dataBufferLength) {
271 *dataBuffer = realloc(*dataBuffer, dataLength);
272 *dataBufferLength = dataLength;
273 }
274
275 if (unqlite_kv_cursor_key(cursor, *keyBuffer, &keyLength) == UNQLITE_OK &&
276 unqlite_kv_cursor_data(cursor, *dataBuffer, &dataLength) == UNQLITE_OK) {
277 resultHandler(*keyBuffer, keyLength, *dataBuffer, dataLength);
278 }
279 }
280}
281
282void Storage::scan(const char *keyData, uint keySize,
283 const std::function<bool(void *keyPtr, int keySize, void *valuePtr, int valueSize)> &resultHandler,
284 const std::function<void(const Storage::Error &error)> &errorHandler)
285{
286 if (!d->db) {
287 Error error(d->name.toStdString(), -1, "Not open");
288 errorHandler(error);
289 return;
290 }
291
292 unqlite_kv_cursor *cursor;
293
294 int rc = unqlite_kv_cursor_init(d->db, &cursor);
295 if (rc != UNQLITE_OK) {
296 d->reportDbError("unqlite_kv_cursor_init", rc, errorHandler);
297 return;
298 }
299
300 void *keyBuffer = nullptr;
301 int keyBufferLength = 0;
302 void *dataBuffer = nullptr;
303 //FIXME: 64bit ints, but feeding int lenghts to the callbacks. can result in truncation
304 unqlite_int64 dataBufferLength = 0;
305 if (!keyData || keySize == 0) {
306 for (unqlite_kv_cursor_first_entry(cursor); unqlite_kv_cursor_valid_entry(cursor); unqlite_kv_cursor_next_entry(cursor)) {
307 fetchCursorData(cursor, &keyBuffer, &keyBufferLength, &dataBuffer, &dataBufferLength, resultHandler);
308 }
309 } else {
310 rc = unqlite_kv_cursor_seek(cursor, keyData, keySize, UNQLITE_CURSOR_MATCH_EXACT);
311 if (rc == UNQLITE_OK) {
312 fetchCursorData(cursor, &keyBuffer, &keyBufferLength, &dataBuffer, &dataBufferLength, resultHandler);
313 } else {
314 std::cout << "couldn't find value " << std::string(keyData, keySize) << std::endl;
315 }
316
317 }
318
319 free(keyBuffer);
320 free(dataBuffer);
321 unqlite_kv_cursor_release(d->db, cursor);
322}
323
324qint64 Storage::diskUsage() const
325{
326 QFileInfo info(d->storageRoot + s_unqliteDir + d->name);
327 return info.size();
328}
329
330bool Storage::exists() const
331{
332 return d->db != 0;
333}
334
335void Storage::removeFromDisk() const
336{
337 QFile::remove(d->storageRoot + s_unqliteDir + d->name);
338}
339
340} // namespace Akonadi2
diff --git a/common/unqlite/unqlite.c b/common/unqlite/unqlite.c
deleted file mode 100644
index 7128a47..0000000
--- a/common/unqlite/unqlite.c
+++ /dev/null
@@ -1,59972 +0,0 @@
1/*
2 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
3 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
4 * Version 1.1.6
5 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6 * please contact Symisc Systems via:
7 * legal@symisc.net
8 * licensing@symisc.net
9 * contact@symisc.net
10 * or visit:
11 * http://unqlite.org/licensing.html
12 */
13/*
14 * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution.
25 *
26 * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
27 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
29 * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
33 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
34 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
35 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
36 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38/*
39 * $SymiscID: unqlite.c v1.1.6 Unix|Win32/64 2013-07-08 03:38:18 stable <chm@symisc.net> $
40 */
41/* This file is an amalgamation of many separate C source files from unqlite version 1.1.6
42 * By combining all the individual C code files into this single large file, the entire code
43 * can be compiled as a single translation unit. This allows many compilers to do optimization's
44 * that would not be possible if the files were compiled separately. Performance improvements
45 * are commonly seen when unqlite is compiled as a single translation unit.
46 *
47 * This file is all you need to compile unqlite. To use unqlite in other programs, you need
48 * this file and the "unqlite.h" header file that defines the programming interface to the
49 * unqlite engine.(If you do not have the "unqlite.h" header file at hand, you will find
50 * a copy embedded within the text of this file.Search for "Header file: <unqlite.h>" to find
51 * the start of the embedded unqlite.h header file.) Additional code files may be needed if
52 * you want a wrapper to interface unqlite with your choice of programming language.
53 * To get the official documentation, please visit http://unqlite.org/
54 */
55 /*
56 * Make the sure the following directive is defined in the amalgamation build.
57 */
58 #ifndef UNQLITE_AMALGAMATION
59 #define UNQLITE_AMALGAMATION
60 #define JX9_AMALGAMATION
61 /* Marker for routines not intended for external use */
62 #define JX9_PRIVATE static
63 #endif /* UNQLITE_AMALGAMATION */
64/*
65 * Embedded header file for unqlite: <unqlite.h>
66 */
67/*
68 * ----------------------------------------------------------
69 * File: unqlite.h
70 * MD5: d26e9847c6587edbbb183d0115d172cb
71 * ----------------------------------------------------------
72 */
73/* This file was automatically generated. Do not edit (Except for compile time directives)! */
74#ifndef _UNQLITE_H_
75#define _UNQLITE_H_
76/*
77 * Symisc UnQLite: An Embeddable NoSQL (Post Modern) Database Engine.
78 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
79 * Version 1.1.6
80 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
81 * please contact Symisc Systems via:
82 * legal@symisc.net
83 * licensing@symisc.net
84 * contact@symisc.net
85 * or visit:
86 * http://unqlite.org/licensing.html
87 */
88/*
89 * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
90 * All rights reserved.
91 *
92 * Redistribution and use in source and binary forms, with or without
93 * modification, are permitted provided that the following conditions
94 * are met:
95 * 1. Redistributions of source code must retain the above copyright
96 * notice, this list of conditions and the following disclaimer.
97 * 2. Redistributions in binary form must reproduce the above copyright
98 * notice, this list of conditions and the following disclaimer in the
99 * documentation and/or other materials provided with the distribution.
100 *
101 * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
102 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
103 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
104 * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS
105 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
106 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
107 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
108 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
109 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
110 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
111 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
112 */
113 /* $SymiscID: unqlite.h v1.1 UNIX|WIN32/64 2012-11-02 02:10 stable <chm@symisc.net> $ */
114#include <stdarg.h> /* needed for the definition of va_list */
115/*
116 * Compile time engine version, signature, identification in the symisc source tree
117 * and copyright notice.
118 * Each macro have an equivalent C interface associated with it that provide the same
119 * information but are associated with the library instead of the header file.
120 * Refer to [unqlite_lib_version()], [unqlite_lib_signature()], [unqlite_lib_ident()] and
121 * [unqlite_lib_copyright()] for more information.
122 */
123/*
124 * The UNQLITE_VERSION C preprocessor macroevaluates to a string literal
125 * that is the unqlite version in the format "X.Y.Z" where X is the major
126 * version number and Y is the minor version number and Z is the release
127 * number.
128 */
129#define UNQLITE_VERSION "1.1.6"
130/*
131 * The UNQLITE_VERSION_NUMBER C preprocessor macro resolves to an integer
132 * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same
133 * numbers used in [UNQLITE_VERSION].
134 */
135#define UNQLITE_VERSION_NUMBER 1001006
136/*
137 * The UNQLITE_SIG C preprocessor macro evaluates to a string
138 * literal which is the public signature of the unqlite engine.
139 * This signature could be included for example in a host-application
140 * generated Server MIME header as follows:
141 * Server: YourWebServer/x.x unqlite/x.x.x \r\n
142 */
143#define UNQLITE_SIG "unqlite/1.1.6"
144/*
145 * UnQLite identification in the Symisc source tree:
146 * Each particular check-in of a particular software released
147 * by symisc systems have an unique identifier associated with it.
148 * This macro hold the one associated with unqlite.
149 */
150#define UNQLITE_IDENT "unqlite:b172a1e2c3f62fb35c8e1fb2795121f82356cad6"
151/*
152 * Copyright notice.
153 * If you have any questions about the licensing situation, please
154 * visit http://unqlite.org/licensing.html
155 * or contact Symisc Systems via:
156 * legal@symisc.net
157 * licensing@symisc.net
158 * contact@symisc.net
159 */
160#define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine <chm@symisc.net>] 2012-2013, http://unqlite.org/"
161/* Make sure we can call this stuff from C++ */
162#ifdef __cplusplus
163extern "C" {
164#endif
165/* Forward declaration to public objects */
166typedef struct unqlite_io_methods unqlite_io_methods;
167typedef struct unqlite_kv_methods unqlite_kv_methods;
168typedef struct unqlite_kv_engine unqlite_kv_engine;
169typedef struct jx9_io_stream unqlite_io_stream;
170typedef struct jx9_context unqlite_context;
171typedef struct jx9_value unqlite_value;
172typedef struct unqlite_vfs unqlite_vfs;
173typedef struct unqlite_vm unqlite_vm;
174typedef struct unqlite unqlite;
175/*
176 * ------------------------------
177 * Compile time directives
178 * ------------------------------
179 * For most purposes, UnQLite can be built just fine using the default compilation options.
180 * However, if required, the compile-time options documented below can be used to omit UnQLite
181 * features (resulting in a smaller compiled library size) or to change the default values
182 * of some parameters.
183 * Every effort has been made to ensure that the various combinations of compilation options
184 * work harmoniously and produce a working library.
185 *
186 * UNQLITE_ENABLE_THREADS
187 * This option controls whether or not code is included in UnQLite to enable it to operate
188 * safely in a multithreaded environment. The default is not. All mutexing code is omitted
189 * and it is unsafe to use UnQLite in a multithreaded program. When compiled with the
190 * UNQLITE_ENABLE_THREADS directive enabled, UnQLite can be used in a multithreaded program
191 * and it is safe to share the same virtual machine and engine handle between two or more threads.
192 * The value of UNQLITE_ENABLE_THREADS can be determined at run-time using the unqlite_lib_is_threadsafe()
193 * interface.
194 * When UnQLite has been compiled with threading support then the threading mode can be altered
195 * at run-time using the unqlite_lib_config() interface together with one of these verbs:
196 * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE
197 * UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI
198 * Platforms others than Windows and UNIX systems must install their own mutex subsystem via
199 * unqlite_lib_config() with a configuration verb set to UNQLITE_LIB_CONFIG_USER_MUTEX.
200 * Otherwise the library is not threadsafe.
201 * Note that you must link UnQLite with the POSIX threads library under UNIX systems (i.e: -lpthread).
202 *
203 * Options To Omit/Enable Features
204 *
205 * The following options can be used to reduce the size of the compiled library by omitting optional
206 * features. This is probably only useful in embedded systems where space is especially tight, as even
207 * with all features included the UnQLite library is relatively small. Don't forget to tell your
208 * compiler to optimize for binary size! (the -Os option if using GCC). Telling your compiler
209 * to optimize for size usually has a much larger impact on library footprint than employing
210 * any of these compile-time options.
211 *
212 * JX9_DISABLE_BUILTIN_FUNC
213 * Jx9 is shipped with more than 312 built-in functions suitable for most purposes like
214 * string and INI processing, ZIP extracting, Base64 encoding/decoding, JSON encoding/decoding
215 * and so forth.
216 * If this directive is enabled, then all built-in Jx9 functions are omitted from the build.
217 * Note that special functions such as db_create(), db_store(), db_fetch(), etc. are not omitted
218 * from the build and are not affected by this directive.
219 *
220 * JX9_ENABLE_MATH_FUNC
221 * If this directive is enabled, built-in math functions such as sqrt(), abs(), log(), ceil(), etc.
222 * are included in the build. Note that you may need to link UnQLite with the math library in same
223 * Linux/BSD flavor (i.e: -lm).
224 *
225 * JX9_DISABLE_DISK_IO
226 * If this directive is enabled, built-in VFS functions such as chdir(), mkdir(), chroot(), unlink(),
227 * sleep(), etc. are omitted from the build.
228 *
229 * UNQLITE_ENABLE_JX9_HASH_IO
230 * If this directive is enabled, built-in hash functions such as md5(), sha1(), md5_file(), crc32(), etc.
231 * are included in the build.
232 */
233/* Symisc public definitions */
234#if !defined(SYMISC_STANDARD_DEFS)
235#define SYMISC_STANDARD_DEFS
236#if defined (_WIN32) || defined (WIN32) || defined(__MINGW32__) || defined (_MSC_VER) || defined (_WIN32_WCE)
237/* Windows Systems */
238#if !defined(__WINNT__)
239#define __WINNT__
240#endif
241/*
242 * Determine if we are dealing with WindowsCE - which has a much
243 * reduced API.
244 */
245#if defined(_WIN32_WCE)
246#ifndef __WIN_CE__
247#define __WIN_CE__
248#endif /* __WIN_CE__ */
249#endif /* _WIN32_WCE */
250#else
251/*
252 * By default we will assume that we are compiling on a UNIX systems.
253 * Otherwise the OS_OTHER directive must be defined.
254 */
255#if !defined(OS_OTHER)
256#if !defined(__UNIXES__)
257#define __UNIXES__
258#endif /* __UNIXES__ */
259#else
260#endif /* OS_OTHER */
261#endif /* __WINNT__/__UNIXES__ */
262#if defined(_MSC_VER) || defined(__BORLANDC__)
263typedef signed __int64 sxi64; /* 64 bits(8 bytes) signed int64 */
264typedef unsigned __int64 sxu64; /* 64 bits(8 bytes) unsigned int64 */
265#else
266typedef signed long long int sxi64; /* 64 bits(8 bytes) signed int64 */
267typedef unsigned long long int sxu64; /* 64 bits(8 bytes) unsigned int64 */
268#endif /* _MSC_VER */
269/* Signature of the consumer routine */
270typedef int (*ProcConsumer)(const void *, unsigned int, void *);
271/* Forward reference */
272typedef struct SyMutexMethods SyMutexMethods;
273typedef struct SyMemMethods SyMemMethods;
274typedef struct SyString SyString;
275typedef struct syiovec syiovec;
276typedef struct SyMutex SyMutex;
277typedef struct Sytm Sytm;
278/* Scatter and gather array. */
279struct syiovec
280{
281#if defined (__WINNT__)
282 /* Same fields type and offset as WSABUF structure defined one winsock2 header */
283 unsigned long nLen;
284 char *pBase;
285#else
286 void *pBase;
287 unsigned long nLen;
288#endif
289};
290struct SyString
291{
292 const char *zString; /* Raw string (may not be null terminated) */
293 unsigned int nByte; /* Raw string length */
294};
295/* Time structure. */
296struct Sytm
297{
298 int tm_sec; /* seconds (0 - 60) */
299 int tm_min; /* minutes (0 - 59) */
300 int tm_hour; /* hours (0 - 23) */
301 int tm_mday; /* day of month (1 - 31) */
302 int tm_mon; /* month of year (0 - 11) */
303 int tm_year; /* year + 1900 */
304 int tm_wday; /* day of week (Sunday = 0) */
305 int tm_yday; /* day of year (0 - 365) */
306 int tm_isdst; /* is summer time in effect? */
307 char *tm_zone; /* abbreviation of timezone name */
308 long tm_gmtoff; /* offset from UTC in seconds */
309};
310/* Convert a tm structure (struct tm *) found in <time.h> to a Sytm structure */
311#define STRUCT_TM_TO_SYTM(pTM, pSYTM) \
312 (pSYTM)->tm_hour = (pTM)->tm_hour;\
313 (pSYTM)->tm_min = (pTM)->tm_min;\
314 (pSYTM)->tm_sec = (pTM)->tm_sec;\
315 (pSYTM)->tm_mon = (pTM)->tm_mon;\
316 (pSYTM)->tm_mday = (pTM)->tm_mday;\
317 (pSYTM)->tm_year = (pTM)->tm_year + 1900;\
318 (pSYTM)->tm_yday = (pTM)->tm_yday;\
319 (pSYTM)->tm_wday = (pTM)->tm_wday;\
320 (pSYTM)->tm_isdst = (pTM)->tm_isdst;\
321 (pSYTM)->tm_gmtoff = 0;\
322 (pSYTM)->tm_zone = 0;
323
324/* Convert a SYSTEMTIME structure (LPSYSTEMTIME: Windows Systems only ) to a Sytm structure */
325#define SYSTEMTIME_TO_SYTM(pSYSTIME, pSYTM) \
326 (pSYTM)->tm_hour = (pSYSTIME)->wHour;\
327 (pSYTM)->tm_min = (pSYSTIME)->wMinute;\
328 (pSYTM)->tm_sec = (pSYSTIME)->wSecond;\
329 (pSYTM)->tm_mon = (pSYSTIME)->wMonth - 1;\
330 (pSYTM)->tm_mday = (pSYSTIME)->wDay;\
331 (pSYTM)->tm_year = (pSYSTIME)->wYear;\
332 (pSYTM)->tm_yday = 0;\
333 (pSYTM)->tm_wday = (pSYSTIME)->wDayOfWeek;\
334 (pSYTM)->tm_gmtoff = 0;\
335 (pSYTM)->tm_isdst = -1;\
336 (pSYTM)->tm_zone = 0;
337
338/* Dynamic memory allocation methods. */
339struct SyMemMethods
340{
341 void * (*xAlloc)(unsigned int); /* [Required:] Allocate a memory chunk */
342 void * (*xRealloc)(void *, unsigned int); /* [Required:] Re-allocate a memory chunk */
343 void (*xFree)(void *); /* [Required:] Release a memory chunk */
344 unsigned int (*xChunkSize)(void *); /* [Optional:] Return chunk size */
345 int (*xInit)(void *); /* [Optional:] Initialization callback */
346 void (*xRelease)(void *); /* [Optional:] Release callback */
347 void *pUserData; /* [Optional:] First argument to xInit() and xRelease() */
348};
349/* Out of memory callback signature. */
350typedef int (*ProcMemError)(void *);
351/* Mutex methods. */
352struct SyMutexMethods
353{
354 int (*xGlobalInit)(void); /* [Optional:] Global mutex initialization */
355 void (*xGlobalRelease)(void); /* [Optional:] Global Release callback () */
356 SyMutex * (*xNew)(int); /* [Required:] Request a new mutex */
357 void (*xRelease)(SyMutex *); /* [Optional:] Release a mutex */
358 void (*xEnter)(SyMutex *); /* [Required:] Enter mutex */
359 int (*xTryEnter)(SyMutex *); /* [Optional:] Try to enter a mutex */
360 void (*xLeave)(SyMutex *); /* [Required:] Leave a locked mutex */
361};
362#if defined (_MSC_VER) || defined (__MINGW32__) || defined (__GNUC__) && defined (__declspec)
363#define SX_APIIMPORT __declspec(dllimport)
364#define SX_APIEXPORT __declspec(dllexport)
365#else
366#define SX_APIIMPORT
367#define SX_APIEXPORT
368#endif
369/* Standard return values from Symisc public interfaces */
370#define SXRET_OK 0 /* Not an error */
371#define SXERR_MEM (-1) /* Out of memory */
372#define SXERR_IO (-2) /* IO error */
373#define SXERR_EMPTY (-3) /* Empty field */
374#define SXERR_LOCKED (-4) /* Locked operation */
375#define SXERR_ORANGE (-5) /* Out of range value */
376#define SXERR_NOTFOUND (-6) /* Item not found */
377#define SXERR_LIMIT (-7) /* Limit reached */
378#define SXERR_MORE (-8) /* Need more input */
379#define SXERR_INVALID (-9) /* Invalid parameter */
380#define SXERR_ABORT (-10) /* User callback request an operation abort */
381#define SXERR_EXISTS (-11) /* Item exists */
382#define SXERR_SYNTAX (-12) /* Syntax error */
383#define SXERR_UNKNOWN (-13) /* Unknown error */
384#define SXERR_BUSY (-14) /* Busy operation */
385#define SXERR_OVERFLOW (-15) /* Stack or buffer overflow */
386#define SXERR_WILLBLOCK (-16) /* Operation will block */
387#define SXERR_NOTIMPLEMENTED (-17) /* Operation not implemented */
388#define SXERR_EOF (-18) /* End of input */
389#define SXERR_PERM (-19) /* Permission error */
390#define SXERR_NOOP (-20) /* No-op */
391#define SXERR_FORMAT (-21) /* Invalid format */
392#define SXERR_NEXT (-22) /* Not an error */
393#define SXERR_OS (-23) /* System call return an error */
394#define SXERR_CORRUPT (-24) /* Corrupted pointer */
395#define SXERR_CONTINUE (-25) /* Not an error: Operation in progress */
396#define SXERR_NOMATCH (-26) /* No match */
397#define SXERR_RESET (-27) /* Operation reset */
398#define SXERR_DONE (-28) /* Not an error */
399#define SXERR_SHORT (-29) /* Buffer too short */
400#define SXERR_PATH (-30) /* Path error */
401#define SXERR_TIMEOUT (-31) /* Timeout */
402#define SXERR_BIG (-32) /* Too big for processing */
403#define SXERR_RETRY (-33) /* Retry your call */
404#define SXERR_IGNORE (-63) /* Ignore */
405#endif /* SYMISC_PUBLIC_DEFS */
406/*
407 * Marker for exported interfaces.
408 */
409#define UNQLITE_APIEXPORT SX_APIEXPORT
410/*
411 * If compiling for a processor that lacks floating point
412 * support, substitute integer for floating-point.
413 */
414#ifdef UNQLITE_OMIT_FLOATING_POINT
415typedef sxi64 uqlite_real;
416#else
417typedef double unqlite_real;
418#endif
419typedef sxi64 unqlite_int64;
420/* Standard UnQLite return values */
421#define UNQLITE_OK SXRET_OK /* Successful result */
422/* Beginning of error codes */
423#define UNQLITE_NOMEM SXERR_MEM /* Out of memory */
424#define UNQLITE_ABORT SXERR_ABORT /* Another thread have released this instance */
425#define UNQLITE_IOERR SXERR_IO /* IO error */
426#define UNQLITE_CORRUPT SXERR_CORRUPT /* Corrupt pointer */
427#define UNQLITE_LOCKED SXERR_LOCKED /* Forbidden Operation */
428#define UNQLITE_BUSY SXERR_BUSY /* The database file is locked */
429#define UNQLITE_DONE SXERR_DONE /* Operation done */
430#define UNQLITE_PERM SXERR_PERM /* Permission error */
431#define UNQLITE_NOTIMPLEMENTED SXERR_NOTIMPLEMENTED /* Method not implemented by the underlying Key/Value storage engine */
432#define UNQLITE_NOTFOUND SXERR_NOTFOUND /* No such record */
433#define UNQLITE_NOOP SXERR_NOOP /* No such method */
434#define UNQLITE_INVALID SXERR_INVALID /* Invalid parameter */
435#define UNQLITE_EOF SXERR_EOF /* End Of Input */
436#define UNQLITE_UNKNOWN SXERR_UNKNOWN /* Unknown configuration option */
437#define UNQLITE_LIMIT SXERR_LIMIT /* Database limit reached */
438#define UNQLITE_EXISTS SXERR_EXISTS /* Record exists */
439#define UNQLITE_EMPTY SXERR_EMPTY /* Empty record */
440#define UNQLITE_COMPILE_ERR (-70) /* Compilation error */
441#define UNQLITE_VM_ERR (-71) /* Virtual machine error */
442#define UNQLITE_FULL (-73) /* Full database (unlikely) */
443#define UNQLITE_CANTOPEN (-74) /* Unable to open the database file */
444#define UNQLITE_READ_ONLY (-75) /* Read only Key/Value storage engine */
445#define UNQLITE_LOCKERR (-76) /* Locking protocol error */
446/* end-of-error-codes */
447/*
448 * Database Handle Configuration Commands.
449 *
450 * The following set of constants are the available configuration verbs that can
451 * be used by the host-application to configure an UnQLite database handle.
452 * These constants must be passed as the second argument to [unqlite_config()].
453 *
454 * Each options require a variable number of arguments.
455 * The [unqlite_config()] interface will return UNQLITE_OK on success, any other
456 * return value indicates failure.
457 * For a full discussion on the configuration verbs and their expected
458 * parameters, please refer to this page:
459 * http://unqlite.org/c_api/unqlite_config.html
460 */
461#define UNQLITE_CONFIG_JX9_ERR_LOG 1 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
462#define UNQLITE_CONFIG_MAX_PAGE_CACHE 2 /* ONE ARGUMENT: int nMaxPage */
463#define UNQLITE_CONFIG_ERR_LOG 3 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
464#define UNQLITE_CONFIG_KV_ENGINE 4 /* ONE ARGUMENT: const char *zKvName */
465#define UNQLITE_CONFIG_DISABLE_AUTO_COMMIT 5 /* NO ARGUMENTS */
466#define UNQLITE_CONFIG_GET_KV_NAME 6 /* ONE ARGUMENT: const char **pzPtr */
467/*
468 * UnQLite/Jx9 Virtual Machine Configuration Commands.
469 *
470 * The following set of constants are the available configuration verbs that can
471 * be used by the host-application to configure the Jx9 (Via UnQLite) Virtual machine.
472 * These constants must be passed as the second argument to the [unqlite_vm_config()]
473 * interface.
474 * Each options require a variable number of arguments.
475 * The [unqlite_vm_config()] interface will return UNQLITE_OK on success, any other return
476 * value indicates failure.
477 * There are many options but the most importants are: UNQLITE_VM_CONFIG_OUTPUT which install
478 * a VM output consumer callback, UNQLITE_VM_CONFIG_HTTP_REQUEST which parse and register
479 * a HTTP request and UNQLITE_VM_CONFIG_ARGV_ENTRY which populate the $argv array.
480 * For a full discussion on the configuration verbs and their expected parameters, please
481 * refer to this page:
482 * http://unqlite.org/c_api/unqlite_vm_config.html
483 */
484#define UNQLITE_VM_CONFIG_OUTPUT 1 /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
485#define UNQLITE_VM_CONFIG_IMPORT_PATH 2 /* ONE ARGUMENT: const char *zIncludePath */
486#define UNQLITE_VM_CONFIG_ERR_REPORT 3 /* NO ARGUMENTS: Report all run-time errors in the VM output */
487#define UNQLITE_VM_CONFIG_RECURSION_DEPTH 4 /* ONE ARGUMENT: int nMaxDepth */
488#define UNQLITE_VM_OUTPUT_LENGTH 5 /* ONE ARGUMENT: unsigned int *pLength */
489#define UNQLITE_VM_CONFIG_CREATE_VAR 6 /* TWO ARGUMENTS: const char *zName, unqlite_value *pValue */
490#define UNQLITE_VM_CONFIG_HTTP_REQUEST 7 /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
491#define UNQLITE_VM_CONFIG_SERVER_ATTR 8 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
492#define UNQLITE_VM_CONFIG_ENV_ATTR 9 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
493#define UNQLITE_VM_CONFIG_EXEC_VALUE 10 /* ONE ARGUMENT: unqlite_value **ppValue */
494#define UNQLITE_VM_CONFIG_IO_STREAM 11 /* ONE ARGUMENT: const unqlite_io_stream *pStream */
495#define UNQLITE_VM_CONFIG_ARGV_ENTRY 12 /* ONE ARGUMENT: const char *zValue */
496#define UNQLITE_VM_CONFIG_EXTRACT_OUTPUT 13 /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
497/*
498 * Storage engine configuration commands.
499 *
500 * The following set of constants are the available configuration verbs that can
501 * be used by the host-application to configure the underlying storage engine (i.e Hash, B+tree, R+tree).
502 * These constants must be passed as the first argument to [unqlite_kv_config()].
503 * Each options require a variable number of arguments.
504 * The [unqlite_kv_config()] interface will return UNQLITE_OK on success, any other return
505 * value indicates failure.
506 * For a full discussion on the configuration verbs and their expected parameters, please
507 * refer to this page:
508 * http://unqlite.org/c_api/unqlite_kv_config.html
509 */
510#define UNQLITE_KV_CONFIG_HASH_FUNC 1 /* ONE ARGUMENT: unsigned int (*xHash)(const void *,unsigned int) */
511#define UNQLITE_KV_CONFIG_CMP_FUNC 2 /* ONE ARGUMENT: int (*xCmp)(const void *,const void *,unsigned int) */
512/*
513 * Global Library Configuration Commands.
514 *
515 * The following set of constants are the available configuration verbs that can
516 * be used by the host-application to configure the whole library.
517 * These constants must be passed as the first argument to [unqlite_lib_config()].
518 *
519 * Each options require a variable number of arguments.
520 * The [unqlite_lib_config()] interface will return UNQLITE_OK on success, any other return
521 * value indicates failure.
522 * Notes:
523 * The default configuration is recommended for most applications and so the call to
524 * [unqlite_lib_config()] is usually not necessary. It is provided to support rare
525 * applications with unusual needs.
526 * The [unqlite_lib_config()] interface is not threadsafe. The application must insure that
527 * no other [unqlite_*()] interfaces are invoked by other threads while [unqlite_lib_config()]
528 * is running. Furthermore, [unqlite_lib_config()] may only be invoked prior to library
529 * initialization using [unqlite_lib_init()] or [unqlite_init()] or after shutdown
530 * by [unqlite_lib_shutdown()]. If [unqlite_lib_config()] is called after [unqlite_lib_init()]
531 * or [unqlite_init()] and before [unqlite_lib_shutdown()] then it will return UNQLITE_LOCKED.
532 * For a full discussion on the configuration verbs and their expected parameters, please
533 * refer to this page:
534 * http://unqlite.org/c_api/unqlite_lib.html
535 */
536#define UNQLITE_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */
537#define UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
538#define UNQLITE_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */
539#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */
540#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */
541#define UNQLITE_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const unqlite_vfs *pVfs */
542#define UNQLITE_LIB_CONFIG_STORAGE_ENGINE 7 /* ONE ARGUMENT: unqlite_kv_methods *pStorage */
543#define UNQLITE_LIB_CONFIG_PAGE_SIZE 8 /* ONE ARGUMENT: int iPageSize */
544/*
545 * These bit values are intended for use in the 3rd parameter to the [unqlite_open()] interface
546 * and in the 4th parameter to the xOpen method of the [unqlite_vfs] object.
547 */
548#define UNQLITE_OPEN_READONLY 0x00000001 /* Read only mode. Ok for [unqlite_open] */
549#define UNQLITE_OPEN_READWRITE 0x00000002 /* Ok for [unqlite_open] */
550#define UNQLITE_OPEN_CREATE 0x00000004 /* Ok for [unqlite_open] */
551#define UNQLITE_OPEN_EXCLUSIVE 0x00000008 /* VFS only */
552#define UNQLITE_OPEN_TEMP_DB 0x00000010 /* VFS only */
553#define UNQLITE_OPEN_NOMUTEX 0x00000020 /* Ok for [unqlite_open] */
554#define UNQLITE_OPEN_OMIT_JOURNALING 0x00000040 /* Omit journaling for this database. Ok for [unqlite_open] */
555#define UNQLITE_OPEN_IN_MEMORY 0x00000080 /* An in memory database. Ok for [unqlite_open]*/
556#define UNQLITE_OPEN_MMAP 0x00000100 /* Obtain a memory view of the whole file. Ok for [unqlite_open] */
557/*
558 * Synchronization Type Flags
559 *
560 * When UnQLite invokes the xSync() method of an [unqlite_io_methods] object it uses
561 * a combination of these integer values as the second argument.
562 *
563 * When the UNQLITE_SYNC_DATAONLY flag is used, it means that the sync operation only
564 * needs to flush data to mass storage. Inode information need not be flushed.
565 * If the lower four bits of the flag equal UNQLITE_SYNC_NORMAL, that means to use normal
566 * fsync() semantics. If the lower four bits equal UNQLITE_SYNC_FULL, that means to use
567 * Mac OS X style fullsync instead of fsync().
568 */
569#define UNQLITE_SYNC_NORMAL 0x00002
570#define UNQLITE_SYNC_FULL 0x00003
571#define UNQLITE_SYNC_DATAONLY 0x00010
572/*
573 * File Locking Levels
574 *
575 * UnQLite uses one of these integer values as the second
576 * argument to calls it makes to the xLock() and xUnlock() methods
577 * of an [unqlite_io_methods] object.
578 */
579#define UNQLITE_LOCK_NONE 0
580#define UNQLITE_LOCK_SHARED 1
581#define UNQLITE_LOCK_RESERVED 2
582#define UNQLITE_LOCK_PENDING 3
583#define UNQLITE_LOCK_EXCLUSIVE 4
584/*
585 * CAPIREF: OS Interface: Open File Handle
586 *
587 * An [unqlite_file] object represents an open file in the [unqlite_vfs] OS interface
588 * layer.
589 * Individual OS interface implementations will want to subclass this object by appending
590 * additional fields for their own use. The pMethods entry is a pointer to an
591 * [unqlite_io_methods] object that defines methods for performing
592 * I/O operations on the open file.
593*/
594typedef struct unqlite_file unqlite_file;
595struct unqlite_file {
596 const unqlite_io_methods *pMethods; /* Methods for an open file. MUST BE FIRST */
597};
598/*
599 * CAPIREF: OS Interface: File Methods Object
600 *
601 * Every file opened by the [unqlite_vfs] xOpen method populates an
602 * [unqlite_file] object (or, more commonly, a subclass of the
603 * [unqlite_file] object) with a pointer to an instance of this object.
604 * This object defines the methods used to perform various operations
605 * against the open file represented by the [unqlite_file] object.
606 *
607 * If the xOpen method sets the unqlite_file.pMethods element
608 * to a non-NULL pointer, then the unqlite_io_methods.xClose method
609 * may be invoked even if the xOpen reported that it failed. The
610 * only way to prevent a call to xClose following a failed xOpen
611 * is for the xOpen to set the unqlite_file.pMethods element to NULL.
612 *
613 * The flags argument to xSync may be one of [UNQLITE_SYNC_NORMAL] or
614 * [UNQLITE_SYNC_FULL]. The first choice is the normal fsync().
615 * The second choice is a Mac OS X style fullsync. The [UNQLITE_SYNC_DATAONLY]
616 * flag may be ORed in to indicate that only the data of the file
617 * and not its inode needs to be synced.
618 *
619 * The integer values to xLock() and xUnlock() are one of
620 *
621 * UNQLITE_LOCK_NONE
622 * UNQLITE_LOCK_SHARED
623 * UNQLITE_LOCK_RESERVED
624 * UNQLITE_LOCK_PENDING
625 * UNQLITE_LOCK_EXCLUSIVE
626 *
627 * xLock() increases the lock. xUnlock() decreases the lock.
628 * The xCheckReservedLock() method checks whether any database connection,
629 * either in this process or in some other process, is holding a RESERVED,
630 * PENDING, or EXCLUSIVE lock on the file. It returns true if such a lock exists
631 * and false otherwise.
632 *
633 * The xSectorSize() method returns the sector size of the device that underlies
634 * the file. The sector size is the minimum write that can be performed without
635 * disturbing other bytes in the file.
636 *
637 */
638struct unqlite_io_methods {
639 int iVersion; /* Structure version number (currently 1) */
640 int (*xClose)(unqlite_file*);
641 int (*xRead)(unqlite_file*, void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
642 int (*xWrite)(unqlite_file*, const void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
643 int (*xTruncate)(unqlite_file*, unqlite_int64 size);
644 int (*xSync)(unqlite_file*, int flags);
645 int (*xFileSize)(unqlite_file*, unqlite_int64 *pSize);
646 int (*xLock)(unqlite_file*, int);
647 int (*xUnlock)(unqlite_file*, int);
648 int (*xCheckReservedLock)(unqlite_file*, int *pResOut);
649 int (*xSectorSize)(unqlite_file*);
650};
651/*
652 * CAPIREF: OS Interface Object
653 *
654 * An instance of the unqlite_vfs object defines the interface between
655 * the UnQLite core and the underlying operating system. The "vfs"
656 * in the name of the object stands for "Virtual File System".
657 *
658 * Only a single vfs can be registered within the UnQLite core.
659 * Vfs registration is done using the [unqlite_lib_config()] interface
660 * with a configuration verb set to UNQLITE_LIB_CONFIG_VFS.
661 * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users
662 * does not have to worry about registering and installing a vfs since UnQLite
663 * come with a built-in vfs for these platforms that implements most the methods
664 * defined below.
665 *
666 * Clients running on exotic systems (ie: Other than Windows and UNIX systems)
667 * must register their own vfs in order to be able to use the UnQLite library.
668 *
669 * The value of the iVersion field is initially 1 but may be larger in
670 * future versions of UnQLite.
671 *
672 * The szOsFile field is the size of the subclassed [unqlite_file] structure
673 * used by this VFS. mxPathname is the maximum length of a pathname in this VFS.
674 *
675 * At least szOsFile bytes of memory are allocated by UnQLite to hold the [unqlite_file]
676 * structure passed as the third argument to xOpen. The xOpen method does not have to
677 * allocate the structure; it should just fill it in. Note that the xOpen method must
678 * set the unqlite_file.pMethods to either a valid [unqlite_io_methods] object or to NULL.
679 * xOpen must do this even if the open fails. UnQLite expects that the unqlite_file.pMethods
680 * element will be valid after xOpen returns regardless of the success or failure of the
681 * xOpen call.
682 *
683 */
684struct unqlite_vfs {
685 const char *zName; /* Name of this virtual file system [i.e: Windows, UNIX, etc.] */
686 int iVersion; /* Structure version number (currently 1) */
687 int szOsFile; /* Size of subclassed unqlite_file */
688 int mxPathname; /* Maximum file pathname length */
689 int (*xOpen)(unqlite_vfs*, const char *zName, unqlite_file*,unsigned int flags);
690 int (*xDelete)(unqlite_vfs*, const char *zName, int syncDir);
691 int (*xAccess)(unqlite_vfs*, const char *zName, int flags, int *pResOut);
692 int (*xFullPathname)(unqlite_vfs*, const char *zName,int buf_len,char *zBuf);
693 int (*xTmpDir)(unqlite_vfs*,char *zBuf,int buf_len);
694 int (*xSleep)(unqlite_vfs*, int microseconds);
695 int (*xCurrentTime)(unqlite_vfs*,Sytm *pOut);
696 int (*xGetLastError)(unqlite_vfs*, int, char *);
697};
698/*
699 * Flags for the xAccess VFS method
700 *
701 * These integer constants can be used as the third parameter to
702 * the xAccess method of an [unqlite_vfs] object. They determine
703 * what kind of permissions the xAccess method is looking for.
704 * With UNQLITE_ACCESS_EXISTS, the xAccess method
705 * simply checks whether the file exists.
706 * With UNQLITE_ACCESS_READWRITE, the xAccess method
707 * checks whether the named directory is both readable and writable
708 * (in other words, if files can be added, removed, and renamed within
709 * the directory).
710 * The UNQLITE_ACCESS_READWRITE constant is currently used only by the
711 * [temp_store_directory pragma], though this could change in a future
712 * release of UnQLite.
713 * With UNQLITE_ACCESS_READ, the xAccess method
714 * checks whether the file is readable. The UNQLITE_ACCESS_READ constant is
715 * currently unused, though it might be used in a future release of
716 * UnQLite.
717 */
718#define UNQLITE_ACCESS_EXISTS 0
719#define UNQLITE_ACCESS_READWRITE 1
720#define UNQLITE_ACCESS_READ 2
721/*
722 * The type used to represent a page number. The first page in a file
723 * is called page 1. 0 is used to represent "not a page".
724 * A page number is an unsigned 64-bit integer.
725 */
726typedef sxu64 pgno;
727/*
728 * A database disk page is represented by an instance
729 * of the follwoing structure.
730 */
731typedef struct unqlite_page unqlite_page;
732struct unqlite_page
733{
734 unsigned char *zData; /* Content of this page */
735 void *pUserData; /* Extra content */
736 pgno pgno; /* Page number for this page */
737};
738/*
739 * UnQLite handle to the underlying Key/Value Storage Engine (See below).
740 */
741typedef void * unqlite_kv_handle;
742/*
743 * UnQLite pager IO methods.
744 *
745 * An instance of the following structure define the exported methods of the UnQLite pager
746 * to the underlying Key/Value storage engine.
747 */
748typedef struct unqlite_kv_io unqlite_kv_io;
749struct unqlite_kv_io
750{
751 unqlite_kv_handle pHandle; /* UnQLite handle passed as the first parameter to the
752 * method defined below.
753 */
754 unqlite_kv_methods *pMethods; /* Underlying storage engine */
755 /* Pager methods */
756 int (*xGet)(unqlite_kv_handle,pgno,unqlite_page **);
757 int (*xLookup)(unqlite_kv_handle,pgno,unqlite_page **);
758 int (*xNew)(unqlite_kv_handle,unqlite_page **);
759 int (*xWrite)(unqlite_page *);
760 int (*xDontWrite)(unqlite_page *);
761 int (*xDontJournal)(unqlite_page *);
762 int (*xDontMkHot)(unqlite_page *);
763 int (*xPageRef)(unqlite_page *);
764 int (*xPageUnref)(unqlite_page *);
765 int (*xPageSize)(unqlite_kv_handle);
766 int (*xReadOnly)(unqlite_kv_handle);
767 unsigned char * (*xTmpPage)(unqlite_kv_handle);
768 void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *));
769 void (*xSetReload)(unqlite_kv_handle,void (*xPageReload)(void *));
770 void (*xErr)(unqlite_kv_handle,const char *);
771};
772/*
773 * Key/Value Storage Engine Cursor Object
774 *
775 * An instance of a subclass of the following object defines a cursor
776 * used to scan through a key-value storage engine.
777 */
778typedef struct unqlite_kv_cursor unqlite_kv_cursor;
779struct unqlite_kv_cursor
780{
781 unqlite_kv_engine *pStore; /* Must be first */
782 /* Subclasses will typically add additional fields */
783};
784/*
785 * Possible seek positions.
786 */
787#define UNQLITE_CURSOR_MATCH_EXACT 1
788#define UNQLITE_CURSOR_MATCH_LE 2
789#define UNQLITE_CURSOR_MATCH_GE 3
790/*
791 * Key/Value Storage Engine.
792 *
793 * A Key-Value storage engine is defined by an instance of the following
794 * object.
795 * UnQLite works with run-time interchangeable storage engines (i.e. Hash, B+Tree, R+Tree, LSM, etc.).
796 * The storage engine works with key/value pairs where both the key
797 * and the value are byte arrays of arbitrary length and with no restrictions on content.
798 * UnQLite come with two built-in KV storage engine: A Virtual Linear Hash (VLH) storage
799 * engine is used for persistent on-disk databases with O(1) lookup time and an in-memory
800 * hash-table or Red-black tree storage engine is used for in-memory databases.
801 * Future versions of UnQLite might add other built-in storage engines (i.e. LSM).
802 * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
803 * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
804 */
805struct unqlite_kv_engine
806{
807 const unqlite_kv_io *pIo; /* IO methods: MUST be first */
808 /* Subclasses will typically add additional fields */
809};
810/*
811 * Key/Value Storage Engine Virtual Method Table.
812 *
813 * Key/Value storage engine methods is defined by an instance of the following
814 * object.
815 * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
816 * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
817 */
818struct unqlite_kv_methods
819{
820 const char *zName; /* Storage engine name [i.e. Hash, B+tree, LSM, R-tree, Mem, etc.]*/
821 int szKv; /* 'unqlite_kv_engine' subclass size */
822 int szCursor; /* 'unqlite_kv_cursor' subclass size */
823 int iVersion; /* Structure version, currently 1 */
824 /* Storage engine methods */
825 int (*xInit)(unqlite_kv_engine *,int iPageSize);
826 void (*xRelease)(unqlite_kv_engine *);
827 int (*xConfig)(unqlite_kv_engine *,int op,va_list ap);
828 int (*xOpen)(unqlite_kv_engine *,pgno);
829 int (*xReplace)(
830 unqlite_kv_engine *,
831 const void *pKey,int nKeyLen,
832 const void *pData,unqlite_int64 nDataLen
833 );
834 int (*xAppend)(
835 unqlite_kv_engine *,
836 const void *pKey,int nKeyLen,
837 const void *pData,unqlite_int64 nDataLen
838 );
839 void (*xCursorInit)(unqlite_kv_cursor *);
840 int (*xSeek)(unqlite_kv_cursor *,const void *pKey,int nByte,int iPos); /* Mandatory */
841 int (*xFirst)(unqlite_kv_cursor *);
842 int (*xLast)(unqlite_kv_cursor *);
843 int (*xValid)(unqlite_kv_cursor *);
844 int (*xNext)(unqlite_kv_cursor *);
845 int (*xPrev)(unqlite_kv_cursor *);
846 int (*xDelete)(unqlite_kv_cursor *);
847 int (*xKeyLength)(unqlite_kv_cursor *,int *);
848 int (*xKey)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
849 int (*xDataLength)(unqlite_kv_cursor *,unqlite_int64 *);
850 int (*xData)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
851 void (*xReset)(unqlite_kv_cursor *);
852 void (*xCursorRelease)(unqlite_kv_cursor *);
853};
854/*
855 * UnQLite journal file suffix.
856 */
857#ifndef UNQLITE_JOURNAL_FILE_SUFFIX
858#define UNQLITE_JOURNAL_FILE_SUFFIX "_unqlite_journal"
859#endif
860/*
861 * Call Context - Error Message Serverity Level.
862 *
863 * The following constans are the allowed severity level that can
864 * passed as the second argument to the [unqlite_context_throw_error()] or
865 * [unqlite_context_throw_error_format()] interfaces.
866 * Refer to the official documentation for additional information.
867 */
868#define UNQLITE_CTX_ERR 1 /* Call context error such as unexpected number of arguments, invalid types and so on. */
869#define UNQLITE_CTX_WARNING 2 /* Call context Warning */
870#define UNQLITE_CTX_NOTICE 3 /* Call context Notice */
871/*
872 * C-API-REF: Please refer to the official documentation for interfaces
873 * purpose and expected parameters.
874 */
875
876/* Database Engine Handle */
877UNQLITE_APIEXPORT int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode);
878UNQLITE_APIEXPORT int unqlite_config(unqlite *pDb,int nOp,...);
879UNQLITE_APIEXPORT int unqlite_close(unqlite *pDb);
880
881/* Key/Value (KV) Store Interfaces */
882UNQLITE_APIEXPORT int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
883UNQLITE_APIEXPORT int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
884UNQLITE_APIEXPORT int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
885UNQLITE_APIEXPORT int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
886UNQLITE_APIEXPORT int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 /* in|out */*pBufLen);
887UNQLITE_APIEXPORT int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,
888 int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
889UNQLITE_APIEXPORT int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen);
890UNQLITE_APIEXPORT int unqlite_kv_config(unqlite *pDb,int iOp,...);
891
892/* Document (JSON) Store Interfaces powered by the Jx9 Scripting Language */
893UNQLITE_APIEXPORT int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut);
894UNQLITE_APIEXPORT int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut);
895UNQLITE_APIEXPORT int unqlite_vm_config(unqlite_vm *pVm,int iOp,...);
896UNQLITE_APIEXPORT int unqlite_vm_exec(unqlite_vm *pVm);
897UNQLITE_APIEXPORT int unqlite_vm_reset(unqlite_vm *pVm);
898UNQLITE_APIEXPORT int unqlite_vm_release(unqlite_vm *pVm);
899UNQLITE_APIEXPORT int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);
900UNQLITE_APIEXPORT unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname);
901
902/* Cursor Iterator Interfaces */
903UNQLITE_APIEXPORT int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut);
904UNQLITE_APIEXPORT int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur);
905UNQLITE_APIEXPORT int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos);
906UNQLITE_APIEXPORT int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor);
907UNQLITE_APIEXPORT int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor);
908UNQLITE_APIEXPORT int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor);
909UNQLITE_APIEXPORT int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor);
910UNQLITE_APIEXPORT int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor);
911UNQLITE_APIEXPORT int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte);
912UNQLITE_APIEXPORT int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
913UNQLITE_APIEXPORT int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnData);
914UNQLITE_APIEXPORT int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
915UNQLITE_APIEXPORT int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor);
916UNQLITE_APIEXPORT int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor);
917
918/* Manual Transaction Manager */
919UNQLITE_APIEXPORT int unqlite_begin(unqlite *pDb);
920UNQLITE_APIEXPORT int unqlite_commit(unqlite *pDb);
921UNQLITE_APIEXPORT int unqlite_rollback(unqlite *pDb);
922
923/* Utility interfaces */
924UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize);
925UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize);
926UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size);
927UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb);
928
929/* In-process extending interfaces */
930UNQLITE_APIEXPORT int unqlite_create_function(unqlite_vm *pVm,const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData);
931UNQLITE_APIEXPORT int unqlite_delete_function(unqlite_vm *pVm, const char *zName);
932UNQLITE_APIEXPORT int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData);
933UNQLITE_APIEXPORT int unqlite_delete_constant(unqlite_vm *pVm, const char *zName);
934
935/* On Demand Object allocation interfaces */
936UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm);
937UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm);
938UNQLITE_APIEXPORT int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue);
939UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx);
940UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_array(unqlite_context *pCtx);
941UNQLITE_APIEXPORT void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue);
942
943/* Dynamically Typed Value Object Management Interfaces */
944UNQLITE_APIEXPORT int unqlite_value_int(unqlite_value *pVal, int iValue);
945UNQLITE_APIEXPORT int unqlite_value_int64(unqlite_value *pVal, unqlite_int64 iValue);
946UNQLITE_APIEXPORT int unqlite_value_bool(unqlite_value *pVal, int iBool);
947UNQLITE_APIEXPORT int unqlite_value_null(unqlite_value *pVal);
948UNQLITE_APIEXPORT int unqlite_value_double(unqlite_value *pVal, double Value);
949UNQLITE_APIEXPORT int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen);
950UNQLITE_APIEXPORT int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...);
951UNQLITE_APIEXPORT int unqlite_value_reset_string_cursor(unqlite_value *pVal);
952UNQLITE_APIEXPORT int unqlite_value_resource(unqlite_value *pVal, void *pUserData);
953UNQLITE_APIEXPORT int unqlite_value_release(unqlite_value *pVal);
954
955/* Foreign Function Parameter Values */
956UNQLITE_APIEXPORT int unqlite_value_to_int(unqlite_value *pValue);
957UNQLITE_APIEXPORT int unqlite_value_to_bool(unqlite_value *pValue);
958UNQLITE_APIEXPORT unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue);
959UNQLITE_APIEXPORT double unqlite_value_to_double(unqlite_value *pValue);
960UNQLITE_APIEXPORT const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen);
961UNQLITE_APIEXPORT void * unqlite_value_to_resource(unqlite_value *pValue);
962UNQLITE_APIEXPORT int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict);
963
964/* Setting The Result Of A Foreign Function */
965UNQLITE_APIEXPORT int unqlite_result_int(unqlite_context *pCtx, int iValue);
966UNQLITE_APIEXPORT int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue);
967UNQLITE_APIEXPORT int unqlite_result_bool(unqlite_context *pCtx, int iBool);
968UNQLITE_APIEXPORT int unqlite_result_double(unqlite_context *pCtx, double Value);
969UNQLITE_APIEXPORT int unqlite_result_null(unqlite_context *pCtx);
970UNQLITE_APIEXPORT int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen);
971UNQLITE_APIEXPORT int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...);
972UNQLITE_APIEXPORT int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue);
973UNQLITE_APIEXPORT int unqlite_result_resource(unqlite_context *pCtx, void *pUserData);
974
975/* Dynamically Typed Value Object Query Interfaces */
976UNQLITE_APIEXPORT int unqlite_value_is_int(unqlite_value *pVal);
977UNQLITE_APIEXPORT int unqlite_value_is_float(unqlite_value *pVal);
978UNQLITE_APIEXPORT int unqlite_value_is_bool(unqlite_value *pVal);
979UNQLITE_APIEXPORT int unqlite_value_is_string(unqlite_value *pVal);
980UNQLITE_APIEXPORT int unqlite_value_is_null(unqlite_value *pVal);
981UNQLITE_APIEXPORT int unqlite_value_is_numeric(unqlite_value *pVal);
982UNQLITE_APIEXPORT int unqlite_value_is_callable(unqlite_value *pVal);
983UNQLITE_APIEXPORT int unqlite_value_is_scalar(unqlite_value *pVal);
984UNQLITE_APIEXPORT int unqlite_value_is_json_array(unqlite_value *pVal);
985UNQLITE_APIEXPORT int unqlite_value_is_json_object(unqlite_value *pVal);
986UNQLITE_APIEXPORT int unqlite_value_is_resource(unqlite_value *pVal);
987UNQLITE_APIEXPORT int unqlite_value_is_empty(unqlite_value *pVal);
988
989/* JSON Array/Object Management Interfaces */
990UNQLITE_APIEXPORT unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte);
991UNQLITE_APIEXPORT int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData);
992UNQLITE_APIEXPORT int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue);
993UNQLITE_APIEXPORT int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue);
994UNQLITE_APIEXPORT int unqlite_array_count(unqlite_value *pArray);
995
996/* Call Context Handling Interfaces */
997UNQLITE_APIEXPORT int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen);
998UNQLITE_APIEXPORT int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...);
999UNQLITE_APIEXPORT int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr);
1000UNQLITE_APIEXPORT int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...);
1001UNQLITE_APIEXPORT unsigned int unqlite_context_random_num(unqlite_context *pCtx);
1002UNQLITE_APIEXPORT int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen);
1003UNQLITE_APIEXPORT void * unqlite_context_user_data(unqlite_context *pCtx);
1004UNQLITE_APIEXPORT int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData);
1005UNQLITE_APIEXPORT void * unqlite_context_peek_aux_data(unqlite_context *pCtx);
1006UNQLITE_APIEXPORT unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx);
1007UNQLITE_APIEXPORT const char * unqlite_function_name(unqlite_context *pCtx);
1008
1009/* Call Context Memory Management Interfaces */
1010UNQLITE_APIEXPORT void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease);
1011UNQLITE_APIEXPORT void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte);
1012UNQLITE_APIEXPORT void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk);
1013
1014/* Global Library Management Interfaces */
1015UNQLITE_APIEXPORT int unqlite_lib_config(int nConfigOp,...);
1016UNQLITE_APIEXPORT int unqlite_lib_init(void);
1017UNQLITE_APIEXPORT int unqlite_lib_shutdown(void);
1018UNQLITE_APIEXPORT int unqlite_lib_is_threadsafe(void);
1019UNQLITE_APIEXPORT const char * unqlite_lib_version(void);
1020UNQLITE_APIEXPORT const char * unqlite_lib_signature(void);
1021UNQLITE_APIEXPORT const char * unqlite_lib_ident(void);
1022UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void);
1023#ifdef __cplusplus
1024}
1025#endif /* __cplusplus */
1026#endif /* _UNQLITE_H_ */
1027/*
1028 * ----------------------------------------------------------
1029 * File: jx9.h
1030 * MD5: d23a1e182f596794001533e1d6aa16a0
1031 * ----------------------------------------------------------
1032 */
1033/* This file was automatically generated. Do not edit (except for compile time directive)! */
1034#ifndef _JX9H_
1035#define _JX9H_
1036/*
1037 * Symisc Jx9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
1038 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
1039 * Version 1.7.2
1040 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
1041 * please contact Symisc Systems via:
1042 * legal@symisc.net
1043 * licensing@symisc.net
1044 * contact@symisc.net
1045 * or visit:
1046 * http://jx9.symisc.net/
1047 */
1048/*
1049 * Copyright (C) 2012, 2013 Symisc Systems. All rights reserved.
1050 *
1051 * Redistribution and use in source and binary forms, with or without
1052 * modification, are permitted provided that the following conditions
1053 * are met:
1054 * 1. Redistributions of source code must retain the above copyright
1055 * notice, this list of conditions and the following disclaimer.
1056 * 2. Redistributions in binary form must reproduce the above copyright
1057 * notice, this list of conditions and the following disclaimer in the
1058 * documentation and/or other materials provided with the distribution.
1059 * 3. Redistributions in any form must be accompanied by information on
1060 * how to obtain complete source code for the JX9 engine and any
1061 * accompanying software that uses the JX9 engine software.
1062 * The source code must either be included in the distribution
1063 * or be available for no more than the cost of distribution plus
1064 * a nominal fee, and must be freely redistributable under reasonable
1065 * conditions. For an executable file, complete source code means
1066 * the source code for all modules it contains.It does not include
1067 * source code for modules or files that typically accompany the major
1068 * components of the operating system on which the executable file runs.
1069 *
1070 * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
1071 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1072 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
1073 * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS
1074 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1075 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1076 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
1077 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
1078 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
1079 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
1080 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1081 */
1082 /* $SymiscID: jx9.h v2.1 UNIX|WIN32/64 2012-09-15 09:43 stable <chm@symisc.net> $ */
1083#include "unqlite.h"
1084/*
1085 * Compile time engine version, signature, identification in the symisc source tree
1086 * and copyright notice.
1087 * Each macro have an equivalent C interface associated with it that provide the same
1088 * information but are associated with the library instead of the header file.
1089 * Refer to [jx9_lib_version()], [jx9_lib_signature()], [jx9_lib_ident()] and
1090 * [jx9_lib_copyright()] for more information.
1091 */
1092/*
1093 * The JX9_VERSION C preprocessor macroevaluates to a string literal
1094 * that is the jx9 version in the format "X.Y.Z" where X is the major
1095 * version number and Y is the minor version number and Z is the release
1096 * number.
1097 */
1098#define JX9_VERSION "1.7.2"
1099/*
1100 * The JX9_VERSION_NUMBER C preprocessor macro resolves to an integer
1101 * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same
1102 * numbers used in [JX9_VERSION].
1103 */
1104#define JX9_VERSION_NUMBER 1007002
1105/*
1106 * The JX9_SIG C preprocessor macro evaluates to a string
1107 * literal which is the public signature of the jx9 engine.
1108 * This signature could be included for example in a host-application
1109 * generated Server MIME header as follows:
1110 * Server: YourWebServer/x.x Jx9/x.x.x \r\n
1111 */
1112#define JX9_SIG "Jx9/1.7.2"
1113/*
1114 * JX9 identification in the Symisc source tree:
1115 * Each particular check-in of a particular software released
1116 * by symisc systems have an unique identifier associated with it.
1117 * This macro hold the one associated with jx9.
1118 */
1119#define JX9_IDENT "jx9:d217a6e8c7f10fb35a8becb2793101fd2036aeb7"
1120/*
1121 * Copyright notice.
1122 * If you have any questions about the licensing situation, please
1123 * visit http://jx9.symisc.net/licensing.html
1124 * or contact Symisc Systems via:
1125 * legal@symisc.net
1126 * licensing@symisc.net
1127 * contact@symisc.net
1128 */
1129#define JX9_COPYRIGHT "Copyright (C) Symisc Systems 2012-2013, http://jx9.symisc.net/"
1130
1131/* Forward declaration to public objects */
1132typedef struct jx9_io_stream jx9_io_stream;
1133typedef struct jx9_context jx9_context;
1134typedef struct jx9_value jx9_value;
1135typedef struct jx9_vfs jx9_vfs;
1136typedef struct jx9_vm jx9_vm;
1137typedef struct jx9 jx9;
1138
1139#include "unqlite.h"
1140
1141#if !defined( UNQLITE_ENABLE_JX9_HASH_FUNC )
1142#define JX9_DISABLE_HASH_FUNC
1143#endif /* UNQLITE_ENABLE_JX9_HASH_FUNC */
1144#ifdef UNQLITE_ENABLE_THREADS
1145#define JX9_ENABLE_THREADS
1146#endif /* UNQLITE_ENABLE_THREADS */
1147/* Standard JX9 return values */
1148#define JX9_OK SXRET_OK /* Successful result */
1149/* beginning-of-error-codes */
1150#define JX9_NOMEM UNQLITE_NOMEM /* Out of memory */
1151#define JX9_ABORT UNQLITE_ABORT /* Foreign Function request operation abort/Another thread have released this instance */
1152#define JX9_IO_ERR UNQLITE_IOERR /* IO error */
1153#define JX9_CORRUPT UNQLITE_CORRUPT /* Corrupt pointer/Unknown configuration option */
1154#define JX9_LOOKED UNQLITE_LOCKED /* Forbidden Operation */
1155#define JX9_COMPILE_ERR UNQLITE_COMPILE_ERR /* Compilation error */
1156#define JX9_VM_ERR UNQLITE_VM_ERR /* Virtual machine error */
1157/* end-of-error-codes */
1158/*
1159 * If compiling for a processor that lacks floating point
1160 * support, substitute integer for floating-point.
1161 */
1162#ifdef JX9_OMIT_FLOATING_POINT
1163typedef sxi64 jx9_real;
1164#else
1165typedef double jx9_real;
1166#endif
1167typedef sxi64 jx9_int64;
1168/*
1169 * Engine Configuration Commands.
1170 *
1171 * The following set of constants are the available configuration verbs that can
1172 * be used by the host-application to configure the JX9 engine.
1173 * These constants must be passed as the second argument to the [jx9_config()]
1174 * interface.
1175 * Each options require a variable number of arguments.
1176 * The [jx9_config()] interface will return JX9_OK on success, any other
1177 * return value indicates failure.
1178 * For a full discussion on the configuration verbs and their expected
1179 * parameters, please refer to this page:
1180 * http://jx9.symisc.net/c_api_func.html#jx9_config
1181 */
1182#define JX9_CONFIG_ERR_ABORT 1 /* RESERVED FOR FUTURE USE */
1183#define JX9_CONFIG_ERR_LOG 2 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
1184/*
1185 * Virtual Machine Configuration Commands.
1186 *
1187 * The following set of constants are the available configuration verbs that can
1188 * be used by the host-application to configure the JX9 Virtual machine.
1189 * These constants must be passed as the second argument to the [jx9_vm_config()]
1190 * interface.
1191 * Each options require a variable number of arguments.
1192 * The [jx9_vm_config()] interface will return JX9_OK on success, any other return
1193 * value indicates failure.
1194 * There are many options but the most importants are: JX9_VM_CONFIG_OUTPUT which install
1195 * a VM output consumer callback, JX9_VM_CONFIG_HTTP_REQUEST which parse and register
1196 * a HTTP request and JX9_VM_CONFIG_ARGV_ENTRY which populate the $argv array.
1197 * For a full discussion on the configuration verbs and their expected parameters, please
1198 * refer to this page:
1199 * http://jx9.symisc.net/c_api_func.html#jx9_vm_config
1200 */
1201#define JX9_VM_CONFIG_OUTPUT UNQLITE_VM_CONFIG_OUTPUT /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
1202#define JX9_VM_CONFIG_IMPORT_PATH UNQLITE_VM_CONFIG_IMPORT_PATH /* ONE ARGUMENT: const char *zIncludePath */
1203#define JX9_VM_CONFIG_ERR_REPORT UNQLITE_VM_CONFIG_ERR_REPORT /* NO ARGUMENTS: Report all run-time errors in the VM output */
1204#define JX9_VM_CONFIG_RECURSION_DEPTH UNQLITE_VM_CONFIG_RECURSION_DEPTH /* ONE ARGUMENT: int nMaxDepth */
1205#define JX9_VM_OUTPUT_LENGTH UNQLITE_VM_OUTPUT_LENGTH /* ONE ARGUMENT: unsigned int *pLength */
1206#define JX9_VM_CONFIG_CREATE_VAR UNQLITE_VM_CONFIG_CREATE_VAR /* TWO ARGUMENTS: const char *zName, jx9_value *pValue */
1207#define JX9_VM_CONFIG_HTTP_REQUEST UNQLITE_VM_CONFIG_HTTP_REQUEST /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
1208#define JX9_VM_CONFIG_SERVER_ATTR UNQLITE_VM_CONFIG_SERVER_ATTR /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
1209#define JX9_VM_CONFIG_ENV_ATTR UNQLITE_VM_CONFIG_ENV_ATTR /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
1210#define JX9_VM_CONFIG_EXEC_VALUE UNQLITE_VM_CONFIG_EXEC_VALUE /* ONE ARGUMENT: jx9_value **ppValue */
1211#define JX9_VM_CONFIG_IO_STREAM UNQLITE_VM_CONFIG_IO_STREAM /* ONE ARGUMENT: const jx9_io_stream *pStream */
1212#define JX9_VM_CONFIG_ARGV_ENTRY UNQLITE_VM_CONFIG_ARGV_ENTRY /* ONE ARGUMENT: const char *zValue */
1213#define JX9_VM_CONFIG_EXTRACT_OUTPUT UNQLITE_VM_CONFIG_EXTRACT_OUTPUT /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
1214/*
1215 * Global Library Configuration Commands.
1216 *
1217 * The following set of constants are the available configuration verbs that can
1218 * be used by the host-application to configure the whole library.
1219 * These constants must be passed as the first argument to the [jx9_lib_config()]
1220 * interface.
1221 * Each options require a variable number of arguments.
1222 * The [jx9_lib_config()] interface will return JX9_OK on success, any other return
1223 * value indicates failure.
1224 * Notes:
1225 * The default configuration is recommended for most applications and so the call to
1226 * [jx9_lib_config()] is usually not necessary. It is provided to support rare
1227 * applications with unusual needs.
1228 * The [jx9_lib_config()] interface is not threadsafe. The application must insure that
1229 * no other [jx9_*()] interfaces are invoked by other threads while [jx9_lib_config()]
1230 * is running. Furthermore, [jx9_lib_config()] may only be invoked prior to library
1231 * initialization using [jx9_lib_init()] or [jx9_init()] or after shutdown
1232 * by [jx9_lib_shutdown()]. If [jx9_lib_config()] is called after [jx9_lib_init()]
1233 * or [jx9_init()] and before [jx9_lib_shutdown()] then it will return jx9LOCKED.
1234 * For a full discussion on the configuration verbs and their expected parameters, please
1235 * refer to this page:
1236 * http://jx9.symisc.net/c_api_func.html#Global_Library_Management_Interfaces
1237 */
1238#define JX9_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */
1239#define JX9_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
1240#define JX9_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */
1241#define JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */
1242#define JX9_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */
1243#define JX9_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const jx9_vfs *pVfs */
1244/*
1245 * Call Context - Error Message Serverity Level.
1246 */
1247#define JX9_CTX_ERR UNQLITE_CTX_ERR /* Call context error such as unexpected number of arguments, invalid types and so on. */
1248#define JX9_CTX_WARNING UNQLITE_CTX_WARNING /* Call context Warning */
1249#define JX9_CTX_NOTICE UNQLITE_CTX_NOTICE /* Call context Notice */
1250/* Current VFS structure version*/
1251#define JX9_VFS_VERSION 2
1252/*
1253 * JX9 Virtual File System (VFS).
1254 *
1255 * An instance of the jx9_vfs object defines the interface between the JX9 core
1256 * and the underlying operating system. The "vfs" in the name of the object stands
1257 * for "virtual file system". The vfs is used to implement JX9 system functions
1258 * such as mkdir(), chdir(), stat(), get_user_name() and many more.
1259 * The value of the iVersion field is initially 2 but may be larger in future versions
1260 * of JX9.
1261 * Additional fields may be appended to this object when the iVersion value is increased.
1262 * Only a single vfs can be registered within the JX9 core. Vfs registration is done
1263 * using the jx9_lib_config() interface with a configuration verb set to JX9_LIB_CONFIG_VFS.
1264 * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users does not have to
1265 * worry about registering and installing a vfs since JX9 come with a built-in vfs for these
1266 * platforms which implement most the methods defined below.
1267 * Host-application running on exotic systems (ie: Other than Windows and UNIX systems) must
1268 * register their own vfs in order to be able to use and call JX9 system functions.
1269 * Also note that the jx9_compile_file() interface depend on the xMmap() method of the underlying
1270 * vfs which mean that this method must be available (Always the case using the built-in VFS)
1271 * in order to use this interface.
1272 * Developers wishing to implement their own vfs an contact symisc systems to obtain
1273 * the JX9 VFS C/C++ Specification manual.
1274 */
1275struct jx9_vfs
1276{
1277 const char *zName; /* Underlying VFS name [i.e: FreeBSD/Linux/Windows...] */
1278 int iVersion; /* Current VFS structure version [default 2] */
1279 /* Directory functions */
1280 int (*xChdir)(const char *); /* Change directory */
1281 int (*xChroot)(const char *); /* Change the root directory */
1282 int (*xGetcwd)(jx9_context *); /* Get the current working directory */
1283 int (*xMkdir)(const char *, int, int); /* Make directory */
1284 int (*xRmdir)(const char *); /* Remove directory */
1285 int (*xIsdir)(const char *); /* Tells whether the filename is a directory */
1286 int (*xRename)(const char *, const char *); /* Renames a file or directory */
1287 int (*xRealpath)(const char *, jx9_context *); /* Return canonicalized absolute pathname*/
1288 /* Systems functions */
1289 int (*xSleep)(unsigned int); /* Delay execution in microseconds */
1290 int (*xUnlink)(const char *); /* Deletes a file */
1291 int (*xFileExists)(const char *); /* Checks whether a file or directory exists */
1292 int (*xChmod)(const char *, int); /* Changes file mode */
1293 int (*xChown)(const char *, const char *); /* Changes file owner */
1294 int (*xChgrp)(const char *, const char *); /* Changes file group */
1295 jx9_int64 (*xFreeSpace)(const char *); /* Available space on filesystem or disk partition */
1296 jx9_int64 (*xTotalSpace)(const char *); /* Total space on filesystem or disk partition */
1297 jx9_int64 (*xFileSize)(const char *); /* Gets file size */
1298 jx9_int64 (*xFileAtime)(const char *); /* Gets last access time of file */
1299 jx9_int64 (*xFileMtime)(const char *); /* Gets file modification time */
1300 jx9_int64 (*xFileCtime)(const char *); /* Gets inode change time of file */
1301 int (*xStat)(const char *, jx9_value *, jx9_value *); /* Gives information about a file */
1302 int (*xlStat)(const char *, jx9_value *, jx9_value *); /* Gives information about a file */
1303 int (*xIsfile)(const char *); /* Tells whether the filename is a regular file */
1304 int (*xIslink)(const char *); /* Tells whether the filename is a symbolic link */
1305 int (*xReadable)(const char *); /* Tells whether a file exists and is readable */
1306 int (*xWritable)(const char *); /* Tells whether the filename is writable */
1307 int (*xExecutable)(const char *); /* Tells whether the filename is executable */
1308 int (*xFiletype)(const char *, jx9_context *); /* Gets file type [i.e: fifo, dir, file..] */
1309 int (*xGetenv)(const char *, jx9_context *); /* Gets the value of an environment variable */
1310 int (*xSetenv)(const char *, const char *); /* Sets the value of an environment variable */
1311 int (*xTouch)(const char *, jx9_int64, jx9_int64); /* Sets access and modification time of file */
1312 int (*xMmap)(const char *, void **, jx9_int64 *); /* Read-only memory map of the whole file */
1313 void (*xUnmap)(void *, jx9_int64); /* Unmap a memory view */
1314 int (*xLink)(const char *, const char *, int); /* Create hard or symbolic link */
1315 int (*xUmask)(int); /* Change the current umask */
1316 void (*xTempDir)(jx9_context *); /* Get path of the temporary directory */
1317 unsigned int (*xProcessId)(void); /* Get running process ID */
1318 int (*xUid)(void); /* user ID of the process */
1319 int (*xGid)(void); /* group ID of the process */
1320 void (*xUsername)(jx9_context *); /* Running username */
1321 int (*xExec)(const char *, jx9_context *); /* Execute an external program */
1322};
1323/* Current JX9 IO stream structure version. */
1324#define JX9_IO_STREAM_VERSION 1
1325/*
1326 * Possible open mode flags that can be passed to the xOpen() routine
1327 * of the underlying IO stream device .
1328 * Refer to the JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html)
1329 * for additional information.
1330 */
1331#define JX9_IO_OPEN_RDONLY 0x001 /* Read-only open */
1332#define JX9_IO_OPEN_WRONLY 0x002 /* Write-only open */
1333#define JX9_IO_OPEN_RDWR 0x004 /* Read-write open. */
1334#define JX9_IO_OPEN_CREATE 0x008 /* If the file does not exist it will be created */
1335#define JX9_IO_OPEN_TRUNC 0x010 /* Truncate the file to zero length */
1336#define JX9_IO_OPEN_APPEND 0x020 /* Append mode.The file offset is positioned at the end of the file */
1337#define JX9_IO_OPEN_EXCL 0x040 /* Ensure that this call creates the file, the file must not exist before */
1338#define JX9_IO_OPEN_BINARY 0x080 /* Simple hint: Data is binary */
1339#define JX9_IO_OPEN_TEMP 0x100 /* Simple hint: Temporary file */
1340#define JX9_IO_OPEN_TEXT 0x200 /* Simple hint: Data is textual */
1341/*
1342 * JX9 IO Stream Device.
1343 *
1344 * An instance of the jx9_io_stream object defines the interface between the JX9 core
1345 * and the underlying stream device.
1346 * A stream is a smart mechanism for generalizing file, network, data compression
1347 * and other IO operations which share a common set of functions using an abstracted
1348 * unified interface.
1349 * A stream device is additional code which tells the stream how to handle specific
1350 * protocols/encodings. For example, the http device knows how to translate a URL
1351 * into an HTTP/1.1 request for a file on a remote server.
1352 * JX9 come with two built-in IO streams device:
1353 * The file:// stream which perform very efficient disk IO and the jx9:// stream
1354 * which is a special stream that allow access various I/O streams (See the JX9 official
1355 * documentation for more information on this stream).
1356 * A stream is referenced as: scheme://target
1357 * scheme(string) - The name of the wrapper to be used. Examples include: file, http, https, ftp,
1358 * ftps, compress.zlib, compress.bz2, and jx9. If no wrapper is specified, the function default
1359 * is used (typically file://).
1360 * target - Depends on the device used. For filesystem related streams this is typically a path
1361 * and filename of the desired file.For network related streams this is typically a hostname, often
1362 * with a path appended.
1363 * IO stream devices are registered using a call to jx9_vm_config() with a configuration verb
1364 * set to JX9_VM_CONFIG_IO_STREAM.
1365 * Currently the JX9 development team is working on the implementation of the http:// and ftp://
1366 * IO stream protocols. These devices will be available in the next major release of the JX9 engine.
1367 * Developers wishing to implement their own IO stream devices must understand and follow
1368 * The JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html).
1369 */
1370struct jx9_io_stream
1371{
1372 const char *zName; /* Underlying stream name [i.e: file/http/zip/jx9, ..] */
1373 int iVersion; /* IO stream structure version [default 1]*/
1374 int (*xOpen)(const char *, int, jx9_value *, void **); /* Open handle*/
1375 int (*xOpenDir)(const char *, jx9_value *, void **); /* Open directory handle */
1376 void (*xClose)(void *); /* Close file handle */
1377 void (*xCloseDir)(void *); /* Close directory handle */
1378 jx9_int64 (*xRead)(void *, void *, jx9_int64); /* Read from the open stream */
1379 int (*xReadDir)(void *, jx9_context *); /* Read entry from directory handle */
1380 jx9_int64 (*xWrite)(void *, const void *, jx9_int64); /* Write to the open stream */
1381 int (*xSeek)(void *, jx9_int64, int); /* Seek on the open stream */
1382 int (*xLock)(void *, int); /* Lock/Unlock the open stream */
1383 void (*xRewindDir)(void *); /* Rewind directory handle */
1384 jx9_int64 (*xTell)(void *); /* Current position of the stream read/write pointer */
1385 int (*xTrunc)(void *, jx9_int64); /* Truncates the open stream to a given length */
1386 int (*xSync)(void *); /* Flush open stream data */
1387 int (*xStat)(void *, jx9_value *, jx9_value *); /* Stat an open stream handle */
1388};
1389/*
1390 * C-API-REF: Please refer to the official documentation for interfaces
1391 * purpose and expected parameters.
1392 */
1393/* Engine Handling Interfaces */
1394JX9_PRIVATE int jx9_init(jx9 **ppEngine);
1395/*JX9_PRIVATE int jx9_config(jx9 *pEngine, int nConfigOp, ...);*/
1396JX9_PRIVATE int jx9_release(jx9 *pEngine);
1397/* Compile Interfaces */
1398JX9_PRIVATE int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm);
1399JX9_PRIVATE int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm);
1400/* Virtual Machine Handling Interfaces */
1401JX9_PRIVATE int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...);
1402/*JX9_PRIVATE int jx9_vm_exec(jx9_vm *pVm, int *pExitStatus);*/
1403/*JX9_PRIVATE jx9_value * jx9_vm_extract_variable(jx9_vm *pVm,const char *zVarname);*/
1404/*JX9_PRIVATE int jx9_vm_reset(jx9_vm *pVm);*/
1405JX9_PRIVATE int jx9_vm_release(jx9_vm *pVm);
1406/*JX9_PRIVATE int jx9_vm_dump_v2(jx9_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);*/
1407/* In-process Extending Interfaces */
1408JX9_PRIVATE int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData);
1409/*JX9_PRIVATE int jx9_delete_function(jx9_vm *pVm, const char *zName);*/
1410JX9_PRIVATE int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData);
1411/*JX9_PRIVATE int jx9_delete_constant(jx9_vm *pVm, const char *zName);*/
1412/* Foreign Function Parameter Values */
1413JX9_PRIVATE int jx9_value_to_int(jx9_value *pValue);
1414JX9_PRIVATE int jx9_value_to_bool(jx9_value *pValue);
1415JX9_PRIVATE jx9_int64 jx9_value_to_int64(jx9_value *pValue);
1416JX9_PRIVATE double jx9_value_to_double(jx9_value *pValue);
1417JX9_PRIVATE const char * jx9_value_to_string(jx9_value *pValue, int *pLen);
1418JX9_PRIVATE void * jx9_value_to_resource(jx9_value *pValue);
1419JX9_PRIVATE int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict);
1420/* Setting The Result Of A Foreign Function */
1421JX9_PRIVATE int jx9_result_int(jx9_context *pCtx, int iValue);
1422JX9_PRIVATE int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue);
1423JX9_PRIVATE int jx9_result_bool(jx9_context *pCtx, int iBool);
1424JX9_PRIVATE int jx9_result_double(jx9_context *pCtx, double Value);
1425JX9_PRIVATE int jx9_result_null(jx9_context *pCtx);
1426JX9_PRIVATE int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen);
1427JX9_PRIVATE int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...);
1428JX9_PRIVATE int jx9_result_value(jx9_context *pCtx, jx9_value *pValue);
1429JX9_PRIVATE int jx9_result_resource(jx9_context *pCtx, void *pUserData);
1430/* Call Context Handling Interfaces */
1431JX9_PRIVATE int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen);
1432/*JX9_PRIVATE int jx9_context_output_format(jx9_context *pCtx, const char *zFormat, ...);*/
1433JX9_PRIVATE int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr);
1434JX9_PRIVATE int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...);
1435JX9_PRIVATE unsigned int jx9_context_random_num(jx9_context *pCtx);
1436JX9_PRIVATE int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen);
1437JX9_PRIVATE void * jx9_context_user_data(jx9_context *pCtx);
1438JX9_PRIVATE int jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData);
1439JX9_PRIVATE void * jx9_context_peek_aux_data(jx9_context *pCtx);
1440JX9_PRIVATE void * jx9_context_pop_aux_data(jx9_context *pCtx);
1441JX9_PRIVATE unsigned int jx9_context_result_buf_length(jx9_context *pCtx);
1442JX9_PRIVATE const char * jx9_function_name(jx9_context *pCtx);
1443/* Call Context Memory Management Interfaces */
1444JX9_PRIVATE void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease);
1445JX9_PRIVATE void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte);
1446JX9_PRIVATE void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk);
1447/* On Demand Dynamically Typed Value Object allocation interfaces */
1448JX9_PRIVATE jx9_value * jx9_new_scalar(jx9_vm *pVm);
1449JX9_PRIVATE jx9_value * jx9_new_array(jx9_vm *pVm);
1450JX9_PRIVATE int jx9_release_value(jx9_vm *pVm, jx9_value *pValue);
1451JX9_PRIVATE jx9_value * jx9_context_new_scalar(jx9_context *pCtx);
1452JX9_PRIVATE jx9_value * jx9_context_new_array(jx9_context *pCtx);
1453JX9_PRIVATE void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue);
1454/* Dynamically Typed Value Object Management Interfaces */
1455JX9_PRIVATE int jx9_value_int(jx9_value *pVal, int iValue);
1456JX9_PRIVATE int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue);
1457JX9_PRIVATE int jx9_value_bool(jx9_value *pVal, int iBool);
1458JX9_PRIVATE int jx9_value_null(jx9_value *pVal);
1459JX9_PRIVATE int jx9_value_double(jx9_value *pVal, double Value);
1460JX9_PRIVATE int jx9_value_string(jx9_value *pVal, const char *zString, int nLen);
1461JX9_PRIVATE int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...);
1462JX9_PRIVATE int jx9_value_reset_string_cursor(jx9_value *pVal);
1463JX9_PRIVATE int jx9_value_resource(jx9_value *pVal, void *pUserData);
1464JX9_PRIVATE int jx9_value_release(jx9_value *pVal);
1465/* JSON Array/Object Management Interfaces */
1466JX9_PRIVATE jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte);
1467JX9_PRIVATE int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
1468JX9_PRIVATE int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue);
1469JX9_PRIVATE int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue);
1470JX9_PRIVATE unsigned int jx9_array_count(jx9_value *pArray);
1471/* Dynamically Typed Value Object Query Interfaces */
1472JX9_PRIVATE int jx9_value_is_int(jx9_value *pVal);
1473JX9_PRIVATE int jx9_value_is_float(jx9_value *pVal);
1474JX9_PRIVATE int jx9_value_is_bool(jx9_value *pVal);
1475JX9_PRIVATE int jx9_value_is_string(jx9_value *pVal);
1476JX9_PRIVATE int jx9_value_is_null(jx9_value *pVal);
1477JX9_PRIVATE int jx9_value_is_numeric(jx9_value *pVal);
1478JX9_PRIVATE int jx9_value_is_callable(jx9_value *pVal);
1479JX9_PRIVATE int jx9_value_is_scalar(jx9_value *pVal);
1480JX9_PRIVATE int jx9_value_is_json_array(jx9_value *pVal);
1481JX9_PRIVATE int jx9_value_is_json_object(jx9_value *pVal);
1482JX9_PRIVATE int jx9_value_is_resource(jx9_value *pVal);
1483JX9_PRIVATE int jx9_value_is_empty(jx9_value *pVal);
1484/* Global Library Management Interfaces */
1485/*JX9_PRIVATE int jx9_lib_init(void);*/
1486JX9_PRIVATE int jx9_lib_config(int nConfigOp, ...);
1487JX9_PRIVATE int jx9_lib_shutdown(void);
1488/*JX9_PRIVATE int jx9_lib_is_threadsafe(void);*/
1489/*JX9_PRIVATE const char * jx9_lib_version(void);*/
1490JX9_PRIVATE const char * jx9_lib_signature(void);
1491/*JX9_PRIVATE const char * jx9_lib_ident(void);*/
1492/*JX9_PRIVATE const char * jx9_lib_copyright(void);*/
1493
1494#endif /* _JX9H_ */
1495
1496/*
1497 * ----------------------------------------------------------
1498 * File: jx9Int.h
1499 * MD5: fb8dffc8ba1425a139091aa145067e16
1500 * ----------------------------------------------------------
1501 */
1502/*
1503 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
1504 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
1505 * Version 1.7.2
1506 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
1507 * please contact Symisc Systems via:
1508 * legal@symisc.net
1509 * licensing@symisc.net
1510 * contact@symisc.net
1511 * or visit:
1512 * http://jx9.symisc.net/
1513 */
1514 /* $SymiscID: jx9Int.h v1.9 FreeBSD 2012-08-13 23:25 devel <chm@symisc.net> $ */
1515#ifndef __JX9INT_H__
1516#define __JX9INT_H__
1517/* Internal interface definitions for JX9. */
1518#ifdef JX9_AMALGAMATION
1519#ifndef JX9_PRIVATE
1520/* Marker for routines not intended for external use */
1521#define JX9_PRIVATE static
1522#endif /* JX9_PRIVATE */
1523#else
1524#define JX9_PRIVATE
1525#include "jx9.h"
1526#endif
1527#ifndef JX9_PI
1528/* Value of PI */
1529#define JX9_PI 3.1415926535898
1530#endif
1531/*
1532 * Constants for the largest and smallest possible 64-bit signed integers.
1533 * These macros are designed to work correctly on both 32-bit and 64-bit
1534 * compilers.
1535 */
1536#ifndef LARGEST_INT64
1537#define LARGEST_INT64 (0xffffffff|(((sxi64)0x7fffffff)<<32))
1538#endif
1539#ifndef SMALLEST_INT64
1540#define SMALLEST_INT64 (((sxi64)-1) - LARGEST_INT64)
1541#endif
1542/* Forward declaration of private structures */
1543typedef struct jx9_foreach_info jx9_foreach_info;
1544typedef struct jx9_foreach_step jx9_foreach_step;
1545typedef struct jx9_hashmap_node jx9_hashmap_node;
1546typedef struct jx9_hashmap jx9_hashmap;
1547/* Symisc Standard types */
1548#if !defined(SYMISC_STD_TYPES)
1549#define SYMISC_STD_TYPES
1550#ifdef __WINNT__
1551/* Disable nuisance warnings on Borland compilers */
1552#if defined(__BORLANDC__)
1553#pragma warn -rch /* unreachable code */
1554#pragma warn -ccc /* Condition is always true or false */
1555#pragma warn -aus /* Assigned value is never used */
1556#pragma warn -csu /* Comparing signed and unsigned */
1557#pragma warn -spa /* Suspicious pointer arithmetic */
1558#endif
1559#endif
1560typedef signed char sxi8; /* signed char */
1561typedef unsigned char sxu8; /* unsigned char */
1562typedef signed short int sxi16; /* 16 bits(2 bytes) signed integer */
1563typedef unsigned short int sxu16; /* 16 bits(2 bytes) unsigned integer */
1564typedef int sxi32; /* 32 bits(4 bytes) integer */
1565typedef unsigned int sxu32; /* 32 bits(4 bytes) unsigned integer */
1566typedef long sxptr;
1567typedef unsigned long sxuptr;
1568typedef long sxlong;
1569typedef unsigned long sxulong;
1570typedef sxi32 sxofft;
1571typedef sxi64 sxofft64;
1572typedef long double sxlongreal;
1573typedef double sxreal;
1574#define SXI8_HIGH 0x7F
1575#define SXU8_HIGH 0xFF
1576#define SXI16_HIGH 0x7FFF
1577#define SXU16_HIGH 0xFFFF
1578#define SXI32_HIGH 0x7FFFFFFF
1579#define SXU32_HIGH 0xFFFFFFFF
1580#define SXI64_HIGH 0x7FFFFFFFFFFFFFFF
1581#define SXU64_HIGH 0xFFFFFFFFFFFFFFFF
1582#if !defined(TRUE)
1583#define TRUE 1
1584#endif
1585#if !defined(FALSE)
1586#define FALSE 0
1587#endif
1588/*
1589 * The following macros are used to cast pointers to integers and
1590 * integers to pointers.
1591 */
1592#if defined(__PTRDIFF_TYPE__)
1593# define SX_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
1594# define SX_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
1595#elif !defined(__GNUC__)
1596# define SX_INT_TO_PTR(X) ((void*)&((char*)0)[X])
1597# define SX_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
1598#else
1599# define SX_INT_TO_PTR(X) ((void*)(X))
1600# define SX_PTR_TO_INT(X) ((int)(X))
1601#endif
1602#define SXMIN(a, b) ((a < b) ? (a) : (b))
1603#define SXMAX(a, b) ((a < b) ? (b) : (a))
1604#endif /* SYMISC_STD_TYPES */
1605/* Symisc Run-time API private definitions */
1606#if !defined(SYMISC_PRIVATE_DEFS)
1607#define SYMISC_PRIVATE_DEFS
1608
1609typedef sxi32 (*ProcRawStrCmp)(const SyString *, const SyString *);
1610#define SyStringData(RAW) ((RAW)->zString)
1611#define SyStringLength(RAW) ((RAW)->nByte)
1612#define SyStringInitFromBuf(RAW, ZBUF, NLEN){\
1613 (RAW)->zString = (const char *)ZBUF;\
1614 (RAW)->nByte = (sxu32)(NLEN);\
1615}
1616#define SyStringUpdatePtr(RAW, NBYTES){\
1617 if( NBYTES > (RAW)->nByte ){\
1618 (RAW)->nByte = 0;\
1619 }else{\
1620 (RAW)->zString += NBYTES;\
1621 (RAW)->nByte -= NBYTES;\
1622 }\
1623}
1624#define SyStringDupPtr(RAW1, RAW2)\
1625 (RAW1)->zString = (RAW2)->zString;\
1626 (RAW1)->nByte = (RAW2)->nByte;
1627
1628#define SyStringTrimLeadingChar(RAW, CHAR)\
1629 while((RAW)->nByte > 0 && (RAW)->zString[0] == CHAR ){\
1630 (RAW)->zString++;\
1631 (RAW)->nByte--;\
1632 }
1633#define SyStringTrimTrailingChar(RAW, CHAR)\
1634 while((RAW)->nByte > 0 && (RAW)->zString[(RAW)->nByte - 1] == CHAR){\
1635 (RAW)->nByte--;\
1636 }
1637#define SyStringCmp(RAW1, RAW2, xCMP)\
1638 (((RAW1)->nByte == (RAW2)->nByte) ? xCMP((RAW1)->zString, (RAW2)->zString, (RAW2)->nByte) : (sxi32)((RAW1)->nByte - (RAW2)->nByte))
1639
1640#define SyStringCmp2(RAW1, RAW2, xCMP)\
1641 (((RAW1)->nByte >= (RAW2)->nByte) ? xCMP((RAW1)->zString, (RAW2)->zString, (RAW2)->nByte) : (sxi32)((RAW2)->nByte - (RAW1)->nByte))
1642
1643#define SyStringCharCmp(RAW, CHAR) \
1644 (((RAW)->nByte == sizeof(char)) ? ((RAW)->zString[0] == CHAR ? 0 : CHAR - (RAW)->zString[0]) : ((RAW)->zString[0] == CHAR ? 0 : (RAW)->nByte - sizeof(char)))
1645
1646#define SX_ADDR(PTR) ((sxptr)PTR)
1647#define SX_ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))
1648#define SXUNUSED(P) (P = 0)
1649#define SX_EMPTY(PTR) (PTR == 0)
1650#define SX_EMPTY_STR(STR) (STR == 0 || STR[0] == 0 )
1651typedef struct SyMemBackend SyMemBackend;
1652typedef struct SyBlob SyBlob;
1653typedef struct SySet SySet;
1654/* Standard function signatures */
1655typedef sxi32 (*ProcCmp)(const void *, const void *, sxu32);
1656typedef sxi32 (*ProcPatternMatch)(const char *, sxu32, const char *, sxu32, sxu32 *);
1657typedef sxi32 (*ProcSearch)(const void *, sxu32, const void *, sxu32, ProcCmp, sxu32 *);
1658typedef sxu32 (*ProcHash)(const void *, sxu32);
1659typedef sxi32 (*ProcHashSum)(const void *, sxu32, unsigned char *, sxu32);
1660typedef sxi32 (*ProcSort)(void *, sxu32, sxu32, ProcCmp);
1661#define MACRO_LIST_PUSH(Head, Item)\
1662 Item->pNext = Head;\
1663 Head = Item;
1664#define MACRO_LD_PUSH(Head, Item)\
1665 if( Head == 0 ){\
1666 Head = Item;\
1667 }else{\
1668 Item->pNext = Head;\
1669 Head->pPrev = Item;\
1670 Head = Item;\
1671 }
1672#define MACRO_LD_REMOVE(Head, Item)\
1673 if( Head == Item ){\
1674 Head = Head->pNext;\
1675 }\
1676 if( Item->pPrev ){ Item->pPrev->pNext = Item->pNext;}\
1677 if( Item->pNext ){ Item->pNext->pPrev = Item->pPrev;}
1678/*
1679 * A generic dynamic set.
1680 */
1681struct SySet
1682{
1683 SyMemBackend *pAllocator; /* Memory backend */
1684 void *pBase; /* Base pointer */
1685 sxu32 nUsed; /* Total number of used slots */
1686 sxu32 nSize; /* Total number of available slots */
1687 sxu32 eSize; /* Size of a single slot */
1688 sxu32 nCursor; /* Loop cursor */
1689 void *pUserData; /* User private data associated with this container */
1690};
1691#define SySetBasePtr(S) ((S)->pBase)
1692#define SySetBasePtrJump(S, OFFT) (&((char *)(S)->pBase)[OFFT*(S)->eSize])
1693#define SySetUsed(S) ((S)->nUsed)
1694#define SySetSize(S) ((S)->nSize)
1695#define SySetElemSize(S) ((S)->eSize)
1696#define SySetCursor(S) ((S)->nCursor)
1697#define SySetGetAllocator(S) ((S)->pAllocator)
1698#define SySetSetUserData(S, DATA) ((S)->pUserData = DATA)
1699#define SySetGetUserData(S) ((S)->pUserData)
1700/*
1701 * A variable length containers for generic data.
1702 */
1703struct SyBlob
1704{
1705 SyMemBackend *pAllocator; /* Memory backend */
1706 void *pBlob; /* Base pointer */
1707 sxu32 nByte; /* Total number of used bytes */
1708 sxu32 mByte; /* Total number of available bytes */
1709 sxu32 nFlags; /* Blob internal flags, see below */
1710};
1711#define SXBLOB_LOCKED 0x01 /* Blob is locked [i.e: Cannot auto grow] */
1712#define SXBLOB_STATIC 0x02 /* Not allocated from heap */
1713#define SXBLOB_RDONLY 0x04 /* Read-Only data */
1714
1715#define SyBlobFreeSpace(BLOB) ((BLOB)->mByte - (BLOB)->nByte)
1716#define SyBlobLength(BLOB) ((BLOB)->nByte)
1717#define SyBlobData(BLOB) ((BLOB)->pBlob)
1718#define SyBlobCurData(BLOB) ((void*)(&((char*)(BLOB)->pBlob)[(BLOB)->nByte]))
1719#define SyBlobDataAt(BLOB, OFFT) ((void *)(&((char *)(BLOB)->pBlob)[OFFT]))
1720#define SyBlobGetAllocator(BLOB) ((BLOB)->pAllocator)
1721
1722#define SXMEM_POOL_INCR 3
1723#define SXMEM_POOL_NBUCKETS 12
1724#define SXMEM_BACKEND_MAGIC 0xBAC3E67D
1725#define SXMEM_BACKEND_CORRUPT(BACKEND) (BACKEND == 0 || BACKEND->nMagic != SXMEM_BACKEND_MAGIC)
1726
1727#define SXMEM_BACKEND_RETRY 3
1728/* A memory backend subsystem is defined by an instance of the following structures */
1729typedef union SyMemHeader SyMemHeader;
1730typedef struct SyMemBlock SyMemBlock;
1731struct SyMemBlock
1732{
1733 SyMemBlock *pNext, *pPrev; /* Chain of allocated memory blocks */
1734#ifdef UNTRUST
1735 sxu32 nGuard; /* magic number associated with each valid block, so we
1736 * can detect misuse.
1737 */
1738#endif
1739};
1740/*
1741 * Header associated with each valid memory pool block.
1742 */
1743union SyMemHeader
1744{
1745 SyMemHeader *pNext; /* Next chunk of size 1 << (nBucket + SXMEM_POOL_INCR) in the list */
1746 sxu32 nBucket; /* Bucket index in aPool[] */
1747};
1748struct SyMemBackend
1749{
1750 const SyMutexMethods *pMutexMethods; /* Mutex methods */
1751 const SyMemMethods *pMethods; /* Memory allocation methods */
1752 SyMemBlock *pBlocks; /* List of valid memory blocks */
1753 sxu32 nBlock; /* Total number of memory blocks allocated so far */
1754 ProcMemError xMemError; /* Out-of memory callback */
1755 void *pUserData; /* First arg to xMemError() */
1756 SyMutex *pMutex; /* Per instance mutex */
1757 sxu32 nMagic; /* Sanity check against misuse */
1758 SyMemHeader *apPool[SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR]; /* Pool of memory chunks */
1759};
1760/* Mutex types */
1761#define SXMUTEX_TYPE_FAST 1
1762#define SXMUTEX_TYPE_RECURSIVE 2
1763#define SXMUTEX_TYPE_STATIC_1 3
1764#define SXMUTEX_TYPE_STATIC_2 4
1765#define SXMUTEX_TYPE_STATIC_3 5
1766#define SXMUTEX_TYPE_STATIC_4 6
1767#define SXMUTEX_TYPE_STATIC_5 7
1768#define SXMUTEX_TYPE_STATIC_6 8
1769
1770#define SyMutexGlobalInit(METHOD){\
1771 if( (METHOD)->xGlobalInit ){\
1772 (METHOD)->xGlobalInit();\
1773 }\
1774}
1775#define SyMutexGlobalRelease(METHOD){\
1776 if( (METHOD)->xGlobalRelease ){\
1777 (METHOD)->xGlobalRelease();\
1778 }\
1779}
1780#define SyMutexNew(METHOD, TYPE) (METHOD)->xNew(TYPE)
1781#define SyMutexRelease(METHOD, MUTEX){\
1782 if( MUTEX && (METHOD)->xRelease ){\
1783 (METHOD)->xRelease(MUTEX);\
1784 }\
1785}
1786#define SyMutexEnter(METHOD, MUTEX){\
1787 if( MUTEX ){\
1788 (METHOD)->xEnter(MUTEX);\
1789 }\
1790}
1791#define SyMutexTryEnter(METHOD, MUTEX){\
1792 if( MUTEX && (METHOD)->xTryEnter ){\
1793 (METHOD)->xTryEnter(MUTEX);\
1794 }\
1795}
1796#define SyMutexLeave(METHOD, MUTEX){\
1797 if( MUTEX ){\
1798 (METHOD)->xLeave(MUTEX);\
1799 }\
1800}
1801/* Comparison, byte swap, byte copy macros */
1802#define SX_MACRO_FAST_CMP(X1, X2, SIZE, RC){\
1803 register unsigned char *r1 = (unsigned char *)X1;\
1804 register unsigned char *r2 = (unsigned char *)X2;\
1805 register sxu32 LEN = SIZE;\
1806 for(;;){\
1807 if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
1808 if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
1809 if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
1810 if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
1811 }\
1812 RC = !LEN ? 0 : r1[0] - r2[0];\
1813}
1814#define SX_MACRO_FAST_MEMCPY(SRC, DST, SIZ){\
1815 register unsigned char *xSrc = (unsigned char *)SRC;\
1816 register unsigned char *xDst = (unsigned char *)DST;\
1817 register sxu32 xLen = SIZ;\
1818 for(;;){\
1819 if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
1820 if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
1821 if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
1822 if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
1823 }\
1824}
1825#define SX_MACRO_BYTE_SWAP(X, Y, Z){\
1826 register unsigned char *s = (unsigned char *)X;\
1827 register unsigned char *d = (unsigned char *)Y;\
1828 sxu32 ZLong = Z; \
1829 sxi32 c; \
1830 for(;;){\
1831 if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
1832 if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
1833 if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
1834 if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
1835 }\
1836}
1837#define SX_MSEC_PER_SEC (1000) /* Millisec per seconds */
1838#define SX_USEC_PER_SEC (1000000) /* Microsec per seconds */
1839#define SX_NSEC_PER_SEC (1000000000) /* Nanosec per seconds */
1840#endif /* SYMISC_PRIVATE_DEFS */
1841/* Symisc Run-time API auxiliary definitions */
1842#if !defined(SYMISC_PRIVATE_AUX_DEFS)
1843#define SYMISC_PRIVATE_AUX_DEFS
1844
1845typedef struct SyHashEntry_Pr SyHashEntry_Pr;
1846typedef struct SyHashEntry SyHashEntry;
1847typedef struct SyHash SyHash;
1848/*
1849 * Each public hashtable entry is represented by an instance
1850 * of the following structure.
1851 */
1852struct SyHashEntry
1853{
1854 const void *pKey; /* Hash key */
1855 sxu32 nKeyLen; /* Key length */
1856 void *pUserData; /* User private data */
1857};
1858#define SyHashEntryGetUserData(ENTRY) ((ENTRY)->pUserData)
1859#define SyHashEntryGetKey(ENTRY) ((ENTRY)->pKey)
1860/* Each active hashtable is identified by an instance of the following structure */
1861struct SyHash
1862{
1863 SyMemBackend *pAllocator; /* Memory backend */
1864 ProcHash xHash; /* Hash function */
1865 ProcCmp xCmp; /* Comparison function */
1866 SyHashEntry_Pr *pList, *pCurrent; /* Linked list of hash entries user for linear traversal */
1867 sxu32 nEntry; /* Total number of entries */
1868 SyHashEntry_Pr **apBucket; /* Hash buckets */
1869 sxu32 nBucketSize; /* Current bucket size */
1870};
1871#define SXHASH_BUCKET_SIZE 16 /* Initial bucket size: must be a power of two */
1872#define SXHASH_FILL_FACTOR 3
1873/* Hash access macro */
1874#define SyHashFunc(HASH) ((HASH)->xHash)
1875#define SyHashCmpFunc(HASH) ((HASH)->xCmp)
1876#define SyHashTotalEntry(HASH) ((HASH)->nEntry)
1877#define SyHashGetPool(HASH) ((HASH)->pAllocator)
1878/*
1879 * An instance of the following structure define a single context
1880 * for an Pseudo Random Number Generator.
1881 *
1882 * Nothing in this file or anywhere else in the library does any kind of
1883 * encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
1884 * number generator) not as an encryption device.
1885 * This implementation is taken from the SQLite3 source tree.
1886 */
1887typedef struct SyPRNGCtx SyPRNGCtx;
1888struct SyPRNGCtx
1889{
1890 sxu8 i, j; /* State variables */
1891 unsigned char s[256]; /* State variables */
1892 sxu16 nMagic; /* Sanity check */
1893 };
1894typedef sxi32 (*ProcRandomSeed)(void *, unsigned int, void *);
1895/* High resolution timer.*/
1896typedef struct sytime sytime;
1897struct sytime
1898{
1899 long tm_sec; /* seconds */
1900 long tm_usec; /* microseconds */
1901};
1902/* Forward declaration */
1903typedef struct SyStream SyStream;
1904typedef struct SyToken SyToken;
1905typedef struct SyLex SyLex;
1906/*
1907 * Tokenizer callback signature.
1908 */
1909typedef sxi32 (*ProcTokenizer)(SyStream *, SyToken *, void *, void *);
1910/*
1911 * Each token in the input is represented by an instance
1912 * of the following structure.
1913 */
1914struct SyToken
1915{
1916 SyString sData; /* Token text and length */
1917 sxu32 nType; /* Token type */
1918 sxu32 nLine; /* Token line number */
1919 void *pUserData; /* User private data associated with this token */
1920};
1921/*
1922 * During tokenization, information about the state of the input
1923 * stream is held in an instance of the following structure.
1924 */
1925struct SyStream
1926{
1927 const unsigned char *zInput; /* Complete text of the input */
1928 const unsigned char *zText; /* Current input we are processing */
1929 const unsigned char *zEnd; /* End of input marker */
1930 sxu32 nLine; /* Total number of processed lines */
1931 sxu32 nIgn; /* Total number of ignored tokens */
1932 SySet *pSet; /* Token containers */
1933};
1934/*
1935 * Each lexer is represented by an instance of the following structure.
1936 */
1937struct SyLex
1938{
1939 SyStream sStream; /* Input stream */
1940 ProcTokenizer xTokenizer; /* Tokenizer callback */
1941 void * pUserData; /* Third argument to xTokenizer() */
1942 SySet *pTokenSet; /* Token set */
1943};
1944#define SyLexTotalToken(LEX) SySetTotalEntry(&(LEX)->aTokenSet)
1945#define SyLexTotalLines(LEX) ((LEX)->sStream.nLine)
1946#define SyLexTotalIgnored(LEX) ((LEX)->sStream.nIgn)
1947#define XLEX_IN_LEN(STREAM) (sxu32)(STREAM->zEnd - STREAM->zText)
1948#endif /* SYMISC_PRIVATE_AUX_DEFS */
1949/*
1950** Notes on UTF-8 (According to SQLite3 authors):
1951**
1952** Byte-0 Byte-1 Byte-2 Byte-3 Value
1953** 0xxxxxxx 00000000 00000000 0xxxxxxx
1954** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx
1955** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx
1956** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx
1957**
1958*/
1959/*
1960** Assuming zIn points to the first byte of a UTF-8 character,
1961** advance zIn to point to the first byte of the next UTF-8 character.
1962*/
1963#define SX_JMP_UTF8(zIn, zEnd)\
1964 while(zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){ zIn++; }
1965#define SX_WRITE_UTF8(zOut, c) { \
1966 if( c<0x00080 ){ \
1967 *zOut++ = (sxu8)(c&0xFF); \
1968 }else if( c<0x00800 ){ \
1969 *zOut++ = 0xC0 + (sxu8)((c>>6)&0x1F); \
1970 *zOut++ = 0x80 + (sxu8)(c & 0x3F); \
1971 }else if( c<0x10000 ){ \
1972 *zOut++ = 0xE0 + (sxu8)((c>>12)&0x0F); \
1973 *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F); \
1974 *zOut++ = 0x80 + (sxu8)(c & 0x3F); \
1975 }else{ \
1976 *zOut++ = 0xF0 + (sxu8)((c>>18) & 0x07); \
1977 *zOut++ = 0x80 + (sxu8)((c>>12) & 0x3F); \
1978 *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F); \
1979 *zOut++ = 0x80 + (sxu8)(c & 0x3F); \
1980 } \
1981}
1982/* Rely on the standard ctype */
1983#include <ctype.h>
1984#define SyToUpper(c) toupper(c)
1985#define SyToLower(c) tolower(c)
1986#define SyisUpper(c) isupper(c)
1987#define SyisLower(c) islower(c)
1988#define SyisSpace(c) isspace(c)
1989#define SyisBlank(c) isspace(c)
1990#define SyisAlpha(c) isalpha(c)
1991#define SyisDigit(c) isdigit(c)
1992#define SyisHex(c) isxdigit(c)
1993#define SyisPrint(c) isprint(c)
1994#define SyisPunct(c) ispunct(c)
1995#define SyisSpec(c) iscntrl(c)
1996#define SyisCtrl(c) iscntrl(c)
1997#define SyisAscii(c) isascii(c)
1998#define SyisAlphaNum(c) isalnum(c)
1999#define SyisGraph(c) isgraph(c)
2000#define SyDigToHex(c) "0123456789ABCDEF"[c & 0x0F]
2001#define SyDigToInt(c) ((c < 0xc0 && SyisDigit(c))? (c - '0') : 0 )
2002#define SyCharToUpper(c) ((c < 0xc0 && SyisLower(c))? SyToUpper(c) : c)
2003#define SyCharToLower(c) ((c < 0xc0 && SyisUpper(c))? SyToLower(c) : c)
2004/* Remove white space/NUL byte from a raw string */
2005#define SyStringLeftTrim(RAW)\
2006 while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && SyisSpace((RAW)->zString[0])){\
2007 (RAW)->nByte--;\
2008 (RAW)->zString++;\
2009 }
2010#define SyStringLeftTrimSafe(RAW)\
2011 while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && ((RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\
2012 (RAW)->nByte--;\
2013 (RAW)->zString++;\
2014 }
2015#define SyStringRightTrim(RAW)\
2016 while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\
2017 (RAW)->nByte--;\
2018 }
2019#define SyStringRightTrimSafe(RAW)\
2020 while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && \
2021 (( RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\
2022 (RAW)->nByte--;\
2023 }
2024
2025#define SyStringFullTrim(RAW)\
2026 while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && SyisSpace((RAW)->zString[0])){\
2027 (RAW)->nByte--;\
2028 (RAW)->zString++;\
2029 }\
2030 while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\
2031 (RAW)->nByte--;\
2032 }
2033#define SyStringFullTrimSafe(RAW)\
2034 while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && \
2035 ( (RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\
2036 (RAW)->nByte--;\
2037 (RAW)->zString++;\
2038 }\
2039 while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && \
2040 ( (RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\
2041 (RAW)->nByte--;\
2042 }
2043#ifndef JX9_DISABLE_BUILTIN_FUNC
2044/*
2045 * An XML raw text, CDATA, tag name and son is parsed out and stored
2046 * in an instance of the following structure.
2047 */
2048typedef struct SyXMLRawStr SyXMLRawStr;
2049struct SyXMLRawStr
2050{
2051 const char *zString; /* Raw text [UTF-8 ENCODED EXCEPT CDATA] [NOT NULL TERMINATED] */
2052 sxu32 nByte; /* Text length */
2053 sxu32 nLine; /* Line number this text occurs */
2054};
2055/*
2056 * Event callback signatures.
2057 */
2058typedef sxi32 (*ProcXMLStartTagHandler)(SyXMLRawStr *, SyXMLRawStr *, sxu32, SyXMLRawStr *, void *);
2059typedef sxi32 (*ProcXMLTextHandler)(SyXMLRawStr *, void *);
2060typedef sxi32 (*ProcXMLEndTagHandler)(SyXMLRawStr *, SyXMLRawStr *, void *);
2061typedef sxi32 (*ProcXMLPIHandler)(SyXMLRawStr *, SyXMLRawStr *, void *);
2062typedef sxi32 (*ProcXMLDoctypeHandler)(SyXMLRawStr *, void *);
2063typedef sxi32 (*ProcXMLSyntaxErrorHandler)(const char *, int, SyToken *, void *);
2064typedef sxi32 (*ProcXMLStartDocument)(void *);
2065typedef sxi32 (*ProcXMLNameSpaceStart)(SyXMLRawStr *, SyXMLRawStr *, void *);
2066typedef sxi32 (*ProcXMLNameSpaceEnd)(SyXMLRawStr *, void *);
2067typedef sxi32 (*ProcXMLEndDocument)(void *);
2068/* XML processing control flags */
2069#define SXML_ENABLE_NAMESPACE 0x01 /* Parse XML with namespace support enbaled */
2070#define SXML_ENABLE_QUERY 0x02 /* Not used */
2071#define SXML_OPTION_CASE_FOLDING 0x04 /* Controls whether case-folding is enabled for this XML parser */
2072#define SXML_OPTION_SKIP_TAGSTART 0x08 /* Specify how many characters should be skipped in the beginning of a tag name.*/
2073#define SXML_OPTION_SKIP_WHITE 0x10 /* Whether to skip values consisting of whitespace characters. */
2074#define SXML_OPTION_TARGET_ENCODING 0x20 /* Default encoding: UTF-8 */
2075/* XML error codes */
2076enum xml_err_code{
2077 SXML_ERROR_NONE = 1,
2078 SXML_ERROR_NO_MEMORY,
2079 SXML_ERROR_SYNTAX,
2080 SXML_ERROR_NO_ELEMENTS,
2081 SXML_ERROR_INVALID_TOKEN,
2082 SXML_ERROR_UNCLOSED_TOKEN,
2083 SXML_ERROR_PARTIAL_CHAR,
2084 SXML_ERROR_TAG_MISMATCH,
2085 SXML_ERROR_DUPLICATE_ATTRIBUTE,
2086 SXML_ERROR_JUNK_AFTER_DOC_ELEMENT,
2087 SXML_ERROR_PARAM_ENTITY_REF,
2088 SXML_ERROR_UNDEFINED_ENTITY,
2089 SXML_ERROR_RECURSIVE_ENTITY_REF,
2090 SXML_ERROR_ASYNC_ENTITY,
2091 SXML_ERROR_BAD_CHAR_REF,
2092 SXML_ERROR_BINARY_ENTITY_REF,
2093 SXML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF,
2094 SXML_ERROR_MISPLACED_XML_PI,
2095 SXML_ERROR_UNKNOWN_ENCODING,
2096 SXML_ERROR_INCORRECT_ENCODING,
2097 SXML_ERROR_UNCLOSED_CDATA_SECTION,
2098 SXML_ERROR_EXTERNAL_ENTITY_HANDLING
2099};
2100/* Each active XML SAX parser is represented by an instance
2101 * of the following structure.
2102 */
2103typedef struct SyXMLParser SyXMLParser;
2104struct SyXMLParser
2105{
2106 SyMemBackend *pAllocator; /* Memory backend */
2107 void *pUserData; /* User private data forwarded varbatim by the XML parser
2108 * as the last argument to the users callbacks.
2109 */
2110 SyHash hns; /* Namespace hashtable */
2111 SySet sToken; /* XML tokens */
2112 SyLex sLex; /* Lexical analyzer */
2113 sxi32 nFlags; /* Control flags */
2114 /* User callbacks */
2115 ProcXMLStartTagHandler xStartTag; /* Start element handler */
2116 ProcXMLEndTagHandler xEndTag; /* End element handler */
2117 ProcXMLTextHandler xRaw; /* Raw text/CDATA handler */
2118 ProcXMLDoctypeHandler xDoctype; /* DOCTYPE handler */
2119 ProcXMLPIHandler xPi; /* Processing instruction (PI) handler*/
2120 ProcXMLSyntaxErrorHandler xError; /* Error handler */
2121 ProcXMLStartDocument xStartDoc; /* StartDoc handler */
2122 ProcXMLEndDocument xEndDoc; /* EndDoc handler */
2123 ProcXMLNameSpaceStart xNameSpace; /* Namespace declaration handler */
2124 ProcXMLNameSpaceEnd xNameSpaceEnd; /* End namespace declaration handler */
2125};
2126/*
2127 * --------------
2128 * Archive extractor:
2129 * --------------
2130 * Each open ZIP/TAR archive is identified by an instance of the following structure.
2131 * That is, a process can open one or more archives and manipulates them in thread safe
2132 * way by simply working with pointers to the following structure.
2133 * Each entry in the archive is remembered in a hashtable.
2134 * Lookup is very fast and entry with the same name are chained together.
2135 */
2136 typedef struct SyArchiveEntry SyArchiveEntry;
2137 typedef struct SyArchive SyArchive;
2138 struct SyArchive
2139 {
2140 SyMemBackend *pAllocator; /* Memory backend */
2141 SyArchiveEntry *pCursor; /* Cursor for linear traversal of archive entries */
2142 SyArchiveEntry *pList; /* Pointer to the List of the loaded archive */
2143 SyArchiveEntry **apHash; /* Hashtable for archive entry */
2144 ProcRawStrCmp xCmp; /* Hash comparison function */
2145 ProcHash xHash; /* Hash Function */
2146 sxu32 nSize; /* Hashtable size */
2147 sxu32 nEntry; /* Total number of entries in the zip/tar archive */
2148 sxu32 nLoaded; /* Total number of entries loaded in memory */
2149 sxu32 nCentralOfft; /* Central directory offset(ZIP only. Otherwise Zero) */
2150 sxu32 nCentralSize; /* Central directory size(ZIP only. Otherwise Zero) */
2151 void *pUserData; /* Upper layer private data */
2152 sxu32 nMagic; /* Sanity check */
2153
2154 };
2155#define SXARCH_MAGIC 0xDEAD635A
2156#define SXARCH_INVALID(ARCH) (ARCH == 0 || ARCH->nMagic != SXARCH_MAGIC)
2157#define SXARCH_ENTRY_INVALID(ENTRY) (ENTRY == 0 || ENTRY->nMagic != SXARCH_MAGIC)
2158#define SyArchiveHashFunc(ARCH) (ARCH)->xHash
2159#define SyArchiveCmpFunc(ARCH) (ARCH)->xCmp
2160#define SyArchiveUserData(ARCH) (ARCH)->pUserData
2161#define SyArchiveSetUserData(ARCH, DATA) (ARCH)->pUserData = DATA
2162/*
2163 * Each loaded archive record is identified by an instance
2164 * of the following structure.
2165 */
2166 struct SyArchiveEntry
2167 {
2168 sxu32 nByte; /* Contents size before compression */
2169 sxu32 nByteCompr; /* Contents size after compression */
2170 sxu32 nReadCount; /* Read counter */
2171 sxu32 nCrc; /* Contents CRC32 */
2172 Sytm sFmt; /* Last-modification time */
2173 sxu32 nOfft; /* Data offset. */
2174 sxu16 nComprMeth; /* Compression method 0 == stored/8 == deflated and so on (see appnote.txt)*/
2175 sxu16 nExtra; /* Extra size if any */
2176 SyString sFileName; /* entry name & length */
2177 sxu32 nDup; /* Total number of entries with the same name */
2178 SyArchiveEntry *pNextHash, *pPrevHash; /* Hash collision chains */
2179 SyArchiveEntry *pNextName; /* Next entry with the same name */
2180 SyArchiveEntry *pNext, *pPrev; /* Next and previous entry in the list */
2181 sxu32 nHash; /* Hash of the entry name */
2182 void *pUserData; /* User data */
2183 sxu32 nMagic; /* Sanity check */
2184 };
2185 /*
2186 * Extra flags for extending the file local header
2187 */
2188#define SXZIP_EXTRA_TIMESTAMP 0x001 /* Extended UNIX timestamp */
2189#endif /* JX9_DISABLE_BUILTIN_FUNC */
2190#ifndef JX9_DISABLE_HASH_FUNC
2191/* MD5 context */
2192typedef struct MD5Context MD5Context;
2193struct MD5Context {
2194 sxu32 buf[4];
2195 sxu32 bits[2];
2196 unsigned char in[64];
2197};
2198/* SHA1 context */
2199typedef struct SHA1Context SHA1Context;
2200struct SHA1Context {
2201 unsigned int state[5];
2202 unsigned int count[2];
2203 unsigned char buffer[64];
2204};
2205#endif /* JX9_DISABLE_HASH_FUNC */
2206/* JX9 private declaration */
2207/*
2208 * Memory Objects.
2209 * Internally, the JX9 virtual machine manipulates nearly all JX9 values
2210 * [i.e: string, int, float, resource, object, bool, null] as jx9_values structures.
2211 * Each jx9_values struct may cache multiple representations (string, integer etc.)
2212 * of the same value.
2213 */
2214struct jx9_value
2215{
2216 union{
2217 jx9_real rVal; /* Real value */
2218 sxi64 iVal; /* Integer value */
2219 void *pOther; /* Other values (Object, Array, Resource, Namespace, etc.) */
2220 }x;
2221 sxi32 iFlags; /* Control flags (see below) */
2222 jx9_vm *pVm; /* VM this instance belong */
2223 SyBlob sBlob; /* String values */
2224 sxu32 nIdx; /* Object index in the global pool */
2225};
2226/* Allowed value types.
2227 */
2228#define MEMOBJ_STRING 0x001 /* Memory value is a UTF-8 string */
2229#define MEMOBJ_INT 0x002 /* Memory value is an integer */
2230#define MEMOBJ_REAL 0x004 /* Memory value is a real number */
2231#define MEMOBJ_BOOL 0x008 /* Memory value is a boolean */
2232#define MEMOBJ_NULL 0x020 /* Memory value is NULL */
2233#define MEMOBJ_HASHMAP 0x040 /* Memory value is a hashmap (JSON representation of Array and Objects) */
2234#define MEMOBJ_RES 0x100 /* Memory value is a resource [User private data] */
2235/* Mask of all known types */
2236#define MEMOBJ_ALL (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)
2237/* Scalar variables
2238 * According to the JX9 language reference manual
2239 * Scalar variables are those containing an integer, float, string or boolean.
2240 * Types array, object and resource are not scalar.
2241 */
2242#define MEMOBJ_SCALAR (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL)
2243/*
2244 * The following macro clear the current jx9_value type and replace
2245 * it with the given one.
2246 */
2247#define MemObjSetType(OBJ, TYPE) ((OBJ)->iFlags = ((OBJ)->iFlags&~MEMOBJ_ALL)|TYPE)
2248/* jx9_value cast method signature */
2249typedef sxi32 (*ProcMemObjCast)(jx9_value *);
2250/* Forward reference */
2251typedef struct jx9_output_consumer jx9_output_consumer;
2252typedef struct jx9_user_func jx9_user_func;
2253typedef struct jx9_conf jx9_conf;
2254/*
2255 * An instance of the following structure store the default VM output
2256 * consumer and it's private data.
2257 * Client-programs can register their own output consumer callback
2258 * via the [JX9_VM_CONFIG_OUTPUT] configuration directive.
2259 * Please refer to the official documentation for more information
2260 * on how to register an output consumer callback.
2261 */
2262struct jx9_output_consumer
2263{
2264 ProcConsumer xConsumer; /* VM output consumer routine */
2265 void *pUserData; /* Third argument to xConsumer() */
2266 ProcConsumer xDef; /* Default output consumer routine */
2267 void *pDefData; /* Third argument to xDef() */
2268};
2269/*
2270 * JX9 engine [i.e: jx9 instance] configuration is stored in
2271 * an instance of the following structure.
2272 * Please refer to the official documentation for more information
2273 * on how to configure your jx9 engine instance.
2274 */
2275struct jx9_conf
2276{
2277 ProcConsumer xErr; /* Compile-time error consumer callback */
2278 void *pErrData; /* Third argument to xErr() */
2279 SyBlob sErrConsumer; /* Default error consumer */
2280};
2281/*
2282 * Signature of the C function responsible of expanding constant values.
2283 */
2284typedef void (*ProcConstant)(jx9_value *, void *);
2285/*
2286 * Each registered constant [i.e: __TIME__, __DATE__, JX9_OS, INT_MAX, etc.] is stored
2287 * in an instance of the following structure.
2288 * Please refer to the official documentation for more information
2289 * on how to create/install foreign constants.
2290 */
2291typedef struct jx9_constant jx9_constant;
2292struct jx9_constant
2293{
2294 SyString sName; /* Constant name */
2295 ProcConstant xExpand; /* Function responsible of expanding constant value */
2296 void *pUserData; /* Last argument to xExpand() */
2297};
2298typedef struct jx9_aux_data jx9_aux_data;
2299/*
2300 * Auxiliary data associated with each foreign function is stored
2301 * in a stack of the following structure.
2302 * Note that automatic tracked chunks are also stored in an instance
2303 * of this structure.
2304 */
2305struct jx9_aux_data
2306{
2307 void *pAuxData; /* Aux data */
2308};
2309/* Foreign functions signature */
2310typedef int (*ProcHostFunction)(jx9_context *, int, jx9_value **);
2311/*
2312 * Each installed foreign function is recored in an instance of the following
2313 * structure.
2314 * Please refer to the official documentation for more information on how
2315 * to create/install foreign functions.
2316 */
2317struct jx9_user_func
2318{
2319 jx9_vm *pVm; /* VM that own this instance */
2320 SyString sName; /* Foreign function name */
2321 ProcHostFunction xFunc; /* Implementation of the foreign function */
2322 void *pUserData; /* User private data [Refer to the official documentation for more information]*/
2323 SySet aAux; /* Stack of auxiliary data [Refer to the official documentation for more information]*/
2324};
2325/*
2326 * The 'context' argument for an installable function. A pointer to an
2327 * instance of this structure is the first argument to the routines used
2328 * implement the foreign functions.
2329 */
2330struct jx9_context
2331{
2332 jx9_user_func *pFunc; /* Function information. */
2333 jx9_value *pRet; /* Return value is stored here. */
2334 SySet sVar; /* Container of dynamically allocated jx9_values
2335 * [i.e: Garbage collection purposes.]
2336 */
2337 SySet sChunk; /* Track dynamically allocated chunks [jx9_aux_data instance].
2338 * [i.e: Garbage collection purposes.]
2339 */
2340 jx9_vm *pVm; /* Virtual machine that own this context */
2341 sxi32 iFlags; /* Call flags */
2342};
2343/* Hashmap control flags */
2344#define HASHMAP_JSON_OBJECT 0x001 /* Hashmap represent JSON Object*/
2345/*
2346 * Each hashmap entry [i.e: array(4, 5, 6)] is recorded in an instance
2347 * of the following structure.
2348 */
2349struct jx9_hashmap_node
2350{
2351 jx9_hashmap *pMap; /* Hashmap that own this instance */
2352 sxi32 iType; /* Node type */
2353 union{
2354 sxi64 iKey; /* Int key */
2355 SyBlob sKey; /* Blob key */
2356 }xKey;
2357 sxi32 iFlags; /* Control flags */
2358 sxu32 nHash; /* Key hash value */
2359 sxu32 nValIdx; /* Value stored in this node */
2360 jx9_hashmap_node *pNext, *pPrev; /* Link to other entries [i.e: linear traversal] */
2361 jx9_hashmap_node *pNextCollide, *pPrevCollide; /* Collision chain */
2362};
2363/*
2364 * Each active hashmap aka array in the JX9 jargon is represented
2365 * by an instance of the following structure.
2366 */
2367struct jx9_hashmap
2368{
2369 jx9_vm *pVm; /* VM that own this instance */
2370 jx9_hashmap_node **apBucket; /* Hash bucket */
2371 jx9_hashmap_node *pFirst; /* First inserted entry */
2372 jx9_hashmap_node *pLast; /* Last inserted entry */
2373 jx9_hashmap_node *pCur; /* Current entry */
2374 sxu32 nSize; /* Bucket size */
2375 sxu32 nEntry; /* Total number of inserted entries */
2376 sxu32 (*xIntHash)(sxi64); /* Hash function for int_keys */
2377 sxu32 (*xBlobHash)(const void *, sxu32); /* Hash function for blob_keys */
2378 sxi32 iFlags; /* Hashmap control flags */
2379 sxi64 iNextIdx; /* Next available automatically assigned index */
2380 sxi32 iRef; /* Reference count */
2381};
2382/* An instance of the following structure is the context
2383 * for the FOREACH_STEP/FOREACH_INIT VM instructions.
2384 * Those instructions are used to implement the 'foreach'
2385 * statement.
2386 * This structure is made available to these instructions
2387 * as the P3 operand.
2388 */
2389struct jx9_foreach_info
2390{
2391 SyString sKey; /* Key name. Empty otherwise*/
2392 SyString sValue; /* Value name */
2393 sxi32 iFlags; /* Control flags */
2394 SySet aStep; /* Stack of steps [i.e: jx9_foreach_step instance] */
2395};
2396struct jx9_foreach_step
2397{
2398 sxi32 iFlags; /* Control flags (see below) */
2399 /* Iterate on this map*/
2400 jx9_hashmap *pMap; /* Hashmap [i.e: array in the JX9 jargon] iteration
2401 * Ex: foreach(array(1, 2, 3) as $key=>$value){}
2402 */
2403
2404};
2405/* Foreach step control flags */
2406#define JX9_4EACH_STEP_KEY 0x001 /* Make Key available */
2407/*
2408 * Each JX9 engine is identified by an instance of the following structure.
2409 * Please refer to the official documentation for more information
2410 * on how to configure your JX9 engine instance.
2411 */
2412struct jx9
2413{
2414 SyMemBackend sAllocator; /* Low level memory allocation subsystem */
2415 const jx9_vfs *pVfs; /* Underlying Virtual File System */
2416 jx9_conf xConf; /* Configuration */
2417#if defined(JX9_ENABLE_THREADS)
2418 SyMutex *pMutex; /* Per-engine mutex */
2419#endif
2420 jx9_vm *pVms; /* List of active VM */
2421 sxi32 iVm; /* Total number of active VM */
2422 jx9 *pNext, *pPrev; /* List of active engines */
2423 sxu32 nMagic; /* Sanity check against misuse */
2424};
2425/* Code generation data structures */
2426typedef sxi32 (*ProcErrorGen)(void *, sxi32, sxu32, const char *, ...);
2427typedef struct jx9_expr_node jx9_expr_node;
2428typedef struct jx9_expr_op jx9_expr_op;
2429typedef struct jx9_gen_state jx9_gen_state;
2430typedef struct GenBlock GenBlock;
2431typedef sxi32 (*ProcLangConstruct)(jx9_gen_state *);
2432typedef sxi32 (*ProcNodeConstruct)(jx9_gen_state *, sxi32);
2433/*
2434 * Each supported operator [i.e: +, -, ==, *, %, >>, >=, new, etc.] is represented
2435 * by an instance of the following structure.
2436 * The JX9 parser does not use any external tools and is 100% handcoded.
2437 * That is, the JX9 parser is thread-safe , full reentrant, produce consistant
2438 * compile-time errrors and at least 7 times faster than the standard JX9 parser.
2439 */
2440struct jx9_expr_op
2441{
2442 SyString sOp; /* String representation of the operator [i.e: "+", "*", "=="...] */
2443 sxi32 iOp; /* Operator ID */
2444 sxi32 iPrec; /* Operator precedence: 1 == Highest */
2445 sxi32 iAssoc; /* Operator associativity (either left, right or non-associative) */
2446 sxi32 iVmOp; /* VM OP code for this operator [i.e: JX9_OP_EQ, JX9_OP_LT, JX9_OP_MUL...]*/
2447};
2448/*
2449 * Each expression node is parsed out and recorded
2450 * in an instance of the following structure.
2451 */
2452struct jx9_expr_node
2453{
2454 const jx9_expr_op *pOp; /* Operator ID or NULL if literal, constant, variable, function or object method call */
2455 jx9_expr_node *pLeft; /* Left expression tree */
2456 jx9_expr_node *pRight; /* Right expression tree */
2457 SyToken *pStart; /* Stream of tokens that belong to this node */
2458 SyToken *pEnd; /* End of token stream */
2459 sxi32 iFlags; /* Node construct flags */
2460 ProcNodeConstruct xCode; /* C routine responsible of compiling this node */
2461 SySet aNodeArgs; /* Node arguments. Only used by postfix operators [i.e: function call]*/
2462 jx9_expr_node *pCond; /* Condition: Only used by the ternary operator '?:' */
2463};
2464/* Node Construct flags */
2465#define EXPR_NODE_PRE_INCR 0x01 /* Pre-icrement/decrement [i.e: ++$i, --$j] node */
2466/*
2467 * A block of instructions is recorded in an instance of the following structure.
2468 * This structure is used only during compile-time and have no meaning
2469 * during bytecode execution.
2470 */
2471struct GenBlock
2472{
2473 jx9_gen_state *pGen; /* State of the code generator */
2474 GenBlock *pParent; /* Upper block or NULL if global */
2475 sxu32 nFirstInstr; /* First instruction to execute */
2476 sxi32 iFlags; /* Block control flags (see below) */
2477 SySet aJumpFix; /* Jump fixup (JumpFixup instance) */
2478 void *pUserData; /* Upper layer private data */
2479 /* The following two fields are used only when compiling
2480 * the 'do..while()' language construct.
2481 */
2482 sxu8 bPostContinue; /* TRUE when compiling the do..while() statement */
2483 SySet aPostContFix; /* Post-continue jump fix */
2484};
2485/*
2486 * Code generator state is remembered in an instance of the following
2487 * structure. We put the information in this structure and pass around
2488 * a pointer to this structure, rather than pass around all of the
2489 * information separately. This helps reduce the number of arguments
2490 * to generator functions.
2491 * This structure is used only during compile-time and have no meaning
2492 * during bytecode execution.
2493 */
2494struct jx9_gen_state
2495{
2496 jx9_vm *pVm; /* VM that own this instance */
2497 SyHash hLiteral; /* Constant string Literals table */
2498 SyHash hNumLiteral; /* Numeric literals table */
2499 SyHash hVar; /* Collected variable hashtable */
2500 GenBlock *pCurrent; /* Current processed block */
2501 GenBlock sGlobal; /* Global block */
2502 ProcConsumer xErr; /* Error consumer callback */
2503 void *pErrData; /* Third argument to xErr() */
2504 SyToken *pIn; /* Current processed token */
2505 SyToken *pEnd; /* Last token in the stream */
2506 sxu32 nErr; /* Total number of compilation error */
2507};
2508/* Forward references */
2509typedef struct jx9_vm_func_static_var jx9_vm_func_static_var;
2510typedef struct jx9_vm_func_arg jx9_vm_func_arg;
2511typedef struct jx9_vm_func jx9_vm_func;
2512typedef struct VmFrame VmFrame;
2513/*
2514 * Each collected function argument is recorded in an instance
2515 * of the following structure.
2516 * Note that as an extension, JX9 implements full type hinting
2517 * which mean that any function can have it's own signature.
2518 * Example:
2519 * function foo(int $a, string $b, float $c, ClassInstance $d){}
2520 * This is how the powerful function overloading mechanism is
2521 * implemented.
2522 * Note that as an extension, JX9 allow function arguments to have
2523 * any complex default value associated with them unlike the standard
2524 * JX9 engine.
2525 * Example:
2526 * function foo(int $a = rand() & 1023){}
2527 * now, when foo is called without arguments [i.e: foo()] the
2528 * $a variable (first parameter) will be set to a random number
2529 * between 0 and 1023 inclusive.
2530 * Refer to the official documentation for more information on this
2531 * mechanism and other extension introduced by the JX9 engine.
2532 */
2533struct jx9_vm_func_arg
2534{
2535 SyString sName; /* Argument name */
2536 SySet aByteCode; /* Compiled default value associated with this argument */
2537 sxu32 nType; /* Type of this argument [i.e: array, int, string, float, object, etc.] */
2538 sxi32 iFlags; /* Configuration flags */
2539};
2540/*
2541 * Each static variable is parsed out and remembered in an instance
2542 * of the following structure.
2543 * Note that as an extension, JX9 allow static variable have
2544 * any complex default value associated with them unlike the standard
2545 * JX9 engine.
2546 * Example:
2547 * static $rand_str = 'JX9'.rand_str(3); // Concatenate 'JX9' with
2548 * // a random three characters(English alphabet)
2549 * dump($rand_str);
2550 * //You should see something like this
2551 * string(6 'JX9awt');
2552 */
2553struct jx9_vm_func_static_var
2554{
2555 SyString sName; /* Static variable name */
2556 SySet aByteCode; /* Compiled initialization expression */
2557 sxu32 nIdx; /* Object index in the global memory object container */
2558};
2559/* Function configuration flags */
2560#define VM_FUNC_ARG_HAS_DEF 0x001 /* Argument has default value associated with it */
2561#define VM_FUNC_ARG_IGNORE 0x002 /* Do not install argument in the current frame */
2562/*
2563 * Each user defined function is parsed out and stored in an instance
2564 * of the following structure.
2565 * JX9 introduced some powerfull extensions to the JX9 5 programming
2566 * language like function overloading, type hinting, complex default
2567 * arguments values and many more.
2568 * Please refer to the official documentation for more information.
2569 */
2570struct jx9_vm_func
2571{
2572 SySet aArgs; /* Expected arguments (jx9_vm_func_arg instance) */
2573 SySet aStatic; /* Static variable (jx9_vm_func_static_var instance) */
2574 SyString sName; /* Function name */
2575 SySet aByteCode; /* Compiled function body */
2576 sxi32 iFlags; /* VM function configuration */
2577 SyString sSignature; /* Function signature used to implement function overloading
2578 * (Refer to the official docuemntation for more information
2579 * on this powerfull feature)
2580 */
2581 void *pUserData; /* Upper layer private data associated with this instance */
2582 jx9_vm_func *pNextName; /* Next VM function with the same name as this one */
2583};
2584/* Forward reference */
2585typedef struct jx9_builtin_constant jx9_builtin_constant;
2586typedef struct jx9_builtin_func jx9_builtin_func;
2587/*
2588 * Each built-in foreign function (C function) is stored in an
2589 * instance of the following structure.
2590 * Please refer to the official documentation for more information
2591 * on how to create/install foreign functions.
2592 */
2593struct jx9_builtin_func
2594{
2595 const char *zName; /* Function name [i.e: strlen(), rand(), array_merge(), etc.]*/
2596 ProcHostFunction xFunc; /* C routine performing the computation */
2597};
2598/*
2599 * Each built-in foreign constant is stored in an instance
2600 * of the following structure.
2601 * Please refer to the official documentation for more information
2602 * on how to create/install foreign constants.
2603 */
2604struct jx9_builtin_constant
2605{
2606 const char *zName; /* Constant name */
2607 ProcConstant xExpand; /* C routine responsible of expanding constant value*/
2608};
2609/*
2610 * A single instruction of the virtual machine has an opcode
2611 * and as many as three operands.
2612 * Each VM instruction resulting from compiling a JX9 script
2613 * is stored in an instance of the following structure.
2614 */
2615typedef struct VmInstr VmInstr;
2616struct VmInstr
2617{
2618 sxu8 iOp; /* Operation to preform */
2619 sxi32 iP1; /* First operand */
2620 sxu32 iP2; /* Second operand (Often the jump destination) */
2621 void *p3; /* Third operand (Often Upper layer private data) */
2622};
2623/* Forward reference */
2624typedef struct jx9_case_expr jx9_case_expr;
2625typedef struct jx9_switch jx9_switch;
2626/*
2627 * Each compiled case block in a swicth statement is compiled
2628 * and stored in an instance of the following structure.
2629 */
2630struct jx9_case_expr
2631{
2632 SySet aByteCode; /* Compiled body of the case block */
2633 sxu32 nStart; /* First instruction to execute */
2634};
2635/*
2636 * Each compiled switch statement is parsed out and stored
2637 * in an instance of the following structure.
2638 */
2639struct jx9_switch
2640{
2641 SySet aCaseExpr; /* Compile case block */
2642 sxu32 nOut; /* First instruction to execute after this statement */
2643 sxu32 nDefault; /* First instruction to execute in the default block */
2644};
2645/* Assertion flags */
2646#define JX9_ASSERT_DISABLE 0x01 /* Disable assertion */
2647#define JX9_ASSERT_WARNING 0x02 /* Issue a warning for each failed assertion */
2648#define JX9_ASSERT_BAIL 0x04 /* Terminate execution on failed assertions */
2649#define JX9_ASSERT_QUIET_EVAL 0x08 /* Not used */
2650#define JX9_ASSERT_CALLBACK 0x10 /* Callback to call on failed assertions */
2651/*
2652 * An instance of the following structure hold the bytecode instructions
2653 * resulting from compiling a JX9 script.
2654 * This structure contains the complete state of the virtual machine.
2655 */
2656struct jx9_vm
2657{
2658 SyMemBackend sAllocator; /* Memory backend */
2659#if defined(JX9_ENABLE_THREADS)
2660 SyMutex *pMutex; /* Recursive mutex associated with this VM. */
2661#endif
2662 jx9 *pEngine; /* Interpreter that own this VM */
2663 SySet aByteCode; /* Default bytecode container */
2664 SySet *pByteContainer; /* Current bytecode container */
2665 VmFrame *pFrame; /* Stack of active frames */
2666 SyPRNGCtx sPrng; /* PRNG context */
2667 SySet aMemObj; /* Object allocation table */
2668 SySet aLitObj; /* Literals allocation table */
2669 jx9_value *aOps; /* Operand stack */
2670 SySet aFreeObj; /* Stack of free memory objects */
2671 SyHash hConstant; /* Host-application and user defined constants container */
2672 SyHash hHostFunction; /* Host-application installable functions */
2673 SyHash hFunction; /* Compiled functions */
2674 SyHash hSuper; /* Global variable */
2675 SyBlob sConsumer; /* Default VM consumer [i.e Redirect all VM output to this blob] */
2676 SyBlob sWorker; /* General purpose working buffer */
2677 SyBlob sArgv; /* $argv[] collector [refer to the [getopt()] implementation for more information] */
2678 SySet aFiles; /* Stack of processed files */
2679 SySet aPaths; /* Set of import paths */
2680 SySet aIncluded; /* Set of included files */
2681 SySet aIOstream; /* Installed IO stream container */
2682 const jx9_io_stream *pDefStream; /* Default IO stream [i.e: typically this is the 'file://' stream] */
2683 jx9_value sExec; /* Compiled script return value [Can be extracted via the JX9_VM_CONFIG_EXEC_VALUE directive]*/
2684 void *pStdin; /* STDIN IO stream */
2685 void *pStdout; /* STDOUT IO stream */
2686 void *pStderr; /* STDERR IO stream */
2687 int bErrReport; /* TRUE to report all runtime Error/Warning/Notice */
2688 int nRecursionDepth; /* Current recursion depth */
2689 int nMaxDepth; /* Maximum allowed recusion depth */
2690 sxu32 nOutputLen; /* Total number of generated output */
2691 jx9_output_consumer sVmConsumer; /* Registered output consumer callback */
2692 int iAssertFlags; /* Assertion flags */
2693 jx9_value sAssertCallback; /* Callback to call on failed assertions */
2694 sxi32 iExitStatus; /* Script exit status */
2695 jx9_gen_state sCodeGen; /* Code generator module */
2696 jx9_vm *pNext, *pPrev; /* List of active VM's */
2697 sxu32 nMagic; /* Sanity check against misuse */
2698};
2699/*
2700 * Allowed value for jx9_vm.nMagic
2701 */
2702#define JX9_VM_INIT 0xEA12CD72 /* VM correctly initialized */
2703#define JX9_VM_RUN 0xBA851227 /* VM ready to execute JX9 bytecode */
2704#define JX9_VM_EXEC 0xCDFE1DAD /* VM executing JX9 bytecode */
2705#define JX9_VM_STALE 0xDEAD2BAD /* Stale VM */
2706/*
2707 * Error codes according to the JX9 language reference manual.
2708 */
2709enum iErrCode
2710{
2711 E_ERROR = 1, /* Fatal run-time errors. These indicate errors that can not be recovered
2712 * from, such as a memory allocation problem. Execution of the script is
2713 * halted.
2714 * The only fatal error under JX9 is an out-of-memory. All others erros
2715 * even a call to undefined function will not halt script execution.
2716 */
2717 E_WARNING , /* Run-time warnings (non-fatal errors). Execution of the script is not halted. */
2718 E_PARSE , /* Compile-time parse errors. Parse errors should only be generated by the parser.*/
2719 E_NOTICE , /* Run-time notices. Indicate that the script encountered something that could
2720 * indicate an error, but could also happen in the normal course of running a script.
2721 */
2722};
2723/*
2724 * Each VM instruction resulting from compiling a JX9 script is represented
2725 * by one of the following OP codes.
2726 * The program consists of a linear sequence of operations. Each operation
2727 * has an opcode and 3 operands.Operands P1 is an integer.
2728 * Operand P2 is an unsigned integer and operand P3 is a memory address.
2729 * Few opcodes use all 3 operands.
2730 */
2731enum jx9_vm_op {
2732 JX9_OP_DONE = 1, /* Done */
2733 JX9_OP_HALT, /* Halt */
2734 JX9_OP_LOAD, /* Load memory object */
2735 JX9_OP_LOADC, /* Load constant */
2736 JX9_OP_LOAD_IDX, /* Load array entry */
2737 JX9_OP_LOAD_MAP, /* Load hashmap('array') */
2738 JX9_OP_NOOP, /* NOOP */
2739 JX9_OP_JMP, /* Unconditional jump */
2740 JX9_OP_JZ, /* Jump on zero (FALSE jump) */
2741 JX9_OP_JNZ, /* Jump on non-zero (TRUE jump) */
2742 JX9_OP_POP, /* Stack POP */
2743 JX9_OP_CAT, /* Concatenation */
2744 JX9_OP_CVT_INT, /* Integer cast */
2745 JX9_OP_CVT_STR, /* String cast */
2746 JX9_OP_CVT_REAL, /* Float cast */
2747 JX9_OP_CALL, /* Function call */
2748 JX9_OP_UMINUS, /* Unary minus '-'*/
2749 JX9_OP_UPLUS, /* Unary plus '+'*/
2750 JX9_OP_BITNOT, /* Bitwise not '~' */
2751 JX9_OP_LNOT, /* Logical not '!' */
2752 JX9_OP_MUL, /* Multiplication '*' */
2753 JX9_OP_DIV, /* Division '/' */
2754 JX9_OP_MOD, /* Modulus '%' */
2755 JX9_OP_ADD, /* Add '+' */
2756 JX9_OP_SUB, /* Sub '-' */
2757 JX9_OP_SHL, /* Left shift '<<' */
2758 JX9_OP_SHR, /* Right shift '>>' */
2759 JX9_OP_LT, /* Less than '<' */
2760 JX9_OP_LE, /* Less or equal '<=' */
2761 JX9_OP_GT, /* Greater than '>' */
2762 JX9_OP_GE, /* Greater or equal '>=' */
2763 JX9_OP_EQ, /* Equal '==' */
2764 JX9_OP_NEQ, /* Not equal '!=' */
2765 JX9_OP_TEQ, /* Type equal '===' */
2766 JX9_OP_TNE, /* Type not equal '!==' */
2767 JX9_OP_BAND, /* Bitwise and '&' */
2768 JX9_OP_BXOR, /* Bitwise xor '^' */
2769 JX9_OP_BOR, /* Bitwise or '|' */
2770 JX9_OP_LAND, /* Logical and '&&','and' */
2771 JX9_OP_LOR, /* Logical or '||','or' */
2772 JX9_OP_LXOR, /* Logical xor 'xor' */
2773 JX9_OP_STORE, /* Store Object */
2774 JX9_OP_STORE_IDX, /* Store indexed object */
2775 JX9_OP_PULL, /* Stack pull */
2776 JX9_OP_SWAP, /* Stack swap */
2777 JX9_OP_YIELD, /* Stack yield */
2778 JX9_OP_CVT_BOOL, /* Boolean cast */
2779 JX9_OP_CVT_NUMC, /* Numeric (integer, real or both) type cast */
2780 JX9_OP_INCR, /* Increment ++ */
2781 JX9_OP_DECR, /* Decrement -- */
2782 JX9_OP_ADD_STORE, /* Add and store '+=' */
2783 JX9_OP_SUB_STORE, /* Sub and store '-=' */
2784 JX9_OP_MUL_STORE, /* Mul and store '*=' */
2785 JX9_OP_DIV_STORE, /* Div and store '/=' */
2786 JX9_OP_MOD_STORE, /* Mod and store '%=' */
2787 JX9_OP_CAT_STORE, /* Cat and store '.=' */
2788 JX9_OP_SHL_STORE, /* Shift left and store '>>=' */
2789 JX9_OP_SHR_STORE, /* Shift right and store '<<=' */
2790 JX9_OP_BAND_STORE, /* Bitand and store '&=' */
2791 JX9_OP_BOR_STORE, /* Bitor and store '|=' */
2792 JX9_OP_BXOR_STORE, /* Bitxor and store '^=' */
2793 JX9_OP_CONSUME, /* Consume VM output */
2794 JX9_OP_MEMBER, /* Object member run-time access */
2795 JX9_OP_UPLINK, /* Run-Time frame link */
2796 JX9_OP_CVT_NULL, /* NULL cast */
2797 JX9_OP_CVT_ARRAY, /* Array cast */
2798 JX9_OP_FOREACH_INIT, /* For each init */
2799 JX9_OP_FOREACH_STEP, /* For each step */
2800 JX9_OP_SWITCH /* Switch operation */
2801};
2802/* -- END-OF INSTRUCTIONS -- */
2803/*
2804 * Expression Operators ID.
2805 */
2806enum jx9_expr_id {
2807 EXPR_OP_DOT, /* Member access */
2808 EXPR_OP_DC, /* :: */
2809 EXPR_OP_SUBSCRIPT, /* []: Subscripting */
2810 EXPR_OP_FUNC_CALL, /* func_call() */
2811 EXPR_OP_INCR, /* ++ */
2812 EXPR_OP_DECR, /* -- */
2813 EXPR_OP_BITNOT, /* ~ */
2814 EXPR_OP_UMINUS, /* Unary minus */
2815 EXPR_OP_UPLUS, /* Unary plus */
2816 EXPR_OP_TYPECAST, /* Type cast [i.e: (int), (float), (string)...] */
2817 EXPR_OP_ALT, /* @ */
2818 EXPR_OP_INSTOF, /* instanceof */
2819 EXPR_OP_LOGNOT, /* logical not ! */
2820 EXPR_OP_MUL, /* Multiplication */
2821 EXPR_OP_DIV, /* division */
2822 EXPR_OP_MOD, /* Modulus */
2823 EXPR_OP_ADD, /* Addition */
2824 EXPR_OP_SUB, /* Substraction */
2825 EXPR_OP_DDOT, /* Concatenation */
2826 EXPR_OP_SHL, /* Left shift */
2827 EXPR_OP_SHR, /* Right shift */
2828 EXPR_OP_LT, /* Less than */
2829 EXPR_OP_LE, /* Less equal */
2830 EXPR_OP_GT, /* Greater than */
2831 EXPR_OP_GE, /* Greater equal */
2832 EXPR_OP_EQ, /* Equal == */
2833 EXPR_OP_NE, /* Not equal != <> */
2834 EXPR_OP_TEQ, /* Type equal === */
2835 EXPR_OP_TNE, /* Type not equal !== */
2836 EXPR_OP_SEQ, /* String equal 'eq' */
2837 EXPR_OP_SNE, /* String not equal 'ne' */
2838 EXPR_OP_BAND, /* Biwise and '&' */
2839 EXPR_OP_REF, /* Reference operator '&' */
2840 EXPR_OP_XOR, /* bitwise xor '^' */
2841 EXPR_OP_BOR, /* bitwise or '|' */
2842 EXPR_OP_LAND, /* Logical and '&&','and' */
2843 EXPR_OP_LOR, /* Logical or '||','or'*/
2844 EXPR_OP_LXOR, /* Logical xor 'xor' */
2845 EXPR_OP_QUESTY, /* Ternary operator '?' */
2846 EXPR_OP_ASSIGN, /* Assignment '=' */
2847 EXPR_OP_ADD_ASSIGN, /* Combined operator: += */
2848 EXPR_OP_SUB_ASSIGN, /* Combined operator: -= */
2849 EXPR_OP_MUL_ASSIGN, /* Combined operator: *= */
2850 EXPR_OP_DIV_ASSIGN, /* Combined operator: /= */
2851 EXPR_OP_MOD_ASSIGN, /* Combined operator: %= */
2852 EXPR_OP_DOT_ASSIGN, /* Combined operator: .= */
2853 EXPR_OP_AND_ASSIGN, /* Combined operator: &= */
2854 EXPR_OP_OR_ASSIGN, /* Combined operator: |= */
2855 EXPR_OP_XOR_ASSIGN, /* Combined operator: ^= */
2856 EXPR_OP_SHL_ASSIGN, /* Combined operator: <<= */
2857 EXPR_OP_SHR_ASSIGN, /* Combined operator: >>= */
2858 EXPR_OP_COMMA /* Comma expression */
2859};
2860/*
2861 * Lexer token codes
2862 * The following set of constants are the tokens recognized
2863 * by the lexer when processing JX9 input.
2864 * Important: Token values MUST BE A POWER OF TWO.
2865 */
2866#define JX9_TK_INTEGER 0x0000001 /* Integer */
2867#define JX9_TK_REAL 0x0000002 /* Real number */
2868#define JX9_TK_NUM (JX9_TK_INTEGER|JX9_TK_REAL) /* Numeric token, either integer or real */
2869#define JX9_TK_KEYWORD 0x0000004 /* Keyword [i.e: while, for, if, foreach...] */
2870#define JX9_TK_ID 0x0000008 /* Alphanumeric or UTF-8 stream */
2871#define JX9_TK_DOLLAR 0x0000010 /* '$' Dollar sign */
2872#define JX9_TK_OP 0x0000020 /* Operator [i.e: +, *, /...] */
2873#define JX9_TK_OCB 0x0000040 /* Open curly brace'{' */
2874#define JX9_TK_CCB 0x0000080 /* Closing curly brace'}' */
2875#define JX9_TK_DOT 0x0000100 /* Dot . */
2876#define JX9_TK_LPAREN 0x0000200 /* Left parenthesis '(' */
2877#define JX9_TK_RPAREN 0x0000400 /* Right parenthesis ')' */
2878#define JX9_TK_OSB 0x0000800 /* Open square bracket '[' */
2879#define JX9_TK_CSB 0x0001000 /* Closing square bracket ']' */
2880#define JX9_TK_DSTR 0x0002000 /* Double quoted string "$str" */
2881#define JX9_TK_SSTR 0x0004000 /* Single quoted string 'str' */
2882#define JX9_TK_NOWDOC 0x0010000 /* Nowdoc <<< */
2883#define JX9_TK_COMMA 0x0020000 /* Comma ',' */
2884#define JX9_TK_SEMI 0x0040000 /* Semi-colon ";" */
2885#define JX9_TK_BSTR 0x0080000 /* Backtick quoted string [i.e: Shell command `date`] */
2886#define JX9_TK_COLON 0x0100000 /* single Colon ':' */
2887#define JX9_TK_AMPER 0x0200000 /* Ampersand '&' */
2888#define JX9_TK_EQUAL 0x0400000 /* Equal '=' */
2889#define JX9_TK_OTHER 0x1000000 /* Other symbols */
2890/*
2891 * JX9 keyword.
2892 * These words have special meaning in JX9. Some of them represent things which look like
2893 * functions, some look like constants, and so on, but they're not, really: they are language constructs.
2894 * You cannot use any of the following words as constants, object names, function or method names.
2895 * Using them as variable names is generally OK, but could lead to confusion.
2896 */
2897#define JX9_TKWRD_SWITCH 1 /* switch */
2898#define JX9_TKWRD_PRINT 2 /* print */
2899#define JX9_TKWRD_ELIF 0x4000000 /* elseif: MUST BE A POWER OF TWO */
2900#define JX9_TKWRD_ELSE 0x8000000 /* else: MUST BE A POWER OF TWO */
2901#define JX9_TKWRD_IF 3 /* if */
2902#define JX9_TKWRD_STATIC 4 /* static */
2903#define JX9_TKWRD_CASE 5 /* case */
2904#define JX9_TKWRD_FUNCTION 6 /* function */
2905#define JX9_TKWRD_CONST 7 /* const */
2906/* The number '8' is reserved for JX9_TK_ID */
2907#define JX9_TKWRD_WHILE 9 /* while */
2908#define JX9_TKWRD_DEFAULT 10 /* default */
2909#define JX9_TKWRD_AS 11 /* as */
2910#define JX9_TKWRD_CONTINUE 12 /* continue */
2911#define JX9_TKWRD_EXIT 13 /* exit */
2912#define JX9_TKWRD_DIE 14 /* die */
2913#define JX9_TKWRD_IMPORT 15 /* import */
2914#define JX9_TKWRD_INCLUDE 16 /* include */
2915#define JX9_TKWRD_FOR 17 /* for */
2916#define JX9_TKWRD_FOREACH 18 /* foreach */
2917#define JX9_TKWRD_RETURN 19 /* return */
2918#define JX9_TKWRD_BREAK 20 /* break */
2919#define JX9_TKWRD_UPLINK 21 /* uplink */
2920#define JX9_TKWRD_BOOL 0x8000 /* bool: MUST BE A POWER OF TWO */
2921#define JX9_TKWRD_INT 0x10000 /* int: MUST BE A POWER OF TWO */
2922#define JX9_TKWRD_FLOAT 0x20000 /* float: MUST BE A POWER OF TWO */
2923#define JX9_TKWRD_STRING 0x40000 /* string: MUST BE A POWER OF TWO */
2924
2925/* api.c */
2926JX9_PRIVATE sxi32 jx9EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap);
2927JX9_PRIVATE int jx9DeleteFunction(jx9_vm *pVm,const char *zName);
2928JX9_PRIVATE int Jx9DeleteConstant(jx9_vm *pVm,const char *zName);
2929/* json.c function prototypes */
2930JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut);
2931JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte);
2932/* memobj.c function prototypes */
2933JX9_PRIVATE sxi32 jx9MemObjDump(SyBlob *pOut, jx9_value *pObj);
2934JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal);
2935JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore);
2936JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest);
2937JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal);
2938JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray);
2939#if 0
2940/* Not used in the current release of the JX9 engine */
2941JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal);
2942#endif
2943JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal);
2944JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal);
2945JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj);
2946JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen);
2947#if 0
2948/* Not used in the current release of the JX9 engine */
2949JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap);
2950#endif
2951JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest);
2952JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest);
2953JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj);
2954JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj);
2955JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj);
2956JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags);
2957JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj);
2958JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj);
2959JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj);
2960JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj);
2961JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj);
2962JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj);
2963JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj);
2964JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj);
2965JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pData);
2966/* lex.c function prototypes */
2967JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput, sxu32 nLen, SySet *pOut);
2968/* vm.c function prototypes */
2969JX9_PRIVATE void jx9VmReleaseContextValue(jx9_context *pCtx, jx9_value *pValue);
2970JX9_PRIVATE sxi32 jx9VmInitFuncState(jx9_vm *pVm, jx9_vm_func *pFunc, const char *zName, sxu32 nByte,
2971 sxi32 iFlags, void *pUserData);
2972JX9_PRIVATE sxi32 jx9VmInstallUserFunction(jx9_vm *pVm, jx9_vm_func *pFunc, SyString *pName);
2973JX9_PRIVATE sxi32 jx9VmRegisterConstant(jx9_vm *pVm, const SyString *pName, ProcConstant xExpand, void *pUserData);
2974JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(jx9_vm *pVm, const SyString *pName, ProcHostFunction xFunc, void *pUserData);
2975JX9_PRIVATE sxi32 jx9VmBlobConsumer(const void *pSrc, unsigned int nLen, void *pUserData);
2976JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIndex);
2977JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex);
2978JX9_PRIVATE sxi32 jx9VmOutputConsume(jx9_vm *pVm, SyString *pString);
2979JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(jx9_vm *pVm, const char *zFormat, va_list ap);
2980JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap);
2981JX9_PRIVATE sxi32 jx9VmThrowError(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zMessage);
2982JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData);
2983JX9_PRIVATE sxi32 jx9VmDump(jx9_vm *pVm, ProcConsumer xConsumer, void *pUserData);
2984JX9_PRIVATE sxi32 jx9VmInit(jx9_vm *pVm, jx9 *pEngine);
2985JX9_PRIVATE sxi32 jx9VmConfigure(jx9_vm *pVm, sxi32 nOp, va_list ap);
2986JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm);
2987JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar);
2988JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm);
2989JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm);
2990JX9_PRIVATE sxi32 jx9VmMakeReady(jx9_vm *pVm);
2991JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm);
2992JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm);
2993JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm);
2994JX9_PRIVATE VmInstr *jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex);
2995JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm);
2996JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer);
2997JX9_PRIVATE sxi32 jx9VmEmitInstr(jx9_vm *pVm, sxi32 iOp, sxi32 iP1, sxu32 iP2, void *p3, sxu32 *pIndex);
2998JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm);
2999JX9_PRIVATE sxi32 jx9VmCallUserFunction(jx9_vm *pVm, jx9_value *pFunc, int nArg, jx9_value **apArg, jx9_value *pResult);
3000JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(jx9_vm *pVm, jx9_value *pFunc, jx9_value *pResult, ...);
3001JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm, sxu32 nObjIdx);
3002JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen);
3003JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue);
3004JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew);
3005#ifndef JX9_DISABLE_BUILTIN_FUNC
3006JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(jx9_vm *pVm, const char **pzDevice, int nByte);
3007#endif /* JX9_DISABLE_BUILTIN_FUNC */
3008JX9_PRIVATE int jx9Utf8Read(
3009 const unsigned char *z, /* First byte of UTF-8 character */
3010 const unsigned char *zTerm, /* Pretend this byte is 0x00 */
3011 const unsigned char **pzNext /* Write first byte past UTF-8 char here */
3012);
3013/* parse.c function prototypes */
3014JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID);
3015JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot);
3016JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart, SyToken *pEnd, SyToken **ppNext);
3017JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn, SyToken *pEnd, sxu32 nTokStart, sxu32 nTokEnd, SyToken **ppEnd);
3018JX9_PRIVATE const jx9_expr_op * jx9ExprExtractOperator(SyString *pStr, SyToken *pLast);
3019JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet);
3020/* compile.c function prototypes */
3021JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType);
3022JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen, sxi32 iCompileFlag);
3023JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag);
3024JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag);
3025JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen, sxi32 iCompileFlag);
3026JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen, sxi32 iCompileFlag);
3027JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag);
3028JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag);
3029JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen, sxi32 iCompileFlag);
3030JX9_PRIVATE sxi32 jx9InitCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData);
3031JX9_PRIVATE sxi32 jx9ResetCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData);
3032JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen, sxi32 nErrType, sxu32 nLine, const char *zFormat, ...);
3033JX9_PRIVATE sxi32 jx9CompileScript(jx9_vm *pVm, SyString *pScript, sxi32 iFlags);
3034/* constant.c function prototypes */
3035JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm);
3036/* builtin.c function prototypes */
3037JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm);
3038/* hashmap.c function prototypes */
3039JX9_PRIVATE jx9_hashmap * jx9NewHashmap(jx9_vm *pVm, sxu32 (*xIntHash)(sxi64), sxu32 (*xBlobHash)(const void *, sxu32));
3040JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm);
3041JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS);
3042JX9_PRIVATE void jx9HashmapUnref(jx9_hashmap *pMap);
3043JX9_PRIVATE sxi32 jx9HashmapLookup(jx9_hashmap *pMap, jx9_value *pKey, jx9_hashmap_node **ppNode);
3044JX9_PRIVATE sxi32 jx9HashmapInsert(jx9_hashmap *pMap, jx9_value *pKey, jx9_value *pVal);
3045JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight);
3046JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest);
3047JX9_PRIVATE sxi32 jx9HashmapCmp(jx9_hashmap *pLeft, jx9_hashmap *pRight, int bStrict);
3048JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap);
3049JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap);
3050JX9_PRIVATE jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode);
3051JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore);
3052JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode, jx9_value *pKey);
3053JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm);
3054JX9_PRIVATE sxi32 jx9HashmapWalk(jx9_hashmap *pMap, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
3055#ifndef JX9_DISABLE_BUILTIN_FUNC
3056JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut);
3057/* builtin.c function prototypes */
3058JX9_PRIVATE sxi32 jx9InputFormat(int (*xConsumer)(jx9_context *, const char *, int, void *),
3059 jx9_context *pCtx, const char *zIn, int nByte, int nArg, jx9_value **apArg, void *pUserData, int vf);
3060JX9_PRIVATE sxi32 jx9ProcessCsv(const char *zInput, int nByte, int delim, int encl,
3061 int escape, sxi32 (*xConsumer)(const char *, int, void *), void *pUserData);
3062JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData);
3063JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen);
3064JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection);
3065#endif
3066/* vfs.c */
3067#ifndef JX9_DISABLE_BUILTIN_FUNC
3068JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile,
3069 int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew);
3070JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut);
3071JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle);
3072#endif /* JX9_DISABLE_BUILTIN_FUNC */
3073JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen);
3074JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm);
3075JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void);
3076JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm);
3077JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm);
3078JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm);
3079/* lib.c function prototypes */
3080#ifndef JX9_DISABLE_BUILTIN_FUNC
3081JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp);
3082JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch);
3083JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch);
3084JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry);
3085JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen);
3086#endif /* JX9_DISABLE_BUILTIN_FUNC */
3087#ifndef JX9_DISABLE_BUILTIN_FUNC
3088JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData);
3089#endif /* JX9_DISABLE_BUILTIN_FUNC */
3090#ifndef JX9_DISABLE_BUILTIN_FUNC
3091#ifndef JX9_DISABLE_HASH_FUNC
3092JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen);
3093JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len);
3094JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx);
3095JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx);
3096JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16]);
3097JX9_PRIVATE void SHA1Init(SHA1Context *context);
3098JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len);
3099JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]);
3100JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20]);
3101#endif
3102#endif /* JX9_DISABLE_BUILTIN_FUNC */
3103JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen);
3104JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void *pUserData);
3105JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...);
3106JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap);
3107JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...);
3108JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...);
3109#ifndef JX9_DISABLE_BUILTIN_FUNC
3110JX9_PRIVATE const char *SyTimeGetMonth(sxi32 iMonth);
3111JX9_PRIVATE const char *SyTimeGetDay(sxi32 iDay);
3112#endif /* JX9_DISABLE_BUILTIN_FUNC */
3113JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8);
3114#ifndef JX9_DISABLE_BUILTIN_FUNC
3115JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
3116#endif
3117JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex);
3118JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp);
3119JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData);
3120#ifndef JX9_DISABLE_BUILTIN_FUNC
3121JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
3122JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
3123#endif /* JX9_DISABLE_BUILTIN_FUNC */
3124JX9_PRIVATE sxu32 SyBinHash(const void *pSrc, sxu32 nLen);
3125JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
3126JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
3127JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
3128JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
3129JX9_PRIVATE sxi32 SyHexToint(sxi32 c);
3130JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
3131JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
3132JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char **pzTail);
3133JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData);
3134JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32(*xStep)(SyHashEntry *, void *), void *pUserData);
3135JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData);
3136JX9_PRIVATE SyHashEntry *SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen);
3137JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash);
3138JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp);
3139JX9_PRIVATE void *SySetAt(SySet *pSet, sxu32 nIdx);
3140JX9_PRIVATE void *SySetPop(SySet *pSet);
3141JX9_PRIVATE void *SySetPeek(SySet *pSet);
3142JX9_PRIVATE sxi32 SySetRelease(SySet *pSet);
3143JX9_PRIVATE sxi32 SySetReset(SySet *pSet);
3144JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet);
3145JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry);
3146JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem);
3147JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem);
3148JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize);
3149#ifndef JX9_DISABLE_BUILTIN_FUNC
3150JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft);
3151#endif
3152JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob);
3153JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob);
3154JX9_PRIVATE sxi32 SyBlobTruncate(SyBlob *pBlob,sxu32 nNewLen);
3155JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest);
3156JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob);
3157JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize);
3158JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte);
3159JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator);
3160JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize);
3161JX9_PRIVATE char *SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize);
3162JX9_PRIVATE void *SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize);
3163JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend);
3164JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void *pUserData);
3165JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void *pUserData);
3166JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,const SyMemBackend *pParent);
3167#if 0
3168/* Not used in the current release of the JX9 engine */
3169JX9_PRIVATE void *SyMemBackendPoolRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte);
3170#endif
3171JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void *pChunk);
3172JX9_PRIVATE void *SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte);
3173JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void *pChunk);
3174JX9_PRIVATE void *SyMemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte);
3175JX9_PRIVATE void *SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte);
3176JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen);
3177JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize);
3178JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize);
3179JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen);
3180JX9_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen);
3181#if !defined(JX9_DISABLE_BUILTIN_FUNC) || defined(__APPLE__)
3182JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen);
3183#endif
3184JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos);
3185#ifndef JX9_DISABLE_BUILTIN_FUNC
3186JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos);
3187#endif
3188JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos);
3189JX9_PRIVATE sxu32 SyStrlen(const char *zSrc);
3190#if defined(JX9_ENABLE_THREADS)
3191JX9_PRIVATE const SyMutexMethods *SyMutexExportMethods(void);
3192JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods);
3193JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend);
3194#endif
3195JX9_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb);
3196JX9_PRIVATE void SyBigEndianUnpack32(const unsigned char *buf,sxu32 *uNB);
3197JX9_PRIVATE void SyBigEndianPack16(unsigned char *buf,sxu16 nb);
3198JX9_PRIVATE void SyBigEndianUnpack16(const unsigned char *buf,sxu16 *uNB);
3199JX9_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64);
3200JX9_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64);
3201JX9_PRIVATE sxi32 SyBlobAppendBig64(SyBlob *pBlob,sxu64 n64);
3202JX9_PRIVATE sxi32 SyBlobAppendBig32(SyBlob *pBlob,sxu32 n32);
3203JX9_PRIVATE sxi32 SyBlobAppendBig16(SyBlob *pBlob,sxu16 n16);
3204JX9_PRIVATE void SyTimeFormatToDos(Sytm *pFmt,sxu32 *pOut);
3205JX9_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut);
3206#endif /* __JX9INT_H__ */
3207
3208/*
3209 * ----------------------------------------------------------
3210 * File: unqliteInt.h
3211 * MD5: 325816ce05f6adbaab2c39a41875dedd
3212 * ----------------------------------------------------------
3213 */
3214/*
3215 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
3216 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
3217 * Version 1.1.6
3218 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
3219 * please contact Symisc Systems via:
3220 * legal@symisc.net
3221 * licensing@symisc.net
3222 * contact@symisc.net
3223 * or visit:
3224 * http://unqlite.org/licensing.html
3225 */
3226 /* $SymiscID: unqliteInt.h v1.7 FreeBSD 2012-11-02 11:25 devel <chm@symisc.net> $ */
3227#ifndef __UNQLITEINT_H__
3228#define __UNQLITEINT_H__
3229/* Internal interface definitions for UnQLite. */
3230#ifdef UNQLITE_AMALGAMATION
3231/* Marker for routines not intended for external use */
3232#define UNQLITE_PRIVATE static
3233#define JX9_AMALGAMATION
3234#else
3235#define UNQLITE_PRIVATE
3236#include "unqlite.h"
3237#include "jx9Int.h"
3238#endif
3239/* forward declaration */
3240typedef struct unqlite_db unqlite_db;
3241/*
3242** The following values may be passed as the second argument to
3243** UnqliteOsLock(). The various locks exhibit the following semantics:
3244**
3245** SHARED: Any number of processes may hold a SHARED lock simultaneously.
3246** RESERVED: A single process may hold a RESERVED lock on a file at
3247** any time. Other processes may hold and obtain new SHARED locks.
3248** PENDING: A single process may hold a PENDING lock on a file at
3249** any one time. Existing SHARED locks may persist, but no new
3250** SHARED locks may be obtained by other processes.
3251** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
3252**
3253** PENDING_LOCK may not be passed directly to UnqliteOsLock(). Instead, a
3254** process that requests an EXCLUSIVE lock may actually obtain a PENDING
3255** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
3256** UnqliteOsLock().
3257*/
3258#define NO_LOCK 0
3259#define SHARED_LOCK 1
3260#define RESERVED_LOCK 2
3261#define PENDING_LOCK 3
3262#define EXCLUSIVE_LOCK 4
3263/*
3264 * UnQLite Locking Strategy (Same as SQLite3)
3265 *
3266 * The following #defines specify the range of bytes used for locking.
3267 * SHARED_SIZE is the number of bytes available in the pool from which
3268 * a random byte is selected for a shared lock. The pool of bytes for
3269 * shared locks begins at SHARED_FIRST.
3270 *
3271 * The same locking strategy and byte ranges are used for Unix and Windows.
3272 * This leaves open the possiblity of having clients on winNT, and
3273 * unix all talking to the same shared file and all locking correctly.
3274 * To do so would require that samba (or whatever
3275 * tool is being used for file sharing) implements locks correctly between
3276 * windows and unix. I'm guessing that isn't likely to happen, but by
3277 * using the same locking range we are at least open to the possibility.
3278 *
3279 * Locking in windows is mandatory. For this reason, we cannot store
3280 * actual data in the bytes used for locking. The pager never allocates
3281 * the pages involved in locking therefore. SHARED_SIZE is selected so
3282 * that all locks will fit on a single page even at the minimum page size.
3283 * PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE
3284 * is set high so that we don't have to allocate an unused page except
3285 * for very large databases. But one should test the page skipping logic
3286 * by setting PENDING_BYTE low and running the entire regression suite.
3287 *
3288 * Changing the value of PENDING_BYTE results in a subtly incompatible
3289 * file format. Depending on how it is changed, you might not notice
3290 * the incompatibility right away, even running a full regression test.
3291 * The default location of PENDING_BYTE is the first byte past the
3292 * 1GB boundary.
3293 */
3294#define PENDING_BYTE (0x40000000)
3295#define RESERVED_BYTE (PENDING_BYTE+1)
3296#define SHARED_FIRST (PENDING_BYTE+2)
3297#define SHARED_SIZE 510
3298/*
3299 * The default size of a disk sector in bytes.
3300 */
3301#ifndef UNQLITE_DEFAULT_SECTOR_SIZE
3302#define UNQLITE_DEFAULT_SECTOR_SIZE 512
3303#endif
3304/*
3305 * Each open database file is managed by a separate instance
3306 * of the "Pager" structure.
3307 */
3308typedef struct Pager Pager;
3309/*
3310 * Each database file to be accessed by the system is an instance
3311 * of the following structure.
3312 */
3313struct unqlite_db
3314{
3315 Pager *pPager; /* Pager and Transaction manager */
3316 jx9 *pJx9; /* Jx9 Engine handle */
3317 unqlite_kv_cursor *pCursor; /* Database cursor for common usage */
3318};
3319/*
3320 * Each database connection is an instance of the following structure.
3321 */
3322struct unqlite
3323{
3324 SyMemBackend sMem; /* Memory allocator subsystem */
3325 SyBlob sErr; /* Error log */
3326 unqlite_db sDB; /* Storage backend */
3327#if defined(UNQLITE_ENABLE_THREADS)
3328 const SyMutexMethods *pMethods; /* Mutex methods */
3329 SyMutex *pMutex; /* Per-handle mutex */
3330#endif
3331 unqlite_vm *pVms; /* List of active VM */
3332 sxi32 iVm; /* Total number of active VM */
3333 sxi32 iFlags; /* Control flags (See below) */
3334 unqlite *pNext,*pPrev; /* List of active DB handles */
3335 sxu32 nMagic; /* Sanity check against misuse */
3336};
3337#define UNQLITE_FL_DISABLE_AUTO_COMMIT 0x001 /* Disable auto-commit on close */
3338/*
3339 * VM control flags (Mostly related to collection handling).
3340 */
3341#define UNQLITE_VM_COLLECTION_CREATE 0x001 /* Create a new collection */
3342#define UNQLITE_VM_COLLECTION_OVERWRITE 0x002 /* Overwrite old collection */
3343#define UNQLITE_VM_AUTO_LOAD 0x004 /* Auto load a collection from the vfs */
3344/* Forward declaration */
3345typedef struct unqlite_col_record unqlite_col_record;
3346typedef struct unqlite_col unqlite_col;
3347/*
3348 * Each an in-memory collection record is stored in an instance
3349 * of the following structure.
3350 */
3351struct unqlite_col_record
3352{
3353 unqlite_col *pCol; /* Collecion this record belong */
3354 jx9_int64 nId; /* Unique record ID */
3355 jx9_value sValue; /* In-memory value of the record */
3356 unqlite_col_record *pNextCol,*pPrevCol; /* Collision chain */
3357 unqlite_col_record *pNext,*pPrev; /* Linked list of records */
3358};
3359/*
3360 * Magic number to identify a valid collection on disk.
3361 */
3362#define UNQLITE_COLLECTION_MAGIC 0x611E /* sizeof(unsigned short) 2 bytes */
3363/*
3364 * A loaded collection is identified by an instance of the following structure.
3365 */
3366struct unqlite_col
3367{
3368 unqlite_vm *pVm; /* VM that own this instance */
3369 SyString sName; /* ID of the collection */
3370 sxu32 nHash; /* sName hash */
3371 jx9_value sSchema; /* Collection schema */
3372 sxu32 nSchemaOfft; /* Shema offset in sHeader */
3373 SyBlob sWorker; /* General purpose working buffer */
3374 SyBlob sHeader; /* Collection binary header */
3375 jx9_int64 nLastid; /* Last collection record ID */
3376 jx9_int64 nCurid; /* Current record ID */
3377 jx9_int64 nTotRec; /* Total number of records in the collection */
3378 int iFlags; /* Control flags (see below) */
3379 unqlite_col_record **apRecord; /* Hashtable of loaded records */
3380 unqlite_col_record *pList; /* Linked list of records */
3381 sxu32 nRec; /* Total number of records in apRecord[] */
3382 sxu32 nRecSize; /* apRecord[] size */
3383 Sytm sCreation; /* Colleation creation time */
3384 unqlite_kv_cursor *pCursor; /* Cursor pointing to the raw binary data */
3385 unqlite_col *pNext,*pPrev; /* Next and previous collection in the chain */
3386 unqlite_col *pNextCol,*pPrevCol; /* Collision chain */
3387};
3388/*
3389 * Each unQLite Virtual Machine resulting from successful compilation of
3390 * a Jx9 script is represented by an instance of the following structure.
3391 */
3392struct unqlite_vm
3393{
3394 unqlite *pDb; /* Database handle that own this instance */
3395 SyMemBackend sAlloc; /* Private memory allocator */
3396#if defined(UNQLITE_ENABLE_THREADS)
3397 SyMutex *pMutex; /* Recursive mutex associated with this VM. */
3398#endif
3399 unqlite_col **apCol; /* Table of loaded collections */
3400 unqlite_col *pCol; /* List of loaded collections */
3401 sxu32 iCol; /* Total number of loaded collections */
3402 sxu32 iColSize; /* apCol[] size */
3403 jx9_vm *pJx9Vm; /* Compiled Jx9 script*/
3404 unqlite_vm *pNext,*pPrev; /* Linked list of active unQLite VM */
3405 sxu32 nMagic; /* Magic number to avoid misuse */
3406};
3407/*
3408 * Database signature to identify a valid database image.
3409 */
3410#define UNQLITE_DB_SIG "unqlite"
3411/*
3412 * Database magic number (4 bytes).
3413 */
3414#define UNQLITE_DB_MAGIC 0xDB7C2712
3415/*
3416 * Maximum page size in bytes.
3417 */
3418#ifdef UNQLITE_MAX_PAGE_SIZE
3419# undef UNQLITE_MAX_PAGE_SIZE
3420#endif
3421#define UNQLITE_MAX_PAGE_SIZE 65536 /* 65K */
3422/*
3423 * Minimum page size in bytes.
3424 */
3425#ifdef UNQLITE_MIN_PAGE_SIZE
3426# undef UNQLITE_MIN_PAGE_SIZE
3427#endif
3428#define UNQLITE_MIN_PAGE_SIZE 512
3429/*
3430 * The default size of a database page.
3431 */
3432#ifndef UNQLITE_DEFAULT_PAGE_SIZE
3433# undef UNQLITE_DEFAULT_PAGE_SIZE
3434#endif
3435# define UNQLITE_DEFAULT_PAGE_SIZE 4096 /* 4K */
3436/* Forward declaration */
3437typedef struct Bitvec Bitvec;
3438/* Private library functions */
3439/* api.c */
3440UNQLITE_PRIVATE const SyMemBackend * unqliteExportMemBackend(void);
3441UNQLITE_PRIVATE int unqliteDataConsumer(
3442 const void *pOut, /* Data to consume */
3443 unsigned int nLen, /* Data length */
3444 void *pUserData /* User private data */
3445 );
3446UNQLITE_PRIVATE unqlite_kv_methods * unqliteFindKVStore(
3447 const char *zName, /* Storage engine name [i.e. Hash, B+tree, LSM, etc.] */
3448 sxu32 nByte /* zName length */
3449 );
3450UNQLITE_PRIVATE int unqliteGetPageSize(void);
3451UNQLITE_PRIVATE int unqliteGenError(unqlite *pDb,const char *zErr);
3452UNQLITE_PRIVATE int unqliteGenErrorFormat(unqlite *pDb,const char *zFmt,...);
3453UNQLITE_PRIVATE int unqliteGenOutofMem(unqlite *pDb);
3454/* unql_vm.c */
3455UNQLITE_PRIVATE int unqliteCreateCollection(unqlite_vm *pVm,SyString *pName);
3456UNQLITE_PRIVATE jx9_int64 unqliteCollectionLastRecordId(unqlite_col *pCol);
3457UNQLITE_PRIVATE jx9_int64 unqliteCollectionCurrentRecordId(unqlite_col *pCol);
3458UNQLITE_PRIVATE int unqliteCollectionCacheRemoveRecord(unqlite_col *pCol,jx9_int64 nId);
3459UNQLITE_PRIVATE jx9_int64 unqliteCollectionTotalRecords(unqlite_col *pCol);
3460UNQLITE_PRIVATE void unqliteCollectionResetRecordCursor(unqlite_col *pCol);
3461UNQLITE_PRIVATE int unqliteCollectionFetchNextRecord(unqlite_col *pCol,jx9_value *pValue);
3462UNQLITE_PRIVATE int unqliteCollectionFetchRecordById(unqlite_col *pCol,jx9_int64 nId,jx9_value *pValue);
3463UNQLITE_PRIVATE unqlite_col * unqliteCollectionFetch(unqlite_vm *pVm,SyString *pCol,int iFlag);
3464UNQLITE_PRIVATE int unqliteCollectionSetSchema(unqlite_col *pCol,jx9_value *pValue);
3465UNQLITE_PRIVATE int unqliteCollectionPut(unqlite_col *pCol,jx9_value *pValue,int iFlag);
3466UNQLITE_PRIVATE int unqliteCollectionDropRecord(unqlite_col *pCol,jx9_int64 nId,int wr_header,int log_err);
3467UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol);
3468/* unql_jx9.c */
3469UNQLITE_PRIVATE int unqliteRegisterJx9Functions(unqlite_vm *pVm);
3470/* fastjson.c */
3471UNQLITE_PRIVATE sxi32 FastJsonEncode(
3472 jx9_value *pValue, /* Value to encode */
3473 SyBlob *pOut, /* Store encoded value here */
3474 int iNest /* Nesting limit */
3475 );
3476UNQLITE_PRIVATE sxi32 FastJsonDecode(
3477 const void *pIn, /* Binary JSON */
3478 sxu32 nByte, /* Chunk delimiter */
3479 jx9_value *pOut, /* Decoded value */
3480 const unsigned char **pzPtr,
3481 int iNest /* Nesting limit */
3482 );
3483/* vfs.c [io_win.c, io_unix.c ] */
3484UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void);
3485/* mem_kv.c */
3486UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void);
3487/* lhash_kv.c */
3488UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void);
3489/* os.c */
3490UNQLITE_PRIVATE int unqliteOsRead(unqlite_file *id, void *pBuf, unqlite_int64 amt, unqlite_int64 offset);
3491UNQLITE_PRIVATE int unqliteOsWrite(unqlite_file *id, const void *pBuf, unqlite_int64 amt, unqlite_int64 offset);
3492UNQLITE_PRIVATE int unqliteOsTruncate(unqlite_file *id, unqlite_int64 size);
3493UNQLITE_PRIVATE int unqliteOsSync(unqlite_file *id, int flags);
3494UNQLITE_PRIVATE int unqliteOsFileSize(unqlite_file *id, unqlite_int64 *pSize);
3495UNQLITE_PRIVATE int unqliteOsLock(unqlite_file *id, int lockType);
3496UNQLITE_PRIVATE int unqliteOsUnlock(unqlite_file *id, int lockType);
3497UNQLITE_PRIVATE int unqliteOsCheckReservedLock(unqlite_file *id, int *pResOut);
3498UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id);
3499UNQLITE_PRIVATE int unqliteOsOpen(
3500 unqlite_vfs *pVfs,
3501 SyMemBackend *pAlloc,
3502 const char *zPath,
3503 unqlite_file **ppOut,
3504 unsigned int flags
3505);
3506UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId);
3507UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int dirSync);
3508UNQLITE_PRIVATE int unqliteOsAccess(unqlite_vfs *pVfs,const char *zPath,int flags,int *pResOut);
3509/* bitmap.c */
3510UNQLITE_PRIVATE Bitvec *unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize);
3511UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i);
3512UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i);
3513UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p);
3514/* pager.c */
3515UNQLITE_PRIVATE int unqliteInitCursor(unqlite *pDb,unqlite_kv_cursor **ppOut);
3516UNQLITE_PRIVATE int unqliteReleaseCursor(unqlite *pDb,unqlite_kv_cursor *pCur);
3517UNQLITE_PRIVATE int unqlitePagerSetCachesize(Pager *pPager,int mxPage);
3518UNQLITE_PRIVATE int unqlitePagerClose(Pager *pPager);
3519UNQLITE_PRIVATE int unqlitePagerOpen(
3520 unqlite_vfs *pVfs, /* The virtual file system to use */
3521 unqlite *pDb, /* Database handle */
3522 const char *zFilename, /* Name of the database file to open */
3523 unsigned int iFlags /* flags controlling this file */
3524 );
3525UNQLITE_PRIVATE int unqlitePagerRegisterKvEngine(Pager *pPager,unqlite_kv_methods *pMethods);
3526UNQLITE_PRIVATE unqlite_kv_engine * unqlitePagerGetKvEngine(unqlite *pDb);
3527UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager);
3528UNQLITE_PRIVATE int unqlitePagerCommit(Pager *pPager);
3529UNQLITE_PRIVATE int unqlitePagerRollback(Pager *pPager,int bResetKvEngine);
3530UNQLITE_PRIVATE void unqlitePagerRandomString(Pager *pPager,char *zBuf,sxu32 nLen);
3531UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager);
3532#endif /* __UNQLITEINT_H__ */
3533/*
3534 * ----------------------------------------------------------
3535 * File: api.c
3536 * MD5: d79e8404e50dacd0ea75635c1ebe553a
3537 * ----------------------------------------------------------
3538 */
3539/*
3540 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
3541 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
3542 * Version 1.1.6
3543 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
3544 * please contact Symisc Systems via:
3545 * legal@symisc.net
3546 * licensing@symisc.net
3547 * contact@symisc.net
3548 * or visit:
3549 * http://unqlite.org/licensing.html
3550 */
3551 /* $SymiscID: api.c v2.0 FreeBSD 2012-11-08 23:07 stable <chm@symisc.net> $ */
3552#ifndef UNQLITE_AMALGAMATION
3553#include "unqliteInt.h"
3554#endif
3555/* This file implement the public interfaces presented to host-applications.
3556 * Routines in other files are for internal use by UnQLite and should not be
3557 * accessed by users of the library.
3558 */
3559#define UNQLITE_DB_MISUSE(DB) (DB == 0 || DB->nMagic != UNQLITE_DB_MAGIC)
3560#define UNQLITE_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE)
3561/* If another thread have released a working instance, the following macros
3562 * evaluates to true. These macros are only used when the library
3563 * is built with threading support enabled.
3564 */
3565#define UNQLITE_THRD_DB_RELEASE(DB) (DB->nMagic != UNQLITE_DB_MAGIC)
3566#define UNQLITE_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE)
3567/* IMPLEMENTATION: unqlite@embedded@symisc 118-09-4785 */
3568/*
3569 * All global variables are collected in the structure named "sUnqlMPGlobal".
3570 * That way it is clear in the code when we are using static variable because
3571 * its name start with sUnqlMPGlobal.
3572 */
3573static struct unqlGlobal_Data
3574{
3575 SyMemBackend sAllocator; /* Global low level memory allocator */
3576#if defined(UNQLITE_ENABLE_THREADS)
3577 const SyMutexMethods *pMutexMethods; /* Mutex methods */
3578 SyMutex *pMutex; /* Global mutex */
3579 sxu32 nThreadingLevel; /* Threading level: 0 == Single threaded/1 == Multi-Threaded
3580 * The threading level can be set using the [unqlite_lib_config()]
3581 * interface with a configuration verb set to
3582 * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE or
3583 * UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI
3584 */
3585#endif
3586 SySet kv_storage; /* Installed KV storage engines */
3587 int iPageSize; /* Default Page size */
3588 unqlite_vfs *pVfs; /* Underlying virtual file system (Vfs) */
3589 sxi32 nDB; /* Total number of active DB handles */
3590 unqlite *pDB; /* List of active DB handles */
3591 sxu32 nMagic; /* Sanity check against library misuse */
3592}sUnqlMPGlobal = {
3593 {0, 0, 0, 0, 0, 0, 0, 0, {0}},
3594#if defined(UNQLITE_ENABLE_THREADS)
3595 0,
3596 0,
3597 0,
3598#endif
3599 {0, 0, 0, 0, 0, 0, 0 },
3600 UNQLITE_DEFAULT_PAGE_SIZE,
3601 0,
3602 0,
3603 0,
3604 0
3605};
3606#define UNQLITE_LIB_MAGIC 0xEA1495BA
3607#define UNQLITE_LIB_MISUSE (sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC)
3608/*
3609 * Supported threading level.
3610 * These options have meaning only when the library is compiled with multi-threading
3611 * support. That is, the UNQLITE_ENABLE_THREADS compile time directive must be defined
3612 * when UnQLite is built.
3613 * UNQLITE_THREAD_LEVEL_SINGLE:
3614 * In this mode, mutexing is disabled and the library can only be used by a single thread.
3615 * UNQLITE_THREAD_LEVEL_MULTI
3616 * In this mode, all mutexes including the recursive mutexes on [unqlite] objects
3617 * are enabled so that the application is free to share the same database handle
3618 * between different threads at the same time.
3619 */
3620#define UNQLITE_THREAD_LEVEL_SINGLE 1
3621#define UNQLITE_THREAD_LEVEL_MULTI 2
3622/*
3623 * Find a Key Value storage engine from the set of installed engines.
3624 * Return a pointer to the storage engine methods on success. NULL on failure.
3625 */
3626UNQLITE_PRIVATE unqlite_kv_methods * unqliteFindKVStore(
3627 const char *zName, /* Storage engine name [i.e. Hash, B+tree, LSM, etc.] */
3628 sxu32 nByte /* zName length */
3629 )
3630{
3631 unqlite_kv_methods **apStore,*pEntry;
3632 sxu32 n,nMax;
3633 /* Point to the set of installed engines */
3634 apStore = (unqlite_kv_methods **)SySetBasePtr(&sUnqlMPGlobal.kv_storage);
3635 nMax = SySetUsed(&sUnqlMPGlobal.kv_storage);
3636 for( n = 0 ; n < nMax; ++n ){
3637 pEntry = apStore[n];
3638 if( nByte == SyStrlen(pEntry->zName) && SyStrnicmp(pEntry->zName,zName,nByte) == 0 ){
3639 /* Storage engine found */
3640 return pEntry;
3641 }
3642 }
3643 /* No such entry, return NULL */
3644 return 0;
3645}
3646/*
3647 * Configure the UnQLite library.
3648 * Return UNQLITE_OK on success. Any other return value indicates failure.
3649 * Refer to [unqlite_lib_config()].
3650 */
3651static sxi32 unqliteCoreConfigure(sxi32 nOp, va_list ap)
3652{
3653 int rc = UNQLITE_OK;
3654 switch(nOp){
3655 case UNQLITE_LIB_CONFIG_PAGE_SIZE: {
3656 /* Default page size: Must be a power of two */
3657 int iPage = va_arg(ap,int);
3658 if( iPage >= UNQLITE_MIN_PAGE_SIZE && iPage <= UNQLITE_MAX_PAGE_SIZE ){
3659 if( !(iPage & (iPage - 1)) ){
3660 sUnqlMPGlobal.iPageSize = iPage;
3661 }else{
3662 /* Invalid page size */
3663 rc = UNQLITE_INVALID;
3664 }
3665 }else{
3666 /* Invalid page size */
3667 rc = UNQLITE_INVALID;
3668 }
3669 break;
3670 }
3671 case UNQLITE_LIB_CONFIG_STORAGE_ENGINE: {
3672 /* Install a key value storage engine */
3673 unqlite_kv_methods *pMethods = va_arg(ap,unqlite_kv_methods *);
3674 /* Make sure we are delaing with a valid methods */
3675 if( pMethods == 0 || SX_EMPTY_STR(pMethods->zName) || pMethods->xSeek == 0 || pMethods->xData == 0
3676 || pMethods->xKey == 0 || pMethods->xDataLength == 0 || pMethods->xKeyLength == 0
3677 || pMethods->szKv < (int)sizeof(unqlite_kv_engine) ){
3678 rc = UNQLITE_INVALID;
3679 break;
3680 }
3681 /* Install it */
3682 rc = SySetPut(&sUnqlMPGlobal.kv_storage,(const void *)&pMethods);
3683 break;
3684 }
3685 case UNQLITE_LIB_CONFIG_VFS:{
3686 /* Install a virtual file system */
3687 unqlite_vfs *pVfs = va_arg(ap,unqlite_vfs *);
3688 if( pVfs ){
3689 sUnqlMPGlobal.pVfs = pVfs;
3690 }
3691 break;
3692 }
3693 case UNQLITE_LIB_CONFIG_USER_MALLOC: {
3694 /* Use an alternative low-level memory allocation routines */
3695 const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *);
3696 /* Save the memory failure callback (if available) */
3697 ProcMemError xMemErr = sUnqlMPGlobal.sAllocator.xMemError;
3698 void *pMemErr = sUnqlMPGlobal.sAllocator.pUserData;
3699 if( pMethods == 0 ){
3700 /* Use the built-in memory allocation subsystem */
3701 rc = SyMemBackendInit(&sUnqlMPGlobal.sAllocator, xMemErr, pMemErr);
3702 }else{
3703 rc = SyMemBackendInitFromOthers(&sUnqlMPGlobal.sAllocator, pMethods, xMemErr, pMemErr);
3704 }
3705 break;
3706 }
3707 case UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK: {
3708 /* Memory failure callback */
3709 ProcMemError xMemErr = va_arg(ap, ProcMemError);
3710 void *pUserData = va_arg(ap, void *);
3711 sUnqlMPGlobal.sAllocator.xMemError = xMemErr;
3712 sUnqlMPGlobal.sAllocator.pUserData = pUserData;
3713 break;
3714 }
3715 case UNQLITE_LIB_CONFIG_USER_MUTEX: {
3716#if defined(UNQLITE_ENABLE_THREADS)
3717 /* Use an alternative low-level mutex subsystem */
3718 const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *);
3719#if defined (UNTRUST)
3720 if( pMethods == 0 ){
3721 rc = UNQLITE_CORRUPT;
3722 }
3723#endif
3724 /* Sanity check */
3725 if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){
3726 /* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */
3727 rc = UNQLITE_CORRUPT;
3728 break;
3729 }
3730 if( sUnqlMPGlobal.pMutexMethods ){
3731 /* Overwrite the previous mutex subsystem */
3732 SyMutexRelease(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex);
3733 if( sUnqlMPGlobal.pMutexMethods->xGlobalRelease ){
3734 sUnqlMPGlobal.pMutexMethods->xGlobalRelease();
3735 }
3736 sUnqlMPGlobal.pMutex = 0;
3737 }
3738 /* Initialize and install the new mutex subsystem */
3739 if( pMethods->xGlobalInit ){
3740 rc = pMethods->xGlobalInit();
3741 if ( rc != UNQLITE_OK ){
3742 break;
3743 }
3744 }
3745 /* Create the global mutex */
3746 sUnqlMPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
3747 if( sUnqlMPGlobal.pMutex == 0 ){
3748 /*
3749 * If the supplied mutex subsystem is so sick that we are unable to
3750 * create a single mutex, there is no much we can do here.
3751 */
3752 if( pMethods->xGlobalRelease ){
3753 pMethods->xGlobalRelease();
3754 }
3755 rc = UNQLITE_CORRUPT;
3756 break;
3757 }
3758 sUnqlMPGlobal.pMutexMethods = pMethods;
3759 if( sUnqlMPGlobal.nThreadingLevel == 0 ){
3760 /* Set a default threading level */
3761 sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI;
3762 }
3763#endif
3764 break;
3765 }
3766 case UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE:
3767#if defined(UNQLITE_ENABLE_THREADS)
3768 /* Single thread mode (Only one thread is allowed to play with the library) */
3769 sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_SINGLE;
3770 jx9_lib_config(JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE);
3771#endif
3772 break;
3773 case UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI:
3774#if defined(UNQLITE_ENABLE_THREADS)
3775 /* Multi-threading mode (library is thread safe and database handles and virtual machines
3776 * may be shared between multiple threads).
3777 */
3778 sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI;
3779 jx9_lib_config(JX9_LIB_CONFIG_THREAD_LEVEL_MULTI);
3780#endif
3781 break;
3782 default:
3783 /* Unknown configuration option */
3784 rc = UNQLITE_CORRUPT;
3785 break;
3786 }
3787 return rc;
3788}
3789/*
3790 * [CAPIREF: unqlite_lib_config()]
3791 * Please refer to the official documentation for function purpose and expected parameters.
3792 */
3793int unqlite_lib_config(int nConfigOp,...)
3794{
3795 va_list ap;
3796 int rc;
3797 if( sUnqlMPGlobal.nMagic == UNQLITE_LIB_MAGIC ){
3798 /* Library is already initialized, this operation is forbidden */
3799 return UNQLITE_LOCKED;
3800 }
3801 va_start(ap,nConfigOp);
3802 rc = unqliteCoreConfigure(nConfigOp,ap);
3803 va_end(ap);
3804 return rc;
3805}
3806/*
3807 * Global library initialization
3808 * Refer to [unqlite_lib_init()]
3809 * This routine must be called to initialize the memory allocation subsystem, the mutex
3810 * subsystem prior to doing any serious work with the library. The first thread to call
3811 * this routine does the initialization process and set the magic number so no body later
3812 * can re-initialize the library. If subsequent threads call this routine before the first
3813 * thread have finished the initialization process, then the subsequent threads must block
3814 * until the initialization process is done.
3815 */
3816static sxi32 unqliteCoreInitialize(void)
3817{
3818 const unqlite_kv_methods *pMethods;
3819 const unqlite_vfs *pVfs; /* Built-in vfs */
3820#if defined(UNQLITE_ENABLE_THREADS)
3821 const SyMutexMethods *pMutexMethods = 0;
3822 SyMutex *pMaster = 0;
3823#endif
3824 int rc;
3825 /*
3826 * If the library is already initialized, then a call to this routine
3827 * is a no-op.
3828 */
3829 if( sUnqlMPGlobal.nMagic == UNQLITE_LIB_MAGIC ){
3830 return UNQLITE_OK; /* Already initialized */
3831 }
3832 /* Point to the built-in vfs */
3833 pVfs = unqliteExportBuiltinVfs();
3834 /* Install it */
3835 unqlite_lib_config(UNQLITE_LIB_CONFIG_VFS, pVfs);
3836#if defined(UNQLITE_ENABLE_THREADS)
3837 if( sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_SINGLE ){
3838 pMutexMethods = sUnqlMPGlobal.pMutexMethods;
3839 if( pMutexMethods == 0 ){
3840 /* Use the built-in mutex subsystem */
3841 pMutexMethods = SyMutexExportMethods();
3842 if( pMutexMethods == 0 ){
3843 return UNQLITE_CORRUPT; /* Can't happen */
3844 }
3845 /* Install the mutex subsystem */
3846 rc = unqlite_lib_config(UNQLITE_LIB_CONFIG_USER_MUTEX, pMutexMethods);
3847 if( rc != UNQLITE_OK ){
3848 return rc;
3849 }
3850 }
3851 /* Obtain a static mutex so we can initialize the library without calling malloc() */
3852 pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1);
3853 if( pMaster == 0 ){
3854 return UNQLITE_CORRUPT; /* Can't happen */
3855 }
3856 }
3857 /* Lock the master mutex */
3858 rc = UNQLITE_OK;
3859 SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
3860 if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
3861#endif
3862 if( sUnqlMPGlobal.sAllocator.pMethods == 0 ){
3863 /* Install a memory subsystem */
3864 rc = unqlite_lib_config(UNQLITE_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */
3865 if( rc != UNQLITE_OK ){
3866 /* If we are unable to initialize the memory backend, there is no much we can do here.*/
3867 goto End;
3868 }
3869 }
3870#if defined(UNQLITE_ENABLE_THREADS)
3871 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
3872 /* Protect the memory allocation subsystem */
3873 rc = SyMemBackendMakeThreadSafe(&sUnqlMPGlobal.sAllocator, sUnqlMPGlobal.pMutexMethods);
3874 if( rc != UNQLITE_OK ){
3875 goto End;
3876 }
3877 }
3878#endif
3879 SySetInit(&sUnqlMPGlobal.kv_storage,&sUnqlMPGlobal.sAllocator,sizeof(unqlite_kv_methods *));
3880 /* Install the built-in Key Value storage engines */
3881 pMethods = unqliteExportMemKvStorage(); /* In-memory storage */
3882 unqlite_lib_config(UNQLITE_LIB_CONFIG_STORAGE_ENGINE,pMethods);
3883 /* Default disk key/value storage engine */
3884 pMethods = unqliteExportDiskKvStorage(); /* Disk storage */
3885 unqlite_lib_config(UNQLITE_LIB_CONFIG_STORAGE_ENGINE,pMethods);
3886 /* Default page size */
3887 if( sUnqlMPGlobal.iPageSize < UNQLITE_MIN_PAGE_SIZE ){
3888 unqlite_lib_config(UNQLITE_LIB_CONFIG_PAGE_SIZE,UNQLITE_DEFAULT_PAGE_SIZE);
3889 }
3890 /* Our library is initialized, set the magic number */
3891 sUnqlMPGlobal.nMagic = UNQLITE_LIB_MAGIC;
3892 rc = UNQLITE_OK;
3893#if defined(UNQLITE_ENABLE_THREADS)
3894 } /* sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC */
3895#endif
3896End:
3897#if defined(UNQLITE_ENABLE_THREADS)
3898 /* Unlock the master mutex */
3899 SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
3900#endif
3901 return rc;
3902}
3903/* Forward declaration */
3904static int unqliteVmRelease(unqlite_vm *pVm);
3905/*
3906 * Release a single instance of an unqlite database handle.
3907 */
3908static int unqliteDbRelease(unqlite *pDb)
3909{
3910 unqlite_db *pStore = &pDb->sDB;
3911 unqlite_vm *pVm,*pNext;
3912 int rc = UNQLITE_OK;
3913 if( (pDb->iFlags & UNQLITE_FL_DISABLE_AUTO_COMMIT) == 0 ){
3914 /* Commit any outstanding transaction */
3915 rc = unqlitePagerCommit(pStore->pPager);
3916 if( rc != UNQLITE_OK ){
3917 /* Rollback the transaction */
3918 rc = unqlitePagerRollback(pStore->pPager,FALSE);
3919 }
3920 }else{
3921 /* Rollback any outstanding transaction */
3922 rc = unqlitePagerRollback(pStore->pPager,FALSE);
3923 }
3924 /* Close the pager */
3925 unqlitePagerClose(pStore->pPager);
3926 /* Release any active VM's */
3927 pVm = pDb->pVms;
3928 for(;;){
3929 if( pDb->iVm < 1 ){
3930 break;
3931 }
3932 /* Point to the next entry */
3933 pNext = pVm->pNext;
3934 unqliteVmRelease(pVm);
3935 pVm = pNext;
3936 pDb->iVm--;
3937 }
3938 /* Release the Jx9 handle */
3939 jx9_release(pStore->pJx9);
3940 /* Set a dummy magic number */
3941 pDb->nMagic = 0x7250;
3942 /* Release the whole memory subsystem */
3943 SyMemBackendRelease(&pDb->sMem);
3944 /* Commit or rollback result */
3945 return rc;
3946}
3947/*
3948 * Release all resources consumed by the library.
3949 * Note: This call is not thread safe. Refer to [unqlite_lib_shutdown()].
3950 */
3951static void unqliteCoreShutdown(void)
3952{
3953 unqlite *pDb, *pNext;
3954 /* Release all active databases handles */
3955 pDb = sUnqlMPGlobal.pDB;
3956 for(;;){
3957 if( sUnqlMPGlobal.nDB < 1 ){
3958 break;
3959 }
3960 pNext = pDb->pNext;
3961 unqliteDbRelease(pDb);
3962 pDb = pNext;
3963 sUnqlMPGlobal.nDB--;
3964 }
3965 /* Release the storage methods container */
3966 SySetRelease(&sUnqlMPGlobal.kv_storage);
3967#if defined(UNQLITE_ENABLE_THREADS)
3968 /* Release the mutex subsystem */
3969 if( sUnqlMPGlobal.pMutexMethods ){
3970 if( sUnqlMPGlobal.pMutex ){
3971 SyMutexRelease(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex);
3972 sUnqlMPGlobal.pMutex = 0;
3973 }
3974 if( sUnqlMPGlobal.pMutexMethods->xGlobalRelease ){
3975 sUnqlMPGlobal.pMutexMethods->xGlobalRelease();
3976 }
3977 sUnqlMPGlobal.pMutexMethods = 0;
3978 }
3979 sUnqlMPGlobal.nThreadingLevel = 0;
3980#endif
3981 if( sUnqlMPGlobal.sAllocator.pMethods ){
3982 /* Release the memory backend */
3983 SyMemBackendRelease(&sUnqlMPGlobal.sAllocator);
3984 }
3985 sUnqlMPGlobal.nMagic = 0x1764;
3986 /* Finally, shutdown the Jx9 library */
3987 jx9_lib_shutdown();
3988}
3989/*
3990 * [CAPIREF: unqlite_lib_init()]
3991 * Please refer to the official documentation for function purpose and expected parameters.
3992 */
3993int unqlite_lib_init(void)
3994{
3995 int rc;
3996 rc = unqliteCoreInitialize();
3997 return rc;
3998}
3999/*
4000 * [CAPIREF: unqlite_lib_shutdown()]
4001 * Please refer to the official documentation for function purpose and expected parameters.
4002 */
4003int unqlite_lib_shutdown(void)
4004{
4005 if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
4006 /* Already shut */
4007 return UNQLITE_OK;
4008 }
4009 unqliteCoreShutdown();
4010 return UNQLITE_OK;
4011}
4012/*
4013 * [CAPIREF: unqlite_lib_is_threadsafe()]
4014 * Please refer to the official documentation for function purpose and expected parameters.
4015 */
4016int unqlite_lib_is_threadsafe(void)
4017{
4018 if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
4019 return 0;
4020 }
4021#if defined(UNQLITE_ENABLE_THREADS)
4022 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
4023 /* Muli-threading support is enabled */
4024 return 1;
4025 }else{
4026 /* Single-threading */
4027 return 0;
4028 }
4029#else
4030 return 0;
4031#endif
4032}
4033/*
4034 *
4035 * [CAPIREF: unqlite_lib_version()]
4036 * Please refer to the official documentation for function purpose and expected parameters.
4037 */
4038const char * unqlite_lib_version(void)
4039{
4040 return UNQLITE_VERSION;
4041}
4042/*
4043 *
4044 * [CAPIREF: unqlite_lib_signature()]
4045 * Please refer to the official documentation for function purpose and expected parameters.
4046 */
4047const char * unqlite_lib_signature(void)
4048{
4049 return UNQLITE_SIG;
4050}
4051/*
4052 *
4053 * [CAPIREF: unqlite_lib_ident()]
4054 * Please refer to the official documentation for function purpose and expected parameters.
4055 */
4056const char * unqlite_lib_ident(void)
4057{
4058 return UNQLITE_IDENT;
4059}
4060/*
4061 *
4062 * [CAPIREF: unqlite_lib_copyright()]
4063 * Please refer to the official documentation for function purpose and expected parameters.
4064 */
4065const char * unqlite_lib_copyright(void)
4066{
4067 return UNQLITE_COPYRIGHT;
4068}
4069/*
4070 * Remove harmfull and/or stale flags passed to the [unqlite_open()] interface.
4071 */
4072static unsigned int unqliteSanityzeFlag(unsigned int iFlags)
4073{
4074 iFlags &= ~UNQLITE_OPEN_EXCLUSIVE; /* Reserved flag */
4075 if( iFlags & UNQLITE_OPEN_TEMP_DB ){
4076 /* Omit journaling for temporary database */
4077 iFlags |= UNQLITE_OPEN_OMIT_JOURNALING|UNQLITE_OPEN_CREATE;
4078 }
4079 if( (iFlags & (UNQLITE_OPEN_READONLY|UNQLITE_OPEN_READWRITE)) == 0 ){
4080 /* Auto-append the R+W flag */
4081 iFlags |= UNQLITE_OPEN_READWRITE;
4082 }
4083 if( iFlags & UNQLITE_OPEN_CREATE ){
4084 iFlags &= ~(UNQLITE_OPEN_MMAP|UNQLITE_OPEN_READONLY);
4085 /* Auto-append the R+W flag */
4086 iFlags |= UNQLITE_OPEN_READWRITE;
4087 }else{
4088 if( iFlags & UNQLITE_OPEN_READONLY ){
4089 iFlags &= ~UNQLITE_OPEN_READWRITE;
4090 }else if( iFlags & UNQLITE_OPEN_READWRITE ){
4091 iFlags &= ~UNQLITE_OPEN_MMAP;
4092 }
4093 }
4094 return iFlags;
4095}
4096/*
4097 * This routine does the work of initializing a database handle on behalf
4098 * of [unqlite_open()].
4099 */
4100static int unqliteInitDatabase(
4101 unqlite *pDB, /* Database handle */
4102 SyMemBackend *pParent, /* Master memory backend */
4103 const char *zFilename, /* Target database */
4104 unsigned int iFlags /* Open flags */
4105 )
4106{
4107 unqlite_db *pStorage = &pDB->sDB;
4108 int rc;
4109 /* Initialiaze the memory subsystem */
4110 SyMemBackendInitFromParent(&pDB->sMem,pParent);
4111#if defined(UNQLITE_ENABLE_THREADS)
4112 /* No need for internal mutexes */
4113 SyMemBackendDisbaleMutexing(&pDB->sMem);
4114#endif
4115 SyBlobInit(&pDB->sErr,&pDB->sMem);
4116 /* Sanityze flags */
4117 iFlags = unqliteSanityzeFlag(iFlags);
4118 /* Init the pager and the transaction manager */
4119 rc = unqlitePagerOpen(sUnqlMPGlobal.pVfs,pDB,zFilename,iFlags);
4120 if( rc != UNQLITE_OK ){
4121 return rc;
4122 }
4123 /* Allocate a new Jx9 engine handle */
4124 rc = jx9_init(&pStorage->pJx9);
4125 if( rc != JX9_OK ){
4126 return rc;
4127 }
4128 return UNQLITE_OK;
4129}
4130/*
4131 * Allocate and initialize a new UnQLite Virtual Mahcine and attach it
4132 * to the compiled Jx9 script.
4133 */
4134static int unqliteInitVm(unqlite *pDb,jx9_vm *pJx9Vm,unqlite_vm **ppOut)
4135{
4136 unqlite_vm *pVm;
4137
4138 *ppOut = 0;
4139 /* Allocate a new VM instance */
4140 pVm = (unqlite_vm *)SyMemBackendPoolAlloc(&pDb->sMem,sizeof(unqlite_vm));
4141 if( pVm == 0 ){
4142 return UNQLITE_NOMEM;
4143 }
4144 /* Zero the structure */
4145 SyZero(pVm,sizeof(unqlite_vm));
4146 /* Initialize */
4147 SyMemBackendInitFromParent(&pVm->sAlloc,&pDb->sMem);
4148 /* Allocate a new collection table */
4149 pVm->apCol = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc,32 * sizeof(unqlite_col *));
4150 if( pVm->apCol == 0 ){
4151 goto fail;
4152 }
4153 pVm->iColSize = 32; /* Must be a power of two */
4154 /* Zero the table */
4155 SyZero((void *)pVm->apCol,pVm->iColSize * sizeof(unqlite_col *));
4156#if defined(UNQLITE_ENABLE_THREADS)
4157 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
4158 /* Associate a recursive mutex with this instance */
4159 pVm->pMutex = SyMutexNew(sUnqlMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
4160 if( pVm->pMutex == 0 ){
4161 goto fail;
4162 }
4163 }
4164#endif
4165 /* Link the VM to the list of active virtual machines */
4166 pVm->pJx9Vm = pJx9Vm;
4167 pVm->pDb = pDb;
4168 MACRO_LD_PUSH(pDb->pVms,pVm);
4169 pDb->iVm++;
4170 /* Register Jx9 functions */
4171 unqliteRegisterJx9Functions(pVm);
4172 /* Set the magic number */
4173 pVm->nMagic = JX9_VM_INIT; /* Same magic number as Jx9 */
4174 /* All done */
4175 *ppOut = pVm;
4176 return UNQLITE_OK;
4177fail:
4178 SyMemBackendRelease(&pVm->sAlloc);
4179 SyMemBackendPoolFree(&pDb->sMem,pVm);
4180 return UNQLITE_NOMEM;
4181}
4182/*
4183 * Release an active VM.
4184 */
4185static int unqliteVmRelease(unqlite_vm *pVm)
4186{
4187 /* Release the Jx9 VM */
4188 jx9_vm_release(pVm->pJx9Vm);
4189 /* Release the private memory backend */
4190 SyMemBackendRelease(&pVm->sAlloc);
4191 /* Upper layer will discard this VM from the list
4192 * of active VM.
4193 */
4194 return UNQLITE_OK;
4195}
4196/*
4197 * Return the default page size.
4198 */
4199UNQLITE_PRIVATE int unqliteGetPageSize(void)
4200{
4201 int iSize = sUnqlMPGlobal.iPageSize;
4202 if( iSize < UNQLITE_MIN_PAGE_SIZE || iSize > UNQLITE_MAX_PAGE_SIZE ){
4203 iSize = UNQLITE_DEFAULT_PAGE_SIZE;
4204 }
4205 return iSize;
4206}
4207/*
4208 * Generate an error message.
4209 */
4210UNQLITE_PRIVATE int unqliteGenError(unqlite *pDb,const char *zErr)
4211{
4212 int rc;
4213 /* Append the error message */
4214 rc = SyBlobAppend(&pDb->sErr,(const void *)zErr,SyStrlen(zErr));
4215 /* Append a new line */
4216 SyBlobAppend(&pDb->sErr,(const void *)"\n",sizeof(char));
4217 return rc;
4218}
4219/*
4220 * Generate an error message (Printf like).
4221 */
4222UNQLITE_PRIVATE int unqliteGenErrorFormat(unqlite *pDb,const char *zFmt,...)
4223{
4224 va_list ap;
4225 int rc;
4226 va_start(ap,zFmt);
4227 rc = SyBlobFormatAp(&pDb->sErr,zFmt,ap);
4228 va_end(ap);
4229 /* Append a new line */
4230 SyBlobAppend(&pDb->sErr,(const void *)"\n",sizeof(char));
4231 return rc;
4232}
4233/*
4234 * Generate an error message (Out of memory).
4235 */
4236UNQLITE_PRIVATE int unqliteGenOutofMem(unqlite *pDb)
4237{
4238 int rc;
4239 rc = unqliteGenError(pDb,"unQLite is running out of memory");
4240 return rc;
4241}
4242/*
4243 * Configure a working UnQLite database handle.
4244 */
4245static int unqliteConfigure(unqlite *pDb,int nOp,va_list ap)
4246{
4247 int rc = UNQLITE_OK;
4248 switch(nOp){
4249 case UNQLITE_CONFIG_JX9_ERR_LOG:
4250 /* Jx9 compile-time error log */
4251 rc = jx9EngineConfig(pDb->sDB.pJx9,JX9_CONFIG_ERR_LOG,ap);
4252 break;
4253 case UNQLITE_CONFIG_MAX_PAGE_CACHE: {
4254 int max_page = va_arg(ap,int);
4255 /* Maximum number of page to cache (Simple hint). */
4256 rc = unqlitePagerSetCachesize(pDb->sDB.pPager,max_page);
4257 break;
4258 }
4259 case UNQLITE_CONFIG_ERR_LOG: {
4260 /* Database error log if any */
4261 const char **pzPtr = va_arg(ap, const char **);
4262 int *pLen = va_arg(ap, int *);
4263 if( pzPtr == 0 ){
4264 rc = JX9_CORRUPT;
4265 break;
4266 }
4267 /* NULL terminate the error-log buffer */
4268 SyBlobNullAppend(&pDb->sErr);
4269 /* Point to the error-log buffer */
4270 *pzPtr = (const char *)SyBlobData(&pDb->sErr);
4271 if( pLen ){
4272 if( SyBlobLength(&pDb->sErr) > 1 /* NULL '\0' terminator */ ){
4273 *pLen = (int)SyBlobLength(&pDb->sErr);
4274 }else{
4275 *pLen = 0;
4276 }
4277 }
4278 break;
4279 }
4280 case UNQLITE_CONFIG_DISABLE_AUTO_COMMIT:{
4281 /* Disable auto-commit */
4282 pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
4283 break;
4284 }
4285 case UNQLITE_CONFIG_GET_KV_NAME: {
4286 /* Name of the underlying KV storage engine */
4287 const char **pzPtr = va_arg(ap,const char **);
4288 if( pzPtr ){
4289 unqlite_kv_engine *pEngine;
4290 pEngine = unqlitePagerGetKvEngine(pDb);
4291 /* Point to the name */
4292 *pzPtr = pEngine->pIo->pMethods->zName;
4293 }
4294 break;
4295 }
4296 default:
4297 /* Unknown configuration option */
4298 rc = UNQLITE_UNKNOWN;
4299 break;
4300 }
4301 return rc;
4302}
4303/*
4304 * Export the global (master) memory allocator to submodules.
4305 */
4306UNQLITE_PRIVATE const SyMemBackend * unqliteExportMemBackend(void)
4307{
4308 return &sUnqlMPGlobal.sAllocator;
4309}
4310/*
4311 * [CAPIREF: unqlite_open()]
4312 * Please refer to the official documentation for function purpose and expected parameters.
4313 */
4314int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode)
4315{
4316 unqlite *pHandle;
4317 int rc;
4318#if defined(UNTRUST)
4319 if( ppDB == 0 ){
4320 return UNQLITE_CORRUPT;
4321 }
4322#endif
4323 *ppDB = 0;
4324 /* One-time automatic library initialization */
4325 rc = unqliteCoreInitialize();
4326 if( rc != UNQLITE_OK ){
4327 return rc;
4328 }
4329 /* Allocate a new database handle */
4330 pHandle = (unqlite *)SyMemBackendPoolAlloc(&sUnqlMPGlobal.sAllocator, sizeof(unqlite));
4331 if( pHandle == 0 ){
4332 return UNQLITE_NOMEM;
4333 }
4334 /* Zero the structure */
4335 SyZero(pHandle,sizeof(unqlite));
4336 if( iMode < 1 ){
4337 /* Assume a read-only database */
4338 iMode = UNQLITE_OPEN_READONLY|UNQLITE_OPEN_MMAP;
4339 }
4340 /* Init the database */
4341 rc = unqliteInitDatabase(pHandle,&sUnqlMPGlobal.sAllocator,zFilename,iMode);
4342 if( rc != UNQLITE_OK ){
4343 goto Release;
4344 }
4345#if defined(UNQLITE_ENABLE_THREADS)
4346 if( !(iMode & UNQLITE_OPEN_NOMUTEX) && (sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE) ){
4347 /* Associate a recursive mutex with this instance */
4348 pHandle->pMutex = SyMutexNew(sUnqlMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
4349 if( pHandle->pMutex == 0 ){
4350 rc = UNQLITE_NOMEM;
4351 goto Release;
4352 }
4353 }
4354#endif
4355 /* Link to the list of active DB handles */
4356#if defined(UNQLITE_ENABLE_THREADS)
4357 /* Enter the global mutex */
4358 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
4359#endif
4360 MACRO_LD_PUSH(sUnqlMPGlobal.pDB,pHandle);
4361 sUnqlMPGlobal.nDB++;
4362#if defined(UNQLITE_ENABLE_THREADS)
4363 /* Leave the global mutex */
4364 SyMutexLeave(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
4365#endif
4366 /* Set the magic number to identify a valid DB handle */
4367 pHandle->nMagic = UNQLITE_DB_MAGIC;
4368 /* Make the handle available to the caller */
4369 *ppDB = pHandle;
4370 return UNQLITE_OK;
4371Release:
4372 SyMemBackendRelease(&pHandle->sMem);
4373 SyMemBackendPoolFree(&sUnqlMPGlobal.sAllocator,pHandle);
4374 return rc;
4375}
4376/*
4377 * [CAPIREF: unqlite_config()]
4378 * Please refer to the official documentation for function purpose and expected parameters.
4379 */
4380int unqlite_config(unqlite *pDb,int nConfigOp,...)
4381{
4382 va_list ap;
4383 int rc;
4384 if( UNQLITE_DB_MISUSE(pDb) ){
4385 return UNQLITE_CORRUPT;
4386 }
4387#if defined(UNQLITE_ENABLE_THREADS)
4388 /* Acquire DB mutex */
4389 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4390 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4391 UNQLITE_THRD_DB_RELEASE(pDb) ){
4392 return UNQLITE_ABORT; /* Another thread have released this instance */
4393 }
4394#endif
4395 va_start(ap, nConfigOp);
4396 rc = unqliteConfigure(&(*pDb),nConfigOp, ap);
4397 va_end(ap);
4398#if defined(UNQLITE_ENABLE_THREADS)
4399 /* Leave DB mutex */
4400 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4401#endif
4402 return rc;
4403}
4404/*
4405 * [CAPIREF: unqlite_close()]
4406 * Please refer to the official documentation for function purpose and expected parameters.
4407 */
4408int unqlite_close(unqlite *pDb)
4409{
4410 int rc;
4411 if( UNQLITE_DB_MISUSE(pDb) ){
4412 return UNQLITE_CORRUPT;
4413 }
4414#if defined(UNQLITE_ENABLE_THREADS)
4415 /* Acquire DB mutex */
4416 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4417 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4418 UNQLITE_THRD_DB_RELEASE(pDb) ){
4419 return UNQLITE_ABORT; /* Another thread have released this instance */
4420 }
4421#endif
4422 /* Release the database handle */
4423 rc = unqliteDbRelease(pDb);
4424#if defined(UNQLITE_ENABLE_THREADS)
4425 /* Leave DB mutex */
4426 SyMutexLeave(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4427 /* Release DB mutex */
4428 SyMutexRelease(sUnqlMPGlobal.pMutexMethods, pDb->pMutex) /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4429#endif
4430#if defined(UNQLITE_ENABLE_THREADS)
4431 /* Enter the global mutex */
4432 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
4433#endif
4434 /* Unlink from the list of active database handles */
4435 MACRO_LD_REMOVE(sUnqlMPGlobal.pDB, pDb);
4436 sUnqlMPGlobal.nDB--;
4437#if defined(UNQLITE_ENABLE_THREADS)
4438 /* Leave the global mutex */
4439 SyMutexLeave(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
4440#endif
4441 /* Release the memory chunk allocated to this handle */
4442 SyMemBackendPoolFree(&sUnqlMPGlobal.sAllocator,pDb);
4443 return rc;
4444}
4445/*
4446 * [CAPIREF: unqlite_compile()]
4447 * Please refer to the official documentation for function purpose and expected parameters.
4448 */
4449int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut)
4450{
4451 jx9_vm *pVm;
4452 int rc;
4453 if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0){
4454 return UNQLITE_CORRUPT;
4455 }
4456#if defined(UNQLITE_ENABLE_THREADS)
4457 /* Acquire DB mutex */
4458 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4459 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4460 UNQLITE_THRD_DB_RELEASE(pDb) ){
4461 return UNQLITE_ABORT;
4462 }
4463#endif
4464 /* Compile the Jx9 script first */
4465 rc = jx9_compile(pDb->sDB.pJx9,zJx9,nByte,&pVm);
4466 if( rc == JX9_OK ){
4467 /* Allocate a new unqlite VM instance */
4468 rc = unqliteInitVm(pDb,pVm,ppOut);
4469 if( rc != UNQLITE_OK ){
4470 /* Release the Jx9 VM */
4471 jx9_vm_release(pVm);
4472 }
4473 }
4474#if defined(UNQLITE_ENABLE_THREADS)
4475 /* Leave DB mutex */
4476 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4477#endif
4478 return rc;
4479}
4480/*
4481 * [CAPIREF: unqlite_compile_file()]
4482 * Please refer to the official documentation for function purpose and expected parameters.
4483 */
4484int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut)
4485{
4486 jx9_vm *pVm;
4487 int rc;
4488 if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0){
4489 return UNQLITE_CORRUPT;
4490 }
4491#if defined(UNQLITE_ENABLE_THREADS)
4492 /* Acquire DB mutex */
4493 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4494 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4495 UNQLITE_THRD_DB_RELEASE(pDb) ){
4496 return UNQLITE_ABORT;
4497 }
4498#endif
4499 /* Compile the Jx9 script first */
4500 rc = jx9_compile_file(pDb->sDB.pJx9,zPath,&pVm);
4501 if( rc == JX9_OK ){
4502 /* Allocate a new unqlite VM instance */
4503 rc = unqliteInitVm(pDb,pVm,ppOut);
4504 if( rc != UNQLITE_OK ){
4505 /* Release the Jx9 VM */
4506 jx9_vm_release(pVm);
4507 }
4508 }
4509#if defined(UNQLITE_ENABLE_THREADS)
4510 /* Leave DB mutex */
4511 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4512#endif
4513 return rc;
4514}
4515/*
4516 * Configure an unqlite virtual machine (Mostly Jx9 VM) instance.
4517 */
4518static int unqliteVmConfig(unqlite_vm *pVm,sxi32 iOp,va_list ap)
4519{
4520 int rc;
4521 rc = jx9VmConfigure(pVm->pJx9Vm,iOp,ap);
4522 return rc;
4523}
4524/*
4525 * [CAPIREF: unqlite_vm_config()]
4526 * Please refer to the official documentation for function purpose and expected parameters.
4527 */
4528int unqlite_vm_config(unqlite_vm *pVm,int iOp,...)
4529{
4530 va_list ap;
4531 int rc;
4532 if( UNQLITE_VM_MISUSE(pVm) ){
4533 return UNQLITE_CORRUPT;
4534 }
4535#if defined(UNQLITE_ENABLE_THREADS)
4536 /* Acquire VM mutex */
4537 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4538 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4539 UNQLITE_THRD_VM_RELEASE(pVm) ){
4540 return UNQLITE_ABORT; /* Another thread have released this instance */
4541 }
4542#endif
4543 va_start(ap,iOp);
4544 rc = unqliteVmConfig(pVm,iOp,ap);
4545 va_end(ap);
4546#if defined(UNQLITE_ENABLE_THREADS)
4547 /* Leave DB mutex */
4548 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4549#endif
4550 return rc;
4551}
4552/*
4553 * [CAPIREF: unqlite_vm_exec()]
4554 * Please refer to the official documentation for function purpose and expected parameters.
4555 */
4556int unqlite_vm_exec(unqlite_vm *pVm)
4557{
4558 int rc;
4559 if( UNQLITE_VM_MISUSE(pVm) ){
4560 return UNQLITE_CORRUPT;
4561 }
4562#if defined(UNQLITE_ENABLE_THREADS)
4563 /* Acquire VM mutex */
4564 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4565 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4566 UNQLITE_THRD_VM_RELEASE(pVm) ){
4567 return UNQLITE_ABORT; /* Another thread have released this instance */
4568 }
4569#endif
4570 /* Execute the Jx9 bytecode program */
4571 rc = jx9VmByteCodeExec(pVm->pJx9Vm);
4572#if defined(UNQLITE_ENABLE_THREADS)
4573 /* Leave DB mutex */
4574 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4575#endif
4576 return rc;
4577}
4578/*
4579 * [CAPIREF: unqlite_vm_release()]
4580 * Please refer to the official documentation for function purpose and expected parameters.
4581 */
4582int unqlite_vm_release(unqlite_vm *pVm)
4583{
4584 int rc;
4585 if( UNQLITE_VM_MISUSE(pVm) ){
4586 return UNQLITE_CORRUPT;
4587 }
4588#if defined(UNQLITE_ENABLE_THREADS)
4589 /* Acquire VM mutex */
4590 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4591 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4592 UNQLITE_THRD_VM_RELEASE(pVm) ){
4593 return UNQLITE_ABORT; /* Another thread have released this instance */
4594 }
4595#endif
4596 /* Release the VM */
4597 rc = unqliteVmRelease(pVm);
4598#if defined(UNQLITE_ENABLE_THREADS)
4599 /* Leave VM mutex */
4600 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4601 /* Release VM mutex */
4602 SyMutexRelease(sUnqlMPGlobal.pMutexMethods,pVm->pMutex) /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4603#endif
4604 if( rc == UNQLITE_OK ){
4605 unqlite *pDb = pVm->pDb;
4606 /* Unlink from the list of active VM's */
4607#if defined(UNQLITE_ENABLE_THREADS)
4608 /* Acquire DB mutex */
4609 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4610 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4611 UNQLITE_THRD_DB_RELEASE(pDb) ){
4612 return UNQLITE_ABORT; /* Another thread have released this instance */
4613 }
4614#endif
4615 MACRO_LD_REMOVE(pDb->pVms, pVm);
4616 pDb->iVm--;
4617 /* Release the memory chunk allocated to this instance */
4618 SyMemBackendPoolFree(&pDb->sMem,pVm);
4619#if defined(UNQLITE_ENABLE_THREADS)
4620 /* Leave DB mutex */
4621 SyMutexLeave(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4622#endif
4623 }
4624 return rc;
4625}
4626/*
4627 * [CAPIREF: unqlite_vm_reset()]
4628 * Please refer to the official documentation for function purpose and expected parameters.
4629 */
4630int unqlite_vm_reset(unqlite_vm *pVm)
4631{
4632 int rc;
4633 if( UNQLITE_VM_MISUSE(pVm) ){
4634 return UNQLITE_CORRUPT;
4635 }
4636#if defined(UNQLITE_ENABLE_THREADS)
4637 /* Acquire VM mutex */
4638 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4639 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4640 UNQLITE_THRD_VM_RELEASE(pVm) ){
4641 return UNQLITE_ABORT; /* Another thread have released this instance */
4642 }
4643#endif
4644 /* Reset the Jx9 VM */
4645 rc = jx9VmReset(pVm->pJx9Vm);
4646#if defined(UNQLITE_ENABLE_THREADS)
4647 /* Leave DB mutex */
4648 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4649#endif
4650 return rc;
4651}
4652/*
4653 * [CAPIREF: unqlite_vm_dump()]
4654 * Please refer to the official documentation for function purpose and expected parameters.
4655 */
4656int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData)
4657{
4658 int rc;
4659 if( UNQLITE_VM_MISUSE(pVm) ){
4660 return UNQLITE_CORRUPT;
4661 }
4662#if defined(UNQLITE_ENABLE_THREADS)
4663 /* Acquire VM mutex */
4664 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4665 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4666 UNQLITE_THRD_VM_RELEASE(pVm) ){
4667 return UNQLITE_ABORT; /* Another thread have released this instance */
4668 }
4669#endif
4670 /* Dump the Jx9 VM */
4671 rc = jx9VmDump(pVm->pJx9Vm,xConsumer,pUserData);
4672#if defined(UNQLITE_ENABLE_THREADS)
4673 /* Leave DB mutex */
4674 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4675#endif
4676 return rc;
4677}
4678/*
4679 * [CAPIREF: unqlite_vm_extract_variable()]
4680 * Please refer to the official documentation for function purpose and expected parameters.
4681 */
4682unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname)
4683{
4684 unqlite_value *pValue;
4685 SyString sVariable;
4686 if( UNQLITE_VM_MISUSE(pVm) ){
4687 return 0;
4688 }
4689#if defined(UNQLITE_ENABLE_THREADS)
4690 /* Acquire VM mutex */
4691 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4692 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4693 UNQLITE_THRD_VM_RELEASE(pVm) ){
4694 return 0; /* Another thread have released this instance */
4695 }
4696#endif
4697 /* Extract the target variable */
4698 SyStringInitFromBuf(&sVariable,zVarname,SyStrlen(zVarname));
4699 pValue = jx9VmExtractVariable(pVm->pJx9Vm,&sVariable);
4700#if defined(UNQLITE_ENABLE_THREADS)
4701 /* Leave DB mutex */
4702 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4703#endif
4704 return pValue;
4705}
4706/*
4707 * [CAPIREF: unqlite_create_function()]
4708 * Please refer to the official documentation for function purpose and expected parameters.
4709 */
4710int unqlite_create_function(unqlite_vm *pVm, const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData)
4711{
4712 SyString sName;
4713 int rc;
4714 if( UNQLITE_VM_MISUSE(pVm) ){
4715 return UNQLITE_CORRUPT;
4716 }
4717 SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
4718 /* Remove leading and trailing white spaces */
4719 SyStringFullTrim(&sName);
4720 /* Ticket 1433-003: NULL values are not allowed */
4721 if( sName.nByte < 1 || xFunc == 0 ){
4722 return UNQLITE_INVALID;
4723 }
4724#if defined(UNQLITE_ENABLE_THREADS)
4725 /* Acquire VM mutex */
4726 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4727 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4728 UNQLITE_THRD_VM_RELEASE(pVm) ){
4729 return UNQLITE_ABORT; /* Another thread have released this instance */
4730 }
4731#endif
4732 /* Install the foreign function */
4733 rc = jx9VmInstallForeignFunction(pVm->pJx9Vm,&sName,xFunc,pUserData);
4734#if defined(UNQLITE_ENABLE_THREADS)
4735 /* Leave DB mutex */
4736 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4737#endif
4738 return rc;
4739}
4740/*
4741 * [CAPIREF: unqlite_delete_function()]
4742 * Please refer to the official documentation for function purpose and expected parameters.
4743 */
4744int unqlite_delete_function(unqlite_vm *pVm, const char *zName)
4745{
4746 int rc;
4747 if( UNQLITE_VM_MISUSE(pVm) ){
4748 return UNQLITE_CORRUPT;
4749 }
4750#if defined(UNQLITE_ENABLE_THREADS)
4751 /* Acquire VM mutex */
4752 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4753 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4754 UNQLITE_THRD_VM_RELEASE(pVm) ){
4755 return UNQLITE_ABORT; /* Another thread have released this instance */
4756 }
4757#endif
4758 /* Unlink the foreign function */
4759 rc = jx9DeleteFunction(pVm->pJx9Vm,zName);
4760#if defined(UNQLITE_ENABLE_THREADS)
4761 /* Leave DB mutex */
4762 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4763#endif
4764 return rc;
4765}
4766/*
4767 * [CAPIREF: unqlite_create_constant()]
4768 * Please refer to the official documentation for function purpose and expected parameters.
4769 */
4770int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData)
4771{
4772 SyString sName;
4773 int rc;
4774 if( UNQLITE_VM_MISUSE(pVm) ){
4775 return UNQLITE_CORRUPT;
4776 }
4777 SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
4778 /* Remove leading and trailing white spaces */
4779 SyStringFullTrim(&sName);
4780 if( sName.nByte < 1 ){
4781 /* Empty constant name */
4782 return UNQLITE_INVALID;
4783 }
4784 /* TICKET 1433-003: NULL pointer is harmless operation */
4785 if( xExpand == 0 ){
4786 return UNQLITE_INVALID;
4787 }
4788#if defined(UNQLITE_ENABLE_THREADS)
4789 /* Acquire VM mutex */
4790 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4791 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4792 UNQLITE_THRD_VM_RELEASE(pVm) ){
4793 return UNQLITE_ABORT; /* Another thread have released this instance */
4794 }
4795#endif
4796 /* Install the foreign constant */
4797 rc = jx9VmRegisterConstant(pVm->pJx9Vm,&sName,xExpand,pUserData);
4798#if defined(UNQLITE_ENABLE_THREADS)
4799 /* Leave DB mutex */
4800 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4801#endif
4802 return rc;
4803}
4804/*
4805 * [CAPIREF: unqlite_delete_constant()]
4806 * Please refer to the official documentation for function purpose and expected parameters.
4807 */
4808int unqlite_delete_constant(unqlite_vm *pVm, const char *zName)
4809{
4810 int rc;
4811 if( UNQLITE_VM_MISUSE(pVm) ){
4812 return UNQLITE_CORRUPT;
4813 }
4814#if defined(UNQLITE_ENABLE_THREADS)
4815 /* Acquire VM mutex */
4816 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4817 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4818 UNQLITE_THRD_VM_RELEASE(pVm) ){
4819 return UNQLITE_ABORT; /* Another thread have released this instance */
4820 }
4821#endif
4822 /* Unlink the foreign constant */
4823 rc = Jx9DeleteConstant(pVm->pJx9Vm,zName);
4824#if defined(UNQLITE_ENABLE_THREADS)
4825 /* Leave DB mutex */
4826 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4827#endif
4828 return rc;
4829}
4830/*
4831 * [CAPIREF: unqlite_value_int()]
4832 * Please refer to the official documentation for function purpose and expected parameters.
4833 */
4834int unqlite_value_int(unqlite_value *pVal, int iValue)
4835{
4836 return jx9_value_int(pVal,iValue);
4837}
4838/*
4839 * [CAPIREF: unqlite_value_int64()]
4840 * Please refer to the official documentation for function purpose and expected parameters.
4841 */
4842int unqlite_value_int64(unqlite_value *pVal,unqlite_int64 iValue)
4843{
4844 return jx9_value_int64(pVal,iValue);
4845}
4846/*
4847 * [CAPIREF: unqlite_value_bool()]
4848 * Please refer to the official documentation for function purpose and expected parameters.
4849 */
4850int unqlite_value_bool(unqlite_value *pVal, int iBool)
4851{
4852 return jx9_value_bool(pVal,iBool);
4853}
4854/*
4855 * [CAPIREF: unqlite_value_null()]
4856 * Please refer to the official documentation for function purpose and expected parameters.
4857 */
4858int unqlite_value_null(unqlite_value *pVal)
4859{
4860 return jx9_value_null(pVal);
4861}
4862/*
4863 * [CAPIREF: unqlite_value_double()]
4864 * Please refer to the official documentation for function purpose and expected parameters.
4865 */
4866int unqlite_value_double(unqlite_value *pVal, double Value)
4867{
4868 return jx9_value_double(pVal,Value);
4869}
4870/*
4871 * [CAPIREF: unqlite_value_string()]
4872 * Please refer to the official documentation for function purpose and expected parameters.
4873 */
4874int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen)
4875{
4876 return jx9_value_string(pVal,zString,nLen);
4877}
4878/*
4879 * [CAPIREF: unqlite_value_string_format()]
4880 * Please refer to the official documentation for function purpose and expected parameters.
4881 */
4882int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...)
4883{
4884 va_list ap;
4885 int rc;
4886 if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
4887 /* Invalidate any prior representation */
4888 jx9MemObjRelease(pVal);
4889 MemObjSetType(pVal, MEMOBJ_STRING);
4890 }
4891 va_start(ap, zFormat);
4892 rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap);
4893 va_end(ap);
4894 return UNQLITE_OK;
4895}
4896/*
4897 * [CAPIREF: unqlite_value_reset_string_cursor()]
4898 * Please refer to the official documentation for function purpose and expected parameters.
4899 */
4900int unqlite_value_reset_string_cursor(unqlite_value *pVal)
4901{
4902 return jx9_value_reset_string_cursor(pVal);
4903}
4904/*
4905 * [CAPIREF: unqlite_value_resource()]
4906 * Please refer to the official documentation for function purpose and expected parameters.
4907 */
4908int unqlite_value_resource(unqlite_value *pVal,void *pUserData)
4909{
4910 return jx9_value_resource(pVal,pUserData);
4911}
4912/*
4913 * [CAPIREF: unqlite_value_release()]
4914 * Please refer to the official documentation for function purpose and expected parameters.
4915 */
4916int unqlite_value_release(unqlite_value *pVal)
4917{
4918 return jx9_value_release(pVal);
4919}
4920/*
4921 * [CAPIREF: unqlite_value_to_int()]
4922 * Please refer to the official documentation for function purpose and expected parameters.
4923 */
4924int unqlite_value_to_int(unqlite_value *pValue)
4925{
4926 return jx9_value_to_int(pValue);
4927}
4928/*
4929 * [CAPIREF: unqlite_value_to_bool()]
4930 * Please refer to the official documentation for function purpose and expected parameters.
4931 */
4932int unqlite_value_to_bool(unqlite_value *pValue)
4933{
4934 return jx9_value_to_bool(pValue);
4935}
4936/*
4937 * [CAPIREF: unqlite_value_to_int64()]
4938 * Please refer to the official documentation for function purpose and expected parameters.
4939 */
4940unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue)
4941{
4942 return jx9_value_to_int64(pValue);
4943}
4944/*
4945 * [CAPIREF: unqlite_value_to_double()]
4946 * Please refer to the official documentation for function purpose and expected parameters.
4947 */
4948double unqlite_value_to_double(unqlite_value *pValue)
4949{
4950 return jx9_value_to_double(pValue);
4951}
4952/*
4953 * [CAPIREF: unqlite_value_to_string()]
4954 * Please refer to the official documentation for function purpose and expected parameters.
4955 */
4956const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen)
4957{
4958 return jx9_value_to_string(pValue,pLen);
4959}
4960/*
4961 * [CAPIREF: unqlite_value_to_resource()]
4962 * Please refer to the official documentation for function purpose and expected parameters.
4963 */
4964void * unqlite_value_to_resource(unqlite_value *pValue)
4965{
4966 return jx9_value_to_resource(pValue);
4967}
4968/*
4969 * [CAPIREF: unqlite_value_compare()]
4970 * Please refer to the official documentation for function purpose and expected parameters.
4971 */
4972int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict)
4973{
4974 return jx9_value_compare(pLeft,pRight,bStrict);
4975}
4976/*
4977 * [CAPIREF: unqlite_result_int()]
4978 * Please refer to the official documentation for function purpose and expected parameters.
4979 */
4980int unqlite_result_int(unqlite_context *pCtx, int iValue)
4981{
4982 return jx9_result_int(pCtx,iValue);
4983}
4984/*
4985 * [CAPIREF: unqlite_result_int64()]
4986 * Please refer to the official documentation for function purpose and expected parameters.
4987 */
4988int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue)
4989{
4990 return jx9_result_int64(pCtx,iValue);
4991}
4992/*
4993 * [CAPIREF: unqlite_result_bool()]
4994 * Please refer to the official documentation for function purpose and expected parameters.
4995 */
4996int unqlite_result_bool(unqlite_context *pCtx, int iBool)
4997{
4998 return jx9_result_bool(pCtx,iBool);
4999}
5000/*
5001 * [CAPIREF: unqlite_result_double()]
5002 * Please refer to the official documentation for function purpose and expected parameters.
5003 */
5004int unqlite_result_double(unqlite_context *pCtx, double Value)
5005{
5006 return jx9_result_double(pCtx,Value);
5007}
5008/*
5009 * [CAPIREF: unqlite_result_null()]
5010 * Please refer to the official documentation for function purpose and expected parameters.
5011 */
5012int unqlite_result_null(unqlite_context *pCtx)
5013{
5014 return jx9_result_null(pCtx);
5015}
5016/*
5017 * [CAPIREF: unqlite_result_string()]
5018 * Please refer to the official documentation for function purpose and expected parameters.
5019 */
5020int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen)
5021{
5022 return jx9_result_string(pCtx,zString,nLen);
5023}
5024/*
5025 * [CAPIREF: unqlite_result_string_format()]
5026 * Please refer to the official documentation for function purpose and expected parameters.
5027 */
5028int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...)
5029{
5030 jx9_value *p;
5031 va_list ap;
5032 int rc;
5033 p = pCtx->pRet;
5034 if( (p->iFlags & MEMOBJ_STRING) == 0 ){
5035 /* Invalidate any prior representation */
5036 jx9MemObjRelease(p);
5037 MemObjSetType(p, MEMOBJ_STRING);
5038 }
5039 /* Format the given string */
5040 va_start(ap, zFormat);
5041 rc = SyBlobFormatAp(&p->sBlob, zFormat, ap);
5042 va_end(ap);
5043 return rc;
5044}
5045/*
5046 * [CAPIREF: unqlite_result_value()]
5047 * Please refer to the official documentation for function purpose and expected parameters.
5048 */
5049int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue)
5050{
5051 return jx9_result_value(pCtx,pValue);
5052}
5053/*
5054 * [CAPIREF: unqlite_result_resource()]
5055 * Please refer to the official documentation for function purpose and expected parameters.
5056 */
5057int unqlite_result_resource(unqlite_context *pCtx, void *pUserData)
5058{
5059 return jx9_result_resource(pCtx,pUserData);
5060}
5061/*
5062 * [CAPIREF: unqlite_value_is_int()]
5063 * Please refer to the official documentation for function purpose and expected parameters.
5064 */
5065int unqlite_value_is_int(unqlite_value *pVal)
5066{
5067 return jx9_value_is_int(pVal);
5068}
5069/*
5070 * [CAPIREF: unqlite_value_is_float()]
5071 * Please refer to the official documentation for function purpose and expected parameters.
5072 */
5073int unqlite_value_is_float(unqlite_value *pVal)
5074{
5075 return jx9_value_is_float(pVal);
5076}
5077/*
5078 * [CAPIREF: unqlite_value_is_bool()]
5079 * Please refer to the official documentation for function purpose and expected parameters.
5080 */
5081int unqlite_value_is_bool(unqlite_value *pVal)
5082{
5083 return jx9_value_is_bool(pVal);
5084}
5085/*
5086 * [CAPIREF: unqlite_value_is_string()]
5087 * Please refer to the official documentation for function purpose and expected parameters.
5088 */
5089int unqlite_value_is_string(unqlite_value *pVal)
5090{
5091 return jx9_value_is_string(pVal);
5092}
5093/*
5094 * [CAPIREF: unqlite_value_is_null()]
5095 * Please refer to the official documentation for function purpose and expected parameters.
5096 */
5097int unqlite_value_is_null(unqlite_value *pVal)
5098{
5099 return jx9_value_is_null(pVal);
5100}
5101/*
5102 * [CAPIREF: unqlite_value_is_numeric()]
5103 * Please refer to the official documentation for function purpose and expected parameters.
5104 */
5105int unqlite_value_is_numeric(unqlite_value *pVal)
5106{
5107 return jx9_value_is_numeric(pVal);
5108}
5109/*
5110 * [CAPIREF: unqlite_value_is_callable()]
5111 * Please refer to the official documentation for function purpose and expected parameters.
5112 */
5113int unqlite_value_is_callable(unqlite_value *pVal)
5114{
5115 return jx9_value_is_callable(pVal);
5116}
5117/*
5118 * [CAPIREF: unqlite_value_is_scalar()]
5119 * Please refer to the official documentation for function purpose and expected parameters.
5120 */
5121int unqlite_value_is_scalar(unqlite_value *pVal)
5122{
5123 return jx9_value_is_scalar(pVal);
5124}
5125/*
5126 * [CAPIREF: unqlite_value_is_json_array()]
5127 * Please refer to the official documentation for function purpose and expected parameters.
5128 */
5129int unqlite_value_is_json_array(unqlite_value *pVal)
5130{
5131 return jx9_value_is_json_array(pVal);
5132}
5133/*
5134 * [CAPIREF: unqlite_value_is_json_object()]
5135 * Please refer to the official documentation for function purpose and expected parameters.
5136 */
5137int unqlite_value_is_json_object(unqlite_value *pVal)
5138{
5139 return jx9_value_is_json_object(pVal);
5140}
5141/*
5142 * [CAPIREF: unqlite_value_is_resource()]
5143 * Please refer to the official documentation for function purpose and expected parameters.
5144 */
5145int unqlite_value_is_resource(unqlite_value *pVal)
5146{
5147 return jx9_value_is_resource(pVal);
5148}
5149/*
5150 * [CAPIREF: unqlite_value_is_empty()]
5151 * Please refer to the official documentation for function purpose and expected parameters.
5152 */
5153int unqlite_value_is_empty(unqlite_value *pVal)
5154{
5155 return jx9_value_is_empty(pVal);
5156}
5157/*
5158 * [CAPIREF: unqlite_array_fetch()]
5159 * Please refer to the official documentation for function purpose and expected parameters.
5160 */
5161unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte)
5162{
5163 return jx9_array_fetch(pArray,zKey,nByte);
5164}
5165/*
5166 * [CAPIREF: unqlite_array_walk()]
5167 * Please refer to the official documentation for function purpose and expected parameters.
5168 */
5169int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData)
5170{
5171 return jx9_array_walk(pArray,xWalk,pUserData);
5172}
5173/*
5174 * [CAPIREF: unqlite_array_add_elem()]
5175 * Please refer to the official documentation for function purpose and expected parameters.
5176 */
5177int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue)
5178{
5179 return jx9_array_add_elem(pArray,pKey,pValue);
5180}
5181/*
5182 * [CAPIREF: unqlite_array_add_strkey_elem()]
5183 * Please refer to the official documentation for function purpose and expected parameters.
5184 */
5185int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue)
5186{
5187 return jx9_array_add_strkey_elem(pArray,zKey,pValue);
5188}
5189/*
5190 * [CAPIREF: unqlite_array_count()]
5191 * Please refer to the official documentation for function purpose and expected parameters.
5192 */
5193int unqlite_array_count(unqlite_value *pArray)
5194{
5195 return (int)jx9_array_count(pArray);
5196}
5197/*
5198 * [CAPIREF: unqlite_vm_new_scalar()]
5199 * Please refer to the official documentation for function purpose and expected parameters.
5200 */
5201unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm)
5202{
5203 unqlite_value *pValue;
5204 if( UNQLITE_VM_MISUSE(pVm) ){
5205 return 0;
5206 }
5207#if defined(UNQLITE_ENABLE_THREADS)
5208 /* Acquire VM mutex */
5209 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5210 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5211 UNQLITE_THRD_VM_RELEASE(pVm) ){
5212 return 0; /* Another thread have released this instance */
5213 }
5214#endif
5215 pValue = jx9_new_scalar(pVm->pJx9Vm);
5216#if defined(UNQLITE_ENABLE_THREADS)
5217 /* Leave DB mutex */
5218 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5219#endif
5220 return pValue;
5221}
5222/*
5223 * [CAPIREF: unqlite_vm_new_array()]
5224 * Please refer to the official documentation for function purpose and expected parameters.
5225 */
5226unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm)
5227{
5228 unqlite_value *pValue;
5229 if( UNQLITE_VM_MISUSE(pVm) ){
5230 return 0;
5231 }
5232#if defined(UNQLITE_ENABLE_THREADS)
5233 /* Acquire VM mutex */
5234 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5235 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5236 UNQLITE_THRD_VM_RELEASE(pVm) ){
5237 return 0; /* Another thread have released this instance */
5238 }
5239#endif
5240 pValue = jx9_new_array(pVm->pJx9Vm);
5241#if defined(UNQLITE_ENABLE_THREADS)
5242 /* Leave DB mutex */
5243 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5244#endif
5245 return pValue;
5246}
5247/*
5248 * [CAPIREF: unqlite_vm_release_value()]
5249 * Please refer to the official documentation for function purpose and expected parameters.
5250 */
5251int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue)
5252{
5253 int rc;
5254 if( UNQLITE_VM_MISUSE(pVm) ){
5255 return UNQLITE_CORRUPT;
5256 }
5257#if defined(UNQLITE_ENABLE_THREADS)
5258 /* Acquire VM mutex */
5259 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5260 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5261 UNQLITE_THRD_VM_RELEASE(pVm) ){
5262 return UNQLITE_ABORT; /* Another thread have released this instance */
5263 }
5264#endif
5265 rc = jx9_release_value(pVm->pJx9Vm,pValue);
5266#if defined(UNQLITE_ENABLE_THREADS)
5267 /* Leave DB mutex */
5268 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5269#endif
5270 return rc;
5271}
5272/*
5273 * [CAPIREF: unqlite_context_output()]
5274 * Please refer to the official documentation for function purpose and expected parameters.
5275 */
5276int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen)
5277{
5278 return jx9_context_output(pCtx,zString,nLen);
5279}
5280/*
5281 * [CAPIREF: unqlite_context_output_format()]
5282 * Please refer to the official documentation for function purpose and expected parameters.
5283 */
5284int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...)
5285{
5286 va_list ap;
5287 int rc;
5288 va_start(ap, zFormat);
5289 rc = jx9VmOutputConsumeAp(pCtx->pVm,zFormat, ap);
5290 va_end(ap);
5291 return rc;
5292}
5293/*
5294 * [CAPIREF: unqlite_context_throw_error()]
5295 * Please refer to the official documentation for function purpose and expected parameters.
5296 */
5297int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr)
5298{
5299 return jx9_context_throw_error(pCtx,iErr,zErr);
5300}
5301/*
5302 * [CAPIREF: unqlite_context_throw_error_format()]
5303 * Please refer to the official documentation for function purpose and expected parameters.
5304 */
5305int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...)
5306{
5307 va_list ap;
5308 int rc;
5309 if( zFormat == 0){
5310 return JX9_OK;
5311 }
5312 va_start(ap, zFormat);
5313 rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap);
5314 va_end(ap);
5315 return rc;
5316}
5317/*
5318 * [CAPIREF: unqlite_context_random_num()]
5319 * Please refer to the official documentation for function purpose and expected parameters.
5320 */
5321unsigned int unqlite_context_random_num(unqlite_context *pCtx)
5322{
5323 return jx9_context_random_num(pCtx);
5324}
5325/*
5326 * [CAPIREF: unqlite_context_random_string()]
5327 * Please refer to the official documentation for function purpose and expected parameters.
5328 */
5329int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen)
5330{
5331 return jx9_context_random_string(pCtx,zBuf,nBuflen);
5332}
5333/*
5334 * [CAPIREF: unqlite_context_user_data()]
5335 * Please refer to the official documentation for function purpose and expected parameters.
5336 */
5337void * unqlite_context_user_data(unqlite_context *pCtx)
5338{
5339 return jx9_context_user_data(pCtx);
5340}
5341/*
5342 * [CAPIREF: unqlite_context_push_aux_data()]
5343 * Please refer to the official documentation for function purpose and expected parameters.
5344 */
5345int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData)
5346{
5347 return jx9_context_push_aux_data(pCtx,pUserData);
5348}
5349/*
5350 * [CAPIREF: unqlite_context_peek_aux_data()]
5351 * Please refer to the official documentation for function purpose and expected parameters.
5352 */
5353void * unqlite_context_peek_aux_data(unqlite_context *pCtx)
5354{
5355 return jx9_context_peek_aux_data(pCtx);
5356}
5357/*
5358 * [CAPIREF: unqlite_context_pop_aux_data()]
5359 * Please refer to the official documentation for function purpose and expected parameters.
5360 */
5361void * unqlite_context_pop_aux_data(unqlite_context *pCtx)
5362{
5363 return jx9_context_pop_aux_data(pCtx);
5364}
5365/*
5366 * [CAPIREF: unqlite_context_result_buf_length()]
5367 * Please refer to the official documentation for function purpose and expected parameters.
5368 */
5369unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx)
5370{
5371 return jx9_context_result_buf_length(pCtx);
5372}
5373/*
5374 * [CAPIREF: unqlite_function_name()]
5375 * Please refer to the official documentation for function purpose and expected parameters.
5376 */
5377const char * unqlite_function_name(unqlite_context *pCtx)
5378{
5379 return jx9_function_name(pCtx);
5380}
5381/*
5382 * [CAPIREF: unqlite_context_new_scalar()]
5383 * Please refer to the official documentation for function purpose and expected parameters.
5384 */
5385unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx)
5386{
5387 return jx9_context_new_scalar(pCtx);
5388}
5389/*
5390 * [CAPIREF: unqlite_context_new_array()]
5391 * Please refer to the official documentation for function purpose and expected parameters.
5392 */
5393unqlite_value * unqlite_context_new_array(unqlite_context *pCtx)
5394{
5395 return jx9_context_new_array(pCtx);
5396}
5397/*
5398 * [CAPIREF: unqlite_context_release_value()]
5399 * Please refer to the official documentation for function purpose and expected parameters.
5400 */
5401void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue)
5402{
5403 jx9_context_release_value(pCtx,pValue);
5404}
5405/*
5406 * [CAPIREF: unqlite_context_alloc_chunk()]
5407 * Please refer to the official documentation for function purpose and expected parameters.
5408 */
5409void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease)
5410{
5411 return jx9_context_alloc_chunk(pCtx,nByte,ZeroChunk,AutoRelease);
5412}
5413/*
5414 * [CAPIREF: unqlite_context_realloc_chunk()]
5415 * Please refer to the official documentation for function purpose and expected parameters.
5416 */
5417void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte)
5418{
5419 return jx9_context_realloc_chunk(pCtx,pChunk,nByte);
5420}
5421/*
5422 * [CAPIREF: unqlite_context_free_chunk()]
5423 * Please refer to the official documentation for function purpose and expected parameters.
5424 */
5425void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk)
5426{
5427 jx9_context_free_chunk(pCtx,pChunk);
5428}
5429/*
5430 * [CAPIREF: unqlite_kv_store()]
5431 * Please refer to the official documentation for function purpose and expected parameters.
5432 */
5433int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen)
5434{
5435 unqlite_kv_engine *pEngine;
5436 int rc;
5437 if( UNQLITE_DB_MISUSE(pDb) ){
5438 return UNQLITE_CORRUPT;
5439 }
5440#if defined(UNQLITE_ENABLE_THREADS)
5441 /* Acquire DB mutex */
5442 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5443 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5444 UNQLITE_THRD_DB_RELEASE(pDb) ){
5445 return UNQLITE_ABORT; /* Another thread have released this instance */
5446 }
5447#endif
5448 /* Point to the underlying storage engine */
5449 pEngine = unqlitePagerGetKvEngine(pDb);
5450 if( pEngine->pIo->pMethods->xReplace == 0 ){
5451 /* Storage engine does not implement such method */
5452 unqliteGenError(pDb,"xReplace() method not implemented in the underlying storage engine");
5453 rc = UNQLITE_NOTIMPLEMENTED;
5454 }else{
5455 if( nKeyLen < 0 ){
5456 /* Assume a null terminated string and compute it's length */
5457 nKeyLen = SyStrlen((const char *)pKey);
5458 }
5459 if( !nKeyLen ){
5460 unqliteGenError(pDb,"Empty key");
5461 rc = UNQLITE_EMPTY;
5462 }else{
5463 /* Perform the requested operation */
5464 rc = pEngine->pIo->pMethods->xReplace(pEngine,pKey,nKeyLen,pData,nDataLen);
5465 }
5466 }
5467#if defined(UNQLITE_ENABLE_THREADS)
5468 /* Leave DB mutex */
5469 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5470#endif
5471 return rc;
5472}
5473/*
5474 * [CAPIREF: unqlite_kv_store_fmt()]
5475 * Please refer to the official documentation for function purpose and expected parameters.
5476 */
5477int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...)
5478{
5479 unqlite_kv_engine *pEngine;
5480 int rc;
5481 if( UNQLITE_DB_MISUSE(pDb) ){
5482 return UNQLITE_CORRUPT;
5483 }
5484#if defined(UNQLITE_ENABLE_THREADS)
5485 /* Acquire DB mutex */
5486 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5487 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5488 UNQLITE_THRD_DB_RELEASE(pDb) ){
5489 return UNQLITE_ABORT; /* Another thread have released this instance */
5490 }
5491#endif
5492 /* Point to the underlying storage engine */
5493 pEngine = unqlitePagerGetKvEngine(pDb);
5494 if( pEngine->pIo->pMethods->xReplace == 0 ){
5495 /* Storage engine does not implement such method */
5496 unqliteGenError(pDb,"xReplace() method not implemented in the underlying storage engine");
5497 rc = UNQLITE_NOTIMPLEMENTED;
5498 }else{
5499 if( nKeyLen < 0 ){
5500 /* Assume a null terminated string and compute it's length */
5501 nKeyLen = SyStrlen((const char *)pKey);
5502 }
5503 if( !nKeyLen ){
5504 unqliteGenError(pDb,"Empty key");
5505 rc = UNQLITE_EMPTY;
5506 }else{
5507 SyBlob sWorker; /* Working buffer */
5508 va_list ap;
5509 SyBlobInit(&sWorker,&pDb->sMem);
5510 /* Format the data */
5511 va_start(ap,zFormat);
5512 SyBlobFormatAp(&sWorker,zFormat,ap);
5513 va_end(ap);
5514 /* Perform the requested operation */
5515 rc = pEngine->pIo->pMethods->xReplace(pEngine,pKey,nKeyLen,SyBlobData(&sWorker),SyBlobLength(&sWorker));
5516 /* Clean up */
5517 SyBlobRelease(&sWorker);
5518 }
5519 }
5520#if defined(UNQLITE_ENABLE_THREADS)
5521 /* Leave DB mutex */
5522 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5523#endif
5524 return rc;
5525}
5526/*
5527 * [CAPIREF: unqlite_kv_append()]
5528 * Please refer to the official documentation for function purpose and expected parameters.
5529 */
5530int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen)
5531{
5532 unqlite_kv_engine *pEngine;
5533 int rc;
5534 if( UNQLITE_DB_MISUSE(pDb) ){
5535 return UNQLITE_CORRUPT;
5536 }
5537#if defined(UNQLITE_ENABLE_THREADS)
5538 /* Acquire DB mutex */
5539 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5540 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5541 UNQLITE_THRD_DB_RELEASE(pDb) ){
5542 return UNQLITE_ABORT; /* Another thread have released this instance */
5543 }
5544#endif
5545 /* Point to the underlying storage engine */
5546 pEngine = unqlitePagerGetKvEngine(pDb);
5547 if( pEngine->pIo->pMethods->xAppend == 0 ){
5548 /* Storage engine does not implement such method */
5549 unqliteGenError(pDb,"xAppend() method not implemented in the underlying storage engine");
5550 rc = UNQLITE_NOTIMPLEMENTED;
5551 }else{
5552 if( nKeyLen < 0 ){
5553 /* Assume a null terminated string and compute it's length */
5554 nKeyLen = SyStrlen((const char *)pKey);
5555 }
5556 if( !nKeyLen ){
5557 unqliteGenError(pDb,"Empty key");
5558 rc = UNQLITE_EMPTY;
5559 }else{
5560 /* Perform the requested operation */
5561 rc = pEngine->pIo->pMethods->xAppend(pEngine,pKey,nKeyLen,pData,nDataLen);
5562 }
5563 }
5564#if defined(UNQLITE_ENABLE_THREADS)
5565 /* Leave DB mutex */
5566 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5567#endif
5568 return rc;
5569}
5570/*
5571 * [CAPIREF: unqlite_kv_append_fmt()]
5572 * Please refer to the official documentation for function purpose and expected parameters.
5573 */
5574int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...)
5575{
5576 unqlite_kv_engine *pEngine;
5577 int rc;
5578 if( UNQLITE_DB_MISUSE(pDb) ){
5579 return UNQLITE_CORRUPT;
5580 }
5581#if defined(UNQLITE_ENABLE_THREADS)
5582 /* Acquire DB mutex */
5583 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5584 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5585 UNQLITE_THRD_DB_RELEASE(pDb) ){
5586 return UNQLITE_ABORT; /* Another thread have released this instance */
5587 }
5588#endif
5589 /* Point to the underlying storage engine */
5590 pEngine = unqlitePagerGetKvEngine(pDb);
5591 if( pEngine->pIo->pMethods->xAppend == 0 ){
5592 /* Storage engine does not implement such method */
5593 unqliteGenError(pDb,"xAppend() method not implemented in the underlying storage engine");
5594 rc = UNQLITE_NOTIMPLEMENTED;
5595 }else{
5596 if( nKeyLen < 0 ){
5597 /* Assume a null terminated string and compute it's length */
5598 nKeyLen = SyStrlen((const char *)pKey);
5599 }
5600 if( !nKeyLen ){
5601 unqliteGenError(pDb,"Empty key");
5602 rc = UNQLITE_EMPTY;
5603 }else{
5604 SyBlob sWorker; /* Working buffer */
5605 va_list ap;
5606 SyBlobInit(&sWorker,&pDb->sMem);
5607 /* Format the data */
5608 va_start(ap,zFormat);
5609 SyBlobFormatAp(&sWorker,zFormat,ap);
5610 va_end(ap);
5611 /* Perform the requested operation */
5612 rc = pEngine->pIo->pMethods->xAppend(pEngine,pKey,nKeyLen,SyBlobData(&sWorker),SyBlobLength(&sWorker));
5613 /* Clean up */
5614 SyBlobRelease(&sWorker);
5615 }
5616 }
5617#if defined(UNQLITE_ENABLE_THREADS)
5618 /* Leave DB mutex */
5619 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5620#endif
5621 return rc;
5622}
5623/*
5624 * [CAPIREF: unqlite_kv_fetch()]
5625 * Please refer to the official documentation for function purpose and expected parameters.
5626 */
5627int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 *pBufLen)
5628{
5629 unqlite_kv_methods *pMethods;
5630 unqlite_kv_engine *pEngine;
5631 unqlite_kv_cursor *pCur;
5632 int rc;
5633 if( UNQLITE_DB_MISUSE(pDb) ){
5634 return UNQLITE_CORRUPT;
5635 }
5636#if defined(UNQLITE_ENABLE_THREADS)
5637 /* Acquire DB mutex */
5638 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5639 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5640 UNQLITE_THRD_DB_RELEASE(pDb) ){
5641 return UNQLITE_ABORT; /* Another thread have released this instance */
5642 }
5643#endif
5644 /* Point to the underlying storage engine */
5645 pEngine = unqlitePagerGetKvEngine(pDb);
5646 pMethods = pEngine->pIo->pMethods;
5647 pCur = pDb->sDB.pCursor;
5648 if( nKeyLen < 0 ){
5649 /* Assume a null terminated string and compute it's length */
5650 nKeyLen = SyStrlen((const char *)pKey);
5651 }
5652 if( !nKeyLen ){
5653 unqliteGenError(pDb,"Empty key");
5654 rc = UNQLITE_EMPTY;
5655 }else{
5656 /* Seek to the record position */
5657 rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
5658 }
5659 if( rc == UNQLITE_OK ){
5660 if( pBuf == 0 ){
5661 /* Data length only */
5662 rc = pMethods->xDataLength(pCur,pBufLen);
5663 }else{
5664 SyBlob sBlob;
5665 /* Initialize the data consumer */
5666 SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)*pBufLen);
5667 /* Consume the data */
5668 rc = pMethods->xData(pCur,unqliteDataConsumer,&sBlob);
5669 /* Data length */
5670 *pBufLen = (unqlite_int64)SyBlobLength(&sBlob);
5671 /* Cleanup */
5672 SyBlobRelease(&sBlob);
5673 }
5674 }
5675#if defined(UNQLITE_ENABLE_THREADS)
5676 /* Leave DB mutex */
5677 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5678#endif
5679 return rc;
5680}
5681/*
5682 * [CAPIREF: unqlite_kv_fetch_callback()]
5683 * Please refer to the official documentation for function purpose and expected parameters.
5684 */
5685int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
5686{
5687 unqlite_kv_methods *pMethods;
5688 unqlite_kv_engine *pEngine;
5689 unqlite_kv_cursor *pCur;
5690 int rc;
5691 if( UNQLITE_DB_MISUSE(pDb) ){
5692 return UNQLITE_CORRUPT;
5693 }
5694#if defined(UNQLITE_ENABLE_THREADS)
5695 /* Acquire DB mutex */
5696 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5697 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5698 UNQLITE_THRD_DB_RELEASE(pDb) ){
5699 return UNQLITE_ABORT; /* Another thread have released this instance */
5700 }
5701#endif
5702 /* Point to the underlying storage engine */
5703 pEngine = unqlitePagerGetKvEngine(pDb);
5704 pMethods = pEngine->pIo->pMethods;
5705 pCur = pDb->sDB.pCursor;
5706 if( nKeyLen < 0 ){
5707 /* Assume a null terminated string and compute it's length */
5708 nKeyLen = SyStrlen((const char *)pKey);
5709 }
5710 if( !nKeyLen ){
5711 unqliteGenError(pDb,"Empty key");
5712 rc = UNQLITE_EMPTY;
5713 }else{
5714 /* Seek to the record position */
5715 rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
5716 }
5717 if( rc == UNQLITE_OK && xConsumer ){
5718 /* Consume the data directly */
5719 rc = pMethods->xData(pCur,xConsumer,pUserData);
5720 }
5721#if defined(UNQLITE_ENABLE_THREADS)
5722 /* Leave DB mutex */
5723 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5724#endif
5725 return rc;
5726}
5727/*
5728 * [CAPIREF: unqlite_kv_delete()]
5729 * Please refer to the official documentation for function purpose and expected parameters.
5730 */
5731int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen)
5732{
5733 unqlite_kv_methods *pMethods;
5734 unqlite_kv_engine *pEngine;
5735 unqlite_kv_cursor *pCur;
5736 int rc;
5737 if( UNQLITE_DB_MISUSE(pDb) ){
5738 return UNQLITE_CORRUPT;
5739 }
5740#if defined(UNQLITE_ENABLE_THREADS)
5741 /* Acquire DB mutex */
5742 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5743 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5744 UNQLITE_THRD_DB_RELEASE(pDb) ){
5745 return UNQLITE_ABORT; /* Another thread have released this instance */
5746 }
5747#endif
5748 /* Point to the underlying storage engine */
5749 pEngine = unqlitePagerGetKvEngine(pDb);
5750 pMethods = pEngine->pIo->pMethods;
5751 pCur = pDb->sDB.pCursor;
5752 if( pMethods->xDelete == 0 ){
5753 /* Storage engine does not implement such method */
5754 unqliteGenError(pDb,"xDelete() method not implemented in the underlying storage engine");
5755 rc = UNQLITE_NOTIMPLEMENTED;
5756 }else{
5757 if( nKeyLen < 0 ){
5758 /* Assume a null terminated string and compute it's length */
5759 nKeyLen = SyStrlen((const char *)pKey);
5760 }
5761 if( !nKeyLen ){
5762 unqliteGenError(pDb,"Empty key");
5763 rc = UNQLITE_EMPTY;
5764 }else{
5765 /* Seek to the record position */
5766 rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
5767 }
5768 if( rc == UNQLITE_OK ){
5769 /* Exact match found, delete the entry */
5770 rc = pMethods->xDelete(pCur);
5771 }
5772 }
5773#if defined(UNQLITE_ENABLE_THREADS)
5774 /* Leave DB mutex */
5775 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5776#endif
5777 return rc;
5778}
5779/*
5780 * [CAPIREF: unqlite_kv_config()]
5781 * Please refer to the official documentation for function purpose and expected parameters.
5782 */
5783int unqlite_kv_config(unqlite *pDb,int iOp,...)
5784{
5785 unqlite_kv_engine *pEngine;
5786 int rc;
5787 if( UNQLITE_DB_MISUSE(pDb) ){
5788 return UNQLITE_CORRUPT;
5789 }
5790#if defined(UNQLITE_ENABLE_THREADS)
5791 /* Acquire DB mutex */
5792 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5793 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5794 UNQLITE_THRD_DB_RELEASE(pDb) ){
5795 return UNQLITE_ABORT; /* Another thread have released this instance */
5796 }
5797#endif
5798 /* Point to the underlying storage engine */
5799 pEngine = unqlitePagerGetKvEngine(pDb);
5800 if( pEngine->pIo->pMethods->xConfig == 0 ){
5801 /* Storage engine does not implements such method */
5802 unqliteGenError(pDb,"xConfig() method not implemented in the underlying storage engine");
5803 rc = UNQLITE_NOTIMPLEMENTED;
5804 }else{
5805 va_list ap;
5806 /* Configure the storage engine */
5807 va_start(ap,iOp);
5808 rc = pEngine->pIo->pMethods->xConfig(pEngine,iOp,ap);
5809 va_end(ap);
5810 }
5811#if defined(UNQLITE_ENABLE_THREADS)
5812 /* Leave DB mutex */
5813 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5814#endif
5815 return rc;
5816}
5817/*
5818 * [CAPIREF: unqlite_kv_cursor_init()]
5819 * Please refer to the official documentation for function purpose and expected parameters.
5820 */
5821int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut)
5822{
5823 int rc;
5824 if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0 /* Noop */){
5825 return UNQLITE_CORRUPT;
5826 }
5827#if defined(UNQLITE_ENABLE_THREADS)
5828 /* Acquire DB mutex */
5829 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5830 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5831 UNQLITE_THRD_DB_RELEASE(pDb) ){
5832 return UNQLITE_ABORT; /* Another thread have released this instance */
5833 }
5834#endif
5835 /* Allocate a new cursor */
5836 rc = unqliteInitCursor(pDb,ppOut);
5837#if defined(UNQLITE_ENABLE_THREADS)
5838 /* Leave DB mutex */
5839 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5840#endif
5841 return rc;
5842}
5843/*
5844 * [CAPIREF: unqlite_kv_cursor_release()]
5845 * Please refer to the official documentation for function purpose and expected parameters.
5846 */
5847int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur)
5848{
5849 int rc;
5850 if( UNQLITE_DB_MISUSE(pDb) || pCur == 0 /* Noop */){
5851 return UNQLITE_CORRUPT;
5852 }
5853#if defined(UNQLITE_ENABLE_THREADS)
5854 /* Acquire DB mutex */
5855 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5856 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5857 UNQLITE_THRD_DB_RELEASE(pDb) ){
5858 return UNQLITE_ABORT; /* Another thread have released this instance */
5859 }
5860#endif
5861 /* Release the cursor */
5862 rc = unqliteReleaseCursor(pDb,pCur);
5863#if defined(UNQLITE_ENABLE_THREADS)
5864 /* Leave DB mutex */
5865 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5866#endif
5867 return rc;
5868}
5869/*
5870 * [CAPIREF: unqlite_kv_cursor_first_entry()]
5871 * Please refer to the official documentation for function purpose and expected parameters.
5872 */
5873int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor)
5874{
5875 int rc;
5876#ifdef UNTRUST
5877 if( pCursor == 0 ){
5878 return UNQLITE_CORRUPT;
5879 }
5880#endif
5881 /* Check if the requested method is implemented by the underlying storage engine */
5882 if( pCursor->pStore->pIo->pMethods->xFirst == 0 ){
5883 rc = UNQLITE_NOTIMPLEMENTED;
5884 }else{
5885 /* Seek to the first entry */
5886 rc = pCursor->pStore->pIo->pMethods->xFirst(pCursor);
5887 }
5888 return rc;
5889}
5890/*
5891 * [CAPIREF: unqlite_kv_cursor_last_entry()]
5892 * Please refer to the official documentation for function purpose and expected parameters.
5893 */
5894int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor)
5895{
5896 int rc;
5897#ifdef UNTRUST
5898 if( pCursor == 0 ){
5899 return UNQLITE_CORRUPT;
5900 }
5901#endif
5902 /* Check if the requested method is implemented by the underlying storage engine */
5903 if( pCursor->pStore->pIo->pMethods->xLast == 0 ){
5904 rc = UNQLITE_NOTIMPLEMENTED;
5905 }else{
5906 /* Seek to the last entry */
5907 rc = pCursor->pStore->pIo->pMethods->xLast(pCursor);
5908 }
5909 return rc;
5910}
5911/*
5912 * [CAPIREF: unqlite_kv_cursor_valid_entry()]
5913 * Please refer to the official documentation for function purpose and expected parameters.
5914 */
5915int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor)
5916{
5917 int rc;
5918#ifdef UNTRUST
5919 if( pCursor == 0 ){
5920 return UNQLITE_CORRUPT;
5921 }
5922#endif
5923 /* Check if the requested method is implemented by the underlying storage engine */
5924 if( pCursor->pStore->pIo->pMethods->xValid == 0 ){
5925 rc = UNQLITE_NOTIMPLEMENTED;
5926 }else{
5927 rc = pCursor->pStore->pIo->pMethods->xValid(pCursor);
5928 }
5929 return rc;
5930}
5931/*
5932 * [CAPIREF: unqlite_kv_cursor_next_entry()]
5933 * Please refer to the official documentation for function purpose and expected parameters.
5934 */
5935int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor)
5936{
5937 int rc;
5938#ifdef UNTRUST
5939 if( pCursor == 0 ){
5940 return UNQLITE_CORRUPT;
5941 }
5942#endif
5943 /* Check if the requested method is implemented by the underlying storage engine */
5944 if( pCursor->pStore->pIo->pMethods->xNext == 0 ){
5945 rc = UNQLITE_NOTIMPLEMENTED;
5946 }else{
5947 /* Seek to the next entry */
5948 rc = pCursor->pStore->pIo->pMethods->xNext(pCursor);
5949 }
5950 return rc;
5951}
5952/*
5953 * [CAPIREF: unqlite_kv_cursor_prev_entry()]
5954 * Please refer to the official documentation for function purpose and expected parameters.
5955 */
5956int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor)
5957{
5958 int rc;
5959#ifdef UNTRUST
5960 if( pCursor == 0 ){
5961 return UNQLITE_CORRUPT;
5962 }
5963#endif
5964 /* Check if the requested method is implemented by the underlying storage engine */
5965 if( pCursor->pStore->pIo->pMethods->xPrev == 0 ){
5966 rc = UNQLITE_NOTIMPLEMENTED;
5967 }else{
5968 /* Seek to the previous entry */
5969 rc = pCursor->pStore->pIo->pMethods->xPrev(pCursor);
5970 }
5971 return rc;
5972}
5973/*
5974 * [CAPIREF: unqlite_kv_cursor_delete_entry()]
5975 * Please refer to the official documentation for function purpose and expected parameters.
5976 */
5977int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor)
5978{
5979 int rc;
5980#ifdef UNTRUST
5981 if( pCursor == 0 ){
5982 return UNQLITE_CORRUPT;
5983 }
5984#endif
5985 /* Check if the requested method is implemented by the underlying storage engine */
5986 if( pCursor->pStore->pIo->pMethods->xDelete == 0 ){
5987 rc = UNQLITE_NOTIMPLEMENTED;
5988 }else{
5989 /* Delete the entry */
5990 rc = pCursor->pStore->pIo->pMethods->xDelete(pCursor);
5991 }
5992 return rc;
5993}
5994/*
5995 * [CAPIREF: unqlite_kv_cursor_reset()]
5996 * Please refer to the official documentation for function purpose and expected parameters.
5997 */
5998int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor)
5999{
6000 int rc = UNQLITE_OK;
6001#ifdef UNTRUST
6002 if( pCursor == 0 ){
6003 return UNQLITE_CORRUPT;
6004 }
6005#endif
6006 /* Check if the requested method is implemented by the underlying storage engine */
6007 if( pCursor->pStore->pIo->pMethods->xReset == 0 ){
6008 rc = UNQLITE_NOTIMPLEMENTED;
6009 }else{
6010 /* Reset */
6011 pCursor->pStore->pIo->pMethods->xReset(pCursor);
6012 }
6013 return rc;
6014}
6015/*
6016 * [CAPIREF: unqlite_kv_cursor_seek()]
6017 * Please refer to the official documentation for function purpose and expected parameters.
6018 */
6019int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos)
6020{
6021 int rc = UNQLITE_OK;
6022#ifdef UNTRUST
6023 if( pCursor == 0 ){
6024 return UNQLITE_CORRUPT;
6025 }
6026#endif
6027 if( nKeyLen < 0 ){
6028 /* Assume a null terminated string and compute it's length */
6029 nKeyLen = SyStrlen((const char *)pKey);
6030 }
6031 if( !nKeyLen ){
6032 rc = UNQLITE_EMPTY;
6033 }else{
6034 /* Seek to the desired location */
6035 rc = pCursor->pStore->pIo->pMethods->xSeek(pCursor,pKey,nKeyLen,iPos);
6036 }
6037 return rc;
6038}
6039/*
6040 * Default data consumer callback. That is, all retrieved is redirected to this
6041 * routine which store the output in an internal blob.
6042 */
6043UNQLITE_PRIVATE int unqliteDataConsumer(
6044 const void *pOut, /* Data to consume */
6045 unsigned int nLen, /* Data length */
6046 void *pUserData /* User private data */
6047 )
6048{
6049 sxi32 rc;
6050 /* Store the output in an internal BLOB */
6051 rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen);
6052 return rc;
6053}
6054/*
6055 * [CAPIREF: unqlite_kv_cursor_data_callback()]
6056 * Please refer to the official documentation for function purpose and expected parameters.
6057 */
6058int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
6059{
6060 int rc;
6061#ifdef UNTRUST
6062 if( pCursor == 0 ){
6063 return UNQLITE_CORRUPT;
6064 }
6065#endif
6066 /* Consume the key directly */
6067 rc = pCursor->pStore->pIo->pMethods->xKey(pCursor,xConsumer,pUserData);
6068 return rc;
6069}
6070/*
6071 * [CAPIREF: unqlite_kv_cursor_key()]
6072 * Please refer to the official documentation for function purpose and expected parameters.
6073 */
6074int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte)
6075{
6076 int rc;
6077#ifdef UNTRUST
6078 if( pCursor == 0 ){
6079 return UNQLITE_CORRUPT;
6080 }
6081#endif
6082 if( pBuf == 0 ){
6083 /* Key length only */
6084 rc = pCursor->pStore->pIo->pMethods->xKeyLength(pCursor,pnByte);
6085 }else{
6086 SyBlob sBlob;
6087 if( (*pnByte) < 0 ){
6088 return UNQLITE_CORRUPT;
6089 }
6090 /* Initialize the data consumer */
6091 SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)(*pnByte));
6092 /* Consume the key */
6093 rc = pCursor->pStore->pIo->pMethods->xKey(pCursor,unqliteDataConsumer,&sBlob);
6094 /* Key length */
6095 *pnByte = SyBlobLength(&sBlob);
6096 /* Cleanup */
6097 SyBlobRelease(&sBlob);
6098 }
6099 return rc;
6100}
6101/*
6102 * [CAPIREF: unqlite_kv_cursor_data_callback()]
6103 * Please refer to the official documentation for function purpose and expected parameters.
6104 */
6105int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
6106{
6107 int rc;
6108#ifdef UNTRUST
6109 if( pCursor == 0 ){
6110 return UNQLITE_CORRUPT;
6111 }
6112#endif
6113 /* Consume the data directly */
6114 rc = pCursor->pStore->pIo->pMethods->xData(pCursor,xConsumer,pUserData);
6115 return rc;
6116}
6117/*
6118 * [CAPIREF: unqlite_kv_cursor_data()]
6119 * Please refer to the official documentation for function purpose and expected parameters.
6120 */
6121int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnByte)
6122{
6123 int rc;
6124#ifdef UNTRUST
6125 if( pCursor == 0 ){
6126 return UNQLITE_CORRUPT;
6127 }
6128#endif
6129 if( pBuf == 0 ){
6130 /* Data length only */
6131 rc = pCursor->pStore->pIo->pMethods->xDataLength(pCursor,pnByte);
6132 }else{
6133 SyBlob sBlob;
6134 if( (*pnByte) < 0 ){
6135 return UNQLITE_CORRUPT;
6136 }
6137 /* Initialize the data consumer */
6138 SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)(*pnByte));
6139 /* Consume the data */
6140 rc = pCursor->pStore->pIo->pMethods->xData(pCursor,unqliteDataConsumer,&sBlob);
6141 /* Data length */
6142 *pnByte = SyBlobLength(&sBlob);
6143 /* Cleanup */
6144 SyBlobRelease(&sBlob);
6145 }
6146 return rc;
6147}
6148/*
6149 * [CAPIREF: unqlite_begin()]
6150 * Please refer to the official documentation for function purpose and expected parameters.
6151 */
6152int unqlite_begin(unqlite *pDb)
6153{
6154 int rc;
6155 if( UNQLITE_DB_MISUSE(pDb) ){
6156 return UNQLITE_CORRUPT;
6157 }
6158#if defined(UNQLITE_ENABLE_THREADS)
6159 /* Acquire DB mutex */
6160 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6161 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
6162 UNQLITE_THRD_DB_RELEASE(pDb) ){
6163 return UNQLITE_ABORT; /* Another thread have released this instance */
6164 }
6165#endif
6166 /* Begin the write transaction */
6167 rc = unqlitePagerBegin(pDb->sDB.pPager);
6168#if defined(UNQLITE_ENABLE_THREADS)
6169 /* Leave DB mutex */
6170 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6171#endif
6172 return rc;
6173}
6174/*
6175 * [CAPIREF: unqlite_commit()]
6176 * Please refer to the official documentation for function purpose and expected parameters.
6177 */
6178int unqlite_commit(unqlite *pDb)
6179{
6180 int rc;
6181 if( UNQLITE_DB_MISUSE(pDb) ){
6182 return UNQLITE_CORRUPT;
6183 }
6184#if defined(UNQLITE_ENABLE_THREADS)
6185 /* Acquire DB mutex */
6186 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6187 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
6188 UNQLITE_THRD_DB_RELEASE(pDb) ){
6189 return UNQLITE_ABORT; /* Another thread have released this instance */
6190 }
6191#endif
6192 /* Commit the transaction */
6193 rc = unqlitePagerCommit(pDb->sDB.pPager);
6194#if defined(UNQLITE_ENABLE_THREADS)
6195 /* Leave DB mutex */
6196 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6197#endif
6198 return rc;
6199}
6200/*
6201 * [CAPIREF: unqlite_rollback()]
6202 * Please refer to the official documentation for function purpose and expected parameters.
6203 */
6204int unqlite_rollback(unqlite *pDb)
6205{
6206 int rc;
6207 if( UNQLITE_DB_MISUSE(pDb) ){
6208 return UNQLITE_CORRUPT;
6209 }
6210#if defined(UNQLITE_ENABLE_THREADS)
6211 /* Acquire DB mutex */
6212 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6213 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
6214 UNQLITE_THRD_DB_RELEASE(pDb) ){
6215 return UNQLITE_ABORT; /* Another thread have released this instance */
6216 }
6217#endif
6218 /* Rollback the transaction */
6219 rc = unqlitePagerRollback(pDb->sDB.pPager,TRUE);
6220#if defined(UNQLITE_ENABLE_THREADS)
6221 /* Leave DB mutex */
6222 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6223#endif
6224 return rc;
6225}
6226/*
6227 * [CAPIREF: unqlite_util_load_mmaped_file()]
6228 * Please refer to the official documentation for function purpose and expected parameters.
6229 */
6230UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize)
6231{
6232 const jx9_vfs *pVfs;
6233 int rc;
6234 if( SX_EMPTY_STR(zFile) || ppMap == 0 || pFileSize == 0){
6235 /* Sanity check */
6236 return UNQLITE_CORRUPT;
6237 }
6238 *ppMap = 0;
6239 /* Extract the Jx9 Vfs */
6240 pVfs = jx9ExportBuiltinVfs();
6241 /*
6242 * Check if the underlying vfs implement the memory map routines
6243 * [i.e: mmap() under UNIX/MapViewOfFile() under windows].
6244 */
6245 if( pVfs == 0 || pVfs->xMmap == 0 ){
6246 rc = UNQLITE_NOTIMPLEMENTED;
6247 }else{
6248 /* Try to get a read-only memory view of the whole file */
6249 rc = pVfs->xMmap(zFile,ppMap,pFileSize);
6250 }
6251 return rc;
6252}
6253/*
6254 * [CAPIREF: unqlite_util_release_mmaped_file()]
6255 * Please refer to the official documentation for function purpose and expected parameters.
6256 */
6257UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize)
6258{
6259 const jx9_vfs *pVfs;
6260 int rc = UNQLITE_OK;
6261 if( pMap == 0 ){
6262 return UNQLITE_OK;
6263 }
6264 /* Extract the Jx9 Vfs */
6265 pVfs = jx9ExportBuiltinVfs();
6266 if( pVfs == 0 || pVfs->xUnmap == 0 ){
6267 rc = UNQLITE_NOTIMPLEMENTED;
6268 }else{
6269 pVfs->xUnmap(pMap,iFileSize);
6270 }
6271 return rc;
6272}
6273/*
6274 * [CAPIREF: unqlite_util_random_string()]
6275 * Please refer to the official documentation for function purpose and expected parameters.
6276 */
6277UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size)
6278{
6279 if( UNQLITE_DB_MISUSE(pDb) ){
6280 return UNQLITE_CORRUPT;
6281 }
6282 if( zBuf == 0 || buf_size < 3 ){
6283 /* Buffer must be long enough to hold three bytes */
6284 return UNQLITE_INVALID;
6285 }
6286#if defined(UNQLITE_ENABLE_THREADS)
6287 /* Acquire DB mutex */
6288 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6289 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
6290 UNQLITE_THRD_DB_RELEASE(pDb) ){
6291 return UNQLITE_ABORT; /* Another thread have released this instance */
6292 }
6293#endif
6294 /* Generate the random string */
6295 unqlitePagerRandomString(pDb->sDB.pPager,zBuf,buf_size);
6296#if defined(UNQLITE_ENABLE_THREADS)
6297 /* Leave DB mutex */
6298 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6299#endif
6300 return UNQLITE_OK;
6301}
6302/*
6303 * [CAPIREF: unqlite_util_random_num()]
6304 * Please refer to the official documentation for function purpose and expected parameters.
6305 */
6306UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb)
6307{
6308 sxu32 iNum;
6309 if( UNQLITE_DB_MISUSE(pDb) ){
6310 return 0;
6311 }
6312#if defined(UNQLITE_ENABLE_THREADS)
6313 /* Acquire DB mutex */
6314 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6315 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
6316 UNQLITE_THRD_DB_RELEASE(pDb) ){
6317 return 0; /* Another thread have released this instance */
6318 }
6319#endif
6320 /* Generate the random number */
6321 iNum = unqlitePagerRandomNum(pDb->sDB.pPager);
6322#if defined(UNQLITE_ENABLE_THREADS)
6323 /* Leave DB mutex */
6324 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6325#endif
6326 return iNum;
6327}
6328/*
6329 * ----------------------------------------------------------
6330 * File: bitvec.c
6331 * MD5: 7e3376710d8454ebcf8c77baacca880f
6332 * ----------------------------------------------------------
6333 */
6334/*
6335 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
6336 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
6337 * Version 1.1.6
6338 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6339 * please contact Symisc Systems via:
6340 * legal@symisc.net
6341 * licensing@symisc.net
6342 * contact@symisc.net
6343 * or visit:
6344 * http://unqlite.org/licensing.html
6345 */
6346 /* $SymiscID: bitvec.c v1.0 Win7 2013-02-27 15:16 stable <chm@symisc.net> $ */
6347#ifndef UNQLITE_AMALGAMATION
6348#include "unqliteInt.h"
6349#endif
6350
6351/** This file implements an object that represents a dynmaic
6352** bitmap.
6353**
6354** A bitmap is used to record which pages of a database file have been
6355** journalled during a transaction, or which pages have the "dont-write"
6356** property. Usually only a few pages are meet either condition.
6357** So the bitmap is usually sparse and has low cardinality.
6358*/
6359/*
6360 * Actually, this is not a bitmap but a simple hashtable where page
6361 * number (64-bit unsigned integers) are used as the lookup keys.
6362 */
6363typedef struct bitvec_rec bitvec_rec;
6364struct bitvec_rec
6365{
6366 pgno iPage; /* Page number */
6367 bitvec_rec *pNext,*pNextCol; /* Collison link */
6368};
6369struct Bitvec
6370{
6371 SyMemBackend *pAlloc; /* Memory allocator */
6372 sxu32 nRec; /* Total number of records */
6373 sxu32 nSize; /* Table size */
6374 bitvec_rec **apRec; /* Record table */
6375 bitvec_rec *pList; /* List of records */
6376};
6377/*
6378 * Allocate a new bitvec instance.
6379*/
6380UNQLITE_PRIVATE Bitvec * unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize)
6381{
6382 bitvec_rec **apNew;
6383 Bitvec *p;
6384
6385 p = (Bitvec *)SyMemBackendAlloc(pAlloc,sizeof(*p) );
6386 if( p == 0 ){
6387 SXUNUSED(iSize); /* cc warning */
6388 return 0;
6389 }
6390 /* Zero the structure */
6391 SyZero(p,sizeof(Bitvec));
6392 /* Allocate a new table */
6393 p->nSize = 64; /* Must be a power of two */
6394 apNew = (bitvec_rec **)SyMemBackendAlloc(pAlloc,p->nSize * sizeof(bitvec_rec *));
6395 if( apNew == 0 ){
6396 SyMemBackendFree(pAlloc,p);
6397 return 0;
6398 }
6399 /* Zero the new table */
6400 SyZero((void *)apNew,p->nSize * sizeof(bitvec_rec *));
6401 /* Fill-in */
6402 p->apRec = apNew;
6403 p->pAlloc = pAlloc;
6404 return p;
6405}
6406/*
6407 * Check if the given page number is already installed in the table.
6408 * Return true if installed. False otherwise.
6409 */
6410UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i)
6411{
6412 bitvec_rec *pRec;
6413 /* Point to the desired bucket */
6414 pRec = p->apRec[i & (p->nSize - 1)];
6415 for(;;){
6416 if( pRec == 0 ){ break; }
6417 if( pRec->iPage == i ){
6418 /* Page found */
6419 return 1;
6420 }
6421 /* Point to the next entry */
6422 pRec = pRec->pNextCol;
6423
6424 if( pRec == 0 ){ break; }
6425 if( pRec->iPage == i ){
6426 /* Page found */
6427 return 1;
6428 }
6429 /* Point to the next entry */
6430 pRec = pRec->pNextCol;
6431
6432
6433 if( pRec == 0 ){ break; }
6434 if( pRec->iPage == i ){
6435 /* Page found */
6436 return 1;
6437 }
6438 /* Point to the next entry */
6439 pRec = pRec->pNextCol;
6440
6441
6442 if( pRec == 0 ){ break; }
6443 if( pRec->iPage == i ){
6444 /* Page found */
6445 return 1;
6446 }
6447 /* Point to the next entry */
6448 pRec = pRec->pNextCol;
6449 }
6450 /* No such entry */
6451 return 0;
6452}
6453/*
6454 * Install a given page number in our bitmap (Actually, our hashtable).
6455 */
6456UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i)
6457{
6458 bitvec_rec *pRec;
6459 sxi32 iBuck;
6460 /* Allocate a new instance */
6461 pRec = (bitvec_rec *)SyMemBackendPoolAlloc(p->pAlloc,sizeof(bitvec_rec));
6462 if( pRec == 0 ){
6463 return UNQLITE_NOMEM;
6464 }
6465 /* Zero the structure */
6466 SyZero(pRec,sizeof(bitvec_rec));
6467 /* Fill-in */
6468 pRec->iPage = i;
6469 iBuck = i & (p->nSize - 1);
6470 pRec->pNextCol = p->apRec[iBuck];
6471 p->apRec[iBuck] = pRec;
6472 pRec->pNext = p->pList;
6473 p->pList = pRec;
6474 p->nRec++;
6475 if( p->nRec >= (p->nSize * 3) && p->nRec < 100000 ){
6476 /* Grow the hashtable */
6477 sxu32 nNewSize = p->nSize << 1;
6478 bitvec_rec *pEntry,**apNew;
6479 sxu32 n;
6480 apNew = (bitvec_rec **)SyMemBackendAlloc(p->pAlloc, nNewSize * sizeof(bitvec_rec *));
6481 if( apNew ){
6482 sxu32 iBucket;
6483 /* Zero the new table */
6484 SyZero((void *)apNew, nNewSize * sizeof(bitvec_rec *));
6485 /* Rehash all entries */
6486 n = 0;
6487 pEntry = p->pList;
6488 for(;;){
6489 /* Loop one */
6490 if( n >= p->nRec ){
6491 break;
6492 }
6493 pEntry->pNextCol = 0;
6494 /* Install in the new bucket */
6495 iBucket = pEntry->iPage & (nNewSize - 1);
6496 pEntry->pNextCol = apNew[iBucket];
6497 apNew[iBucket] = pEntry;
6498 /* Point to the next entry */
6499 pEntry = pEntry->pNext;
6500 n++;
6501 }
6502 /* Release the old table and reflect the change */
6503 SyMemBackendFree(p->pAlloc,(void *)p->apRec);
6504 p->apRec = apNew;
6505 p->nSize = nNewSize;
6506 }
6507 }
6508 return UNQLITE_OK;
6509}
6510/*
6511 * Destroy a bitvec instance. Reclaim all memory used.
6512 */
6513UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p)
6514{
6515 bitvec_rec *pNext,*pRec = p->pList;
6516 SyMemBackend *pAlloc = p->pAlloc;
6517
6518 for(;;){
6519 if( p->nRec < 1 ){
6520 break;
6521 }
6522 pNext = pRec->pNext;
6523 SyMemBackendPoolFree(pAlloc,(void *)pRec);
6524 pRec = pNext;
6525 p->nRec--;
6526
6527 if( p->nRec < 1 ){
6528 break;
6529 }
6530 pNext = pRec->pNext;
6531 SyMemBackendPoolFree(pAlloc,(void *)pRec);
6532 pRec = pNext;
6533 p->nRec--;
6534
6535
6536 if( p->nRec < 1 ){
6537 break;
6538 }
6539 pNext = pRec->pNext;
6540 SyMemBackendPoolFree(pAlloc,(void *)pRec);
6541 pRec = pNext;
6542 p->nRec--;
6543
6544
6545 if( p->nRec < 1 ){
6546 break;
6547 }
6548 pNext = pRec->pNext;
6549 SyMemBackendPoolFree(pAlloc,(void *)pRec);
6550 pRec = pNext;
6551 p->nRec--;
6552 }
6553 SyMemBackendFree(pAlloc,(void *)p->apRec);
6554 SyMemBackendFree(pAlloc,p);
6555}
6556/*
6557 * ----------------------------------------------------------
6558 * File: fastjson.c
6559 * MD5: 3693c0022edc7d37b65124d7aef68397
6560 * ----------------------------------------------------------
6561 */
6562/*
6563 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
6564 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
6565 * Version 1.1.6
6566 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6567 * please contact Symisc Systems via:
6568 * legal@symisc.net
6569 * licensing@symisc.net
6570 * contact@symisc.net
6571 * or visit:
6572 * http://unqlite.org/licensing.html
6573 */
6574 /* $SymiscID: fastjson.c v1.1 FreeBSD 2012-12-05 22:52 stable <chm@symisc.net> $ */
6575#ifndef UNQLITE_AMALGAMATION
6576#include "unqliteInt.h"
6577#endif
6578/* JSON binary encoding, decoding and stuff like that */
6579#ifndef UNQLITE_FAST_JSON_NEST_LIMIT
6580#if defined(__WINNT__) || defined(__UNIXES__)
6581#define UNQLITE_FAST_JSON_NEST_LIMIT 64 /* Nesting limit */
6582#else
6583#define UNQLITE_FAST_JSON_NEST_LIMIT 32 /* Nesting limit */
6584#endif
6585#endif /* UNQLITE_FAST_JSON_NEST_LIMIT */
6586/*
6587 * JSON to Binary using the FastJSON implementation (BigEndian).
6588 */
6589/*
6590 * FastJSON implemented binary token.
6591 */
6592#define FJSON_DOC_START 1 /* { */
6593#define FJSON_DOC_END 2 /* } */
6594#define FJSON_ARRAY_START 3 /* [ */
6595#define FJSON_ARRAY_END 4 /* ] */
6596#define FJSON_COLON 5 /* : */
6597#define FJSON_COMMA 6 /* , */
6598#define FJSON_ID 7 /* ID + 4 Bytes length */
6599#define FJSON_STRING 8 /* String + 4 bytes length */
6600#define FJSON_BYTE 9 /* Byte */
6601#define FJSON_INT64 10 /* Integer 64 + 8 bytes */
6602#define FJSON_REAL 18 /* Floating point value + 2 bytes */
6603#define FJSON_NULL 23 /* NULL */
6604#define FJSON_TRUE 24 /* TRUE */
6605#define FJSON_FALSE 25 /* FALSE */
6606/*
6607 * Encode a Jx9 value to binary JSON.
6608 */
6609UNQLITE_PRIVATE sxi32 FastJsonEncode(
6610 jx9_value *pValue, /* Value to encode */
6611 SyBlob *pOut, /* Store encoded value here */
6612 int iNest /* Nesting limit */
6613 )
6614{
6615 sxi32 iType = pValue ? pValue->iFlags : MEMOBJ_NULL;
6616 sxi32 rc = SXRET_OK;
6617 int c;
6618 if( iNest >= UNQLITE_FAST_JSON_NEST_LIMIT ){
6619 /* Nesting limit reached */
6620 return SXERR_LIMIT;
6621 }
6622 if( iType & (MEMOBJ_NULL|MEMOBJ_RES) ){
6623 /*
6624 * Resources are encoded as null also.
6625 */
6626 c = FJSON_NULL;
6627 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6628 }else if( iType & MEMOBJ_BOOL ){
6629 c = pValue->x.iVal ? FJSON_TRUE : FJSON_FALSE;
6630 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6631 }else if( iType & MEMOBJ_STRING ){
6632 unsigned char zBuf[sizeof(sxu32)]; /* String length */
6633 c = FJSON_STRING;
6634 SyBigEndianPack32(zBuf,SyBlobLength(&pValue->sBlob));
6635 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6636 if( rc == SXRET_OK ){
6637 rc = SyBlobAppend(pOut,(const void *)zBuf,sizeof(zBuf));
6638 if( rc == SXRET_OK ){
6639 rc = SyBlobAppend(pOut,SyBlobData(&pValue->sBlob),SyBlobLength(&pValue->sBlob));
6640 }
6641 }
6642 }else if( iType & MEMOBJ_INT ){
6643 unsigned char zBuf[8];
6644 /* 64bit big endian integer */
6645 c = FJSON_INT64;
6646 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6647 if( rc == SXRET_OK ){
6648 SyBigEndianPack64(zBuf,(sxu64)pValue->x.iVal);
6649 rc = SyBlobAppend(pOut,(const void *)zBuf,sizeof(zBuf));
6650 }
6651 }else if( iType & MEMOBJ_REAL ){
6652 /* Real number */
6653 c = FJSON_REAL;
6654 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6655 if( rc == SXRET_OK ){
6656 sxu32 iOfft = SyBlobLength(pOut);
6657 rc = SyBlobAppendBig16(pOut,0);
6658 if( rc == SXRET_OK ){
6659 unsigned char *zBlob;
6660 SyBlobFormat(pOut,"%.15g",pValue->x.rVal);
6661 zBlob = (unsigned char *)SyBlobDataAt(pOut,iOfft);
6662 SyBigEndianPack16(zBlob,(sxu16)(SyBlobLength(pOut) - ( 2 + iOfft)));
6663 }
6664 }
6665 }else if( iType & MEMOBJ_HASHMAP ){
6666 /* A JSON object or array */
6667 jx9_hashmap *pMap = (jx9_hashmap *)pValue->x.pOther;
6668 jx9_hashmap_node *pNode;
6669 jx9_value *pEntry;
6670 /* Reset the hashmap loop cursor */
6671 jx9HashmapResetLoopCursor(pMap);
6672 if( pMap->iFlags & HASHMAP_JSON_OBJECT ){
6673 jx9_value sKey;
6674 /* A JSON object */
6675 c = FJSON_DOC_START; /* { */
6676 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6677 if( rc == SXRET_OK ){
6678 jx9MemObjInit(pMap->pVm,&sKey);
6679 /* Encode object entries */
6680 while((pNode = jx9HashmapGetNextEntry(pMap)) != 0 ){
6681 /* Extract the key */
6682 jx9HashmapExtractNodeKey(pNode,&sKey);
6683 /* Encode it */
6684 rc = FastJsonEncode(&sKey,pOut,iNest+1);
6685 if( rc != SXRET_OK ){
6686 break;
6687 }
6688 c = FJSON_COLON;
6689 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6690 if( rc != SXRET_OK ){
6691 break;
6692 }
6693 /* Extract the value */
6694 pEntry = jx9HashmapGetNodeValue(pNode);
6695 /* Encode it */
6696 rc = FastJsonEncode(pEntry,pOut,iNest+1);
6697 if( rc != SXRET_OK ){
6698 break;
6699 }
6700 /* Delimit the entry */
6701 c = FJSON_COMMA;
6702 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6703 if( rc != SXRET_OK ){
6704 break;
6705 }
6706 }
6707 jx9MemObjRelease(&sKey);
6708 if( rc == SXRET_OK ){
6709 c = FJSON_DOC_END; /* } */
6710 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6711 }
6712 }
6713 }else{
6714 /* A JSON array */
6715 c = FJSON_ARRAY_START; /* [ */
6716 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6717 if( rc == SXRET_OK ){
6718 /* Encode array entries */
6719 while( (pNode = jx9HashmapGetNextEntry(pMap)) != 0 ){
6720 /* Extract the value */
6721 pEntry = jx9HashmapGetNodeValue(pNode);
6722 /* Encode it */
6723 rc = FastJsonEncode(pEntry,pOut,iNest+1);
6724 if( rc != SXRET_OK ){
6725 break;
6726 }
6727 /* Delimit the entry */
6728 c = FJSON_COMMA;
6729 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6730 if( rc != SXRET_OK ){
6731 break;
6732 }
6733 }
6734 if( rc == SXRET_OK ){
6735 c = FJSON_ARRAY_END; /* ] */
6736 rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6737 }
6738 }
6739 }
6740 }
6741 return rc;
6742}
6743/*
6744 * Decode a FastJSON binary blob.
6745 */
6746UNQLITE_PRIVATE sxi32 FastJsonDecode(
6747 const void *pIn, /* Binary JSON */
6748 sxu32 nByte, /* Chunk delimiter */
6749 jx9_value *pOut, /* Decoded value */
6750 const unsigned char **pzPtr,
6751 int iNest /* Nesting limit */
6752 )
6753{
6754 const unsigned char *zIn = (const unsigned char *)pIn;
6755 const unsigned char *zEnd = &zIn[nByte];
6756 sxi32 rc = SXRET_OK;
6757 int c;
6758 if( iNest >= UNQLITE_FAST_JSON_NEST_LIMIT ){
6759 /* Nesting limit reached */
6760 return SXERR_LIMIT;
6761 }
6762 c = zIn[0];
6763 /* Advance the stream cursor */
6764 zIn++;
6765 /* Process the binary token */
6766 switch(c){
6767 case FJSON_NULL:
6768 /* null */
6769 jx9_value_null(pOut);
6770 break;
6771 case FJSON_FALSE:
6772 /* Boolean FALSE */
6773 jx9_value_bool(pOut,0);
6774 break;
6775 case FJSON_TRUE:
6776 /* Boolean TRUE */
6777 jx9_value_bool(pOut,1);
6778 break;
6779 case FJSON_INT64: {
6780 /* 64Bit integer */
6781 sxu64 iVal;
6782 /* Sanity check */
6783 if( &zIn[8] >= zEnd ){
6784 /* Corrupt chunk */
6785 rc = SXERR_CORRUPT;
6786 break;
6787 }
6788 SyBigEndianUnpack64(zIn,&iVal);
6789 /* Advance the pointer */
6790 zIn += 8;
6791 jx9_value_int64(pOut,(jx9_int64)iVal);
6792 break;
6793 }
6794 case FJSON_REAL: {
6795 /* Real number */
6796 double iVal = 0; /* cc warning */
6797 sxu16 iLen;
6798 /* Sanity check */
6799 if( &zIn[2] >= zEnd ){
6800 /* Corrupt chunk */
6801 rc = SXERR_CORRUPT;
6802 break;
6803 }
6804 SyBigEndianUnpack16(zIn,&iLen);
6805 if( &zIn[iLen] >= zEnd ){
6806 /* Corrupt chunk */
6807 rc = SXERR_CORRUPT;
6808 break;
6809 }
6810 zIn += 2;
6811 SyStrToReal((const char *)zIn,(sxu32)iLen,&iVal,0);
6812 /* Advance the pointer */
6813 zIn += iLen;
6814 jx9_value_double(pOut,iVal);
6815 break;
6816 }
6817 case FJSON_STRING: {
6818 /* UTF-8/Binary chunk */
6819 sxu32 iLength;
6820 /* Sanity check */
6821 if( &zIn[4] >= zEnd ){
6822 /* Corrupt chunk */
6823 rc = SXERR_CORRUPT;
6824 break;
6825 }
6826 SyBigEndianUnpack32(zIn,&iLength);
6827 if( &zIn[iLength] >= zEnd ){
6828 /* Corrupt chunk */
6829 rc = SXERR_CORRUPT;
6830 break;
6831 }
6832 zIn += 4;
6833 /* Invalidate any prior representation */
6834 if( pOut->iFlags & MEMOBJ_STRING ){
6835 /* Reset the string cursor */
6836 SyBlobReset(&pOut->sBlob);
6837 }
6838 rc = jx9MemObjStringAppend(pOut,(const char *)zIn,iLength);
6839 /* Update pointer */
6840 zIn += iLength;
6841 break;
6842 }
6843 case FJSON_ARRAY_START: {
6844 /* Binary JSON array */
6845 jx9_hashmap *pMap;
6846 jx9_value sVal;
6847 /* Allocate a new hashmap */
6848 pMap = (jx9_hashmap *)jx9NewHashmap(pOut->pVm,0,0);
6849 if( pMap == 0 ){
6850 rc = SXERR_MEM;
6851 break;
6852 }
6853 jx9MemObjInit(pOut->pVm,&sVal);
6854 jx9MemObjRelease(pOut);
6855 MemObjSetType(pOut,MEMOBJ_HASHMAP);
6856 pOut->x.pOther = pMap;
6857 rc = SXRET_OK;
6858 for(;;){
6859 /* Jump leading binary commas */
6860 while (zIn < zEnd && zIn[0] == FJSON_COMMA ){
6861 zIn++;
6862 }
6863 if( zIn >= zEnd || zIn[0] == FJSON_ARRAY_END ){
6864 if( zIn < zEnd ){
6865 zIn++; /* Jump the trailing binary ] */
6866 }
6867 break;
6868 }
6869 /* Decode the value */
6870 rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sVal,&zIn,iNest+1);
6871 if( rc != SXRET_OK ){
6872 break;
6873 }
6874 /* Insert the decoded value */
6875 rc = jx9HashmapInsert(pMap,0,&sVal);
6876 if( rc != UNQLITE_OK ){
6877 break;
6878 }
6879 }
6880 if( rc != SXRET_OK ){
6881 jx9MemObjRelease(pOut);
6882 }
6883 jx9MemObjRelease(&sVal);
6884 break;
6885 }
6886 case FJSON_DOC_START: {
6887 /* Binary JSON object */
6888 jx9_value sVal,sKey;
6889 jx9_hashmap *pMap;
6890 /* Allocate a new hashmap */
6891 pMap = (jx9_hashmap *)jx9NewHashmap(pOut->pVm,0,0);
6892 if( pMap == 0 ){
6893 rc = SXERR_MEM;
6894 break;
6895 }
6896 jx9MemObjInit(pOut->pVm,&sVal);
6897 jx9MemObjInit(pOut->pVm,&sKey);
6898 jx9MemObjRelease(pOut);
6899 MemObjSetType(pOut,MEMOBJ_HASHMAP);
6900 pOut->x.pOther = pMap;
6901 rc = SXRET_OK;
6902 for(;;){
6903 /* Jump leading binary commas */
6904 while (zIn < zEnd && zIn[0] == FJSON_COMMA ){
6905 zIn++;
6906 }
6907 if( zIn >= zEnd || zIn[0] == FJSON_DOC_END ){
6908 if( zIn < zEnd ){
6909 zIn++; /* Jump the trailing binary } */
6910 }
6911 break;
6912 }
6913 /* Extract the key */
6914 rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sKey,&zIn,iNest+1);
6915 if( rc != UNQLITE_OK ){
6916 break;
6917 }
6918 if( zIn >= zEnd || zIn[0] != FJSON_COLON ){
6919 rc = UNQLITE_CORRUPT;
6920 break;
6921 }
6922 zIn++; /* Jump the binary colon ':' */
6923 if( zIn >= zEnd ){
6924 rc = UNQLITE_CORRUPT;
6925 break;
6926 }
6927 /* Decode the value */
6928 rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sVal,&zIn,iNest+1);
6929 if( rc != SXRET_OK ){
6930 break;
6931 }
6932 /* Insert the key and its associated value */
6933 rc = jx9HashmapInsert(pMap,&sKey,&sVal);
6934 if( rc != UNQLITE_OK ){
6935 break;
6936 }
6937 }
6938 if( rc != SXRET_OK ){
6939 jx9MemObjRelease(pOut);
6940 }
6941 jx9MemObjRelease(&sVal);
6942 jx9MemObjRelease(&sKey);
6943 break;
6944 }
6945 default:
6946 /* Corrupt data */
6947 rc = SXERR_CORRUPT;
6948 break;
6949 }
6950 if( pzPtr ){
6951 *pzPtr = zIn;
6952 }
6953 return rc;
6954}
6955/*
6956 * ----------------------------------------------------------
6957 * File: jx9_api.c
6958 * MD5: 73cba599c009cee0ff878666d0543438
6959 * ----------------------------------------------------------
6960 */
6961/*
6962 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
6963 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
6964 * Version 1.7.2
6965 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6966 * please contact Symisc Systems via:
6967 * legal@symisc.net
6968 * licensing@symisc.net
6969 * contact@symisc.net
6970 * or visit:
6971 * http://jx9.symisc.net/
6972 */
6973 /* $SymiscID: api.c v1.7 FreeBSD 2012-12-18 06:54 stable <chm@symisc.net> $ */
6974#ifndef JX9_AMALGAMATION
6975#include "jx9Int.h"
6976#endif
6977/* This file implement the public interfaces presented to host-applications.
6978 * Routines in other files are for internal use by JX9 and should not be
6979 * accessed by users of the library.
6980 */
6981#define JX9_ENGINE_MAGIC 0xF874BCD7
6982#define JX9_ENGINE_MISUSE(ENGINE) (ENGINE == 0 || ENGINE->nMagic != JX9_ENGINE_MAGIC)
6983#define JX9_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE)
6984/* If another thread have released a working instance, the following macros
6985 * evaluates to true. These macros are only used when the library
6986 * is built with threading support enabled which is not the case in
6987 * the default built.
6988 */
6989#define JX9_THRD_ENGINE_RELEASE(ENGINE) (ENGINE->nMagic != JX9_ENGINE_MAGIC)
6990#define JX9_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE)
6991/* IMPLEMENTATION: jx9@embedded@symisc 311-12-32 */
6992/*
6993 * All global variables are collected in the structure named "sJx9MPGlobal".
6994 * That way it is clear in the code when we are using static variable because
6995 * its name start with sJx9MPGlobal.
6996 */
6997static struct Jx9Global_Data
6998{
6999 SyMemBackend sAllocator; /* Global low level memory allocator */
7000#if defined(JX9_ENABLE_THREADS)
7001 const SyMutexMethods *pMutexMethods; /* Mutex methods */
7002 SyMutex *pMutex; /* Global mutex */
7003 sxu32 nThreadingLevel; /* Threading level: 0 == Single threaded/1 == Multi-Threaded
7004 * The threading level can be set using the [jx9_lib_config()]
7005 * interface with a configuration verb set to
7006 * JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE or
7007 * JX9_LIB_CONFIG_THREAD_LEVEL_MULTI
7008 */
7009#endif
7010 const jx9_vfs *pVfs; /* Underlying virtual file system */
7011 sxi32 nEngine; /* Total number of active engines */
7012 jx9 *pEngines; /* List of active engine */
7013 sxu32 nMagic; /* Sanity check against library misuse */
7014}sJx9MPGlobal = {
7015 {0, 0, 0, 0, 0, 0, 0, 0, {0}},
7016#if defined(JX9_ENABLE_THREADS)
7017 0,
7018 0,
7019 0,
7020#endif
7021 0,
7022 0,
7023 0,
7024 0
7025};
7026#define JX9_LIB_MAGIC 0xEA1495BA
7027#define JX9_LIB_MISUSE (sJx9MPGlobal.nMagic != JX9_LIB_MAGIC)
7028/*
7029 * Supported threading level.
7030 * These options have meaning only when the library is compiled with multi-threading
7031 * support.That is, the JX9_ENABLE_THREADS compile time directive must be defined
7032 * when JX9 is built.
7033 * JX9_THREAD_LEVEL_SINGLE:
7034 * In this mode, mutexing is disabled and the library can only be used by a single thread.
7035 * JX9_THREAD_LEVEL_MULTI
7036 * In this mode, all mutexes including the recursive mutexes on [jx9] objects
7037 * are enabled so that the application is free to share the same engine
7038 * between different threads at the same time.
7039 */
7040#define JX9_THREAD_LEVEL_SINGLE 1
7041#define JX9_THREAD_LEVEL_MULTI 2
7042/*
7043 * Configure a running JX9 engine instance.
7044 * return JX9_OK on success.Any other return
7045 * value indicates failure.
7046 * Refer to [jx9_config()].
7047 */
7048JX9_PRIVATE sxi32 jx9EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap)
7049{
7050 jx9_conf *pConf = &pEngine->xConf;
7051 int rc = JX9_OK;
7052 /* Perform the requested operation */
7053 switch(nOp){
7054 case JX9_CONFIG_ERR_LOG:{
7055 /* Extract compile-time error log if any */
7056 const char **pzPtr = va_arg(ap, const char **);
7057 int *pLen = va_arg(ap, int *);
7058 if( pzPtr == 0 ){
7059 rc = JX9_CORRUPT;
7060 break;
7061 }
7062 /* NULL terminate the error-log buffer */
7063 SyBlobNullAppend(&pConf->sErrConsumer);
7064 /* Point to the error-log buffer */
7065 *pzPtr = (const char *)SyBlobData(&pConf->sErrConsumer);
7066 if( pLen ){
7067 if( SyBlobLength(&pConf->sErrConsumer) > 1 /* NULL '\0' terminator */ ){
7068 *pLen = (int)SyBlobLength(&pConf->sErrConsumer);
7069 }else{
7070 *pLen = 0;
7071 }
7072 }
7073 break;
7074 }
7075 case JX9_CONFIG_ERR_ABORT:
7076 /* Reserved for future use */
7077 break;
7078 default:
7079 /* Unknown configuration verb */
7080 rc = JX9_CORRUPT;
7081 break;
7082 } /* Switch() */
7083 return rc;
7084}
7085/*
7086 * Configure the JX9 library.
7087 * Return JX9_OK on success. Any other return value indicates failure.
7088 * Refer to [jx9_lib_config()].
7089 */
7090static sxi32 Jx9CoreConfigure(sxi32 nOp, va_list ap)
7091{
7092 int rc = JX9_OK;
7093 switch(nOp){
7094 case JX9_LIB_CONFIG_VFS:{
7095 /* Install a virtual file system */
7096 const jx9_vfs *pVfs = va_arg(ap, const jx9_vfs *);
7097 sJx9MPGlobal.pVfs = pVfs;
7098 break;
7099 }
7100 case JX9_LIB_CONFIG_USER_MALLOC: {
7101 /* Use an alternative low-level memory allocation routines */
7102 const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *);
7103 /* Save the memory failure callback (if available) */
7104 ProcMemError xMemErr = sJx9MPGlobal.sAllocator.xMemError;
7105 void *pMemErr = sJx9MPGlobal.sAllocator.pUserData;
7106 if( pMethods == 0 ){
7107 /* Use the built-in memory allocation subsystem */
7108 rc = SyMemBackendInit(&sJx9MPGlobal.sAllocator, xMemErr, pMemErr);
7109 }else{
7110 rc = SyMemBackendInitFromOthers(&sJx9MPGlobal.sAllocator, pMethods, xMemErr, pMemErr);
7111 }
7112 break;
7113 }
7114 case JX9_LIB_CONFIG_MEM_ERR_CALLBACK: {
7115 /* Memory failure callback */
7116 ProcMemError xMemErr = va_arg(ap, ProcMemError);
7117 void *pUserData = va_arg(ap, void *);
7118 sJx9MPGlobal.sAllocator.xMemError = xMemErr;
7119 sJx9MPGlobal.sAllocator.pUserData = pUserData;
7120 break;
7121 }
7122 case JX9_LIB_CONFIG_USER_MUTEX: {
7123#if defined(JX9_ENABLE_THREADS)
7124 /* Use an alternative low-level mutex subsystem */
7125 const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *);
7126#if defined (UNTRUST)
7127 if( pMethods == 0 ){
7128 rc = JX9_CORRUPT;
7129 }
7130#endif
7131 /* Sanity check */
7132 if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){
7133 /* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */
7134 rc = JX9_CORRUPT;
7135 break;
7136 }
7137 if( sJx9MPGlobal.pMutexMethods ){
7138 /* Overwrite the previous mutex subsystem */
7139 SyMutexRelease(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex);
7140 if( sJx9MPGlobal.pMutexMethods->xGlobalRelease ){
7141 sJx9MPGlobal.pMutexMethods->xGlobalRelease();
7142 }
7143 sJx9MPGlobal.pMutex = 0;
7144 }
7145 /* Initialize and install the new mutex subsystem */
7146 if( pMethods->xGlobalInit ){
7147 rc = pMethods->xGlobalInit();
7148 if ( rc != JX9_OK ){
7149 break;
7150 }
7151 }
7152 /* Create the global mutex */
7153 sJx9MPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
7154 if( sJx9MPGlobal.pMutex == 0 ){
7155 /*
7156 * If the supplied mutex subsystem is so sick that we are unable to
7157 * create a single mutex, there is no much we can do here.
7158 */
7159 if( pMethods->xGlobalRelease ){
7160 pMethods->xGlobalRelease();
7161 }
7162 rc = JX9_CORRUPT;
7163 break;
7164 }
7165 sJx9MPGlobal.pMutexMethods = pMethods;
7166 if( sJx9MPGlobal.nThreadingLevel == 0 ){
7167 /* Set a default threading level */
7168 sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI;
7169 }
7170#endif
7171 break;
7172 }
7173 case JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE:
7174#if defined(JX9_ENABLE_THREADS)
7175 /* Single thread mode(Only one thread is allowed to play with the library) */
7176 sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_SINGLE;
7177#endif
7178 break;
7179 case JX9_LIB_CONFIG_THREAD_LEVEL_MULTI:
7180#if defined(JX9_ENABLE_THREADS)
7181 /* Multi-threading mode (library is thread safe and JX9 engines and virtual machines
7182 * may be shared between multiple threads).
7183 */
7184 sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI;
7185#endif
7186 break;
7187 default:
7188 /* Unknown configuration option */
7189 rc = JX9_CORRUPT;
7190 break;
7191 }
7192 return rc;
7193}
7194/*
7195 * [CAPIREF: jx9_lib_config()]
7196 * Please refer to the official documentation for function purpose and expected parameters.
7197 */
7198JX9_PRIVATE int jx9_lib_config(int nConfigOp, ...)
7199{
7200 va_list ap;
7201 int rc;
7202 if( sJx9MPGlobal.nMagic == JX9_LIB_MAGIC ){
7203 /* Library is already initialized, this operation is forbidden */
7204 return JX9_LOOKED;
7205 }
7206 va_start(ap, nConfigOp);
7207 rc = Jx9CoreConfigure(nConfigOp, ap);
7208 va_end(ap);
7209 return rc;
7210}
7211/*
7212 * Global library initialization
7213 * Refer to [jx9_lib_init()]
7214 * This routine must be called to initialize the memory allocation subsystem, the mutex
7215 * subsystem prior to doing any serious work with the library.The first thread to call
7216 * this routine does the initialization process and set the magic number so no body later
7217 * can re-initialize the library.If subsequent threads call this routine before the first
7218 * thread have finished the initialization process, then the subsequent threads must block
7219 * until the initialization process is done.
7220 */
7221static sxi32 Jx9CoreInitialize(void)
7222{
7223 const jx9_vfs *pVfs; /* Built-in vfs */
7224#if defined(JX9_ENABLE_THREADS)
7225 const SyMutexMethods *pMutexMethods = 0;
7226 SyMutex *pMaster = 0;
7227#endif
7228 int rc;
7229 /*
7230 * If the library is already initialized, then a call to this routine
7231 * is a no-op.
7232 */
7233 if( sJx9MPGlobal.nMagic == JX9_LIB_MAGIC ){
7234 return JX9_OK; /* Already initialized */
7235 }
7236 /* Point to the built-in vfs */
7237 pVfs = jx9ExportBuiltinVfs();
7238 /* Install it */
7239 jx9_lib_config(JX9_LIB_CONFIG_VFS, pVfs);
7240#if defined(JX9_ENABLE_THREADS)
7241 if( sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_SINGLE ){
7242 pMutexMethods = sJx9MPGlobal.pMutexMethods;
7243 if( pMutexMethods == 0 ){
7244 /* Use the built-in mutex subsystem */
7245 pMutexMethods = SyMutexExportMethods();
7246 if( pMutexMethods == 0 ){
7247 return JX9_CORRUPT; /* Can't happen */
7248 }
7249 /* Install the mutex subsystem */
7250 rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MUTEX, pMutexMethods);
7251 if( rc != JX9_OK ){
7252 return rc;
7253 }
7254 }
7255 /* Obtain a static mutex so we can initialize the library without calling malloc() */
7256 pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1);
7257 if( pMaster == 0 ){
7258 return JX9_CORRUPT; /* Can't happen */
7259 }
7260 }
7261 /* Lock the master mutex */
7262 rc = JX9_OK;
7263 SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7264 if( sJx9MPGlobal.nMagic != JX9_LIB_MAGIC ){
7265#endif
7266 if( sJx9MPGlobal.sAllocator.pMethods == 0 ){
7267 /* Install a memory subsystem */
7268 rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */
7269 if( rc != JX9_OK ){
7270 /* If we are unable to initialize the memory backend, there is no much we can do here.*/
7271 goto End;
7272 }
7273 }
7274#if defined(JX9_ENABLE_THREADS)
7275 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
7276 /* Protect the memory allocation subsystem */
7277 rc = SyMemBackendMakeThreadSafe(&sJx9MPGlobal.sAllocator, sJx9MPGlobal.pMutexMethods);
7278 if( rc != JX9_OK ){
7279 goto End;
7280 }
7281 }
7282#endif
7283 /* Our library is initialized, set the magic number */
7284 sJx9MPGlobal.nMagic = JX9_LIB_MAGIC;
7285 rc = JX9_OK;
7286#if defined(JX9_ENABLE_THREADS)
7287 } /* sJx9MPGlobal.nMagic != JX9_LIB_MAGIC */
7288#endif
7289End:
7290#if defined(JX9_ENABLE_THREADS)
7291 /* Unlock the master mutex */
7292 SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7293#endif
7294 return rc;
7295}
7296/*
7297 * Release an active JX9 engine and it's associated active virtual machines.
7298 */
7299static sxi32 EngineRelease(jx9 *pEngine)
7300{
7301 jx9_vm *pVm, *pNext;
7302 /* Release all active VM */
7303 pVm = pEngine->pVms;
7304 for(;;){
7305 if( pEngine->iVm < 1 ){
7306 break;
7307 }
7308 pNext = pVm->pNext;
7309 jx9VmRelease(pVm);
7310 pVm = pNext;
7311 pEngine->iVm--;
7312 }
7313 /* Set a dummy magic number */
7314 pEngine->nMagic = 0x7635;
7315 /* Release the private memory subsystem */
7316 SyMemBackendRelease(&pEngine->sAllocator);
7317 return JX9_OK;
7318}
7319/*
7320 * Release all resources consumed by the library.
7321 * If JX9 is already shut when this routine is invoked then this
7322 * routine is a harmless no-op.
7323 * Note: This call is not thread safe. Refer to [jx9_lib_shutdown()].
7324 */
7325static void JX9CoreShutdown(void)
7326{
7327 jx9 *pEngine, *pNext;
7328 /* Release all active engines first */
7329 pEngine = sJx9MPGlobal.pEngines;
7330 for(;;){
7331 if( sJx9MPGlobal.nEngine < 1 ){
7332 break;
7333 }
7334 pNext = pEngine->pNext;
7335 EngineRelease(pEngine);
7336 pEngine = pNext;
7337 sJx9MPGlobal.nEngine--;
7338 }
7339#if defined(JX9_ENABLE_THREADS)
7340 /* Release the mutex subsystem */
7341 if( sJx9MPGlobal.pMutexMethods ){
7342 if( sJx9MPGlobal.pMutex ){
7343 SyMutexRelease(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex);
7344 sJx9MPGlobal.pMutex = 0;
7345 }
7346 if( sJx9MPGlobal.pMutexMethods->xGlobalRelease ){
7347 sJx9MPGlobal.pMutexMethods->xGlobalRelease();
7348 }
7349 sJx9MPGlobal.pMutexMethods = 0;
7350 }
7351 sJx9MPGlobal.nThreadingLevel = 0;
7352#endif
7353 if( sJx9MPGlobal.sAllocator.pMethods ){
7354 /* Release the memory backend */
7355 SyMemBackendRelease(&sJx9MPGlobal.sAllocator);
7356 }
7357 sJx9MPGlobal.nMagic = 0x1928;
7358}
7359/*
7360 * [CAPIREF: jx9_lib_shutdown()]
7361 * Please refer to the official documentation for function purpose and expected parameters.
7362 */
7363JX9_PRIVATE int jx9_lib_shutdown(void)
7364{
7365 if( sJx9MPGlobal.nMagic != JX9_LIB_MAGIC ){
7366 /* Already shut */
7367 return JX9_OK;
7368 }
7369 JX9CoreShutdown();
7370 return JX9_OK;
7371}
7372/*
7373 * [CAPIREF: jx9_lib_signature()]
7374 * Please refer to the official documentation for function purpose and expected parameters.
7375 */
7376JX9_PRIVATE const char * jx9_lib_signature(void)
7377{
7378 return JX9_SIG;
7379}
7380/*
7381 * [CAPIREF: jx9_init()]
7382 * Please refer to the official documentation for function purpose and expected parameters.
7383 */
7384JX9_PRIVATE int jx9_init(jx9 **ppEngine)
7385{
7386 jx9 *pEngine;
7387 int rc;
7388#if defined(UNTRUST)
7389 if( ppEngine == 0 ){
7390 return JX9_CORRUPT;
7391 }
7392#endif
7393 *ppEngine = 0;
7394 /* One-time automatic library initialization */
7395 rc = Jx9CoreInitialize();
7396 if( rc != JX9_OK ){
7397 return rc;
7398 }
7399 /* Allocate a new engine */
7400 pEngine = (jx9 *)SyMemBackendPoolAlloc(&sJx9MPGlobal.sAllocator, sizeof(jx9));
7401 if( pEngine == 0 ){
7402 return JX9_NOMEM;
7403 }
7404 /* Zero the structure */
7405 SyZero(pEngine, sizeof(jx9));
7406 /* Initialize engine fields */
7407 pEngine->nMagic = JX9_ENGINE_MAGIC;
7408 rc = SyMemBackendInitFromParent(&pEngine->sAllocator, &sJx9MPGlobal.sAllocator);
7409 if( rc != JX9_OK ){
7410 goto Release;
7411 }
7412#if defined(JX9_ENABLE_THREADS)
7413 SyMemBackendDisbaleMutexing(&pEngine->sAllocator);
7414#endif
7415 /* Default configuration */
7416 SyBlobInit(&pEngine->xConf.sErrConsumer, &pEngine->sAllocator);
7417 /* Install a default compile-time error consumer routine */
7418 pEngine->xConf.xErr = jx9VmBlobConsumer;
7419 pEngine->xConf.pErrData = &pEngine->xConf.sErrConsumer;
7420 /* Built-in vfs */
7421 pEngine->pVfs = sJx9MPGlobal.pVfs;
7422#if defined(JX9_ENABLE_THREADS)
7423 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
7424 /* Associate a recursive mutex with this instance */
7425 pEngine->pMutex = SyMutexNew(sJx9MPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
7426 if( pEngine->pMutex == 0 ){
7427 rc = JX9_NOMEM;
7428 goto Release;
7429 }
7430 }
7431#endif
7432 /* Link to the list of active engines */
7433#if defined(JX9_ENABLE_THREADS)
7434 /* Enter the global mutex */
7435 SyMutexEnter(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7436#endif
7437 MACRO_LD_PUSH(sJx9MPGlobal.pEngines, pEngine);
7438 sJx9MPGlobal.nEngine++;
7439#if defined(JX9_ENABLE_THREADS)
7440 /* Leave the global mutex */
7441 SyMutexLeave(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7442#endif
7443 /* Write a pointer to the new instance */
7444 *ppEngine = pEngine;
7445 return JX9_OK;
7446Release:
7447 SyMemBackendRelease(&pEngine->sAllocator);
7448 SyMemBackendPoolFree(&sJx9MPGlobal.sAllocator,pEngine);
7449 return rc;
7450}
7451/*
7452 * [CAPIREF: jx9_release()]
7453 * Please refer to the official documentation for function purpose and expected parameters.
7454 */
7455JX9_PRIVATE int jx9_release(jx9 *pEngine)
7456{
7457 int rc;
7458 if( JX9_ENGINE_MISUSE(pEngine) ){
7459 return JX9_CORRUPT;
7460 }
7461#if defined(JX9_ENABLE_THREADS)
7462 /* Acquire engine mutex */
7463 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7464 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7465 JX9_THRD_ENGINE_RELEASE(pEngine) ){
7466 return JX9_ABORT; /* Another thread have released this instance */
7467 }
7468#endif
7469 /* Release the engine */
7470 rc = EngineRelease(&(*pEngine));
7471#if defined(JX9_ENABLE_THREADS)
7472 /* Leave engine mutex */
7473 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7474 /* Release engine mutex */
7475 SyMutexRelease(sJx9MPGlobal.pMutexMethods, pEngine->pMutex) /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7476#endif
7477#if defined(JX9_ENABLE_THREADS)
7478 /* Enter the global mutex */
7479 SyMutexEnter(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7480#endif
7481 /* Unlink from the list of active engines */
7482 MACRO_LD_REMOVE(sJx9MPGlobal.pEngines, pEngine);
7483 sJx9MPGlobal.nEngine--;
7484#if defined(JX9_ENABLE_THREADS)
7485 /* Leave the global mutex */
7486 SyMutexLeave(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7487#endif
7488 /* Release the memory chunk allocated to this engine */
7489 SyMemBackendPoolFree(&sJx9MPGlobal.sAllocator, pEngine);
7490 return rc;
7491}
7492/*
7493 * Compile a raw JX9 script.
7494 * To execute a JX9 code, it must first be compiled into a bytecode program using this routine.
7495 * If something goes wrong [i.e: compile-time error], your error log [i.e: error consumer callback]
7496 * should display the appropriate error message and this function set ppVm to null and return
7497 * an error code that is different from JX9_OK. Otherwise when the script is successfully compiled
7498 * ppVm should hold the JX9 bytecode and it's safe to call [jx9_vm_exec(), jx9_vm_reset(), etc.].
7499 * This API does not actually evaluate the JX9 code. It merely compile and prepares the JX9 script
7500 * for evaluation.
7501 */
7502static sxi32 ProcessScript(
7503 jx9 *pEngine, /* Running JX9 engine */
7504 jx9_vm **ppVm, /* OUT: A pointer to the virtual machine */
7505 SyString *pScript, /* Raw JX9 script to compile */
7506 sxi32 iFlags, /* Compile-time flags */
7507 const char *zFilePath /* File path if script come from a file. NULL otherwise */
7508 )
7509{
7510 jx9_vm *pVm;
7511 int rc;
7512 /* Allocate a new virtual machine */
7513 pVm = (jx9_vm *)SyMemBackendPoolAlloc(&pEngine->sAllocator, sizeof(jx9_vm));
7514 if( pVm == 0 ){
7515 /* If the supplied memory subsystem is so sick that we are unable to allocate
7516 * a tiny chunk of memory, there is no much we can do here. */
7517 if( ppVm ){
7518 *ppVm = 0;
7519 }
7520 return JX9_NOMEM;
7521 }
7522 if( iFlags < 0 ){
7523 /* Default compile-time flags */
7524 iFlags = 0;
7525 }
7526 /* Initialize the Virtual Machine */
7527 rc = jx9VmInit(pVm, &(*pEngine));
7528 if( rc != JX9_OK ){
7529 SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
7530 if( ppVm ){
7531 *ppVm = 0;
7532 }
7533 return JX9_VM_ERR;
7534 }
7535 if( zFilePath ){
7536 /* Push processed file path */
7537 jx9VmPushFilePath(pVm, zFilePath, -1, TRUE, 0);
7538 }
7539 /* Reset the error message consumer */
7540 SyBlobReset(&pEngine->xConf.sErrConsumer);
7541 /* Compile the script */
7542 jx9CompileScript(pVm, &(*pScript), iFlags);
7543 if( pVm->sCodeGen.nErr > 0 || pVm == 0){
7544 sxu32 nErr = pVm->sCodeGen.nErr;
7545 /* Compilation error or null ppVm pointer, release this VM */
7546 SyMemBackendRelease(&pVm->sAllocator);
7547 SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
7548 if( ppVm ){
7549 *ppVm = 0;
7550 }
7551 return nErr > 0 ? JX9_COMPILE_ERR : JX9_OK;
7552 }
7553 /* Prepare the virtual machine for bytecode execution */
7554 rc = jx9VmMakeReady(pVm);
7555 if( rc != JX9_OK ){
7556 goto Release;
7557 }
7558 /* Install local import path which is the current directory */
7559 jx9_vm_config(pVm, JX9_VM_CONFIG_IMPORT_PATH, "./");
7560#if defined(JX9_ENABLE_THREADS)
7561 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
7562 /* Associate a recursive mutex with this instance */
7563 pVm->pMutex = SyMutexNew(sJx9MPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
7564 if( pVm->pMutex == 0 ){
7565 goto Release;
7566 }
7567 }
7568#endif
7569 /* Script successfully compiled, link to the list of active virtual machines */
7570 MACRO_LD_PUSH(pEngine->pVms, pVm);
7571 pEngine->iVm++;
7572 /* Point to the freshly created VM */
7573 *ppVm = pVm;
7574 /* Ready to execute JX9 bytecode */
7575 return JX9_OK;
7576Release:
7577 SyMemBackendRelease(&pVm->sAllocator);
7578 SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
7579 *ppVm = 0;
7580 return JX9_VM_ERR;
7581}
7582/*
7583 * [CAPIREF: jx9_compile()]
7584 * Please refer to the official documentation for function purpose and expected parameters.
7585 */
7586JX9_PRIVATE int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm)
7587{
7588 SyString sScript;
7589 int rc;
7590 if( JX9_ENGINE_MISUSE(pEngine) ){
7591 return JX9_CORRUPT;
7592 }
7593 if( zSource == 0 ){
7594 /* Empty Jx9 statement ';' */
7595 zSource = ";";
7596 nLen = (int)sizeof(char);
7597 }
7598 if( nLen < 0 ){
7599 /* Compute input length automatically */
7600 nLen = (int)SyStrlen(zSource);
7601 }
7602 SyStringInitFromBuf(&sScript, zSource, nLen);
7603#if defined(JX9_ENABLE_THREADS)
7604 /* Acquire engine mutex */
7605 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7606 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7607 JX9_THRD_ENGINE_RELEASE(pEngine) ){
7608 return JX9_ABORT; /* Another thread have released this instance */
7609 }
7610#endif
7611 /* Compile the script */
7612 rc = ProcessScript(&(*pEngine),ppOutVm,&sScript,0,0);
7613#if defined(JX9_ENABLE_THREADS)
7614 /* Leave engine mutex */
7615 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7616#endif
7617 /* Compilation result */
7618 return rc;
7619}
7620/*
7621 * [CAPIREF: jx9_compile_file()]
7622 * Please refer to the official documentation for function purpose and expected parameters.
7623 */
7624JX9_PRIVATE int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm)
7625{
7626 const jx9_vfs *pVfs;
7627 int rc;
7628 if( ppOutVm ){
7629 *ppOutVm = 0;
7630 }
7631 rc = JX9_OK; /* cc warning */
7632 if( JX9_ENGINE_MISUSE(pEngine) || SX_EMPTY_STR(zFilePath) ){
7633 return JX9_CORRUPT;
7634 }
7635#if defined(JX9_ENABLE_THREADS)
7636 /* Acquire engine mutex */
7637 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7638 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7639 JX9_THRD_ENGINE_RELEASE(pEngine) ){
7640 return JX9_ABORT; /* Another thread have released this instance */
7641 }
7642#endif
7643 /*
7644 * Check if the underlying vfs implement the memory map
7645 * [i.e: mmap() under UNIX/MapViewOfFile() under windows] function.
7646 */
7647 pVfs = pEngine->pVfs;
7648 if( pVfs == 0 || pVfs->xMmap == 0 ){
7649 /* Memory map routine not implemented */
7650 rc = JX9_IO_ERR;
7651 }else{
7652 void *pMapView = 0; /* cc warning */
7653 jx9_int64 nSize = 0; /* cc warning */
7654 SyString sScript;
7655 /* Try to get a memory view of the whole file */
7656 rc = pVfs->xMmap(zFilePath, &pMapView, &nSize);
7657 if( rc != JX9_OK ){
7658 /* Assume an IO error */
7659 rc = JX9_IO_ERR;
7660 }else{
7661 /* Compile the file */
7662 SyStringInitFromBuf(&sScript, pMapView, nSize);
7663 rc = ProcessScript(&(*pEngine), ppOutVm, &sScript,0,zFilePath);
7664 /* Release the memory view of the whole file */
7665 if( pVfs->xUnmap ){
7666 pVfs->xUnmap(pMapView, nSize);
7667 }
7668 }
7669 }
7670#if defined(JX9_ENABLE_THREADS)
7671 /* Leave engine mutex */
7672 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7673#endif
7674 /* Compilation result */
7675 return rc;
7676}
7677/*
7678 * [CAPIREF: jx9_vm_config()]
7679 * Please refer to the official documentation for function purpose and expected parameters.
7680 */
7681JX9_PRIVATE int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...)
7682{
7683 va_list ap;
7684 int rc;
7685 /* Ticket 1433-002: NULL VM is harmless operation */
7686 if ( JX9_VM_MISUSE(pVm) ){
7687 return JX9_CORRUPT;
7688 }
7689#if defined(JX9_ENABLE_THREADS)
7690 /* Acquire VM mutex */
7691 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7692 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7693 JX9_THRD_VM_RELEASE(pVm) ){
7694 return JX9_ABORT; /* Another thread have released this instance */
7695 }
7696#endif
7697 /* Confiugure the virtual machine */
7698 va_start(ap, iConfigOp);
7699 rc = jx9VmConfigure(&(*pVm), iConfigOp, ap);
7700 va_end(ap);
7701#if defined(JX9_ENABLE_THREADS)
7702 /* Leave VM mutex */
7703 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7704#endif
7705 return rc;
7706}
7707/*
7708 * [CAPIREF: jx9_vm_release()]
7709 * Please refer to the official documentation for function purpose and expected parameters.
7710 */
7711JX9_PRIVATE int jx9_vm_release(jx9_vm *pVm)
7712{
7713 jx9 *pEngine;
7714 int rc;
7715 /* Ticket 1433-002: NULL VM is harmless operation */
7716 if ( JX9_VM_MISUSE(pVm) ){
7717 return JX9_CORRUPT;
7718 }
7719#if defined(JX9_ENABLE_THREADS)
7720 /* Acquire VM mutex */
7721 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7722 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7723 JX9_THRD_VM_RELEASE(pVm) ){
7724 return JX9_ABORT; /* Another thread have released this instance */
7725 }
7726#endif
7727 pEngine = pVm->pEngine;
7728 rc = jx9VmRelease(&(*pVm));
7729#if defined(JX9_ENABLE_THREADS)
7730 /* Leave VM mutex */
7731 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7732 /* Release VM mutex */
7733 SyMutexRelease(sJx9MPGlobal.pMutexMethods, pVm->pMutex) /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7734#endif
7735 if( rc == JX9_OK ){
7736 /* Unlink from the list of active VM */
7737#if defined(JX9_ENABLE_THREADS)
7738 /* Acquire engine mutex */
7739 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7740 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7741 JX9_THRD_ENGINE_RELEASE(pEngine) ){
7742 return JX9_ABORT; /* Another thread have released this instance */
7743 }
7744#endif
7745 MACRO_LD_REMOVE(pEngine->pVms, pVm);
7746 pEngine->iVm--;
7747 /* Release the memory chunk allocated to this VM */
7748 SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
7749#if defined(JX9_ENABLE_THREADS)
7750 /* Leave engine mutex */
7751 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7752#endif
7753 }
7754 return rc;
7755}
7756/*
7757 * [CAPIREF: jx9_create_function()]
7758 * Please refer to the official documentation for function purpose and expected parameters.
7759 */
7760JX9_PRIVATE int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData)
7761{
7762 SyString sName;
7763 int rc;
7764 /* Ticket 1433-002: NULL VM is harmless operation */
7765 if ( JX9_VM_MISUSE(pVm) ){
7766 return JX9_CORRUPT;
7767 }
7768 SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
7769 /* Remove leading and trailing white spaces */
7770 SyStringFullTrim(&sName);
7771 /* Ticket 1433-003: NULL values are not allowed */
7772 if( sName.nByte < 1 || xFunc == 0 ){
7773 return JX9_CORRUPT;
7774 }
7775#if defined(JX9_ENABLE_THREADS)
7776 /* Acquire VM mutex */
7777 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7778 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7779 JX9_THRD_VM_RELEASE(pVm) ){
7780 return JX9_ABORT; /* Another thread have released this instance */
7781 }
7782#endif
7783 /* Install the foreign function */
7784 rc = jx9VmInstallForeignFunction(&(*pVm), &sName, xFunc, pUserData);
7785#if defined(JX9_ENABLE_THREADS)
7786 /* Leave VM mutex */
7787 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7788#endif
7789 return rc;
7790}
7791JX9_PRIVATE int jx9DeleteFunction(jx9_vm *pVm,const char *zName)
7792{
7793 jx9_user_func *pFunc = 0; /* cc warning */
7794 int rc;
7795 /* Perform the deletion */
7796 rc = SyHashDeleteEntry(&pVm->hHostFunction, (const void *)zName, SyStrlen(zName), (void **)&pFunc);
7797 if( rc == JX9_OK ){
7798 /* Release internal fields */
7799 SySetRelease(&pFunc->aAux);
7800 SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
7801 SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
7802 }
7803 return rc;
7804}
7805/*
7806 * [CAPIREF: jx9_create_constant()]
7807 * Please refer to the official documentation for function purpose and expected parameters.
7808 */
7809JX9_PRIVATE int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData)
7810{
7811 SyString sName;
7812 int rc;
7813 /* Ticket 1433-002: NULL VM is harmless operation */
7814 if ( JX9_VM_MISUSE(pVm) ){
7815 return JX9_CORRUPT;
7816 }
7817 SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
7818 /* Remove leading and trailing white spaces */
7819 SyStringFullTrim(&sName);
7820 if( sName.nByte < 1 ){
7821 /* Empty constant name */
7822 return JX9_CORRUPT;
7823 }
7824 /* TICKET 1433-003: NULL pointer is harmless operation */
7825 if( xExpand == 0 ){
7826 return JX9_CORRUPT;
7827 }
7828#if defined(JX9_ENABLE_THREADS)
7829 /* Acquire VM mutex */
7830 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7831 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7832 JX9_THRD_VM_RELEASE(pVm) ){
7833 return JX9_ABORT; /* Another thread have released this instance */
7834 }
7835#endif
7836 /* Perform the registration */
7837 rc = jx9VmRegisterConstant(&(*pVm), &sName, xExpand, pUserData);
7838#if defined(JX9_ENABLE_THREADS)
7839 /* Leave VM mutex */
7840 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7841#endif
7842 return rc;
7843}
7844JX9_PRIVATE int Jx9DeleteConstant(jx9_vm *pVm,const char *zName)
7845{
7846 jx9_constant *pCons;
7847 int rc;
7848 /* Query the constant hashtable */
7849 rc = SyHashDeleteEntry(&pVm->hConstant, (const void *)zName, SyStrlen(zName), (void **)&pCons);
7850 if( rc == JX9_OK ){
7851 /* Perform the deletion */
7852 SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pCons->sName));
7853 SyMemBackendPoolFree(&pVm->sAllocator, pCons);
7854 }
7855 return rc;
7856}
7857/*
7858 * [CAPIREF: jx9_new_scalar()]
7859 * Please refer to the official documentation for function purpose and expected parameters.
7860 */
7861JX9_PRIVATE jx9_value * jx9_new_scalar(jx9_vm *pVm)
7862{
7863 jx9_value *pObj;
7864 /* Ticket 1433-002: NULL VM is harmless operation */
7865 if ( JX9_VM_MISUSE(pVm) ){
7866 return 0;
7867 }
7868 /* Allocate a new scalar variable */
7869 pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value));
7870 if( pObj == 0 ){
7871 return 0;
7872 }
7873 /* Nullify the new scalar */
7874 jx9MemObjInit(pVm, pObj);
7875 return pObj;
7876}
7877/*
7878 * [CAPIREF: jx9_new_array()]
7879 * Please refer to the official documentation for function purpose and expected parameters.
7880 */
7881JX9_PRIVATE jx9_value * jx9_new_array(jx9_vm *pVm)
7882{
7883 jx9_hashmap *pMap;
7884 jx9_value *pObj;
7885 /* Ticket 1433-002: NULL VM is harmless operation */
7886 if ( JX9_VM_MISUSE(pVm) ){
7887 return 0;
7888 }
7889 /* Create a new hashmap first */
7890 pMap = jx9NewHashmap(&(*pVm), 0, 0);
7891 if( pMap == 0 ){
7892 return 0;
7893 }
7894 /* Associate a new jx9_value with this hashmap */
7895 pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value));
7896 if( pObj == 0 ){
7897 jx9HashmapRelease(pMap, TRUE);
7898 return 0;
7899 }
7900 jx9MemObjInitFromArray(pVm, pObj, pMap);
7901 return pObj;
7902}
7903/*
7904 * [CAPIREF: jx9_release_value()]
7905 * Please refer to the official documentation for function purpose and expected parameters.
7906 */
7907JX9_PRIVATE int jx9_release_value(jx9_vm *pVm, jx9_value *pValue)
7908{
7909 /* Ticket 1433-002: NULL VM is a harmless operation */
7910 if ( JX9_VM_MISUSE(pVm) ){
7911 return JX9_CORRUPT;
7912 }
7913 if( pValue ){
7914 /* Release the value */
7915 jx9MemObjRelease(pValue);
7916 SyMemBackendPoolFree(&pVm->sAllocator, pValue);
7917 }
7918 return JX9_OK;
7919}
7920/*
7921 * [CAPIREF: jx9_value_to_int()]
7922 * Please refer to the official documentation for function purpose and expected parameters.
7923 */
7924JX9_PRIVATE int jx9_value_to_int(jx9_value *pValue)
7925{
7926 int rc;
7927 rc = jx9MemObjToInteger(pValue);
7928 if( rc != JX9_OK ){
7929 return 0;
7930 }
7931 return (int)pValue->x.iVal;
7932}
7933/*
7934 * [CAPIREF: jx9_value_to_bool()]
7935 * Please refer to the official documentation for function purpose and expected parameters.
7936 */
7937JX9_PRIVATE int jx9_value_to_bool(jx9_value *pValue)
7938{
7939 int rc;
7940 rc = jx9MemObjToBool(pValue);
7941 if( rc != JX9_OK ){
7942 return 0;
7943 }
7944 return (int)pValue->x.iVal;
7945}
7946/*
7947 * [CAPIREF: jx9_value_to_int64()]
7948 * Please refer to the official documentation for function purpose and expected parameters.
7949 */
7950JX9_PRIVATE jx9_int64 jx9_value_to_int64(jx9_value *pValue)
7951{
7952 int rc;
7953 rc = jx9MemObjToInteger(pValue);
7954 if( rc != JX9_OK ){
7955 return 0;
7956 }
7957 return pValue->x.iVal;
7958}
7959/*
7960 * [CAPIREF: jx9_value_to_double()]
7961 * Please refer to the official documentation for function purpose and expected parameters.
7962 */
7963JX9_PRIVATE double jx9_value_to_double(jx9_value *pValue)
7964{
7965 int rc;
7966 rc = jx9MemObjToReal(pValue);
7967 if( rc != JX9_OK ){
7968 return (double)0;
7969 }
7970 return (double)pValue->x.rVal;
7971}
7972/*
7973 * [CAPIREF: jx9_value_to_string()]
7974 * Please refer to the official documentation for function purpose and expected parameters.
7975 */
7976JX9_PRIVATE const char * jx9_value_to_string(jx9_value *pValue, int *pLen)
7977{
7978 jx9MemObjToString(pValue);
7979 if( SyBlobLength(&pValue->sBlob) > 0 ){
7980 SyBlobNullAppend(&pValue->sBlob);
7981 if( pLen ){
7982 *pLen = (int)SyBlobLength(&pValue->sBlob);
7983 }
7984 return (const char *)SyBlobData(&pValue->sBlob);
7985 }else{
7986 /* Return the empty string */
7987 if( pLen ){
7988 *pLen = 0;
7989 }
7990 return "";
7991 }
7992}
7993/*
7994 * [CAPIREF: jx9_value_to_resource()]
7995 * Please refer to the official documentation for function purpose and expected parameters.
7996 */
7997JX9_PRIVATE void * jx9_value_to_resource(jx9_value *pValue)
7998{
7999 if( (pValue->iFlags & MEMOBJ_RES) == 0 ){
8000 /* Not a resource, return NULL */
8001 return 0;
8002 }
8003 return pValue->x.pOther;
8004}
8005/*
8006 * [CAPIREF: jx9_value_compare()]
8007 * Please refer to the official documentation for function purpose and expected parameters.
8008 */
8009JX9_PRIVATE int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict)
8010{
8011 int rc;
8012 if( pLeft == 0 || pRight == 0 ){
8013 /* TICKET 1433-24: NULL values is harmless operation */
8014 return 1;
8015 }
8016 /* Perform the comparison */
8017 rc = jx9MemObjCmp(&(*pLeft), &(*pRight), bStrict, 0);
8018 /* Comparison result */
8019 return rc;
8020}
8021/*
8022 * [CAPIREF: jx9_result_int()]
8023 * Please refer to the official documentation for function purpose and expected parameters.
8024 */
8025JX9_PRIVATE int jx9_result_int(jx9_context *pCtx, int iValue)
8026{
8027 return jx9_value_int(pCtx->pRet, iValue);
8028}
8029/*
8030 * [CAPIREF: jx9_result_int64()]
8031 * Please refer to the official documentation for function purpose and expected parameters.
8032 */
8033JX9_PRIVATE int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue)
8034{
8035 return jx9_value_int64(pCtx->pRet, iValue);
8036}
8037/*
8038 * [CAPIREF: jx9_result_bool()]
8039 * Please refer to the official documentation for function purpose and expected parameters.
8040 */
8041JX9_PRIVATE int jx9_result_bool(jx9_context *pCtx, int iBool)
8042{
8043 return jx9_value_bool(pCtx->pRet, iBool);
8044}
8045/*
8046 * [CAPIREF: jx9_result_double()]
8047 * Please refer to the official documentation for function purpose and expected parameters.
8048 */
8049JX9_PRIVATE int jx9_result_double(jx9_context *pCtx, double Value)
8050{
8051 return jx9_value_double(pCtx->pRet, Value);
8052}
8053/*
8054 * [CAPIREF: jx9_result_null()]
8055 * Please refer to the official documentation for function purpose and expected parameters.
8056 */
8057JX9_PRIVATE int jx9_result_null(jx9_context *pCtx)
8058{
8059 /* Invalidate any prior representation and set the NULL flag */
8060 jx9MemObjRelease(pCtx->pRet);
8061 return JX9_OK;
8062}
8063/*
8064 * [CAPIREF: jx9_result_string()]
8065 * Please refer to the official documentation for function purpose and expected parameters.
8066 */
8067JX9_PRIVATE int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen)
8068{
8069 return jx9_value_string(pCtx->pRet, zString, nLen);
8070}
8071/*
8072 * [CAPIREF: jx9_result_string_format()]
8073 * Please refer to the official documentation for function purpose and expected parameters.
8074 */
8075JX9_PRIVATE int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...)
8076{
8077 jx9_value *p;
8078 va_list ap;
8079 int rc;
8080 p = pCtx->pRet;
8081 if( (p->iFlags & MEMOBJ_STRING) == 0 ){
8082 /* Invalidate any prior representation */
8083 jx9MemObjRelease(p);
8084 MemObjSetType(p, MEMOBJ_STRING);
8085 }
8086 /* Format the given string */
8087 va_start(ap, zFormat);
8088 rc = SyBlobFormatAp(&p->sBlob, zFormat, ap);
8089 va_end(ap);
8090 return rc;
8091}
8092/*
8093 * [CAPIREF: jx9_result_value()]
8094 * Please refer to the official documentation for function purpose and expected parameters.
8095 */
8096JX9_PRIVATE int jx9_result_value(jx9_context *pCtx, jx9_value *pValue)
8097{
8098 int rc = JX9_OK;
8099 if( pValue == 0 ){
8100 jx9MemObjRelease(pCtx->pRet);
8101 }else{
8102 rc = jx9MemObjStore(pValue, pCtx->pRet);
8103 }
8104 return rc;
8105}
8106/*
8107 * [CAPIREF: jx9_result_resource()]
8108 * Please refer to the official documentation for function purpose and expected parameters.
8109 */
8110JX9_PRIVATE int jx9_result_resource(jx9_context *pCtx, void *pUserData)
8111{
8112 return jx9_value_resource(pCtx->pRet, pUserData);
8113}
8114/*
8115 * [CAPIREF: jx9_context_new_scalar()]
8116 * Please refer to the official documentation for function purpose and expected parameters.
8117 */
8118JX9_PRIVATE jx9_value * jx9_context_new_scalar(jx9_context *pCtx)
8119{
8120 jx9_value *pVal;
8121 pVal = jx9_new_scalar(pCtx->pVm);
8122 if( pVal ){
8123 /* Record value address so it can be freed automatically
8124 * when the calling function returns.
8125 */
8126 SySetPut(&pCtx->sVar, (const void *)&pVal);
8127 }
8128 return pVal;
8129}
8130/*
8131 * [CAPIREF: jx9_context_new_array()]
8132 * Please refer to the official documentation for function purpose and expected parameters.
8133 */
8134JX9_PRIVATE jx9_value * jx9_context_new_array(jx9_context *pCtx)
8135{
8136 jx9_value *pVal;
8137 pVal = jx9_new_array(pCtx->pVm);
8138 if( pVal ){
8139 /* Record value address so it can be freed automatically
8140 * when the calling function returns.
8141 */
8142 SySetPut(&pCtx->sVar, (const void *)&pVal);
8143 }
8144 return pVal;
8145}
8146/*
8147 * [CAPIREF: jx9_context_release_value()]
8148 * Please refer to the official documentation for function purpose and expected parameters.
8149 */
8150JX9_PRIVATE void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue)
8151{
8152 jx9VmReleaseContextValue(&(*pCtx), pValue);
8153}
8154/*
8155 * [CAPIREF: jx9_context_alloc_chunk()]
8156 * Please refer to the official documentation for function purpose and expected parameters.
8157 */
8158JX9_PRIVATE void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease)
8159{
8160 void *pChunk;
8161 pChunk = SyMemBackendAlloc(&pCtx->pVm->sAllocator, nByte);
8162 if( pChunk ){
8163 if( ZeroChunk ){
8164 /* Zero the memory chunk */
8165 SyZero(pChunk, nByte);
8166 }
8167 if( AutoRelease ){
8168 jx9_aux_data sAux;
8169 /* Track the chunk so that it can be released automatically
8170 * upon this context is destroyed.
8171 */
8172 sAux.pAuxData = pChunk;
8173 SySetPut(&pCtx->sChunk, (const void *)&sAux);
8174 }
8175 }
8176 return pChunk;
8177}
8178/*
8179 * Check if the given chunk address is registered in the call context
8180 * chunk container.
8181 * Return TRUE if registered.FALSE otherwise.
8182 * Refer to [jx9_context_realloc_chunk(), jx9_context_free_chunk()].
8183 */
8184static jx9_aux_data * ContextFindChunk(jx9_context *pCtx, void *pChunk)
8185{
8186 jx9_aux_data *aAux, *pAux;
8187 sxu32 n;
8188 if( SySetUsed(&pCtx->sChunk) < 1 ){
8189 /* Don't bother processing, the container is empty */
8190 return 0;
8191 }
8192 /* Perform the lookup */
8193 aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk);
8194 for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){
8195 pAux = &aAux[n];
8196 if( pAux->pAuxData == pChunk ){
8197 /* Chunk found */
8198 return pAux;
8199 }
8200 }
8201 /* No such allocated chunk */
8202 return 0;
8203}
8204/*
8205 * [CAPIREF: jx9_context_realloc_chunk()]
8206 * Please refer to the official documentation for function purpose and expected parameters.
8207 */
8208JX9_PRIVATE void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte)
8209{
8210 jx9_aux_data *pAux;
8211 void *pNew;
8212 pNew = SyMemBackendRealloc(&pCtx->pVm->sAllocator, pChunk, nByte);
8213 if( pNew ){
8214 pAux = ContextFindChunk(pCtx, pChunk);
8215 if( pAux ){
8216 pAux->pAuxData = pNew;
8217 }
8218 }
8219 return pNew;
8220}
8221/*
8222 * [CAPIREF: jx9_context_free_chunk()]
8223 * Please refer to the official documentation for function purpose and expected parameters.
8224 */
8225JX9_PRIVATE void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk)
8226{
8227 jx9_aux_data *pAux;
8228 if( pChunk == 0 ){
8229 /* TICKET-1433-93: NULL chunk is a harmless operation */
8230 return;
8231 }
8232 pAux = ContextFindChunk(pCtx, pChunk);
8233 if( pAux ){
8234 /* Mark as destroyed */
8235 pAux->pAuxData = 0;
8236 }
8237 SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk);
8238}
8239/*
8240 * [CAPIREF: jx9_array_fetch()]
8241 * Please refer to the official documentation for function purpose and expected parameters.
8242 */
8243JX9_PRIVATE jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte)
8244{
8245 jx9_hashmap_node *pNode;
8246 jx9_value *pValue;
8247 jx9_value skey;
8248 int rc;
8249 /* Make sure we are dealing with a valid hashmap */
8250 if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
8251 return 0;
8252 }
8253 if( nByte < 0 ){
8254 nByte = (int)SyStrlen(zKey);
8255 }
8256 /* Convert the key to a jx9_value */
8257 jx9MemObjInit(pArray->pVm, &skey);
8258 jx9MemObjStringAppend(&skey, zKey, (sxu32)nByte);
8259 /* Perform the lookup */
8260 rc = jx9HashmapLookup((jx9_hashmap *)pArray->x.pOther, &skey, &pNode);
8261 jx9MemObjRelease(&skey);
8262 if( rc != JX9_OK ){
8263 /* No such entry */
8264 return 0;
8265 }
8266 /* Extract the target value */
8267 pValue = (jx9_value *)SySetAt(&pArray->pVm->aMemObj, pNode->nValIdx);
8268 return pValue;
8269}
8270/*
8271 * [CAPIREF: jx9_array_walk()]
8272 * Please refer to the official documentation for function purpose and expected parameters.
8273 */
8274JX9_PRIVATE int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *pValue, jx9_value *, void *), void *pUserData)
8275{
8276 int rc;
8277 if( xWalk == 0 ){
8278 return JX9_CORRUPT;
8279 }
8280 /* Make sure we are dealing with a valid hashmap */
8281 if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
8282 return JX9_CORRUPT;
8283 }
8284 /* Start the walk process */
8285 rc = jx9HashmapWalk((jx9_hashmap *)pArray->x.pOther, xWalk, pUserData);
8286 return rc != JX9_OK ? JX9_ABORT /* User callback request an operation abort*/ : JX9_OK;
8287}
8288/*
8289 * [CAPIREF: jx9_array_add_elem()]
8290 * Please refer to the official documentation for function purpose and expected parameters.
8291 */
8292JX9_PRIVATE int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue)
8293{
8294 int rc;
8295 /* Make sure we are dealing with a valid hashmap */
8296 if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
8297 return JX9_CORRUPT;
8298 }
8299 /* Perform the insertion */
8300 rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &(*pKey), &(*pValue));
8301 return rc;
8302}
8303/*
8304 * [CAPIREF: jx9_array_add_strkey_elem()]
8305 * Please refer to the official documentation for function purpose and expected parameters.
8306 */
8307JX9_PRIVATE int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue)
8308{
8309 int rc;
8310 /* Make sure we are dealing with a valid hashmap */
8311 if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
8312 return JX9_CORRUPT;
8313 }
8314 /* Perform the insertion */
8315 if( SX_EMPTY_STR(zKey) ){
8316 /* Empty key, assign an automatic index */
8317 rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, 0, &(*pValue));
8318 }else{
8319 jx9_value sKey;
8320 jx9MemObjInitFromString(pArray->pVm, &sKey, 0);
8321 jx9MemObjStringAppend(&sKey, zKey, (sxu32)SyStrlen(zKey));
8322 rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &sKey, &(*pValue));
8323 jx9MemObjRelease(&sKey);
8324 }
8325 return rc;
8326}
8327/*
8328 * [CAPIREF: jx9_array_count()]
8329 * Please refer to the official documentation for function purpose and expected parameters.
8330 */
8331JX9_PRIVATE unsigned int jx9_array_count(jx9_value *pArray)
8332{
8333 jx9_hashmap *pMap;
8334 /* Make sure we are dealing with a valid hashmap */
8335 if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
8336 return 0;
8337 }
8338 /* Point to the internal representation of the hashmap */
8339 pMap = (jx9_hashmap *)pArray->x.pOther;
8340 return pMap->nEntry;
8341}
8342/*
8343 * [CAPIREF: jx9_context_output()]
8344 * Please refer to the official documentation for function purpose and expected parameters.
8345 */
8346JX9_PRIVATE int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen)
8347{
8348 SyString sData;
8349 int rc;
8350 if( nLen < 0 ){
8351 nLen = (int)SyStrlen(zString);
8352 }
8353 SyStringInitFromBuf(&sData, zString, nLen);
8354 rc = jx9VmOutputConsume(pCtx->pVm, &sData);
8355 return rc;
8356}
8357/*
8358 * [CAPIREF: jx9_context_throw_error()]
8359 * Please refer to the official documentation for function purpose and expected parameters.
8360 */
8361JX9_PRIVATE int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr)
8362{
8363 int rc = JX9_OK;
8364 if( zErr ){
8365 rc = jx9VmThrowError(pCtx->pVm, &pCtx->pFunc->sName, iErr, zErr);
8366 }
8367 return rc;
8368}
8369/*
8370 * [CAPIREF: jx9_context_throw_error_format()]
8371 * Please refer to the official documentation for function purpose and expected parameters.
8372 */
8373JX9_PRIVATE int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...)
8374{
8375 va_list ap;
8376 int rc;
8377 if( zFormat == 0){
8378 return JX9_OK;
8379 }
8380 va_start(ap, zFormat);
8381 rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap);
8382 va_end(ap);
8383 return rc;
8384}
8385/*
8386 * [CAPIREF: jx9_context_random_num()]
8387 * Please refer to the official documentation for function purpose and expected parameters.
8388 */
8389JX9_PRIVATE unsigned int jx9_context_random_num(jx9_context *pCtx)
8390{
8391 sxu32 n;
8392 n = jx9VmRandomNum(pCtx->pVm);
8393 return n;
8394}
8395/*
8396 * [CAPIREF: jx9_context_random_string()]
8397 * Please refer to the official documentation for function purpose and expected parameters.
8398 */
8399JX9_PRIVATE int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen)
8400{
8401 if( nBuflen < 3 ){
8402 return JX9_CORRUPT;
8403 }
8404 jx9VmRandomString(pCtx->pVm, zBuf, nBuflen);
8405 return JX9_OK;
8406}
8407/*
8408 * [CAPIREF: jx9_context_user_data()]
8409 * Please refer to the official documentation for function purpose and expected parameters.
8410 */
8411JX9_PRIVATE void * jx9_context_user_data(jx9_context *pCtx)
8412{
8413 return pCtx->pFunc->pUserData;
8414}
8415/*
8416 * [CAPIREF: jx9_context_push_aux_data()]
8417 * Please refer to the official documentation for function purpose and expected parameters.
8418 */
8419JX9_PRIVATE int jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData)
8420{
8421 jx9_aux_data sAux;
8422 int rc;
8423 sAux.pAuxData = pUserData;
8424 rc = SySetPut(&pCtx->pFunc->aAux, (const void *)&sAux);
8425 return rc;
8426}
8427/*
8428 * [CAPIREF: jx9_context_peek_aux_data()]
8429 * Please refer to the official documentation for function purpose and expected parameters.
8430 */
8431JX9_PRIVATE void * jx9_context_peek_aux_data(jx9_context *pCtx)
8432{
8433 jx9_aux_data *pAux;
8434 pAux = (jx9_aux_data *)SySetPeek(&pCtx->pFunc->aAux);
8435 return pAux ? pAux->pAuxData : 0;
8436}
8437/*
8438 * [CAPIREF: jx9_context_pop_aux_data()]
8439 * Please refer to the official documentation for function purpose and expected parameters.
8440 */
8441JX9_PRIVATE void * jx9_context_pop_aux_data(jx9_context *pCtx)
8442{
8443 jx9_aux_data *pAux;
8444 pAux = (jx9_aux_data *)SySetPop(&pCtx->pFunc->aAux);
8445 return pAux ? pAux->pAuxData : 0;
8446}
8447/*
8448 * [CAPIREF: jx9_context_result_buf_length()]
8449 * Please refer to the official documentation for function purpose and expected parameters.
8450 */
8451JX9_PRIVATE unsigned int jx9_context_result_buf_length(jx9_context *pCtx)
8452{
8453 return SyBlobLength(&pCtx->pRet->sBlob);
8454}
8455/*
8456 * [CAPIREF: jx9_function_name()]
8457 * Please refer to the official documentation for function purpose and expected parameters.
8458 */
8459JX9_PRIVATE const char * jx9_function_name(jx9_context *pCtx)
8460{
8461 SyString *pName;
8462 pName = &pCtx->pFunc->sName;
8463 return pName->zString;
8464}
8465/*
8466 * [CAPIREF: jx9_value_int()]
8467 * Please refer to the official documentation for function purpose and expected parameters.
8468 */
8469JX9_PRIVATE int jx9_value_int(jx9_value *pVal, int iValue)
8470{
8471 /* Invalidate any prior representation */
8472 jx9MemObjRelease(pVal);
8473 pVal->x.iVal = (jx9_int64)iValue;
8474 MemObjSetType(pVal, MEMOBJ_INT);
8475 return JX9_OK;
8476}
8477/*
8478 * [CAPIREF: jx9_value_int64()]
8479 * Please refer to the official documentation for function purpose and expected parameters.
8480 */
8481JX9_PRIVATE int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue)
8482{
8483 /* Invalidate any prior representation */
8484 jx9MemObjRelease(pVal);
8485 pVal->x.iVal = iValue;
8486 MemObjSetType(pVal, MEMOBJ_INT);
8487 return JX9_OK;
8488}
8489/*
8490 * [CAPIREF: jx9_value_bool()]
8491 * Please refer to the official documentation for function purpose and expected parameters.
8492 */
8493JX9_PRIVATE int jx9_value_bool(jx9_value *pVal, int iBool)
8494{
8495 /* Invalidate any prior representation */
8496 jx9MemObjRelease(pVal);
8497 pVal->x.iVal = iBool ? 1 : 0;
8498 MemObjSetType(pVal, MEMOBJ_BOOL);
8499 return JX9_OK;
8500}
8501/*
8502 * [CAPIREF: jx9_value_null()]
8503 * Please refer to the official documentation for function purpose and expected parameters.
8504 */
8505JX9_PRIVATE int jx9_value_null(jx9_value *pVal)
8506{
8507 /* Invalidate any prior representation and set the NULL flag */
8508 jx9MemObjRelease(pVal);
8509 return JX9_OK;
8510}
8511/*
8512 * [CAPIREF: jx9_value_double()]
8513 * Please refer to the official documentation for function purpose and expected parameters.
8514 */
8515JX9_PRIVATE int jx9_value_double(jx9_value *pVal, double Value)
8516{
8517 /* Invalidate any prior representation */
8518 jx9MemObjRelease(pVal);
8519 pVal->x.rVal = (jx9_real)Value;
8520 MemObjSetType(pVal, MEMOBJ_REAL);
8521 /* Try to get an integer representation also */
8522 jx9MemObjTryInteger(pVal);
8523 return JX9_OK;
8524}
8525/*
8526 * [CAPIREF: jx9_value_string()]
8527 * Please refer to the official documentation for function purpose and expected parameters.
8528 */
8529JX9_PRIVATE int jx9_value_string(jx9_value *pVal, const char *zString, int nLen)
8530{
8531 if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
8532 /* Invalidate any prior representation */
8533 jx9MemObjRelease(pVal);
8534 MemObjSetType(pVal, MEMOBJ_STRING);
8535 }
8536 if( zString ){
8537 if( nLen < 0 ){
8538 /* Compute length automatically */
8539 nLen = (int)SyStrlen(zString);
8540 }
8541 SyBlobAppend(&pVal->sBlob, (const void *)zString, (sxu32)nLen);
8542 }
8543 return JX9_OK;
8544}
8545/*
8546 * [CAPIREF: jx9_value_string_format()]
8547 * Please refer to the official documentation for function purpose and expected parameters.
8548 */
8549JX9_PRIVATE int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...)
8550{
8551 va_list ap;
8552 int rc;
8553 if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
8554 /* Invalidate any prior representation */
8555 jx9MemObjRelease(pVal);
8556 MemObjSetType(pVal, MEMOBJ_STRING);
8557 }
8558 va_start(ap, zFormat);
8559 rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap);
8560 va_end(ap);
8561 return JX9_OK;
8562}
8563/*
8564 * [CAPIREF: jx9_value_reset_string_cursor()]
8565 * Please refer to the official documentation for function purpose and expected parameters.
8566 */
8567JX9_PRIVATE int jx9_value_reset_string_cursor(jx9_value *pVal)
8568{
8569 /* Reset the string cursor */
8570 SyBlobReset(&pVal->sBlob);
8571 return JX9_OK;
8572}
8573/*
8574 * [CAPIREF: jx9_value_resource()]
8575 * Please refer to the official documentation for function purpose and expected parameters.
8576 */
8577JX9_PRIVATE int jx9_value_resource(jx9_value *pVal, void *pUserData)
8578{
8579 /* Invalidate any prior representation */
8580 jx9MemObjRelease(pVal);
8581 /* Reflect the new type */
8582 pVal->x.pOther = pUserData;
8583 MemObjSetType(pVal, MEMOBJ_RES);
8584 return JX9_OK;
8585}
8586/*
8587 * [CAPIREF: jx9_value_release()]
8588 * Please refer to the official documentation for function purpose and expected parameters.
8589 */
8590JX9_PRIVATE int jx9_value_release(jx9_value *pVal)
8591{
8592 jx9MemObjRelease(pVal);
8593 return JX9_OK;
8594}
8595/*
8596 * [CAPIREF: jx9_value_is_int()]
8597 * Please refer to the official documentation for function purpose and expected parameters.
8598 */
8599JX9_PRIVATE int jx9_value_is_int(jx9_value *pVal)
8600{
8601 return (pVal->iFlags & MEMOBJ_INT) ? TRUE : FALSE;
8602}
8603/*
8604 * [CAPIREF: jx9_value_is_float()]
8605 * Please refer to the official documentation for function purpose and expected parameters.
8606 */
8607JX9_PRIVATE int jx9_value_is_float(jx9_value *pVal)
8608{
8609 return (pVal->iFlags & MEMOBJ_REAL) ? TRUE : FALSE;
8610}
8611/*
8612 * [CAPIREF: jx9_value_is_bool()]
8613 * Please refer to the official documentation for function purpose and expected parameters.
8614 */
8615JX9_PRIVATE int jx9_value_is_bool(jx9_value *pVal)
8616{
8617 return (pVal->iFlags & MEMOBJ_BOOL) ? TRUE : FALSE;
8618}
8619/*
8620 * [CAPIREF: jx9_value_is_string()]
8621 * Please refer to the official documentation for function purpose and expected parameters.
8622 */
8623JX9_PRIVATE int jx9_value_is_string(jx9_value *pVal)
8624{
8625 return (pVal->iFlags & MEMOBJ_STRING) ? TRUE : FALSE;
8626}
8627/*
8628 * [CAPIREF: jx9_value_is_null()]
8629 * Please refer to the official documentation for function purpose and expected parameters.
8630 */
8631JX9_PRIVATE int jx9_value_is_null(jx9_value *pVal)
8632{
8633 return (pVal->iFlags & MEMOBJ_NULL) ? TRUE : FALSE;
8634}
8635/*
8636 * [CAPIREF: jx9_value_is_numeric()]
8637 * Please refer to the official documentation for function purpose and expected parameters.
8638 */
8639JX9_PRIVATE int jx9_value_is_numeric(jx9_value *pVal)
8640{
8641 int rc;
8642 rc = jx9MemObjIsNumeric(pVal);
8643 return rc;
8644}
8645/*
8646 * [CAPIREF: jx9_value_is_callable()]
8647 * Please refer to the official documentation for function purpose and expected parameters.
8648 */
8649JX9_PRIVATE int jx9_value_is_callable(jx9_value *pVal)
8650{
8651 int rc;
8652 rc = jx9VmIsCallable(pVal->pVm, pVal);
8653 return rc;
8654}
8655/*
8656 * [CAPIREF: jx9_value_is_scalar()]
8657 * Please refer to the official documentation for function purpose and expected parameters.
8658 */
8659JX9_PRIVATE int jx9_value_is_scalar(jx9_value *pVal)
8660{
8661 return (pVal->iFlags & MEMOBJ_SCALAR) ? TRUE : FALSE;
8662}
8663/*
8664 * [CAPIREF: jx9_value_is_json_array()]
8665 * Please refer to the official documentation for function purpose and expected parameters.
8666 */
8667JX9_PRIVATE int jx9_value_is_json_array(jx9_value *pVal)
8668{
8669 return (pVal->iFlags & MEMOBJ_HASHMAP) ? TRUE : FALSE;
8670}
8671/*
8672 * [CAPIREF: jx9_value_is_json_object()]
8673 * Please refer to the official documentation for function purpose and expected parameters.
8674 */
8675JX9_PRIVATE int jx9_value_is_json_object(jx9_value *pVal)
8676{
8677 jx9_hashmap *pMap;
8678 if( (pVal->iFlags & MEMOBJ_HASHMAP) == 0 ){
8679 return FALSE;
8680 }
8681 pMap = (jx9_hashmap *)pVal->x.pOther;
8682 if( (pMap->iFlags & HASHMAP_JSON_OBJECT) == 0 ){
8683 return FALSE;
8684 }
8685 return TRUE;
8686}
8687/*
8688 * [CAPIREF: jx9_value_is_resource()]
8689 * Please refer to the official documentation for function purpose and expected parameters.
8690 */
8691JX9_PRIVATE int jx9_value_is_resource(jx9_value *pVal)
8692{
8693 return (pVal->iFlags & MEMOBJ_RES) ? TRUE : FALSE;
8694}
8695/*
8696 * [CAPIREF: jx9_value_is_empty()]
8697 * Please refer to the official documentation for function purpose and expected parameters.
8698 */
8699JX9_PRIVATE int jx9_value_is_empty(jx9_value *pVal)
8700{
8701 int rc;
8702 rc = jx9MemObjIsEmpty(pVal);
8703 return rc;
8704}
8705/*
8706 * ----------------------------------------------------------
8707 * File: jx9_builtin.c
8708 * MD5: 97ae6ddf8ded9fe14634060675e12f80
8709 * ----------------------------------------------------------
8710 */
8711/*
8712 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
8713 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
8714 * Version 1.7.2
8715 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
8716 * please contact Symisc Systems via:
8717 * legal@symisc.net
8718 * licensing@symisc.net
8719 * contact@symisc.net
8720 * or visit:
8721 * http://jx9.symisc.net/
8722 */
8723 /* $SymiscID: builtin.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
8724#ifndef JX9_AMALGAMATION
8725#include "jx9Int.h"
8726#endif
8727/* This file implement built-in 'foreign' functions for the JX9 engine */
8728/*
8729 * Section:
8730 * Variable handling Functions.
8731 * Authors:
8732 * Symisc Systems, devel@symisc.net.
8733 * Copyright (C) Symisc Systems, http://jx9.symisc.net
8734 * Status:
8735 * Stable.
8736 */
8737/*
8738 * bool is_bool($var)
8739 * Finds out whether a variable is a boolean.
8740 * Parameters
8741 * $var: The variable being evaluated.
8742 * Return
8743 * TRUE if var is a boolean. False otherwise.
8744 */
8745static int jx9Builtin_is_bool(jx9_context *pCtx, int nArg, jx9_value **apArg)
8746{
8747 int res = 0; /* Assume false by default */
8748 if( nArg > 0 ){
8749 res = jx9_value_is_bool(apArg[0]);
8750 }
8751 /* Query result */
8752 jx9_result_bool(pCtx, res);
8753 return JX9_OK;
8754}
8755/*
8756 * bool is_float($var)
8757 * bool is_real($var)
8758 * bool is_double($var)
8759 * Finds out whether a variable is a float.
8760 * Parameters
8761 * $var: The variable being evaluated.
8762 * Return
8763 * TRUE if var is a float. False otherwise.
8764 */
8765static int jx9Builtin_is_float(jx9_context *pCtx, int nArg, jx9_value **apArg)
8766{
8767 int res = 0; /* Assume false by default */
8768 if( nArg > 0 ){
8769 res = jx9_value_is_float(apArg[0]);
8770 }
8771 /* Query result */
8772 jx9_result_bool(pCtx, res);
8773 return JX9_OK;
8774}
8775/*
8776 * bool is_int($var)
8777 * bool is_integer($var)
8778 * bool is_long($var)
8779 * Finds out whether a variable is an integer.
8780 * Parameters
8781 * $var: The variable being evaluated.
8782 * Return
8783 * TRUE if var is an integer. False otherwise.
8784 */
8785static int jx9Builtin_is_int(jx9_context *pCtx, int nArg, jx9_value **apArg)
8786{
8787 int res = 0; /* Assume false by default */
8788 if( nArg > 0 ){
8789 res = jx9_value_is_int(apArg[0]);
8790 }
8791 /* Query result */
8792 jx9_result_bool(pCtx, res);
8793 return JX9_OK;
8794}
8795/*
8796 * bool is_string($var)
8797 * Finds out whether a variable is a string.
8798 * Parameters
8799 * $var: The variable being evaluated.
8800 * Return
8801 * TRUE if var is string. False otherwise.
8802 */
8803static int jx9Builtin_is_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
8804{
8805 int res = 0; /* Assume false by default */
8806 if( nArg > 0 ){
8807 res = jx9_value_is_string(apArg[0]);
8808 }
8809 /* Query result */
8810 jx9_result_bool(pCtx, res);
8811 return JX9_OK;
8812}
8813/*
8814 * bool is_null($var)
8815 * Finds out whether a variable is NULL.
8816 * Parameters
8817 * $var: The variable being evaluated.
8818 * Return
8819 * TRUE if var is NULL. False otherwise.
8820 */
8821static int jx9Builtin_is_null(jx9_context *pCtx, int nArg, jx9_value **apArg)
8822{
8823 int res = 0; /* Assume false by default */
8824 if( nArg > 0 ){
8825 res = jx9_value_is_null(apArg[0]);
8826 }
8827 /* Query result */
8828 jx9_result_bool(pCtx, res);
8829 return JX9_OK;
8830}
8831/*
8832 * bool is_numeric($var)
8833 * Find out whether a variable is NULL.
8834 * Parameters
8835 * $var: The variable being evaluated.
8836 * Return
8837 * True if var is numeric. False otherwise.
8838 */
8839static int jx9Builtin_is_numeric(jx9_context *pCtx, int nArg, jx9_value **apArg)
8840{
8841 int res = 0; /* Assume false by default */
8842 if( nArg > 0 ){
8843 res = jx9_value_is_numeric(apArg[0]);
8844 }
8845 /* Query result */
8846 jx9_result_bool(pCtx, res);
8847 return JX9_OK;
8848}
8849/*
8850 * bool is_scalar($var)
8851 * Find out whether a variable is a scalar.
8852 * Parameters
8853 * $var: The variable being evaluated.
8854 * Return
8855 * True if var is scalar. False otherwise.
8856 */
8857static int jx9Builtin_is_scalar(jx9_context *pCtx, int nArg, jx9_value **apArg)
8858{
8859 int res = 0; /* Assume false by default */
8860 if( nArg > 0 ){
8861 res = jx9_value_is_scalar(apArg[0]);
8862 }
8863 /* Query result */
8864 jx9_result_bool(pCtx, res);
8865 return JX9_OK;
8866}
8867/*
8868 * bool is_array($var)
8869 * Find out whether a variable is an array.
8870 * Parameters
8871 * $var: The variable being evaluated.
8872 * Return
8873 * True if var is an array. False otherwise.
8874 */
8875static int jx9Builtin_is_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
8876{
8877 int res = 0; /* Assume false by default */
8878 if( nArg > 0 ){
8879 res = jx9_value_is_json_array(apArg[0]);
8880 }
8881 /* Query result */
8882 jx9_result_bool(pCtx, res);
8883 return JX9_OK;
8884}
8885/*
8886 * bool is_object($var)
8887 * Find out whether a variable is an object.
8888 * Parameters
8889 * $var: The variable being evaluated.
8890 * Return
8891 * True if var is an object. False otherwise.
8892 */
8893static int jx9Builtin_is_object(jx9_context *pCtx, int nArg, jx9_value **apArg)
8894{
8895 int res = 0; /* Assume false by default */
8896 if( nArg > 0 ){
8897 res = jx9_value_is_json_object(apArg[0]);
8898 }
8899 /* Query result */
8900 jx9_result_bool(pCtx, res);
8901 return JX9_OK;
8902}
8903/*
8904 * bool is_resource($var)
8905 * Find out whether a variable is a resource.
8906 * Parameters
8907 * $var: The variable being evaluated.
8908 * Return
8909 * True if a resource. False otherwise.
8910 */
8911static int jx9Builtin_is_resource(jx9_context *pCtx, int nArg, jx9_value **apArg)
8912{
8913 int res = 0; /* Assume false by default */
8914 if( nArg > 0 ){
8915 res = jx9_value_is_resource(apArg[0]);
8916 }
8917 jx9_result_bool(pCtx, res);
8918 return JX9_OK;
8919}
8920/*
8921 * float floatval($var)
8922 * Get float value of a variable.
8923 * Parameter
8924 * $var: The variable being processed.
8925 * Return
8926 * the float value of a variable.
8927 */
8928static int jx9Builtin_floatval(jx9_context *pCtx, int nArg, jx9_value **apArg)
8929{
8930 if( nArg < 1 ){
8931 /* return 0.0 */
8932 jx9_result_double(pCtx, 0);
8933 }else{
8934 double dval;
8935 /* Perform the cast */
8936 dval = jx9_value_to_double(apArg[0]);
8937 jx9_result_double(pCtx, dval);
8938 }
8939 return JX9_OK;
8940}
8941/*
8942 * int intval($var)
8943 * Get integer value of a variable.
8944 * Parameter
8945 * $var: The variable being processed.
8946 * Return
8947 * the int value of a variable.
8948 */
8949static int jx9Builtin_intval(jx9_context *pCtx, int nArg, jx9_value **apArg)
8950{
8951 if( nArg < 1 ){
8952 /* return 0 */
8953 jx9_result_int(pCtx, 0);
8954 }else{
8955 sxi64 iVal;
8956 /* Perform the cast */
8957 iVal = jx9_value_to_int64(apArg[0]);
8958 jx9_result_int64(pCtx, iVal);
8959 }
8960 return JX9_OK;
8961}
8962/*
8963 * string strval($var)
8964 * Get the string representation of a variable.
8965 * Parameter
8966 * $var: The variable being processed.
8967 * Return
8968 * the string value of a variable.
8969 */
8970static int jx9Builtin_strval(jx9_context *pCtx, int nArg, jx9_value **apArg)
8971{
8972 if( nArg < 1 ){
8973 /* return NULL */
8974 jx9_result_null(pCtx);
8975 }else{
8976 const char *zVal;
8977 int iLen = 0; /* cc -O6 warning */
8978 /* Perform the cast */
8979 zVal = jx9_value_to_string(apArg[0], &iLen);
8980 jx9_result_string(pCtx, zVal, iLen);
8981 }
8982 return JX9_OK;
8983}
8984/*
8985 * bool empty($var)
8986 * Determine whether a variable is empty.
8987 * Parameters
8988 * $var: The variable being checked.
8989 * Return
8990 * 0 if var has a non-empty and non-zero value.1 otherwise.
8991 */
8992static int jx9Builtin_empty(jx9_context *pCtx, int nArg, jx9_value **apArg)
8993{
8994 int res = 1; /* Assume empty by default */
8995 if( nArg > 0 ){
8996 res = jx9_value_is_empty(apArg[0]);
8997 }
8998 jx9_result_bool(pCtx, res);
8999 return JX9_OK;
9000
9001}
9002#ifndef JX9_DISABLE_BUILTIN_FUNC
9003#ifdef JX9_ENABLE_MATH_FUNC
9004/*
9005 * Section:
9006 * Math Functions.
9007 * Authors:
9008 * Symisc Systems, devel@symisc.net.
9009 * Copyright (C) Symisc Systems, http://jx9.symisc.net
9010 * Status:
9011 * Stable.
9012 */
9013#include <stdlib.h> /* abs */
9014#include <math.h>
9015/*
9016 * float sqrt(float $arg )
9017 * Square root of the given number.
9018 * Parameter
9019 * The number to process.
9020 * Return
9021 * The square root of arg or the special value Nan of failure.
9022 */
9023static int jx9Builtin_sqrt(jx9_context *pCtx, int nArg, jx9_value **apArg)
9024{
9025 double r, x;
9026 if( nArg < 1 ){
9027 /* Missing argument, return 0 */
9028 jx9_result_int(pCtx, 0);
9029 return JX9_OK;
9030 }
9031 x = jx9_value_to_double(apArg[0]);
9032 /* Perform the requested operation */
9033 r = sqrt(x);
9034 /* store the result back */
9035 jx9_result_double(pCtx, r);
9036 return JX9_OK;
9037}
9038/*
9039 * float exp(float $arg )
9040 * Calculates the exponent of e.
9041 * Parameter
9042 * The number to process.
9043 * Return
9044 * 'e' raised to the power of arg.
9045 */
9046static int jx9Builtin_exp(jx9_context *pCtx, int nArg, jx9_value **apArg)
9047{
9048 double r, x;
9049 if( nArg < 1 ){
9050 /* Missing argument, return 0 */
9051 jx9_result_int(pCtx, 0);
9052 return JX9_OK;
9053 }
9054 x = jx9_value_to_double(apArg[0]);
9055 /* Perform the requested operation */
9056 r = exp(x);
9057 /* store the result back */
9058 jx9_result_double(pCtx, r);
9059 return JX9_OK;
9060}
9061/*
9062 * float floor(float $arg )
9063 * Round fractions down.
9064 * Parameter
9065 * The number to process.
9066 * Return
9067 * Returns the next lowest integer value by rounding down value if necessary.
9068 */
9069static int jx9Builtin_floor(jx9_context *pCtx, int nArg, jx9_value **apArg)
9070{
9071 double r, x;
9072 if( nArg < 1 ){
9073 /* Missing argument, return 0 */
9074 jx9_result_int(pCtx, 0);
9075 return JX9_OK;
9076 }
9077 x = jx9_value_to_double(apArg[0]);
9078 /* Perform the requested operation */
9079 r = floor(x);
9080 /* store the result back */
9081 jx9_result_double(pCtx, r);
9082 return JX9_OK;
9083}
9084/*
9085 * float cos(float $arg )
9086 * Cosine.
9087 * Parameter
9088 * The number to process.
9089 * Return
9090 * The cosine of arg.
9091 */
9092static int jx9Builtin_cos(jx9_context *pCtx, int nArg, jx9_value **apArg)
9093{
9094 double r, x;
9095 if( nArg < 1 ){
9096 /* Missing argument, return 0 */
9097 jx9_result_int(pCtx, 0);
9098 return JX9_OK;
9099 }
9100 x = jx9_value_to_double(apArg[0]);
9101 /* Perform the requested operation */
9102 r = cos(x);
9103 /* store the result back */
9104 jx9_result_double(pCtx, r);
9105 return JX9_OK;
9106}
9107/*
9108 * float acos(float $arg )
9109 * Arc cosine.
9110 * Parameter
9111 * The number to process.
9112 * Return
9113 * The arc cosine of arg.
9114 */
9115static int jx9Builtin_acos(jx9_context *pCtx, int nArg, jx9_value **apArg)
9116{
9117 double r, x;
9118 if( nArg < 1 ){
9119 /* Missing argument, return 0 */
9120 jx9_result_int(pCtx, 0);
9121 return JX9_OK;
9122 }
9123 x = jx9_value_to_double(apArg[0]);
9124 /* Perform the requested operation */
9125 r = acos(x);
9126 /* store the result back */
9127 jx9_result_double(pCtx, r);
9128 return JX9_OK;
9129}
9130/*
9131 * float cosh(float $arg )
9132 * Hyperbolic cosine.
9133 * Parameter
9134 * The number to process.
9135 * Return
9136 * The hyperbolic cosine of arg.
9137 */
9138static int jx9Builtin_cosh(jx9_context *pCtx, int nArg, jx9_value **apArg)
9139{
9140 double r, x;
9141 if( nArg < 1 ){
9142 /* Missing argument, return 0 */
9143 jx9_result_int(pCtx, 0);
9144 return JX9_OK;
9145 }
9146 x = jx9_value_to_double(apArg[0]);
9147 /* Perform the requested operation */
9148 r = cosh(x);
9149 /* store the result back */
9150 jx9_result_double(pCtx, r);
9151 return JX9_OK;
9152}
9153/*
9154 * float sin(float $arg )
9155 * Sine.
9156 * Parameter
9157 * The number to process.
9158 * Return
9159 * The sine of arg.
9160 */
9161static int jx9Builtin_sin(jx9_context *pCtx, int nArg, jx9_value **apArg)
9162{
9163 double r, x;
9164 if( nArg < 1 ){
9165 /* Missing argument, return 0 */
9166 jx9_result_int(pCtx, 0);
9167 return JX9_OK;
9168 }
9169 x = jx9_value_to_double(apArg[0]);
9170 /* Perform the requested operation */
9171 r = sin(x);
9172 /* store the result back */
9173 jx9_result_double(pCtx, r);
9174 return JX9_OK;
9175}
9176/*
9177 * float asin(float $arg )
9178 * Arc sine.
9179 * Parameter
9180 * The number to process.
9181 * Return
9182 * The arc sine of arg.
9183 */
9184static int jx9Builtin_asin(jx9_context *pCtx, int nArg, jx9_value **apArg)
9185{
9186 double r, x;
9187 if( nArg < 1 ){
9188 /* Missing argument, return 0 */
9189 jx9_result_int(pCtx, 0);
9190 return JX9_OK;
9191 }
9192 x = jx9_value_to_double(apArg[0]);
9193 /* Perform the requested operation */
9194 r = asin(x);
9195 /* store the result back */
9196 jx9_result_double(pCtx, r);
9197 return JX9_OK;
9198}
9199/*
9200 * float sinh(float $arg )
9201 * Hyperbolic sine.
9202 * Parameter
9203 * The number to process.
9204 * Return
9205 * The hyperbolic sine of arg.
9206 */
9207static int jx9Builtin_sinh(jx9_context *pCtx, int nArg, jx9_value **apArg)
9208{
9209 double r, x;
9210 if( nArg < 1 ){
9211 /* Missing argument, return 0 */
9212 jx9_result_int(pCtx, 0);
9213 return JX9_OK;
9214 }
9215 x = jx9_value_to_double(apArg[0]);
9216 /* Perform the requested operation */
9217 r = sinh(x);
9218 /* store the result back */
9219 jx9_result_double(pCtx, r);
9220 return JX9_OK;
9221}
9222/*
9223 * float ceil(float $arg )
9224 * Round fractions up.
9225 * Parameter
9226 * The number to process.
9227 * Return
9228 * The next highest integer value by rounding up value if necessary.
9229 */
9230static int jx9Builtin_ceil(jx9_context *pCtx, int nArg, jx9_value **apArg)
9231{
9232 double r, x;
9233 if( nArg < 1 ){
9234 /* Missing argument, return 0 */
9235 jx9_result_int(pCtx, 0);
9236 return JX9_OK;
9237 }
9238 x = jx9_value_to_double(apArg[0]);
9239 /* Perform the requested operation */
9240 r = ceil(x);
9241 /* store the result back */
9242 jx9_result_double(pCtx, r);
9243 return JX9_OK;
9244}
9245/*
9246 * float tan(float $arg )
9247 * Tangent.
9248 * Parameter
9249 * The number to process.
9250 * Return
9251 * The tangent of arg.
9252 */
9253static int jx9Builtin_tan(jx9_context *pCtx, int nArg, jx9_value **apArg)
9254{
9255 double r, x;
9256 if( nArg < 1 ){
9257 /* Missing argument, return 0 */
9258 jx9_result_int(pCtx, 0);
9259 return JX9_OK;
9260 }
9261 x = jx9_value_to_double(apArg[0]);
9262 /* Perform the requested operation */
9263 r = tan(x);
9264 /* store the result back */
9265 jx9_result_double(pCtx, r);
9266 return JX9_OK;
9267}
9268/*
9269 * float atan(float $arg )
9270 * Arc tangent.
9271 * Parameter
9272 * The number to process.
9273 * Return
9274 * The arc tangent of arg.
9275 */
9276static int jx9Builtin_atan(jx9_context *pCtx, int nArg, jx9_value **apArg)
9277{
9278 double r, x;
9279 if( nArg < 1 ){
9280 /* Missing argument, return 0 */
9281 jx9_result_int(pCtx, 0);
9282 return JX9_OK;
9283 }
9284 x = jx9_value_to_double(apArg[0]);
9285 /* Perform the requested operation */
9286 r = atan(x);
9287 /* store the result back */
9288 jx9_result_double(pCtx, r);
9289 return JX9_OK;
9290}
9291/*
9292 * float tanh(float $arg )
9293 * Hyperbolic tangent.
9294 * Parameter
9295 * The number to process.
9296 * Return
9297 * The Hyperbolic tangent of arg.
9298 */
9299static int jx9Builtin_tanh(jx9_context *pCtx, int nArg, jx9_value **apArg)
9300{
9301 double r, x;
9302 if( nArg < 1 ){
9303 /* Missing argument, return 0 */
9304 jx9_result_int(pCtx, 0);
9305 return JX9_OK;
9306 }
9307 x = jx9_value_to_double(apArg[0]);
9308 /* Perform the requested operation */
9309 r = tanh(x);
9310 /* store the result back */
9311 jx9_result_double(pCtx, r);
9312 return JX9_OK;
9313}
9314/*
9315 * float atan2(float $y, float $x)
9316 * Arc tangent of two variable.
9317 * Parameter
9318 * $y = Dividend parameter.
9319 * $x = Divisor parameter.
9320 * Return
9321 * The arc tangent of y/x in radian.
9322 */
9323static int jx9Builtin_atan2(jx9_context *pCtx, int nArg, jx9_value **apArg)
9324{
9325 double r, x, y;
9326 if( nArg < 2 ){
9327 /* Missing arguments, return 0 */
9328 jx9_result_int(pCtx, 0);
9329 return JX9_OK;
9330 }
9331 y = jx9_value_to_double(apArg[0]);
9332 x = jx9_value_to_double(apArg[1]);
9333 /* Perform the requested operation */
9334 r = atan2(y, x);
9335 /* store the result back */
9336 jx9_result_double(pCtx, r);
9337 return JX9_OK;
9338}
9339/*
9340 * float/int64 abs(float/int64 $arg )
9341 * Absolute value.
9342 * Parameter
9343 * The number to process.
9344 * Return
9345 * The absolute value of number.
9346 */
9347static int jx9Builtin_abs(jx9_context *pCtx, int nArg, jx9_value **apArg)
9348{
9349 int is_float;
9350 if( nArg < 1 ){
9351 /* Missing argument, return 0 */
9352 jx9_result_int(pCtx, 0);
9353 return JX9_OK;
9354 }
9355 is_float = jx9_value_is_float(apArg[0]);
9356 if( is_float ){
9357 double r, x;
9358 x = jx9_value_to_double(apArg[0]);
9359 /* Perform the requested operation */
9360 r = fabs(x);
9361 jx9_result_double(pCtx, r);
9362 }else{
9363 int r, x;
9364 x = jx9_value_to_int(apArg[0]);
9365 /* Perform the requested operation */
9366 r = abs(x);
9367 jx9_result_int(pCtx, r);
9368 }
9369 return JX9_OK;
9370}
9371/*
9372 * float log(float $arg, [int/float $base])
9373 * Natural logarithm.
9374 * Parameter
9375 * $arg: The number to process.
9376 * $base: The optional logarithmic base to use. (only base-10 is supported)
9377 * Return
9378 * The logarithm of arg to base, if given, or the natural logarithm.
9379 * Note:
9380 * only Natural log and base-10 log are supported.
9381 */
9382static int jx9Builtin_log(jx9_context *pCtx, int nArg, jx9_value **apArg)
9383{
9384 double r, x;
9385 if( nArg < 1 ){
9386 /* Missing argument, return 0 */
9387 jx9_result_int(pCtx, 0);
9388 return JX9_OK;
9389 }
9390 x = jx9_value_to_double(apArg[0]);
9391 /* Perform the requested operation */
9392 if( nArg == 2 && jx9_value_is_numeric(apArg[1]) && jx9_value_to_int(apArg[1]) == 10 ){
9393 /* Base-10 log */
9394 r = log10(x);
9395 }else{
9396 r = log(x);
9397 }
9398 /* store the result back */
9399 jx9_result_double(pCtx, r);
9400 return JX9_OK;
9401}
9402/*
9403 * float log10(float $arg )
9404 * Base-10 logarithm.
9405 * Parameter
9406 * The number to process.
9407 * Return
9408 * The Base-10 logarithm of the given number.
9409 */
9410static int jx9Builtin_log10(jx9_context *pCtx, int nArg, jx9_value **apArg)
9411{
9412 double r, x;
9413 if( nArg < 1 ){
9414 /* Missing argument, return 0 */
9415 jx9_result_int(pCtx, 0);
9416 return JX9_OK;
9417 }
9418 x = jx9_value_to_double(apArg[0]);
9419 /* Perform the requested operation */
9420 r = log10(x);
9421 /* store the result back */
9422 jx9_result_double(pCtx, r);
9423 return JX9_OK;
9424}
9425/*
9426 * number pow(number $base, number $exp)
9427 * Exponential expression.
9428 * Parameter
9429 * base
9430 * The base to use.
9431 * exp
9432 * The exponent.
9433 * Return
9434 * base raised to the power of exp.
9435 * If the result can be represented as integer it will be returned
9436 * as type integer, else it will be returned as type float.
9437 */
9438static int jx9Builtin_pow(jx9_context *pCtx, int nArg, jx9_value **apArg)
9439{
9440 double r, x, y;
9441 if( nArg < 1 ){
9442 /* Missing argument, return 0 */
9443 jx9_result_int(pCtx, 0);
9444 return JX9_OK;
9445 }
9446 x = jx9_value_to_double(apArg[0]);
9447 y = jx9_value_to_double(apArg[1]);
9448 /* Perform the requested operation */
9449 r = pow(x, y);
9450 jx9_result_double(pCtx, r);
9451 return JX9_OK;
9452}
9453/*
9454 * float pi(void)
9455 * Returns an approximation of pi.
9456 * Note
9457 * you can use the M_PI constant which yields identical results to pi().
9458 * Return
9459 * The value of pi as float.
9460 */
9461static int jx9Builtin_pi(jx9_context *pCtx, int nArg, jx9_value **apArg)
9462{
9463 SXUNUSED(nArg); /* cc warning */
9464 SXUNUSED(apArg);
9465 jx9_result_double(pCtx, JX9_PI);
9466 return JX9_OK;
9467}
9468/*
9469 * float fmod(float $x, float $y)
9470 * Returns the floating point remainder (modulo) of the division of the arguments.
9471 * Parameters
9472 * $x
9473 * The dividend
9474 * $y
9475 * The divisor
9476 * Return
9477 * The floating point remainder of x/y.
9478 */
9479static int jx9Builtin_fmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
9480{
9481 double x, y, r;
9482 if( nArg < 2 ){
9483 /* Missing arguments */
9484 jx9_result_double(pCtx, 0);
9485 return JX9_OK;
9486 }
9487 /* Extract given arguments */
9488 x = jx9_value_to_double(apArg[0]);
9489 y = jx9_value_to_double(apArg[1]);
9490 /* Perform the requested operation */
9491 r = fmod(x, y);
9492 /* Processing result */
9493 jx9_result_double(pCtx, r);
9494 return JX9_OK;
9495}
9496/*
9497 * float hypot(float $x, float $y)
9498 * Calculate the length of the hypotenuse of a right-angle triangle .
9499 * Parameters
9500 * $x
9501 * Length of first side
9502 * $y
9503 * Length of first side
9504 * Return
9505 * Calculated length of the hypotenuse.
9506 */
9507static int jx9Builtin_hypot(jx9_context *pCtx, int nArg, jx9_value **apArg)
9508{
9509 double x, y, r;
9510 if( nArg < 2 ){
9511 /* Missing arguments */
9512 jx9_result_double(pCtx, 0);
9513 return JX9_OK;
9514 }
9515 /* Extract given arguments */
9516 x = jx9_value_to_double(apArg[0]);
9517 y = jx9_value_to_double(apArg[1]);
9518 /* Perform the requested operation */
9519 r = hypot(x, y);
9520 /* Processing result */
9521 jx9_result_double(pCtx, r);
9522 return JX9_OK;
9523}
9524#endif /* JX9_ENABLE_MATH_FUNC */
9525/*
9526 * float round ( float $val [, int $precision = 0 [, int $mode = JX9_ROUND_HALF_UP ]] )
9527 * Exponential expression.
9528 * Parameter
9529 * $val
9530 * The value to round.
9531 * $precision
9532 * The optional number of decimal digits to round to.
9533 * $mode
9534 * One of JX9_ROUND_HALF_UP, JX9_ROUND_HALF_DOWN, JX9_ROUND_HALF_EVEN, or JX9_ROUND_HALF_ODD.
9535 * (not supported).
9536 * Return
9537 * The rounded value.
9538 */
9539static int jx9Builtin_round(jx9_context *pCtx, int nArg, jx9_value **apArg)
9540{
9541 int n = 0;
9542 double r;
9543 if( nArg < 1 ){
9544 /* Missing argument, return 0 */
9545 jx9_result_int(pCtx, 0);
9546 return JX9_OK;
9547 }
9548 /* Extract the precision if available */
9549 if( nArg > 1 ){
9550 n = jx9_value_to_int(apArg[1]);
9551 if( n>30 ){
9552 n = 30;
9553 }
9554 if( n<0 ){
9555 n = 0;
9556 }
9557 }
9558 r = jx9_value_to_double(apArg[0]);
9559 /* If Y==0 and X will fit in a 64-bit int,
9560 * handle the rounding directly.Otherwise
9561 * use our own cutsom printf [i.e:SyBufferFormat()].
9562 */
9563 if( n==0 && r>=0 && r<LARGEST_INT64-1 ){
9564 r = (double)((jx9_int64)(r+0.5));
9565 }else if( n==0 && r<0 && (-r)<LARGEST_INT64-1 ){
9566 r = -(double)((jx9_int64)((-r)+0.5));
9567 }else{
9568 char zBuf[256];
9569 sxu32 nLen;
9570 nLen = SyBufferFormat(zBuf, sizeof(zBuf), "%.*f", n, r);
9571 /* Convert the string to real number */
9572 SyStrToReal(zBuf, nLen, (void *)&r, 0);
9573 }
9574 /* Return thr rounded value */
9575 jx9_result_double(pCtx, r);
9576 return JX9_OK;
9577}
9578/*
9579 * string dechex(int $number)
9580 * Decimal to hexadecimal.
9581 * Parameters
9582 * $number
9583 * Decimal value to convert
9584 * Return
9585 * Hexadecimal string representation of number
9586 */
9587static int jx9Builtin_dechex(jx9_context *pCtx, int nArg, jx9_value **apArg)
9588{
9589 int iVal;
9590 if( nArg < 1 ){
9591 /* Missing arguments, return null */
9592 jx9_result_null(pCtx);
9593 return JX9_OK;
9594 }
9595 /* Extract the given number */
9596 iVal = jx9_value_to_int(apArg[0]);
9597 /* Format */
9598 jx9_result_string_format(pCtx, "%x", iVal);
9599 return JX9_OK;
9600}
9601/*
9602 * string decoct(int $number)
9603 * Decimal to Octal.
9604 * Parameters
9605 * $number
9606 * Decimal value to convert
9607 * Return
9608 * Octal string representation of number
9609 */
9610static int jx9Builtin_decoct(jx9_context *pCtx, int nArg, jx9_value **apArg)
9611{
9612 int iVal;
9613 if( nArg < 1 ){
9614 /* Missing arguments, return null */
9615 jx9_result_null(pCtx);
9616 return JX9_OK;
9617 }
9618 /* Extract the given number */
9619 iVal = jx9_value_to_int(apArg[0]);
9620 /* Format */
9621 jx9_result_string_format(pCtx, "%o", iVal);
9622 return JX9_OK;
9623}
9624/*
9625 * string decbin(int $number)
9626 * Decimal to binary.
9627 * Parameters
9628 * $number
9629 * Decimal value to convert
9630 * Return
9631 * Binary string representation of number
9632 */
9633static int jx9Builtin_decbin(jx9_context *pCtx, int nArg, jx9_value **apArg)
9634{
9635 int iVal;
9636 if( nArg < 1 ){
9637 /* Missing arguments, return null */
9638 jx9_result_null(pCtx);
9639 return JX9_OK;
9640 }
9641 /* Extract the given number */
9642 iVal = jx9_value_to_int(apArg[0]);
9643 /* Format */
9644 jx9_result_string_format(pCtx, "%B", iVal);
9645 return JX9_OK;
9646}
9647/*
9648 * int64 hexdec(string $hex_string)
9649 * Hexadecimal to decimal.
9650 * Parameters
9651 * $hex_string
9652 * The hexadecimal string to convert
9653 * Return
9654 * The decimal representation of hex_string
9655 */
9656static int jx9Builtin_hexdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
9657{
9658 const char *zString, *zEnd;
9659 jx9_int64 iVal;
9660 int nLen;
9661 if( nArg < 1 ){
9662 /* Missing arguments, return -1 */
9663 jx9_result_int(pCtx, -1);
9664 return JX9_OK;
9665 }
9666 iVal = 0;
9667 if( jx9_value_is_string(apArg[0]) ){
9668 /* Extract the given string */
9669 zString = jx9_value_to_string(apArg[0], &nLen);
9670 /* Delimit the string */
9671 zEnd = &zString[nLen];
9672 /* Ignore non hex-stream */
9673 while( zString < zEnd ){
9674 if( (unsigned char)zString[0] >= 0xc0 ){
9675 /* UTF-8 stream */
9676 zString++;
9677 while( zString < zEnd && (((unsigned char)zString[0] & 0xc0) == 0x80) ){
9678 zString++;
9679 }
9680 }else{
9681 if( SyisHex(zString[0]) ){
9682 break;
9683 }
9684 /* Ignore */
9685 zString++;
9686 }
9687 }
9688 if( zString < zEnd ){
9689 /* Cast */
9690 SyHexStrToInt64(zString, (sxu32)(zEnd-zString), (void *)&iVal, 0);
9691 }
9692 }else{
9693 /* Extract as a 64-bit integer */
9694 iVal = jx9_value_to_int64(apArg[0]);
9695 }
9696 /* Return the number */
9697 jx9_result_int64(pCtx, iVal);
9698 return JX9_OK;
9699}
9700/*
9701 * int64 bindec(string $bin_string)
9702 * Binary to decimal.
9703 * Parameters
9704 * $bin_string
9705 * The binary string to convert
9706 * Return
9707 * Returns the decimal equivalent of the binary number represented by the binary_string argument.
9708 */
9709static int jx9Builtin_bindec(jx9_context *pCtx, int nArg, jx9_value **apArg)
9710{
9711 const char *zString;
9712 jx9_int64 iVal;
9713 int nLen;
9714 if( nArg < 1 ){
9715 /* Missing arguments, return -1 */
9716 jx9_result_int(pCtx, -1);
9717 return JX9_OK;
9718 }
9719 iVal = 0;
9720 if( jx9_value_is_string(apArg[0]) ){
9721 /* Extract the given string */
9722 zString = jx9_value_to_string(apArg[0], &nLen);
9723 if( nLen > 0 ){
9724 /* Perform a binary cast */
9725 SyBinaryStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
9726 }
9727 }else{
9728 /* Extract as a 64-bit integer */
9729 iVal = jx9_value_to_int64(apArg[0]);
9730 }
9731 /* Return the number */
9732 jx9_result_int64(pCtx, iVal);
9733 return JX9_OK;
9734}
9735/*
9736 * int64 octdec(string $oct_string)
9737 * Octal to decimal.
9738 * Parameters
9739 * $oct_string
9740 * The octal string to convert
9741 * Return
9742 * Returns the decimal equivalent of the octal number represented by the octal_string argument.
9743 */
9744static int jx9Builtin_octdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
9745{
9746 const char *zString;
9747 jx9_int64 iVal;
9748 int nLen;
9749 if( nArg < 1 ){
9750 /* Missing arguments, return -1 */
9751 jx9_result_int(pCtx, -1);
9752 return JX9_OK;
9753 }
9754 iVal = 0;
9755 if( jx9_value_is_string(apArg[0]) ){
9756 /* Extract the given string */
9757 zString = jx9_value_to_string(apArg[0], &nLen);
9758 if( nLen > 0 ){
9759 /* Perform the cast */
9760 SyOctalStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
9761 }
9762 }else{
9763 /* Extract as a 64-bit integer */
9764 iVal = jx9_value_to_int64(apArg[0]);
9765 }
9766 /* Return the number */
9767 jx9_result_int64(pCtx, iVal);
9768 return JX9_OK;
9769}
9770/*
9771 * string base_convert(string $number, int $frombase, int $tobase)
9772 * Convert a number between arbitrary bases.
9773 * Parameters
9774 * $number
9775 * The number to convert
9776 * $frombase
9777 * The base number is in
9778 * $tobase
9779 * The base to convert number to
9780 * Return
9781 * Number converted to base tobase
9782 */
9783static int jx9Builtin_base_convert(jx9_context *pCtx, int nArg, jx9_value **apArg)
9784{
9785 int nLen, iFbase, iTobase;
9786 const char *zNum;
9787 jx9_int64 iNum;
9788 if( nArg < 3 ){
9789 /* Return the empty string*/
9790 jx9_result_string(pCtx, "", 0);
9791 return JX9_OK;
9792 }
9793 /* Base numbers */
9794 iFbase = jx9_value_to_int(apArg[1]);
9795 iTobase = jx9_value_to_int(apArg[2]);
9796 if( jx9_value_is_string(apArg[0]) ){
9797 /* Extract the target number */
9798 zNum = jx9_value_to_string(apArg[0], &nLen);
9799 if( nLen < 1 ){
9800 /* Return the empty string*/
9801 jx9_result_string(pCtx, "", 0);
9802 return JX9_OK;
9803 }
9804 /* Base conversion */
9805 switch(iFbase){
9806 case 16:
9807 /* Hex */
9808 SyHexStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
9809 break;
9810 case 8:
9811 /* Octal */
9812 SyOctalStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
9813 break;
9814 case 2:
9815 /* Binary */
9816 SyBinaryStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
9817 break;
9818 default:
9819 /* Decimal */
9820 SyStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
9821 break;
9822 }
9823 }else{
9824 iNum = jx9_value_to_int64(apArg[0]);
9825 }
9826 switch(iTobase){
9827 case 16:
9828 /* Hex */
9829 jx9_result_string_format(pCtx, "%qx", iNum); /* Quad hex */
9830 break;
9831 case 8:
9832 /* Octal */
9833 jx9_result_string_format(pCtx, "%qo", iNum); /* Quad octal */
9834 break;
9835 case 2:
9836 /* Binary */
9837 jx9_result_string_format(pCtx, "%qB", iNum); /* Quad binary */
9838 break;
9839 default:
9840 /* Decimal */
9841 jx9_result_string_format(pCtx, "%qd", iNum); /* Quad decimal */
9842 break;
9843 }
9844 return JX9_OK;
9845}
9846/*
9847 * Section:
9848 * String handling Functions.
9849 * Authors:
9850 * Symisc Systems, devel@symisc.net.
9851 * Copyright (C) Symisc Systems, http://jx9.symisc.net
9852 * Status:
9853 * Stable.
9854 */
9855/*
9856 * string substr(string $string, int $start[, int $length ])
9857 * Return part of a string.
9858 * Parameters
9859 * $string
9860 * The input string. Must be one character or longer.
9861 * $start
9862 * If start is non-negative, the returned string will start at the start'th position
9863 * in string, counting from zero. For instance, in the string 'abcdef', the character
9864 * at position 0 is 'a', the character at position 2 is 'c', and so forth.
9865 * If start is negative, the returned string will start at the start'th character
9866 * from the end of string.
9867 * If string is less than or equal to start characters long, FALSE will be returned.
9868 * $length
9869 * If length is given and is positive, the string returned will contain at most length
9870 * characters beginning from start (depending on the length of string).
9871 * If length is given and is negative, then that many characters will be omitted from
9872 * the end of string (after the start position has been calculated when a start is negative).
9873 * If start denotes the position of this truncation or beyond, false will be returned.
9874 * If length is given and is 0, FALSE or NULL an empty string will be returned.
9875 * If length is omitted, the substring starting from start until the end of the string
9876 * will be returned.
9877 * Return
9878 * Returns the extracted part of string, or FALSE on failure or an empty string.
9879 */
9880static int jx9Builtin_substr(jx9_context *pCtx, int nArg, jx9_value **apArg)
9881{
9882 const char *zSource, *zOfft;
9883 int nOfft, nLen, nSrcLen;
9884 if( nArg < 2 ){
9885 /* return FALSE */
9886 jx9_result_bool(pCtx, 0);
9887 return JX9_OK;
9888 }
9889 /* Extract the target string */
9890 zSource = jx9_value_to_string(apArg[0], &nSrcLen);
9891 if( nSrcLen < 1 ){
9892 /* Empty string, return FALSE */
9893 jx9_result_bool(pCtx, 0);
9894 return JX9_OK;
9895 }
9896 nLen = nSrcLen; /* cc warning */
9897 /* Extract the offset */
9898 nOfft = jx9_value_to_int(apArg[1]);
9899 if( nOfft < 0 ){
9900 zOfft = &zSource[nSrcLen+nOfft];
9901 if( zOfft < zSource ){
9902 /* Invalid offset */
9903 jx9_result_bool(pCtx, 0);
9904 return JX9_OK;
9905 }
9906 nLen = (int)(&zSource[nSrcLen]-zOfft);
9907 nOfft = (int)(zOfft-zSource);
9908 }else if( nOfft >= nSrcLen ){
9909 /* Invalid offset */
9910 jx9_result_bool(pCtx, 0);
9911 return JX9_OK;
9912 }else{
9913 zOfft = &zSource[nOfft];
9914 nLen = nSrcLen - nOfft;
9915 }
9916 if( nArg > 2 ){
9917 /* Extract the length */
9918 nLen = jx9_value_to_int(apArg[2]);
9919 if( nLen == 0 ){
9920 /* Invalid length, return an empty string */
9921 jx9_result_string(pCtx, "", 0);
9922 return JX9_OK;
9923 }else if( nLen < 0 ){
9924 nLen = nSrcLen + nLen - nOfft;
9925 if( nLen < 1 ){
9926 /* Invalid length */
9927 nLen = nSrcLen - nOfft;
9928 }
9929 }
9930 if( nLen + nOfft > nSrcLen ){
9931 /* Invalid length */
9932 nLen = nSrcLen - nOfft;
9933 }
9934 }
9935 /* Return the substring */
9936 jx9_result_string(pCtx, zOfft, nLen);
9937 return JX9_OK;
9938}
9939/*
9940 * int substr_compare(string $main_str, string $str , int $offset[, int $length[, bool $case_insensitivity = false ]])
9941 * Binary safe comparison of two strings from an offset, up to length characters.
9942 * Parameters
9943 * $main_str
9944 * The main string being compared.
9945 * $str
9946 * The secondary string being compared.
9947 * $offset
9948 * The start position for the comparison. If negative, it starts counting from
9949 * the end of the string.
9950 * $length
9951 * The length of the comparison. The default value is the largest of the length
9952 * of the str compared to the length of main_str less the offset.
9953 * $case_insensitivity
9954 * If case_insensitivity is TRUE, comparison is case insensitive.
9955 * Return
9956 * Returns < 0 if main_str from position offset is less than str, > 0 if it is greater than
9957 * str, and 0 if they are equal. If offset is equal to or greater than the length of main_str
9958 * or length is set and is less than 1, substr_compare() prints a warning and returns FALSE.
9959 */
9960static int jx9Builtin_substr_compare(jx9_context *pCtx, int nArg, jx9_value **apArg)
9961{
9962 const char *zSource, *zOfft, *zSub;
9963 int nOfft, nLen, nSrcLen, nSublen;
9964 int iCase = 0;
9965 int rc;
9966 if( nArg < 3 ){
9967 /* Missing arguments, return FALSE */
9968 jx9_result_bool(pCtx, 0);
9969 return JX9_OK;
9970 }
9971 /* Extract the target string */
9972 zSource = jx9_value_to_string(apArg[0], &nSrcLen);
9973 if( nSrcLen < 1 ){
9974 /* Empty string, return FALSE */
9975 jx9_result_bool(pCtx, 0);
9976 return JX9_OK;
9977 }
9978 nLen = nSrcLen; /* cc warning */
9979 /* Extract the substring */
9980 zSub = jx9_value_to_string(apArg[1], &nSublen);
9981 if( nSublen < 1 || nSublen > nSrcLen){
9982 /* Empty string, return FALSE */
9983 jx9_result_bool(pCtx, 0);
9984 return JX9_OK;
9985 }
9986 /* Extract the offset */
9987 nOfft = jx9_value_to_int(apArg[2]);
9988 if( nOfft < 0 ){
9989 zOfft = &zSource[nSrcLen+nOfft];
9990 if( zOfft < zSource ){
9991 /* Invalid offset */
9992 jx9_result_bool(pCtx, 0);
9993 return JX9_OK;
9994 }
9995 nLen = (int)(&zSource[nSrcLen]-zOfft);
9996 nOfft = (int)(zOfft-zSource);
9997 }else if( nOfft >= nSrcLen ){
9998 /* Invalid offset */
9999 jx9_result_bool(pCtx, 0);
10000 return JX9_OK;
10001 }else{
10002 zOfft = &zSource[nOfft];
10003 nLen = nSrcLen - nOfft;
10004 }
10005 if( nArg > 3 ){
10006 /* Extract the length */
10007 nLen = jx9_value_to_int(apArg[3]);
10008 if( nLen < 1 ){
10009 /* Invalid length */
10010 jx9_result_int(pCtx, 1);
10011 return JX9_OK;
10012 }else if( nLen + nOfft > nSrcLen ){
10013 /* Invalid length */
10014 nLen = nSrcLen - nOfft;
10015 }
10016 if( nArg > 4 ){
10017 /* Case-sensitive or not */
10018 iCase = jx9_value_to_bool(apArg[4]);
10019 }
10020 }
10021 /* Perform the comparison */
10022 if( iCase ){
10023 rc = SyStrnicmp(zOfft, zSub, (sxu32)nLen);
10024 }else{
10025 rc = SyStrncmp(zOfft, zSub, (sxu32)nLen);
10026 }
10027 /* Comparison result */
10028 jx9_result_int(pCtx, rc);
10029 return JX9_OK;
10030}
10031/*
10032 * int substr_count(string $haystack, string $needle[, int $offset = 0 [, int $length ]])
10033 * Count the number of substring occurrences.
10034 * Parameters
10035 * $haystack
10036 * The string to search in
10037 * $needle
10038 * The substring to search for
10039 * $offset
10040 * The offset where to start counting
10041 * $length (NOT USED)
10042 * The maximum length after the specified offset to search for the substring.
10043 * It outputs a warning if the offset plus the length is greater than the haystack length.
10044 * Return
10045 * Toral number of substring occurrences.
10046 */
10047static int jx9Builtin_substr_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
10048{
10049 const char *zText, *zPattern, *zEnd;
10050 int nTextlen, nPatlen;
10051 int iCount = 0;
10052 sxu32 nOfft;
10053 sxi32 rc;
10054 if( nArg < 2 ){
10055 /* Missing arguments */
10056 jx9_result_int(pCtx, 0);
10057 return JX9_OK;
10058 }
10059 /* Point to the haystack */
10060 zText = jx9_value_to_string(apArg[0], &nTextlen);
10061 /* Point to the neddle */
10062 zPattern = jx9_value_to_string(apArg[1], &nPatlen);
10063 if( nTextlen < 1 || nPatlen < 1 || nPatlen > nTextlen ){
10064 /* NOOP, return zero */
10065 jx9_result_int(pCtx, 0);
10066 return JX9_OK;
10067 }
10068 if( nArg > 2 ){
10069 int nOfft;
10070 /* Extract the offset */
10071 nOfft = jx9_value_to_int(apArg[2]);
10072 if( nOfft < 0 || nOfft > nTextlen ){
10073 /* Invalid offset, return zero */
10074 jx9_result_int(pCtx, 0);
10075 return JX9_OK;
10076 }
10077 /* Point to the desired offset */
10078 zText = &zText[nOfft];
10079 /* Adjust length */
10080 nTextlen -= nOfft;
10081 }
10082 /* Point to the end of the string */
10083 zEnd = &zText[nTextlen];
10084 if( nArg > 3 ){
10085 int nLen;
10086 /* Extract the length */
10087 nLen = jx9_value_to_int(apArg[3]);
10088 if( nLen < 0 || nLen > nTextlen ){
10089 /* Invalid length, return 0 */
10090 jx9_result_int(pCtx, 0);
10091 return JX9_OK;
10092 }
10093 /* Adjust pointer */
10094 nTextlen = nLen;
10095 zEnd = &zText[nTextlen];
10096 }
10097 /* Perform the search */
10098 for(;;){
10099 rc = SyBlobSearch((const void *)zText, (sxu32)(zEnd-zText), (const void *)zPattern, nPatlen, &nOfft);
10100 if( rc != SXRET_OK ){
10101 /* Pattern not found, break immediately */
10102 break;
10103 }
10104 /* Increment counter and update the offset */
10105 iCount++;
10106 zText += nOfft + nPatlen;
10107 if( zText >= zEnd ){
10108 break;
10109 }
10110 }
10111 /* Pattern count */
10112 jx9_result_int(pCtx, iCount);
10113 return JX9_OK;
10114}
10115/*
10116 * string chunk_split(string $body[, int $chunklen = 76 [, string $end = "\r\n" ]])
10117 * Split a string into smaller chunks.
10118 * Parameters
10119 * $body
10120 * The string to be chunked.
10121 * $chunklen
10122 * The chunk length.
10123 * $end
10124 * The line ending sequence.
10125 * Return
10126 * The chunked string or NULL on failure.
10127 */
10128static int jx9Builtin_chunk_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
10129{
10130 const char *zIn, *zEnd, *zSep = "\r\n";
10131 int nSepLen, nChunkLen, nLen;
10132 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
10133 /* Nothing to split, return null */
10134 jx9_result_null(pCtx);
10135 return JX9_OK;
10136 }
10137 /* initialize/Extract arguments */
10138 nSepLen = (int)sizeof("\r\n") - 1;
10139 nChunkLen = 76;
10140 zIn = jx9_value_to_string(apArg[0], &nLen);
10141 zEnd = &zIn[nLen];
10142 if( nArg > 1 ){
10143 /* Chunk length */
10144 nChunkLen = jx9_value_to_int(apArg[1]);
10145 if( nChunkLen < 1 ){
10146 /* Switch back to the default length */
10147 nChunkLen = 76;
10148 }
10149 if( nArg > 2 ){
10150 /* Separator */
10151 zSep = jx9_value_to_string(apArg[2], &nSepLen);
10152 if( nSepLen < 1 ){
10153 /* Switch back to the default separator */
10154 zSep = "\r\n";
10155 nSepLen = (int)sizeof("\r\n") - 1;
10156 }
10157 }
10158 }
10159 /* Perform the requested operation */
10160 if( nChunkLen > nLen ){
10161 /* Nothing to split, return the string and the separator */
10162 jx9_result_string_format(pCtx, "%.*s%.*s", nLen, zIn, nSepLen, zSep);
10163 return JX9_OK;
10164 }
10165 while( zIn < zEnd ){
10166 if( nChunkLen > (int)(zEnd-zIn) ){
10167 nChunkLen = (int)(zEnd - zIn);
10168 }
10169 /* Append the chunk and the separator */
10170 jx9_result_string_format(pCtx, "%.*s%.*s", nChunkLen, zIn, nSepLen, zSep);
10171 /* Point beyond the chunk */
10172 zIn += nChunkLen;
10173 }
10174 return JX9_OK;
10175}
10176/*
10177 * string htmlspecialchars(string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $charset]])
10178 * HTML escaping of special characters.
10179 * The translations performed are:
10180 * '&' (ampersand) ==> '&amp;'
10181 * '"' (double quote) ==> '&quot;' when ENT_NOQUOTES is not set.
10182 * "'" (single quote) ==> '&#039;' only when ENT_QUOTES is set.
10183 * '<' (less than) ==> '&lt;'
10184 * '>' (greater than) ==> '&gt;'
10185 * Parameters
10186 * $string
10187 * The string being converted.
10188 * $flags
10189 * A bitmask of one or more of the following flags, which specify how to handle quotes.
10190 * The default is ENT_COMPAT | ENT_HTML401.
10191 * ENT_COMPAT Will convert double-quotes and leave single-quotes alone.
10192 * ENT_QUOTES Will convert both double and single quotes.
10193 * ENT_NOQUOTES Will leave both double and single quotes unconverted.
10194 * ENT_IGNORE Silently discard invalid code unit sequences instead of returning an empty string.
10195 * $charset
10196 * Defines character set used in conversion. The default character set is ISO-8859-1. (Not used)
10197 * Return
10198 * The escaped string or NULL on failure.
10199 */
10200static int jx9Builtin_htmlspecialchars(jx9_context *pCtx, int nArg, jx9_value **apArg)
10201{
10202 const char *zCur, *zIn, *zEnd;
10203 int iFlags = 0x01|0x40; /* ENT_COMPAT | ENT_HTML401 */
10204 int nLen, c;
10205 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
10206 /* Missing/Invalid arguments, return NULL */
10207 jx9_result_null(pCtx);
10208 return JX9_OK;
10209 }
10210 /* Extract the target string */
10211 zIn = jx9_value_to_string(apArg[0], &nLen);
10212 zEnd = &zIn[nLen];
10213 /* Extract the flags if available */
10214 if( nArg > 1 ){
10215 iFlags = jx9_value_to_int(apArg[1]);
10216 if( iFlags < 0 ){
10217 iFlags = 0x01|0x40;
10218 }
10219 }
10220 /* Perform the requested operation */
10221 for(;;){
10222 if( zIn >= zEnd ){
10223 break;
10224 }
10225 zCur = zIn;
10226 while( zIn < zEnd && zIn[0] != '&' && zIn[0] != '\'' && zIn[0] != '"' && zIn[0] != '<' && zIn[0] != '>' ){
10227 zIn++;
10228 }
10229 if( zCur < zIn ){
10230 /* Append the raw string verbatim */
10231 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
10232 }
10233 if( zIn >= zEnd ){
10234 break;
10235 }
10236 c = zIn[0];
10237 if( c == '&' ){
10238 /* Expand '&amp;' */
10239 jx9_result_string(pCtx, "&amp;", (int)sizeof("&amp;")-1);
10240 }else if( c == '<' ){
10241 /* Expand '&lt;' */
10242 jx9_result_string(pCtx, "&lt;", (int)sizeof("&lt;")-1);
10243 }else if( c == '>' ){
10244 /* Expand '&gt;' */
10245 jx9_result_string(pCtx, "&gt;", (int)sizeof("&gt;")-1);
10246 }else if( c == '\'' ){
10247 if( iFlags & 0x02 /*ENT_QUOTES*/ ){
10248 /* Expand '&#039;' */
10249 jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
10250 }else{
10251 /* Leave the single quote untouched */
10252 jx9_result_string(pCtx, "'", (int)sizeof(char));
10253 }
10254 }else if( c == '"' ){
10255 if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
10256 /* Expand '&quot;' */
10257 jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
10258 }else{
10259 /* Leave the double quote untouched */
10260 jx9_result_string(pCtx, "\"", (int)sizeof(char));
10261 }
10262 }
10263 /* Ignore the unsafe HTML character */
10264 zIn++;
10265 }
10266 return JX9_OK;
10267}
10268/*
10269 * string htmlspecialchars_decode(string $string[, int $quote_style = ENT_COMPAT ])
10270 * Unescape HTML entities.
10271 * Parameters
10272 * $string
10273 * The string to decode
10274 * $quote_style
10275 * The quote style. One of the following constants:
10276 * ENT_COMPAT Will convert double-quotes and leave single-quotes alone (default)
10277 * ENT_QUOTES Will convert both double and single quotes
10278 * ENT_NOQUOTES Will leave both double and single quotes unconverted
10279 * Return
10280 * The unescaped string or NULL on failure.
10281 */
10282static int jx9Builtin_htmlspecialchars_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
10283{
10284 const char *zCur, *zIn, *zEnd;
10285 int iFlags = 0x01; /* ENT_COMPAT */
10286 int nLen, nJump;
10287 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
10288 /* Missing/Invalid arguments, return NULL */
10289 jx9_result_null(pCtx);
10290 return JX9_OK;
10291 }
10292 /* Extract the target string */
10293 zIn = jx9_value_to_string(apArg[0], &nLen);
10294 zEnd = &zIn[nLen];
10295 /* Extract the flags if available */
10296 if( nArg > 1 ){
10297 iFlags = jx9_value_to_int(apArg[1]);
10298 if( iFlags < 0 ){
10299 iFlags = 0x01;
10300 }
10301 }
10302 /* Perform the requested operation */
10303 for(;;){
10304 if( zIn >= zEnd ){
10305 break;
10306 }
10307 zCur = zIn;
10308 while( zIn < zEnd && zIn[0] != '&' ){
10309 zIn++;
10310 }
10311 if( zCur < zIn ){
10312 /* Append the raw string verbatim */
10313 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
10314 }
10315 nLen = (int)(zEnd-zIn);
10316 nJump = (int)sizeof(char);
10317 if( nLen >= (int)sizeof("&amp;")-1 && SyStrnicmp(zIn, "&amp;", sizeof("&amp;")-1) == 0 ){
10318 /* &amp; ==> '&' */
10319 jx9_result_string(pCtx, "&", (int)sizeof(char));
10320 nJump = (int)sizeof("&amp;")-1;
10321 }else if( nLen >= (int)sizeof("&lt;")-1 && SyStrnicmp(zIn, "&lt;", sizeof("&lt;")-1) == 0 ){
10322 /* &lt; ==> < */
10323 jx9_result_string(pCtx, "<", (int)sizeof(char));
10324 nJump = (int)sizeof("&lt;")-1;
10325 }else if( nLen >= (int)sizeof("&gt;")-1 && SyStrnicmp(zIn, "&gt;", sizeof("&gt;")-1) == 0 ){
10326 /* &gt; ==> '>' */
10327 jx9_result_string(pCtx, ">", (int)sizeof(char));
10328 nJump = (int)sizeof("&gt;")-1;
10329 }else if( nLen >= (int)sizeof("&quot;")-1 && SyStrnicmp(zIn, "&quot;", sizeof("&quot;")-1) == 0 ){
10330 /* &quot; ==> '"' */
10331 if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
10332 jx9_result_string(pCtx, "\"", (int)sizeof(char));
10333 }else{
10334 /* Leave untouched */
10335 jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
10336 }
10337 nJump = (int)sizeof("&quot;")-1;
10338 }else if( nLen >= (int)sizeof("&#039;")-1 && SyStrnicmp(zIn, "&#039;", sizeof("&#039;")-1) == 0 ){
10339 /* &#039; ==> ''' */
10340 if( iFlags & 0x02 /*ENT_QUOTES*/ ){
10341 /* Expand ''' */
10342 jx9_result_string(pCtx, "'", (int)sizeof(char));
10343 }else{
10344 /* Leave untouched */
10345 jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
10346 }
10347 nJump = (int)sizeof("&#039;")-1;
10348 }else if( nLen >= (int)sizeof(char) ){
10349 /* expand '&' */
10350 jx9_result_string(pCtx, "&", (int)sizeof(char));
10351 }else{
10352 /* No more input to process */
10353 break;
10354 }
10355 zIn += nJump;
10356 }
10357 return JX9_OK;
10358}
10359/* HTML encoding/Decoding table
10360 * Source: Symisc RunTime API.[chm@symisc.net]
10361 */
10362static const char *azHtmlEscape[] = {
10363 "&lt;", "<", "&gt;", ">", "&amp;", "&", "&quot;", "\"", "&#39;", "'",
10364 "&#33;", "!", "&#36;", "$", "&#35;", "#", "&#37;", "%", "&#40;", "(",
10365 "&#41;", ")", "&#123;", "{", "&#125;", "}", "&#61;", "=", "&#43;", "+",
10366 "&#63;", "?", "&#91;", "[", "&#93;", "]", "&#64;", "@", "&#44;", ","
10367 };
10368/*
10369 * array get_html_translation_table(void)
10370 * Returns the translation table used by htmlspecialchars() and htmlentities().
10371 * Parameters
10372 * None
10373 * Return
10374 * The translation table as an array or NULL on failure.
10375 */
10376static int jx9Builtin_get_html_translation_table(jx9_context *pCtx, int nArg, jx9_value **apArg)
10377{
10378 jx9_value *pArray, *pValue;
10379 sxu32 n;
10380 /* Element value */
10381 pValue = jx9_context_new_scalar(pCtx);
10382 if( pValue == 0 ){
10383 SXUNUSED(nArg); /* cc warning */
10384 SXUNUSED(apArg);
10385 /* Return NULL */
10386 jx9_result_null(pCtx);
10387 return JX9_OK;
10388 }
10389 /* Create a new array */
10390 pArray = jx9_context_new_array(pCtx);
10391 if( pArray == 0 ){
10392 /* Return NULL */
10393 jx9_result_null(pCtx);
10394 return JX9_OK;
10395 }
10396 /* Make the table */
10397 for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
10398 /* Prepare the value */
10399 jx9_value_string(pValue, azHtmlEscape[n], -1 /* Compute length automatically */);
10400 /* Insert the value */
10401 jx9_array_add_strkey_elem(pArray, azHtmlEscape[n+1], pValue);
10402 /* Reset the string cursor */
10403 jx9_value_reset_string_cursor(pValue);
10404 }
10405 /*
10406 * Return the array.
10407 * Don't worry about freeing memory, everything will be automatically
10408 * released upon we return from this function.
10409 */
10410 jx9_result_value(pCtx, pArray);
10411 return JX9_OK;
10412}
10413/*
10414 * string htmlentities( string $string [, int $flags = ENT_COMPAT | ENT_HTML401]);
10415 * Convert all applicable characters to HTML entities
10416 * Parameters
10417 * $string
10418 * The input string.
10419 * $flags
10420 * A bitmask of one or more of the flags (see block-comment on jx9Builtin_htmlspecialchars())
10421 * Return
10422 * The encoded string.
10423 */
10424static int jx9Builtin_htmlentities(jx9_context *pCtx, int nArg, jx9_value **apArg)
10425{
10426 int iFlags = 0x01; /* ENT_COMPAT */
10427 const char *zIn, *zEnd;
10428 int nLen, c;
10429 sxu32 n;
10430 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
10431 /* Missing/Invalid arguments, return NULL */
10432 jx9_result_null(pCtx);
10433 return JX9_OK;
10434 }
10435 /* Extract the target string */
10436 zIn = jx9_value_to_string(apArg[0], &nLen);
10437 zEnd = &zIn[nLen];
10438 /* Extract the flags if available */
10439 if( nArg > 1 ){
10440 iFlags = jx9_value_to_int(apArg[1]);
10441 if( iFlags < 0 ){
10442 iFlags = 0x01;
10443 }
10444 }
10445 /* Perform the requested operation */
10446 for(;;){
10447 if( zIn >= zEnd ){
10448 /* No more input to process */
10449 break;
10450 }
10451 c = zIn[0];
10452 /* Perform a linear lookup on the decoding table */
10453 for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
10454 if( azHtmlEscape[n+1][0] == c ){
10455 /* Got one */
10456 break;
10457 }
10458 }
10459 if( n < SX_ARRAYSIZE(azHtmlEscape) ){
10460 /* Output the safe sequence [i.e: '<' ==> '&lt;"] */
10461 if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
10462 /* Expand the double quote verbatim */
10463 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
10464 }else if(c == '\'' && ((iFlags & 0x02 /*ENT_QUOTES*/) == 0 || (iFlags & 0x04) /*ENT_NOQUOTES*/) ){
10465 /* expand single quote verbatim */
10466 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
10467 }else{
10468 jx9_result_string(pCtx, azHtmlEscape[n], -1/*Compute length automatically */);
10469 }
10470 }else{
10471 /* Output character verbatim */
10472 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
10473 }
10474 zIn++;
10475 }
10476 return JX9_OK;
10477}
10478/*
10479 * string html_entity_decode(string $string [, int $quote_style = ENT_COMPAT [, string $charset = 'UTF-8' ]])
10480 * Perform the reverse operation of html_entity_decode().
10481 * Parameters
10482 * $string
10483 * The input string.
10484 * $flags
10485 * A bitmask of one or more of the flags (see comment on jx9Builtin_htmlspecialchars())
10486 * Return
10487 * The decoded string.
10488 */
10489static int jx9Builtin_html_entity_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
10490{
10491 const char *zCur, *zIn, *zEnd;
10492 int iFlags = 0x01; /* ENT_COMPAT */
10493 int nLen;
10494 sxu32 n;
10495 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
10496 /* Missing/Invalid arguments, return NULL */
10497 jx9_result_null(pCtx);
10498 return JX9_OK;
10499 }
10500 /* Extract the target string */
10501 zIn = jx9_value_to_string(apArg[0], &nLen);
10502 zEnd = &zIn[nLen];
10503 /* Extract the flags if available */
10504 if( nArg > 1 ){
10505 iFlags = jx9_value_to_int(apArg[1]);
10506 if( iFlags < 0 ){
10507 iFlags = 0x01;
10508 }
10509 }
10510 /* Perform the requested operation */
10511 for(;;){
10512 if( zIn >= zEnd ){
10513 /* No more input to process */
10514 break;
10515 }
10516 zCur = zIn;
10517 while( zIn < zEnd && zIn[0] != '&' ){
10518 zIn++;
10519 }
10520 if( zCur < zIn ){
10521 /* Append raw string verbatim */
10522 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
10523 }
10524 if( zIn >= zEnd ){
10525 break;
10526 }
10527 nLen = (int)(zEnd-zIn);
10528 /* Find an encoded sequence */
10529 for(n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
10530 int iLen = (int)SyStrlen(azHtmlEscape[n]);
10531 if( nLen >= iLen && SyStrnicmp(zIn, azHtmlEscape[n], (sxu32)iLen) == 0 ){
10532 /* Got one */
10533 zIn += iLen;
10534 break;
10535 }
10536 }
10537 if( n < SX_ARRAYSIZE(azHtmlEscape) ){
10538 int c = azHtmlEscape[n+1][0];
10539 /* Output the decoded character */
10540 if( c == '\'' && ((iFlags & 0x02) == 0 /*ENT_QUOTES*/|| (iFlags & 0x04) /*ENT_NOQUOTES*/) ){
10541 /* Do not process single quotes */
10542 jx9_result_string(pCtx, azHtmlEscape[n], -1);
10543 }else if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
10544 /* Do not process double quotes */
10545 jx9_result_string(pCtx, azHtmlEscape[n], -1);
10546 }else{
10547 jx9_result_string(pCtx, azHtmlEscape[n+1], -1); /* Compute length automatically */
10548 }
10549 }else{
10550 /* Append '&' */
10551 jx9_result_string(pCtx, "&", (int)sizeof(char));
10552 zIn++;
10553 }
10554 }
10555 return JX9_OK;
10556}
10557/*
10558 * int strlen($string)
10559 * return the length of the given string.
10560 * Parameter
10561 * string: The string being measured for length.
10562 * Return
10563 * length of the given string.
10564 */
10565static int jx9Builtin_strlen(jx9_context *pCtx, int nArg, jx9_value **apArg)
10566{
10567 int iLen = 0;
10568 if( nArg > 0 ){
10569 jx9_value_to_string(apArg[0], &iLen);
10570 }
10571 /* String length */
10572 jx9_result_int(pCtx, iLen);
10573 return JX9_OK;
10574}
10575/*
10576 * int strcmp(string $str1, string $str2)
10577 * Perform a binary safe string comparison.
10578 * Parameter
10579 * str1: The first string
10580 * str2: The second string
10581 * Return
10582 * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
10583 * than str2, and 0 if they are equal.
10584 */
10585static int jx9Builtin_strcmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
10586{
10587 const char *z1, *z2;
10588 int n1, n2;
10589 int res;
10590 if( nArg < 2 ){
10591 res = nArg == 0 ? 0 : 1;
10592 jx9_result_int(pCtx, res);
10593 return JX9_OK;
10594 }
10595 /* Perform the comparison */
10596 z1 = jx9_value_to_string(apArg[0], &n1);
10597 z2 = jx9_value_to_string(apArg[1], &n2);
10598 res = SyStrncmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
10599 /* Comparison result */
10600 jx9_result_int(pCtx, res);
10601 return JX9_OK;
10602}
10603/*
10604 * int strncmp(string $str1, string $str2, int n)
10605 * Perform a binary safe string comparison of the first n characters.
10606 * Parameter
10607 * str1: The first string
10608 * str2: The second string
10609 * Return
10610 * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
10611 * than str2, and 0 if they are equal.
10612 */
10613static int jx9Builtin_strncmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
10614{
10615 const char *z1, *z2;
10616 int res;
10617 int n;
10618 if( nArg < 3 ){
10619 /* Perform a standard comparison */
10620 return jx9Builtin_strcmp(pCtx, nArg, apArg);
10621 }
10622 /* Desired comparison length */
10623 n = jx9_value_to_int(apArg[2]);
10624 if( n < 0 ){
10625 /* Invalid length */
10626 jx9_result_int(pCtx, -1);
10627 return JX9_OK;
10628 }
10629 /* Perform the comparison */
10630 z1 = jx9_value_to_string(apArg[0], 0);
10631 z2 = jx9_value_to_string(apArg[1], 0);
10632 res = SyStrncmp(z1, z2, (sxu32)n);
10633 /* Comparison result */
10634 jx9_result_int(pCtx, res);
10635 return JX9_OK;
10636}
10637/*
10638 * int strcasecmp(string $str1, string $str2, int n)
10639 * Perform a binary safe case-insensitive string comparison.
10640 * Parameter
10641 * str1: The first string
10642 * str2: The second string
10643 * Return
10644 * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
10645 * than str2, and 0 if they are equal.
10646 */
10647static int jx9Builtin_strcasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
10648{
10649 const char *z1, *z2;
10650 int n1, n2;
10651 int res;
10652 if( nArg < 2 ){
10653 res = nArg == 0 ? 0 : 1;
10654 jx9_result_int(pCtx, res);
10655 return JX9_OK;
10656 }
10657 /* Perform the comparison */
10658 z1 = jx9_value_to_string(apArg[0], &n1);
10659 z2 = jx9_value_to_string(apArg[1], &n2);
10660 res = SyStrnicmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
10661 /* Comparison result */
10662 jx9_result_int(pCtx, res);
10663 return JX9_OK;
10664}
10665/*
10666 * int strncasecmp(string $str1, string $str2, int n)
10667 * Perform a binary safe case-insensitive string comparison of the first n characters.
10668 * Parameter
10669 * $str1: The first string
10670 * $str2: The second string
10671 * $len: The length of strings to be used in the comparison.
10672 * Return
10673 * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
10674 * than str2, and 0 if they are equal.
10675 */
10676static int jx9Builtin_strncasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
10677{
10678 const char *z1, *z2;
10679 int res;
10680 int n;
10681 if( nArg < 3 ){
10682 /* Perform a standard comparison */
10683 return jx9Builtin_strcasecmp(pCtx, nArg, apArg);
10684 }
10685 /* Desired comparison length */
10686 n = jx9_value_to_int(apArg[2]);
10687 if( n < 0 ){
10688 /* Invalid length */
10689 jx9_result_int(pCtx, -1);
10690 return JX9_OK;
10691 }
10692 /* Perform the comparison */
10693 z1 = jx9_value_to_string(apArg[0], 0);
10694 z2 = jx9_value_to_string(apArg[1], 0);
10695 res = SyStrnicmp(z1, z2, (sxu32)n);
10696 /* Comparison result */
10697 jx9_result_int(pCtx, res);
10698 return JX9_OK;
10699}
10700/*
10701 * Implode context [i.e: it's private data].
10702 * A pointer to the following structure is forwarded
10703 * verbatim to the array walker callback defined below.
10704 */
10705struct implode_data {
10706 jx9_context *pCtx; /* Call context */
10707 int bRecursive; /* TRUE if recursive implode [this is a symisc eXtension] */
10708 const char *zSep; /* Arguments separator if any */
10709 int nSeplen; /* Separator length */
10710 int bFirst; /* TRUE if first call */
10711 int nRecCount; /* Recursion count to avoid infinite loop */
10712};
10713/*
10714 * Implode walker callback for the [jx9_array_walk()] interface.
10715 * The following routine is invoked for each array entry passed
10716 * to the implode() function.
10717 */
10718static int implode_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
10719{
10720 struct implode_data *pData = (struct implode_data *)pUserData;
10721 const char *zData;
10722 int nLen;
10723 if( pData->bRecursive && jx9_value_is_json_array(pValue) && pData->nRecCount < 32 ){
10724 if( pData->nSeplen > 0 ){
10725 if( !pData->bFirst ){
10726 /* append the separator first */
10727 jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
10728 }else{
10729 pData->bFirst = 0;
10730 }
10731 }
10732 /* Recurse */
10733 pData->bFirst = 1;
10734 pData->nRecCount++;
10735 jx9HashmapWalk((jx9_hashmap *)pValue->x.pOther, implode_callback, pData);
10736 pData->nRecCount--;
10737 return JX9_OK;
10738 }
10739 /* Extract the string representation of the entry value */
10740 zData = jx9_value_to_string(pValue, &nLen);
10741 if( nLen > 0 ){
10742 if( pData->nSeplen > 0 ){
10743 if( !pData->bFirst ){
10744 /* append the separator first */
10745 jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
10746 }else{
10747 pData->bFirst = 0;
10748 }
10749 }
10750 jx9_result_string(pData->pCtx, zData, nLen);
10751 }else{
10752 SXUNUSED(pKey); /* cc warning */
10753 }
10754 return JX9_OK;
10755}
10756/*
10757 * string implode(string $glue, array $pieces, ...)
10758 * string implode(array $pieces, ...)
10759 * Join array elements with a string.
10760 * $glue
10761 * Defaults to an empty string. This is not the preferred usage of implode() as glue
10762 * would be the second parameter and thus, the bad prototype would be used.
10763 * $pieces
10764 * The array of strings to implode.
10765 * Return
10766 * Returns a string containing a string representation of all the array elements in the same
10767 * order, with the glue string between each element.
10768 */
10769static int jx9Builtin_implode(jx9_context *pCtx, int nArg, jx9_value **apArg)
10770{
10771 struct implode_data imp_data;
10772 int i = 1;
10773 if( nArg < 1 ){
10774 /* Missing argument, return NULL */
10775 jx9_result_null(pCtx);
10776 return JX9_OK;
10777 }
10778 /* Prepare the implode context */
10779 imp_data.pCtx = pCtx;
10780 imp_data.bRecursive = 0;
10781 imp_data.bFirst = 1;
10782 imp_data.nRecCount = 0;
10783 if( !jx9_value_is_json_array(apArg[0]) ){
10784 imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
10785 }else{
10786 imp_data.zSep = 0;
10787 imp_data.nSeplen = 0;
10788 i = 0;
10789 }
10790 jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
10791 /* Start the 'join' process */
10792 while( i < nArg ){
10793 if( jx9_value_is_json_array(apArg[i]) ){
10794 /* Iterate throw array entries */
10795 jx9_array_walk(apArg[i], implode_callback, &imp_data);
10796 }else{
10797 const char *zData;
10798 int nLen;
10799 /* Extract the string representation of the jx9 value */
10800 zData = jx9_value_to_string(apArg[i], &nLen);
10801 if( nLen > 0 ){
10802 if( imp_data.nSeplen > 0 ){
10803 if( !imp_data.bFirst ){
10804 /* append the separator first */
10805 jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
10806 }else{
10807 imp_data.bFirst = 0;
10808 }
10809 }
10810 jx9_result_string(pCtx, zData, nLen);
10811 }
10812 }
10813 i++;
10814 }
10815 return JX9_OK;
10816}
10817/*
10818 * string implode_recursive(string $glue, array $pieces, ...)
10819 * Purpose
10820 * Same as implode() but recurse on arrays.
10821 * Example:
10822 * $a = array('usr', array('home', 'dean'));
10823 * print implode_recursive("/", $a);
10824 * Will output
10825 * usr/home/dean.
10826 * While the standard implode would produce.
10827 * usr/Array.
10828 * Parameter
10829 * Refer to implode().
10830 * Return
10831 * Refer to implode().
10832 */
10833static int jx9Builtin_implode_recursive(jx9_context *pCtx, int nArg, jx9_value **apArg)
10834{
10835 struct implode_data imp_data;
10836 int i = 1;
10837 if( nArg < 1 ){
10838 /* Missing argument, return NULL */
10839 jx9_result_null(pCtx);
10840 return JX9_OK;
10841 }
10842 /* Prepare the implode context */
10843 imp_data.pCtx = pCtx;
10844 imp_data.bRecursive = 1;
10845 imp_data.bFirst = 1;
10846 imp_data.nRecCount = 0;
10847 if( !jx9_value_is_json_array(apArg[0]) ){
10848 imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
10849 }else{
10850 imp_data.zSep = 0;
10851 imp_data.nSeplen = 0;
10852 i = 0;
10853 }
10854 jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
10855 /* Start the 'join' process */
10856 while( i < nArg ){
10857 if( jx9_value_is_json_array(apArg[i]) ){
10858 /* Iterate throw array entries */
10859 jx9_array_walk(apArg[i], implode_callback, &imp_data);
10860 }else{
10861 const char *zData;
10862 int nLen;
10863 /* Extract the string representation of the jx9 value */
10864 zData = jx9_value_to_string(apArg[i], &nLen);
10865 if( nLen > 0 ){
10866 if( imp_data.nSeplen > 0 ){
10867 if( !imp_data.bFirst ){
10868 /* append the separator first */
10869 jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
10870 }else{
10871 imp_data.bFirst = 0;
10872 }
10873 }
10874 jx9_result_string(pCtx, zData, nLen);
10875 }
10876 }
10877 i++;
10878 }
10879 return JX9_OK;
10880}
10881/*
10882 * array explode(string $delimiter, string $string[, int $limit ])
10883 * Returns an array of strings, each of which is a substring of string
10884 * formed by splitting it on boundaries formed by the string delimiter.
10885 * Parameters
10886 * $delimiter
10887 * The boundary string.
10888 * $string
10889 * The input string.
10890 * $limit
10891 * If limit is set and positive, the returned array will contain a maximum
10892 * of limit elements with the last element containing the rest of string.
10893 * If the limit parameter is negative, all fields except the last -limit are returned.
10894 * If the limit parameter is zero, then this is treated as 1.
10895 * Returns
10896 * Returns an array of strings created by splitting the string parameter
10897 * on boundaries formed by the delimiter.
10898 * If delimiter is an empty string (""), explode() will return FALSE.
10899 * If delimiter contains a value that is not contained in string and a negative
10900 * limit is used, then an empty array will be returned, otherwise an array containing string
10901 * will be returned.
10902 * NOTE:
10903 * Negative limit is not supported.
10904 */
10905static int jx9Builtin_explode(jx9_context *pCtx, int nArg, jx9_value **apArg)
10906{
10907 const char *zDelim, *zString, *zCur, *zEnd;
10908 int nDelim, nStrlen, iLimit;
10909 jx9_value *pArray;
10910 jx9_value *pValue;
10911 sxu32 nOfft;
10912 sxi32 rc;
10913 if( nArg < 2 ){
10914 /* Missing arguments, return FALSE */
10915 jx9_result_bool(pCtx, 0);
10916 return JX9_OK;
10917 }
10918 /* Extract the delimiter */
10919 zDelim = jx9_value_to_string(apArg[0], &nDelim);
10920 if( nDelim < 1 ){
10921 /* Empty delimiter, return FALSE */
10922 jx9_result_bool(pCtx, 0);
10923 return JX9_OK;
10924 }
10925 /* Extract the string */
10926 zString = jx9_value_to_string(apArg[1], &nStrlen);
10927 if( nStrlen < 1 ){
10928 /* Empty delimiter, return FALSE */
10929 jx9_result_bool(pCtx, 0);
10930 return JX9_OK;
10931 }
10932 /* Point to the end of the string */
10933 zEnd = &zString[nStrlen];
10934 /* Create the array */
10935 pArray = jx9_context_new_array(pCtx);
10936 pValue = jx9_context_new_scalar(pCtx);
10937 if( pArray == 0 || pValue == 0 ){
10938 /* Out of memory, return FALSE */
10939 jx9_result_bool(pCtx, 0);
10940 return JX9_OK;
10941 }
10942 /* Set a defualt limit */
10943 iLimit = SXI32_HIGH;
10944 if( nArg > 2 ){
10945 iLimit = jx9_value_to_int(apArg[2]);
10946 if( iLimit < 0 ){
10947 iLimit = -iLimit;
10948 }
10949 if( iLimit == 0 ){
10950 iLimit = 1;
10951 }
10952 iLimit--;
10953 }
10954 /* Start exploding */
10955 for(;;){
10956 if( zString >= zEnd ){
10957 /* No more entry to process */
10958 break;
10959 }
10960 rc = SyBlobSearch(zString, (sxu32)(zEnd-zString), zDelim, nDelim, &nOfft);
10961 if( rc != SXRET_OK || iLimit <= (int)jx9_array_count(pArray) ){
10962 /* Limit reached, insert the rest of the string and break */
10963 if( zEnd > zString ){
10964 jx9_value_string(pValue, zString, (int)(zEnd-zString));
10965 jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
10966 }
10967 break;
10968 }
10969 /* Point to the desired offset */
10970 zCur = &zString[nOfft];
10971 if( zCur > zString ){
10972 /* Perform the store operation */
10973 jx9_value_string(pValue, zString, (int)(zCur-zString));
10974 jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
10975 }
10976 /* Point beyond the delimiter */
10977 zString = &zCur[nDelim];
10978 /* Reset the cursor */
10979 jx9_value_reset_string_cursor(pValue);
10980 }
10981 /* Return the freshly created array */
10982 jx9_result_value(pCtx, pArray);
10983 /* NOTE that every allocated jx9_value will be automatically
10984 * released as soon we return from this foregin function.
10985 */
10986 return JX9_OK;
10987}
10988/*
10989 * string trim(string $str[, string $charlist ])
10990 * Strip whitespace (or other characters) from the beginning and end of a string.
10991 * Parameters
10992 * $str
10993 * The string that will be trimmed.
10994 * $charlist
10995 * Optionally, the stripped characters can also be specified using the charlist parameter.
10996 * Simply list all characters that you want to be stripped.
10997 * With .. you can specify a range of characters.
10998 * Returns.
10999 * Thr processed string.
11000 */
11001static int jx9Builtin_trim(jx9_context *pCtx, int nArg, jx9_value **apArg)
11002{
11003 const char *zString;
11004 int nLen;
11005 if( nArg < 1 ){
11006 /* Missing arguments, return null */
11007 jx9_result_null(pCtx);
11008 return JX9_OK;
11009 }
11010 /* Extract the target string */
11011 zString = jx9_value_to_string(apArg[0], &nLen);
11012 if( nLen < 1 ){
11013 /* Empty string, return */
11014 jx9_result_string(pCtx, "", 0);
11015 return JX9_OK;
11016 }
11017 /* Start the trim process */
11018 if( nArg < 2 ){
11019 SyString sStr;
11020 /* Remove white spaces and NUL bytes */
11021 SyStringInitFromBuf(&sStr, zString, nLen);
11022 SyStringFullTrimSafe(&sStr);
11023 jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
11024 }else{
11025 /* Char list */
11026 const char *zList;
11027 int nListlen;
11028 zList = jx9_value_to_string(apArg[1], &nListlen);
11029 if( nListlen < 1 ){
11030 /* Return the string unchanged */
11031 jx9_result_string(pCtx, zString, nLen);
11032 }else{
11033 const char *zEnd = &zString[nLen];
11034 const char *zCur = zString;
11035 const char *zPtr;
11036 int i;
11037 /* Left trim */
11038 for(;;){
11039 if( zCur >= zEnd ){
11040 break;
11041 }
11042 zPtr = zCur;
11043 for( i = 0 ; i < nListlen ; i++ ){
11044 if( zCur < zEnd && zCur[0] == zList[i] ){
11045 zCur++;
11046 }
11047 }
11048 if( zCur == zPtr ){
11049 /* No match, break immediately */
11050 break;
11051 }
11052 }
11053 /* Right trim */
11054 zEnd--;
11055 for(;;){
11056 if( zEnd <= zCur ){
11057 break;
11058 }
11059 zPtr = zEnd;
11060 for( i = 0 ; i < nListlen ; i++ ){
11061 if( zEnd > zCur && zEnd[0] == zList[i] ){
11062 zEnd--;
11063 }
11064 }
11065 if( zEnd == zPtr ){
11066 break;
11067 }
11068 }
11069 if( zCur >= zEnd ){
11070 /* Return the empty string */
11071 jx9_result_string(pCtx, "", 0);
11072 }else{
11073 zEnd++;
11074 jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
11075 }
11076 }
11077 }
11078 return JX9_OK;
11079}
11080/*
11081 * string rtrim(string $str[, string $charlist ])
11082 * Strip whitespace (or other characters) from the end of a string.
11083 * Parameters
11084 * $str
11085 * The string that will be trimmed.
11086 * $charlist
11087 * Optionally, the stripped characters can also be specified using the charlist parameter.
11088 * Simply list all characters that you want to be stripped.
11089 * With .. you can specify a range of characters.
11090 * Returns.
11091 * Thr processed string.
11092 */
11093static int jx9Builtin_rtrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
11094{
11095 const char *zString;
11096 int nLen;
11097 if( nArg < 1 ){
11098 /* Missing arguments, return null */
11099 jx9_result_null(pCtx);
11100 return JX9_OK;
11101 }
11102 /* Extract the target string */
11103 zString = jx9_value_to_string(apArg[0], &nLen);
11104 if( nLen < 1 ){
11105 /* Empty string, return */
11106 jx9_result_string(pCtx, "", 0);
11107 return JX9_OK;
11108 }
11109 /* Start the trim process */
11110 if( nArg < 2 ){
11111 SyString sStr;
11112 /* Remove white spaces and NUL bytes*/
11113 SyStringInitFromBuf(&sStr, zString, nLen);
11114 SyStringRightTrimSafe(&sStr);
11115 jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
11116 }else{
11117 /* Char list */
11118 const char *zList;
11119 int nListlen;
11120 zList = jx9_value_to_string(apArg[1], &nListlen);
11121 if( nListlen < 1 ){
11122 /* Return the string unchanged */
11123 jx9_result_string(pCtx, zString, nLen);
11124 }else{
11125 const char *zEnd = &zString[nLen - 1];
11126 const char *zCur = zString;
11127 const char *zPtr;
11128 int i;
11129 /* Right trim */
11130 for(;;){
11131 if( zEnd <= zCur ){
11132 break;
11133 }
11134 zPtr = zEnd;
11135 for( i = 0 ; i < nListlen ; i++ ){
11136 if( zEnd > zCur && zEnd[0] == zList[i] ){
11137 zEnd--;
11138 }
11139 }
11140 if( zEnd == zPtr ){
11141 break;
11142 }
11143 }
11144 if( zEnd <= zCur ){
11145 /* Return the empty string */
11146 jx9_result_string(pCtx, "", 0);
11147 }else{
11148 zEnd++;
11149 jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
11150 }
11151 }
11152 }
11153 return JX9_OK;
11154}
11155/*
11156 * string ltrim(string $str[, string $charlist ])
11157 * Strip whitespace (or other characters) from the beginning and end of a string.
11158 * Parameters
11159 * $str
11160 * The string that will be trimmed.
11161 * $charlist
11162 * Optionally, the stripped characters can also be specified using the charlist parameter.
11163 * Simply list all characters that you want to be stripped.
11164 * With .. you can specify a range of characters.
11165 * Returns.
11166 * The processed string.
11167 */
11168static int jx9Builtin_ltrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
11169{
11170 const char *zString;
11171 int nLen;
11172 if( nArg < 1 ){
11173 /* Missing arguments, return null */
11174 jx9_result_null(pCtx);
11175 return JX9_OK;
11176 }
11177 /* Extract the target string */
11178 zString = jx9_value_to_string(apArg[0], &nLen);
11179 if( nLen < 1 ){
11180 /* Empty string, return */
11181 jx9_result_string(pCtx, "", 0);
11182 return JX9_OK;
11183 }
11184 /* Start the trim process */
11185 if( nArg < 2 ){
11186 SyString sStr;
11187 /* Remove white spaces and NUL byte */
11188 SyStringInitFromBuf(&sStr, zString, nLen);
11189 SyStringLeftTrimSafe(&sStr);
11190 jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
11191 }else{
11192 /* Char list */
11193 const char *zList;
11194 int nListlen;
11195 zList = jx9_value_to_string(apArg[1], &nListlen);
11196 if( nListlen < 1 ){
11197 /* Return the string unchanged */
11198 jx9_result_string(pCtx, zString, nLen);
11199 }else{
11200 const char *zEnd = &zString[nLen];
11201 const char *zCur = zString;
11202 const char *zPtr;
11203 int i;
11204 /* Left trim */
11205 for(;;){
11206 if( zCur >= zEnd ){
11207 break;
11208 }
11209 zPtr = zCur;
11210 for( i = 0 ; i < nListlen ; i++ ){
11211 if( zCur < zEnd && zCur[0] == zList[i] ){
11212 zCur++;
11213 }
11214 }
11215 if( zCur == zPtr ){
11216 /* No match, break immediately */
11217 break;
11218 }
11219 }
11220 if( zCur >= zEnd ){
11221 /* Return the empty string */
11222 jx9_result_string(pCtx, "", 0);
11223 }else{
11224 jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
11225 }
11226 }
11227 }
11228 return JX9_OK;
11229}
11230/*
11231 * string strtolower(string $str)
11232 * Make a string lowercase.
11233 * Parameters
11234 * $str
11235 * The input string.
11236 * Returns.
11237 * The lowercased string.
11238 */
11239static int jx9Builtin_strtolower(jx9_context *pCtx, int nArg, jx9_value **apArg)
11240{
11241 const char *zString, *zCur, *zEnd;
11242 int nLen;
11243 if( nArg < 1 ){
11244 /* Missing arguments, return null */
11245 jx9_result_null(pCtx);
11246 return JX9_OK;
11247 }
11248 /* Extract the target string */
11249 zString = jx9_value_to_string(apArg[0], &nLen);
11250 if( nLen < 1 ){
11251 /* Empty string, return */
11252 jx9_result_string(pCtx, "", 0);
11253 return JX9_OK;
11254 }
11255 /* Perform the requested operation */
11256 zEnd = &zString[nLen];
11257 for(;;){
11258 if( zString >= zEnd ){
11259 /* No more input, break immediately */
11260 break;
11261 }
11262 if( (unsigned char)zString[0] >= 0xc0 ){
11263 /* UTF-8 stream, output verbatim */
11264 zCur = zString;
11265 zString++;
11266 while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
11267 zString++;
11268 }
11269 /* Append UTF-8 stream */
11270 jx9_result_string(pCtx, zCur, (int)(zString-zCur));
11271 }else{
11272 int c = zString[0];
11273 if( SyisUpper(c) ){
11274 c = SyToLower(zString[0]);
11275 }
11276 /* Append character */
11277 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
11278 /* Advance the cursor */
11279 zString++;
11280 }
11281 }
11282 return JX9_OK;
11283}
11284/*
11285 * string strtolower(string $str)
11286 * Make a string uppercase.
11287 * Parameters
11288 * $str
11289 * The input string.
11290 * Returns.
11291 * The uppercased string.
11292 */
11293static int jx9Builtin_strtoupper(jx9_context *pCtx, int nArg, jx9_value **apArg)
11294{
11295 const char *zString, *zCur, *zEnd;
11296 int nLen;
11297 if( nArg < 1 ){
11298 /* Missing arguments, return null */
11299 jx9_result_null(pCtx);
11300 return JX9_OK;
11301 }
11302 /* Extract the target string */
11303 zString = jx9_value_to_string(apArg[0], &nLen);
11304 if( nLen < 1 ){
11305 /* Empty string, return */
11306 jx9_result_string(pCtx, "", 0);
11307 return JX9_OK;
11308 }
11309 /* Perform the requested operation */
11310 zEnd = &zString[nLen];
11311 for(;;){
11312 if( zString >= zEnd ){
11313 /* No more input, break immediately */
11314 break;
11315 }
11316 if( (unsigned char)zString[0] >= 0xc0 ){
11317 /* UTF-8 stream, output verbatim */
11318 zCur = zString;
11319 zString++;
11320 while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
11321 zString++;
11322 }
11323 /* Append UTF-8 stream */
11324 jx9_result_string(pCtx, zCur, (int)(zString-zCur));
11325 }else{
11326 int c = zString[0];
11327 if( SyisLower(c) ){
11328 c = SyToUpper(zString[0]);
11329 }
11330 /* Append character */
11331 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
11332 /* Advance the cursor */
11333 zString++;
11334 }
11335 }
11336 return JX9_OK;
11337}
11338/*
11339 * int ord(string $string)
11340 * Returns the ASCII value of the first character of string.
11341 * Parameters
11342 * $str
11343 * The input string.
11344 * Returns.
11345 * The ASCII value as an integer.
11346 */
11347static int jx9Builtin_ord(jx9_context *pCtx, int nArg, jx9_value **apArg)
11348{
11349 const char *zString;
11350 int nLen, c;
11351 if( nArg < 1 ){
11352 /* Missing arguments, return -1 */
11353 jx9_result_int(pCtx, -1);
11354 return JX9_OK;
11355 }
11356 /* Extract the target string */
11357 zString = jx9_value_to_string(apArg[0], &nLen);
11358 if( nLen < 1 ){
11359 /* Empty string, return -1 */
11360 jx9_result_int(pCtx, -1);
11361 return JX9_OK;
11362 }
11363 /* Extract the ASCII value of the first character */
11364 c = zString[0];
11365 /* Return that value */
11366 jx9_result_int(pCtx, c);
11367 return JX9_OK;
11368}
11369/*
11370 * string chr(int $ascii)
11371 * Returns a one-character string containing the character specified by ascii.
11372 * Parameters
11373 * $ascii
11374 * The ascii code.
11375 * Returns.
11376 * The specified character.
11377 */
11378static int jx9Builtin_chr(jx9_context *pCtx, int nArg, jx9_value **apArg)
11379{
11380 int c;
11381 if( nArg < 1 ){
11382 /* Missing arguments, return null */
11383 jx9_result_null(pCtx);
11384 return JX9_OK;
11385 }
11386 /* Extract the ASCII value */
11387 c = jx9_value_to_int(apArg[0]);
11388 /* Return the specified character */
11389 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
11390 return JX9_OK;
11391}
11392/*
11393 * Binary to hex consumer callback.
11394 * This callback is the default consumer used by the hash functions
11395 * [i.e: bin2hex(), md5(), sha1(), md5_file() ... ] defined below.
11396 */
11397static int HashConsumer(const void *pData, unsigned int nLen, void *pUserData)
11398{
11399 /* Append hex chunk verbatim */
11400 jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
11401 return SXRET_OK;
11402}
11403/*
11404 * string bin2hex(string $str)
11405 * Convert binary data into hexadecimal representation.
11406 * Parameters
11407 * $str
11408 * The input string.
11409 * Returns.
11410 * Returns the hexadecimal representation of the given string.
11411 */
11412static int jx9Builtin_bin2hex(jx9_context *pCtx, int nArg, jx9_value **apArg)
11413{
11414 const char *zString;
11415 int nLen;
11416 if( nArg < 1 ){
11417 /* Missing arguments, return null */
11418 jx9_result_null(pCtx);
11419 return JX9_OK;
11420 }
11421 /* Extract the target string */
11422 zString = jx9_value_to_string(apArg[0], &nLen);
11423 if( nLen < 1 ){
11424 /* Empty string, return */
11425 jx9_result_string(pCtx, "", 0);
11426 return JX9_OK;
11427 }
11428 /* Perform the requested operation */
11429 SyBinToHexConsumer((const void *)zString, (sxu32)nLen, HashConsumer, pCtx);
11430 return JX9_OK;
11431}
11432/* Search callback signature */
11433typedef sxi32 (*ProcStringMatch)(const void *, sxu32, const void *, sxu32, sxu32 *);
11434/*
11435 * Case-insensitive pattern match.
11436 * Brute force is the default search method used here.
11437 * This is due to the fact that brute-forcing works quite
11438 * well for short/medium texts on modern hardware.
11439 */
11440static sxi32 iPatternMatch(const void *pText, sxu32 nLen, const void *pPattern, sxu32 iPatLen, sxu32 *pOfft)
11441{
11442 const char *zpIn = (const char *)pPattern;
11443 const char *zIn = (const char *)pText;
11444 const char *zpEnd = &zpIn[iPatLen];
11445 const char *zEnd = &zIn[nLen];
11446 const char *zPtr, *zPtr2;
11447 int c, d;
11448 if( iPatLen > nLen ){
11449 /* Don't bother processing */
11450 return SXERR_NOTFOUND;
11451 }
11452 for(;;){
11453 if( zIn >= zEnd ){
11454 break;
11455 }
11456 c = SyToLower(zIn[0]);
11457 d = SyToLower(zpIn[0]);
11458 if( c == d ){
11459 zPtr = &zIn[1];
11460 zPtr2 = &zpIn[1];
11461 for(;;){
11462 if( zPtr2 >= zpEnd ){
11463 /* Pattern found */
11464 if( pOfft ){ *pOfft = (sxu32)(zIn-(const char *)pText); }
11465 return SXRET_OK;
11466 }
11467 if( zPtr >= zEnd ){
11468 break;
11469 }
11470 c = SyToLower(zPtr[0]);
11471 d = SyToLower(zPtr2[0]);
11472 if( c != d ){
11473 break;
11474 }
11475 zPtr++; zPtr2++;
11476 }
11477 }
11478 zIn++;
11479 }
11480 /* Pattern not found */
11481 return SXERR_NOTFOUND;
11482}
11483/*
11484 * string strstr(string $haystack, string $needle[, bool $before_needle = false ])
11485 * Find the first occurrence of a string.
11486 * Parameters
11487 * $haystack
11488 * The input string.
11489 * $needle
11490 * Search pattern (must be a string).
11491 * $before_needle
11492 * If TRUE, strstr() returns the part of the haystack before the first occurrence
11493 * of the needle (excluding the needle).
11494 * Return
11495 * Returns the portion of string, or FALSE if needle is not found.
11496 */
11497static int jx9Builtin_strstr(jx9_context *pCtx, int nArg, jx9_value **apArg)
11498{
11499 ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
11500 const char *zBlob, *zPattern;
11501 int nLen, nPatLen;
11502 sxu32 nOfft;
11503 sxi32 rc;
11504 if( nArg < 2 ){
11505 /* Missing arguments, return FALSE */
11506 jx9_result_bool(pCtx, 0);
11507 return JX9_OK;
11508 }
11509 /* Extract the needle and the haystack */
11510 zBlob = jx9_value_to_string(apArg[0], &nLen);
11511 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11512 nOfft = 0; /* cc warning */
11513 if( nLen > 0 && nPatLen > 0 ){
11514 int before = 0;
11515 /* Perform the lookup */
11516 rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
11517 if( rc != SXRET_OK ){
11518 /* Pattern not found, return FALSE */
11519 jx9_result_bool(pCtx, 0);
11520 return JX9_OK;
11521 }
11522 /* Return the portion of the string */
11523 if( nArg > 2 ){
11524 before = jx9_value_to_int(apArg[2]);
11525 }
11526 if( before ){
11527 jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
11528 }else{
11529 jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
11530 }
11531 }else{
11532 jx9_result_bool(pCtx, 0);
11533 }
11534 return JX9_OK;
11535}
11536/*
11537 * string stristr(string $haystack, string $needle[, bool $before_needle = false ])
11538 * Case-insensitive strstr().
11539 * Parameters
11540 * $haystack
11541 * The input string.
11542 * $needle
11543 * Search pattern (must be a string).
11544 * $before_needle
11545 * If TRUE, strstr() returns the part of the haystack before the first occurrence
11546 * of the needle (excluding the needle).
11547 * Return
11548 * Returns the portion of string, or FALSE if needle is not found.
11549 */
11550static int jx9Builtin_stristr(jx9_context *pCtx, int nArg, jx9_value **apArg)
11551{
11552 ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
11553 const char *zBlob, *zPattern;
11554 int nLen, nPatLen;
11555 sxu32 nOfft;
11556 sxi32 rc;
11557 if( nArg < 2 ){
11558 /* Missing arguments, return FALSE */
11559 jx9_result_bool(pCtx, 0);
11560 return JX9_OK;
11561 }
11562 /* Extract the needle and the haystack */
11563 zBlob = jx9_value_to_string(apArg[0], &nLen);
11564 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11565 nOfft = 0; /* cc warning */
11566 if( nLen > 0 && nPatLen > 0 ){
11567 int before = 0;
11568 /* Perform the lookup */
11569 rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
11570 if( rc != SXRET_OK ){
11571 /* Pattern not found, return FALSE */
11572 jx9_result_bool(pCtx, 0);
11573 return JX9_OK;
11574 }
11575 /* Return the portion of the string */
11576 if( nArg > 2 ){
11577 before = jx9_value_to_int(apArg[2]);
11578 }
11579 if( before ){
11580 jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
11581 }else{
11582 jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
11583 }
11584 }else{
11585 jx9_result_bool(pCtx, 0);
11586 }
11587 return JX9_OK;
11588}
11589/*
11590 * int strpos(string $haystack, string $needle [, int $offset = 0 ] )
11591 * Returns the numeric position of the first occurrence of needle in the haystack string.
11592 * Parameters
11593 * $haystack
11594 * The input string.
11595 * $needle
11596 * Search pattern (must be a string).
11597 * $offset
11598 * This optional offset parameter allows you to specify which character in haystack
11599 * to start searching. The position returned is still relative to the beginning
11600 * of haystack.
11601 * Return
11602 * Returns the position as an integer.If needle is not found, strpos() will return FALSE.
11603 */
11604static int jx9Builtin_strpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
11605{
11606 ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
11607 const char *zBlob, *zPattern;
11608 int nLen, nPatLen, nStart;
11609 sxu32 nOfft;
11610 sxi32 rc;
11611 if( nArg < 2 ){
11612 /* Missing arguments, return FALSE */
11613 jx9_result_bool(pCtx, 0);
11614 return JX9_OK;
11615 }
11616 /* Extract the needle and the haystack */
11617 zBlob = jx9_value_to_string(apArg[0], &nLen);
11618 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11619 nOfft = 0; /* cc warning */
11620 nStart = 0;
11621 /* Peek the starting offset if available */
11622 if( nArg > 2 ){
11623 nStart = jx9_value_to_int(apArg[2]);
11624 if( nStart < 0 ){
11625 nStart = -nStart;
11626 }
11627 if( nStart >= nLen ){
11628 /* Invalid offset */
11629 nStart = 0;
11630 }else{
11631 zBlob += nStart;
11632 nLen -= nStart;
11633 }
11634 }
11635 if( nLen > 0 && nPatLen > 0 ){
11636 /* Perform the lookup */
11637 rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
11638 if( rc != SXRET_OK ){
11639 /* Pattern not found, return FALSE */
11640 jx9_result_bool(pCtx, 0);
11641 return JX9_OK;
11642 }
11643 /* Return the pattern position */
11644 jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
11645 }else{
11646 jx9_result_bool(pCtx, 0);
11647 }
11648 return JX9_OK;
11649}
11650/*
11651 * int stripos(string $haystack, string $needle [, int $offset = 0 ] )
11652 * Case-insensitive strpos.
11653 * Parameters
11654 * $haystack
11655 * The input string.
11656 * $needle
11657 * Search pattern (must be a string).
11658 * $offset
11659 * This optional offset parameter allows you to specify which character in haystack
11660 * to start searching. The position returned is still relative to the beginning
11661 * of haystack.
11662 * Return
11663 * Returns the position as an integer.If needle is not found, strpos() will return FALSE.
11664 */
11665static int jx9Builtin_stripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
11666{
11667 ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
11668 const char *zBlob, *zPattern;
11669 int nLen, nPatLen, nStart;
11670 sxu32 nOfft;
11671 sxi32 rc;
11672 if( nArg < 2 ){
11673 /* Missing arguments, return FALSE */
11674 jx9_result_bool(pCtx, 0);
11675 return JX9_OK;
11676 }
11677 /* Extract the needle and the haystack */
11678 zBlob = jx9_value_to_string(apArg[0], &nLen);
11679 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11680 nOfft = 0; /* cc warning */
11681 nStart = 0;
11682 /* Peek the starting offset if available */
11683 if( nArg > 2 ){
11684 nStart = jx9_value_to_int(apArg[2]);
11685 if( nStart < 0 ){
11686 nStart = -nStart;
11687 }
11688 if( nStart >= nLen ){
11689 /* Invalid offset */
11690 nStart = 0;
11691 }else{
11692 zBlob += nStart;
11693 nLen -= nStart;
11694 }
11695 }
11696 if( nLen > 0 && nPatLen > 0 ){
11697 /* Perform the lookup */
11698 rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
11699 if( rc != SXRET_OK ){
11700 /* Pattern not found, return FALSE */
11701 jx9_result_bool(pCtx, 0);
11702 return JX9_OK;
11703 }
11704 /* Return the pattern position */
11705 jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
11706 }else{
11707 jx9_result_bool(pCtx, 0);
11708 }
11709 return JX9_OK;
11710}
11711/*
11712 * int strrpos(string $haystack, string $needle [, int $offset = 0 ] )
11713 * Find the numeric position of the last occurrence of needle in the haystack string.
11714 * Parameters
11715 * $haystack
11716 * The input string.
11717 * $needle
11718 * Search pattern (must be a string).
11719 * $offset
11720 * If specified, search will start this number of characters counted from the beginning
11721 * of the string. If the value is negative, search will instead start from that many
11722 * characters from the end of the string, searching backwards.
11723 * Return
11724 * Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
11725 */
11726static int jx9Builtin_strrpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
11727{
11728 const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
11729 ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
11730 int nLen, nPatLen;
11731 sxu32 nOfft;
11732 sxi32 rc;
11733 if( nArg < 2 ){
11734 /* Missing arguments, return FALSE */
11735 jx9_result_bool(pCtx, 0);
11736 return JX9_OK;
11737 }
11738 /* Extract the needle and the haystack */
11739 zBlob = jx9_value_to_string(apArg[0], &nLen);
11740 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11741 /* Point to the end of the pattern */
11742 zPtr = &zBlob[nLen - 1];
11743 zEnd = &zBlob[nLen];
11744 /* Save the starting posistion */
11745 zStart = zBlob;
11746 nOfft = 0; /* cc warning */
11747 /* Peek the starting offset if available */
11748 if( nArg > 2 ){
11749 int nStart;
11750 nStart = jx9_value_to_int(apArg[2]);
11751 if( nStart < 0 ){
11752 nStart = -nStart;
11753 if( nStart >= nLen ){
11754 /* Invalid offset */
11755 jx9_result_bool(pCtx, 0);
11756 return JX9_OK;
11757 }else{
11758 nLen -= nStart;
11759 zPtr = &zBlob[nLen - 1];
11760 zEnd = &zBlob[nLen];
11761 }
11762 }else{
11763 if( nStart >= nLen ){
11764 /* Invalid offset */
11765 jx9_result_bool(pCtx, 0);
11766 return JX9_OK;
11767 }else{
11768 zBlob += nStart;
11769 nLen -= nStart;
11770 }
11771 }
11772 }
11773 if( nLen > 0 && nPatLen > 0 ){
11774 /* Perform the lookup */
11775 for(;;){
11776 if( zBlob >= zPtr ){
11777 break;
11778 }
11779 rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
11780 if( rc == SXRET_OK ){
11781 /* Pattern found, return it's position */
11782 jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
11783 return JX9_OK;
11784 }
11785 zPtr--;
11786 }
11787 /* Pattern not found, return FALSE */
11788 jx9_result_bool(pCtx, 0);
11789 }else{
11790 jx9_result_bool(pCtx, 0);
11791 }
11792 return JX9_OK;
11793}
11794/*
11795 * int strripos(string $haystack, string $needle [, int $offset = 0 ] )
11796 * Case-insensitive strrpos.
11797 * Parameters
11798 * $haystack
11799 * The input string.
11800 * $needle
11801 * Search pattern (must be a string).
11802 * $offset
11803 * If specified, search will start this number of characters counted from the beginning
11804 * of the string. If the value is negative, search will instead start from that many
11805 * characters from the end of the string, searching backwards.
11806 * Return
11807 * Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
11808 */
11809static int jx9Builtin_strripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
11810{
11811 const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
11812 ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
11813 int nLen, nPatLen;
11814 sxu32 nOfft;
11815 sxi32 rc;
11816 if( nArg < 2 ){
11817 /* Missing arguments, return FALSE */
11818 jx9_result_bool(pCtx, 0);
11819 return JX9_OK;
11820 }
11821 /* Extract the needle and the haystack */
11822 zBlob = jx9_value_to_string(apArg[0], &nLen);
11823 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11824 /* Point to the end of the pattern */
11825 zPtr = &zBlob[nLen - 1];
11826 zEnd = &zBlob[nLen];
11827 /* Save the starting posistion */
11828 zStart = zBlob;
11829 nOfft = 0; /* cc warning */
11830 /* Peek the starting offset if available */
11831 if( nArg > 2 ){
11832 int nStart;
11833 nStart = jx9_value_to_int(apArg[2]);
11834 if( nStart < 0 ){
11835 nStart = -nStart;
11836 if( nStart >= nLen ){
11837 /* Invalid offset */
11838 jx9_result_bool(pCtx, 0);
11839 return JX9_OK;
11840 }else{
11841 nLen -= nStart;
11842 zPtr = &zBlob[nLen - 1];
11843 zEnd = &zBlob[nLen];
11844 }
11845 }else{
11846 if( nStart >= nLen ){
11847 /* Invalid offset */
11848 jx9_result_bool(pCtx, 0);
11849 return JX9_OK;
11850 }else{
11851 zBlob += nStart;
11852 nLen -= nStart;
11853 }
11854 }
11855 }
11856 if( nLen > 0 && nPatLen > 0 ){
11857 /* Perform the lookup */
11858 for(;;){
11859 if( zBlob >= zPtr ){
11860 break;
11861 }
11862 rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
11863 if( rc == SXRET_OK ){
11864 /* Pattern found, return it's position */
11865 jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
11866 return JX9_OK;
11867 }
11868 zPtr--;
11869 }
11870 /* Pattern not found, return FALSE */
11871 jx9_result_bool(pCtx, 0);
11872 }else{
11873 jx9_result_bool(pCtx, 0);
11874 }
11875 return JX9_OK;
11876}
11877/*
11878 * int strrchr(string $haystack, mixed $needle)
11879 * Find the last occurrence of a character in a string.
11880 * Parameters
11881 * $haystack
11882 * The input string.
11883 * $needle
11884 * If needle contains more than one character, only the first is used.
11885 * This behavior is different from that of strstr().
11886 * If needle is not a string, it is converted to an integer and applied
11887 * as the ordinal value of a character.
11888 * Return
11889 * This function returns the portion of string, or FALSE if needle is not found.
11890 */
11891static int jx9Builtin_strrchr(jx9_context *pCtx, int nArg, jx9_value **apArg)
11892{
11893 const char *zBlob;
11894 int nLen, c;
11895 if( nArg < 2 ){
11896 /* Missing arguments, return FALSE */
11897 jx9_result_bool(pCtx, 0);
11898 return JX9_OK;
11899 }
11900 /* Extract the haystack */
11901 zBlob = jx9_value_to_string(apArg[0], &nLen);
11902 c = 0; /* cc warning */
11903 if( nLen > 0 ){
11904 sxu32 nOfft;
11905 sxi32 rc;
11906 if( jx9_value_is_string(apArg[1]) ){
11907 const char *zPattern;
11908 zPattern = jx9_value_to_string(apArg[1], 0); /* Never fail, so there is no need to check
11909 * for NULL pointer.
11910 */
11911 c = zPattern[0];
11912 }else{
11913 /* Int cast */
11914 c = jx9_value_to_int(apArg[1]);
11915 }
11916 /* Perform the lookup */
11917 rc = SyByteFind2(zBlob, (sxu32)nLen, c, &nOfft);
11918 if( rc != SXRET_OK ){
11919 /* No such entry, return FALSE */
11920 jx9_result_bool(pCtx, 0);
11921 return JX9_OK;
11922 }
11923 /* Return the string portion */
11924 jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
11925 }else{
11926 jx9_result_bool(pCtx, 0);
11927 }
11928 return JX9_OK;
11929}
11930/*
11931 * string strrev(string $string)
11932 * Reverse a string.
11933 * Parameters
11934 * $string
11935 * String to be reversed.
11936 * Return
11937 * The reversed string.
11938 */
11939static int jx9Builtin_strrev(jx9_context *pCtx, int nArg, jx9_value **apArg)
11940{
11941 const char *zIn, *zEnd;
11942 int nLen, c;
11943 if( nArg < 1 ){
11944 /* Missing arguments, return NULL */
11945 jx9_result_null(pCtx);
11946 return JX9_OK;
11947 }
11948 /* Extract the target string */
11949 zIn = jx9_value_to_string(apArg[0], &nLen);
11950 if( nLen < 1 ){
11951 /* Empty string Return null */
11952 jx9_result_null(pCtx);
11953 return JX9_OK;
11954 }
11955 /* Perform the requested operation */
11956 zEnd = &zIn[nLen - 1];
11957 for(;;){
11958 if( zEnd < zIn ){
11959 /* No more input to process */
11960 break;
11961 }
11962 /* Append current character */
11963 c = zEnd[0];
11964 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
11965 zEnd--;
11966 }
11967 return JX9_OK;
11968}
11969/*
11970 * string str_repeat(string $input, int $multiplier)
11971 * Returns input repeated multiplier times.
11972 * Parameters
11973 * $string
11974 * String to be repeated.
11975 * $multiplier
11976 * Number of time the input string should be repeated.
11977 * multiplier has to be greater than or equal to 0. If the multiplier is set
11978 * to 0, the function will return an empty string.
11979 * Return
11980 * The repeated string.
11981 */
11982static int jx9Builtin_str_repeat(jx9_context *pCtx, int nArg, jx9_value **apArg)
11983{
11984 const char *zIn;
11985 int nLen, nMul;
11986 int rc;
11987 if( nArg < 2 ){
11988 /* Missing arguments, return NULL */
11989 jx9_result_null(pCtx);
11990 return JX9_OK;
11991 }
11992 /* Extract the target string */
11993 zIn = jx9_value_to_string(apArg[0], &nLen);
11994 if( nLen < 1 ){
11995 /* Empty string.Return null */
11996 jx9_result_null(pCtx);
11997 return JX9_OK;
11998 }
11999 /* Extract the multiplier */
12000 nMul = jx9_value_to_int(apArg[1]);
12001 if( nMul < 1 ){
12002 /* Return the empty string */
12003 jx9_result_string(pCtx, "", 0);
12004 return JX9_OK;
12005 }
12006 /* Perform the requested operation */
12007 for(;;){
12008 if( nMul < 1 ){
12009 break;
12010 }
12011 /* Append the copy */
12012 rc = jx9_result_string(pCtx, zIn, nLen);
12013 if( rc != JX9_OK ){
12014 /* Out of memory, break immediately */
12015 break;
12016 }
12017 nMul--;
12018 }
12019 return JX9_OK;
12020}
12021/*
12022 * string nl2br(string $string[, bool $is_xhtml = true ])
12023 * Inserts HTML line breaks before all newlines in a string.
12024 * Parameters
12025 * $string
12026 * The input string.
12027 * $is_xhtml
12028 * Whenever to use XHTML compatible line breaks or not.
12029 * Return
12030 * The processed string.
12031 */
12032static int jx9Builtin_nl2br(jx9_context *pCtx, int nArg, jx9_value **apArg)
12033{
12034 const char *zIn, *zCur, *zEnd;
12035 int is_xhtml = 0;
12036 int nLen;
12037 if( nArg < 1 ){
12038 /* Missing arguments, return the empty string */
12039 jx9_result_string(pCtx, "", 0);
12040 return JX9_OK;
12041 }
12042 /* Extract the target string */
12043 zIn = jx9_value_to_string(apArg[0], &nLen);
12044 if( nLen < 1 ){
12045 /* Empty string, return null */
12046 jx9_result_null(pCtx);
12047 return JX9_OK;
12048 }
12049 if( nArg > 1 ){
12050 is_xhtml = jx9_value_to_bool(apArg[1]);
12051 }
12052 zEnd = &zIn[nLen];
12053 /* Perform the requested operation */
12054 for(;;){
12055 zCur = zIn;
12056 /* Delimit the string */
12057 while( zIn < zEnd && (zIn[0] != '\n'&& zIn[0] != '\r') ){
12058 zIn++;
12059 }
12060 if( zCur < zIn ){
12061 /* Output chunk verbatim */
12062 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
12063 }
12064 if( zIn >= zEnd ){
12065 /* No more input to process */
12066 break;
12067 }
12068 /* Output the HTML line break */
12069 if( is_xhtml ){
12070 jx9_result_string(pCtx, "<br>", (int)sizeof("<br>")-1);
12071 }else{
12072 jx9_result_string(pCtx, "<br/>", (int)sizeof("<br/>")-1);
12073 }
12074 zCur = zIn;
12075 /* Append trailing line */
12076 while( zIn < zEnd && (zIn[0] == '\n' || zIn[0] == '\r') ){
12077 zIn++;
12078 }
12079 if( zCur < zIn ){
12080 /* Output chunk verbatim */
12081 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
12082 }
12083 }
12084 return JX9_OK;
12085}
12086/*
12087 * Format a given string and invoke the given callback on each processed chunk.
12088 * According to the JX9 reference manual.
12089 * The format string is composed of zero or more directives: ordinary characters
12090 * (excluding %) that are copied directly to the result, and conversion
12091 * specifications, each of which results in fetching its own parameter.
12092 * This applies to both sprintf() and printf().
12093 * Each conversion specification consists of a percent sign (%), followed by one
12094 * or more of these elements, in order:
12095 * An optional sign specifier that forces a sign (- or +) to be used on a number.
12096 * By default, only the - sign is used on a number if it's negative. This specifier forces
12097 * positive numbers to have the + sign attached as well.
12098 * An optional padding specifier that says what character will be used for padding
12099 * the results to the right string size. This may be a space character or a 0 (zero character).
12100 * The default is to pad with spaces. An alternate padding character can be specified by prefixing
12101 * it with a single quote ('). See the examples below.
12102 * An optional alignment specifier that says if the result should be left-justified or right-justified.
12103 * The default is right-justified; a - character here will make it left-justified.
12104 * An optional number, a width specifier that says how many characters (minimum) this conversion
12105 * should result in.
12106 * An optional precision specifier in the form of a period (`.') followed by an optional decimal
12107 * digit string that says how many decimal digits should be displayed for floating-point numbers.
12108 * When using this specifier on a string, it acts as a cutoff point, setting a maximum character
12109 * limit to the string.
12110 * A type specifier that says what type the argument data should be treated as. Possible types:
12111 * % - a literal percent character. No argument is required.
12112 * b - the argument is treated as an integer, and presented as a binary number.
12113 * c - the argument is treated as an integer, and presented as the character with that ASCII value.
12114 * d - the argument is treated as an integer, and presented as a (signed) decimal number.
12115 * e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands
12116 * for the number of digits after the decimal point.
12117 * E - like %e but uses uppercase letter (e.g. 1.2E+2).
12118 * u - the argument is treated as an integer, and presented as an unsigned decimal number.
12119 * f - the argument is treated as a float, and presented as a floating-point number (locale aware).
12120 * F - the argument is treated as a float, and presented as a floating-point number (non-locale aware).
12121 * g - shorter of %e and %f.
12122 * G - shorter of %E and %f.
12123 * o - the argument is treated as an integer, and presented as an octal number.
12124 * s - the argument is treated as and presented as a string.
12125 * x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters).
12126 * X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters).
12127 */
12128/*
12129 * This implementation is based on the one found in the SQLite3 source tree.
12130 */
12131#define JX9_FMT_BUFSIZ 1024 /* Conversion buffer size */
12132/*
12133** Conversion types fall into various categories as defined by the
12134** following enumeration.
12135*/
12136#define JX9_FMT_RADIX 1 /* Integer types.%d, %x, %o, and so forth */
12137#define JX9_FMT_FLOAT 2 /* Floating point.%f */
12138#define JX9_FMT_EXP 3 /* Exponentional notation.%e and %E */
12139#define JX9_FMT_GENERIC 4 /* Floating or exponential, depending on exponent.%g */
12140#define JX9_FMT_SIZE 5 /* Total number of characters processed so far.%n */
12141#define JX9_FMT_STRING 6 /* Strings.%s */
12142#define JX9_FMT_PERCENT 7 /* Percent symbol.%% */
12143#define JX9_FMT_CHARX 8 /* Characters.%c */
12144#define JX9_FMT_ERROR 9 /* Used to indicate no such conversion type */
12145/*
12146** Allowed values for jx9_fmt_info.flags
12147*/
12148#define JX9_FMT_FLAG_SIGNED 0x01
12149#define JX9_FMT_FLAG_UNSIGNED 0x02
12150/*
12151** Each builtin conversion character (ex: the 'd' in "%d") is described
12152** by an instance of the following structure
12153*/
12154typedef struct jx9_fmt_info jx9_fmt_info;
12155struct jx9_fmt_info
12156{
12157 char fmttype; /* The format field code letter [i.e: 'd', 's', 'x'] */
12158 sxu8 base; /* The base for radix conversion */
12159 int flags; /* One or more of JX9_FMT_FLAG_ constants below */
12160 sxu8 type; /* Conversion paradigm */
12161 char *charset; /* The character set for conversion */
12162 char *prefix; /* Prefix on non-zero values in alt format */
12163};
12164#ifndef JX9_OMIT_FLOATING_POINT
12165/*
12166** "*val" is a double such that 0.1 <= *val < 10.0
12167** Return the ascii code for the leading digit of *val, then
12168** multiply "*val" by 10.0 to renormalize.
12169**
12170** Example:
12171** input: *val = 3.14159
12172** output: *val = 1.4159 function return = '3'
12173**
12174** The counter *cnt is incremented each time. After counter exceeds
12175** 16 (the number of significant digits in a 64-bit float) '0' is
12176** always returned.
12177*/
12178static int vxGetdigit(sxlongreal *val, int *cnt)
12179{
12180 sxlongreal d;
12181 int digit;
12182
12183 if( (*cnt)++ >= 16 ){
12184 return '0';
12185 }
12186 digit = (int)*val;
12187 d = digit;
12188 *val = (*val - d)*10.0;
12189 return digit + '0' ;
12190}
12191#endif /* JX9_OMIT_FLOATING_POINT */
12192/*
12193 * The following table is searched linearly, so it is good to put the most frequently
12194 * used conversion types first.
12195 */
12196static const jx9_fmt_info aFmt[] = {
12197 { 'd', 10, JX9_FMT_FLAG_SIGNED, JX9_FMT_RADIX, "0123456789", 0 },
12198 { 's', 0, 0, JX9_FMT_STRING, 0, 0 },
12199 { 'c', 0, 0, JX9_FMT_CHARX, 0, 0 },
12200 { 'x', 16, 0, JX9_FMT_RADIX, "0123456789abcdef", "x0" },
12201 { 'X', 16, 0, JX9_FMT_RADIX, "0123456789ABCDEF", "X0" },
12202 { 'b', 2, 0, JX9_FMT_RADIX, "01", "b0"},
12203 { 'o', 8, 0, JX9_FMT_RADIX, "01234567", "0" },
12204 { 'u', 10, 0, JX9_FMT_RADIX, "0123456789", 0 },
12205 { 'f', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT, 0, 0 },
12206 { 'F', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT, 0, 0 },
12207 { 'e', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP, "e", 0 },
12208 { 'E', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP, "E", 0 },
12209 { 'g', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC, "e", 0 },
12210 { 'G', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC, "E", 0 },
12211 { '%', 0, 0, JX9_FMT_PERCENT, 0, 0 }
12212};
12213/*
12214 * Format a given string.
12215 * The root program. All variations call this core.
12216 * INPUTS:
12217 * xConsumer This is a pointer to a function taking four arguments
12218 * 1. A pointer to the call context.
12219 * 2. A pointer to the list of characters to be output
12220 * (Note, this list is NOT null terminated.)
12221 * 3. An integer number of characters to be output.
12222 * (Note: This number might be zero.)
12223 * 4. Upper layer private data.
12224 * zIn This is the format string, as in the usual print.
12225 * apArg This is a pointer to a list of arguments.
12226 */
12227JX9_PRIVATE sxi32 jx9InputFormat(
12228 int (*xConsumer)(jx9_context *, const char *, int, void *), /* Format consumer */
12229 jx9_context *pCtx, /* call context */
12230 const char *zIn, /* Format string */
12231 int nByte, /* Format string length */
12232 int nArg, /* Total argument of the given arguments */
12233 jx9_value **apArg, /* User arguments */
12234 void *pUserData, /* Last argument to xConsumer() */
12235 int vf /* TRUE if called from vfprintf, vsprintf context */
12236 )
12237{
12238 char spaces[] = " ";
12239#define etSPACESIZE ((int)sizeof(spaces)-1)
12240 const char *zCur, *zEnd = &zIn[nByte];
12241 char *zBuf, zWorker[JX9_FMT_BUFSIZ]; /* Working buffer */
12242 const jx9_fmt_info *pInfo; /* Pointer to the appropriate info structure */
12243 int flag_alternateform; /* True if "#" flag is present */
12244 int flag_leftjustify; /* True if "-" flag is present */
12245 int flag_blanksign; /* True if " " flag is present */
12246 int flag_plussign; /* True if "+" flag is present */
12247 int flag_zeropad; /* True if field width constant starts with zero */
12248 jx9_value *pArg; /* Current processed argument */
12249 jx9_int64 iVal;
12250 int precision; /* Precision of the current field */
12251 char *zExtra;
12252 int c, rc, n;
12253 int length; /* Length of the field */
12254 int prefix;
12255 sxu8 xtype; /* Conversion paradigm */
12256 int width; /* Width of the current field */
12257 int idx;
12258 n = (vf == TRUE) ? 0 : 1;
12259#define NEXT_ARG ( n < nArg ? apArg[n++] : 0 )
12260 /* Start the format process */
12261 for(;;){
12262 zCur = zIn;
12263 while( zIn < zEnd && zIn[0] != '%' ){
12264 zIn++;
12265 }
12266 if( zCur < zIn ){
12267 /* Consume chunk verbatim */
12268 rc = xConsumer(pCtx, zCur, (int)(zIn-zCur), pUserData);
12269 if( rc == SXERR_ABORT ){
12270 /* Callback request an operation abort */
12271 break;
12272 }
12273 }
12274 if( zIn >= zEnd ){
12275 /* No more input to process, break immediately */
12276 break;
12277 }
12278 /* Find out what flags are present */
12279 flag_leftjustify = flag_plussign = flag_blanksign =
12280 flag_alternateform = flag_zeropad = 0;
12281 zIn++; /* Jump the precent sign */
12282 do{
12283 c = zIn[0];
12284 switch( c ){
12285 case '-': flag_leftjustify = 1; c = 0; break;
12286 case '+': flag_plussign = 1; c = 0; break;
12287 case ' ': flag_blanksign = 1; c = 0; break;
12288 case '#': flag_alternateform = 1; c = 0; break;
12289 case '0': flag_zeropad = 1; c = 0; break;
12290 case '\'':
12291 zIn++;
12292 if( zIn < zEnd ){
12293 /* An alternate padding character can be specified by prefixing it with a single quote (') */
12294 c = zIn[0];
12295 for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
12296 spaces[idx] = (char)c;
12297 }
12298 c = 0;
12299 }
12300 break;
12301 default: break;
12302 }
12303 }while( c==0 && (zIn++ < zEnd) );
12304 /* Get the field width */
12305 width = 0;
12306 while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
12307 width = width*10 + (zIn[0] - '0');
12308 zIn++;
12309 }
12310 if( zIn < zEnd && zIn[0] == '$' ){
12311 /* Position specifer */
12312 if( width > 0 ){
12313 n = width;
12314 if( vf && n > 0 ){
12315 n--;
12316 }
12317 }
12318 zIn++;
12319 width = 0;
12320 if( zIn < zEnd && zIn[0] == '0' ){
12321 flag_zeropad = 1;
12322 zIn++;
12323 }
12324 while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
12325 width = width*10 + (zIn[0] - '0');
12326 zIn++;
12327 }
12328 }
12329 if( width > JX9_FMT_BUFSIZ-10 ){
12330 width = JX9_FMT_BUFSIZ-10;
12331 }
12332 /* Get the precision */
12333 precision = -1;
12334 if( zIn < zEnd && zIn[0] == '.' ){
12335 precision = 0;
12336 zIn++;
12337 while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
12338 precision = precision*10 + (zIn[0] - '0');
12339 zIn++;
12340 }
12341 }
12342 if( zIn >= zEnd ){
12343 /* No more input */
12344 break;
12345 }
12346 /* Fetch the info entry for the field */
12347 pInfo = 0;
12348 xtype = JX9_FMT_ERROR;
12349 c = zIn[0];
12350 zIn++; /* Jump the format specifer */
12351 for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
12352 if( c==aFmt[idx].fmttype ){
12353 pInfo = &aFmt[idx];
12354 xtype = pInfo->type;
12355 break;
12356 }
12357 }
12358 zBuf = zWorker; /* Point to the working buffer */
12359 length = 0;
12360 zExtra = 0;
12361 /*
12362 ** At this point, variables are initialized as follows:
12363 **
12364 ** flag_alternateform TRUE if a '#' is present.
12365 ** flag_plussign TRUE if a '+' is present.
12366 ** flag_leftjustify TRUE if a '-' is present or if the
12367 ** field width was negative.
12368 ** flag_zeropad TRUE if the width began with 0.
12369 ** the conversion character.
12370 ** flag_blanksign TRUE if a ' ' is present.
12371 ** width The specified field width. This is
12372 ** always non-negative. Zero is the default.
12373 ** precision The specified precision. The default
12374 ** is -1.
12375 */
12376 switch(xtype){
12377 case JX9_FMT_PERCENT:
12378 /* A literal percent character */
12379 zWorker[0] = '%';
12380 length = (int)sizeof(char);
12381 break;
12382 case JX9_FMT_CHARX:
12383 /* The argument is treated as an integer, and presented as the character
12384 * with that ASCII value
12385 */
12386 pArg = NEXT_ARG;
12387 if( pArg == 0 ){
12388 c = 0;
12389 }else{
12390 c = jx9_value_to_int(pArg);
12391 }
12392 /* NUL byte is an acceptable value */
12393 zWorker[0] = (char)c;
12394 length = (int)sizeof(char);
12395 break;
12396 case JX9_FMT_STRING:
12397 /* the argument is treated as and presented as a string */
12398 pArg = NEXT_ARG;
12399 if( pArg == 0 ){
12400 length = 0;
12401 }else{
12402 zBuf = (char *)jx9_value_to_string(pArg, &length);
12403 }
12404 if( length < 1 ){
12405 zBuf = " ";
12406 length = (int)sizeof(char);
12407 }
12408 if( precision>=0 && precision<length ){
12409 length = precision;
12410 }
12411 if( flag_zeropad ){
12412 /* zero-padding works on strings too */
12413 for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
12414 spaces[idx] = '0';
12415 }
12416 }
12417 break;
12418 case JX9_FMT_RADIX:
12419 pArg = NEXT_ARG;
12420 if( pArg == 0 ){
12421 iVal = 0;
12422 }else{
12423 iVal = jx9_value_to_int64(pArg);
12424 }
12425 /* Limit the precision to prevent overflowing buf[] during conversion */
12426 if( precision>JX9_FMT_BUFSIZ-40 ){
12427 precision = JX9_FMT_BUFSIZ-40;
12428 }
12429#if 1
12430 /* For the format %#x, the value zero is printed "0" not "0x0".
12431 ** I think this is stupid.*/
12432 if( iVal==0 ) flag_alternateform = 0;
12433#else
12434 /* More sensible: turn off the prefix for octal (to prevent "00"),
12435 ** but leave the prefix for hex.*/
12436 if( iVal==0 && pInfo->base==8 ) flag_alternateform = 0;
12437#endif
12438 if( pInfo->flags & JX9_FMT_FLAG_SIGNED ){
12439 if( iVal<0 ){
12440 iVal = -iVal;
12441 /* Ticket 1433-003 */
12442 if( iVal < 0 ){
12443 /* Overflow */
12444 iVal= 0x7FFFFFFFFFFFFFFF;
12445 }
12446 prefix = '-';
12447 }else if( flag_plussign ) prefix = '+';
12448 else if( flag_blanksign ) prefix = ' ';
12449 else prefix = 0;
12450 }else{
12451 if( iVal<0 ){
12452 iVal = -iVal;
12453 /* Ticket 1433-003 */
12454 if( iVal < 0 ){
12455 /* Overflow */
12456 iVal= 0x7FFFFFFFFFFFFFFF;
12457 }
12458 }
12459 prefix = 0;
12460 }
12461 if( flag_zeropad && precision<width-(prefix!=0) ){
12462 precision = width-(prefix!=0);
12463 }
12464 zBuf = &zWorker[JX9_FMT_BUFSIZ-1];
12465 {
12466 register char *cset; /* Use registers for speed */
12467 register int base;
12468 cset = pInfo->charset;
12469 base = pInfo->base;
12470 do{ /* Convert to ascii */
12471 *(--zBuf) = cset[iVal%base];
12472 iVal = iVal/base;
12473 }while( iVal>0 );
12474 }
12475 length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
12476 for(idx=precision-length; idx>0; idx--){
12477 *(--zBuf) = '0'; /* Zero pad */
12478 }
12479 if( prefix ) *(--zBuf) = (char)prefix; /* Add sign */
12480 if( flag_alternateform && pInfo->prefix ){ /* Add "0" or "0x" */
12481 char *pre, x;
12482 pre = pInfo->prefix;
12483 if( *zBuf!=pre[0] ){
12484 for(pre=pInfo->prefix; (x=(*pre))!=0; pre++) *(--zBuf) = x;
12485 }
12486 }
12487 length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
12488 break;
12489 case JX9_FMT_FLOAT:
12490 case JX9_FMT_EXP:
12491 case JX9_FMT_GENERIC:{
12492#ifndef JX9_OMIT_FLOATING_POINT
12493 long double realvalue;
12494 int exp; /* exponent of real numbers */
12495 double rounder; /* Used for rounding floating point values */
12496 int flag_dp; /* True if decimal point should be shown */
12497 int flag_rtz; /* True if trailing zeros should be removed */
12498 int flag_exp; /* True to force display of the exponent */
12499 int nsd; /* Number of significant digits returned */
12500 pArg = NEXT_ARG;
12501 if( pArg == 0 ){
12502 realvalue = 0;
12503 }else{
12504 realvalue = jx9_value_to_double(pArg);
12505 }
12506 if( precision<0 ) precision = 6; /* Set default precision */
12507 if( precision>JX9_FMT_BUFSIZ-40) precision = JX9_FMT_BUFSIZ-40;
12508 if( realvalue<0.0 ){
12509 realvalue = -realvalue;
12510 prefix = '-';
12511 }else{
12512 if( flag_plussign ) prefix = '+';
12513 else if( flag_blanksign ) prefix = ' ';
12514 else prefix = 0;
12515 }
12516 if( pInfo->type==JX9_FMT_GENERIC && precision>0 ) precision--;
12517 rounder = 0.0;
12518#if 0
12519 /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
12520 for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
12521#else
12522 /* It makes more sense to use 0.5 */
12523 for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
12524#endif
12525 if( pInfo->type==JX9_FMT_FLOAT ) realvalue += rounder;
12526 /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
12527 exp = 0;
12528 if( realvalue>0.0 ){
12529 while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
12530 while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
12531 while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
12532 while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
12533 if( exp>350 || exp<-350 ){
12534 zBuf = "NaN";
12535 length = 3;
12536 break;
12537 }
12538 }
12539 zBuf = zWorker;
12540 /*
12541 ** If the field type is etGENERIC, then convert to either etEXP
12542 ** or etFLOAT, as appropriate.
12543 */
12544 flag_exp = xtype==JX9_FMT_EXP;
12545 if( xtype!=JX9_FMT_FLOAT ){
12546 realvalue += rounder;
12547 if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
12548 }
12549 if( xtype==JX9_FMT_GENERIC ){
12550 flag_rtz = !flag_alternateform;
12551 if( exp<-4 || exp>precision ){
12552 xtype = JX9_FMT_EXP;
12553 }else{
12554 precision = precision - exp;
12555 xtype = JX9_FMT_FLOAT;
12556 }
12557 }else{
12558 flag_rtz = 0;
12559 }
12560 /*
12561 ** The "exp+precision" test causes output to be of type etEXP if
12562 ** the precision is too large to fit in buf[].
12563 */
12564 nsd = 0;
12565 if( xtype==JX9_FMT_FLOAT && exp+precision<JX9_FMT_BUFSIZ-30 ){
12566 flag_dp = (precision>0 || flag_alternateform);
12567 if( prefix ) *(zBuf++) = (char)prefix; /* Sign */
12568 if( exp<0 ) *(zBuf++) = '0'; /* Digits before "." */
12569 else for(; exp>=0; exp--) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
12570 if( flag_dp ) *(zBuf++) = '.'; /* The decimal point */
12571 for(exp++; exp<0 && precision>0; precision--, exp++){
12572 *(zBuf++) = '0';
12573 }
12574 while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
12575 *(zBuf--) = 0; /* Null terminate */
12576 if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */
12577 while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
12578 if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
12579 }
12580 zBuf++; /* point to next free slot */
12581 }else{ /* etEXP or etGENERIC */
12582 flag_dp = (precision>0 || flag_alternateform);
12583 if( prefix ) *(zBuf++) = (char)prefix; /* Sign */
12584 *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); /* First digit */
12585 if( flag_dp ) *(zBuf++) = '.'; /* Decimal point */
12586 while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
12587 zBuf--; /* point to last digit */
12588 if( flag_rtz && flag_dp ){ /* Remove tail zeros */
12589 while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
12590 if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
12591 }
12592 zBuf++; /* point to next free slot */
12593 if( exp || flag_exp ){
12594 *(zBuf++) = pInfo->charset[0];
12595 if( exp<0 ){ *(zBuf++) = '-'; exp = -exp; } /* sign of exp */
12596 else { *(zBuf++) = '+'; }
12597 if( exp>=100 ){
12598 *(zBuf++) = (char)((exp/100)+'0'); /* 100's digit */
12599 exp %= 100;
12600 }
12601 *(zBuf++) = (char)(exp/10+'0'); /* 10's digit */
12602 *(zBuf++) = (char)(exp%10+'0'); /* 1's digit */
12603 }
12604 }
12605 /* The converted number is in buf[] and zero terminated.Output it.
12606 ** Note that the number is in the usual order, not reversed as with
12607 ** integer conversions.*/
12608 length = (int)(zBuf-zWorker);
12609 zBuf = zWorker;
12610 /* Special case: Add leading zeros if the flag_zeropad flag is
12611 ** set and we are not left justified */
12612 if( flag_zeropad && !flag_leftjustify && length < width){
12613 int i;
12614 int nPad = width - length;
12615 for(i=width; i>=nPad; i--){
12616 zBuf[i] = zBuf[i-nPad];
12617 }
12618 i = prefix!=0;
12619 while( nPad-- ) zBuf[i++] = '0';
12620 length = width;
12621 }
12622#else
12623 zBuf = " ";
12624 length = (int)sizeof(char);
12625#endif /* JX9_OMIT_FLOATING_POINT */
12626 break;
12627 }
12628 default:
12629 /* Invalid format specifer */
12630 zWorker[0] = '?';
12631 length = (int)sizeof(char);
12632 break;
12633 }
12634 /*
12635 ** The text of the conversion is pointed to by "zBuf" and is
12636 ** "length" characters long.The field width is "width".Do
12637 ** the output.
12638 */
12639 if( !flag_leftjustify ){
12640 register int nspace;
12641 nspace = width-length;
12642 if( nspace>0 ){
12643 while( nspace>=etSPACESIZE ){
12644 rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
12645 if( rc != SXRET_OK ){
12646 return SXERR_ABORT; /* Consumer routine request an operation abort */
12647 }
12648 nspace -= etSPACESIZE;
12649 }
12650 if( nspace>0 ){
12651 rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
12652 if( rc != SXRET_OK ){
12653 return SXERR_ABORT; /* Consumer routine request an operation abort */
12654 }
12655 }
12656 }
12657 }
12658 if( length>0 ){
12659 rc = xConsumer(pCtx, zBuf, (unsigned int)length, pUserData);
12660 if( rc != SXRET_OK ){
12661 return SXERR_ABORT; /* Consumer routine request an operation abort */
12662 }
12663 }
12664 if( flag_leftjustify ){
12665 register int nspace;
12666 nspace = width-length;
12667 if( nspace>0 ){
12668 while( nspace>=etSPACESIZE ){
12669 rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
12670 if( rc != SXRET_OK ){
12671 return SXERR_ABORT; /* Consumer routine request an operation abort */
12672 }
12673 nspace -= etSPACESIZE;
12674 }
12675 if( nspace>0 ){
12676 rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
12677 if( rc != SXRET_OK ){
12678 return SXERR_ABORT; /* Consumer routine request an operation abort */
12679 }
12680 }
12681 }
12682 }
12683 }/* for(;;) */
12684 return SXRET_OK;
12685}
12686/*
12687 * Callback [i.e: Formatted input consumer] of the sprintf function.
12688 */
12689static int sprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
12690{
12691 /* Consume directly */
12692 jx9_result_string(pCtx, zInput, nLen);
12693 SXUNUSED(pUserData); /* cc warning */
12694 return JX9_OK;
12695}
12696/*
12697 * string sprintf(string $format[, mixed $args [, mixed $... ]])
12698 * Return a formatted string.
12699 * Parameters
12700 * $format
12701 * The format string (see block comment above)
12702 * Return
12703 * A string produced according to the formatting string format.
12704 */
12705static int jx9Builtin_sprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
12706{
12707 const char *zFormat;
12708 int nLen;
12709 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
12710 /* Missing/Invalid arguments, return the empty string */
12711 jx9_result_string(pCtx, "", 0);
12712 return JX9_OK;
12713 }
12714 /* Extract the string format */
12715 zFormat = jx9_value_to_string(apArg[0], &nLen);
12716 if( nLen < 1 ){
12717 /* Empty string */
12718 jx9_result_string(pCtx, "", 0);
12719 return JX9_OK;
12720 }
12721 /* Format the string */
12722 jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, nArg, apArg, 0, FALSE);
12723 return JX9_OK;
12724}
12725/*
12726 * Callback [i.e: Formatted input consumer] of the printf function.
12727 */
12728static int printfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
12729{
12730 jx9_int64 *pCounter = (jx9_int64 *)pUserData;
12731 /* Call the VM output consumer directly */
12732 jx9_context_output(pCtx, zInput, nLen);
12733 /* Increment counter */
12734 *pCounter += nLen;
12735 return JX9_OK;
12736}
12737/*
12738 * int64 printf(string $format[, mixed $args[, mixed $... ]])
12739 * Output a formatted string.
12740 * Parameters
12741 * $format
12742 * See sprintf() for a description of format.
12743 * Return
12744 * The length of the outputted string.
12745 */
12746static int jx9Builtin_printf(jx9_context *pCtx, int nArg, jx9_value **apArg)
12747{
12748 jx9_int64 nCounter = 0;
12749 const char *zFormat;
12750 int nLen;
12751 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
12752 /* Missing/Invalid arguments, return 0 */
12753 jx9_result_int(pCtx, 0);
12754 return JX9_OK;
12755 }
12756 /* Extract the string format */
12757 zFormat = jx9_value_to_string(apArg[0], &nLen);
12758 if( nLen < 1 ){
12759 /* Empty string */
12760 jx9_result_int(pCtx, 0);
12761 return JX9_OK;
12762 }
12763 /* Format the string */
12764 jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, nArg, apArg, (void *)&nCounter, FALSE);
12765 /* Return the length of the outputted string */
12766 jx9_result_int64(pCtx, nCounter);
12767 return JX9_OK;
12768}
12769/*
12770 * int vprintf(string $format, array $args)
12771 * Output a formatted string.
12772 * Parameters
12773 * $format
12774 * See sprintf() for a description of format.
12775 * Return
12776 * The length of the outputted string.
12777 */
12778static int jx9Builtin_vprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
12779{
12780 jx9_int64 nCounter = 0;
12781 const char *zFormat;
12782 jx9_hashmap *pMap;
12783 SySet sArg;
12784 int nLen, n;
12785 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
12786 /* Missing/Invalid arguments, return 0 */
12787 jx9_result_int(pCtx, 0);
12788 return JX9_OK;
12789 }
12790 /* Extract the string format */
12791 zFormat = jx9_value_to_string(apArg[0], &nLen);
12792 if( nLen < 1 ){
12793 /* Empty string */
12794 jx9_result_int(pCtx, 0);
12795 return JX9_OK;
12796 }
12797 /* Point to the hashmap */
12798 pMap = (jx9_hashmap *)apArg[1]->x.pOther;
12799 /* Extract arguments from the hashmap */
12800 n = jx9HashmapValuesToSet(pMap, &sArg);
12801 /* Format the string */
12802 jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&nCounter, TRUE);
12803 /* Return the length of the outputted string */
12804 jx9_result_int64(pCtx, nCounter);
12805 /* Release the container */
12806 SySetRelease(&sArg);
12807 return JX9_OK;
12808}
12809/*
12810 * int vsprintf(string $format, array $args)
12811 * Output a formatted string.
12812 * Parameters
12813 * $format
12814 * See sprintf() for a description of format.
12815 * Return
12816 * A string produced according to the formatting string format.
12817 */
12818static int jx9Builtin_vsprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
12819{
12820 const char *zFormat;
12821 jx9_hashmap *pMap;
12822 SySet sArg;
12823 int nLen, n;
12824 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
12825 /* Missing/Invalid arguments, return the empty string */
12826 jx9_result_string(pCtx, "", 0);
12827 return JX9_OK;
12828 }
12829 /* Extract the string format */
12830 zFormat = jx9_value_to_string(apArg[0], &nLen);
12831 if( nLen < 1 ){
12832 /* Empty string */
12833 jx9_result_string(pCtx, "", 0);
12834 return JX9_OK;
12835 }
12836 /* Point to hashmap */
12837 pMap = (jx9_hashmap *)apArg[1]->x.pOther;
12838 /* Extract arguments from the hashmap */
12839 n = jx9HashmapValuesToSet(pMap, &sArg);
12840 /* Format the string */
12841 jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), 0, TRUE);
12842 /* Release the container */
12843 SySetRelease(&sArg);
12844 return JX9_OK;
12845}
12846/*
12847 * string size_format(int64 $size)
12848 * Return a smart string represenation of the given size [i.e: 64-bit integer]
12849 * Example:
12850 * print size_format(1*1024*1024*1024);// 1GB
12851 * print size_format(512*1024*1024); // 512 MB
12852 * print size_format(file_size(/path/to/my/file_8192)); //8KB
12853 * Parameter
12854 * $size
12855 * Entity size in bytes.
12856 * Return
12857 * Formatted string representation of the given size.
12858 */
12859static int jx9Builtin_size_format(jx9_context *pCtx, int nArg, jx9_value **apArg)
12860{
12861 /*Kilo*/ /*Mega*/ /*Giga*/ /*Tera*/ /*Peta*/ /*Exa*/ /*Zeta*/
12862 static const char zUnit[] = {"KMGTPEZ"};
12863 sxi32 nRest, i_32;
12864 jx9_int64 iSize;
12865 int c = -1; /* index in zUnit[] */
12866
12867 if( nArg < 1 ){
12868 /* Missing argument, return the empty string */
12869 jx9_result_string(pCtx, "", 0);
12870 return JX9_OK;
12871 }
12872 /* Extract the given size */
12873 iSize = jx9_value_to_int64(apArg[0]);
12874 if( iSize < 100 /* Bytes */ ){
12875 /* Don't bother formatting, return immediately */
12876 jx9_result_string(pCtx, "0.1 KB", (int)sizeof("0.1 KB")-1);
12877 return JX9_OK;
12878 }
12879 for(;;){
12880 nRest = (sxi32)(iSize & 0x3FF);
12881 iSize >>= 10;
12882 c++;
12883 if( (iSize & (~0 ^ 1023)) == 0 ){
12884 break;
12885 }
12886 }
12887 nRest /= 100;
12888 if( nRest > 9 ){
12889 nRest = 9;
12890 }
12891 if( iSize > 999 ){
12892 c++;
12893 nRest = 9;
12894 iSize = 0;
12895 }
12896 i_32 = (sxi32)iSize;
12897 /* Format */
12898 jx9_result_string_format(pCtx, "%d.%d %cB", i_32, nRest, zUnit[c]);
12899 return JX9_OK;
12900}
12901#if !defined(JX9_DISABLE_HASH_FUNC)
12902/*
12903 * string md5(string $str[, bool $raw_output = false])
12904 * Calculate the md5 hash of a string.
12905 * Parameter
12906 * $str
12907 * Input string
12908 * $raw_output
12909 * If the optional raw_output is set to TRUE, then the md5 digest
12910 * is instead returned in raw binary format with a length of 16.
12911 * Return
12912 * MD5 Hash as a 32-character hexadecimal string.
12913 */
12914static int jx9Builtin_md5(jx9_context *pCtx, int nArg, jx9_value **apArg)
12915{
12916 unsigned char zDigest[16];
12917 int raw_output = FALSE;
12918 const void *pIn;
12919 int nLen;
12920 if( nArg < 1 ){
12921 /* Missing arguments, return the empty string */
12922 jx9_result_string(pCtx, "", 0);
12923 return JX9_OK;
12924 }
12925 /* Extract the input string */
12926 pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
12927 if( nLen < 1 ){
12928 /* Empty string */
12929 jx9_result_string(pCtx, "", 0);
12930 return JX9_OK;
12931 }
12932 if( nArg > 1 && jx9_value_is_bool(apArg[1])){
12933 raw_output = jx9_value_to_bool(apArg[1]);
12934 }
12935 /* Compute the MD5 digest */
12936 SyMD5Compute(pIn, (sxu32)nLen, zDigest);
12937 if( raw_output ){
12938 /* Output raw digest */
12939 jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
12940 }else{
12941 /* Perform a binary to hex conversion */
12942 SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
12943 }
12944 return JX9_OK;
12945}
12946/*
12947 * string sha1(string $str[, bool $raw_output = false])
12948 * Calculate the sha1 hash of a string.
12949 * Parameter
12950 * $str
12951 * Input string
12952 * $raw_output
12953 * If the optional raw_output is set to TRUE, then the md5 digest
12954 * is instead returned in raw binary format with a length of 16.
12955 * Return
12956 * SHA1 Hash as a 40-character hexadecimal string.
12957 */
12958static int jx9Builtin_sha1(jx9_context *pCtx, int nArg, jx9_value **apArg)
12959{
12960 unsigned char zDigest[20];
12961 int raw_output = FALSE;
12962 const void *pIn;
12963 int nLen;
12964 if( nArg < 1 ){
12965 /* Missing arguments, return the empty string */
12966 jx9_result_string(pCtx, "", 0);
12967 return JX9_OK;
12968 }
12969 /* Extract the input string */
12970 pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
12971 if( nLen < 1 ){
12972 /* Empty string */
12973 jx9_result_string(pCtx, "", 0);
12974 return JX9_OK;
12975 }
12976 if( nArg > 1 && jx9_value_is_bool(apArg[1])){
12977 raw_output = jx9_value_to_bool(apArg[1]);
12978 }
12979 /* Compute the SHA1 digest */
12980 SySha1Compute(pIn, (sxu32)nLen, zDigest);
12981 if( raw_output ){
12982 /* Output raw digest */
12983 jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
12984 }else{
12985 /* Perform a binary to hex conversion */
12986 SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
12987 }
12988 return JX9_OK;
12989}
12990/*
12991 * int64 crc32(string $str)
12992 * Calculates the crc32 polynomial of a strin.
12993 * Parameter
12994 * $str
12995 * Input string
12996 * Return
12997 * CRC32 checksum of the given input (64-bit integer).
12998 */
12999static int jx9Builtin_crc32(jx9_context *pCtx, int nArg, jx9_value **apArg)
13000{
13001 const void *pIn;
13002 sxu32 nCRC;
13003 int nLen;
13004 if( nArg < 1 ){
13005 /* Missing arguments, return 0 */
13006 jx9_result_int(pCtx, 0);
13007 return JX9_OK;
13008 }
13009 /* Extract the input string */
13010 pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
13011 if( nLen < 1 ){
13012 /* Empty string */
13013 jx9_result_int(pCtx, 0);
13014 return JX9_OK;
13015 }
13016 /* Calculate the sum */
13017 nCRC = SyCrc32(pIn, (sxu32)nLen);
13018 /* Return the CRC32 as 64-bit integer */
13019 jx9_result_int64(pCtx, (jx9_int64)nCRC^ 0xFFFFFFFF);
13020 return JX9_OK;
13021}
13022#endif /* JX9_DISABLE_HASH_FUNC */
13023/*
13024 * Parse a CSV string and invoke the supplied callback for each processed xhunk.
13025 */
13026JX9_PRIVATE sxi32 jx9ProcessCsv(
13027 const char *zInput, /* Raw input */
13028 int nByte, /* Input length */
13029 int delim, /* Delimiter */
13030 int encl, /* Enclosure */
13031 int escape, /* Escape character */
13032 sxi32 (*xConsumer)(const char *, int, void *), /* User callback */
13033 void *pUserData /* Last argument to xConsumer() */
13034 )
13035{
13036 const char *zEnd = &zInput[nByte];
13037 const char *zIn = zInput;
13038 const char *zPtr;
13039 int isEnc;
13040 /* Start processing */
13041 for(;;){
13042 if( zIn >= zEnd ){
13043 /* No more input to process */
13044 break;
13045 }
13046 isEnc = 0;
13047 zPtr = zIn;
13048 /* Find the first delimiter */
13049 while( zIn < zEnd ){
13050 if( zIn[0] == delim && !isEnc){
13051 /* Delimiter found, break imediately */
13052 break;
13053 }else if( zIn[0] == encl ){
13054 /* Inside enclosure? */
13055 isEnc = !isEnc;
13056 }else if( zIn[0] == escape ){
13057 /* Escape sequence */
13058 zIn++;
13059 }
13060 /* Advance the cursor */
13061 zIn++;
13062 }
13063 if( zIn > zPtr ){
13064 int nByte = (int)(zIn-zPtr);
13065 sxi32 rc;
13066 /* Invoke the supllied callback */
13067 if( zPtr[0] == encl ){
13068 zPtr++;
13069 nByte-=2;
13070 }
13071 if( nByte > 0 ){
13072 rc = xConsumer(zPtr, nByte, pUserData);
13073 if( rc == SXERR_ABORT ){
13074 /* User callback request an operation abort */
13075 break;
13076 }
13077 }
13078 }
13079 /* Ignore trailing delimiter */
13080 while( zIn < zEnd && zIn[0] == delim ){
13081 zIn++;
13082 }
13083 }
13084 return SXRET_OK;
13085}
13086/*
13087 * Default consumer callback for the CSV parsing routine defined above.
13088 * All the processed input is insereted into an array passed as the last
13089 * argument to this callback.
13090 */
13091JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData)
13092{
13093 jx9_value *pArray = (jx9_value *)pUserData;
13094 jx9_value sEntry;
13095 SyString sToken;
13096 /* Insert the token in the given array */
13097 SyStringInitFromBuf(&sToken, zToken, nTokenLen);
13098 /* Remove trailing and leading white spcaces and null bytes */
13099 SyStringFullTrimSafe(&sToken);
13100 if( sToken.nByte < 1){
13101 return SXRET_OK;
13102 }
13103 jx9MemObjInitFromString(pArray->pVm, &sEntry, &sToken);
13104 jx9_array_add_elem(pArray, 0, &sEntry);
13105 jx9MemObjRelease(&sEntry);
13106 return SXRET_OK;
13107}
13108/*
13109 * array str_getcsv(string $input[, string $delimiter = ', '[, string $enclosure = '"' [, string $escape='\\']]])
13110 * Parse a CSV string into an array.
13111 * Parameters
13112 * $input
13113 * The string to parse.
13114 * $delimiter
13115 * Set the field delimiter (one character only).
13116 * $enclosure
13117 * Set the field enclosure character (one character only).
13118 * $escape
13119 * Set the escape character (one character only). Defaults as a backslash (\)
13120 * Return
13121 * An indexed array containing the CSV fields or NULL on failure.
13122 */
13123static int jx9Builtin_str_getcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
13124{
13125 const char *zInput, *zPtr;
13126 jx9_value *pArray;
13127 int delim = ','; /* Delimiter */
13128 int encl = '"' ; /* Enclosure */
13129 int escape = '\\'; /* Escape character */
13130 int nLen;
13131 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
13132 /* Missing/Invalid arguments, return NULL */
13133 jx9_result_null(pCtx);
13134 return JX9_OK;
13135 }
13136 /* Extract the raw input */
13137 zInput = jx9_value_to_string(apArg[0], &nLen);
13138 if( nArg > 1 ){
13139 int i;
13140 if( jx9_value_is_string(apArg[1]) ){
13141 /* Extract the delimiter */
13142 zPtr = jx9_value_to_string(apArg[1], &i);
13143 if( i > 0 ){
13144 delim = zPtr[0];
13145 }
13146 }
13147 if( nArg > 2 ){
13148 if( jx9_value_is_string(apArg[2]) ){
13149 /* Extract the enclosure */
13150 zPtr = jx9_value_to_string(apArg[2], &i);
13151 if( i > 0 ){
13152 encl = zPtr[0];
13153 }
13154 }
13155 if( nArg > 3 ){
13156 if( jx9_value_is_string(apArg[3]) ){
13157 /* Extract the escape character */
13158 zPtr = jx9_value_to_string(apArg[3], &i);
13159 if( i > 0 ){
13160 escape = zPtr[0];
13161 }
13162 }
13163 }
13164 }
13165 }
13166 /* Create our array */
13167 pArray = jx9_context_new_array(pCtx);
13168 if( pArray == 0 ){
13169 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
13170 jx9_result_null(pCtx);
13171 return JX9_OK;
13172 }
13173 /* Parse the raw input */
13174 jx9ProcessCsv(zInput, nLen, delim, encl, escape, jx9CsvConsumer, pArray);
13175 /* Return the freshly created array */
13176 jx9_result_value(pCtx, pArray);
13177 return JX9_OK;
13178}
13179/*
13180 * Extract a tag name from a raw HTML input and insert it in the given
13181 * container.
13182 * Refer to [strip_tags()].
13183 */
13184static sxi32 AddTag(SySet *pSet, const char *zTag, int nByte)
13185{
13186 const char *zEnd = &zTag[nByte];
13187 const char *zPtr;
13188 SyString sEntry;
13189 /* Strip tags */
13190 for(;;){
13191 while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?'
13192 || zTag[0] == '!' || zTag[0] == '-' || ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
13193 zTag++;
13194 }
13195 if( zTag >= zEnd ){
13196 break;
13197 }
13198 zPtr = zTag;
13199 /* Delimit the tag */
13200 while(zTag < zEnd ){
13201 if( (unsigned char)zTag[0] >= 0xc0 ){
13202 /* UTF-8 stream */
13203 zTag++;
13204 SX_JMP_UTF8(zTag, zEnd);
13205 }else if( !SyisAlphaNum(zTag[0]) ){
13206 break;
13207 }else{
13208 zTag++;
13209 }
13210 }
13211 if( zTag > zPtr ){
13212 /* Perform the insertion */
13213 SyStringInitFromBuf(&sEntry, zPtr, (int)(zTag-zPtr));
13214 SyStringFullTrim(&sEntry);
13215 SySetPut(pSet, (const void *)&sEntry);
13216 }
13217 /* Jump the trailing '>' */
13218 zTag++;
13219 }
13220 return SXRET_OK;
13221}
13222/*
13223 * Check if the given HTML tag name is present in the given container.
13224 * Return SXRET_OK if present.SXERR_NOTFOUND otherwise.
13225 * Refer to [strip_tags()].
13226 */
13227static sxi32 FindTag(SySet *pSet, const char *zTag, int nByte)
13228{
13229 if( SySetUsed(pSet) > 0 ){
13230 const char *zCur, *zEnd = &zTag[nByte];
13231 SyString sTag;
13232 while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' ||
13233 ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
13234 zTag++;
13235 }
13236 /* Delimit the tag */
13237 zCur = zTag;
13238 while(zTag < zEnd ){
13239 if( (unsigned char)zTag[0] >= 0xc0 ){
13240 /* UTF-8 stream */
13241 zTag++;
13242 SX_JMP_UTF8(zTag, zEnd);
13243 }else if( !SyisAlphaNum(zTag[0]) ){
13244 break;
13245 }else{
13246 zTag++;
13247 }
13248 }
13249 SyStringInitFromBuf(&sTag, zCur, zTag-zCur);
13250 /* Trim leading white spaces and null bytes */
13251 SyStringLeftTrimSafe(&sTag);
13252 if( sTag.nByte > 0 ){
13253 SyString *aEntry, *pEntry;
13254 sxi32 rc;
13255 sxu32 n;
13256 /* Perform the lookup */
13257 aEntry = (SyString *)SySetBasePtr(pSet);
13258 for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
13259 pEntry = &aEntry[n];
13260 /* Do the comparison */
13261 rc = SyStringCmp(pEntry, &sTag, SyStrnicmp);
13262 if( !rc ){
13263 return SXRET_OK;
13264 }
13265 }
13266 }
13267 }
13268 /* No such tag */
13269 return SXERR_NOTFOUND;
13270}
13271/*
13272 * This function tries to return a string [i.e: in the call context result buffer]
13273 * with all NUL bytes, HTML and JX9 tags stripped from a given string.
13274 * Refer to [strip_tags()].
13275 */
13276JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen)
13277{
13278 const char *zEnd = &zIn[nByte];
13279 const char *zPtr, *zTag;
13280 SySet sSet;
13281 /* initialize the set of allowed tags */
13282 SySetInit(&sSet, &pCtx->pVm->sAllocator, sizeof(SyString));
13283 if( nTaglen > 0 ){
13284 /* Set of allowed tags */
13285 AddTag(&sSet, zTaglist, nTaglen);
13286 }
13287 /* Set the empty string */
13288 jx9_result_string(pCtx, "", 0);
13289 /* Start processing */
13290 for(;;){
13291 if(zIn >= zEnd){
13292 /* No more input to process */
13293 break;
13294 }
13295 zPtr = zIn;
13296 /* Find a tag */
13297 while( zIn < zEnd && zIn[0] != '<' && zIn[0] != 0 /* NUL byte */ ){
13298 zIn++;
13299 }
13300 if( zIn > zPtr ){
13301 /* Consume raw input */
13302 jx9_result_string(pCtx, zPtr, (int)(zIn-zPtr));
13303 }
13304 /* Ignore trailing null bytes */
13305 while( zIn < zEnd && zIn[0] == 0 ){
13306 zIn++;
13307 }
13308 if(zIn >= zEnd){
13309 /* No more input to process */
13310 break;
13311 }
13312 if( zIn[0] == '<' ){
13313 sxi32 rc;
13314 zTag = zIn++;
13315 /* Delimit the tag */
13316 while( zIn < zEnd && zIn[0] != '>' ){
13317 zIn++;
13318 }
13319 if( zIn < zEnd ){
13320 zIn++; /* Ignore the trailing closing tag */
13321 }
13322 /* Query the set */
13323 rc = FindTag(&sSet, zTag, (int)(zIn-zTag));
13324 if( rc == SXRET_OK ){
13325 /* Keep the tag */
13326 jx9_result_string(pCtx, zTag, (int)(zIn-zTag));
13327 }
13328 }
13329 }
13330 /* Cleanup */
13331 SySetRelease(&sSet);
13332 return SXRET_OK;
13333}
13334/*
13335 * string strip_tags(string $str[, string $allowable_tags])
13336 * Strip HTML and JX9 tags from a string.
13337 * Parameters
13338 * $str
13339 * The input string.
13340 * $allowable_tags
13341 * You can use the optional second parameter to specify tags which should not be stripped.
13342 * Return
13343 * Returns the stripped string.
13344 */
13345static int jx9Builtin_strip_tags(jx9_context *pCtx, int nArg, jx9_value **apArg)
13346{
13347 const char *zTaglist = 0;
13348 const char *zString;
13349 int nTaglen = 0;
13350 int nLen;
13351 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
13352 /* Missing/Invalid arguments, return the empty string */
13353 jx9_result_string(pCtx, "", 0);
13354 return JX9_OK;
13355 }
13356 /* Point to the raw string */
13357 zString = jx9_value_to_string(apArg[0], &nLen);
13358 if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
13359 /* Allowed tag */
13360 zTaglist = jx9_value_to_string(apArg[1], &nTaglen);
13361 }
13362 /* Process input */
13363 jx9StripTagsFromString(pCtx, zString, nLen, zTaglist, nTaglen);
13364 return JX9_OK;
13365}
13366/*
13367 * array str_split(string $string[, int $split_length = 1 ])
13368 * Convert a string to an array.
13369 * Parameters
13370 * $str
13371 * The input string.
13372 * $split_length
13373 * Maximum length of the chunk.
13374 * Return
13375 * If the optional split_length parameter is specified, the returned array
13376 * will be broken down into chunks with each being split_length in length, otherwise
13377 * each chunk will be one character in length. FALSE is returned if split_length is less than 1.
13378 * If the split_length length exceeds the length of string, the entire string is returned
13379 * as the first (and only) array element.
13380 */
13381static int jx9Builtin_str_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
13382{
13383 const char *zString, *zEnd;
13384 jx9_value *pArray, *pValue;
13385 int split_len;
13386 int nLen;
13387 if( nArg < 1 ){
13388 /* Missing arguments, return FALSE */
13389 jx9_result_bool(pCtx, 0);
13390 return JX9_OK;
13391 }
13392 /* Point to the target string */
13393 zString = jx9_value_to_string(apArg[0], &nLen);
13394 if( nLen < 1 ){
13395 /* Nothing to process, return FALSE */
13396 jx9_result_bool(pCtx, 0);
13397 return JX9_OK;
13398 }
13399 split_len = (int)sizeof(char);
13400 if( nArg > 1 ){
13401 /* Split length */
13402 split_len = jx9_value_to_int(apArg[1]);
13403 if( split_len < 1 ){
13404 /* Invalid length, return FALSE */
13405 jx9_result_bool(pCtx, 0);
13406 return JX9_OK;
13407 }
13408 if( split_len > nLen ){
13409 split_len = nLen;
13410 }
13411 }
13412 /* Create the array and the scalar value */
13413 pArray = jx9_context_new_array(pCtx);
13414 /*Chunk value */
13415 pValue = jx9_context_new_scalar(pCtx);
13416 if( pValue == 0 || pArray == 0 ){
13417 /* Return FALSE */
13418 jx9_result_bool(pCtx, 0);
13419 return JX9_OK;
13420 }
13421 /* Point to the end of the string */
13422 zEnd = &zString[nLen];
13423 /* Perform the requested operation */
13424 for(;;){
13425 int nMax;
13426 if( zString >= zEnd ){
13427 /* No more input to process */
13428 break;
13429 }
13430 nMax = (int)(zEnd-zString);
13431 if( nMax < split_len ){
13432 split_len = nMax;
13433 }
13434 /* Copy the current chunk */
13435 jx9_value_string(pValue, zString, split_len);
13436 /* Insert it */
13437 jx9_array_add_elem(pArray, 0, pValue); /* Will make it's own copy */
13438 /* reset the string cursor */
13439 jx9_value_reset_string_cursor(pValue);
13440 /* Update position */
13441 zString += split_len;
13442 }
13443 /*
13444 * Return the array.
13445 * Don't worry about freeing memory, everything will be automatically released
13446 * upon we return from this function.
13447 */
13448 jx9_result_value(pCtx, pArray);
13449 return JX9_OK;
13450}
13451/*
13452 * Tokenize a raw string and extract the first non-space token.
13453 * Refer to [strspn()].
13454 */
13455static sxi32 ExtractNonSpaceToken(const char **pzIn, const char *zEnd, SyString *pOut)
13456{
13457 const char *zIn = *pzIn;
13458 const char *zPtr;
13459 /* Ignore leading white spaces */
13460 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
13461 zIn++;
13462 }
13463 if( zIn >= zEnd ){
13464 /* End of input */
13465 return SXERR_EOF;
13466 }
13467 zPtr = zIn;
13468 /* Extract the token */
13469 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && !SyisSpace(zIn[0]) ){
13470 zIn++;
13471 }
13472 SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
13473 /* Synchronize pointers */
13474 *pzIn = zIn;
13475 /* Return to the caller */
13476 return SXRET_OK;
13477}
13478/*
13479 * Check if the given string contains only characters from the given mask.
13480 * return the longest match.
13481 * Refer to [strspn()].
13482 */
13483static int LongestStringMask(const char *zString, int nLen, const char *zMask, int nMaskLen)
13484{
13485 const char *zEnd = &zString[nLen];
13486 const char *zIn = zString;
13487 int i, c;
13488 for(;;){
13489 if( zString >= zEnd ){
13490 break;
13491 }
13492 /* Extract current character */
13493 c = zString[0];
13494 /* Perform the lookup */
13495 for( i = 0 ; i < nMaskLen ; i++ ){
13496 if( c == zMask[i] ){
13497 /* Character found */
13498 break;
13499 }
13500 }
13501 if( i >= nMaskLen ){
13502 /* Character not in the current mask, break immediately */
13503 break;
13504 }
13505 /* Advance cursor */
13506 zString++;
13507 }
13508 /* Longest match */
13509 return (int)(zString-zIn);
13510}
13511/*
13512 * Do the reverse operation of the previous function [i.e: LongestStringMask()].
13513 * Refer to [strcspn()].
13514 */
13515static int LongestStringMask2(const char *zString, int nLen, const char *zMask, int nMaskLen)
13516{
13517 const char *zEnd = &zString[nLen];
13518 const char *zIn = zString;
13519 int i, c;
13520 for(;;){
13521 if( zString >= zEnd ){
13522 break;
13523 }
13524 /* Extract current character */
13525 c = zString[0];
13526 /* Perform the lookup */
13527 for( i = 0 ; i < nMaskLen ; i++ ){
13528 if( c == zMask[i] ){
13529 break;
13530 }
13531 }
13532 if( i < nMaskLen ){
13533 /* Character in the current mask, break immediately */
13534 break;
13535 }
13536 /* Advance cursor */
13537 zString++;
13538 }
13539 /* Longest match */
13540 return (int)(zString-zIn);
13541}
13542/*
13543 * int strspn(string $str, string $mask[, int $start[, int $length]])
13544 * Finds the length of the initial segment of a string consisting entirely
13545 * of characters contained within a given mask.
13546 * Parameters
13547 * $str
13548 * The input string.
13549 * $mask
13550 * The list of allowable characters.
13551 * $start
13552 * The position in subject to start searching.
13553 * If start is given and is non-negative, then strspn() will begin examining
13554 * subject at the start'th position. For instance, in the string 'abcdef', the character
13555 * at position 0 is 'a', the character at position 2 is 'c', and so forth.
13556 * If start is given and is negative, then strspn() will begin examining subject at the
13557 * start'th position from the end of subject.
13558 * $length
13559 * The length of the segment from subject to examine.
13560 * If length is given and is non-negative, then subject will be examined for length
13561 * characters after the starting position.
13562 * If lengthis given and is negative, then subject will be examined from the starting
13563 * position up to length characters from the end of subject.
13564 * Return
13565 * Returns the length of the initial segment of subject which consists entirely of characters
13566 * in mask.
13567 */
13568static int jx9Builtin_strspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
13569{
13570 const char *zString, *zMask, *zEnd;
13571 int iMasklen, iLen;
13572 SyString sToken;
13573 int iCount = 0;
13574 int rc;
13575 if( nArg < 2 ){
13576 /* Missing agruments, return zero */
13577 jx9_result_int(pCtx, 0);
13578 return JX9_OK;
13579 }
13580 /* Extract the target string */
13581 zString = jx9_value_to_string(apArg[0], &iLen);
13582 /* Extract the mask */
13583 zMask = jx9_value_to_string(apArg[1], &iMasklen);
13584 if( iLen < 1 || iMasklen < 1 ){
13585 /* Nothing to process, return zero */
13586 jx9_result_int(pCtx, 0);
13587 return JX9_OK;
13588 }
13589 if( nArg > 2 ){
13590 int nOfft;
13591 /* Extract the offset */
13592 nOfft = jx9_value_to_int(apArg[2]);
13593 if( nOfft < 0 ){
13594 const char *zBase = &zString[iLen + nOfft];
13595 if( zBase > zString ){
13596 iLen = (int)(&zString[iLen]-zBase);
13597 zString = zBase;
13598 }else{
13599 /* Invalid offset */
13600 jx9_result_int(pCtx, 0);
13601 return JX9_OK;
13602 }
13603 }else{
13604 if( nOfft >= iLen ){
13605 /* Invalid offset */
13606 jx9_result_int(pCtx, 0);
13607 return JX9_OK;
13608 }else{
13609 /* Update offset */
13610 zString += nOfft;
13611 iLen -= nOfft;
13612 }
13613 }
13614 if( nArg > 3 ){
13615 int iUserlen;
13616 /* Extract the desired length */
13617 iUserlen = jx9_value_to_int(apArg[3]);
13618 if( iUserlen > 0 && iUserlen < iLen ){
13619 iLen = iUserlen;
13620 }
13621 }
13622 }
13623 /* Point to the end of the string */
13624 zEnd = &zString[iLen];
13625 /* Extract the first non-space token */
13626 rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
13627 if( rc == SXRET_OK && sToken.nByte > 0 ){
13628 /* Compare against the current mask */
13629 iCount = LongestStringMask(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
13630 }
13631 /* Longest match */
13632 jx9_result_int(pCtx, iCount);
13633 return JX9_OK;
13634}
13635/*
13636 * int strcspn(string $str, string $mask[, int $start[, int $length]])
13637 * Find length of initial segment not matching mask.
13638 * Parameters
13639 * $str
13640 * The input string.
13641 * $mask
13642 * The list of not allowed characters.
13643 * $start
13644 * The position in subject to start searching.
13645 * If start is given and is non-negative, then strspn() will begin examining
13646 * subject at the start'th position. For instance, in the string 'abcdef', the character
13647 * at position 0 is 'a', the character at position 2 is 'c', and so forth.
13648 * If start is given and is negative, then strspn() will begin examining subject at the
13649 * start'th position from the end of subject.
13650 * $length
13651 * The length of the segment from subject to examine.
13652 * If length is given and is non-negative, then subject will be examined for length
13653 * characters after the starting position.
13654 * If lengthis given and is negative, then subject will be examined from the starting
13655 * position up to length characters from the end of subject.
13656 * Return
13657 * Returns the length of the segment as an integer.
13658 */
13659static int jx9Builtin_strcspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
13660{
13661 const char *zString, *zMask, *zEnd;
13662 int iMasklen, iLen;
13663 SyString sToken;
13664 int iCount = 0;
13665 int rc;
13666 if( nArg < 2 ){
13667 /* Missing agruments, return zero */
13668 jx9_result_int(pCtx, 0);
13669 return JX9_OK;
13670 }
13671 /* Extract the target string */
13672 zString = jx9_value_to_string(apArg[0], &iLen);
13673 /* Extract the mask */
13674 zMask = jx9_value_to_string(apArg[1], &iMasklen);
13675 if( iLen < 1 ){
13676 /* Nothing to process, return zero */
13677 jx9_result_int(pCtx, 0);
13678 return JX9_OK;
13679 }
13680 if( iMasklen < 1 ){
13681 /* No given mask, return the string length */
13682 jx9_result_int(pCtx, iLen);
13683 return JX9_OK;
13684 }
13685 if( nArg > 2 ){
13686 int nOfft;
13687 /* Extract the offset */
13688 nOfft = jx9_value_to_int(apArg[2]);
13689 if( nOfft < 0 ){
13690 const char *zBase = &zString[iLen + nOfft];
13691 if( zBase > zString ){
13692 iLen = (int)(&zString[iLen]-zBase);
13693 zString = zBase;
13694 }else{
13695 /* Invalid offset */
13696 jx9_result_int(pCtx, 0);
13697 return JX9_OK;
13698 }
13699 }else{
13700 if( nOfft >= iLen ){
13701 /* Invalid offset */
13702 jx9_result_int(pCtx, 0);
13703 return JX9_OK;
13704 }else{
13705 /* Update offset */
13706 zString += nOfft;
13707 iLen -= nOfft;
13708 }
13709 }
13710 if( nArg > 3 ){
13711 int iUserlen;
13712 /* Extract the desired length */
13713 iUserlen = jx9_value_to_int(apArg[3]);
13714 if( iUserlen > 0 && iUserlen < iLen ){
13715 iLen = iUserlen;
13716 }
13717 }
13718 }
13719 /* Point to the end of the string */
13720 zEnd = &zString[iLen];
13721 /* Extract the first non-space token */
13722 rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
13723 if( rc == SXRET_OK && sToken.nByte > 0 ){
13724 /* Compare against the current mask */
13725 iCount = LongestStringMask2(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
13726 }
13727 /* Longest match */
13728 jx9_result_int(pCtx, iCount);
13729 return JX9_OK;
13730}
13731/*
13732 * string strpbrk(string $haystack, string $char_list)
13733 * Search a string for any of a set of characters.
13734 * Parameters
13735 * $haystack
13736 * The string where char_list is looked for.
13737 * $char_list
13738 * This parameter is case sensitive.
13739 * Return
13740 * Returns a string starting from the character found, or FALSE if it is not found.
13741 */
13742static int jx9Builtin_strpbrk(jx9_context *pCtx, int nArg, jx9_value **apArg)
13743{
13744 const char *zString, *zList, *zEnd;
13745 int iLen, iListLen, i, c;
13746 sxu32 nOfft, nMax;
13747 sxi32 rc;
13748 if( nArg < 2 ){
13749 /* Missing arguments, return FALSE */
13750 jx9_result_bool(pCtx, 0);
13751 return JX9_OK;
13752 }
13753 /* Extract the haystack and the char list */
13754 zString = jx9_value_to_string(apArg[0], &iLen);
13755 zList = jx9_value_to_string(apArg[1], &iListLen);
13756 if( iLen < 1 ){
13757 /* Nothing to process, return FALSE */
13758 jx9_result_bool(pCtx, 0);
13759 return JX9_OK;
13760 }
13761 /* Point to the end of the string */
13762 zEnd = &zString[iLen];
13763 nOfft = nMax = SXU32_HIGH;
13764 /* perform the requested operation */
13765 for( i = 0 ; i < iListLen ; i++ ){
13766 c = zList[i];
13767 rc = SyByteFind(zString, (sxu32)iLen, c, &nMax);
13768 if( rc == SXRET_OK ){
13769 if( nMax < nOfft ){
13770 nOfft = nMax;
13771 }
13772 }
13773 }
13774 if( nOfft == SXU32_HIGH ){
13775 /* No such substring, return FALSE */
13776 jx9_result_bool(pCtx, 0);
13777 }else{
13778 /* Return the substring */
13779 jx9_result_string(pCtx, &zString[nOfft], (int)(zEnd-&zString[nOfft]));
13780 }
13781 return JX9_OK;
13782}
13783/*
13784 * string soundex(string $str)
13785 * Calculate the soundex key of a string.
13786 * Parameters
13787 * $str
13788 * The input string.
13789 * Return
13790 * Returns the soundex key as a string.
13791 * Note:
13792 * This implementation is based on the one found in the SQLite3
13793 * source tree.
13794 */
13795static int jx9Builtin_soundex(jx9_context *pCtx, int nArg, jx9_value **apArg)
13796{
13797 const unsigned char *zIn;
13798 char zResult[8];
13799 int i, j;
13800 static const unsigned char iCode[] = {
13801 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13802 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13803 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13804 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13805 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
13806 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
13807 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
13808 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
13809 };
13810 if( nArg < 1 ){
13811 /* Missing arguments, return the empty string */
13812 jx9_result_string(pCtx, "", 0);
13813 return JX9_OK;
13814 }
13815 zIn = (unsigned char *)jx9_value_to_string(apArg[0], 0);
13816 for(i=0; zIn[i] && zIn[i] < 0xc0 && !SyisAlpha(zIn[i]); i++){}
13817 if( zIn[i] ){
13818 unsigned char prevcode = iCode[zIn[i]&0x7f];
13819 zResult[0] = (char)SyToUpper(zIn[i]);
13820 for(j=1; j<4 && zIn[i]; i++){
13821 int code = iCode[zIn[i]&0x7f];
13822 if( code>0 ){
13823 if( code!=prevcode ){
13824 prevcode = (unsigned char)code;
13825 zResult[j++] = (char)code + '0';
13826 }
13827 }else{
13828 prevcode = 0;
13829 }
13830 }
13831 while( j<4 ){
13832 zResult[j++] = '0';
13833 }
13834 jx9_result_string(pCtx, zResult, 4);
13835 }else{
13836 jx9_result_string(pCtx, "?000", 4);
13837 }
13838 return JX9_OK;
13839}
13840/*
13841 * string wordwrap(string $str[, int $width = 75[, string $break = "\n"]])
13842 * Wraps a string to a given number of characters.
13843 * Parameters
13844 * $str
13845 * The input string.
13846 * $width
13847 * The column width.
13848 * $break
13849 * The line is broken using the optional break parameter.
13850 * Return
13851 * Returns the given string wrapped at the specified column.
13852 */
13853static int jx9Builtin_wordwrap(jx9_context *pCtx, int nArg, jx9_value **apArg)
13854{
13855 const char *zIn, *zEnd, *zBreak;
13856 int iLen, iBreaklen, iChunk;
13857 if( nArg < 1 ){
13858 /* Missing arguments, return the empty string */
13859 jx9_result_string(pCtx, "", 0);
13860 return JX9_OK;
13861 }
13862 /* Extract the input string */
13863 zIn = jx9_value_to_string(apArg[0], &iLen);
13864 if( iLen < 1 ){
13865 /* Nothing to process, return the empty string */
13866 jx9_result_string(pCtx, "", 0);
13867 return JX9_OK;
13868 }
13869 /* Chunk length */
13870 iChunk = 75;
13871 iBreaklen = 0;
13872 zBreak = ""; /* cc warning */
13873 if( nArg > 1 ){
13874 iChunk = jx9_value_to_int(apArg[1]);
13875 if( iChunk < 1 ){
13876 iChunk = 75;
13877 }
13878 if( nArg > 2 ){
13879 zBreak = jx9_value_to_string(apArg[2], &iBreaklen);
13880 }
13881 }
13882 if( iBreaklen < 1 ){
13883 /* Set a default column break */
13884#ifdef __WINNT__
13885 zBreak = "\r\n";
13886 iBreaklen = (int)sizeof("\r\n")-1;
13887#else
13888 zBreak = "\n";
13889 iBreaklen = (int)sizeof(char);
13890#endif
13891 }
13892 /* Perform the requested operation */
13893 zEnd = &zIn[iLen];
13894 for(;;){
13895 int nMax;
13896 if( zIn >= zEnd ){
13897 /* No more input to process */
13898 break;
13899 }
13900 nMax = (int)(zEnd-zIn);
13901 if( iChunk > nMax ){
13902 iChunk = nMax;
13903 }
13904 /* Append the column first */
13905 jx9_result_string(pCtx, zIn, iChunk); /* Will make it's own copy */
13906 /* Advance the cursor */
13907 zIn += iChunk;
13908 if( zIn < zEnd ){
13909 /* Append the line break */
13910 jx9_result_string(pCtx, zBreak, iBreaklen);
13911 }
13912 }
13913 return JX9_OK;
13914}
13915/*
13916 * Check if the given character is a member of the given mask.
13917 * Return TRUE on success. FALSE otherwise.
13918 * Refer to [strtok()].
13919 */
13920static int CheckMask(int c, const char *zMask, int nMasklen, int *pOfft)
13921{
13922 int i;
13923 for( i = 0 ; i < nMasklen ; ++i ){
13924 if( c == zMask[i] ){
13925 if( pOfft ){
13926 *pOfft = i;
13927 }
13928 return TRUE;
13929 }
13930 }
13931 return FALSE;
13932}
13933/*
13934 * Extract a single token from the input stream.
13935 * Refer to [strtok()].
13936 */
13937static sxi32 ExtractToken(const char **pzIn, const char *zEnd, const char *zMask, int nMasklen, SyString *pOut)
13938{
13939 const char *zIn = *pzIn;
13940 const char *zPtr;
13941 /* Ignore leading delimiter */
13942 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && CheckMask(zIn[0], zMask, nMasklen, 0) ){
13943 zIn++;
13944 }
13945 if( zIn >= zEnd ){
13946 /* End of input */
13947 return SXERR_EOF;
13948 }
13949 zPtr = zIn;
13950 /* Extract the token */
13951 while( zIn < zEnd ){
13952 if( (unsigned char)zIn[0] >= 0xc0 ){
13953 /* UTF-8 stream */
13954 zIn++;
13955 SX_JMP_UTF8(zIn, zEnd);
13956 }else{
13957 if( CheckMask(zIn[0], zMask, nMasklen, 0) ){
13958 break;
13959 }
13960 zIn++;
13961 }
13962 }
13963 SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
13964 /* Update the cursor */
13965 *pzIn = zIn;
13966 /* Return to the caller */
13967 return SXRET_OK;
13968}
13969/* strtok auxiliary private data */
13970typedef struct strtok_aux_data strtok_aux_data;
13971struct strtok_aux_data
13972{
13973 const char *zDup; /* Complete duplicate of the input */
13974 const char *zIn; /* Current input stream */
13975 const char *zEnd; /* End of input */
13976};
13977/*
13978 * string strtok(string $str, string $token)
13979 * string strtok(string $token)
13980 * strtok() splits a string (str) into smaller strings (tokens), with each token
13981 * being delimited by any character from token. That is, if you have a string like
13982 * "This is an example string" you could tokenize this string into its individual
13983 * words by using the space character as the token.
13984 * Note that only the first call to strtok uses the string argument. Every subsequent
13985 * call to strtok only needs the token to use, as it keeps track of where it is in
13986 * the current string. To start over, or to tokenize a new string you simply call strtok
13987 * with the string argument again to initialize it. Note that you may put multiple tokens
13988 * in the token parameter. The string will be tokenized when any one of the characters in
13989 * the argument are found.
13990 * Parameters
13991 * $str
13992 * The string being split up into smaller strings (tokens).
13993 * $token
13994 * The delimiter used when splitting up str.
13995 * Return
13996 * Current token or FALSE on EOF.
13997 */
13998static int jx9Builtin_strtok(jx9_context *pCtx, int nArg, jx9_value **apArg)
13999{
14000 strtok_aux_data *pAux;
14001 const char *zMask;
14002 SyString sToken;
14003 int nMasklen;
14004 sxi32 rc;
14005 if( nArg < 2 ){
14006 /* Extract top aux data */
14007 pAux = (strtok_aux_data *)jx9_context_peek_aux_data(pCtx);
14008 if( pAux == 0 ){
14009 /* No aux data, return FALSE */
14010 jx9_result_bool(pCtx, 0);
14011 return JX9_OK;
14012 }
14013 nMasklen = 0;
14014 zMask = ""; /* cc warning */
14015 if( nArg > 0 ){
14016 /* Extract the mask */
14017 zMask = jx9_value_to_string(apArg[0], &nMasklen);
14018 }
14019 if( nMasklen < 1 ){
14020 /* Invalid mask, return FALSE */
14021 jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
14022 jx9_context_free_chunk(pCtx, pAux);
14023 (void)jx9_context_pop_aux_data(pCtx);
14024 jx9_result_bool(pCtx, 0);
14025 return JX9_OK;
14026 }
14027 /* Extract the token */
14028 rc = ExtractToken(&pAux->zIn, pAux->zEnd, zMask, nMasklen, &sToken);
14029 if( rc != SXRET_OK ){
14030 /* EOF , discard the aux data */
14031 jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
14032 jx9_context_free_chunk(pCtx, pAux);
14033 (void)jx9_context_pop_aux_data(pCtx);
14034 jx9_result_bool(pCtx, 0);
14035 }else{
14036 /* Return the extracted token */
14037 jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
14038 }
14039 }else{
14040 const char *zInput, *zCur;
14041 char *zDup;
14042 int nLen;
14043 /* Extract the raw input */
14044 zCur = zInput = jx9_value_to_string(apArg[0], &nLen);
14045 if( nLen < 1 ){
14046 /* Empty input, return FALSE */
14047 jx9_result_bool(pCtx, 0);
14048 return JX9_OK;
14049 }
14050 /* Extract the mask */
14051 zMask = jx9_value_to_string(apArg[1], &nMasklen);
14052 if( nMasklen < 1 ){
14053 /* Set a default mask */
14054#define TOK_MASK " \n\t\r\f"
14055 zMask = TOK_MASK;
14056 nMasklen = (int)sizeof(TOK_MASK) - 1;
14057#undef TOK_MASK
14058 }
14059 /* Extract a single token */
14060 rc = ExtractToken(&zInput, &zInput[nLen], zMask, nMasklen, &sToken);
14061 if( rc != SXRET_OK ){
14062 /* Empty input */
14063 jx9_result_bool(pCtx, 0);
14064 return JX9_OK;
14065 }else{
14066 /* Return the extracted token */
14067 jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
14068 }
14069 /* Create our auxilliary data and copy the input */
14070 pAux = (strtok_aux_data *)jx9_context_alloc_chunk(pCtx, sizeof(strtok_aux_data), TRUE, FALSE);
14071 if( pAux ){
14072 nLen -= (int)(zInput-zCur);
14073 if( nLen < 1 ){
14074 jx9_context_free_chunk(pCtx, pAux);
14075 return JX9_OK;
14076 }
14077 /* Duplicate input */
14078 zDup = (char *)jx9_context_alloc_chunk(pCtx, (unsigned int)(nLen+1), TRUE, FALSE);
14079 if( zDup ){
14080 SyMemcpy(zInput, zDup, (sxu32)nLen);
14081 /* Register the aux data */
14082 pAux->zDup = pAux->zIn = zDup;
14083 pAux->zEnd = &zDup[nLen];
14084 jx9_context_push_aux_data(pCtx, pAux);
14085 }
14086 }
14087 }
14088 return JX9_OK;
14089}
14090/*
14091 * string str_pad(string $input, int $pad_length[, string $pad_string = " " [, int $pad_type = STR_PAD_RIGHT]])
14092 * Pad a string to a certain length with another string
14093 * Parameters
14094 * $input
14095 * The input string.
14096 * $pad_length
14097 * If the value of pad_length is negative, less than, or equal to the length of the input
14098 * string, no padding takes place.
14099 * $pad_string
14100 * Note:
14101 * The pad_string WIIL NOT BE truncated if the required number of padding characters can't be evenly
14102 * divided by the pad_string's length.
14103 * $pad_type
14104 * Optional argument pad_type can be STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH. If pad_type
14105 * is not specified it is assumed to be STR_PAD_RIGHT.
14106 * Return
14107 * The padded string.
14108 */
14109static int jx9Builtin_str_pad(jx9_context *pCtx, int nArg, jx9_value **apArg)
14110{
14111 int iLen, iPadlen, iType, i, iDiv, iStrpad, iRealPad, jPad;
14112 const char *zIn, *zPad;
14113 if( nArg < 2 ){
14114 /* Missing arguments, return the empty string */
14115 jx9_result_string(pCtx, "", 0);
14116 return JX9_OK;
14117 }
14118 /* Extract the target string */
14119 zIn = jx9_value_to_string(apArg[0], &iLen);
14120 /* Padding length */
14121 iRealPad = iPadlen = jx9_value_to_int(apArg[1]);
14122 if( iPadlen > 0 ){
14123 iPadlen -= iLen;
14124 }
14125 if( iPadlen < 1 ){
14126 /* Return the string verbatim */
14127 jx9_result_string(pCtx, zIn, iLen);
14128 return JX9_OK;
14129 }
14130 zPad = " "; /* Whitespace padding */
14131 iStrpad = (int)sizeof(char);
14132 iType = 1 ; /* STR_PAD_RIGHT */
14133 if( nArg > 2 ){
14134 /* Padding string */
14135 zPad = jx9_value_to_string(apArg[2], &iStrpad);
14136 if( iStrpad < 1 ){
14137 /* Empty string */
14138 zPad = " "; /* Whitespace padding */
14139 iStrpad = (int)sizeof(char);
14140 }
14141 if( nArg > 3 ){
14142 /* Padd type */
14143 iType = jx9_value_to_int(apArg[3]);
14144 if( iType != 0 /* STR_PAD_LEFT */ && iType != 2 /* STR_PAD_BOTH */ ){
14145 iType = 1 ; /* STR_PAD_RIGHT */
14146 }
14147 }
14148 }
14149 iDiv = 1;
14150 if( iType == 2 ){
14151 iDiv = 2; /* STR_PAD_BOTH */
14152 }
14153 /* Perform the requested operation */
14154 if( iType == 0 /* STR_PAD_LEFT */ || iType == 2 /* STR_PAD_BOTH */ ){
14155 jPad = iStrpad;
14156 for( i = 0 ; i < iPadlen/iDiv ; i += jPad ){
14157 /* Padding */
14158 if( (int)jx9_context_result_buf_length(pCtx) + iLen + jPad >= iRealPad ){
14159 break;
14160 }
14161 jx9_result_string(pCtx, zPad, jPad);
14162 }
14163 if( iType == 0 /* STR_PAD_LEFT */ ){
14164 while( (int)jx9_context_result_buf_length(pCtx) + iLen < iRealPad ){
14165 jPad = iRealPad - (iLen + (int)jx9_context_result_buf_length(pCtx) );
14166 if( jPad > iStrpad ){
14167 jPad = iStrpad;
14168 }
14169 if( jPad < 1){
14170 break;
14171 }
14172 jx9_result_string(pCtx, zPad, jPad);
14173 }
14174 }
14175 }
14176 if( iLen > 0 ){
14177 /* Append the input string */
14178 jx9_result_string(pCtx, zIn, iLen);
14179 }
14180 if( iType == 1 /* STR_PAD_RIGHT */ || iType == 2 /* STR_PAD_BOTH */ ){
14181 for( i = 0 ; i < iPadlen/iDiv ; i += iStrpad ){
14182 /* Padding */
14183 if( (int)jx9_context_result_buf_length(pCtx) + iStrpad >= iRealPad ){
14184 break;
14185 }
14186 jx9_result_string(pCtx, zPad, iStrpad);
14187 }
14188 while( (int)jx9_context_result_buf_length(pCtx) < iRealPad ){
14189 jPad = iRealPad - (int)jx9_context_result_buf_length(pCtx);
14190 if( jPad > iStrpad ){
14191 jPad = iStrpad;
14192 }
14193 if( jPad < 1){
14194 break;
14195 }
14196 jx9_result_string(pCtx, zPad, jPad);
14197 }
14198 }
14199 return JX9_OK;
14200}
14201/*
14202 * String replacement private data.
14203 */
14204typedef struct str_replace_data str_replace_data;
14205struct str_replace_data
14206{
14207 /* The following two fields are only used by the strtr function */
14208 SyBlob *pWorker; /* Working buffer */
14209 ProcStringMatch xMatch; /* Pattern match routine */
14210 /* The following two fields are only used by the str_replace function */
14211 SySet *pCollector; /* Argument collector*/
14212 jx9_context *pCtx; /* Call context */
14213};
14214/*
14215 * Remove a substring.
14216 */
14217#define STRDEL(SRC, SLEN, OFFT, ILEN){\
14218 for(;;){\
14219 if( OFFT + ILEN >= SLEN ) break; SRC[OFFT] = SRC[OFFT+ILEN]; ++OFFT;\
14220 }\
14221}
14222/*
14223 * Shift right and insert algorithm.
14224 */
14225#define SHIFTRANDINSERT(SRC, LEN, OFFT, ENTRY, ELEN){\
14226 sxu32 INLEN = LEN - OFFT;\
14227 for(;;){\
14228 if( LEN > 0 ){ LEN--; } if(INLEN < 1 ) break; SRC[LEN + ELEN] = SRC[LEN] ; --INLEN; \
14229 }\
14230 for(;;){\
14231 if(ELEN < 1)break; SRC[OFFT] = ENTRY[0]; OFFT++; ENTRY++; --ELEN;\
14232 }\
14233}
14234/*
14235 * Replace all occurrences of the search string at offset (nOfft) with the given
14236 * replacement string [i.e: zReplace].
14237 */
14238static int StringReplace(SyBlob *pWorker, sxu32 nOfft, int nLen, const char *zReplace, int nReplen)
14239{
14240 char *zInput = (char *)SyBlobData(pWorker);
14241 sxu32 n, m;
14242 n = SyBlobLength(pWorker);
14243 m = nOfft;
14244 /* Delete the old entry */
14245 STRDEL(zInput, n, m, nLen);
14246 SyBlobLength(pWorker) -= nLen;
14247 if( nReplen > 0 ){
14248 sxi32 iRep = nReplen;
14249 sxi32 rc;
14250 /*
14251 * Make sure the working buffer is big enough to hold the replacement
14252 * string.
14253 */
14254 rc = SyBlobAppend(pWorker, 0/* Grow without an append operation*/, (sxu32)nReplen);
14255 if( rc != SXRET_OK ){
14256 /* Simply ignore any memory failure problem */
14257 return SXRET_OK;
14258 }
14259 /* Perform the insertion now */
14260 zInput = (char *)SyBlobData(pWorker);
14261 n = SyBlobLength(pWorker);
14262 SHIFTRANDINSERT(zInput, n, nOfft, zReplace, iRep);
14263 SyBlobLength(pWorker) += nReplen;
14264 }
14265 return SXRET_OK;
14266}
14267/*
14268 * String replacement walker callback.
14269 * The following callback is invoked for each array entry that hold
14270 * the replace string.
14271 * Refer to the strtr() implementation for more information.
14272 */
14273static int StringReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
14274{
14275 str_replace_data *pRepData = (str_replace_data *)pUserData;
14276 const char *zTarget, *zReplace;
14277 SyBlob *pWorker;
14278 int tLen, nLen;
14279 sxu32 nOfft;
14280 sxi32 rc;
14281 /* Point to the working buffer */
14282 pWorker = pRepData->pWorker;
14283 if( !jx9_value_is_string(pKey) ){
14284 /* Target and replace must be a string */
14285 return JX9_OK;
14286 }
14287 /* Extract the target and the replace */
14288 zTarget = jx9_value_to_string(pKey, &tLen);
14289 if( tLen < 1 ){
14290 /* Empty target, return immediately */
14291 return JX9_OK;
14292 }
14293 /* Perform a pattern search */
14294 rc = pRepData->xMatch(SyBlobData(pWorker), SyBlobLength(pWorker), (const void *)zTarget, (sxu32)tLen, &nOfft);
14295 if( rc != SXRET_OK ){
14296 /* Pattern not found */
14297 return JX9_OK;
14298 }
14299 /* Extract the replace string */
14300 zReplace = jx9_value_to_string(pData, &nLen);
14301 /* Perform the replace process */
14302 StringReplace(pWorker, nOfft, tLen, zReplace, nLen);
14303 /* All done */
14304 return JX9_OK;
14305}
14306/*
14307 * The following walker callback is invoked by the str_rplace() function inorder
14308 * to collect search/replace string.
14309 * This callback is invoked only if the given argument is of type array.
14310 */
14311static int StrReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
14312{
14313 str_replace_data *pRep = (str_replace_data *)pUserData;
14314 SyString sWorker;
14315 const char *zIn;
14316 int nByte;
14317 /* Extract a string representation of the given argument */
14318 zIn = jx9_value_to_string(pData, &nByte);
14319 SyStringInitFromBuf(&sWorker, 0, 0);
14320 if( nByte > 0 ){
14321 char *zDup;
14322 /* Duplicate the chunk */
14323 zDup = (char *)jx9_context_alloc_chunk(pRep->pCtx, (unsigned int)nByte, FALSE,
14324 TRUE /* Release the chunk automatically, upon this context is destroyd */
14325 );
14326 if( zDup == 0 ){
14327 /* Ignore any memory failure problem */
14328 jx9_context_throw_error(pRep->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
14329 return JX9_OK;
14330 }
14331 SyMemcpy(zIn, zDup, (sxu32)nByte);
14332 /* Save the chunk */
14333 SyStringInitFromBuf(&sWorker, zDup, nByte);
14334 }
14335 /* Save for later processing */
14336 SySetPut(pRep->pCollector, (const void *)&sWorker);
14337 /* All done */
14338 SXUNUSED(pKey); /* cc warning */
14339 return JX9_OK;
14340}
14341/*
14342 * mixed str_replace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
14343 * mixed str_ireplace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
14344 * Replace all occurrences of the search string with the replacement string.
14345 * Parameters
14346 * If search and replace are arrays, then str_replace() takes a value from each
14347 * array and uses them to search and replace on subject. If replace has fewer values
14348 * than search, then an empty string is used for the rest of replacement values.
14349 * If search is an array and replace is a string, then this replacement string is used
14350 * for every value of search. The converse would not make sense, though.
14351 * If search or replace are arrays, their elements are processed first to last.
14352 * $search
14353 * The value being searched for, otherwise known as the needle. An array may be used
14354 * to designate multiple needles.
14355 * $replace
14356 * The replacement value that replaces found search values. An array may be used
14357 * to designate multiple replacements.
14358 * $subject
14359 * The string or array being searched and replaced on, otherwise known as the haystack.
14360 * If subject is an array, then the search and replace is performed with every entry
14361 * of subject, and the return value is an array as well.
14362 * $count (Not used)
14363 * If passed, this will be set to the number of replacements performed.
14364 * Return
14365 * This function returns a string or an array with the replaced values.
14366 */
14367static int jx9Builtin_str_replace(jx9_context *pCtx, int nArg, jx9_value **apArg)
14368{
14369 SyString sTemp, *pSearch, *pReplace;
14370 ProcStringMatch xMatch;
14371 const char *zIn, *zFunc;
14372 str_replace_data sRep;
14373 SyBlob sWorker;
14374 SySet sReplace;
14375 SySet sSearch;
14376 int rep_str;
14377 int nByte;
14378 sxi32 rc;
14379 if( nArg < 3 ){
14380 /* Missing/Invalid arguments, return null */
14381 jx9_result_null(pCtx);
14382 return JX9_OK;
14383 }
14384 /* Initialize fields */
14385 SySetInit(&sSearch, &pCtx->pVm->sAllocator, sizeof(SyString));
14386 SySetInit(&sReplace, &pCtx->pVm->sAllocator, sizeof(SyString));
14387 SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
14388 SyZero(&sRep, sizeof(str_replace_data));
14389 sRep.pCtx = pCtx;
14390 sRep.pCollector = &sSearch;
14391 rep_str = 0;
14392 /* Extract the subject */
14393 zIn = jx9_value_to_string(apArg[2], &nByte);
14394 if( nByte < 1 ){
14395 /* Nothing to replace, return the empty string */
14396 jx9_result_string(pCtx, "", 0);
14397 return JX9_OK;
14398 }
14399 /* Copy the subject */
14400 SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nByte);
14401 /* Search string */
14402 if( jx9_value_is_json_array(apArg[0]) ){
14403 /* Collect search string */
14404 jx9_array_walk(apArg[0], StrReplaceWalker, &sRep);
14405 }else{
14406 /* Single pattern */
14407 zIn = jx9_value_to_string(apArg[0], &nByte);
14408 if( nByte < 1 ){
14409 /* Return the subject untouched since no search string is available */
14410 jx9_result_value(pCtx, apArg[2]/* Subject as thrird argument*/);
14411 return JX9_OK;
14412 }
14413 SyStringInitFromBuf(&sTemp, zIn, nByte);
14414 /* Save for later processing */
14415 SySetPut(&sSearch, (const void *)&sTemp);
14416 }
14417 /* Replace string */
14418 if( jx9_value_is_json_array(apArg[1]) ){
14419 /* Collect replace string */
14420 sRep.pCollector = &sReplace;
14421 jx9_array_walk(apArg[1], StrReplaceWalker, &sRep);
14422 }else{
14423 /* Single needle */
14424 zIn = jx9_value_to_string(apArg[1], &nByte);
14425 rep_str = 1;
14426 SyStringInitFromBuf(&sTemp, zIn, nByte);
14427 /* Save for later processing */
14428 SySetPut(&sReplace, (const void *)&sTemp);
14429 }
14430 /* Reset loop cursors */
14431 SySetResetCursor(&sSearch);
14432 SySetResetCursor(&sReplace);
14433 pReplace = pSearch = 0; /* cc warning */
14434 SyStringInitFromBuf(&sTemp, "", 0);
14435 /* Extract function name */
14436 zFunc = jx9_function_name(pCtx);
14437 /* Set the default pattern match routine */
14438 xMatch = SyBlobSearch;
14439 if( SyStrncmp(zFunc, "str_ireplace", sizeof("str_ireplace") - 1) == 0 ){
14440 /* Case insensitive pattern match */
14441 xMatch = iPatternMatch;
14442 }
14443 /* Start the replace process */
14444 while( SXRET_OK == SySetGetNextEntry(&sSearch, (void **)&pSearch) ){
14445 sxu32 nCount, nOfft;
14446 if( pSearch->nByte < 1 ){
14447 /* Empty string, ignore */
14448 continue;
14449 }
14450 /* Extract the replace string */
14451 if( rep_str ){
14452 pReplace = (SyString *)SySetPeek(&sReplace);
14453 }else{
14454 if( SXRET_OK != SySetGetNextEntry(&sReplace, (void **)&pReplace) ){
14455 /* Sepecial case when 'replace set' has fewer values than the search set.
14456 * An empty string is used for the rest of replacement values
14457 */
14458 pReplace = 0;
14459 }
14460 }
14461 if( pReplace == 0 ){
14462 /* Use an empty string instead */
14463 pReplace = &sTemp;
14464 }
14465 nOfft = nCount = 0;
14466 for(;;){
14467 if( nCount >= SyBlobLength(&sWorker) ){
14468 break;
14469 }
14470 /* Perform a pattern lookup */
14471 rc = xMatch(SyBlobDataAt(&sWorker, nCount), SyBlobLength(&sWorker) - nCount, (const void *)pSearch->zString,
14472 pSearch->nByte, &nOfft);
14473 if( rc != SXRET_OK ){
14474 /* Pattern not found */
14475 break;
14476 }
14477 /* Perform the replace operation */
14478 StringReplace(&sWorker, nCount+nOfft, (int)pSearch->nByte, pReplace->zString, (int)pReplace->nByte);
14479 /* Increment offset counter */
14480 nCount += nOfft + pReplace->nByte;
14481 }
14482 }
14483 /* All done, clean-up the mess left behind */
14484 jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), (int)SyBlobLength(&sWorker));
14485 SySetRelease(&sSearch);
14486 SySetRelease(&sReplace);
14487 SyBlobRelease(&sWorker);
14488 return JX9_OK;
14489}
14490/*
14491 * string strtr(string $str, string $from, string $to)
14492 * string strtr(string $str, array $replace_pairs)
14493 * Translate characters or replace substrings.
14494 * Parameters
14495 * $str
14496 * The string being translated.
14497 * $from
14498 * The string being translated to to.
14499 * $to
14500 * The string replacing from.
14501 * $replace_pairs
14502 * The replace_pairs parameter may be used instead of to and
14503 * from, in which case it's an array in the form array('from' => 'to', ...).
14504 * Return
14505 * The translated string.
14506 * If replace_pairs contains a key which is an empty string (""), FALSE will be returned.
14507 */
14508static int jx9Builtin_strtr(jx9_context *pCtx, int nArg, jx9_value **apArg)
14509{
14510 const char *zIn;
14511 int nLen;
14512 if( nArg < 1 ){
14513 /* Nothing to replace, return FALSE */
14514 jx9_result_bool(pCtx, 0);
14515 return JX9_OK;
14516 }
14517 zIn = jx9_value_to_string(apArg[0], &nLen);
14518 if( nLen < 1 || nArg < 2 ){
14519 /* Invalid arguments */
14520 jx9_result_string(pCtx, zIn, nLen);
14521 return JX9_OK;
14522 }
14523 if( nArg == 2 && jx9_value_is_json_array(apArg[1]) ){
14524 str_replace_data sRepData;
14525 SyBlob sWorker;
14526 /* Initilaize the working buffer */
14527 SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
14528 /* Copy raw string */
14529 SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nLen);
14530 /* Init our replace data instance */
14531 sRepData.pWorker = &sWorker;
14532 sRepData.xMatch = SyBlobSearch;
14533 /* Iterate throw array entries and perform the replace operation.*/
14534 jx9_array_walk(apArg[1], StringReplaceWalker, &sRepData);
14535 /* All done, return the result string */
14536 jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker),
14537 (int)SyBlobLength(&sWorker)); /* Will make it's own copy */
14538 /* Clean-up */
14539 SyBlobRelease(&sWorker);
14540 }else{
14541 int i, flen, tlen, c, iOfft;
14542 const char *zFrom, *zTo;
14543 if( nArg < 3 ){
14544 /* Nothing to replace */
14545 jx9_result_string(pCtx, zIn, nLen);
14546 return JX9_OK;
14547 }
14548 /* Extract given arguments */
14549 zFrom = jx9_value_to_string(apArg[1], &flen);
14550 zTo = jx9_value_to_string(apArg[2], &tlen);
14551 if( flen < 1 || tlen < 1 ){
14552 /* Nothing to replace */
14553 jx9_result_string(pCtx, zIn, nLen);
14554 return JX9_OK;
14555 }
14556 /* Start the replace process */
14557 for( i = 0 ; i < nLen ; ++i ){
14558 c = zIn[i];
14559 if( CheckMask(c, zFrom, flen, &iOfft) ){
14560 if ( iOfft < tlen ){
14561 c = zTo[iOfft];
14562 }
14563 }
14564 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
14565
14566 }
14567 }
14568 return JX9_OK;
14569}
14570/*
14571 * Parse an INI string.
14572 * According to wikipedia
14573 * The INI file format is an informal standard for configuration files for some platforms or software.
14574 * INI files are simple text files with a basic structure composed of "sections" and "properties".
14575 * Format
14576* Properties
14577* The basic element contained in an INI file is the property. Every property has a name and a value
14578* delimited by an equals sign (=). The name appears to the left of the equals sign.
14579* Example:
14580* name=value
14581* Sections
14582* Properties may be grouped into arbitrarily named sections. The section name appears on a line by itself
14583* in square brackets ([ and ]). All properties after the section declaration are associated with that section.
14584* There is no explicit "end of section" delimiter; sections end at the next section declaration
14585* or the end of the file. Sections may not be nested.
14586* Example:
14587* [section]
14588* Comments
14589* Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored.
14590* This function return an array holding parsed values on success.FALSE otherwise.
14591*/
14592JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection)
14593{
14594 jx9_value *pCur, *pArray, *pSection, *pWorker, *pValue;
14595 const char *zCur, *zEnd = &zIn[nByte];
14596 SyHashEntry *pEntry;
14597 SyString sEntry;
14598 SyHash sHash;
14599 int c;
14600 /* Create an empty array and worker variables */
14601 pArray = jx9_context_new_array(pCtx);
14602 pWorker = jx9_context_new_scalar(pCtx);
14603 pValue = jx9_context_new_scalar(pCtx);
14604 if( pArray == 0 || pWorker == 0 || pValue == 0){
14605 /* Out of memory */
14606 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
14607 /* Return FALSE */
14608 jx9_result_bool(pCtx, 0);
14609 return JX9_OK;
14610 }
14611 SyHashInit(&sHash, &pCtx->pVm->sAllocator, 0, 0);
14612 pCur = pArray;
14613 /* Start the parse process */
14614 for(;;){
14615 /* Ignore leading white spaces */
14616 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0])){
14617 zIn++;
14618 }
14619 if( zIn >= zEnd ){
14620 /* No more input to process */
14621 break;
14622 }
14623 if( zIn[0] == ';' || zIn[0] == '#' ){
14624 /* Comment til the end of line */
14625 zIn++;
14626 while(zIn < zEnd && zIn[0] != '\n' ){
14627 zIn++;
14628 }
14629 continue;
14630 }
14631 /* Reset the string cursor of the working variable */
14632 jx9_value_reset_string_cursor(pWorker);
14633 if( zIn[0] == '[' ){
14634 /* Section: Extract the section name */
14635 zIn++;
14636 zCur = zIn;
14637 while( zIn < zEnd && zIn[0] != ']' ){
14638 zIn++;
14639 }
14640 if( zIn > zCur && bProcessSection ){
14641 /* Save the section name */
14642 SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
14643 SyStringFullTrim(&sEntry);
14644 jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
14645 if( sEntry.nByte > 0 ){
14646 /* Associate an array with the section */
14647 pSection = jx9_context_new_array(pCtx);
14648 if( pSection ){
14649 jx9_array_add_elem(pArray, pWorker/*Section name*/, pSection);
14650 pCur = pSection;
14651 }
14652 }
14653 }
14654 zIn++; /* Trailing square brackets ']' */
14655 }else{
14656 jx9_value *pOldCur;
14657 int is_array;
14658 int iLen;
14659 /* Properties */
14660 is_array = 0;
14661 zCur = zIn;
14662 iLen = 0; /* cc warning */
14663 pOldCur = pCur;
14664 while( zIn < zEnd && zIn[0] != '=' ){
14665 if( zIn[0] == '[' && !is_array ){
14666 /* Array */
14667 iLen = (int)(zIn-zCur);
14668 is_array = 1;
14669 if( iLen > 0 ){
14670 jx9_value *pvArr = 0; /* cc warning */
14671 /* Query the hashtable */
14672 SyStringInitFromBuf(&sEntry, zCur, iLen);
14673 SyStringFullTrim(&sEntry);
14674 pEntry = SyHashGet(&sHash, (const void *)sEntry.zString, sEntry.nByte);
14675 if( pEntry ){
14676 pvArr = (jx9_value *)SyHashEntryGetUserData(pEntry);
14677 }else{
14678 /* Create an empty array */
14679 pvArr = jx9_context_new_array(pCtx);
14680 if( pvArr ){
14681 /* Save the entry */
14682 SyHashInsert(&sHash, (const void *)sEntry.zString, sEntry.nByte, pvArr);
14683 /* Insert the entry */
14684 jx9_value_reset_string_cursor(pWorker);
14685 jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
14686 jx9_array_add_elem(pCur, pWorker, pvArr);
14687 jx9_value_reset_string_cursor(pWorker);
14688 }
14689 }
14690 if( pvArr ){
14691 pCur = pvArr;
14692 }
14693 }
14694 while ( zIn < zEnd && zIn[0] != ']' ){
14695 zIn++;
14696 }
14697 }
14698 zIn++;
14699 }
14700 if( !is_array ){
14701 iLen = (int)(zIn-zCur);
14702 }
14703 /* Trim the key */
14704 SyStringInitFromBuf(&sEntry, zCur, iLen);
14705 SyStringFullTrim(&sEntry);
14706 if( sEntry.nByte > 0 ){
14707 if( !is_array ){
14708 /* Save the key name */
14709 jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
14710 }
14711 /* extract key value */
14712 jx9_value_reset_string_cursor(pValue);
14713 zIn++; /* '=' */
14714 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
14715 zIn++;
14716 }
14717 if( zIn < zEnd ){
14718 zCur = zIn;
14719 c = zIn[0];
14720 if( c == '"' || c == '\'' ){
14721 zIn++;
14722 /* Delimit the value */
14723 while( zIn < zEnd ){
14724 if ( zIn[0] == c && zIn[-1] != '\\' ){
14725 break;
14726 }
14727 zIn++;
14728 }
14729 if( zIn < zEnd ){
14730 zIn++;
14731 }
14732 }else{
14733 while( zIn < zEnd ){
14734 if( zIn[0] == '\n' ){
14735 if( zIn[-1] != '\\' ){
14736 break;
14737 }
14738 }else if( zIn[0] == ';' || zIn[0] == '#' ){
14739 /* Inline comments */
14740 break;
14741 }
14742 zIn++;
14743 }
14744 }
14745 /* Trim the value */
14746 SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
14747 SyStringFullTrim(&sEntry);
14748 if( c == '"' || c == '\'' ){
14749 SyStringTrimLeadingChar(&sEntry, c);
14750 SyStringTrimTrailingChar(&sEntry, c);
14751 }
14752 if( sEntry.nByte > 0 ){
14753 jx9_value_string(pValue, sEntry.zString, (int)sEntry.nByte);
14754 }
14755 /* Insert the key and it's value */
14756 jx9_array_add_elem(pCur, is_array ? 0 /*Automatic index assign */: pWorker, pValue);
14757 }
14758 }else{
14759 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && ( SyisSpace(zIn[0]) || zIn[0] == '=' ) ){
14760 zIn++;
14761 }
14762 }
14763 pCur = pOldCur;
14764 }
14765 }
14766 SyHashRelease(&sHash);
14767 /* Return the parse of the INI string */
14768 jx9_result_value(pCtx, pArray);
14769 return SXRET_OK;
14770}
14771/*
14772 * array parse_ini_string(string $ini[, bool $process_sections = false[, int $scanner_mode = INI_SCANNER_NORMAL ]])
14773 * Parse a configuration string.
14774 * Parameters
14775 * $ini
14776 * The contents of the ini file being parsed.
14777 * $process_sections
14778 * By setting the process_sections parameter to TRUE, you get a multidimensional array, with the section names
14779 * and settings included. The default for process_sections is FALSE.
14780 * $scanner_mode (Not used)
14781 * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. If INI_SCANNER_RAW is supplied
14782 * then option values will not be parsed.
14783 * Return
14784 * The settings are returned as an associative array on success, and FALSE on failure.
14785 */
14786static int jx9Builtin_parse_ini_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
14787{
14788 const char *zIni;
14789 int nByte;
14790 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
14791 /* Missing/Invalid arguments, return FALSE*/
14792 jx9_result_bool(pCtx, 0);
14793 return JX9_OK;
14794 }
14795 /* Extract the raw INI buffer */
14796 zIni = jx9_value_to_string(apArg[0], &nByte);
14797 /* Process the INI buffer*/
14798 jx9ParseIniString(pCtx, zIni, (sxu32)nByte, (nArg > 1) ? jx9_value_to_bool(apArg[1]) : 0);
14799 return JX9_OK;
14800}
14801/*
14802 * Ctype Functions.
14803 * Authors:
14804 * Symisc Systems, devel@symisc.net.
14805 * Copyright (C) Symisc Systems, http://jx9.symisc.net
14806 * Status:
14807 * Stable.
14808 */
14809/*
14810 * bool ctype_alnum(string $text)
14811 * Checks if all of the characters in the provided string, text, are alphanumeric.
14812 * Parameters
14813 * $text
14814 * The tested string.
14815 * Return
14816 * TRUE if every character in text is either a letter or a digit, FALSE otherwise.
14817 */
14818static int jx9Builtin_ctype_alnum(jx9_context *pCtx, int nArg, jx9_value **apArg)
14819{
14820 const unsigned char *zIn, *zEnd;
14821 int nLen;
14822 if( nArg < 1 ){
14823 /* Missing arguments, return FALSE */
14824 jx9_result_bool(pCtx, 0);
14825 return JX9_OK;
14826 }
14827 /* Extract the target string */
14828 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
14829 zEnd = &zIn[nLen];
14830 if( nLen < 1 ){
14831 /* Empty string, return FALSE */
14832 jx9_result_bool(pCtx, 0);
14833 return JX9_OK;
14834 }
14835 /* Perform the requested operation */
14836 for(;;){
14837 if( zIn >= zEnd ){
14838 /* If we reach the end of the string, then the test succeeded. */
14839 jx9_result_bool(pCtx, 1);
14840 return JX9_OK;
14841 }
14842 if( !SyisAlphaNum(zIn[0]) ){
14843 break;
14844 }
14845 /* Point to the next character */
14846 zIn++;
14847 }
14848 /* The test failed, return FALSE */
14849 jx9_result_bool(pCtx, 0);
14850 return JX9_OK;
14851}
14852/*
14853 * bool ctype_alpha(string $text)
14854 * Checks if all of the characters in the provided string, text, are alphabetic.
14855 * Parameters
14856 * $text
14857 * The tested string.
14858 * Return
14859 * TRUE if every character in text is a letter from the current locale, FALSE otherwise.
14860 */
14861static int jx9Builtin_ctype_alpha(jx9_context *pCtx, int nArg, jx9_value **apArg)
14862{
14863 const unsigned char *zIn, *zEnd;
14864 int nLen;
14865 if( nArg < 1 ){
14866 /* Missing arguments, return FALSE */
14867 jx9_result_bool(pCtx, 0);
14868 return JX9_OK;
14869 }
14870 /* Extract the target string */
14871 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
14872 zEnd = &zIn[nLen];
14873 if( nLen < 1 ){
14874 /* Empty string, return FALSE */
14875 jx9_result_bool(pCtx, 0);
14876 return JX9_OK;
14877 }
14878 /* Perform the requested operation */
14879 for(;;){
14880 if( zIn >= zEnd ){
14881 /* If we reach the end of the string, then the test succeeded. */
14882 jx9_result_bool(pCtx, 1);
14883 return JX9_OK;
14884 }
14885 if( !SyisAlpha(zIn[0]) ){
14886 break;
14887 }
14888 /* Point to the next character */
14889 zIn++;
14890 }
14891 /* The test failed, return FALSE */
14892 jx9_result_bool(pCtx, 0);
14893 return JX9_OK;
14894}
14895/*
14896 * bool ctype_cntrl(string $text)
14897 * Checks if all of the characters in the provided string, text, are control characters.
14898 * Parameters
14899 * $text
14900 * The tested string.
14901 * Return
14902 * TRUE if every character in text is a control characters, FALSE otherwise.
14903 */
14904static int jx9Builtin_ctype_cntrl(jx9_context *pCtx, int nArg, jx9_value **apArg)
14905{
14906 const unsigned char *zIn, *zEnd;
14907 int nLen;
14908 if( nArg < 1 ){
14909 /* Missing arguments, return FALSE */
14910 jx9_result_bool(pCtx, 0);
14911 return JX9_OK;
14912 }
14913 /* Extract the target string */
14914 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
14915 zEnd = &zIn[nLen];
14916 if( nLen < 1 ){
14917 /* Empty string, return FALSE */
14918 jx9_result_bool(pCtx, 0);
14919 return JX9_OK;
14920 }
14921 /* Perform the requested operation */
14922 for(;;){
14923 if( zIn >= zEnd ){
14924 /* If we reach the end of the string, then the test succeeded. */
14925 jx9_result_bool(pCtx, 1);
14926 return JX9_OK;
14927 }
14928 if( zIn[0] >= 0xc0 ){
14929 /* UTF-8 stream */
14930 break;
14931 }
14932 if( !SyisCtrl(zIn[0]) ){
14933 break;
14934 }
14935 /* Point to the next character */
14936 zIn++;
14937 }
14938 /* The test failed, return FALSE */
14939 jx9_result_bool(pCtx, 0);
14940 return JX9_OK;
14941}
14942/*
14943 * bool ctype_digit(string $text)
14944 * Checks if all of the characters in the provided string, text, are numerical.
14945 * Parameters
14946 * $text
14947 * The tested string.
14948 * Return
14949 * TRUE if every character in the string text is a decimal digit, FALSE otherwise.
14950 */
14951static int jx9Builtin_ctype_digit(jx9_context *pCtx, int nArg, jx9_value **apArg)
14952{
14953 const unsigned char *zIn, *zEnd;
14954 int nLen;
14955 if( nArg < 1 ){
14956 /* Missing arguments, return FALSE */
14957 jx9_result_bool(pCtx, 0);
14958 return JX9_OK;
14959 }
14960 /* Extract the target string */
14961 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
14962 zEnd = &zIn[nLen];
14963 if( nLen < 1 ){
14964 /* Empty string, return FALSE */
14965 jx9_result_bool(pCtx, 0);
14966 return JX9_OK;
14967 }
14968 /* Perform the requested operation */
14969 for(;;){
14970 if( zIn >= zEnd ){
14971 /* If we reach the end of the string, then the test succeeded. */
14972 jx9_result_bool(pCtx, 1);
14973 return JX9_OK;
14974 }
14975 if( zIn[0] >= 0xc0 ){
14976 /* UTF-8 stream */
14977 break;
14978 }
14979 if( !SyisDigit(zIn[0]) ){
14980 break;
14981 }
14982 /* Point to the next character */
14983 zIn++;
14984 }
14985 /* The test failed, return FALSE */
14986 jx9_result_bool(pCtx, 0);
14987 return JX9_OK;
14988}
14989/*
14990 * bool ctype_xdigit(string $text)
14991 * Check for character(s) representing a hexadecimal digit.
14992 * Parameters
14993 * $text
14994 * The tested string.
14995 * Return
14996 * Returns TRUE if every character in text is a hexadecimal 'digit', that is
14997 * a decimal digit or a character from [A-Fa-f] , FALSE otherwise.
14998 */
14999static int jx9Builtin_ctype_xdigit(jx9_context *pCtx, int nArg, jx9_value **apArg)
15000{
15001 const unsigned char *zIn, *zEnd;
15002 int nLen;
15003 if( nArg < 1 ){
15004 /* Missing arguments, return FALSE */
15005 jx9_result_bool(pCtx, 0);
15006 return JX9_OK;
15007 }
15008 /* Extract the target string */
15009 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15010 zEnd = &zIn[nLen];
15011 if( nLen < 1 ){
15012 /* Empty string, return FALSE */
15013 jx9_result_bool(pCtx, 0);
15014 return JX9_OK;
15015 }
15016 /* Perform the requested operation */
15017 for(;;){
15018 if( zIn >= zEnd ){
15019 /* If we reach the end of the string, then the test succeeded. */
15020 jx9_result_bool(pCtx, 1);
15021 return JX9_OK;
15022 }
15023 if( zIn[0] >= 0xc0 ){
15024 /* UTF-8 stream */
15025 break;
15026 }
15027 if( !SyisHex(zIn[0]) ){
15028 break;
15029 }
15030 /* Point to the next character */
15031 zIn++;
15032 }
15033 /* The test failed, return FALSE */
15034 jx9_result_bool(pCtx, 0);
15035 return JX9_OK;
15036}
15037/*
15038 * bool ctype_graph(string $text)
15039 * Checks if all of the characters in the provided string, text, creates visible output.
15040 * Parameters
15041 * $text
15042 * The tested string.
15043 * Return
15044 * Returns TRUE if every character in text is printable and actually creates visible output
15045 * (no white space), FALSE otherwise.
15046 */
15047static int jx9Builtin_ctype_graph(jx9_context *pCtx, int nArg, jx9_value **apArg)
15048{
15049 const unsigned char *zIn, *zEnd;
15050 int nLen;
15051 if( nArg < 1 ){
15052 /* Missing arguments, return FALSE */
15053 jx9_result_bool(pCtx, 0);
15054 return JX9_OK;
15055 }
15056 /* Extract the target string */
15057 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15058 zEnd = &zIn[nLen];
15059 if( nLen < 1 ){
15060 /* Empty string, return FALSE */
15061 jx9_result_bool(pCtx, 0);
15062 return JX9_OK;
15063 }
15064 /* Perform the requested operation */
15065 for(;;){
15066 if( zIn >= zEnd ){
15067 /* If we reach the end of the string, then the test succeeded. */
15068 jx9_result_bool(pCtx, 1);
15069 return JX9_OK;
15070 }
15071 if( zIn[0] >= 0xc0 ){
15072 /* UTF-8 stream */
15073 break;
15074 }
15075 if( !SyisGraph(zIn[0]) ){
15076 break;
15077 }
15078 /* Point to the next character */
15079 zIn++;
15080 }
15081 /* The test failed, return FALSE */
15082 jx9_result_bool(pCtx, 0);
15083 return JX9_OK;
15084}
15085/*
15086 * bool ctype_print(string $text)
15087 * Checks if all of the characters in the provided string, text, are printable.
15088 * Parameters
15089 * $text
15090 * The tested string.
15091 * Return
15092 * Returns TRUE if every character in text will actually create output (including blanks).
15093 * Returns FALSE if text contains control characters or characters that do not have any output
15094 * or control function at all.
15095 */
15096static int jx9Builtin_ctype_print(jx9_context *pCtx, int nArg, jx9_value **apArg)
15097{
15098 const unsigned char *zIn, *zEnd;
15099 int nLen;
15100 if( nArg < 1 ){
15101 /* Missing arguments, return FALSE */
15102 jx9_result_bool(pCtx, 0);
15103 return JX9_OK;
15104 }
15105 /* Extract the target string */
15106 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15107 zEnd = &zIn[nLen];
15108 if( nLen < 1 ){
15109 /* Empty string, return FALSE */
15110 jx9_result_bool(pCtx, 0);
15111 return JX9_OK;
15112 }
15113 /* Perform the requested operation */
15114 for(;;){
15115 if( zIn >= zEnd ){
15116 /* If we reach the end of the string, then the test succeeded. */
15117 jx9_result_bool(pCtx, 1);
15118 return JX9_OK;
15119 }
15120 if( zIn[0] >= 0xc0 ){
15121 /* UTF-8 stream */
15122 break;
15123 }
15124 if( !SyisPrint(zIn[0]) ){
15125 break;
15126 }
15127 /* Point to the next character */
15128 zIn++;
15129 }
15130 /* The test failed, return FALSE */
15131 jx9_result_bool(pCtx, 0);
15132 return JX9_OK;
15133}
15134/*
15135 * bool ctype_punct(string $text)
15136 * Checks if all of the characters in the provided string, text, are punctuation character.
15137 * Parameters
15138 * $text
15139 * The tested string.
15140 * Return
15141 * Returns TRUE if every character in text is printable, but neither letter
15142 * digit or blank, FALSE otherwise.
15143 */
15144static int jx9Builtin_ctype_punct(jx9_context *pCtx, int nArg, jx9_value **apArg)
15145{
15146 const unsigned char *zIn, *zEnd;
15147 int nLen;
15148 if( nArg < 1 ){
15149 /* Missing arguments, return FALSE */
15150 jx9_result_bool(pCtx, 0);
15151 return JX9_OK;
15152 }
15153 /* Extract the target string */
15154 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15155 zEnd = &zIn[nLen];
15156 if( nLen < 1 ){
15157 /* Empty string, return FALSE */
15158 jx9_result_bool(pCtx, 0);
15159 return JX9_OK;
15160 }
15161 /* Perform the requested operation */
15162 for(;;){
15163 if( zIn >= zEnd ){
15164 /* If we reach the end of the string, then the test succeeded. */
15165 jx9_result_bool(pCtx, 1);
15166 return JX9_OK;
15167 }
15168 if( zIn[0] >= 0xc0 ){
15169 /* UTF-8 stream */
15170 break;
15171 }
15172 if( !SyisPunct(zIn[0]) ){
15173 break;
15174 }
15175 /* Point to the next character */
15176 zIn++;
15177 }
15178 /* The test failed, return FALSE */
15179 jx9_result_bool(pCtx, 0);
15180 return JX9_OK;
15181}
15182/*
15183 * bool ctype_space(string $text)
15184 * Checks if all of the characters in the provided string, text, creates whitespace.
15185 * Parameters
15186 * $text
15187 * The tested string.
15188 * Return
15189 * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise.
15190 * Besides the blank character this also includes tab, vertical tab, line feed, carriage return
15191 * and form feed characters.
15192 */
15193static int jx9Builtin_ctype_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
15194{
15195 const unsigned char *zIn, *zEnd;
15196 int nLen;
15197 if( nArg < 1 ){
15198 /* Missing arguments, return FALSE */
15199 jx9_result_bool(pCtx, 0);
15200 return JX9_OK;
15201 }
15202 /* Extract the target string */
15203 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15204 zEnd = &zIn[nLen];
15205 if( nLen < 1 ){
15206 /* Empty string, return FALSE */
15207 jx9_result_bool(pCtx, 0);
15208 return JX9_OK;
15209 }
15210 /* Perform the requested operation */
15211 for(;;){
15212 if( zIn >= zEnd ){
15213 /* If we reach the end of the string, then the test succeeded. */
15214 jx9_result_bool(pCtx, 1);
15215 return JX9_OK;
15216 }
15217 if( zIn[0] >= 0xc0 ){
15218 /* UTF-8 stream */
15219 break;
15220 }
15221 if( !SyisSpace(zIn[0]) ){
15222 break;
15223 }
15224 /* Point to the next character */
15225 zIn++;
15226 }
15227 /* The test failed, return FALSE */
15228 jx9_result_bool(pCtx, 0);
15229 return JX9_OK;
15230}
15231/*
15232 * bool ctype_lower(string $text)
15233 * Checks if all of the characters in the provided string, text, are lowercase letters.
15234 * Parameters
15235 * $text
15236 * The tested string.
15237 * Return
15238 * Returns TRUE if every character in text is a lowercase letter in the current locale.
15239 */
15240static int jx9Builtin_ctype_lower(jx9_context *pCtx, int nArg, jx9_value **apArg)
15241{
15242 const unsigned char *zIn, *zEnd;
15243 int nLen;
15244 if( nArg < 1 ){
15245 /* Missing arguments, return FALSE */
15246 jx9_result_bool(pCtx, 0);
15247 return JX9_OK;
15248 }
15249 /* Extract the target string */
15250 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15251 zEnd = &zIn[nLen];
15252 if( nLen < 1 ){
15253 /* Empty string, return FALSE */
15254 jx9_result_bool(pCtx, 0);
15255 return JX9_OK;
15256 }
15257 /* Perform the requested operation */
15258 for(;;){
15259 if( zIn >= zEnd ){
15260 /* If we reach the end of the string, then the test succeeded. */
15261 jx9_result_bool(pCtx, 1);
15262 return JX9_OK;
15263 }
15264 if( !SyisLower(zIn[0]) ){
15265 break;
15266 }
15267 /* Point to the next character */
15268 zIn++;
15269 }
15270 /* The test failed, return FALSE */
15271 jx9_result_bool(pCtx, 0);
15272 return JX9_OK;
15273}
15274/*
15275 * bool ctype_upper(string $text)
15276 * Checks if all of the characters in the provided string, text, are uppercase letters.
15277 * Parameters
15278 * $text
15279 * The tested string.
15280 * Return
15281 * Returns TRUE if every character in text is a uppercase letter in the current locale.
15282 */
15283static int jx9Builtin_ctype_upper(jx9_context *pCtx, int nArg, jx9_value **apArg)
15284{
15285 const unsigned char *zIn, *zEnd;
15286 int nLen;
15287 if( nArg < 1 ){
15288 /* Missing arguments, return FALSE */
15289 jx9_result_bool(pCtx, 0);
15290 return JX9_OK;
15291 }
15292 /* Extract the target string */
15293 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15294 zEnd = &zIn[nLen];
15295 if( nLen < 1 ){
15296 /* Empty string, return FALSE */
15297 jx9_result_bool(pCtx, 0);
15298 return JX9_OK;
15299 }
15300 /* Perform the requested operation */
15301 for(;;){
15302 if( zIn >= zEnd ){
15303 /* If we reach the end of the string, then the test succeeded. */
15304 jx9_result_bool(pCtx, 1);
15305 return JX9_OK;
15306 }
15307 if( !SyisUpper(zIn[0]) ){
15308 break;
15309 }
15310 /* Point to the next character */
15311 zIn++;
15312 }
15313 /* The test failed, return FALSE */
15314 jx9_result_bool(pCtx, 0);
15315 return JX9_OK;
15316}
15317/*
15318 * Date/Time functions
15319 * Authors:
15320 * Symisc Systems, devel@symisc.net.
15321 * Copyright (C) Symisc Systems, http://jx9.symisc.net
15322 * Status:
15323 * Devel.
15324 */
15325#include <time.h>
15326#ifdef __WINNT__
15327/* GetSystemTime() */
15328#include <Windows.h>
15329#ifdef _WIN32_WCE
15330/*
15331** WindowsCE does not have a localtime() function. So create a
15332** substitute.
15333** Taken from the SQLite3 source tree.
15334** Status: Public domain
15335*/
15336struct tm *__cdecl localtime(const time_t *t)
15337{
15338 static struct tm y;
15339 FILETIME uTm, lTm;
15340 SYSTEMTIME pTm;
15341 jx9_int64 t64;
15342 t64 = *t;
15343 t64 = (t64 + 11644473600)*10000000;
15344 uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF);
15345 uTm.dwHighDateTime= (DWORD)(t64 >> 32);
15346 FileTimeToLocalFileTime(&uTm, &lTm);
15347 FileTimeToSystemTime(&lTm, &pTm);
15348 y.tm_year = pTm.wYear - 1900;
15349 y.tm_mon = pTm.wMonth - 1;
15350 y.tm_wday = pTm.wDayOfWeek;
15351 y.tm_mday = pTm.wDay;
15352 y.tm_hour = pTm.wHour;
15353 y.tm_min = pTm.wMinute;
15354 y.tm_sec = pTm.wSecond;
15355 return &y;
15356}
15357#endif /*_WIN32_WCE */
15358#elif defined(__UNIXES__)
15359#include <sys/time.h>
15360#endif /* __WINNT__*/
15361 /*
15362 * int64 time(void)
15363 * Current Unix timestamp
15364 * Parameters
15365 * None.
15366 * Return
15367 * Returns the current time measured in the number of seconds
15368 * since the Unix Epoch (January 1 1970 00:00:00 GMT).
15369 */
15370static int jx9Builtin_time(jx9_context *pCtx, int nArg, jx9_value **apArg)
15371{
15372 time_t tt;
15373 SXUNUSED(nArg); /* cc warning */
15374 SXUNUSED(apArg);
15375 /* Extract the current time */
15376 time(&tt);
15377 /* Return as 64-bit integer */
15378 jx9_result_int64(pCtx, (jx9_int64)tt);
15379 return JX9_OK;
15380}
15381/*
15382 * string/float microtime([ bool $get_as_float = false ])
15383 * microtime() returns the current Unix timestamp with microseconds.
15384 * Parameters
15385 * $get_as_float
15386 * If used and set to TRUE, microtime() will return a float instead of a string
15387 * as described in the return values section below.
15388 * Return
15389 * By default, microtime() returns a string in the form "msec sec", where sec
15390 * is the current time measured in the number of seconds since the Unix
15391 * epoch (0:00:00 January 1, 1970 GMT), and msec is the number of microseconds
15392 * that have elapsed since sec expressed in seconds.
15393 * If get_as_float is set to TRUE, then microtime() returns a float, which represents
15394 * the current time in seconds since the Unix epoch accurate to the nearest microsecond.
15395 */
15396static int jx9Builtin_microtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
15397{
15398 int bFloat = 0;
15399 sytime sTime;
15400#if defined(__UNIXES__)
15401 struct timeval tv;
15402 gettimeofday(&tv, 0);
15403 sTime.tm_sec = (long)tv.tv_sec;
15404 sTime.tm_usec = (long)tv.tv_usec;
15405#else
15406 time_t tt;
15407 time(&tt);
15408 sTime.tm_sec = (long)tt;
15409 sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
15410#endif /* __UNIXES__ */
15411 if( nArg > 0 ){
15412 bFloat = jx9_value_to_bool(apArg[0]);
15413 }
15414 if( bFloat ){
15415 /* Return as float */
15416 jx9_result_double(pCtx, (double)sTime.tm_sec);
15417 }else{
15418 /* Return as string */
15419 jx9_result_string_format(pCtx, "%ld %ld", sTime.tm_usec, sTime.tm_sec);
15420 }
15421 return JX9_OK;
15422}
15423/*
15424 * array getdate ([ int $timestamp = time() ])
15425 * Get date/time information.
15426 * Parameter
15427 * $timestamp: The optional timestamp parameter is an integer Unix timestamp
15428 * that defaults to the current local time if a timestamp is not given.
15429 * In other words, it defaults to the value of time().
15430 * Returns
15431 * Returns an associative array of information related to the timestamp.
15432 * Elements from the returned associative array are as follows:
15433 * KEY VALUE
15434 * --------- -------
15435 * "seconds" Numeric representation of seconds 0 to 59
15436 * "minutes" Numeric representation of minutes 0 to 59
15437 * "hours" Numeric representation of hours 0 to 23
15438 * "mday" Numeric representation of the day of the month 1 to 31
15439 * "wday" Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
15440 * "mon" Numeric representation of a month 1 through 12
15441 * "year" A full numeric representation of a year, 4 digits Examples: 1999 or 2003
15442 * "yday" Numeric representation of the day of the year 0 through 365
15443 * "weekday" A full textual representation of the day of the week Sunday through Saturday
15444 * "month" A full textual representation of a month, such as January or March January through December
15445 * 0 Seconds since the Unix Epoch, similar to the values returned by time() and used by date().
15446 * NOTE:
15447 * NULL is returned on failure.
15448 */
15449static int jx9Builtin_getdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
15450{
15451 jx9_value *pValue, *pArray;
15452 Sytm sTm;
15453 if( nArg < 1 ){
15454#ifdef __WINNT__
15455 SYSTEMTIME sOS;
15456 GetSystemTime(&sOS);
15457 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
15458#else
15459 struct tm *pTm;
15460 time_t t;
15461 time(&t);
15462 pTm = localtime(&t);
15463 STRUCT_TM_TO_SYTM(pTm, &sTm);
15464#endif
15465 }else{
15466 /* Use the given timestamp */
15467 time_t t;
15468 struct tm *pTm;
15469#ifdef __WINNT__
15470#ifdef _MSC_VER
15471#if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
15472#pragma warning(disable:4996) /* _CRT_SECURE...*/
15473#endif
15474#endif
15475#endif
15476 if( jx9_value_is_int(apArg[0]) ){
15477 t = (time_t)jx9_value_to_int64(apArg[0]);
15478 pTm = localtime(&t);
15479 if( pTm == 0 ){
15480 time(&t);
15481 }
15482 }else{
15483 time(&t);
15484 }
15485 pTm = localtime(&t);
15486 STRUCT_TM_TO_SYTM(pTm, &sTm);
15487 }
15488 /* Element value */
15489 pValue = jx9_context_new_scalar(pCtx);
15490 if( pValue == 0 ){
15491 /* Return NULL */
15492 jx9_result_null(pCtx);
15493 return JX9_OK;
15494 }
15495 /* Create a new array */
15496 pArray = jx9_context_new_array(pCtx);
15497 if( pArray == 0 ){
15498 /* Return NULL */
15499 jx9_result_null(pCtx);
15500 return JX9_OK;
15501 }
15502 /* Fill the array */
15503 /* Seconds */
15504 jx9_value_int(pValue, sTm.tm_sec);
15505 jx9_array_add_strkey_elem(pArray, "seconds", pValue);
15506 /* Minutes */
15507 jx9_value_int(pValue, sTm.tm_min);
15508 jx9_array_add_strkey_elem(pArray, "minutes", pValue);
15509 /* Hours */
15510 jx9_value_int(pValue, sTm.tm_hour);
15511 jx9_array_add_strkey_elem(pArray, "hours", pValue);
15512 /* mday */
15513 jx9_value_int(pValue, sTm.tm_mday);
15514 jx9_array_add_strkey_elem(pArray, "mday", pValue);
15515 /* wday */
15516 jx9_value_int(pValue, sTm.tm_wday);
15517 jx9_array_add_strkey_elem(pArray, "wday", pValue);
15518 /* mon */
15519 jx9_value_int(pValue, sTm.tm_mon+1);
15520 jx9_array_add_strkey_elem(pArray, "mon", pValue);
15521 /* year */
15522 jx9_value_int(pValue, sTm.tm_year);
15523 jx9_array_add_strkey_elem(pArray, "year", pValue);
15524 /* yday */
15525 jx9_value_int(pValue, sTm.tm_yday);
15526 jx9_array_add_strkey_elem(pArray, "yday", pValue);
15527 /* Weekday */
15528 jx9_value_string(pValue, SyTimeGetDay(sTm.tm_wday), -1);
15529 jx9_array_add_strkey_elem(pArray, "weekday", pValue);
15530 /* Month */
15531 jx9_value_reset_string_cursor(pValue);
15532 jx9_value_string(pValue, SyTimeGetMonth(sTm.tm_mon), -1);
15533 jx9_array_add_strkey_elem(pArray, "month", pValue);
15534 /* Seconds since the epoch */
15535 jx9_value_int64(pValue, (jx9_int64)time(0));
15536 jx9_array_add_elem(pArray, 0 /* Index zero */, pValue);
15537 /* Return the freshly created array */
15538 jx9_result_value(pCtx, pArray);
15539 return JX9_OK;
15540}
15541/*
15542 * mixed gettimeofday([ bool $return_float = false ] )
15543 * Returns an associative array containing the data returned from the system call.
15544 * Parameters
15545 * $return_float
15546 * When set to TRUE, a float instead of an array is returned.
15547 * Return
15548 * By default an array is returned. If return_float is set, then
15549 * a float is returned.
15550 */
15551static int jx9Builtin_gettimeofday(jx9_context *pCtx, int nArg, jx9_value **apArg)
15552{
15553 int bFloat = 0;
15554 sytime sTime;
15555#if defined(__UNIXES__)
15556 struct timeval tv;
15557 gettimeofday(&tv, 0);
15558 sTime.tm_sec = (long)tv.tv_sec;
15559 sTime.tm_usec = (long)tv.tv_usec;
15560#else
15561 time_t tt;
15562 time(&tt);
15563 sTime.tm_sec = (long)tt;
15564 sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
15565#endif /* __UNIXES__ */
15566 if( nArg > 0 ){
15567 bFloat = jx9_value_to_bool(apArg[0]);
15568 }
15569 if( bFloat ){
15570 /* Return as float */
15571 jx9_result_double(pCtx, (double)sTime.tm_sec);
15572 }else{
15573 /* Return an associative array */
15574 jx9_value *pValue, *pArray;
15575 /* Create a new array */
15576 pArray = jx9_context_new_array(pCtx);
15577 /* Element value */
15578 pValue = jx9_context_new_scalar(pCtx);
15579 if( pValue == 0 || pArray == 0 ){
15580 /* Return NULL */
15581 jx9_result_null(pCtx);
15582 return JX9_OK;
15583 }
15584 /* Fill the array */
15585 /* sec */
15586 jx9_value_int64(pValue, sTime.tm_sec);
15587 jx9_array_add_strkey_elem(pArray, "sec", pValue);
15588 /* usec */
15589 jx9_value_int64(pValue, sTime.tm_usec);
15590 jx9_array_add_strkey_elem(pArray, "usec", pValue);
15591 /* Return the array */
15592 jx9_result_value(pCtx, pArray);
15593 }
15594 return JX9_OK;
15595}
15596/* Check if the given year is leap or not */
15597#define IS_LEAP_YEAR(YEAR) (YEAR % 400 ? ( YEAR % 100 ? ( YEAR % 4 ? 0 : 1 ) : 0 ) : 1)
15598/* ISO-8601 numeric representation of the day of the week */
15599static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
15600/*
15601 * Format a given date string.
15602 * Supported format: (Taken from JX9 online docs)
15603 * character Description
15604 * d Day of the month
15605 * D A textual representation of a days
15606 * j Day of the month without leading zeros
15607 * l A full textual representation of the day of the week
15608 * N ISO-8601 numeric representation of the day of the week
15609 * w Numeric representation of the day of the week
15610 * z The day of the year (starting from 0)
15611 * F A full textual representation of a month, such as January or March
15612 * m Numeric representation of a month, with leading zeros 01 through 12
15613 * M A short textual representation of a month, three letters Jan through Dec
15614 * n Numeric representation of a month, without leading zeros 1 through 12
15615 * t Number of days in the given month 28 through 31
15616 * L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
15617 * o ISO-8601 year number. This has the same value as Y, except that if the ISO week number
15618 * (W) belongs to the previous or next year, that year is used instead. (added in JX9 5.1.0) Examples: 1999 or 2003
15619 * Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
15620 * y A two digit representation of a year Examples: 99 or 03
15621 * a Lowercase Ante meridiem and Post meridiem am or pm
15622 * A Uppercase Ante meridiem and Post meridiem AM or PM
15623 * g 12-hour format of an hour without leading zeros 1 through 12
15624 * G 24-hour format of an hour without leading zeros 0 through 23
15625 * h 12-hour format of an hour with leading zeros 01 through 12
15626 * H 24-hour format of an hour with leading zeros 00 through 23
15627 * i Minutes with leading zeros 00 to 59
15628 * s Seconds, with leading zeros 00 through 59
15629 * u Microseconds Example: 654321
15630 * e Timezone identifier Examples: UTC, GMT, Atlantic/Azores
15631 * I (capital i) Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise.
15632 * r RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200
15633 * U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
15634 * S English ordinal suffix for the day of the month, 2 characters
15635 * O Difference to Greenwich time (GMT) in hours
15636 * Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those
15637 * east of UTC is always positive.
15638 * c ISO 8601 date
15639 */
15640static sxi32 DateFormat(jx9_context *pCtx, const char *zIn, int nLen, Sytm *pTm)
15641{
15642 const char *zEnd = &zIn[nLen];
15643 const char *zCur;
15644 /* Start the format process */
15645 for(;;){
15646 if( zIn >= zEnd ){
15647 /* No more input to process */
15648 break;
15649 }
15650 switch(zIn[0]){
15651 case 'd':
15652 /* Day of the month, 2 digits with leading zeros */
15653 jx9_result_string_format(pCtx, "%02d", pTm->tm_mday);
15654 break;
15655 case 'D':
15656 /*A textual representation of a day, three letters*/
15657 zCur = SyTimeGetDay(pTm->tm_wday);
15658 jx9_result_string(pCtx, zCur, 3);
15659 break;
15660 case 'j':
15661 /* Day of the month without leading zeros */
15662 jx9_result_string_format(pCtx, "%d", pTm->tm_mday);
15663 break;
15664 case 'l':
15665 /* A full textual representation of the day of the week */
15666 zCur = SyTimeGetDay(pTm->tm_wday);
15667 jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
15668 break;
15669 case 'N':{
15670 /* ISO-8601 numeric representation of the day of the week */
15671 jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
15672 break;
15673 }
15674 case 'w':
15675 /*Numeric representation of the day of the week*/
15676 jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
15677 break;
15678 case 'z':
15679 /*The day of the year*/
15680 jx9_result_string_format(pCtx, "%d", pTm->tm_yday);
15681 break;
15682 case 'F':
15683 /*A full textual representation of a month, such as January or March*/
15684 zCur = SyTimeGetMonth(pTm->tm_mon);
15685 jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
15686 break;
15687 case 'm':
15688 /*Numeric representation of a month, with leading zeros*/
15689 jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
15690 break;
15691 case 'M':
15692 /*A short textual representation of a month, three letters*/
15693 zCur = SyTimeGetMonth(pTm->tm_mon);
15694 jx9_result_string(pCtx, zCur, 3);
15695 break;
15696 case 'n':
15697 /*Numeric representation of a month, without leading zeros*/
15698 jx9_result_string_format(pCtx, "%d", pTm->tm_mon + 1);
15699 break;
15700 case 't':{
15701 static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
15702 int nDays = aMonDays[pTm->tm_mon % 12 ];
15703 if( pTm->tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(pTm->tm_year) ){
15704 nDays = 28;
15705 }
15706 /*Number of days in the given month*/
15707 jx9_result_string_format(pCtx, "%d", nDays);
15708 break;
15709 }
15710 case 'L':{
15711 int isLeap = IS_LEAP_YEAR(pTm->tm_year);
15712 /* Whether it's a leap year */
15713 jx9_result_string_format(pCtx, "%d", isLeap);
15714 break;
15715 }
15716 case 'o':
15717 /* ISO-8601 year number.*/
15718 jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
15719 break;
15720 case 'Y':
15721 /* A full numeric representation of a year, 4 digits */
15722 jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
15723 break;
15724 case 'y':
15725 /*A two digit representation of a year*/
15726 jx9_result_string_format(pCtx, "%02d", pTm->tm_year%100);
15727 break;
15728 case 'a':
15729 /* Lowercase Ante meridiem and Post meridiem */
15730 jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", 2);
15731 break;
15732 case 'A':
15733 /* Uppercase Ante meridiem and Post meridiem */
15734 jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", 2);
15735 break;
15736 case 'g':
15737 /* 12-hour format of an hour without leading zeros*/
15738 jx9_result_string_format(pCtx, "%d", 1+(pTm->tm_hour%12));
15739 break;
15740 case 'G':
15741 /* 24-hour format of an hour without leading zeros */
15742 jx9_result_string_format(pCtx, "%d", pTm->tm_hour);
15743 break;
15744 case 'h':
15745 /* 12-hour format of an hour with leading zeros */
15746 jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
15747 break;
15748 case 'H':
15749 /* 24-hour format of an hour with leading zeros */
15750 jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
15751 break;
15752 case 'i':
15753 /* Minutes with leading zeros */
15754 jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
15755 break;
15756 case 's':
15757 /* second with leading zeros */
15758 jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
15759 break;
15760 case 'u':
15761 /* Microseconds */
15762 jx9_result_string_format(pCtx, "%u", pTm->tm_sec * SX_USEC_PER_SEC);
15763 break;
15764 case 'S':{
15765 /* English ordinal suffix for the day of the month, 2 characters */
15766 static const char zSuffix[] = "thstndrdthththththth";
15767 int v = pTm->tm_mday;
15768 jx9_result_string(pCtx, &zSuffix[2 * (int)(v / 10 % 10 != 1 ? v % 10 : 0)], (int)sizeof(char) * 2);
15769 break;
15770 }
15771 case 'e':
15772 /* Timezone identifier */
15773 zCur = pTm->tm_zone;
15774 if( zCur == 0 ){
15775 /* Assume GMT */
15776 zCur = "GMT";
15777 }
15778 jx9_result_string(pCtx, zCur, -1);
15779 break;
15780 case 'I':
15781 /* Whether or not the date is in daylight saving time */
15782#ifdef __WINNT__
15783#ifdef _MSC_VER
15784#ifndef _WIN32_WCE
15785 _get_daylight(&pTm->tm_isdst);
15786#endif
15787#endif
15788#endif
15789 jx9_result_string_format(pCtx, "%d", pTm->tm_isdst == 1);
15790 break;
15791 case 'r':
15792 /* RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 */
15793 jx9_result_string_format(pCtx, "%.3s, %02d %.3s %4d %02d:%02d:%02d",
15794 SyTimeGetDay(pTm->tm_wday),
15795 pTm->tm_mday,
15796 SyTimeGetMonth(pTm->tm_mon),
15797 pTm->tm_year,
15798 pTm->tm_hour,
15799 pTm->tm_min,
15800 pTm->tm_sec
15801 );
15802 break;
15803 case 'U':{
15804 time_t tt;
15805 /* Seconds since the Unix Epoch */
15806 time(&tt);
15807 jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
15808 break;
15809 }
15810 case 'O':
15811 case 'P':
15812 /* Difference to Greenwich time (GMT) in hours */
15813 jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
15814 break;
15815 case 'Z':
15816 /* Timezone offset in seconds. The offset for timezones west of UTC
15817 * is always negative, and for those east of UTC is always positive.
15818 */
15819 jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
15820 break;
15821 case 'c':
15822 /* ISO 8601 date */
15823 jx9_result_string_format(pCtx, "%4d-%02d-%02dT%02d:%02d:%02d%+05d",
15824 pTm->tm_year,
15825 pTm->tm_mon+1,
15826 pTm->tm_mday,
15827 pTm->tm_hour,
15828 pTm->tm_min,
15829 pTm->tm_sec,
15830 pTm->tm_gmtoff
15831 );
15832 break;
15833 case '\\':
15834 zIn++;
15835 /* Expand verbatim */
15836 if( zIn < zEnd ){
15837 jx9_result_string(pCtx, zIn, (int)sizeof(char));
15838 }
15839 break;
15840 default:
15841 /* Unknown format specifer, expand verbatim */
15842 jx9_result_string(pCtx, zIn, (int)sizeof(char));
15843 break;
15844 }
15845 /* Point to the next character */
15846 zIn++;
15847 }
15848 return SXRET_OK;
15849}
15850/*
15851 * JX9 implementation of the strftime() function.
15852 * The following formats are supported:
15853 * %a An abbreviated textual representation of the day
15854 * %A A full textual representation of the day
15855 * %d Two-digit day of the month (with leading zeros)
15856 * %e Day of the month, with a space preceding single digits.
15857 * %j Day of the year, 3 digits with leading zeros
15858 * %u ISO-8601 numeric representation of the day of the week 1 (for Monday) though 7 (for Sunday)
15859 * %w Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
15860 * %U Week number of the given year, starting with the first Sunday as the first week
15861 * %V ISO-8601:1988 week number of the given year, starting with the first week of the year with at least
15862 * 4 weekdays, with Monday being the start of the week.
15863 * %W A numeric representation of the week of the year
15864 * %b Abbreviated month name, based on the locale
15865 * %B Full month name, based on the locale
15866 * %h Abbreviated month name, based on the locale (an alias of %b)
15867 * %m Two digit representation of the month
15868 * %C Two digit representation of the century (year divided by 100, truncated to an integer)
15869 * %g Two digit representation of the year going by ISO-8601:1988 standards (see %V)
15870 * %G The full four-digit version of %g
15871 * %y Two digit representation of the year
15872 * %Y Four digit representation for the year
15873 * %H Two digit representation of the hour in 24-hour format
15874 * %I Two digit representation of the hour in 12-hour format
15875 * %l (lower-case 'L') Hour in 12-hour format, with a space preceeding single digits
15876 * %M Two digit representation of the minute
15877 * %p UPPER-CASE 'AM' or 'PM' based on the given time
15878 * %P lower-case 'am' or 'pm' based on the given time
15879 * %r Same as "%I:%M:%S %p"
15880 * %R Same as "%H:%M"
15881 * %S Two digit representation of the second
15882 * %T Same as "%H:%M:%S"
15883 * %X Preferred time representation based on locale, without the date
15884 * %z Either the time zone offset from UTC or the abbreviation
15885 * %Z The time zone offset/abbreviation option NOT given by %z
15886 * %c Preferred date and time stamp based on local
15887 * %D Same as "%m/%d/%y"
15888 * %F Same as "%Y-%m-%d"
15889 * %s Unix Epoch Time timestamp (same as the time() function)
15890 * %x Preferred date representation based on locale, without the time
15891 * %n A newline character ("\n")
15892 * %t A Tab character ("\t")
15893 * %% A literal percentage character ("%")
15894 */
15895static int jx9Strftime(
15896 jx9_context *pCtx, /* Call context */
15897 const char *zIn, /* Input string */
15898 int nLen, /* Input length */
15899 Sytm *pTm /* Parse of the given time */
15900 )
15901{
15902 const char *zCur, *zEnd = &zIn[nLen];
15903 int c;
15904 /* Start the format process */
15905 for(;;){
15906 zCur = zIn;
15907 while(zIn < zEnd && zIn[0] != '%' ){
15908 zIn++;
15909 }
15910 if( zIn > zCur ){
15911 /* Consume input verbatim */
15912 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
15913 }
15914 zIn++; /* Jump the percent sign */
15915 if( zIn >= zEnd ){
15916 /* No more input to process */
15917 break;
15918 }
15919 c = zIn[0];
15920 /* Act according to the current specifer */
15921 switch(c){
15922 case '%':
15923 /* A literal percentage character ("%") */
15924 jx9_result_string(pCtx, "%", (int)sizeof(char));
15925 break;
15926 case 't':
15927 /* A Tab character */
15928 jx9_result_string(pCtx, "\t", (int)sizeof(char));
15929 break;
15930 case 'n':
15931 /* A newline character */
15932 jx9_result_string(pCtx, "\n", (int)sizeof(char));
15933 break;
15934 case 'a':
15935 /* An abbreviated textual representation of the day */
15936 jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), (int)sizeof(char)*3);
15937 break;
15938 case 'A':
15939 /* A full textual representation of the day */
15940 jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), -1/*Compute length automatically*/);
15941 break;
15942 case 'e':
15943 /* Day of the month, 2 digits with leading space for single digit*/
15944 jx9_result_string_format(pCtx, "%2d", pTm->tm_mday);
15945 break;
15946 case 'd':
15947 /* Two-digit day of the month (with leading zeros) */
15948 jx9_result_string_format(pCtx, "%02d", pTm->tm_mon+1);
15949 break;
15950 case 'j':
15951 /*The day of the year, 3 digits with leading zeros*/
15952 jx9_result_string_format(pCtx, "%03d", pTm->tm_yday);
15953 break;
15954 case 'u':
15955 /* ISO-8601 numeric representation of the day of the week */
15956 jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
15957 break;
15958 case 'w':
15959 /* Numeric representation of the day of the week */
15960 jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
15961 break;
15962 case 'b':
15963 case 'h':
15964 /*A short textual representation of a month, three letters (Not based on locale)*/
15965 jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), (int)sizeof(char)*3);
15966 break;
15967 case 'B':
15968 /* Full month name (Not based on locale) */
15969 jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), -1/*Compute length automatically*/);
15970 break;
15971 case 'm':
15972 /*Numeric representation of a month, with leading zeros*/
15973 jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
15974 break;
15975 case 'C':
15976 /* Two digit representation of the century */
15977 jx9_result_string_format(pCtx, "%2d", pTm->tm_year/100);
15978 break;
15979 case 'y':
15980 case 'g':
15981 /* Two digit representation of the year */
15982 jx9_result_string_format(pCtx, "%2d", pTm->tm_year%100);
15983 break;
15984 case 'Y':
15985 case 'G':
15986 /* Four digit representation of the year */
15987 jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
15988 break;
15989 case 'I':
15990 /* 12-hour format of an hour with leading zeros */
15991 jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
15992 break;
15993 case 'l':
15994 /* 12-hour format of an hour with leading space */
15995 jx9_result_string_format(pCtx, "%2d", 1+(pTm->tm_hour%12));
15996 break;
15997 case 'H':
15998 /* 24-hour format of an hour with leading zeros */
15999 jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
16000 break;
16001 case 'M':
16002 /* Minutes with leading zeros */
16003 jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
16004 break;
16005 case 'S':
16006 /* Seconds with leading zeros */
16007 jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
16008 break;
16009 case 'z':
16010 case 'Z':
16011 /* Timezone identifier */
16012 zCur = pTm->tm_zone;
16013 if( zCur == 0 ){
16014 /* Assume GMT */
16015 zCur = "GMT";
16016 }
16017 jx9_result_string(pCtx, zCur, -1);
16018 break;
16019 case 'T':
16020 case 'X':
16021 /* Same as "%H:%M:%S" */
16022 jx9_result_string_format(pCtx, "%02d:%02d:%02d", pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
16023 break;
16024 case 'R':
16025 /* Same as "%H:%M" */
16026 jx9_result_string_format(pCtx, "%02d:%02d", pTm->tm_hour, pTm->tm_min);
16027 break;
16028 case 'P':
16029 /* Lowercase Ante meridiem and Post meridiem */
16030 jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", (int)sizeof(char)*2);
16031 break;
16032 case 'p':
16033 /* Uppercase Ante meridiem and Post meridiem */
16034 jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", (int)sizeof(char)*2);
16035 break;
16036 case 'r':
16037 /* Same as "%I:%M:%S %p" */
16038 jx9_result_string_format(pCtx, "%02d:%02d:%02d %s",
16039 1+(pTm->tm_hour%12),
16040 pTm->tm_min,
16041 pTm->tm_sec,
16042 pTm->tm_hour > 12 ? "PM" : "AM"
16043 );
16044 break;
16045 case 'D':
16046 case 'x':
16047 /* Same as "%m/%d/%y" */
16048 jx9_result_string_format(pCtx, "%02d/%02d/%02d",
16049 pTm->tm_mon+1,
16050 pTm->tm_mday,
16051 pTm->tm_year%100
16052 );
16053 break;
16054 case 'F':
16055 /* Same as "%Y-%m-%d" */
16056 jx9_result_string_format(pCtx, "%d-%02d-%02d",
16057 pTm->tm_year,
16058 pTm->tm_mon+1,
16059 pTm->tm_mday
16060 );
16061 break;
16062 case 'c':
16063 jx9_result_string_format(pCtx, "%d-%02d-%02d %02d:%02d:%02d",
16064 pTm->tm_year,
16065 pTm->tm_mon+1,
16066 pTm->tm_mday,
16067 pTm->tm_hour,
16068 pTm->tm_min,
16069 pTm->tm_sec
16070 );
16071 break;
16072 case 's':{
16073 time_t tt;
16074 /* Seconds since the Unix Epoch */
16075 time(&tt);
16076 jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
16077 break;
16078 }
16079 default:
16080 /* unknown specifer, simply ignore*/
16081 break;
16082 }
16083 /* Advance the cursor */
16084 zIn++;
16085 }
16086 return SXRET_OK;
16087}
16088/*
16089 * string date(string $format [, int $timestamp = time() ] )
16090 * Returns a string formatted according to the given format string using
16091 * the given integer timestamp or the current time if no timestamp is given.
16092 * In other words, timestamp is optional and defaults to the value of time().
16093 * Parameters
16094 * $format
16095 * The format of the outputted date string (See code above)
16096 * $timestamp
16097 * The optional timestamp parameter is an integer Unix timestamp
16098 * that defaults to the current local time if a timestamp is not given.
16099 * In other words, it defaults to the value of time().
16100 * Return
16101 * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
16102 */
16103static int jx9Builtin_date(jx9_context *pCtx, int nArg, jx9_value **apArg)
16104{
16105 const char *zFormat;
16106 int nLen;
16107 Sytm sTm;
16108 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
16109 /* Missing/Invalid argument, return FALSE */
16110 jx9_result_bool(pCtx, 0);
16111 return JX9_OK;
16112 }
16113 zFormat = jx9_value_to_string(apArg[0], &nLen);
16114 if( nLen < 1 ){
16115 /* Don't bother processing return the empty string */
16116 jx9_result_string(pCtx, "", 0);
16117 }
16118 if( nArg < 2 ){
16119#ifdef __WINNT__
16120 SYSTEMTIME sOS;
16121 GetSystemTime(&sOS);
16122 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
16123#else
16124 struct tm *pTm;
16125 time_t t;
16126 time(&t);
16127 pTm = localtime(&t);
16128 STRUCT_TM_TO_SYTM(pTm, &sTm);
16129#endif
16130 }else{
16131 /* Use the given timestamp */
16132 time_t t;
16133 struct tm *pTm;
16134 if( jx9_value_is_int(apArg[1]) ){
16135 t = (time_t)jx9_value_to_int64(apArg[1]);
16136 pTm = localtime(&t);
16137 if( pTm == 0 ){
16138 time(&t);
16139 }
16140 }else{
16141 time(&t);
16142 }
16143 pTm = localtime(&t);
16144 STRUCT_TM_TO_SYTM(pTm, &sTm);
16145 }
16146 /* Format the given string */
16147 DateFormat(pCtx, zFormat, nLen, &sTm);
16148 return JX9_OK;
16149}
16150/*
16151 * string strftime(string $format [, int $timestamp = time() ] )
16152 * Format a local time/date (PLATFORM INDEPENDANT IMPLEENTATION NOT BASED ON LOCALE)
16153 * Parameters
16154 * $format
16155 * The format of the outputted date string (See code above)
16156 * $timestamp
16157 * The optional timestamp parameter is an integer Unix timestamp
16158 * that defaults to the current local time if a timestamp is not given.
16159 * In other words, it defaults to the value of time().
16160 * Return
16161 * Returns a string formatted according format using the given timestamp
16162 * or the current local time if no timestamp is given.
16163 */
16164static int jx9Builtin_strftime(jx9_context *pCtx, int nArg, jx9_value **apArg)
16165{
16166 const char *zFormat;
16167 int nLen;
16168 Sytm sTm;
16169 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
16170 /* Missing/Invalid argument, return FALSE */
16171 jx9_result_bool(pCtx, 0);
16172 return JX9_OK;
16173 }
16174 zFormat = jx9_value_to_string(apArg[0], &nLen);
16175 if( nLen < 1 ){
16176 /* Don't bother processing return FALSE */
16177 jx9_result_bool(pCtx, 0);
16178 }
16179 if( nArg < 2 ){
16180#ifdef __WINNT__
16181 SYSTEMTIME sOS;
16182 GetSystemTime(&sOS);
16183 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
16184#else
16185 struct tm *pTm;
16186 time_t t;
16187 time(&t);
16188 pTm = localtime(&t);
16189 STRUCT_TM_TO_SYTM(pTm, &sTm);
16190#endif
16191 }else{
16192 /* Use the given timestamp */
16193 time_t t;
16194 struct tm *pTm;
16195 if( jx9_value_is_int(apArg[1]) ){
16196 t = (time_t)jx9_value_to_int64(apArg[1]);
16197 pTm = localtime(&t);
16198 if( pTm == 0 ){
16199 time(&t);
16200 }
16201 }else{
16202 time(&t);
16203 }
16204 pTm = localtime(&t);
16205 STRUCT_TM_TO_SYTM(pTm, &sTm);
16206 }
16207 /* Format the given string */
16208 jx9Strftime(pCtx, zFormat, nLen, &sTm);
16209 if( jx9_context_result_buf_length(pCtx) < 1 ){
16210 /* Nothing was formatted, return FALSE */
16211 jx9_result_bool(pCtx, 0);
16212 }
16213 return JX9_OK;
16214}
16215/*
16216 * string gmdate(string $format [, int $timestamp = time() ] )
16217 * Identical to the date() function except that the time returned
16218 * is Greenwich Mean Time (GMT).
16219 * Parameters
16220 * $format
16221 * The format of the outputted date string (See code above)
16222 * $timestamp
16223 * The optional timestamp parameter is an integer Unix timestamp
16224 * that defaults to the current local time if a timestamp is not given.
16225 * In other words, it defaults to the value of time().
16226 * Return
16227 * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
16228 */
16229static int jx9Builtin_gmdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
16230{
16231 const char *zFormat;
16232 int nLen;
16233 Sytm sTm;
16234 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
16235 /* Missing/Invalid argument, return FALSE */
16236 jx9_result_bool(pCtx, 0);
16237 return JX9_OK;
16238 }
16239 zFormat = jx9_value_to_string(apArg[0], &nLen);
16240 if( nLen < 1 ){
16241 /* Don't bother processing return the empty string */
16242 jx9_result_string(pCtx, "", 0);
16243 }
16244 if( nArg < 2 ){
16245#ifdef __WINNT__
16246 SYSTEMTIME sOS;
16247 GetSystemTime(&sOS);
16248 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
16249#else
16250 struct tm *pTm;
16251 time_t t;
16252 time(&t);
16253 pTm = gmtime(&t);
16254 STRUCT_TM_TO_SYTM(pTm, &sTm);
16255#endif
16256 }else{
16257 /* Use the given timestamp */
16258 time_t t;
16259 struct tm *pTm;
16260 if( jx9_value_is_int(apArg[1]) ){
16261 t = (time_t)jx9_value_to_int64(apArg[1]);
16262 pTm = gmtime(&t);
16263 if( pTm == 0 ){
16264 time(&t);
16265 }
16266 }else{
16267 time(&t);
16268 }
16269 pTm = gmtime(&t);
16270 STRUCT_TM_TO_SYTM(pTm, &sTm);
16271 }
16272 /* Format the given string */
16273 DateFormat(pCtx, zFormat, nLen, &sTm);
16274 return JX9_OK;
16275}
16276/*
16277 * array localtime([ int $timestamp = time() [, bool $is_associative = false ]])
16278 * Return the local time.
16279 * Parameter
16280 * $timestamp: The optional timestamp parameter is an integer Unix timestamp
16281 * that defaults to the current local time if a timestamp is not given.
16282 * In other words, it defaults to the value of time().
16283 * $is_associative
16284 * If set to FALSE or not supplied then the array is returned as a regular, numerically
16285 * indexed array. If the argument is set to TRUE then localtime() returns an associative
16286 * array containing all the different elements of the structure returned by the C function
16287 * call to localtime. The names of the different keys of the associative array are as follows:
16288 * "tm_sec" - seconds, 0 to 59
16289 * "tm_min" - minutes, 0 to 59
16290 * "tm_hour" - hours, 0 to 23
16291 * "tm_mday" - day of the month, 1 to 31
16292 * "tm_mon" - month of the year, 0 (Jan) to 11 (Dec)
16293 * "tm_year" - years since 1900
16294 * "tm_wday" - day of the week, 0 (Sun) to 6 (Sat)
16295 * "tm_yday" - day of the year, 0 to 365
16296 * "tm_isdst" - is daylight savings time in effect? Positive if yes, 0 if not, negative if unknown.
16297 * Returns
16298 * An associative array of information related to the timestamp.
16299 */
16300static int jx9Builtin_localtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
16301{
16302 jx9_value *pValue, *pArray;
16303 int isAssoc = 0;
16304 Sytm sTm;
16305 if( nArg < 1 ){
16306#ifdef __WINNT__
16307 SYSTEMTIME sOS;
16308 GetSystemTime(&sOS); /* TODO(chems): GMT not local */
16309 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
16310#else
16311 struct tm *pTm;
16312 time_t t;
16313 time(&t);
16314 pTm = localtime(&t);
16315 STRUCT_TM_TO_SYTM(pTm, &sTm);
16316#endif
16317 }else{
16318 /* Use the given timestamp */
16319 time_t t;
16320 struct tm *pTm;
16321 if( jx9_value_is_int(apArg[0]) ){
16322 t = (time_t)jx9_value_to_int64(apArg[0]);
16323 pTm = localtime(&t);
16324 if( pTm == 0 ){
16325 time(&t);
16326 }
16327 }else{
16328 time(&t);
16329 }
16330 pTm = localtime(&t);
16331 STRUCT_TM_TO_SYTM(pTm, &sTm);
16332 }
16333 /* Element value */
16334 pValue = jx9_context_new_scalar(pCtx);
16335 if( pValue == 0 ){
16336 /* Return NULL */
16337 jx9_result_null(pCtx);
16338 return JX9_OK;
16339 }
16340 /* Create a new array */
16341 pArray = jx9_context_new_array(pCtx);
16342 if( pArray == 0 ){
16343 /* Return NULL */
16344 jx9_result_null(pCtx);
16345 return JX9_OK;
16346 }
16347 if( nArg > 1 ){
16348 isAssoc = jx9_value_to_bool(apArg[1]);
16349 }
16350 /* Fill the array */
16351 /* Seconds */
16352 jx9_value_int(pValue, sTm.tm_sec);
16353 if( isAssoc ){
16354 jx9_array_add_strkey_elem(pArray, "tm_sec", pValue);
16355 }else{
16356 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16357 }
16358 /* Minutes */
16359 jx9_value_int(pValue, sTm.tm_min);
16360 if( isAssoc ){
16361 jx9_array_add_strkey_elem(pArray, "tm_min", pValue);
16362 }else{
16363 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16364 }
16365 /* Hours */
16366 jx9_value_int(pValue, sTm.tm_hour);
16367 if( isAssoc ){
16368 jx9_array_add_strkey_elem(pArray, "tm_hour", pValue);
16369 }else{
16370 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16371 }
16372 /* mday */
16373 jx9_value_int(pValue, sTm.tm_mday);
16374 if( isAssoc ){
16375 jx9_array_add_strkey_elem(pArray, "tm_mday", pValue);
16376 }else{
16377 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16378 }
16379 /* mon */
16380 jx9_value_int(pValue, sTm.tm_mon);
16381 if( isAssoc ){
16382 jx9_array_add_strkey_elem(pArray, "tm_mon", pValue);
16383 }else{
16384 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16385 }
16386 /* year since 1900 */
16387 jx9_value_int(pValue, sTm.tm_year-1900);
16388 if( isAssoc ){
16389 jx9_array_add_strkey_elem(pArray, "tm_year", pValue);
16390 }else{
16391 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16392 }
16393 /* wday */
16394 jx9_value_int(pValue, sTm.tm_wday);
16395 if( isAssoc ){
16396 jx9_array_add_strkey_elem(pArray, "tm_wday", pValue);
16397 }else{
16398 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16399 }
16400 /* yday */
16401 jx9_value_int(pValue, sTm.tm_yday);
16402 if( isAssoc ){
16403 jx9_array_add_strkey_elem(pArray, "tm_yday", pValue);
16404 }else{
16405 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16406 }
16407 /* isdst */
16408#ifdef __WINNT__
16409#ifdef _MSC_VER
16410#ifndef _WIN32_WCE
16411 _get_daylight(&sTm.tm_isdst);
16412#endif
16413#endif
16414#endif
16415 jx9_value_int(pValue, sTm.tm_isdst);
16416 if( isAssoc ){
16417 jx9_array_add_strkey_elem(pArray, "tm_isdst", pValue);
16418 }else{
16419 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16420 }
16421 /* Return the array */
16422 jx9_result_value(pCtx, pArray);
16423 return JX9_OK;
16424}
16425/*
16426 * int idate(string $format [, int $timestamp = time() ])
16427 * Returns a number formatted according to the given format string
16428 * using the given integer timestamp or the current local time if
16429 * no timestamp is given. In other words, timestamp is optional and defaults
16430 * to the value of time().
16431 * Unlike the function date(), idate() accepts just one char in the format
16432 * parameter.
16433 * $Parameters
16434 * Supported format
16435 * d Day of the month
16436 * h Hour (12 hour format)
16437 * H Hour (24 hour format)
16438 * i Minutes
16439 * I (uppercase i)1 if DST is activated, 0 otherwise
16440 * L (uppercase l) returns 1 for leap year, 0 otherwise
16441 * m Month number
16442 * s Seconds
16443 * t Days in current month
16444 * U Seconds since the Unix Epoch - January 1 1970 00:00:00 UTC - this is the same as time()
16445 * w Day of the week (0 on Sunday)
16446 * W ISO-8601 week number of year, weeks starting on Monday
16447 * y Year (1 or 2 digits - check note below)
16448 * Y Year (4 digits)
16449 * z Day of the year
16450 * Z Timezone offset in seconds
16451 * $timestamp
16452 * The optional timestamp parameter is an integer Unix timestamp that defaults
16453 * to the current local time if a timestamp is not given. In other words, it defaults
16454 * to the value of time().
16455 * Return
16456 * An integer.
16457 */
16458static int jx9Builtin_idate(jx9_context *pCtx, int nArg, jx9_value **apArg)
16459{
16460 const char *zFormat;
16461 jx9_int64 iVal = 0;
16462 int nLen;
16463 Sytm sTm;
16464 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
16465 /* Missing/Invalid argument, return -1 */
16466 jx9_result_int(pCtx, -1);
16467 return JX9_OK;
16468 }
16469 zFormat = jx9_value_to_string(apArg[0], &nLen);
16470 if( nLen < 1 ){
16471 /* Don't bother processing return -1*/
16472 jx9_result_int(pCtx, -1);
16473 }
16474 if( nArg < 2 ){
16475#ifdef __WINNT__
16476 SYSTEMTIME sOS;
16477 GetSystemTime(&sOS);
16478 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
16479#else
16480 struct tm *pTm;
16481 time_t t;
16482 time(&t);
16483 pTm = localtime(&t);
16484 STRUCT_TM_TO_SYTM(pTm, &sTm);
16485#endif
16486 }else{
16487 /* Use the given timestamp */
16488 time_t t;
16489 struct tm *pTm;
16490 if( jx9_value_is_int(apArg[1]) ){
16491 t = (time_t)jx9_value_to_int64(apArg[1]);
16492 pTm = localtime(&t);
16493 if( pTm == 0 ){
16494 time(&t);
16495 }
16496 }else{
16497 time(&t);
16498 }
16499 pTm = localtime(&t);
16500 STRUCT_TM_TO_SYTM(pTm, &sTm);
16501 }
16502 /* Perform the requested operation */
16503 switch(zFormat[0]){
16504 case 'd':
16505 /* Day of the month */
16506 iVal = sTm.tm_mday;
16507 break;
16508 case 'h':
16509 /* Hour (12 hour format)*/
16510 iVal = 1 + (sTm.tm_hour % 12);
16511 break;
16512 case 'H':
16513 /* Hour (24 hour format)*/
16514 iVal = sTm.tm_hour;
16515 break;
16516 case 'i':
16517 /*Minutes*/
16518 iVal = sTm.tm_min;
16519 break;
16520 case 'I':
16521 /* returns 1 if DST is activated, 0 otherwise */
16522#ifdef __WINNT__
16523#ifdef _MSC_VER
16524#ifndef _WIN32_WCE
16525 _get_daylight(&sTm.tm_isdst);
16526#endif
16527#endif
16528#endif
16529 iVal = sTm.tm_isdst;
16530 break;
16531 case 'L':
16532 /* returns 1 for leap year, 0 otherwise */
16533 iVal = IS_LEAP_YEAR(sTm.tm_year);
16534 break;
16535 case 'm':
16536 /* Month number*/
16537 iVal = sTm.tm_mon;
16538 break;
16539 case 's':
16540 /*Seconds*/
16541 iVal = sTm.tm_sec;
16542 break;
16543 case 't':{
16544 /*Days in current month*/
16545 static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
16546 int nDays = aMonDays[sTm.tm_mon % 12 ];
16547 if( sTm.tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(sTm.tm_year) ){
16548 nDays = 28;
16549 }
16550 iVal = nDays;
16551 break;
16552 }
16553 case 'U':
16554 /*Seconds since the Unix Epoch*/
16555 iVal = (jx9_int64)time(0);
16556 break;
16557 case 'w':
16558 /* Day of the week (0 on Sunday) */
16559 iVal = sTm.tm_wday;
16560 break;
16561 case 'W': {
16562 /* ISO-8601 week number of year, weeks starting on Monday */
16563 static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
16564 iVal = aISO8601[sTm.tm_wday % 7 ];
16565 break;
16566 }
16567 case 'y':
16568 /* Year (2 digits) */
16569 iVal = sTm.tm_year % 100;
16570 break;
16571 case 'Y':
16572 /* Year (4 digits) */
16573 iVal = sTm.tm_year;
16574 break;
16575 case 'z':
16576 /* Day of the year */
16577 iVal = sTm.tm_yday;
16578 break;
16579 case 'Z':
16580 /*Timezone offset in seconds*/
16581 iVal = sTm.tm_gmtoff;
16582 break;
16583 default:
16584 /* unknown format, throw a warning */
16585 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Unknown date format token");
16586 break;
16587 }
16588 /* Return the time value */
16589 jx9_result_int64(pCtx, iVal);
16590 return JX9_OK;
16591}
16592/*
16593 * int mktime/gmmktime([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s")
16594 * [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] )
16595 * Returns the Unix timestamp corresponding to the arguments given. This timestamp is a 64bit integer
16596 * containing the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time
16597 * specified.
16598 * Arguments may be left out in order from right to left; any arguments thus omitted will be set to
16599 * the current value according to the local date and time.
16600 * Parameters
16601 * $hour
16602 * The number of the hour relevant to the start of the day determined by month, day and year.
16603 * Negative values reference the hour before midnight of the day in question. Values greater
16604 * than 23 reference the appropriate hour in the following day(s).
16605 * $minute
16606 * The number of the minute relevant to the start of the hour. Negative values reference
16607 * the minute in the previous hour. Values greater than 59 reference the appropriate minute
16608 * in the following hour(s).
16609 * $second
16610 * The number of seconds relevant to the start of the minute. Negative values reference
16611 * the second in the previous minute. Values greater than 59 reference the appropriate
16612 * second in the following minute(s).
16613 * $month
16614 * The number of the month relevant to the end of the previous year. Values 1 to 12 reference
16615 * the normal calendar months of the year in question. Values less than 1 (including negative values)
16616 * reference the months in the previous year in reverse order, so 0 is December, -1 is November)...
16617 * $day
16618 * The number of the day relevant to the end of the previous month. Values 1 to 28, 29, 30 or 31
16619 * (depending upon the month) reference the normal days in the relevant month. Values less than 1
16620 * (including negative values) reference the days in the previous month, so 0 is the last day
16621 * of the previous month, -1 is the day before that, etc. Values greater than the number of days
16622 * in the relevant month reference the appropriate day in the following month(s).
16623 * $year
16624 * The number of the year, may be a two or four digit value, with values between 0-69 mapping
16625 * to 2000-2069 and 70-100 to 1970-2000. On systems where time_t is a 32bit signed integer, as
16626 * most common today, the valid range for year is somewhere between 1901 and 2038.
16627 * $is_dst
16628 * This parameter can be set to 1 if the time is during daylight savings time (DST), 0 if it is not,
16629 * or -1 (the default) if it is unknown whether the time is within daylight savings time or not.
16630 * Return
16631 * mktime() returns the Unix timestamp of the arguments given.
16632 * If the arguments are invalid, the function returns FALSE
16633 */
16634static int jx9Builtin_mktime(jx9_context *pCtx, int nArg, jx9_value **apArg)
16635{
16636 const char *zFunction;
16637 jx9_int64 iVal = 0;
16638 struct tm *pTm;
16639 time_t t;
16640 /* Extract function name */
16641 zFunction = jx9_function_name(pCtx);
16642 /* Get the current time */
16643 time(&t);
16644 if( zFunction[0] == 'g' /* gmmktime */ ){
16645 pTm = gmtime(&t);
16646 }else{
16647 /* localtime */
16648 pTm = localtime(&t);
16649 }
16650 if( nArg > 0 ){
16651 int iVal;
16652 /* Hour */
16653 iVal = jx9_value_to_int(apArg[0]);
16654 pTm->tm_hour = iVal;
16655 if( nArg > 1 ){
16656 /* Minutes */
16657 iVal = jx9_value_to_int(apArg[1]);
16658 pTm->tm_min = iVal;
16659 if( nArg > 2 ){
16660 /* Seconds */
16661 iVal = jx9_value_to_int(apArg[2]);
16662 pTm->tm_sec = iVal;
16663 if( nArg > 3 ){
16664 /* Month */
16665 iVal = jx9_value_to_int(apArg[3]);
16666 pTm->tm_mon = iVal - 1;
16667 if( nArg > 4 ){
16668 /* mday */
16669 iVal = jx9_value_to_int(apArg[4]);
16670 pTm->tm_mday = iVal;
16671 if( nArg > 5 ){
16672 /* Year */
16673 iVal = jx9_value_to_int(apArg[5]);
16674 if( iVal > 1900 ){
16675 iVal -= 1900;
16676 }
16677 pTm->tm_year = iVal;
16678 if( nArg > 6 ){
16679 /* is_dst */
16680 iVal = jx9_value_to_bool(apArg[6]);
16681 pTm->tm_isdst = iVal;
16682 }
16683 }
16684 }
16685 }
16686 }
16687 }
16688 }
16689 /* Make the time */
16690 iVal = (jx9_int64)mktime(pTm);
16691 /* Return the timesatmp as a 64bit integer */
16692 jx9_result_int64(pCtx, iVal);
16693 return JX9_OK;
16694}
16695/*
16696 * Section:
16697 * URL handling Functions.
16698 * Authors:
16699 * Symisc Systems, devel@symisc.net.
16700 * Copyright (C) Symisc Systems, http://jx9.symisc.net
16701 * Status:
16702 * Stable.
16703 */
16704/*
16705 * Output consumer callback for the standard Symisc routines.
16706 * [i.e: SyBase64Encode(), SyBase64Decode(), SyUriEncode(), ...].
16707 */
16708static int Consumer(const void *pData, unsigned int nLen, void *pUserData)
16709{
16710 /* Store in the call context result buffer */
16711 jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
16712 return SXRET_OK;
16713}
16714/*
16715 * string base64_encode(string $data)
16716 * string convert_uuencode(string $data)
16717 * Encodes data with MIME base64
16718 * Parameter
16719 * $data
16720 * Data to encode
16721 * Return
16722 * Encoded data or FALSE on failure.
16723 */
16724static int jx9Builtin_base64_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
16725{
16726 const char *zIn;
16727 int nLen;
16728 if( nArg < 1 ){
16729 /* Missing arguments, return FALSE */
16730 jx9_result_bool(pCtx, 0);
16731 return JX9_OK;
16732 }
16733 /* Extract the input string */
16734 zIn = jx9_value_to_string(apArg[0], &nLen);
16735 if( nLen < 1 ){
16736 /* Nothing to process, return FALSE */
16737 jx9_result_bool(pCtx, 0);
16738 return JX9_OK;
16739 }
16740 /* Perform the BASE64 encoding */
16741 SyBase64Encode(zIn, (sxu32)nLen, Consumer, pCtx);
16742 return JX9_OK;
16743}
16744/*
16745 * string base64_decode(string $data)
16746 * string convert_uudecode(string $data)
16747 * Decodes data encoded with MIME base64
16748 * Parameter
16749 * $data
16750 * Encoded data.
16751 * Return
16752 * Returns the original data or FALSE on failure.
16753 */
16754static int jx9Builtin_base64_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
16755{
16756 const char *zIn;
16757 int nLen;
16758 if( nArg < 1 ){
16759 /* Missing arguments, return FALSE */
16760 jx9_result_bool(pCtx, 0);
16761 return JX9_OK;
16762 }
16763 /* Extract the input string */
16764 zIn = jx9_value_to_string(apArg[0], &nLen);
16765 if( nLen < 1 ){
16766 /* Nothing to process, return FALSE */
16767 jx9_result_bool(pCtx, 0);
16768 return JX9_OK;
16769 }
16770 /* Perform the BASE64 decoding */
16771 SyBase64Decode(zIn, (sxu32)nLen, Consumer, pCtx);
16772 return JX9_OK;
16773}
16774/*
16775 * string urlencode(string $str)
16776 * URL encoding
16777 * Parameter
16778 * $data
16779 * Input string.
16780 * Return
16781 * Returns a string in which all non-alphanumeric characters except -_. have
16782 * been replaced with a percent (%) sign followed by two hex digits and spaces
16783 * encoded as plus (+) signs.
16784 */
16785static int jx9Builtin_urlencode(jx9_context *pCtx, int nArg, jx9_value **apArg)
16786{
16787 const char *zIn;
16788 int nLen;
16789 if( nArg < 1 ){
16790 /* Missing arguments, return FALSE */
16791 jx9_result_bool(pCtx, 0);
16792 return JX9_OK;
16793 }
16794 /* Extract the input string */
16795 zIn = jx9_value_to_string(apArg[0], &nLen);
16796 if( nLen < 1 ){
16797 /* Nothing to process, return FALSE */
16798 jx9_result_bool(pCtx, 0);
16799 return JX9_OK;
16800 }
16801 /* Perform the URL encoding */
16802 SyUriEncode(zIn, (sxu32)nLen, Consumer, pCtx);
16803 return JX9_OK;
16804}
16805/*
16806 * string urldecode(string $str)
16807 * Decodes any %## encoding in the given string.
16808 * Plus symbols ('+') are decoded to a space character.
16809 * Parameter
16810 * $data
16811 * Input string.
16812 * Return
16813 * Decoded URL or FALSE on failure.
16814 */
16815static int jx9Builtin_urldecode(jx9_context *pCtx, int nArg, jx9_value **apArg)
16816{
16817 const char *zIn;
16818 int nLen;
16819 if( nArg < 1 ){
16820 /* Missing arguments, return FALSE */
16821 jx9_result_bool(pCtx, 0);
16822 return JX9_OK;
16823 }
16824 /* Extract the input string */
16825 zIn = jx9_value_to_string(apArg[0], &nLen);
16826 if( nLen < 1 ){
16827 /* Nothing to process, return FALSE */
16828 jx9_result_bool(pCtx, 0);
16829 return JX9_OK;
16830 }
16831 /* Perform the URL decoding */
16832 SyUriDecode(zIn, (sxu32)nLen, Consumer, pCtx, TRUE);
16833 return JX9_OK;
16834}
16835#endif /* JX9_DISABLE_BUILTIN_FUNC */
16836/* Table of the built-in functions */
16837static const jx9_builtin_func aBuiltInFunc[] = {
16838 /* Variable handling functions */
16839 { "is_bool" , jx9Builtin_is_bool },
16840 { "is_float" , jx9Builtin_is_float },
16841 { "is_real" , jx9Builtin_is_float },
16842 { "is_double" , jx9Builtin_is_float },
16843 { "is_int" , jx9Builtin_is_int },
16844 { "is_integer" , jx9Builtin_is_int },
16845 { "is_long" , jx9Builtin_is_int },
16846 { "is_string" , jx9Builtin_is_string },
16847 { "is_null" , jx9Builtin_is_null },
16848 { "is_numeric" , jx9Builtin_is_numeric },
16849 { "is_scalar" , jx9Builtin_is_scalar },
16850 { "is_array" , jx9Builtin_is_array },
16851 { "is_object" , jx9Builtin_is_object },
16852 { "is_resource", jx9Builtin_is_resource },
16853 { "douleval" , jx9Builtin_floatval },
16854 { "floatval" , jx9Builtin_floatval },
16855 { "intval" , jx9Builtin_intval },
16856 { "strval" , jx9Builtin_strval },
16857 { "empty" , jx9Builtin_empty },
16858#ifndef JX9_DISABLE_BUILTIN_FUNC
16859#ifdef JX9_ENABLE_MATH_FUNC
16860 /* Math functions */
16861 { "abs" , jx9Builtin_abs },
16862 { "sqrt" , jx9Builtin_sqrt },
16863 { "exp" , jx9Builtin_exp },
16864 { "floor", jx9Builtin_floor },
16865 { "cos" , jx9Builtin_cos },
16866 { "sin" , jx9Builtin_sin },
16867 { "acos" , jx9Builtin_acos },
16868 { "asin" , jx9Builtin_asin },
16869 { "cosh" , jx9Builtin_cosh },
16870 { "sinh" , jx9Builtin_sinh },
16871 { "ceil" , jx9Builtin_ceil },
16872 { "tan" , jx9Builtin_tan },
16873 { "tanh" , jx9Builtin_tanh },
16874 { "atan" , jx9Builtin_atan },
16875 { "atan2", jx9Builtin_atan2 },
16876 { "log" , jx9Builtin_log },
16877 { "log10" , jx9Builtin_log10 },
16878 { "pow" , jx9Builtin_pow },
16879 { "pi", jx9Builtin_pi },
16880 { "fmod", jx9Builtin_fmod },
16881 { "hypot", jx9Builtin_hypot },
16882#endif /* JX9_ENABLE_MATH_FUNC */
16883 { "round", jx9Builtin_round },
16884 { "dechex", jx9Builtin_dechex },
16885 { "decoct", jx9Builtin_decoct },
16886 { "decbin", jx9Builtin_decbin },
16887 { "hexdec", jx9Builtin_hexdec },
16888 { "bindec", jx9Builtin_bindec },
16889 { "octdec", jx9Builtin_octdec },
16890 { "base_convert", jx9Builtin_base_convert },
16891 /* String handling functions */
16892 { "substr", jx9Builtin_substr },
16893 { "substr_compare", jx9Builtin_substr_compare },
16894 { "substr_count", jx9Builtin_substr_count },
16895 { "chunk_split", jx9Builtin_chunk_split},
16896 { "htmlspecialchars", jx9Builtin_htmlspecialchars },
16897 { "htmlspecialchars_decode", jx9Builtin_htmlspecialchars_decode },
16898 { "get_html_translation_table", jx9Builtin_get_html_translation_table },
16899 { "htmlentities", jx9Builtin_htmlentities},
16900 { "html_entity_decode", jx9Builtin_html_entity_decode},
16901 { "strlen" , jx9Builtin_strlen },
16902 { "strcmp" , jx9Builtin_strcmp },
16903 { "strcoll" , jx9Builtin_strcmp },
16904 { "strncmp" , jx9Builtin_strncmp },
16905 { "strcasecmp" , jx9Builtin_strcasecmp },
16906 { "strncasecmp", jx9Builtin_strncasecmp},
16907 { "implode" , jx9Builtin_implode },
16908 { "join" , jx9Builtin_implode },
16909 { "implode_recursive" , jx9Builtin_implode_recursive },
16910 { "join_recursive" , jx9Builtin_implode_recursive },
16911 { "explode" , jx9Builtin_explode },
16912 { "trim" , jx9Builtin_trim },
16913 { "rtrim" , jx9Builtin_rtrim },
16914 { "chop" , jx9Builtin_rtrim },
16915 { "ltrim" , jx9Builtin_ltrim },
16916 { "strtolower", jx9Builtin_strtolower },
16917 { "mb_strtolower", jx9Builtin_strtolower }, /* Only UTF-8 encoding is supported */
16918 { "strtoupper", jx9Builtin_strtoupper },
16919 { "mb_strtoupper", jx9Builtin_strtoupper }, /* Only UTF-8 encoding is supported */
16920 { "ord", jx9Builtin_ord },
16921 { "chr", jx9Builtin_chr },
16922 { "bin2hex", jx9Builtin_bin2hex },
16923 { "strstr", jx9Builtin_strstr },
16924 { "stristr", jx9Builtin_stristr },
16925 { "strchr", jx9Builtin_strstr },
16926 { "strpos", jx9Builtin_strpos },
16927 { "stripos", jx9Builtin_stripos },
16928 { "strrpos", jx9Builtin_strrpos },
16929 { "strripos", jx9Builtin_strripos },
16930 { "strrchr", jx9Builtin_strrchr },
16931 { "strrev", jx9Builtin_strrev },
16932 { "str_repeat", jx9Builtin_str_repeat },
16933 { "nl2br", jx9Builtin_nl2br },
16934 { "sprintf", jx9Builtin_sprintf },
16935 { "printf", jx9Builtin_printf },
16936 { "vprintf", jx9Builtin_vprintf },
16937 { "vsprintf", jx9Builtin_vsprintf },
16938 { "size_format", jx9Builtin_size_format},
16939#if !defined(JX9_DISABLE_HASH_FUNC)
16940 { "md5", jx9Builtin_md5 },
16941 { "sha1", jx9Builtin_sha1 },
16942 { "crc32", jx9Builtin_crc32 },
16943#endif /* JX9_DISABLE_HASH_FUNC */
16944 { "str_getcsv", jx9Builtin_str_getcsv },
16945 { "strip_tags", jx9Builtin_strip_tags },
16946 { "str_split", jx9Builtin_str_split },
16947 { "strspn", jx9Builtin_strspn },
16948 { "strcspn", jx9Builtin_strcspn },
16949 { "strpbrk", jx9Builtin_strpbrk },
16950 { "soundex", jx9Builtin_soundex },
16951 { "wordwrap", jx9Builtin_wordwrap },
16952 { "strtok", jx9Builtin_strtok },
16953 { "str_pad", jx9Builtin_str_pad },
16954 { "str_replace", jx9Builtin_str_replace},
16955 { "str_ireplace", jx9Builtin_str_replace},
16956 { "strtr", jx9Builtin_strtr },
16957 { "parse_ini_string", jx9Builtin_parse_ini_string},
16958 /* Ctype functions */
16959 { "ctype_alnum", jx9Builtin_ctype_alnum },
16960 { "ctype_alpha", jx9Builtin_ctype_alpha },
16961 { "ctype_cntrl", jx9Builtin_ctype_cntrl },
16962 { "ctype_digit", jx9Builtin_ctype_digit },
16963 { "ctype_xdigit", jx9Builtin_ctype_xdigit},
16964 { "ctype_graph", jx9Builtin_ctype_graph },
16965 { "ctype_print", jx9Builtin_ctype_print },
16966 { "ctype_punct", jx9Builtin_ctype_punct },
16967 { "ctype_space", jx9Builtin_ctype_space },
16968 { "ctype_lower", jx9Builtin_ctype_lower },
16969 { "ctype_upper", jx9Builtin_ctype_upper },
16970 /* Time functions */
16971 { "time" , jx9Builtin_time },
16972 { "microtime", jx9Builtin_microtime },
16973 { "getdate" , jx9Builtin_getdate },
16974 { "gettimeofday", jx9Builtin_gettimeofday },
16975 { "date", jx9Builtin_date },
16976 { "strftime", jx9Builtin_strftime },
16977 { "idate", jx9Builtin_idate },
16978 { "gmdate", jx9Builtin_gmdate },
16979 { "localtime", jx9Builtin_localtime },
16980 { "mktime", jx9Builtin_mktime },
16981 { "gmmktime", jx9Builtin_mktime },
16982 /* URL functions */
16983 { "base64_encode", jx9Builtin_base64_encode },
16984 { "base64_decode", jx9Builtin_base64_decode },
16985 { "convert_uuencode", jx9Builtin_base64_encode },
16986 { "convert_uudecode", jx9Builtin_base64_decode },
16987 { "urlencode", jx9Builtin_urlencode },
16988 { "urldecode", jx9Builtin_urldecode },
16989 { "rawurlencode", jx9Builtin_urlencode },
16990 { "rawurldecode", jx9Builtin_urldecode },
16991#endif /* JX9_DISABLE_BUILTIN_FUNC */
16992};
16993/*
16994 * Register the built-in functions defined above, the array functions
16995 * defined in hashmap.c and the IO functions defined in vfs.c.
16996 */
16997JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm)
16998{
16999 sxu32 n;
17000 for( n = 0 ; n < SX_ARRAYSIZE(aBuiltInFunc) ; ++n ){
17001 jx9_create_function(&(*pVm), aBuiltInFunc[n].zName, aBuiltInFunc[n].xFunc, 0);
17002 }
17003 /* Register hashmap functions [i.e: sort(), count(), array_diff(), ...] */
17004 jx9RegisterHashmapFunctions(&(*pVm));
17005 /* Register IO functions [i.e: fread(), fwrite(), chdir(), mkdir(), file(), ...] */
17006 jx9RegisterIORoutine(&(*pVm));
17007}
17008
17009/*
17010 * ----------------------------------------------------------
17011 * File: jx9_compile.c
17012 * MD5: 562e73eb7214f890e71713c6b97a7863
17013 * ----------------------------------------------------------
17014 */
17015/*
17016 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
17017 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
17018 * Version 1.7.2
17019 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
17020 * please contact Symisc Systems via:
17021 * legal@symisc.net
17022 * licensing@symisc.net
17023 * contact@symisc.net
17024 * or visit:
17025 * http://jx9.symisc.net/
17026 */
17027 /* $SymiscID: compile.c v1.7 FreeBSD 2012-12-11 21:46 stable <chm@symisc.net> $ */
17028#ifndef JX9_AMALGAMATION
17029#include "jx9Int.h"
17030#endif
17031/*
17032 * This file implement a thread-safe and full-reentrant compiler for the JX9 engine.
17033 * That is, routines defined in this file takes a stream of tokens and output
17034 * JX9 bytecode instructions.
17035 */
17036/* Forward declaration */
17037typedef struct LangConstruct LangConstruct;
17038typedef struct JumpFixup JumpFixup;
17039/* Block [i.e: set of statements] control flags */
17040#define GEN_BLOCK_LOOP 0x001 /* Loop block [i.e: for, while, ...] */
17041#define GEN_BLOCK_PROTECTED 0x002 /* Protected block */
17042#define GEN_BLOCK_COND 0x004 /* Conditional block [i.e: if(condition){} ]*/
17043#define GEN_BLOCK_FUNC 0x008 /* Function body */
17044#define GEN_BLOCK_GLOBAL 0x010 /* Global block (always set)*/
17045#define GEN_BLOC_NESTED_FUNC 0x020 /* Nested function body */
17046#define GEN_BLOCK_EXPR 0x040 /* Expression */
17047#define GEN_BLOCK_STD 0x080 /* Standard block */
17048#define GEN_BLOCK_SWITCH 0x100 /* Switch statement */
17049/*
17050 * Compilation of some JX9 constructs such as if, for, while, the logical or
17051 * (||) and logical and (&&) operators in expressions requires the
17052 * generation of forward jumps.
17053 * Since the destination PC target of these jumps isn't known when the jumps
17054 * are emitted, we record each forward jump in an instance of the following
17055 * structure. Those jumps are fixed later when the jump destination is resolved.
17056 */
17057struct JumpFixup
17058{
17059 sxi32 nJumpType; /* Jump type. Either TRUE jump, FALSE jump or Unconditional jump */
17060 sxu32 nInstrIdx; /* Instruction index to fix later when the jump destination is resolved. */
17061};
17062/*
17063 * Each language construct is represented by an instance
17064 * of the following structure.
17065 */
17066struct LangConstruct
17067{
17068 sxu32 nID; /* Language construct ID [i.e: JX9_TKWRD_WHILE, JX9_TKWRD_FOR, JX9_TKWRD_IF...] */
17069 ProcLangConstruct xConstruct; /* C function implementing the language construct */
17070};
17071/* Compilation flags */
17072#define JX9_COMPILE_SINGLE_STMT 0x001 /* Compile a single statement */
17073/* Token stream synchronization macros */
17074#define SWAP_TOKEN_STREAM(GEN, START, END)\
17075 pTmp = GEN->pEnd;\
17076 pGen->pIn = START;\
17077 pGen->pEnd = END
17078#define UPDATE_TOKEN_STREAM(GEN)\
17079 if( GEN->pIn < pTmp ){\
17080 GEN->pIn++;\
17081 }\
17082 GEN->pEnd = pTmp
17083#define SWAP_DELIMITER(GEN, START, END)\
17084 pTmpIn = GEN->pIn;\
17085 pTmpEnd = GEN->pEnd;\
17086 GEN->pIn = START;\
17087 GEN->pEnd = END
17088#define RE_SWAP_DELIMITER(GEN)\
17089 GEN->pIn = pTmpIn;\
17090 GEN->pEnd = pTmpEnd
17091/* Flags related to expression compilation */
17092#define EXPR_FLAG_LOAD_IDX_STORE 0x001 /* Set the iP2 flag when dealing with the LOAD_IDX instruction */
17093#define EXPR_FLAG_RDONLY_LOAD 0x002 /* Read-only load, refer to the 'JX9_OP_LOAD' VM instruction for more information */
17094#define EXPR_FLAG_COMMA_STATEMENT 0x004 /* Treat comma expression as a single statement (used by object attributes) */
17095/* Forward declaration */
17096static sxi32 jx9CompileExpr(
17097 jx9_gen_state *pGen, /* Code generator state */
17098 sxi32 iFlags, /* Control flags */
17099 sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
17100 );
17101
17102/*
17103 * Recover from a compile-time error. In other words synchronize
17104 * the token stream cursor with the first semi-colon seen.
17105 */
17106static sxi32 jx9ErrorRecover(jx9_gen_state *pGen)
17107{
17108 /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
17109 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI /*';'*/) == 0){
17110 pGen->pIn++;
17111 }
17112 return SXRET_OK;
17113}
17114/*
17115 * Check if the given identifier name is reserved or not.
17116 * Return TRUE if reserved.FALSE otherwise.
17117 */
17118static int GenStateIsReservedID(SyString *pName)
17119{
17120 if( pName->nByte == sizeof("null") - 1 ){
17121 if( SyStrnicmp(pName->zString, "null", sizeof("null")-1) == 0 ){
17122 return TRUE;
17123 }else if( SyStrnicmp(pName->zString, "true", sizeof("true")-1) == 0 ){
17124 return TRUE;
17125 }
17126 }else if( pName->nByte == sizeof("false") - 1 ){
17127 if( SyStrnicmp(pName->zString, "false", sizeof("false")-1) == 0 ){
17128 return TRUE;
17129 }
17130 }
17131 /* Not a reserved constant */
17132 return FALSE;
17133}
17134/*
17135 * Check if a given token value is installed in the literal table.
17136 */
17137static sxi32 GenStateFindLiteral(jx9_gen_state *pGen, const SyString *pValue, sxu32 *pIdx)
17138{
17139 SyHashEntry *pEntry;
17140 pEntry = SyHashGet(&pGen->hLiteral, (const void *)pValue->zString, pValue->nByte);
17141 if( pEntry == 0 ){
17142 return SXERR_NOTFOUND;
17143 }
17144 *pIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
17145 return SXRET_OK;
17146}
17147/*
17148 * Install a given constant index in the literal table.
17149 * In order to be installed, the jx9_value must be of type string.
17150 */
17151static sxi32 GenStateInstallLiteral(jx9_gen_state *pGen,jx9_value *pObj, sxu32 nIdx)
17152{
17153 if( SyBlobLength(&pObj->sBlob) > 0 ){
17154 SyHashInsert(&pGen->hLiteral, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), SX_INT_TO_PTR(nIdx));
17155 }
17156 return SXRET_OK;
17157}
17158/*
17159 * Generate a fatal error.
17160 */
17161static sxi32 GenStateOutOfMem(jx9_gen_state *pGen)
17162{
17163 jx9GenCompileError(pGen,E_ERROR,1,"Fatal, Jx9 compiler is running out of memory");
17164 /* Abort compilation immediately */
17165 return SXERR_ABORT;
17166}
17167/*
17168 * Fetch a block that correspond to the given criteria from the stack of
17169 * compiled blocks.
17170 * Return a pointer to that block on success. NULL otherwise.
17171 */
17172static GenBlock * GenStateFetchBlock(GenBlock *pCurrent, sxi32 iBlockType, sxi32 iCount)
17173{
17174 GenBlock *pBlock = pCurrent;
17175 for(;;){
17176 if( pBlock->iFlags & iBlockType ){
17177 iCount--; /* Decrement nesting level */
17178 if( iCount < 1 ){
17179 /* Block meet with the desired criteria */
17180 return pBlock;
17181 }
17182 }
17183 /* Point to the upper block */
17184 pBlock = pBlock->pParent;
17185 if( pBlock == 0 || (pBlock->iFlags & (GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC)) ){
17186 /* Forbidden */
17187 break;
17188 }
17189 }
17190 /* No such block */
17191 return 0;
17192}
17193/*
17194 * Initialize a freshly allocated block instance.
17195 */
17196static void GenStateInitBlock(
17197 jx9_gen_state *pGen, /* Code generator state */
17198 GenBlock *pBlock, /* Target block */
17199 sxi32 iType, /* Block type [i.e: loop, conditional, function body, etc.]*/
17200 sxu32 nFirstInstr, /* First instruction to compile */
17201 void *pUserData /* Upper layer private data */
17202 )
17203{
17204 /* Initialize block fields */
17205 pBlock->nFirstInstr = nFirstInstr;
17206 pBlock->pUserData = pUserData;
17207 pBlock->pGen = pGen;
17208 pBlock->iFlags = iType;
17209 pBlock->pParent = 0;
17210 pBlock->bPostContinue = 0;
17211 SySetInit(&pBlock->aJumpFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
17212 SySetInit(&pBlock->aPostContFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
17213}
17214/*
17215 * Allocate a new block instance.
17216 * Return SXRET_OK and write a pointer to the new instantiated block
17217 * on success.Otherwise generate a compile-time error and abort
17218 * processing on failure.
17219 */
17220static sxi32 GenStateEnterBlock(
17221 jx9_gen_state *pGen, /* Code generator state */
17222 sxi32 iType, /* Block type [i.e: loop, conditional, function body, etc.]*/
17223 sxu32 nFirstInstr, /* First instruction to compile */
17224 void *pUserData, /* Upper layer private data */
17225 GenBlock **ppBlock /* OUT: instantiated block */
17226 )
17227{
17228 GenBlock *pBlock;
17229 /* Allocate a new block instance */
17230 pBlock = (GenBlock *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(GenBlock));
17231 if( pBlock == 0 ){
17232 /* If the supplied memory subsystem is so sick that we are unable to allocate
17233 * a tiny chunk of memory, there is no much we can do here.
17234 */
17235 return GenStateOutOfMem(pGen);
17236 }
17237 /* Zero the structure */
17238 SyZero(pBlock, sizeof(GenBlock));
17239 GenStateInitBlock(&(*pGen), pBlock, iType, nFirstInstr, pUserData);
17240 /* Link to the parent block */
17241 pBlock->pParent = pGen->pCurrent;
17242 /* Mark as the current block */
17243 pGen->pCurrent = pBlock;
17244 if( ppBlock ){
17245 /* Write a pointer to the new instance */
17246 *ppBlock = pBlock;
17247 }
17248 return SXRET_OK;
17249}
17250/*
17251 * Release block fields without freeing the whole instance.
17252 */
17253static void GenStateReleaseBlock(GenBlock *pBlock)
17254{
17255 SySetRelease(&pBlock->aPostContFix);
17256 SySetRelease(&pBlock->aJumpFix);
17257}
17258/*
17259 * Release a block.
17260 */
17261static void GenStateFreeBlock(GenBlock *pBlock)
17262{
17263 jx9_gen_state *pGen = pBlock->pGen;
17264 GenStateReleaseBlock(&(*pBlock));
17265 /* Free the instance */
17266 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pBlock);
17267}
17268/*
17269 * POP and release a block from the stack of compiled blocks.
17270 */
17271static sxi32 GenStateLeaveBlock(jx9_gen_state *pGen, GenBlock **ppBlock)
17272{
17273 GenBlock *pBlock = pGen->pCurrent;
17274 if( pBlock == 0 ){
17275 /* No more block to pop */
17276 return SXERR_EMPTY;
17277 }
17278 /* Point to the upper block */
17279 pGen->pCurrent = pBlock->pParent;
17280 if( ppBlock ){
17281 /* Write a pointer to the popped block */
17282 *ppBlock = pBlock;
17283 }else{
17284 /* Safely release the block */
17285 GenStateFreeBlock(&(*pBlock));
17286 }
17287 return SXRET_OK;
17288}
17289/*
17290 * Emit a forward jump.
17291 * Notes on forward jumps
17292 * Compilation of some JX9 constructs such as if, for, while and the logical or
17293 * (||) and logical and (&&) operators in expressions requires the
17294 * generation of forward jumps.
17295 * Since the destination PC target of these jumps isn't known when the jumps
17296 * are emitted, we record each forward jump in an instance of the following
17297 * structure. Those jumps are fixed later when the jump destination is resolved.
17298 */
17299static sxi32 GenStateNewJumpFixup(GenBlock *pBlock, sxi32 nJumpType, sxu32 nInstrIdx)
17300{
17301 JumpFixup sJumpFix;
17302 sxi32 rc;
17303 /* Init the JumpFixup structure */
17304 sJumpFix.nJumpType = nJumpType;
17305 sJumpFix.nInstrIdx = nInstrIdx;
17306 /* Insert in the jump fixup table */
17307 rc = SySetPut(&pBlock->aJumpFix,(const void *)&sJumpFix);
17308 return rc;
17309}
17310/*
17311 * Fix a forward jump now the jump destination is resolved.
17312 * Return the total number of fixed jumps.
17313 * Notes on forward jumps:
17314 * Compilation of some JX9 constructs such as if, for, while and the logical or
17315 * (||) and logical and (&&) operators in expressions requires the
17316 * generation of forward jumps.
17317 * Since the destination PC target of these jumps isn't known when the jumps
17318 * are emitted, we record each forward jump in an instance of the following
17319 * structure.Those jumps are fixed later when the jump destination is resolved.
17320 */
17321static sxu32 GenStateFixJumps(GenBlock *pBlock, sxi32 nJumpType, sxu32 nJumpDest)
17322{
17323 JumpFixup *aFix;
17324 VmInstr *pInstr;
17325 sxu32 nFixed;
17326 sxu32 n;
17327 /* Point to the jump fixup table */
17328 aFix = (JumpFixup *)SySetBasePtr(&pBlock->aJumpFix);
17329 /* Fix the desired jumps */
17330 for( nFixed = n = 0 ; n < SySetUsed(&pBlock->aJumpFix) ; ++n ){
17331 if( aFix[n].nJumpType < 0 ){
17332 /* Already fixed */
17333 continue;
17334 }
17335 if( nJumpType > 0 && aFix[n].nJumpType != nJumpType ){
17336 /* Not of our interest */
17337 continue;
17338 }
17339 /* Point to the instruction to fix */
17340 pInstr = jx9VmGetInstr(pBlock->pGen->pVm, aFix[n].nInstrIdx);
17341 if( pInstr ){
17342 pInstr->iP2 = nJumpDest;
17343 nFixed++;
17344 /* Mark as fixed */
17345 aFix[n].nJumpType = -1;
17346 }
17347 }
17348 /* Total number of fixed jumps */
17349 return nFixed;
17350}
17351/*
17352 * Reserve a room for a numeric constant [i.e: 64-bit integer or real number]
17353 * in the constant table.
17354 */
17355static jx9_value * GenStateInstallNumLiteral(jx9_gen_state *pGen, sxu32 *pIdx)
17356{
17357 jx9_value *pObj;
17358 sxu32 nIdx = 0; /* cc warning */
17359 /* Reserve a new constant */
17360 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17361 if( pObj == 0 ){
17362 GenStateOutOfMem(pGen);
17363 return 0;
17364 }
17365 *pIdx = nIdx;
17366 /* TODO(chems): Create a numeric table (64bit int keys) same as
17367 * the constant string iterals table [optimization purposes].
17368 */
17369 return pObj;
17370}
17371/*
17372 * Compile a numeric [i.e: integer or real] literal.
17373 * Notes on the integer type.
17374 * According to the JX9 language reference manual
17375 * Integers can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8)
17376 * or binary (base 2) notation, optionally preceded by a sign (- or +).
17377 * To use octal notation, precede the number with a 0 (zero). To use hexadecimal
17378 * notation precede the number with 0x. To use binary notation precede the number with 0b.
17379 */
17380static sxi32 jx9CompileNumLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
17381{
17382 SyToken *pToken = pGen->pIn; /* Raw token */
17383 sxu32 nIdx = 0;
17384 if( pToken->nType & JX9_TK_INTEGER ){
17385 jx9_value *pObj;
17386 sxi64 iValue;
17387 iValue = jx9TokenValueToInt64(&pToken->sData);
17388 pObj = GenStateInstallNumLiteral(&(*pGen), &nIdx);
17389 if( pObj == 0 ){
17390 SXUNUSED(iCompileFlag); /* cc warning */
17391 return SXERR_ABORT;
17392 }
17393 jx9MemObjInitFromInt(pGen->pVm, pObj, iValue);
17394 }else{
17395 /* Real number */
17396 jx9_value *pObj;
17397 /* Reserve a new constant */
17398 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17399 if( pObj == 0 ){
17400 return GenStateOutOfMem(pGen);
17401 }
17402 jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
17403 jx9MemObjToReal(pObj);
17404 }
17405 /* Emit the load constant instruction */
17406 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17407 /* Node successfully compiled */
17408 return SXRET_OK;
17409}
17410/*
17411 * Compile a nowdoc string.
17412 * According to the JX9 language reference manual:
17413 *
17414 * Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
17415 * A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
17416 * The construct is ideal for embedding JX9 code or other large blocks of text without the
17417 * need for escaping. It shares some features in common with the SGML <![CDATA[ ]]>
17418 * construct, in that it declares a block of text which is not for parsing.
17419 * A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier
17420 * which follows is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc
17421 * identifiers also apply to nowdoc identifiers, especially those regarding the appearance
17422 * of the closing identifier.
17423 */
17424static sxi32 jx9CompileNowdoc(jx9_gen_state *pGen,sxi32 iCompileFlag)
17425{
17426 SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
17427 jx9_value *pObj;
17428 sxu32 nIdx;
17429 nIdx = 0; /* Prevent compiler warning */
17430 if( pStr->nByte <= 0 ){
17431 /* Empty string, load NULL */
17432 jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC, 0, 0, 0, 0);
17433 return SXRET_OK;
17434 }
17435 /* Reserve a new constant */
17436 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17437 if( pObj == 0 ){
17438 jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "JX9 engine is running out of memory");
17439 SXUNUSED(iCompileFlag); /* cc warning */
17440 return SXERR_ABORT;
17441 }
17442 /* No processing is done here, simply a memcpy() operation */
17443 jx9MemObjInitFromString(pGen->pVm, pObj, pStr);
17444 /* Emit the load constant instruction */
17445 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17446 /* Node successfully compiled */
17447 return SXRET_OK;
17448}
17449/*
17450 * Compile a single quoted string.
17451 * According to the JX9 language reference manual:
17452 *
17453 * The simplest way to specify a string is to enclose it in single quotes (the character ' ).
17454 * To specify a literal single quote, escape it with a backslash (\). To specify a literal
17455 * backslash, double it (\\). All other instances of backslash will be treated as a literal
17456 * backslash: this means that the other escape sequences you might be used to, such as \r
17457 * or \n, will be output literally as specified rather than having any special meaning.
17458 *
17459 */
17460JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag)
17461{
17462 SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
17463 const char *zIn, *zCur, *zEnd;
17464 jx9_value *pObj;
17465 sxu32 nIdx;
17466 nIdx = 0; /* Prevent compiler warning */
17467 /* Delimit the string */
17468 zIn = pStr->zString;
17469 zEnd = &zIn[pStr->nByte];
17470 if( zIn >= zEnd ){
17471 /* Empty string, load NULL */
17472 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
17473 return SXRET_OK;
17474 }
17475 if( SXRET_OK == GenStateFindLiteral(&(*pGen), pStr, &nIdx) ){
17476 /* Already processed, emit the load constant instruction
17477 * and return.
17478 */
17479 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17480 return SXRET_OK;
17481 }
17482 /* Reserve a new constant */
17483 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17484 if( pObj == 0 ){
17485 jx9GenCompileError(&(*pGen), E_ERROR, 1, "JX9 engine is running out of memory");
17486 SXUNUSED(iCompileFlag); /* cc warning */
17487 return SXERR_ABORT;
17488 }
17489 jx9MemObjInitFromString(pGen->pVm, pObj, 0);
17490 /* Compile the node */
17491 for(;;){
17492 if( zIn >= zEnd ){
17493 /* End of input */
17494 break;
17495 }
17496 zCur = zIn;
17497 while( zIn < zEnd && zIn[0] != '\\' ){
17498 zIn++;
17499 }
17500 if( zIn > zCur ){
17501 /* Append raw contents*/
17502 jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
17503 }
17504 zIn++;
17505 if( zIn < zEnd ){
17506 if( zIn[0] == '\\' ){
17507 /* A literal backslash */
17508 jx9MemObjStringAppend(pObj, "\\", sizeof(char));
17509 }else if( zIn[0] == '\'' ){
17510 /* A single quote */
17511 jx9MemObjStringAppend(pObj, "'", sizeof(char));
17512 }else{
17513 /* verbatim copy */
17514 zIn--;
17515 jx9MemObjStringAppend(pObj, zIn, sizeof(char)*2);
17516 zIn++;
17517 }
17518 }
17519 /* Advance the stream cursor */
17520 zIn++;
17521 }
17522 /* Emit the load constant instruction */
17523 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17524 if( pStr->nByte < 1024 ){
17525 /* Install in the literal table */
17526 GenStateInstallLiteral(pGen, pObj, nIdx);
17527 }
17528 /* Node successfully compiled */
17529 return SXRET_OK;
17530}
17531/*
17532 * Process variable expression [i.e: "$var", "${var}"] embedded in a double quoted/heredoc string.
17533 * According to the JX9 language reference manual
17534 * When a string is specified in double quotes or with heredoc, variables are parsed within it.
17535 * There are two types of syntax: a simple one and a complex one. The simple syntax is the most
17536 * common and convenient. It provides a way to embed a variable, an array value, or an object
17537 * property in a string with a minimum of effort.
17538 * Simple syntax
17539 * If a dollar sign ($) is encountered, the parser will greedily take as many tokens as possible
17540 * to form a valid variable name. Enclose the variable name in curly braces to explicitly specify
17541 * the end of the name.
17542 * Similarly, an array index or an object property can be parsed. With array indices, the closing
17543 * square bracket (]) marks the end of the index. The same rules apply to object properties
17544 * as to simple variables.
17545 * Complex (curly) syntax
17546 * This isn't called complex because the syntax is complex, but because it allows for the use
17547 * of complex expressions.
17548 * Any scalar variable, array element or object property with a string representation can be
17549 * included via this syntax. Simply write the expression the same way as it would appear outside
17550 * the string, and then wrap it in { and }. Since { can not be escaped, this syntax will only
17551 * be recognised when the $ immediately follows the {. Use {\$ to get a literal {$
17552 */
17553static sxi32 GenStateProcessStringExpression(
17554 jx9_gen_state *pGen, /* Code generator state */
17555 const char *zIn, /* Raw expression */
17556 const char *zEnd /* End of the expression */
17557 )
17558{
17559 SyToken *pTmpIn, *pTmpEnd;
17560 SySet sToken;
17561 sxi32 rc;
17562 /* Initialize the token set */
17563 SySetInit(&sToken, &pGen->pVm->sAllocator, sizeof(SyToken));
17564 /* Preallocate some slots */
17565 SySetAlloc(&sToken, 0x08);
17566 /* Tokenize the text */
17567 jx9Tokenize(zIn,(sxu32)(zEnd-zIn),&sToken);
17568 /* Swap delimiter */
17569 pTmpIn = pGen->pIn;
17570 pTmpEnd = pGen->pEnd;
17571 pGen->pIn = (SyToken *)SySetBasePtr(&sToken);
17572 pGen->pEnd = &pGen->pIn[SySetUsed(&sToken)];
17573 /* Compile the expression */
17574 rc = jx9CompileExpr(&(*pGen), 0, 0);
17575 /* Restore token stream */
17576 pGen->pIn = pTmpIn;
17577 pGen->pEnd = pTmpEnd;
17578 /* Release the token set */
17579 SySetRelease(&sToken);
17580 /* Compilation result */
17581 return rc;
17582}
17583/*
17584 * Reserve a new constant for a double quoted/heredoc string.
17585 */
17586static jx9_value * GenStateNewStrObj(jx9_gen_state *pGen,sxi32 *pCount)
17587{
17588 jx9_value *pConstObj;
17589 sxu32 nIdx = 0;
17590 /* Reserve a new constant */
17591 pConstObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17592 if( pConstObj == 0 ){
17593 GenStateOutOfMem(&(*pGen));
17594 return 0;
17595 }
17596 (*pCount)++;
17597 jx9MemObjInitFromString(pGen->pVm, pConstObj, 0);
17598 /* Emit the load constant instruction */
17599 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17600 return pConstObj;
17601}
17602/*
17603 * Compile a double quoted/heredoc string.
17604 * According to the JX9 language reference manual
17605 * Heredoc
17606 * A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
17607 * is provided, then a newline. The string itself follows, and then the same identifier again
17608 * to close the quotation.
17609 * The closing identifier must begin in the first column of the line. Also, the identifier must
17610 * follow the same naming rules as any other label in JX9: it must contain only alphanumeric
17611 * characters and underscores, and must start with a non-digit character or underscore.
17612 * Warning
17613 * It is very important to note that the line with the closing identifier must contain
17614 * no other characters, except possibly a semicolon (;). That means especially that the identifier
17615 * may not be indented, and there may not be any spaces or tabs before or after the semicolon.
17616 * It's also important to realize that the first character before the closing identifier must
17617 * be a newline as defined by the local operating system. This is \n on UNIX systems, including Mac OS X.
17618 * The closing delimiter (possibly followed by a semicolon) must also be followed by a newline.
17619 * If this rule is broken and the closing identifier is not "clean", it will not be considered a closing
17620 * identifier, and JX9 will continue looking for one. If a proper closing identifier is not found before
17621 * the end of the current file, a parse error will result at the last line.
17622 * Heredocs can not be used for initializing object properties.
17623 * Double quoted
17624 * If the string is enclosed in double-quotes ("), JX9 will interpret more escape sequences for special characters:
17625 * Escaped characters Sequence Meaning
17626 * \n linefeed (LF or 0x0A (10) in ASCII)
17627 * \r carriage return (CR or 0x0D (13) in ASCII)
17628 * \t horizontal tab (HT or 0x09 (9) in ASCII)
17629 * \v vertical tab (VT or 0x0B (11) in ASCII)
17630 * \f form feed (FF or 0x0C (12) in ASCII)
17631 * \\ backslash
17632 * \$ dollar sign
17633 * \" double-quote
17634 * \[0-7]{1, 3} the sequence of characters matching the regular expression is a character in octal notation
17635 * \x[0-9A-Fa-f]{1, 2} the sequence of characters matching the regular expression is a character in hexadecimal notation
17636 * As in single quoted strings, escaping any other character will result in the backslash being printed too.
17637 * The most important feature of double-quoted strings is the fact that variable names will be expanded.
17638 * See string parsing for details.
17639 */
17640static sxi32 GenStateCompileString(jx9_gen_state *pGen)
17641{
17642 SyString *pStr = &pGen->pIn->sData; /* Raw token value */
17643 const char *zIn, *zCur, *zEnd;
17644 jx9_value *pObj = 0;
17645 sxi32 iCons;
17646 sxi32 rc;
17647 /* Delimit the string */
17648 zIn = pStr->zString;
17649 zEnd = &zIn[pStr->nByte];
17650 if( zIn >= zEnd ){
17651 /* Empty string, load NULL */
17652 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
17653 return SXRET_OK;
17654 }
17655 zCur = 0;
17656 /* Compile the node */
17657 iCons = 0;
17658 for(;;){
17659 zCur = zIn;
17660 while( zIn < zEnd && zIn[0] != '\\' ){
17661 if(zIn[0] == '$' && &zIn[1] < zEnd &&
17662 (((unsigned char)zIn[1] >= 0xc0 || SyisAlpha(zIn[1]) || zIn[1] == '_')) ){
17663 break;
17664 }
17665 zIn++;
17666 }
17667 if( zIn > zCur ){
17668 if( pObj == 0 ){
17669 pObj = GenStateNewStrObj(&(*pGen), &iCons);
17670 if( pObj == 0 ){
17671 return SXERR_ABORT;
17672 }
17673 }
17674 jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
17675 }
17676 if( zIn >= zEnd ){
17677 break;
17678 }
17679 if( zIn[0] == '\\' ){
17680 const char *zPtr = 0;
17681 sxu32 n;
17682 zIn++;
17683 if( zIn >= zEnd ){
17684 break;
17685 }
17686 if( pObj == 0 ){
17687 pObj = GenStateNewStrObj(&(*pGen), &iCons);
17688 if( pObj == 0 ){
17689 return SXERR_ABORT;
17690 }
17691 }
17692 n = sizeof(char); /* size of conversion */
17693 switch( zIn[0] ){
17694 case '$':
17695 /* Dollar sign */
17696 jx9MemObjStringAppend(pObj, "$", sizeof(char));
17697 break;
17698 case '\\':
17699 /* A literal backslash */
17700 jx9MemObjStringAppend(pObj, "\\", sizeof(char));
17701 break;
17702 case 'a':
17703 /* The "alert" character (BEL)[ctrl+g] ASCII code 7 */
17704 jx9MemObjStringAppend(pObj, "\a", sizeof(char));
17705 break;
17706 case 'b':
17707 /* Backspace (BS)[ctrl+h] ASCII code 8 */
17708 jx9MemObjStringAppend(pObj, "\b", sizeof(char));
17709 break;
17710 case 'f':
17711 /* Form-feed (FF)[ctrl+l] ASCII code 12 */
17712 jx9MemObjStringAppend(pObj, "\f", sizeof(char));
17713 break;
17714 case 'n':
17715 /* Line feed(new line) (LF)[ctrl+j] ASCII code 10 */
17716 jx9MemObjStringAppend(pObj, "\n", sizeof(char));
17717 break;
17718 case 'r':
17719 /* Carriage return (CR)[ctrl+m] ASCII code 13 */
17720 jx9MemObjStringAppend(pObj, "\r", sizeof(char));
17721 break;
17722 case 't':
17723 /* Horizontal tab (HT)[ctrl+i] ASCII code 9 */
17724 jx9MemObjStringAppend(pObj, "\t", sizeof(char));
17725 break;
17726 case 'v':
17727 /* Vertical tab(VT)[ctrl+k] ASCII code 11 */
17728 jx9MemObjStringAppend(pObj, "\v", sizeof(char));
17729 break;
17730 case '\'':
17731 /* Single quote */
17732 jx9MemObjStringAppend(pObj, "'", sizeof(char));
17733 break;
17734 case '"':
17735 /* Double quote */
17736 jx9MemObjStringAppend(pObj, "\"", sizeof(char));
17737 break;
17738 case '0':
17739 /* NUL byte */
17740 jx9MemObjStringAppend(pObj, "\0", sizeof(char));
17741 break;
17742 case 'x':
17743 if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1]) ){
17744 int c;
17745 /* Hex digit */
17746 c = SyHexToint(zIn[1]) << 4;
17747 if( &zIn[2] < zEnd ){
17748 c += SyHexToint(zIn[2]);
17749 }
17750 /* Output char */
17751 jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
17752 n += sizeof(char) * 2;
17753 }else{
17754 /* Output literal character */
17755 jx9MemObjStringAppend(pObj, "x", sizeof(char));
17756 }
17757 break;
17758 case 'o':
17759 if( &zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8 ){
17760 /* Octal digit stream */
17761 int c;
17762 c = 0;
17763 zIn++;
17764 for( zPtr = zIn ; zPtr < &zIn[3*sizeof(char)] ; zPtr++ ){
17765 if( zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7 ){
17766 break;
17767 }
17768 c = c * 8 + (zPtr[0] - '0');
17769 }
17770 if ( c > 0 ){
17771 jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
17772 }
17773 n = (sxu32)(zPtr-zIn);
17774 }else{
17775 /* Output literal character */
17776 jx9MemObjStringAppend(pObj, "o", sizeof(char));
17777 }
17778 break;
17779 default:
17780 /* Output without a slash */
17781 jx9MemObjStringAppend(pObj, zIn, sizeof(char));
17782 break;
17783 }
17784 /* Advance the stream cursor */
17785 zIn += n;
17786 continue;
17787 }
17788 if( zIn[0] == '{' ){
17789 /* Curly syntax */
17790 const char *zExpr;
17791 sxi32 iNest = 1;
17792 zIn++;
17793 zExpr = zIn;
17794 /* Synchronize with the next closing curly braces */
17795 while( zIn < zEnd ){
17796 if( zIn[0] == '{' ){
17797 /* Increment nesting level */
17798 iNest++;
17799 }else if(zIn[0] == '}' ){
17800 /* Decrement nesting level */
17801 iNest--;
17802 if( iNest <= 0 ){
17803 break;
17804 }
17805 }
17806 zIn++;
17807 }
17808 /* Process the expression */
17809 rc = GenStateProcessStringExpression(&(*pGen),zExpr,zIn);
17810 if( rc == SXERR_ABORT ){
17811 return SXERR_ABORT;
17812 }
17813 if( rc != SXERR_EMPTY ){
17814 ++iCons;
17815 }
17816 if( zIn < zEnd ){
17817 /* Jump the trailing curly */
17818 zIn++;
17819 }
17820 }else{
17821 /* Simple syntax */
17822 const char *zExpr = zIn;
17823 /* Assemble variable name */
17824 for(;;){
17825 /* Jump leading dollars */
17826 while( zIn < zEnd && zIn[0] == '$' ){
17827 zIn++;
17828 }
17829 for(;;){
17830 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_' ) ){
17831 zIn++;
17832 }
17833 if((unsigned char)zIn[0] >= 0xc0 ){
17834 /* UTF-8 stream */
17835 zIn++;
17836 while( zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){
17837 zIn++;
17838 }
17839 continue;
17840 }
17841 break;
17842 }
17843 if( zIn >= zEnd ){
17844 break;
17845 }
17846 if( zIn[0] == '[' ){
17847 sxi32 iSquare = 1;
17848 zIn++;
17849 while( zIn < zEnd ){
17850 if( zIn[0] == '[' ){
17851 iSquare++;
17852 }else if (zIn[0] == ']' ){
17853 iSquare--;
17854 if( iSquare <= 0 ){
17855 break;
17856 }
17857 }
17858 zIn++;
17859 }
17860 if( zIn < zEnd ){
17861 zIn++;
17862 }
17863 break;
17864 }else if( zIn[0] == '.' ){
17865 /* Member access operator '.' */
17866 zIn++;
17867 }else{
17868 break;
17869 }
17870 }
17871 /* Process the expression */
17872 rc = GenStateProcessStringExpression(&(*pGen),zExpr, zIn);
17873 if( rc == SXERR_ABORT ){
17874 return SXERR_ABORT;
17875 }
17876 if( rc != SXERR_EMPTY ){
17877 ++iCons;
17878 }
17879 }
17880 /* Invalidate the previously used constant */
17881 pObj = 0;
17882 }/*for(;;)*/
17883 if( iCons > 1 ){
17884 /* Concatenate all compiled constants */
17885 jx9VmEmitInstr(pGen->pVm, JX9_OP_CAT, iCons, 0, 0, 0);
17886 }
17887 /* Node successfully compiled */
17888 return SXRET_OK;
17889}
17890/*
17891 * Compile a double quoted string.
17892 * See the block-comment above for more information.
17893 */
17894JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag)
17895{
17896 sxi32 rc;
17897 rc = GenStateCompileString(&(*pGen));
17898 SXUNUSED(iCompileFlag); /* cc warning */
17899 /* Compilation result */
17900 return rc;
17901}
17902/*
17903 * Compile a literal which is an identifier(name) for simple values.
17904 */
17905JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
17906{
17907 SyToken *pToken = pGen->pIn;
17908 jx9_value *pObj;
17909 SyString *pStr;
17910 sxu32 nIdx;
17911 /* Extract token value */
17912 pStr = &pToken->sData;
17913 /* Deal with the reserved literals [i.e: null, false, true, ...] first */
17914 if( pStr->nByte == sizeof("NULL") - 1 ){
17915 if( SyStrnicmp(pStr->zString, "null", sizeof("NULL")-1) == 0 ){
17916 /* NULL constant are always indexed at 0 */
17917 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
17918 return SXRET_OK;
17919 }else if( SyStrnicmp(pStr->zString, "true", sizeof("TRUE")-1) == 0 ){
17920 /* TRUE constant are always indexed at 1 */
17921 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1, 0, 0);
17922 return SXRET_OK;
17923 }
17924 }else if (pStr->nByte == sizeof("FALSE") - 1 &&
17925 SyStrnicmp(pStr->zString, "false", sizeof("FALSE")-1) == 0 ){
17926 /* FALSE constant are always indexed at 2 */
17927 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 2, 0, 0);
17928 return SXRET_OK;
17929 }else if(pStr->nByte == sizeof("__LINE__") - 1 &&
17930 SyMemcmp(pStr->zString, "__LINE__", sizeof("__LINE__")-1) == 0 ){
17931 /* TICKET 1433-004: __LINE__ constant must be resolved at compile time, not run time */
17932 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17933 if( pObj == 0 ){
17934 SXUNUSED(iCompileFlag); /* cc warning */
17935 return GenStateOutOfMem(pGen);
17936 }
17937 jx9MemObjInitFromInt(pGen->pVm, pObj, pToken->nLine);
17938 /* Emit the load constant instruction */
17939 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17940 return SXRET_OK;
17941 }else if( pStr->nByte == sizeof("__FUNCTION__") - 1 &&
17942 SyMemcmp(pStr->zString, "__FUNCTION__", sizeof("__FUNCTION__")-1) == 0 ){
17943 GenBlock *pBlock = pGen->pCurrent;
17944 /* TICKET 1433-004: __FUNCTION__/__METHOD__ constants must be resolved at compile time, not run time */
17945 while( pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0 ){
17946 /* Point to the upper block */
17947 pBlock = pBlock->pParent;
17948 }
17949 if( pBlock == 0 ){
17950 /* Called in the global scope, load NULL */
17951 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
17952 }else{
17953 /* Extract the target function/method */
17954 jx9_vm_func *pFunc = (jx9_vm_func *)pBlock->pUserData;
17955 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17956 if( pObj == 0 ){
17957 return GenStateOutOfMem(pGen);
17958 }
17959 jx9MemObjInitFromString(pGen->pVm, pObj, &pFunc->sName);
17960 /* Emit the load constant instruction */
17961 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17962 }
17963 return SXRET_OK;
17964 }
17965 /* Query literal table */
17966 if( SXRET_OK != GenStateFindLiteral(&(*pGen), &pToken->sData, &nIdx) ){
17967 jx9_value *pObj;
17968 /* Unknown literal, install it in the literal table */
17969 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17970 if( pObj == 0 ){
17971 return GenStateOutOfMem(pGen);
17972 }
17973 jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
17974 GenStateInstallLiteral(&(*pGen), pObj, nIdx);
17975 }
17976 /* Emit the load constant instruction */
17977 jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC,1,nIdx, 0, 0);
17978 /* Node successfully compiled */
17979 return SXRET_OK;
17980}
17981/*
17982 * Compile an array entry whether it is a key or a value.
17983 */
17984static sxi32 GenStateCompileJSONEntry(
17985 jx9_gen_state *pGen, /* Code generator state */
17986 SyToken *pIn, /* Token stream */
17987 SyToken *pEnd, /* End of the token stream */
17988 sxi32 iFlags, /* Compilation flags */
17989 sxi32 (*xValidator)(jx9_gen_state *,jx9_expr_node *) /* Expression tree validator callback */
17990 )
17991{
17992 SyToken *pTmpIn, *pTmpEnd;
17993 sxi32 rc;
17994 /* Swap token stream */
17995 SWAP_DELIMITER(pGen, pIn, pEnd);
17996 /* Compile the expression*/
17997 rc = jx9CompileExpr(&(*pGen), iFlags, xValidator);
17998 /* Restore token stream */
17999 RE_SWAP_DELIMITER(pGen);
18000 return rc;
18001}
18002/*
18003 * Compile a Jx9 JSON Array.
18004 */
18005JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag)
18006{
18007 sxi32 nPair = 0;
18008 SyToken *pCur;
18009 sxi32 rc;
18010
18011 pGen->pIn++; /* Jump the open square bracket '['*/
18012 pGen->pEnd--;
18013 SXUNUSED(iCompileFlag); /* cc warning */
18014 for(;;){
18015 /* Jump leading commas */
18016 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
18017 pGen->pIn++;
18018 }
18019 pCur = pGen->pIn;
18020 if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
18021 /* No more entry to process */
18022 break;
18023 }
18024 /* Compile entry */
18025 rc = GenStateCompileJSONEntry(&(*pGen),pCur,pGen->pIn,EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
18026 if( rc == SXERR_ABORT ){
18027 return SXERR_ABORT;
18028 }
18029 nPair++;
18030 }
18031 /* Emit the load map instruction */
18032 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP,nPair,0,0,0);
18033 /* Node successfully compiled */
18034 return SXRET_OK;
18035}
18036/*
18037 * Node validator for a given JSON key.
18038 */
18039static sxi32 GenStateJSONObjectKeyNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
18040{
18041 sxi32 rc = SXRET_OK;
18042 if( pRoot->xCode != jx9CompileVariable && pRoot->xCode != jx9CompileString
18043 && pRoot->xCode != jx9CompileSimpleString && pRoot->xCode != jx9CompileLiteral ){
18044 /* Unexpected expression */
18045 rc = jx9GenCompileError(&(*pGen), E_ERROR, pRoot->pStart? pRoot->pStart->nLine : 0,
18046 "JSON Object: Unexpected expression, key must be of type string, literal or simple variable");
18047 if( rc != SXERR_ABORT ){
18048 rc = SXERR_INVALID;
18049 }
18050 }
18051 return rc;
18052}
18053/*
18054 * Compile a Jx9 JSON Object
18055 */
18056JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag)
18057{
18058 SyToken *pKey, *pCur;
18059 sxi32 nPair = 0;
18060 sxi32 rc;
18061
18062 pGen->pIn++; /* Jump the open querly braces '{'*/
18063 pGen->pEnd--;
18064 SXUNUSED(iCompileFlag); /* cc warning */
18065 for(;;){
18066 /* Jump leading commas */
18067 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
18068 pGen->pIn++;
18069 }
18070 pCur = pGen->pIn;
18071 if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
18072 /* No more entry to process */
18073 break;
18074 }
18075 /* Compile the key */
18076 pKey = pCur;
18077 while( pCur < pGen->pIn ){
18078 if( pCur->nType & JX9_TK_COLON /*':'*/ ){
18079 break;
18080 }
18081 pCur++;
18082 }
18083 rc = SXERR_EMPTY;
18084 if( pCur < pGen->pIn ){
18085 if( &pCur[1] >= pGen->pIn ){
18086 /* Missing value */
18087 rc = jx9GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "JSON Object: Missing entry value");
18088 if( rc == SXERR_ABORT ){
18089 return SXERR_ABORT;
18090 }
18091 return SXRET_OK;
18092 }
18093 /* Compile the expression holding the key */
18094 rc = GenStateCompileJSONEntry(&(*pGen), pKey, pCur,
18095 EXPR_FLAG_RDONLY_LOAD /* Do not create the variable if inexistant */,
18096 GenStateJSONObjectKeyNodeValidator /* Node validator callback */
18097 );
18098 if( rc == SXERR_ABORT ){
18099 return SXERR_ABORT;
18100 }
18101 pCur++; /* Jump the double colon ':' */
18102 }else if( pKey == pCur ){
18103 /* Key is omitted, emit an error */
18104 jx9GenCompileError(&(*pGen),E_ERROR, pCur->nLine, "JSON Object: Missing entry key");
18105 pCur++; /* Jump the double colon ':' */
18106 }else{
18107 /* Reset back the cursor and point to the entry value */
18108 pCur = pKey;
18109 }
18110 /* Compile indice value */
18111 rc = GenStateCompileJSONEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
18112 if( rc == SXERR_ABORT ){
18113 return SXERR_ABORT;
18114 }
18115 nPair++;
18116 }
18117 /* Emit the load map instruction */
18118 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP, nPair * 2, 1, 0, 0);
18119 /* Node successfully compiled */
18120 return SXRET_OK;
18121}
18122/*
18123 * Compile a function [i.e: print, exit(), include(), ...] which is a langauge
18124 * construct.
18125 */
18126JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen,sxi32 iCompileFlag)
18127{
18128 SyString *pName;
18129 sxu32 nKeyID;
18130 sxi32 rc;
18131 /* Name of the language construct [i.e: print, die...]*/
18132 pName = &pGen->pIn->sData;
18133 nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
18134 pGen->pIn++; /* Jump the language construct keyword */
18135 if( nKeyID == JX9_TKWRD_PRINT ){
18136 SyToken *pTmp, *pNext = 0;
18137 /* Compile arguments one after one */
18138 pTmp = pGen->pEnd;
18139 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1 /* Boolean true index */, 0, 0);
18140 while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
18141 if( pGen->pIn < pNext ){
18142 pGen->pEnd = pNext;
18143 rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
18144 if( rc == SXERR_ABORT ){
18145 return SXERR_ABORT;
18146 }
18147 if( rc != SXERR_EMPTY ){
18148 /* Ticket 1433-008: Optimization #1: Consume input directly
18149 * without the overhead of a function call.
18150 * This is a very powerful optimization that improve
18151 * performance greatly.
18152 */
18153 jx9VmEmitInstr(pGen->pVm,JX9_OP_CONSUME,1,0,0,0);
18154 }
18155 }
18156 /* Jump trailing commas */
18157 while( pNext < pTmp && (pNext->nType & JX9_TK_COMMA) ){
18158 pNext++;
18159 }
18160 pGen->pIn = pNext;
18161 }
18162 /* Restore token stream */
18163 pGen->pEnd = pTmp;
18164 }else{
18165 sxi32 nArg = 0;
18166 sxu32 nIdx = 0;
18167 rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
18168 if( rc == SXERR_ABORT ){
18169 return SXERR_ABORT;
18170 }else if(rc != SXERR_EMPTY ){
18171 nArg = 1;
18172 }
18173 if( SXRET_OK != GenStateFindLiteral(&(*pGen), pName, &nIdx) ){
18174 jx9_value *pObj;
18175 /* Emit the call instruction */
18176 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
18177 if( pObj == 0 ){
18178 SXUNUSED(iCompileFlag); /* cc warning */
18179 return GenStateOutOfMem(pGen);
18180 }
18181 jx9MemObjInitFromString(pGen->pVm, pObj, pName);
18182 /* Install in the literal table */
18183 GenStateInstallLiteral(&(*pGen), pObj, nIdx);
18184 }
18185 /* Emit the call instruction */
18186 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
18187 jx9VmEmitInstr(pGen->pVm, JX9_OP_CALL, nArg, 0, 0, 0);
18188 }
18189 /* Node successfully compiled */
18190 return SXRET_OK;
18191}
18192/*
18193 * Compile a node holding a variable declaration.
18194 * According to the J9X language reference
18195 * Variables in JX9 are represented by a dollar sign followed by the name of the variable.
18196 * The variable name is case-sensitive.
18197 * Variable names follow the same rules as other labels in JX9. A valid variable name
18198 * starts with a letter, underscore or any UTF-8 stream, followed by any number of letters
18199 * numbers, or underscores.
18200 * By default, variables are always assigned by value unless the target value is a JSON
18201 * array or a JSON object which is passed by reference.
18202 */
18203JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen,sxi32 iCompileFlag)
18204{
18205 sxu32 nLine = pGen->pIn->nLine;
18206 SyHashEntry *pEntry;
18207 SyString *pName;
18208 char *zName = 0;
18209 sxi32 iP1;
18210 void *p3;
18211 sxi32 rc;
18212
18213 pGen->pIn++; /* Jump the dollar sign '$' */
18214 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
18215 /* Invalid variable name */
18216 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Invalid variable name");
18217 if( rc == SXERR_ABORT ){
18218 /* Error count limit reached, abort immediately */
18219 return SXERR_ABORT;
18220 }
18221 return SXRET_OK;
18222 }
18223 /* Extract variable name */
18224 pName = &pGen->pIn->sData;
18225 /* Advance the stream cursor */
18226 pGen->pIn++;
18227 pEntry = SyHashGet(&pGen->hVar, (const void *)pName->zString, pName->nByte);
18228 if( pEntry == 0 ){
18229 /* Duplicate name */
18230 zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
18231 if( zName == 0 ){
18232 return GenStateOutOfMem(pGen);
18233 }
18234 /* Install in the hashtable */
18235 SyHashInsert(&pGen->hVar, zName, pName->nByte, zName);
18236 }else{
18237 /* Name already available */
18238 zName = (char *)pEntry->pUserData;
18239 }
18240 p3 = (void *)zName;
18241 iP1 = 0;
18242 if( iCompileFlag & EXPR_FLAG_RDONLY_LOAD ){
18243 if( (iCompileFlag & EXPR_FLAG_LOAD_IDX_STORE) == 0 ){
18244 /* Read-only load.In other words do not create the variable if inexistant */
18245 iP1 = 1;
18246 }
18247 }
18248 /* Emit the load instruction */
18249 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD, iP1, 0, p3, 0);
18250 /* Node successfully compiled */
18251 return SXRET_OK;
18252}
18253/* Forward declaration */
18254static sxi32 GenStateCompileFunc(jx9_gen_state *pGen,SyString *pName,sxi32 iFlags,jx9_vm_func **ppFunc);
18255/*
18256 * Compile an annoynmous function or a closure.
18257 * According to the JX9 language reference
18258 * Anonymous functions, also known as closures, allow the creation of functions
18259 * which have no specified name. They are most useful as the value of callback
18260 * parameters, but they have many other uses. Closures can also be used as
18261 * the values of variables; Assigning a closure to a variable uses the same
18262 * syntax as any other assignment, including the trailing semicolon:
18263 * Example Anonymous function variable assignment example
18264 * $greet = function($name)
18265 * {
18266 * printf("Hello %s\r\n", $name);
18267 * };
18268 * $greet('World');
18269 * $greet('JX9');
18270 * Note that the implementation of annoynmous function and closure under
18271 * JX9 is completely different from the one used by the engine.
18272 */
18273JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen,sxi32 iCompileFlag)
18274{
18275 jx9_vm_func *pAnnonFunc; /* Annonymous function body */
18276 char zName[512]; /* Unique lambda name */
18277 static int iCnt = 1; /* There is no worry about thread-safety here, because only
18278 * one thread is allowed to compile the script.
18279 */
18280 jx9_value *pObj;
18281 SyString sName;
18282 sxu32 nIdx;
18283 sxu32 nLen;
18284 sxi32 rc;
18285
18286 pGen->pIn++; /* Jump the 'function' keyword */
18287 if( pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
18288 pGen->pIn++;
18289 }
18290 /* Reserve a constant for the lambda */
18291 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
18292 if( pObj == 0 ){
18293 GenStateOutOfMem(pGen);
18294 SXUNUSED(iCompileFlag); /* cc warning */
18295 return SXERR_ABORT;
18296 }
18297 /* Generate a unique name */
18298 nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
18299 /* Make sure the generated name is unique */
18300 while( SyHashGet(&pGen->pVm->hFunction, zName, nLen) != 0 && nLen < sizeof(zName) - 2 ){
18301 nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
18302 }
18303 SyStringInitFromBuf(&sName, zName, nLen);
18304 jx9MemObjInitFromString(pGen->pVm, pObj, &sName);
18305 /* Compile the lambda body */
18306 rc = GenStateCompileFunc(&(*pGen),&sName,0,&pAnnonFunc);
18307 if( rc == SXERR_ABORT ){
18308 return SXERR_ABORT;
18309 }
18310 /* Emit the load constant instruction */
18311 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
18312 /* Node successfully compiled */
18313 return SXRET_OK;
18314}
18315/*
18316 * Compile the 'continue' statement.
18317 * According to the JX9 language reference
18318 * continue is used within looping structures to skip the rest of the current loop iteration
18319 * and continue execution at the condition evaluation and then the beginning of the next
18320 * iteration.
18321 * Note: Note that in JX9 the switch statement is considered a looping structure for
18322 * the purposes of continue.
18323 * continue accepts an optional numeric argument which tells it how many levels
18324 * of enclosing loops it should skip to the end of.
18325 * Note:
18326 * continue 0; and continue 1; is the same as running continue;.
18327 */
18328static sxi32 jx9CompileContinue(jx9_gen_state *pGen)
18329{
18330 GenBlock *pLoop; /* Target loop */
18331 sxi32 iLevel; /* How many nesting loop to skip */
18332 sxu32 nLine;
18333 sxi32 rc;
18334 nLine = pGen->pIn->nLine;
18335 iLevel = 0;
18336 /* Jump the 'continue' keyword */
18337 pGen->pIn++;
18338 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
18339 /* optional numeric argument which tells us how many levels
18340 * of enclosing loops we should skip to the end of.
18341 */
18342 iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
18343 if( iLevel < 2 ){
18344 iLevel = 0;
18345 }
18346 pGen->pIn++; /* Jump the optional numeric argument */
18347 }
18348 /* Point to the target loop */
18349 pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
18350 if( pLoop == 0 ){
18351 /* Illegal continue */
18352 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop or switch");
18353 if( rc == SXERR_ABORT ){
18354 /* Error count limit reached, abort immediately */
18355 return SXERR_ABORT;
18356 }
18357 }else{
18358 sxu32 nInstrIdx = 0;
18359 /* Emit the unconditional jump to the beginning of the target loop */
18360 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx);
18361 if( pLoop->bPostContinue == TRUE ){
18362 JumpFixup sJumpFix;
18363 /* Post-continue */
18364 sJumpFix.nJumpType = JX9_OP_JMP;
18365 sJumpFix.nInstrIdx = nInstrIdx;
18366 SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix);
18367 }
18368 }
18369 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
18370 /* Not so fatal, emit a warning only */
18371 jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'continue' statement");
18372 }
18373 /* Statement successfully compiled */
18374 return SXRET_OK;
18375}
18376/*
18377 * Compile the 'break' statement.
18378 * According to the JX9 language reference
18379 * break ends execution of the current for, foreach, while, do-while or switch
18380 * structure.
18381 * break accepts an optional numeric argument which tells it how many nested
18382 * enclosing structures are to be broken out of.
18383 */
18384static sxi32 jx9CompileBreak(jx9_gen_state *pGen)
18385{
18386 GenBlock *pLoop; /* Target loop */
18387 sxi32 iLevel; /* How many nesting loop to skip */
18388 sxu32 nLine;
18389 sxi32 rc;
18390 nLine = pGen->pIn->nLine;
18391 iLevel = 0;
18392 /* Jump the 'break' keyword */
18393 pGen->pIn++;
18394 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
18395 /* optional numeric argument which tells us how many levels
18396 * of enclosing loops we should skip to the end of.
18397 */
18398 iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
18399 if( iLevel < 2 ){
18400 iLevel = 0;
18401 }
18402 pGen->pIn++; /* Jump the optional numeric argument */
18403 }
18404 /* Extract the target loop */
18405 pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
18406 if( pLoop == 0 ){
18407 /* Illegal break */
18408 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch");
18409 if( rc == SXERR_ABORT ){
18410 /* Error count limit reached, abort immediately */
18411 return SXERR_ABORT;
18412 }
18413 }else{
18414 sxu32 nInstrIdx;
18415 rc = jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nInstrIdx);
18416 if( rc == SXRET_OK ){
18417 /* Fix the jump later when the jump destination is resolved */
18418 GenStateNewJumpFixup(pLoop, JX9_OP_JMP, nInstrIdx);
18419 }
18420 }
18421 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
18422 /* Not so fatal, emit a warning only */
18423 jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'break' statement");
18424 }
18425 /* Statement successfully compiled */
18426 return SXRET_OK;
18427}
18428/* Forward declaration */
18429static sxi32 GenStateCompileChunk(jx9_gen_state *pGen,sxi32 iFlags);
18430/*
18431 * Compile a JX9 block.
18432 * A block is simply one or more JX9 statements and expressions to compile
18433 * optionally delimited by braces {}.
18434 * Return SXRET_OK on success. Any other return value indicates failure
18435 * and this function takes care of generating the appropriate error
18436 * message.
18437 */
18438static sxi32 jx9CompileBlock(
18439 jx9_gen_state *pGen /* Code generator state */
18440 )
18441{
18442 sxi32 rc;
18443 if( pGen->pIn->nType & JX9_TK_OCB /* '{' */ ){
18444 sxu32 nLine = pGen->pIn->nLine;
18445 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_STD, jx9VmInstrLength(pGen->pVm), 0, 0);
18446 if( rc != SXRET_OK ){
18447 return SXERR_ABORT;
18448 }
18449 pGen->pIn++;
18450 /* Compile until we hit the closing braces '}' */
18451 for(;;){
18452 if( pGen->pIn >= pGen->pEnd ){
18453 /* No more token to process. Missing closing braces */
18454 jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing closing braces '}'");
18455 break;
18456 }
18457 if( pGen->pIn->nType & JX9_TK_CCB/*'}'*/ ){
18458 /* Closing braces found, break immediately*/
18459 pGen->pIn++;
18460 break;
18461 }
18462 /* Compile a single statement */
18463 rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
18464 if( rc == SXERR_ABORT ){
18465 return SXERR_ABORT;
18466 }
18467 }
18468 GenStateLeaveBlock(&(*pGen), 0);
18469 }else{
18470 /* Compile a single statement */
18471 rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
18472 if( rc == SXERR_ABORT ){
18473 return SXERR_ABORT;
18474 }
18475 }
18476 /* Jump trailing semi-colons ';' */
18477 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
18478 pGen->pIn++;
18479 }
18480 return SXRET_OK;
18481}
18482/*
18483 * Compile the gentle 'while' statement.
18484 * According to the JX9 language reference
18485 * while loops are the simplest type of loop in JX9.They behave just like their C counterparts.
18486 * The basic form of a while statement is:
18487 * while (expr)
18488 * statement
18489 * The meaning of a while statement is simple. It tells JX9 to execute the nested statement(s)
18490 * repeatedly, as long as the while expression evaluates to TRUE. The value of the expression
18491 * is checked each time at the beginning of the loop, so even if this value changes during
18492 * the execution of the nested statement(s), execution will not stop until the end of the iteration
18493 * (each time JX9 runs the statements in the loop is one iteration). Sometimes, if the while
18494 * expression evaluates to FALSE from the very beginning, the nested statement(s) won't even be run once.
18495 * Like with the if statement, you can group multiple statements within the same while loop by surrounding
18496 * a group of statements with curly braces, or by using the alternate syntax:
18497 * while (expr):
18498 * statement
18499 * endwhile;
18500 */
18501static sxi32 jx9CompileWhile(jx9_gen_state *pGen)
18502{
18503 GenBlock *pWhileBlock = 0;
18504 SyToken *pTmp, *pEnd = 0;
18505 sxu32 nFalseJump;
18506 sxu32 nLine;
18507 sxi32 rc;
18508 nLine = pGen->pIn->nLine;
18509 /* Jump the 'while' keyword */
18510 pGen->pIn++;
18511 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
18512 /* Syntax error */
18513 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'while' keyword");
18514 if( rc == SXERR_ABORT ){
18515 /* Error count limit reached, abort immediately */
18516 return SXERR_ABORT;
18517 }
18518 goto Synchronize;
18519 }
18520 /* Jump the left parenthesis '(' */
18521 pGen->pIn++;
18522 /* Create the loop block */
18523 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pWhileBlock);
18524 if( rc != SXRET_OK ){
18525 return SXERR_ABORT;
18526 }
18527 /* Delimit the condition */
18528 jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
18529 if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
18530 /* Empty expression */
18531 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'while' keyword");
18532 if( rc == SXERR_ABORT ){
18533 /* Error count limit reached, abort immediately */
18534 return SXERR_ABORT;
18535 }
18536 }
18537 /* Swap token streams */
18538 pTmp = pGen->pEnd;
18539 pGen->pEnd = pEnd;
18540 /* Compile the expression */
18541 rc = jx9CompileExpr(&(*pGen), 0, 0);
18542 if( rc == SXERR_ABORT ){
18543 /* Expression handler request an operation abort [i.e: Out-of-memory] */
18544 return SXERR_ABORT;
18545 }
18546 /* Update token stream */
18547 while(pGen->pIn < pEnd ){
18548 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
18549 if( rc == SXERR_ABORT ){
18550 return SXERR_ABORT;
18551 }
18552 pGen->pIn++;
18553 }
18554 /* Synchronize pointers */
18555 pGen->pIn = &pEnd[1];
18556 pGen->pEnd = pTmp;
18557 /* Emit the false jump */
18558 jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
18559 /* Save the instruction index so we can fix it later when the jump destination is resolved */
18560 GenStateNewJumpFixup(pWhileBlock, JX9_OP_JZ, nFalseJump);
18561 /* Compile the loop body */
18562 rc = jx9CompileBlock(&(*pGen));
18563 if( rc == SXERR_ABORT ){
18564 return SXERR_ABORT;
18565 }
18566 /* Emit the unconditional jump to the start of the loop */
18567 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pWhileBlock->nFirstInstr, 0, 0);
18568 /* Fix all jumps now the destination is resolved */
18569 GenStateFixJumps(pWhileBlock, -1, jx9VmInstrLength(pGen->pVm));
18570 /* Release the loop block */
18571 GenStateLeaveBlock(pGen, 0);
18572 /* Statement successfully compiled */
18573 return SXRET_OK;
18574Synchronize:
18575 /* Synchronize with the first semi-colon ';' so we can avoid
18576 * compiling this erroneous block.
18577 */
18578 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
18579 pGen->pIn++;
18580 }
18581 return SXRET_OK;
18582}
18583/*
18584 * Compile the complex and powerful 'for' statement.
18585 * According to the JX9 language reference
18586 * for loops are the most complex loops in JX9. They behave like their C counterparts.
18587 * The syntax of a for loop is:
18588 * for (expr1; expr2; expr3)
18589 * statement
18590 * The first expression (expr1) is evaluated (executed) once unconditionally at
18591 * the beginning of the loop.
18592 * In the beginning of each iteration, expr2 is evaluated. If it evaluates to
18593 * TRUE, the loop continues and the nested statement(s) are executed. If it evaluates
18594 * to FALSE, the execution of the loop ends.
18595 * At the end of each iteration, expr3 is evaluated (executed).
18596 * Each of the expressions can be empty or contain multiple expressions separated by commas.
18597 * In expr2, all expressions separated by a comma are evaluated but the result is taken
18598 * from the last part. expr2 being empty means the loop should be run indefinitely
18599 * (JX9 implicitly considers it as TRUE, like C). This may not be as useless as you might
18600 * think, since often you'd want to end the loop using a conditional break statement instead
18601 * of using the for truth expression.
18602 */
18603static sxi32 jx9CompileFor(jx9_gen_state *pGen)
18604{
18605 SyToken *pTmp, *pPostStart, *pEnd = 0;
18606 GenBlock *pForBlock = 0;
18607 sxu32 nFalseJump;
18608 sxu32 nLine;
18609 sxi32 rc;
18610 nLine = pGen->pIn->nLine;
18611 /* Jump the 'for' keyword */
18612 pGen->pIn++;
18613 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
18614 /* Syntax error */
18615 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'for' keyword");
18616 if( rc == SXERR_ABORT ){
18617 /* Error count limit reached, abort immediately */
18618 return SXERR_ABORT;
18619 }
18620 return SXRET_OK;
18621 }
18622 /* Jump the left parenthesis '(' */
18623 pGen->pIn++;
18624 /* Delimit the init-expr;condition;post-expr */
18625 jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
18626 if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
18627 /* Empty expression */
18628 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "for: Invalid expression");
18629 if( rc == SXERR_ABORT ){
18630 /* Error count limit reached, abort immediately */
18631 return SXERR_ABORT;
18632 }
18633 /* Synchronize */
18634 pGen->pIn = pEnd;
18635 if( pGen->pIn < pGen->pEnd ){
18636 pGen->pIn++;
18637 }
18638 return SXRET_OK;
18639 }
18640 /* Swap token streams */
18641 pTmp = pGen->pEnd;
18642 pGen->pEnd = pEnd;
18643 /* Compile initialization expressions if available */
18644 rc = jx9CompileExpr(&(*pGen), 0, 0);
18645 /* Pop operand lvalues */
18646 if( rc == SXERR_ABORT ){
18647 /* Expression handler request an operation abort [i.e: Out-of-memory] */
18648 return SXERR_ABORT;
18649 }else if( rc != SXERR_EMPTY ){
18650 jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
18651 }
18652 if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
18653 /* Syntax error */
18654 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
18655 "for: Expected ';' after initialization expressions");
18656 if( rc == SXERR_ABORT ){
18657 /* Error count limit reached, abort immediately */
18658 return SXERR_ABORT;
18659 }
18660 return SXRET_OK;
18661 }
18662 /* Jump the trailing ';' */
18663 pGen->pIn++;
18664 /* Create the loop block */
18665 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForBlock);
18666 if( rc != SXRET_OK ){
18667 return SXERR_ABORT;
18668 }
18669 /* Deffer continue jumps */
18670 pForBlock->bPostContinue = TRUE;
18671 /* Compile the condition */
18672 rc = jx9CompileExpr(&(*pGen), 0, 0);
18673 if( rc == SXERR_ABORT ){
18674 /* Expression handler request an operation abort [i.e: Out-of-memory] */
18675 return SXERR_ABORT;
18676 }else if( rc != SXERR_EMPTY ){
18677 /* Emit the false jump */
18678 jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
18679 /* Save the instruction index so we can fix it later when the jump destination is resolved */
18680 GenStateNewJumpFixup(pForBlock, JX9_OP_JZ, nFalseJump);
18681 }
18682 if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
18683 /* Syntax error */
18684 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
18685 "for: Expected ';' after conditionals expressions");
18686 if( rc == SXERR_ABORT ){
18687 /* Error count limit reached, abort immediately */
18688 return SXERR_ABORT;
18689 }
18690 return SXRET_OK;
18691 }
18692 /* Jump the trailing ';' */
18693 pGen->pIn++;
18694 /* Save the post condition stream */
18695 pPostStart = pGen->pIn;
18696 /* Compile the loop body */
18697 pGen->pIn = &pEnd[1]; /* Jump the trailing parenthesis ')' */
18698 pGen->pEnd = pTmp;
18699 rc = jx9CompileBlock(&(*pGen));
18700 if( rc == SXERR_ABORT ){
18701 return SXERR_ABORT;
18702 }
18703 /* Fix post-continue jumps */
18704 if( SySetUsed(&pForBlock->aPostContFix) > 0 ){
18705 JumpFixup *aPost;
18706 VmInstr *pInstr;
18707 sxu32 nJumpDest;
18708 sxu32 n;
18709 aPost = (JumpFixup *)SySetBasePtr(&pForBlock->aPostContFix);
18710 nJumpDest = jx9VmInstrLength(pGen->pVm);
18711 for( n = 0 ; n < SySetUsed(&pForBlock->aPostContFix) ; ++n ){
18712 pInstr = jx9VmGetInstr(pGen->pVm, aPost[n].nInstrIdx);
18713 if( pInstr ){
18714 /* Fix jump */
18715 pInstr->iP2 = nJumpDest;
18716 }
18717 }
18718 }
18719 /* compile the post-expressions if available */
18720 while( pPostStart < pEnd && (pPostStart->nType & JX9_TK_SEMI) ){
18721 pPostStart++;
18722 }
18723 if( pPostStart < pEnd ){
18724 SyToken *pTmpIn, *pTmpEnd;
18725 SWAP_DELIMITER(pGen, pPostStart, pEnd);
18726 rc = jx9CompileExpr(&(*pGen), 0, 0);
18727 if( pGen->pIn < pGen->pEnd ){
18728 /* Syntax error */
18729 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "for: Expected ')' after post-expressions");
18730 if( rc == SXERR_ABORT ){
18731 /* Error count limit reached, abort immediately */
18732 return SXERR_ABORT;
18733 }
18734 return SXRET_OK;
18735 }
18736 RE_SWAP_DELIMITER(pGen);
18737 if( rc == SXERR_ABORT ){
18738 /* Expression handler request an operation abort [i.e: Out-of-memory] */
18739 return SXERR_ABORT;
18740 }else if( rc != SXERR_EMPTY){
18741 /* Pop operand lvalue */
18742 jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
18743 }
18744 }
18745 /* Emit the unconditional jump to the start of the loop */
18746 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForBlock->nFirstInstr, 0, 0);
18747 /* Fix all jumps now the destination is resolved */
18748 GenStateFixJumps(pForBlock, -1, jx9VmInstrLength(pGen->pVm));
18749 /* Release the loop block */
18750 GenStateLeaveBlock(pGen, 0);
18751 /* Statement successfully compiled */
18752 return SXRET_OK;
18753}
18754/* Expression tree validator callback used by the 'foreach' statement.
18755 * Note that only variable expression [i.e: $x; ${'My'.'Var'}; ${$a['key]};...]
18756 * are allowed.
18757 */
18758static sxi32 GenStateForEachNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
18759{
18760 sxi32 rc = SXRET_OK; /* Assume a valid expression tree */
18761 if( pRoot->xCode != jx9CompileVariable ){
18762 /* Unexpected expression */
18763 rc = jx9GenCompileError(&(*pGen),
18764 E_ERROR,
18765 pRoot->pStart? pRoot->pStart->nLine : 0,
18766 "foreach: Expecting a variable name"
18767 );
18768 if( rc != SXERR_ABORT ){
18769 rc = SXERR_INVALID;
18770 }
18771 }
18772 return rc;
18773}
18774/*
18775 * Compile the 'foreach' statement.
18776 * According to the JX9 language reference
18777 * The foreach construct simply gives an easy way to iterate over arrays. foreach works
18778 * only on arrays (and objects), and will issue an error when you try to use it on a variable
18779 * with a different data type or an uninitialized variable. There are two syntaxes; the second
18780 * is a minor but useful extension of the first:
18781 * foreach (json_array_json_object as $value)
18782 * statement
18783 * foreach (json_array_json_objec as $key,$value)
18784 * statement
18785 * The first form loops over the array given by array_expression. On each loop, the value
18786 * of the current element is assigned to $value and the internal array pointer is advanced
18787 * by one (so on the next loop, you'll be looking at the next element).
18788 * The second form does the same thing, except that the current element's key will be assigned
18789 * to the variable $key on each loop.
18790 * Note:
18791 * When foreach first starts executing, the internal array pointer is automatically reset to the
18792 * first element of the array. This means that you do not need to call reset() before a foreach loop.
18793 */
18794static sxi32 jx9CompileForeach(jx9_gen_state *pGen)
18795{
18796 SyToken *pCur, *pTmp, *pEnd = 0;
18797 GenBlock *pForeachBlock = 0;
18798 jx9_foreach_info *pInfo;
18799 sxu32 nFalseJump;
18800 VmInstr *pInstr;
18801 sxu32 nLine;
18802 sxi32 rc;
18803 nLine = pGen->pIn->nLine;
18804 /* Jump the 'foreach' keyword */
18805 pGen->pIn++;
18806 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
18807 /* Syntax error */
18808 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Expected '('");
18809 if( rc == SXERR_ABORT ){
18810 /* Error count limit reached, abort immediately */
18811 return SXERR_ABORT;
18812 }
18813 goto Synchronize;
18814 }
18815 /* Jump the left parenthesis '(' */
18816 pGen->pIn++;
18817 /* Create the loop block */
18818 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForeachBlock);
18819 if( rc != SXRET_OK ){
18820 return SXERR_ABORT;
18821 }
18822 /* Delimit the expression */
18823 jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
18824 if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
18825 /* Empty expression */
18826 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Missing expression");
18827 if( rc == SXERR_ABORT ){
18828 /* Error count limit reached, abort immediately */
18829 return SXERR_ABORT;
18830 }
18831 /* Synchronize */
18832 pGen->pIn = pEnd;
18833 if( pGen->pIn < pGen->pEnd ){
18834 pGen->pIn++;
18835 }
18836 return SXRET_OK;
18837 }
18838 /* Compile the array expression */
18839 pCur = pGen->pIn;
18840 while( pCur < pEnd ){
18841 if( pCur->nType & JX9_TK_KEYWORD ){
18842 sxi32 nKeywrd = SX_PTR_TO_INT(pCur->pUserData);
18843 if( nKeywrd == JX9_TKWRD_AS ){
18844 /* Break with the first 'as' found */
18845 break;
18846 }
18847 }
18848 /* Advance the stream cursor */
18849 pCur++;
18850 }
18851 if( pCur <= pGen->pIn ){
18852 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine,
18853 "foreach: Missing array/object expression");
18854 if( rc == SXERR_ABORT ){
18855 /* Don't worry about freeing memory, everything will be released shortly */
18856 return SXERR_ABORT;
18857 }
18858 goto Synchronize;
18859 }
18860 /* Swap token streams */
18861 pTmp = pGen->pEnd;
18862 pGen->pEnd = pCur;
18863 rc = jx9CompileExpr(&(*pGen), 0, 0);
18864 if( rc == SXERR_ABORT ){
18865 /* Expression handler request an operation abort [i.e: Out-of-memory] */
18866 return SXERR_ABORT;
18867 }
18868 /* Update token stream */
18869 while(pGen->pIn < pCur ){
18870 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Unexpected token '%z'", &pGen->pIn->sData);
18871 if( rc == SXERR_ABORT ){
18872 /* Don't worry about freeing memory, everything will be released shortly */
18873 return SXERR_ABORT;
18874 }
18875 pGen->pIn++;
18876 }
18877 pCur++; /* Jump the 'as' keyword */
18878 pGen->pIn = pCur;
18879 if( pGen->pIn >= pEnd ){
18880 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key => $value pair");
18881 if( rc == SXERR_ABORT ){
18882 return SXERR_ABORT;
18883 }
18884 }
18885 /* Create the foreach context */
18886 pInfo = (jx9_foreach_info *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_foreach_info));
18887 if( pInfo == 0 ){
18888 jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Fatal, JX9 engine is running out-of-memory");
18889 return SXERR_ABORT;
18890 }
18891 /* Zero the structure */
18892 SyZero(pInfo, sizeof(jx9_foreach_info));
18893 /* Initialize structure fields */
18894 SySetInit(&pInfo->aStep, &pGen->pVm->sAllocator, sizeof(jx9_foreach_step *));
18895 /* Check if we have a key field */
18896 while( pCur < pEnd && (pCur->nType & JX9_TK_COMMA) == 0 ){
18897 pCur++;
18898 }
18899 if( pCur < pEnd ){
18900 /* Compile the expression holding the key name */
18901 if( pGen->pIn >= pCur ){
18902 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key");
18903 if( rc == SXERR_ABORT ){
18904 /* Don't worry about freeing memory, everything will be released shortly */
18905 return SXERR_ABORT;
18906 }
18907 }else{
18908 pGen->pEnd = pCur;
18909 rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
18910 if( rc == SXERR_ABORT ){
18911 /* Don't worry about freeing memory, everything will be released shortly */
18912 return SXERR_ABORT;
18913 }
18914 pInstr = jx9VmPopInstr(pGen->pVm);
18915 if( pInstr->p3 ){
18916 /* Record key name */
18917 SyStringInitFromBuf(&pInfo->sKey, pInstr->p3, SyStrlen((const char *)pInstr->p3));
18918 }
18919 pInfo->iFlags |= JX9_4EACH_STEP_KEY;
18920 }
18921 pGen->pIn = &pCur[1]; /* Jump the arrow */
18922 }
18923 pGen->pEnd = pEnd;
18924 if( pGen->pIn >= pEnd ){
18925 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $value");
18926 if( rc == SXERR_ABORT ){
18927 /* Don't worry about freeing memory, everything will be released shortly */
18928 return SXERR_ABORT;
18929 }
18930 goto Synchronize;
18931 }
18932 /* Compile the expression holding the value name */
18933 rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
18934 if( rc == SXERR_ABORT ){
18935 /* Don't worry about freeing memory, everything will be released shortly */
18936 return SXERR_ABORT;
18937 }
18938 pInstr = jx9VmPopInstr(pGen->pVm);
18939 if( pInstr->p3 ){
18940 /* Record value name */
18941 SyStringInitFromBuf(&pInfo->sValue, pInstr->p3, SyStrlen((const char *)pInstr->p3));
18942 }
18943 /* Emit the 'FOREACH_INIT' instruction */
18944 jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_INIT, 0, 0, pInfo, &nFalseJump);
18945 /* Save the instruction index so we can fix it later when the jump destination is resolved */
18946 GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_INIT, nFalseJump);
18947 /* Record the first instruction to execute */
18948 pForeachBlock->nFirstInstr = jx9VmInstrLength(pGen->pVm);
18949 /* Emit the FOREACH_STEP instruction */
18950 jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_STEP, 0, 0, pInfo, &nFalseJump);
18951 /* Save the instruction index so we can fix it later when the jump destination is resolved */
18952 GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_STEP, nFalseJump);
18953 /* Compile the loop body */
18954 pGen->pIn = &pEnd[1];
18955 pGen->pEnd = pTmp;
18956 rc = jx9CompileBlock(&(*pGen));
18957 if( rc == SXERR_ABORT ){
18958 /* Don't worry about freeing memory, everything will be released shortly */
18959 return SXERR_ABORT;
18960 }
18961 /* Emit the unconditional jump to the start of the loop */
18962 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForeachBlock->nFirstInstr, 0, 0);
18963 /* Fix all jumps now the destination is resolved */
18964 GenStateFixJumps(pForeachBlock, -1,jx9VmInstrLength(pGen->pVm));
18965 /* Release the loop block */
18966 GenStateLeaveBlock(pGen, 0);
18967 /* Statement successfully compiled */
18968 return SXRET_OK;
18969Synchronize:
18970 /* Synchronize with the first semi-colon ';' so we can avoid
18971 * compiling this erroneous block.
18972 */
18973 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
18974 pGen->pIn++;
18975 }
18976 return SXRET_OK;
18977}
18978/*
18979 * Compile the infamous if/elseif/else if/else statements.
18980 * According to the JX9 language reference
18981 * The if construct is one of the most important features of many languages JX9 included.
18982 * It allows for conditional execution of code fragments. JX9 features an if structure
18983 * that is similar to that of C:
18984 * if (expr)
18985 * statement
18986 * else construct:
18987 * Often you'd want to execute a statement if a certain condition is met, and a different
18988 * statement if the condition is not met. This is what else is for. else extends an if statement
18989 * to execute a statement in case the expression in the if statement evaluates to FALSE.
18990 * For example, the following code would display a is greater than b if $a is greater than
18991 * $b, and a is NOT greater than b otherwise.
18992 * The else statement is only executed if the if expression evaluated to FALSE, and if there
18993 * were any elseif expressions - only if they evaluated to FALSE as well
18994 * elseif
18995 * elseif, as its name suggests, is a combination of if and else. Like else, it extends
18996 * an if statement to execute a different statement in case the original if expression evaluates
18997 * to FALSE. However, unlike else, it will execute that alternative expression only if the elseif
18998 * conditional expression evaluates to TRUE. For example, the following code would display a is bigger
18999 * than b, a equal to b or a is smaller than b:
19000 * if ($a > $b) {
19001 * print "a is bigger than b";
19002 * } elseif ($a == $b) {
19003 * print "a is equal to b";
19004 * } else {
19005 * print "a is smaller than b";
19006 * }
19007 */
19008static sxi32 jx9CompileIf(jx9_gen_state *pGen)
19009{
19010 SyToken *pToken, *pTmp, *pEnd = 0;
19011 GenBlock *pCondBlock = 0;
19012 sxu32 nJumpIdx;
19013 sxu32 nKeyID;
19014 sxi32 rc;
19015 /* Jump the 'if' keyword */
19016 pGen->pIn++;
19017 pToken = pGen->pIn;
19018 /* Create the conditional block */
19019 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_COND, jx9VmInstrLength(pGen->pVm), 0, &pCondBlock);
19020 if( rc != SXRET_OK ){
19021 return SXERR_ABORT;
19022 }
19023 /* Process as many [if/else if/elseif/else] blocks as we can */
19024 for(;;){
19025 if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_LPAREN) == 0 ){
19026 /* Syntax error */
19027 if( pToken >= pGen->pEnd ){
19028 pToken--;
19029 }
19030 rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing '('");
19031 if( rc == SXERR_ABORT ){
19032 /* Error count limit reached, abort immediately */
19033 return SXERR_ABORT;
19034 }
19035 goto Synchronize;
19036 }
19037 /* Jump the left parenthesis '(' */
19038 pToken++;
19039 /* Delimit the condition */
19040 jx9DelimitNestedTokens(pToken, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
19041 if( pToken >= pEnd || (pEnd->nType & JX9_TK_RPAREN) == 0 ){
19042 /* Syntax error */
19043 if( pToken >= pGen->pEnd ){
19044 pToken--;
19045 }
19046 rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing ')'");
19047 if( rc == SXERR_ABORT ){
19048 /* Error count limit reached, abort immediately */
19049 return SXERR_ABORT;
19050 }
19051 goto Synchronize;
19052 }
19053 /* Swap token streams */
19054 SWAP_TOKEN_STREAM(pGen, pToken, pEnd);
19055 /* Compile the condition */
19056 rc = jx9CompileExpr(&(*pGen), 0, 0);
19057 /* Update token stream */
19058 while(pGen->pIn < pEnd ){
19059 jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
19060 pGen->pIn++;
19061 }
19062 pGen->pIn = &pEnd[1];
19063 pGen->pEnd = pTmp;
19064 if( rc == SXERR_ABORT ){
19065 /* Expression handler request an operation abort [i.e: Out-of-memory] */
19066 return SXERR_ABORT;
19067 }
19068 /* Emit the false jump */
19069 jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJumpIdx);
19070 /* Save the instruction index so we can fix it later when the jump destination is resolved */
19071 GenStateNewJumpFixup(pCondBlock, JX9_OP_JZ, nJumpIdx);
19072 /* Compile the body */
19073 rc = jx9CompileBlock(&(*pGen));
19074 if( rc == SXERR_ABORT ){
19075 return SXERR_ABORT;
19076 }
19077 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
19078 break;
19079 }
19080 /* Ensure that the keyword ID is 'else if' or 'else' */
19081 nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
19082 if( (nKeyID & (JX9_TKWRD_ELSE|JX9_TKWRD_ELIF)) == 0 ){
19083 break;
19084 }
19085 /* Emit the unconditional jump */
19086 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJumpIdx);
19087 /* Save the instruction index so we can fix it later when the jump destination is resolved */
19088 GenStateNewJumpFixup(pCondBlock, JX9_OP_JMP, nJumpIdx);
19089 if( nKeyID & JX9_TKWRD_ELSE ){
19090 pToken = &pGen->pIn[1];
19091 if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_KEYWORD) == 0 ||
19092 SX_PTR_TO_INT(pToken->pUserData) != JX9_TKWRD_IF ){
19093 break;
19094 }
19095 pGen->pIn++; /* Jump the 'else' keyword */
19096 }
19097 pGen->pIn++; /* Jump the 'elseif/if' keyword */
19098 /* Synchronize cursors */
19099 pToken = pGen->pIn;
19100 /* Fix the false jump */
19101 GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
19102 } /* For(;;) */
19103 /* Fix the false jump */
19104 GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
19105 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_KEYWORD) &&
19106 (SX_PTR_TO_INT(pGen->pIn->pUserData) & JX9_TKWRD_ELSE) ){
19107 /* Compile the else block */
19108 pGen->pIn++;
19109 rc = jx9CompileBlock(&(*pGen));
19110 if( rc == SXERR_ABORT ){
19111
19112 return SXERR_ABORT;
19113 }
19114 }
19115 nJumpIdx = jx9VmInstrLength(pGen->pVm);
19116 /* Fix all unconditional jumps now the destination is resolved */
19117 GenStateFixJumps(pCondBlock, JX9_OP_JMP, nJumpIdx);
19118 /* Release the conditional block */
19119 GenStateLeaveBlock(pGen, 0);
19120 /* Statement successfully compiled */
19121 return SXRET_OK;
19122Synchronize:
19123 /* Synchronize with the first semi-colon ';' so we can avoid compiling this erroneous block.
19124 */
19125 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
19126 pGen->pIn++;
19127 }
19128 return SXRET_OK;
19129}
19130/*
19131 * Compile the return statement.
19132 * According to the JX9 language reference
19133 * If called from within a function, the return() statement immediately ends execution
19134 * of the current function, and returns its argument as the value of the function call.
19135 * return() will also end the execution of an eval() statement or script file.
19136 * If called from the global scope, then execution of the current script file is ended.
19137 * If the current script file was include()ed or require()ed, then control is passed back
19138 * to the calling file. Furthermore, if the current script file was include()ed, then the value
19139 * given to return() will be returned as the value of the include() call. If return() is called
19140 * from within the main script file, then script execution end.
19141 * Note that since return() is a language construct and not a function, the parentheses
19142 * surrounding its arguments are not required. It is common to leave them out, and you actually
19143 * should do so as JX9 has less work to do in this case.
19144 * Note: If no parameter is supplied, then the parentheses must be omitted and JX9 is returning NULL instead..
19145 */
19146static sxi32 jx9CompileReturn(jx9_gen_state *pGen)
19147{
19148 sxi32 nRet = 0; /* TRUE if there is a return value */
19149 sxi32 rc;
19150 /* Jump the 'return' keyword */
19151 pGen->pIn++;
19152 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
19153 /* Compile the expression */
19154 rc = jx9CompileExpr(&(*pGen), 0, 0);
19155 if( rc == SXERR_ABORT ){
19156 return SXERR_ABORT;
19157 }else if(rc != SXERR_EMPTY ){
19158 nRet = 1;
19159 }
19160 }
19161 /* Emit the done instruction */
19162 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, nRet, 0, 0, 0);
19163 return SXRET_OK;
19164}
19165/*
19166 * Compile the die/exit language construct.
19167 * The role of these constructs is to terminate execution of the script.
19168 * Shutdown functions will always be executed even if exit() is called.
19169 */
19170static sxi32 jx9CompileHalt(jx9_gen_state *pGen)
19171{
19172 sxi32 nExpr = 0;
19173 sxi32 rc;
19174 /* Jump the die/exit keyword */
19175 pGen->pIn++;
19176 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
19177 /* Compile the expression */
19178 rc = jx9CompileExpr(&(*pGen), 0, 0);
19179 if( rc == SXERR_ABORT ){
19180 return SXERR_ABORT;
19181 }else if(rc != SXERR_EMPTY ){
19182 nExpr = 1;
19183 }
19184 }
19185 /* Emit the HALT instruction */
19186 jx9VmEmitInstr(pGen->pVm, JX9_OP_HALT, nExpr, 0, 0, 0);
19187 return SXRET_OK;
19188}
19189/*
19190 * Compile the static statement.
19191 * According to the JX9 language reference
19192 * Another important feature of variable scoping is the static variable.
19193 * A static variable exists only in a local function scope, but it does not lose its value
19194 * when program execution leaves this scope.
19195 * Static variables also provide one way to deal with recursive functions.
19196 */
19197static sxi32 jx9CompileStatic(jx9_gen_state *pGen)
19198{
19199 jx9_vm_func_static_var sStatic; /* Structure describing the static variable */
19200 jx9_vm_func *pFunc; /* Enclosing function */
19201 GenBlock *pBlock;
19202 SyString *pName;
19203 char *zDup;
19204 sxu32 nLine;
19205 sxi32 rc;
19206 /* Jump the static keyword */
19207 nLine = pGen->pIn->nLine;
19208 pGen->pIn++;
19209 /* Extract the enclosing function if any */
19210 pBlock = pGen->pCurrent;
19211 while( pBlock ){
19212 if( pBlock->iFlags & GEN_BLOCK_FUNC){
19213 break;
19214 }
19215 /* Point to the upper block */
19216 pBlock = pBlock->pParent;
19217 }
19218 if( pBlock == 0 ){
19219 /* Static statement, called outside of a function body, treat it as a simple variable. */
19220 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
19221 rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
19222 if( rc == SXERR_ABORT ){
19223 return SXERR_ABORT;
19224 }
19225 goto Synchronize;
19226 }
19227 /* Compile the expression holding the variable */
19228 rc = jx9CompileExpr(&(*pGen), 0, 0);
19229 if( rc == SXERR_ABORT ){
19230 return SXERR_ABORT;
19231 }else if( rc != SXERR_EMPTY ){
19232 /* Emit the POP instruction */
19233 jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
19234 }
19235 return SXRET_OK;
19236 }
19237 pFunc = (jx9_vm_func *)pBlock->pUserData;
19238 /* Make sure we are dealing with a valid statement */
19239 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd ||
19240 (pGen->pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
19241 rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
19242 if( rc == SXERR_ABORT ){
19243 return SXERR_ABORT;
19244 }
19245 goto Synchronize;
19246 }
19247 pGen->pIn++;
19248 /* Extract variable name */
19249 pName = &pGen->pIn->sData;
19250 pGen->pIn++; /* Jump the var name */
19251 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_EQUAL/*'='*/)) == 0 ){
19252 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "static: Unexpected token '%z'", &pGen->pIn->sData);
19253 goto Synchronize;
19254 }
19255 /* Initialize the structure describing the static variable */
19256 SySetInit(&sStatic.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
19257 sStatic.nIdx = SXU32_HIGH; /* Not yet created */
19258 /* Duplicate variable name */
19259 zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
19260 if( zDup == 0 ){
19261 jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, JX9 engine is running out of memory");
19262 return SXERR_ABORT;
19263 }
19264 SyStringInitFromBuf(&sStatic.sName, zDup, pName->nByte);
19265 /* Check if we have an expression to compile */
19266 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_EQUAL) ){
19267 SySet *pInstrContainer;
19268 pGen->pIn++; /* Jump the equal '=' sign */
19269 /* Swap bytecode container */
19270 pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
19271 jx9VmSetByteCodeContainer(pGen->pVm, &sStatic.aByteCode);
19272 /* Compile the expression */
19273 rc = jx9CompileExpr(&(*pGen), 0, 0);
19274 /* Emit the done instruction */
19275 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
19276 /* Restore default bytecode container */
19277 jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
19278 }
19279 /* Finally save the compiled static variable in the appropriate container */
19280 SySetPut(&pFunc->aStatic, (const void *)&sStatic);
19281 return SXRET_OK;
19282Synchronize:
19283 /* Synchronize with the first semi-colon ';', so we can avoid compiling this erroneous
19284 * statement.
19285 */
19286 while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
19287 pGen->pIn++;
19288 }
19289 return SXRET_OK;
19290}
19291/*
19292 * Compile the 'const' statement.
19293 * According to the JX9 language reference
19294 * A constant is an identifier (name) for a simple value. As the name suggests, that value
19295 * cannot change during the execution of the script (except for magic constants, which aren't actually constants).
19296 * A constant is case-sensitive by default. By convention, constant identifiers are always uppercase.
19297 * The name of a constant follows the same rules as any label in JX9. A valid constant name starts
19298 * with a letter or underscore, followed by any number of letters, numbers, or underscores.
19299 * As a regular expression it would be expressed thusly: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
19300 * Syntax
19301 * You can define a constant by using the define()-function or by using the const keyword outside
19302 * a object definition. Once a constant is defined, it can never be changed or undefined.
19303 * You can get the value of a constant by simply specifying its name. Unlike with variables
19304 * you should not prepend a constant with a $. You can also use the function constant() to read
19305 * a constant's value if you wish to obtain the constant's name dynamically. Use get_defined_constants()
19306 * to get a list of all defined constants.
19307 */
19308static sxi32 jx9CompileConstant(jx9_gen_state *pGen)
19309{
19310 SySet *pConsCode, *pInstrContainer;
19311 sxu32 nLine = pGen->pIn->nLine;
19312 SyString *pName;
19313 sxi32 rc;
19314 pGen->pIn++; /* Jump the 'const' keyword */
19315 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
19316 /* Invalid constant name */
19317 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Invalid constant name");
19318 if( rc == SXERR_ABORT ){
19319 /* Error count limit reached, abort immediately */
19320 return SXERR_ABORT;
19321 }
19322 goto Synchronize;
19323 }
19324 /* Peek constant name */
19325 pName = &pGen->pIn->sData;
19326 /* Make sure the constant name isn't reserved */
19327 if( GenStateIsReservedID(pName) ){
19328 /* Reserved constant */
19329 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Cannot redeclare a reserved constant '%z'", pName);
19330 if( rc == SXERR_ABORT ){
19331 /* Error count limit reached, abort immediately */
19332 return SXERR_ABORT;
19333 }
19334 goto Synchronize;
19335 }
19336 pGen->pIn++;
19337 if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_EQUAL /* '=' */) == 0 ){
19338 /* Invalid statement*/
19339 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Expected '=' after constant name");
19340 if( rc == SXERR_ABORT ){
19341 /* Error count limit reached, abort immediately */
19342 return SXERR_ABORT;
19343 }
19344 goto Synchronize;
19345 }
19346 pGen->pIn++; /*Jump the equal sign */
19347 /* Allocate a new constant value container */
19348 pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(SySet));
19349 if( pConsCode == 0 ){
19350 return GenStateOutOfMem(pGen);
19351 }
19352 SySetInit(pConsCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
19353 /* Swap bytecode container */
19354 pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
19355 jx9VmSetByteCodeContainer(pGen->pVm, pConsCode);
19356 /* Compile constant value */
19357 rc = jx9CompileExpr(&(*pGen), 0, 0);
19358 /* Emit the done instruction */
19359 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
19360 jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
19361 if( rc == SXERR_ABORT ){
19362 /* Don't worry about freeing memory, everything will be released shortly */
19363 return SXERR_ABORT;
19364 }
19365 SySetSetUserData(pConsCode, pGen->pVm);
19366 /* Register the constant */
19367 rc = jx9VmRegisterConstant(pGen->pVm, pName, jx9VmExpandConstantValue, pConsCode);
19368 if( rc != SXRET_OK ){
19369 SySetRelease(pConsCode);
19370 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pConsCode);
19371 }
19372 return SXRET_OK;
19373Synchronize:
19374 /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
19375 while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
19376 pGen->pIn++;
19377 }
19378 return SXRET_OK;
19379}
19380/*
19381 * Compile the uplink construct.
19382 * According to the JX9 language reference
19383 * In JX9 global variables must be declared uplink inside a function if they are going
19384 * to be used in that function.
19385 * Example #1 Using global
19386 * $a = 1;
19387 * $b = 2;
19388 * function Sum()
19389 * {
19390 * uplink $a, $b;
19391 * $b = $a + $b;
19392 * }
19393 * Sum();
19394 * print $b;
19395 * ?>
19396 * The above script will output 3. By declaring $a and $b global within the function
19397 * all references to either variable will refer to the global version. There is no limit
19398 * to the number of global variables that can be manipulated by a function.
19399 */
19400static sxi32 jx9CompileUplink(jx9_gen_state *pGen)
19401{
19402 SyToken *pTmp, *pNext = 0;
19403 sxi32 nExpr;
19404 sxi32 rc;
19405 /* Jump the 'uplink' keyword */
19406 pGen->pIn++;
19407 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_SEMI) ){
19408 /* Nothing to process */
19409 return SXRET_OK;
19410 }
19411 pTmp = pGen->pEnd;
19412 nExpr = 0;
19413 while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
19414 if( pGen->pIn < pNext ){
19415 pGen->pEnd = pNext;
19416 if( (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
19417 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "uplink: Expected variable name");
19418 if( rc == SXERR_ABORT ){
19419 return SXERR_ABORT;
19420 }
19421 }else{
19422 pGen->pIn++;
19423 if( pGen->pIn >= pGen->pEnd ){
19424 /* Emit a warning */
19425 jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn[-1].nLine, "uplink: Empty variable name");
19426 }else{
19427 rc = jx9CompileExpr(&(*pGen), 0, 0);
19428 if( rc == SXERR_ABORT ){
19429 return SXERR_ABORT;
19430 }else if(rc != SXERR_EMPTY ){
19431 nExpr++;
19432 }
19433 }
19434 }
19435 }
19436 /* Next expression in the stream */
19437 pGen->pIn = pNext;
19438 /* Jump trailing commas */
19439 while( pGen->pIn < pTmp && (pGen->pIn->nType & JX9_TK_COMMA) ){
19440 pGen->pIn++;
19441 }
19442 }
19443 /* Restore token stream */
19444 pGen->pEnd = pTmp;
19445 if( nExpr > 0 ){
19446 /* Emit the uplink instruction */
19447 jx9VmEmitInstr(pGen->pVm, JX9_OP_UPLINK, nExpr, 0, 0, 0);
19448 }
19449 return SXRET_OK;
19450}
19451/*
19452 * Compile a switch block.
19453 * (See block-comment below for more information)
19454 */
19455static sxi32 GenStateCompileSwitchBlock(jx9_gen_state *pGen,sxu32 *pBlockStart)
19456{
19457 sxi32 rc = SXRET_OK;
19458 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*':'*/)) == 0 ){
19459 /* Unexpected token */
19460 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
19461 if( rc == SXERR_ABORT ){
19462 return SXERR_ABORT;
19463 }
19464 pGen->pIn++;
19465 }
19466 pGen->pIn++;
19467 /* First instruction to execute in this block. */
19468 *pBlockStart = jx9VmInstrLength(pGen->pVm);
19469 /* Compile the block until we hit a case/default/endswitch keyword
19470 * or the '}' token */
19471 for(;;){
19472 if( pGen->pIn >= pGen->pEnd ){
19473 /* No more input to process */
19474 break;
19475 }
19476 rc = SXRET_OK;
19477 if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
19478 if( pGen->pIn->nType & JX9_TK_CCB /*'}' */ ){
19479 rc = SXERR_EOF;
19480 break;
19481 }
19482 }else{
19483 sxi32 nKwrd;
19484 /* Extract the keyword */
19485 nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
19486 if( nKwrd == JX9_TKWRD_CASE || nKwrd == JX9_TKWRD_DEFAULT ){
19487 break;
19488 }
19489 }
19490 /* Compile block */
19491 rc = jx9CompileBlock(&(*pGen));
19492 if( rc == SXERR_ABORT ){
19493 return SXERR_ABORT;
19494 }
19495 }
19496 return rc;
19497}
19498/*
19499 * Compile a case eXpression.
19500 * (See block-comment below for more information)
19501 */
19502static sxi32 GenStateCompileCaseExpr(jx9_gen_state *pGen, jx9_case_expr *pExpr)
19503{
19504 SySet *pInstrContainer;
19505 SyToken *pEnd, *pTmp;
19506 sxi32 iNest = 0;
19507 sxi32 rc;
19508 /* Delimit the expression */
19509 pEnd = pGen->pIn;
19510 while( pEnd < pGen->pEnd ){
19511 if( pEnd->nType & JX9_TK_LPAREN /*(*/ ){
19512 /* Increment nesting level */
19513 iNest++;
19514 }else if( pEnd->nType & JX9_TK_RPAREN /*)*/ ){
19515 /* Decrement nesting level */
19516 iNest--;
19517 }else if( pEnd->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*;'*/) && iNest < 1 ){
19518 break;
19519 }
19520 pEnd++;
19521 }
19522 if( pGen->pIn >= pEnd ){
19523 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Empty case expression");
19524 if( rc == SXERR_ABORT ){
19525 /* Error count limit reached, abort immediately */
19526 return SXERR_ABORT;
19527 }
19528 }
19529 /* Swap token stream */
19530 pTmp = pGen->pEnd;
19531 pGen->pEnd = pEnd;
19532 pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
19533 jx9VmSetByteCodeContainer(pGen->pVm, &pExpr->aByteCode);
19534 rc = jx9CompileExpr(&(*pGen), 0, 0);
19535 /* Emit the done instruction */
19536 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
19537 jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
19538 /* Update token stream */
19539 pGen->pIn = pEnd;
19540 pGen->pEnd = pTmp;
19541 if( rc == SXERR_ABORT ){
19542 return SXERR_ABORT;
19543 }
19544 return SXRET_OK;
19545}
19546/*
19547 * Compile the smart switch statement.
19548 * According to the JX9 language reference manual
19549 * The switch statement is similar to a series of IF statements on the same expression.
19550 * In many occasions, you may want to compare the same variable (or expression) with many
19551 * different values, and execute a different piece of code depending on which value it equals to.
19552 * This is exactly what the switch statement is for.
19553 * Note: Note that unlike some other languages, the continue statement applies to switch and acts
19554 * similar to break. If you have a switch inside a loop and wish to continue to the next iteration
19555 * of the outer loop, use continue 2.
19556 * Note that switch/case does loose comparision.
19557 * It is important to understand how the switch statement is executed in order to avoid mistakes.
19558 * The switch statement executes line by line (actually, statement by statement).
19559 * In the beginning, no code is executed. Only when a case statement is found with a value that
19560 * matches the value of the switch expression does JX9 begin to execute the statements.
19561 * JX9 continues to execute the statements until the end of the switch block, or the first time
19562 * it sees a break statement. If you don't write a break statement at the end of a case's statement list.
19563 * In a switch statement, the condition is evaluated only once and the result is compared to each
19564 * case statement. In an elseif statement, the condition is evaluated again. If your condition
19565 * is more complicated than a simple compare and/or is in a tight loop, a switch may be faster.
19566 * The statement list for a case can also be empty, which simply passes control into the statement
19567 * list for the next case.
19568 * The case expression may be any expression that evaluates to a simple type, that is, integer
19569 * or floating-point numbers and strings.
19570 */
19571static sxi32 jx9CompileSwitch(jx9_gen_state *pGen)
19572{
19573 GenBlock *pSwitchBlock;
19574 SyToken *pTmp, *pEnd;
19575 jx9_switch *pSwitch;
19576 sxu32 nLine;
19577 sxi32 rc;
19578 nLine = pGen->pIn->nLine;
19579 /* Jump the 'switch' keyword */
19580 pGen->pIn++;
19581 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
19582 /* Syntax error */
19583 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'switch' keyword");
19584 if( rc == SXERR_ABORT ){
19585 /* Error count limit reached, abort immediately */
19586 return SXERR_ABORT;
19587 }
19588 goto Synchronize;
19589 }
19590 /* Jump the left parenthesis '(' */
19591 pGen->pIn++;
19592 pEnd = 0; /* cc warning */
19593 /* Create the loop block */
19594 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP|GEN_BLOCK_SWITCH,
19595 jx9VmInstrLength(pGen->pVm), 0, &pSwitchBlock);
19596 if( rc != SXRET_OK ){
19597 return SXERR_ABORT;
19598 }
19599 /* Delimit the condition */
19600 jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
19601 if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
19602 /* Empty expression */
19603 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'switch' keyword");
19604 if( rc == SXERR_ABORT ){
19605 /* Error count limit reached, abort immediately */
19606 return SXERR_ABORT;
19607 }
19608 }
19609 /* Swap token streams */
19610 pTmp = pGen->pEnd;
19611 pGen->pEnd = pEnd;
19612 /* Compile the expression */
19613 rc = jx9CompileExpr(&(*pGen), 0, 0);
19614 if( rc == SXERR_ABORT ){
19615 /* Expression handler request an operation abort [i.e: Out-of-memory] */
19616 return SXERR_ABORT;
19617 }
19618 /* Update token stream */
19619 while(pGen->pIn < pEnd ){
19620 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine,
19621 "Switch: Unexpected token '%z'", &pGen->pIn->sData);
19622 if( rc == SXERR_ABORT ){
19623 return SXERR_ABORT;
19624 }
19625 pGen->pIn++;
19626 }
19627 pGen->pIn = &pEnd[1];
19628 pGen->pEnd = pTmp;
19629 if( pGen->pIn >= pGen->pEnd || &pGen->pIn[1] >= pGen->pEnd ||
19630 (pGen->pIn->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_COLON/*:*/)) == 0 ){
19631 pTmp = pGen->pIn;
19632 if( pTmp >= pGen->pEnd ){
19633 pTmp--;
19634 }
19635 /* Unexpected token */
19636 rc = jx9GenCompileError(&(*pGen), E_ERROR, pTmp->nLine, "Switch: Unexpected token '%z'", &pTmp->sData);
19637 if( rc == SXERR_ABORT ){
19638 return SXERR_ABORT;
19639 }
19640 goto Synchronize;
19641 }
19642 pGen->pIn++; /* Jump the leading curly braces/colons */
19643 /* Create the switch blocks container */
19644 pSwitch = (jx9_switch *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_switch));
19645 if( pSwitch == 0 ){
19646 /* Abort compilation */
19647 return GenStateOutOfMem(pGen);
19648 }
19649 /* Zero the structure */
19650 SyZero(pSwitch, sizeof(jx9_switch));
19651 /* Initialize fields */
19652 SySetInit(&pSwitch->aCaseExpr, &pGen->pVm->sAllocator, sizeof(jx9_case_expr));
19653 /* Emit the switch instruction */
19654 jx9VmEmitInstr(pGen->pVm, JX9_OP_SWITCH, 0, 0, pSwitch, 0);
19655 /* Compile case blocks */
19656 for(;;){
19657 sxu32 nKwrd;
19658 if( pGen->pIn >= pGen->pEnd ){
19659 /* No more input to process */
19660 break;
19661 }
19662 if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
19663 if( (pGen->pIn->nType & JX9_TK_CCB /*}*/) == 0 ){
19664 /* Unexpected token */
19665 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
19666 &pGen->pIn->sData);
19667 if( rc == SXERR_ABORT ){
19668 return SXERR_ABORT;
19669 }
19670 /* FALL THROUGH */
19671 }
19672 /* Block compiled */
19673 break;
19674 }
19675 /* Extract the keyword */
19676 nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
19677 if( nKwrd == JX9_TKWRD_DEFAULT ){
19678 /*
19679 * Accroding to the JX9 language reference manual
19680 * A special case is the default case. This case matches anything
19681 * that wasn't matched by the other cases.
19682 */
19683 if( pSwitch->nDefault > 0 ){
19684 /* Default case already compiled */
19685 rc = jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Switch: 'default' case already compiled");
19686 if( rc == SXERR_ABORT ){
19687 return SXERR_ABORT;
19688 }
19689 }
19690 pGen->pIn++; /* Jump the 'default' keyword */
19691 /* Compile the default block */
19692 rc = GenStateCompileSwitchBlock(pGen,&pSwitch->nDefault);
19693 if( rc == SXERR_ABORT){
19694 return SXERR_ABORT;
19695 }else if( rc == SXERR_EOF ){
19696 break;
19697 }
19698 }else if( nKwrd == JX9_TKWRD_CASE ){
19699 jx9_case_expr sCase;
19700 /* Standard case block */
19701 pGen->pIn++; /* Jump the 'case' keyword */
19702 /* initialize the structure */
19703 SySetInit(&sCase.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
19704 /* Compile the case expression */
19705 rc = GenStateCompileCaseExpr(pGen, &sCase);
19706 if( rc == SXERR_ABORT ){
19707 return SXERR_ABORT;
19708 }
19709 /* Compile the case block */
19710 rc = GenStateCompileSwitchBlock(pGen,&sCase.nStart);
19711 /* Insert in the switch container */
19712 SySetPut(&pSwitch->aCaseExpr, (const void *)&sCase);
19713 if( rc == SXERR_ABORT){
19714 return SXERR_ABORT;
19715 }else if( rc == SXERR_EOF ){
19716 break;
19717 }
19718 }else{
19719 /* Unexpected token */
19720 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
19721 &pGen->pIn->sData);
19722 if( rc == SXERR_ABORT ){
19723 return SXERR_ABORT;
19724 }
19725 break;
19726 }
19727 }
19728 /* Fix all jumps now the destination is resolved */
19729 pSwitch->nOut = jx9VmInstrLength(pGen->pVm);
19730 GenStateFixJumps(pSwitchBlock, -1, jx9VmInstrLength(pGen->pVm));
19731 /* Release the loop block */
19732 GenStateLeaveBlock(pGen, 0);
19733 if( pGen->pIn < pGen->pEnd ){
19734 /* Jump the trailing curly braces */
19735 pGen->pIn++;
19736 }
19737 /* Statement successfully compiled */
19738 return SXRET_OK;
19739Synchronize:
19740 /* Synchronize with the first semi-colon */
19741 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
19742 pGen->pIn++;
19743 }
19744 return SXRET_OK;
19745}
19746/*
19747 * Process default argument values. That is, a function may define C++-style default value
19748 * as follows:
19749 * function makecoffee($type = "cappuccino")
19750 * {
19751 * return "Making a cup of $type.\n";
19752 * }
19753 * Some features:
19754 * 1 -) Default arguments value can be any complex expression [i.e: function call, annynoymous
19755 * functions, array member, ..]
19756 * 2 -) Full type hinting: (Arguments are automatically casted to the desired type)
19757 * Example:
19758 * function a(string $a){} function b(int $a, string $c, float $d){}
19759 * 3 -) Function overloading!!
19760 * Example:
19761 * function foo($a) {
19762 * return $a.JX9_EOL;
19763 * }
19764 * function foo($a, $b) {
19765 * return $a + $b;
19766 * }
19767 * print foo(5); // Prints "5"
19768 * print foo(5, 2); // Prints "7"
19769 * // Same arg
19770 * function foo(string $a)
19771 * {
19772 * print "a is a string\n";
19773 * dump($a);
19774 * }
19775 * function foo(int $a)
19776 * {
19777 * print "a is integer\n";
19778 * dump($a);
19779 * }
19780 * function foo(array $a)
19781 * {
19782 * print "a is an array\n";
19783 * dump($a);
19784 * }
19785 * foo('This is a great feature'); // a is a string [first foo]
19786 * foo(52); // a is integer [second foo]
19787 * foo(array(14, __TIME__, __DATE__)); // a is an array [third foo]
19788 * Please refer to the official documentation for more information on the powerful extension
19789 * introduced by the JX9 engine.
19790 */
19791static sxi32 GenStateProcessArgValue(jx9_gen_state *pGen, jx9_vm_func_arg *pArg, SyToken *pIn, SyToken *pEnd)
19792{
19793 SyToken *pTmpIn, *pTmpEnd;
19794 SySet *pInstrContainer;
19795 sxi32 rc;
19796 /* Swap token stream */
19797 SWAP_DELIMITER(pGen, pIn, pEnd);
19798 pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
19799 jx9VmSetByteCodeContainer(pGen->pVm, &pArg->aByteCode);
19800 /* Compile the expression holding the argument value */
19801 rc = jx9CompileExpr(&(*pGen), 0, 0);
19802 /* Emit the done instruction */
19803 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
19804 jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
19805 RE_SWAP_DELIMITER(pGen);
19806 if( rc == SXERR_ABORT ){
19807 return SXERR_ABORT;
19808 }
19809 return SXRET_OK;
19810}
19811/*
19812 * Collect function arguments one after one.
19813 * According to the JX9 language reference manual.
19814 * Information may be passed to functions via the argument list, which is a comma-delimited
19815 * list of expressions.
19816 * JX9 supports passing arguments by value (the default), passing by reference
19817 * and default argument values. Variable-length argument lists are also supported,
19818 * see also the function references for func_num_args(), func_get_arg(), and func_get_args()
19819 * for more information.
19820 * Example #1 Passing arrays to functions
19821 * <?jx9
19822 * function takes_array($input)
19823 * {
19824 * print "$input[0] + $input[1] = ", $input[0]+$input[1];
19825 * }
19826 * ?>
19827 * Making arguments be passed by reference
19828 * By default, function arguments are passed by value (so that if the value of the argument
19829 * within the function is changed, it does not get changed outside of the function).
19830 * To allow a function to modify its arguments, they must be passed by reference.
19831 * To have an argument to a function always passed by reference, prepend an ampersand (&)
19832 * to the argument name in the function definition:
19833 * Example #2 Passing function parameters by reference
19834 * <?jx9
19835 * function add_some_extra(&$string)
19836 * {
19837 * $string .= 'and something extra.';
19838 * }
19839 * $str = 'This is a string, ';
19840 * add_some_extra($str);
19841 * print $str; // outputs 'This is a string, and something extra.'
19842 * ?>
19843 *
19844 * JX9 have introduced powerful extension including full type hinting, function overloading
19845 * complex agrument values.Please refer to the official documentation for more information
19846 * on these extension.
19847 */
19848static sxi32 GenStateCollectFuncArgs(jx9_vm_func *pFunc, jx9_gen_state *pGen, SyToken *pEnd)
19849{
19850 jx9_vm_func_arg sArg; /* Current processed argument */
19851 SyToken *pCur, *pIn; /* Token stream */
19852 SyBlob sSig; /* Function signature */
19853 char *zDup; /* Copy of argument name */
19854 sxi32 rc;
19855
19856 pIn = pGen->pIn;
19857 pCur = 0;
19858 SyBlobInit(&sSig, &pGen->pVm->sAllocator);
19859 /* Process arguments one after one */
19860 for(;;){
19861 if( pIn >= pEnd ){
19862 /* No more arguments to process */
19863 break;
19864 }
19865 SyZero(&sArg, sizeof(jx9_vm_func_arg));
19866 SySetInit(&sArg.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
19867 if( pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
19868 if( pIn->nType & JX9_TK_KEYWORD ){
19869 sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pIn->pUserData));
19870 if( nKey & JX9_TKWRD_BOOL ){
19871 sArg.nType = MEMOBJ_BOOL;
19872 }else if( nKey & JX9_TKWRD_INT ){
19873 sArg.nType = MEMOBJ_INT;
19874 }else if( nKey & JX9_TKWRD_STRING ){
19875 sArg.nType = MEMOBJ_STRING;
19876 }else if( nKey & JX9_TKWRD_FLOAT ){
19877 sArg.nType = MEMOBJ_REAL;
19878 }else{
19879 jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine,
19880 "Invalid argument type '%z', Automatic cast will not be performed",
19881 &pIn->sData);
19882 }
19883 }
19884 pIn++;
19885 }
19886 if( pIn >= pEnd ){
19887 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Missing argument name");
19888 return rc;
19889 }
19890 if( pIn >= pEnd || (pIn->nType & JX9_TK_DOLLAR) == 0 || &pIn[1] >= pEnd || (pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
19891 /* Invalid argument */
19892 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid argument name");
19893 return rc;
19894 }
19895 pIn++; /* Jump the dollar sign */
19896 /* Copy argument name */
19897 zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, SyStringData(&pIn->sData), SyStringLength(&pIn->sData));
19898 if( zDup == 0 ){
19899 return GenStateOutOfMem(pGen);
19900 }
19901 SyStringInitFromBuf(&sArg.sName, zDup, SyStringLength(&pIn->sData));
19902 pIn++;
19903 if( pIn < pEnd ){
19904 if( pIn->nType & JX9_TK_EQUAL ){
19905 SyToken *pDefend;
19906 sxi32 iNest = 0;
19907 pIn++; /* Jump the equal sign */
19908 pDefend = pIn;
19909 /* Process the default value associated with this argument */
19910 while( pDefend < pEnd ){
19911 if( (pDefend->nType & JX9_TK_COMMA) && iNest <= 0 ){
19912 break;
19913 }
19914 if( pDefend->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*[*/) ){
19915 /* Increment nesting level */
19916 iNest++;
19917 }else if( pDefend->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*]*/) ){
19918 /* Decrement nesting level */
19919 iNest--;
19920 }
19921 pDefend++;
19922 }
19923 if( pIn >= pDefend ){
19924 rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Missing argument default value");
19925 return rc;
19926 }
19927 /* Process default value */
19928 rc = GenStateProcessArgValue(&(*pGen), &sArg, pIn, pDefend);
19929 if( rc != SXRET_OK ){
19930 return rc;
19931 }
19932 /* Point beyond the default value */
19933 pIn = pDefend;
19934 }
19935 if( pIn < pEnd && (pIn->nType & JX9_TK_COMMA) == 0 ){
19936 rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Unexpected token '%z'", &pIn->sData);
19937 return rc;
19938 }
19939 pIn++; /* Jump the trailing comma */
19940 }
19941 /* Append argument signature */
19942 if( sArg.nType > 0 ){
19943 int c;
19944 c = 'n'; /* cc warning */
19945 /* Type leading character */
19946 switch(sArg.nType){
19947 case MEMOBJ_HASHMAP:
19948 /* Hashmap aka 'array' */
19949 c = 'h';
19950 break;
19951 case MEMOBJ_INT:
19952 /* Integer */
19953 c = 'i';
19954 break;
19955 case MEMOBJ_BOOL:
19956 /* Bool */
19957 c = 'b';
19958 break;
19959 case MEMOBJ_REAL:
19960 /* Float */
19961 c = 'f';
19962 break;
19963 case MEMOBJ_STRING:
19964 /* String */
19965 c = 's';
19966 break;
19967 default:
19968 break;
19969 }
19970 SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
19971 }
19972 /* Save in the argument set */
19973 SySetPut(&pFunc->aArgs, (const void *)&sArg);
19974 }
19975 if( SyBlobLength(&sSig) > 0 ){
19976 /* Save function signature */
19977 SyStringInitFromBuf(&pFunc->sSignature, SyBlobData(&sSig), SyBlobLength(&sSig));
19978 }
19979 return SXRET_OK;
19980}
19981/*
19982 * Compile function [i.e: standard function, annonymous function or closure ] body.
19983 * Return SXRET_OK on success. Any other return value indicates failure
19984 * and this routine takes care of generating the appropriate error message.
19985 */
19986static sxi32 GenStateCompileFuncBody(
19987 jx9_gen_state *pGen, /* Code generator state */
19988 jx9_vm_func *pFunc /* Function state */
19989 )
19990{
19991 SySet *pInstrContainer; /* Instruction container */
19992 GenBlock *pBlock;
19993 sxi32 rc;
19994 /* Attach the new function */
19995 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC,jx9VmInstrLength(pGen->pVm), pFunc, &pBlock);
19996 if( rc != SXRET_OK ){
19997 return GenStateOutOfMem(pGen);
19998 }
19999 /* Swap bytecode containers */
20000 pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
20001 jx9VmSetByteCodeContainer(pGen->pVm, &pFunc->aByteCode);
20002 /* Compile the body */
20003 jx9CompileBlock(&(*pGen));
20004 /* Emit the final return if not yet done */
20005 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, 0, 0, 0, 0);
20006 /* Restore the default container */
20007 jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
20008 /* Leave function block */
20009 GenStateLeaveBlock(&(*pGen), 0);
20010 if( rc == SXERR_ABORT ){
20011 /* Don't worry about freeing memory, everything will be released shortly */
20012 return SXERR_ABORT;
20013 }
20014 /* All done, function body compiled */
20015 return SXRET_OK;
20016}
20017/*
20018 * Compile a JX9 function whether is a Standard or Annonymous function.
20019 * According to the JX9 language reference manual.
20020 * Function names follow the same rules as other labels in JX9. A valid function name
20021 * starts with a letter or underscore, followed by any number of letters, numbers, or
20022 * underscores. As a regular expression, it would be expressed thus:
20023 * [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*.
20024 * Functions need not be defined before they are referenced.
20025 * All functions and objectes in JX9 have the global scope - they can be called outside
20026 * a function even if they were defined inside and vice versa.
20027 * It is possible to call recursive functions in JX9. However avoid recursive function/method
20028 * calls with over 32-64 recursion levels.
20029 *
20030 * JX9 have introduced powerful extension including full type hinting, function overloading,
20031 * complex agrument values and more. Please refer to the official documentation for more information
20032 * on these extension.
20033 */
20034static sxi32 GenStateCompileFunc(
20035 jx9_gen_state *pGen, /* Code generator state */
20036 SyString *pName, /* Function name. NULL otherwise */
20037 sxi32 iFlags, /* Control flags */
20038 jx9_vm_func **ppFunc /* OUT: function state */
20039 )
20040{
20041 jx9_vm_func *pFunc;
20042 SyToken *pEnd;
20043 sxu32 nLine;
20044 char *zName;
20045 sxi32 rc;
20046 /* Extract line number */
20047 nLine = pGen->pIn->nLine;
20048 /* Jump the left parenthesis '(' */
20049 pGen->pIn++;
20050 /* Delimit the function signature */
20051 jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
20052 if( pEnd >= pGen->pEnd ){
20053 /* Syntax error */
20054 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Missing ')' after function '%z' signature", pName);
20055 if( rc == SXERR_ABORT ){
20056 /* Error count limit reached, abort immediately */
20057 return SXERR_ABORT;
20058 }
20059 pGen->pIn = pGen->pEnd;
20060 return SXRET_OK;
20061 }
20062 /* Create the function state */
20063 pFunc = (jx9_vm_func *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_vm_func));
20064 if( pFunc == 0 ){
20065 goto OutOfMem;
20066 }
20067 /* function ID */
20068 zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
20069 if( zName == 0 ){
20070 /* Don't worry about freeing memory, everything will be released shortly */
20071 goto OutOfMem;
20072 }
20073 /* Initialize the function state */
20074 jx9VmInitFuncState(pGen->pVm, pFunc, zName, pName->nByte, iFlags, 0);
20075 if( pGen->pIn < pEnd ){
20076 /* Collect function arguments */
20077 rc = GenStateCollectFuncArgs(pFunc, &(*pGen), pEnd);
20078 if( rc == SXERR_ABORT ){
20079 /* Don't worry about freeing memory, everything will be released shortly */
20080 return SXERR_ABORT;
20081 }
20082 }
20083 /* Compile function body */
20084 pGen->pIn = &pEnd[1];
20085 /* Compile the body */
20086 rc = GenStateCompileFuncBody(&(*pGen), pFunc);
20087 if( rc == SXERR_ABORT ){
20088 return SXERR_ABORT;
20089 }
20090 if( ppFunc ){
20091 *ppFunc = pFunc;
20092 }
20093 /* Finally register the function */
20094 rc = jx9VmInstallUserFunction(pGen->pVm, pFunc, 0);
20095 return rc;
20096 /* Fall through if something goes wrong */
20097OutOfMem:
20098 /* If the supplied memory subsystem is so sick that we are unable to allocate
20099 * a tiny chunk of memory, there is no much we can do here.
20100 */
20101 return GenStateOutOfMem(pGen);
20102}
20103/*
20104 * Compile a standard JX9 function.
20105 * Refer to the block-comment above for more information.
20106 */
20107static sxi32 jx9CompileFunction(jx9_gen_state *pGen)
20108{
20109 SyString *pName;
20110 sxi32 iFlags;
20111 sxu32 nLine;
20112 sxi32 rc;
20113
20114 nLine = pGen->pIn->nLine;
20115 pGen->pIn++; /* Jump the 'function' keyword */
20116 iFlags = 0;
20117 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
20118 /* Invalid function name */
20119 rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Invalid function name");
20120 if( rc == SXERR_ABORT ){
20121 return SXERR_ABORT;
20122 }
20123 /* Sychronize with the next semi-colon or braces*/
20124 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
20125 pGen->pIn++;
20126 }
20127 return SXRET_OK;
20128 }
20129 pName = &pGen->pIn->sData;
20130 nLine = pGen->pIn->nLine;
20131 /* Jump the function name */
20132 pGen->pIn++;
20133 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
20134 /* Syntax error */
20135 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after function name '%z'", pName);
20136 if( rc == SXERR_ABORT ){
20137 /* Error count limit reached, abort immediately */
20138 return SXERR_ABORT;
20139 }
20140 /* Sychronize with the next semi-colon or '{' */
20141 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
20142 pGen->pIn++;
20143 }
20144 return SXRET_OK;
20145 }
20146 /* Compile function body */
20147 rc = GenStateCompileFunc(&(*pGen),pName,iFlags,0);
20148 return rc;
20149}
20150/*
20151 * Generate bytecode for a given expression tree.
20152 * If something goes wrong while generating bytecode
20153 * for the expression tree (A very unlikely scenario)
20154 * this function takes care of generating the appropriate
20155 * error message.
20156 */
20157static sxi32 GenStateEmitExprCode(
20158 jx9_gen_state *pGen, /* Code generator state */
20159 jx9_expr_node *pNode, /* Root of the expression tree */
20160 sxi32 iFlags /* Control flags */
20161 )
20162{
20163 VmInstr *pInstr;
20164 sxu32 nJmpIdx;
20165 sxi32 iP1 = 0;
20166 sxu32 iP2 = 0;
20167 void *p3 = 0;
20168 sxi32 iVmOp;
20169 sxi32 rc;
20170 if( pNode->xCode ){
20171 SyToken *pTmpIn, *pTmpEnd;
20172 /* Compile node */
20173 SWAP_DELIMITER(pGen, pNode->pStart, pNode->pEnd);
20174 rc = pNode->xCode(&(*pGen), iFlags);
20175 RE_SWAP_DELIMITER(pGen);
20176 return rc;
20177 }
20178 if( pNode->pOp == 0 ){
20179 jx9GenCompileError(&(*pGen), E_ERROR, pNode->pStart->nLine,
20180 "Invalid expression node, JX9 is aborting compilation");
20181 return SXERR_ABORT;
20182 }
20183 iVmOp = pNode->pOp->iVmOp;
20184 if( pNode->pOp->iOp == EXPR_OP_QUESTY ){
20185 sxu32 nJz, nJmp;
20186 /* Ternary operator require special handling */
20187 /* Phase#1: Compile the condition */
20188 rc = GenStateEmitExprCode(&(*pGen), pNode->pCond, iFlags);
20189 if( rc != SXRET_OK ){
20190 return rc;
20191 }
20192 nJz = nJmp = 0; /* cc -O6 warning */
20193 /* Phase#2: Emit the false jump */
20194 jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJz);
20195 if( pNode->pLeft ){
20196 /* Phase#3: Compile the 'then' expression */
20197 rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
20198 if( rc != SXRET_OK ){
20199 return rc;
20200 }
20201 }
20202 /* Phase#4: Emit the unconditional jump */
20203 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJmp);
20204 /* Phase#5: Fix the false jump now the jump destination is resolved. */
20205 pInstr = jx9VmGetInstr(pGen->pVm, nJz);
20206 if( pInstr ){
20207 pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
20208 }
20209 /* Phase#6: Compile the 'else' expression */
20210 if( pNode->pRight ){
20211 rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
20212 if( rc != SXRET_OK ){
20213 return rc;
20214 }
20215 }
20216 if( nJmp > 0 ){
20217 /* Phase#7: Fix the unconditional jump */
20218 pInstr = jx9VmGetInstr(pGen->pVm, nJmp);
20219 if( pInstr ){
20220 pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
20221 }
20222 }
20223 /* All done */
20224 return SXRET_OK;
20225 }
20226 /* Generate code for the left tree */
20227 if( pNode->pLeft ){
20228 if( iVmOp == JX9_OP_CALL ){
20229 jx9_expr_node **apNode;
20230 sxi32 n;
20231 /* Recurse and generate bytecodes for function arguments */
20232 apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
20233 /* Read-only load */
20234 iFlags |= EXPR_FLAG_RDONLY_LOAD;
20235 for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
20236 rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
20237 if( rc != SXRET_OK ){
20238 return rc;
20239 }
20240 }
20241 /* Total number of given arguments */
20242 iP1 = (sxi32)SySetUsed(&pNode->aNodeArgs);
20243 /* Remove stale flags now */
20244 iFlags &= ~EXPR_FLAG_RDONLY_LOAD;
20245 }
20246 rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
20247 if( rc != SXRET_OK ){
20248 return rc;
20249 }
20250 if( iVmOp == JX9_OP_CALL ){
20251 pInstr = jx9VmPeekInstr(pGen->pVm);
20252 if( pInstr ){
20253 if ( pInstr->iOp == JX9_OP_LOADC ){
20254 /* Prevent constant expansion */
20255 pInstr->iP1 = 0;
20256 }else if( pInstr->iOp == JX9_OP_MEMBER /* $a.b(1, 2, 3) */ ){
20257 /* Annonymous function call, flag that */
20258 pInstr->iP2 = 1;
20259 }
20260 }
20261 }else if( iVmOp == JX9_OP_LOAD_IDX ){
20262 jx9_expr_node **apNode;
20263 sxi32 n;
20264 /* Recurse and generate bytecodes for array index */
20265 apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
20266 for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
20267 rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
20268 if( rc != SXRET_OK ){
20269 return rc;
20270 }
20271 }
20272 if( SySetUsed(&pNode->aNodeArgs) > 0 ){
20273 iP1 = 1; /* Node have an index associated with it */
20274 }
20275 if( iFlags & EXPR_FLAG_LOAD_IDX_STORE ){
20276 /* Create an empty entry when the desired index is not found */
20277 iP2 = 1;
20278 }
20279 }else if( pNode->pOp->iOp == EXPR_OP_COMMA ){
20280 /* POP the left node */
20281 jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
20282 }
20283 }
20284 rc = SXRET_OK;
20285 nJmpIdx = 0;
20286 /* Generate code for the right tree */
20287 if( pNode->pRight ){
20288 if( iVmOp == JX9_OP_LAND ){
20289 /* Emit the false jump so we can short-circuit the logical and */
20290 jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
20291 }else if (iVmOp == JX9_OP_LOR ){
20292 /* Emit the true jump so we can short-circuit the logical or*/
20293 jx9VmEmitInstr(pGen->pVm, JX9_OP_JNZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
20294 }else if( pNode->pOp->iPrec == 18 /* Combined binary operators [i.e: =, '.=', '+=', *=' ...] precedence */ ){
20295 iFlags |= EXPR_FLAG_LOAD_IDX_STORE;
20296 }
20297 rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
20298 if( iVmOp == JX9_OP_STORE ){
20299 pInstr = jx9VmPeekInstr(pGen->pVm);
20300 if( pInstr ){
20301 if(pInstr->iOp == JX9_OP_MEMBER ){
20302 /* Perform a member store operation [i.e: $this.x = 50] */
20303 iP2 = 1;
20304 }else{
20305 if( pInstr->iOp == JX9_OP_LOAD_IDX ){
20306 /* Transform the STORE instruction to STORE_IDX instruction */
20307 iVmOp = JX9_OP_STORE_IDX;
20308 iP1 = pInstr->iP1;
20309 }else{
20310 p3 = pInstr->p3;
20311 }
20312 /* POP the last dynamic load instruction */
20313 (void)jx9VmPopInstr(pGen->pVm);
20314 }
20315 }
20316 }
20317 }
20318 if( iVmOp > 0 ){
20319 if( iVmOp == JX9_OP_INCR || iVmOp == JX9_OP_DECR ){
20320 if( pNode->iFlags & EXPR_NODE_PRE_INCR ){
20321 /* Pre-increment/decrement operator [i.e: ++$i, --$j ] */
20322 iP1 = 1;
20323 }
20324 }
20325 /* Finally, emit the VM instruction associated with this operator */
20326 jx9VmEmitInstr(pGen->pVm, iVmOp, iP1, iP2, p3, 0);
20327 if( nJmpIdx > 0 ){
20328 /* Fix short-circuited jumps now the destination is resolved */
20329 pInstr = jx9VmGetInstr(pGen->pVm, nJmpIdx);
20330 if( pInstr ){
20331 pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
20332 }
20333 }
20334 }
20335 return rc;
20336}
20337/*
20338 * Compile a JX9 expression.
20339 * According to the JX9 language reference manual:
20340 * Expressions are the most important building stones of JX9.
20341 * In JX9, almost anything you write is an expression.
20342 * The simplest yet most accurate way to define an expression
20343 * is "anything that has a value".
20344 * If something goes wrong while compiling the expression, this
20345 * function takes care of generating the appropriate error
20346 * message.
20347 */
20348static sxi32 jx9CompileExpr(
20349 jx9_gen_state *pGen, /* Code generator state */
20350 sxi32 iFlags, /* Control flags */
20351 sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
20352 )
20353{
20354 jx9_expr_node *pRoot;
20355 SySet sExprNode;
20356 SyToken *pEnd;
20357 sxi32 nExpr;
20358 sxi32 iNest;
20359 sxi32 rc;
20360 /* Initialize worker variables */
20361 nExpr = 0;
20362 pRoot = 0;
20363 SySetInit(&sExprNode, &pGen->pVm->sAllocator, sizeof(jx9_expr_node *));
20364 SySetAlloc(&sExprNode, 0x10);
20365 rc = SXRET_OK;
20366 /* Delimit the expression */
20367 pEnd = pGen->pIn;
20368 iNest = 0;
20369 while( pEnd < pGen->pEnd ){
20370 if( pEnd->nType & JX9_TK_OCB /* '{' */ ){
20371 /* Ticket 1433-30: Annonymous/Closure functions body */
20372 iNest++;
20373 }else if(pEnd->nType & JX9_TK_CCB /* '}' */ ){
20374 iNest--;
20375 }else if( pEnd->nType & JX9_TK_SEMI /* ';' */ ){
20376 if( iNest <= 0 ){
20377 break;
20378 }
20379 }
20380 pEnd++;
20381 }
20382 if( iFlags & EXPR_FLAG_COMMA_STATEMENT ){
20383 SyToken *pEnd2 = pGen->pIn;
20384 iNest = 0;
20385 /* Stop at the first comma */
20386 while( pEnd2 < pEnd ){
20387 if( pEnd2->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_LPAREN/*'('*/) ){
20388 iNest++;
20389 }else if(pEnd2->nType & (JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*']'*/|JX9_TK_RPAREN/*')'*/)){
20390 iNest--;
20391 }else if( pEnd2->nType & JX9_TK_COMMA /*','*/ ){
20392 if( iNest <= 0 ){
20393 break;
20394 }
20395 }
20396 pEnd2++;
20397 }
20398 if( pEnd2 <pEnd ){
20399 pEnd = pEnd2;
20400 }
20401 }
20402 if( pEnd > pGen->pIn ){
20403 SyToken *pTmp = pGen->pEnd;
20404 /* Swap delimiter */
20405 pGen->pEnd = pEnd;
20406 /* Try to get an expression tree */
20407 rc = jx9ExprMakeTree(&(*pGen), &sExprNode, &pRoot);
20408 if( rc == SXRET_OK && pRoot ){
20409 rc = SXRET_OK;
20410 if( xTreeValidator ){
20411 /* Call the upper layer validator callback */
20412 rc = xTreeValidator(&(*pGen), pRoot);
20413 }
20414 if( rc != SXERR_ABORT ){
20415 /* Generate code for the given tree */
20416 rc = GenStateEmitExprCode(&(*pGen), pRoot, iFlags);
20417 }
20418 nExpr = 1;
20419 }
20420 /* Release the whole tree */
20421 jx9ExprFreeTree(&(*pGen), &sExprNode);
20422 /* Synchronize token stream */
20423 pGen->pEnd = pTmp;
20424 pGen->pIn = pEnd;
20425 if( rc == SXERR_ABORT ){
20426 SySetRelease(&sExprNode);
20427 return SXERR_ABORT;
20428 }
20429 }
20430 SySetRelease(&sExprNode);
20431 return nExpr > 0 ? SXRET_OK : SXERR_EMPTY;
20432}
20433/*
20434 * Return a pointer to the node construct handler associated
20435 * with a given node type [i.e: string, integer, float, ...].
20436 */
20437JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType)
20438{
20439 if( nNodeType & JX9_TK_NUM ){
20440 /* Numeric literal: Either real or integer */
20441 return jx9CompileNumLiteral;
20442 }else if( nNodeType & JX9_TK_DSTR ){
20443 /* Double quoted string */
20444 return jx9CompileString;
20445 }else if( nNodeType & JX9_TK_SSTR ){
20446 /* Single quoted string */
20447 return jx9CompileSimpleString;
20448 }else if( nNodeType & JX9_TK_NOWDOC ){
20449 /* Nowdoc */
20450 return jx9CompileNowdoc;
20451 }
20452 return 0;
20453}
20454/*
20455 * Jx9 Language construct table.
20456 */
20457static const LangConstruct aLangConstruct[] = {
20458 { JX9_TKWRD_IF, jx9CompileIf },
20459 { JX9_TKWRD_FUNCTION, jx9CompileFunction },
20460 { JX9_TKWRD_FOREACH, jx9CompileForeach },
20461 { JX9_TKWRD_WHILE, jx9CompileWhile },
20462 { JX9_TKWRD_FOR, jx9CompileFor },
20463 { JX9_TKWRD_SWITCH, jx9CompileSwitch },
20464 { JX9_TKWRD_DIE, jx9CompileHalt },
20465 { JX9_TKWRD_EXIT, jx9CompileHalt },
20466 { JX9_TKWRD_RETURN, jx9CompileReturn },
20467 { JX9_TKWRD_BREAK, jx9CompileBreak },
20468 { JX9_TKWRD_CONTINUE, jx9CompileContinue },
20469 { JX9_TKWRD_STATIC, jx9CompileStatic },
20470 { JX9_TKWRD_UPLINK, jx9CompileUplink },
20471 { JX9_TKWRD_CONST, jx9CompileConstant },
20472};
20473/*
20474 * Return a pointer to the statement handler routine associated
20475 * with a given JX9 keyword [i.e: if, for, while, ...].
20476 */
20477static ProcLangConstruct GenStateGetStatementHandler(
20478 sxu32 nKeywordID /* Keyword ID*/
20479 )
20480{
20481 sxu32 n = 0;
20482 for(;;){
20483 if( n >= SX_ARRAYSIZE(aLangConstruct) ){
20484 break;
20485 }
20486 if( aLangConstruct[n].nID == nKeywordID ){
20487 /* Return a pointer to the handler.
20488 */
20489 return aLangConstruct[n].xConstruct;
20490 }
20491 n++;
20492 }
20493 /* Not a language construct */
20494 return 0;
20495}
20496/*
20497 * Compile a jx9 program.
20498 * If something goes wrong while compiling the Jx9 chunk, this function
20499 * takes care of generating the appropriate error message.
20500 */
20501static sxi32 GenStateCompileChunk(
20502 jx9_gen_state *pGen, /* Code generator state */
20503 sxi32 iFlags /* Compile flags */
20504 )
20505{
20506 ProcLangConstruct xCons;
20507 sxi32 rc;
20508 rc = SXRET_OK; /* Prevent compiler warning */
20509 for(;;){
20510 if( pGen->pIn >= pGen->pEnd ){
20511 /* No more input to process */
20512 break;
20513 }
20514 xCons = 0;
20515 if( pGen->pIn->nType & JX9_TK_KEYWORD ){
20516 sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
20517 /* Try to extract a language construct handler */
20518 xCons = GenStateGetStatementHandler(nKeyword);
20519 if( xCons == 0 && !jx9IsLangConstruct(nKeyword) ){
20520 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
20521 "Syntax error: Unexpected keyword '%z'",
20522 &pGen->pIn->sData);
20523 if( rc == SXERR_ABORT ){
20524 break;
20525 }
20526 /* Synchronize with the first semi-colon and avoid compiling
20527 * this erroneous statement.
20528 */
20529 xCons = jx9ErrorRecover;
20530 }
20531 }
20532 if( xCons == 0 ){
20533 /* Assume an expression an try to compile it */
20534 rc = jx9CompileExpr(&(*pGen), 0, 0);
20535 if( rc != SXERR_EMPTY ){
20536 /* Pop l-value */
20537 jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
20538 }
20539 }else{
20540 /* Go compile the sucker */
20541 rc = xCons(&(*pGen));
20542 }
20543 if( rc == SXERR_ABORT ){
20544 /* Request to abort compilation */
20545 break;
20546 }
20547 /* Ignore trailing semi-colons ';' */
20548 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
20549 pGen->pIn++;
20550 }
20551 if( iFlags & JX9_COMPILE_SINGLE_STMT ){
20552 /* Compile a single statement and return */
20553 break;
20554 }
20555 /* LOOP ONE */
20556 /* LOOP TWO */
20557 /* LOOP THREE */
20558 /* LOOP FOUR */
20559 }
20560 /* Return compilation status */
20561 return rc;
20562}
20563/*
20564 * Compile a raw chunk. The raw chunk can contain JX9 code embedded
20565 * in HTML, XML and so on. This function handle all the stuff.
20566 * This is the only compile interface exported from this file.
20567 */
20568JX9_PRIVATE sxi32 jx9CompileScript(
20569 jx9_vm *pVm, /* Generate JX9 bytecodes for this Virtual Machine */
20570 SyString *pScript, /* Script to compile */
20571 sxi32 iFlags /* Compile flags */
20572 )
20573{
20574 jx9_gen_state *pGen;
20575 SySet aToken;
20576 sxi32 rc;
20577 if( pScript->nByte < 1 ){
20578 /* Nothing to compile */
20579 return JX9_OK;
20580 }
20581 /* Initialize the tokens containers */
20582 SySetInit(&aToken, &pVm->sAllocator, sizeof(SyToken));
20583 SySetAlloc(&aToken, 0xc0);
20584 pGen = &pVm->sCodeGen;
20585 rc = JX9_OK;
20586 /* Tokenize the JX9 chunk first */
20587 jx9Tokenize(pScript->zString,pScript->nByte,&aToken);
20588 if( SySetUsed(&aToken) < 1 ){
20589 return SXERR_EMPTY;
20590 }
20591 /* Point to the head and tail of the token stream. */
20592 pGen->pIn = (SyToken *)SySetBasePtr(&aToken);
20593 pGen->pEnd = &pGen->pIn[SySetUsed(&aToken)];
20594 /* Compile the chunk */
20595 rc = GenStateCompileChunk(pGen,iFlags);
20596 /* Cleanup */
20597 SySetRelease(&aToken);
20598 return rc;
20599}
20600/*
20601 * Utility routines.Initialize the code generator.
20602 */
20603JX9_PRIVATE sxi32 jx9InitCodeGenerator(
20604 jx9_vm *pVm, /* Target VM */
20605 ProcConsumer xErr, /* Error log consumer callabck */
20606 void *pErrData /* Last argument to xErr() */
20607 )
20608{
20609 jx9_gen_state *pGen = &pVm->sCodeGen;
20610 /* Zero the structure */
20611 SyZero(pGen, sizeof(jx9_gen_state));
20612 /* Initial state */
20613 pGen->pVm = &(*pVm);
20614 pGen->xErr = xErr;
20615 pGen->pErrData = pErrData;
20616 SyHashInit(&pGen->hLiteral, &pVm->sAllocator, 0, 0);
20617 SyHashInit(&pGen->hVar, &pVm->sAllocator, 0, 0);
20618 /* Create the global scope */
20619 GenStateInitBlock(pGen, &pGen->sGlobal,GEN_BLOCK_GLOBAL,jx9VmInstrLength(&(*pVm)), 0);
20620 /* Point to the global scope */
20621 pGen->pCurrent = &pGen->sGlobal;
20622 return SXRET_OK;
20623}
20624/*
20625 * Utility routines. Reset the code generator to it's initial state.
20626 */
20627JX9_PRIVATE sxi32 jx9ResetCodeGenerator(
20628 jx9_vm *pVm, /* Target VM */
20629 ProcConsumer xErr, /* Error log consumer callabck */
20630 void *pErrData /* Last argument to xErr() */
20631 )
20632{
20633 jx9_gen_state *pGen = &pVm->sCodeGen;
20634 GenBlock *pBlock, *pParent;
20635 /* Point to the global scope */
20636 pBlock = pGen->pCurrent;
20637 while( pBlock->pParent != 0 ){
20638 pParent = pBlock->pParent;
20639 GenStateFreeBlock(pBlock);
20640 pBlock = pParent;
20641 }
20642 pGen->xErr = xErr;
20643 pGen->pErrData = pErrData;
20644 pGen->pCurrent = &pGen->sGlobal;
20645 pGen->pIn = pGen->pEnd = 0;
20646 pGen->nErr = 0;
20647 return SXRET_OK;
20648}
20649/*
20650 * Generate a compile-time error message.
20651 * If the error count limit is reached (usually 15 error message)
20652 * this function return SXERR_ABORT.In that case upper-layers must
20653 * abort compilation immediately.
20654 */
20655JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen,sxi32 nErrType,sxu32 nLine,const char *zFormat,...)
20656{
20657 SyBlob *pWorker = &pGen->pVm->pEngine->xConf.sErrConsumer;
20658 const char *zErr = "Error";
20659 va_list ap;
20660 if( nErrType == E_ERROR ){
20661 /* Increment the error counter */
20662 pGen->nErr++;
20663 if( pGen->nErr > 15 ){
20664 /* Error count limit reached */
20665 SyBlobFormat(pWorker, "%u Error count limit reached, JX9 is aborting compilation\n", nLine);
20666 /* Abort immediately */
20667 return SXERR_ABORT;
20668 }
20669 }
20670 switch(nErrType){
20671 case E_WARNING: zErr = "Warning"; break;
20672 case E_PARSE: zErr = "Parse error"; break;
20673 case E_NOTICE: zErr = "Notice"; break;
20674 default:
20675 break;
20676 }
20677 /* Format the error message */
20678 SyBlobFormat(pWorker, "%u %s: ", nLine, zErr);
20679 va_start(ap, zFormat);
20680 SyBlobFormatAp(pWorker, zFormat, ap);
20681 va_end(ap);
20682 /* Append a new line */
20683 SyBlobAppend(pWorker, (const void *)"\n", sizeof(char));
20684 return JX9_OK;
20685}
20686/*
20687 * ----------------------------------------------------------
20688 * File: jx9_const.c
20689 * MD5: f3980b00dd1eda0bb2b749424a8dfffe
20690 * ----------------------------------------------------------
20691 */
20692/*
20693 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
20694 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
20695 * Version 1.7.2
20696 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
20697 * please contact Symisc Systems via:
20698 * legal@symisc.net
20699 * licensing@symisc.net
20700 * contact@symisc.net
20701 * or visit:
20702 * http://jx9.symisc.net/
20703 */
20704 /* $SymiscID: const.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
20705#ifndef JX9_AMALGAMATION
20706#include "jx9Int.h"
20707#endif
20708/* This file implement built-in constants for the JX9 engine. */
20709/*
20710 * JX9_VERSION
20711 * __JX9__
20712 * Expand the current version of the JX9 engine.
20713 */
20714static void JX9_VER_Const(jx9_value *pVal, void *pUnused)
20715{
20716 SXUNUSED(pUnused);
20717 jx9_value_string(pVal, jx9_lib_signature(), -1/*Compute length automatically*/);
20718}
20719#ifdef __WINNT__
20720#include <Windows.h>
20721#elif defined(__UNIXES__)
20722#include <sys/utsname.h>
20723#endif
20724/*
20725 * JX9_OS
20726 * __OS__
20727 * Expand the name of the host Operating System.
20728 */
20729static void JX9_OS_Const(jx9_value *pVal, void *pUnused)
20730{
20731#if defined(__WINNT__)
20732 jx9_value_string(pVal, "WinNT", (int)sizeof("WinNT")-1);
20733#elif defined(__UNIXES__)
20734 struct utsname sInfo;
20735 if( uname(&sInfo) != 0 ){
20736 jx9_value_string(pVal, "Unix", (int)sizeof("Unix")-1);
20737 }else{
20738 jx9_value_string(pVal, sInfo.sysname, -1);
20739 }
20740#else
20741 jx9_value_string(pVal,"Host OS", (int)sizeof("Host OS")-1);
20742#endif
20743 SXUNUSED(pUnused);
20744}
20745/*
20746 * JX9_EOL
20747 * Expand the correct 'End Of Line' symbol for this platform.
20748 */
20749static void JX9_EOL_Const(jx9_value *pVal, void *pUnused)
20750{
20751 SXUNUSED(pUnused);
20752#ifdef __WINNT__
20753 jx9_value_string(pVal, "\r\n", (int)sizeof("\r\n")-1);
20754#else
20755 jx9_value_string(pVal, "\n", (int)sizeof(char));
20756#endif
20757}
20758/*
20759 * JX9_INT_MAX
20760 * Expand the largest integer supported.
20761 * Note that JX9 deals with 64-bit integer for all platforms.
20762 */
20763static void JX9_INTMAX_Const(jx9_value *pVal, void *pUnused)
20764{
20765 SXUNUSED(pUnused);
20766 jx9_value_int64(pVal, SXI64_HIGH);
20767}
20768/*
20769 * JX9_INT_SIZE
20770 * Expand the size in bytes of a 64-bit integer.
20771 */
20772static void JX9_INTSIZE_Const(jx9_value *pVal, void *pUnused)
20773{
20774 SXUNUSED(pUnused);
20775 jx9_value_int64(pVal, sizeof(sxi64));
20776}
20777/*
20778 * DIRECTORY_SEPARATOR.
20779 * Expand the directory separator character.
20780 */
20781static void JX9_DIRSEP_Const(jx9_value *pVal, void *pUnused)
20782{
20783 SXUNUSED(pUnused);
20784#ifdef __WINNT__
20785 jx9_value_string(pVal, "\\", (int)sizeof(char));
20786#else
20787 jx9_value_string(pVal, "/", (int)sizeof(char));
20788#endif
20789}
20790/*
20791 * PATH_SEPARATOR.
20792 * Expand the path separator character.
20793 */
20794static void JX9_PATHSEP_Const(jx9_value *pVal, void *pUnused)
20795{
20796 SXUNUSED(pUnused);
20797#ifdef __WINNT__
20798 jx9_value_string(pVal, ";", (int)sizeof(char));
20799#else
20800 jx9_value_string(pVal, ":", (int)sizeof(char));
20801#endif
20802}
20803#ifndef __WINNT__
20804#include <time.h>
20805#endif
20806/*
20807 * __TIME__
20808 * Expand the current time (GMT).
20809 */
20810static void JX9_TIME_Const(jx9_value *pVal, void *pUnused)
20811{
20812 Sytm sTm;
20813#ifdef __WINNT__
20814 SYSTEMTIME sOS;
20815 GetSystemTime(&sOS);
20816 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
20817#else
20818 struct tm *pTm;
20819 time_t t;
20820 time(&t);
20821 pTm = gmtime(&t);
20822 STRUCT_TM_TO_SYTM(pTm, &sTm);
20823#endif
20824 SXUNUSED(pUnused); /* cc warning */
20825 /* Expand */
20826 jx9_value_string_format(pVal, "%02d:%02d:%02d", sTm.tm_hour, sTm.tm_min, sTm.tm_sec);
20827}
20828/*
20829 * __DATE__
20830 * Expand the current date in the ISO-8601 format.
20831 */
20832static void JX9_DATE_Const(jx9_value *pVal, void *pUnused)
20833{
20834 Sytm sTm;
20835#ifdef __WINNT__
20836 SYSTEMTIME sOS;
20837 GetSystemTime(&sOS);
20838 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
20839#else
20840 struct tm *pTm;
20841 time_t t;
20842 time(&t);
20843 pTm = gmtime(&t);
20844 STRUCT_TM_TO_SYTM(pTm, &sTm);
20845#endif
20846 SXUNUSED(pUnused); /* cc warning */
20847 /* Expand */
20848 jx9_value_string_format(pVal, "%04d-%02d-%02d", sTm.tm_year, sTm.tm_mon+1, sTm.tm_mday);
20849}
20850/*
20851 * __FILE__
20852 * Path of the processed script.
20853 */
20854static void JX9_FILE_Const(jx9_value *pVal, void *pUserData)
20855{
20856 jx9_vm *pVm = (jx9_vm *)pUserData;
20857 SyString *pFile;
20858 /* Peek the top entry */
20859 pFile = (SyString *)SySetPeek(&pVm->aFiles);
20860 if( pFile == 0 ){
20861 /* Expand the magic word: ":MEMORY:" */
20862 jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1);
20863 }else{
20864 jx9_value_string(pVal, pFile->zString, pFile->nByte);
20865 }
20866}
20867/*
20868 * __DIR__
20869 * Directory holding the processed script.
20870 */
20871static void JX9_DIR_Const(jx9_value *pVal, void *pUserData)
20872{
20873 jx9_vm *pVm = (jx9_vm *)pUserData;
20874 SyString *pFile;
20875 /* Peek the top entry */
20876 pFile = (SyString *)SySetPeek(&pVm->aFiles);
20877 if( pFile == 0 ){
20878 /* Expand the magic word: ":MEMORY:" */
20879 jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1);
20880 }else{
20881 if( pFile->nByte > 0 ){
20882 const char *zDir;
20883 int nLen;
20884 zDir = jx9ExtractDirName(pFile->zString, (int)pFile->nByte, &nLen);
20885 jx9_value_string(pVal, zDir, nLen);
20886 }else{
20887 /* Expand '.' as the current directory*/
20888 jx9_value_string(pVal, ".", (int)sizeof(char));
20889 }
20890 }
20891}
20892/*
20893 * E_ERROR
20894 * Expands 1
20895 */
20896static void JX9_E_ERROR_Const(jx9_value *pVal, void *pUserData)
20897{
20898 jx9_value_int(pVal, 1);
20899 SXUNUSED(pUserData);
20900}
20901/*
20902 * E_WARNING
20903 * Expands 2
20904 */
20905static void JX9_E_WARNING_Const(jx9_value *pVal, void *pUserData)
20906{
20907 jx9_value_int(pVal, 2);
20908 SXUNUSED(pUserData);
20909}
20910/*
20911 * E_PARSE
20912 * Expands 4
20913 */
20914static void JX9_E_PARSE_Const(jx9_value *pVal, void *pUserData)
20915{
20916 jx9_value_int(pVal, 4);
20917 SXUNUSED(pUserData);
20918}
20919/*
20920 * E_NOTICE
20921 * Expands 8
20922 */
20923static void JX9_E_NOTICE_Const(jx9_value *pVal, void *pUserData)
20924{
20925 jx9_value_int(pVal, 8);
20926 SXUNUSED(pUserData);
20927}
20928/*
20929 * CASE_LOWER
20930 * Expands 0.
20931 */
20932static void JX9_CASE_LOWER_Const(jx9_value *pVal, void *pUserData)
20933{
20934 jx9_value_int(pVal, 0);
20935 SXUNUSED(pUserData);
20936}
20937/*
20938 * CASE_UPPER
20939 * Expands 1.
20940 */
20941static void JX9_CASE_UPPER_Const(jx9_value *pVal, void *pUserData)
20942{
20943 jx9_value_int(pVal, 1);
20944 SXUNUSED(pUserData);
20945}
20946/*
20947 * STR_PAD_LEFT
20948 * Expands 0.
20949 */
20950static void JX9_STR_PAD_LEFT_Const(jx9_value *pVal, void *pUserData)
20951{
20952 jx9_value_int(pVal, 0);
20953 SXUNUSED(pUserData);
20954}
20955/*
20956 * STR_PAD_RIGHT
20957 * Expands 1.
20958 */
20959static void JX9_STR_PAD_RIGHT_Const(jx9_value *pVal, void *pUserData)
20960{
20961 jx9_value_int(pVal, 1);
20962 SXUNUSED(pUserData);
20963}
20964/*
20965 * STR_PAD_BOTH
20966 * Expands 2.
20967 */
20968static void JX9_STR_PAD_BOTH_Const(jx9_value *pVal, void *pUserData)
20969{
20970 jx9_value_int(pVal, 2);
20971 SXUNUSED(pUserData);
20972}
20973/*
20974 * COUNT_NORMAL
20975 * Expands 0
20976 */
20977static void JX9_COUNT_NORMAL_Const(jx9_value *pVal, void *pUserData)
20978{
20979 jx9_value_int(pVal, 0);
20980 SXUNUSED(pUserData);
20981}
20982/*
20983 * COUNT_RECURSIVE
20984 * Expands 1.
20985 */
20986static void JX9_COUNT_RECURSIVE_Const(jx9_value *pVal, void *pUserData)
20987{
20988 jx9_value_int(pVal, 1);
20989 SXUNUSED(pUserData);
20990}
20991/*
20992 * SORT_ASC
20993 * Expands 1.
20994 */
20995static void JX9_SORT_ASC_Const(jx9_value *pVal, void *pUserData)
20996{
20997 jx9_value_int(pVal, 1);
20998 SXUNUSED(pUserData);
20999}
21000/*
21001 * SORT_DESC
21002 * Expands 2.
21003 */
21004static void JX9_SORT_DESC_Const(jx9_value *pVal, void *pUserData)
21005{
21006 jx9_value_int(pVal, 2);
21007 SXUNUSED(pUserData);
21008}
21009/*
21010 * SORT_REGULAR
21011 * Expands 3.
21012 */
21013static void JX9_SORT_REG_Const(jx9_value *pVal, void *pUserData)
21014{
21015 jx9_value_int(pVal, 3);
21016 SXUNUSED(pUserData);
21017}
21018/*
21019 * SORT_NUMERIC
21020 * Expands 4.
21021 */
21022static void JX9_SORT_NUMERIC_Const(jx9_value *pVal, void *pUserData)
21023{
21024 jx9_value_int(pVal, 4);
21025 SXUNUSED(pUserData);
21026}
21027/*
21028 * SORT_STRING
21029 * Expands 5.
21030 */
21031static void JX9_SORT_STRING_Const(jx9_value *pVal, void *pUserData)
21032{
21033 jx9_value_int(pVal, 5);
21034 SXUNUSED(pUserData);
21035}
21036/*
21037 * JX9_ROUND_HALF_UP
21038 * Expands 1.
21039 */
21040static void JX9_JX9_ROUND_HALF_UP_Const(jx9_value *pVal, void *pUserData)
21041{
21042 jx9_value_int(pVal, 1);
21043 SXUNUSED(pUserData);
21044}
21045/*
21046 * SJX9_ROUND_HALF_DOWN
21047 * Expands 2.
21048 */
21049static void JX9_JX9_ROUND_HALF_DOWN_Const(jx9_value *pVal, void *pUserData)
21050{
21051 jx9_value_int(pVal, 2);
21052 SXUNUSED(pUserData);
21053}
21054/*
21055 * JX9_ROUND_HALF_EVEN
21056 * Expands 3.
21057 */
21058static void JX9_JX9_ROUND_HALF_EVEN_Const(jx9_value *pVal, void *pUserData)
21059{
21060 jx9_value_int(pVal, 3);
21061 SXUNUSED(pUserData);
21062}
21063/*
21064 * JX9_ROUND_HALF_ODD
21065 * Expands 4.
21066 */
21067static void JX9_JX9_ROUND_HALF_ODD_Const(jx9_value *pVal, void *pUserData)
21068{
21069 jx9_value_int(pVal, 4);
21070 SXUNUSED(pUserData);
21071}
21072#ifdef JX9_ENABLE_MATH_FUNC
21073/*
21074 * PI
21075 * Expand the value of pi.
21076 */
21077static void JX9_M_PI_Const(jx9_value *pVal, void *pUserData)
21078{
21079 SXUNUSED(pUserData); /* cc warning */
21080 jx9_value_double(pVal, JX9_PI);
21081}
21082/*
21083 * M_E
21084 * Expand 2.7182818284590452354
21085 */
21086static void JX9_M_E_Const(jx9_value *pVal, void *pUserData)
21087{
21088 SXUNUSED(pUserData); /* cc warning */
21089 jx9_value_double(pVal, 2.7182818284590452354);
21090}
21091/*
21092 * M_LOG2E
21093 * Expand 2.7182818284590452354
21094 */
21095static void JX9_M_LOG2E_Const(jx9_value *pVal, void *pUserData)
21096{
21097 SXUNUSED(pUserData); /* cc warning */
21098 jx9_value_double(pVal, 1.4426950408889634074);
21099}
21100/*
21101 * M_LOG10E
21102 * Expand 0.4342944819032518276
21103 */
21104static void JX9_M_LOG10E_Const(jx9_value *pVal, void *pUserData)
21105{
21106 SXUNUSED(pUserData); /* cc warning */
21107 jx9_value_double(pVal, 0.4342944819032518276);
21108}
21109/*
21110 * M_LN2
21111 * Expand 0.69314718055994530942
21112 */
21113static void JX9_M_LN2_Const(jx9_value *pVal, void *pUserData)
21114{
21115 SXUNUSED(pUserData); /* cc warning */
21116 jx9_value_double(pVal, 0.69314718055994530942);
21117}
21118/*
21119 * M_LN10
21120 * Expand 2.30258509299404568402
21121 */
21122static void JX9_M_LN10_Const(jx9_value *pVal, void *pUserData)
21123{
21124 SXUNUSED(pUserData); /* cc warning */
21125 jx9_value_double(pVal, 2.30258509299404568402);
21126}
21127/*
21128 * M_PI_2
21129 * Expand 1.57079632679489661923
21130 */
21131static void JX9_M_PI_2_Const(jx9_value *pVal, void *pUserData)
21132{
21133 SXUNUSED(pUserData); /* cc warning */
21134 jx9_value_double(pVal, 1.57079632679489661923);
21135}
21136/*
21137 * M_PI_4
21138 * Expand 0.78539816339744830962
21139 */
21140static void JX9_M_PI_4_Const(jx9_value *pVal, void *pUserData)
21141{
21142 SXUNUSED(pUserData); /* cc warning */
21143 jx9_value_double(pVal, 0.78539816339744830962);
21144}
21145/*
21146 * M_1_PI
21147 * Expand 0.31830988618379067154
21148 */
21149static void JX9_M_1_PI_Const(jx9_value *pVal, void *pUserData)
21150{
21151 SXUNUSED(pUserData); /* cc warning */
21152 jx9_value_double(pVal, 0.31830988618379067154);
21153}
21154/*
21155 * M_2_PI
21156 * Expand 0.63661977236758134308
21157 */
21158static void JX9_M_2_PI_Const(jx9_value *pVal, void *pUserData)
21159{
21160 SXUNUSED(pUserData); /* cc warning */
21161 jx9_value_double(pVal, 0.63661977236758134308);
21162}
21163/*
21164 * M_SQRTPI
21165 * Expand 1.77245385090551602729
21166 */
21167static void JX9_M_SQRTPI_Const(jx9_value *pVal, void *pUserData)
21168{
21169 SXUNUSED(pUserData); /* cc warning */
21170 jx9_value_double(pVal, 1.77245385090551602729);
21171}
21172/*
21173 * M_2_SQRTPI
21174 * Expand 1.12837916709551257390
21175 */
21176static void JX9_M_2_SQRTPI_Const(jx9_value *pVal, void *pUserData)
21177{
21178 SXUNUSED(pUserData); /* cc warning */
21179 jx9_value_double(pVal, 1.12837916709551257390);
21180}
21181/*
21182 * M_SQRT2
21183 * Expand 1.41421356237309504880
21184 */
21185static void JX9_M_SQRT2_Const(jx9_value *pVal, void *pUserData)
21186{
21187 SXUNUSED(pUserData); /* cc warning */
21188 jx9_value_double(pVal, 1.41421356237309504880);
21189}
21190/*
21191 * M_SQRT3
21192 * Expand 1.73205080756887729352
21193 */
21194static void JX9_M_SQRT3_Const(jx9_value *pVal, void *pUserData)
21195{
21196 SXUNUSED(pUserData); /* cc warning */
21197 jx9_value_double(pVal, 1.73205080756887729352);
21198}
21199/*
21200 * M_SQRT1_2
21201 * Expand 0.70710678118654752440
21202 */
21203static void JX9_M_SQRT1_2_Const(jx9_value *pVal, void *pUserData)
21204{
21205 SXUNUSED(pUserData); /* cc warning */
21206 jx9_value_double(pVal, 0.70710678118654752440);
21207}
21208/*
21209 * M_LNPI
21210 * Expand 1.14472988584940017414
21211 */
21212static void JX9_M_LNPI_Const(jx9_value *pVal, void *pUserData)
21213{
21214 SXUNUSED(pUserData); /* cc warning */
21215 jx9_value_double(pVal, 1.14472988584940017414);
21216}
21217/*
21218 * M_EULER
21219 * Expand 0.57721566490153286061
21220 */
21221static void JX9_M_EULER_Const(jx9_value *pVal, void *pUserData)
21222{
21223 SXUNUSED(pUserData); /* cc warning */
21224 jx9_value_double(pVal, 0.57721566490153286061);
21225}
21226#endif /* JX9_DISABLE_BUILTIN_MATH */
21227/*
21228 * DATE_ATOM
21229 * Expand Atom (example: 2005-08-15T15:52:01+00:00)
21230 */
21231static void JX9_DATE_ATOM_Const(jx9_value *pVal, void *pUserData)
21232{
21233 SXUNUSED(pUserData); /* cc warning */
21234 jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/);
21235}
21236/*
21237 * DATE_COOKIE
21238 * HTTP Cookies (example: Monday, 15-Aug-05 15:52:01 UTC)
21239 */
21240static void JX9_DATE_COOKIE_Const(jx9_value *pVal, void *pUserData)
21241{
21242 SXUNUSED(pUserData); /* cc warning */
21243 jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/);
21244}
21245/*
21246 * DATE_ISO8601
21247 * ISO-8601 (example: 2005-08-15T15:52:01+0000)
21248 */
21249static void JX9_DATE_ISO8601_Const(jx9_value *pVal, void *pUserData)
21250{
21251 SXUNUSED(pUserData); /* cc warning */
21252 jx9_value_string(pVal, "Y-m-d\\TH:i:sO", -1/*Compute length automatically*/);
21253}
21254/*
21255 * DATE_RFC822
21256 * RFC 822 (example: Mon, 15 Aug 05 15:52:01 +0000)
21257 */
21258static void JX9_DATE_RFC822_Const(jx9_value *pVal, void *pUserData)
21259{
21260 SXUNUSED(pUserData); /* cc warning */
21261 jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/);
21262}
21263/*
21264 * DATE_RFC850
21265 * RFC 850 (example: Monday, 15-Aug-05 15:52:01 UTC)
21266 */
21267static void JX9_DATE_RFC850_Const(jx9_value *pVal, void *pUserData)
21268{
21269 SXUNUSED(pUserData); /* cc warning */
21270 jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/);
21271}
21272/*
21273 * DATE_RFC1036
21274 * RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000)
21275 */
21276static void JX9_DATE_RFC1036_Const(jx9_value *pVal, void *pUserData)
21277{
21278 SXUNUSED(pUserData); /* cc warning */
21279 jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/);
21280}
21281/*
21282 * DATE_RFC1123
21283 * RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000)
21284 */
21285static void JX9_DATE_RFC1123_Const(jx9_value *pVal, void *pUserData)
21286{
21287 SXUNUSED(pUserData); /* cc warning */
21288 jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
21289}
21290/*
21291 * DATE_RFC2822
21292 * RFC 2822 (Mon, 15 Aug 2005 15:52:01 +0000)
21293 */
21294static void JX9_DATE_RFC2822_Const(jx9_value *pVal, void *pUserData)
21295{
21296 SXUNUSED(pUserData); /* cc warning */
21297 jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
21298}
21299/*
21300 * DATE_RSS
21301 * RSS (Mon, 15 Aug 2005 15:52:01 +0000)
21302 */
21303static void JX9_DATE_RSS_Const(jx9_value *pVal, void *pUserData)
21304{
21305 SXUNUSED(pUserData); /* cc warning */
21306 jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
21307}
21308/*
21309 * DATE_W3C
21310 * World Wide Web Consortium (example: 2005-08-15T15:52:01+00:00)
21311 */
21312static void JX9_DATE_W3C_Const(jx9_value *pVal, void *pUserData)
21313{
21314 SXUNUSED(pUserData); /* cc warning */
21315 jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/);
21316}
21317/*
21318 * ENT_COMPAT
21319 * Expand 0x01 (Must be a power of two)
21320 */
21321static void JX9_ENT_COMPAT_Const(jx9_value *pVal, void *pUserData)
21322{
21323 SXUNUSED(pUserData); /* cc warning */
21324 jx9_value_int(pVal, 0x01);
21325}
21326/*
21327 * ENT_QUOTES
21328 * Expand 0x02 (Must be a power of two)
21329 */
21330static void JX9_ENT_QUOTES_Const(jx9_value *pVal, void *pUserData)
21331{
21332 SXUNUSED(pUserData); /* cc warning */
21333 jx9_value_int(pVal, 0x02);
21334}
21335/*
21336 * ENT_NOQUOTES
21337 * Expand 0x04 (Must be a power of two)
21338 */
21339static void JX9_ENT_NOQUOTES_Const(jx9_value *pVal, void *pUserData)
21340{
21341 SXUNUSED(pUserData); /* cc warning */
21342 jx9_value_int(pVal, 0x04);
21343}
21344/*
21345 * ENT_IGNORE
21346 * Expand 0x08 (Must be a power of two)
21347 */
21348static void JX9_ENT_IGNORE_Const(jx9_value *pVal, void *pUserData)
21349{
21350 SXUNUSED(pUserData); /* cc warning */
21351 jx9_value_int(pVal, 0x08);
21352}
21353/*
21354 * ENT_SUBSTITUTE
21355 * Expand 0x10 (Must be a power of two)
21356 */
21357static void JX9_ENT_SUBSTITUTE_Const(jx9_value *pVal, void *pUserData)
21358{
21359 SXUNUSED(pUserData); /* cc warning */
21360 jx9_value_int(pVal, 0x10);
21361}
21362/*
21363 * ENT_DISALLOWED
21364 * Expand 0x20 (Must be a power of two)
21365 */
21366static void JX9_ENT_DISALLOWED_Const(jx9_value *pVal, void *pUserData)
21367{
21368 SXUNUSED(pUserData); /* cc warning */
21369 jx9_value_int(pVal, 0x20);
21370}
21371/*
21372 * ENT_HTML401
21373 * Expand 0x40 (Must be a power of two)
21374 */
21375static void JX9_ENT_HTML401_Const(jx9_value *pVal, void *pUserData)
21376{
21377 SXUNUSED(pUserData); /* cc warning */
21378 jx9_value_int(pVal, 0x40);
21379}
21380/*
21381 * ENT_XML1
21382 * Expand 0x80 (Must be a power of two)
21383 */
21384static void JX9_ENT_XML1_Const(jx9_value *pVal, void *pUserData)
21385{
21386 SXUNUSED(pUserData); /* cc warning */
21387 jx9_value_int(pVal, 0x80);
21388}
21389/*
21390 * ENT_XHTML
21391 * Expand 0x100 (Must be a power of two)
21392 */
21393static void JX9_ENT_XHTML_Const(jx9_value *pVal, void *pUserData)
21394{
21395 SXUNUSED(pUserData); /* cc warning */
21396 jx9_value_int(pVal, 0x100);
21397}
21398/*
21399 * ENT_HTML5
21400 * Expand 0x200 (Must be a power of two)
21401 */
21402static void JX9_ENT_HTML5_Const(jx9_value *pVal, void *pUserData)
21403{
21404 SXUNUSED(pUserData); /* cc warning */
21405 jx9_value_int(pVal, 0x200);
21406}
21407/*
21408 * ISO-8859-1
21409 * ISO_8859_1
21410 * Expand 1
21411 */
21412static void JX9_ISO88591_Const(jx9_value *pVal, void *pUserData)
21413{
21414 SXUNUSED(pUserData); /* cc warning */
21415 jx9_value_int(pVal, 1);
21416}
21417/*
21418 * UTF-8
21419 * UTF8
21420 * Expand 2
21421 */
21422static void JX9_UTF8_Const(jx9_value *pVal, void *pUserData)
21423{
21424 SXUNUSED(pUserData); /* cc warning */
21425 jx9_value_int(pVal, 1);
21426}
21427/*
21428 * HTML_ENTITIES
21429 * Expand 1
21430 */
21431static void JX9_HTML_ENTITIES_Const(jx9_value *pVal, void *pUserData)
21432{
21433 SXUNUSED(pUserData); /* cc warning */
21434 jx9_value_int(pVal, 1);
21435}
21436/*
21437 * HTML_SPECIALCHARS
21438 * Expand 2
21439 */
21440static void JX9_HTML_SPECIALCHARS_Const(jx9_value *pVal, void *pUserData)
21441{
21442 SXUNUSED(pUserData); /* cc warning */
21443 jx9_value_int(pVal, 2);
21444}
21445/*
21446 * JX9_URL_SCHEME.
21447 * Expand 1
21448 */
21449static void JX9_JX9_URL_SCHEME_Const(jx9_value *pVal, void *pUserData)
21450{
21451 SXUNUSED(pUserData); /* cc warning */
21452 jx9_value_int(pVal, 1);
21453}
21454/*
21455 * JX9_URL_HOST.
21456 * Expand 2
21457 */
21458static void JX9_JX9_URL_HOST_Const(jx9_value *pVal, void *pUserData)
21459{
21460 SXUNUSED(pUserData); /* cc warning */
21461 jx9_value_int(pVal, 2);
21462}
21463/*
21464 * JX9_URL_PORT.
21465 * Expand 3
21466 */
21467static void JX9_JX9_URL_PORT_Const(jx9_value *pVal, void *pUserData)
21468{
21469 SXUNUSED(pUserData); /* cc warning */
21470 jx9_value_int(pVal, 3);
21471}
21472/*
21473 * JX9_URL_USER.
21474 * Expand 4
21475 */
21476static void JX9_JX9_URL_USER_Const(jx9_value *pVal, void *pUserData)
21477{
21478 SXUNUSED(pUserData); /* cc warning */
21479 jx9_value_int(pVal, 4);
21480}
21481/*
21482 * JX9_URL_PASS.
21483 * Expand 5
21484 */
21485static void JX9_JX9_URL_PASS_Const(jx9_value *pVal, void *pUserData)
21486{
21487 SXUNUSED(pUserData); /* cc warning */
21488 jx9_value_int(pVal, 5);
21489}
21490/*
21491 * JX9_URL_PATH.
21492 * Expand 6
21493 */
21494static void JX9_JX9_URL_PATH_Const(jx9_value *pVal, void *pUserData)
21495{
21496 SXUNUSED(pUserData); /* cc warning */
21497 jx9_value_int(pVal, 6);
21498}
21499/*
21500 * JX9_URL_QUERY.
21501 * Expand 7
21502 */
21503static void JX9_JX9_URL_QUERY_Const(jx9_value *pVal, void *pUserData)
21504{
21505 SXUNUSED(pUserData); /* cc warning */
21506 jx9_value_int(pVal, 7);
21507}
21508/*
21509 * JX9_URL_FRAGMENT.
21510 * Expand 8
21511 */
21512static void JX9_JX9_URL_FRAGMENT_Const(jx9_value *pVal, void *pUserData)
21513{
21514 SXUNUSED(pUserData); /* cc warning */
21515 jx9_value_int(pVal, 8);
21516}
21517/*
21518 * JX9_QUERY_RFC1738
21519 * Expand 1
21520 */
21521static void JX9_JX9_QUERY_RFC1738_Const(jx9_value *pVal, void *pUserData)
21522{
21523 SXUNUSED(pUserData); /* cc warning */
21524 jx9_value_int(pVal, 1);
21525}
21526/*
21527 * JX9_QUERY_RFC3986
21528 * Expand 1
21529 */
21530static void JX9_JX9_QUERY_RFC3986_Const(jx9_value *pVal, void *pUserData)
21531{
21532 SXUNUSED(pUserData); /* cc warning */
21533 jx9_value_int(pVal, 2);
21534}
21535/*
21536 * FNM_NOESCAPE
21537 * Expand 0x01 (Must be a power of two)
21538 */
21539static void JX9_FNM_NOESCAPE_Const(jx9_value *pVal, void *pUserData)
21540{
21541 SXUNUSED(pUserData); /* cc warning */
21542 jx9_value_int(pVal, 0x01);
21543}
21544/*
21545 * FNM_PATHNAME
21546 * Expand 0x02 (Must be a power of two)
21547 */
21548static void JX9_FNM_PATHNAME_Const(jx9_value *pVal, void *pUserData)
21549{
21550 SXUNUSED(pUserData); /* cc warning */
21551 jx9_value_int(pVal, 0x02);
21552}
21553/*
21554 * FNM_PERIOD
21555 * Expand 0x04 (Must be a power of two)
21556 */
21557static void JX9_FNM_PERIOD_Const(jx9_value *pVal, void *pUserData)
21558{
21559 SXUNUSED(pUserData); /* cc warning */
21560 jx9_value_int(pVal, 0x04);
21561}
21562/*
21563 * FNM_CASEFOLD
21564 * Expand 0x08 (Must be a power of two)
21565 */
21566static void JX9_FNM_CASEFOLD_Const(jx9_value *pVal, void *pUserData)
21567{
21568 SXUNUSED(pUserData); /* cc warning */
21569 jx9_value_int(pVal, 0x08);
21570}
21571/*
21572 * PATHINFO_DIRNAME
21573 * Expand 1.
21574 */
21575static void JX9_PATHINFO_DIRNAME_Const(jx9_value *pVal, void *pUserData)
21576{
21577 SXUNUSED(pUserData); /* cc warning */
21578 jx9_value_int(pVal, 1);
21579}
21580/*
21581 * PATHINFO_BASENAME
21582 * Expand 2.
21583 */
21584static void JX9_PATHINFO_BASENAME_Const(jx9_value *pVal, void *pUserData)
21585{
21586 SXUNUSED(pUserData); /* cc warning */
21587 jx9_value_int(pVal, 2);
21588}
21589/*
21590 * PATHINFO_EXTENSION
21591 * Expand 3.
21592 */
21593static void JX9_PATHINFO_EXTENSION_Const(jx9_value *pVal, void *pUserData)
21594{
21595 SXUNUSED(pUserData); /* cc warning */
21596 jx9_value_int(pVal, 3);
21597}
21598/*
21599 * PATHINFO_FILENAME
21600 * Expand 4.
21601 */
21602static void JX9_PATHINFO_FILENAME_Const(jx9_value *pVal, void *pUserData)
21603{
21604 SXUNUSED(pUserData); /* cc warning */
21605 jx9_value_int(pVal, 4);
21606}
21607/*
21608 * ASSERT_ACTIVE.
21609 * Expand the value of JX9_ASSERT_ACTIVE defined in jx9Int.h
21610 */
21611static void JX9_ASSERT_ACTIVE_Const(jx9_value *pVal, void *pUserData)
21612{
21613 SXUNUSED(pUserData); /* cc warning */
21614 jx9_value_int(pVal, JX9_ASSERT_DISABLE);
21615}
21616/*
21617 * ASSERT_WARNING.
21618 * Expand the value of JX9_ASSERT_WARNING defined in jx9Int.h
21619 */
21620static void JX9_ASSERT_WARNING_Const(jx9_value *pVal, void *pUserData)
21621{
21622 SXUNUSED(pUserData); /* cc warning */
21623 jx9_value_int(pVal, JX9_ASSERT_WARNING);
21624}
21625/*
21626 * ASSERT_BAIL.
21627 * Expand the value of JX9_ASSERT_BAIL defined in jx9Int.h
21628 */
21629static void JX9_ASSERT_BAIL_Const(jx9_value *pVal, void *pUserData)
21630{
21631 SXUNUSED(pUserData); /* cc warning */
21632 jx9_value_int(pVal, JX9_ASSERT_BAIL);
21633}
21634/*
21635 * ASSERT_QUIET_EVAL.
21636 * Expand the value of JX9_ASSERT_QUIET_EVAL defined in jx9Int.h
21637 */
21638static void JX9_ASSERT_QUIET_EVAL_Const(jx9_value *pVal, void *pUserData)
21639{
21640 SXUNUSED(pUserData); /* cc warning */
21641 jx9_value_int(pVal, JX9_ASSERT_QUIET_EVAL);
21642}
21643/*
21644 * ASSERT_CALLBACK.
21645 * Expand the value of JX9_ASSERT_CALLBACK defined in jx9Int.h
21646 */
21647static void JX9_ASSERT_CALLBACK_Const(jx9_value *pVal, void *pUserData)
21648{
21649 SXUNUSED(pUserData); /* cc warning */
21650 jx9_value_int(pVal, JX9_ASSERT_CALLBACK);
21651}
21652/*
21653 * SEEK_SET.
21654 * Expand 0
21655 */
21656static void JX9_SEEK_SET_Const(jx9_value *pVal, void *pUserData)
21657{
21658 SXUNUSED(pUserData); /* cc warning */
21659 jx9_value_int(pVal, 0);
21660}
21661/*
21662 * SEEK_CUR.
21663 * Expand 1
21664 */
21665static void JX9_SEEK_CUR_Const(jx9_value *pVal, void *pUserData)
21666{
21667 SXUNUSED(pUserData); /* cc warning */
21668 jx9_value_int(pVal, 1);
21669}
21670/*
21671 * SEEK_END.
21672 * Expand 2
21673 */
21674static void JX9_SEEK_END_Const(jx9_value *pVal, void *pUserData)
21675{
21676 SXUNUSED(pUserData); /* cc warning */
21677 jx9_value_int(pVal, 2);
21678}
21679/*
21680 * LOCK_SH.
21681 * Expand 2
21682 */
21683static void JX9_LOCK_SH_Const(jx9_value *pVal, void *pUserData)
21684{
21685 SXUNUSED(pUserData); /* cc warning */
21686 jx9_value_int(pVal, 1);
21687}
21688/*
21689 * LOCK_NB.
21690 * Expand 5
21691 */
21692static void JX9_LOCK_NB_Const(jx9_value *pVal, void *pUserData)
21693{
21694 SXUNUSED(pUserData); /* cc warning */
21695 jx9_value_int(pVal, 5);
21696}
21697/*
21698 * LOCK_EX.
21699 * Expand 0x01 (MUST BE A POWER OF TWO)
21700 */
21701static void JX9_LOCK_EX_Const(jx9_value *pVal, void *pUserData)
21702{
21703 SXUNUSED(pUserData); /* cc warning */
21704 jx9_value_int(pVal, 0x01);
21705}
21706/*
21707 * LOCK_UN.
21708 * Expand 0
21709 */
21710static void JX9_LOCK_UN_Const(jx9_value *pVal, void *pUserData)
21711{
21712 SXUNUSED(pUserData); /* cc warning */
21713 jx9_value_int(pVal, 0);
21714}
21715/*
21716 * FILE_USE_INC_PATH
21717 * Expand 0x01 (Must be a power of two)
21718 */
21719static void JX9_FILE_USE_INCLUDE_PATH_Const(jx9_value *pVal, void *pUserData)
21720{
21721 SXUNUSED(pUserData); /* cc warning */
21722 jx9_value_int(pVal, 0x1);
21723}
21724/*
21725 * FILE_IGN_NL
21726 * Expand 0x02 (Must be a power of two)
21727 */
21728static void JX9_FILE_IGNORE_NEW_LINES_Const(jx9_value *pVal, void *pUserData)
21729{
21730 SXUNUSED(pUserData); /* cc warning */
21731 jx9_value_int(pVal, 0x2);
21732}
21733/*
21734 * FILE_SKIP_EL
21735 * Expand 0x04 (Must be a power of two)
21736 */
21737static void JX9_FILE_SKIP_EMPTY_LINES_Const(jx9_value *pVal, void *pUserData)
21738{
21739 SXUNUSED(pUserData); /* cc warning */
21740 jx9_value_int(pVal, 0x4);
21741}
21742/*
21743 * FILE_APPEND
21744 * Expand 0x08 (Must be a power of two)
21745 */
21746static void JX9_FILE_APPEND_Const(jx9_value *pVal, void *pUserData)
21747{
21748 SXUNUSED(pUserData); /* cc warning */
21749 jx9_value_int(pVal, 0x08);
21750}
21751/*
21752 * SCANDIR_SORT_ASCENDING
21753 * Expand 0
21754 */
21755static void JX9_SCANDIR_SORT_ASCENDING_Const(jx9_value *pVal, void *pUserData)
21756{
21757 SXUNUSED(pUserData); /* cc warning */
21758 jx9_value_int(pVal, 0);
21759}
21760/*
21761 * SCANDIR_SORT_DESCENDING
21762 * Expand 1
21763 */
21764static void JX9_SCANDIR_SORT_DESCENDING_Const(jx9_value *pVal, void *pUserData)
21765{
21766 SXUNUSED(pUserData); /* cc warning */
21767 jx9_value_int(pVal, 1);
21768}
21769/*
21770 * SCANDIR_SORT_NONE
21771 * Expand 2
21772 */
21773static void JX9_SCANDIR_SORT_NONE_Const(jx9_value *pVal, void *pUserData)
21774{
21775 SXUNUSED(pUserData); /* cc warning */
21776 jx9_value_int(pVal, 2);
21777}
21778/*
21779 * GLOB_MARK
21780 * Expand 0x01 (must be a power of two)
21781 */
21782static void JX9_GLOB_MARK_Const(jx9_value *pVal, void *pUserData)
21783{
21784 SXUNUSED(pUserData); /* cc warning */
21785 jx9_value_int(pVal, 0x01);
21786}
21787/*
21788 * GLOB_NOSORT
21789 * Expand 0x02 (must be a power of two)
21790 */
21791static void JX9_GLOB_NOSORT_Const(jx9_value *pVal, void *pUserData)
21792{
21793 SXUNUSED(pUserData); /* cc warning */
21794 jx9_value_int(pVal, 0x02);
21795}
21796/*
21797 * GLOB_NOCHECK
21798 * Expand 0x04 (must be a power of two)
21799 */
21800static void JX9_GLOB_NOCHECK_Const(jx9_value *pVal, void *pUserData)
21801{
21802 SXUNUSED(pUserData); /* cc warning */
21803 jx9_value_int(pVal, 0x04);
21804}
21805/*
21806 * GLOB_NOESCAPE
21807 * Expand 0x08 (must be a power of two)
21808 */
21809static void JX9_GLOB_NOESCAPE_Const(jx9_value *pVal, void *pUserData)
21810{
21811 SXUNUSED(pUserData); /* cc warning */
21812 jx9_value_int(pVal, 0x08);
21813}
21814/*
21815 * GLOB_BRACE
21816 * Expand 0x10 (must be a power of two)
21817 */
21818static void JX9_GLOB_BRACE_Const(jx9_value *pVal, void *pUserData)
21819{
21820 SXUNUSED(pUserData); /* cc warning */
21821 jx9_value_int(pVal, 0x10);
21822}
21823/*
21824 * GLOB_ONLYDIR
21825 * Expand 0x20 (must be a power of two)
21826 */
21827static void JX9_GLOB_ONLYDIR_Const(jx9_value *pVal, void *pUserData)
21828{
21829 SXUNUSED(pUserData); /* cc warning */
21830 jx9_value_int(pVal, 0x20);
21831}
21832/*
21833 * GLOB_ERR
21834 * Expand 0x40 (must be a power of two)
21835 */
21836static void JX9_GLOB_ERR_Const(jx9_value *pVal, void *pUserData)
21837{
21838 SXUNUSED(pUserData); /* cc warning */
21839 jx9_value_int(pVal, 0x40);
21840}
21841/*
21842 * STDIN
21843 * Expand the STDIN handle as a resource.
21844 */
21845static void JX9_STDIN_Const(jx9_value *pVal, void *pUserData)
21846{
21847 jx9_vm *pVm = (jx9_vm *)pUserData;
21848 void *pResource;
21849 pResource = jx9ExportStdin(pVm);
21850 jx9_value_resource(pVal, pResource);
21851}
21852/*
21853 * STDOUT
21854 * Expand the STDOUT handle as a resource.
21855 */
21856static void JX9_STDOUT_Const(jx9_value *pVal, void *pUserData)
21857{
21858 jx9_vm *pVm = (jx9_vm *)pUserData;
21859 void *pResource;
21860 pResource = jx9ExportStdout(pVm);
21861 jx9_value_resource(pVal, pResource);
21862}
21863/*
21864 * STDERR
21865 * Expand the STDERR handle as a resource.
21866 */
21867static void JX9_STDERR_Const(jx9_value *pVal, void *pUserData)
21868{
21869 jx9_vm *pVm = (jx9_vm *)pUserData;
21870 void *pResource;
21871 pResource = jx9ExportStderr(pVm);
21872 jx9_value_resource(pVal, pResource);
21873}
21874/*
21875 * INI_SCANNER_NORMAL
21876 * Expand 1
21877 */
21878static void JX9_INI_SCANNER_NORMAL_Const(jx9_value *pVal, void *pUserData)
21879{
21880 SXUNUSED(pUserData); /* cc warning */
21881 jx9_value_int(pVal, 1);
21882}
21883/*
21884 * INI_SCANNER_RAW
21885 * Expand 2
21886 */
21887static void JX9_INI_SCANNER_RAW_Const(jx9_value *pVal, void *pUserData)
21888{
21889 SXUNUSED(pUserData); /* cc warning */
21890 jx9_value_int(pVal, 2);
21891}
21892/*
21893 * EXTR_OVERWRITE
21894 * Expand 0x01 (Must be a power of two)
21895 */
21896static void JX9_EXTR_OVERWRITE_Const(jx9_value *pVal, void *pUserData)
21897{
21898 SXUNUSED(pUserData); /* cc warning */
21899 jx9_value_int(pVal, 0x1);
21900}
21901/*
21902 * EXTR_SKIP
21903 * Expand 0x02 (Must be a power of two)
21904 */
21905static void JX9_EXTR_SKIP_Const(jx9_value *pVal, void *pUserData)
21906{
21907 SXUNUSED(pUserData); /* cc warning */
21908 jx9_value_int(pVal, 0x2);
21909}
21910/*
21911 * EXTR_PREFIX_SAME
21912 * Expand 0x04 (Must be a power of two)
21913 */
21914static void JX9_EXTR_PREFIX_SAME_Const(jx9_value *pVal, void *pUserData)
21915{
21916 SXUNUSED(pUserData); /* cc warning */
21917 jx9_value_int(pVal, 0x4);
21918}
21919/*
21920 * EXTR_PREFIX_ALL
21921 * Expand 0x08 (Must be a power of two)
21922 */
21923static void JX9_EXTR_PREFIX_ALL_Const(jx9_value *pVal, void *pUserData)
21924{
21925 SXUNUSED(pUserData); /* cc warning */
21926 jx9_value_int(pVal, 0x8);
21927}
21928/*
21929 * EXTR_PREFIX_INVALID
21930 * Expand 0x10 (Must be a power of two)
21931 */
21932static void JX9_EXTR_PREFIX_INVALID_Const(jx9_value *pVal, void *pUserData)
21933{
21934 SXUNUSED(pUserData); /* cc warning */
21935 jx9_value_int(pVal, 0x10);
21936}
21937/*
21938 * EXTR_IF_EXISTS
21939 * Expand 0x20 (Must be a power of two)
21940 */
21941static void JX9_EXTR_IF_EXISTS_Const(jx9_value *pVal, void *pUserData)
21942{
21943 SXUNUSED(pUserData); /* cc warning */
21944 jx9_value_int(pVal, 0x20);
21945}
21946/*
21947 * EXTR_PREFIX_IF_EXISTS
21948 * Expand 0x40 (Must be a power of two)
21949 */
21950static void JX9_EXTR_PREFIX_IF_EXISTS_Const(jx9_value *pVal, void *pUserData)
21951{
21952 SXUNUSED(pUserData); /* cc warning */
21953 jx9_value_int(pVal, 0x40);
21954}
21955/*
21956 * Table of built-in constants.
21957 */
21958static const jx9_builtin_constant aBuiltIn[] = {
21959 {"JX9_VERSION", JX9_VER_Const },
21960 {"JX9_ENGINE", JX9_VER_Const },
21961 {"__JX9__", JX9_VER_Const },
21962 {"JX9_OS", JX9_OS_Const },
21963 {"__OS__", JX9_OS_Const },
21964 {"JX9_EOL", JX9_EOL_Const },
21965 {"JX9_INT_MAX", JX9_INTMAX_Const },
21966 {"MAXINT", JX9_INTMAX_Const },
21967 {"JX9_INT_SIZE", JX9_INTSIZE_Const },
21968 {"PATH_SEPARATOR", JX9_PATHSEP_Const },
21969 {"DIRECTORY_SEPARATOR", JX9_DIRSEP_Const },
21970 {"DIR_SEP", JX9_DIRSEP_Const },
21971 {"__TIME__", JX9_TIME_Const },
21972 {"__DATE__", JX9_DATE_Const },
21973 {"__FILE__", JX9_FILE_Const },
21974 {"__DIR__", JX9_DIR_Const },
21975 {"E_ERROR", JX9_E_ERROR_Const },
21976 {"E_WARNING", JX9_E_WARNING_Const},
21977 {"E_PARSE", JX9_E_PARSE_Const },
21978 {"E_NOTICE", JX9_E_NOTICE_Const },
21979 {"CASE_LOWER", JX9_CASE_LOWER_Const },
21980 {"CASE_UPPER", JX9_CASE_UPPER_Const },
21981 {"STR_PAD_LEFT", JX9_STR_PAD_LEFT_Const },
21982 {"STR_PAD_RIGHT", JX9_STR_PAD_RIGHT_Const},
21983 {"STR_PAD_BOTH", JX9_STR_PAD_BOTH_Const },
21984 {"COUNT_NORMAL", JX9_COUNT_NORMAL_Const },
21985 {"COUNT_RECURSIVE", JX9_COUNT_RECURSIVE_Const },
21986 {"SORT_ASC", JX9_SORT_ASC_Const },
21987 {"SORT_DESC", JX9_SORT_DESC_Const },
21988 {"SORT_REGULAR", JX9_SORT_REG_Const },
21989 {"SORT_NUMERIC", JX9_SORT_NUMERIC_Const },
21990 {"SORT_STRING", JX9_SORT_STRING_Const },
21991 {"JX9_ROUND_HALF_DOWN", JX9_JX9_ROUND_HALF_DOWN_Const },
21992 {"JX9_ROUND_HALF_EVEN", JX9_JX9_ROUND_HALF_EVEN_Const },
21993 {"JX9_ROUND_HALF_UP", JX9_JX9_ROUND_HALF_UP_Const },
21994 {"JX9_ROUND_HALF_ODD", JX9_JX9_ROUND_HALF_ODD_Const },
21995#ifdef JX9_ENABLE_MATH_FUNC
21996 {"PI", JX9_M_PI_Const },
21997 {"M_E", JX9_M_E_Const },
21998 {"M_LOG2E", JX9_M_LOG2E_Const },
21999 {"M_LOG10E", JX9_M_LOG10E_Const },
22000 {"M_LN2", JX9_M_LN2_Const },
22001 {"M_LN10", JX9_M_LN10_Const },
22002 {"M_PI_2", JX9_M_PI_2_Const },
22003 {"M_PI_4", JX9_M_PI_4_Const },
22004 {"M_1_PI", JX9_M_1_PI_Const },
22005 {"M_2_PI", JX9_M_2_PI_Const },
22006 {"M_SQRTPI", JX9_M_SQRTPI_Const },
22007 {"M_2_SQRTPI", JX9_M_2_SQRTPI_Const },
22008 {"M_SQRT2", JX9_M_SQRT2_Const },
22009 {"M_SQRT3", JX9_M_SQRT3_Const },
22010 {"M_SQRT1_2", JX9_M_SQRT1_2_Const },
22011 {"M_LNPI", JX9_M_LNPI_Const },
22012 {"M_EULER", JX9_M_EULER_Const },
22013#endif /* JX9_ENABLE_MATH_FUNC */
22014 {"DATE_ATOM", JX9_DATE_ATOM_Const },
22015 {"DATE_COOKIE", JX9_DATE_COOKIE_Const },
22016 {"DATE_ISO8601", JX9_DATE_ISO8601_Const },
22017 {"DATE_RFC822", JX9_DATE_RFC822_Const },
22018 {"DATE_RFC850", JX9_DATE_RFC850_Const },
22019 {"DATE_RFC1036", JX9_DATE_RFC1036_Const },
22020 {"DATE_RFC1123", JX9_DATE_RFC1123_Const },
22021 {"DATE_RFC2822", JX9_DATE_RFC2822_Const },
22022 {"DATE_RFC3339", JX9_DATE_ATOM_Const },
22023 {"DATE_RSS", JX9_DATE_RSS_Const },
22024 {"DATE_W3C", JX9_DATE_W3C_Const },
22025 {"ENT_COMPAT", JX9_ENT_COMPAT_Const },
22026 {"ENT_QUOTES", JX9_ENT_QUOTES_Const },
22027 {"ENT_NOQUOTES", JX9_ENT_NOQUOTES_Const },
22028 {"ENT_IGNORE", JX9_ENT_IGNORE_Const },
22029 {"ENT_SUBSTITUTE", JX9_ENT_SUBSTITUTE_Const},
22030 {"ENT_DISALLOWED", JX9_ENT_DISALLOWED_Const},
22031 {"ENT_HTML401", JX9_ENT_HTML401_Const },
22032 {"ENT_XML1", JX9_ENT_XML1_Const },
22033 {"ENT_XHTML", JX9_ENT_XHTML_Const },
22034 {"ENT_HTML5", JX9_ENT_HTML5_Const },
22035 {"ISO-8859-1", JX9_ISO88591_Const },
22036 {"ISO_8859_1", JX9_ISO88591_Const },
22037 {"UTF-8", JX9_UTF8_Const },
22038 {"UTF8", JX9_UTF8_Const },
22039 {"HTML_ENTITIES", JX9_HTML_ENTITIES_Const},
22040 {"HTML_SPECIALCHARS", JX9_HTML_SPECIALCHARS_Const },
22041 {"JX9_URL_SCHEME", JX9_JX9_URL_SCHEME_Const},
22042 {"JX9_URL_HOST", JX9_JX9_URL_HOST_Const},
22043 {"JX9_URL_PORT", JX9_JX9_URL_PORT_Const},
22044 {"JX9_URL_USER", JX9_JX9_URL_USER_Const},
22045 {"JX9_URL_PASS", JX9_JX9_URL_PASS_Const},
22046 {"JX9_URL_PATH", JX9_JX9_URL_PATH_Const},
22047 {"JX9_URL_QUERY", JX9_JX9_URL_QUERY_Const},
22048 {"JX9_URL_FRAGMENT", JX9_JX9_URL_FRAGMENT_Const},
22049 {"JX9_QUERY_RFC1738", JX9_JX9_QUERY_RFC1738_Const},
22050 {"JX9_QUERY_RFC3986", JX9_JX9_QUERY_RFC3986_Const},
22051 {"FNM_NOESCAPE", JX9_FNM_NOESCAPE_Const },
22052 {"FNM_PATHNAME", JX9_FNM_PATHNAME_Const },
22053 {"FNM_PERIOD", JX9_FNM_PERIOD_Const },
22054 {"FNM_CASEFOLD", JX9_FNM_CASEFOLD_Const },
22055 {"PATHINFO_DIRNAME", JX9_PATHINFO_DIRNAME_Const },
22056 {"PATHINFO_BASENAME", JX9_PATHINFO_BASENAME_Const },
22057 {"PATHINFO_EXTENSION", JX9_PATHINFO_EXTENSION_Const},
22058 {"PATHINFO_FILENAME", JX9_PATHINFO_FILENAME_Const },
22059 {"ASSERT_ACTIVE", JX9_ASSERT_ACTIVE_Const },
22060 {"ASSERT_WARNING", JX9_ASSERT_WARNING_Const },
22061 {"ASSERT_BAIL", JX9_ASSERT_BAIL_Const },
22062 {"ASSERT_QUIET_EVAL", JX9_ASSERT_QUIET_EVAL_Const },
22063 {"ASSERT_CALLBACK", JX9_ASSERT_CALLBACK_Const },
22064 {"SEEK_SET", JX9_SEEK_SET_Const },
22065 {"SEEK_CUR", JX9_SEEK_CUR_Const },
22066 {"SEEK_END", JX9_SEEK_END_Const },
22067 {"LOCK_EX", JX9_LOCK_EX_Const },
22068 {"LOCK_SH", JX9_LOCK_SH_Const },
22069 {"LOCK_NB", JX9_LOCK_NB_Const },
22070 {"LOCK_UN", JX9_LOCK_UN_Const },
22071 {"FILE_USE_INC_PATH", JX9_FILE_USE_INCLUDE_PATH_Const},
22072 {"FILE_IGN_NL", JX9_FILE_IGNORE_NEW_LINES_Const},
22073 {"FILE_SKIP_EL", JX9_FILE_SKIP_EMPTY_LINES_Const},
22074 {"FILE_APPEND", JX9_FILE_APPEND_Const },
22075 {"SCANDIR_SORT_ASC", JX9_SCANDIR_SORT_ASCENDING_Const },
22076 {"SCANDIR_SORT_DESC", JX9_SCANDIR_SORT_DESCENDING_Const },
22077 {"SCANDIR_SORT_NONE", JX9_SCANDIR_SORT_NONE_Const },
22078 {"GLOB_MARK", JX9_GLOB_MARK_Const },
22079 {"GLOB_NOSORT", JX9_GLOB_NOSORT_Const },
22080 {"GLOB_NOCHECK", JX9_GLOB_NOCHECK_Const },
22081 {"GLOB_NOESCAPE", JX9_GLOB_NOESCAPE_Const},
22082 {"GLOB_BRACE", JX9_GLOB_BRACE_Const },
22083 {"GLOB_ONLYDIR", JX9_GLOB_ONLYDIR_Const },
22084 {"GLOB_ERR", JX9_GLOB_ERR_Const },
22085 {"STDIN", JX9_STDIN_Const },
22086 {"stdin", JX9_STDIN_Const },
22087 {"STDOUT", JX9_STDOUT_Const },
22088 {"stdout", JX9_STDOUT_Const },
22089 {"STDERR", JX9_STDERR_Const },
22090 {"stderr", JX9_STDERR_Const },
22091 {"INI_SCANNER_NORMAL", JX9_INI_SCANNER_NORMAL_Const },
22092 {"INI_SCANNER_RAW", JX9_INI_SCANNER_RAW_Const },
22093 {"EXTR_OVERWRITE", JX9_EXTR_OVERWRITE_Const },
22094 {"EXTR_SKIP", JX9_EXTR_SKIP_Const },
22095 {"EXTR_PREFIX_SAME", JX9_EXTR_PREFIX_SAME_Const },
22096 {"EXTR_PREFIX_ALL", JX9_EXTR_PREFIX_ALL_Const },
22097 {"EXTR_PREFIX_INVALID", JX9_EXTR_PREFIX_INVALID_Const },
22098 {"EXTR_IF_EXISTS", JX9_EXTR_IF_EXISTS_Const },
22099 {"EXTR_PREFIX_IF_EXISTS", JX9_EXTR_PREFIX_IF_EXISTS_Const}
22100};
22101/*
22102 * Register the built-in constants defined above.
22103 */
22104JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm)
22105{
22106 sxu32 n;
22107 /*
22108 * Note that all built-in constants have access to the jx9 virtual machine
22109 * that trigger the constant invocation as their private data.
22110 */
22111 for( n = 0 ; n < SX_ARRAYSIZE(aBuiltIn) ; ++n ){
22112 jx9_create_constant(&(*pVm), aBuiltIn[n].zName, aBuiltIn[n].xExpand, &(*pVm));
22113 }
22114}
22115/*
22116 * ----------------------------------------------------------
22117 * File: jx9_hashmap.c
22118 * MD5: 4e93d15cd37e6093e25d8ede3064e210
22119 * ----------------------------------------------------------
22120 */
22121/*
22122 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
22123 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
22124 * Version 1.7.2
22125 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
22126 * please contact Symisc Systems via:
22127 * legal@symisc.net
22128 * licensing@symisc.net
22129 * contact@symisc.net
22130 * or visit:
22131 * http://jx9.symisc.net/
22132 */
22133 /* $SymiscID: hashmap.c v2.6 Win7 2012-12-11 00:50 stable <chm@symisc.net> $ */
22134#ifndef JX9_AMALGAMATION
22135#include "jx9Int.h"
22136#endif
22137/* This file implement generic hashmaps used to represent JSON arrays and objects */
22138/* Allowed node types */
22139#define HASHMAP_INT_NODE 1 /* Node with an int [i.e: 64-bit integer] key */
22140#define HASHMAP_BLOB_NODE 2 /* Node with a string/BLOB key */
22141/*
22142 * Default hash function for int [i.e; 64-bit integer] keys.
22143 */
22144static sxu32 IntHash(sxi64 iKey)
22145{
22146 return (sxu32)(iKey ^ (iKey << 8) ^ (iKey >> 8));
22147}
22148/*
22149 * Default hash function for string/BLOB keys.
22150 */
22151static sxu32 BinHash(const void *pSrc, sxu32 nLen)
22152{
22153 register unsigned char *zIn = (unsigned char *)pSrc;
22154 unsigned char *zEnd;
22155 sxu32 nH = 5381;
22156 zEnd = &zIn[nLen];
22157 for(;;){
22158 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
22159 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
22160 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
22161 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
22162 }
22163 return nH;
22164}
22165/*
22166 * Return the total number of entries in a given hashmap.
22167 * If bRecurisve is set to TRUE then recurse on hashmap entries.
22168 * If the nesting limit is reached, this function abort immediately.
22169 */
22170static sxi64 HashmapCount(jx9_hashmap *pMap, int bRecursive, int iRecCount)
22171{
22172 sxi64 iCount = 0;
22173 if( !bRecursive ){
22174 iCount = pMap->nEntry;
22175 }else{
22176 /* Recursive hashmap walk */
22177 jx9_hashmap_node *pEntry = pMap->pLast;
22178 jx9_value *pElem;
22179 sxu32 n = 0;
22180 for(;;){
22181 if( n >= pMap->nEntry ){
22182 break;
22183 }
22184 /* Point to the element value */
22185 pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pEntry->nValIdx);
22186 if( pElem ){
22187 if( pElem->iFlags & MEMOBJ_HASHMAP ){
22188 if( iRecCount > 31 ){
22189 /* Nesting limit reached */
22190 return iCount;
22191 }
22192 /* Recurse */
22193 iRecCount++;
22194 iCount += HashmapCount((jx9_hashmap *)pElem->x.pOther, TRUE, iRecCount);
22195 iRecCount--;
22196 }
22197 }
22198 /* Point to the next entry */
22199 pEntry = pEntry->pNext;
22200 ++n;
22201 }
22202 /* Update count */
22203 iCount += pMap->nEntry;
22204 }
22205 return iCount;
22206}
22207/*
22208 * Allocate a new hashmap node with a 64-bit integer key.
22209 * If something goes wrong [i.e: out of memory], this function return NULL.
22210 * Otherwise a fresh [jx9_hashmap_node] instance is returned.
22211 */
22212static jx9_hashmap_node * HashmapNewIntNode(jx9_hashmap *pMap, sxi64 iKey, sxu32 nHash, sxu32 nValIdx)
22213{
22214 jx9_hashmap_node *pNode;
22215 /* Allocate a new node */
22216 pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node));
22217 if( pNode == 0 ){
22218 return 0;
22219 }
22220 /* Zero the stucture */
22221 SyZero(pNode, sizeof(jx9_hashmap_node));
22222 /* Fill in the structure */
22223 pNode->pMap = &(*pMap);
22224 pNode->iType = HASHMAP_INT_NODE;
22225 pNode->nHash = nHash;
22226 pNode->xKey.iKey = iKey;
22227 pNode->nValIdx = nValIdx;
22228 return pNode;
22229}
22230/*
22231 * Allocate a new hashmap node with a BLOB key.
22232 * If something goes wrong [i.e: out of memory], this function return NULL.
22233 * Otherwise a fresh [jx9_hashmap_node] instance is returned.
22234 */
22235static jx9_hashmap_node * HashmapNewBlobNode(jx9_hashmap *pMap, const void *pKey, sxu32 nKeyLen, sxu32 nHash, sxu32 nValIdx)
22236{
22237 jx9_hashmap_node *pNode;
22238 /* Allocate a new node */
22239 pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node));
22240 if( pNode == 0 ){
22241 return 0;
22242 }
22243 /* Zero the stucture */
22244 SyZero(pNode, sizeof(jx9_hashmap_node));
22245 /* Fill in the structure */
22246 pNode->pMap = &(*pMap);
22247 pNode->iType = HASHMAP_BLOB_NODE;
22248 pNode->nHash = nHash;
22249 SyBlobInit(&pNode->xKey.sKey, &pMap->pVm->sAllocator);
22250 SyBlobAppend(&pNode->xKey.sKey, pKey, nKeyLen);
22251 pNode->nValIdx = nValIdx;
22252 return pNode;
22253}
22254/*
22255 * link a hashmap node to the given bucket index (last argument to this function).
22256 */
22257static void HashmapNodeLink(jx9_hashmap *pMap, jx9_hashmap_node *pNode, sxu32 nBucketIdx)
22258{
22259 /* Link */
22260 if( pMap->apBucket[nBucketIdx] != 0 ){
22261 pNode->pNextCollide = pMap->apBucket[nBucketIdx];
22262 pMap->apBucket[nBucketIdx]->pPrevCollide = pNode;
22263 }
22264 pMap->apBucket[nBucketIdx] = pNode;
22265 /* Link to the map list */
22266 if( pMap->pFirst == 0 ){
22267 pMap->pFirst = pMap->pLast = pNode;
22268 /* Point to the first inserted node */
22269 pMap->pCur = pNode;
22270 }else{
22271 MACRO_LD_PUSH(pMap->pLast, pNode);
22272 }
22273 ++pMap->nEntry;
22274}
22275/*
22276 * Unlink a node from the hashmap.
22277 * If the node count reaches zero then release the whole hash-bucket.
22278 */
22279static void jx9HashmapUnlinkNode(jx9_hashmap_node *pNode)
22280{
22281 jx9_hashmap *pMap = pNode->pMap;
22282 jx9_vm *pVm = pMap->pVm;
22283 /* Unlink from the corresponding bucket */
22284 if( pNode->pPrevCollide == 0 ){
22285 pMap->apBucket[pNode->nHash & (pMap->nSize - 1)] = pNode->pNextCollide;
22286 }else{
22287 pNode->pPrevCollide->pNextCollide = pNode->pNextCollide;
22288 }
22289 if( pNode->pNextCollide ){
22290 pNode->pNextCollide->pPrevCollide = pNode->pPrevCollide;
22291 }
22292 if( pMap->pFirst == pNode ){
22293 pMap->pFirst = pNode->pPrev;
22294 }
22295 if( pMap->pCur == pNode ){
22296 /* Advance the node cursor */
22297 pMap->pCur = pMap->pCur->pPrev; /* Reverse link */
22298 }
22299 /* Unlink from the map list */
22300 MACRO_LD_REMOVE(pMap->pLast, pNode);
22301 /* Restore to the free list */
22302 jx9VmUnsetMemObj(pVm, pNode->nValIdx);
22303 if( pNode->iType == HASHMAP_BLOB_NODE ){
22304 SyBlobRelease(&pNode->xKey.sKey);
22305 }
22306 SyMemBackendPoolFree(&pVm->sAllocator, pNode);
22307 pMap->nEntry--;
22308 if( pMap->nEntry < 1 ){
22309 /* Free the hash-bucket */
22310 SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
22311 pMap->apBucket = 0;
22312 pMap->nSize = 0;
22313 pMap->pFirst = pMap->pLast = pMap->pCur = 0;
22314 }
22315}
22316#define HASHMAP_FILL_FACTOR 3
22317/*
22318 * Grow the hash-table and rehash all entries.
22319 */
22320static sxi32 HashmapGrowBucket(jx9_hashmap *pMap)
22321{
22322 if( pMap->nEntry >= pMap->nSize * HASHMAP_FILL_FACTOR ){
22323 jx9_hashmap_node **apOld = pMap->apBucket;
22324 jx9_hashmap_node *pEntry, **apNew;
22325 sxu32 nNew = pMap->nSize << 1;
22326 sxu32 nBucket;
22327 sxu32 n;
22328 if( nNew < 1 ){
22329 nNew = 16;
22330 }
22331 /* Allocate a new bucket */
22332 apNew = (jx9_hashmap_node **)SyMemBackendAlloc(&pMap->pVm->sAllocator, nNew * sizeof(jx9_hashmap_node *));
22333 if( apNew == 0 ){
22334 if( pMap->nSize < 1 ){
22335 return SXERR_MEM; /* Fatal */
22336 }
22337 /* Not so fatal here, simply a performance hit */
22338 return SXRET_OK;
22339 }
22340 /* Zero the table */
22341 SyZero((void *)apNew, nNew * sizeof(jx9_hashmap_node *));
22342 /* Reflect the change */
22343 pMap->apBucket = apNew;
22344 pMap->nSize = nNew;
22345 if( apOld == 0 ){
22346 /* First allocated table [i.e: no entry], return immediately */
22347 return SXRET_OK;
22348 }
22349 /* Rehash old entries */
22350 pEntry = pMap->pFirst;
22351 n = 0;
22352 for( ;; ){
22353 if( n >= pMap->nEntry ){
22354 break;
22355 }
22356 /* Clear the old collision link */
22357 pEntry->pNextCollide = pEntry->pPrevCollide = 0;
22358 /* Link to the new bucket */
22359 nBucket = pEntry->nHash & (nNew - 1);
22360 if( pMap->apBucket[nBucket] != 0 ){
22361 pEntry->pNextCollide = pMap->apBucket[nBucket];
22362 pMap->apBucket[nBucket]->pPrevCollide = pEntry;
22363 }
22364 pMap->apBucket[nBucket] = pEntry;
22365 /* Point to the next entry */
22366 pEntry = pEntry->pPrev; /* Reverse link */
22367 n++;
22368 }
22369 /* Free the old table */
22370 SyMemBackendFree(&pMap->pVm->sAllocator, (void *)apOld);
22371 }
22372 return SXRET_OK;
22373}
22374/*
22375 * Insert a 64-bit integer key and it's associated value (if any) in the given
22376 * hashmap.
22377 */
22378static sxi32 HashmapInsertIntKey(jx9_hashmap *pMap,sxi64 iKey,jx9_value *pValue)
22379{
22380 jx9_hashmap_node *pNode;
22381 jx9_value *pObj;
22382 sxu32 nIdx;
22383 sxu32 nHash;
22384 sxi32 rc;
22385 /* Reserve a jx9_value for the value */
22386 pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx);
22387 if( pObj == 0 ){
22388 return SXERR_MEM;
22389 }
22390 if( pValue ){
22391 /* Duplicate the value */
22392 jx9MemObjStore(pValue, pObj);
22393 }
22394 /* Hash the key */
22395 nHash = pMap->xIntHash(iKey);
22396 /* Allocate a new int node */
22397 pNode = HashmapNewIntNode(&(*pMap), iKey, nHash, nIdx);
22398 if( pNode == 0 ){
22399 return SXERR_MEM;
22400 }
22401 /* Make sure the bucket is big enough to hold the new entry */
22402 rc = HashmapGrowBucket(&(*pMap));
22403 if( rc != SXRET_OK ){
22404 SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
22405 return rc;
22406 }
22407 /* Perform the insertion */
22408 HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
22409 /* All done */
22410 return SXRET_OK;
22411}
22412/*
22413 * Insert a BLOB key and it's associated value (if any) in the given
22414 * hashmap.
22415 */
22416static sxi32 HashmapInsertBlobKey(jx9_hashmap *pMap,const void *pKey,sxu32 nKeyLen,jx9_value *pValue)
22417{
22418 jx9_hashmap_node *pNode;
22419 jx9_value *pObj;
22420 sxu32 nHash;
22421 sxu32 nIdx;
22422 sxi32 rc;
22423 /* Reserve a jx9_value for the value */
22424 pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx);
22425 if( pObj == 0 ){
22426 return SXERR_MEM;
22427 }
22428 if( pValue ){
22429 /* Duplicate the value */
22430 jx9MemObjStore(pValue, pObj);
22431 }
22432 /* Hash the key */
22433 nHash = pMap->xBlobHash(pKey, nKeyLen);
22434 /* Allocate a new blob node */
22435 pNode = HashmapNewBlobNode(&(*pMap), pKey, nKeyLen, nHash, nIdx);
22436 if( pNode == 0 ){
22437 return SXERR_MEM;
22438 }
22439 /* Make sure the bucket is big enough to hold the new entry */
22440 rc = HashmapGrowBucket(&(*pMap));
22441 if( rc != SXRET_OK ){
22442 SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
22443 return rc;
22444 }
22445 /* Perform the insertion */
22446 HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
22447 /* All done */
22448 return SXRET_OK;
22449}
22450/*
22451 * Check if a given 64-bit integer key exists in the given hashmap.
22452 * Write a pointer to the target node on success. Otherwise
22453 * SXERR_NOTFOUND is returned on failure.
22454 */
22455static sxi32 HashmapLookupIntKey(
22456 jx9_hashmap *pMap, /* Target hashmap */
22457 sxi64 iKey, /* lookup key */
22458 jx9_hashmap_node **ppNode /* OUT: target node on success */
22459 )
22460{
22461 jx9_hashmap_node *pNode;
22462 sxu32 nHash;
22463 if( pMap->nEntry < 1 ){
22464 /* Don't bother hashing, there is no entry anyway */
22465 return SXERR_NOTFOUND;
22466 }
22467 /* Hash the key first */
22468 nHash = pMap->xIntHash(iKey);
22469 /* Point to the appropriate bucket */
22470 pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
22471 /* Perform the lookup */
22472 for(;;){
22473 if( pNode == 0 ){
22474 break;
22475 }
22476 if( pNode->iType == HASHMAP_INT_NODE
22477 && pNode->nHash == nHash
22478 && pNode->xKey.iKey == iKey ){
22479 /* Node found */
22480 if( ppNode ){
22481 *ppNode = pNode;
22482 }
22483 return SXRET_OK;
22484 }
22485 /* Follow the collision link */
22486 pNode = pNode->pNextCollide;
22487 }
22488 /* No such entry */
22489 return SXERR_NOTFOUND;
22490}
22491/*
22492 * Check if a given BLOB key exists in the given hashmap.
22493 * Write a pointer to the target node on success. Otherwise
22494 * SXERR_NOTFOUND is returned on failure.
22495 */
22496static sxi32 HashmapLookupBlobKey(
22497 jx9_hashmap *pMap, /* Target hashmap */
22498 const void *pKey, /* Lookup key */
22499 sxu32 nKeyLen, /* Key length in bytes */
22500 jx9_hashmap_node **ppNode /* OUT: target node on success */
22501 )
22502{
22503 jx9_hashmap_node *pNode;
22504 sxu32 nHash;
22505 if( pMap->nEntry < 1 ){
22506 /* Don't bother hashing, there is no entry anyway */
22507 return SXERR_NOTFOUND;
22508 }
22509 /* Hash the key first */
22510 nHash = pMap->xBlobHash(pKey, nKeyLen);
22511 /* Point to the appropriate bucket */
22512 pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
22513 /* Perform the lookup */
22514 for(;;){
22515 if( pNode == 0 ){
22516 break;
22517 }
22518 if( pNode->iType == HASHMAP_BLOB_NODE
22519 && pNode->nHash == nHash
22520 && SyBlobLength(&pNode->xKey.sKey) == nKeyLen
22521 && SyMemcmp(SyBlobData(&pNode->xKey.sKey), pKey, nKeyLen) == 0 ){
22522 /* Node found */
22523 if( ppNode ){
22524 *ppNode = pNode;
22525 }
22526 return SXRET_OK;
22527 }
22528 /* Follow the collision link */
22529 pNode = pNode->pNextCollide;
22530 }
22531 /* No such entry */
22532 return SXERR_NOTFOUND;
22533}
22534/*
22535 * Check if the given BLOB key looks like a decimal number.
22536 * Retrurn TRUE on success.FALSE otherwise.
22537 */
22538static int HashmapIsIntKey(SyBlob *pKey)
22539{
22540 const char *zIn = (const char *)SyBlobData(pKey);
22541 const char *zEnd = &zIn[SyBlobLength(pKey)];
22542 if( (int)(zEnd-zIn) > 1 && zIn[0] == '0' ){
22543 /* Octal not decimal number */
22544 return FALSE;
22545 }
22546 if( (zIn[0] == '-' || zIn[0] == '+') && &zIn[1] < zEnd ){
22547 zIn++;
22548 }
22549 for(;;){
22550 if( zIn >= zEnd ){
22551 return TRUE;
22552 }
22553 if( (unsigned char)zIn[0] >= 0xc0 /* UTF-8 stream */ || !SyisDigit(zIn[0]) ){
22554 break;
22555 }
22556 zIn++;
22557 }
22558 /* Key does not look like a decimal number */
22559 return FALSE;
22560}
22561/*
22562 * Check if a given key exists in the given hashmap.
22563 * Write a pointer to the target node on success.
22564 * Otherwise SXERR_NOTFOUND is returned on failure.
22565 */
22566static sxi32 HashmapLookup(
22567 jx9_hashmap *pMap, /* Target hashmap */
22568 jx9_value *pKey, /* Lookup key */
22569 jx9_hashmap_node **ppNode /* OUT: target node on success */
22570 )
22571{
22572 jx9_hashmap_node *pNode = 0; /* cc -O6 warning */
22573 sxi32 rc;
22574 if( pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES) ){
22575 if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){
22576 /* Force a string cast */
22577 jx9MemObjToString(&(*pKey));
22578 }
22579 if( SyBlobLength(&pKey->sBlob) > 0 ){
22580 /* Perform a blob lookup */
22581 rc = HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &pNode);
22582 goto result;
22583 }
22584 }
22585 /* Perform an int lookup */
22586 if((pKey->iFlags & MEMOBJ_INT) == 0 ){
22587 /* Force an integer cast */
22588 jx9MemObjToInteger(pKey);
22589 }
22590 /* Perform an int lookup */
22591 rc = HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode);
22592result:
22593 if( rc == SXRET_OK ){
22594 /* Node found */
22595 if( ppNode ){
22596 *ppNode = pNode;
22597 }
22598 return SXRET_OK;
22599 }
22600 /* No such entry */
22601 return SXERR_NOTFOUND;
22602}
22603/*
22604 * Insert a given key and it's associated value (if any) in the given
22605 * hashmap.
22606 * If a node with the given key already exists in the database
22607 * then this function overwrite the old value.
22608 */
22609static sxi32 HashmapInsert(
22610 jx9_hashmap *pMap, /* Target hashmap */
22611 jx9_value *pKey, /* Lookup key */
22612 jx9_value *pVal /* Node value */
22613 )
22614{
22615 jx9_hashmap_node *pNode = 0;
22616 sxi32 rc = SXRET_OK;
22617 if( pMap->nEntry < 1 && pKey && (pKey->iFlags & MEMOBJ_STRING) ){
22618 pMap->iFlags |= HASHMAP_JSON_OBJECT;
22619 }
22620 if( pKey && (pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES)) ){
22621 if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){
22622 /* Force a string cast */
22623 jx9MemObjToString(&(*pKey));
22624 }
22625 if( SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob) ){
22626 if(SyBlobLength(&pKey->sBlob) < 1){
22627 /* Automatic index assign */
22628 pKey = 0;
22629 }
22630 goto IntKey;
22631 }
22632 if( SXRET_OK == HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob),
22633 SyBlobLength(&pKey->sBlob), &pNode) ){
22634 /* Overwrite the old value */
22635 jx9_value *pElem;
22636 pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
22637 if( pElem ){
22638 if( pVal ){
22639 jx9MemObjStore(pVal, pElem);
22640 }else{
22641 /* Nullify the entry */
22642 jx9MemObjToNull(pElem);
22643 }
22644 }
22645 return SXRET_OK;
22646 }
22647 /* Perform a blob-key insertion */
22648 rc = HashmapInsertBlobKey(&(*pMap),SyBlobData(&pKey->sBlob),SyBlobLength(&pKey->sBlob),&(*pVal));
22649 return rc;
22650 }
22651IntKey:
22652 if( pKey ){
22653 if((pKey->iFlags & MEMOBJ_INT) == 0 ){
22654 /* Force an integer cast */
22655 jx9MemObjToInteger(pKey);
22656 }
22657 if( SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode) ){
22658 /* Overwrite the old value */
22659 jx9_value *pElem;
22660 pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
22661 if( pElem ){
22662 if( pVal ){
22663 jx9MemObjStore(pVal, pElem);
22664 }else{
22665 /* Nullify the entry */
22666 jx9MemObjToNull(pElem);
22667 }
22668 }
22669 return SXRET_OK;
22670 }
22671 /* Perform a 64-bit-int-key insertion */
22672 rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal));
22673 if( rc == SXRET_OK ){
22674 if( pKey->x.iVal >= pMap->iNextIdx ){
22675 /* Increment the automatic index */
22676 pMap->iNextIdx = pKey->x.iVal + 1;
22677 /* Make sure the automatic index is not reserved */
22678 while( SXRET_OK == HashmapLookupIntKey(&(*pMap), pMap->iNextIdx, 0) ){
22679 pMap->iNextIdx++;
22680 }
22681 }
22682 }
22683 }else{
22684 /* Assign an automatic index */
22685 rc = HashmapInsertIntKey(&(*pMap),pMap->iNextIdx,&(*pVal));
22686 if( rc == SXRET_OK ){
22687 ++pMap->iNextIdx;
22688 }
22689 }
22690 /* Insertion result */
22691 return rc;
22692}
22693/*
22694 * Extract node value.
22695 */
22696static jx9_value * HashmapExtractNodeValue(jx9_hashmap_node *pNode)
22697{
22698 /* Point to the desired object */
22699 jx9_value *pObj;
22700 pObj = (jx9_value *)SySetAt(&pNode->pMap->pVm->aMemObj, pNode->nValIdx);
22701 return pObj;
22702}
22703/*
22704 * Insert a node in the given hashmap.
22705 * If a node with the given key already exists in the database
22706 * then this function overwrite the old value.
22707 */
22708static sxi32 HashmapInsertNode(jx9_hashmap *pMap, jx9_hashmap_node *pNode, int bPreserve)
22709{
22710 jx9_value *pObj;
22711 sxi32 rc;
22712 /* Extract the node value */
22713 pObj = HashmapExtractNodeValue(&(*pNode));
22714 if( pObj == 0 ){
22715 return SXERR_EMPTY;
22716 }
22717 /* Preserve key */
22718 if( pNode->iType == HASHMAP_INT_NODE){
22719 /* Int64 key */
22720 if( !bPreserve ){
22721 /* Assign an automatic index */
22722 rc = HashmapInsert(&(*pMap), 0, pObj);
22723 }else{
22724 rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj);
22725 }
22726 }else{
22727 /* Blob key */
22728 rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey),
22729 SyBlobLength(&pNode->xKey.sKey), pObj);
22730 }
22731 return rc;
22732}
22733/*
22734 * Compare two node values.
22735 * Return 0 if the node values are equals, > 0 if pLeft is greater than pRight
22736 * or < 0 if pRight is greater than pLeft.
22737 * For a full description on jx9_values comparison, refer to the implementation
22738 * of the [jx9MemObjCmp()] function defined in memobj.c or the official
22739 * documenation.
22740 */
22741static sxi32 HashmapNodeCmp(jx9_hashmap_node *pLeft, jx9_hashmap_node *pRight, int bStrict)
22742{
22743 jx9_value sObj1, sObj2;
22744 sxi32 rc;
22745 if( pLeft == pRight ){
22746 /*
22747 * Same node.Refer to the sort() implementation defined
22748 * below for more information on this sceanario.
22749 */
22750 return 0;
22751 }
22752 /* Do the comparison */
22753 jx9MemObjInit(pLeft->pMap->pVm, &sObj1);
22754 jx9MemObjInit(pLeft->pMap->pVm, &sObj2);
22755 jx9HashmapExtractNodeValue(pLeft, &sObj1, FALSE);
22756 jx9HashmapExtractNodeValue(pRight, &sObj2, FALSE);
22757 rc = jx9MemObjCmp(&sObj1, &sObj2, bStrict, 0);
22758 jx9MemObjRelease(&sObj1);
22759 jx9MemObjRelease(&sObj2);
22760 return rc;
22761}
22762/*
22763 * Rehash a node with a 64-bit integer key.
22764 * Refer to [merge_sort(), array_shift()] implementations for more information.
22765 */
22766static void HashmapRehashIntNode(jx9_hashmap_node *pEntry)
22767{
22768 jx9_hashmap *pMap = pEntry->pMap;
22769 sxu32 nBucket;
22770 /* Remove old collision links */
22771 if( pEntry->pPrevCollide ){
22772 pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
22773 }else{
22774 pMap->apBucket[pEntry->nHash & (pMap->nSize - 1)] = pEntry->pNextCollide;
22775 }
22776 if( pEntry->pNextCollide ){
22777 pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
22778 }
22779 pEntry->pNextCollide = pEntry->pPrevCollide = 0;
22780 /* Compute the new hash */
22781 pEntry->nHash = pMap->xIntHash(pMap->iNextIdx);
22782 pEntry->xKey.iKey = pMap->iNextIdx;
22783 nBucket = pEntry->nHash & (pMap->nSize - 1);
22784 /* Link to the new bucket */
22785 pEntry->pNextCollide = pMap->apBucket[nBucket];
22786 if( pMap->apBucket[nBucket] ){
22787 pMap->apBucket[nBucket]->pPrevCollide = pEntry;
22788 }
22789 pEntry->pNextCollide = pMap->apBucket[nBucket];
22790 pMap->apBucket[nBucket] = pEntry;
22791 /* Increment the automatic index */
22792 pMap->iNextIdx++;
22793}
22794/*
22795 * Perform a linear search on a given hashmap.
22796 * Write a pointer to the target node on success.
22797 * Otherwise SXERR_NOTFOUND is returned on failure.
22798 * Refer to [array_intersect(), array_diff(), in_array(), ...] implementations
22799 * for more information.
22800 */
22801static int HashmapFindValue(
22802 jx9_hashmap *pMap, /* Target hashmap */
22803 jx9_value *pNeedle, /* Lookup key */
22804 jx9_hashmap_node **ppNode, /* OUT: target node on success */
22805 int bStrict /* TRUE for strict comparison */
22806 )
22807{
22808 jx9_hashmap_node *pEntry;
22809 jx9_value sVal, *pVal;
22810 jx9_value sNeedle;
22811 sxi32 rc;
22812 sxu32 n;
22813 /* Perform a linear search since we cannot sort the hashmap based on values */
22814 pEntry = pMap->pFirst;
22815 n = pMap->nEntry;
22816 jx9MemObjInit(pMap->pVm, &sVal);
22817 jx9MemObjInit(pMap->pVm, &sNeedle);
22818 for(;;){
22819 if( n < 1 ){
22820 break;
22821 }
22822 /* Extract node value */
22823 pVal = HashmapExtractNodeValue(pEntry);
22824 if( pVal ){
22825 if( (pVal->iFlags|pNeedle->iFlags) & MEMOBJ_NULL ){
22826 sxi32 iF1 = pVal->iFlags;
22827 sxi32 iF2 = pNeedle->iFlags;
22828 if( iF1 == iF2 ){
22829 /* NULL values are equals */
22830 if( ppNode ){
22831 *ppNode = pEntry;
22832 }
22833 return SXRET_OK;
22834 }
22835 }else{
22836 /* Duplicate value */
22837 jx9MemObjLoad(pVal, &sVal);
22838 jx9MemObjLoad(pNeedle, &sNeedle);
22839 rc = jx9MemObjCmp(&sNeedle, &sVal, bStrict, 0);
22840 jx9MemObjRelease(&sVal);
22841 jx9MemObjRelease(&sNeedle);
22842 if( rc == 0 ){
22843 if( ppNode ){
22844 *ppNode = pEntry;
22845 }
22846 /* Match found*/
22847 return SXRET_OK;
22848 }
22849 }
22850 }
22851 /* Point to the next entry */
22852 pEntry = pEntry->pPrev; /* Reverse link */
22853 n--;
22854 }
22855 /* No such entry */
22856 return SXERR_NOTFOUND;
22857}
22858/*
22859 * Compare two hashmaps.
22860 * Return 0 if the hashmaps are equals.Any other value indicates inequality.
22861 * Note on array comparison operators.
22862 * According to the JX9 language reference manual.
22863 * Array Operators Example Name Result
22864 * $a + $b Union Union of $a and $b.
22865 * $a == $b Equality TRUE if $a and $b have the same key/value pairs.
22866 * $a === $b Identity TRUE if $a and $b have the same key/value pairs in the same
22867 * order and of the same types.
22868 * $a != $b Inequality TRUE if $a is not equal to $b.
22869 * $a <> $b Inequality TRUE if $a is not equal to $b.
22870 * $a !== $b Non-identity TRUE if $a is not identical to $b.
22871 * The + operator returns the right-hand array appended to the left-hand array;
22872 * For keys that exist in both arrays, the elements from the left-hand array will be used
22873 * and the matching elements from the right-hand array will be ignored.
22874 * <?jx9
22875 * $a = array("a" => "apple", "b" => "banana");
22876 * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
22877 * $c = $a + $b; // Union of $a and $b
22878 * print "Union of \$a and \$b: \n";
22879 * dump($c);
22880 * $c = $b + $a; // Union of $b and $a
22881 * print "Union of \$b and \$a: \n";
22882 * dump($c);
22883 * ?>
22884 * When executed, this script will print the following:
22885 * Union of $a and $b:
22886 * array(3) {
22887 * ["a"]=>
22888 * string(5) "apple"
22889 * ["b"]=>
22890 * string(6) "banana"
22891 * ["c"]=>
22892 * string(6) "cherry"
22893 * }
22894 * Union of $b and $a:
22895 * array(3) {
22896 * ["a"]=>
22897 * string(4) "pear"
22898 * ["b"]=>
22899 * string(10) "strawberry"
22900 * ["c"]=>
22901 * string(6) "cherry"
22902 * }
22903 * Elements of arrays are equal for the comparison if they have the same key and value.
22904 */
22905JX9_PRIVATE sxi32 jx9HashmapCmp(
22906 jx9_hashmap *pLeft, /* Left hashmap */
22907 jx9_hashmap *pRight, /* Right hashmap */
22908 int bStrict /* TRUE for strict comparison */
22909 )
22910{
22911 jx9_hashmap_node *pLe, *pRe;
22912 sxi32 rc;
22913 sxu32 n;
22914 if( pLeft == pRight ){
22915 /* Same hashmap instance. This can easily happen since hashmaps are passed by reference.
22916 * Unlike the engine.
22917 */
22918 return 0;
22919 }
22920 if( pLeft->nEntry != pRight->nEntry ){
22921 /* Must have the same number of entries */
22922 return pLeft->nEntry > pRight->nEntry ? 1 : -1;
22923 }
22924 /* Point to the first inserted entry of the left hashmap */
22925 pLe = pLeft->pFirst;
22926 pRe = 0; /* cc warning */
22927 /* Perform the comparison */
22928 n = pLeft->nEntry;
22929 for(;;){
22930 if( n < 1 ){
22931 break;
22932 }
22933 if( pLe->iType == HASHMAP_INT_NODE){
22934 /* Int key */
22935 rc = HashmapLookupIntKey(&(*pRight), pLe->xKey.iKey, &pRe);
22936 }else{
22937 SyBlob *pKey = &pLe->xKey.sKey;
22938 /* Blob key */
22939 rc = HashmapLookupBlobKey(&(*pRight), SyBlobData(pKey), SyBlobLength(pKey), &pRe);
22940 }
22941 if( rc != SXRET_OK ){
22942 /* No such entry in the right side */
22943 return 1;
22944 }
22945 rc = 0;
22946 if( bStrict ){
22947 /* Make sure, the keys are of the same type */
22948 if( pLe->iType != pRe->iType ){
22949 rc = 1;
22950 }
22951 }
22952 if( !rc ){
22953 /* Compare nodes */
22954 rc = HashmapNodeCmp(pLe, pRe, bStrict);
22955 }
22956 if( rc != 0 ){
22957 /* Nodes key/value differ */
22958 return rc;
22959 }
22960 /* Point to the next entry */
22961 pLe = pLe->pPrev; /* Reverse link */
22962 n--;
22963 }
22964 return 0; /* Hashmaps are equals */
22965}
22966/*
22967 * Merge two hashmaps.
22968 * Note on the merge process
22969 * According to the JX9 language reference manual.
22970 * Merges the elements of two arrays together so that the values of one are appended
22971 * to the end of the previous one. It returns the resulting array (pDest).
22972 * If the input arrays have the same string keys, then the later value for that key
22973 * will overwrite the previous one. If, however, the arrays contain numeric keys
22974 * the later value will not overwrite the original value, but will be appended.
22975 * Values in the input array with numeric keys will be renumbered with incrementing
22976 * keys starting from zero in the result array.
22977 */
22978static sxi32 HashmapMerge(jx9_hashmap *pSrc, jx9_hashmap *pDest)
22979{
22980 jx9_hashmap_node *pEntry;
22981 jx9_value sKey, *pVal;
22982 sxi32 rc;
22983 sxu32 n;
22984 if( pSrc == pDest ){
22985 /* Same map. This can easily happen since hashmaps are passed by reference.
22986 * Unlike the engine.
22987 */
22988 return SXRET_OK;
22989 }
22990 /* Point to the first inserted entry in the source */
22991 pEntry = pSrc->pFirst;
22992 /* Perform the merge */
22993 for( n = 0 ; n < pSrc->nEntry ; ++n ){
22994 /* Extract the node value */
22995 pVal = HashmapExtractNodeValue(pEntry);
22996 if( pEntry->iType == HASHMAP_BLOB_NODE ){
22997 /* Blob key insertion */
22998 jx9MemObjInitFromString(pDest->pVm, &sKey, 0);
22999 jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
23000 rc = jx9HashmapInsert(&(*pDest), &sKey, pVal);
23001 jx9MemObjRelease(&sKey);
23002 }else{
23003 rc = HashmapInsert(&(*pDest), 0/* Automatic index assign */, pVal);
23004 }
23005 if( rc != SXRET_OK ){
23006 return rc;
23007 }
23008 /* Point to the next entry */
23009 pEntry = pEntry->pPrev; /* Reverse link */
23010 }
23011 return SXRET_OK;
23012}
23013/*
23014 * Duplicate the contents of a hashmap. Store the copy in pDest.
23015 * Refer to the [array_pad(), array_copy(), ...] implementation for more information.
23016 */
23017JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest)
23018{
23019 jx9_hashmap_node *pEntry;
23020 jx9_value sKey, *pVal;
23021 sxi32 rc;
23022 sxu32 n;
23023 if( pSrc == pDest ){
23024 /* Same map. This can easily happen since hashmaps are passed by reference.
23025 * Unlike the engine.
23026 */
23027 return SXRET_OK;
23028 }
23029 /* Point to the first inserted entry in the source */
23030 pEntry = pSrc->pFirst;
23031 /* Perform the duplication */
23032 for( n = 0 ; n < pSrc->nEntry ; ++n ){
23033 /* Extract the node value */
23034 pVal = HashmapExtractNodeValue(pEntry);
23035 if( pEntry->iType == HASHMAP_BLOB_NODE ){
23036 /* Blob key insertion */
23037 jx9MemObjInitFromString(pDest->pVm, &sKey, 0);
23038 jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
23039 rc = jx9HashmapInsert(&(*pDest), &sKey, pVal);
23040 jx9MemObjRelease(&sKey);
23041 }else{
23042 /* Int key insertion */
23043 rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal);
23044 }
23045 if( rc != SXRET_OK ){
23046 return rc;
23047 }
23048 /* Point to the next entry */
23049 pEntry = pEntry->pPrev; /* Reverse link */
23050 }
23051 return SXRET_OK;
23052}
23053/*
23054 * Perform the union of two hashmaps.
23055 * This operation is performed only if the user uses the '+' operator
23056 * with a variable holding an array as follows:
23057 * <?jx9
23058 * $a = array("a" => "apple", "b" => "banana");
23059 * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
23060 * $c = $a + $b; // Union of $a and $b
23061 * print "Union of \$a and \$b: \n";
23062 * dump($c);
23063 * $c = $b + $a; // Union of $b and $a
23064 * print "Union of \$b and \$a: \n";
23065 * dump($c);
23066 * ?>
23067 * When executed, this script will print the following:
23068 * Union of $a and $b:
23069 * array(3) {
23070 * ["a"]=>
23071 * string(5) "apple"
23072 * ["b"]=>
23073 * string(6) "banana"
23074 * ["c"]=>
23075 * string(6) "cherry"
23076 * }
23077 * Union of $b and $a:
23078 * array(3) {
23079 * ["a"]=>
23080 * string(4) "pear"
23081 * ["b"]=>
23082 * string(10) "strawberry"
23083 * ["c"]=>
23084 * string(6) "cherry"
23085 * }
23086 * The + operator returns the right-hand array appended to the left-hand array;
23087 * For keys that exist in both arrays, the elements from the left-hand array will be used
23088 * and the matching elements from the right-hand array will be ignored.
23089 */
23090JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight)
23091{
23092 jx9_hashmap_node *pEntry;
23093 sxi32 rc = SXRET_OK;
23094 jx9_value *pObj;
23095 sxu32 n;
23096 if( pLeft == pRight ){
23097 /* Same map. This can easily happen since hashmaps are passed by reference.
23098 * Unlike the engine.
23099 */
23100 return SXRET_OK;
23101 }
23102 /* Perform the union */
23103 pEntry = pRight->pFirst;
23104 for(n = 0 ; n < pRight->nEntry ; ++n ){
23105 /* Make sure the given key does not exists in the left array */
23106 if( pEntry->iType == HASHMAP_BLOB_NODE ){
23107 /* BLOB key */
23108 if( SXRET_OK !=
23109 HashmapLookupBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), 0) ){
23110 pObj = HashmapExtractNodeValue(pEntry);
23111 if( pObj ){
23112 /* Perform the insertion */
23113 rc = HashmapInsertBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey),
23114 SyBlobLength(&pEntry->xKey.sKey),pObj);
23115 if( rc != SXRET_OK ){
23116 return rc;
23117 }
23118 }
23119 }
23120 }else{
23121 /* INT key */
23122 if( SXRET_OK != HashmapLookupIntKey(&(*pLeft), pEntry->xKey.iKey, 0) ){
23123 pObj = HashmapExtractNodeValue(pEntry);
23124 if( pObj ){
23125 /* Perform the insertion */
23126 rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj);
23127 if( rc != SXRET_OK ){
23128 return rc;
23129 }
23130 }
23131 }
23132 }
23133 /* Point to the next entry */
23134 pEntry = pEntry->pPrev; /* Reverse link */
23135 }
23136 return SXRET_OK;
23137}
23138/*
23139 * Allocate a new hashmap.
23140 * Return a pointer to the freshly allocated hashmap on success.NULL otherwise.
23141 */
23142JX9_PRIVATE jx9_hashmap * jx9NewHashmap(
23143 jx9_vm *pVm, /* VM that trigger the hashmap creation */
23144 sxu32 (*xIntHash)(sxi64), /* Hash function for int keys.NULL otherwise*/
23145 sxu32 (*xBlobHash)(const void *, sxu32) /* Hash function for BLOB keys.NULL otherwise */
23146 )
23147{
23148 jx9_hashmap *pMap;
23149 /* Allocate a new instance */
23150 pMap = (jx9_hashmap *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_hashmap));
23151 if( pMap == 0 ){
23152 return 0;
23153 }
23154 /* Zero the structure */
23155 SyZero(pMap, sizeof(jx9_hashmap));
23156 /* Fill in the structure */
23157 pMap->pVm = &(*pVm);
23158 pMap->iRef = 1;
23159 /* pMap->iFlags = 0; */
23160 /* Default hash functions */
23161 pMap->xIntHash = xIntHash ? xIntHash : IntHash;
23162 pMap->xBlobHash = xBlobHash ? xBlobHash : BinHash;
23163 return pMap;
23164}
23165/*
23166 * Install superglobals in the given virtual machine.
23167 * Note on superglobals.
23168 * According to the JX9 language reference manual.
23169 * Superglobals are built-in variables that are always available in all scopes.
23170* Description
23171* All predefined variables in JX9 are "superglobals", which means they
23172* are available in all scopes throughout a script.
23173* These variables are:
23174* $_SERVER
23175* $_GET
23176* $_POST
23177* $_FILES
23178* $_REQUEST
23179* $_ENV
23180*/
23181JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm)
23182{
23183 static const char * azSuper[] = {
23184 "_SERVER", /* $_SERVER */
23185 "_GET", /* $_GET */
23186 "_POST", /* $_POST */
23187 "_FILES", /* $_FILES */
23188 "_REQUEST", /* $_REQUEST */
23189 "_COOKIE", /* $_COOKIE */
23190 "_ENV", /* $_ENV */
23191 "_HEADER", /* $_HEADER */
23192 "argv" /* $argv */
23193 };
23194 SyString *pFile;
23195 sxi32 rc;
23196 sxu32 n;
23197 /* Install globals variable now */
23198 for( n = 0 ; n < SX_ARRAYSIZE(azSuper) ; n++ ){
23199 jx9_value *pSuper;
23200 /* Request an empty array */
23201 pSuper = jx9_new_array(&(*pVm));
23202 if( pSuper == 0 ){
23203 return SXERR_MEM;
23204 }
23205 /* Install */
23206 rc = jx9_vm_config(&(*pVm),JX9_VM_CONFIG_CREATE_VAR, azSuper[n]/* Super-global name*/, pSuper/* Super-global value */);
23207 if( rc != SXRET_OK ){
23208 return rc;
23209 }
23210 /* Release the value now it have been installed */
23211 jx9_release_value(&(*pVm), pSuper);
23212 }
23213 /* Set some $_SERVER entries */
23214 pFile = (SyString *)SySetPeek(&pVm->aFiles);
23215 /*
23216 * 'SCRIPT_FILENAME'
23217 * The absolute pathname of the currently executing script.
23218 */
23219 jx9_vm_config(pVm, JX9_VM_CONFIG_SERVER_ATTR,
23220 "SCRIPT_FILENAME",
23221 pFile ? pFile->zString : ":Memory:",
23222 pFile ? pFile->nByte : sizeof(":Memory:") - 1
23223 );
23224 /* All done, all global variables are installed now */
23225 return SXRET_OK;
23226}
23227/*
23228 * Release a hashmap.
23229 */
23230JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS)
23231{
23232 jx9_hashmap_node *pEntry, *pNext;
23233 jx9_vm *pVm = pMap->pVm;
23234 sxu32 n;
23235 /* Start the release process */
23236 n = 0;
23237 pEntry = pMap->pFirst;
23238 for(;;){
23239 if( n >= pMap->nEntry ){
23240 break;
23241 }
23242 pNext = pEntry->pPrev; /* Reverse link */
23243 /* Restore the jx9_value to the free list */
23244 jx9VmUnsetMemObj(pVm, pEntry->nValIdx);
23245 /* Release the node */
23246 if( pEntry->iType == HASHMAP_BLOB_NODE ){
23247 SyBlobRelease(&pEntry->xKey.sKey);
23248 }
23249 SyMemBackendPoolFree(&pVm->sAllocator, pEntry);
23250 /* Point to the next entry */
23251 pEntry = pNext;
23252 n++;
23253 }
23254 if( pMap->nEntry > 0 ){
23255 /* Release the hash bucket */
23256 SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
23257 }
23258 if( FreeDS ){
23259 /* Free the whole instance */
23260 SyMemBackendPoolFree(&pVm->sAllocator, pMap);
23261 }else{
23262 /* Keep the instance but reset it's fields */
23263 pMap->apBucket = 0;
23264 pMap->iNextIdx = 0;
23265 pMap->nEntry = pMap->nSize = 0;
23266 pMap->pFirst = pMap->pLast = pMap->pCur = 0;
23267 }
23268 return SXRET_OK;
23269}
23270/*
23271 * Decrement the reference count of a given hashmap.
23272 * If the count reaches zero which mean no more variables
23273 * are pointing to this hashmap, then release the whole instance.
23274 */
23275JX9_PRIVATE void jx9HashmapUnref(jx9_hashmap *pMap)
23276{
23277 pMap->iRef--;
23278 if( pMap->iRef < 1 ){
23279 jx9HashmapRelease(pMap, TRUE);
23280 }
23281}
23282/*
23283 * Check if a given key exists in the given hashmap.
23284 * Write a pointer to the target node on success.
23285 * Otherwise SXERR_NOTFOUND is returned on failure.
23286 */
23287JX9_PRIVATE sxi32 jx9HashmapLookup(
23288 jx9_hashmap *pMap, /* Target hashmap */
23289 jx9_value *pKey, /* Lookup key */
23290 jx9_hashmap_node **ppNode /* OUT: Target node on success */
23291 )
23292{
23293 sxi32 rc;
23294 if( pMap->nEntry < 1 ){
23295 /* TICKET 1433-25: Don't bother hashing, the hashmap is empty anyway.
23296 */
23297 return SXERR_NOTFOUND;
23298 }
23299 rc = HashmapLookup(&(*pMap), &(*pKey), ppNode);
23300 return rc;
23301}
23302/*
23303 * Insert a given key and it's associated value (if any) in the given
23304 * hashmap.
23305 * If a node with the given key already exists in the database
23306 * then this function overwrite the old value.
23307 */
23308JX9_PRIVATE sxi32 jx9HashmapInsert(
23309 jx9_hashmap *pMap, /* Target hashmap */
23310 jx9_value *pKey, /* Lookup key */
23311 jx9_value *pVal /* Node value.NULL otherwise */
23312 )
23313{
23314 sxi32 rc;
23315 rc = HashmapInsert(&(*pMap), &(*pKey), &(*pVal));
23316 return rc;
23317}
23318/*
23319 * Reset the node cursor of a given hashmap.
23320 */
23321JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap)
23322{
23323 /* Reset the loop cursor */
23324 pMap->pCur = pMap->pFirst;
23325}
23326/*
23327 * Return a pointer to the node currently pointed by the node cursor.
23328 * If the cursor reaches the end of the list, then this function
23329 * return NULL.
23330 * Note that the node cursor is automatically advanced by this function.
23331 */
23332JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap)
23333{
23334 jx9_hashmap_node *pCur = pMap->pCur;
23335 if( pCur == 0 ){
23336 /* End of the list, return null */
23337 return 0;
23338 }
23339 /* Advance the node cursor */
23340 pMap->pCur = pCur->pPrev; /* Reverse link */
23341 return pCur;
23342}
23343/*
23344 * Extract a node value.
23345 */
23346JX9_PRIVATE jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode)
23347{
23348 jx9_value *pValue;
23349 pValue = HashmapExtractNodeValue(pNode);
23350 return pValue;
23351}
23352/*
23353 * Extract a node value (Second).
23354 */
23355JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore)
23356{
23357 jx9_value *pEntry = HashmapExtractNodeValue(pNode);
23358 if( pEntry ){
23359 if( bStore ){
23360 jx9MemObjStore(pEntry, pValue);
23361 }else{
23362 jx9MemObjLoad(pEntry, pValue);
23363 }
23364 }else{
23365 jx9MemObjRelease(pValue);
23366 }
23367}
23368/*
23369 * Extract a node key.
23370 */
23371JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode,jx9_value *pKey)
23372{
23373 /* Fill with the current key */
23374 if( pNode->iType == HASHMAP_INT_NODE ){
23375 if( SyBlobLength(&pKey->sBlob) > 0 ){
23376 SyBlobRelease(&pKey->sBlob);
23377 }
23378 pKey->x.iVal = pNode->xKey.iKey;
23379 MemObjSetType(pKey, MEMOBJ_INT);
23380 }else{
23381 SyBlobReset(&pKey->sBlob);
23382 SyBlobAppend(&pKey->sBlob, SyBlobData(&pNode->xKey.sKey), SyBlobLength(&pNode->xKey.sKey));
23383 MemObjSetType(pKey, MEMOBJ_STRING);
23384 }
23385}
23386#ifndef JX9_DISABLE_BUILTIN_FUNC
23387/*
23388 * Store the address of nodes value in the given container.
23389 * Refer to the [vfprintf(), vprintf(), vsprintf()] implementations
23390 * defined in 'builtin.c' for more information.
23391 */
23392JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut)
23393{
23394 jx9_hashmap_node *pEntry = pMap->pFirst;
23395 jx9_value *pValue;
23396 sxu32 n;
23397 /* Initialize the container */
23398 SySetInit(pOut, &pMap->pVm->sAllocator, sizeof(jx9_value *));
23399 for(n = 0 ; n < pMap->nEntry ; n++ ){
23400 /* Extract node value */
23401 pValue = HashmapExtractNodeValue(pEntry);
23402 if( pValue ){
23403 SySetPut(pOut, (const void *)&pValue);
23404 }
23405 /* Point to the next entry */
23406 pEntry = pEntry->pPrev; /* Reverse link */
23407 }
23408 /* Total inserted entries */
23409 return (int)SySetUsed(pOut);
23410}
23411#endif /* JX9_DISABLE_BUILTIN_FUNC */
23412/*
23413 * Merge sort.
23414 * The merge sort implementation is based on the one found in the SQLite3 source tree.
23415 * Status: Public domain
23416 */
23417/* Node comparison callback signature */
23418typedef sxi32 (*ProcNodeCmp)(jx9_hashmap_node *, jx9_hashmap_node *, void *);
23419/*
23420** Inputs:
23421** a: A sorted, null-terminated linked list. (May be null).
23422** b: A sorted, null-terminated linked list. (May be null).
23423** cmp: A pointer to the comparison function.
23424**
23425** Return Value:
23426** A pointer to the head of a sorted list containing the elements
23427** of both a and b.
23428**
23429** Side effects:
23430** The "next", "prev" pointers for elements in the lists a and b are
23431** changed.
23432*/
23433static jx9_hashmap_node * HashmapNodeMerge(jx9_hashmap_node *pA, jx9_hashmap_node *pB, ProcNodeCmp xCmp, void *pCmpData)
23434{
23435 jx9_hashmap_node result, *pTail;
23436 /* Prevent compiler warning */
23437 result.pNext = result.pPrev = 0;
23438 pTail = &result;
23439 while( pA && pB ){
23440 if( xCmp(pA, pB, pCmpData) < 0 ){
23441 pTail->pPrev = pA;
23442 pA->pNext = pTail;
23443 pTail = pA;
23444 pA = pA->pPrev;
23445 }else{
23446 pTail->pPrev = pB;
23447 pB->pNext = pTail;
23448 pTail = pB;
23449 pB = pB->pPrev;
23450 }
23451 }
23452 if( pA ){
23453 pTail->pPrev = pA;
23454 pA->pNext = pTail;
23455 }else if( pB ){
23456 pTail->pPrev = pB;
23457 pB->pNext = pTail;
23458 }else{
23459 pTail->pPrev = pTail->pNext = 0;
23460 }
23461 return result.pPrev;
23462}
23463/*
23464** Inputs:
23465** Map: Input hashmap
23466** cmp: A comparison function.
23467**
23468** Return Value:
23469** Sorted hashmap.
23470**
23471** Side effects:
23472** The "next" pointers for elements in list are changed.
23473*/
23474#define N_SORT_BUCKET 32
23475static sxi32 HashmapMergeSort(jx9_hashmap *pMap, ProcNodeCmp xCmp, void *pCmpData)
23476{
23477 jx9_hashmap_node *a[N_SORT_BUCKET], *p, *pIn;
23478 sxu32 i;
23479 SyZero(a, sizeof(a));
23480 /* Point to the first inserted entry */
23481 pIn = pMap->pFirst;
23482 while( pIn ){
23483 p = pIn;
23484 pIn = p->pPrev;
23485 p->pPrev = 0;
23486 for(i=0; i<N_SORT_BUCKET-1; i++){
23487 if( a[i]==0 ){
23488 a[i] = p;
23489 break;
23490 }else{
23491 p = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
23492 a[i] = 0;
23493 }
23494 }
23495 if( i==N_SORT_BUCKET-1 ){
23496 /* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
23497 * But that is impossible.
23498 */
23499 a[i] = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
23500 }
23501 }
23502 p = a[0];
23503 for(i=1; i<N_SORT_BUCKET; i++){
23504 p = HashmapNodeMerge(p, a[i], xCmp, pCmpData);
23505 }
23506 p->pNext = 0;
23507 /* Reflect the change */
23508 pMap->pFirst = p;
23509 /* Reset the loop cursor */
23510 pMap->pCur = pMap->pFirst;
23511 return SXRET_OK;
23512}
23513/*
23514 * Node comparison callback.
23515 * used-by: [sort(), asort(), ...]
23516 */
23517static sxi32 HashmapCmpCallback1(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
23518{
23519 jx9_value sA, sB;
23520 sxi32 iFlags;
23521 int rc;
23522 if( pCmpData == 0 ){
23523 /* Perform a standard comparison */
23524 rc = HashmapNodeCmp(pA, pB, FALSE);
23525 return rc;
23526 }
23527 iFlags = SX_PTR_TO_INT(pCmpData);
23528 /* Duplicate node values */
23529 jx9MemObjInit(pA->pMap->pVm, &sA);
23530 jx9MemObjInit(pA->pMap->pVm, &sB);
23531 jx9HashmapExtractNodeValue(pA, &sA, FALSE);
23532 jx9HashmapExtractNodeValue(pB, &sB, FALSE);
23533 if( iFlags == 5 ){
23534 /* String cast */
23535 if( (sA.iFlags & MEMOBJ_STRING) == 0 ){
23536 jx9MemObjToString(&sA);
23537 }
23538 if( (sB.iFlags & MEMOBJ_STRING) == 0 ){
23539 jx9MemObjToString(&sB);
23540 }
23541 }else{
23542 /* Numeric cast */
23543 jx9MemObjToNumeric(&sA);
23544 jx9MemObjToNumeric(&sB);
23545 }
23546 /* Perform the comparison */
23547 rc = jx9MemObjCmp(&sA, &sB, FALSE, 0);
23548 jx9MemObjRelease(&sA);
23549 jx9MemObjRelease(&sB);
23550 return rc;
23551}
23552/*
23553 * Node comparison callback.
23554 * Used by: [rsort(), arsort()];
23555 */
23556static sxi32 HashmapCmpCallback3(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
23557{
23558 jx9_value sA, sB;
23559 sxi32 iFlags;
23560 int rc;
23561 if( pCmpData == 0 ){
23562 /* Perform a standard comparison */
23563 rc = HashmapNodeCmp(pA, pB, FALSE);
23564 return -rc;
23565 }
23566 iFlags = SX_PTR_TO_INT(pCmpData);
23567 /* Duplicate node values */
23568 jx9MemObjInit(pA->pMap->pVm, &sA);
23569 jx9MemObjInit(pA->pMap->pVm, &sB);
23570 jx9HashmapExtractNodeValue(pA, &sA, FALSE);
23571 jx9HashmapExtractNodeValue(pB, &sB, FALSE);
23572 if( iFlags == 5 ){
23573 /* String cast */
23574 if( (sA.iFlags & MEMOBJ_STRING) == 0 ){
23575 jx9MemObjToString(&sA);
23576 }
23577 if( (sB.iFlags & MEMOBJ_STRING) == 0 ){
23578 jx9MemObjToString(&sB);
23579 }
23580 }else{
23581 /* Numeric cast */
23582 jx9MemObjToNumeric(&sA);
23583 jx9MemObjToNumeric(&sB);
23584 }
23585 /* Perform the comparison */
23586 rc = jx9MemObjCmp(&sA, &sB, FALSE, 0);
23587 jx9MemObjRelease(&sA);
23588 jx9MemObjRelease(&sB);
23589 return -rc;
23590}
23591/*
23592 * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison.
23593 * used-by: [usort(), uasort()]
23594 */
23595static sxi32 HashmapCmpCallback4(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
23596{
23597 jx9_value sResult, *pCallback;
23598 jx9_value *pV1, *pV2;
23599 jx9_value *apArg[2]; /* Callback arguments */
23600 sxi32 rc;
23601 /* Point to the desired callback */
23602 pCallback = (jx9_value *)pCmpData;
23603 /* initialize the result value */
23604 jx9MemObjInit(pA->pMap->pVm, &sResult);
23605 /* Extract nodes values */
23606 pV1 = HashmapExtractNodeValue(pA);
23607 pV2 = HashmapExtractNodeValue(pB);
23608 apArg[0] = pV1;
23609 apArg[1] = pV2;
23610 /* Invoke the callback */
23611 rc = jx9VmCallUserFunction(pA->pMap->pVm, pCallback, 2, apArg, &sResult);
23612 if( rc != SXRET_OK ){
23613 /* An error occured while calling user defined function [i.e: not defined] */
23614 rc = -1; /* Set a dummy result */
23615 }else{
23616 /* Extract callback result */
23617 if((sResult.iFlags & MEMOBJ_INT) == 0 ){
23618 /* Perform an int cast */
23619 jx9MemObjToInteger(&sResult);
23620 }
23621 rc = (sxi32)sResult.x.iVal;
23622 }
23623 jx9MemObjRelease(&sResult);
23624 /* Callback result */
23625 return rc;
23626}
23627/*
23628 * Rehash all nodes keys after a merge-sort have been applied.
23629 * Used by [sort(), usort() and rsort()].
23630 */
23631static void HashmapSortRehash(jx9_hashmap *pMap)
23632{
23633 jx9_hashmap_node *p, *pLast;
23634 sxu32 i;
23635 /* Rehash all entries */
23636 pLast = p = pMap->pFirst;
23637 pMap->iNextIdx = 0; /* Reset the automatic index */
23638 i = 0;
23639 for( ;; ){
23640 if( i >= pMap->nEntry ){
23641 pMap->pLast = pLast; /* Fix the last link broken by the merge-sort */
23642 break;
23643 }
23644 if( p->iType == HASHMAP_BLOB_NODE ){
23645 /* Do not maintain index association as requested by the JX9 specification */
23646 SyBlobRelease(&p->xKey.sKey);
23647 /* Change key type */
23648 p->iType = HASHMAP_INT_NODE;
23649 }
23650 HashmapRehashIntNode(p);
23651 /* Point to the next entry */
23652 i++;
23653 pLast = p;
23654 p = p->pPrev; /* Reverse link */
23655 }
23656}
23657/*
23658 * Array functions implementation.
23659 * Authors:
23660 * Symisc Systems, devel@symisc.net.
23661 * Copyright (C) Symisc Systems, http://jx9.symisc.net
23662 * Status:
23663 * Stable.
23664 */
23665/*
23666 * bool sort(array &$array[, int $sort_flags = SORT_REGULAR ] )
23667 * Sort an array.
23668 * Parameters
23669 * $array
23670 * The input array.
23671 * $sort_flags
23672 * The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
23673 * Sorting type flags:
23674 * SORT_REGULAR - compare items normally (don't change types)
23675 * SORT_NUMERIC - compare items numerically
23676 * SORT_STRING - compare items as strings
23677 * Return
23678 * TRUE on success or FALSE on failure.
23679 *
23680 */
23681static int jx9_hashmap_sort(jx9_context *pCtx, int nArg, jx9_value **apArg)
23682{
23683 jx9_hashmap *pMap;
23684 /* Make sure we are dealing with a valid hashmap */
23685 if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
23686 /* Missing/Invalid arguments, return FALSE */
23687 jx9_result_bool(pCtx, 0);
23688 return JX9_OK;
23689 }
23690 /* Point to the internal representation of the input hashmap */
23691 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23692 if( pMap->nEntry > 1 ){
23693 sxi32 iCmpFlags = 0;
23694 if( nArg > 1 ){
23695 /* Extract comparison flags */
23696 iCmpFlags = jx9_value_to_int(apArg[1]);
23697 if( iCmpFlags == 3 /* SORT_REGULAR */ ){
23698 iCmpFlags = 0; /* Standard comparison */
23699 }
23700 }
23701 /* Do the merge sort */
23702 HashmapMergeSort(pMap, HashmapCmpCallback1, SX_INT_TO_PTR(iCmpFlags));
23703 /* Rehash [Do not maintain index association as requested by the JX9 specification] */
23704 HashmapSortRehash(pMap);
23705 }
23706 /* All done, return TRUE */
23707 jx9_result_bool(pCtx, 1);
23708 return JX9_OK;
23709}
23710/*
23711 * bool rsort(array &$array[, int $sort_flags = SORT_REGULAR ] )
23712 * Sort an array in reverse order.
23713 * Parameters
23714 * $array
23715 * The input array.
23716 * $sort_flags
23717 * The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
23718 * Sorting type flags:
23719 * SORT_REGULAR - compare items normally (don't change types)
23720 * SORT_NUMERIC - compare items numerically
23721 * SORT_STRING - compare items as strings
23722 * Return
23723 * TRUE on success or FALSE on failure.
23724 */
23725static int jx9_hashmap_rsort(jx9_context *pCtx, int nArg, jx9_value **apArg)
23726{
23727 jx9_hashmap *pMap;
23728 /* Make sure we are dealing with a valid hashmap */
23729 if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
23730 /* Missing/Invalid arguments, return FALSE */
23731 jx9_result_bool(pCtx, 0);
23732 return JX9_OK;
23733 }
23734 /* Point to the internal representation of the input hashmap */
23735 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23736 if( pMap->nEntry > 1 ){
23737 sxi32 iCmpFlags = 0;
23738 if( nArg > 1 ){
23739 /* Extract comparison flags */
23740 iCmpFlags = jx9_value_to_int(apArg[1]);
23741 if( iCmpFlags == 3 /* SORT_REGULAR */ ){
23742 iCmpFlags = 0; /* Standard comparison */
23743 }
23744 }
23745 /* Do the merge sort */
23746 HashmapMergeSort(pMap, HashmapCmpCallback3, SX_INT_TO_PTR(iCmpFlags));
23747 /* Rehash [Do not maintain index association as requested by the JX9 specification] */
23748 HashmapSortRehash(pMap);
23749 }
23750 /* All done, return TRUE */
23751 jx9_result_bool(pCtx, 1);
23752 return JX9_OK;
23753}
23754/*
23755 * bool usort(array &$array, callable $cmp_function)
23756 * Sort an array by values using a user-defined comparison function.
23757 * Parameters
23758 * $array
23759 * The input array.
23760 * $cmp_function
23761 * The comparison function must return an integer less than, equal to, or greater
23762 * than zero if the first argument is considered to be respectively less than, equal
23763 * to, or greater than the second.
23764 * int callback ( mixed $a, mixed $b )
23765 * Return
23766 * TRUE on success or FALSE on failure.
23767 */
23768static int jx9_hashmap_usort(jx9_context *pCtx, int nArg, jx9_value **apArg)
23769{
23770 jx9_hashmap *pMap;
23771 /* Make sure we are dealing with a valid hashmap */
23772 if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
23773 /* Missing/Invalid arguments, return FALSE */
23774 jx9_result_bool(pCtx, 0);
23775 return JX9_OK;
23776 }
23777 /* Point to the internal representation of the input hashmap */
23778 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23779 if( pMap->nEntry > 1 ){
23780 jx9_value *pCallback = 0;
23781 ProcNodeCmp xCmp;
23782 xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */
23783 if( nArg > 1 && jx9_value_is_callable(apArg[1]) ){
23784 /* Point to the desired callback */
23785 pCallback = apArg[1];
23786 }else{
23787 /* Use the default comparison function */
23788 xCmp = HashmapCmpCallback1;
23789 }
23790 /* Do the merge sort */
23791 HashmapMergeSort(pMap, xCmp, pCallback);
23792 /* Rehash [Do not maintain index association as requested by the JX9 specification] */
23793 HashmapSortRehash(pMap);
23794 }
23795 /* All done, return TRUE */
23796 jx9_result_bool(pCtx, 1);
23797 return JX9_OK;
23798}
23799/*
23800 * int count(array $var [, int $mode = COUNT_NORMAL ])
23801 * Count all elements in an array, or something in an object.
23802 * Parameters
23803 * $var
23804 * The array or the object.
23805 * $mode
23806 * If the optional mode parameter is set to COUNT_RECURSIVE (or 1), count()
23807 * will recursively count the array. This is particularly useful for counting
23808 * all the elements of a multidimensional array. count() does not detect infinite
23809 * recursion.
23810 * Return
23811 * Returns the number of elements in the array.
23812 */
23813static int jx9_hashmap_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
23814{
23815 int bRecursive = FALSE;
23816 sxi64 iCount;
23817 if( nArg < 1 ){
23818 /* Missing arguments, return 0 */
23819 jx9_result_int(pCtx, 0);
23820 return JX9_OK;
23821 }
23822 if( !jx9_value_is_json_array(apArg[0]) ){
23823 /* TICKET 1433-19: Handle objects */
23824 int res = !jx9_value_is_null(apArg[0]);
23825 jx9_result_int(pCtx, res);
23826 return JX9_OK;
23827 }
23828 if( nArg > 1 ){
23829 /* Recursive count? */
23830 bRecursive = jx9_value_to_int(apArg[1]) == 1 /* COUNT_RECURSIVE */;
23831 }
23832 /* Count */
23833 iCount = HashmapCount((jx9_hashmap *)apArg[0]->x.pOther, bRecursive, 0);
23834 jx9_result_int64(pCtx, iCount);
23835 return JX9_OK;
23836}
23837/*
23838 * bool array_key_exists(value $key, array $search)
23839 * Checks if the given key or index exists in the array.
23840 * Parameters
23841 * $key
23842 * Value to check.
23843 * $search
23844 * An array with keys to check.
23845 * Return
23846 * TRUE on success or FALSE on failure.
23847 */
23848static int jx9_hashmap_key_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
23849{
23850 sxi32 rc;
23851 if( nArg < 2 ){
23852 /* Missing arguments, return FALSE */
23853 jx9_result_bool(pCtx, 0);
23854 return JX9_OK;
23855 }
23856 /* Make sure we are dealing with a valid hashmap */
23857 if( !jx9_value_is_json_array(apArg[1]) ){
23858 /* Invalid argument, return FALSE */
23859 jx9_result_bool(pCtx, 0);
23860 return JX9_OK;
23861 }
23862 /* Perform the lookup */
23863 rc = jx9HashmapLookup((jx9_hashmap *)apArg[1]->x.pOther, apArg[0], 0);
23864 /* lookup result */
23865 jx9_result_bool(pCtx, rc == SXRET_OK ? 1 : 0);
23866 return JX9_OK;
23867}
23868/*
23869 * value array_pop(array $array)
23870 * POP the last inserted element from the array.
23871 * Parameter
23872 * The array to get the value from.
23873 * Return
23874 * Poped value or NULL on failure.
23875 */
23876static int jx9_hashmap_pop(jx9_context *pCtx, int nArg, jx9_value **apArg)
23877{
23878 jx9_hashmap *pMap;
23879 if( nArg < 1 ){
23880 /* Missing arguments, return null */
23881 jx9_result_null(pCtx);
23882 return JX9_OK;
23883 }
23884 /* Make sure we are dealing with a valid hashmap */
23885 if( !jx9_value_is_json_array(apArg[0]) ){
23886 /* Invalid argument, return null */
23887 jx9_result_null(pCtx);
23888 return JX9_OK;
23889 }
23890 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23891 if( pMap->nEntry < 1 ){
23892 /* Noting to pop, return NULL */
23893 jx9_result_null(pCtx);
23894 }else{
23895 jx9_hashmap_node *pLast = pMap->pLast;
23896 jx9_value *pObj;
23897 pObj = HashmapExtractNodeValue(pLast);
23898 if( pObj ){
23899 /* Node value */
23900 jx9_result_value(pCtx, pObj);
23901 /* Unlink the node */
23902 jx9HashmapUnlinkNode(pLast);
23903 }else{
23904 jx9_result_null(pCtx);
23905 }
23906 /* Reset the cursor */
23907 pMap->pCur = pMap->pFirst;
23908 }
23909 return JX9_OK;
23910}
23911/*
23912 * int array_push($array, $var, ...)
23913 * Push one or more elements onto the end of array. (Stack insertion)
23914 * Parameters
23915 * array
23916 * The input array.
23917 * var
23918 * On or more value to push.
23919 * Return
23920 * New array count (including old items).
23921 */
23922static int jx9_hashmap_push(jx9_context *pCtx, int nArg, jx9_value **apArg)
23923{
23924 jx9_hashmap *pMap;
23925 sxi32 rc;
23926 int i;
23927 if( nArg < 1 ){
23928 /* Missing arguments, return 0 */
23929 jx9_result_int(pCtx, 0);
23930 return JX9_OK;
23931 }
23932 /* Make sure we are dealing with a valid hashmap */
23933 if( !jx9_value_is_json_array(apArg[0]) ){
23934 /* Invalid argument, return 0 */
23935 jx9_result_int(pCtx, 0);
23936 return JX9_OK;
23937 }
23938 /* Point to the internal representation of the input hashmap */
23939 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23940 /* Start pushing given values */
23941 for( i = 1 ; i < nArg ; ++i ){
23942 rc = jx9HashmapInsert(pMap, 0, apArg[i]);
23943 if( rc != SXRET_OK ){
23944 break;
23945 }
23946 }
23947 /* Return the new count */
23948 jx9_result_int64(pCtx, (sxi64)pMap->nEntry);
23949 return JX9_OK;
23950}
23951/*
23952 * value array_shift(array $array)
23953 * Shift an element off the beginning of array.
23954 * Parameter
23955 * The array to get the value from.
23956 * Return
23957 * Shifted value or NULL on failure.
23958 */
23959static int jx9_hashmap_shift(jx9_context *pCtx, int nArg, jx9_value **apArg)
23960{
23961 jx9_hashmap *pMap;
23962 if( nArg < 1 ){
23963 /* Missing arguments, return null */
23964 jx9_result_null(pCtx);
23965 return JX9_OK;
23966 }
23967 /* Make sure we are dealing with a valid hashmap */
23968 if( !jx9_value_is_json_array(apArg[0]) ){
23969 /* Invalid argument, return null */
23970 jx9_result_null(pCtx);
23971 return JX9_OK;
23972 }
23973 /* Point to the internal representation of the hashmap */
23974 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23975 if( pMap->nEntry < 1 ){
23976 /* Empty hashmap, return NULL */
23977 jx9_result_null(pCtx);
23978 }else{
23979 jx9_hashmap_node *pEntry = pMap->pFirst;
23980 jx9_value *pObj;
23981 sxu32 n;
23982 pObj = HashmapExtractNodeValue(pEntry);
23983 if( pObj ){
23984 /* Node value */
23985 jx9_result_value(pCtx, pObj);
23986 /* Unlink the first node */
23987 jx9HashmapUnlinkNode(pEntry);
23988 }else{
23989 jx9_result_null(pCtx);
23990 }
23991 /* Rehash all int keys */
23992 n = pMap->nEntry;
23993 pEntry = pMap->pFirst;
23994 pMap->iNextIdx = 0; /* Reset the automatic index */
23995 for(;;){
23996 if( n < 1 ){
23997 break;
23998 }
23999 if( pEntry->iType == HASHMAP_INT_NODE ){
24000 HashmapRehashIntNode(pEntry);
24001 }
24002 /* Point to the next entry */
24003 pEntry = pEntry->pPrev; /* Reverse link */
24004 n--;
24005 }
24006 /* Reset the cursor */
24007 pMap->pCur = pMap->pFirst;
24008 }
24009 return JX9_OK;
24010}
24011/*
24012 * Extract the node cursor value.
24013 */
24014static sxi32 HashmapCurrentValue(jx9_context *pCtx, jx9_hashmap *pMap, int iDirection)
24015{
24016 jx9_hashmap_node *pCur = pMap->pCur;
24017 jx9_value *pVal;
24018 if( pCur == 0 ){
24019 /* Cursor does not point to anything, return FALSE */
24020 jx9_result_bool(pCtx, 0);
24021 return JX9_OK;
24022 }
24023 if( iDirection != 0 ){
24024 if( iDirection > 0 ){
24025 /* Point to the next entry */
24026 pMap->pCur = pCur->pPrev; /* Reverse link */
24027 pCur = pMap->pCur;
24028 }else{
24029 /* Point to the previous entry */
24030 pMap->pCur = pCur->pNext; /* Reverse link */
24031 pCur = pMap->pCur;
24032 }
24033 if( pCur == 0 ){
24034 /* End of input reached, return FALSE */
24035 jx9_result_bool(pCtx, 0);
24036 return JX9_OK;
24037 }
24038 }
24039 /* Point to the desired element */
24040 pVal = HashmapExtractNodeValue(pCur);
24041 if( pVal ){
24042 jx9_result_value(pCtx, pVal);
24043 }else{
24044 jx9_result_bool(pCtx, 0);
24045 }
24046 return JX9_OK;
24047}
24048/*
24049 * value current(array $array)
24050 * Return the current element in an array.
24051 * Parameter
24052 * $input: The input array.
24053 * Return
24054 * The current() function simply returns the value of the array element that's currently
24055 * being pointed to by the internal pointer. It does not move the pointer in any way.
24056 * If the internal pointer points beyond the end of the elements list or the array
24057 * is empty, current() returns FALSE.
24058 */
24059static int jx9_hashmap_current(jx9_context *pCtx, int nArg, jx9_value **apArg)
24060{
24061 if( nArg < 1 ){
24062 /* Missing arguments, return FALSE */
24063 jx9_result_bool(pCtx, 0);
24064 return JX9_OK;
24065 }
24066 /* Make sure we are dealing with a valid hashmap */
24067 if( !jx9_value_is_json_array(apArg[0]) ){
24068 /* Invalid argument, return FALSE */
24069 jx9_result_bool(pCtx, 0);
24070 return JX9_OK;
24071 }
24072 HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 0);
24073 return JX9_OK;
24074}
24075/*
24076 * value next(array $input)
24077 * Advance the internal array pointer of an array.
24078 * Parameter
24079 * $input: The input array.
24080 * Return
24081 * next() behaves like current(), with one difference. It advances the internal array
24082 * pointer one place forward before returning the element value. That means it returns
24083 * the next array value and advances the internal array pointer by one.
24084 */
24085static int jx9_hashmap_next(jx9_context *pCtx, int nArg, jx9_value **apArg)
24086{
24087 if( nArg < 1 ){
24088 /* Missing arguments, return FALSE */
24089 jx9_result_bool(pCtx, 0);
24090 return JX9_OK;
24091 }
24092 /* Make sure we are dealing with a valid hashmap */
24093 if( !jx9_value_is_json_array(apArg[0]) ){
24094 /* Invalid argument, return FALSE */
24095 jx9_result_bool(pCtx, 0);
24096 return JX9_OK;
24097 }
24098 HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 1);
24099 return JX9_OK;
24100}
24101/*
24102 * value prev(array $input)
24103 * Rewind the internal array pointer.
24104 * Parameter
24105 * $input: The input array.
24106 * Return
24107 * Returns the array value in the previous place that's pointed
24108 * to by the internal array pointer, or FALSE if there are no more
24109 * elements.
24110 */
24111static int jx9_hashmap_prev(jx9_context *pCtx, int nArg, jx9_value **apArg)
24112{
24113 if( nArg < 1 ){
24114 /* Missing arguments, return FALSE */
24115 jx9_result_bool(pCtx, 0);
24116 return JX9_OK;
24117 }
24118 /* Make sure we are dealing with a valid hashmap */
24119 if( !jx9_value_is_json_array(apArg[0]) ){
24120 /* Invalid argument, return FALSE */
24121 jx9_result_bool(pCtx, 0);
24122 return JX9_OK;
24123 }
24124 HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, -1);
24125 return JX9_OK;
24126}
24127/*
24128 * value end(array $input)
24129 * Set the internal pointer of an array to its last element.
24130 * Parameter
24131 * $input: The input array.
24132 * Return
24133 * Returns the value of the last element or FALSE for empty array.
24134 */
24135static int jx9_hashmap_end(jx9_context *pCtx, int nArg, jx9_value **apArg)
24136{
24137 jx9_hashmap *pMap;
24138 if( nArg < 1 ){
24139 /* Missing arguments, return FALSE */
24140 jx9_result_bool(pCtx, 0);
24141 return JX9_OK;
24142 }
24143 /* Make sure we are dealing with a valid hashmap */
24144 if( !jx9_value_is_json_array(apArg[0]) ){
24145 /* Invalid argument, return FALSE */
24146 jx9_result_bool(pCtx, 0);
24147 return JX9_OK;
24148 }
24149 /* Point to the internal representation of the input hashmap */
24150 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24151 /* Point to the last node */
24152 pMap->pCur = pMap->pLast;
24153 /* Return the last node value */
24154 HashmapCurrentValue(&(*pCtx), pMap, 0);
24155 return JX9_OK;
24156}
24157/*
24158 * value reset(array $array )
24159 * Set the internal pointer of an array to its first element.
24160 * Parameter
24161 * $input: The input array.
24162 * Return
24163 * Returns the value of the first array element, or FALSE if the array is empty.
24164 */
24165static int jx9_hashmap_reset(jx9_context *pCtx, int nArg, jx9_value **apArg)
24166{
24167 jx9_hashmap *pMap;
24168 if( nArg < 1 ){
24169 /* Missing arguments, return FALSE */
24170 jx9_result_bool(pCtx, 0);
24171 return JX9_OK;
24172 }
24173 /* Make sure we are dealing with a valid hashmap */
24174 if( !jx9_value_is_json_array(apArg[0]) ){
24175 /* Invalid argument, return FALSE */
24176 jx9_result_bool(pCtx, 0);
24177 return JX9_OK;
24178 }
24179 /* Point to the internal representation of the input hashmap */
24180 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24181 /* Point to the first node */
24182 pMap->pCur = pMap->pFirst;
24183 /* Return the last node value if available */
24184 HashmapCurrentValue(&(*pCtx), pMap, 0);
24185 return JX9_OK;
24186}
24187/*
24188 * value key(array $array)
24189 * Fetch a key from an array
24190 * Parameter
24191 * $input
24192 * The input array.
24193 * Return
24194 * The key() function simply returns the key of the array element that's currently
24195 * being pointed to by the internal pointer. It does not move the pointer in any way.
24196 * If the internal pointer points beyond the end of the elements list or the array
24197 * is empty, key() returns NULL.
24198 */
24199static int jx9_hashmap_simple_key(jx9_context *pCtx, int nArg, jx9_value **apArg)
24200{
24201 jx9_hashmap_node *pCur;
24202 jx9_hashmap *pMap;
24203 if( nArg < 1 ){
24204 /* Missing arguments, return NULL */
24205 jx9_result_null(pCtx);
24206 return JX9_OK;
24207 }
24208 /* Make sure we are dealing with a valid hashmap */
24209 if( !jx9_value_is_json_array(apArg[0]) ){
24210 /* Invalid argument, return NULL */
24211 jx9_result_null(pCtx);
24212 return JX9_OK;
24213 }
24214 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24215 pCur = pMap->pCur;
24216 if( pCur == 0 ){
24217 /* Cursor does not point to anything, return NULL */
24218 jx9_result_null(pCtx);
24219 return JX9_OK;
24220 }
24221 if( pCur->iType == HASHMAP_INT_NODE){
24222 /* Key is integer */
24223 jx9_result_int64(pCtx, pCur->xKey.iKey);
24224 }else{
24225 /* Key is blob */
24226 jx9_result_string(pCtx,
24227 (const char *)SyBlobData(&pCur->xKey.sKey), (int)SyBlobLength(&pCur->xKey.sKey));
24228 }
24229 return JX9_OK;
24230}
24231/*
24232 * array each(array $input)
24233 * Return the current key and value pair from an array and advance the array cursor.
24234 * Parameter
24235 * $input
24236 * The input array.
24237 * Return
24238 * Returns the current key and value pair from the array array. This pair is returned
24239 * in a four-element array, with the keys 0, 1, key, and value. Elements 0 and key
24240 * contain the key name of the array element, and 1 and value contain the data.
24241 * If the internal pointer for the array points past the end of the array contents
24242 * each() returns FALSE.
24243 */
24244static int jx9_hashmap_each(jx9_context *pCtx, int nArg, jx9_value **apArg)
24245{
24246 jx9_hashmap_node *pCur;
24247 jx9_hashmap *pMap;
24248 jx9_value *pArray;
24249 jx9_value *pVal;
24250 jx9_value sKey;
24251 if( nArg < 1 ){
24252 /* Missing arguments, return FALSE */
24253 jx9_result_bool(pCtx, 0);
24254 return JX9_OK;
24255 }
24256 /* Make sure we are dealing with a valid hashmap */
24257 if( !jx9_value_is_json_array(apArg[0]) ){
24258 /* Invalid argument, return FALSE */
24259 jx9_result_bool(pCtx, 0);
24260 return JX9_OK;
24261 }
24262 /* Point to the internal representation that describe the input hashmap */
24263 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24264 if( pMap->pCur == 0 ){
24265 /* Cursor does not point to anything, return FALSE */
24266 jx9_result_bool(pCtx, 0);
24267 return JX9_OK;
24268 }
24269 pCur = pMap->pCur;
24270 /* Create a new array */
24271 pArray = jx9_context_new_array(pCtx);
24272 if( pArray == 0 ){
24273 jx9_result_bool(pCtx, 0);
24274 return JX9_OK;
24275 }
24276 pVal = HashmapExtractNodeValue(pCur);
24277 /* Insert the current value */
24278 jx9_array_add_strkey_elem(pArray, "1", pVal);
24279 jx9_array_add_strkey_elem(pArray, "value", pVal);
24280 /* Make the key */
24281 if( pCur->iType == HASHMAP_INT_NODE ){
24282 jx9MemObjInitFromInt(pMap->pVm, &sKey, pCur->xKey.iKey);
24283 }else{
24284 jx9MemObjInitFromString(pMap->pVm, &sKey, 0);
24285 jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pCur->xKey.sKey), SyBlobLength(&pCur->xKey.sKey));
24286 }
24287 /* Insert the current key */
24288 jx9_array_add_elem(pArray, 0, &sKey);
24289 jx9_array_add_strkey_elem(pArray, "key", &sKey);
24290 jx9MemObjRelease(&sKey);
24291 /* Advance the cursor */
24292 pMap->pCur = pCur->pPrev; /* Reverse link */
24293 /* Return the current entry */
24294 jx9_result_value(pCtx, pArray);
24295 return JX9_OK;
24296}
24297/*
24298 * array array_values(array $input)
24299 * Returns all the values from the input array and indexes numerically the array.
24300 * Parameters
24301 * input: The input array.
24302 * Return
24303 * An indexed array of values or NULL on failure.
24304 */
24305static int jx9_hashmap_values(jx9_context *pCtx, int nArg, jx9_value **apArg)
24306{
24307 jx9_hashmap_node *pNode;
24308 jx9_hashmap *pMap;
24309 jx9_value *pArray;
24310 jx9_value *pObj;
24311 sxu32 n;
24312 if( nArg < 1 ){
24313 /* Missing arguments, return NULL */
24314 jx9_result_null(pCtx);
24315 return JX9_OK;
24316 }
24317 /* Make sure we are dealing with a valid hashmap */
24318 if( !jx9_value_is_json_array(apArg[0]) ){
24319 /* Invalid argument, return NULL */
24320 jx9_result_null(pCtx);
24321 return JX9_OK;
24322 }
24323 /* Point to the internal representation that describe the input hashmap */
24324 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24325 /* Create a new array */
24326 pArray = jx9_context_new_array(pCtx);
24327 if( pArray == 0 ){
24328 jx9_result_null(pCtx);
24329 return JX9_OK;
24330 }
24331 /* Perform the requested operation */
24332 pNode = pMap->pFirst;
24333 for( n = 0 ; n < pMap->nEntry ; ++n ){
24334 pObj = HashmapExtractNodeValue(pNode);
24335 if( pObj ){
24336 /* perform the insertion */
24337 jx9_array_add_elem(pArray, 0/* Automatic index assign */, pObj);
24338 }
24339 /* Point to the next entry */
24340 pNode = pNode->pPrev; /* Reverse link */
24341 }
24342 /* return the new array */
24343 jx9_result_value(pCtx, pArray);
24344 return JX9_OK;
24345}
24346/*
24347 * bool array_same(array $arr1, array $arr2)
24348 * Return TRUE if the given arrays are the same instance.
24349 * This function is useful under JX9 since arrays and objects
24350 * are passed by reference.
24351 * Parameters
24352 * $arr1
24353 * First array
24354 * $arr2
24355 * Second array
24356 * Return
24357 * TRUE if the arrays are the same instance. FALSE otherwise.
24358 * Note
24359 * This function is a symisc eXtension.
24360 */
24361static int jx9_hashmap_same(jx9_context *pCtx, int nArg, jx9_value **apArg)
24362{
24363 jx9_hashmap *p1, *p2;
24364 int rc;
24365 if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
24366 /* Missing or invalid arguments, return FALSE*/
24367 jx9_result_bool(pCtx, 0);
24368 return JX9_OK;
24369 }
24370 /* Point to the hashmaps */
24371 p1 = (jx9_hashmap *)apArg[0]->x.pOther;
24372 p2 = (jx9_hashmap *)apArg[1]->x.pOther;
24373 rc = (p1 == p2);
24374 /* Same instance? */
24375 jx9_result_bool(pCtx, rc);
24376 return JX9_OK;
24377}
24378/*
24379 * array array_merge(array $array1, ...)
24380 * Merge one or more arrays.
24381 * Parameters
24382 * $array1
24383 * Initial array to merge.
24384 * ...
24385 * More array to merge.
24386 * Return
24387 * The resulting array.
24388 */
24389static int jx9_hashmap_merge(jx9_context *pCtx, int nArg, jx9_value **apArg)
24390{
24391 jx9_hashmap *pMap, *pSrc;
24392 jx9_value *pArray;
24393 int i;
24394 if( nArg < 1 ){
24395 /* Missing arguments, return NULL */
24396 jx9_result_null(pCtx);
24397 return JX9_OK;
24398 }
24399 /* Create a new array */
24400 pArray = jx9_context_new_array(pCtx);
24401 if( pArray == 0 ){
24402 jx9_result_null(pCtx);
24403 return JX9_OK;
24404 }
24405 /* Point to the internal representation of the hashmap */
24406 pMap = (jx9_hashmap *)pArray->x.pOther;
24407 /* Start merging */
24408 for( i = 0 ; i < nArg ; i++ ){
24409 /* Make sure we are dealing with a valid hashmap */
24410 if( !jx9_value_is_json_array(apArg[i]) ){
24411 /* Insert scalar value */
24412 jx9_array_add_elem(pArray, 0, apArg[i]);
24413 }else{
24414 pSrc = (jx9_hashmap *)apArg[i]->x.pOther;
24415 /* Merge the two hashmaps */
24416 HashmapMerge(pSrc, pMap);
24417 }
24418 }
24419 /* Return the freshly created array */
24420 jx9_result_value(pCtx, pArray);
24421 return JX9_OK;
24422}
24423/*
24424 * bool in_array(value $needle, array $haystack[, bool $strict = FALSE ])
24425 * Checks if a value exists in an array.
24426 * Parameters
24427 * $needle
24428 * The searched value.
24429 * Note:
24430 * If needle is a string, the comparison is done in a case-sensitive manner.
24431 * $haystack
24432 * The target array.
24433 * $strict
24434 * If the third parameter strict is set to TRUE then the in_array() function
24435 * will also check the types of the needle in the haystack.
24436 */
24437static int jx9_hashmap_in_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
24438{
24439 jx9_value *pNeedle;
24440 int bStrict;
24441 int rc;
24442 if( nArg < 2 ){
24443 /* Missing argument, return FALSE */
24444 jx9_result_bool(pCtx, 0);
24445 return JX9_OK;
24446 }
24447 pNeedle = apArg[0];
24448 bStrict = 0;
24449 if( nArg > 2 ){
24450 bStrict = jx9_value_to_bool(apArg[2]);
24451 }
24452 if( !jx9_value_is_json_array(apArg[1]) ){
24453 /* haystack must be an array, perform a standard comparison */
24454 rc = jx9_value_compare(pNeedle, apArg[1], bStrict);
24455 /* Set the comparison result */
24456 jx9_result_bool(pCtx, rc == 0);
24457 return JX9_OK;
24458 }
24459 /* Perform the lookup */
24460 rc = HashmapFindValue((jx9_hashmap *)apArg[1]->x.pOther, pNeedle, 0, bStrict);
24461 /* Lookup result */
24462 jx9_result_bool(pCtx, rc == SXRET_OK);
24463 return JX9_OK;
24464}
24465/*
24466 * array array_copy(array $source)
24467 * Make a blind copy of the target array.
24468 * Parameters
24469 * $source
24470 * Target array
24471 * Return
24472 * Copy of the target array on success. NULL otherwise.
24473 * Note
24474 * This function is a symisc eXtension.
24475 */
24476static int jx9_hashmap_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
24477{
24478 jx9_hashmap *pMap;
24479 jx9_value *pArray;
24480 if( nArg < 1 ){
24481 /* Missing arguments, return NULL */
24482 jx9_result_null(pCtx);
24483 return JX9_OK;
24484 }
24485 /* Create a new array */
24486 pArray = jx9_context_new_array(pCtx);
24487 if( pArray == 0 ){
24488 jx9_result_null(pCtx);
24489 return JX9_OK;
24490 }
24491 /* Point to the internal representation of the hashmap */
24492 pMap = (jx9_hashmap *)pArray->x.pOther;
24493 if( jx9_value_is_json_array(apArg[0])){
24494 /* Point to the internal representation of the source */
24495 jx9_hashmap *pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
24496 /* Perform the copy */
24497 jx9HashmapDup(pSrc, pMap);
24498 }else{
24499 /* Simple insertion */
24500 jx9HashmapInsert(pMap, 0/* Automatic index assign*/, apArg[0]);
24501 }
24502 /* Return the duplicated array */
24503 jx9_result_value(pCtx, pArray);
24504 return JX9_OK;
24505}
24506/*
24507 * bool array_erase(array $source)
24508 * Remove all elements from a given array.
24509 * Parameters
24510 * $source
24511 * Target array
24512 * Return
24513 * TRUE on success. FALSE otherwise.
24514 * Note
24515 * This function is a symisc eXtension.
24516 */
24517static int jx9_hashmap_erase(jx9_context *pCtx, int nArg, jx9_value **apArg)
24518{
24519 jx9_hashmap *pMap;
24520 if( nArg < 1 ){
24521 /* Missing arguments */
24522 jx9_result_bool(pCtx, 0);
24523 return JX9_OK;
24524 }
24525 /* Point to the target hashmap */
24526 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24527 /* Erase */
24528 jx9HashmapRelease(pMap, FALSE);
24529 return JX9_OK;
24530}
24531/*
24532 * array array_diff(array $array1, array $array2, ...)
24533 * Computes the difference of arrays.
24534 * Parameters
24535 * $array1
24536 * The array to compare from
24537 * $array2
24538 * An array to compare against
24539 * $...
24540 * More arrays to compare against
24541 * Return
24542 * Returns an array containing all the entries from array1 that
24543 * are not present in any of the other arrays.
24544 */
24545static int jx9_hashmap_diff(jx9_context *pCtx, int nArg, jx9_value **apArg)
24546{
24547 jx9_hashmap_node *pEntry;
24548 jx9_hashmap *pSrc, *pMap;
24549 jx9_value *pArray;
24550 jx9_value *pVal;
24551 sxi32 rc;
24552 sxu32 n;
24553 int i;
24554 if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
24555 /* Missing arguments, return NULL */
24556 jx9_result_null(pCtx);
24557 return JX9_OK;
24558 }
24559 if( nArg == 1 ){
24560 /* Return the first array since we cannot perform a diff */
24561 jx9_result_value(pCtx, apArg[0]);
24562 return JX9_OK;
24563 }
24564 /* Create a new array */
24565 pArray = jx9_context_new_array(pCtx);
24566 if( pArray == 0 ){
24567 jx9_result_null(pCtx);
24568 return JX9_OK;
24569 }
24570 /* Point to the internal representation of the source hashmap */
24571 pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
24572 /* Perform the diff */
24573 pEntry = pSrc->pFirst;
24574 n = pSrc->nEntry;
24575 for(;;){
24576 if( n < 1 ){
24577 break;
24578 }
24579 /* Extract the node value */
24580 pVal = HashmapExtractNodeValue(pEntry);
24581 if( pVal ){
24582 for( i = 1 ; i < nArg ; i++ ){
24583 if( !jx9_value_is_json_array(apArg[i])) {
24584 /* ignore */
24585 continue;
24586 }
24587 /* Point to the internal representation of the hashmap */
24588 pMap = (jx9_hashmap *)apArg[i]->x.pOther;
24589 /* Perform the lookup */
24590 rc = HashmapFindValue(pMap, pVal, 0, TRUE);
24591 if( rc == SXRET_OK ){
24592 /* Value exist */
24593 break;
24594 }
24595 }
24596 if( i >= nArg ){
24597 /* Perform the insertion */
24598 HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE);
24599 }
24600 }
24601 /* Point to the next entry */
24602 pEntry = pEntry->pPrev; /* Reverse link */
24603 n--;
24604 }
24605 /* Return the freshly created array */
24606 jx9_result_value(pCtx, pArray);
24607 return JX9_OK;
24608}
24609/*
24610 * array array_intersect(array $array1 , array $array2, ...)
24611 * Computes the intersection of arrays.
24612 * Parameters
24613 * $array1
24614 * The array to compare from
24615 * $array2
24616 * An array to compare against
24617 * $...
24618 * More arrays to compare against
24619 * Return
24620 * Returns an array containing all of the values in array1 whose values exist
24621 * in all of the parameters. .
24622 * Note that NULL is returned on failure.
24623 */
24624static int jx9_hashmap_intersect(jx9_context *pCtx, int nArg, jx9_value **apArg)
24625{
24626 jx9_hashmap_node *pEntry;
24627 jx9_hashmap *pSrc, *pMap;
24628 jx9_value *pArray;
24629 jx9_value *pVal;
24630 sxi32 rc;
24631 sxu32 n;
24632 int i;
24633 if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
24634 /* Missing arguments, return NULL */
24635 jx9_result_null(pCtx);
24636 return JX9_OK;
24637 }
24638 if( nArg == 1 ){
24639 /* Return the first array since we cannot perform a diff */
24640 jx9_result_value(pCtx, apArg[0]);
24641 return JX9_OK;
24642 }
24643 /* Create a new array */
24644 pArray = jx9_context_new_array(pCtx);
24645 if( pArray == 0 ){
24646 jx9_result_null(pCtx);
24647 return JX9_OK;
24648 }
24649 /* Point to the internal representation of the source hashmap */
24650 pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
24651 /* Perform the intersection */
24652 pEntry = pSrc->pFirst;
24653 n = pSrc->nEntry;
24654 for(;;){
24655 if( n < 1 ){
24656 break;
24657 }
24658 /* Extract the node value */
24659 pVal = HashmapExtractNodeValue(pEntry);
24660 if( pVal ){
24661 for( i = 1 ; i < nArg ; i++ ){
24662 if( !jx9_value_is_json_array(apArg[i])) {
24663 /* ignore */
24664 continue;
24665 }
24666 /* Point to the internal representation of the hashmap */
24667 pMap = (jx9_hashmap *)apArg[i]->x.pOther;
24668 /* Perform the lookup */
24669 rc = HashmapFindValue(pMap, pVal, 0, TRUE);
24670 if( rc != SXRET_OK ){
24671 /* Value does not exist */
24672 break;
24673 }
24674 }
24675 if( i >= nArg ){
24676 /* Perform the insertion */
24677 HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE);
24678 }
24679 }
24680 /* Point to the next entry */
24681 pEntry = pEntry->pPrev; /* Reverse link */
24682 n--;
24683 }
24684 /* Return the freshly created array */
24685 jx9_result_value(pCtx, pArray);
24686 return JX9_OK;
24687}
24688/*
24689 * number array_sum(array $array )
24690 * Calculate the sum of values in an array.
24691 * Parameters
24692 * $array: The input array.
24693 * Return
24694 * Returns the sum of values as an integer or float.
24695 */
24696static void DoubleSum(jx9_context *pCtx, jx9_hashmap *pMap)
24697{
24698 jx9_hashmap_node *pEntry;
24699 jx9_value *pObj;
24700 double dSum = 0;
24701 sxu32 n;
24702 pEntry = pMap->pFirst;
24703 for( n = 0 ; n < pMap->nEntry ; n++ ){
24704 pObj = HashmapExtractNodeValue(pEntry);
24705 if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
24706 if( pObj->iFlags & MEMOBJ_REAL ){
24707 dSum += pObj->x.rVal;
24708 }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
24709 dSum += (double)pObj->x.iVal;
24710 }else if( pObj->iFlags & MEMOBJ_STRING ){
24711 if( SyBlobLength(&pObj->sBlob) > 0 ){
24712 double dv = 0;
24713 SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
24714 dSum += dv;
24715 }
24716 }
24717 }
24718 /* Point to the next entry */
24719 pEntry = pEntry->pPrev; /* Reverse link */
24720 }
24721 /* Return sum */
24722 jx9_result_double(pCtx, dSum);
24723}
24724static void Int64Sum(jx9_context *pCtx, jx9_hashmap *pMap)
24725{
24726 jx9_hashmap_node *pEntry;
24727 jx9_value *pObj;
24728 sxi64 nSum = 0;
24729 sxu32 n;
24730 pEntry = pMap->pFirst;
24731 for( n = 0 ; n < pMap->nEntry ; n++ ){
24732 pObj = HashmapExtractNodeValue(pEntry);
24733 if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
24734 if( pObj->iFlags & MEMOBJ_REAL ){
24735 nSum += (sxi64)pObj->x.rVal;
24736 }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
24737 nSum += pObj->x.iVal;
24738 }else if( pObj->iFlags & MEMOBJ_STRING ){
24739 if( SyBlobLength(&pObj->sBlob) > 0 ){
24740 sxi64 nv = 0;
24741 SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
24742 nSum += nv;
24743 }
24744 }
24745 }
24746 /* Point to the next entry */
24747 pEntry = pEntry->pPrev; /* Reverse link */
24748 }
24749 /* Return sum */
24750 jx9_result_int64(pCtx, nSum);
24751}
24752/* number array_sum(array $array )
24753 * (See block-coment above)
24754 */
24755static int jx9_hashmap_sum(jx9_context *pCtx, int nArg, jx9_value **apArg)
24756{
24757 jx9_hashmap *pMap;
24758 jx9_value *pObj;
24759 if( nArg < 1 ){
24760 /* Missing arguments, return 0 */
24761 jx9_result_int(pCtx, 0);
24762 return JX9_OK;
24763 }
24764 /* Make sure we are dealing with a valid hashmap */
24765 if( !jx9_value_is_json_array(apArg[0]) ){
24766 /* Invalid argument, return 0 */
24767 jx9_result_int(pCtx, 0);
24768 return JX9_OK;
24769 }
24770 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24771 if( pMap->nEntry < 1 ){
24772 /* Nothing to compute, return 0 */
24773 jx9_result_int(pCtx, 0);
24774 return JX9_OK;
24775 }
24776 /* If the first element is of type float, then perform floating
24777 * point computaion.Otherwise switch to int64 computaion.
24778 */
24779 pObj = HashmapExtractNodeValue(pMap->pFirst);
24780 if( pObj == 0 ){
24781 jx9_result_int(pCtx, 0);
24782 return JX9_OK;
24783 }
24784 if( pObj->iFlags & MEMOBJ_REAL ){
24785 DoubleSum(pCtx, pMap);
24786 }else{
24787 Int64Sum(pCtx, pMap);
24788 }
24789 return JX9_OK;
24790}
24791/*
24792 * number array_product(array $array )
24793 * Calculate the product of values in an array.
24794 * Parameters
24795 * $array: The input array.
24796 * Return
24797 * Returns the product of values as an integer or float.
24798 */
24799static void DoubleProd(jx9_context *pCtx, jx9_hashmap *pMap)
24800{
24801 jx9_hashmap_node *pEntry;
24802 jx9_value *pObj;
24803 double dProd;
24804 sxu32 n;
24805 pEntry = pMap->pFirst;
24806 dProd = 1;
24807 for( n = 0 ; n < pMap->nEntry ; n++ ){
24808 pObj = HashmapExtractNodeValue(pEntry);
24809 if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
24810 if( pObj->iFlags & MEMOBJ_REAL ){
24811 dProd *= pObj->x.rVal;
24812 }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
24813 dProd *= (double)pObj->x.iVal;
24814 }else if( pObj->iFlags & MEMOBJ_STRING ){
24815 if( SyBlobLength(&pObj->sBlob) > 0 ){
24816 double dv = 0;
24817 SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
24818 dProd *= dv;
24819 }
24820 }
24821 }
24822 /* Point to the next entry */
24823 pEntry = pEntry->pPrev; /* Reverse link */
24824 }
24825 /* Return product */
24826 jx9_result_double(pCtx, dProd);
24827}
24828static void Int64Prod(jx9_context *pCtx, jx9_hashmap *pMap)
24829{
24830 jx9_hashmap_node *pEntry;
24831 jx9_value *pObj;
24832 sxi64 nProd;
24833 sxu32 n;
24834 pEntry = pMap->pFirst;
24835 nProd = 1;
24836 for( n = 0 ; n < pMap->nEntry ; n++ ){
24837 pObj = HashmapExtractNodeValue(pEntry);
24838 if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
24839 if( pObj->iFlags & MEMOBJ_REAL ){
24840 nProd *= (sxi64)pObj->x.rVal;
24841 }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
24842 nProd *= pObj->x.iVal;
24843 }else if( pObj->iFlags & MEMOBJ_STRING ){
24844 if( SyBlobLength(&pObj->sBlob) > 0 ){
24845 sxi64 nv = 0;
24846 SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
24847 nProd *= nv;
24848 }
24849 }
24850 }
24851 /* Point to the next entry */
24852 pEntry = pEntry->pPrev; /* Reverse link */
24853 }
24854 /* Return product */
24855 jx9_result_int64(pCtx, nProd);
24856}
24857/* number array_product(array $array )
24858 * (See block-block comment above)
24859 */
24860static int jx9_hashmap_product(jx9_context *pCtx, int nArg, jx9_value **apArg)
24861{
24862 jx9_hashmap *pMap;
24863 jx9_value *pObj;
24864 if( nArg < 1 ){
24865 /* Missing arguments, return 0 */
24866 jx9_result_int(pCtx, 0);
24867 return JX9_OK;
24868 }
24869 /* Make sure we are dealing with a valid hashmap */
24870 if( !jx9_value_is_json_array(apArg[0]) ){
24871 /* Invalid argument, return 0 */
24872 jx9_result_int(pCtx, 0);
24873 return JX9_OK;
24874 }
24875 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24876 if( pMap->nEntry < 1 ){
24877 /* Nothing to compute, return 0 */
24878 jx9_result_int(pCtx, 0);
24879 return JX9_OK;
24880 }
24881 /* If the first element is of type float, then perform floating
24882 * point computaion.Otherwise switch to int64 computaion.
24883 */
24884 pObj = HashmapExtractNodeValue(pMap->pFirst);
24885 if( pObj == 0 ){
24886 jx9_result_int(pCtx, 0);
24887 return JX9_OK;
24888 }
24889 if( pObj->iFlags & MEMOBJ_REAL ){
24890 DoubleProd(pCtx, pMap);
24891 }else{
24892 Int64Prod(pCtx, pMap);
24893 }
24894 return JX9_OK;
24895}
24896/*
24897 * array array_map(callback $callback, array $arr1)
24898 * Applies the callback to the elements of the given arrays.
24899 * Parameters
24900 * $callback
24901 * Callback function to run for each element in each array.
24902 * $arr1
24903 * An array to run through the callback function.
24904 * Return
24905 * Returns an array containing all the elements of arr1 after applying
24906 * the callback function to each one.
24907 * NOTE:
24908 * array_map() passes only a single value to the callback.
24909 */
24910static int jx9_hashmap_map(jx9_context *pCtx, int nArg, jx9_value **apArg)
24911{
24912 jx9_value *pArray, *pValue, sKey, sResult;
24913 jx9_hashmap_node *pEntry;
24914 jx9_hashmap *pMap;
24915 sxu32 n;
24916 if( nArg < 2 || !jx9_value_is_json_array(apArg[1]) ){
24917 /* Invalid arguments, return NULL */
24918 jx9_result_null(pCtx);
24919 return JX9_OK;
24920 }
24921 /* Create a new array */
24922 pArray = jx9_context_new_array(pCtx);
24923 if( pArray == 0 ){
24924 jx9_result_null(pCtx);
24925 return JX9_OK;
24926 }
24927 /* Point to the internal representation of the input hashmap */
24928 pMap = (jx9_hashmap *)apArg[1]->x.pOther;
24929 jx9MemObjInit(pMap->pVm, &sResult);
24930 jx9MemObjInit(pMap->pVm, &sKey);
24931 /* Perform the requested operation */
24932 pEntry = pMap->pFirst;
24933 for( n = 0 ; n < pMap->nEntry ; n++ ){
24934 /* Extrcat the node value */
24935 pValue = HashmapExtractNodeValue(pEntry);
24936 if( pValue ){
24937 sxi32 rc;
24938 /* Invoke the supplied callback */
24939 rc = jx9VmCallUserFunction(pMap->pVm, apArg[0], 1, &pValue, &sResult);
24940 /* Extract the node key */
24941 jx9HashmapExtractNodeKey(pEntry, &sKey);
24942 if( rc != SXRET_OK ){
24943 /* An error occured while invoking the supplied callback [i.e: not defined] */
24944 jx9_array_add_elem(pArray, &sKey, pValue); /* Keep the same value */
24945 }else{
24946 /* Insert the callback return value */
24947 jx9_array_add_elem(pArray, &sKey, &sResult);
24948 }
24949 jx9MemObjRelease(&sKey);
24950 jx9MemObjRelease(&sResult);
24951 }
24952 /* Point to the next entry */
24953 pEntry = pEntry->pPrev; /* Reverse link */
24954 }
24955 jx9_result_value(pCtx, pArray);
24956 return JX9_OK;
24957}
24958/*
24959 * bool array_walk(array &$array, callback $funcname [, value $userdata ] )
24960 * Apply a user function to every member of an array.
24961 * Parameters
24962 * $array
24963 * The input array.
24964 * $funcname
24965 * Typically, funcname takes on two parameters.The array parameter's value being
24966 * the first, and the key/index second.
24967 * Note:
24968 * If funcname needs to be working with the actual values of the array, specify the first
24969 * parameter of funcname as a reference. Then, any changes made to those elements will
24970 * be made in the original array itself.
24971 * $userdata
24972 * If the optional userdata parameter is supplied, it will be passed as the third parameter
24973 * to the callback funcname.
24974 * Return
24975 * Returns TRUE on success or FALSE on failure.
24976 */
24977static int jx9_hashmap_walk(jx9_context *pCtx, int nArg, jx9_value **apArg)
24978{
24979 jx9_value *pValue, *pUserData, sKey;
24980 jx9_hashmap_node *pEntry;
24981 jx9_hashmap *pMap;
24982 sxi32 rc;
24983 sxu32 n;
24984 if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) ){
24985 /* Invalid/Missing arguments, return FALSE */
24986 jx9_result_bool(pCtx, 0);
24987 return JX9_OK;
24988 }
24989 pUserData = nArg > 2 ? apArg[2] : 0;
24990 /* Point to the internal representation of the input hashmap */
24991 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24992 jx9MemObjInit(pMap->pVm, &sKey);
24993 /* Perform the desired operation */
24994 pEntry = pMap->pFirst;
24995 for( n = 0 ; n < pMap->nEntry ; n++ ){
24996 /* Extract the node value */
24997 pValue = HashmapExtractNodeValue(pEntry);
24998 if( pValue ){
24999 /* Extract the entry key */
25000 jx9HashmapExtractNodeKey(pEntry, &sKey);
25001 /* Invoke the supplied callback */
25002 rc = jx9VmCallUserFunctionAp(pMap->pVm, apArg[1], 0, pValue, &sKey, pUserData, 0);
25003 jx9MemObjRelease(&sKey);
25004 if( rc != SXRET_OK ){
25005 /* An error occured while invoking the supplied callback [i.e: not defined] */
25006 jx9_result_bool(pCtx, 0); /* return FALSE */
25007 return JX9_OK;
25008 }
25009 }
25010 /* Point to the next entry */
25011 pEntry = pEntry->pPrev; /* Reverse link */
25012 }
25013 /* All done, return TRUE */
25014 jx9_result_bool(pCtx, 1);
25015 return JX9_OK;
25016}
25017/*
25018 * Table of built-in hashmap functions.
25019 */
25020static const jx9_builtin_func aHashmapFunc[] = {
25021 {"count", jx9_hashmap_count },
25022 {"sizeof", jx9_hashmap_count },
25023 {"array_key_exists", jx9_hashmap_key_exists },
25024 {"array_pop", jx9_hashmap_pop },
25025 {"array_push", jx9_hashmap_push },
25026 {"array_shift", jx9_hashmap_shift },
25027 {"array_product", jx9_hashmap_product },
25028 {"array_sum", jx9_hashmap_sum },
25029 {"array_values", jx9_hashmap_values },
25030 {"array_same", jx9_hashmap_same },
25031 {"array_merge", jx9_hashmap_merge },
25032 {"array_diff", jx9_hashmap_diff },
25033 {"array_intersect", jx9_hashmap_intersect},
25034 {"in_array", jx9_hashmap_in_array },
25035 {"array_copy", jx9_hashmap_copy },
25036 {"array_erase", jx9_hashmap_erase },
25037 {"array_map", jx9_hashmap_map },
25038 {"array_walk", jx9_hashmap_walk },
25039 {"sort", jx9_hashmap_sort },
25040 {"rsort", jx9_hashmap_rsort },
25041 {"usort", jx9_hashmap_usort },
25042 {"current", jx9_hashmap_current },
25043 {"each", jx9_hashmap_each },
25044 {"pos", jx9_hashmap_current },
25045 {"next", jx9_hashmap_next },
25046 {"prev", jx9_hashmap_prev },
25047 {"end", jx9_hashmap_end },
25048 {"reset", jx9_hashmap_reset },
25049 {"key", jx9_hashmap_simple_key }
25050};
25051/*
25052 * Register the built-in hashmap functions defined above.
25053 */
25054JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm)
25055{
25056 sxu32 n;
25057 for( n = 0 ; n < SX_ARRAYSIZE(aHashmapFunc) ; n++ ){
25058 jx9_create_function(&(*pVm), aHashmapFunc[n].zName, aHashmapFunc[n].xFunc, 0);
25059 }
25060}
25061/*
25062 * Iterate throw hashmap entries and invoke the given callback [i.e: xWalk()] for each
25063 * retrieved entry.
25064 * Note that argument are passed to the callback by copy. That is, any modification to
25065 * the entry value in the callback body will not alter the real value.
25066 * If the callback wishes to abort processing [i.e: it's invocation] it must return
25067 * a value different from JX9_OK.
25068 * Refer to [jx9_array_walk()] for more information.
25069 */
25070JX9_PRIVATE sxi32 jx9HashmapWalk(
25071 jx9_hashmap *pMap, /* Target hashmap */
25072 int (*xWalk)(jx9_value *, jx9_value *, void *), /* Walker callback */
25073 void *pUserData /* Last argument to xWalk() */
25074 )
25075{
25076 jx9_hashmap_node *pEntry;
25077 jx9_value sKey, sValue;
25078 sxi32 rc;
25079 sxu32 n;
25080 /* Initialize walker parameter */
25081 rc = SXRET_OK;
25082 jx9MemObjInit(pMap->pVm, &sKey);
25083 jx9MemObjInit(pMap->pVm, &sValue);
25084 n = pMap->nEntry;
25085 pEntry = pMap->pFirst;
25086 /* Start the iteration process */
25087 for(;;){
25088 if( n < 1 ){
25089 break;
25090 }
25091 /* Extract a copy of the key and a copy the current value */
25092 jx9HashmapExtractNodeKey(pEntry, &sKey);
25093 jx9HashmapExtractNodeValue(pEntry, &sValue, FALSE);
25094 /* Invoke the user callback */
25095 rc = xWalk(&sKey, &sValue, pUserData);
25096 /* Release the copy of the key and the value */
25097 jx9MemObjRelease(&sKey);
25098 jx9MemObjRelease(&sValue);
25099 if( rc != JX9_OK ){
25100 /* Callback request an operation abort */
25101 return SXERR_ABORT;
25102 }
25103 /* Point to the next entry */
25104 pEntry = pEntry->pPrev; /* Reverse link */
25105 n--;
25106 }
25107 /* All done */
25108 return SXRET_OK;
25109}
25110/*
25111 * ----------------------------------------------------------
25112 * File: jx9_json.c
25113 * MD5: 31a27f8797418de511c669feed763341
25114 * ----------------------------------------------------------
25115 */
25116/*
25117 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
25118 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
25119 * Version 1.7.2
25120 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
25121 * please contact Symisc Systems via:
25122 * legal@symisc.net
25123 * licensing@symisc.net
25124 * contact@symisc.net
25125 * or visit:
25126 * http://jx9.symisc.net/
25127 */
25128 /* $SymiscID: json.c v1.0 FreeBSD 2012-12-16 00:28 stable <chm@symisc.net> $ */
25129#ifndef JX9_AMALGAMATION
25130#include "jx9Int.h"
25131#endif
25132/* This file deals with JSON serialization, decoding and stuff like that. */
25133/*
25134 * Section:
25135 * JSON encoding/decoding routines.
25136 * Authors:
25137 * Symisc Systems, devel@symisc.net.
25138 * Copyright (C) Symisc Systems, http://jx9.symisc.net
25139 * Status:
25140 * Devel.
25141 */
25142/* Forward reference */
25143static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
25144static int VmJsonObjectEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
25145/*
25146 * JSON encoder state is stored in an instance
25147 * of the following structure.
25148 */
25149typedef struct json_private_data json_private_data;
25150struct json_private_data
25151{
25152 SyBlob *pOut; /* Output consumer buffer */
25153 int isFirst; /* True if first encoded entry */
25154 int iFlags; /* JSON encoding flags */
25155 int nRecCount; /* Recursion count */
25156};
25157/*
25158 * Returns the JSON representation of a value.In other word perform a JSON encoding operation.
25159 * According to wikipedia
25160 * JSON's basic types are:
25161 * Number (double precision floating-point format in JavaScript, generally depends on implementation)
25162 * String (double-quoted Unicode, with backslash escaping)
25163 * Boolean (true or false)
25164 * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
25165 * do not need to be of the same type)
25166 * Object (an unordered collection of key:value pairs with the ':' character separating the key
25167 * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
25168 * be distinct from each other)
25169 * null (empty)
25170 * Non-significant white space may be added freely around the "structural characters"
25171 * (i.e. the brackets "[{]}", colon ":" and comma ",").
25172 */
25173static sxi32 VmJsonEncode(
25174 jx9_value *pIn, /* Encode this value */
25175 json_private_data *pData /* Context data */
25176 ){
25177 SyBlob *pOut = pData->pOut;
25178 int nByte;
25179 if( jx9_value_is_null(pIn) || jx9_value_is_resource(pIn)){
25180 /* null */
25181 SyBlobAppend(pOut, "null", sizeof("null")-1);
25182 }else if( jx9_value_is_bool(pIn) ){
25183 int iBool = jx9_value_to_bool(pIn);
25184 sxu32 iLen;
25185 /* true/false */
25186 iLen = iBool ? sizeof("true") : sizeof("false");
25187 SyBlobAppend(pOut, iBool ? "true" : "false", iLen-1);
25188 }else if( jx9_value_is_numeric(pIn) && !jx9_value_is_string(pIn) ){
25189 const char *zNum;
25190 /* Get a string representation of the number */
25191 zNum = jx9_value_to_string(pIn, &nByte);
25192 SyBlobAppend(pOut,zNum,nByte);
25193 }else if( jx9_value_is_string(pIn) ){
25194 const char *zIn, *zEnd;
25195 int c;
25196 /* Encode the string */
25197 zIn = jx9_value_to_string(pIn, &nByte);
25198 zEnd = &zIn[nByte];
25199 /* Append the double quote */
25200 SyBlobAppend(pOut,"\"", sizeof(char));
25201 for(;;){
25202 if( zIn >= zEnd ){
25203 /* No more input to process */
25204 break;
25205 }
25206 c = zIn[0];
25207 /* Advance the stream cursor */
25208 zIn++;
25209 if( c == '"' || c == '\\' ){
25210 /* Unescape the character */
25211 SyBlobAppend(pOut,"\\", sizeof(char));
25212 }
25213 /* Append character verbatim */
25214 SyBlobAppend(pOut,(const char *)&c,sizeof(char));
25215 }
25216 /* Append the double quote */
25217 SyBlobAppend(pOut,"\"",sizeof(char));
25218 }else if( jx9_value_is_json_array(pIn) ){
25219 /* Encode the array/object */
25220 pData->isFirst = 1;
25221 if( jx9_value_is_json_object(pIn) ){
25222 /* Encode the object instance */
25223 pData->isFirst = 1;
25224 /* Append the curly braces */
25225 SyBlobAppend(pOut, "{",sizeof(char));
25226 /* Iterate throw object attribute */
25227 jx9_array_walk(pIn,VmJsonObjectEncode, pData);
25228 /* Append the closing curly braces */
25229 SyBlobAppend(pOut, "}",sizeof(char));
25230 }else{
25231 /* Append the square bracket or curly braces */
25232 SyBlobAppend(pOut,"[",sizeof(char));
25233 /* Iterate throw array entries */
25234 jx9_array_walk(pIn, VmJsonArrayEncode, pData);
25235 /* Append the closing square bracket or curly braces */
25236 SyBlobAppend(pOut,"]",sizeof(char));
25237 }
25238 }else{
25239 /* Can't happen */
25240 SyBlobAppend(pOut,"null",sizeof("null")-1);
25241 }
25242 /* All done */
25243 return JX9_OK;
25244}
25245/*
25246 * The following walker callback is invoked each time we need
25247 * to encode an array to JSON.
25248 */
25249static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData)
25250{
25251 json_private_data *pJson = (json_private_data *)pUserData;
25252 if( pJson->nRecCount > 31 ){
25253 /* Recursion limit reached, return immediately */
25254 SXUNUSED(pKey); /* cc warning */
25255 return JX9_OK;
25256 }
25257 if( !pJson->isFirst ){
25258 /* Append the colon first */
25259 SyBlobAppend(pJson->pOut,",",(int)sizeof(char));
25260 }
25261 /* Encode the value */
25262 pJson->nRecCount++;
25263 VmJsonEncode(pValue, pJson);
25264 pJson->nRecCount--;
25265 pJson->isFirst = 0;
25266 return JX9_OK;
25267}
25268/*
25269 * The following walker callback is invoked each time we need to encode
25270 * a object instance [i.e: Object in the JX9 jargon] to JSON.
25271 */
25272static int VmJsonObjectEncode(jx9_value *pKey,jx9_value *pValue,void *pUserData)
25273{
25274 json_private_data *pJson = (json_private_data *)pUserData;
25275 const char *zKey;
25276 int nByte;
25277 if( pJson->nRecCount > 31 ){
25278 /* Recursion limit reached, return immediately */
25279 return JX9_OK;
25280 }
25281 if( !pJson->isFirst ){
25282 /* Append the colon first */
25283 SyBlobAppend(pJson->pOut,",",sizeof(char));
25284 }
25285 /* Extract a string representation of the key */
25286 zKey = jx9_value_to_string(pKey, &nByte);
25287 /* Append the key and the double colon */
25288 if( nByte > 0 ){
25289 SyBlobAppend(pJson->pOut,"\"",sizeof(char));
25290 SyBlobAppend(pJson->pOut,zKey,(sxu32)nByte);
25291 SyBlobAppend(pJson->pOut,"\"",sizeof(char));
25292 }else{
25293 /* Can't happen */
25294 SyBlobAppend(pJson->pOut,"null",sizeof("null")-1);
25295 }
25296 SyBlobAppend(pJson->pOut,":",sizeof(char));
25297 /* Encode the value */
25298 pJson->nRecCount++;
25299 VmJsonEncode(pValue, pJson);
25300 pJson->nRecCount--;
25301 pJson->isFirst = 0;
25302 return JX9_OK;
25303}
25304/*
25305 * Returns a string containing the JSON representation of value.
25306 * In other words, perform the serialization of the given JSON object.
25307 */
25308JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut)
25309{
25310 json_private_data sJson;
25311 /* Prepare the JSON data */
25312 sJson.nRecCount = 0;
25313 sJson.pOut = pOut;
25314 sJson.isFirst = 1;
25315 sJson.iFlags = 0;
25316 /* Perform the encoding operation */
25317 VmJsonEncode(pValue, &sJson);
25318 /* All done */
25319 return JX9_OK;
25320}
25321/* Possible tokens from the JSON tokenization process */
25322#define JSON_TK_TRUE 0x001 /* Boolean true */
25323#define JSON_TK_FALSE 0x002 /* Boolean false */
25324#define JSON_TK_STR 0x004 /* String enclosed in double quotes */
25325#define JSON_TK_NULL 0x008 /* null */
25326#define JSON_TK_NUM 0x010 /* Numeric */
25327#define JSON_TK_OCB 0x020 /* Open curly braces '{' */
25328#define JSON_TK_CCB 0x040 /* Closing curly braces '}' */
25329#define JSON_TK_OSB 0x080 /* Open square bracke '[' */
25330#define JSON_TK_CSB 0x100 /* Closing square bracket ']' */
25331#define JSON_TK_COLON 0x200 /* Single colon ':' */
25332#define JSON_TK_COMMA 0x400 /* Single comma ',' */
25333#define JSON_TK_ID 0x800 /* ID */
25334#define JSON_TK_INVALID 0x1000 /* Unexpected token */
25335/*
25336 * Tokenize an entire JSON input.
25337 * Get a single low-level token from the input file.
25338 * Update the stream pointer so that it points to the first
25339 * character beyond the extracted token.
25340 */
25341static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData)
25342{
25343 int *pJsonErr = (int *)pUserData;
25344 SyString *pStr;
25345 int c;
25346 /* Ignore leading white spaces */
25347 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
25348 /* Advance the stream cursor */
25349 if( pStream->zText[0] == '\n' ){
25350 /* Update line counter */
25351 pStream->nLine++;
25352 }
25353 pStream->zText++;
25354 }
25355 if( pStream->zText >= pStream->zEnd ){
25356 /* End of input reached */
25357 SXUNUSED(pCtxData); /* cc warning */
25358 return SXERR_EOF;
25359 }
25360 /* Record token starting position and line */
25361 pToken->nLine = pStream->nLine;
25362 pToken->pUserData = 0;
25363 pStr = &pToken->sData;
25364 SyStringInitFromBuf(pStr, pStream->zText, 0);
25365 if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
25366 /* The following code fragment is taken verbatim from the xPP source tree.
25367 * xPP is a modern embeddable macro processor with advanced features useful for
25368 * application seeking for a production quality, ready to use macro processor.
25369 * xPP is a widely used library developed and maintened by Symisc Systems.
25370 * You can reach the xPP home page by following this link:
25371 * http://xpp.symisc.net/
25372 */
25373 const unsigned char *zIn;
25374 /* Isolate UTF-8 or alphanumeric stream */
25375 if( pStream->zText[0] < 0xc0 ){
25376 pStream->zText++;
25377 }
25378 for(;;){
25379 zIn = pStream->zText;
25380 if( zIn[0] >= 0xc0 ){
25381 zIn++;
25382 /* UTF-8 stream */
25383 while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
25384 zIn++;
25385 }
25386 }
25387 /* Skip alphanumeric stream */
25388 while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
25389 zIn++;
25390 }
25391 if( zIn == pStream->zText ){
25392 /* Not an UTF-8 or alphanumeric stream */
25393 break;
25394 }
25395 /* Synchronize pointers */
25396 pStream->zText = zIn;
25397 }
25398 /* Record token length */
25399 pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
25400 /* A simple identifier */
25401 pToken->nType = JSON_TK_ID;
25402 if( pStr->nByte == sizeof("true") -1 && SyStrnicmp(pStr->zString, "true", sizeof("true")-1) == 0 ){
25403 /* boolean true */
25404 pToken->nType = JSON_TK_TRUE;
25405 }else if( pStr->nByte == sizeof("false") -1 && SyStrnicmp(pStr->zString,"false", sizeof("false")-1) == 0 ){
25406 /* boolean false */
25407 pToken->nType = JSON_TK_FALSE;
25408 }else if( pStr->nByte == sizeof("null") -1 && SyStrnicmp(pStr->zString,"null", sizeof("null")-1) == 0 ){
25409 /* NULL */
25410 pToken->nType = JSON_TK_NULL;
25411 }
25412 return SXRET_OK;
25413 }
25414 if( pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']'
25415 || pStream->zText[0] == ':' || pStream->zText[0] == ',' ){
25416 /* Single character */
25417 c = pStream->zText[0];
25418 /* Set token type */
25419 switch(c){
25420 case '[': pToken->nType = JSON_TK_OSB; break;
25421 case '{': pToken->nType = JSON_TK_OCB; break;
25422 case '}': pToken->nType = JSON_TK_CCB; break;
25423 case ']': pToken->nType = JSON_TK_CSB; break;
25424 case ':': pToken->nType = JSON_TK_COLON; break;
25425 case ',': pToken->nType = JSON_TK_COMMA; break;
25426 default:
25427 break;
25428 }
25429 /* Advance the stream cursor */
25430 pStream->zText++;
25431 }else if( pStream->zText[0] == '"') {
25432 /* JSON string */
25433 pStream->zText++;
25434 pStr->zString++;
25435 /* Delimit the string */
25436 while( pStream->zText < pStream->zEnd ){
25437 if( pStream->zText[0] == '"' && pStream->zText[-1] != '\\' ){
25438 break;
25439 }
25440 if( pStream->zText[0] == '\n' ){
25441 /* Update line counter */
25442 pStream->nLine++;
25443 }
25444 pStream->zText++;
25445 }
25446 if( pStream->zText >= pStream->zEnd ){
25447 /* Missing closing '"' */
25448 pToken->nType = JSON_TK_INVALID;
25449 *pJsonErr = SXERR_SYNTAX;
25450 }else{
25451 pToken->nType = JSON_TK_STR;
25452 pStream->zText++; /* Jump the closing double quotes */
25453 }
25454 }else if( pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25455 /* Number */
25456 pStream->zText++;
25457 pToken->nType = JSON_TK_NUM;
25458 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25459 pStream->zText++;
25460 }
25461 if( pStream->zText < pStream->zEnd ){
25462 c = pStream->zText[0];
25463 if( c == '.' ){
25464 /* Real number */
25465 pStream->zText++;
25466 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25467 pStream->zText++;
25468 }
25469 if( pStream->zText < pStream->zEnd ){
25470 c = pStream->zText[0];
25471 if( c=='e' || c=='E' ){
25472 pStream->zText++;
25473 if( pStream->zText < pStream->zEnd ){
25474 c = pStream->zText[0];
25475 if( c =='+' || c=='-' ){
25476 pStream->zText++;
25477 }
25478 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25479 pStream->zText++;
25480 }
25481 }
25482 }
25483 }
25484 }else if( c=='e' || c=='E' ){
25485 /* Real number */
25486 pStream->zText++;
25487 if( pStream->zText < pStream->zEnd ){
25488 c = pStream->zText[0];
25489 if( c =='+' || c=='-' ){
25490 pStream->zText++;
25491 }
25492 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25493 pStream->zText++;
25494 }
25495 }
25496 }
25497 }
25498 }else{
25499 /* Unexpected token */
25500 pToken->nType = JSON_TK_INVALID;
25501 /* Advance the stream cursor */
25502 pStream->zText++;
25503 *pJsonErr = SXERR_SYNTAX;
25504 /* Abort processing immediatley */
25505 return SXERR_ABORT;
25506 }
25507 /* record token length */
25508 pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
25509 if( pToken->nType == JSON_TK_STR ){
25510 pStr->nByte--;
25511 }
25512 /* Return to the lexer */
25513 return SXRET_OK;
25514}
25515/*
25516 * JSON decoded input consumer callback signature.
25517 */
25518typedef int (*ProcJSONConsumer)(jx9_context *, jx9_value *, jx9_value *, void *);
25519/*
25520 * JSON decoder state is kept in the following structure.
25521 */
25522typedef struct json_decoder json_decoder;
25523struct json_decoder
25524{
25525 jx9_context *pCtx; /* Call context */
25526 ProcJSONConsumer xConsumer; /* Consumer callback */
25527 void *pUserData; /* Last argument to xConsumer() */
25528 int iFlags; /* Configuration flags */
25529 SyToken *pIn; /* Token stream */
25530 SyToken *pEnd; /* End of the token stream */
25531 int rec_count; /* Current nesting level */
25532 int *pErr; /* JSON decoding error if any */
25533};
25534/* Forward declaration */
25535static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData);
25536/*
25537 * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store
25538 * the result in the given jx9_value.
25539 */
25540static void VmJsonDequoteString(const SyString *pStr, jx9_value *pWorker)
25541{
25542 const char *zIn = pStr->zString;
25543 const char *zEnd = &pStr->zString[pStr->nByte];
25544 const char *zCur;
25545 int c;
25546 /* Mark the value as a string */
25547 jx9_value_string(pWorker, "", 0); /* Empty string */
25548 for(;;){
25549 zCur = zIn;
25550 while( zIn < zEnd && zIn[0] != '\\' ){
25551 zIn++;
25552 }
25553 if( zIn > zCur ){
25554 /* Append chunk verbatim */
25555 jx9_value_string(pWorker, zCur, (int)(zIn-zCur));
25556 }
25557 zIn++;
25558 if( zIn >= zEnd ){
25559 /* End of the input reached */
25560 break;
25561 }
25562 c = zIn[0];
25563 /* Unescape the character */
25564 switch(c){
25565 case '"': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
25566 case '\\': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
25567 case 'n': jx9_value_string(pWorker, "\n", (int)sizeof(char)); break;
25568 case 'r': jx9_value_string(pWorker, "\r", (int)sizeof(char)); break;
25569 case 't': jx9_value_string(pWorker, "\t", (int)sizeof(char)); break;
25570 case 'f': jx9_value_string(pWorker, "\f", (int)sizeof(char)); break;
25571 default:
25572 jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char));
25573 break;
25574 }
25575 /* Advance the stream cursor */
25576 zIn++;
25577 }
25578}
25579/*
25580 * Returns a jx9_value holding the image of a JSON string. In other word perform a JSON decoding operation.
25581 * According to wikipedia
25582 * JSON's basic types are:
25583 * Number (double precision floating-point format in JavaScript, generally depends on implementation)
25584 * String (double-quoted Unicode, with backslash escaping)
25585 * Boolean (true or false)
25586 * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
25587 * do not need to be of the same type)
25588 * Object (an unordered collection of key:value pairs with the ':' character separating the key
25589 * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
25590 * be distinct from each other)
25591 * null (empty)
25592 * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ", ").
25593 */
25594static sxi32 VmJsonDecode(
25595 json_decoder *pDecoder, /* JSON decoder */
25596 jx9_value *pArrayKey /* Key for the decoded array */
25597 ){
25598 jx9_value *pWorker; /* Worker variable */
25599 sxi32 rc;
25600 /* Check if we do not nest to much */
25601 if( pDecoder->rec_count > 31 ){
25602 /* Nesting limit reached, abort decoding immediately */
25603 return SXERR_ABORT;
25604 }
25605 if( pDecoder->pIn->nType & (JSON_TK_STR|JSON_TK_ID|JSON_TK_TRUE|JSON_TK_FALSE|JSON_TK_NULL|JSON_TK_NUM) ){
25606 /* Scalar value */
25607 pWorker = jx9_context_new_scalar(pDecoder->pCtx);
25608 if( pWorker == 0 ){
25609 jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
25610 /* Abort the decoding operation immediately */
25611 return SXERR_ABORT;
25612 }
25613 /* Reflect the JSON image */
25614 if( pDecoder->pIn->nType & JSON_TK_NULL ){
25615 /* Nullify the value.*/
25616 jx9_value_null(pWorker);
25617 }else if( pDecoder->pIn->nType & (JSON_TK_TRUE|JSON_TK_FALSE) ){
25618 /* Boolean value */
25619 jx9_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0 );
25620 }else if( pDecoder->pIn->nType & JSON_TK_NUM ){
25621 SyString *pStr = &pDecoder->pIn->sData;
25622 /*
25623 * Numeric value.
25624 * Get a string representation first then try to get a numeric
25625 * value.
25626 */
25627 jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
25628 /* Obtain a numeric representation */
25629 jx9MemObjToNumeric(pWorker);
25630 }else if( pDecoder->pIn->nType & JSON_TK_ID ){
25631 SyString *pStr = &pDecoder->pIn->sData;
25632 jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
25633 }else{
25634 /* Dequote the string */
25635 VmJsonDequoteString(&pDecoder->pIn->sData, pWorker);
25636 }
25637 /* Invoke the consumer callback */
25638 rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData);
25639 if( rc == SXERR_ABORT ){
25640 return SXERR_ABORT;
25641 }
25642 /* All done, advance the stream cursor */
25643 pDecoder->pIn++;
25644 }else if( pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) {
25645 ProcJSONConsumer xOld;
25646 void *pOld;
25647 /* Array representation*/
25648 pDecoder->pIn++;
25649 /* Create a working array */
25650 pWorker = jx9_context_new_array(pDecoder->pCtx);
25651 if( pWorker == 0 ){
25652 jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
25653 /* Abort the decoding operation immediately */
25654 return SXERR_ABORT;
25655 }
25656 /* Save the old consumer */
25657 xOld = pDecoder->xConsumer;
25658 pOld = pDecoder->pUserData;
25659 /* Set the new consumer */
25660 pDecoder->xConsumer = VmJsonArrayDecoder;
25661 pDecoder->pUserData = pWorker;
25662 /* Decode the array */
25663 for(;;){
25664 /* Jump trailing comma. Note that the standard JX9 engine will not let you
25665 * do this.
25666 */
25667 while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
25668 pDecoder->pIn++;
25669 }
25670 if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/ ){
25671 if( pDecoder->pIn < pDecoder->pEnd ){
25672 pDecoder->pIn++; /* Jump the trailing ']' */
25673 }
25674 break;
25675 }
25676 /* Recurse and decode the entry */
25677 pDecoder->rec_count++;
25678 rc = VmJsonDecode(pDecoder, 0);
25679 pDecoder->rec_count--;
25680 if( rc == SXERR_ABORT ){
25681 /* Abort processing immediately */
25682 return SXERR_ABORT;
25683 }
25684 /*The cursor is automatically advanced by the VmJsonDecode() function */
25685 if( (pDecoder->pIn < pDecoder->pEnd) &&
25686 ((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/|JSON_TK_COMMA/*','*/))==0) ){
25687 /* Unexpected token, abort immediatley */
25688 *pDecoder->pErr = SXERR_SYNTAX;
25689 return SXERR_ABORT;
25690 }
25691 }
25692 /* Restore the old consumer */
25693 pDecoder->xConsumer = xOld;
25694 pDecoder->pUserData = pOld;
25695 /* Invoke the old consumer on the decoded array */
25696 xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
25697 }else if( pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) {
25698 ProcJSONConsumer xOld;
25699 jx9_value *pKey;
25700 void *pOld;
25701 /* Object representation*/
25702 pDecoder->pIn++;
25703 /* Create a working array */
25704 pWorker = jx9_context_new_array(pDecoder->pCtx);
25705 pKey = jx9_context_new_scalar(pDecoder->pCtx);
25706 if( pWorker == 0 || pKey == 0){
25707 jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
25708 /* Abort the decoding operation immediately */
25709 return SXERR_ABORT;
25710 }
25711 /* Save the old consumer */
25712 xOld = pDecoder->xConsumer;
25713 pOld = pDecoder->pUserData;
25714 /* Set the new consumer */
25715 pDecoder->xConsumer = VmJsonArrayDecoder;
25716 pDecoder->pUserData = pWorker;
25717 /* Decode the object */
25718 for(;;){
25719 /* Jump trailing comma. Note that the standard JX9 engine will not let you
25720 * do this.
25721 */
25722 while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
25723 pDecoder->pIn++;
25724 }
25725 if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/ ){
25726 if( pDecoder->pIn < pDecoder->pEnd ){
25727 pDecoder->pIn++; /* Jump the trailing ']' */
25728 }
25729 break;
25730 }
25731 if( (pDecoder->pIn->nType & (JSON_TK_ID|JSON_TK_STR)) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd
25732 || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0){
25733 /* Syntax error, return immediately */
25734 *pDecoder->pErr = SXERR_SYNTAX;
25735 return SXERR_ABORT;
25736 }
25737 if( pDecoder->pIn->nType & JSON_TK_ID ){
25738 SyString *pStr = &pDecoder->pIn->sData;
25739 jx9_value_string(pKey, pStr->zString, (int)pStr->nByte);
25740 }else{
25741 /* Dequote the key */
25742 VmJsonDequoteString(&pDecoder->pIn->sData, pKey);
25743 }
25744 /* Jump the key and the colon */
25745 pDecoder->pIn += 2;
25746 /* Recurse and decode the value */
25747 pDecoder->rec_count++;
25748 rc = VmJsonDecode(pDecoder, pKey);
25749 pDecoder->rec_count--;
25750 if( rc == SXERR_ABORT ){
25751 /* Abort processing immediately */
25752 return SXERR_ABORT;
25753 }
25754 /* Reset the internal buffer of the key */
25755 jx9_value_reset_string_cursor(pKey);
25756 /*The cursor is automatically advanced by the VmJsonDecode() function */
25757 }
25758 /* Restore the old consumer */
25759 pDecoder->xConsumer = xOld;
25760 pDecoder->pUserData = pOld;
25761 /* Invoke the old consumer on the decoded object*/
25762 xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
25763 /* Release the key */
25764 jx9_context_release_value(pDecoder->pCtx, pKey);
25765 }else{
25766 /* Unexpected token */
25767 return SXERR_ABORT; /* Abort immediately */
25768 }
25769 /* Release the worker variable */
25770 jx9_context_release_value(pDecoder->pCtx, pWorker);
25771 return SXRET_OK;
25772}
25773/*
25774 * The following JSON decoder callback is invoked each time
25775 * a JSON array representation [i.e: [15, "hello", FALSE] ]
25776 * is being decoded.
25777 */
25778static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
25779{
25780 jx9_value *pArray = (jx9_value *)pUserData;
25781 /* Insert the entry */
25782 jx9_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */
25783 SXUNUSED(pCtx); /* cc warning */
25784 /* All done */
25785 return SXRET_OK;
25786}
25787/*
25788 * Standard JSON decoder callback.
25789 */
25790static int VmJsonDefaultDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
25791{
25792 /* Return the value directly */
25793 jx9_result_value(pCtx, pWorker); /* Will make it's own copy */
25794 SXUNUSED(pKey); /* cc warning */
25795 SXUNUSED(pUserData);
25796 /* All done */
25797 return SXRET_OK;
25798}
25799/*
25800 * Exported JSON decoding interface
25801 */
25802JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte)
25803{
25804 jx9_vm *pVm = pCtx->pVm;
25805 json_decoder sDecoder;
25806 SySet sToken;
25807 SyLex sLex;
25808 sxi32 rc;
25809 /* Tokenize the input */
25810 SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken));
25811 rc = SXRET_OK;
25812 SyLexInit(&sLex, &sToken, VmJsonTokenize, &rc);
25813 SyLexTokenizeInput(&sLex,zJSON,(sxu32)nByte, 0, 0, 0);
25814 if( rc != SXRET_OK ){
25815 /* Something goes wrong while tokenizing input. [i.e: Unexpected token] */
25816 SyLexRelease(&sLex);
25817 SySetRelease(&sToken);
25818 /* return NULL */
25819 jx9_result_null(pCtx);
25820 return JX9_OK;
25821 }
25822 /* Fill the decoder */
25823 sDecoder.pCtx = pCtx;
25824 sDecoder.pErr = &rc;
25825 sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken);
25826 sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)];
25827 sDecoder.iFlags = 0;
25828 sDecoder.rec_count = 0;
25829 /* Set a default consumer */
25830 sDecoder.xConsumer = VmJsonDefaultDecoder;
25831 sDecoder.pUserData = 0;
25832 /* Decode the raw JSON input */
25833 rc = VmJsonDecode(&sDecoder, 0);
25834 if( rc == SXERR_ABORT ){
25835 /*
25836 * Something goes wrong while decoding JSON input.Return NULL.
25837 */
25838 jx9_result_null(pCtx);
25839 }
25840 /* Clean-up the mess left behind */
25841 SyLexRelease(&sLex);
25842 SySetRelease(&sToken);
25843 /* All done */
25844 return JX9_OK;
25845}
25846/*
25847 * ----------------------------------------------------------
25848 * File: jx9_lex.c
25849 * MD5: a79518c0635dbaf5dcfaca62efa2faf8
25850 * ----------------------------------------------------------
25851 */
25852/*
25853 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
25854 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
25855 * Version 1.7.2
25856 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
25857 * please contact Symisc Systems via:
25858 * legal@symisc.net
25859 * licensing@symisc.net
25860 * contact@symisc.net
25861 * or visit:
25862 * http://jx9.symisc.net/
25863 */
25864 /* $SymiscID: lex.c v1.0 FreeBSD 2012-12-09 00:19 stable <chm@symisc.net> $ */
25865#ifndef JX9_AMALGAMATION
25866#include "jx9Int.h"
25867#endif
25868/* This file implements a thread-safe and full reentrant lexical analyzer for the Jx9 programming language */
25869/* Forward declarations */
25870static sxu32 keywordCode(const char *z,int n);
25871static sxi32 LexExtractNowdoc(SyStream *pStream,SyToken *pToken);
25872/*
25873 * Tokenize a raw jx9 input.
25874 * Get a single low-level token from the input file. Update the stream pointer so that
25875 * it points to the first character beyond the extracted token.
25876 */
25877static sxi32 jx9TokenizeInput(SyStream *pStream,SyToken *pToken,void *pUserData,void *pCtxData)
25878{
25879 SyString *pStr;
25880 sxi32 rc;
25881 /* Ignore leading white spaces */
25882 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
25883 /* Advance the stream cursor */
25884 if( pStream->zText[0] == '\n' ){
25885 /* Update line counter */
25886 pStream->nLine++;
25887 }
25888 pStream->zText++;
25889 }
25890 if( pStream->zText >= pStream->zEnd ){
25891 /* End of input reached */
25892 return SXERR_EOF;
25893 }
25894 /* Record token starting position and line */
25895 pToken->nLine = pStream->nLine;
25896 pToken->pUserData = 0;
25897 pStr = &pToken->sData;
25898 SyStringInitFromBuf(pStr, pStream->zText, 0);
25899 if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
25900 /* The following code fragment is taken verbatim from the xPP source tree.
25901 * xPP is a modern embeddable macro processor with advanced features useful for
25902 * application seeking for a production quality, ready to use macro processor.
25903 * xPP is a widely used library developed and maintened by Symisc Systems.
25904 * You can reach the xPP home page by following this link:
25905 * http://xpp.symisc.net/
25906 */
25907 const unsigned char *zIn;
25908 sxu32 nKeyword;
25909 /* Isolate UTF-8 or alphanumeric stream */
25910 if( pStream->zText[0] < 0xc0 ){
25911 pStream->zText++;
25912 }
25913 for(;;){
25914 zIn = pStream->zText;
25915 if( zIn[0] >= 0xc0 ){
25916 zIn++;
25917 /* UTF-8 stream */
25918 while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
25919 zIn++;
25920 }
25921 }
25922 /* Skip alphanumeric stream */
25923 while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
25924 zIn++;
25925 }
25926 if( zIn == pStream->zText ){
25927 /* Not an UTF-8 or alphanumeric stream */
25928 break;
25929 }
25930 /* Synchronize pointers */
25931 pStream->zText = zIn;
25932 }
25933 /* Record token length */
25934 pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
25935 nKeyword = keywordCode(pStr->zString, (int)pStr->nByte);
25936 if( nKeyword != JX9_TK_ID ){
25937 /* We are dealing with a keyword [i.e: if, function, CREATE, ...], save the keyword ID */
25938 pToken->nType = JX9_TK_KEYWORD;
25939 pToken->pUserData = SX_INT_TO_PTR(nKeyword);
25940 }else{
25941 /* A simple identifier */
25942 pToken->nType = JX9_TK_ID;
25943 }
25944 }else{
25945 sxi32 c;
25946 /* Non-alpha stream */
25947 if( pStream->zText[0] == '#' ||
25948 ( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '/') ){
25949 pStream->zText++;
25950 /* Inline comments */
25951 while( pStream->zText < pStream->zEnd && pStream->zText[0] != '\n' ){
25952 pStream->zText++;
25953 }
25954 /* Tell the upper-layer to ignore this token */
25955 return SXERR_CONTINUE;
25956 }else if( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '*' ){
25957 pStream->zText += 2;
25958 /* Block comment */
25959 while( pStream->zText < pStream->zEnd ){
25960 if( pStream->zText[0] == '*' ){
25961 if( &pStream->zText[1] >= pStream->zEnd || pStream->zText[1] == '/' ){
25962 break;
25963 }
25964 }
25965 if( pStream->zText[0] == '\n' ){
25966 pStream->nLine++;
25967 }
25968 pStream->zText++;
25969 }
25970 pStream->zText += 2;
25971 /* Tell the upper-layer to ignore this token */
25972 return SXERR_CONTINUE;
25973 }else if( SyisDigit(pStream->zText[0]) ){
25974 pStream->zText++;
25975 /* Decimal digit stream */
25976 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25977 pStream->zText++;
25978 }
25979 /* Mark the token as integer until we encounter a real number */
25980 pToken->nType = JX9_TK_INTEGER;
25981 if( pStream->zText < pStream->zEnd ){
25982 c = pStream->zText[0];
25983 if( c == '.' ){
25984 /* Real number */
25985 pStream->zText++;
25986 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25987 pStream->zText++;
25988 }
25989 if( pStream->zText < pStream->zEnd ){
25990 c = pStream->zText[0];
25991 if( c=='e' || c=='E' ){
25992 pStream->zText++;
25993 if( pStream->zText < pStream->zEnd ){
25994 c = pStream->zText[0];
25995 if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd &&
25996 pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){
25997 pStream->zText++;
25998 }
25999 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
26000 pStream->zText++;
26001 }
26002 }
26003 }
26004 }
26005 pToken->nType = JX9_TK_REAL;
26006 }else if( c=='e' || c=='E' ){
26007 SXUNUSED(pUserData); /* Prevent compiler warning */
26008 SXUNUSED(pCtxData);
26009 pStream->zText++;
26010 if( pStream->zText < pStream->zEnd ){
26011 c = pStream->zText[0];
26012 if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd &&
26013 pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){
26014 pStream->zText++;
26015 }
26016 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
26017 pStream->zText++;
26018 }
26019 }
26020 pToken->nType = JX9_TK_REAL;
26021 }else if( c == 'x' || c == 'X' ){
26022 /* Hex digit stream */
26023 pStream->zText++;
26024 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisHex(pStream->zText[0]) ){
26025 pStream->zText++;
26026 }
26027 }else if(c == 'b' || c == 'B' ){
26028 /* Binary digit stream */
26029 pStream->zText++;
26030 while( pStream->zText < pStream->zEnd && (pStream->zText[0] == '0' || pStream->zText[0] == '1') ){
26031 pStream->zText++;
26032 }
26033 }
26034 }
26035 /* Record token length */
26036 pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
26037 return SXRET_OK;
26038 }
26039 c = pStream->zText[0];
26040 pStream->zText++; /* Advance the stream cursor */
26041 /* Assume we are dealing with an operator*/
26042 pToken->nType = JX9_TK_OP;
26043 switch(c){
26044 case '$': pToken->nType = JX9_TK_DOLLAR; break;
26045 case '{': pToken->nType = JX9_TK_OCB; break;
26046 case '}': pToken->nType = JX9_TK_CCB; break;
26047 case '(': pToken->nType = JX9_TK_LPAREN; break;
26048 case '[': pToken->nType |= JX9_TK_OSB; break; /* Bitwise operation here, since the square bracket token '['
26049 * is a potential operator [i.e: subscripting] */
26050 case ']': pToken->nType = JX9_TK_CSB; break;
26051 case ')': {
26052 SySet *pTokSet = pStream->pSet;
26053 /* Assemble type cast operators [i.e: (int), (float), (bool)...] */
26054 if( pTokSet->nUsed >= 2 ){
26055 SyToken *pTmp;
26056 /* Peek the last recongnized token */
26057 pTmp = (SyToken *)SySetPeek(pTokSet);
26058 if( pTmp->nType & JX9_TK_KEYWORD ){
26059 sxi32 nID = SX_PTR_TO_INT(pTmp->pUserData);
26060 if( (sxu32)nID & (JX9_TKWRD_INT|JX9_TKWRD_FLOAT|JX9_TKWRD_STRING|JX9_TKWRD_BOOL) ){
26061 pTmp = (SyToken *)SySetAt(pTokSet, pTokSet->nUsed - 2);
26062 if( pTmp->nType & JX9_TK_LPAREN ){
26063 /* Merge the three tokens '(' 'TYPE' ')' into a single one */
26064 const char * zTypeCast = "(int)";
26065 if( nID & JX9_TKWRD_FLOAT ){
26066 zTypeCast = "(float)";
26067 }else if( nID & JX9_TKWRD_BOOL ){
26068 zTypeCast = "(bool)";
26069 }else if( nID & JX9_TKWRD_STRING ){
26070 zTypeCast = "(string)";
26071 }
26072 /* Reflect the change */
26073 pToken->nType = JX9_TK_OP;
26074 SyStringInitFromBuf(&pToken->sData, zTypeCast, SyStrlen(zTypeCast));
26075 /* Save the instance associated with the type cast operator */
26076 pToken->pUserData = (void *)jx9ExprExtractOperator(&pToken->sData, 0);
26077 /* Remove the two previous tokens */
26078 pTokSet->nUsed -= 2;
26079 return SXRET_OK;
26080 }
26081 }
26082 }
26083 }
26084 pToken->nType = JX9_TK_RPAREN;
26085 break;
26086 }
26087 case '\'':{
26088 /* Single quoted string */
26089 pStr->zString++;
26090 while( pStream->zText < pStream->zEnd ){
26091 if( pStream->zText[0] == '\'' ){
26092 if( pStream->zText[-1] != '\\' ){
26093 break;
26094 }else{
26095 const unsigned char *zPtr = &pStream->zText[-2];
26096 sxi32 i = 1;
26097 while( zPtr > pStream->zInput && zPtr[0] == '\\' ){
26098 zPtr--;
26099 i++;
26100 }
26101 if((i&1)==0){
26102 break;
26103 }
26104 }
26105 }
26106 if( pStream->zText[0] == '\n' ){
26107 pStream->nLine++;
26108 }
26109 pStream->zText++;
26110 }
26111 /* Record token length and type */
26112 pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
26113 pToken->nType = JX9_TK_SSTR;
26114 /* Jump the trailing single quote */
26115 pStream->zText++;
26116 return SXRET_OK;
26117 }
26118 case '"':{
26119 sxi32 iNest;
26120 /* Double quoted string */
26121 pStr->zString++;
26122 while( pStream->zText < pStream->zEnd ){
26123 if( pStream->zText[0] == '{' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '$'){
26124 iNest = 1;
26125 pStream->zText++;
26126 /* TICKET 1433-40: Hnadle braces'{}' in double quoted string where everything is allowed */
26127 while(pStream->zText < pStream->zEnd ){
26128 if( pStream->zText[0] == '{' ){
26129 iNest++;
26130 }else if (pStream->zText[0] == '}' ){
26131 iNest--;
26132 if( iNest <= 0 ){
26133 pStream->zText++;
26134 break;
26135 }
26136 }else if( pStream->zText[0] == '\n' ){
26137 pStream->nLine++;
26138 }
26139 pStream->zText++;
26140 }
26141 if( pStream->zText >= pStream->zEnd ){
26142 break;
26143 }
26144 }
26145 if( pStream->zText[0] == '"' ){
26146 if( pStream->zText[-1] != '\\' ){
26147 break;
26148 }else{
26149 const unsigned char *zPtr = &pStream->zText[-2];
26150 sxi32 i = 1;
26151 while( zPtr > pStream->zInput && zPtr[0] == '\\' ){
26152 zPtr--;
26153 i++;
26154 }
26155 if((i&1)==0){
26156 break;
26157 }
26158 }
26159 }
26160 if( pStream->zText[0] == '\n' ){
26161 pStream->nLine++;
26162 }
26163 pStream->zText++;
26164 }
26165 /* Record token length and type */
26166 pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
26167 pToken->nType = JX9_TK_DSTR;
26168 /* Jump the trailing quote */
26169 pStream->zText++;
26170 return SXRET_OK;
26171 }
26172 case ':':
26173 pToken->nType = JX9_TK_COLON; /* Single colon */
26174 break;
26175 case ',': pToken->nType |= JX9_TK_COMMA; break; /* Comma is also an operator */
26176 case ';': pToken->nType = JX9_TK_SEMI; break;
26177 /* Handle combined operators [i.e: +=, ===, !=== ...] */
26178 case '=':
26179 pToken->nType |= JX9_TK_EQUAL;
26180 if( pStream->zText < pStream->zEnd ){
26181 if( pStream->zText[0] == '=' ){
26182 pToken->nType &= ~JX9_TK_EQUAL;
26183 /* Current operator: == */
26184 pStream->zText++;
26185 if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26186 /* Current operator: === */
26187 pStream->zText++;
26188 }
26189 }
26190 }
26191 break;
26192 case '!':
26193 if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26194 /* Current operator: != */
26195 pStream->zText++;
26196 if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26197 /* Current operator: !== */
26198 pStream->zText++;
26199 }
26200 }
26201 break;
26202 case '&':
26203 pToken->nType |= JX9_TK_AMPER;
26204 if( pStream->zText < pStream->zEnd ){
26205 if( pStream->zText[0] == '&' ){
26206 pToken->nType &= ~JX9_TK_AMPER;
26207 /* Current operator: && */
26208 pStream->zText++;
26209 }else if( pStream->zText[0] == '=' ){
26210 pToken->nType &= ~JX9_TK_AMPER;
26211 /* Current operator: &= */
26212 pStream->zText++;
26213 }
26214 }
26215 case '.':
26216 if( pStream->zText < pStream->zEnd && (pStream->zText[0] == '.' || pStream->zText[0] == '=') ){
26217 /* Concatenation operator: '..' or '.=' */
26218 pStream->zText++;
26219 }
26220 break;
26221 case '|':
26222 if( pStream->zText < pStream->zEnd ){
26223 if( pStream->zText[0] == '|' ){
26224 /* Current operator: || */
26225 pStream->zText++;
26226 }else if( pStream->zText[0] == '=' ){
26227 /* Current operator: |= */
26228 pStream->zText++;
26229 }
26230 }
26231 break;
26232 case '+':
26233 if( pStream->zText < pStream->zEnd ){
26234 if( pStream->zText[0] == '+' ){
26235 /* Current operator: ++ */
26236 pStream->zText++;
26237 }else if( pStream->zText[0] == '=' ){
26238 /* Current operator: += */
26239 pStream->zText++;
26240 }
26241 }
26242 break;
26243 case '-':
26244 if( pStream->zText < pStream->zEnd ){
26245 if( pStream->zText[0] == '-' ){
26246 /* Current operator: -- */
26247 pStream->zText++;
26248 }else if( pStream->zText[0] == '=' ){
26249 /* Current operator: -= */
26250 pStream->zText++;
26251 }else if( pStream->zText[0] == '>' ){
26252 /* Current operator: -> */
26253 pStream->zText++;
26254 }
26255 }
26256 break;
26257 case '*':
26258 if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26259 /* Current operator: *= */
26260 pStream->zText++;
26261 }
26262 break;
26263 case '/':
26264 if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26265 /* Current operator: /= */
26266 pStream->zText++;
26267 }
26268 break;
26269 case '%':
26270 if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26271 /* Current operator: %= */
26272 pStream->zText++;
26273 }
26274 break;
26275 case '^':
26276 if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26277 /* Current operator: ^= */
26278 pStream->zText++;
26279 }
26280 break;
26281 case '<':
26282 if( pStream->zText < pStream->zEnd ){
26283 if( pStream->zText[0] == '<' ){
26284 /* Current operator: << */
26285 pStream->zText++;
26286 if( pStream->zText < pStream->zEnd ){
26287 if( pStream->zText[0] == '=' ){
26288 /* Current operator: <<= */
26289 pStream->zText++;
26290 }else if( pStream->zText[0] == '<' ){
26291 /* Current Token: <<< */
26292 pStream->zText++;
26293 /* This may be the beginning of a Heredoc/Nowdoc string, try to delimit it */
26294 rc = LexExtractNowdoc(&(*pStream), &(*pToken));
26295 if( rc == SXRET_OK ){
26296 /* Here/Now doc successfuly extracted */
26297 return SXRET_OK;
26298 }
26299 }
26300 }
26301 }else if( pStream->zText[0] == '>' ){
26302 /* Current operator: <> */
26303 pStream->zText++;
26304 }else if( pStream->zText[0] == '=' ){
26305 /* Current operator: <= */
26306 pStream->zText++;
26307 }
26308 }
26309 break;
26310 case '>':
26311 if( pStream->zText < pStream->zEnd ){
26312 if( pStream->zText[0] == '>' ){
26313 /* Current operator: >> */
26314 pStream->zText++;
26315 if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26316 /* Current operator: >>= */
26317 pStream->zText++;
26318 }
26319 }else if( pStream->zText[0] == '=' ){
26320 /* Current operator: >= */
26321 pStream->zText++;
26322 }
26323 }
26324 break;
26325 default:
26326 break;
26327 }
26328 if( pStr->nByte <= 0 ){
26329 /* Record token length */
26330 pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
26331 }
26332 if( pToken->nType & JX9_TK_OP ){
26333 const jx9_expr_op *pOp;
26334 /* Check if the extracted token is an operator */
26335 pOp = jx9ExprExtractOperator(pStr, (SyToken *)SySetPeek(pStream->pSet));
26336 if( pOp == 0 ){
26337 /* Not an operator */
26338 pToken->nType &= ~JX9_TK_OP;
26339 if( pToken->nType <= 0 ){
26340 pToken->nType = JX9_TK_OTHER;
26341 }
26342 }else{
26343 /* Save the instance associated with this operator for later processing */
26344 pToken->pUserData = (void *)pOp;
26345 }
26346 }
26347 }
26348 /* Tell the upper-layer to save the extracted token for later processing */
26349 return SXRET_OK;
26350}
26351/***** This file contains automatically generated code ******
26352**
26353** The code in this file has been automatically generated by
26354**
26355** $Header: /sqlite/sqlite/tool/mkkeywordhash.c,v 1.38 2011/12/21 01:00:46 <chm@symisc.net> $
26356**
26357** The code in this file implements a function that determines whether
26358** or not a given identifier is really a JX9 keyword. The same thing
26359** might be implemented more directly using a hand-written hash table.
26360** But by using this automatically generated code, the size of the code
26361** is substantially reduced. This is important for embedded applications
26362** on platforms with limited memory.
26363*/
26364/* Hash score: 35 */
26365static sxu32 keywordCode(const char *z, int n)
26366{
26367 /* zText[] encodes 188 bytes of keywords in 128 bytes */
26368 /* printegereturnconstaticaselseifloatincludefaultDIEXITcontinue */
26369 /* diewhileASPRINTbooleanbreakforeachfunctionimportstringswitch */
26370 /* uplink */
26371 static const char zText[127] = {
26372 'p','r','i','n','t','e','g','e','r','e','t','u','r','n','c','o','n','s',
26373 't','a','t','i','c','a','s','e','l','s','e','i','f','l','o','a','t','i',
26374 'n','c','l','u','d','e','f','a','u','l','t','D','I','E','X','I','T','c',
26375 'o','n','t','i','n','u','e','d','i','e','w','h','i','l','e','A','S','P',
26376 'R','I','N','T','b','o','o','l','e','a','n','b','r','e','a','k','f','o',
26377 'r','e','a','c','h','f','u','n','c','t','i','o','n','i','m','p','o','r',
26378 't','s','t','r','i','n','g','s','w','i','t','c','h','u','p','l','i','n',
26379 'k',
26380 };
26381 static const unsigned char aHash[59] = {
26382 0, 0, 0, 0, 15, 0, 30, 0, 0, 2, 19, 18, 0,
26383 0, 10, 3, 12, 0, 28, 29, 23, 0, 13, 22, 0, 0,
26384 14, 24, 25, 31, 11, 0, 0, 0, 0, 1, 5, 0, 0,
26385 20, 0, 27, 9, 0, 0, 0, 8, 0, 0, 26, 6, 0,
26386 0, 17, 0, 0, 0, 0, 0,
26387 };
26388 static const unsigned char aNext[31] = {
26389 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
26390 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 21, 7,
26391 0, 0, 0, 0, 0,
26392 };
26393 static const unsigned char aLen[31] = {
26394 5, 7, 3, 6, 5, 6, 4, 2, 6, 4, 2, 5, 7,
26395 7, 3, 4, 8, 3, 5, 2, 5, 4, 7, 5, 3, 7,
26396 8, 6, 6, 6, 6,
26397 };
26398 static const sxu16 aOffset[31] = {
26399 0, 2, 2, 8, 14, 17, 22, 23, 25, 25, 29, 30, 35,
26400 40, 47, 49, 53, 61, 64, 69, 71, 76, 76, 83, 88, 88,
26401 95, 103, 109, 115, 121,
26402 };
26403 static const sxu32 aCode[31] = {
26404 JX9_TKWRD_PRINT, JX9_TKWRD_INT, JX9_TKWRD_INT, JX9_TKWRD_RETURN, JX9_TKWRD_CONST,
26405 JX9_TKWRD_STATIC, JX9_TKWRD_CASE, JX9_TKWRD_AS, JX9_TKWRD_ELIF, JX9_TKWRD_ELSE,
26406 JX9_TKWRD_IF, JX9_TKWRD_FLOAT, JX9_TKWRD_INCLUDE, JX9_TKWRD_DEFAULT, JX9_TKWRD_DIE,
26407 JX9_TKWRD_EXIT, JX9_TKWRD_CONTINUE, JX9_TKWRD_DIE, JX9_TKWRD_WHILE, JX9_TKWRD_AS,
26408 JX9_TKWRD_PRINT, JX9_TKWRD_BOOL, JX9_TKWRD_BOOL, JX9_TKWRD_BREAK, JX9_TKWRD_FOR,
26409 JX9_TKWRD_FOREACH, JX9_TKWRD_FUNCTION, JX9_TKWRD_IMPORT, JX9_TKWRD_STRING, JX9_TKWRD_SWITCH,
26410 JX9_TKWRD_UPLINK,
26411 };
26412 int h, i;
26413 if( n<2 ) return JX9_TK_ID;
26414 h = (((int)z[0]*4) ^ ((int)z[n-1]*3) ^ n) % 59;
26415 for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){
26416 if( (int)aLen[i]==n && SyMemcmp(&zText[aOffset[i]],z,n)==0 ){
26417 /* JX9_TKWRD_PRINT */
26418 /* JX9_TKWRD_INT */
26419 /* JX9_TKWRD_INT */
26420 /* JX9_TKWRD_RETURN */
26421 /* JX9_TKWRD_CONST */
26422 /* JX9_TKWRD_STATIC */
26423 /* JX9_TKWRD_CASE */
26424 /* JX9_TKWRD_AS */
26425 /* JX9_TKWRD_ELIF */
26426 /* JX9_TKWRD_ELSE */
26427 /* JX9_TKWRD_IF */
26428 /* JX9_TKWRD_FLOAT */
26429 /* JX9_TKWRD_INCLUDE */
26430 /* JX9_TKWRD_DEFAULT */
26431 /* JX9_TKWRD_DIE */
26432 /* JX9_TKWRD_EXIT */
26433 /* JX9_TKWRD_CONTINUE */
26434 /* JX9_TKWRD_DIE */
26435 /* JX9_TKWRD_WHILE */
26436 /* JX9_TKWRD_AS */
26437 /* JX9_TKWRD_PRINT */
26438 /* JX9_TKWRD_BOOL */
26439 /* JX9_TKWRD_BOOL */
26440 /* JX9_TKWRD_BREAK */
26441 /* JX9_TKWRD_FOR */
26442 /* JX9_TKWRD_FOREACH */
26443 /* JX9_TKWRD_FUNCTION */
26444 /* JX9_TKWRD_IMPORT */
26445 /* JX9_TKWRD_STRING */
26446 /* JX9_TKWRD_SWITCH */
26447 /* JX9_TKWRD_UPLINK */
26448 return aCode[i];
26449 }
26450 }
26451 return JX9_TK_ID;
26452}
26453/*
26454 * Extract a heredoc/nowdoc text from a raw JX9 input.
26455 * According to the JX9 language reference manual:
26456 * A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
26457 * is provided, then a newline. The string itself follows, and then the same identifier again
26458 * to close the quotation.
26459 * The closing identifier must begin in the first column of the line. Also, the identifier must
26460 * follow the same naming rules as any other label in JX9: it must contain only alphanumeric
26461 * characters and underscores, and must start with a non-digit character or underscore.
26462 * Heredoc text behaves just like a double-quoted string, without the double quotes.
26463 * This means that quotes in a heredoc do not need to be escaped, but the escape codes listed
26464 * above can still be used. Variables are expanded, but the same care must be taken when expressing
26465 * complex variables inside a heredoc as with strings.
26466 * Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
26467 * A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
26468 * The construct is ideal for embedding JX9 code or other large blocks of text without the need
26469 * for escaping. It shares some features in common with the SGML <![CDATA[ ]]> construct, in that
26470 * it declares a block of text which is not for parsing.
26471 * A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier which follows
26472 * is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc identifiers also apply to nowdoc
26473 * identifiers, especially those regarding the appearance of the closing identifier.
26474 */
26475static sxi32 LexExtractNowdoc(SyStream *pStream, SyToken *pToken)
26476{
26477 const unsigned char *zIn = pStream->zText;
26478 const unsigned char *zEnd = pStream->zEnd;
26479 const unsigned char *zPtr;
26480 SyString sDelim;
26481 SyString sStr;
26482 /* Jump leading white spaces */
26483 while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){
26484 zIn++;
26485 }
26486 if( zIn >= zEnd ){
26487 /* A simple symbol, return immediately */
26488 return SXERR_CONTINUE;
26489 }
26490 if( zIn[0] == '\'' || zIn[0] == '"' ){
26491 zIn++;
26492 }
26493 if( zIn[0] < 0xc0 && !SyisAlphaNum(zIn[0]) && zIn[0] != '_' ){
26494 /* Invalid delimiter, return immediately */
26495 return SXERR_CONTINUE;
26496 }
26497 /* Isolate the identifier */
26498 sDelim.zString = (const char *)zIn;
26499 for(;;){
26500 zPtr = zIn;
26501 /* Skip alphanumeric stream */
26502 while( zPtr < zEnd && zPtr[0] < 0xc0 && (SyisAlphaNum(zPtr[0]) || zPtr[0] == '_') ){
26503 zPtr++;
26504 }
26505 if( zPtr < zEnd && zPtr[0] >= 0xc0 ){
26506 zPtr++;
26507 /* UTF-8 stream */
26508 while( zPtr < zEnd && ((zPtr[0] & 0xc0) == 0x80) ){
26509 zPtr++;
26510 }
26511 }
26512 if( zPtr == zIn ){
26513 /* Not an UTF-8 or alphanumeric stream */
26514 break;
26515 }
26516 /* Synchronize pointers */
26517 zIn = zPtr;
26518 }
26519 /* Get the identifier length */
26520 sDelim.nByte = (sxu32)((const char *)zIn-sDelim.zString);
26521 if( zIn[0] == '"' || zIn[0] == '\'' ){
26522 /* Jump the trailing single quote */
26523 zIn++;
26524 }
26525 /* Jump trailing white spaces */
26526 while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){
26527 zIn++;
26528 }
26529 if( sDelim.nByte <= 0 || zIn >= zEnd || zIn[0] != '\n' ){
26530 /* Invalid syntax */
26531 return SXERR_CONTINUE;
26532 }
26533 pStream->nLine++; /* Increment line counter */
26534 zIn++;
26535 /* Isolate the delimited string */
26536 sStr.zString = (const char *)zIn;
26537 /* Go and found the closing delimiter */
26538 for(;;){
26539 /* Synchronize with the next line */
26540 while( zIn < zEnd && zIn[0] != '\n' ){
26541 zIn++;
26542 }
26543 if( zIn >= zEnd ){
26544 /* End of the input reached, break immediately */
26545 pStream->zText = pStream->zEnd;
26546 break;
26547 }
26548 pStream->nLine++; /* Increment line counter */
26549 zIn++;
26550 if( (sxu32)(zEnd - zIn) >= sDelim.nByte && SyMemcmp((const void *)sDelim.zString, (const void *)zIn, sDelim.nByte) == 0 ){
26551 zPtr = &zIn[sDelim.nByte];
26552 while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){
26553 zPtr++;
26554 }
26555 if( zPtr >= zEnd ){
26556 /* End of input */
26557 pStream->zText = zPtr;
26558 break;
26559 }
26560 if( zPtr[0] == ';' ){
26561 const unsigned char *zCur = zPtr;
26562 zPtr++;
26563 while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){
26564 zPtr++;
26565 }
26566 if( zPtr >= zEnd || zPtr[0] == '\n' ){
26567 /* Closing delimiter found, break immediately */
26568 pStream->zText = zCur; /* Keep the semi-colon */
26569 break;
26570 }
26571 }else if( zPtr[0] == '\n' ){
26572 /* Closing delimiter found, break immediately */
26573 pStream->zText = zPtr; /* Synchronize with the stream cursor */
26574 break;
26575 }
26576 /* Synchronize pointers and continue searching */
26577 zIn = zPtr;
26578 }
26579 } /* For(;;) */
26580 /* Get the delimited string length */
26581 sStr.nByte = (sxu32)((const char *)zIn-sStr.zString);
26582 /* Record token type and length */
26583 pToken->nType = JX9_TK_NOWDOC;
26584 SyStringDupPtr(&pToken->sData, &sStr);
26585 /* Remove trailing white spaces */
26586 SyStringRightTrim(&pToken->sData);
26587 /* All done */
26588 return SXRET_OK;
26589}
26590/*
26591 * Tokenize a raw jx9 input.
26592 * This is the public tokenizer called by most code generator routines.
26593 */
26594JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput,sxu32 nLen,SySet *pOut)
26595{
26596 SyLex sLexer;
26597 sxi32 rc;
26598 /* Initialize the lexer */
26599 rc = SyLexInit(&sLexer, &(*pOut),jx9TokenizeInput,0);
26600 if( rc != SXRET_OK ){
26601 return rc;
26602 }
26603 /* Tokenize input */
26604 rc = SyLexTokenizeInput(&sLexer, zInput, nLen, 0, 0, 0);
26605 /* Release the lexer */
26606 SyLexRelease(&sLexer);
26607 /* Tokenization result */
26608 return rc;
26609}
26610
26611/*
26612 * ----------------------------------------------------------
26613 * File: jx9_lib.c
26614 * MD5: a684fb6677b1ab0110d03536f1280c50
26615 * ----------------------------------------------------------
26616 */
26617/*
26618 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
26619 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
26620 * Version 1.7.2
26621 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
26622 * please contact Symisc Systems via:
26623 * legal@symisc.net
26624 * licensing@symisc.net
26625 * contact@symisc.net
26626 * or visit:
26627 * http://jx9.symisc.net/
26628 */
26629 /* $SymiscID: lib.c v5.1 Win7 2012-08-08 04:19 stable <chm@symisc.net> $ */
26630/*
26631 * Symisc Run-Time API: A modern thread safe replacement of the standard libc
26632 * Copyright (C) Symisc Systems 2007-2012, http://www.symisc.net/
26633 *
26634 * The Symisc Run-Time API is an independent project developed by symisc systems
26635 * internally as a secure replacement of the standard libc.
26636 * The library is re-entrant, thread-safe and platform independent.
26637 */
26638#ifndef JX9_AMALGAMATION
26639#include "jx9Int.h"
26640#endif
26641#if defined(__WINNT__)
26642#include <Windows.h>
26643#else
26644#include <stdlib.h>
26645#endif
26646#if defined(JX9_ENABLE_THREADS)
26647/* SyRunTimeApi: sxmutex.c */
26648#if defined(__WINNT__)
26649struct SyMutex
26650{
26651 CRITICAL_SECTION sMutex;
26652 sxu32 nType; /* Mutex type, one of SXMUTEX_TYPE_* */
26653};
26654/* Preallocated static mutex */
26655static SyMutex aStaticMutexes[] = {
26656 {{0}, SXMUTEX_TYPE_STATIC_1},
26657 {{0}, SXMUTEX_TYPE_STATIC_2},
26658 {{0}, SXMUTEX_TYPE_STATIC_3},
26659 {{0}, SXMUTEX_TYPE_STATIC_4},
26660 {{0}, SXMUTEX_TYPE_STATIC_5},
26661 {{0}, SXMUTEX_TYPE_STATIC_6}
26662};
26663static BOOL winMutexInit = FALSE;
26664static LONG winMutexLock = 0;
26665
26666static sxi32 WinMutexGlobaInit(void)
26667{
26668 LONG rc;
26669 rc = InterlockedCompareExchange(&winMutexLock, 1, 0);
26670 if ( rc == 0 ){
26671 sxu32 n;
26672 for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){
26673 InitializeCriticalSection(&aStaticMutexes[n].sMutex);
26674 }
26675 winMutexInit = TRUE;
26676 }else{
26677 /* Someone else is doing this for us */
26678 while( winMutexInit == FALSE ){
26679 Sleep(1);
26680 }
26681 }
26682 return SXRET_OK;
26683}
26684static void WinMutexGlobalRelease(void)
26685{
26686 LONG rc;
26687 rc = InterlockedCompareExchange(&winMutexLock, 0, 1);
26688 if( rc == 1 ){
26689 /* The first to decrement to zero does the actual global release */
26690 if( winMutexInit == TRUE ){
26691 sxu32 n;
26692 for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){
26693 DeleteCriticalSection(&aStaticMutexes[n].sMutex);
26694 }
26695 winMutexInit = FALSE;
26696 }
26697 }
26698}
26699static SyMutex * WinMutexNew(int nType)
26700{
26701 SyMutex *pMutex = 0;
26702 if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){
26703 /* Allocate a new mutex */
26704 pMutex = (SyMutex *)HeapAlloc(GetProcessHeap(), 0, sizeof(SyMutex));
26705 if( pMutex == 0 ){
26706 return 0;
26707 }
26708 InitializeCriticalSection(&pMutex->sMutex);
26709 }else{
26710 /* Use a pre-allocated static mutex */
26711 if( nType > SXMUTEX_TYPE_STATIC_6 ){
26712 nType = SXMUTEX_TYPE_STATIC_6;
26713 }
26714 pMutex = &aStaticMutexes[nType - 3];
26715 }
26716 pMutex->nType = nType;
26717 return pMutex;
26718}
26719static void WinMutexRelease(SyMutex *pMutex)
26720{
26721 if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){
26722 DeleteCriticalSection(&pMutex->sMutex);
26723 HeapFree(GetProcessHeap(), 0, pMutex);
26724 }
26725}
26726static void WinMutexEnter(SyMutex *pMutex)
26727{
26728 EnterCriticalSection(&pMutex->sMutex);
26729}
26730static sxi32 WinMutexTryEnter(SyMutex *pMutex)
26731{
26732#ifdef _WIN32_WINNT
26733 BOOL rc;
26734 /* Only WindowsNT platforms */
26735 rc = TryEnterCriticalSection(&pMutex->sMutex);
26736 if( rc ){
26737 return SXRET_OK;
26738 }else{
26739 return SXERR_BUSY;
26740 }
26741#else
26742 return SXERR_NOTIMPLEMENTED;
26743#endif
26744}
26745static void WinMutexLeave(SyMutex *pMutex)
26746{
26747 LeaveCriticalSection(&pMutex->sMutex);
26748}
26749/* Export Windows mutex interfaces */
26750static const SyMutexMethods sWinMutexMethods = {
26751 WinMutexGlobaInit, /* xGlobalInit() */
26752 WinMutexGlobalRelease, /* xGlobalRelease() */
26753 WinMutexNew, /* xNew() */
26754 WinMutexRelease, /* xRelease() */
26755 WinMutexEnter, /* xEnter() */
26756 WinMutexTryEnter, /* xTryEnter() */
26757 WinMutexLeave /* xLeave() */
26758};
26759JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
26760{
26761 return &sWinMutexMethods;
26762}
26763#elif defined(__UNIXES__)
26764#include <pthread.h>
26765struct SyMutex
26766{
26767 pthread_mutex_t sMutex;
26768 sxu32 nType;
26769};
26770static SyMutex * UnixMutexNew(int nType)
26771{
26772 static SyMutex aStaticMutexes[] = {
26773 {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_1},
26774 {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_2},
26775 {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_3},
26776 {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_4},
26777 {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_5},
26778 {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_6}
26779 };
26780 SyMutex *pMutex;
26781
26782 if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){
26783 pthread_mutexattr_t sRecursiveAttr;
26784 /* Allocate a new mutex */
26785 pMutex = (SyMutex *)malloc(sizeof(SyMutex));
26786 if( pMutex == 0 ){
26787 return 0;
26788 }
26789 if( nType == SXMUTEX_TYPE_RECURSIVE ){
26790 pthread_mutexattr_init(&sRecursiveAttr);
26791 pthread_mutexattr_settype(&sRecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
26792 }
26793 pthread_mutex_init(&pMutex->sMutex, nType == SXMUTEX_TYPE_RECURSIVE ? &sRecursiveAttr : 0 );
26794 if( nType == SXMUTEX_TYPE_RECURSIVE ){
26795 pthread_mutexattr_destroy(&sRecursiveAttr);
26796 }
26797 }else{
26798 /* Use a pre-allocated static mutex */
26799 if( nType > SXMUTEX_TYPE_STATIC_6 ){
26800 nType = SXMUTEX_TYPE_STATIC_6;
26801 }
26802 pMutex = &aStaticMutexes[nType - 3];
26803 }
26804 pMutex->nType = nType;
26805
26806 return pMutex;
26807}
26808static void UnixMutexRelease(SyMutex *pMutex)
26809{
26810 if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){
26811 pthread_mutex_destroy(&pMutex->sMutex);
26812 free(pMutex);
26813 }
26814}
26815static void UnixMutexEnter(SyMutex *pMutex)
26816{
26817 pthread_mutex_lock(&pMutex->sMutex);
26818}
26819static void UnixMutexLeave(SyMutex *pMutex)
26820{
26821 pthread_mutex_unlock(&pMutex->sMutex);
26822}
26823/* Export pthread mutex interfaces */
26824static const SyMutexMethods sPthreadMutexMethods = {
26825 0, /* xGlobalInit() */
26826 0, /* xGlobalRelease() */
26827 UnixMutexNew, /* xNew() */
26828 UnixMutexRelease, /* xRelease() */
26829 UnixMutexEnter, /* xEnter() */
26830 0, /* xTryEnter() */
26831 UnixMutexLeave /* xLeave() */
26832};
26833JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
26834{
26835 return &sPthreadMutexMethods;
26836}
26837#else
26838/* Host application must register their own mutex subsystem if the target
26839 * platform is not an UNIX-like or windows systems.
26840 */
26841struct SyMutex
26842{
26843 sxu32 nType;
26844};
26845static SyMutex * DummyMutexNew(int nType)
26846{
26847 static SyMutex sMutex;
26848 SXUNUSED(nType);
26849 return &sMutex;
26850}
26851static void DummyMutexRelease(SyMutex *pMutex)
26852{
26853 SXUNUSED(pMutex);
26854}
26855static void DummyMutexEnter(SyMutex *pMutex)
26856{
26857 SXUNUSED(pMutex);
26858}
26859static void DummyMutexLeave(SyMutex *pMutex)
26860{
26861 SXUNUSED(pMutex);
26862}
26863/* Export the dummy mutex interfaces */
26864static const SyMutexMethods sDummyMutexMethods = {
26865 0, /* xGlobalInit() */
26866 0, /* xGlobalRelease() */
26867 DummyMutexNew, /* xNew() */
26868 DummyMutexRelease, /* xRelease() */
26869 DummyMutexEnter, /* xEnter() */
26870 0, /* xTryEnter() */
26871 DummyMutexLeave /* xLeave() */
26872};
26873JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
26874{
26875 return &sDummyMutexMethods;
26876}
26877#endif /* __WINNT__ */
26878#endif /* JX9_ENABLE_THREADS */
26879static void * SyOSHeapAlloc(sxu32 nByte)
26880{
26881 void *pNew;
26882#if defined(__WINNT__)
26883 pNew = HeapAlloc(GetProcessHeap(), 0, nByte);
26884#else
26885 pNew = malloc((size_t)nByte);
26886#endif
26887 return pNew;
26888}
26889static void * SyOSHeapRealloc(void *pOld, sxu32 nByte)
26890{
26891 void *pNew;
26892#if defined(__WINNT__)
26893 pNew = HeapReAlloc(GetProcessHeap(), 0, pOld, nByte);
26894#else
26895 pNew = realloc(pOld, (size_t)nByte);
26896#endif
26897 return pNew;
26898}
26899static void SyOSHeapFree(void *pPtr)
26900{
26901#if defined(__WINNT__)
26902 HeapFree(GetProcessHeap(), 0, pPtr);
26903#else
26904 free(pPtr);
26905#endif
26906}
26907/* SyRunTimeApi:sxstr.c */
26908JX9_PRIVATE sxu32 SyStrlen(const char *zSrc)
26909{
26910 register const char *zIn = zSrc;
26911#if defined(UNTRUST)
26912 if( zIn == 0 ){
26913 return 0;
26914 }
26915#endif
26916 for(;;){
26917 if( !zIn[0] ){ break; } zIn++;
26918 if( !zIn[0] ){ break; } zIn++;
26919 if( !zIn[0] ){ break; } zIn++;
26920 if( !zIn[0] ){ break; } zIn++;
26921 }
26922 return (sxu32)(zIn - zSrc);
26923}
26924JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos)
26925{
26926 const char *zIn = zStr;
26927 const char *zEnd;
26928
26929 zEnd = &zIn[nLen];
26930 for(;;){
26931 if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
26932 if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
26933 if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
26934 if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
26935 }
26936 return SXERR_NOTFOUND;
26937}
26938#ifndef JX9_DISABLE_BUILTIN_FUNC
26939JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos)
26940{
26941 const char *zIn = zStr;
26942 const char *zEnd;
26943
26944 zEnd = &zIn[nLen - 1];
26945 for( ;; ){
26946 if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
26947 if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
26948 if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
26949 if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
26950 }
26951 return SXERR_NOTFOUND;
26952}
26953#endif /* JX9_DISABLE_BUILTIN_FUNC */
26954JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos)
26955{
26956 const char *zIn = zSrc;
26957 const char *zPtr;
26958 const char *zEnd;
26959 sxi32 c;
26960 zEnd = &zSrc[nLen];
26961 for(;;){
26962 if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++;
26963 if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++;
26964 if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++;
26965 if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++;
26966 }
26967 return SXERR_NOTFOUND;
26968}
26969#ifndef JX9_DISABLE_BUILTIN_FUNC
26970JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen)
26971{
26972 const unsigned char *zP = (const unsigned char *)zLeft;
26973 const unsigned char *zQ = (const unsigned char *)zRight;
26974
26975 if( SX_EMPTY_STR(zP) || SX_EMPTY_STR(zQ) ){
26976 return SX_EMPTY_STR(zP) ? (SX_EMPTY_STR(zQ) ? 0 : -1) :1;
26977 }
26978 if( nLen <= 0 ){
26979 return 0;
26980 }
26981 for(;;){
26982 if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
26983 if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
26984 if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
26985 if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
26986 }
26987 return (sxi32)(zP[0] - zQ[0]);
26988}
26989#endif
26990JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen)
26991{
26992 register unsigned char *p = (unsigned char *)zLeft;
26993 register unsigned char *q = (unsigned char *)zRight;
26994
26995 if( SX_EMPTY_STR(p) || SX_EMPTY_STR(q) ){
26996 return SX_EMPTY_STR(p)? SX_EMPTY_STR(q) ? 0 : -1 :1;
26997 }
26998 for(;;){
26999 if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
27000 if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
27001 if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
27002 if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
27003
27004 }
27005 return (sxi32)(SyCharToLower(p[0]) - SyCharToLower(q[0]));
27006}
27007JX9_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen)
27008{
27009 unsigned char *zBuf = (unsigned char *)zDest;
27010 unsigned char *zIn = (unsigned char *)zSrc;
27011 unsigned char *zEnd;
27012#if defined(UNTRUST)
27013 if( zSrc == (const char *)zDest ){
27014 return 0;
27015 }
27016#endif
27017 if( nLen <= 0 ){
27018 nLen = SyStrlen(zSrc);
27019 }
27020 zEnd = &zBuf[nDestLen - 1]; /* reserve a room for the null terminator */
27021 for(;;){
27022 if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
27023 if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
27024 if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
27025 if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
27026 }
27027 zBuf[0] = 0;
27028 return (sxu32)(zBuf-(unsigned char *)zDest);
27029}
27030/* SyRunTimeApi:sxmem.c */
27031JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize)
27032{
27033 register unsigned char *zSrc = (unsigned char *)pSrc;
27034 unsigned char *zEnd;
27035#if defined(UNTRUST)
27036 if( zSrc == 0 || nSize <= 0 ){
27037 return ;
27038 }
27039#endif
27040 zEnd = &zSrc[nSize];
27041 for(;;){
27042 if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
27043 if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
27044 if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
27045 if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
27046 }
27047}
27048JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize)
27049{
27050 sxi32 rc;
27051 if( nSize <= 0 ){
27052 return 0;
27053 }
27054 if( pB1 == 0 || pB2 == 0 ){
27055 return pB1 != 0 ? 1 : (pB2 == 0 ? 0 : -1);
27056 }
27057 SX_MACRO_FAST_CMP(pB1, pB2, nSize, rc);
27058 return rc;
27059}
27060JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen)
27061{
27062 if( pSrc == 0 || pDest == 0 ){
27063 return 0;
27064 }
27065 if( pSrc == (const void *)pDest ){
27066 return nLen;
27067 }
27068 SX_MACRO_FAST_MEMCPY(pSrc, pDest, nLen);
27069 return nLen;
27070}
27071static void * MemOSAlloc(sxu32 nBytes)
27072{
27073 sxu32 *pChunk;
27074 pChunk = (sxu32 *)SyOSHeapAlloc(nBytes + sizeof(sxu32));
27075 if( pChunk == 0 ){
27076 return 0;
27077 }
27078 pChunk[0] = nBytes;
27079 return (void *)&pChunk[1];
27080}
27081static void * MemOSRealloc(void *pOld, sxu32 nBytes)
27082{
27083 sxu32 *pOldChunk;
27084 sxu32 *pChunk;
27085 pOldChunk = (sxu32 *)(((char *)pOld)-sizeof(sxu32));
27086 if( pOldChunk[0] >= nBytes ){
27087 return pOld;
27088 }
27089 pChunk = (sxu32 *)SyOSHeapRealloc(pOldChunk, nBytes + sizeof(sxu32));
27090 if( pChunk == 0 ){
27091 return 0;
27092 }
27093 pChunk[0] = nBytes;
27094 return (void *)&pChunk[1];
27095}
27096static void MemOSFree(void *pBlock)
27097{
27098 void *pChunk;
27099 pChunk = (void *)(((char *)pBlock)-sizeof(sxu32));
27100 SyOSHeapFree(pChunk);
27101}
27102static sxu32 MemOSChunkSize(void *pBlock)
27103{
27104 sxu32 *pChunk;
27105 pChunk = (sxu32 *)(((char *)pBlock)-sizeof(sxu32));
27106 return pChunk[0];
27107}
27108/* Export OS allocation methods */
27109static const SyMemMethods sOSAllocMethods = {
27110 MemOSAlloc,
27111 MemOSRealloc,
27112 MemOSFree,
27113 MemOSChunkSize,
27114 0,
27115 0,
27116 0
27117};
27118static void * MemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte)
27119{
27120 SyMemBlock *pBlock;
27121 sxi32 nRetry = 0;
27122
27123 /* Append an extra block so we can tracks allocated chunks and avoid memory
27124 * leaks.
27125 */
27126 nByte += sizeof(SyMemBlock);
27127 for(;;){
27128 pBlock = (SyMemBlock *)pBackend->pMethods->xAlloc(nByte);
27129 if( pBlock != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY
27130 || SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){
27131 break;
27132 }
27133 nRetry++;
27134 }
27135 if( pBlock == 0 ){
27136 return 0;
27137 }
27138 pBlock->pNext = pBlock->pPrev = 0;
27139 /* Link to the list of already tracked blocks */
27140 MACRO_LD_PUSH(pBackend->pBlocks, pBlock);
27141#if defined(UNTRUST)
27142 pBlock->nGuard = SXMEM_BACKEND_MAGIC;
27143#endif
27144 pBackend->nBlock++;
27145 return (void *)&pBlock[1];
27146}
27147JX9_PRIVATE void * SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte)
27148{
27149 void *pChunk;
27150#if defined(UNTRUST)
27151 if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27152 return 0;
27153 }
27154#endif
27155 if( pBackend->pMutexMethods ){
27156 SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27157 }
27158 pChunk = MemBackendAlloc(&(*pBackend), nByte);
27159 if( pBackend->pMutexMethods ){
27160 SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27161 }
27162 return pChunk;
27163}
27164static void * MemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
27165{
27166 SyMemBlock *pBlock, *pNew, *pPrev, *pNext;
27167 sxu32 nRetry = 0;
27168
27169 if( pOld == 0 ){
27170 return MemBackendAlloc(&(*pBackend), nByte);
27171 }
27172 pBlock = (SyMemBlock *)(((char *)pOld) - sizeof(SyMemBlock));
27173#if defined(UNTRUST)
27174 if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){
27175 return 0;
27176 }
27177#endif
27178 nByte += sizeof(SyMemBlock);
27179 pPrev = pBlock->pPrev;
27180 pNext = pBlock->pNext;
27181 for(;;){
27182 pNew = (SyMemBlock *)pBackend->pMethods->xRealloc(pBlock, nByte);
27183 if( pNew != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY ||
27184 SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){
27185 break;
27186 }
27187 nRetry++;
27188 }
27189 if( pNew == 0 ){
27190 return 0;
27191 }
27192 if( pNew != pBlock ){
27193 if( pPrev == 0 ){
27194 pBackend->pBlocks = pNew;
27195 }else{
27196 pPrev->pNext = pNew;
27197 }
27198 if( pNext ){
27199 pNext->pPrev = pNew;
27200 }
27201#if defined(UNTRUST)
27202 pNew->nGuard = SXMEM_BACKEND_MAGIC;
27203#endif
27204 }
27205 return (void *)&pNew[1];
27206}
27207JX9_PRIVATE void * SyMemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
27208{
27209 void *pChunk;
27210#if defined(UNTRUST)
27211 if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27212 return 0;
27213 }
27214#endif
27215 if( pBackend->pMutexMethods ){
27216 SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27217 }
27218 pChunk = MemBackendRealloc(&(*pBackend), pOld, nByte);
27219 if( pBackend->pMutexMethods ){
27220 SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27221 }
27222 return pChunk;
27223}
27224static sxi32 MemBackendFree(SyMemBackend *pBackend, void * pChunk)
27225{
27226 SyMemBlock *pBlock;
27227 pBlock = (SyMemBlock *)(((char *)pChunk) - sizeof(SyMemBlock));
27228#if defined(UNTRUST)
27229 if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){
27230 return SXERR_CORRUPT;
27231 }
27232#endif
27233 /* Unlink from the list of active blocks */
27234 if( pBackend->nBlock > 0 ){
27235 /* Release the block */
27236#if defined(UNTRUST)
27237 /* Mark as stale block */
27238 pBlock->nGuard = 0x635B;
27239#endif
27240 MACRO_LD_REMOVE(pBackend->pBlocks, pBlock);
27241 pBackend->nBlock--;
27242 pBackend->pMethods->xFree(pBlock);
27243 }
27244 return SXRET_OK;
27245}
27246JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void * pChunk)
27247{
27248 sxi32 rc;
27249#if defined(UNTRUST)
27250 if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27251 return SXERR_CORRUPT;
27252 }
27253#endif
27254 if( pChunk == 0 ){
27255 return SXRET_OK;
27256 }
27257 if( pBackend->pMutexMethods ){
27258 SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27259 }
27260 rc = MemBackendFree(&(*pBackend), pChunk);
27261 if( pBackend->pMutexMethods ){
27262 SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27263 }
27264 return rc;
27265}
27266#if defined(JX9_ENABLE_THREADS)
27267JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods)
27268{
27269 SyMutex *pMutex;
27270#if defined(UNTRUST)
27271 if( SXMEM_BACKEND_CORRUPT(pBackend) || pMethods == 0 || pMethods->xNew == 0){
27272 return SXERR_CORRUPT;
27273 }
27274#endif
27275 pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
27276 if( pMutex == 0 ){
27277 return SXERR_OS;
27278 }
27279 /* Attach the mutex to the memory backend */
27280 pBackend->pMutex = pMutex;
27281 pBackend->pMutexMethods = pMethods;
27282 return SXRET_OK;
27283}
27284JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend)
27285{
27286#if defined(UNTRUST)
27287 if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27288 return SXERR_CORRUPT;
27289 }
27290#endif
27291 if( pBackend->pMutex == 0 ){
27292 /* There is no mutex subsystem at all */
27293 return SXRET_OK;
27294 }
27295 SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
27296 pBackend->pMutexMethods = 0;
27297 pBackend->pMutex = 0;
27298 return SXRET_OK;
27299}
27300#endif
27301/*
27302 * Memory pool allocator
27303 */
27304#define SXMEM_POOL_MAGIC 0xDEAD
27305#define SXMEM_POOL_MAXALLOC (1<<(SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR))
27306#define SXMEM_POOL_MINALLOC (1<<(SXMEM_POOL_INCR))
27307static sxi32 MemPoolBucketAlloc(SyMemBackend *pBackend, sxu32 nBucket)
27308{
27309 char *zBucket, *zBucketEnd;
27310 SyMemHeader *pHeader;
27311 sxu32 nBucketSize;
27312
27313 /* Allocate one big block first */
27314 zBucket = (char *)MemBackendAlloc(&(*pBackend), SXMEM_POOL_MAXALLOC);
27315 if( zBucket == 0 ){
27316 return SXERR_MEM;
27317 }
27318 zBucketEnd = &zBucket[SXMEM_POOL_MAXALLOC];
27319 /* Divide the big block into mini bucket pool */
27320 nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR);
27321 pBackend->apPool[nBucket] = pHeader = (SyMemHeader *)zBucket;
27322 for(;;){
27323 if( &zBucket[nBucketSize] >= zBucketEnd ){
27324 break;
27325 }
27326 pHeader->pNext = (SyMemHeader *)&zBucket[nBucketSize];
27327 /* Advance the cursor to the next available chunk */
27328 pHeader = pHeader->pNext;
27329 zBucket += nBucketSize;
27330 }
27331 pHeader->pNext = 0;
27332
27333 return SXRET_OK;
27334}
27335static void * MemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte)
27336{
27337 SyMemHeader *pBucket, *pNext;
27338 sxu32 nBucketSize;
27339 sxu32 nBucket;
27340
27341 if( nByte + sizeof(SyMemHeader) >= SXMEM_POOL_MAXALLOC ){
27342 /* Allocate a big chunk directly */
27343 pBucket = (SyMemHeader *)MemBackendAlloc(&(*pBackend), nByte+sizeof(SyMemHeader));
27344 if( pBucket == 0 ){
27345 return 0;
27346 }
27347 /* Record as big block */
27348 pBucket->nBucket = (sxu32)(SXMEM_POOL_MAGIC << 16) | SXU16_HIGH;
27349 return (void *)(pBucket+1);
27350 }
27351 /* Locate the appropriate bucket */
27352 nBucket = 0;
27353 nBucketSize = SXMEM_POOL_MINALLOC;
27354 while( nByte + sizeof(SyMemHeader) > nBucketSize ){
27355 nBucketSize <<= 1;
27356 nBucket++;
27357 }
27358 pBucket = pBackend->apPool[nBucket];
27359 if( pBucket == 0 ){
27360 sxi32 rc;
27361 rc = MemPoolBucketAlloc(&(*pBackend), nBucket);
27362 if( rc != SXRET_OK ){
27363 return 0;
27364 }
27365 pBucket = pBackend->apPool[nBucket];
27366 }
27367 /* Remove from the free list */
27368 pNext = pBucket->pNext;
27369 pBackend->apPool[nBucket] = pNext;
27370 /* Record bucket&magic number */
27371 pBucket->nBucket = (SXMEM_POOL_MAGIC << 16) | nBucket;
27372 return (void *)&pBucket[1];
27373}
27374JX9_PRIVATE void * SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte)
27375{
27376 void *pChunk;
27377#if defined(UNTRUST)
27378 if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27379 return 0;
27380 }
27381#endif
27382 if( pBackend->pMutexMethods ){
27383 SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27384 }
27385 pChunk = MemBackendPoolAlloc(&(*pBackend), nByte);
27386 if( pBackend->pMutexMethods ){
27387 SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27388 }
27389 return pChunk;
27390}
27391static sxi32 MemBackendPoolFree(SyMemBackend *pBackend, void * pChunk)
27392{
27393 SyMemHeader *pHeader;
27394 sxu32 nBucket;
27395 /* Get the corresponding bucket */
27396 pHeader = (SyMemHeader *)(((char *)pChunk) - sizeof(SyMemHeader));
27397 /* Sanity check to avoid misuse */
27398 if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){
27399 return SXERR_CORRUPT;
27400 }
27401 nBucket = pHeader->nBucket & 0xFFFF;
27402 if( nBucket == SXU16_HIGH ){
27403 /* Free the big block */
27404 MemBackendFree(&(*pBackend), pHeader);
27405 }else{
27406 /* Return to the free list */
27407 pHeader->pNext = pBackend->apPool[nBucket & 0x0f];
27408 pBackend->apPool[nBucket & 0x0f] = pHeader;
27409 }
27410 return SXRET_OK;
27411}
27412JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void * pChunk)
27413{
27414 sxi32 rc;
27415#if defined(UNTRUST)
27416 if( SXMEM_BACKEND_CORRUPT(pBackend) || pChunk == 0 ){
27417 return SXERR_CORRUPT;
27418 }
27419#endif
27420 if( pBackend->pMutexMethods ){
27421 SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27422 }
27423 rc = MemBackendPoolFree(&(*pBackend), pChunk);
27424 if( pBackend->pMutexMethods ){
27425 SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27426 }
27427 return rc;
27428}
27429#if 0
27430static void * MemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
27431{
27432 sxu32 nBucket, nBucketSize;
27433 SyMemHeader *pHeader;
27434 void * pNew;
27435
27436 if( pOld == 0 ){
27437 /* Allocate a new pool */
27438 pNew = MemBackendPoolAlloc(&(*pBackend), nByte);
27439 return pNew;
27440 }
27441 /* Get the corresponding bucket */
27442 pHeader = (SyMemHeader *)(((char *)pOld) - sizeof(SyMemHeader));
27443 /* Sanity check to avoid misuse */
27444 if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){
27445 return 0;
27446 }
27447 nBucket = pHeader->nBucket & 0xFFFF;
27448 if( nBucket == SXU16_HIGH ){
27449 /* Big block */
27450 return MemBackendRealloc(&(*pBackend), pHeader, nByte);
27451 }
27452 nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR);
27453 if( nBucketSize >= nByte + sizeof(SyMemHeader) ){
27454 /* The old bucket can honor the requested size */
27455 return pOld;
27456 }
27457 /* Allocate a new pool */
27458 pNew = MemBackendPoolAlloc(&(*pBackend), nByte);
27459 if( pNew == 0 ){
27460 return 0;
27461 }
27462 /* Copy the old data into the new block */
27463 SyMemcpy(pOld, pNew, nBucketSize);
27464 /* Free the stale block */
27465 MemBackendPoolFree(&(*pBackend), pOld);
27466 return pNew;
27467}
27468JX9_PRIVATE void * SyMemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
27469{
27470 void *pChunk;
27471#if defined(UNTRUST)
27472 if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27473 return 0;
27474 }
27475#endif
27476 if( pBackend->pMutexMethods ){
27477 SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27478 }
27479 pChunk = MemBackendPoolRealloc(&(*pBackend), pOld, nByte);
27480 if( pBackend->pMutexMethods ){
27481 SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27482 }
27483 return pChunk;
27484}
27485#endif
27486JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void * pUserData)
27487{
27488#if defined(UNTRUST)
27489 if( pBackend == 0 ){
27490 return SXERR_EMPTY;
27491 }
27492#endif
27493 /* Zero the allocator first */
27494 SyZero(&(*pBackend), sizeof(SyMemBackend));
27495 pBackend->xMemError = xMemErr;
27496 pBackend->pUserData = pUserData;
27497 /* Switch to the OS memory allocator */
27498 pBackend->pMethods = &sOSAllocMethods;
27499 if( pBackend->pMethods->xInit ){
27500 /* Initialize the backend */
27501 if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){
27502 return SXERR_ABORT;
27503 }
27504 }
27505#if defined(UNTRUST)
27506 pBackend->nMagic = SXMEM_BACKEND_MAGIC;
27507#endif
27508 return SXRET_OK;
27509}
27510JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void * pUserData)
27511{
27512#if defined(UNTRUST)
27513 if( pBackend == 0 || pMethods == 0){
27514 return SXERR_EMPTY;
27515 }
27516#endif
27517 if( pMethods->xAlloc == 0 || pMethods->xRealloc == 0 || pMethods->xFree == 0 || pMethods->xChunkSize == 0 ){
27518 /* mandatory methods are missing */
27519 return SXERR_INVALID;
27520 }
27521 /* Zero the allocator first */
27522 SyZero(&(*pBackend), sizeof(SyMemBackend));
27523 pBackend->xMemError = xMemErr;
27524 pBackend->pUserData = pUserData;
27525 /* Switch to the host application memory allocator */
27526 pBackend->pMethods = pMethods;
27527 if( pBackend->pMethods->xInit ){
27528 /* Initialize the backend */
27529 if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){
27530 return SXERR_ABORT;
27531 }
27532 }
27533#if defined(UNTRUST)
27534 pBackend->nMagic = SXMEM_BACKEND_MAGIC;
27535#endif
27536 return SXRET_OK;
27537}
27538JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,const SyMemBackend *pParent)
27539{
27540 sxu8 bInheritMutex;
27541#if defined(UNTRUST)
27542 if( pBackend == 0 || SXMEM_BACKEND_CORRUPT(pParent) ){
27543 return SXERR_CORRUPT;
27544 }
27545#endif
27546 /* Zero the allocator first */
27547 SyZero(&(*pBackend), sizeof(SyMemBackend));
27548 pBackend->pMethods = pParent->pMethods;
27549 pBackend->xMemError = pParent->xMemError;
27550 pBackend->pUserData = pParent->pUserData;
27551 bInheritMutex = pParent->pMutexMethods ? TRUE : FALSE;
27552 if( bInheritMutex ){
27553 pBackend->pMutexMethods = pParent->pMutexMethods;
27554 /* Create a private mutex */
27555 pBackend->pMutex = pBackend->pMutexMethods->xNew(SXMUTEX_TYPE_FAST);
27556 if( pBackend->pMutex == 0){
27557 return SXERR_OS;
27558 }
27559 }
27560#if defined(UNTRUST)
27561 pBackend->nMagic = SXMEM_BACKEND_MAGIC;
27562#endif
27563 return SXRET_OK;
27564}
27565static sxi32 MemBackendRelease(SyMemBackend *pBackend)
27566{
27567 SyMemBlock *pBlock, *pNext;
27568
27569 pBlock = pBackend->pBlocks;
27570 for(;;){
27571 if( pBackend->nBlock == 0 ){
27572 break;
27573 }
27574 pNext = pBlock->pNext;
27575 pBackend->pMethods->xFree(pBlock);
27576 pBlock = pNext;
27577 pBackend->nBlock--;
27578 /* LOOP ONE */
27579 if( pBackend->nBlock == 0 ){
27580 break;
27581 }
27582 pNext = pBlock->pNext;
27583 pBackend->pMethods->xFree(pBlock);
27584 pBlock = pNext;
27585 pBackend->nBlock--;
27586 /* LOOP TWO */
27587 if( pBackend->nBlock == 0 ){
27588 break;
27589 }
27590 pNext = pBlock->pNext;
27591 pBackend->pMethods->xFree(pBlock);
27592 pBlock = pNext;
27593 pBackend->nBlock--;
27594 /* LOOP THREE */
27595 if( pBackend->nBlock == 0 ){
27596 break;
27597 }
27598 pNext = pBlock->pNext;
27599 pBackend->pMethods->xFree(pBlock);
27600 pBlock = pNext;
27601 pBackend->nBlock--;
27602 /* LOOP FOUR */
27603 }
27604 if( pBackend->pMethods->xRelease ){
27605 pBackend->pMethods->xRelease(pBackend->pMethods->pUserData);
27606 }
27607 pBackend->pMethods = 0;
27608 pBackend->pBlocks = 0;
27609#if defined(UNTRUST)
27610 pBackend->nMagic = 0x2626;
27611#endif
27612 return SXRET_OK;
27613}
27614JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend)
27615{
27616 sxi32 rc;
27617#if defined(UNTRUST)
27618 if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27619 return SXERR_INVALID;
27620 }
27621#endif
27622 if( pBackend->pMutexMethods ){
27623 SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27624 }
27625 rc = MemBackendRelease(&(*pBackend));
27626 if( pBackend->pMutexMethods ){
27627 SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27628 SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
27629 }
27630 return rc;
27631}
27632JX9_PRIVATE void * SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize)
27633{
27634 void *pNew;
27635#if defined(UNTRUST)
27636 if( pSrc == 0 || nSize <= 0 ){
27637 return 0;
27638 }
27639#endif
27640 pNew = SyMemBackendAlloc(&(*pBackend), nSize);
27641 if( pNew ){
27642 SyMemcpy(pSrc, pNew, nSize);
27643 }
27644 return pNew;
27645}
27646JX9_PRIVATE char * SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize)
27647{
27648 char *zDest;
27649 zDest = (char *)SyMemBackendAlloc(&(*pBackend), nSize + 1);
27650 if( zDest ){
27651 Systrcpy(zDest, nSize+1, zSrc, nSize);
27652 }
27653 return zDest;
27654}
27655JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize)
27656{
27657#if defined(UNTRUST)
27658 if( pBlob == 0 || pBuffer == 0 || nSize < 1 ){
27659 return SXERR_EMPTY;
27660 }
27661#endif
27662 pBlob->pBlob = pBuffer;
27663 pBlob->mByte = nSize;
27664 pBlob->nByte = 0;
27665 pBlob->pAllocator = 0;
27666 pBlob->nFlags = SXBLOB_LOCKED|SXBLOB_STATIC;
27667 return SXRET_OK;
27668}
27669JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator)
27670{
27671#if defined(UNTRUST)
27672 if( pBlob == 0 ){
27673 return SXERR_EMPTY;
27674 }
27675#endif
27676 pBlob->pBlob = 0;
27677 pBlob->mByte = pBlob->nByte = 0;
27678 pBlob->pAllocator = &(*pAllocator);
27679 pBlob->nFlags = 0;
27680 return SXRET_OK;
27681}
27682JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte)
27683{
27684#if defined(UNTRUST)
27685 if( pBlob == 0 ){
27686 return SXERR_EMPTY;
27687 }
27688#endif
27689 pBlob->pBlob = (void *)pData;
27690 pBlob->nByte = nByte;
27691 pBlob->mByte = 0;
27692 pBlob->nFlags |= SXBLOB_RDONLY;
27693 return SXRET_OK;
27694}
27695#ifndef SXBLOB_MIN_GROWTH
27696#define SXBLOB_MIN_GROWTH 16
27697#endif
27698static sxi32 BlobPrepareGrow(SyBlob *pBlob, sxu32 *pByte)
27699{
27700 sxu32 nByte;
27701 void *pNew;
27702 nByte = *pByte;
27703 if( pBlob->nFlags & (SXBLOB_LOCKED|SXBLOB_STATIC) ){
27704 if ( SyBlobFreeSpace(pBlob) < nByte ){
27705 *pByte = SyBlobFreeSpace(pBlob);
27706 if( (*pByte) == 0 ){
27707 return SXERR_SHORT;
27708 }
27709 }
27710 return SXRET_OK;
27711 }
27712 if( pBlob->nFlags & SXBLOB_RDONLY ){
27713 /* Make a copy of the read-only item */
27714 if( pBlob->nByte > 0 ){
27715 pNew = SyMemBackendDup(pBlob->pAllocator, pBlob->pBlob, pBlob->nByte);
27716 if( pNew == 0 ){
27717 return SXERR_MEM;
27718 }
27719 pBlob->pBlob = pNew;
27720 pBlob->mByte = pBlob->nByte;
27721 }else{
27722 pBlob->pBlob = 0;
27723 pBlob->mByte = 0;
27724 }
27725 /* Remove the read-only flag */
27726 pBlob->nFlags &= ~SXBLOB_RDONLY;
27727 }
27728 if( SyBlobFreeSpace(pBlob) >= nByte ){
27729 return SXRET_OK;
27730 }
27731 if( pBlob->mByte > 0 ){
27732 nByte = nByte + pBlob->mByte * 2 + SXBLOB_MIN_GROWTH;
27733 }else if ( nByte < SXBLOB_MIN_GROWTH ){
27734 nByte = SXBLOB_MIN_GROWTH;
27735 }
27736 pNew = SyMemBackendRealloc(pBlob->pAllocator, pBlob->pBlob, nByte);
27737 if( pNew == 0 ){
27738 return SXERR_MEM;
27739 }
27740 pBlob->pBlob = pNew;
27741 pBlob->mByte = nByte;
27742 return SXRET_OK;
27743}
27744JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize)
27745{
27746 sxu8 *zBlob;
27747 sxi32 rc;
27748 if( nSize < 1 ){
27749 return SXRET_OK;
27750 }
27751 rc = BlobPrepareGrow(&(*pBlob), &nSize);
27752 if( SXRET_OK != rc ){
27753 return rc;
27754 }
27755 if( pData ){
27756 zBlob = (sxu8 *)pBlob->pBlob ;
27757 zBlob = &zBlob[pBlob->nByte];
27758 pBlob->nByte += nSize;
27759 SX_MACRO_FAST_MEMCPY(pData, zBlob, nSize);
27760 }
27761 return SXRET_OK;
27762}
27763JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob)
27764{
27765 sxi32 rc;
27766 sxu32 n;
27767 n = pBlob->nByte;
27768 rc = SyBlobAppend(&(*pBlob), (const void *)"\0", sizeof(char));
27769 if (rc == SXRET_OK ){
27770 pBlob->nByte = n;
27771 }
27772 return rc;
27773}
27774JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest)
27775{
27776 sxi32 rc = SXRET_OK;
27777 if( pSrc->nByte > 0 ){
27778 rc = SyBlobAppend(&(*pDest), pSrc->pBlob, pSrc->nByte);
27779 }
27780 return rc;
27781}
27782JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob)
27783{
27784 pBlob->nByte = 0;
27785 if( pBlob->nFlags & SXBLOB_RDONLY ){
27786 /* Read-only (Not malloced chunk) */
27787 pBlob->pBlob = 0;
27788 pBlob->mByte = 0;
27789 pBlob->nFlags &= ~SXBLOB_RDONLY;
27790 }
27791 return SXRET_OK;
27792}
27793JX9_PRIVATE sxi32 SyBlobTruncate(SyBlob *pBlob,sxu32 nNewLen)
27794{
27795 if( nNewLen < pBlob->nByte ){
27796 pBlob->nByte = nNewLen;
27797 }
27798 return SXRET_OK;
27799}
27800JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob)
27801{
27802 if( (pBlob->nFlags & (SXBLOB_STATIC|SXBLOB_RDONLY)) == 0 && pBlob->mByte > 0 ){
27803 SyMemBackendFree(pBlob->pAllocator, pBlob->pBlob);
27804 }
27805 pBlob->pBlob = 0;
27806 pBlob->nByte = pBlob->mByte = 0;
27807 pBlob->nFlags = 0;
27808 return SXRET_OK;
27809}
27810#ifndef JX9_DISABLE_BUILTIN_FUNC
27811JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft)
27812{
27813 const char *zIn = (const char *)pBlob;
27814 const char *zEnd;
27815 sxi32 rc;
27816 if( pLen > nLen ){
27817 return SXERR_NOTFOUND;
27818 }
27819 zEnd = &zIn[nLen-pLen];
27820 for(;;){
27821 if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++;
27822 if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++;
27823 if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++;
27824 if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++;
27825 }
27826 return SXERR_NOTFOUND;
27827}
27828#endif /* JX9_DISABLE_BUILTIN_FUNC */
27829/* SyRunTimeApi:sxds.c */
27830JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize)
27831{
27832 pSet->nSize = 0 ;
27833 pSet->nUsed = 0;
27834 pSet->nCursor = 0;
27835 pSet->eSize = ElemSize;
27836 pSet->pAllocator = pAllocator;
27837 pSet->pBase = 0;
27838 pSet->pUserData = 0;
27839 return SXRET_OK;
27840}
27841JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem)
27842{
27843 unsigned char *zbase;
27844 if( pSet->nUsed >= pSet->nSize ){
27845 void *pNew;
27846 if( pSet->pAllocator == 0 ){
27847 return SXERR_LOCKED;
27848 }
27849 if( pSet->nSize <= 0 ){
27850 pSet->nSize = 4;
27851 }
27852 pNew = SyMemBackendRealloc(pSet->pAllocator, pSet->pBase, pSet->eSize * pSet->nSize * 2);
27853 if( pNew == 0 ){
27854 return SXERR_MEM;
27855 }
27856 pSet->pBase = pNew;
27857 pSet->nSize <<= 1;
27858 }
27859 zbase = (unsigned char *)pSet->pBase;
27860 SX_MACRO_FAST_MEMCPY(pItem, &zbase[pSet->nUsed * pSet->eSize], pSet->eSize);
27861 pSet->nUsed++;
27862 return SXRET_OK;
27863}
27864JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem)
27865{
27866 if( pSet->nSize > 0 ){
27867 return SXERR_LOCKED;
27868 }
27869 if( nItem < 8 ){
27870 nItem = 8;
27871 }
27872 pSet->pBase = SyMemBackendAlloc(pSet->pAllocator, pSet->eSize * nItem);
27873 if( pSet->pBase == 0 ){
27874 return SXERR_MEM;
27875 }
27876 pSet->nSize = nItem;
27877 return SXRET_OK;
27878}
27879JX9_PRIVATE sxi32 SySetReset(SySet *pSet)
27880{
27881 pSet->nUsed = 0;
27882 pSet->nCursor = 0;
27883 return SXRET_OK;
27884}
27885JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet)
27886{
27887 pSet->nCursor = 0;
27888 return SXRET_OK;
27889}
27890JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry)
27891{
27892 register unsigned char *zSrc;
27893 if( pSet->nCursor >= pSet->nUsed ){
27894 /* Reset cursor */
27895 pSet->nCursor = 0;
27896 return SXERR_EOF;
27897 }
27898 zSrc = (unsigned char *)SySetBasePtr(pSet);
27899 if( ppEntry ){
27900 *ppEntry = (void *)&zSrc[pSet->nCursor * pSet->eSize];
27901 }
27902 pSet->nCursor++;
27903 return SXRET_OK;
27904}
27905JX9_PRIVATE sxi32 SySetRelease(SySet *pSet)
27906{
27907 sxi32 rc = SXRET_OK;
27908 if( pSet->pAllocator && pSet->pBase ){
27909 rc = SyMemBackendFree(pSet->pAllocator, pSet->pBase);
27910 }
27911 pSet->pBase = 0;
27912 pSet->nUsed = 0;
27913 pSet->nCursor = 0;
27914 return rc;
27915}
27916JX9_PRIVATE void * SySetPeek(SySet *pSet)
27917{
27918 const char *zBase;
27919 if( pSet->nUsed <= 0 ){
27920 return 0;
27921 }
27922 zBase = (const char *)pSet->pBase;
27923 return (void *)&zBase[(pSet->nUsed - 1) * pSet->eSize];
27924}
27925JX9_PRIVATE void * SySetPop(SySet *pSet)
27926{
27927 const char *zBase;
27928 void *pData;
27929 if( pSet->nUsed <= 0 ){
27930 return 0;
27931 }
27932 zBase = (const char *)pSet->pBase;
27933 pSet->nUsed--;
27934 pData = (void *)&zBase[pSet->nUsed * pSet->eSize];
27935 return pData;
27936}
27937JX9_PRIVATE void * SySetAt(SySet *pSet, sxu32 nIdx)
27938{
27939 const char *zBase;
27940 if( nIdx >= pSet->nUsed ){
27941 /* Out of range */
27942 return 0;
27943 }
27944 zBase = (const char *)pSet->pBase;
27945 return (void *)&zBase[nIdx * pSet->eSize];
27946}
27947/* Private hash entry */
27948struct SyHashEntry_Pr
27949{
27950 const void *pKey; /* Hash key */
27951 sxu32 nKeyLen; /* Key length */
27952 void *pUserData; /* User private data */
27953 /* Private fields */
27954 sxu32 nHash;
27955 SyHash *pHash;
27956 SyHashEntry_Pr *pNext, *pPrev; /* Next and previous entry in the list */
27957 SyHashEntry_Pr *pNextCollide, *pPrevCollide; /* Collision list */
27958};
27959#define INVALID_HASH(H) ((H)->apBucket == 0)
27960JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp)
27961{
27962 SyHashEntry_Pr **apNew;
27963#if defined(UNTRUST)
27964 if( pHash == 0 ){
27965 return SXERR_EMPTY;
27966 }
27967#endif
27968 /* Allocate a new table */
27969 apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(&(*pAllocator), sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE);
27970 if( apNew == 0 ){
27971 return SXERR_MEM;
27972 }
27973 SyZero((void *)apNew, sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE);
27974 pHash->pAllocator = &(*pAllocator);
27975 pHash->xHash = xHash ? xHash : SyBinHash;
27976 pHash->xCmp = xCmp ? xCmp : SyMemcmp;
27977 pHash->pCurrent = pHash->pList = 0;
27978 pHash->nEntry = 0;
27979 pHash->apBucket = apNew;
27980 pHash->nBucketSize = SXHASH_BUCKET_SIZE;
27981 return SXRET_OK;
27982}
27983JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash)
27984{
27985 SyHashEntry_Pr *pEntry, *pNext;
27986#if defined(UNTRUST)
27987 if( INVALID_HASH(pHash) ){
27988 return SXERR_EMPTY;
27989 }
27990#endif
27991 pEntry = pHash->pList;
27992 for(;;){
27993 if( pHash->nEntry == 0 ){
27994 break;
27995 }
27996 pNext = pEntry->pNext;
27997 SyMemBackendPoolFree(pHash->pAllocator, pEntry);
27998 pEntry = pNext;
27999 pHash->nEntry--;
28000 }
28001 if( pHash->apBucket ){
28002 SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket);
28003 }
28004 pHash->apBucket = 0;
28005 pHash->nBucketSize = 0;
28006 pHash->pAllocator = 0;
28007 return SXRET_OK;
28008}
28009static SyHashEntry_Pr * HashGetEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen)
28010{
28011 SyHashEntry_Pr *pEntry;
28012 sxu32 nHash;
28013
28014 nHash = pHash->xHash(pKey, nKeyLen);
28015 pEntry = pHash->apBucket[nHash & (pHash->nBucketSize - 1)];
28016 for(;;){
28017 if( pEntry == 0 ){
28018 break;
28019 }
28020 if( pEntry->nHash == nHash && pEntry->nKeyLen == nKeyLen &&
28021 pHash->xCmp(pEntry->pKey, pKey, nKeyLen) == 0 ){
28022 return pEntry;
28023 }
28024 pEntry = pEntry->pNextCollide;
28025 }
28026 /* Entry not found */
28027 return 0;
28028}
28029JX9_PRIVATE SyHashEntry * SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen)
28030{
28031 SyHashEntry_Pr *pEntry;
28032#if defined(UNTRUST)
28033 if( INVALID_HASH(pHash) ){
28034 return 0;
28035 }
28036#endif
28037 if( pHash->nEntry < 1 || nKeyLen < 1 ){
28038 /* Don't bother hashing, return immediately */
28039 return 0;
28040 }
28041 pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen);
28042 if( pEntry == 0 ){
28043 return 0;
28044 }
28045 return (SyHashEntry *)pEntry;
28046}
28047static sxi32 HashDeleteEntry(SyHash *pHash, SyHashEntry_Pr *pEntry, void **ppUserData)
28048{
28049 sxi32 rc;
28050 if( pEntry->pPrevCollide == 0 ){
28051 pHash->apBucket[pEntry->nHash & (pHash->nBucketSize - 1)] = pEntry->pNextCollide;
28052 }else{
28053 pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
28054 }
28055 if( pEntry->pNextCollide ){
28056 pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
28057 }
28058 MACRO_LD_REMOVE(pHash->pList, pEntry);
28059 pHash->nEntry--;
28060 if( ppUserData ){
28061 /* Write a pointer to the user data */
28062 *ppUserData = pEntry->pUserData;
28063 }
28064 /* Release the entry */
28065 rc = SyMemBackendPoolFree(pHash->pAllocator, pEntry);
28066 return rc;
28067}
28068JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData)
28069{
28070 SyHashEntry_Pr *pEntry;
28071 sxi32 rc;
28072#if defined(UNTRUST)
28073 if( INVALID_HASH(pHash) ){
28074 return SXERR_CORRUPT;
28075 }
28076#endif
28077 pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen);
28078 if( pEntry == 0 ){
28079 return SXERR_NOTFOUND;
28080 }
28081 rc = HashDeleteEntry(&(*pHash), pEntry, ppUserData);
28082 return rc;
28083}
28084JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32 (*xStep)(SyHashEntry *, void *), void *pUserData)
28085{
28086 SyHashEntry_Pr *pEntry;
28087 sxi32 rc;
28088 sxu32 n;
28089#if defined(UNTRUST)
28090 if( INVALID_HASH(pHash) || xStep == 0){
28091 return 0;
28092 }
28093#endif
28094 pEntry = pHash->pList;
28095 for( n = 0 ; n < pHash->nEntry ; n++ ){
28096 /* Invoke the callback */
28097 rc = xStep((SyHashEntry *)pEntry, pUserData);
28098 if( rc != SXRET_OK ){
28099 return rc;
28100 }
28101 /* Point to the next entry */
28102 pEntry = pEntry->pNext;
28103 }
28104 return SXRET_OK;
28105}
28106static sxi32 HashGrowTable(SyHash *pHash)
28107{
28108 sxu32 nNewSize = pHash->nBucketSize * 2;
28109 SyHashEntry_Pr *pEntry;
28110 SyHashEntry_Pr **apNew;
28111 sxu32 n, iBucket;
28112
28113 /* Allocate a new larger table */
28114 apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(pHash->pAllocator, nNewSize * sizeof(SyHashEntry_Pr *));
28115 if( apNew == 0 ){
28116 /* Not so fatal, simply a performance hit */
28117 return SXRET_OK;
28118 }
28119 /* Zero the new table */
28120 SyZero((void *)apNew, nNewSize * sizeof(SyHashEntry_Pr *));
28121 /* Rehash all entries */
28122 for( n = 0, pEntry = pHash->pList; n < pHash->nEntry ; n++ ){
28123 pEntry->pNextCollide = pEntry->pPrevCollide = 0;
28124 /* Install in the new bucket */
28125 iBucket = pEntry->nHash & (nNewSize - 1);
28126 pEntry->pNextCollide = apNew[iBucket];
28127 if( apNew[iBucket] != 0 ){
28128 apNew[iBucket]->pPrevCollide = pEntry;
28129 }
28130 apNew[iBucket] = pEntry;
28131 /* Point to the next entry */
28132 pEntry = pEntry->pNext;
28133 }
28134 /* Release the old table and reflect the change */
28135 SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket);
28136 pHash->apBucket = apNew;
28137 pHash->nBucketSize = nNewSize;
28138 return SXRET_OK;
28139}
28140static sxi32 HashInsert(SyHash *pHash, SyHashEntry_Pr *pEntry)
28141{
28142 sxu32 iBucket = pEntry->nHash & (pHash->nBucketSize - 1);
28143 /* Insert the entry in its corresponding bcuket */
28144 pEntry->pNextCollide = pHash->apBucket[iBucket];
28145 if( pHash->apBucket[iBucket] != 0 ){
28146 pHash->apBucket[iBucket]->pPrevCollide = pEntry;
28147 }
28148 pHash->apBucket[iBucket] = pEntry;
28149 /* Link to the entry list */
28150 MACRO_LD_PUSH(pHash->pList, pEntry);
28151 if( pHash->nEntry == 0 ){
28152 pHash->pCurrent = pHash->pList;
28153 }
28154 pHash->nEntry++;
28155 return SXRET_OK;
28156}
28157JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData)
28158{
28159 SyHashEntry_Pr *pEntry;
28160 sxi32 rc;
28161#if defined(UNTRUST)
28162 if( INVALID_HASH(pHash) || pKey == 0 ){
28163 return SXERR_CORRUPT;
28164 }
28165#endif
28166 if( pHash->nEntry >= pHash->nBucketSize * SXHASH_FILL_FACTOR ){
28167 rc = HashGrowTable(&(*pHash));
28168 if( rc != SXRET_OK ){
28169 return rc;
28170 }
28171 }
28172 /* Allocate a new hash entry */
28173 pEntry = (SyHashEntry_Pr *)SyMemBackendPoolAlloc(pHash->pAllocator, sizeof(SyHashEntry_Pr));
28174 if( pEntry == 0 ){
28175 return SXERR_MEM;
28176 }
28177 /* Zero the entry */
28178 SyZero(pEntry, sizeof(SyHashEntry_Pr));
28179 pEntry->pHash = pHash;
28180 pEntry->pKey = pKey;
28181 pEntry->nKeyLen = nKeyLen;
28182 pEntry->pUserData = pUserData;
28183 pEntry->nHash = pHash->xHash(pEntry->pKey, pEntry->nKeyLen);
28184 /* Finally insert the entry in its corresponding bucket */
28185 rc = HashInsert(&(*pHash), pEntry);
28186 return rc;
28187}
28188/* SyRunTimeApi:sxutils.c */
28189JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char **pzTail)
28190{
28191 const char *zCur, *zEnd;
28192#ifdef UNTRUST
28193 if( SX_EMPTY_STR(zSrc) ){
28194 return SXERR_EMPTY;
28195 }
28196#endif
28197 zEnd = &zSrc[nLen];
28198 /* Jump leading white spaces */
28199 while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisSpace(zSrc[0]) ){
28200 zSrc++;
28201 }
28202 if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
28203 zSrc++;
28204 }
28205 zCur = zSrc;
28206 if( pReal ){
28207 *pReal = FALSE;
28208 }
28209 for(;;){
28210 if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
28211 if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
28212 if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
28213 if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
28214 };
28215 if( zSrc < zEnd && zSrc > zCur ){
28216 int c = zSrc[0];
28217 if( c == '.' ){
28218 zSrc++;
28219 if( pReal ){
28220 *pReal = TRUE;
28221 }
28222 if( pzTail ){
28223 while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
28224 zSrc++;
28225 }
28226 if( zSrc < zEnd && (zSrc[0] == 'e' || zSrc[0] == 'E') ){
28227 zSrc++;
28228 if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
28229 zSrc++;
28230 }
28231 while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
28232 zSrc++;
28233 }
28234 }
28235 }
28236 }else if( c == 'e' || c == 'E' ){
28237 zSrc++;
28238 if( pReal ){
28239 *pReal = TRUE;
28240 }
28241 if( pzTail ){
28242 if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
28243 zSrc++;
28244 }
28245 while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
28246 zSrc++;
28247 }
28248 }
28249 }
28250 }
28251 if( pzTail ){
28252 /* Point to the non numeric part */
28253 *pzTail = zSrc;
28254 }
28255 return zSrc > zCur ? SXRET_OK /* String prefix is numeric */ : SXERR_INVALID /* Not a digit stream */;
28256}
28257#define SXINT32_MIN_STR "2147483648"
28258#define SXINT32_MAX_STR "2147483647"
28259#define SXINT64_MIN_STR "9223372036854775808"
28260#define SXINT64_MAX_STR "9223372036854775807"
28261JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28262{
28263 int isNeg = FALSE;
28264 const char *zEnd;
28265 sxi32 nVal = 0;
28266 sxi16 i;
28267#if defined(UNTRUST)
28268 if( SX_EMPTY_STR(zSrc) ){
28269 if( pOutVal ){
28270 *(sxi32 *)pOutVal = 0;
28271 }
28272 return SXERR_EMPTY;
28273 }
28274#endif
28275 zEnd = &zSrc[nLen];
28276 while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
28277 zSrc++;
28278 }
28279 if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
28280 isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
28281 zSrc++;
28282 }
28283 /* Skip leading zero */
28284 while(zSrc < zEnd && zSrc[0] == '0' ){
28285 zSrc++;
28286 }
28287 i = 10;
28288 if( (sxu32)(zEnd-zSrc) >= 10 ){
28289 /* Handle overflow */
28290 i = SyMemcmp(zSrc, (isNeg == TRUE) ? SXINT32_MIN_STR : SXINT32_MAX_STR, nLen) <= 0 ? 10 : 9;
28291 }
28292 for(;;){
28293 if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28294 if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28295 if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28296 if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28297 }
28298 /* Skip trailing spaces */
28299 while(zSrc < zEnd && SyisSpace(zSrc[0])){
28300 zSrc++;
28301 }
28302 if( zRest ){
28303 *zRest = (char *)zSrc;
28304 }
28305 if( pOutVal ){
28306 if( isNeg == TRUE && nVal != 0 ){
28307 nVal = -nVal;
28308 }
28309 *(sxi32 *)pOutVal = nVal;
28310 }
28311 return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
28312}
28313JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28314{
28315 int isNeg = FALSE;
28316 const char *zEnd;
28317 sxi64 nVal;
28318 sxi16 i;
28319#if defined(UNTRUST)
28320 if( SX_EMPTY_STR(zSrc) ){
28321 if( pOutVal ){
28322 *(sxi32 *)pOutVal = 0;
28323 }
28324 return SXERR_EMPTY;
28325 }
28326#endif
28327 zEnd = &zSrc[nLen];
28328 while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
28329 zSrc++;
28330 }
28331 if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
28332 isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
28333 zSrc++;
28334 }
28335 /* Skip leading zero */
28336 while(zSrc < zEnd && zSrc[0] == '0' ){
28337 zSrc++;
28338 }
28339 i = 19;
28340 if( (sxu32)(zEnd-zSrc) >= 19 ){
28341 i = SyMemcmp(zSrc, isNeg ? SXINT64_MIN_STR : SXINT64_MAX_STR, 19) <= 0 ? 19 : 18 ;
28342 }
28343 nVal = 0;
28344 for(;;){
28345 if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28346 if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28347 if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28348 if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28349 }
28350 /* Skip trailing spaces */
28351 while(zSrc < zEnd && SyisSpace(zSrc[0])){
28352 zSrc++;
28353 }
28354 if( zRest ){
28355 *zRest = (char *)zSrc;
28356 }
28357 if( pOutVal ){
28358 if( isNeg == TRUE && nVal != 0 ){
28359 nVal = -nVal;
28360 }
28361 *(sxi64 *)pOutVal = nVal;
28362 }
28363 return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
28364}
28365JX9_PRIVATE sxi32 SyHexToint(sxi32 c)
28366{
28367 switch(c){
28368 case '0': return 0;
28369 case '1': return 1;
28370 case '2': return 2;
28371 case '3': return 3;
28372 case '4': return 4;
28373 case '5': return 5;
28374 case '6': return 6;
28375 case '7': return 7;
28376 case '8': return 8;
28377 case '9': return 9;
28378 case 'A': case 'a': return 10;
28379 case 'B': case 'b': return 11;
28380 case 'C': case 'c': return 12;
28381 case 'D': case 'd': return 13;
28382 case 'E': case 'e': return 14;
28383 case 'F': case 'f': return 15;
28384 }
28385 return -1;
28386}
28387JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28388{
28389 const char *zIn, *zEnd;
28390 int isNeg = FALSE;
28391 sxi64 nVal = 0;
28392#if defined(UNTRUST)
28393 if( SX_EMPTY_STR(zSrc) ){
28394 if( pOutVal ){
28395 *(sxi32 *)pOutVal = 0;
28396 }
28397 return SXERR_EMPTY;
28398 }
28399#endif
28400 zEnd = &zSrc[nLen];
28401 while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
28402 zSrc++;
28403 }
28404 if( zSrc < zEnd && ( *zSrc == '-' || *zSrc == '+' ) ){
28405 isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
28406 zSrc++;
28407 }
28408 if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'x' || zSrc[1] == 'X') ){
28409 /* Bypass hex prefix */
28410 zSrc += sizeof(char) * 2;
28411 }
28412 /* Skip leading zero */
28413 while(zSrc < zEnd && zSrc[0] == '0' ){
28414 zSrc++;
28415 }
28416 zIn = zSrc;
28417 for(;;){
28418 if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ;
28419 if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ;
28420 if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ;
28421 if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ;
28422 }
28423 while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
28424 zSrc++;
28425 }
28426 if( zRest ){
28427 *zRest = zSrc;
28428 }
28429 if( pOutVal ){
28430 if( isNeg == TRUE && nVal != 0 ){
28431 nVal = -nVal;
28432 }
28433 *(sxi64 *)pOutVal = nVal;
28434 }
28435 return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
28436}
28437JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28438{
28439 const char *zIn, *zEnd;
28440 int isNeg = FALSE;
28441 sxi64 nVal = 0;
28442 int c;
28443#if defined(UNTRUST)
28444 if( SX_EMPTY_STR(zSrc) ){
28445 if( pOutVal ){
28446 *(sxi32 *)pOutVal = 0;
28447 }
28448 return SXERR_EMPTY;
28449 }
28450#endif
28451 zEnd = &zSrc[nLen];
28452 while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
28453 zSrc++;
28454 }
28455 if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
28456 isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
28457 zSrc++;
28458 }
28459 /* Skip leading zero */
28460 while(zSrc < zEnd && zSrc[0] == '0' ){
28461 zSrc++;
28462 }
28463 zIn = zSrc;
28464 for(;;){
28465 if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++;
28466 if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++;
28467 if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++;
28468 if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++;
28469 }
28470 /* Skip trailing spaces */
28471 while(zSrc < zEnd && SyisSpace(zSrc[0])){
28472 zSrc++;
28473 }
28474 if( zRest ){
28475 *zRest = zSrc;
28476 }
28477 if( pOutVal ){
28478 if( isNeg == TRUE && nVal != 0 ){
28479 nVal = -nVal;
28480 }
28481 *(sxi64 *)pOutVal = nVal;
28482 }
28483 return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
28484}
28485JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28486{
28487 const char *zIn, *zEnd;
28488 int isNeg = FALSE;
28489 sxi64 nVal = 0;
28490 int c;
28491#if defined(UNTRUST)
28492 if( SX_EMPTY_STR(zSrc) ){
28493 if( pOutVal ){
28494 *(sxi32 *)pOutVal = 0;
28495 }
28496 return SXERR_EMPTY;
28497 }
28498#endif
28499 zEnd = &zSrc[nLen];
28500 while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
28501 zSrc++;
28502 }
28503 if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
28504 isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
28505 zSrc++;
28506 }
28507 if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'b' || zSrc[1] == 'B') ){
28508 /* Bypass binary prefix */
28509 zSrc += sizeof(char) * 2;
28510 }
28511 /* Skip leading zero */
28512 while(zSrc < zEnd && zSrc[0] == '0' ){
28513 zSrc++;
28514 }
28515 zIn = zSrc;
28516 for(;;){
28517 if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
28518 if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
28519 if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
28520 if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
28521 }
28522 /* Skip trailing spaces */
28523 while(zSrc < zEnd && SyisSpace(zSrc[0])){
28524 zSrc++;
28525 }
28526 if( zRest ){
28527 *zRest = zSrc;
28528 }
28529 if( pOutVal ){
28530 if( isNeg == TRUE && nVal != 0 ){
28531 nVal = -nVal;
28532 }
28533 *(sxi64 *)pOutVal = nVal;
28534 }
28535 return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
28536}
28537JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28538{
28539#define SXDBL_DIG 15
28540#define SXDBL_MAX_EXP 308
28541#define SXDBL_MIN_EXP_PLUS 307
28542 static const sxreal aTab[] = {
28543 10,
28544 1.0e2,
28545 1.0e4,
28546 1.0e8,
28547 1.0e16,
28548 1.0e32,
28549 1.0e64,
28550 1.0e128,
28551 1.0e256
28552 };
28553 sxu8 neg = FALSE;
28554 sxreal Val = 0.0;
28555 const char *zEnd;
28556 sxi32 Lim, exp;
28557 sxreal *p = 0;
28558#ifdef UNTRUST
28559 if( SX_EMPTY_STR(zSrc) ){
28560 if( pOutVal ){
28561 *(sxreal *)pOutVal = 0.0;
28562 }
28563 return SXERR_EMPTY;
28564 }
28565#endif
28566 zEnd = &zSrc[nLen];
28567 while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
28568 zSrc++;
28569 }
28570 if( zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+' ) ){
28571 neg = zSrc[0] == '-' ? TRUE : FALSE ;
28572 zSrc++;
28573 }
28574 Lim = SXDBL_DIG ;
28575 for(;;){
28576 if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
28577 if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
28578 if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
28579 if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
28580 }
28581 if( zSrc < zEnd && ( zSrc[0] == '.' || zSrc[0] == ',' ) ){
28582 sxreal dec = 1.0;
28583 zSrc++;
28584 for(;;){
28585 if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
28586 if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
28587 if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
28588 if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
28589 }
28590 Val /= dec;
28591 }
28592 if( neg == TRUE && Val != 0.0 ) {
28593 Val = -Val ;
28594 }
28595 if( Lim <= 0 ){
28596 /* jump overflow digit */
28597 while( zSrc < zEnd ){
28598 if( zSrc[0] == 'e' || zSrc[0] == 'E' ){
28599 break;
28600 }
28601 zSrc++;
28602 }
28603 }
28604 neg = FALSE;
28605 if( zSrc < zEnd && ( zSrc[0] == 'e' || zSrc[0] == 'E' ) ){
28606 zSrc++;
28607 if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+') ){
28608 neg = zSrc[0] == '-' ? TRUE : FALSE ;
28609 zSrc++;
28610 }
28611 exp = 0;
28612 while( zSrc < zEnd && SyisDigit(zSrc[0]) && exp < SXDBL_MAX_EXP ){
28613 exp = exp * 10 + (zSrc[0] - '0');
28614 zSrc++;
28615 }
28616 if( neg ){
28617 if( exp > SXDBL_MIN_EXP_PLUS ) exp = SXDBL_MIN_EXP_PLUS ;
28618 }else if ( exp > SXDBL_MAX_EXP ){
28619 exp = SXDBL_MAX_EXP;
28620 }
28621 for( p = (sxreal *)aTab ; exp ; exp >>= 1 , p++ ){
28622 if( exp & 01 ){
28623 if( neg ){
28624 Val /= *p ;
28625 }else{
28626 Val *= *p;
28627 }
28628 }
28629 }
28630 }
28631 while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
28632 zSrc++;
28633 }
28634 if( zRest ){
28635 *zRest = zSrc;
28636 }
28637 if( pOutVal ){
28638 *(sxreal *)pOutVal = Val;
28639 }
28640 return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
28641}
28642/* SyRunTimeApi:sxlib.c */
28643JX9_PRIVATE sxu32 SyBinHash(const void *pSrc, sxu32 nLen)
28644{
28645 register unsigned char *zIn = (unsigned char *)pSrc;
28646 unsigned char *zEnd;
28647 sxu32 nH = 5381;
28648 zEnd = &zIn[nLen];
28649 for(;;){
28650 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
28651 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
28652 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
28653 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
28654 }
28655 return nH;
28656}
28657#ifndef JX9_DISABLE_BUILTIN_FUNC
28658JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
28659{
28660 static const unsigned char zBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
28661 unsigned char *zIn = (unsigned char *)zSrc;
28662 unsigned char z64[4];
28663 sxu32 i;
28664 sxi32 rc;
28665#if defined(UNTRUST)
28666 if( SX_EMPTY_STR(zSrc) || xConsumer == 0){
28667 return SXERR_EMPTY;
28668 }
28669#endif
28670 for(i = 0; i + 2 < nLen; i += 3){
28671 z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
28672 z64[1] = zBase64[( ((zIn[i] & 0x03) << 4) | (zIn[i+1] >> 4)) & 0x3F];
28673 z64[2] = zBase64[( ((zIn[i+1] & 0x0F) << 2) | (zIn[i + 2] >> 6) ) & 0x3F];
28674 z64[3] = zBase64[ zIn[i + 2] & 0x3F];
28675
28676 rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
28677 if( rc != SXRET_OK ){return SXERR_ABORT;}
28678
28679 }
28680 if ( i+1 < nLen ){
28681 z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
28682 z64[1] = zBase64[( ((zIn[i] & 0x03) << 4) | (zIn[i+1] >> 4)) & 0x3F];
28683 z64[2] = zBase64[(zIn[i+1] & 0x0F) << 2 ];
28684 z64[3] = '=';
28685
28686 rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
28687 if( rc != SXRET_OK ){return SXERR_ABORT;}
28688
28689 }else if( i < nLen ){
28690 z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
28691 z64[1] = zBase64[(zIn[i] & 0x03) << 4];
28692 z64[2] = '=';
28693 z64[3] = '=';
28694
28695 rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
28696 if( rc != SXRET_OK ){return SXERR_ABORT;}
28697 }
28698
28699 return SXRET_OK;
28700}
28701JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
28702{
28703 static const sxu32 aBase64Trans[] = {
28704 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
28705 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4,
28706 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27,
28707 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0,
28708 0, 0, 0
28709 };
28710 sxu32 n, w, x, y, z;
28711 sxi32 rc;
28712 unsigned char zOut[10];
28713#if defined(UNTRUST)
28714 if( SX_EMPTY_STR(zB64) || xConsumer == 0 ){
28715 return SXERR_EMPTY;
28716 }
28717#endif
28718 while(nLen > 0 && zB64[nLen - 1] == '=' ){
28719 nLen--;
28720 }
28721 for( n = 0 ; n+3<nLen ; n += 4){
28722 w = aBase64Trans[zB64[n] & 0x7F];
28723 x = aBase64Trans[zB64[n+1] & 0x7F];
28724 y = aBase64Trans[zB64[n+2] & 0x7F];
28725 z = aBase64Trans[zB64[n+3] & 0x7F];
28726 zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
28727 zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F);
28728 zOut[2] = ((y<<6) & 0xC0) | (z & 0x3F);
28729
28730 rc = xConsumer((const void *)zOut, sizeof(unsigned char)*3, pUserData);
28731 if( rc != SXRET_OK ){ return SXERR_ABORT;}
28732 }
28733 if( n+2 < nLen ){
28734 w = aBase64Trans[zB64[n] & 0x7F];
28735 x = aBase64Trans[zB64[n+1] & 0x7F];
28736 y = aBase64Trans[zB64[n+2] & 0x7F];
28737
28738 zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
28739 zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F);
28740
28741 rc = xConsumer((const void *)zOut, sizeof(unsigned char)*2, pUserData);
28742 if( rc != SXRET_OK ){ return SXERR_ABORT;}
28743 }else if( n+1 < nLen ){
28744 w = aBase64Trans[zB64[n] & 0x7F];
28745 x = aBase64Trans[zB64[n+1] & 0x7F];
28746
28747 zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
28748
28749 rc = xConsumer((const void *)zOut, sizeof(unsigned char)*1, pUserData);
28750 if( rc != SXRET_OK ){ return SXERR_ABORT;}
28751 }
28752 return SXRET_OK;
28753}
28754#endif /* JX9_DISABLE_BUILTIN_FUNC */
28755#define INVALID_LEXER(LEX) ( LEX == 0 || LEX->xTokenizer == 0 )
28756JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData)
28757{
28758 SyStream *pStream;
28759#if defined (UNTRUST)
28760 if ( pLex == 0 || xTokenizer == 0 ){
28761 return SXERR_CORRUPT;
28762 }
28763#endif
28764 pLex->pTokenSet = 0;
28765 /* Initialize lexer fields */
28766 if( pSet ){
28767 if ( SySetElemSize(pSet) != sizeof(SyToken) ){
28768 return SXERR_INVALID;
28769 }
28770 pLex->pTokenSet = pSet;
28771 }
28772 pStream = &pLex->sStream;
28773 pLex->xTokenizer = xTokenizer;
28774 pLex->pUserData = pUserData;
28775
28776 pStream->nLine = 1;
28777 pStream->nIgn = 0;
28778 pStream->zText = pStream->zEnd = 0;
28779 pStream->pSet = pSet;
28780 return SXRET_OK;
28781}
28782JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp)
28783{
28784 const unsigned char *zCur;
28785 SyStream *pStream;
28786 SyToken sToken;
28787 sxi32 rc;
28788#if defined (UNTRUST)
28789 if ( INVALID_LEXER(pLex) || zInput == 0 ){
28790 return SXERR_CORRUPT;
28791 }
28792#endif
28793 pStream = &pLex->sStream;
28794 /* Point to the head of the input */
28795 pStream->zText = pStream->zInput = (const unsigned char *)zInput;
28796 /* Point to the end of the input */
28797 pStream->zEnd = &pStream->zInput[nLen];
28798 for(;;){
28799 if( pStream->zText >= pStream->zEnd ){
28800 /* End of the input reached */
28801 break;
28802 }
28803 zCur = pStream->zText;
28804 /* Call the tokenizer callback */
28805 rc = pLex->xTokenizer(pStream, &sToken, pLex->pUserData, pCtxData);
28806 if( rc != SXRET_OK && rc != SXERR_CONTINUE ){
28807 /* Tokenizer callback request an operation abort */
28808 if( rc == SXERR_ABORT ){
28809 return SXERR_ABORT;
28810 }
28811 break;
28812 }
28813 if( rc == SXERR_CONTINUE ){
28814 /* Request to ignore this token */
28815 pStream->nIgn++;
28816 }else if( pLex->pTokenSet ){
28817 /* Put the token in the set */
28818 rc = SySetPut(pLex->pTokenSet, (const void *)&sToken);
28819 if( rc != SXRET_OK ){
28820 break;
28821 }
28822 }
28823 if( zCur >= pStream->zText ){
28824 /* Automatic advance of the stream cursor */
28825 pStream->zText = &zCur[1];
28826 }
28827 }
28828 if( xSort && pLex->pTokenSet ){
28829 SyToken *aToken = (SyToken *)SySetBasePtr(pLex->pTokenSet);
28830 /* Sort the extrated tokens */
28831 if( xCmp == 0 ){
28832 /* Use a default comparison function */
28833 xCmp = SyMemcmp;
28834 }
28835 xSort(aToken, SySetUsed(pLex->pTokenSet), sizeof(SyToken), xCmp);
28836 }
28837 return SXRET_OK;
28838}
28839JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex)
28840{
28841 sxi32 rc = SXRET_OK;
28842#if defined (UNTRUST)
28843 if ( INVALID_LEXER(pLex) ){
28844 return SXERR_CORRUPT;
28845 }
28846#else
28847 SXUNUSED(pLex); /* Prevent compiler warning */
28848#endif
28849 return rc;
28850}
28851#ifndef JX9_DISABLE_BUILTIN_FUNC
28852#define SAFE_HTTP(C) (SyisAlphaNum(c) || c == '_' || c == '-' || c == '$' || c == '.' )
28853JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
28854{
28855 unsigned char *zIn = (unsigned char *)zSrc;
28856 unsigned char zHex[3] = { '%', 0, 0 };
28857 unsigned char zOut[2];
28858 unsigned char *zCur, *zEnd;
28859 sxi32 c;
28860 sxi32 rc;
28861#ifdef UNTRUST
28862 if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){
28863 return SXERR_EMPTY;
28864 }
28865#endif
28866 rc = SXRET_OK;
28867 zEnd = &zIn[nLen]; zCur = zIn;
28868 for(;;){
28869 if( zCur >= zEnd ){
28870 if( zCur != zIn ){
28871 rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData);
28872 }
28873 break;
28874 }
28875 c = zCur[0];
28876 if( SAFE_HTTP(c) ){
28877 zCur++; continue;
28878 }
28879 if( zCur != zIn && SXRET_OK != (rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData))){
28880 break;
28881 }
28882 if( c == ' ' ){
28883 zOut[0] = '+';
28884 rc = xConsumer((const void *)zOut, sizeof(unsigned char), pUserData);
28885 }else{
28886 zHex[1] = "0123456789ABCDEF"[(c >> 4) & 0x0F];
28887 zHex[2] = "0123456789ABCDEF"[c & 0x0F];
28888 rc = xConsumer(zHex, sizeof(zHex), pUserData);
28889 }
28890 if( SXRET_OK != rc ){
28891 break;
28892 }
28893 zIn = &zCur[1]; zCur = zIn ;
28894 }
28895 return rc == SXRET_OK ? SXRET_OK : SXERR_ABORT;
28896}
28897#endif /* JX9_DISABLE_BUILTIN_FUNC */
28898static sxi32 SyAsciiToHex(sxi32 c)
28899{
28900 if( c >= 'a' && c <= 'f' ){
28901 c += 10 - 'a';
28902 return c;
28903 }
28904 if( c >= '0' && c <= '9' ){
28905 c -= '0';
28906 return c;
28907 }
28908 if( c >= 'A' && c <= 'F') {
28909 c += 10 - 'A';
28910 return c;
28911 }
28912 return 0;
28913}
28914JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8)
28915{
28916 static const sxu8 Utf8Trans[] = {
28917 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
28918 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
28919 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
28920 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
28921 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
28922 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
28923 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
28924 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00
28925 };
28926 const char *zIn = zSrc;
28927 const char *zEnd;
28928 const char *zCur;
28929 sxu8 *zOutPtr;
28930 sxu8 zOut[10];
28931 sxi32 c, d;
28932 sxi32 rc;
28933#if defined(UNTRUST)
28934 if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){
28935 return SXERR_EMPTY;
28936 }
28937#endif
28938 rc = SXRET_OK;
28939 zEnd = &zSrc[nLen];
28940 zCur = zIn;
28941 for(;;){
28942 while(zCur < zEnd && zCur[0] != '%' && zCur[0] != '+' ){
28943 zCur++;
28944 }
28945 if( zCur != zIn ){
28946 /* Consume input */
28947 rc = xConsumer(zIn, (unsigned int)(zCur-zIn), pUserData);
28948 if( rc != SXRET_OK ){
28949 /* User consumer routine request an operation abort */
28950 break;
28951 }
28952 }
28953 if( zCur >= zEnd ){
28954 rc = SXRET_OK;
28955 break;
28956 }
28957 /* Decode unsafe HTTP characters */
28958 zOutPtr = zOut;
28959 if( zCur[0] == '+' ){
28960 *zOutPtr++ = ' ';
28961 zCur++;
28962 }else{
28963 if( &zCur[2] >= zEnd ){
28964 rc = SXERR_OVERFLOW;
28965 break;
28966 }
28967 c = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]);
28968 zCur += 3;
28969 if( c < 0x000C0 ){
28970 *zOutPtr++ = (sxu8)c;
28971 }else{
28972 c = Utf8Trans[c-0xC0];
28973 while( zCur[0] == '%' ){
28974 d = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]);
28975 if( (d&0xC0) != 0x80 ){
28976 break;
28977 }
28978 c = (c<<6) + (0x3f & d);
28979 zCur += 3;
28980 }
28981 if( bUTF8 == FALSE ){
28982 *zOutPtr++ = (sxu8)c;
28983 }else{
28984 SX_WRITE_UTF8(zOutPtr, c);
28985 }
28986 }
28987
28988 }
28989 /* Consume the decoded characters */
28990 rc = xConsumer((const void *)zOut, (unsigned int)(zOutPtr-zOut), pUserData);
28991 if( rc != SXRET_OK ){
28992 break;
28993 }
28994 /* Synchronize pointers */
28995 zIn = zCur;
28996 }
28997 return rc;
28998}
28999#ifndef JX9_DISABLE_BUILTIN_FUNC
29000static const char *zEngDay[] = {
29001 "Sunday", "Monday", "Tuesday", "Wednesday",
29002 "Thursday", "Friday", "Saturday"
29003};
29004static const char *zEngMonth[] = {
29005 "January", "February", "March", "April",
29006 "May", "June", "July", "August",
29007 "September", "October", "November", "December"
29008};
29009static const char * GetDay(sxi32 i)
29010{
29011 return zEngDay[ i % 7 ];
29012}
29013static const char * GetMonth(sxi32 i)
29014{
29015 return zEngMonth[ i % 12 ];
29016}
29017JX9_PRIVATE const char * SyTimeGetDay(sxi32 iDay)
29018{
29019 return GetDay(iDay);
29020}
29021JX9_PRIVATE const char * SyTimeGetMonth(sxi32 iMonth)
29022{
29023 return GetMonth(iMonth);
29024}
29025#endif /* JX9_DISABLE_BUILTIN_FUNC */
29026/* SyRunTimeApi: sxfmt.c */
29027#define SXFMT_BUFSIZ 1024 /* Conversion buffer size */
29028/*
29029** Conversion types fall into various categories as defined by the
29030** following enumeration.
29031*/
29032#define SXFMT_RADIX 1 /* Integer types.%d, %x, %o, and so forth */
29033#define SXFMT_FLOAT 2 /* Floating point.%f */
29034#define SXFMT_EXP 3 /* Exponentional notation.%e and %E */
29035#define SXFMT_GENERIC 4 /* Floating or exponential, depending on exponent.%g */
29036#define SXFMT_SIZE 5 /* Total number of characters processed so far.%n */
29037#define SXFMT_STRING 6 /* Strings.%s */
29038#define SXFMT_PERCENT 7 /* Percent symbol.%% */
29039#define SXFMT_CHARX 8 /* Characters.%c */
29040#define SXFMT_ERROR 9 /* Used to indicate no such conversion type */
29041/* Extension by Symisc Systems */
29042#define SXFMT_RAWSTR 13 /* %z Pointer to raw string (SyString *) */
29043#define SXFMT_UNUSED 15
29044/*
29045** Allowed values for SyFmtInfo.flags
29046*/
29047#define SXFLAG_SIGNED 0x01
29048#define SXFLAG_UNSIGNED 0x02
29049/* Allowed values for SyFmtConsumer.nType */
29050#define SXFMT_CONS_PROC 1 /* Consumer is a procedure */
29051#define SXFMT_CONS_STR 2 /* Consumer is a managed string */
29052#define SXFMT_CONS_FILE 5 /* Consumer is an open File */
29053#define SXFMT_CONS_BLOB 6 /* Consumer is a BLOB */
29054/*
29055** Each builtin conversion character (ex: the 'd' in "%d") is described
29056** by an instance of the following structure
29057*/
29058typedef struct SyFmtInfo SyFmtInfo;
29059struct SyFmtInfo
29060{
29061 char fmttype; /* The format field code letter [i.e: 'd', 's', 'x'] */
29062 sxu8 base; /* The base for radix conversion */
29063 int flags; /* One or more of SXFLAG_ constants below */
29064 sxu8 type; /* Conversion paradigm */
29065 char *charset; /* The character set for conversion */
29066 char *prefix; /* Prefix on non-zero values in alt format */
29067};
29068typedef struct SyFmtConsumer SyFmtConsumer;
29069struct SyFmtConsumer
29070{
29071 sxu32 nLen; /* Total output length */
29072 sxi32 nType; /* Type of the consumer see below */
29073 sxi32 rc; /* Consumer return value;Abort processing if rc != SXRET_OK */
29074 union{
29075 struct{
29076 ProcConsumer xUserConsumer;
29077 void *pUserData;
29078 }sFunc;
29079 SyBlob *pBlob;
29080 }uConsumer;
29081};
29082#ifndef SX_OMIT_FLOATINGPOINT
29083static int getdigit(sxlongreal *val, int *cnt)
29084{
29085 sxlongreal d;
29086 int digit;
29087
29088 if( (*cnt)++ >= 16 ){
29089 return '0';
29090 }
29091 digit = (int)*val;
29092 d = digit;
29093 *val = (*val - d)*10.0;
29094 return digit + '0' ;
29095}
29096#endif /* SX_OMIT_FLOATINGPOINT */
29097/*
29098 * The following routine was taken from the SQLITE2 source tree and was
29099 * extended by Symisc Systems to fit its need.
29100 * Status: Public Domain
29101 */
29102static sxi32 InternFormat(ProcConsumer xConsumer, void *pUserData, const char *zFormat, va_list ap)
29103{
29104 /*
29105 * The following table is searched linearly, so it is good to put the most frequently
29106 * used conversion types first.
29107 */
29108static const SyFmtInfo aFmt[] = {
29109 { 'd', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0 },
29110 { 's', 0, 0, SXFMT_STRING, 0, 0 },
29111 { 'c', 0, 0, SXFMT_CHARX, 0, 0 },
29112 { 'x', 16, 0, SXFMT_RADIX, "0123456789abcdef", "x0" },
29113 { 'X', 16, 0, SXFMT_RADIX, "0123456789ABCDEF", "X0" },
29114 /* -- Extensions by Symisc Systems -- */
29115 { 'z', 0, 0, SXFMT_RAWSTR, 0, 0 }, /* Pointer to a raw string (SyString *) */
29116 { 'B', 2, 0, SXFMT_RADIX, "01", "b0"},
29117 /* -- End of Extensions -- */
29118 { 'o', 8, 0, SXFMT_RADIX, "01234567", "0" },
29119 { 'u', 10, 0, SXFMT_RADIX, "0123456789", 0 },
29120#ifndef SX_OMIT_FLOATINGPOINT
29121 { 'f', 0, SXFLAG_SIGNED, SXFMT_FLOAT, 0, 0 },
29122 { 'e', 0, SXFLAG_SIGNED, SXFMT_EXP, "e", 0 },
29123 { 'E', 0, SXFLAG_SIGNED, SXFMT_EXP, "E", 0 },
29124 { 'g', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "e", 0 },
29125 { 'G', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "E", 0 },
29126#endif
29127 { 'i', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0 },
29128 { 'n', 0, 0, SXFMT_SIZE, 0, 0 },
29129 { '%', 0, 0, SXFMT_PERCENT, 0, 0 },
29130 { 'p', 10, 0, SXFMT_RADIX, "0123456789", 0 }
29131};
29132 int c; /* Next character in the format string */
29133 char *bufpt; /* Pointer to the conversion buffer */
29134 int precision; /* Precision of the current field */
29135 int length; /* Length of the field */
29136 int idx; /* A general purpose loop counter */
29137 int width; /* Width of the current field */
29138 sxu8 flag_leftjustify; /* True if "-" flag is present */
29139 sxu8 flag_plussign; /* True if "+" flag is present */
29140 sxu8 flag_blanksign; /* True if " " flag is present */
29141 sxu8 flag_alternateform; /* True if "#" flag is present */
29142 sxu8 flag_zeropad; /* True if field width constant starts with zero */
29143 sxu8 flag_long; /* True if "l" flag is present */
29144 sxi64 longvalue; /* Value for integer types */
29145 const SyFmtInfo *infop; /* Pointer to the appropriate info structure */
29146 char buf[SXFMT_BUFSIZ]; /* Conversion buffer */
29147 char prefix; /* Prefix character."+" or "-" or " " or '\0'.*/
29148 sxu8 errorflag = 0; /* True if an error is encountered */
29149 sxu8 xtype; /* Conversion paradigm */
29150 char *zExtra;
29151 static char spaces[] = " ";
29152#define etSPACESIZE ((int)sizeof(spaces)-1)
29153#ifndef SX_OMIT_FLOATINGPOINT
29154 sxlongreal realvalue; /* Value for real types */
29155 int exp; /* exponent of real numbers */
29156 double rounder; /* Used for rounding floating point values */
29157 sxu8 flag_dp; /* True if decimal point should be shown */
29158 sxu8 flag_rtz; /* True if trailing zeros should be removed */
29159 sxu8 flag_exp; /* True to force display of the exponent */
29160 int nsd; /* Number of significant digits returned */
29161#endif
29162 int rc;
29163
29164 length = 0;
29165 bufpt = 0;
29166 for(; (c=(*zFormat))!=0; ++zFormat){
29167 if( c!='%' ){
29168 unsigned int amt;
29169 bufpt = (char *)zFormat;
29170 amt = 1;
29171 while( (c=(*++zFormat))!='%' && c!=0 ) amt++;
29172 rc = xConsumer((const void *)bufpt, amt, pUserData);
29173 if( rc != SXRET_OK ){
29174 return SXERR_ABORT; /* Consumer routine request an operation abort */
29175 }
29176 if( c==0 ){
29177 return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
29178 }
29179 }
29180 if( (c=(*++zFormat))==0 ){
29181 errorflag = 1;
29182 rc = xConsumer("%", sizeof("%")-1, pUserData);
29183 if( rc != SXRET_OK ){
29184 return SXERR_ABORT; /* Consumer routine request an operation abort */
29185 }
29186 return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
29187 }
29188 /* Find out what flags are present */
29189 flag_leftjustify = flag_plussign = flag_blanksign =
29190 flag_alternateform = flag_zeropad = 0;
29191 do{
29192 switch( c ){
29193 case '-': flag_leftjustify = 1; c = 0; break;
29194 case '+': flag_plussign = 1; c = 0; break;
29195 case ' ': flag_blanksign = 1; c = 0; break;
29196 case '#': flag_alternateform = 1; c = 0; break;
29197 case '0': flag_zeropad = 1; c = 0; break;
29198 default: break;
29199 }
29200 }while( c==0 && (c=(*++zFormat))!=0 );
29201 /* Get the field width */
29202 width = 0;
29203 if( c=='*' ){
29204 width = va_arg(ap, int);
29205 if( width<0 ){
29206 flag_leftjustify = 1;
29207 width = -width;
29208 }
29209 c = *++zFormat;
29210 }else{
29211 while( c>='0' && c<='9' ){
29212 width = width*10 + c - '0';
29213 c = *++zFormat;
29214 }
29215 }
29216 if( width > SXFMT_BUFSIZ-10 ){
29217 width = SXFMT_BUFSIZ-10;
29218 }
29219 /* Get the precision */
29220 precision = -1;
29221 if( c=='.' ){
29222 precision = 0;
29223 c = *++zFormat;
29224 if( c=='*' ){
29225 precision = va_arg(ap, int);
29226 if( precision<0 ) precision = -precision;
29227 c = *++zFormat;
29228 }else{
29229 while( c>='0' && c<='9' ){
29230 precision = precision*10 + c - '0';
29231 c = *++zFormat;
29232 }
29233 }
29234 }
29235 /* Get the conversion type modifier */
29236 flag_long = 0;
29237 if( c=='l' || c == 'q' /* BSD quad (expect a 64-bit integer) */ ){
29238 flag_long = (c == 'q') ? 2 : 1;
29239 c = *++zFormat;
29240 if( c == 'l' ){
29241 /* Standard printf emulation 'lld' (expect a 64bit integer) */
29242 flag_long = 2;
29243 }
29244 }
29245 /* Fetch the info entry for the field */
29246 infop = 0;
29247 xtype = SXFMT_ERROR;
29248 for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
29249 if( c==aFmt[idx].fmttype ){
29250 infop = &aFmt[idx];
29251 xtype = infop->type;
29252 break;
29253 }
29254 }
29255 zExtra = 0;
29256
29257 /*
29258 ** At this point, variables are initialized as follows:
29259 **
29260 ** flag_alternateform TRUE if a '#' is present.
29261 ** flag_plussign TRUE if a '+' is present.
29262 ** flag_leftjustify TRUE if a '-' is present or if the
29263 ** field width was negative.
29264 ** flag_zeropad TRUE if the width began with 0.
29265 ** flag_long TRUE if the letter 'l' (ell) or 'q'(BSD quad) prefixed
29266 ** the conversion character.
29267 ** flag_blanksign TRUE if a ' ' is present.
29268 ** width The specified field width.This is
29269 ** always non-negative.Zero is the default.
29270 ** precision The specified precision.The default
29271 ** is -1.
29272 ** xtype The object of the conversion.
29273 ** infop Pointer to the appropriate info struct.
29274 */
29275 switch( xtype ){
29276 case SXFMT_RADIX:
29277 if( flag_long > 0 ){
29278 if( flag_long > 1 ){
29279 /* BSD quad: expect a 64-bit integer */
29280 longvalue = va_arg(ap, sxi64);
29281 }else{
29282 longvalue = va_arg(ap, sxlong);
29283 }
29284 }else{
29285 if( infop->flags & SXFLAG_SIGNED ){
29286 longvalue = va_arg(ap, sxi32);
29287 }else{
29288 longvalue = va_arg(ap, sxu32);
29289 }
29290 }
29291 /* Limit the precision to prevent overflowing buf[] during conversion */
29292 if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40;
29293#if 1
29294 /* For the format %#x, the value zero is printed "0" not "0x0".
29295 ** I think this is stupid.*/
29296 if( longvalue==0 ) flag_alternateform = 0;
29297#else
29298 /* More sensible: turn off the prefix for octal (to prevent "00"),
29299 ** but leave the prefix for hex.*/
29300 if( longvalue==0 && infop->base==8 ) flag_alternateform = 0;
29301#endif
29302 if( infop->flags & SXFLAG_SIGNED ){
29303 if( longvalue<0 ){
29304 longvalue = -longvalue;
29305 /* Ticket 1433-003 */
29306 if( longvalue < 0 ){
29307 /* Overflow */
29308 longvalue= 0x7FFFFFFFFFFFFFFF;
29309 }
29310 prefix = '-';
29311 }else if( flag_plussign ) prefix = '+';
29312 else if( flag_blanksign ) prefix = ' ';
29313 else prefix = 0;
29314 }else{
29315 if( longvalue<0 ){
29316 longvalue = -longvalue;
29317 /* Ticket 1433-003 */
29318 if( longvalue < 0 ){
29319 /* Overflow */
29320 longvalue= 0x7FFFFFFFFFFFFFFF;
29321 }
29322 }
29323 prefix = 0;
29324 }
29325 if( flag_zeropad && precision<width-(prefix!=0) ){
29326 precision = width-(prefix!=0);
29327 }
29328 bufpt = &buf[SXFMT_BUFSIZ-1];
29329 {
29330 register char *cset; /* Use registers for speed */
29331 register int base;
29332 cset = infop->charset;
29333 base = infop->base;
29334 do{ /* Convert to ascii */
29335 *(--bufpt) = cset[longvalue%base];
29336 longvalue = longvalue/base;
29337 }while( longvalue>0 );
29338 }
29339 length = &buf[SXFMT_BUFSIZ-1]-bufpt;
29340 for(idx=precision-length; idx>0; idx--){
29341 *(--bufpt) = '0'; /* Zero pad */
29342 }
29343 if( prefix ) *(--bufpt) = prefix; /* Add sign */
29344 if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */
29345 char *pre, x;
29346 pre = infop->prefix;
29347 if( *bufpt!=pre[0] ){
29348 for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x;
29349 }
29350 }
29351 length = &buf[SXFMT_BUFSIZ-1]-bufpt;
29352 break;
29353 case SXFMT_FLOAT:
29354 case SXFMT_EXP:
29355 case SXFMT_GENERIC:
29356#ifndef SX_OMIT_FLOATINGPOINT
29357 realvalue = va_arg(ap, double);
29358 if( precision<0 ) precision = 6; /* Set default precision */
29359 if( precision>SXFMT_BUFSIZ-40) precision = SXFMT_BUFSIZ-40;
29360 if( realvalue<0.0 ){
29361 realvalue = -realvalue;
29362 prefix = '-';
29363 }else{
29364 if( flag_plussign ) prefix = '+';
29365 else if( flag_blanksign ) prefix = ' ';
29366 else prefix = 0;
29367 }
29368 if( infop->type==SXFMT_GENERIC && precision>0 ) precision--;
29369 rounder = 0.0;
29370#if 0
29371 /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
29372 for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
29373#else
29374 /* It makes more sense to use 0.5 */
29375 for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
29376#endif
29377 if( infop->type==SXFMT_FLOAT ) realvalue += rounder;
29378 /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
29379 exp = 0;
29380 if( realvalue>0.0 ){
29381 while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
29382 while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
29383 while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
29384 while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
29385 if( exp>350 || exp<-350 ){
29386 bufpt = "NaN";
29387 length = 3;
29388 break;
29389 }
29390 }
29391 bufpt = buf;
29392 /*
29393 ** If the field type is etGENERIC, then convert to either etEXP
29394 ** or etFLOAT, as appropriate.
29395 */
29396 flag_exp = xtype==SXFMT_EXP;
29397 if( xtype!=SXFMT_FLOAT ){
29398 realvalue += rounder;
29399 if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
29400 }
29401 if( xtype==SXFMT_GENERIC ){
29402 flag_rtz = !flag_alternateform;
29403 if( exp<-4 || exp>precision ){
29404 xtype = SXFMT_EXP;
29405 }else{
29406 precision = precision - exp;
29407 xtype = SXFMT_FLOAT;
29408 }
29409 }else{
29410 flag_rtz = 0;
29411 }
29412 /*
29413 ** The "exp+precision" test causes output to be of type etEXP if
29414 ** the precision is too large to fit in buf[].
29415 */
29416 nsd = 0;
29417 if( xtype==SXFMT_FLOAT && exp+precision<SXFMT_BUFSIZ-30 ){
29418 flag_dp = (precision>0 || flag_alternateform);
29419 if( prefix ) *(bufpt++) = prefix; /* Sign */
29420 if( exp<0 ) *(bufpt++) = '0'; /* Digits before "." */
29421 else for(; exp>=0; exp--) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
29422 if( flag_dp ) *(bufpt++) = '.'; /* The decimal point */
29423 for(exp++; exp<0 && precision>0; precision--, exp++){
29424 *(bufpt++) = '0';
29425 }
29426 while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
29427 *(bufpt--) = 0; /* Null terminate */
29428 if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */
29429 while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
29430 if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
29431 }
29432 bufpt++; /* point to next free slot */
29433 }else{ /* etEXP or etGENERIC */
29434 flag_dp = (precision>0 || flag_alternateform);
29435 if( prefix ) *(bufpt++) = prefix; /* Sign */
29436 *(bufpt++) = (char)getdigit(&realvalue, &nsd); /* First digit */
29437 if( flag_dp ) *(bufpt++) = '.'; /* Decimal point */
29438 while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
29439 bufpt--; /* point to last digit */
29440 if( flag_rtz && flag_dp ){ /* Remove tail zeros */
29441 while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
29442 if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
29443 }
29444 bufpt++; /* point to next free slot */
29445 if( exp || flag_exp ){
29446 *(bufpt++) = infop->charset[0];
29447 if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */
29448 else { *(bufpt++) = '+'; }
29449 if( exp>=100 ){
29450 *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */
29451 exp %= 100;
29452 }
29453 *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */
29454 *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */
29455 }
29456 }
29457 /* The converted number is in buf[] and zero terminated.Output it.
29458 ** Note that the number is in the usual order, not reversed as with
29459 ** integer conversions.*/
29460 length = bufpt-buf;
29461 bufpt = buf;
29462
29463 /* Special case: Add leading zeros if the flag_zeropad flag is
29464 ** set and we are not left justified */
29465 if( flag_zeropad && !flag_leftjustify && length < width){
29466 int i;
29467 int nPad = width - length;
29468 for(i=width; i>=nPad; i--){
29469 bufpt[i] = bufpt[i-nPad];
29470 }
29471 i = prefix!=0;
29472 while( nPad-- ) bufpt[i++] = '0';
29473 length = width;
29474 }
29475#else
29476 bufpt = " ";
29477 length = (int)sizeof(" ") - 1;
29478#endif /* SX_OMIT_FLOATINGPOINT */
29479 break;
29480 case SXFMT_SIZE:{
29481 int *pSize = va_arg(ap, int *);
29482 *pSize = ((SyFmtConsumer *)pUserData)->nLen;
29483 length = width = 0;
29484 }
29485 break;
29486 case SXFMT_PERCENT:
29487 buf[0] = '%';
29488 bufpt = buf;
29489 length = 1;
29490 break;
29491 case SXFMT_CHARX:
29492 c = va_arg(ap, int);
29493 buf[0] = (char)c;
29494 /* Limit the precision to prevent overflowing buf[] during conversion */
29495 if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40;
29496 if( precision>=0 ){
29497 for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
29498 length = precision;
29499 }else{
29500 length =1;
29501 }
29502 bufpt = buf;
29503 break;
29504 case SXFMT_STRING:
29505 bufpt = va_arg(ap, char*);
29506 if( bufpt==0 ){
29507 bufpt = " ";
29508 length = (int)sizeof(" ")-1;
29509 break;
29510 }
29511 length = precision;
29512 if( precision < 0 ){
29513 /* Symisc extension */
29514 length = (int)SyStrlen(bufpt);
29515 }
29516 if( precision>=0 && precision<length ) length = precision;
29517 break;
29518 case SXFMT_RAWSTR:{
29519 /* Symisc extension */
29520 SyString *pStr = va_arg(ap, SyString *);
29521 if( pStr == 0 || pStr->zString == 0 ){
29522 bufpt = " ";
29523 length = (int)sizeof(char);
29524 break;
29525 }
29526 bufpt = (char *)pStr->zString;
29527 length = (int)pStr->nByte;
29528 break;
29529 }
29530 case SXFMT_ERROR:
29531 buf[0] = '?';
29532 bufpt = buf;
29533 length = (int)sizeof(char);
29534 if( c==0 ) zFormat--;
29535 break;
29536 }/* End switch over the format type */
29537 /*
29538 ** The text of the conversion is pointed to by "bufpt" and is
29539 ** "length" characters long.The field width is "width".Do
29540 ** the output.
29541 */
29542 if( !flag_leftjustify ){
29543 register int nspace;
29544 nspace = width-length;
29545 if( nspace>0 ){
29546 while( nspace>=etSPACESIZE ){
29547 rc = xConsumer(spaces, etSPACESIZE, pUserData);
29548 if( rc != SXRET_OK ){
29549 return SXERR_ABORT; /* Consumer routine request an operation abort */
29550 }
29551 nspace -= etSPACESIZE;
29552 }
29553 if( nspace>0 ){
29554 rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
29555 if( rc != SXRET_OK ){
29556 return SXERR_ABORT; /* Consumer routine request an operation abort */
29557 }
29558 }
29559 }
29560 }
29561 if( length>0 ){
29562 rc = xConsumer(bufpt, (unsigned int)length, pUserData);
29563 if( rc != SXRET_OK ){
29564 return SXERR_ABORT; /* Consumer routine request an operation abort */
29565 }
29566 }
29567 if( flag_leftjustify ){
29568 register int nspace;
29569 nspace = width-length;
29570 if( nspace>0 ){
29571 while( nspace>=etSPACESIZE ){
29572 rc = xConsumer(spaces, etSPACESIZE, pUserData);
29573 if( rc != SXRET_OK ){
29574 return SXERR_ABORT; /* Consumer routine request an operation abort */
29575 }
29576 nspace -= etSPACESIZE;
29577 }
29578 if( nspace>0 ){
29579 rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
29580 if( rc != SXRET_OK ){
29581 return SXERR_ABORT; /* Consumer routine request an operation abort */
29582 }
29583 }
29584 }
29585 }
29586 }/* End for loop over the format string */
29587 return errorflag ? SXERR_FORMAT : SXRET_OK;
29588}
29589static sxi32 FormatConsumer(const void *pSrc, unsigned int nLen, void *pData)
29590{
29591 SyFmtConsumer *pConsumer = (SyFmtConsumer *)pData;
29592 sxi32 rc = SXERR_ABORT;
29593 switch(pConsumer->nType){
29594 case SXFMT_CONS_PROC:
29595 /* User callback */
29596 rc = pConsumer->uConsumer.sFunc.xUserConsumer(pSrc, nLen, pConsumer->uConsumer.sFunc.pUserData);
29597 break;
29598 case SXFMT_CONS_BLOB:
29599 /* Blob consumer */
29600 rc = SyBlobAppend(pConsumer->uConsumer.pBlob, pSrc, (sxu32)nLen);
29601 break;
29602 default:
29603 /* Unknown consumer */
29604 break;
29605 }
29606 /* Update total number of bytes consumed so far */
29607 pConsumer->nLen += nLen;
29608 pConsumer->rc = rc;
29609 return rc;
29610}
29611static sxi32 FormatMount(sxi32 nType, void *pConsumer, ProcConsumer xUserCons, void *pUserData, sxu32 *pOutLen, const char *zFormat, va_list ap)
29612{
29613 SyFmtConsumer sCons;
29614 sCons.nType = nType;
29615 sCons.rc = SXRET_OK;
29616 sCons.nLen = 0;
29617 if( pOutLen ){
29618 *pOutLen = 0;
29619 }
29620 switch(nType){
29621 case SXFMT_CONS_PROC:
29622#if defined(UNTRUST)
29623 if( xUserCons == 0 ){
29624 return SXERR_EMPTY;
29625 }
29626#endif
29627 sCons.uConsumer.sFunc.xUserConsumer = xUserCons;
29628 sCons.uConsumer.sFunc.pUserData = pUserData;
29629 break;
29630 case SXFMT_CONS_BLOB:
29631 sCons.uConsumer.pBlob = (SyBlob *)pConsumer;
29632 break;
29633 default:
29634 return SXERR_UNKNOWN;
29635 }
29636 InternFormat(FormatConsumer, &sCons, zFormat, ap);
29637 if( pOutLen ){
29638 *pOutLen = sCons.nLen;
29639 }
29640 return sCons.rc;
29641}
29642JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...)
29643{
29644 va_list ap;
29645 sxi32 rc;
29646#if defined(UNTRUST)
29647 if( SX_EMPTY_STR(zFormat) ){
29648 return SXERR_EMPTY;
29649 }
29650#endif
29651 va_start(ap, zFormat);
29652 rc = FormatMount(SXFMT_CONS_PROC, 0, xConsumer, pData, 0, zFormat, ap);
29653 va_end(ap);
29654 return rc;
29655}
29656JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...)
29657{
29658 va_list ap;
29659 sxu32 n;
29660#if defined(UNTRUST)
29661 if( SX_EMPTY_STR(zFormat) ){
29662 return 0;
29663 }
29664#endif
29665 va_start(ap, zFormat);
29666 FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
29667 va_end(ap);
29668 return n;
29669}
29670JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap)
29671{
29672 sxu32 n = 0; /* cc warning */
29673#if defined(UNTRUST)
29674 if( SX_EMPTY_STR(zFormat) ){
29675 return 0;
29676 }
29677#endif
29678 FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
29679 return n;
29680}
29681JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...)
29682{
29683 SyBlob sBlob;
29684 va_list ap;
29685 sxu32 n;
29686#if defined(UNTRUST)
29687 if( SX_EMPTY_STR(zFormat) ){
29688 return 0;
29689 }
29690#endif
29691 if( SXRET_OK != SyBlobInitFromBuf(&sBlob, zBuf, nLen - 1) ){
29692 return 0;
29693 }
29694 va_start(ap, zFormat);
29695 FormatMount(SXFMT_CONS_BLOB, &sBlob, 0, 0, 0, zFormat, ap);
29696 va_end(ap);
29697 n = SyBlobLength(&sBlob);
29698 /* Append the null terminator */
29699 sBlob.mByte++;
29700 SyBlobAppend(&sBlob, "\0", sizeof(char));
29701 return n;
29702}
29703#ifndef JX9_DISABLE_BUILTIN_FUNC
29704/*
29705 * Zip File Format:
29706 *
29707 * Byte order: Little-endian
29708 *
29709 * [Local file header + Compressed data [+ Extended local header]?]*
29710 * [Central directory]*
29711 * [End of central directory record]
29712 *
29713 * Local file header:*
29714 * Offset Length Contents
29715 * 0 4 bytes Local file header signature (0x04034b50)
29716 * 4 2 bytes Version needed to extract
29717 * 6 2 bytes General purpose bit flag
29718 * 8 2 bytes Compression method
29719 * 10 2 bytes Last mod file time
29720 * 12 2 bytes Last mod file date
29721 * 14 4 bytes CRC-32
29722 * 18 4 bytes Compressed size (n)
29723 * 22 4 bytes Uncompressed size
29724 * 26 2 bytes Filename length (f)
29725 * 28 2 bytes Extra field length (e)
29726 * 30 (f)bytes Filename
29727 * (e)bytes Extra field
29728 * (n)bytes Compressed data
29729 *
29730 * Extended local header:*
29731 * Offset Length Contents
29732 * 0 4 bytes Extended Local file header signature (0x08074b50)
29733 * 4 4 bytes CRC-32
29734 * 8 4 bytes Compressed size
29735 * 12 4 bytes Uncompressed size
29736 *
29737 * Extra field:?(if any)
29738 * Offset Length Contents
29739 * 0 2 bytes Header ID (0x001 until 0xfb4a) see extended appnote from Info-zip
29740 * 2 2 bytes Data size (g)
29741 * (g) bytes (g) bytes of extra field
29742 *
29743 * Central directory:*
29744 * Offset Length Contents
29745 * 0 4 bytes Central file header signature (0x02014b50)
29746 * 4 2 bytes Version made by
29747 * 6 2 bytes Version needed to extract
29748 * 8 2 bytes General purpose bit flag
29749 * 10 2 bytes Compression method
29750 * 12 2 bytes Last mod file time
29751 * 14 2 bytes Last mod file date
29752 * 16 4 bytes CRC-32
29753 * 20 4 bytes Compressed size
29754 * 24 4 bytes Uncompressed size
29755 * 28 2 bytes Filename length (f)
29756 * 30 2 bytes Extra field length (e)
29757 * 32 2 bytes File comment length (c)
29758 * 34 2 bytes Disk number start
29759 * 36 2 bytes Internal file attributes
29760 * 38 4 bytes External file attributes
29761 * 42 4 bytes Relative offset of local header
29762 * 46 (f)bytes Filename
29763 * (e)bytes Extra field
29764 * (c)bytes File comment
29765 *
29766 * End of central directory record:
29767 * Offset Length Contents
29768 * 0 4 bytes End of central dir signature (0x06054b50)
29769 * 4 2 bytes Number of this disk
29770 * 6 2 bytes Number of the disk with the start of the central directory
29771 * 8 2 bytes Total number of entries in the central dir on this disk
29772 * 10 2 bytes Total number of entries in the central dir
29773 * 12 4 bytes Size of the central directory
29774 * 16 4 bytes Offset of start of central directory with respect to the starting disk number
29775 * 20 2 bytes zipfile comment length (c)
29776 * 22 (c)bytes zipfile comment
29777 *
29778 * compression method: (2 bytes)
29779 * 0 - The file is stored (no compression)
29780 * 1 - The file is Shrunk
29781 * 2 - The file is Reduced with compression factor 1
29782 * 3 - The file is Reduced with compression factor 2
29783 * 4 - The file is Reduced with compression factor 3
29784 * 5 - The file is Reduced with compression factor 4
29785 * 6 - The file is Imploded
29786 * 7 - Reserved for Tokenizing compression algorithm
29787 * 8 - The file is Deflated
29788 */
29789
29790#define SXMAKE_ZIP_WORKBUF (SXU16_HIGH/2) /* 32KB Initial working buffer size */
29791#define SXMAKE_ZIP_EXTRACT_VER 0x000a /* Version needed to extract */
29792#define SXMAKE_ZIP_VER 0x003 /* Version made by */
29793
29794#define SXZIP_CENTRAL_MAGIC 0x02014b50
29795#define SXZIP_END_CENTRAL_MAGIC 0x06054b50
29796#define SXZIP_LOCAL_MAGIC 0x04034b50
29797/*#define SXZIP_CRC32_START 0xdebb20e3*/
29798
29799#define SXZIP_LOCAL_HDRSZ 30 /* Local header size */
29800#define SXZIP_LOCAL_EXT_HDRZ 16 /* Extended local header(footer) size */
29801#define SXZIP_CENTRAL_HDRSZ 46 /* Central directory header size */
29802#define SXZIP_END_CENTRAL_HDRSZ 22 /* End of central directory header size */
29803
29804#define SXARCHIVE_HASH_SIZE 64 /* Starting hash table size(MUST BE POWER OF 2)*/
29805static sxi32 SyLittleEndianUnpack32(sxu32 *uNB, const unsigned char *buf, sxu32 Len)
29806{
29807 if( Len < sizeof(sxu32) ){
29808 return SXERR_SHORT;
29809 }
29810 *uNB = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
29811 return SXRET_OK;
29812}
29813static sxi32 SyLittleEndianUnpack16(sxu16 *pOut, const unsigned char *zBuf, sxu32 nLen)
29814{
29815 if( nLen < sizeof(sxu16) ){
29816 return SXERR_SHORT;
29817 }
29818 *pOut = zBuf[0] + (zBuf[1] <<8);
29819
29820 return SXRET_OK;
29821}
29822/*
29823 * Archive hashtable manager
29824 */
29825static sxi32 ArchiveHashGetEntry(SyArchive *pArch, const char *zName, sxu32 nLen, SyArchiveEntry **ppEntry)
29826{
29827 SyArchiveEntry *pBucketEntry;
29828 SyString sEntry;
29829 sxu32 nHash;
29830
29831 nHash = pArch->xHash(zName, nLen);
29832 pBucketEntry = pArch->apHash[nHash & (pArch->nSize - 1)];
29833
29834 SyStringInitFromBuf(&sEntry, zName, nLen);
29835
29836 for(;;){
29837 if( pBucketEntry == 0 ){
29838 break;
29839 }
29840 if( nHash == pBucketEntry->nHash && pArch->xCmp(&sEntry, &pBucketEntry->sFileName) == 0 ){
29841 if( ppEntry ){
29842 *ppEntry = pBucketEntry;
29843 }
29844 return SXRET_OK;
29845 }
29846 pBucketEntry = pBucketEntry->pNextHash;
29847 }
29848 return SXERR_NOTFOUND;
29849}
29850static void ArchiveHashBucketInstall(SyArchiveEntry **apTable, sxu32 nBucket, SyArchiveEntry *pEntry)
29851{
29852 pEntry->pNextHash = apTable[nBucket];
29853 if( apTable[nBucket] != 0 ){
29854 apTable[nBucket]->pPrevHash = pEntry;
29855 }
29856 apTable[nBucket] = pEntry;
29857}
29858static sxi32 ArchiveHashGrowTable(SyArchive *pArch)
29859{
29860 sxu32 nNewSize = pArch->nSize * 2;
29861 SyArchiveEntry **apNew;
29862 SyArchiveEntry *pEntry;
29863 sxu32 n;
29864
29865 /* Allocate a new table */
29866 apNew = (SyArchiveEntry **)SyMemBackendAlloc(pArch->pAllocator, nNewSize * sizeof(SyArchiveEntry *));
29867 if( apNew == 0 ){
29868 return SXRET_OK; /* Not so fatal, simply a performance hit */
29869 }
29870 SyZero(apNew, nNewSize * sizeof(SyArchiveEntry *));
29871 /* Rehash old entries */
29872 for( n = 0 , pEntry = pArch->pList ; n < pArch->nLoaded ; n++ , pEntry = pEntry->pNext ){
29873 pEntry->pNextHash = pEntry->pPrevHash = 0;
29874 ArchiveHashBucketInstall(apNew, pEntry->nHash & (nNewSize - 1), pEntry);
29875 }
29876 /* Release the old table */
29877 SyMemBackendFree(pArch->pAllocator, pArch->apHash);
29878 pArch->apHash = apNew;
29879 pArch->nSize = nNewSize;
29880
29881 return SXRET_OK;
29882}
29883static sxi32 ArchiveHashInstallEntry(SyArchive *pArch, SyArchiveEntry *pEntry)
29884{
29885 if( pArch->nLoaded > pArch->nSize * 3 ){
29886 ArchiveHashGrowTable(&(*pArch));
29887 }
29888 pEntry->nHash = pArch->xHash(SyStringData(&pEntry->sFileName), SyStringLength(&pEntry->sFileName));
29889 /* Install the entry in its bucket */
29890 ArchiveHashBucketInstall(pArch->apHash, pEntry->nHash & (pArch->nSize - 1), pEntry);
29891 MACRO_LD_PUSH(pArch->pList, pEntry);
29892 pArch->nLoaded++;
29893
29894 return SXRET_OK;
29895}
29896 /*
29897 * Parse the End of central directory and report status
29898 */
29899 static sxi32 ParseEndOfCentralDirectory(SyArchive *pArch, const unsigned char *zBuf)
29900 {
29901 sxu32 nMagic = 0; /* cc -O6 warning */
29902 sxi32 rc;
29903
29904 /* Sanity check */
29905 rc = SyLittleEndianUnpack32(&nMagic, zBuf, sizeof(sxu32));
29906 if( /* rc != SXRET_OK || */nMagic != SXZIP_END_CENTRAL_MAGIC ){
29907 return SXERR_CORRUPT;
29908 }
29909 /* # of entries */
29910 rc = SyLittleEndianUnpack16((sxu16 *)&pArch->nEntry, &zBuf[8], sizeof(sxu16));
29911 if( /* rc != SXRET_OK || */ pArch->nEntry > SXI16_HIGH /* SXU16_HIGH */ ){
29912 return SXERR_CORRUPT;
29913 }
29914 /* Size of central directory */
29915 rc = SyLittleEndianUnpack32(&pArch->nCentralSize, &zBuf[12], sizeof(sxu32));
29916 if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){
29917 return SXERR_CORRUPT;
29918 }
29919 /* Starting offset of central directory */
29920 rc = SyLittleEndianUnpack32(&pArch->nCentralOfft, &zBuf[16], sizeof(sxu32));
29921 if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){
29922 return SXERR_CORRUPT;
29923 }
29924
29925 return SXRET_OK;
29926 }
29927 /*
29928 * Fill the zip entry with the appropriate information from the central directory
29929 */
29930static sxi32 GetCentralDirectoryEntry(SyArchive *pArch, SyArchiveEntry *pEntry, const unsigned char *zCentral, sxu32 *pNextOffset)
29931 {
29932 SyString *pName = &pEntry->sFileName; /* File name */
29933 sxu16 nDosDate, nDosTime;
29934 sxu16 nComment = 0 ;
29935 sxu32 nMagic = 0; /* cc -O6 warning */
29936 sxi32 rc;
29937 nDosDate = nDosTime = 0; /* cc -O6 warning */
29938 SXUNUSED(pArch);
29939 /* Sanity check */
29940 rc = SyLittleEndianUnpack32(&nMagic, zCentral, sizeof(sxu32));
29941 if( /* rc != SXRET_OK || */ nMagic != SXZIP_CENTRAL_MAGIC ){
29942 rc = SXERR_CORRUPT;
29943 /*
29944 * Try to recover by examing the next central directory record.
29945 * Dont worry here, there is no risk of an infinite loop since
29946 * the buffer size is delimited.
29947 */
29948
29949 /* pName->nByte = 0; nComment = 0; pName->nExtra = 0 */
29950 goto update;
29951 }
29952 /*
29953 * entry name length
29954 */
29955 SyLittleEndianUnpack16((sxu16 *)&pName->nByte, &zCentral[28], sizeof(sxu16));
29956 if( pName->nByte > SXI16_HIGH /* SXU16_HIGH */){
29957 rc = SXERR_BIG;
29958 goto update;
29959 }
29960 /* Extra information */
29961 SyLittleEndianUnpack16(&pEntry->nExtra, &zCentral[30], sizeof(sxu16));
29962 /* Comment length */
29963 SyLittleEndianUnpack16(&nComment, &zCentral[32], sizeof(sxu16));
29964 /* Compression method 0 == stored / 8 == deflated */
29965 rc = SyLittleEndianUnpack16(&pEntry->nComprMeth, &zCentral[10], sizeof(sxu16));
29966 /* DOS Timestamp */
29967 SyLittleEndianUnpack16(&nDosTime, &zCentral[12], sizeof(sxu16));
29968 SyLittleEndianUnpack16(&nDosDate, &zCentral[14], sizeof(sxu16));
29969 SyDosTimeFormat((nDosDate << 16 | nDosTime), &pEntry->sFmt);
29970 /* Little hack to fix month index */
29971 pEntry->sFmt.tm_mon--;
29972 /* CRC32 */
29973 rc = SyLittleEndianUnpack32(&pEntry->nCrc, &zCentral[16], sizeof(sxu32));
29974 /* Content size before compression */
29975 rc = SyLittleEndianUnpack32(&pEntry->nByte, &zCentral[24], sizeof(sxu32));
29976 if( pEntry->nByte > SXI32_HIGH ){
29977 rc = SXERR_BIG;
29978 goto update;
29979 }
29980 /*
29981 * Content size after compression.
29982 * Note that if the file is stored pEntry->nByte should be equal to pEntry->nByteCompr
29983 */
29984 rc = SyLittleEndianUnpack32(&pEntry->nByteCompr, &zCentral[20], sizeof(sxu32));
29985 if( pEntry->nByteCompr > SXI32_HIGH ){
29986 rc = SXERR_BIG;
29987 goto update;
29988 }
29989 /* Finally grab the contents offset */
29990 SyLittleEndianUnpack32(&pEntry->nOfft, &zCentral[42], sizeof(sxu32));
29991 if( pEntry->nOfft > SXI32_HIGH ){
29992 rc = SXERR_BIG;
29993 goto update;
29994 }
29995 rc = SXRET_OK;
29996update:
29997 /* Update the offset to point to the next central directory record */
29998 *pNextOffset = SXZIP_CENTRAL_HDRSZ + pName->nByte + pEntry->nExtra + nComment;
29999 return rc; /* Report failure or success */
30000}
30001static sxi32 ZipFixOffset(SyArchiveEntry *pEntry, void *pSrc)
30002{
30003 sxu16 nExtra, nNameLen;
30004 unsigned char *zHdr;
30005 nExtra = nNameLen = 0;
30006 zHdr = (unsigned char *)pSrc;
30007 zHdr = &zHdr[pEntry->nOfft];
30008 if( SyMemcmp(zHdr, "PK\003\004", sizeof(sxu32)) != 0 ){
30009 return SXERR_CORRUPT;
30010 }
30011 SyLittleEndianUnpack16(&nNameLen, &zHdr[26], sizeof(sxu16));
30012 SyLittleEndianUnpack16(&nExtra, &zHdr[28], sizeof(sxu16));
30013 /* Fix contents offset */
30014 pEntry->nOfft += SXZIP_LOCAL_HDRSZ + nExtra + nNameLen;
30015 return SXRET_OK;
30016}
30017/*
30018 * Extract all valid entries from the central directory
30019 */
30020static sxi32 ZipExtract(SyArchive *pArch, const unsigned char *zCentral, sxu32 nLen, void *pSrc)
30021{
30022 SyArchiveEntry *pEntry, *pDup;
30023 const unsigned char *zEnd ; /* End of central directory */
30024 sxu32 nIncr, nOfft; /* Central Offset */
30025 SyString *pName; /* Entry name */
30026 char *zName;
30027 sxi32 rc;
30028
30029 nOfft = nIncr = 0;
30030 zEnd = &zCentral[nLen];
30031
30032 for(;;){
30033 if( &zCentral[nOfft] >= zEnd ){
30034 break;
30035 }
30036 /* Add a new entry */
30037 pEntry = (SyArchiveEntry *)SyMemBackendPoolAlloc(pArch->pAllocator, sizeof(SyArchiveEntry));
30038 if( pEntry == 0 ){
30039 break;
30040 }
30041 SyZero(pEntry, sizeof(SyArchiveEntry));
30042 pEntry->nMagic = SXARCH_MAGIC;
30043 nIncr = 0;
30044 rc = GetCentralDirectoryEntry(&(*pArch), pEntry, &zCentral[nOfft], &nIncr);
30045 if( rc == SXRET_OK ){
30046 /* Fix the starting record offset so we can access entry contents correctly */
30047 rc = ZipFixOffset(pEntry, pSrc);
30048 }
30049 if(rc != SXRET_OK ){
30050 sxu32 nJmp = 0;
30051 SyMemBackendPoolFree(pArch->pAllocator, pEntry);
30052 /* Try to recover by brute-forcing for a valid central directory record */
30053 if( SXRET_OK == SyBlobSearch((const void *)&zCentral[nOfft + nIncr], (sxu32)(zEnd - &zCentral[nOfft + nIncr]),
30054 (const void *)"PK\001\002", sizeof(sxu32), &nJmp)){
30055 nOfft += nIncr + nJmp; /* Check next entry */
30056 continue;
30057 }
30058 break; /* Giving up, archive is hopelessly corrupted */
30059 }
30060 pName = &pEntry->sFileName;
30061 pName->zString = (const char *)&zCentral[nOfft + SXZIP_CENTRAL_HDRSZ];
30062 if( pName->nByte <= 0 || ( pEntry->nByte <= 0 && pName->zString[pName->nByte - 1] != '/') ){
30063 /* Ignore zero length records (except folders) and records without names */
30064 SyMemBackendPoolFree(pArch->pAllocator, pEntry);
30065 nOfft += nIncr; /* Check next entry */
30066 continue;
30067 }
30068 zName = SyMemBackendStrDup(pArch->pAllocator, pName->zString, pName->nByte);
30069 if( zName == 0 ){
30070 SyMemBackendPoolFree(pArch->pAllocator, pEntry);
30071 nOfft += nIncr; /* Check next entry */
30072 continue;
30073 }
30074 pName->zString = (const char *)zName;
30075 /* Check for duplicates */
30076 rc = ArchiveHashGetEntry(&(*pArch), pName->zString, pName->nByte, &pDup);
30077 if( rc == SXRET_OK ){
30078 /* Another entry with the same name exists ; link them together */
30079 pEntry->pNextName = pDup->pNextName;
30080 pDup->pNextName = pEntry;
30081 pDup->nDup++;
30082 }else{
30083 /* Insert in hashtable */
30084 ArchiveHashInstallEntry(pArch, pEntry);
30085 }
30086 nOfft += nIncr; /* Check next record */
30087 }
30088 pArch->pCursor = pArch->pList;
30089
30090 return pArch->nLoaded > 0 ? SXRET_OK : SXERR_EMPTY;
30091}
30092JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen)
30093 {
30094 const unsigned char *zCentral, *zEnd;
30095 sxi32 rc;
30096#if defined(UNTRUST)
30097 if( SXARCH_INVALID(pArch) || zBuf == 0 ){
30098 return SXERR_INVALID;
30099 }
30100#endif
30101 /* The miminal size of a zip archive:
30102 * LOCAL_HDR_SZ + CENTRAL_HDR_SZ + END_OF_CENTRAL_HDR_SZ
30103 * 30 46 22
30104 */
30105 if( nLen < SXZIP_LOCAL_HDRSZ + SXZIP_CENTRAL_HDRSZ + SXZIP_END_CENTRAL_HDRSZ ){
30106 return SXERR_CORRUPT; /* Don't bother processing return immediately */
30107 }
30108
30109 zEnd = (unsigned char *)&zBuf[nLen - SXZIP_END_CENTRAL_HDRSZ];
30110 /* Find the end of central directory */
30111 while( ((sxu32)((unsigned char *)&zBuf[nLen] - zEnd) < (SXZIP_END_CENTRAL_HDRSZ + SXI16_HIGH)) &&
30112 zEnd > (unsigned char *)zBuf && SyMemcmp(zEnd, "PK\005\006", sizeof(sxu32)) != 0 ){
30113 zEnd--;
30114 }
30115 /* Parse the end of central directory */
30116 rc = ParseEndOfCentralDirectory(&(*pArch), zEnd);
30117 if( rc != SXRET_OK ){
30118 return rc;
30119 }
30120
30121 /* Find the starting offset of the central directory */
30122 zCentral = &zEnd[-(sxi32)pArch->nCentralSize];
30123 if( zCentral <= (unsigned char *)zBuf || SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){
30124 if( pArch->nCentralOfft >= nLen ){
30125 /* Corrupted central directory offset */
30126 return SXERR_CORRUPT;
30127 }
30128 zCentral = (unsigned char *)&zBuf[pArch->nCentralOfft];
30129 if( SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){
30130 /* Corrupted zip archive */
30131 return SXERR_CORRUPT;
30132 }
30133 /* Fall thru and extract all valid entries from the central directory */
30134 }
30135 rc = ZipExtract(&(*pArch), zCentral, (sxu32)(zEnd - zCentral), (void *)zBuf);
30136 return rc;
30137 }
30138/*
30139 * Default comparison function.
30140 */
30141 static sxi32 ArchiveHashCmp(const SyString *pStr1, const SyString *pStr2)
30142 {
30143 sxi32 rc;
30144 rc = SyStringCmp(pStr1, pStr2, SyMemcmp);
30145 return rc;
30146 }
30147JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp)
30148 {
30149 SyArchiveEntry **apHash;
30150#if defined(UNTRUST)
30151 if( pArch == 0 ){
30152 return SXERR_EMPTY;
30153 }
30154#endif
30155 SyZero(pArch, sizeof(SyArchive));
30156 /* Allocate a new hashtable */
30157 apHash = (SyArchiveEntry **)SyMemBackendAlloc(&(*pAllocator), SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *));
30158 if( apHash == 0){
30159 return SXERR_MEM;
30160 }
30161 SyZero(apHash, SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *));
30162 pArch->apHash = apHash;
30163 pArch->xHash = xHash ? xHash : SyBinHash;
30164 pArch->xCmp = xCmp ? xCmp : ArchiveHashCmp;
30165 pArch->nSize = SXARCHIVE_HASH_SIZE;
30166 pArch->pAllocator = &(*pAllocator);
30167 pArch->nMagic = SXARCH_MAGIC;
30168 return SXRET_OK;
30169 }
30170 static sxi32 ArchiveReleaseEntry(SyMemBackend *pAllocator, SyArchiveEntry *pEntry)
30171 {
30172 SyArchiveEntry *pDup = pEntry->pNextName;
30173 SyArchiveEntry *pNextDup;
30174
30175 /* Release duplicates first since there are not stored in the hashtable */
30176 for(;;){
30177 if( pEntry->nDup == 0 ){
30178 break;
30179 }
30180 pNextDup = pDup->pNextName;
30181 pDup->nMagic = 0x2661;
30182 SyMemBackendFree(pAllocator, (void *)SyStringData(&pDup->sFileName));
30183 SyMemBackendPoolFree(pAllocator, pDup);
30184 pDup = pNextDup;
30185 pEntry->nDup--;
30186 }
30187 pEntry->nMagic = 0x2661;
30188 SyMemBackendFree(pAllocator, (void *)SyStringData(&pEntry->sFileName));
30189 SyMemBackendPoolFree(pAllocator, pEntry);
30190 return SXRET_OK;
30191 }
30192JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch)
30193 {
30194 SyArchiveEntry *pEntry, *pNext;
30195 pEntry = pArch->pList;
30196 for(;;){
30197 if( pArch->nLoaded < 1 ){
30198 break;
30199 }
30200 pNext = pEntry->pNext;
30201 MACRO_LD_REMOVE(pArch->pList, pEntry);
30202 ArchiveReleaseEntry(pArch->pAllocator, pEntry);
30203 pEntry = pNext;
30204 pArch->nLoaded--;
30205 }
30206 SyMemBackendFree(pArch->pAllocator, pArch->apHash);
30207 pArch->pCursor = 0;
30208 pArch->nMagic = 0x2626;
30209 return SXRET_OK;
30210 }
30211 JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch)
30212 {
30213 pArch->pCursor = pArch->pList;
30214 return SXRET_OK;
30215 }
30216 JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry)
30217 {
30218 SyArchiveEntry *pNext;
30219 if( pArch->pCursor == 0 ){
30220 /* Rewind the cursor */
30221 pArch->pCursor = pArch->pList;
30222 return SXERR_EOF;
30223 }
30224 *ppEntry = pArch->pCursor;
30225 pNext = pArch->pCursor->pNext;
30226 /* Advance the cursor to the next entry */
30227 pArch->pCursor = pNext;
30228 return SXRET_OK;
30229 }
30230#endif /* JX9_DISABLE_BUILTIN_FUNC */
30231/*
30232 * Psuedo Random Number Generator (PRNG)
30233 * @authors: SQLite authors <http://www.sqlite.org/>
30234 * @status: Public Domain
30235 * NOTE:
30236 * Nothing in this file or anywhere else in the library does any kind of
30237 * encryption.The RC4 algorithm is being used as a PRNG (pseudo-random
30238 * number generator) not as an encryption device.
30239 */
30240#define SXPRNG_MAGIC 0x13C4
30241#ifdef __UNIXES__
30242#include <sys/types.h>
30243#include <sys/stat.h>
30244#include <fcntl.h>
30245#include <unistd.h>
30246#include <errno.h>
30247#include <time.h>
30248#include <sys/time.h>
30249#endif
30250static sxi32 SyOSUtilRandomSeed(void *pBuf, sxu32 nLen, void *pUnused)
30251{
30252 char *zBuf = (char *)pBuf;
30253#ifdef __WINNT__
30254 DWORD nProcessID; /* Yes, keep it uninitialized when compiling using the MinGW32 builds tools */
30255#elif defined(__UNIXES__)
30256 pid_t pid;
30257 int fd;
30258#else
30259 char zGarbage[128]; /* Yes, keep this buffer uninitialized */
30260#endif
30261 SXUNUSED(pUnused);
30262#ifdef __WINNT__
30263#ifndef __MINGW32__
30264 nProcessID = GetProcessId(GetCurrentProcess());
30265#endif
30266 SyMemcpy((const void *)&nProcessID, zBuf, SXMIN(nLen, sizeof(DWORD)));
30267 if( (sxu32)(&zBuf[nLen] - &zBuf[sizeof(DWORD)]) >= sizeof(SYSTEMTIME) ){
30268 GetSystemTime((LPSYSTEMTIME)&zBuf[sizeof(DWORD)]);
30269 }
30270#elif defined(__UNIXES__)
30271 fd = open("/dev/urandom", O_RDONLY);
30272 if (fd >= 0 ){
30273 if( read(fd, zBuf, nLen) > 0 ){
30274 return SXRET_OK;
30275 }
30276 /* FALL THRU */
30277 }
30278 pid = getpid();
30279 SyMemcpy((const void *)&pid, zBuf, SXMIN(nLen, sizeof(pid_t)));
30280 if( &zBuf[nLen] - &zBuf[sizeof(pid_t)] >= (int)sizeof(struct timeval) ){
30281 gettimeofday((struct timeval *)&zBuf[sizeof(pid_t)], 0);
30282 }
30283#else
30284 /* Fill with uninitialized data */
30285 SyMemcpy(zGarbage, zBuf, SXMIN(nLen, sizeof(zGarbage)));
30286#endif
30287 return SXRET_OK;
30288}
30289JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void * pUserData)
30290{
30291 char zSeed[256];
30292 sxu8 t;
30293 sxi32 rc;
30294 sxu32 i;
30295 if( pCtx->nMagic == SXPRNG_MAGIC ){
30296 return SXRET_OK; /* Already initialized */
30297 }
30298 /* Initialize the state of the random number generator once,
30299 ** the first time this routine is called.The seed value does
30300 ** not need to contain a lot of randomness since we are not
30301 ** trying to do secure encryption or anything like that...
30302 */
30303 if( xSeed == 0 ){
30304 xSeed = SyOSUtilRandomSeed;
30305 }
30306 rc = xSeed(zSeed, sizeof(zSeed), pUserData);
30307 if( rc != SXRET_OK ){
30308 return rc;
30309 }
30310 pCtx->i = pCtx->j = 0;
30311 for(i=0; i < SX_ARRAYSIZE(pCtx->s) ; i++){
30312 pCtx->s[i] = (unsigned char)i;
30313 }
30314 for(i=0; i < sizeof(zSeed) ; i++){
30315 pCtx->j += pCtx->s[i] + zSeed[i];
30316 t = pCtx->s[pCtx->j];
30317 pCtx->s[pCtx->j] = pCtx->s[i];
30318 pCtx->s[i] = t;
30319 }
30320 pCtx->nMagic = SXPRNG_MAGIC;
30321
30322 return SXRET_OK;
30323}
30324/*
30325 * Get a single 8-bit random value using the RC4 PRNG.
30326 */
30327static sxu8 randomByte(SyPRNGCtx *pCtx)
30328{
30329 sxu8 t;
30330
30331 /* Generate and return single random byte */
30332 pCtx->i++;
30333 t = pCtx->s[pCtx->i];
30334 pCtx->j += t;
30335 pCtx->s[pCtx->i] = pCtx->s[pCtx->j];
30336 pCtx->s[pCtx->j] = t;
30337 t += pCtx->s[pCtx->i];
30338 return pCtx->s[t];
30339}
30340JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen)
30341{
30342 unsigned char *zBuf = (unsigned char *)pBuf;
30343 unsigned char *zEnd = &zBuf[nLen];
30344#if defined(UNTRUST)
30345 if( pCtx == 0 || pBuf == 0 || nLen <= 0 ){
30346 return SXERR_EMPTY;
30347 }
30348#endif
30349 if(pCtx->nMagic != SXPRNG_MAGIC ){
30350 return SXERR_CORRUPT;
30351 }
30352 for(;;){
30353 if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++;
30354 if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++;
30355 if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++;
30356 if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++;
30357 }
30358 return SXRET_OK;
30359}
30360#ifndef JX9_DISABLE_BUILTIN_FUNC
30361#ifndef JX9_DISABLE_HASH_FUNC
30362/* SyRunTimeApi: sxhash.c */
30363/*
30364 * This code implements the MD5 message-digest algorithm.
30365 * The algorithm is due to Ron Rivest.This code was
30366 * written by Colin Plumb in 1993, no copyright is claimed.
30367 * This code is in the public domain; do with it what you wish.
30368 *
30369 * Equivalent code is available from RSA Data Security, Inc.
30370 * This code has been tested against that, and is equivalent,
30371 * except that you don't need to include two pages of legalese
30372 * with every copy.
30373 *
30374 * To compute the message digest of a chunk of bytes, declare an
30375 * MD5Context structure, pass it to MD5Init, call MD5Update as
30376 * needed on buffers full of bytes, and then call MD5Final, which
30377 * will fill a supplied 16-byte array with the digest.
30378 */
30379#define SX_MD5_BINSZ 16
30380#define SX_MD5_HEXSZ 32
30381/*
30382 * Note: this code is harmless on little-endian machines.
30383 */
30384static void byteReverse (unsigned char *buf, unsigned longs)
30385{
30386 sxu32 t;
30387 do {
30388 t = (sxu32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
30389 ((unsigned)buf[1]<<8 | buf[0]);
30390 *(sxu32*)buf = t;
30391 buf += 4;
30392 } while (--longs);
30393}
30394/* The four core functions - F1 is optimized somewhat */
30395
30396/* #define F1(x, y, z) (x & y | ~x & z) */
30397#ifdef F1
30398#undef F1
30399#endif
30400#ifdef F2
30401#undef F2
30402#endif
30403#ifdef F3
30404#undef F3
30405#endif
30406#ifdef F4
30407#undef F4
30408#endif
30409
30410#define F1(x, y, z) (z ^ (x & (y ^ z)))
30411#define F2(x, y, z) F1(z, x, y)
30412#define F3(x, y, z) (x ^ y ^ z)
30413#define F4(x, y, z) (y ^ (x | ~z))
30414
30415/* This is the central step in the MD5 algorithm.*/
30416#define SX_MD5STEP(f, w, x, y, z, data, s) \
30417 ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
30418
30419/*
30420 * The core of the MD5 algorithm, this alters an existing MD5 hash to
30421 * reflect the addition of 16 longwords of new data.MD5Update blocks
30422 * the data and converts bytes into longwords for this routine.
30423 */
30424static void MD5Transform(sxu32 buf[4], const sxu32 in[16])
30425{
30426 register sxu32 a, b, c, d;
30427
30428 a = buf[0];
30429 b = buf[1];
30430 c = buf[2];
30431 d = buf[3];
30432
30433 SX_MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
30434 SX_MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
30435 SX_MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
30436 SX_MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
30437 SX_MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
30438 SX_MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
30439 SX_MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
30440 SX_MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
30441 SX_MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
30442 SX_MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
30443 SX_MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
30444 SX_MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
30445 SX_MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
30446 SX_MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
30447 SX_MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
30448 SX_MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
30449
30450 SX_MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
30451 SX_MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
30452 SX_MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
30453 SX_MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
30454 SX_MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
30455 SX_MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
30456 SX_MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
30457 SX_MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
30458 SX_MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
30459 SX_MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
30460 SX_MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
30461 SX_MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
30462 SX_MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
30463 SX_MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
30464 SX_MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
30465 SX_MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
30466
30467 SX_MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
30468 SX_MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
30469 SX_MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
30470 SX_MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
30471 SX_MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
30472 SX_MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
30473 SX_MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
30474 SX_MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
30475 SX_MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
30476 SX_MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
30477 SX_MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
30478 SX_MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
30479 SX_MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
30480 SX_MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
30481 SX_MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
30482 SX_MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
30483
30484 SX_MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
30485 SX_MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
30486 SX_MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
30487 SX_MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
30488 SX_MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
30489 SX_MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
30490 SX_MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
30491 SX_MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
30492 SX_MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
30493 SX_MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
30494 SX_MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
30495 SX_MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
30496 SX_MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
30497 SX_MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
30498 SX_MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
30499 SX_MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
30500
30501 buf[0] += a;
30502 buf[1] += b;
30503 buf[2] += c;
30504 buf[3] += d;
30505}
30506/*
30507 * Update context to reflect the concatenation of another buffer full
30508 * of bytes.
30509 */
30510JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len)
30511{
30512 sxu32 t;
30513
30514 /* Update bitcount */
30515 t = ctx->bits[0];
30516 if ((ctx->bits[0] = t + ((sxu32)len << 3)) < t)
30517 ctx->bits[1]++; /* Carry from low to high */
30518 ctx->bits[1] += len >> 29;
30519 t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
30520 /* Handle any leading odd-sized chunks */
30521 if ( t ) {
30522 unsigned char *p = (unsigned char *)ctx->in + t;
30523
30524 t = 64-t;
30525 if (len < t) {
30526 SyMemcpy(buf, p, len);
30527 return;
30528 }
30529 SyMemcpy(buf, p, t);
30530 byteReverse(ctx->in, 16);
30531 MD5Transform(ctx->buf, (sxu32*)ctx->in);
30532 buf += t;
30533 len -= t;
30534 }
30535 /* Process data in 64-byte chunks */
30536 while (len >= 64) {
30537 SyMemcpy(buf, ctx->in, 64);
30538 byteReverse(ctx->in, 16);
30539 MD5Transform(ctx->buf, (sxu32*)ctx->in);
30540 buf += 64;
30541 len -= 64;
30542 }
30543 /* Handle any remaining bytes of data.*/
30544 SyMemcpy(buf, ctx->in, len);
30545}
30546/*
30547 * Final wrapup - pad to 64-byte boundary with the bit pattern
30548 * 1 0* (64-bit count of bits processed, MSB-first)
30549 */
30550JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx){
30551 unsigned count;
30552 unsigned char *p;
30553
30554 /* Compute number of bytes mod 64 */
30555 count = (ctx->bits[0] >> 3) & 0x3F;
30556
30557 /* Set the first char of padding to 0x80.This is safe since there is
30558 always at least one byte free */
30559 p = ctx->in + count;
30560 *p++ = 0x80;
30561
30562 /* Bytes of padding needed to make 64 bytes */
30563 count = 64 - 1 - count;
30564
30565 /* Pad out to 56 mod 64 */
30566 if (count < 8) {
30567 /* Two lots of padding: Pad the first block to 64 bytes */
30568 SyZero(p, count);
30569 byteReverse(ctx->in, 16);
30570 MD5Transform(ctx->buf, (sxu32*)ctx->in);
30571
30572 /* Now fill the next block with 56 bytes */
30573 SyZero(ctx->in, 56);
30574 } else {
30575 /* Pad block to 56 bytes */
30576 SyZero(p, count-8);
30577 }
30578 byteReverse(ctx->in, 14);
30579
30580 /* Append length in bits and transform */
30581 ((sxu32*)ctx->in)[ 14 ] = ctx->bits[0];
30582 ((sxu32*)ctx->in)[ 15 ] = ctx->bits[1];
30583
30584 MD5Transform(ctx->buf, (sxu32*)ctx->in);
30585 byteReverse((unsigned char *)ctx->buf, 4);
30586 SyMemcpy(ctx->buf, digest, 0x10);
30587 SyZero(ctx, sizeof(ctx)); /* In case it's sensitive */
30588}
30589#undef F1
30590#undef F2
30591#undef F3
30592#undef F4
30593JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx)
30594{
30595 pCtx->buf[0] = 0x67452301;
30596 pCtx->buf[1] = 0xefcdab89;
30597 pCtx->buf[2] = 0x98badcfe;
30598 pCtx->buf[3] = 0x10325476;
30599 pCtx->bits[0] = 0;
30600 pCtx->bits[1] = 0;
30601
30602 return SXRET_OK;
30603}
30604JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16])
30605{
30606 MD5Context sCtx;
30607 MD5Init(&sCtx);
30608 MD5Update(&sCtx, (const unsigned char *)pIn, nLen);
30609 MD5Final(zDigest, &sCtx);
30610 return SXRET_OK;
30611}
30612/*
30613 * SHA-1 in C
30614 * By Steve Reid <steve@edmweb.com>
30615 * Status: Public Domain
30616 */
30617/*
30618 * blk0() and blk() perform the initial expand.
30619 * I got the idea of expanding during the round function from SSLeay
30620 *
30621 * blk0le() for little-endian and blk0be() for big-endian.
30622 */
30623#if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
30624/*
30625 * GCC by itself only generates left rotates. Use right rotates if
30626 * possible to be kinder to dinky implementations with iterative rotate
30627 * instructions.
30628 */
30629#define SHA_ROT(op, x, k) \
30630 ({ unsigned int y; asm(op " %1, %0" : "=r" (y) : "I" (k), "0" (x)); y; })
30631#define rol(x, k) SHA_ROT("roll", x, k)
30632#define ror(x, k) SHA_ROT("rorl", x, k)
30633
30634#else
30635/* Generic C equivalent */
30636#define SHA_ROT(x, l, r) ((x) << (l) | (x) >> (r))
30637#define rol(x, k) SHA_ROT(x, k, 32-(k))
30638#define ror(x, k) SHA_ROT(x, 32-(k), k)
30639#endif
30640
30641#define blk0le(i) (block[i] = (ror(block[i], 8)&0xFF00FF00) \
30642 |(rol(block[i], 8)&0x00FF00FF))
30643#define blk0be(i) block[i]
30644#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
30645 ^block[(i+2)&15]^block[i&15], 1))
30646
30647/*
30648 * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
30649 *
30650 * Rl0() for little-endian and Rb0() for big-endian. Endianness is
30651 * determined at run-time.
30652 */
30653#define Rl0(v, w, x, y, z, i) \
30654 z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
30655#define Rb0(v, w, x, y, z, i) \
30656 z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
30657#define R1(v, w, x, y, z, i) \
30658 z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
30659#define R2(v, w, x, y, z, i) \
30660 z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v, 5);w=ror(w, 2);
30661#define R3(v, w, x, y, z, i) \
30662 z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v, 5);w=ror(w, 2);
30663#define R4(v, w, x, y, z, i) \
30664 z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v, 5);w=ror(w, 2);
30665
30666/*
30667 * Hash a single 512-bit block. This is the core of the algorithm.
30668 */
30669#define a qq[0]
30670#define b qq[1]
30671#define c qq[2]
30672#define d qq[3]
30673#define e qq[4]
30674
30675static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])
30676{
30677 unsigned int qq[5]; /* a, b, c, d, e; */
30678 static int one = 1;
30679 unsigned int block[16];
30680 SyMemcpy(buffer, (void *)block, 64);
30681 SyMemcpy(state, qq, 5*sizeof(unsigned int));
30682
30683 /* Copy context->state[] to working vars */
30684 /*
30685 a = state[0];
30686 b = state[1];
30687 c = state[2];
30688 d = state[3];
30689 e = state[4];
30690 */
30691
30692 /* 4 rounds of 20 operations each. Loop unrolled. */
30693 if( 1 == *(unsigned char*)&one ){
30694 Rl0(a, b, c, d, e, 0); Rl0(e, a, b, c, d, 1); Rl0(d, e, a, b, c, 2); Rl0(c, d, e, a, b, 3);
30695 Rl0(b, c, d, e, a, 4); Rl0(a, b, c, d, e, 5); Rl0(e, a, b, c, d, 6); Rl0(d, e, a, b, c, 7);
30696 Rl0(c, d, e, a, b, 8); Rl0(b, c, d, e, a, 9); Rl0(a, b, c, d, e, 10); Rl0(e, a, b, c, d, 11);
30697 Rl0(d, e, a, b, c, 12); Rl0(c, d, e, a, b, 13); Rl0(b, c, d, e, a, 14); Rl0(a, b, c, d, e, 15);
30698 }else{
30699 Rb0(a, b, c, d, e, 0); Rb0(e, a, b, c, d, 1); Rb0(d, e, a, b, c, 2); Rb0(c, d, e, a, b, 3);
30700 Rb0(b, c, d, e, a, 4); Rb0(a, b, c, d, e, 5); Rb0(e, a, b, c, d, 6); Rb0(d, e, a, b, c, 7);
30701 Rb0(c, d, e, a, b, 8); Rb0(b, c, d, e, a, 9); Rb0(a, b, c, d, e, 10); Rb0(e, a, b, c, d, 11);
30702 Rb0(d, e, a, b, c, 12); Rb0(c, d, e, a, b, 13); Rb0(b, c, d, e, a, 14); Rb0(a, b, c, d, e, 15);
30703 }
30704 R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19);
30705 R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23);
30706 R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27);
30707 R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31);
30708 R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35);
30709 R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39);
30710 R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43);
30711 R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47);
30712 R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51);
30713 R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55);
30714 R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59);
30715 R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63);
30716 R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67);
30717 R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71);
30718 R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75);
30719 R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79);
30720
30721 /* Add the working vars back into context.state[] */
30722 state[0] += a;
30723 state[1] += b;
30724 state[2] += c;
30725 state[3] += d;
30726 state[4] += e;
30727}
30728#undef a
30729#undef b
30730#undef c
30731#undef d
30732#undef e
30733/*
30734 * SHA1Init - Initialize new context
30735 */
30736JX9_PRIVATE void SHA1Init(SHA1Context *context){
30737 /* SHA1 initialization constants */
30738 context->state[0] = 0x67452301;
30739 context->state[1] = 0xEFCDAB89;
30740 context->state[2] = 0x98BADCFE;
30741 context->state[3] = 0x10325476;
30742 context->state[4] = 0xC3D2E1F0;
30743 context->count[0] = context->count[1] = 0;
30744}
30745/*
30746 * Run your data through this.
30747 */
30748JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len){
30749 unsigned int i, j;
30750
30751 j = context->count[0];
30752 if ((context->count[0] += len << 3) < j)
30753 context->count[1] += (len>>29)+1;
30754 j = (j >> 3) & 63;
30755 if ((j + len) > 63) {
30756 (void)SyMemcpy(data, &context->buffer[j], (i = 64-j));
30757 SHA1Transform(context->state, context->buffer);
30758 for ( ; i + 63 < len; i += 64)
30759 SHA1Transform(context->state, &data[i]);
30760 j = 0;
30761 } else {
30762 i = 0;
30763 }
30764 (void)SyMemcpy(&data[i], &context->buffer[j], len - i);
30765}
30766/*
30767 * Add padding and return the message digest.
30768 */
30769JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]){
30770 unsigned int i;
30771 unsigned char finalcount[8];
30772
30773 for (i = 0; i < 8; i++) {
30774 finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
30775 >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
30776 }
30777 SHA1Update(context, (const unsigned char *)"\200", 1);
30778 while ((context->count[0] & 504) != 448)
30779 SHA1Update(context, (const unsigned char *)"\0", 1);
30780 SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
30781
30782 if (digest) {
30783 for (i = 0; i < 20; i++)
30784 digest[i] = (unsigned char)
30785 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
30786 }
30787}
30788#undef Rl0
30789#undef Rb0
30790#undef R1
30791#undef R2
30792#undef R3
30793#undef R4
30794
30795JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20])
30796{
30797 SHA1Context sCtx;
30798 SHA1Init(&sCtx);
30799 SHA1Update(&sCtx, (const unsigned char *)pIn, nLen);
30800 SHA1Final(&sCtx, zDigest);
30801 return SXRET_OK;
30802}
30803static const sxu32 crc32_table[] = {
30804 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
30805 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
30806 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
30807 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
30808 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
30809 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
30810 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
30811 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
30812 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
30813 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
30814 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
30815 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
30816 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
30817 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
30818 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
30819 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
30820 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
30821 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
30822 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
30823 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
30824 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
30825 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
30826 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
30827 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
30828 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
30829 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
30830 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
30831 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
30832 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
30833 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
30834 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
30835 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
30836 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
30837 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
30838 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
30839 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
30840 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
30841 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
30842 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
30843 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
30844 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
30845 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
30846 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
30847 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
30848 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
30849 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
30850 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
30851 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
30852 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
30853 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
30854 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
30855 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
30856 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
30857 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
30858 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
30859 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
30860 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
30861 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
30862 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
30863 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
30864 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
30865 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
30866 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
30867 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
30868};
30869#define CRC32C(c, d) (c = ( crc32_table[(c ^ (d)) & 0xFF] ^ (c>>8) ) )
30870static sxu32 SyCrc32Update(sxu32 crc32, const void *pSrc, sxu32 nLen)
30871{
30872 register unsigned char *zIn = (unsigned char *)pSrc;
30873 unsigned char *zEnd;
30874 if( zIn == 0 ){
30875 return crc32;
30876 }
30877 zEnd = &zIn[nLen];
30878 for(;;){
30879 if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
30880 if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
30881 if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
30882 if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
30883 }
30884
30885 return crc32;
30886}
30887JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen)
30888{
30889 return SyCrc32Update(SXU32_HIGH, pSrc, nLen);
30890}
30891#endif /* JX9_DISABLE_HASH_FUNC */
30892#endif /* JX9_DISABLE_BUILTIN_FUNC */
30893#ifndef JX9_DISABLE_BUILTIN_FUNC
30894JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData)
30895{
30896 static const unsigned char zHexTab[] = "0123456789abcdef";
30897 const unsigned char *zIn, *zEnd;
30898 unsigned char zOut[3];
30899 sxi32 rc;
30900#if defined(UNTRUST)
30901 if( pIn == 0 || xConsumer == 0 ){
30902 return SXERR_EMPTY;
30903 }
30904#endif
30905 zIn = (const unsigned char *)pIn;
30906 zEnd = &zIn[nLen];
30907 for(;;){
30908 if( zIn >= zEnd ){
30909 break;
30910 }
30911 zOut[0] = zHexTab[zIn[0] >> 4]; zOut[1] = zHexTab[zIn[0] & 0x0F];
30912 rc = xConsumer((const void *)zOut, sizeof(char)*2, pConsumerData);
30913 if( rc != SXRET_OK ){
30914 return rc;
30915 }
30916 zIn++;
30917 }
30918 return SXRET_OK;
30919}
30920#endif /* JX9_DISABLE_BUILTIN_FUNC */
30921JX9_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb)
30922{
30923 buf[3] = nb & 0xFF ; nb >>=8;
30924 buf[2] = nb & 0xFF ; nb >>=8;
30925 buf[1] = nb & 0xFF ; nb >>=8;
30926 buf[0] = (unsigned char)nb ;
30927}
30928JX9_PRIVATE void SyBigEndianUnpack32(const unsigned char *buf,sxu32 *uNB)
30929{
30930 *uNB = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24);
30931}
30932JX9_PRIVATE void SyBigEndianPack16(unsigned char *buf,sxu16 nb)
30933{
30934 buf[1] = nb & 0xFF ; nb >>=8;
30935 buf[0] = (unsigned char)nb ;
30936}
30937JX9_PRIVATE void SyBigEndianUnpack16(const unsigned char *buf,sxu16 *uNB)
30938{
30939 *uNB = buf[1] + (buf[0] << 8);
30940}
30941JX9_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64)
30942{
30943 buf[7] = n64 & 0xFF; n64 >>=8;
30944 buf[6] = n64 & 0xFF; n64 >>=8;
30945 buf[5] = n64 & 0xFF; n64 >>=8;
30946 buf[4] = n64 & 0xFF; n64 >>=8;
30947 buf[3] = n64 & 0xFF; n64 >>=8;
30948 buf[2] = n64 & 0xFF; n64 >>=8;
30949 buf[1] = n64 & 0xFF; n64 >>=8;
30950 buf[0] = (sxu8)n64 ;
30951}
30952JX9_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64)
30953{
30954 sxu32 u1,u2;
30955 u1 = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24);
30956 u2 = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24);
30957 *n64 = (((sxu64)u2) << 32) | u1;
30958}
30959JX9_PRIVATE sxi32 SyBlobAppendBig64(SyBlob *pBlob,sxu64 n64)
30960{
30961 unsigned char zBuf[8];
30962 sxi32 rc;
30963 SyBigEndianPack64(zBuf,n64);
30964 rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
30965 return rc;
30966}
30967JX9_PRIVATE sxi32 SyBlobAppendBig32(SyBlob *pBlob,sxu32 n32)
30968{
30969 unsigned char zBuf[4];
30970 sxi32 rc;
30971 SyBigEndianPack32(zBuf,n32);
30972 rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
30973 return rc;
30974}
30975JX9_PRIVATE sxi32 SyBlobAppendBig16(SyBlob *pBlob,sxu16 n16)
30976{
30977 unsigned char zBuf[2];
30978 sxi32 rc;
30979 SyBigEndianPack16(zBuf,n16);
30980 rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
30981 return rc;
30982}
30983JX9_PRIVATE void SyTimeFormatToDos(Sytm *pFmt,sxu32 *pOut)
30984{
30985 sxi32 nDate,nTime;
30986 nDate = ((pFmt->tm_year - 1980) << 9) + (pFmt->tm_mon << 5) + pFmt->tm_mday;
30987 nTime = (pFmt->tm_hour << 11) + (pFmt->tm_min << 5)+ (pFmt->tm_sec >> 1);
30988 *pOut = (nDate << 16) | nTime;
30989}
30990JX9_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut)
30991{
30992 sxu16 nDate;
30993 sxu16 nTime;
30994 nDate = nDosDate >> 16;
30995 nTime = nDosDate & 0xFFFF;
30996 pOut->tm_isdst = 0;
30997 pOut->tm_year = 1980 + (nDate >> 9);
30998 pOut->tm_mon = (nDate % (1<<9))>>5;
30999 pOut->tm_mday = (nDate % (1<<9))&0x1F;
31000 pOut->tm_hour = nTime >> 11;
31001 pOut->tm_min = (nTime % (1<<11)) >> 5;
31002 pOut->tm_sec = ((nTime % (1<<11))& 0x1F )<<1;
31003}
31004/*
31005 * ----------------------------------------------------------
31006 * File: jx9_memobj.c
31007 * MD5: 8692d7f4cb297c0946066b4a9034c637
31008 * ----------------------------------------------------------
31009 */
31010/*
31011 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
31012 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
31013 * Version 1.7.2
31014 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
31015 * please contact Symisc Systems via:
31016 * legal@symisc.net
31017 * licensing@symisc.net
31018 * contact@symisc.net
31019 * or visit:
31020 * http://jx9.symisc.net/
31021 */
31022 /* $SymiscID: memobj.c v2.7 FreeBSD 2012-08-09 03:40 stable <chm@symisc.net> $ */
31023#ifndef JX9_AMALGAMATION
31024#include "jx9Int.h"
31025#endif
31026/* This file manage low-level stuff related to indexed memory objects [i.e: jx9_value] */
31027/*
31028 * Notes on memory objects [i.e: jx9_value].
31029 * Internally, the JX9 virtual machine manipulates nearly all JX9 values
31030 * [i.e: string, int, float, resource, object, bool, null..] as jx9_values structures.
31031 * Each jx9_values struct may cache multiple representations (string,
31032 * integer etc.) of the same value.
31033 */
31034/*
31035 * Convert a 64-bit IEEE double into a 64-bit signed integer.
31036 * If the double is too large, return 0x8000000000000000.
31037 *
31038 * Most systems appear to do this simply by assigning ariables and without
31039 * the extra range tests.
31040 * But there are reports that windows throws an expection if the floating
31041 * point value is out of range.
31042 */
31043static sxi64 MemObjRealToInt(jx9_value *pObj)
31044{
31045#ifdef JX9_OMIT_FLOATING_POINT
31046 /* Real and 64bit integer are the same when floating point arithmetic
31047 * is omitted from the build.
31048 */
31049 return pObj->x.rVal;
31050#else
31051 /*
31052 ** Many compilers we encounter do not define constants for the
31053 ** minimum and maximum 64-bit integers, or they define them
31054 ** inconsistently. And many do not understand the "LL" notation.
31055 ** So we define our own static constants here using nothing
31056 ** larger than a 32-bit integer constant.
31057 */
31058 static const sxi64 maxInt = LARGEST_INT64;
31059 static const sxi64 minInt = SMALLEST_INT64;
31060 jx9_real r = pObj->x.rVal;
31061 if( r<(jx9_real)minInt ){
31062 return minInt;
31063 }else if( r>(jx9_real)maxInt ){
31064 /* minInt is correct here - not maxInt. It turns out that assigning
31065 ** a very large positive number to an integer results in a very large
31066 ** negative integer. This makes no sense, but it is what x86 hardware
31067 ** does so for compatibility we will do the same in software. */
31068 return minInt;
31069 }else{
31070 return (sxi64)r;
31071 }
31072#endif
31073}
31074/*
31075 * Convert a raw token value typically a stream of digit [i.e: hex, octal, binary or decimal]
31076 * to a 64-bit integer.
31077 */
31078JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pVal)
31079{
31080 sxi64 iVal = 0;
31081 if( pVal->nByte <= 0 ){
31082 return 0;
31083 }
31084 if( pVal->zString[0] == '0' ){
31085 sxi32 c;
31086 if( pVal->nByte == sizeof(char) ){
31087 return 0;
31088 }
31089 c = pVal->zString[1];
31090 if( c == 'x' || c == 'X' ){
31091 /* Hex digit stream */
31092 SyHexStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
31093 }else if( c == 'b' || c == 'B' ){
31094 /* Binary digit stream */
31095 SyBinaryStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
31096 }else{
31097 /* Octal digit stream */
31098 SyOctalStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
31099 }
31100 }else{
31101 /* Decimal digit stream */
31102 SyStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
31103 }
31104 return iVal;
31105}
31106/*
31107 * Return some kind of 64-bit integer value which is the best we can
31108 * do at representing the value that pObj describes as a string
31109 * representation.
31110 */
31111static sxi64 MemObjStringToInt(jx9_value *pObj)
31112{
31113 SyString sVal;
31114 SyStringInitFromBuf(&sVal, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
31115 return jx9TokenValueToInt64(&sVal);
31116}
31117/*
31118 * Return some kind of integer value which is the best we can
31119 * do at representing the value that pObj describes as an integer.
31120 * If pObj is an integer, then the value is exact. If pObj is
31121 * a floating-point then the value returned is the integer part.
31122 * If pObj is a string, then we make an attempt to convert it into
31123 * a integer and return that.
31124 * If pObj represents a NULL value, return 0.
31125 */
31126static sxi64 MemObjIntValue(jx9_value *pObj)
31127{
31128 sxi32 iFlags;
31129 iFlags = pObj->iFlags;
31130 if (iFlags & MEMOBJ_REAL ){
31131 return MemObjRealToInt(&(*pObj));
31132 }else if( iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
31133 return pObj->x.iVal;
31134 }else if (iFlags & MEMOBJ_STRING) {
31135 return MemObjStringToInt(&(*pObj));
31136 }else if( iFlags & MEMOBJ_NULL ){
31137 return 0;
31138 }else if( iFlags & MEMOBJ_HASHMAP ){
31139 jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
31140 sxu32 n = pMap->nEntry;
31141 jx9HashmapUnref(pMap);
31142 /* Return total number of entries in the hashmap */
31143 return n;
31144 }else if(iFlags & MEMOBJ_RES ){
31145 return pObj->x.pOther != 0;
31146 }
31147 /* CANT HAPPEN */
31148 return 0;
31149}
31150/*
31151 * Return some kind of real value which is the best we can
31152 * do at representing the value that pObj describes as a real.
31153 * If pObj is a real, then the value is exact.If pObj is an
31154 * integer then the integer is promoted to real and that value
31155 * is returned.
31156 * If pObj is a string, then we make an attempt to convert it
31157 * into a real and return that.
31158 * If pObj represents a NULL value, return 0.0
31159 */
31160static jx9_real MemObjRealValue(jx9_value *pObj)
31161{
31162 sxi32 iFlags;
31163 iFlags = pObj->iFlags;
31164 if( iFlags & MEMOBJ_REAL ){
31165 return pObj->x.rVal;
31166 }else if (iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
31167 return (jx9_real)pObj->x.iVal;
31168 }else if (iFlags & MEMOBJ_STRING){
31169 SyString sString;
31170#ifdef JX9_OMIT_FLOATING_POINT
31171 jx9_real rVal = 0;
31172#else
31173 jx9_real rVal = 0.0;
31174#endif
31175 SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
31176 if( SyBlobLength(&pObj->sBlob) > 0 ){
31177 /* Convert as much as we can */
31178#ifdef JX9_OMIT_FLOATING_POINT
31179 rVal = MemObjStringToInt(&(*pObj));
31180#else
31181 SyStrToReal(sString.zString, sString.nByte, (void *)&rVal, 0);
31182#endif
31183 }
31184 return rVal;
31185 }else if( iFlags & MEMOBJ_NULL ){
31186#ifdef JX9_OMIT_FLOATING_POINT
31187 return 0;
31188#else
31189 return 0.0;
31190#endif
31191 }else if( iFlags & MEMOBJ_HASHMAP ){
31192 /* Return the total number of entries in the hashmap */
31193 jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
31194 jx9_real n = (jx9_real)pMap->nEntry;
31195 jx9HashmapUnref(pMap);
31196 return n;
31197 }else if(iFlags & MEMOBJ_RES ){
31198 return (jx9_real)(pObj->x.pOther != 0);
31199 }
31200 /* NOT REACHED */
31201 return 0;
31202}
31203/*
31204 * Return the string representation of a given jx9_value.
31205 * This function never fail and always return SXRET_OK.
31206 */
31207static sxi32 MemObjStringValue(SyBlob *pOut,jx9_value *pObj)
31208{
31209 if( pObj->iFlags & MEMOBJ_REAL ){
31210 SyBlobFormat(&(*pOut), "%.15g", pObj->x.rVal);
31211 }else if( pObj->iFlags & MEMOBJ_INT ){
31212 SyBlobFormat(&(*pOut), "%qd", pObj->x.iVal);
31213 /* %qd (BSD quad) is equivalent to %lld in the libc printf */
31214 }else if( pObj->iFlags & MEMOBJ_BOOL ){
31215 if( pObj->x.iVal ){
31216 SyBlobAppend(&(*pOut),"true", sizeof("true")-1);
31217 }else{
31218 SyBlobAppend(&(*pOut),"false", sizeof("false")-1);
31219 }
31220 }else if( pObj->iFlags & MEMOBJ_HASHMAP ){
31221 /* Serialize JSON object or array */
31222 jx9JsonSerialize(pObj,pOut);
31223 jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther);
31224 }else if(pObj->iFlags & MEMOBJ_RES ){
31225 SyBlobFormat(&(*pOut), "ResourceID_%#x", pObj->x.pOther);
31226 }
31227 return SXRET_OK;
31228}
31229/*
31230 * Return some kind of boolean value which is the best we can do
31231 * at representing the value that pObj describes as a boolean.
31232 * When converting to boolean, the following values are considered FALSE:
31233 * NULL
31234 * the boolean FALSE itself.
31235 * the integer 0 (zero).
31236 * the real 0.0 (zero).
31237 * the empty string, a stream of zero [i.e: "0", "00", "000", ...] and the string
31238 * "false".
31239 * an array with zero elements.
31240 */
31241static sxi32 MemObjBooleanValue(jx9_value *pObj)
31242{
31243 sxi32 iFlags;
31244 iFlags = pObj->iFlags;
31245 if (iFlags & MEMOBJ_REAL ){
31246#ifdef JX9_OMIT_FLOATING_POINT
31247 return pObj->x.rVal ? 1 : 0;
31248#else
31249 return pObj->x.rVal != 0.0 ? 1 : 0;
31250#endif
31251 }else if( iFlags & MEMOBJ_INT ){
31252 return pObj->x.iVal ? 1 : 0;
31253 }else if (iFlags & MEMOBJ_STRING) {
31254 SyString sString;
31255 SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
31256 if( sString.nByte == 0 ){
31257 /* Empty string */
31258 return 0;
31259 }else if( (sString.nByte == sizeof("true") - 1 && SyStrnicmp(sString.zString, "true", sizeof("true")-1) == 0) ||
31260 (sString.nByte == sizeof("on") - 1 && SyStrnicmp(sString.zString, "on", sizeof("on")-1) == 0) ||
31261 (sString.nByte == sizeof("yes") - 1 && SyStrnicmp(sString.zString, "yes", sizeof("yes")-1) == 0) ){
31262 return 1;
31263 }else if( sString.nByte == sizeof("false") - 1 && SyStrnicmp(sString.zString, "false", sizeof("false")-1) == 0 ){
31264 return 0;
31265 }else{
31266 const char *zIn, *zEnd;
31267 zIn = sString.zString;
31268 zEnd = &zIn[sString.nByte];
31269 while( zIn < zEnd && zIn[0] == '0' ){
31270 zIn++;
31271 }
31272 return zIn >= zEnd ? 0 : 1;
31273 }
31274 }else if( iFlags & MEMOBJ_NULL ){
31275 return 0;
31276 }else if( iFlags & MEMOBJ_HASHMAP ){
31277 jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
31278 sxu32 n = pMap->nEntry;
31279 jx9HashmapUnref(pMap);
31280 return n > 0 ? TRUE : FALSE;
31281 }else if(iFlags & MEMOBJ_RES ){
31282 return pObj->x.pOther != 0;
31283 }
31284 /* NOT REACHED */
31285 return 0;
31286}
31287/*
31288 * If the jx9_value is of type real, try to make it an integer also.
31289 */
31290static sxi32 MemObjTryIntger(jx9_value *pObj)
31291{
31292 sxi64 iVal = MemObjRealToInt(&(*pObj));
31293 /* Only mark the value as an integer if
31294 **
31295 ** (1) the round-trip conversion real->int->real is a no-op, and
31296 ** (2) The integer is neither the largest nor the smallest
31297 ** possible integer
31298 **
31299 ** The second and third terms in the following conditional enforces
31300 ** the second condition under the assumption that addition overflow causes
31301 ** values to wrap around. On x86 hardware, the third term is always
31302 ** true and could be omitted. But we leave it in because other
31303 ** architectures might behave differently.
31304 */
31305 if( pObj->x.rVal ==(jx9_real)iVal && iVal>SMALLEST_INT64 && iVal<LARGEST_INT64 ){
31306 pObj->x.iVal = iVal;
31307 pObj->iFlags = MEMOBJ_INT;
31308 }
31309 return SXRET_OK;
31310}
31311/*
31312 * Convert a jx9_value to type integer.Invalidate any prior representations.
31313 */
31314JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj)
31315{
31316 if( (pObj->iFlags & MEMOBJ_INT) == 0 ){
31317 /* Preform the conversion */
31318 pObj->x.iVal = MemObjIntValue(&(*pObj));
31319 /* Invalidate any prior representations */
31320 SyBlobRelease(&pObj->sBlob);
31321 MemObjSetType(pObj, MEMOBJ_INT);
31322 }
31323 return SXRET_OK;
31324}
31325/*
31326 * Convert a jx9_value to type real (Try to get an integer representation also).
31327 * Invalidate any prior representations
31328 */
31329JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj)
31330{
31331 if((pObj->iFlags & MEMOBJ_REAL) == 0 ){
31332 /* Preform the conversion */
31333 pObj->x.rVal = MemObjRealValue(&(*pObj));
31334 /* Invalidate any prior representations */
31335 SyBlobRelease(&pObj->sBlob);
31336 MemObjSetType(pObj, MEMOBJ_REAL);
31337 }
31338 return SXRET_OK;
31339}
31340/*
31341 * Convert a jx9_value to type boolean.Invalidate any prior representations.
31342 */
31343JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj)
31344{
31345 if( (pObj->iFlags & MEMOBJ_BOOL) == 0 ){
31346 /* Preform the conversion */
31347 pObj->x.iVal = MemObjBooleanValue(&(*pObj));
31348 /* Invalidate any prior representations */
31349 SyBlobRelease(&pObj->sBlob);
31350 MemObjSetType(pObj, MEMOBJ_BOOL);
31351 }
31352 return SXRET_OK;
31353}
31354/*
31355 * Convert a jx9_value to type string.Prior representations are NOT invalidated.
31356 */
31357JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj)
31358{
31359 sxi32 rc = SXRET_OK;
31360 if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
31361 /* Perform the conversion */
31362 SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */
31363 rc = MemObjStringValue(&pObj->sBlob, &(*pObj));
31364 MemObjSetType(pObj, MEMOBJ_STRING);
31365 }
31366 return rc;
31367}
31368/*
31369 * Nullify a jx9_value.In other words invalidate any prior
31370 * representation.
31371 */
31372JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj)
31373{
31374 return jx9MemObjRelease(pObj);
31375}
31376/*
31377 * Convert a jx9_value to type array.Invalidate any prior representations.
31378 * According to the JX9 language reference manual.
31379 * For any of the types: integer, float, string, boolean converting a value
31380 * to an array results in an array with a single element with index zero
31381 * and the value of the scalar which was converted.
31382 */
31383JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj)
31384{
31385 if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
31386 jx9_hashmap *pMap;
31387 /* Allocate a new hashmap instance */
31388 pMap = jx9NewHashmap(pObj->pVm, 0, 0);
31389 if( pMap == 0 ){
31390 return SXERR_MEM;
31391 }
31392 if( (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){
31393 /*
31394 * According to the JX9 language reference manual.
31395 * For any of the types: integer, float, string, boolean converting a value
31396 * to an array results in an array with a single element with index zero
31397 * and the value of the scalar which was converted.
31398 */
31399 /* Insert a single element */
31400 jx9HashmapInsert(pMap, 0/* Automatic index assign */, &(*pObj));
31401 SyBlobRelease(&pObj->sBlob);
31402 }
31403 /* Invalidate any prior representation */
31404 MemObjSetType(pObj, MEMOBJ_HASHMAP);
31405 pObj->x.pOther = pMap;
31406 }
31407 return SXRET_OK;
31408}
31409/*
31410 * Return a pointer to the appropriate convertion method associated
31411 * with the given type.
31412 * Note on type juggling.
31413 * Accoding to the JX9 language reference manual
31414 * JX9 does not require (or support) explicit type definition in variable
31415 * declaration; a variable's type is determined by the context in which
31416 * the variable is used. That is to say, if a string value is assigned
31417 * to variable $var, $var becomes a string. If an integer value is then
31418 * assigned to $var, it becomes an integer.
31419 */
31420JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags)
31421{
31422 if( iFlags & MEMOBJ_STRING ){
31423 return jx9MemObjToString;
31424 }else if( iFlags & MEMOBJ_INT ){
31425 return jx9MemObjToInteger;
31426 }else if( iFlags & MEMOBJ_REAL ){
31427 return jx9MemObjToReal;
31428 }else if( iFlags & MEMOBJ_BOOL ){
31429 return jx9MemObjToBool;
31430 }else if( iFlags & MEMOBJ_HASHMAP ){
31431 return jx9MemObjToHashmap;
31432 }
31433 /* NULL cast */
31434 return jx9MemObjToNull;
31435}
31436/*
31437 * Check whether the jx9_value is numeric [i.e: int/float/bool] or looks
31438 * like a numeric number [i.e: if the jx9_value is of type string.].
31439 * Return TRUE if numeric.FALSE otherwise.
31440 */
31441JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj)
31442{
31443 if( pObj->iFlags & ( MEMOBJ_BOOL|MEMOBJ_INT|MEMOBJ_REAL) ){
31444 return TRUE;
31445 }else if( pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES) ){
31446 return FALSE;
31447 }else if( pObj->iFlags & MEMOBJ_STRING ){
31448 SyString sStr;
31449 sxi32 rc;
31450 SyStringInitFromBuf(&sStr, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
31451 if( sStr.nByte <= 0 ){
31452 /* Empty string */
31453 return FALSE;
31454 }
31455 /* Check if the string representation looks like a numeric number */
31456 rc = SyStrIsNumeric(sStr.zString, sStr.nByte, 0, 0);
31457 return rc == SXRET_OK ? TRUE : FALSE;
31458 }
31459 /* NOT REACHED */
31460 return FALSE;
31461}
31462/*
31463 * Check whether the jx9_value is empty.Return TRUE if empty.
31464 * FALSE otherwise.
31465 * An jx9_value is considered empty if the following are true:
31466 * NULL value.
31467 * Boolean FALSE.
31468 * Integer/Float with a 0 (zero) value.
31469 * An empty string or a stream of 0 (zero) [i.e: "0", "00", "000", ...].
31470 * An empty array.
31471 * NOTE
31472 * OBJECT VALUE MUST NOT BE MODIFIED.
31473 */
31474JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj)
31475{
31476 if( pObj->iFlags & MEMOBJ_NULL ){
31477 return TRUE;
31478 }else if( pObj->iFlags & MEMOBJ_INT ){
31479 return pObj->x.iVal == 0 ? TRUE : FALSE;
31480 }else if( pObj->iFlags & MEMOBJ_REAL ){
31481 return pObj->x.rVal == (jx9_real)0 ? TRUE : FALSE;
31482 }else if( pObj->iFlags & MEMOBJ_BOOL ){
31483 return !pObj->x.iVal;
31484 }else if( pObj->iFlags & MEMOBJ_STRING ){
31485 if( SyBlobLength(&pObj->sBlob) <= 0 ){
31486 return TRUE;
31487 }else{
31488 const char *zIn, *zEnd;
31489 zIn = (const char *)SyBlobData(&pObj->sBlob);
31490 zEnd = &zIn[SyBlobLength(&pObj->sBlob)];
31491 while( zIn < zEnd ){
31492 if( zIn[0] != '0' ){
31493 break;
31494 }
31495 zIn++;
31496 }
31497 return zIn >= zEnd ? TRUE : FALSE;
31498 }
31499 }else if( pObj->iFlags & MEMOBJ_HASHMAP ){
31500 jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
31501 return pMap->nEntry == 0 ? TRUE : FALSE;
31502 }else if ( pObj->iFlags & (MEMOBJ_RES) ){
31503 return FALSE;
31504 }
31505 /* Assume empty by default */
31506 return TRUE;
31507}
31508/*
31509 * Convert a jx9_value so that it has types MEMOBJ_REAL or MEMOBJ_INT
31510 * or both.
31511 * Invalidate any prior representations. Every effort is made to force
31512 * the conversion, even if the input is a string that does not look
31513 * completely like a number.Convert as much of the string as we can
31514 * and ignore the rest.
31515 */
31516JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj)
31517{
31518 if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) ){
31519 if( pObj->iFlags & (MEMOBJ_BOOL|MEMOBJ_NULL) ){
31520 if( pObj->iFlags & MEMOBJ_NULL ){
31521 pObj->x.iVal = 0;
31522 }
31523 MemObjSetType(pObj, MEMOBJ_INT);
31524 }
31525 /* Already numeric */
31526 return SXRET_OK;
31527 }
31528 if( pObj->iFlags & MEMOBJ_STRING ){
31529 sxi32 rc = SXERR_INVALID;
31530 sxu8 bReal = FALSE;
31531 SyString sString;
31532 SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
31533 /* Check if the given string looks like a numeric number */
31534 if( sString.nByte > 0 ){
31535 rc = SyStrIsNumeric(sString.zString, sString.nByte, &bReal, 0);
31536 }
31537 if( bReal ){
31538 jx9MemObjToReal(&(*pObj));
31539 }else{
31540 if( rc != SXRET_OK ){
31541 /* The input does not look at all like a number, set the value to 0 */
31542 pObj->x.iVal = 0;
31543 }else{
31544 /* Convert as much as we can */
31545 pObj->x.iVal = MemObjStringToInt(&(*pObj));
31546 }
31547 MemObjSetType(pObj, MEMOBJ_INT);
31548 SyBlobRelease(&pObj->sBlob);
31549 }
31550 }else if(pObj->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)){
31551 jx9MemObjToInteger(pObj);
31552 }else{
31553 /* Perform a blind cast */
31554 jx9MemObjToReal(&(*pObj));
31555 }
31556 return SXRET_OK;
31557}
31558/*
31559 * Try a get an integer representation of the given jx9_value.
31560 * If the jx9_value is not of type real, this function is a no-op.
31561 */
31562JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj)
31563{
31564 if( pObj->iFlags & MEMOBJ_REAL ){
31565 /* Work only with reals */
31566 MemObjTryIntger(&(*pObj));
31567 }
31568 return SXRET_OK;
31569}
31570/*
31571 * Initialize a jx9_value to the null type.
31572 */
31573JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj)
31574{
31575 /* Zero the structure */
31576 SyZero(pObj, sizeof(jx9_value));
31577 /* Initialize fields */
31578 pObj->pVm = pVm;
31579 SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31580 /* Set the NULL type */
31581 pObj->iFlags = MEMOBJ_NULL;
31582 return SXRET_OK;
31583}
31584/*
31585 * Initialize a jx9_value to the integer type.
31586 */
31587JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal)
31588{
31589 /* Zero the structure */
31590 SyZero(pObj, sizeof(jx9_value));
31591 /* Initialize fields */
31592 pObj->pVm = pVm;
31593 SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31594 /* Set the desired type */
31595 pObj->x.iVal = iVal;
31596 pObj->iFlags = MEMOBJ_INT;
31597 return SXRET_OK;
31598}
31599/*
31600 * Initialize a jx9_value to the boolean type.
31601 */
31602JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal)
31603{
31604 /* Zero the structure */
31605 SyZero(pObj, sizeof(jx9_value));
31606 /* Initialize fields */
31607 pObj->pVm = pVm;
31608 SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31609 /* Set the desired type */
31610 pObj->x.iVal = iVal ? 1 : 0;
31611 pObj->iFlags = MEMOBJ_BOOL;
31612 return SXRET_OK;
31613}
31614#if 0
31615/*
31616 * Initialize a jx9_value to the real type.
31617 */
31618JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal)
31619{
31620 /* Zero the structure */
31621 SyZero(pObj, sizeof(jx9_value));
31622 /* Initialize fields */
31623 pObj->pVm = pVm;
31624 SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31625 /* Set the desired type */
31626 pObj->x.rVal = rVal;
31627 pObj->iFlags = MEMOBJ_REAL;
31628 return SXRET_OK;
31629}
31630#endif
31631/*
31632 * Initialize a jx9_value to the array type.
31633 */
31634JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray)
31635{
31636 /* Zero the structure */
31637 SyZero(pObj, sizeof(jx9_value));
31638 /* Initialize fields */
31639 pObj->pVm = pVm;
31640 SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31641 /* Set the desired type */
31642 pObj->iFlags = MEMOBJ_HASHMAP;
31643 pObj->x.pOther = pArray;
31644 return SXRET_OK;
31645}
31646/*
31647 * Initialize a jx9_value to the string type.
31648 */
31649JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal)
31650{
31651 /* Zero the structure */
31652 SyZero(pObj, sizeof(jx9_value));
31653 /* Initialize fields */
31654 pObj->pVm = pVm;
31655 SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31656 if( pVal ){
31657 /* Append contents */
31658 SyBlobAppend(&pObj->sBlob, (const void *)pVal->zString, pVal->nByte);
31659 }
31660 /* Set the desired type */
31661 pObj->iFlags = MEMOBJ_STRING;
31662 return SXRET_OK;
31663}
31664/*
31665 * Append some contents to the internal buffer of a given jx9_value.
31666 * If the given jx9_value is not of type string, this function
31667 * invalidate any prior representation and set the string type.
31668 * Then a simple append operation is performed.
31669 */
31670JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen)
31671{
31672 sxi32 rc;
31673 if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
31674 /* Invalidate any prior representation */
31675 jx9MemObjRelease(pObj);
31676 MemObjSetType(pObj, MEMOBJ_STRING);
31677 }
31678 /* Append contents */
31679 rc = SyBlobAppend(&pObj->sBlob, zData, nLen);
31680 return rc;
31681}
31682#if 0
31683/*
31684 * Format and append some contents to the internal buffer of a given jx9_value.
31685 * If the given jx9_value is not of type string, this function invalidate
31686 * any prior representation and set the string type.
31687 * Then a simple format and append operation is performed.
31688 */
31689JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap)
31690{
31691 sxi32 rc;
31692 if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
31693 /* Invalidate any prior representation */
31694 jx9MemObjRelease(pObj);
31695 MemObjSetType(pObj, MEMOBJ_STRING);
31696 }
31697 /* Format and append contents */
31698 rc = SyBlobFormatAp(&pObj->sBlob, zFormat, ap);
31699 return rc;
31700}
31701#endif
31702/*
31703 * Duplicate the contents of a jx9_value.
31704 */
31705JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest)
31706{
31707 jx9_hashmap *pMap = 0;
31708 sxi32 rc;
31709 if( pSrc->iFlags & MEMOBJ_HASHMAP ){
31710 /* Increment reference count */
31711 ((jx9_hashmap *)pSrc->x.pOther)->iRef++;
31712 }
31713 if( pDest->iFlags & MEMOBJ_HASHMAP ){
31714 pMap = (jx9_hashmap *)pDest->x.pOther;
31715 }
31716 SyMemcpy((const void *)&(*pSrc), &(*pDest), sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
31717 rc = SXRET_OK;
31718 if( SyBlobLength(&pSrc->sBlob) > 0 ){
31719 SyBlobReset(&pDest->sBlob);
31720 rc = SyBlobDup(&pSrc->sBlob, &pDest->sBlob);
31721 }else{
31722 if( SyBlobLength(&pDest->sBlob) > 0 ){
31723 SyBlobRelease(&pDest->sBlob);
31724 }
31725 }
31726 if( pMap ){
31727 jx9HashmapUnref(pMap);
31728 }
31729 return rc;
31730}
31731/*
31732 * Duplicate the contents of a jx9_value but do not copy internal
31733 * buffer contents, simply point to it.
31734 */
31735JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest)
31736{
31737 SyMemcpy((const void *)&(*pSrc), &(*pDest),
31738 sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
31739 if( pSrc->iFlags & MEMOBJ_HASHMAP ){
31740 /* Increment reference count */
31741 ((jx9_hashmap *)pSrc->x.pOther)->iRef++;
31742 }
31743 if( SyBlobLength(&pDest->sBlob) > 0 ){
31744 SyBlobRelease(&pDest->sBlob);
31745 }
31746 if( SyBlobLength(&pSrc->sBlob) > 0 ){
31747 SyBlobReadOnly(&pDest->sBlob, SyBlobData(&pSrc->sBlob), SyBlobLength(&pSrc->sBlob));
31748 }
31749 return SXRET_OK;
31750}
31751/*
31752 * Invalidate any prior representation of a given jx9_value.
31753 */
31754JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj)
31755{
31756 if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){
31757 if( pObj->iFlags & MEMOBJ_HASHMAP ){
31758 jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther);
31759 }
31760 /* Release the internal buffer */
31761 SyBlobRelease(&pObj->sBlob);
31762 /* Invalidate any prior representation */
31763 pObj->iFlags = MEMOBJ_NULL;
31764 }
31765 return SXRET_OK;
31766}
31767/*
31768 * Compare two jx9_values.
31769 * Return 0 if the values are equals, > 0 if pObj1 is greater than pObj2
31770 * or < 0 if pObj2 is greater than pObj1.
31771 * Type comparison table taken from the JX9 language reference manual.
31772 * Comparisons of $x with JX9 functions Expression
31773 * gettype() empty() is_null() isset() boolean : if($x)
31774 * $x = ""; string TRUE FALSE TRUE FALSE
31775 * $x = null NULL TRUE TRUE FALSE FALSE
31776 * var $x; NULL TRUE TRUE FALSE FALSE
31777 * $x is undefined NULL TRUE TRUE FALSE FALSE
31778 * $x = array(); array TRUE FALSE TRUE FALSE
31779 * $x = false; boolean TRUE FALSE TRUE FALSE
31780 * $x = true; boolean FALSE FALSE TRUE TRUE
31781 * $x = 1; integer FALSE FALSE TRUE TRUE
31782 * $x = 42; integer FALSE FALSE TRUE TRUE
31783 * $x = 0; integer TRUE FALSE TRUE FALSE
31784 * $x = -1; integer FALSE FALSE TRUE TRUE
31785 * $x = "1"; string FALSE FALSE TRUE TRUE
31786 * $x = "0"; string TRUE FALSE TRUE FALSE
31787 * $x = "-1"; string FALSE FALSE TRUE TRUE
31788 * $x = "jx9"; string FALSE FALSE TRUE TRUE
31789 * $x = "true"; string FALSE FALSE TRUE TRUE
31790 * $x = "false"; string FALSE FALSE TRUE TRUE
31791 * Loose comparisons with ==
31792 * TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "jx9" ""
31793 * TRUE TRUE FALSE TRUE FALSE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE
31794 * FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE TRUE
31795 * 1 TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
31796 * 0 FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE TRUE TRUE
31797 * -1 TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
31798 * "1" TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
31799 * "0" FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
31800 * "-1" TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
31801 * NULL FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE
31802 * array() FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE
31803 * "jx9" TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
31804 * "" FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE
31805 * Strict comparisons with ===
31806 * TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "jx9" ""
31807 * TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
31808 * FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
31809 * 1 FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
31810 * 0 FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
31811 * -1 FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
31812 * "1" FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
31813 * "0" FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
31814 * "-1" FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
31815 * NULL FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
31816 * array() FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
31817 * "jx9" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
31818 * "" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
31819 */
31820JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest)
31821{
31822 sxi32 iComb;
31823 sxi32 rc;
31824 if( bStrict ){
31825 sxi32 iF1, iF2;
31826 /* Strict comparisons with === */
31827 iF1 = pObj1->iFlags;
31828 iF2 = pObj2->iFlags;
31829 if( iF1 != iF2 ){
31830 /* Not of the same type */
31831 return 1;
31832 }
31833 }
31834 /* Combine flag together */
31835 iComb = pObj1->iFlags|pObj2->iFlags;
31836 if( iComb & (MEMOBJ_NULL|MEMOBJ_RES|MEMOBJ_BOOL) ){
31837 /* Convert to boolean: Keep in mind FALSE < TRUE */
31838 if( (pObj1->iFlags & MEMOBJ_BOOL) == 0 ){
31839 jx9MemObjToBool(pObj1);
31840 }
31841 if( (pObj2->iFlags & MEMOBJ_BOOL) == 0 ){
31842 jx9MemObjToBool(pObj2);
31843 }
31844 return (sxi32)((pObj1->x.iVal != 0) - (pObj2->x.iVal != 0));
31845 }else if ( iComb & MEMOBJ_HASHMAP ){
31846 /* Hashmap aka 'array' comparison */
31847 if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){
31848 /* Array is always greater */
31849 return -1;
31850 }
31851 if( (pObj2->iFlags & MEMOBJ_HASHMAP) == 0 ){
31852 /* Array is always greater */
31853 return 1;
31854 }
31855 /* Perform the comparison */
31856 rc = jx9HashmapCmp((jx9_hashmap *)pObj1->x.pOther, (jx9_hashmap *)pObj2->x.pOther, bStrict);
31857 return rc;
31858 }else if ( iComb & MEMOBJ_STRING ){
31859 SyString s1, s2;
31860 /* Perform a strict string comparison.*/
31861 if( (pObj1->iFlags&MEMOBJ_STRING) == 0 ){
31862 jx9MemObjToString(pObj1);
31863 }
31864 if( (pObj2->iFlags&MEMOBJ_STRING) == 0 ){
31865 jx9MemObjToString(pObj2);
31866 }
31867 SyStringInitFromBuf(&s1, SyBlobData(&pObj1->sBlob), SyBlobLength(&pObj1->sBlob));
31868 SyStringInitFromBuf(&s2, SyBlobData(&pObj2->sBlob), SyBlobLength(&pObj2->sBlob));
31869 /*
31870 * Strings are compared using memcmp(). If one value is an exact prefix of the
31871 * other, then the shorter value is less than the longer value.
31872 */
31873 rc = SyMemcmp((const void *)s1.zString, (const void *)s2.zString, SXMIN(s1.nByte, s2.nByte));
31874 if( rc == 0 ){
31875 if( s1.nByte != s2.nByte ){
31876 rc = s1.nByte < s2.nByte ? -1 : 1;
31877 }
31878 }
31879 return rc;
31880 }else if( iComb & (MEMOBJ_INT|MEMOBJ_REAL) ){
31881 /* Perform a numeric comparison if one of the operand is numeric(integer or real) */
31882 if( (pObj1->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
31883 jx9MemObjToNumeric(pObj1);
31884 }
31885 if( (pObj2->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
31886 jx9MemObjToNumeric(pObj2);
31887 }
31888 if( (pObj1->iFlags & pObj2->iFlags & MEMOBJ_INT) == 0) {
31889 jx9_real r1, r2;
31890 /* Compare as reals */
31891 if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
31892 jx9MemObjToReal(pObj1);
31893 }
31894 r1 = pObj1->x.rVal;
31895 if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
31896 jx9MemObjToReal(pObj2);
31897 }
31898 r2 = pObj2->x.rVal;
31899 if( r1 > r2 ){
31900 return 1;
31901 }else if( r1 < r2 ){
31902 return -1;
31903 }
31904 return 0;
31905 }else{
31906 /* Integer comparison */
31907 if( pObj1->x.iVal > pObj2->x.iVal ){
31908 return 1;
31909 }else if( pObj1->x.iVal < pObj2->x.iVal ){
31910 return -1;
31911 }
31912 return 0;
31913 }
31914 }
31915 /* NOT REACHED */
31916 SXUNUSED(iNest);
31917 return 0;
31918}
31919/*
31920 * Perform an addition operation of two jx9_values.
31921 * The reason this function is implemented here rather than 'vm.c'
31922 * is that the '+' operator is overloaded.
31923 * That is, the '+' operator is used for arithmetic operation and also
31924 * used for operation on arrays [i.e: union]. When used with an array
31925 * The + operator returns the right-hand array appended to the left-hand array.
31926 * For keys that exist in both arrays, the elements from the left-hand array
31927 * will be used, and the matching elements from the right-hand array will
31928 * be ignored.
31929 * This function take care of handling all the scenarios.
31930 */
31931JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore)
31932{
31933 if( ((pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP) == 0 ){
31934 /* Arithemtic operation */
31935 jx9MemObjToNumeric(pObj1);
31936 jx9MemObjToNumeric(pObj2);
31937 if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_REAL ){
31938 /* Floating point arithmetic */
31939 jx9_real a, b;
31940 if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
31941 jx9MemObjToReal(pObj1);
31942 }
31943 if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
31944 jx9MemObjToReal(pObj2);
31945 }
31946 a = pObj1->x.rVal;
31947 b = pObj2->x.rVal;
31948 pObj1->x.rVal = a+b;
31949 MemObjSetType(pObj1, MEMOBJ_REAL);
31950 /* Try to get an integer representation also */
31951 MemObjTryIntger(&(*pObj1));
31952 }else{
31953 /* Integer arithmetic */
31954 sxi64 a, b;
31955 a = pObj1->x.iVal;
31956 b = pObj2->x.iVal;
31957 pObj1->x.iVal = a+b;
31958 MemObjSetType(pObj1, MEMOBJ_INT);
31959 }
31960 }else{
31961 if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP ){
31962 jx9_hashmap *pMap;
31963 sxi32 rc;
31964 if( bAddStore ){
31965 /* Do not duplicate the hashmap, use the left one since its an add&store operation.
31966 */
31967 if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){
31968 /* Force a hashmap cast */
31969 rc = jx9MemObjToHashmap(pObj1);
31970 if( rc != SXRET_OK ){
31971 jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array");
31972 return rc;
31973 }
31974 }
31975 /* Point to the structure that describe the hashmap */
31976 pMap = (jx9_hashmap *)pObj1->x.pOther;
31977 }else{
31978 /* Create a new hashmap */
31979 pMap = jx9NewHashmap(pObj1->pVm, 0, 0);
31980 if( pMap == 0){
31981 jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array");
31982 return SXERR_MEM;
31983 }
31984 }
31985 if( !bAddStore ){
31986 if(pObj1->iFlags & MEMOBJ_HASHMAP ){
31987 /* Perform a hashmap duplication */
31988 jx9HashmapDup((jx9_hashmap *)pObj1->x.pOther, pMap);
31989 }else{
31990 if((pObj1->iFlags & MEMOBJ_NULL) == 0 ){
31991 /* Simple insertion */
31992 jx9HashmapInsert(pMap, 0, pObj1);
31993 }
31994 }
31995 }
31996 /* Perform the union */
31997 if(pObj2->iFlags & MEMOBJ_HASHMAP ){
31998 jx9HashmapUnion(pMap, (jx9_hashmap *)pObj2->x.pOther);
31999 }else{
32000 if((pObj2->iFlags & MEMOBJ_NULL) == 0 ){
32001 /* Simple insertion */
32002 jx9HashmapInsert(pMap, 0, pObj2);
32003 }
32004 }
32005 /* Reflect the change */
32006 if( pObj1->iFlags & MEMOBJ_STRING ){
32007 SyBlobRelease(&pObj1->sBlob);
32008 }
32009 pObj1->x.pOther = pMap;
32010 MemObjSetType(pObj1, MEMOBJ_HASHMAP);
32011 }
32012 }
32013 return SXRET_OK;
32014}
32015/*
32016 * Return a printable representation of the type of a given
32017 * jx9_value.
32018 */
32019JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal)
32020{
32021 const char *zType = "";
32022 if( pVal->iFlags & MEMOBJ_NULL ){
32023 zType = "null";
32024 }else if( pVal->iFlags & MEMOBJ_INT ){
32025 zType = "int";
32026 }else if( pVal->iFlags & MEMOBJ_REAL ){
32027 zType = "float";
32028 }else if( pVal->iFlags & MEMOBJ_STRING ){
32029 zType = "string";
32030 }else if( pVal->iFlags & MEMOBJ_BOOL ){
32031 zType = "bool";
32032 }else if( pVal->iFlags & MEMOBJ_HASHMAP ){
32033 jx9_hashmap *pMap = (jx9_hashmap *)pVal->x.pOther;
32034 if( pMap->iFlags & HASHMAP_JSON_OBJECT ){
32035 zType = "JSON Object";
32036 }else{
32037 zType = "JSON Array";
32038 }
32039 }else if( pVal->iFlags & MEMOBJ_RES ){
32040 zType = "resource";
32041 }
32042 return zType;
32043}
32044/*
32045 * Dump a jx9_value [i.e: get a printable representation of it's type and contents.].
32046 * Store the dump in the given blob.
32047 */
32048JX9_PRIVATE sxi32 jx9MemObjDump(
32049 SyBlob *pOut, /* Store the dump here */
32050 jx9_value *pObj /* Dump this */
32051 )
32052{
32053 sxi32 rc = SXRET_OK;
32054 const char *zType;
32055 /* Get value type first */
32056 zType = jx9MemObjTypeDump(pObj);
32057 SyBlobAppend(&(*pOut), zType, SyStrlen(zType));
32058 if((pObj->iFlags & MEMOBJ_NULL) == 0 ){
32059 SyBlobAppend(&(*pOut), "(", sizeof(char));
32060 if( pObj->iFlags & MEMOBJ_HASHMAP ){
32061 jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
32062 SyBlobFormat(pOut,"%u ",pMap->nEntry);
32063 /* Dump hashmap entries */
32064 rc = jx9JsonSerialize(pObj,pOut);
32065 }else{
32066 SyBlob *pContents = &pObj->sBlob;
32067 /* Get a printable representation of the contents */
32068 if((pObj->iFlags & MEMOBJ_STRING) == 0 ){
32069 MemObjStringValue(&(*pOut), &(*pObj));
32070 }else{
32071 /* Append length first */
32072 SyBlobFormat(&(*pOut), "%u '", SyBlobLength(&pObj->sBlob));
32073 if( SyBlobLength(pContents) > 0 ){
32074 SyBlobAppend(&(*pOut), SyBlobData(pContents), SyBlobLength(pContents));
32075 }
32076 SyBlobAppend(&(*pOut), "'", sizeof(char));
32077 }
32078 }
32079 SyBlobAppend(&(*pOut), ")", sizeof(char));
32080 }
32081#ifdef __WINNT__
32082 SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n")-1);
32083#else
32084 SyBlobAppend(&(*pOut), "\n", sizeof(char));
32085#endif
32086 return rc;
32087}
32088/*
32089 * ----------------------------------------------------------
32090 * File: jx9_parse.c
32091 * MD5: d8fcac4c6cd7672f0103c0bf4a4b61fc
32092 * ----------------------------------------------------------
32093 */
32094/*
32095 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
32096 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
32097 * Version 1.7.2
32098 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
32099 * please contact Symisc Systems via:
32100 * legal@symisc.net
32101 * licensing@symisc.net
32102 * contact@symisc.net
32103 * or visit:
32104 * http://jx9.symisc.net/
32105 */
32106 /* $SymiscID: parse.c v1.2 FreeBSD 2012-12-11 00:46 stable <chm@symisc.net> $ */
32107#ifndef JX9_AMALGAMATION
32108#include "jx9Int.h"
32109#endif
32110/* Expression parser for the Jx9 programming language */
32111/* Operators associativity */
32112#define EXPR_OP_ASSOC_LEFT 0x01 /* Left associative operator */
32113#define EXPR_OP_ASSOC_RIGHT 0x02 /* Right associative operator */
32114#define EXPR_OP_NON_ASSOC 0x04 /* Non-associative operator */
32115/*
32116 * Operators table
32117 * This table is sorted by operators priority (highest to lowest) according
32118 * the JX9 language reference manual.
32119 * JX9 implements all the 60 JX9 operators and have introduced the eq and ne operators.
32120 * The operators precedence table have been improved dramatically so that you can do same
32121 * amazing things now such as array dereferencing, on the fly function call, anonymous function
32122 * as array values, object member access on instantiation and so on.
32123 * Refer to the following page for a full discussion on these improvements:
32124 * http://jx9.symisc.net/features.html
32125 */
32126static const jx9_expr_op aOpTable[] = {
32127 /* Postfix operators */
32128 /* Precedence 2(Highest), left-associative */
32129 { {".", sizeof(char)}, EXPR_OP_DOT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_MEMBER },
32130 { {"[", sizeof(char)}, EXPR_OP_SUBSCRIPT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_LOAD_IDX},
32131 /* Precedence 3, non-associative */
32132 { {"++", sizeof(char)*2}, EXPR_OP_INCR, 3, EXPR_OP_NON_ASSOC , JX9_OP_INCR},
32133 { {"--", sizeof(char)*2}, EXPR_OP_DECR, 3, EXPR_OP_NON_ASSOC , JX9_OP_DECR},
32134 /* Unary operators */
32135 /* Precedence 4, right-associative */
32136 { {"-", sizeof(char)}, EXPR_OP_UMINUS, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UMINUS },
32137 { {"+", sizeof(char)}, EXPR_OP_UPLUS, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UPLUS },
32138 { {"~", sizeof(char)}, EXPR_OP_BITNOT, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_BITNOT },
32139 { {"!", sizeof(char)}, EXPR_OP_LOGNOT, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_LNOT },
32140 /* Cast operators */
32141 { {"(int)", sizeof("(int)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_INT },
32142 { {"(bool)", sizeof("(bool)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_BOOL },
32143 { {"(string)", sizeof("(string)")-1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_STR },
32144 { {"(float)", sizeof("(float)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_REAL },
32145 { {"(array)", sizeof("(array)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */
32146 { {"(object)", sizeof("(object)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */
32147 /* Binary operators */
32148 /* Precedence 7, left-associative */
32149 { {"*", sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MUL},
32150 { {"/", sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_DIV},
32151 { {"%", sizeof(char)}, EXPR_OP_MOD, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MOD},
32152 /* Precedence 8, left-associative */
32153 { {"+", sizeof(char)}, EXPR_OP_ADD, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_ADD},
32154 { {"-", sizeof(char)}, EXPR_OP_SUB, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_SUB},
32155 { {"..", sizeof(char)*2},EXPR_OP_DDOT, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_CAT},
32156 /* Precedence 9, left-associative */
32157 { {"<<", sizeof(char)*2}, EXPR_OP_SHL, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHL},
32158 { {">>", sizeof(char)*2}, EXPR_OP_SHR, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHR},
32159 /* Precedence 10, non-associative */
32160 { {"<", sizeof(char)}, EXPR_OP_LT, 10, EXPR_OP_NON_ASSOC, JX9_OP_LT},
32161 { {">", sizeof(char)}, EXPR_OP_GT, 10, EXPR_OP_NON_ASSOC, JX9_OP_GT},
32162 { {"<=", sizeof(char)*2}, EXPR_OP_LE, 10, EXPR_OP_NON_ASSOC, JX9_OP_LE},
32163 { {">=", sizeof(char)*2}, EXPR_OP_GE, 10, EXPR_OP_NON_ASSOC, JX9_OP_GE},
32164 { {"<>", sizeof(char)*2}, EXPR_OP_NE, 10, EXPR_OP_NON_ASSOC, JX9_OP_NEQ},
32165 /* Precedence 11, non-associative */
32166 { {"==", sizeof(char)*2}, EXPR_OP_EQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_EQ},
32167 { {"!=", sizeof(char)*2}, EXPR_OP_NE, 11, EXPR_OP_NON_ASSOC, JX9_OP_NEQ},
32168 { {"===", sizeof(char)*3}, EXPR_OP_TEQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_TEQ},
32169 { {"!==", sizeof(char)*3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, JX9_OP_TNE},
32170 /* Precedence 12, left-associative */
32171 { {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT, JX9_OP_BAND},
32172 /* Binary operators */
32173 /* Precedence 13, left-associative */
32174 { {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, JX9_OP_BXOR},
32175 /* Precedence 14, left-associative */
32176 { {"|", sizeof(char)}, EXPR_OP_BOR, 14, EXPR_OP_ASSOC_LEFT, JX9_OP_BOR},
32177 /* Precedence 15, left-associative */
32178 { {"&&", sizeof(char)*2}, EXPR_OP_LAND, 15, EXPR_OP_ASSOC_LEFT, JX9_OP_LAND},
32179 /* Precedence 16, left-associative */
32180 { {"||", sizeof(char)*2}, EXPR_OP_LOR, 16, EXPR_OP_ASSOC_LEFT, JX9_OP_LOR},
32181 /* Ternary operator */
32182 /* Precedence 17, left-associative */
32183 { {"?", sizeof(char)}, EXPR_OP_QUESTY, 17, EXPR_OP_ASSOC_LEFT, 0},
32184 /* Combined binary operators */
32185 /* Precedence 18, right-associative */
32186 { {"=", sizeof(char)}, EXPR_OP_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_STORE},
32187 { {"+=", sizeof(char)*2}, EXPR_OP_ADD_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_ADD_STORE },
32188 { {"-=", sizeof(char)*2}, EXPR_OP_SUB_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SUB_STORE },
32189 { {".=", sizeof(char)*2}, EXPR_OP_DOT_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_CAT_STORE },
32190 { {"*=", sizeof(char)*2}, EXPR_OP_MUL_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_MUL_STORE },
32191 { {"/=", sizeof(char)*2}, EXPR_OP_DIV_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_DIV_STORE },
32192 { {"%=", sizeof(char)*2}, EXPR_OP_MOD_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_MOD_STORE },
32193 { {"&=", sizeof(char)*2}, EXPR_OP_AND_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BAND_STORE },
32194 { {"|=", sizeof(char)*2}, EXPR_OP_OR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BOR_STORE },
32195 { {"^=", sizeof(char)*2}, EXPR_OP_XOR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BXOR_STORE },
32196 { {"<<=", sizeof(char)*3}, EXPR_OP_SHL_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SHL_STORE },
32197 { {">>=", sizeof(char)*3}, EXPR_OP_SHR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SHR_STORE },
32198 /* Precedence 22, left-associative [Lowest operator] */
32199 { {",", sizeof(char)}, EXPR_OP_COMMA, 22, EXPR_OP_ASSOC_LEFT, 0}, /* IMP-0139-COMMA: Symisc eXtension */
32200};
32201/* Function call operator need special handling */
32202static const jx9_expr_op sFCallOp = {{"(", sizeof(char)}, EXPR_OP_FUNC_CALL, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_CALL};
32203/*
32204 * Check if the given token is a potential operator or not.
32205 * This function is called by the lexer each time it extract a token that may
32206 * look like an operator.
32207 * Return a structure [i.e: jx9_expr_op instnace ] that describe the operator on success.
32208 * Otherwise NULL.
32209 * Note that the function take care of handling ambiguity [i.e: whether we are dealing with
32210 * a binary minus or unary minus.]
32211 */
32212JX9_PRIVATE const jx9_expr_op * jx9ExprExtractOperator(SyString *pStr, SyToken *pLast)
32213{
32214 sxu32 n = 0;
32215 sxi32 rc;
32216 /* Do a linear lookup on the operators table */
32217 for(;;){
32218 if( n >= SX_ARRAYSIZE(aOpTable) ){
32219 break;
32220 }
32221 rc = SyStringCmp(pStr, &aOpTable[n].sOp, SyMemcmp);
32222 if( rc == 0 ){
32223 if( aOpTable[n].sOp.nByte != sizeof(char) || (aOpTable[n].iOp != EXPR_OP_UMINUS && aOpTable[n].iOp != EXPR_OP_UPLUS) || pLast == 0 ){
32224 if( aOpTable[n].iOp == EXPR_OP_SUBSCRIPT && (pLast == 0 || (pLast->nType & (JX9_TK_ID|JX9_TK_CSB/*]*/|JX9_TK_RPAREN/*)*/)) == 0) ){
32225 /* JSON Array not subscripting, return NULL */
32226 return 0;
32227 }
32228 /* There is no ambiguity here, simply return the first operator seen */
32229 return &aOpTable[n];
32230 }
32231 /* Handle ambiguity */
32232 if( pLast->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_COLON/*:*/|JX9_TK_COMMA/*, '*/) ){
32233 /* Unary opertors have prcedence here over binary operators */
32234 return &aOpTable[n];
32235 }
32236 if( pLast->nType & JX9_TK_OP ){
32237 const jx9_expr_op *pOp = (const jx9_expr_op *)pLast->pUserData;
32238 /* Ticket 1433-31: Handle the '++', '--' operators case */
32239 if( pOp->iOp != EXPR_OP_INCR && pOp->iOp != EXPR_OP_DECR ){
32240 /* Unary opertors have prcedence here over binary operators */
32241 return &aOpTable[n];
32242 }
32243
32244 }
32245 }
32246 ++n; /* Next operator in the table */
32247 }
32248 /* No such operator */
32249 return 0;
32250}
32251/*
32252 * Delimit a set of token stream.
32253 * This function take care of handling the nesting level and stops when it hit
32254 * the end of the input or the ending token is found and the nesting level is zero.
32255 */
32256JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn,SyToken *pEnd,sxu32 nTokStart,sxu32 nTokEnd,SyToken **ppEnd)
32257{
32258 SyToken *pCur = pIn;
32259 sxi32 iNest = 1;
32260 for(;;){
32261 if( pCur >= pEnd ){
32262 break;
32263 }
32264 if( pCur->nType & nTokStart ){
32265 /* Increment nesting level */
32266 iNest++;
32267 }else if( pCur->nType & nTokEnd ){
32268 /* Decrement nesting level */
32269 iNest--;
32270 if( iNest <= 0 ){
32271 break;
32272 }
32273 }
32274 /* Advance cursor */
32275 pCur++;
32276 }
32277 /* Point to the end of the chunk */
32278 *ppEnd = pCur;
32279}
32280/*
32281 * Retrun TRUE if the given ID represent a language construct [i.e: print, print..]. FALSE otherwise.
32282 * Note on reserved keywords.
32283 * According to the JX9 language reference manual:
32284 * These words have special meaning in JX9. Some of them represent things which look like
32285 * functions, some look like constants, and so on--but they're not, really: they are language
32286 * constructs. You cannot use any of the following words as constants, object names, function
32287 * or method names. Using them as variable names is generally OK, but could lead to confusion.
32288 */
32289JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID)
32290{
32291 if( nKeyID == JX9_TKWRD_PRINT || nKeyID == JX9_TKWRD_EXIT || nKeyID == JX9_TKWRD_DIE
32292 || nKeyID == JX9_TKWRD_INCLUDE|| nKeyID == JX9_TKWRD_IMPORT ){
32293 return TRUE;
32294 }
32295 /* Not a language construct */
32296 return FALSE;
32297}
32298/*
32299 * Point to the next expression that should be evaluated shortly.
32300 * The cursor stops when it hit a comma ', ' or a semi-colon and the nesting
32301 * level is zero.
32302 */
32303JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart,SyToken *pEnd,SyToken **ppNext)
32304{
32305 SyToken *pCur = pStart;
32306 sxi32 iNest = 0;
32307 if( pCur >= pEnd || (pCur->nType & JX9_TK_SEMI/*';'*/) ){
32308 /* Last expression */
32309 return SXERR_EOF;
32310 }
32311 while( pCur < pEnd ){
32312 if( (pCur->nType & (JX9_TK_COMMA/*','*/|JX9_TK_SEMI/*';'*/)) && iNest <= 0){
32313 break;
32314 }
32315 if( pCur->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OSB/*'['*/|JX9_TK_OCB/*'{'*/) ){
32316 iNest++;
32317 }else if( pCur->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*']'*/|JX9_TK_CCB/*'}*/) ){
32318 iNest--;
32319 }
32320 pCur++;
32321 }
32322 *ppNext = pCur;
32323 return SXRET_OK;
32324}
32325/*
32326 * Collect and assemble tokens holding annonymous functions/closure body.
32327 * When errors, JX9 take care of generating the appropriate error message.
32328 * Note on annonymous functions.
32329 * According to the JX9 language reference manual:
32330 * Anonymous functions, also known as closures, allow the creation of functions
32331 * which have no specified name. They are most useful as the value of callback
32332 * parameters, but they have many other uses.
32333 * Closures may also inherit variables from the parent scope. Any such variables
32334 * must be declared in the function header. Inheriting variables from the parent
32335 * scope is not the same as using global variables. Global variables exist in the global scope
32336 * which is the same no matter what function is executing. The parent scope of a closure is the
32337 * function in which the closure was declared (not necessarily the function it was called from).
32338 *
32339 * Some example:
32340 * $greet = function($name)
32341 * {
32342 * printf("Hello %s\r\n", $name);
32343 * };
32344 * $greet('World');
32345 * $greet('JX9');
32346 *
32347 * $double = function($a) {
32348 * return $a * 2;
32349 * };
32350 * // This is our range of numbers
32351 * $numbers = range(1, 5);
32352 * // Use the Annonymous function as a callback here to
32353 * // double the size of each element in our
32354 * // range
32355 * $new_numbers = array_map($double, $numbers);
32356 * print implode(' ', $new_numbers);
32357 */
32358static sxi32 ExprAssembleAnnon(jx9_gen_state *pGen,SyToken **ppCur, SyToken *pEnd)
32359{
32360 SyToken *pIn = *ppCur;
32361 sxu32 nLine;
32362 sxi32 rc;
32363 /* Jump the 'function' keyword */
32364 nLine = pIn->nLine;
32365 pIn++;
32366 if( pIn < pEnd && (pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) ){
32367 pIn++;
32368 }
32369 if( pIn >= pEnd || (pIn->nType & JX9_TK_LPAREN) == 0 ){
32370 /* Syntax error */
32371 rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing opening parenthesis '(' while declaring annonymous function");
32372 if( rc != SXERR_ABORT ){
32373 rc = SXERR_SYNTAX;
32374 }
32375 goto Synchronize;
32376 }
32377 pIn++; /* Jump the leading parenthesis '(' */
32378 jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_LPAREN/*'('*/, JX9_TK_RPAREN/*')'*/, &pIn);
32379 if( pIn >= pEnd || &pIn[1] >= pEnd ){
32380 /* Syntax error */
32381 rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function");
32382 if( rc != SXERR_ABORT ){
32383 rc = SXERR_SYNTAX;
32384 }
32385 goto Synchronize;
32386 }
32387 pIn++; /* Jump the trailing parenthesis */
32388 if( pIn->nType & JX9_TK_OCB /*'{'*/ ){
32389 pIn++; /* Jump the leading curly '{' */
32390 jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_OCB/*'{'*/, JX9_TK_CCB/*'}'*/, &pIn);
32391 if( pIn < pEnd ){
32392 pIn++;
32393 }
32394 }else{
32395 /* Syntax error */
32396 rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function, missing '{'");
32397 if( rc == SXERR_ABORT ){
32398 return SXERR_ABORT;
32399 }
32400 }
32401 rc = SXRET_OK;
32402Synchronize:
32403 /* Synchronize pointers */
32404 *ppCur = pIn;
32405 return rc;
32406}
32407/*
32408 * Make sure we are dealing with a valid expression tree.
32409 * This function check for balanced parenthesis, braces, brackets and so on.
32410 * When errors, JX9 take care of generating the appropriate error message.
32411 * Return SXRET_OK on success. Any other return value indicates syntax error.
32412 */
32413static sxi32 ExprVerifyNodes(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nNode)
32414{
32415 sxi32 iParen, iSquare, iBraces;
32416 sxi32 i, rc;
32417
32418 if( nNode > 0 && apNode[0]->pOp && (apNode[0]->pOp->iOp == EXPR_OP_ADD || apNode[0]->pOp->iOp == EXPR_OP_SUB) ){
32419 /* Fix and mark as an unary not binary plus/minus operator */
32420 apNode[0]->pOp = jx9ExprExtractOperator(&apNode[0]->pStart->sData, 0);
32421 apNode[0]->pStart->pUserData = (void *)apNode[0]->pOp;
32422 }
32423 iParen = iSquare = iBraces = 0;
32424 for( i = 0 ; i < nNode ; ++i ){
32425 if( apNode[i]->pStart->nType & JX9_TK_LPAREN /*'('*/){
32426 if( i > 0 && ( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ||
32427 (apNode[i - 1]->pStart->nType & (JX9_TK_ID|JX9_TK_KEYWORD|JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*]*/))) ){
32428 /* Ticket 1433-033: Take care to ignore alpha-stream [i.e: or, xor] operators followed by an opening parenthesis */
32429 if( (apNode[i - 1]->pStart->nType & JX9_TK_OP) == 0 ){
32430 /* We are dealing with a postfix [i.e: function call] operator
32431 * not a simple left parenthesis. Mark the node.
32432 */
32433 apNode[i]->pStart->nType |= JX9_TK_OP;
32434 apNode[i]->pStart->pUserData = (void *)&sFCallOp; /* Function call operator */
32435 apNode[i]->pOp = &sFCallOp;
32436 }
32437 }
32438 iParen++;
32439 }else if( apNode[i]->pStart->nType & JX9_TK_RPAREN/*')*/){
32440 if( iParen <= 0 ){
32441 rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ')'");
32442 if( rc != SXERR_ABORT ){
32443 rc = SXERR_SYNTAX;
32444 }
32445 return rc;
32446 }
32447 iParen--;
32448 }else if( apNode[i]->pStart->nType & JX9_TK_OSB /*'['*/ && apNode[i]->xCode == 0 ){
32449 iSquare++;
32450 }else if (apNode[i]->pStart->nType & JX9_TK_CSB /*']'*/){
32451 if( iSquare <= 0 ){
32452 rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ']'");
32453 if( rc != SXERR_ABORT ){
32454 rc = SXERR_SYNTAX;
32455 }
32456 return rc;
32457 }
32458 iSquare--;
32459 }else if( apNode[i]->pStart->nType & JX9_TK_OCB /*'{'*/ && apNode[i]->xCode == 0 ){
32460 iBraces++;
32461 }else if (apNode[i]->pStart->nType & JX9_TK_CCB /*'}'*/){
32462 if( iBraces <= 0 ){
32463 rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token '}'");
32464 if( rc != SXERR_ABORT ){
32465 rc = SXERR_SYNTAX;
32466 }
32467 return rc;
32468 }
32469 iBraces--;
32470 }else if( apNode[i]->pStart->nType & JX9_TK_OP ){
32471 const jx9_expr_op *pOp = (const jx9_expr_op *)apNode[i]->pOp;
32472 if( i > 0 && (pOp->iOp == EXPR_OP_UMINUS || pOp->iOp == EXPR_OP_UPLUS)){
32473 if( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ){
32474 sxi32 iExprOp = EXPR_OP_SUB; /* Binary minus */
32475 sxu32 n = 0;
32476 if( pOp->iOp == EXPR_OP_UPLUS ){
32477 iExprOp = EXPR_OP_ADD; /* Binary plus */
32478 }
32479 /*
32480 * TICKET 1433-013: This is a fix around an obscure bug when the user uses
32481 * a variable name which is an alpha-stream operator [i.e: $and, $xor, $eq..].
32482 */
32483 while( n < SX_ARRAYSIZE(aOpTable) && aOpTable[n].iOp != iExprOp ){
32484 ++n;
32485 }
32486 pOp = &aOpTable[n];
32487 /* Mark as binary '+' or '-', not an unary */
32488 apNode[i]->pOp = pOp;
32489 apNode[i]->pStart->pUserData = (void *)pOp;
32490 }
32491 }
32492 }
32493 }
32494 if( iParen != 0 || iSquare != 0 || iBraces != 0){
32495 rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[0]->pStart->nLine, "Syntax error, mismatched '(', '[' or '{'");
32496 if( rc != SXERR_ABORT ){
32497 rc = SXERR_SYNTAX;
32498 }
32499 return rc;
32500 }
32501 return SXRET_OK;
32502}
32503/*
32504 * Extract a single expression node from the input.
32505 * On success store the freshly extractd node in ppNode.
32506 * When errors, JX9 take care of generating the appropriate error message.
32507 * An expression node can be a variable [i.e: $var], an operator [i.e: ++]
32508 * an annonymous function [i.e: function(){ return "Hello"; }, a double/single
32509 * quoted string, a heredoc/nowdoc, a literal [i.e: JX9_EOL], a namespace path
32510 * [i.e: namespaces\path\to..], a array/list [i.e: array(4, 5, 6)] and so on.
32511 */
32512static sxi32 ExprExtractNode(jx9_gen_state *pGen, jx9_expr_node **ppNode)
32513{
32514 jx9_expr_node *pNode;
32515 SyToken *pCur;
32516 sxi32 rc;
32517 /* Allocate a new node */
32518 pNode = (jx9_expr_node *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_expr_node));
32519 if( pNode == 0 ){
32520 /* If the supplied memory subsystem is so sick that we are unable to allocate
32521 * a tiny chunk of memory, there is no much we can do here.
32522 */
32523 return SXERR_MEM;
32524 }
32525 /* Zero the structure */
32526 SyZero(pNode, sizeof(jx9_expr_node));
32527 SySetInit(&pNode->aNodeArgs, &pGen->pVm->sAllocator, sizeof(jx9_expr_node **));
32528 /* Point to the head of the token stream */
32529 pCur = pNode->pStart = pGen->pIn;
32530 /* Start collecting tokens */
32531 if( pCur->nType & JX9_TK_OP ){
32532 /* Point to the instance that describe this operator */
32533 pNode->pOp = (const jx9_expr_op *)pCur->pUserData;
32534 /* Advance the stream cursor */
32535 pCur++;
32536 }else if( pCur->nType & JX9_TK_DOLLAR ){
32537 /* Isolate variable */
32538 pCur++; /* Jump the dollar sign */
32539 if( pCur >= pGen->pEnd ){
32540 /* Syntax error */
32541 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"Invalid variable name");
32542 if( rc != SXERR_ABORT ){
32543 rc = SXERR_SYNTAX;
32544 }
32545 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32546 return rc;
32547 }
32548 pCur++; /* Jump the variable name */
32549 pNode->xCode = jx9CompileVariable;
32550 }else if( pCur->nType & JX9_TK_OCB /* '{' */ ){
32551 /* JSON Object, assemble tokens */
32552 pCur++;
32553 jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OCB /* '[' */, JX9_TK_CCB /* ']' */, &pCur);
32554 if( pCur < pGen->pEnd ){
32555 pCur++;
32556 }else{
32557 /* Syntax error */
32558 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Object: Missing closing braces '}'");
32559 if( rc != SXERR_ABORT ){
32560 rc = SXERR_SYNTAX;
32561 }
32562 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32563 return rc;
32564 }
32565 pNode->xCode = jx9CompileJsonObject;
32566 }else if( pCur->nType & JX9_TK_OSB /* '[' */ && !(pCur->nType & JX9_TK_OP) ){
32567 /* JSON Array, assemble tokens */
32568 pCur++;
32569 jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OSB /* '[' */, JX9_TK_CSB /* ']' */, &pCur);
32570 if( pCur < pGen->pEnd ){
32571 pCur++;
32572 }else{
32573 /* Syntax error */
32574 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Array: Missing closing square bracket ']'");
32575 if( rc != SXERR_ABORT ){
32576 rc = SXERR_SYNTAX;
32577 }
32578 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32579 return rc;
32580 }
32581 pNode->xCode = jx9CompileJsonArray;
32582 }else if( pCur->nType & JX9_TK_KEYWORD ){
32583 int nKeyword = SX_PTR_TO_INT(pCur->pUserData);
32584 if( nKeyword == JX9_TKWRD_FUNCTION ){
32585 /* Annonymous function */
32586 if( &pCur[1] >= pGen->pEnd ){
32587 /* Assume a literal */
32588 pCur++;
32589 pNode->xCode = jx9CompileLiteral;
32590 }else{
32591 /* Assemble annonymous functions body */
32592 rc = ExprAssembleAnnon(&(*pGen), &pCur, pGen->pEnd);
32593 if( rc != SXRET_OK ){
32594 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32595 return rc;
32596 }
32597 pNode->xCode = jx9CompileAnnonFunc;
32598 }
32599 }else if( jx9IsLangConstruct(nKeyword) && &pCur[1] < pGen->pEnd ){
32600 /* Language constructs [i.e: print,die...] require special handling */
32601 jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_LPAREN|JX9_TK_OCB|JX9_TK_OSB, JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB, &pCur);
32602 pNode->xCode = jx9CompileLangConstruct;
32603 }else{
32604 /* Assume a literal */
32605 pCur++;
32606 pNode->xCode = jx9CompileLiteral;
32607 }
32608 }else if( pCur->nType & (JX9_TK_ID) ){
32609 /* Constants, function name, namespace path, object name... */
32610 pCur++;
32611 pNode->xCode = jx9CompileLiteral;
32612 }else{
32613 if( (pCur->nType & (JX9_TK_LPAREN|JX9_TK_RPAREN|JX9_TK_COMMA|JX9_TK_CSB|JX9_TK_OCB|JX9_TK_CCB|JX9_TK_COLON)) == 0 ){
32614 /* Point to the code generator routine */
32615 pNode->xCode = jx9GetNodeHandler(pCur->nType);
32616 if( pNode->xCode == 0 ){
32617 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Unexpected token '%z'", &pNode->pStart->sData);
32618 if( rc != SXERR_ABORT ){
32619 rc = SXERR_SYNTAX;
32620 }
32621 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32622 return rc;
32623 }
32624 }
32625 /* Advance the stream cursor */
32626 pCur++;
32627 }
32628 /* Point to the end of the token stream */
32629 pNode->pEnd = pCur;
32630 /* Save the node for later processing */
32631 *ppNode = pNode;
32632 /* Synchronize cursors */
32633 pGen->pIn = pCur;
32634 return SXRET_OK;
32635}
32636/*
32637 * Free an expression tree.
32638 */
32639static void ExprFreeTree(jx9_gen_state *pGen, jx9_expr_node *pNode)
32640{
32641 if( pNode->pLeft ){
32642 /* Release the left tree */
32643 ExprFreeTree(&(*pGen), pNode->pLeft);
32644 }
32645 if( pNode->pRight ){
32646 /* Release the right tree */
32647 ExprFreeTree(&(*pGen), pNode->pRight);
32648 }
32649 if( pNode->pCond ){
32650 /* Release the conditional tree used by the ternary operator */
32651 ExprFreeTree(&(*pGen), pNode->pCond);
32652 }
32653 if( SySetUsed(&pNode->aNodeArgs) > 0 ){
32654 jx9_expr_node **apArg;
32655 sxu32 n;
32656 /* Release node arguments */
32657 apArg = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
32658 for( n = 0 ; n < SySetUsed(&pNode->aNodeArgs) ; ++n ){
32659 ExprFreeTree(&(*pGen), apArg[n]);
32660 }
32661 SySetRelease(&pNode->aNodeArgs);
32662 }
32663 /* Finally, release this node */
32664 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32665}
32666/*
32667 * Free an expression tree.
32668 * This function is a wrapper around ExprFreeTree() defined above.
32669 */
32670JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet)
32671{
32672 jx9_expr_node **apNode;
32673 sxu32 n;
32674 apNode = (jx9_expr_node **)SySetBasePtr(pNodeSet);
32675 for( n = 0 ; n < SySetUsed(pNodeSet) ; ++n ){
32676 if( apNode[n] ){
32677 ExprFreeTree(&(*pGen), apNode[n]);
32678 }
32679 }
32680 return SXRET_OK;
32681}
32682/*
32683 * Check if the given node is a modifialbe l/r-value.
32684 * Return TRUE if modifiable.FALSE otherwise.
32685 */
32686static int ExprIsModifiableValue(jx9_expr_node *pNode)
32687{
32688 sxi32 iExprOp;
32689 if( pNode->pOp == 0 ){
32690 return pNode->xCode == jx9CompileVariable ? TRUE : FALSE;
32691 }
32692 iExprOp = pNode->pOp->iOp;
32693 if( iExprOp == EXPR_OP_DOT /*'.' */ ){
32694 return TRUE;
32695 }
32696 if( iExprOp == EXPR_OP_SUBSCRIPT/*'[]'*/ ){
32697 if( pNode->pLeft->pOp ) {
32698 if( pNode->pLeft->pOp->iOp != EXPR_OP_SUBSCRIPT /*'['*/ && pNode->pLeft->pOp->iOp != EXPR_OP_DOT /*'.'*/){
32699 return FALSE;
32700 }
32701 }else if( pNode->pLeft->xCode != jx9CompileVariable ){
32702 return FALSE;
32703 }
32704 return TRUE;
32705 }
32706 /* Not a modifiable l or r-value */
32707 return FALSE;
32708}
32709/* Forward declaration */
32710static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken);
32711/* Macro to check if the given node is a terminal */
32712#define NODE_ISTERM(NODE) (apNode[NODE] && (!apNode[NODE]->pOp || apNode[NODE]->pLeft ))
32713/*
32714 * Buid an expression tree for each given function argument.
32715 * When errors, JX9 take care of generating the appropriate error message.
32716 */
32717static sxi32 ExprProcessFuncArguments(jx9_gen_state *pGen, jx9_expr_node *pOp, jx9_expr_node **apNode, sxi32 nToken)
32718{
32719 sxi32 iNest, iCur, iNode;
32720 sxi32 rc;
32721 /* Process function arguments from left to right */
32722 iCur = 0;
32723 for(;;){
32724 if( iCur >= nToken ){
32725 /* No more arguments to process */
32726 break;
32727 }
32728 iNode = iCur;
32729 iNest = 0;
32730 while( iCur < nToken ){
32731 if( apNode[iCur] ){
32732 if( (apNode[iCur]->pStart->nType & JX9_TK_COMMA) && apNode[iCur]->pLeft == 0 && iNest <= 0 ){
32733 break;
32734 }else if( apNode[iCur]->pStart->nType & (JX9_TK_LPAREN|JX9_TK_OSB|JX9_TK_OCB) ){
32735 iNest++;
32736 }else if( apNode[iCur]->pStart->nType & (JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB) ){
32737 iNest--;
32738 }
32739 }
32740 iCur++;
32741 }
32742 if( iCur > iNode ){
32743 ExprMakeTree(&(*pGen), &apNode[iNode], iCur-iNode);
32744 if( apNode[iNode] ){
32745 /* Put a pointer to the root of the tree in the arguments set */
32746 SySetPut(&pOp->aNodeArgs, (const void *)&apNode[iNode]);
32747 }else{
32748 /* Empty function argument */
32749 rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Empty function argument");
32750 if( rc != SXERR_ABORT ){
32751 rc = SXERR_SYNTAX;
32752 }
32753 return rc;
32754 }
32755 }else{
32756 rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
32757 if( rc != SXERR_ABORT ){
32758 rc = SXERR_SYNTAX;
32759 }
32760 return rc;
32761 }
32762 /* Jump trailing comma */
32763 if( iCur < nToken && apNode[iCur] && (apNode[iCur]->pStart->nType & JX9_TK_COMMA) ){
32764 iCur++;
32765 if( iCur >= nToken ){
32766 /* missing function argument */
32767 rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
32768 if( rc != SXERR_ABORT ){
32769 rc = SXERR_SYNTAX;
32770 }
32771 return rc;
32772 }
32773 }
32774 }
32775 return SXRET_OK;
32776}
32777/*
32778 * Create an expression tree from an array of tokens.
32779 * If successful, the root of the tree is stored in apNode[0].
32780 * When errors, JX9 take care of generating the appropriate error message.
32781 */
32782 static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken)
32783 {
32784 sxi32 i, iLeft, iRight;
32785 jx9_expr_node *pNode;
32786 sxi32 iCur;
32787 sxi32 rc;
32788 if( nToken <= 0 || (nToken == 1 && apNode[0]->xCode) ){
32789 /* TICKET 1433-17: self evaluating node */
32790 return SXRET_OK;
32791 }
32792 /* Process expressions enclosed in parenthesis first */
32793 for( iCur = 0 ; iCur < nToken ; ++iCur ){
32794 sxi32 iNest;
32795 /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator
32796 * since the LPAREN token can also be an operator [i.e: Function call].
32797 */
32798 if( apNode[iCur] == 0 || apNode[iCur]->pStart->nType != JX9_TK_LPAREN ){
32799 continue;
32800 }
32801 iNest = 1;
32802 iLeft = iCur;
32803 /* Find the closing parenthesis */
32804 iCur++;
32805 while( iCur < nToken ){
32806 if( apNode[iCur] ){
32807 if( apNode[iCur]->pStart->nType & JX9_TK_RPAREN /* ')' */){
32808 /* Decrement nesting level */
32809 iNest--;
32810 if( iNest <= 0 ){
32811 break;
32812 }
32813 }else if( apNode[iCur]->pStart->nType & JX9_TK_LPAREN /* '(' */ ){
32814 /* Increment nesting level */
32815 iNest++;
32816 }
32817 }
32818 iCur++;
32819 }
32820 if( iCur - iLeft > 1 ){
32821 /* Recurse and process this expression */
32822 rc = ExprMakeTree(&(*pGen), &apNode[iLeft + 1], iCur - iLeft - 1);
32823 if( rc != SXRET_OK ){
32824 return rc;
32825 }
32826 }
32827 /* Free the left and right nodes */
32828 ExprFreeTree(&(*pGen), apNode[iLeft]);
32829 ExprFreeTree(&(*pGen), apNode[iCur]);
32830 apNode[iLeft] = 0;
32831 apNode[iCur] = 0;
32832 }
32833 /* Handle postfix [i.e: function call, member access] operators with precedence 2 */
32834 iLeft = -1;
32835 for( iCur = 0 ; iCur < nToken ; ++iCur ){
32836 if( apNode[iCur] == 0 ){
32837 continue;
32838 }
32839 pNode = apNode[iCur];
32840 if( pNode->pOp && pNode->pOp->iPrec == 2 && pNode->pLeft == 0 ){
32841 if( pNode->pOp->iOp == EXPR_OP_FUNC_CALL ){
32842 /* Collect function arguments */
32843 sxi32 iPtr = 0;
32844 sxi32 nFuncTok = 0;
32845 while( nFuncTok + iCur < nToken ){
32846 if( apNode[nFuncTok+iCur] ){
32847 if( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_LPAREN /*'('*/ ){
32848 iPtr++;
32849 }else if ( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_RPAREN /*')'*/){
32850 iPtr--;
32851 if( iPtr <= 0 ){
32852 break;
32853 }
32854 }
32855 }
32856 nFuncTok++;
32857 }
32858 if( nFuncTok + iCur >= nToken ){
32859 /* Syntax error */
32860 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Missing right parenthesis ')'");
32861 if( rc != SXERR_ABORT ){
32862 rc = SXERR_SYNTAX;
32863 }
32864 return rc;
32865 }
32866 if( iLeft < 0 || !NODE_ISTERM(iLeft) /*|| ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2)*/ ){
32867 /* Syntax error */
32868 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Invalid function name");
32869 if( rc != SXERR_ABORT ){
32870 rc = SXERR_SYNTAX;
32871 }
32872 return rc;
32873 }
32874 if( nFuncTok > 1 ){
32875 /* Process function arguments */
32876 rc = ExprProcessFuncArguments(&(*pGen), pNode, &apNode[iCur+1], nFuncTok-1);
32877 if( rc != SXRET_OK ){
32878 return rc;
32879 }
32880 }
32881 /* Link the node to the tree */
32882 pNode->pLeft = apNode[iLeft];
32883 apNode[iLeft] = 0;
32884 for( iPtr = 1; iPtr <= nFuncTok ; iPtr++ ){
32885 apNode[iCur+iPtr] = 0;
32886 }
32887 }else if (pNode->pOp->iOp == EXPR_OP_SUBSCRIPT ){
32888 /* Subscripting */
32889 sxi32 iArrTok = iCur + 1;
32890 sxi32 iNest = 1;
32891 if( iLeft >= 0 && (apNode[iLeft]->xCode == jx9CompileVariable || (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* postfix */) ) ){
32892 /* Collect index tokens */
32893 while( iArrTok < nToken ){
32894 if( apNode[iArrTok] ){
32895 if( apNode[iArrTok]->pStart->nType & JX9_TK_OSB /*'['*/){
32896 /* Increment nesting level */
32897 iNest++;
32898 }else if( apNode[iArrTok]->pStart->nType & JX9_TK_CSB /*']'*/){
32899 /* Decrement nesting level */
32900 iNest--;
32901 if( iNest <= 0 ){
32902 break;
32903 }
32904 }
32905 }
32906 ++iArrTok;
32907 }
32908 if( iArrTok > iCur + 1 ){
32909 /* Recurse and process this expression */
32910 rc = ExprMakeTree(&(*pGen), &apNode[iCur+1], iArrTok - iCur - 1);
32911 if( rc != SXRET_OK ){
32912 return rc;
32913 }
32914 /* Link the node to it's index */
32915 SySetPut(&pNode->aNodeArgs, (const void *)&apNode[iCur+1]);
32916 }
32917 /* Link the node to the tree */
32918 pNode->pLeft = apNode[iLeft];
32919 pNode->pRight = 0;
32920 apNode[iLeft] = 0;
32921 for( iNest = iCur + 1 ; iNest <= iArrTok ; ++iNest ){
32922 apNode[iNest] = 0;
32923 }
32924 }
32925 }else{
32926 /* Member access operators [i.e: '.' ] */
32927 iRight = iCur + 1;
32928 while( iRight < nToken && apNode[iRight] == 0 ){
32929 iRight++;
32930 }
32931 if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
32932 /* Syntax error */
32933 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid member name", &pNode->pOp->sOp);
32934 if( rc != SXERR_ABORT ){
32935 rc = SXERR_SYNTAX;
32936 }
32937 return rc;
32938 }
32939 /* Link the node to the tree */
32940 pNode->pLeft = apNode[iLeft];
32941 if( pNode->pLeft->pOp == 0 && pNode->pLeft->xCode != jx9CompileVariable ){
32942 /* Syntax error */
32943 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
32944 "'%z': Expecting a variable as left operand", &pNode->pOp->sOp);
32945 if( rc != SXERR_ABORT ){
32946 rc = SXERR_SYNTAX;
32947 }
32948 return rc;
32949 }
32950 pNode->pRight = apNode[iRight];
32951 apNode[iLeft] = apNode[iRight] = 0;
32952 }
32953 }
32954 iLeft = iCur;
32955 }
32956 /* Handle post/pre icrement/decrement [i.e: ++/--] operators with precedence 3 */
32957 iLeft = -1;
32958 for( iCur = 0 ; iCur < nToken ; ++iCur ){
32959 if( apNode[iCur] == 0 ){
32960 continue;
32961 }
32962 pNode = apNode[iCur];
32963 if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){
32964 if( iLeft >= 0 && ((apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* Postfix */)
32965 || apNode[iLeft]->xCode == jx9CompileVariable) ){
32966 /* Link the node to the tree */
32967 pNode->pLeft = apNode[iLeft];
32968 apNode[iLeft] = 0;
32969 }
32970 }
32971 iLeft = iCur;
32972 }
32973 iLeft = -1;
32974 for( iCur = nToken - 1 ; iCur >= 0 ; iCur-- ){
32975 if( apNode[iCur] == 0 ){
32976 continue;
32977 }
32978 pNode = apNode[iCur];
32979 if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){
32980 if( iLeft < 0 || (apNode[iLeft]->pOp == 0 && apNode[iLeft]->xCode != jx9CompileVariable)
32981 || ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* Postfix */) ){
32982 /* Syntax error */
32983 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z' operator needs l-value", &pNode->pOp->sOp);
32984 if( rc != SXERR_ABORT ){
32985 rc = SXERR_SYNTAX;
32986 }
32987 return rc;
32988 }
32989 /* Link the node to the tree */
32990 pNode->pLeft = apNode[iLeft];
32991 apNode[iLeft] = 0;
32992 /* Mark as pre-increment/decrement node */
32993 pNode->iFlags |= EXPR_NODE_PRE_INCR;
32994 }
32995 iLeft = iCur;
32996 }
32997 /* Handle right associative unary and cast operators [i.e: !, (string), ~...] with precedence 4 */
32998 iLeft = 0;
32999 for( iCur = nToken - 1 ; iCur >= 0 ; iCur-- ){
33000 if( apNode[iCur] ){
33001 pNode = apNode[iCur];
33002 if( pNode->pOp && pNode->pOp->iPrec == 4 && pNode->pLeft == 0){
33003 if( iLeft > 0 ){
33004 /* Link the node to the tree */
33005 pNode->pLeft = apNode[iLeft];
33006 apNode[iLeft] = 0;
33007 if( pNode->pLeft && pNode->pLeft->pOp && pNode->pLeft->pOp->iPrec > 4 ){
33008 if( pNode->pLeft->pLeft == 0 || pNode->pLeft->pRight == 0 ){
33009 /* Syntax error */
33010 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pLeft->pStart->nLine, "'%z': Missing operand", &pNode->pLeft->pOp->sOp);
33011 if( rc != SXERR_ABORT ){
33012 rc = SXERR_SYNTAX;
33013 }
33014 return rc;
33015 }
33016 }
33017 }else{
33018 /* Syntax error */
33019 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing operand", &pNode->pOp->sOp);
33020 if( rc != SXERR_ABORT ){
33021 rc = SXERR_SYNTAX;
33022 }
33023 return rc;
33024 }
33025 }
33026 /* Save terminal position */
33027 iLeft = iCur;
33028 }
33029 }
33030 /* Process left and non-associative binary operators [i.e: *, /, &&, ||...]*/
33031 for( i = 7 ; i < 17 ; i++ ){
33032 iLeft = -1;
33033 for( iCur = 0 ; iCur < nToken ; ++iCur ){
33034 if( apNode[iCur] == 0 ){
33035 continue;
33036 }
33037 pNode = apNode[iCur];
33038 if( pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0 ){
33039 /* Get the right node */
33040 iRight = iCur + 1;
33041 while( iRight < nToken && apNode[iRight] == 0 ){
33042 iRight++;
33043 }
33044 if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
33045 /* Syntax error */
33046 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
33047 if( rc != SXERR_ABORT ){
33048 rc = SXERR_SYNTAX;
33049 }
33050 return rc;
33051 }
33052 /* Link the node to the tree */
33053 pNode->pLeft = apNode[iLeft];
33054 pNode->pRight = apNode[iRight];
33055 apNode[iLeft] = apNode[iRight] = 0;
33056 }
33057 iLeft = iCur;
33058 }
33059 }
33060 /* Handle the ternary operator. (expr1) ? (expr2) : (expr3)
33061 * Note that we do not need a precedence loop here since
33062 * we are dealing with a single operator.
33063 */
33064 iLeft = -1;
33065 for( iCur = 0 ; iCur < nToken ; ++iCur ){
33066 if( apNode[iCur] == 0 ){
33067 continue;
33068 }
33069 pNode = apNode[iCur];
33070 if( pNode->pOp && pNode->pOp->iOp == EXPR_OP_QUESTY && pNode->pLeft == 0 ){
33071 sxi32 iNest = 1;
33072 if( iLeft < 0 || !NODE_ISTERM(iLeft) ){
33073 /* Missing condition */
33074 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Syntax error", &pNode->pOp->sOp);
33075 if( rc != SXERR_ABORT ){
33076 rc = SXERR_SYNTAX;
33077 }
33078 return rc;
33079 }
33080 /* Get the right node */
33081 iRight = iCur + 1;
33082 while( iRight < nToken ){
33083 if( apNode[iRight] ){
33084 if( apNode[iRight]->pOp && apNode[iRight]->pOp->iOp == EXPR_OP_QUESTY && apNode[iRight]->pCond == 0){
33085 /* Increment nesting level */
33086 ++iNest;
33087 }else if( apNode[iRight]->pStart->nType & JX9_TK_COLON /*:*/ ){
33088 /* Decrement nesting level */
33089 --iNest;
33090 if( iNest <= 0 ){
33091 break;
33092 }
33093 }
33094 }
33095 iRight++;
33096 }
33097 if( iRight > iCur + 1 ){
33098 /* Recurse and process the then expression */
33099 rc = ExprMakeTree(&(*pGen), &apNode[iCur + 1], iRight - iCur - 1);
33100 if( rc != SXRET_OK ){
33101 return rc;
33102 }
33103 /* Link the node to the tree */
33104 pNode->pLeft = apNode[iCur + 1];
33105 }else{
33106 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'then' expression", &pNode->pOp->sOp);
33107 if( rc != SXERR_ABORT ){
33108 rc = SXERR_SYNTAX;
33109 }
33110 return rc;
33111 }
33112 apNode[iCur + 1] = 0;
33113 if( iRight + 1 < nToken ){
33114 /* Recurse and process the else expression */
33115 rc = ExprMakeTree(&(*pGen), &apNode[iRight + 1], nToken - iRight - 1);
33116 if( rc != SXRET_OK ){
33117 return rc;
33118 }
33119 /* Link the node to the tree */
33120 pNode->pRight = apNode[iRight + 1];
33121 apNode[iRight + 1] = apNode[iRight] = 0;
33122 }else{
33123 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'else' expression", &pNode->pOp->sOp);
33124 if( rc != SXERR_ABORT ){
33125 rc = SXERR_SYNTAX;
33126 }
33127 return rc;
33128 }
33129 /* Point to the condition */
33130 pNode->pCond = apNode[iLeft];
33131 apNode[iLeft] = 0;
33132 break;
33133 }
33134 iLeft = iCur;
33135 }
33136 /* Process right associative binary operators [i.e: '=', '+=', '/=']
33137 * Note: All right associative binary operators have precedence 18
33138 * so there is no need for a precedence loop here.
33139 */
33140 iRight = -1;
33141 for( iCur = nToken - 1 ; iCur >= 0 ; iCur--){
33142 if( apNode[iCur] == 0 ){
33143 continue;
33144 }
33145 pNode = apNode[iCur];
33146 if( pNode->pOp && pNode->pOp->iPrec == 18 && pNode->pLeft == 0 ){
33147 /* Get the left node */
33148 iLeft = iCur - 1;
33149 while( iLeft >= 0 && apNode[iLeft] == 0 ){
33150 iLeft--;
33151 }
33152 if( iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
33153 /* Syntax error */
33154 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
33155 if( rc != SXERR_ABORT ){
33156 rc = SXERR_SYNTAX;
33157 }
33158 return rc;
33159 }
33160 if( ExprIsModifiableValue(apNode[iLeft]) == FALSE ){
33161 if( pNode->pOp->iVmOp != JX9_OP_STORE ){
33162 /* Left operand must be a modifiable l-value */
33163 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
33164 "'%z': Left operand must be a modifiable l-value", &pNode->pOp->sOp);
33165 if( rc != SXERR_ABORT ){
33166 rc = SXERR_SYNTAX;
33167 }
33168 return rc;
33169 }
33170 }
33171 /* Link the node to the tree (Reverse) */
33172 pNode->pLeft = apNode[iRight];
33173 pNode->pRight = apNode[iLeft];
33174 apNode[iLeft] = apNode[iRight] = 0;
33175 }
33176 iRight = iCur;
33177 }
33178 /* Process the lowest precedence operator (22, comma) */
33179 iLeft = -1;
33180 for( iCur = 0 ; iCur < nToken ; ++iCur ){
33181 if( apNode[iCur] == 0 ){
33182 continue;
33183 }
33184 pNode = apNode[iCur];
33185 if( pNode->pOp && pNode->pOp->iPrec == 22 /* ',' */ && pNode->pLeft == 0 ){
33186 /* Get the right node */
33187 iRight = iCur + 1;
33188 while( iRight < nToken && apNode[iRight] == 0 ){
33189 iRight++;
33190 }
33191 if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
33192 /* Syntax error */
33193 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
33194 if( rc != SXERR_ABORT ){
33195 rc = SXERR_SYNTAX;
33196 }
33197 return rc;
33198 }
33199 /* Link the node to the tree */
33200 pNode->pLeft = apNode[iLeft];
33201 pNode->pRight = apNode[iRight];
33202 apNode[iLeft] = apNode[iRight] = 0;
33203 }
33204 iLeft = iCur;
33205 }
33206 /* Point to the root of the expression tree */
33207 for( iCur = 1 ; iCur < nToken ; ++iCur ){
33208 if( apNode[iCur] ){
33209 if( (apNode[iCur]->pOp || apNode[iCur]->xCode ) && apNode[0] != 0){
33210 rc = jx9GenCompileError(pGen, E_ERROR, apNode[iCur]->pStart->nLine, "Unexpected token '%z'", &apNode[iCur]->pStart->sData);
33211 if( rc != SXERR_ABORT ){
33212 rc = SXERR_SYNTAX;
33213 }
33214 return rc;
33215 }
33216 apNode[0] = apNode[iCur];
33217 apNode[iCur] = 0;
33218 }
33219 }
33220 return SXRET_OK;
33221 }
33222 /*
33223 * Build an expression tree from the freshly extracted raw tokens.
33224 * If successful, the root of the tree is stored in ppRoot.
33225 * When errors, JX9 take care of generating the appropriate error message.
33226 * This is the public interface used by the most code generator routines.
33227 */
33228JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot)
33229{
33230 jx9_expr_node **apNode;
33231 jx9_expr_node *pNode;
33232 sxi32 rc;
33233 /* Reset node container */
33234 SySetReset(pExprNode);
33235 pNode = 0; /* Prevent compiler warning */
33236 /* Extract nodes one after one until we hit the end of the input */
33237 while( pGen->pIn < pGen->pEnd ){
33238 rc = ExprExtractNode(&(*pGen), &pNode);
33239 if( rc != SXRET_OK ){
33240 return rc;
33241 }
33242 /* Save the extracted node */
33243 SySetPut(pExprNode, (const void *)&pNode);
33244 }
33245 if( SySetUsed(pExprNode) < 1 ){
33246 /* Empty expression [i.e: A semi-colon;] */
33247 *ppRoot = 0;
33248 return SXRET_OK;
33249 }
33250 apNode = (jx9_expr_node **)SySetBasePtr(pExprNode);
33251 /* Make sure we are dealing with valid nodes */
33252 rc = ExprVerifyNodes(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
33253 if( rc != SXRET_OK ){
33254 /* Don't worry about freeing memory, upper layer will
33255 * cleanup the mess left behind.
33256 */
33257 *ppRoot = 0;
33258 return rc;
33259 }
33260 /* Build the tree */
33261 rc = ExprMakeTree(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
33262 if( rc != SXRET_OK ){
33263 /* Something goes wrong [i.e: Syntax error] */
33264 *ppRoot = 0;
33265 return rc;
33266 }
33267 /* Point to the root of the tree */
33268 *ppRoot = apNode[0];
33269 return SXRET_OK;
33270}
33271/*
33272 * ----------------------------------------------------------
33273 * File: jx9_vfs.c
33274 * MD5: 8b73046a366acaf6aa7227c2133e16c0
33275 * ----------------------------------------------------------
33276 */
33277/*
33278 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
33279 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
33280 * Version 1.7.2
33281 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
33282 * please contact Symisc Systems via:
33283 * legal@symisc.net
33284 * licensing@symisc.net
33285 * contact@symisc.net
33286 * or visit:
33287 * http://jx9.symisc.net/
33288 */
33289 /* $SymiscID: vfs.c v2.1 Ubuntu 2012-12-13 00:013 stable <chm@symisc.net> $ */
33290#ifndef JX9_AMALGAMATION
33291#include "jx9Int.h"
33292#endif
33293/*
33294 * This file implement a virtual file systems (VFS) for the JX9 engine.
33295 */
33296/*
33297 * Given a string containing the path of a file or directory, this function
33298 * return the parent directory's path.
33299 */
33300JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen)
33301{
33302 const char *zEnd = &zPath[nByte - 1];
33303 int c, d;
33304 c = d = '/';
33305#ifdef __WINNT__
33306 d = '\\';
33307#endif
33308 while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
33309 zEnd--;
33310 }
33311 *pLen = (int)(zEnd-zPath);
33312#ifdef __WINNT__
33313 if( (*pLen) == (int)sizeof(char) && zPath[0] == '/' ){
33314 /* Normalize path on windows */
33315 return "\\";
33316 }
33317#endif
33318 if( zEnd == zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d) ){
33319 /* No separator, return "." as the current directory */
33320 *pLen = sizeof(char);
33321 return ".";
33322 }
33323 if( (*pLen) == 0 ){
33324 *pLen = sizeof(char);
33325#ifdef __WINNT__
33326 return "\\";
33327#else
33328 return "/";
33329#endif
33330 }
33331 return zPath;
33332}
33333/*
33334 * Omit the vfs layer implementation from the built if the JX9_DISABLE_BUILTIN_FUNC directive is defined.
33335 */
33336#ifndef JX9_DISABLE_BUILTIN_FUNC
33337/*
33338 * bool chdir(string $directory)
33339 * Change the current directory.
33340 * Parameters
33341 * $directory
33342 * The new current directory
33343 * Return
33344 * TRUE on success or FALSE on failure.
33345 */
33346static int jx9Vfs_chdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
33347{
33348 const char *zPath;
33349 jx9_vfs *pVfs;
33350 int rc;
33351 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33352 /* Missing/Invalid argument, return FALSE */
33353 jx9_result_bool(pCtx, 0);
33354 return JX9_OK;
33355 }
33356 /* Point to the underlying vfs */
33357 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33358 if( pVfs == 0 || pVfs->xChdir == 0 ){
33359 /* IO routine not implemented, return NULL */
33360 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33361 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33362 jx9_function_name(pCtx)
33363 );
33364 jx9_result_bool(pCtx, 0);
33365 return JX9_OK;
33366 }
33367 /* Point to the desired directory */
33368 zPath = jx9_value_to_string(apArg[0], 0);
33369 /* Perform the requested operation */
33370 rc = pVfs->xChdir(zPath);
33371 /* IO return value */
33372 jx9_result_bool(pCtx, rc == JX9_OK);
33373 return JX9_OK;
33374}
33375/*
33376 * bool chroot(string $directory)
33377 * Change the root directory.
33378 * Parameters
33379 * $directory
33380 * The path to change the root directory to
33381 * Return
33382 * TRUE on success or FALSE on failure.
33383 */
33384static int jx9Vfs_chroot(jx9_context *pCtx, int nArg, jx9_value **apArg)
33385{
33386 const char *zPath;
33387 jx9_vfs *pVfs;
33388 int rc;
33389 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33390 /* Missing/Invalid argument, return FALSE */
33391 jx9_result_bool(pCtx, 0);
33392 return JX9_OK;
33393 }
33394 /* Point to the underlying vfs */
33395 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33396 if( pVfs == 0 || pVfs->xChroot == 0 ){
33397 /* IO routine not implemented, return NULL */
33398 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33399 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33400 jx9_function_name(pCtx)
33401 );
33402 jx9_result_bool(pCtx, 0);
33403 return JX9_OK;
33404 }
33405 /* Point to the desired directory */
33406 zPath = jx9_value_to_string(apArg[0], 0);
33407 /* Perform the requested operation */
33408 rc = pVfs->xChroot(zPath);
33409 /* IO return value */
33410 jx9_result_bool(pCtx, rc == JX9_OK);
33411 return JX9_OK;
33412}
33413/*
33414 * string getcwd(void)
33415 * Gets the current working directory.
33416 * Parameters
33417 * None
33418 * Return
33419 * Returns the current working directory on success, or FALSE on failure.
33420 */
33421static int jx9Vfs_getcwd(jx9_context *pCtx, int nArg, jx9_value **apArg)
33422{
33423 jx9_vfs *pVfs;
33424 int rc;
33425 /* Point to the underlying vfs */
33426 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33427 if( pVfs == 0 || pVfs->xGetcwd == 0 ){
33428 SXUNUSED(nArg); /* cc warning */
33429 SXUNUSED(apArg);
33430 /* IO routine not implemented, return NULL */
33431 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33432 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33433 jx9_function_name(pCtx)
33434 );
33435 jx9_result_bool(pCtx, 0);
33436 return JX9_OK;
33437 }
33438 jx9_result_string(pCtx, "", 0);
33439 /* Perform the requested operation */
33440 rc = pVfs->xGetcwd(pCtx);
33441 if( rc != JX9_OK ){
33442 /* Error, return FALSE */
33443 jx9_result_bool(pCtx, 0);
33444 }
33445 return JX9_OK;
33446}
33447/*
33448 * bool rmdir(string $directory)
33449 * Removes directory.
33450 * Parameters
33451 * $directory
33452 * The path to the directory
33453 * Return
33454 * TRUE on success or FALSE on failure.
33455 */
33456static int jx9Vfs_rmdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
33457{
33458 const char *zPath;
33459 jx9_vfs *pVfs;
33460 int rc;
33461 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33462 /* Missing/Invalid argument, return FALSE */
33463 jx9_result_bool(pCtx, 0);
33464 return JX9_OK;
33465 }
33466 /* Point to the underlying vfs */
33467 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33468 if( pVfs == 0 || pVfs->xRmdir == 0 ){
33469 /* IO routine not implemented, return NULL */
33470 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33471 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33472 jx9_function_name(pCtx)
33473 );
33474 jx9_result_bool(pCtx, 0);
33475 return JX9_OK;
33476 }
33477 /* Point to the desired directory */
33478 zPath = jx9_value_to_string(apArg[0], 0);
33479 /* Perform the requested operation */
33480 rc = pVfs->xRmdir(zPath);
33481 /* IO return value */
33482 jx9_result_bool(pCtx, rc == JX9_OK);
33483 return JX9_OK;
33484}
33485/*
33486 * bool is_dir(string $filename)
33487 * Tells whether the given filename is a directory.
33488 * Parameters
33489 * $filename
33490 * Path to the file.
33491 * Return
33492 * TRUE on success or FALSE on failure.
33493 */
33494static int jx9Vfs_is_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
33495{
33496 const char *zPath;
33497 jx9_vfs *pVfs;
33498 int rc;
33499 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33500 /* Missing/Invalid argument, return FALSE */
33501 jx9_result_bool(pCtx, 0);
33502 return JX9_OK;
33503 }
33504 /* Point to the underlying vfs */
33505 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33506 if( pVfs == 0 || pVfs->xIsdir == 0 ){
33507 /* IO routine not implemented, return NULL */
33508 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33509 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33510 jx9_function_name(pCtx)
33511 );
33512 jx9_result_bool(pCtx, 0);
33513 return JX9_OK;
33514 }
33515 /* Point to the desired directory */
33516 zPath = jx9_value_to_string(apArg[0], 0);
33517 /* Perform the requested operation */
33518 rc = pVfs->xIsdir(zPath);
33519 /* IO return value */
33520 jx9_result_bool(pCtx, rc == JX9_OK);
33521 return JX9_OK;
33522}
33523/*
33524 * bool mkdir(string $pathname[, int $mode = 0777])
33525 * Make a directory.
33526 * Parameters
33527 * $pathname
33528 * The directory path.
33529 * $mode
33530 * The mode is 0777 by default, which means the widest possible access.
33531 * Note:
33532 * mode is ignored on Windows.
33533 * Note that you probably want to specify the mode as an octal number, which means
33534 * it should have a leading zero. The mode is also modified by the current umask
33535 * which you can change using umask().
33536 * Return
33537 * TRUE on success or FALSE on failure.
33538 */
33539static int jx9Vfs_mkdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
33540{
33541 int iRecursive = 0;
33542 const char *zPath;
33543 jx9_vfs *pVfs;
33544 int iMode, rc;
33545 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33546 /* Missing/Invalid argument, return FALSE */
33547 jx9_result_bool(pCtx, 0);
33548 return JX9_OK;
33549 }
33550 /* Point to the underlying vfs */
33551 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33552 if( pVfs == 0 || pVfs->xMkdir == 0 ){
33553 /* IO routine not implemented, return NULL */
33554 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33555 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33556 jx9_function_name(pCtx)
33557 );
33558 jx9_result_bool(pCtx, 0);
33559 return JX9_OK;
33560 }
33561 /* Point to the desired directory */
33562 zPath = jx9_value_to_string(apArg[0], 0);
33563#ifdef __WINNT__
33564 iMode = 0;
33565#else
33566 /* Assume UNIX */
33567 iMode = 0777;
33568#endif
33569 if( nArg > 1 ){
33570 iMode = jx9_value_to_int(apArg[1]);
33571 if( nArg > 2 ){
33572 iRecursive = jx9_value_to_bool(apArg[2]);
33573 }
33574 }
33575 /* Perform the requested operation */
33576 rc = pVfs->xMkdir(zPath, iMode, iRecursive);
33577 /* IO return value */
33578 jx9_result_bool(pCtx, rc == JX9_OK);
33579 return JX9_OK;
33580}
33581/*
33582 * bool rename(string $oldname, string $newname)
33583 * Attempts to rename oldname to newname.
33584 * Parameters
33585 * $oldname
33586 * Old name.
33587 * $newname
33588 * New name.
33589 * Return
33590 * TRUE on success or FALSE on failure.
33591 */
33592static int jx9Vfs_rename(jx9_context *pCtx, int nArg, jx9_value **apArg)
33593{
33594 const char *zOld, *zNew;
33595 jx9_vfs *pVfs;
33596 int rc;
33597 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
33598 /* Missing/Invalid arguments, return FALSE */
33599 jx9_result_bool(pCtx, 0);
33600 return JX9_OK;
33601 }
33602 /* Point to the underlying vfs */
33603 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33604 if( pVfs == 0 || pVfs->xRename == 0 ){
33605 /* IO routine not implemented, return NULL */
33606 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33607 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33608 jx9_function_name(pCtx)
33609 );
33610 jx9_result_bool(pCtx, 0);
33611 return JX9_OK;
33612 }
33613 /* Perform the requested operation */
33614 zOld = jx9_value_to_string(apArg[0], 0);
33615 zNew = jx9_value_to_string(apArg[1], 0);
33616 rc = pVfs->xRename(zOld, zNew);
33617 /* IO result */
33618 jx9_result_bool(pCtx, rc == JX9_OK );
33619 return JX9_OK;
33620}
33621/*
33622 * string realpath(string $path)
33623 * Returns canonicalized absolute pathname.
33624 * Parameters
33625 * $path
33626 * Target path.
33627 * Return
33628 * Canonicalized absolute pathname on success. or FALSE on failure.
33629 */
33630static int jx9Vfs_realpath(jx9_context *pCtx, int nArg, jx9_value **apArg)
33631{
33632 const char *zPath;
33633 jx9_vfs *pVfs;
33634 int rc;
33635 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33636 /* Missing/Invalid argument, return FALSE */
33637 jx9_result_bool(pCtx, 0);
33638 return JX9_OK;
33639 }
33640 /* Point to the underlying vfs */
33641 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33642 if( pVfs == 0 || pVfs->xRealpath == 0 ){
33643 /* IO routine not implemented, return NULL */
33644 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33645 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33646 jx9_function_name(pCtx)
33647 );
33648 jx9_result_bool(pCtx, 0);
33649 return JX9_OK;
33650 }
33651 /* Set an empty string untnil the underlying OS interface change that */
33652 jx9_result_string(pCtx, "", 0);
33653 /* Perform the requested operation */
33654 zPath = jx9_value_to_string(apArg[0], 0);
33655 rc = pVfs->xRealpath(zPath, pCtx);
33656 if( rc != JX9_OK ){
33657 jx9_result_bool(pCtx, 0);
33658 }
33659 return JX9_OK;
33660}
33661/*
33662 * int sleep(int $seconds)
33663 * Delays the program execution for the given number of seconds.
33664 * Parameters
33665 * $seconds
33666 * Halt time in seconds.
33667 * Return
33668 * Zero on success or FALSE on failure.
33669 */
33670static int jx9Vfs_sleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
33671{
33672 jx9_vfs *pVfs;
33673 int rc, nSleep;
33674 if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
33675 /* Missing/Invalid argument, return FALSE */
33676 jx9_result_bool(pCtx, 0);
33677 return JX9_OK;
33678 }
33679 /* Point to the underlying vfs */
33680 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33681 if( pVfs == 0 || pVfs->xSleep == 0 ){
33682 /* IO routine not implemented, return NULL */
33683 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33684 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33685 jx9_function_name(pCtx)
33686 );
33687 jx9_result_bool(pCtx, 0);
33688 return JX9_OK;
33689 }
33690 /* Amount to sleep */
33691 nSleep = jx9_value_to_int(apArg[0]);
33692 if( nSleep < 0 ){
33693 /* Invalid value, return FALSE */
33694 jx9_result_bool(pCtx, 0);
33695 return JX9_OK;
33696 }
33697 /* Perform the requested operation (Microseconds) */
33698 rc = pVfs->xSleep((unsigned int)(nSleep * SX_USEC_PER_SEC));
33699 if( rc != JX9_OK ){
33700 /* Return FALSE */
33701 jx9_result_bool(pCtx, 0);
33702 }else{
33703 /* Return zero */
33704 jx9_result_int(pCtx, 0);
33705 }
33706 return JX9_OK;
33707}
33708/*
33709 * void usleep(int $micro_seconds)
33710 * Delays program execution for the given number of micro seconds.
33711 * Parameters
33712 * $micro_seconds
33713 * Halt time in micro seconds. A micro second is one millionth of a second.
33714 * Return
33715 * None.
33716 */
33717static int jx9Vfs_usleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
33718{
33719 jx9_vfs *pVfs;
33720 int nSleep;
33721 if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
33722 /* Missing/Invalid argument, return immediately */
33723 return JX9_OK;
33724 }
33725 /* Point to the underlying vfs */
33726 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33727 if( pVfs == 0 || pVfs->xSleep == 0 ){
33728 /* IO routine not implemented, return NULL */
33729 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33730 "IO routine(%s) not implemented in the underlying VFS",
33731 jx9_function_name(pCtx)
33732 );
33733 return JX9_OK;
33734 }
33735 /* Amount to sleep */
33736 nSleep = jx9_value_to_int(apArg[0]);
33737 if( nSleep < 0 ){
33738 /* Invalid value, return immediately */
33739 return JX9_OK;
33740 }
33741 /* Perform the requested operation (Microseconds) */
33742 pVfs->xSleep((unsigned int)nSleep);
33743 return JX9_OK;
33744}
33745/*
33746 * bool unlink (string $filename)
33747 * Delete a file.
33748 * Parameters
33749 * $filename
33750 * Path to the file.
33751 * Return
33752 * TRUE on success or FALSE on failure.
33753 */
33754static int jx9Vfs_unlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
33755{
33756 const char *zPath;
33757 jx9_vfs *pVfs;
33758 int rc;
33759 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33760 /* Missing/Invalid argument, return FALSE */
33761 jx9_result_bool(pCtx, 0);
33762 return JX9_OK;
33763 }
33764 /* Point to the underlying vfs */
33765 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33766 if( pVfs == 0 || pVfs->xUnlink == 0 ){
33767 /* IO routine not implemented, return NULL */
33768 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33769 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33770 jx9_function_name(pCtx)
33771 );
33772 jx9_result_bool(pCtx, 0);
33773 return JX9_OK;
33774 }
33775 /* Point to the desired directory */
33776 zPath = jx9_value_to_string(apArg[0], 0);
33777 /* Perform the requested operation */
33778 rc = pVfs->xUnlink(zPath);
33779 /* IO return value */
33780 jx9_result_bool(pCtx, rc == JX9_OK);
33781 return JX9_OK;
33782}
33783/*
33784 * bool chmod(string $filename, int $mode)
33785 * Attempts to change the mode of the specified file to that given in mode.
33786 * Parameters
33787 * $filename
33788 * Path to the file.
33789 * $mode
33790 * Mode (Must be an integer)
33791 * Return
33792 * TRUE on success or FALSE on failure.
33793 */
33794static int jx9Vfs_chmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
33795{
33796 const char *zPath;
33797 jx9_vfs *pVfs;
33798 int iMode;
33799 int rc;
33800 if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
33801 /* Missing/Invalid argument, return FALSE */
33802 jx9_result_bool(pCtx, 0);
33803 return JX9_OK;
33804 }
33805 /* Point to the underlying vfs */
33806 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33807 if( pVfs == 0 || pVfs->xChmod == 0 ){
33808 /* IO routine not implemented, return NULL */
33809 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33810 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33811 jx9_function_name(pCtx)
33812 );
33813 jx9_result_bool(pCtx, 0);
33814 return JX9_OK;
33815 }
33816 /* Point to the desired directory */
33817 zPath = jx9_value_to_string(apArg[0], 0);
33818 /* Extract the mode */
33819 iMode = jx9_value_to_int(apArg[1]);
33820 /* Perform the requested operation */
33821 rc = pVfs->xChmod(zPath, iMode);
33822 /* IO return value */
33823 jx9_result_bool(pCtx, rc == JX9_OK);
33824 return JX9_OK;
33825}
33826/*
33827 * bool chown(string $filename, string $user)
33828 * Attempts to change the owner of the file filename to user user.
33829 * Parameters
33830 * $filename
33831 * Path to the file.
33832 * $user
33833 * Username.
33834 * Return
33835 * TRUE on success or FALSE on failure.
33836 */
33837static int jx9Vfs_chown(jx9_context *pCtx, int nArg, jx9_value **apArg)
33838{
33839 const char *zPath, *zUser;
33840 jx9_vfs *pVfs;
33841 int rc;
33842 if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
33843 /* Missing/Invalid arguments, return FALSE */
33844 jx9_result_bool(pCtx, 0);
33845 return JX9_OK;
33846 }
33847 /* Point to the underlying vfs */
33848 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33849 if( pVfs == 0 || pVfs->xChown == 0 ){
33850 /* IO routine not implemented, return NULL */
33851 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33852 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33853 jx9_function_name(pCtx)
33854 );
33855 jx9_result_bool(pCtx, 0);
33856 return JX9_OK;
33857 }
33858 /* Point to the desired directory */
33859 zPath = jx9_value_to_string(apArg[0], 0);
33860 /* Extract the user */
33861 zUser = jx9_value_to_string(apArg[1], 0);
33862 /* Perform the requested operation */
33863 rc = pVfs->xChown(zPath, zUser);
33864 /* IO return value */
33865 jx9_result_bool(pCtx, rc == JX9_OK);
33866 return JX9_OK;
33867}
33868/*
33869 * bool chgrp(string $filename, string $group)
33870 * Attempts to change the group of the file filename to group.
33871 * Parameters
33872 * $filename
33873 * Path to the file.
33874 * $group
33875 * groupname.
33876 * Return
33877 * TRUE on success or FALSE on failure.
33878 */
33879static int jx9Vfs_chgrp(jx9_context *pCtx, int nArg, jx9_value **apArg)
33880{
33881 const char *zPath, *zGroup;
33882 jx9_vfs *pVfs;
33883 int rc;
33884 if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
33885 /* Missing/Invalid arguments, return FALSE */
33886 jx9_result_bool(pCtx, 0);
33887 return JX9_OK;
33888 }
33889 /* Point to the underlying vfs */
33890 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33891 if( pVfs == 0 || pVfs->xChgrp == 0 ){
33892 /* IO routine not implemented, return NULL */
33893 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33894 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33895 jx9_function_name(pCtx)
33896 );
33897 jx9_result_bool(pCtx, 0);
33898 return JX9_OK;
33899 }
33900 /* Point to the desired directory */
33901 zPath = jx9_value_to_string(apArg[0], 0);
33902 /* Extract the user */
33903 zGroup = jx9_value_to_string(apArg[1], 0);
33904 /* Perform the requested operation */
33905 rc = pVfs->xChgrp(zPath, zGroup);
33906 /* IO return value */
33907 jx9_result_bool(pCtx, rc == JX9_OK);
33908 return JX9_OK;
33909}
33910/*
33911 * int64 disk_free_space(string $directory)
33912 * Returns available space on filesystem or disk partition.
33913 * Parameters
33914 * $directory
33915 * A directory of the filesystem or disk partition.
33916 * Return
33917 * Returns the number of available bytes as a 64-bit integer or FALSE on failure.
33918 */
33919static int jx9Vfs_disk_free_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
33920{
33921 const char *zPath;
33922 jx9_int64 iSize;
33923 jx9_vfs *pVfs;
33924 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33925 /* Missing/Invalid argument, return FALSE */
33926 jx9_result_bool(pCtx, 0);
33927 return JX9_OK;
33928 }
33929 /* Point to the underlying vfs */
33930 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33931 if( pVfs == 0 || pVfs->xFreeSpace == 0 ){
33932 /* IO routine not implemented, return NULL */
33933 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33934 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33935 jx9_function_name(pCtx)
33936 );
33937 jx9_result_bool(pCtx, 0);
33938 return JX9_OK;
33939 }
33940 /* Point to the desired directory */
33941 zPath = jx9_value_to_string(apArg[0], 0);
33942 /* Perform the requested operation */
33943 iSize = pVfs->xFreeSpace(zPath);
33944 /* IO return value */
33945 jx9_result_int64(pCtx, iSize);
33946 return JX9_OK;
33947}
33948/*
33949 * int64 disk_total_space(string $directory)
33950 * Returns the total size of a filesystem or disk partition.
33951 * Parameters
33952 * $directory
33953 * A directory of the filesystem or disk partition.
33954 * Return
33955 * Returns the number of available bytes as a 64-bit integer or FALSE on failure.
33956 */
33957static int jx9Vfs_disk_total_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
33958{
33959 const char *zPath;
33960 jx9_int64 iSize;
33961 jx9_vfs *pVfs;
33962 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33963 /* Missing/Invalid argument, return FALSE */
33964 jx9_result_bool(pCtx, 0);
33965 return JX9_OK;
33966 }
33967 /* Point to the underlying vfs */
33968 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33969 if( pVfs == 0 || pVfs->xTotalSpace == 0 ){
33970 /* IO routine not implemented, return NULL */
33971 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33972 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33973 jx9_function_name(pCtx)
33974 );
33975 jx9_result_bool(pCtx, 0);
33976 return JX9_OK;
33977 }
33978 /* Point to the desired directory */
33979 zPath = jx9_value_to_string(apArg[0], 0);
33980 /* Perform the requested operation */
33981 iSize = pVfs->xTotalSpace(zPath);
33982 /* IO return value */
33983 jx9_result_int64(pCtx, iSize);
33984 return JX9_OK;
33985}
33986/*
33987 * bool file_exists(string $filename)
33988 * Checks whether a file or directory exists.
33989 * Parameters
33990 * $filename
33991 * Path to the file.
33992 * Return
33993 * TRUE on success or FALSE on failure.
33994 */
33995static int jx9Vfs_file_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
33996{
33997 const char *zPath;
33998 jx9_vfs *pVfs;
33999 int rc;
34000 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34001 /* Missing/Invalid argument, return FALSE */
34002 jx9_result_bool(pCtx, 0);
34003 return JX9_OK;
34004 }
34005 /* Point to the underlying vfs */
34006 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34007 if( pVfs == 0 || pVfs->xFileExists == 0 ){
34008 /* IO routine not implemented, return NULL */
34009 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34010 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34011 jx9_function_name(pCtx)
34012 );
34013 jx9_result_bool(pCtx, 0);
34014 return JX9_OK;
34015 }
34016 /* Point to the desired directory */
34017 zPath = jx9_value_to_string(apArg[0], 0);
34018 /* Perform the requested operation */
34019 rc = pVfs->xFileExists(zPath);
34020 /* IO return value */
34021 jx9_result_bool(pCtx, rc == JX9_OK);
34022 return JX9_OK;
34023}
34024/*
34025 * int64 file_size(string $filename)
34026 * Gets the size for the given file.
34027 * Parameters
34028 * $filename
34029 * Path to the file.
34030 * Return
34031 * File size on success or FALSE on failure.
34032 */
34033static int jx9Vfs_file_size(jx9_context *pCtx, int nArg, jx9_value **apArg)
34034{
34035 const char *zPath;
34036 jx9_int64 iSize;
34037 jx9_vfs *pVfs;
34038 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34039 /* Missing/Invalid argument, return FALSE */
34040 jx9_result_bool(pCtx, 0);
34041 return JX9_OK;
34042 }
34043 /* Point to the underlying vfs */
34044 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34045 if( pVfs == 0 || pVfs->xFileSize == 0 ){
34046 /* IO routine not implemented, return NULL */
34047 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34048 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34049 jx9_function_name(pCtx)
34050 );
34051 jx9_result_bool(pCtx, 0);
34052 return JX9_OK;
34053 }
34054 /* Point to the desired directory */
34055 zPath = jx9_value_to_string(apArg[0], 0);
34056 /* Perform the requested operation */
34057 iSize = pVfs->xFileSize(zPath);
34058 /* IO return value */
34059 jx9_result_int64(pCtx, iSize);
34060 return JX9_OK;
34061}
34062/*
34063 * int64 fileatime(string $filename)
34064 * Gets the last access time of the given file.
34065 * Parameters
34066 * $filename
34067 * Path to the file.
34068 * Return
34069 * File atime on success or FALSE on failure.
34070 */
34071static int jx9Vfs_file_atime(jx9_context *pCtx, int nArg, jx9_value **apArg)
34072{
34073 const char *zPath;
34074 jx9_int64 iTime;
34075 jx9_vfs *pVfs;
34076 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34077 /* Missing/Invalid argument, return FALSE */
34078 jx9_result_bool(pCtx, 0);
34079 return JX9_OK;
34080 }
34081 /* Point to the underlying vfs */
34082 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34083 if( pVfs == 0 || pVfs->xFileAtime == 0 ){
34084 /* IO routine not implemented, return NULL */
34085 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34086 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34087 jx9_function_name(pCtx)
34088 );
34089 jx9_result_bool(pCtx, 0);
34090 return JX9_OK;
34091 }
34092 /* Point to the desired directory */
34093 zPath = jx9_value_to_string(apArg[0], 0);
34094 /* Perform the requested operation */
34095 iTime = pVfs->xFileAtime(zPath);
34096 /* IO return value */
34097 jx9_result_int64(pCtx, iTime);
34098 return JX9_OK;
34099}
34100/*
34101 * int64 filemtime(string $filename)
34102 * Gets file modification time.
34103 * Parameters
34104 * $filename
34105 * Path to the file.
34106 * Return
34107 * File mtime on success or FALSE on failure.
34108 */
34109static int jx9Vfs_file_mtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
34110{
34111 const char *zPath;
34112 jx9_int64 iTime;
34113 jx9_vfs *pVfs;
34114 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34115 /* Missing/Invalid argument, return FALSE */
34116 jx9_result_bool(pCtx, 0);
34117 return JX9_OK;
34118 }
34119 /* Point to the underlying vfs */
34120 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34121 if( pVfs == 0 || pVfs->xFileMtime == 0 ){
34122 /* IO routine not implemented, return NULL */
34123 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34124 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34125 jx9_function_name(pCtx)
34126 );
34127 jx9_result_bool(pCtx, 0);
34128 return JX9_OK;
34129 }
34130 /* Point to the desired directory */
34131 zPath = jx9_value_to_string(apArg[0], 0);
34132 /* Perform the requested operation */
34133 iTime = pVfs->xFileMtime(zPath);
34134 /* IO return value */
34135 jx9_result_int64(pCtx, iTime);
34136 return JX9_OK;
34137}
34138/*
34139 * int64 filectime(string $filename)
34140 * Gets inode change time of file.
34141 * Parameters
34142 * $filename
34143 * Path to the file.
34144 * Return
34145 * File ctime on success or FALSE on failure.
34146 */
34147static int jx9Vfs_file_ctime(jx9_context *pCtx, int nArg, jx9_value **apArg)
34148{
34149 const char *zPath;
34150 jx9_int64 iTime;
34151 jx9_vfs *pVfs;
34152 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34153 /* Missing/Invalid argument, return FALSE */
34154 jx9_result_bool(pCtx, 0);
34155 return JX9_OK;
34156 }
34157 /* Point to the underlying vfs */
34158 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34159 if( pVfs == 0 || pVfs->xFileCtime == 0 ){
34160 /* IO routine not implemented, return NULL */
34161 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34162 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34163 jx9_function_name(pCtx)
34164 );
34165 jx9_result_bool(pCtx, 0);
34166 return JX9_OK;
34167 }
34168 /* Point to the desired directory */
34169 zPath = jx9_value_to_string(apArg[0], 0);
34170 /* Perform the requested operation */
34171 iTime = pVfs->xFileCtime(zPath);
34172 /* IO return value */
34173 jx9_result_int64(pCtx, iTime);
34174 return JX9_OK;
34175}
34176/*
34177 * bool is_file(string $filename)
34178 * Tells whether the filename is a regular file.
34179 * Parameters
34180 * $filename
34181 * Path to the file.
34182 * Return
34183 * TRUE on success or FALSE on failure.
34184 */
34185static int jx9Vfs_is_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
34186{
34187 const char *zPath;
34188 jx9_vfs *pVfs;
34189 int rc;
34190 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34191 /* Missing/Invalid argument, return FALSE */
34192 jx9_result_bool(pCtx, 0);
34193 return JX9_OK;
34194 }
34195 /* Point to the underlying vfs */
34196 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34197 if( pVfs == 0 || pVfs->xIsfile == 0 ){
34198 /* IO routine not implemented, return NULL */
34199 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34200 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34201 jx9_function_name(pCtx)
34202 );
34203 jx9_result_bool(pCtx, 0);
34204 return JX9_OK;
34205 }
34206 /* Point to the desired directory */
34207 zPath = jx9_value_to_string(apArg[0], 0);
34208 /* Perform the requested operation */
34209 rc = pVfs->xIsfile(zPath);
34210 /* IO return value */
34211 jx9_result_bool(pCtx, rc == JX9_OK);
34212 return JX9_OK;
34213}
34214/*
34215 * bool is_link(string $filename)
34216 * Tells whether the filename is a symbolic link.
34217 * Parameters
34218 * $filename
34219 * Path to the file.
34220 * Return
34221 * TRUE on success or FALSE on failure.
34222 */
34223static int jx9Vfs_is_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
34224{
34225 const char *zPath;
34226 jx9_vfs *pVfs;
34227 int rc;
34228 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34229 /* Missing/Invalid argument, return FALSE */
34230 jx9_result_bool(pCtx, 0);
34231 return JX9_OK;
34232 }
34233 /* Point to the underlying vfs */
34234 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34235 if( pVfs == 0 || pVfs->xIslink == 0 ){
34236 /* IO routine not implemented, return NULL */
34237 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34238 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34239 jx9_function_name(pCtx)
34240 );
34241 jx9_result_bool(pCtx, 0);
34242 return JX9_OK;
34243 }
34244 /* Point to the desired directory */
34245 zPath = jx9_value_to_string(apArg[0], 0);
34246 /* Perform the requested operation */
34247 rc = pVfs->xIslink(zPath);
34248 /* IO return value */
34249 jx9_result_bool(pCtx, rc == JX9_OK);
34250 return JX9_OK;
34251}
34252/*
34253 * bool is_readable(string $filename)
34254 * Tells whether a file exists and is readable.
34255 * Parameters
34256 * $filename
34257 * Path to the file.
34258 * Return
34259 * TRUE on success or FALSE on failure.
34260 */
34261static int jx9Vfs_is_readable(jx9_context *pCtx, int nArg, jx9_value **apArg)
34262{
34263 const char *zPath;
34264 jx9_vfs *pVfs;
34265 int rc;
34266 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34267 /* Missing/Invalid argument, return FALSE */
34268 jx9_result_bool(pCtx, 0);
34269 return JX9_OK;
34270 }
34271 /* Point to the underlying vfs */
34272 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34273 if( pVfs == 0 || pVfs->xReadable == 0 ){
34274 /* IO routine not implemented, return NULL */
34275 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34276 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34277 jx9_function_name(pCtx)
34278 );
34279 jx9_result_bool(pCtx, 0);
34280 return JX9_OK;
34281 }
34282 /* Point to the desired directory */
34283 zPath = jx9_value_to_string(apArg[0], 0);
34284 /* Perform the requested operation */
34285 rc = pVfs->xReadable(zPath);
34286 /* IO return value */
34287 jx9_result_bool(pCtx, rc == JX9_OK);
34288 return JX9_OK;
34289}
34290/*
34291 * bool is_writable(string $filename)
34292 * Tells whether the filename is writable.
34293 * Parameters
34294 * $filename
34295 * Path to the file.
34296 * Return
34297 * TRUE on success or FALSE on failure.
34298 */
34299static int jx9Vfs_is_writable(jx9_context *pCtx, int nArg, jx9_value **apArg)
34300{
34301 const char *zPath;
34302 jx9_vfs *pVfs;
34303 int rc;
34304 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34305 /* Missing/Invalid argument, return FALSE */
34306 jx9_result_bool(pCtx, 0);
34307 return JX9_OK;
34308 }
34309 /* Point to the underlying vfs */
34310 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34311 if( pVfs == 0 || pVfs->xWritable == 0 ){
34312 /* IO routine not implemented, return NULL */
34313 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34314 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34315 jx9_function_name(pCtx)
34316 );
34317 jx9_result_bool(pCtx, 0);
34318 return JX9_OK;
34319 }
34320 /* Point to the desired directory */
34321 zPath = jx9_value_to_string(apArg[0], 0);
34322 /* Perform the requested operation */
34323 rc = pVfs->xWritable(zPath);
34324 /* IO return value */
34325 jx9_result_bool(pCtx, rc == JX9_OK);
34326 return JX9_OK;
34327}
34328/*
34329 * bool is_executable(string $filename)
34330 * Tells whether the filename is executable.
34331 * Parameters
34332 * $filename
34333 * Path to the file.
34334 * Return
34335 * TRUE on success or FALSE on failure.
34336 */
34337static int jx9Vfs_is_executable(jx9_context *pCtx, int nArg, jx9_value **apArg)
34338{
34339 const char *zPath;
34340 jx9_vfs *pVfs;
34341 int rc;
34342 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34343 /* Missing/Invalid argument, return FALSE */
34344 jx9_result_bool(pCtx, 0);
34345 return JX9_OK;
34346 }
34347 /* Point to the underlying vfs */
34348 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34349 if( pVfs == 0 || pVfs->xExecutable == 0 ){
34350 /* IO routine not implemented, return NULL */
34351 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34352 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34353 jx9_function_name(pCtx)
34354 );
34355 jx9_result_bool(pCtx, 0);
34356 return JX9_OK;
34357 }
34358 /* Point to the desired directory */
34359 zPath = jx9_value_to_string(apArg[0], 0);
34360 /* Perform the requested operation */
34361 rc = pVfs->xExecutable(zPath);
34362 /* IO return value */
34363 jx9_result_bool(pCtx, rc == JX9_OK);
34364 return JX9_OK;
34365}
34366/*
34367 * string filetype(string $filename)
34368 * Gets file type.
34369 * Parameters
34370 * $filename
34371 * Path to the file.
34372 * Return
34373 * The type of the file. Possible values are fifo, char, dir, block, link
34374 * file, socket and unknown.
34375 */
34376static int jx9Vfs_filetype(jx9_context *pCtx, int nArg, jx9_value **apArg)
34377{
34378 const char *zPath;
34379 jx9_vfs *pVfs;
34380 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34381 /* Missing/Invalid argument, return 'unknown' */
34382 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
34383 return JX9_OK;
34384 }
34385 /* Point to the underlying vfs */
34386 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34387 if( pVfs == 0 || pVfs->xFiletype == 0 ){
34388 /* IO routine not implemented, return NULL */
34389 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34390 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34391 jx9_function_name(pCtx)
34392 );
34393 jx9_result_bool(pCtx, 0);
34394 return JX9_OK;
34395 }
34396 /* Point to the desired directory */
34397 zPath = jx9_value_to_string(apArg[0], 0);
34398 /* Set the empty string as the default return value */
34399 jx9_result_string(pCtx, "", 0);
34400 /* Perform the requested operation */
34401 pVfs->xFiletype(zPath, pCtx);
34402 return JX9_OK;
34403}
34404/*
34405 * array stat(string $filename)
34406 * Gives information about a file.
34407 * Parameters
34408 * $filename
34409 * Path to the file.
34410 * Return
34411 * An associative array on success holding the following entries on success
34412 * 0 dev device number
34413 * 1 ino inode number (zero on windows)
34414 * 2 mode inode protection mode
34415 * 3 nlink number of links
34416 * 4 uid userid of owner (zero on windows)
34417 * 5 gid groupid of owner (zero on windows)
34418 * 6 rdev device type, if inode device
34419 * 7 size size in bytes
34420 * 8 atime time of last access (Unix timestamp)
34421 * 9 mtime time of last modification (Unix timestamp)
34422 * 10 ctime time of last inode change (Unix timestamp)
34423 * 11 blksize blocksize of filesystem IO (zero on windows)
34424 * 12 blocks number of 512-byte blocks allocated.
34425 * Note:
34426 * FALSE is returned on failure.
34427 */
34428static int jx9Vfs_stat(jx9_context *pCtx, int nArg, jx9_value **apArg)
34429{
34430 jx9_value *pArray, *pValue;
34431 const char *zPath;
34432 jx9_vfs *pVfs;
34433 int rc;
34434 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34435 /* Missing/Invalid argument, return FALSE */
34436 jx9_result_bool(pCtx, 0);
34437 return JX9_OK;
34438 }
34439 /* Point to the underlying vfs */
34440 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34441 if( pVfs == 0 || pVfs->xStat == 0 ){
34442 /* IO routine not implemented, return NULL */
34443 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34444 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34445 jx9_function_name(pCtx)
34446 );
34447 jx9_result_bool(pCtx, 0);
34448 return JX9_OK;
34449 }
34450 /* Create the array and the working value */
34451 pArray = jx9_context_new_array(pCtx);
34452 pValue = jx9_context_new_scalar(pCtx);
34453 if( pArray == 0 || pValue == 0 ){
34454 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
34455 jx9_result_bool(pCtx, 0);
34456 return JX9_OK;
34457 }
34458 /* Extract the file path */
34459 zPath = jx9_value_to_string(apArg[0], 0);
34460 /* Perform the requested operation */
34461 rc = pVfs->xStat(zPath, pArray, pValue);
34462 if( rc != JX9_OK ){
34463 /* IO error, return FALSE */
34464 jx9_result_bool(pCtx, 0);
34465 }else{
34466 /* Return the associative array */
34467 jx9_result_value(pCtx, pArray);
34468 }
34469 /* Don't worry about freeing memory here, everything will be released
34470 * automatically as soon we return from this function. */
34471 return JX9_OK;
34472}
34473/*
34474 * array lstat(string $filename)
34475 * Gives information about a file or symbolic link.
34476 * Parameters
34477 * $filename
34478 * Path to the file.
34479 * Return
34480 * An associative array on success holding the following entries on success
34481 * 0 dev device number
34482 * 1 ino inode number (zero on windows)
34483 * 2 mode inode protection mode
34484 * 3 nlink number of links
34485 * 4 uid userid of owner (zero on windows)
34486 * 5 gid groupid of owner (zero on windows)
34487 * 6 rdev device type, if inode device
34488 * 7 size size in bytes
34489 * 8 atime time of last access (Unix timestamp)
34490 * 9 mtime time of last modification (Unix timestamp)
34491 * 10 ctime time of last inode change (Unix timestamp)
34492 * 11 blksize blocksize of filesystem IO (zero on windows)
34493 * 12 blocks number of 512-byte blocks allocated.
34494 * Note:
34495 * FALSE is returned on failure.
34496 */
34497static int jx9Vfs_lstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
34498{
34499 jx9_value *pArray, *pValue;
34500 const char *zPath;
34501 jx9_vfs *pVfs;
34502 int rc;
34503 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34504 /* Missing/Invalid argument, return FALSE */
34505 jx9_result_bool(pCtx, 0);
34506 return JX9_OK;
34507 }
34508 /* Point to the underlying vfs */
34509 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34510 if( pVfs == 0 || pVfs->xlStat == 0 ){
34511 /* IO routine not implemented, return NULL */
34512 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34513 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34514 jx9_function_name(pCtx)
34515 );
34516 jx9_result_bool(pCtx, 0);
34517 return JX9_OK;
34518 }
34519 /* Create the array and the working value */
34520 pArray = jx9_context_new_array(pCtx);
34521 pValue = jx9_context_new_scalar(pCtx);
34522 if( pArray == 0 || pValue == 0 ){
34523 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
34524 jx9_result_bool(pCtx, 0);
34525 return JX9_OK;
34526 }
34527 /* Extract the file path */
34528 zPath = jx9_value_to_string(apArg[0], 0);
34529 /* Perform the requested operation */
34530 rc = pVfs->xlStat(zPath, pArray, pValue);
34531 if( rc != JX9_OK ){
34532 /* IO error, return FALSE */
34533 jx9_result_bool(pCtx, 0);
34534 }else{
34535 /* Return the associative array */
34536 jx9_result_value(pCtx, pArray);
34537 }
34538 /* Don't worry about freeing memory here, everything will be released
34539 * automatically as soon we return from this function. */
34540 return JX9_OK;
34541}
34542/*
34543 * string getenv(string $varname)
34544 * Gets the value of an environment variable.
34545 * Parameters
34546 * $varname
34547 * The variable name.
34548 * Return
34549 * Returns the value of the environment variable varname, or FALSE if the environment
34550 * variable varname does not exist.
34551 */
34552static int jx9Vfs_getenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
34553{
34554 const char *zEnv;
34555 jx9_vfs *pVfs;
34556 int iLen;
34557 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34558 /* Missing/Invalid argument, return FALSE */
34559 jx9_result_bool(pCtx, 0);
34560 return JX9_OK;
34561 }
34562 /* Point to the underlying vfs */
34563 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34564 if( pVfs == 0 || pVfs->xGetenv == 0 ){
34565 /* IO routine not implemented, return NULL */
34566 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34567 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34568 jx9_function_name(pCtx)
34569 );
34570 jx9_result_bool(pCtx, 0);
34571 return JX9_OK;
34572 }
34573 /* Extract the environment variable */
34574 zEnv = jx9_value_to_string(apArg[0], &iLen);
34575 /* Set a boolean FALSE as the default return value */
34576 jx9_result_bool(pCtx, 0);
34577 if( iLen < 1 ){
34578 /* Empty string */
34579 return JX9_OK;
34580 }
34581 /* Perform the requested operation */
34582 pVfs->xGetenv(zEnv, pCtx);
34583 return JX9_OK;
34584}
34585/*
34586 * bool putenv(string $settings)
34587 * Set the value of an environment variable.
34588 * Parameters
34589 * $setting
34590 * The setting, like "FOO=BAR"
34591 * Return
34592 * TRUE on success or FALSE on failure.
34593 */
34594static int jx9Vfs_putenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
34595{
34596 const char *zName, *zValue;
34597 char *zSettings, *zEnd;
34598 jx9_vfs *pVfs;
34599 int iLen, rc;
34600 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34601 /* Missing/Invalid argument, return FALSE */
34602 jx9_result_bool(pCtx, 0);
34603 return JX9_OK;
34604 }
34605 /* Extract the setting variable */
34606 zSettings = (char *)jx9_value_to_string(apArg[0], &iLen);
34607 if( iLen < 1 ){
34608 /* Empty string, return FALSE */
34609 jx9_result_bool(pCtx, 0);
34610 return JX9_OK;
34611 }
34612 /* Parse the setting */
34613 zEnd = &zSettings[iLen];
34614 zValue = 0;
34615 zName = zSettings;
34616 while( zSettings < zEnd ){
34617 if( zSettings[0] == '=' ){
34618 /* Null terminate the name */
34619 zSettings[0] = 0;
34620 zValue = &zSettings[1];
34621 break;
34622 }
34623 zSettings++;
34624 }
34625 /* Install the environment variable in the $_Env array */
34626 if( zValue == 0 || zName[0] == 0 || zValue >= zEnd || zName >= zValue ){
34627 /* Invalid settings, retun FALSE */
34628 jx9_result_bool(pCtx, 0);
34629 if( zSettings < zEnd ){
34630 zSettings[0] = '=';
34631 }
34632 return JX9_OK;
34633 }
34634 jx9_vm_config(pCtx->pVm, JX9_VM_CONFIG_ENV_ATTR, zName, zValue, (int)(zEnd-zValue));
34635 /* Point to the underlying vfs */
34636 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34637 if( pVfs == 0 || pVfs->xSetenv == 0 ){
34638 /* IO routine not implemented, return NULL */
34639 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34640 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34641 jx9_function_name(pCtx)
34642 );
34643 jx9_result_bool(pCtx, 0);
34644 zSettings[0] = '=';
34645 return JX9_OK;
34646 }
34647 /* Perform the requested operation */
34648 rc = pVfs->xSetenv(zName, zValue);
34649 jx9_result_bool(pCtx, rc == JX9_OK );
34650 zSettings[0] = '=';
34651 return JX9_OK;
34652}
34653/*
34654 * bool touch(string $filename[, int64 $time = time()[, int64 $atime]])
34655 * Sets access and modification time of file.
34656 * Note: On windows
34657 * If the file does not exists, it will not be created.
34658 * Parameters
34659 * $filename
34660 * The name of the file being touched.
34661 * $time
34662 * The touch time. If time is not supplied, the current system time is used.
34663 * $atime
34664 * If present, the access time of the given filename is set to the value of atime.
34665 * Otherwise, it is set to the value passed to the time parameter. If neither are
34666 * present, the current system time is used.
34667 * Return
34668 * TRUE on success or FALSE on failure.
34669*/
34670static int jx9Vfs_touch(jx9_context *pCtx, int nArg, jx9_value **apArg)
34671{
34672 jx9_int64 nTime, nAccess;
34673 const char *zFile;
34674 jx9_vfs *pVfs;
34675 int rc;
34676 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34677 /* Missing/Invalid argument, return FALSE */
34678 jx9_result_bool(pCtx, 0);
34679 return JX9_OK;
34680 }
34681 /* Point to the underlying vfs */
34682 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34683 if( pVfs == 0 || pVfs->xTouch == 0 ){
34684 /* IO routine not implemented, return NULL */
34685 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34686 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34687 jx9_function_name(pCtx)
34688 );
34689 jx9_result_bool(pCtx, 0);
34690 return JX9_OK;
34691 }
34692 /* Perform the requested operation */
34693 nTime = nAccess = -1;
34694 zFile = jx9_value_to_string(apArg[0], 0);
34695 if( nArg > 1 ){
34696 nTime = jx9_value_to_int64(apArg[1]);
34697 if( nArg > 2 ){
34698 nAccess = jx9_value_to_int64(apArg[1]);
34699 }else{
34700 nAccess = nTime;
34701 }
34702 }
34703 rc = pVfs->xTouch(zFile, nTime, nAccess);
34704 /* IO result */
34705 jx9_result_bool(pCtx, rc == JX9_OK);
34706 return JX9_OK;
34707}
34708/*
34709 * Path processing functions that do not need access to the VFS layer
34710 * Authors:
34711 * Symisc Systems, devel@symisc.net.
34712 * Copyright (C) Symisc Systems, http://jx9.symisc.net
34713 * Status:
34714 * Stable.
34715 */
34716/*
34717 * string dirname(string $path)
34718 * Returns parent directory's path.
34719 * Parameters
34720 * $path
34721 * Target path.
34722 * On Windows, both slash (/) and backslash (\) are used as directory separator character.
34723 * In other environments, it is the forward slash (/).
34724 * Return
34725 * The path of the parent directory. If there are no slashes in path, a dot ('.')
34726 * is returned, indicating the current directory.
34727 */
34728static int jx9Builtin_dirname(jx9_context *pCtx, int nArg, jx9_value **apArg)
34729{
34730 const char *zPath, *zDir;
34731 int iLen, iDirlen;
34732 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34733 /* Missing/Invalid arguments, return the empty string */
34734 jx9_result_string(pCtx, "", 0);
34735 return JX9_OK;
34736 }
34737 /* Point to the target path */
34738 zPath = jx9_value_to_string(apArg[0], &iLen);
34739 if( iLen < 1 ){
34740 /* Reuturn "." */
34741 jx9_result_string(pCtx, ".", sizeof(char));
34742 return JX9_OK;
34743 }
34744 /* Perform the requested operation */
34745 zDir = jx9ExtractDirName(zPath, iLen, &iDirlen);
34746 /* Return directory name */
34747 jx9_result_string(pCtx, zDir, iDirlen);
34748 return JX9_OK;
34749}
34750/*
34751 * string basename(string $path[, string $suffix ])
34752 * Returns trailing name component of path.
34753 * Parameters
34754 * $path
34755 * Target path.
34756 * On Windows, both slash (/) and backslash (\) are used as directory separator character.
34757 * In other environments, it is the forward slash (/).
34758 * $suffix
34759 * If the name component ends in suffix this will also be cut off.
34760 * Return
34761 * The base name of the given path.
34762 */
34763static int jx9Builtin_basename(jx9_context *pCtx, int nArg, jx9_value **apArg)
34764{
34765 const char *zPath, *zBase, *zEnd;
34766 int c, d, iLen;
34767 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34768 /* Missing/Invalid argument, return the empty string */
34769 jx9_result_string(pCtx, "", 0);
34770 return JX9_OK;
34771 }
34772 c = d = '/';
34773#ifdef __WINNT__
34774 d = '\\';
34775#endif
34776 /* Point to the target path */
34777 zPath = jx9_value_to_string(apArg[0], &iLen);
34778 if( iLen < 1 ){
34779 /* Empty string */
34780 jx9_result_string(pCtx, "", 0);
34781 return JX9_OK;
34782 }
34783 /* Perform the requested operation */
34784 zEnd = &zPath[iLen - 1];
34785 /* Ignore trailing '/' */
34786 while( zEnd > zPath && ( (int)zEnd[0] == c || (int)zEnd[0] == d ) ){
34787 zEnd--;
34788 }
34789 iLen = (int)(&zEnd[1]-zPath);
34790 while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
34791 zEnd--;
34792 }
34793 zBase = (zEnd > zPath) ? &zEnd[1] : zPath;
34794 zEnd = &zPath[iLen];
34795 if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
34796 const char *zSuffix;
34797 int nSuffix;
34798 /* Strip suffix */
34799 zSuffix = jx9_value_to_string(apArg[1], &nSuffix);
34800 if( nSuffix > 0 && nSuffix < iLen && SyMemcmp(&zEnd[-nSuffix], zSuffix, nSuffix) == 0 ){
34801 zEnd -= nSuffix;
34802 }
34803 }
34804 /* Store the basename */
34805 jx9_result_string(pCtx, zBase, (int)(zEnd-zBase));
34806 return JX9_OK;
34807}
34808/*
34809 * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ])
34810 * Returns information about a file path.
34811 * Parameter
34812 * $path
34813 * The path to be parsed.
34814 * $options
34815 * If present, specifies a specific element to be returned; one of
34816 * PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION or PATHINFO_FILENAME.
34817 * Return
34818 * If the options parameter is not passed, an associative array containing the following
34819 * elements is returned: dirname, basename, extension (if any), and filename.
34820 * If options is present, returns a string containing the requested element.
34821 */
34822typedef struct path_info path_info;
34823struct path_info
34824{
34825 SyString sDir; /* Directory [i.e: /var/www] */
34826 SyString sBasename; /* Basename [i.e httpd.conf] */
34827 SyString sExtension; /* File extension [i.e xml, pdf..] */
34828 SyString sFilename; /* Filename */
34829};
34830/*
34831 * Extract path fields.
34832 */
34833static sxi32 ExtractPathInfo(const char *zPath, int nByte, path_info *pOut)
34834{
34835 const char *zPtr, *zEnd = &zPath[nByte - 1];
34836 SyString *pCur;
34837 int c, d;
34838 c = d = '/';
34839#ifdef __WINNT__
34840 d = '\\';
34841#endif
34842 /* Zero the structure */
34843 SyZero(pOut, sizeof(path_info));
34844 /* Handle special case */
34845 if( nByte == sizeof(char) && ( (int)zPath[0] == c || (int)zPath[0] == d ) ){
34846#ifdef __WINNT__
34847 SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char));
34848#else
34849 SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char));
34850#endif
34851 return SXRET_OK;
34852 }
34853 /* Extract the basename */
34854 while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
34855 zEnd--;
34856 }
34857 zPtr = (zEnd > zPath) ? &zEnd[1] : zPath;
34858 zEnd = &zPath[nByte];
34859 /* dirname */
34860 pCur = &pOut->sDir;
34861 SyStringInitFromBuf(pCur, zPath, zPtr-zPath);
34862 if( pCur->nByte > 1 ){
34863 SyStringTrimTrailingChar(pCur, '/');
34864#ifdef __WINNT__
34865 SyStringTrimTrailingChar(pCur, '\\');
34866#endif
34867 }else if( (int)zPath[0] == c || (int)zPath[0] == d ){
34868#ifdef __WINNT__
34869 SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char));
34870#else
34871 SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char));
34872#endif
34873 }
34874 /* basename/filename */
34875 pCur = &pOut->sBasename;
34876 SyStringInitFromBuf(pCur, zPtr, zEnd-zPtr);
34877 SyStringTrimLeadingChar(pCur, '/');
34878#ifdef __WINNT__
34879 SyStringTrimLeadingChar(pCur, '\\');
34880#endif
34881 SyStringDupPtr(&pOut->sFilename, pCur);
34882 if( pCur->nByte > 0 ){
34883 /* extension */
34884 zEnd--;
34885 while( zEnd > pCur->zString /*basename*/ && zEnd[0] != '.' ){
34886 zEnd--;
34887 }
34888 if( zEnd > pCur->zString ){
34889 zEnd++; /* Jump leading dot */
34890 SyStringInitFromBuf(&pOut->sExtension, zEnd, &zPath[nByte]-zEnd);
34891 /* Fix filename */
34892 pCur = &pOut->sFilename;
34893 if( pCur->nByte > SyStringLength(&pOut->sExtension) ){
34894 pCur->nByte -= 1 + SyStringLength(&pOut->sExtension);
34895 }
34896 }
34897 }
34898 return SXRET_OK;
34899}
34900/*
34901 * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ])
34902 * See block comment above.
34903 */
34904static int jx9Builtin_pathinfo(jx9_context *pCtx, int nArg, jx9_value **apArg)
34905{
34906 const char *zPath;
34907 path_info sInfo;
34908 SyString *pComp;
34909 int iLen;
34910 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34911 /* Missing/Invalid argument, return the empty string */
34912 jx9_result_string(pCtx, "", 0);
34913 return JX9_OK;
34914 }
34915 /* Point to the target path */
34916 zPath = jx9_value_to_string(apArg[0], &iLen);
34917 if( iLen < 1 ){
34918 /* Empty string */
34919 jx9_result_string(pCtx, "", 0);
34920 return JX9_OK;
34921 }
34922 /* Extract path info */
34923 ExtractPathInfo(zPath, iLen, &sInfo);
34924 if( nArg > 1 && jx9_value_is_int(apArg[1]) ){
34925 /* Return path component */
34926 int nComp = jx9_value_to_int(apArg[1]);
34927 switch(nComp){
34928 case 1: /* PATHINFO_DIRNAME */
34929 pComp = &sInfo.sDir;
34930 if( pComp->nByte > 0 ){
34931 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
34932 }else{
34933 /* Expand the empty string */
34934 jx9_result_string(pCtx, "", 0);
34935 }
34936 break;
34937 case 2: /*PATHINFO_BASENAME*/
34938 pComp = &sInfo.sBasename;
34939 if( pComp->nByte > 0 ){
34940 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
34941 }else{
34942 /* Expand the empty string */
34943 jx9_result_string(pCtx, "", 0);
34944 }
34945 break;
34946 case 3: /*PATHINFO_EXTENSION*/
34947 pComp = &sInfo.sExtension;
34948 if( pComp->nByte > 0 ){
34949 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
34950 }else{
34951 /* Expand the empty string */
34952 jx9_result_string(pCtx, "", 0);
34953 }
34954 break;
34955 case 4: /*PATHINFO_FILENAME*/
34956 pComp = &sInfo.sFilename;
34957 if( pComp->nByte > 0 ){
34958 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
34959 }else{
34960 /* Expand the empty string */
34961 jx9_result_string(pCtx, "", 0);
34962 }
34963 break;
34964 default:
34965 /* Expand the empty string */
34966 jx9_result_string(pCtx, "", 0);
34967 break;
34968 }
34969 }else{
34970 /* Return an associative array */
34971 jx9_value *pArray, *pValue;
34972 pArray = jx9_context_new_array(pCtx);
34973 pValue = jx9_context_new_scalar(pCtx);
34974 if( pArray == 0 || pValue == 0 ){
34975 /* Out of mem, return NULL */
34976 jx9_result_bool(pCtx, 0);
34977 return JX9_OK;
34978 }
34979 /* dirname */
34980 pComp = &sInfo.sDir;
34981 if( pComp->nByte > 0 ){
34982 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
34983 /* Perform the insertion */
34984 jx9_array_add_strkey_elem(pArray, "dirname", pValue); /* Will make it's own copy */
34985 }
34986 /* Reset the string cursor */
34987 jx9_value_reset_string_cursor(pValue);
34988 /* basername */
34989 pComp = &sInfo.sBasename;
34990 if( pComp->nByte > 0 ){
34991 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
34992 /* Perform the insertion */
34993 jx9_array_add_strkey_elem(pArray, "basename", pValue); /* Will make it's own copy */
34994 }
34995 /* Reset the string cursor */
34996 jx9_value_reset_string_cursor(pValue);
34997 /* extension */
34998 pComp = &sInfo.sExtension;
34999 if( pComp->nByte > 0 ){
35000 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
35001 /* Perform the insertion */
35002 jx9_array_add_strkey_elem(pArray, "extension", pValue); /* Will make it's own copy */
35003 }
35004 /* Reset the string cursor */
35005 jx9_value_reset_string_cursor(pValue);
35006 /* filename */
35007 pComp = &sInfo.sFilename;
35008 if( pComp->nByte > 0 ){
35009 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
35010 /* Perform the insertion */
35011 jx9_array_add_strkey_elem(pArray, "filename", pValue); /* Will make it's own copy */
35012 }
35013 /* Return the created array */
35014 jx9_result_value(pCtx, pArray);
35015 /* Don't worry about freeing memory, everything will be released
35016 * automatically as soon we return from this foreign function.
35017 */
35018 }
35019 return JX9_OK;
35020}
35021/*
35022 * Globbing implementation extracted from the sqlite3 source tree.
35023 * Original author: D. Richard Hipp (http://www.sqlite.org)
35024 * Status: Public Domain
35025 */
35026typedef unsigned char u8;
35027/* An array to map all upper-case characters into their corresponding
35028** lower-case character.
35029**
35030** SQLite only considers US-ASCII (or EBCDIC) characters. We do not
35031** handle case conversions for the UTF character set since the tables
35032** involved are nearly as big or bigger than SQLite itself.
35033*/
35034static const unsigned char sqlite3UpperToLower[] = {
35035 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
35036 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
35037 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
35038 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103,
35039 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
35040 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
35041 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
35042 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
35043 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
35044 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
35045 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
35046 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
35047 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
35048 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
35049 252, 253, 254, 255
35050};
35051#define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; }
35052/*
35053** Assuming zIn points to the first byte of a UTF-8 character,
35054** advance zIn to point to the first byte of the next UTF-8 character.
35055*/
35056#define SQLITE_SKIP_UTF8(zIn) { \
35057 if( (*(zIn++))>=0xc0 ){ \
35058 while( (*zIn & 0xc0)==0x80 ){ zIn++; } \
35059 } \
35060}
35061/*
35062** Compare two UTF-8 strings for equality where the first string can
35063** potentially be a "glob" expression. Return true (1) if they
35064** are the same and false (0) if they are different.
35065**
35066** Globbing rules:
35067**
35068** '*' Matches any sequence of zero or more characters.
35069**
35070** '?' Matches exactly one character.
35071**
35072** [...] Matches one character from the enclosed list of
35073** characters.
35074**
35075** [^...] Matches one character not in the enclosed list.
35076**
35077** With the [...] and [^...] matching, a ']' character can be included
35078** in the list by making it the first character after '[' or '^'. A
35079** range of characters can be specified using '-'. Example:
35080** "[a-z]" matches any single lower-case letter. To match a '-', make
35081** it the last character in the list.
35082**
35083** This routine is usually quick, but can be N**2 in the worst case.
35084**
35085** Hints: to match '*' or '?', put them in "[]". Like this:
35086**
35087** abc[*]xyz Matches "abc*xyz" only
35088*/
35089static int patternCompare(
35090 const u8 *zPattern, /* The glob pattern */
35091 const u8 *zString, /* The string to compare against the glob */
35092 const int esc, /* The escape character */
35093 int noCase
35094){
35095 int c, c2;
35096 int invert;
35097 int seen;
35098 u8 matchOne = '?';
35099 u8 matchAll = '*';
35100 u8 matchSet = '[';
35101 int prevEscape = 0; /* True if the previous character was 'escape' */
35102
35103 if( !zPattern || !zString ) return 0;
35104 while( (c = jx9Utf8Read(zPattern, 0, &zPattern))!=0 ){
35105 if( !prevEscape && c==matchAll ){
35106 while( (c= jx9Utf8Read(zPattern, 0, &zPattern)) == matchAll
35107 || c == matchOne ){
35108 if( c==matchOne && jx9Utf8Read(zString, 0, &zString)==0 ){
35109 return 0;
35110 }
35111 }
35112 if( c==0 ){
35113 return 1;
35114 }else if( c==esc ){
35115 c = jx9Utf8Read(zPattern, 0, &zPattern);
35116 if( c==0 ){
35117 return 0;
35118 }
35119 }else if( c==matchSet ){
35120 if( (esc==0) || (matchSet<0x80) ) return 0;
35121 while( *zString && patternCompare(&zPattern[-1], zString, esc, noCase)==0 ){
35122 SQLITE_SKIP_UTF8(zString);
35123 }
35124 return *zString!=0;
35125 }
35126 while( (c2 = jx9Utf8Read(zString, 0, &zString))!=0 ){
35127 if( noCase ){
35128 GlogUpperToLower(c2);
35129 GlogUpperToLower(c);
35130 while( c2 != 0 && c2 != c ){
35131 c2 = jx9Utf8Read(zString, 0, &zString);
35132 GlogUpperToLower(c2);
35133 }
35134 }else{
35135 while( c2 != 0 && c2 != c ){
35136 c2 = jx9Utf8Read(zString, 0, &zString);
35137 }
35138 }
35139 if( c2==0 ) return 0;
35140 if( patternCompare(zPattern, zString, esc, noCase) ) return 1;
35141 }
35142 return 0;
35143 }else if( !prevEscape && c==matchOne ){
35144 if( jx9Utf8Read(zString, 0, &zString)==0 ){
35145 return 0;
35146 }
35147 }else if( c==matchSet ){
35148 int prior_c = 0;
35149 if( esc == 0 ) return 0;
35150 seen = 0;
35151 invert = 0;
35152 c = jx9Utf8Read(zString, 0, &zString);
35153 if( c==0 ) return 0;
35154 c2 = jx9Utf8Read(zPattern, 0, &zPattern);
35155 if( c2=='^' ){
35156 invert = 1;
35157 c2 = jx9Utf8Read(zPattern, 0, &zPattern);
35158 }
35159 if( c2==']' ){
35160 if( c==']' ) seen = 1;
35161 c2 = jx9Utf8Read(zPattern, 0, &zPattern);
35162 }
35163 while( c2 && c2!=']' ){
35164 if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
35165 c2 = jx9Utf8Read(zPattern, 0, &zPattern);
35166 if( c>=prior_c && c<=c2 ) seen = 1;
35167 prior_c = 0;
35168 }else{
35169 if( c==c2 ){
35170 seen = 1;
35171 }
35172 prior_c = c2;
35173 }
35174 c2 = jx9Utf8Read(zPattern, 0, &zPattern);
35175 }
35176 if( c2==0 || (seen ^ invert)==0 ){
35177 return 0;
35178 }
35179 }else if( esc==c && !prevEscape ){
35180 prevEscape = 1;
35181 }else{
35182 c2 = jx9Utf8Read(zString, 0, &zString);
35183 if( noCase ){
35184 GlogUpperToLower(c);
35185 GlogUpperToLower(c2);
35186 }
35187 if( c!=c2 ){
35188 return 0;
35189 }
35190 prevEscape = 0;
35191 }
35192 }
35193 return *zString==0;
35194}
35195/*
35196 * Wrapper around patternCompare() defined above.
35197 * See block comment above for more information.
35198 */
35199static int Glob(const unsigned char *zPattern, const unsigned char *zString, int iEsc, int CaseCompare)
35200{
35201 int rc;
35202 if( iEsc < 0 ){
35203 iEsc = '\\';
35204 }
35205 rc = patternCompare(zPattern, zString, iEsc, CaseCompare);
35206 return rc;
35207}
35208/*
35209 * bool fnmatch(string $pattern, string $string[, int $flags = 0 ])
35210 * Match filename against a pattern.
35211 * Parameters
35212 * $pattern
35213 * The shell wildcard pattern.
35214 * $string
35215 * The tested string.
35216 * $flags
35217 * A list of possible flags:
35218 * FNM_NOESCAPE Disable backslash escaping.
35219 * FNM_PATHNAME Slash in string only matches slash in the given pattern.
35220 * FNM_PERIOD Leading period in string must be exactly matched by period in the given pattern.
35221 * FNM_CASEFOLD Caseless match.
35222 * Return
35223 * TRUE if there is a match, FALSE otherwise.
35224 */
35225static int jx9Builtin_fnmatch(jx9_context *pCtx, int nArg, jx9_value **apArg)
35226{
35227 const char *zString, *zPattern;
35228 int iEsc = '\\';
35229 int noCase = 0;
35230 int rc;
35231 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
35232 /* Missing/Invalid arguments, return FALSE */
35233 jx9_result_bool(pCtx, 0);
35234 return JX9_OK;
35235 }
35236 /* Extract the pattern and the string */
35237 zPattern = jx9_value_to_string(apArg[0], 0);
35238 zString = jx9_value_to_string(apArg[1], 0);
35239 /* Extract the flags if avaialble */
35240 if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
35241 rc = jx9_value_to_int(apArg[2]);
35242 if( rc & 0x01 /*FNM_NOESCAPE*/){
35243 iEsc = 0;
35244 }
35245 if( rc & 0x08 /*FNM_CASEFOLD*/){
35246 noCase = 1;
35247 }
35248 }
35249 /* Go globbing */
35250 rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, noCase);
35251 /* Globbing result */
35252 jx9_result_bool(pCtx, rc);
35253 return JX9_OK;
35254}
35255/*
35256 * bool strglob(string $pattern, string $string)
35257 * Match string against a pattern.
35258 * Parameters
35259 * $pattern
35260 * The shell wildcard pattern.
35261 * $string
35262 * The tested string.
35263 * Return
35264 * TRUE if there is a match, FALSE otherwise.
35265 */
35266static int jx9Builtin_strglob(jx9_context *pCtx, int nArg, jx9_value **apArg)
35267{
35268 const char *zString, *zPattern;
35269 int iEsc = '\\';
35270 int rc;
35271 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
35272 /* Missing/Invalid arguments, return FALSE */
35273 jx9_result_bool(pCtx, 0);
35274 return JX9_OK;
35275 }
35276 /* Extract the pattern and the string */
35277 zPattern = jx9_value_to_string(apArg[0], 0);
35278 zString = jx9_value_to_string(apArg[1], 0);
35279 /* Go globbing */
35280 rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, 0);
35281 /* Globbing result */
35282 jx9_result_bool(pCtx, rc);
35283 return JX9_OK;
35284}
35285/*
35286 * bool link(string $target, string $link)
35287 * Create a hard link.
35288 * Parameters
35289 * $target
35290 * Target of the link.
35291 * $link
35292 * The link name.
35293 * Return
35294 * TRUE on success or FALSE on failure.
35295 */
35296static int jx9Vfs_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
35297{
35298 const char *zTarget, *zLink;
35299 jx9_vfs *pVfs;
35300 int rc;
35301 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
35302 /* Missing/Invalid arguments, return FALSE */
35303 jx9_result_bool(pCtx, 0);
35304 return JX9_OK;
35305 }
35306 /* Point to the underlying vfs */
35307 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35308 if( pVfs == 0 || pVfs->xLink == 0 ){
35309 /* IO routine not implemented, return NULL */
35310 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35311 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
35312 jx9_function_name(pCtx)
35313 );
35314 jx9_result_bool(pCtx, 0);
35315 return JX9_OK;
35316 }
35317 /* Extract the given arguments */
35318 zTarget = jx9_value_to_string(apArg[0], 0);
35319 zLink = jx9_value_to_string(apArg[1], 0);
35320 /* Perform the requested operation */
35321 rc = pVfs->xLink(zTarget, zLink, 0/*Not a symbolic link */);
35322 /* IO result */
35323 jx9_result_bool(pCtx, rc == JX9_OK );
35324 return JX9_OK;
35325}
35326/*
35327 * bool symlink(string $target, string $link)
35328 * Creates a symbolic link.
35329 * Parameters
35330 * $target
35331 * Target of the link.
35332 * $link
35333 * The link name.
35334 * Return
35335 * TRUE on success or FALSE on failure.
35336 */
35337static int jx9Vfs_symlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
35338{
35339 const char *zTarget, *zLink;
35340 jx9_vfs *pVfs;
35341 int rc;
35342 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
35343 /* Missing/Invalid arguments, return FALSE */
35344 jx9_result_bool(pCtx, 0);
35345 return JX9_OK;
35346 }
35347 /* Point to the underlying vfs */
35348 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35349 if( pVfs == 0 || pVfs->xLink == 0 ){
35350 /* IO routine not implemented, return NULL */
35351 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35352 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
35353 jx9_function_name(pCtx)
35354 );
35355 jx9_result_bool(pCtx, 0);
35356 return JX9_OK;
35357 }
35358 /* Extract the given arguments */
35359 zTarget = jx9_value_to_string(apArg[0], 0);
35360 zLink = jx9_value_to_string(apArg[1], 0);
35361 /* Perform the requested operation */
35362 rc = pVfs->xLink(zTarget, zLink, 1/*A symbolic link */);
35363 /* IO result */
35364 jx9_result_bool(pCtx, rc == JX9_OK );
35365 return JX9_OK;
35366}
35367/*
35368 * int umask([ int $mask ])
35369 * Changes the current umask.
35370 * Parameters
35371 * $mask
35372 * The new umask.
35373 * Return
35374 * umask() without arguments simply returns the current umask.
35375 * Otherwise the old umask is returned.
35376 */
35377static int jx9Vfs_umask(jx9_context *pCtx, int nArg, jx9_value **apArg)
35378{
35379 int iOld, iNew;
35380 jx9_vfs *pVfs;
35381 /* Point to the underlying vfs */
35382 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35383 if( pVfs == 0 || pVfs->xUmask == 0 ){
35384 /* IO routine not implemented, return -1 */
35385 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35386 "IO routine(%s) not implemented in the underlying VFS",
35387 jx9_function_name(pCtx)
35388 );
35389 jx9_result_int(pCtx, 0);
35390 return JX9_OK;
35391 }
35392 iNew = 0;
35393 if( nArg > 0 ){
35394 iNew = jx9_value_to_int(apArg[0]);
35395 }
35396 /* Perform the requested operation */
35397 iOld = pVfs->xUmask(iNew);
35398 /* Old mask */
35399 jx9_result_int(pCtx, iOld);
35400 return JX9_OK;
35401}
35402/*
35403 * string sys_get_temp_dir()
35404 * Returns directory path used for temporary files.
35405 * Parameters
35406 * None
35407 * Return
35408 * Returns the path of the temporary directory.
35409 */
35410static int jx9Vfs_sys_get_temp_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
35411{
35412 jx9_vfs *pVfs;
35413 /* Set the empty string as the default return value */
35414 jx9_result_string(pCtx, "", 0);
35415 /* Point to the underlying vfs */
35416 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35417 if( pVfs == 0 || pVfs->xTempDir == 0 ){
35418 SXUNUSED(nArg); /* cc warning */
35419 SXUNUSED(apArg);
35420 /* IO routine not implemented, return "" */
35421 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35422 "IO routine(%s) not implemented in the underlying VFS",
35423 jx9_function_name(pCtx)
35424 );
35425 return JX9_OK;
35426 }
35427 /* Perform the requested operation */
35428 pVfs->xTempDir(pCtx);
35429 return JX9_OK;
35430}
35431/*
35432 * string get_current_user()
35433 * Returns the name of the current working user.
35434 * Parameters
35435 * None
35436 * Return
35437 * Returns the name of the current working user.
35438 */
35439static int jx9Vfs_get_current_user(jx9_context *pCtx, int nArg, jx9_value **apArg)
35440{
35441 jx9_vfs *pVfs;
35442 /* Point to the underlying vfs */
35443 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35444 if( pVfs == 0 || pVfs->xUsername == 0 ){
35445 SXUNUSED(nArg); /* cc warning */
35446 SXUNUSED(apArg);
35447 /* IO routine not implemented */
35448 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35449 "IO routine(%s) not implemented in the underlying VFS",
35450 jx9_function_name(pCtx)
35451 );
35452 /* Set a dummy username */
35453 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
35454 return JX9_OK;
35455 }
35456 /* Perform the requested operation */
35457 pVfs->xUsername(pCtx);
35458 return JX9_OK;
35459}
35460/*
35461 * int64 getmypid()
35462 * Gets process ID.
35463 * Parameters
35464 * None
35465 * Return
35466 * Returns the process ID.
35467 */
35468static int jx9Vfs_getmypid(jx9_context *pCtx, int nArg, jx9_value **apArg)
35469{
35470 jx9_int64 nProcessId;
35471 jx9_vfs *pVfs;
35472 /* Point to the underlying vfs */
35473 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35474 if( pVfs == 0 || pVfs->xProcessId == 0 ){
35475 SXUNUSED(nArg); /* cc warning */
35476 SXUNUSED(apArg);
35477 /* IO routine not implemented, return -1 */
35478 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35479 "IO routine(%s) not implemented in the underlying VFS",
35480 jx9_function_name(pCtx)
35481 );
35482 jx9_result_int(pCtx, -1);
35483 return JX9_OK;
35484 }
35485 /* Perform the requested operation */
35486 nProcessId = (jx9_int64)pVfs->xProcessId();
35487 /* Set the result */
35488 jx9_result_int64(pCtx, nProcessId);
35489 return JX9_OK;
35490}
35491/*
35492 * int getmyuid()
35493 * Get user ID.
35494 * Parameters
35495 * None
35496 * Return
35497 * Returns the user ID.
35498 */
35499static int jx9Vfs_getmyuid(jx9_context *pCtx, int nArg, jx9_value **apArg)
35500{
35501 jx9_vfs *pVfs;
35502 int nUid;
35503 /* Point to the underlying vfs */
35504 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35505 if( pVfs == 0 || pVfs->xUid == 0 ){
35506 SXUNUSED(nArg); /* cc warning */
35507 SXUNUSED(apArg);
35508 /* IO routine not implemented, return -1 */
35509 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35510 "IO routine(%s) not implemented in the underlying VFS",
35511 jx9_function_name(pCtx)
35512 );
35513 jx9_result_int(pCtx, -1);
35514 return JX9_OK;
35515 }
35516 /* Perform the requested operation */
35517 nUid = pVfs->xUid();
35518 /* Set the result */
35519 jx9_result_int(pCtx, nUid);
35520 return JX9_OK;
35521}
35522/*
35523 * int getmygid()
35524 * Get group ID.
35525 * Parameters
35526 * None
35527 * Return
35528 * Returns the group ID.
35529 */
35530static int jx9Vfs_getmygid(jx9_context *pCtx, int nArg, jx9_value **apArg)
35531{
35532 jx9_vfs *pVfs;
35533 int nGid;
35534 /* Point to the underlying vfs */
35535 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35536 if( pVfs == 0 || pVfs->xGid == 0 ){
35537 SXUNUSED(nArg); /* cc warning */
35538 SXUNUSED(apArg);
35539 /* IO routine not implemented, return -1 */
35540 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35541 "IO routine(%s) not implemented in the underlying VFS",
35542 jx9_function_name(pCtx)
35543 );
35544 jx9_result_int(pCtx, -1);
35545 return JX9_OK;
35546 }
35547 /* Perform the requested operation */
35548 nGid = pVfs->xGid();
35549 /* Set the result */
35550 jx9_result_int(pCtx, nGid);
35551 return JX9_OK;
35552}
35553#ifdef __WINNT__
35554#include <Windows.h>
35555#elif defined(__UNIXES__)
35556#include <sys/utsname.h>
35557#endif
35558/*
35559 * string uname([ string $mode = "a" ])
35560 * Returns information about the host operating system.
35561 * Parameters
35562 * $mode
35563 * mode is a single character that defines what information is returned:
35564 * 'a': This is the default. Contains all modes in the sequence "s n r v m".
35565 * 's': Operating system name. eg. FreeBSD.
35566 * 'n': Host name. eg. localhost.example.com.
35567 * 'r': Release name. eg. 5.1.2-RELEASE.
35568 * 'v': Version information. Varies a lot between operating systems.
35569 * 'm': Machine type. eg. i386.
35570 * Return
35571 * OS description as a string.
35572 */
35573static int jx9Vfs_uname(jx9_context *pCtx, int nArg, jx9_value **apArg)
35574{
35575#if defined(__WINNT__)
35576 const char *zName = "Microsoft Windows";
35577 OSVERSIONINFOW sVer;
35578#elif defined(__UNIXES__)
35579 struct utsname sName;
35580#endif
35581 const char *zMode = "a";
35582 if( nArg > 0 && jx9_value_is_string(apArg[0]) ){
35583 /* Extract the desired mode */
35584 zMode = jx9_value_to_string(apArg[0], 0);
35585 }
35586#if defined(__WINNT__)
35587 sVer.dwOSVersionInfoSize = sizeof(sVer);
35588 if( TRUE != GetVersionExW(&sVer)){
35589 jx9_result_string(pCtx, zName, -1);
35590 return JX9_OK;
35591 }
35592 if( sVer.dwPlatformId == VER_PLATFORM_WIN32_NT ){
35593 if( sVer.dwMajorVersion <= 4 ){
35594 zName = "Microsoft Windows NT";
35595 }else if( sVer.dwMajorVersion == 5 ){
35596 switch(sVer.dwMinorVersion){
35597 case 0: zName = "Microsoft Windows 2000"; break;
35598 case 1: zName = "Microsoft Windows XP"; break;
35599 case 2: zName = "Microsoft Windows Server 2003"; break;
35600 }
35601 }else if( sVer.dwMajorVersion == 6){
35602 switch(sVer.dwMinorVersion){
35603 case 0: zName = "Microsoft Windows Vista"; break;
35604 case 1: zName = "Microsoft Windows 7"; break;
35605 case 2: zName = "Microsoft Windows 8"; break;
35606 default: break;
35607 }
35608 }
35609 }
35610 switch(zMode[0]){
35611 case 's':
35612 /* Operating system name */
35613 jx9_result_string(pCtx, zName, -1/* Compute length automatically*/);
35614 break;
35615 case 'n':
35616 /* Host name */
35617 jx9_result_string(pCtx, "localhost", (int)sizeof("localhost")-1);
35618 break;
35619 case 'r':
35620 case 'v':
35621 /* Version information. */
35622 jx9_result_string_format(pCtx, "%u.%u build %u",
35623 sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
35624 );
35625 break;
35626 case 'm':
35627 /* Machine name */
35628 jx9_result_string(pCtx, "x86", (int)sizeof("x86")-1);
35629 break;
35630 default:
35631 jx9_result_string_format(pCtx, "%s localhost %u.%u build %u x86",
35632 zName,
35633 sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
35634 );
35635 break;
35636 }
35637#elif defined(__UNIXES__)
35638 if( uname(&sName) != 0 ){
35639 jx9_result_string(pCtx, "Unix", (int)sizeof("Unix")-1);
35640 return JX9_OK;
35641 }
35642 switch(zMode[0]){
35643 case 's':
35644 /* Operating system name */
35645 jx9_result_string(pCtx, sName.sysname, -1/* Compute length automatically*/);
35646 break;
35647 case 'n':
35648 /* Host name */
35649 jx9_result_string(pCtx, sName.nodename, -1/* Compute length automatically*/);
35650 break;
35651 case 'r':
35652 /* Release information */
35653 jx9_result_string(pCtx, sName.release, -1/* Compute length automatically*/);
35654 break;
35655 case 'v':
35656 /* Version information. */
35657 jx9_result_string(pCtx, sName.version, -1/* Compute length automatically*/);
35658 break;
35659 case 'm':
35660 /* Machine name */
35661 jx9_result_string(pCtx, sName.machine, -1/* Compute length automatically*/);
35662 break;
35663 default:
35664 jx9_result_string_format(pCtx,
35665 "%s %s %s %s %s",
35666 sName.sysname,
35667 sName.release,
35668 sName.version,
35669 sName.nodename,
35670 sName.machine
35671 );
35672 break;
35673 }
35674#else
35675 jx9_result_string(pCtx, "Host Operating System/localhost", (int)sizeof("Host Operating System/localhost")-1);
35676#endif
35677 return JX9_OK;
35678}
35679/*
35680 * Section:
35681 * IO stream implementation.
35682 * Authors:
35683 * Symisc Systems, devel@symisc.net.
35684 * Copyright (C) Symisc Systems, http://jx9.symisc.net
35685 * Status:
35686 * Stable.
35687 */
35688typedef struct io_private io_private;
35689struct io_private
35690{
35691 const jx9_io_stream *pStream; /* Underlying IO device */
35692 void *pHandle; /* IO handle */
35693 /* Unbuffered IO */
35694 SyBlob sBuffer; /* Working buffer */
35695 sxu32 nOfft; /* Current read offset */
35696 sxu32 iMagic; /* Sanity check to avoid misuse */
35697};
35698#define IO_PRIVATE_MAGIC 0xFEAC14
35699/* Make sure we are dealing with a valid io_private instance */
35700#define IO_PRIVATE_INVALID(IO) ( IO == 0 || IO->iMagic != IO_PRIVATE_MAGIC )
35701/* Forward declaration */
35702static void ResetIOPrivate(io_private *pDev);
35703/*
35704 * bool ftruncate(resource $handle, int64 $size)
35705 * Truncates a file to a given length.
35706 * Parameters
35707 * $handle
35708 * The file pointer.
35709 * Note:
35710 * The handle must be open for writing.
35711 * $size
35712 * The size to truncate to.
35713 * Return
35714 * TRUE on success or FALSE on failure.
35715 */
35716static int jx9Builtin_ftruncate(jx9_context *pCtx, int nArg, jx9_value **apArg)
35717{
35718 const jx9_io_stream *pStream;
35719 io_private *pDev;
35720 int rc;
35721 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
35722 /* Missing/Invalid arguments, return FALSE */
35723 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35724 jx9_result_bool(pCtx, 0);
35725 return JX9_OK;
35726 }
35727 /* Extract our private data */
35728 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35729 /* Make sure we are dealing with a valid io_private instance */
35730 if( IO_PRIVATE_INVALID(pDev) ){
35731 /*Expecting an IO handle */
35732 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35733 jx9_result_bool(pCtx, 0);
35734 return JX9_OK;
35735 }
35736 /* Point to the target IO stream device */
35737 pStream = pDev->pStream;
35738 if( pStream == 0 || pStream->xTrunc == 0){
35739 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35740 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
35741 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35742 );
35743 jx9_result_bool(pCtx, 0);
35744 return JX9_OK;
35745 }
35746 /* Perform the requested operation */
35747 rc = pStream->xTrunc(pDev->pHandle, jx9_value_to_int64(apArg[1]));
35748 if( rc == JX9_OK ){
35749 /* Discard buffered data */
35750 ResetIOPrivate(pDev);
35751 }
35752 /* IO result */
35753 jx9_result_bool(pCtx, rc == JX9_OK);
35754 return JX9_OK;
35755}
35756/*
35757 * int fseek(resource $handle, int $offset[, int $whence = SEEK_SET ])
35758 * Seeks on a file pointer.
35759 * Parameters
35760 * $handle
35761 * A file system pointer resource that is typically created using fopen().
35762 * $offset
35763 * The offset.
35764 * To move to a position before the end-of-file, you need to pass a negative
35765 * value in offset and set whence to SEEK_END.
35766 * whence
35767 * whence values are:
35768 * SEEK_SET - Set position equal to offset bytes.
35769 * SEEK_CUR - Set position to current location plus offset.
35770 * SEEK_END - Set position to end-of-file plus offset.
35771 * Return
35772 * 0 on success, -1 on failure
35773 */
35774static int jx9Builtin_fseek(jx9_context *pCtx, int nArg, jx9_value **apArg)
35775{
35776 const jx9_io_stream *pStream;
35777 io_private *pDev;
35778 jx9_int64 iOfft;
35779 int whence;
35780 int rc;
35781 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
35782 /* Missing/Invalid arguments, return FALSE */
35783 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35784 jx9_result_int(pCtx, -1);
35785 return JX9_OK;
35786 }
35787 /* Extract our private data */
35788 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35789 /* Make sure we are dealing with a valid io_private instance */
35790 if( IO_PRIVATE_INVALID(pDev) ){
35791 /*Expecting an IO handle */
35792 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35793 jx9_result_int(pCtx, -1);
35794 return JX9_OK;
35795 }
35796 /* Point to the target IO stream device */
35797 pStream = pDev->pStream;
35798 if( pStream == 0 || pStream->xSeek == 0){
35799 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35800 "IO routine(%s) not implemented in the underlying stream(%s) device",
35801 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35802 );
35803 jx9_result_int(pCtx, -1);
35804 return JX9_OK;
35805 }
35806 /* Extract the offset */
35807 iOfft = jx9_value_to_int64(apArg[1]);
35808 whence = 0;/* SEEK_SET */
35809 if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
35810 whence = jx9_value_to_int(apArg[2]);
35811 }
35812 /* Perform the requested operation */
35813 rc = pStream->xSeek(pDev->pHandle, iOfft, whence);
35814 if( rc == JX9_OK ){
35815 /* Ignore buffered data */
35816 ResetIOPrivate(pDev);
35817 }
35818 /* IO result */
35819 jx9_result_int(pCtx, rc == JX9_OK ? 0 : - 1);
35820 return JX9_OK;
35821}
35822/*
35823 * int64 ftell(resource $handle)
35824 * Returns the current position of the file read/write pointer.
35825 * Parameters
35826 * $handle
35827 * The file pointer.
35828 * Return
35829 * Returns the position of the file pointer referenced by handle
35830 * as an integer; i.e., its offset into the file stream.
35831 * FALSE is returned on failure.
35832 */
35833static int jx9Builtin_ftell(jx9_context *pCtx, int nArg, jx9_value **apArg)
35834{
35835 const jx9_io_stream *pStream;
35836 io_private *pDev;
35837 jx9_int64 iOfft;
35838 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
35839 /* Missing/Invalid arguments, return FALSE */
35840 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35841 jx9_result_bool(pCtx, 0);
35842 return JX9_OK;
35843 }
35844 /* Extract our private data */
35845 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35846 /* Make sure we are dealing with a valid io_private instance */
35847 if( IO_PRIVATE_INVALID(pDev) ){
35848 /*Expecting an IO handle */
35849 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35850 jx9_result_bool(pCtx, 0);
35851 return JX9_OK;
35852 }
35853 /* Point to the target IO stream device */
35854 pStream = pDev->pStream;
35855 if( pStream == 0 || pStream->xTell == 0){
35856 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35857 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
35858 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35859 );
35860 jx9_result_bool(pCtx, 0);
35861 return JX9_OK;
35862 }
35863 /* Perform the requested operation */
35864 iOfft = pStream->xTell(pDev->pHandle);
35865 /* IO result */
35866 jx9_result_int64(pCtx, iOfft);
35867 return JX9_OK;
35868}
35869/*
35870 * bool rewind(resource $handle)
35871 * Rewind the position of a file pointer.
35872 * Parameters
35873 * $handle
35874 * The file pointer.
35875 * Return
35876 * TRUE on success or FALSE on failure.
35877 */
35878static int jx9Builtin_rewind(jx9_context *pCtx, int nArg, jx9_value **apArg)
35879{
35880 const jx9_io_stream *pStream;
35881 io_private *pDev;
35882 int rc;
35883 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
35884 /* Missing/Invalid arguments, return FALSE */
35885 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35886 jx9_result_bool(pCtx, 0);
35887 return JX9_OK;
35888 }
35889 /* Extract our private data */
35890 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35891 /* Make sure we are dealing with a valid io_private instance */
35892 if( IO_PRIVATE_INVALID(pDev) ){
35893 /*Expecting an IO handle */
35894 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35895 jx9_result_bool(pCtx, 0);
35896 return JX9_OK;
35897 }
35898 /* Point to the target IO stream device */
35899 pStream = pDev->pStream;
35900 if( pStream == 0 || pStream->xSeek == 0){
35901 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35902 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
35903 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35904 );
35905 jx9_result_bool(pCtx, 0);
35906 return JX9_OK;
35907 }
35908 /* Perform the requested operation */
35909 rc = pStream->xSeek(pDev->pHandle, 0, 0/*SEEK_SET*/);
35910 if( rc == JX9_OK ){
35911 /* Ignore buffered data */
35912 ResetIOPrivate(pDev);
35913 }
35914 /* IO result */
35915 jx9_result_bool(pCtx, rc == JX9_OK);
35916 return JX9_OK;
35917}
35918/*
35919 * bool fflush(resource $handle)
35920 * Flushes the output to a file.
35921 * Parameters
35922 * $handle
35923 * The file pointer.
35924 * Return
35925 * TRUE on success or FALSE on failure.
35926 */
35927static int jx9Builtin_fflush(jx9_context *pCtx, int nArg, jx9_value **apArg)
35928{
35929 const jx9_io_stream *pStream;
35930 io_private *pDev;
35931 int rc;
35932 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
35933 /* Missing/Invalid arguments, return FALSE */
35934 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35935 jx9_result_bool(pCtx, 0);
35936 return JX9_OK;
35937 }
35938 /* Extract our private data */
35939 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35940 /* Make sure we are dealing with a valid io_private instance */
35941 if( IO_PRIVATE_INVALID(pDev) ){
35942 /*Expecting an IO handle */
35943 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35944 jx9_result_bool(pCtx, 0);
35945 return JX9_OK;
35946 }
35947 /* Point to the target IO stream device */
35948 pStream = pDev->pStream;
35949 if( pStream == 0 || pStream->xSync == 0){
35950 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35951 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
35952 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35953 );
35954 jx9_result_bool(pCtx, 0);
35955 return JX9_OK;
35956 }
35957 /* Perform the requested operation */
35958 rc = pStream->xSync(pDev->pHandle);
35959 /* IO result */
35960 jx9_result_bool(pCtx, rc == JX9_OK);
35961 return JX9_OK;
35962}
35963/*
35964 * bool feof(resource $handle)
35965 * Tests for end-of-file on a file pointer.
35966 * Parameters
35967 * $handle
35968 * The file pointer.
35969 * Return
35970 * Returns TRUE if the file pointer is at EOF.FALSE otherwise
35971 */
35972static int jx9Builtin_feof(jx9_context *pCtx, int nArg, jx9_value **apArg)
35973{
35974 const jx9_io_stream *pStream;
35975 io_private *pDev;
35976 int rc;
35977 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
35978 /* Missing/Invalid arguments */
35979 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35980 jx9_result_bool(pCtx, 1);
35981 return JX9_OK;
35982 }
35983 /* Extract our private data */
35984 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35985 /* Make sure we are dealing with a valid io_private instance */
35986 if( IO_PRIVATE_INVALID(pDev) ){
35987 /*Expecting an IO handle */
35988 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35989 jx9_result_bool(pCtx, 1);
35990 return JX9_OK;
35991 }
35992 /* Point to the target IO stream device */
35993 pStream = pDev->pStream;
35994 if( pStream == 0 ){
35995 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35996 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
35997 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35998 );
35999 jx9_result_bool(pCtx, 1);
36000 return JX9_OK;
36001 }
36002 rc = SXERR_EOF;
36003 /* Perform the requested operation */
36004 if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
36005 /* Data is available */
36006 rc = JX9_OK;
36007 }else{
36008 char zBuf[4096];
36009 jx9_int64 n;
36010 /* Perform a buffered read */
36011 n = pStream->xRead(pDev->pHandle, zBuf, sizeof(zBuf));
36012 if( n > 0 ){
36013 /* Copy buffered data */
36014 SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n);
36015 rc = JX9_OK;
36016 }
36017 }
36018 /* EOF or not */
36019 jx9_result_bool(pCtx, rc == SXERR_EOF);
36020 return JX9_OK;
36021}
36022/*
36023 * Read n bytes from the underlying IO stream device.
36024 * Return total numbers of bytes readen on success. A number < 1 on failure
36025 * [i.e: IO error ] or EOF.
36026 */
36027static jx9_int64 StreamRead(io_private *pDev, void *pBuf, jx9_int64 nLen)
36028{
36029 const jx9_io_stream *pStream = pDev->pStream;
36030 char *zBuf = (char *)pBuf;
36031 jx9_int64 n, nRead;
36032 n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
36033 if( n > 0 ){
36034 if( n > nLen ){
36035 n = nLen;
36036 }
36037 /* Copy the buffered data */
36038 SyMemcpy(SyBlobDataAt(&pDev->sBuffer, pDev->nOfft), pBuf, (sxu32)n);
36039 /* Update the read offset */
36040 pDev->nOfft += (sxu32)n;
36041 if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){
36042 /* Reset the working buffer so that we avoid excessive memory allocation */
36043 SyBlobReset(&pDev->sBuffer);
36044 pDev->nOfft = 0;
36045 }
36046 nLen -= n;
36047 if( nLen < 1 ){
36048 /* All done */
36049 return n;
36050 }
36051 /* Advance the cursor */
36052 zBuf += n;
36053 }
36054 /* Read without buffering */
36055 nRead = pStream->xRead(pDev->pHandle, zBuf, nLen);
36056 if( nRead > 0 ){
36057 n += nRead;
36058 }else if( n < 1 ){
36059 /* EOF or IO error */
36060 return nRead;
36061 }
36062 return n;
36063}
36064/*
36065 * Extract a single line from the buffered input.
36066 */
36067static sxi32 GetLine(io_private *pDev, jx9_int64 *pLen, const char **pzLine)
36068{
36069 const char *zIn, *zEnd, *zPtr;
36070 zIn = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
36071 zEnd = &zIn[SyBlobLength(&pDev->sBuffer)-pDev->nOfft];
36072 zPtr = zIn;
36073 while( zIn < zEnd ){
36074 if( zIn[0] == '\n' ){
36075 /* Line found */
36076 zIn++; /* Include the line ending as requested by the JX9 specification */
36077 *pLen = (jx9_int64)(zIn-zPtr);
36078 *pzLine = zPtr;
36079 return SXRET_OK;
36080 }
36081 zIn++;
36082 }
36083 /* No line were found */
36084 return SXERR_NOTFOUND;
36085}
36086/*
36087 * Read a single line from the underlying IO stream device.
36088 */
36089static jx9_int64 StreamReadLine(io_private *pDev, const char **pzData, jx9_int64 nMaxLen)
36090{
36091 const jx9_io_stream *pStream = pDev->pStream;
36092 char zBuf[8192];
36093 jx9_int64 n;
36094 sxi32 rc;
36095 n = 0;
36096 if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){
36097 /* Reset the working buffer so that we avoid excessive memory allocation */
36098 SyBlobReset(&pDev->sBuffer);
36099 pDev->nOfft = 0;
36100 }
36101 if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
36102 /* Check if there is a line */
36103 rc = GetLine(pDev, &n, pzData);
36104 if( rc == SXRET_OK ){
36105 /* Got line, update the cursor */
36106 pDev->nOfft += (sxu32)n;
36107 return n;
36108 }
36109 }
36110 /* Perform the read operation until a new line is extracted or length
36111 * limit is reached.
36112 */
36113 for(;;){
36114 n = pStream->xRead(pDev->pHandle, zBuf, (nMaxLen > 0 && nMaxLen < sizeof(zBuf)) ? nMaxLen : sizeof(zBuf));
36115 if( n < 1 ){
36116 /* EOF or IO error */
36117 break;
36118 }
36119 /* Append the data just read */
36120 SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n);
36121 /* Try to extract a line */
36122 rc = GetLine(pDev, &n, pzData);
36123 if( rc == SXRET_OK ){
36124 /* Got one, return immediately */
36125 pDev->nOfft += (sxu32)n;
36126 return n;
36127 }
36128 if( nMaxLen > 0 && (SyBlobLength(&pDev->sBuffer) - pDev->nOfft >= nMaxLen) ){
36129 /* Read limit reached, return the available data */
36130 *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
36131 n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
36132 /* Reset the working buffer */
36133 SyBlobReset(&pDev->sBuffer);
36134 pDev->nOfft = 0;
36135 return n;
36136 }
36137 }
36138 if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
36139 /* Read limit reached, return the available data */
36140 *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
36141 n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
36142 /* Reset the working buffer */
36143 SyBlobReset(&pDev->sBuffer);
36144 pDev->nOfft = 0;
36145 }
36146 return n;
36147}
36148/*
36149 * Open an IO stream handle.
36150 * Notes on stream:
36151 * According to the JX9 reference manual.
36152 * In its simplest definition, a stream is a resource object which exhibits streamable behavior.
36153 * That is, it can be read from or written to in a linear fashion, and may be able to fseek()
36154 * to an arbitrary locations within the stream.
36155 * A wrapper is additional code which tells the stream how to handle specific protocols/encodings.
36156 * For example, the http wrapper knows how to translate a URL into an HTTP/1.0 request for a file
36157 * on a remote server.
36158 * A stream is referenced as: scheme://target
36159 * scheme(string) - The name of the wrapper to be used. Examples include: file, http...
36160 * If no wrapper is specified, the function default is used (typically file://).
36161 * target - Depends on the wrapper used. For filesystem related streams this is typically a path
36162 * and filename of the desired file. For network related streams this is typically a hostname, often
36163 * with a path appended.
36164 *
36165 * Note that JX9 IO streams looks like JX9 streams but their implementation differ greately.
36166 * Please refer to the official documentation for a full discussion.
36167 * This function return a handle on success. Otherwise null.
36168 */
36169JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile,
36170 int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew)
36171{
36172 void *pHandle = 0; /* cc warning */
36173 SyString sFile;
36174 int rc;
36175 if( pStream == 0 ){
36176 /* No such stream device */
36177 return 0;
36178 }
36179 SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile));
36180 if( use_include ){
36181 if( sFile.zString[0] == '/' ||
36182#ifdef __WINNT__
36183 (sFile.nByte > 2 && sFile.zString[1] == ':' && (sFile.zString[2] == '\\' || sFile.zString[2] == '/') ) ||
36184#endif
36185 (sFile.nByte > 1 && sFile.zString[0] == '.' && sFile.zString[1] == '/') ||
36186 (sFile.nByte > 2 && sFile.zString[0] == '.' && sFile.zString[1] == '.' && sFile.zString[2] == '/') ){
36187 /* Open the file directly */
36188 rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
36189 }else{
36190 SyString *pPath;
36191 SyBlob sWorker;
36192#ifdef __WINNT__
36193 static const int c = '\\';
36194#else
36195 static const int c = '/';
36196#endif
36197 /* Init the path builder working buffer */
36198 SyBlobInit(&sWorker, &pVm->sAllocator);
36199 /* Build a path from the set of include path */
36200 SySetResetCursor(&pVm->aPaths);
36201 rc = SXERR_IO;
36202 while( SXRET_OK == SySetGetNextEntry(&pVm->aPaths, (void **)&pPath) ){
36203 /* Build full path */
36204 SyBlobFormat(&sWorker, "%z%c%z", pPath, c, &sFile);
36205 /* Append null terminator */
36206 if( SXRET_OK != SyBlobNullAppend(&sWorker) ){
36207 continue;
36208 }
36209 /* Try to open the file */
36210 rc = pStream->xOpen((const char *)SyBlobData(&sWorker), iFlags, pResource, &pHandle);
36211 if( rc == JX9_OK ){
36212 if( bPushInclude ){
36213 /* Mark as included */
36214 jx9VmPushFilePath(pVm, (const char *)SyBlobData(&sWorker), SyBlobLength(&sWorker), FALSE, pNew);
36215 }
36216 break;
36217 }
36218 /* Reset the working buffer */
36219 SyBlobReset(&sWorker);
36220 /* Check the next path */
36221 }
36222 SyBlobRelease(&sWorker);
36223 }
36224 if( rc == JX9_OK ){
36225 if( bPushInclude ){
36226 /* Mark as included */
36227 jx9VmPushFilePath(pVm, sFile.zString, sFile.nByte, FALSE, pNew);
36228 }
36229 }
36230 }else{
36231 /* Open the URI direcly */
36232 rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
36233 }
36234 if( rc != JX9_OK ){
36235 /* IO error */
36236 return 0;
36237 }
36238 /* Return the file handle */
36239 return pHandle;
36240}
36241/*
36242 * Read the whole contents of an open IO stream handle [i.e local file/URL..]
36243 * Store the read data in the given BLOB (last argument).
36244 * The read operation is stopped when he hit the EOF or an IO error occurs.
36245 */
36246JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut)
36247{
36248 jx9_int64 nRead;
36249 char zBuf[8192]; /* 8K */
36250 int rc;
36251 /* Perform the requested operation */
36252 for(;;){
36253 nRead = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
36254 if( nRead < 1 ){
36255 /* EOF or IO error */
36256 break;
36257 }
36258 /* Append contents */
36259 rc = SyBlobAppend(pOut, zBuf, (sxu32)nRead);
36260 if( rc != SXRET_OK ){
36261 break;
36262 }
36263 }
36264 return SyBlobLength(pOut) > 0 ? SXRET_OK : -1;
36265}
36266/*
36267 * Close an open IO stream handle [i.e local file/URI..].
36268 */
36269JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle)
36270{
36271 if( pStream->xClose ){
36272 pStream->xClose(pHandle);
36273 }
36274}
36275/*
36276 * string fgetc(resource $handle)
36277 * Gets a character from the given file pointer.
36278 * Parameters
36279 * $handle
36280 * The file pointer.
36281 * Return
36282 * Returns a string containing a single character read from the file
36283 * pointed to by handle. Returns FALSE on EOF.
36284 * WARNING
36285 * This operation is extremely slow.Avoid using it.
36286 */
36287static int jx9Builtin_fgetc(jx9_context *pCtx, int nArg, jx9_value **apArg)
36288{
36289 const jx9_io_stream *pStream;
36290 io_private *pDev;
36291 int c, n;
36292 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36293 /* Missing/Invalid arguments, return FALSE */
36294 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36295 jx9_result_bool(pCtx, 0);
36296 return JX9_OK;
36297 }
36298 /* Extract our private data */
36299 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36300 /* Make sure we are dealing with a valid io_private instance */
36301 if( IO_PRIVATE_INVALID(pDev) ){
36302 /*Expecting an IO handle */
36303 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36304 jx9_result_bool(pCtx, 0);
36305 return JX9_OK;
36306 }
36307 /* Point to the target IO stream device */
36308 pStream = pDev->pStream;
36309 if( pStream == 0 ){
36310 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36311 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36312 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36313 );
36314 jx9_result_bool(pCtx, 0);
36315 return JX9_OK;
36316 }
36317 /* Perform the requested operation */
36318 n = (int)StreamRead(pDev, (void *)&c, sizeof(char));
36319 /* IO result */
36320 if( n < 1 ){
36321 /* EOF or error, return FALSE */
36322 jx9_result_bool(pCtx, 0);
36323 }else{
36324 /* Return the string holding the character */
36325 jx9_result_string(pCtx, (const char *)&c, sizeof(char));
36326 }
36327 return JX9_OK;
36328}
36329/*
36330 * string fgets(resource $handle[, int64 $length ])
36331 * Gets line from file pointer.
36332 * Parameters
36333 * $handle
36334 * The file pointer.
36335 * $length
36336 * Reading ends when length - 1 bytes have been read, on a newline
36337 * (which is included in the return value), or on EOF (whichever comes first).
36338 * If no length is specified, it will keep reading from the stream until it reaches
36339 * the end of the line.
36340 * Return
36341 * Returns a string of up to length - 1 bytes read from the file pointed to by handle.
36342 * If there is no more data to read in the file pointer, then FALSE is returned.
36343 * If an error occurs, FALSE is returned.
36344 */
36345static int jx9Builtin_fgets(jx9_context *pCtx, int nArg, jx9_value **apArg)
36346{
36347 const jx9_io_stream *pStream;
36348 const char *zLine;
36349 io_private *pDev;
36350 jx9_int64 n, nLen;
36351 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36352 /* Missing/Invalid arguments, return FALSE */
36353 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36354 jx9_result_bool(pCtx, 0);
36355 return JX9_OK;
36356 }
36357 /* Extract our private data */
36358 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36359 /* Make sure we are dealing with a valid io_private instance */
36360 if( IO_PRIVATE_INVALID(pDev) ){
36361 /*Expecting an IO handle */
36362 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36363 jx9_result_bool(pCtx, 0);
36364 return JX9_OK;
36365 }
36366 /* Point to the target IO stream device */
36367 pStream = pDev->pStream;
36368 if( pStream == 0 ){
36369 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36370 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36371 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36372 );
36373 jx9_result_bool(pCtx, 0);
36374 return JX9_OK;
36375 }
36376 nLen = -1;
36377 if( nArg > 1 ){
36378 /* Maximum data to read */
36379 nLen = jx9_value_to_int64(apArg[1]);
36380 }
36381 /* Perform the requested operation */
36382 n = StreamReadLine(pDev, &zLine, nLen);
36383 if( n < 1 ){
36384 /* EOF or IO error, return FALSE */
36385 jx9_result_bool(pCtx, 0);
36386 }else{
36387 /* Return the freshly extracted line */
36388 jx9_result_string(pCtx, zLine, (int)n);
36389 }
36390 return JX9_OK;
36391}
36392/*
36393 * string fread(resource $handle, int64 $length)
36394 * Binary-safe file read.
36395 * Parameters
36396 * $handle
36397 * The file pointer.
36398 * $length
36399 * Up to length number of bytes read.
36400 * Return
36401 * The data readen on success or FALSE on failure.
36402 */
36403static int jx9Builtin_fread(jx9_context *pCtx, int nArg, jx9_value **apArg)
36404{
36405 const jx9_io_stream *pStream;
36406 io_private *pDev;
36407 jx9_int64 nRead;
36408 void *pBuf;
36409 int nLen;
36410 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36411 /* Missing/Invalid arguments, return FALSE */
36412 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36413 jx9_result_bool(pCtx, 0);
36414 return JX9_OK;
36415 }
36416 /* Extract our private data */
36417 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36418 /* Make sure we are dealing with a valid io_private instance */
36419 if( IO_PRIVATE_INVALID(pDev) ){
36420 /*Expecting an IO handle */
36421 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36422 jx9_result_bool(pCtx, 0);
36423 return JX9_OK;
36424 }
36425 /* Point to the target IO stream device */
36426 pStream = pDev->pStream;
36427 if( pStream == 0 ){
36428 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36429 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36430 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36431 );
36432 jx9_result_bool(pCtx, 0);
36433 return JX9_OK;
36434 }
36435 nLen = 4096;
36436 if( nArg > 1 ){
36437 nLen = jx9_value_to_int(apArg[1]);
36438 if( nLen < 1 ){
36439 /* Invalid length, set a default length */
36440 nLen = 4096;
36441 }
36442 }
36443 /* Allocate enough buffer */
36444 pBuf = jx9_context_alloc_chunk(pCtx, (unsigned int)nLen, FALSE, FALSE);
36445 if( pBuf == 0 ){
36446 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
36447 jx9_result_bool(pCtx, 0);
36448 return JX9_OK;
36449 }
36450 /* Perform the requested operation */
36451 nRead = StreamRead(pDev, pBuf, (jx9_int64)nLen);
36452 if( nRead < 1 ){
36453 /* Nothing read, return FALSE */
36454 jx9_result_bool(pCtx, 0);
36455 }else{
36456 /* Make a copy of the data just read */
36457 jx9_result_string(pCtx, (const char *)pBuf, (int)nRead);
36458 }
36459 /* Release the buffer */
36460 jx9_context_free_chunk(pCtx, pBuf);
36461 return JX9_OK;
36462}
36463/*
36464 * array fgetcsv(resource $handle [, int $length = 0
36465 * [, string $delimiter = ', '[, string $enclosure = '"'[, string $escape='\\']]]])
36466 * Gets line from file pointer and parse for CSV fields.
36467 * Parameters
36468 * $handle
36469 * The file pointer.
36470 * $length
36471 * Reading ends when length - 1 bytes have been read, on a newline
36472 * (which is included in the return value), or on EOF (whichever comes first).
36473 * If no length is specified, it will keep reading from the stream until it reaches
36474 * the end of the line.
36475 * $delimiter
36476 * Set the field delimiter (one character only).
36477 * $enclosure
36478 * Set the field enclosure character (one character only).
36479 * $escape
36480 * Set the escape character (one character only). Defaults as a backslash (\)
36481 * Return
36482 * Returns a string of up to length - 1 bytes read from the file pointed to by handle.
36483 * If there is no more data to read in the file pointer, then FALSE is returned.
36484 * If an error occurs, FALSE is returned.
36485 */
36486static int jx9Builtin_fgetcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
36487{
36488 const jx9_io_stream *pStream;
36489 const char *zLine;
36490 io_private *pDev;
36491 jx9_int64 n, nLen;
36492 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36493 /* Missing/Invalid arguments, return FALSE */
36494 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36495 jx9_result_bool(pCtx, 0);
36496 return JX9_OK;
36497 }
36498 /* Extract our private data */
36499 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36500 /* Make sure we are dealing with a valid io_private instance */
36501 if( IO_PRIVATE_INVALID(pDev) ){
36502 /*Expecting an IO handle */
36503 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36504 jx9_result_bool(pCtx, 0);
36505 return JX9_OK;
36506 }
36507 /* Point to the target IO stream device */
36508 pStream = pDev->pStream;
36509 if( pStream == 0 ){
36510 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36511 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36512 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36513 );
36514 jx9_result_bool(pCtx, 0);
36515 return JX9_OK;
36516 }
36517 nLen = -1;
36518 if( nArg > 1 ){
36519 /* Maximum data to read */
36520 nLen = jx9_value_to_int64(apArg[1]);
36521 }
36522 /* Perform the requested operation */
36523 n = StreamReadLine(pDev, &zLine, nLen);
36524 if( n < 1 ){
36525 /* EOF or IO error, return FALSE */
36526 jx9_result_bool(pCtx, 0);
36527 }else{
36528 jx9_value *pArray;
36529 int delim = ','; /* Delimiter */
36530 int encl = '"' ; /* Enclosure */
36531 int escape = '\\'; /* Escape character */
36532 if( nArg > 2 ){
36533 const char *zPtr;
36534 int i;
36535 if( jx9_value_is_string(apArg[2]) ){
36536 /* Extract the delimiter */
36537 zPtr = jx9_value_to_string(apArg[2], &i);
36538 if( i > 0 ){
36539 delim = zPtr[0];
36540 }
36541 }
36542 if( nArg > 3 ){
36543 if( jx9_value_is_string(apArg[3]) ){
36544 /* Extract the enclosure */
36545 zPtr = jx9_value_to_string(apArg[3], &i);
36546 if( i > 0 ){
36547 encl = zPtr[0];
36548 }
36549 }
36550 if( nArg > 4 ){
36551 if( jx9_value_is_string(apArg[4]) ){
36552 /* Extract the escape character */
36553 zPtr = jx9_value_to_string(apArg[4], &i);
36554 if( i > 0 ){
36555 escape = zPtr[0];
36556 }
36557 }
36558 }
36559 }
36560 }
36561 /* Create our array */
36562 pArray = jx9_context_new_array(pCtx);
36563 if( pArray == 0 ){
36564 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
36565 jx9_result_null(pCtx);
36566 return JX9_OK;
36567 }
36568 /* Parse the raw input */
36569 jx9ProcessCsv(zLine, (int)n, delim, encl, escape, jx9CsvConsumer, pArray);
36570 /* Return the freshly created array */
36571 jx9_result_value(pCtx, pArray);
36572 }
36573 return JX9_OK;
36574}
36575/*
36576 * string fgetss(resource $handle [, int $length [, string $allowable_tags ]])
36577 * Gets line from file pointer and strip HTML tags.
36578 * Parameters
36579 * $handle
36580 * The file pointer.
36581 * $length
36582 * Reading ends when length - 1 bytes have been read, on a newline
36583 * (which is included in the return value), or on EOF (whichever comes first).
36584 * If no length is specified, it will keep reading from the stream until it reaches
36585 * the end of the line.
36586 * $allowable_tags
36587 * You can use the optional second parameter to specify tags which should not be stripped.
36588 * Return
36589 * Returns a string of up to length - 1 bytes read from the file pointed to by
36590 * handle, with all HTML and JX9 code stripped. If an error occurs, returns FALSE.
36591 */
36592static int jx9Builtin_fgetss(jx9_context *pCtx, int nArg, jx9_value **apArg)
36593{
36594 const jx9_io_stream *pStream;
36595 const char *zLine;
36596 io_private *pDev;
36597 jx9_int64 n, nLen;
36598 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36599 /* Missing/Invalid arguments, return FALSE */
36600 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36601 jx9_result_bool(pCtx, 0);
36602 return JX9_OK;
36603 }
36604 /* Extract our private data */
36605 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36606 /* Make sure we are dealing with a valid io_private instance */
36607 if( IO_PRIVATE_INVALID(pDev) ){
36608 /*Expecting an IO handle */
36609 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36610 jx9_result_bool(pCtx, 0);
36611 return JX9_OK;
36612 }
36613 /* Point to the target IO stream device */
36614 pStream = pDev->pStream;
36615 if( pStream == 0 ){
36616 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36617 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36618 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36619 );
36620 jx9_result_bool(pCtx, 0);
36621 return JX9_OK;
36622 }
36623 nLen = -1;
36624 if( nArg > 1 ){
36625 /* Maximum data to read */
36626 nLen = jx9_value_to_int64(apArg[1]);
36627 }
36628 /* Perform the requested operation */
36629 n = StreamReadLine(pDev, &zLine, nLen);
36630 if( n < 1 ){
36631 /* EOF or IO error, return FALSE */
36632 jx9_result_bool(pCtx, 0);
36633 }else{
36634 const char *zTaglist = 0;
36635 int nTaglen = 0;
36636 if( nArg > 2 && jx9_value_is_string(apArg[2]) ){
36637 /* Allowed tag */
36638 zTaglist = jx9_value_to_string(apArg[2], &nTaglen);
36639 }
36640 /* Process data just read */
36641 jx9StripTagsFromString(pCtx, zLine, (int)n, zTaglist, nTaglen);
36642 }
36643 return JX9_OK;
36644}
36645/*
36646 * string readdir(resource $dir_handle)
36647 * Read entry from directory handle.
36648 * Parameter
36649 * $dir_handle
36650 * The directory handle resource previously opened with opendir().
36651 * Return
36652 * Returns the filename on success or FALSE on failure.
36653 */
36654static int jx9Builtin_readdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
36655{
36656 const jx9_io_stream *pStream;
36657 io_private *pDev;
36658 int rc;
36659 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36660 /* Missing/Invalid arguments, return FALSE */
36661 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36662 jx9_result_bool(pCtx, 0);
36663 return JX9_OK;
36664 }
36665 /* Extract our private data */
36666 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36667 /* Make sure we are dealing with a valid io_private instance */
36668 if( IO_PRIVATE_INVALID(pDev) ){
36669 /*Expecting an IO handle */
36670 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36671 jx9_result_bool(pCtx, 0);
36672 return JX9_OK;
36673 }
36674 /* Point to the target IO stream device */
36675 pStream = pDev->pStream;
36676 if( pStream == 0 || pStream->xReadDir == 0 ){
36677 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36678 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36679 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36680 );
36681 jx9_result_bool(pCtx, 0);
36682 return JX9_OK;
36683 }
36684 jx9_result_bool(pCtx, 0);
36685 /* Perform the requested operation */
36686 rc = pStream->xReadDir(pDev->pHandle, pCtx);
36687 if( rc != JX9_OK ){
36688 /* Return FALSE */
36689 jx9_result_bool(pCtx, 0);
36690 }
36691 return JX9_OK;
36692}
36693/*
36694 * void rewinddir(resource $dir_handle)
36695 * Rewind directory handle.
36696 * Parameter
36697 * $dir_handle
36698 * The directory handle resource previously opened with opendir().
36699 * Return
36700 * FALSE on failure.
36701 */
36702static int jx9Builtin_rewinddir(jx9_context *pCtx, int nArg, jx9_value **apArg)
36703{
36704 const jx9_io_stream *pStream;
36705 io_private *pDev;
36706 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36707 /* Missing/Invalid arguments, return FALSE */
36708 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36709 jx9_result_bool(pCtx, 0);
36710 return JX9_OK;
36711 }
36712 /* Extract our private data */
36713 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36714 /* Make sure we are dealing with a valid io_private instance */
36715 if( IO_PRIVATE_INVALID(pDev) ){
36716 /*Expecting an IO handle */
36717 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36718 jx9_result_bool(pCtx, 0);
36719 return JX9_OK;
36720 }
36721 /* Point to the target IO stream device */
36722 pStream = pDev->pStream;
36723 if( pStream == 0 || pStream->xRewindDir == 0 ){
36724 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36725 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36726 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36727 );
36728 jx9_result_bool(pCtx, 0);
36729 return JX9_OK;
36730 }
36731 /* Perform the requested operation */
36732 pStream->xRewindDir(pDev->pHandle);
36733 return JX9_OK;
36734 }
36735/* Forward declaration */
36736static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut);
36737static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev);
36738/*
36739 * void closedir(resource $dir_handle)
36740 * Close directory handle.
36741 * Parameter
36742 * $dir_handle
36743 * The directory handle resource previously opened with opendir().
36744 * Return
36745 * FALSE on failure.
36746 */
36747static int jx9Builtin_closedir(jx9_context *pCtx, int nArg, jx9_value **apArg)
36748{
36749 const jx9_io_stream *pStream;
36750 io_private *pDev;
36751 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36752 /* Missing/Invalid arguments, return FALSE */
36753 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36754 jx9_result_bool(pCtx, 0);
36755 return JX9_OK;
36756 }
36757 /* Extract our private data */
36758 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36759 /* Make sure we are dealing with a valid io_private instance */
36760 if( IO_PRIVATE_INVALID(pDev) ){
36761 /*Expecting an IO handle */
36762 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36763 jx9_result_bool(pCtx, 0);
36764 return JX9_OK;
36765 }
36766 /* Point to the target IO stream device */
36767 pStream = pDev->pStream;
36768 if( pStream == 0 || pStream->xCloseDir == 0 ){
36769 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36770 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36771 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36772 );
36773 jx9_result_bool(pCtx, 0);
36774 return JX9_OK;
36775 }
36776 /* Perform the requested operation */
36777 pStream->xCloseDir(pDev->pHandle);
36778 /* Release the private stucture */
36779 ReleaseIOPrivate(pCtx, pDev);
36780 jx9MemObjRelease(apArg[0]);
36781 return JX9_OK;
36782 }
36783/*
36784 * resource opendir(string $path[, resource $context])
36785 * Open directory handle.
36786 * Parameters
36787 * $path
36788 * The directory path that is to be opened.
36789 * $context
36790 * A context stream resource.
36791 * Return
36792 * A directory handle resource on success, or FALSE on failure.
36793 */
36794static int jx9Builtin_opendir(jx9_context *pCtx, int nArg, jx9_value **apArg)
36795{
36796 const jx9_io_stream *pStream;
36797 const char *zPath;
36798 io_private *pDev;
36799 int iLen, rc;
36800 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
36801 /* Missing/Invalid arguments, return FALSE */
36802 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a directory path");
36803 jx9_result_bool(pCtx, 0);
36804 return JX9_OK;
36805 }
36806 /* Extract the target path */
36807 zPath = jx9_value_to_string(apArg[0], &iLen);
36808 /* Try to extract a stream */
36809 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zPath, iLen);
36810 if( pStream == 0 ){
36811 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36812 "No stream device is associated with the given path(%s)", zPath);
36813 jx9_result_bool(pCtx, 0);
36814 return JX9_OK;
36815 }
36816 if( pStream->xOpenDir == 0 ){
36817 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36818 "IO routine(%s) not implemented in the underlying stream(%s) device",
36819 jx9_function_name(pCtx), pStream->zName
36820 );
36821 jx9_result_bool(pCtx, 0);
36822 return JX9_OK;
36823 }
36824 /* Allocate a new IO private instance */
36825 pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
36826 if( pDev == 0 ){
36827 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
36828 jx9_result_bool(pCtx, 0);
36829 return JX9_OK;
36830 }
36831 /* Initialize the structure */
36832 InitIOPrivate(pCtx->pVm, pStream, pDev);
36833 /* Open the target directory */
36834 rc = pStream->xOpenDir(zPath, nArg > 1 ? apArg[1] : 0, &pDev->pHandle);
36835 if( rc != JX9_OK ){
36836 /* IO error, return FALSE */
36837 ReleaseIOPrivate(pCtx, pDev);
36838 jx9_result_bool(pCtx, 0);
36839 }else{
36840 /* Return the handle as a resource */
36841 jx9_result_resource(pCtx, pDev);
36842 }
36843 return JX9_OK;
36844}
36845/*
36846 * int readfile(string $filename[, bool $use_include_path = false [, resource $context ]])
36847 * Reads a file and writes it to the output buffer.
36848 * Parameters
36849 * $filename
36850 * The filename being read.
36851 * $use_include_path
36852 * You can use the optional second parameter and set it to
36853 * TRUE, if you want to search for the file in the include_path, too.
36854 * $context
36855 * A context stream resource.
36856 * Return
36857 * The number of bytes read from the file on success or FALSE on failure.
36858 */
36859static int jx9Builtin_readfile(jx9_context *pCtx, int nArg, jx9_value **apArg)
36860{
36861 int use_include = FALSE;
36862 const jx9_io_stream *pStream;
36863 jx9_int64 n, nRead;
36864 const char *zFile;
36865 char zBuf[8192];
36866 void *pHandle;
36867 int rc, nLen;
36868 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
36869 /* Missing/Invalid arguments, return FALSE */
36870 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
36871 jx9_result_bool(pCtx, 0);
36872 return JX9_OK;
36873 }
36874 /* Extract the file path */
36875 zFile = jx9_value_to_string(apArg[0], &nLen);
36876 /* Point to the target IO stream device */
36877 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
36878 if( pStream == 0 ){
36879 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
36880 jx9_result_bool(pCtx, 0);
36881 return JX9_OK;
36882 }
36883 if( nArg > 1 ){
36884 use_include = jx9_value_to_bool(apArg[1]);
36885 }
36886 /* Try to open the file in read-only mode */
36887 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY,
36888 use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
36889 if( pHandle == 0 ){
36890 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
36891 jx9_result_bool(pCtx, 0);
36892 return JX9_OK;
36893 }
36894 /* Perform the requested operation */
36895 nRead = 0;
36896 for(;;){
36897 n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
36898 if( n < 1 ){
36899 /* EOF or IO error, break immediately */
36900 break;
36901 }
36902 /* Output data */
36903 rc = jx9_context_output(pCtx, zBuf, (int)n);
36904 if( rc == JX9_ABORT ){
36905 break;
36906 }
36907 /* Increment counter */
36908 nRead += n;
36909 }
36910 /* Close the stream */
36911 jx9StreamCloseHandle(pStream, pHandle);
36912 /* Total number of bytes readen */
36913 jx9_result_int64(pCtx, nRead);
36914 return JX9_OK;
36915}
36916/*
36917 * string file_get_contents(string $filename[, bool $use_include_path = false
36918 * [, resource $context [, int $offset = -1 [, int $maxlen ]]]])
36919 * Reads entire file into a string.
36920 * Parameters
36921 * $filename
36922 * The filename being read.
36923 * $use_include_path
36924 * You can use the optional second parameter and set it to
36925 * TRUE, if you want to search for the file in the include_path, too.
36926 * $context
36927 * A context stream resource.
36928 * $offset
36929 * The offset where the reading starts on the original stream.
36930 * $maxlen
36931 * Maximum length of data read. The default is to read until end of file
36932 * is reached. Note that this parameter is applied to the stream processed by the filters.
36933 * Return
36934 * The function returns the read data or FALSE on failure.
36935 */
36936static int jx9Builtin_file_get_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
36937{
36938 const jx9_io_stream *pStream;
36939 jx9_int64 n, nRead, nMaxlen;
36940 int use_include = FALSE;
36941 const char *zFile;
36942 char zBuf[8192];
36943 void *pHandle;
36944 int nLen;
36945
36946 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
36947 /* Missing/Invalid arguments, return FALSE */
36948 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
36949 jx9_result_bool(pCtx, 0);
36950 return JX9_OK;
36951 }
36952 /* Extract the file path */
36953 zFile = jx9_value_to_string(apArg[0], &nLen);
36954 /* Point to the target IO stream device */
36955 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
36956 if( pStream == 0 ){
36957 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
36958 jx9_result_bool(pCtx, 0);
36959 return JX9_OK;
36960 }
36961 nMaxlen = -1;
36962 if( nArg > 1 ){
36963 use_include = jx9_value_to_bool(apArg[1]);
36964 }
36965 /* Try to open the file in read-only mode */
36966 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
36967 if( pHandle == 0 ){
36968 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
36969 jx9_result_bool(pCtx, 0);
36970 return JX9_OK;
36971 }
36972 if( nArg > 3 ){
36973 /* Extract the offset */
36974 n = jx9_value_to_int64(apArg[3]);
36975 if( n > 0 ){
36976 if( pStream->xSeek ){
36977 /* Seek to the desired offset */
36978 pStream->xSeek(pHandle, n, 0/*SEEK_SET*/);
36979 }
36980 }
36981 if( nArg > 4 ){
36982 /* Maximum data to read */
36983 nMaxlen = jx9_value_to_int64(apArg[4]);
36984 }
36985 }
36986 /* Perform the requested operation */
36987 nRead = 0;
36988 for(;;){
36989 n = pStream->xRead(pHandle, zBuf,
36990 (nMaxlen > 0 && (nMaxlen < sizeof(zBuf))) ? nMaxlen : sizeof(zBuf));
36991 if( n < 1 ){
36992 /* EOF or IO error, break immediately */
36993 break;
36994 }
36995 /* Append data */
36996 jx9_result_string(pCtx, zBuf, (int)n);
36997 /* Increment read counter */
36998 nRead += n;
36999 if( nMaxlen > 0 && nRead >= nMaxlen ){
37000 /* Read limit reached */
37001 break;
37002 }
37003 }
37004 /* Close the stream */
37005 jx9StreamCloseHandle(pStream, pHandle);
37006 /* Check if we have read something */
37007 if( jx9_context_result_buf_length(pCtx) < 1 ){
37008 /* Nothing read, return FALSE */
37009 jx9_result_bool(pCtx, 0);
37010 }
37011 return JX9_OK;
37012}
37013/*
37014 * int file_put_contents(string $filename, mixed $data[, int $flags = 0[, resource $context]])
37015 * Write a string to a file.
37016 * Parameters
37017 * $filename
37018 * Path to the file where to write the data.
37019 * $data
37020 * The data to write(Must be a string).
37021 * $flags
37022 * The value of flags can be any combination of the following
37023 * flags, joined with the binary OR (|) operator.
37024 * FILE_USE_INCLUDE_PATH Search for filename in the include directory. See include_path for more information.
37025 * FILE_APPEND If file filename already exists, append the data to the file instead of overwriting it.
37026 * LOCK_EX Acquire an exclusive lock on the file while proceeding to the writing.
37027 * context
37028 * A context stream resource.
37029 * Return
37030 * The function returns the number of bytes that were written to the file, or FALSE on failure.
37031 */
37032static int jx9Builtin_file_put_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
37033{
37034 int use_include = FALSE;
37035 const jx9_io_stream *pStream;
37036 const char *zFile;
37037 const char *zData;
37038 int iOpenFlags;
37039 void *pHandle;
37040 int iFlags;
37041 int nLen;
37042
37043 if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
37044 /* Missing/Invalid arguments, return FALSE */
37045 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
37046 jx9_result_bool(pCtx, 0);
37047 return JX9_OK;
37048 }
37049 /* Extract the file path */
37050 zFile = jx9_value_to_string(apArg[0], &nLen);
37051 /* Point to the target IO stream device */
37052 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
37053 if( pStream == 0 ){
37054 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
37055 jx9_result_bool(pCtx, 0);
37056 return JX9_OK;
37057 }
37058 /* Data to write */
37059 zData = jx9_value_to_string(apArg[1], &nLen);
37060 if( nLen < 1 ){
37061 /* Nothing to write, return immediately */
37062 jx9_result_bool(pCtx, 0);
37063 return JX9_OK;
37064 }
37065 /* Try to open the file in read-write mode */
37066 iOpenFlags = JX9_IO_OPEN_CREATE|JX9_IO_OPEN_RDWR|JX9_IO_OPEN_TRUNC;
37067 /* Extract the flags */
37068 iFlags = 0;
37069 if( nArg > 2 ){
37070 iFlags = jx9_value_to_int(apArg[2]);
37071 if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/){
37072 use_include = TRUE;
37073 }
37074 if( iFlags & 0x08 /* FILE_APPEND */){
37075 /* If the file already exists, append the data to the file
37076 * instead of overwriting it.
37077 */
37078 iOpenFlags &= ~JX9_IO_OPEN_TRUNC;
37079 /* Append mode */
37080 iOpenFlags |= JX9_IO_OPEN_APPEND;
37081 }
37082 }
37083 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, iOpenFlags, use_include,
37084 nArg > 3 ? apArg[3] : 0, FALSE, FALSE);
37085 if( pHandle == 0 ){
37086 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
37087 jx9_result_bool(pCtx, 0);
37088 return JX9_OK;
37089 }
37090 if( pStream->xWrite ){
37091 jx9_int64 n;
37092 if( (iFlags & 0x01/* LOCK_EX */) && pStream->xLock ){
37093 /* Try to acquire an exclusive lock */
37094 pStream->xLock(pHandle, 1/* LOCK_EX */);
37095 }
37096 /* Perform the write operation */
37097 n = pStream->xWrite(pHandle, (const void *)zData, nLen);
37098 if( n < 1 ){
37099 /* IO error, return FALSE */
37100 jx9_result_bool(pCtx, 0);
37101 }else{
37102 /* Total number of bytes written */
37103 jx9_result_int64(pCtx, n);
37104 }
37105 }else{
37106 /* Read-only stream */
37107 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR,
37108 "Read-only stream(%s): Cannot perform write operation",
37109 pStream ? pStream->zName : "null_stream"
37110 );
37111 jx9_result_bool(pCtx, 0);
37112 }
37113 /* Close the handle */
37114 jx9StreamCloseHandle(pStream, pHandle);
37115 return JX9_OK;
37116}
37117/*
37118 * array file(string $filename[, int $flags = 0[, resource $context]])
37119 * Reads entire file into an array.
37120 * Parameters
37121 * $filename
37122 * The filename being read.
37123 * $flags
37124 * The optional parameter flags can be one, or more, of the following constants:
37125 * FILE_USE_INCLUDE_PATH
37126 * Search for the file in the include_path.
37127 * FILE_IGNORE_NEW_LINES
37128 * Do not add newline at the end of each array element
37129 * FILE_SKIP_EMPTY_LINES
37130 * Skip empty lines
37131 * $context
37132 * A context stream resource.
37133 * Return
37134 * The function returns the read data or FALSE on failure.
37135 */
37136static int jx9Builtin_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
37137{
37138 const char *zFile, *zPtr, *zEnd, *zBuf;
37139 jx9_value *pArray, *pLine;
37140 const jx9_io_stream *pStream;
37141 int use_include = 0;
37142 io_private *pDev;
37143 jx9_int64 n;
37144 int iFlags;
37145 int nLen;
37146
37147 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
37148 /* Missing/Invalid arguments, return FALSE */
37149 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
37150 jx9_result_bool(pCtx, 0);
37151 return JX9_OK;
37152 }
37153 /* Extract the file path */
37154 zFile = jx9_value_to_string(apArg[0], &nLen);
37155 /* Point to the target IO stream device */
37156 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
37157 if( pStream == 0 ){
37158 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
37159 jx9_result_bool(pCtx, 0);
37160 return JX9_OK;
37161 }
37162 /* Allocate a new IO private instance */
37163 pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
37164 if( pDev == 0 ){
37165 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
37166 jx9_result_bool(pCtx, 0);
37167 return JX9_OK;
37168 }
37169 /* Initialize the structure */
37170 InitIOPrivate(pCtx->pVm, pStream, pDev);
37171 iFlags = 0;
37172 if( nArg > 1 ){
37173 iFlags = jx9_value_to_int(apArg[1]);
37174 }
37175 if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/ ){
37176 use_include = TRUE;
37177 }
37178 /* Create the array and the working value */
37179 pArray = jx9_context_new_array(pCtx);
37180 pLine = jx9_context_new_scalar(pCtx);
37181 if( pArray == 0 || pLine == 0 ){
37182 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
37183 jx9_result_bool(pCtx, 0);
37184 return JX9_OK;
37185 }
37186 /* Try to open the file in read-only mode */
37187 pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
37188 if( pDev->pHandle == 0 ){
37189 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
37190 jx9_result_bool(pCtx, 0);
37191 /* Don't worry about freeing memory, everything will be released automatically
37192 * as soon we return from this function.
37193 */
37194 return JX9_OK;
37195 }
37196 /* Perform the requested operation */
37197 for(;;){
37198 /* Try to extract a line */
37199 n = StreamReadLine(pDev, &zBuf, -1);
37200 if( n < 1 ){
37201 /* EOF or IO error */
37202 break;
37203 }
37204 /* Reset the cursor */
37205 jx9_value_reset_string_cursor(pLine);
37206 /* Remove line ending if requested by the caller */
37207 zPtr = zBuf;
37208 zEnd = &zBuf[n];
37209 if( iFlags & 0x02 /* FILE_IGNORE_NEW_LINES */ ){
37210 /* Ignore trailig lines */
37211 while( zPtr < zEnd && (zEnd[-1] == '\n'
37212#ifdef __WINNT__
37213 || zEnd[-1] == '\r'
37214#endif
37215 )){
37216 n--;
37217 zEnd--;
37218 }
37219 }
37220 if( iFlags & 0x04 /* FILE_SKIP_EMPTY_LINES */ ){
37221 /* Ignore empty lines */
37222 while( zPtr < zEnd && (unsigned char)zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) ){
37223 zPtr++;
37224 }
37225 if( zPtr >= zEnd ){
37226 /* Empty line */
37227 continue;
37228 }
37229 }
37230 jx9_value_string(pLine, zBuf, (int)(zEnd-zBuf));
37231 /* Insert line */
37232 jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pLine);
37233 }
37234 /* Close the stream */
37235 jx9StreamCloseHandle(pStream, pDev->pHandle);
37236 /* Release the io_private instance */
37237 ReleaseIOPrivate(pCtx, pDev);
37238 /* Return the created array */
37239 jx9_result_value(pCtx, pArray);
37240 return JX9_OK;
37241}
37242/*
37243 * bool copy(string $source, string $dest[, resource $context ] )
37244 * Makes a copy of the file source to dest.
37245 * Parameters
37246 * $source
37247 * Path to the source file.
37248 * $dest
37249 * The destination path. If dest is a URL, the copy operation
37250 * may fail if the wrapper does not support overwriting of existing files.
37251 * $context
37252 * A context stream resource.
37253 * Return
37254 * TRUE on success or FALSE on failure.
37255 */
37256static int jx9Builtin_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
37257{
37258 const jx9_io_stream *pSin, *pSout;
37259 const char *zFile;
37260 char zBuf[8192];
37261 void *pIn, *pOut;
37262 jx9_int64 n;
37263 int nLen;
37264 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1])){
37265 /* Missing/Invalid arguments, return FALSE */
37266 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a source and a destination path");
37267 jx9_result_bool(pCtx, 0);
37268 return JX9_OK;
37269 }
37270 /* Extract the source name */
37271 zFile = jx9_value_to_string(apArg[0], &nLen);
37272 /* Point to the target IO stream device */
37273 pSin = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
37274 if( pSin == 0 ){
37275 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
37276 jx9_result_bool(pCtx, 0);
37277 return JX9_OK;
37278 }
37279 /* Try to open the source file in a read-only mode */
37280 pIn = jx9StreamOpenHandle(pCtx->pVm, pSin, zFile, JX9_IO_OPEN_RDONLY, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
37281 if( pIn == 0 ){
37282 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening source: '%s'", zFile);
37283 jx9_result_bool(pCtx, 0);
37284 return JX9_OK;
37285 }
37286 /* Extract the destination name */
37287 zFile = jx9_value_to_string(apArg[1], &nLen);
37288 /* Point to the target IO stream device */
37289 pSout = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
37290 if( pSout == 0 ){
37291 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
37292 jx9_result_bool(pCtx, 0);
37293 jx9StreamCloseHandle(pSin, pIn);
37294 return JX9_OK;
37295 }
37296 if( pSout->xWrite == 0 ){
37297 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37298 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37299 jx9_function_name(pCtx), pSin->zName
37300 );
37301 jx9_result_bool(pCtx, 0);
37302 jx9StreamCloseHandle(pSin, pIn);
37303 return JX9_OK;
37304 }
37305 /* Try to open the destination file in a read-write mode */
37306 pOut = jx9StreamOpenHandle(pCtx->pVm, pSout, zFile,
37307 JX9_IO_OPEN_CREATE|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_RDWR, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
37308 if( pOut == 0 ){
37309 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening destination: '%s'", zFile);
37310 jx9_result_bool(pCtx, 0);
37311 jx9StreamCloseHandle(pSin, pIn);
37312 return JX9_OK;
37313 }
37314 /* Perform the requested operation */
37315 for(;;){
37316 /* Read from source */
37317 n = pSin->xRead(pIn, zBuf, sizeof(zBuf));
37318 if( n < 1 ){
37319 /* EOF or IO error, break immediately */
37320 break;
37321 }
37322 /* Write to dest */
37323 n = pSout->xWrite(pOut, zBuf, n);
37324 if( n < 1 ){
37325 /* IO error, break immediately */
37326 break;
37327 }
37328 }
37329 /* Close the streams */
37330 jx9StreamCloseHandle(pSin, pIn);
37331 jx9StreamCloseHandle(pSout, pOut);
37332 /* Return TRUE */
37333 jx9_result_bool(pCtx, 1);
37334 return JX9_OK;
37335}
37336/*
37337 * array fstat(resource $handle)
37338 * Gets information about a file using an open file pointer.
37339 * Parameters
37340 * $handle
37341 * The file pointer.
37342 * Return
37343 * Returns an array with the statistics of the file or FALSE on failure.
37344 */
37345static int jx9Builtin_fstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
37346{
37347 jx9_value *pArray, *pValue;
37348 const jx9_io_stream *pStream;
37349 io_private *pDev;
37350 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
37351 /* Missing/Invalid arguments, return FALSE */
37352 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37353 jx9_result_bool(pCtx, 0);
37354 return JX9_OK;
37355 }
37356 /* Extract our private data */
37357 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37358 /* Make sure we are dealing with a valid io_private instance */
37359 if( IO_PRIVATE_INVALID(pDev) ){
37360 /* Expecting an IO handle */
37361 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37362 jx9_result_bool(pCtx, 0);
37363 return JX9_OK;
37364 }
37365 /* Point to the target IO stream device */
37366 pStream = pDev->pStream;
37367 if( pStream == 0 || pStream->xStat == 0){
37368 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37369 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37370 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
37371 );
37372 jx9_result_bool(pCtx, 0);
37373 return JX9_OK;
37374 }
37375 /* Create the array and the working value */
37376 pArray = jx9_context_new_array(pCtx);
37377 pValue = jx9_context_new_scalar(pCtx);
37378 if( pArray == 0 || pValue == 0 ){
37379 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
37380 jx9_result_bool(pCtx, 0);
37381 return JX9_OK;
37382 }
37383 /* Perform the requested operation */
37384 pStream->xStat(pDev->pHandle, pArray, pValue);
37385 /* Return the freshly created array */
37386 jx9_result_value(pCtx, pArray);
37387 /* Don't worry about freeing memory here, everything will be
37388 * released automatically as soon we return from this function.
37389 */
37390 return JX9_OK;
37391}
37392/*
37393 * int fwrite(resource $handle, string $string[, int $length])
37394 * Writes the contents of string to the file stream pointed to by handle.
37395 * Parameters
37396 * $handle
37397 * The file pointer.
37398 * $string
37399 * The string that is to be written.
37400 * $length
37401 * If the length argument is given, writing will stop after length bytes have been written
37402 * or the end of string is reached, whichever comes first.
37403 * Return
37404 * Returns the number of bytes written, or FALSE on error.
37405 */
37406static int jx9Builtin_fwrite(jx9_context *pCtx, int nArg, jx9_value **apArg)
37407{
37408 const jx9_io_stream *pStream;
37409 const char *zString;
37410 io_private *pDev;
37411 int nLen, n;
37412 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
37413 /* Missing/Invalid arguments, return FALSE */
37414 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37415 jx9_result_bool(pCtx, 0);
37416 return JX9_OK;
37417 }
37418 /* Extract our private data */
37419 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37420 /* Make sure we are dealing with a valid io_private instance */
37421 if( IO_PRIVATE_INVALID(pDev) ){
37422 /* Expecting an IO handle */
37423 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37424 jx9_result_bool(pCtx, 0);
37425 return JX9_OK;
37426 }
37427 /* Point to the target IO stream device */
37428 pStream = pDev->pStream;
37429 if( pStream == 0 || pStream->xWrite == 0){
37430 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37431 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37432 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
37433 );
37434 jx9_result_bool(pCtx, 0);
37435 return JX9_OK;
37436 }
37437 /* Extract the data to write */
37438 zString = jx9_value_to_string(apArg[1], &nLen);
37439 if( nArg > 2 ){
37440 /* Maximum data length to write */
37441 n = jx9_value_to_int(apArg[2]);
37442 if( n >= 0 && n < nLen ){
37443 nLen = n;
37444 }
37445 }
37446 if( nLen < 1 ){
37447 /* Nothing to write */
37448 jx9_result_int(pCtx, 0);
37449 return JX9_OK;
37450 }
37451 /* Perform the requested operation */
37452 n = (int)pStream->xWrite(pDev->pHandle, (const void *)zString, nLen);
37453 if( n < 0 ){
37454 /* IO error, return FALSE */
37455 jx9_result_bool(pCtx, 0);
37456 }else{
37457 /* #Bytes written */
37458 jx9_result_int(pCtx, n);
37459 }
37460 return JX9_OK;
37461}
37462/*
37463 * bool flock(resource $handle, int $operation)
37464 * Portable advisory file locking.
37465 * Parameters
37466 * $handle
37467 * The file pointer.
37468 * $operation
37469 * operation is one of the following:
37470 * LOCK_SH to acquire a shared lock (reader).
37471 * LOCK_EX to acquire an exclusive lock (writer).
37472 * LOCK_UN to release a lock (shared or exclusive).
37473 * Return
37474 * Returns TRUE on success or FALSE on failure.
37475 */
37476static int jx9Builtin_flock(jx9_context *pCtx, int nArg, jx9_value **apArg)
37477{
37478 const jx9_io_stream *pStream;
37479 io_private *pDev;
37480 int nLock;
37481 int rc;
37482 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
37483 /* Missing/Invalid arguments, return FALSE */
37484 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37485 jx9_result_bool(pCtx, 0);
37486 return JX9_OK;
37487 }
37488 /* Extract our private data */
37489 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37490 /* Make sure we are dealing with a valid io_private instance */
37491 if( IO_PRIVATE_INVALID(pDev) ){
37492 /*Expecting an IO handle */
37493 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37494 jx9_result_bool(pCtx, 0);
37495 return JX9_OK;
37496 }
37497 /* Point to the target IO stream device */
37498 pStream = pDev->pStream;
37499 if( pStream == 0 || pStream->xLock == 0){
37500 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37501 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37502 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
37503 );
37504 jx9_result_bool(pCtx, 0);
37505 return JX9_OK;
37506 }
37507 /* Requested lock operation */
37508 nLock = jx9_value_to_int(apArg[1]);
37509 /* Lock operation */
37510 rc = pStream->xLock(pDev->pHandle, nLock);
37511 /* IO result */
37512 jx9_result_bool(pCtx, rc == JX9_OK);
37513 return JX9_OK;
37514}
37515/*
37516 * int fpassthru(resource $handle)
37517 * Output all remaining data on a file pointer.
37518 * Parameters
37519 * $handle
37520 * The file pointer.
37521 * Return
37522 * Total number of characters read from handle and passed through
37523 * to the output on success or FALSE on failure.
37524 */
37525static int jx9Builtin_fpassthru(jx9_context *pCtx, int nArg, jx9_value **apArg)
37526{
37527 const jx9_io_stream *pStream;
37528 io_private *pDev;
37529 jx9_int64 n, nRead;
37530 char zBuf[8192];
37531 int rc;
37532 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
37533 /* Missing/Invalid arguments, return FALSE */
37534 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37535 jx9_result_bool(pCtx, 0);
37536 return JX9_OK;
37537 }
37538 /* Extract our private data */
37539 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37540 /* Make sure we are dealing with a valid io_private instance */
37541 if( IO_PRIVATE_INVALID(pDev) ){
37542 /*Expecting an IO handle */
37543 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37544 jx9_result_bool(pCtx, 0);
37545 return JX9_OK;
37546 }
37547 /* Point to the target IO stream device */
37548 pStream = pDev->pStream;
37549 if( pStream == 0 ){
37550 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37551 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37552 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
37553 );
37554 jx9_result_bool(pCtx, 0);
37555 return JX9_OK;
37556 }
37557 /* Perform the requested operation */
37558 nRead = 0;
37559 for(;;){
37560 n = StreamRead(pDev, zBuf, sizeof(zBuf));
37561 if( n < 1 ){
37562 /* Error or EOF */
37563 break;
37564 }
37565 /* Increment the read counter */
37566 nRead += n;
37567 /* Output data */
37568 rc = jx9_context_output(pCtx, zBuf, (int)nRead /* FIXME: 64-bit issues */);
37569 if( rc == JX9_ABORT ){
37570 /* Consumer callback request an operation abort */
37571 break;
37572 }
37573 }
37574 /* Total number of bytes readen */
37575 jx9_result_int64(pCtx, nRead);
37576 return JX9_OK;
37577}
37578/* CSV reader/writer private data */
37579struct csv_data
37580{
37581 int delimiter; /* Delimiter. Default ', ' */
37582 int enclosure; /* Enclosure. Default '"'*/
37583 io_private *pDev; /* Open stream handle */
37584 int iCount; /* Counter */
37585};
37586/*
37587 * The following callback is used by the fputcsv() function inorder to iterate
37588 * throw array entries and output CSV data based on the current key and it's
37589 * associated data.
37590 */
37591static int csv_write_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
37592{
37593 struct csv_data *pData = (struct csv_data *)pUserData;
37594 const char *zData;
37595 int nLen, c2;
37596 sxu32 n;
37597 /* Point to the raw data */
37598 zData = jx9_value_to_string(pValue, &nLen);
37599 if( nLen < 1 ){
37600 /* Nothing to write */
37601 return JX9_OK;
37602 }
37603 if( pData->iCount > 0 ){
37604 /* Write the delimiter */
37605 pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->delimiter, sizeof(char));
37606 }
37607 n = 1;
37608 c2 = 0;
37609 if( SyByteFind(zData, (sxu32)nLen, pData->delimiter, 0) == SXRET_OK ||
37610 SyByteFind(zData, (sxu32)nLen, pData->enclosure, &n) == SXRET_OK ){
37611 c2 = 1;
37612 if( n == 0 ){
37613 c2 = 2;
37614 }
37615 /* Write the enclosure */
37616 pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
37617 if( c2 > 1 ){
37618 pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
37619 }
37620 }
37621 /* Write the data */
37622 if( pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)zData, (jx9_int64)nLen) < 1 ){
37623 SXUNUSED(pKey); /* cc warning */
37624 return JX9_ABORT;
37625 }
37626 if( c2 > 0 ){
37627 /* Write the enclosure */
37628 pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
37629 if( c2 > 1 ){
37630 pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
37631 }
37632 }
37633 pData->iCount++;
37634 return JX9_OK;
37635}
37636/*
37637 * int fputcsv(resource $handle, array $fields[, string $delimiter = ', '[, string $enclosure = '"' ]])
37638 * Format line as CSV and write to file pointer.
37639 * Parameters
37640 * $handle
37641 * Open file handle.
37642 * $fields
37643 * An array of values.
37644 * $delimiter
37645 * The optional delimiter parameter sets the field delimiter (one character only).
37646 * $enclosure
37647 * The optional enclosure parameter sets the field enclosure (one character only).
37648 */
37649static int jx9Builtin_fputcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
37650{
37651 const jx9_io_stream *pStream;
37652 struct csv_data sCsv;
37653 io_private *pDev;
37654 char *zEol;
37655 int eolen;
37656 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
37657 /* Missing/Invalid arguments, return FALSE */
37658 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Missing/Invalid arguments");
37659 jx9_result_bool(pCtx, 0);
37660 return JX9_OK;
37661 }
37662 /* Extract our private data */
37663 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37664 /* Make sure we are dealing with a valid io_private instance */
37665 if( IO_PRIVATE_INVALID(pDev) ){
37666 /*Expecting an IO handle */
37667 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37668 jx9_result_bool(pCtx, 0);
37669 return JX9_OK;
37670 }
37671 /* Point to the target IO stream device */
37672 pStream = pDev->pStream;
37673 if( pStream == 0 || pStream->xWrite == 0){
37674 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37675 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37676 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
37677 );
37678 jx9_result_bool(pCtx, 0);
37679 return JX9_OK;
37680 }
37681 /* Set default csv separator */
37682 sCsv.delimiter = ',';
37683 sCsv.enclosure = '"';
37684 sCsv.pDev = pDev;
37685 sCsv.iCount = 0;
37686 if( nArg > 2 ){
37687 /* User delimiter */
37688 const char *z;
37689 int n;
37690 z = jx9_value_to_string(apArg[2], &n);
37691 if( n > 0 ){
37692 sCsv.delimiter = z[0];
37693 }
37694 if( nArg > 3 ){
37695 z = jx9_value_to_string(apArg[3], &n);
37696 if( n > 0 ){
37697 sCsv.enclosure = z[0];
37698 }
37699 }
37700 }
37701 /* Iterate throw array entries and write csv data */
37702 jx9_array_walk(apArg[1], csv_write_callback, &sCsv);
37703 /* Write a line ending */
37704#ifdef __WINNT__
37705 zEol = "\r\n";
37706 eolen = (int)sizeof("\r\n")-1;
37707#else
37708 /* Assume UNIX LF */
37709 zEol = "\n";
37710 eolen = (int)sizeof(char);
37711#endif
37712 pDev->pStream->xWrite(pDev->pHandle, (const void *)zEol, eolen);
37713 return JX9_OK;
37714}
37715/*
37716 * fprintf, vfprintf private data.
37717 * An instance of the following structure is passed to the formatted
37718 * input consumer callback defined below.
37719 */
37720typedef struct fprintf_data fprintf_data;
37721struct fprintf_data
37722{
37723 io_private *pIO; /* IO stream */
37724 jx9_int64 nCount; /* Total number of bytes written */
37725};
37726/*
37727 * Callback [i.e: Formatted input consumer] for the fprintf function.
37728 */
37729static int fprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
37730{
37731 fprintf_data *pFdata = (fprintf_data *)pUserData;
37732 jx9_int64 n;
37733 /* Write the formatted data */
37734 n = pFdata->pIO->pStream->xWrite(pFdata->pIO->pHandle, (const void *)zInput, nLen);
37735 if( n < 1 ){
37736 SXUNUSED(pCtx); /* cc warning */
37737 /* IO error, abort immediately */
37738 return SXERR_ABORT;
37739 }
37740 /* Increment counter */
37741 pFdata->nCount += n;
37742 return JX9_OK;
37743}
37744/*
37745 * int fprintf(resource $handle, string $format[, mixed $args [, mixed $... ]])
37746 * Write a formatted string to a stream.
37747 * Parameters
37748 * $handle
37749 * The file pointer.
37750 * $format
37751 * String format (see sprintf()).
37752 * Return
37753 * The length of the written string.
37754 */
37755static int jx9Builtin_fprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
37756{
37757 fprintf_data sFdata;
37758 const char *zFormat;
37759 io_private *pDev;
37760 int nLen;
37761 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
37762 /* Missing/Invalid arguments, return zero */
37763 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
37764 jx9_result_int(pCtx, 0);
37765 return JX9_OK;
37766 }
37767 /* Extract our private data */
37768 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37769 /* Make sure we are dealing with a valid io_private instance */
37770 if( IO_PRIVATE_INVALID(pDev) ){
37771 /*Expecting an IO handle */
37772 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37773 jx9_result_int(pCtx, 0);
37774 return JX9_OK;
37775 }
37776 /* Point to the target IO stream device */
37777 if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){
37778 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37779 "IO routine(%s) not implemented in the underlying stream(%s) device",
37780 jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
37781 );
37782 jx9_result_int(pCtx, 0);
37783 return JX9_OK;
37784 }
37785 /* Extract the string format */
37786 zFormat = jx9_value_to_string(apArg[1], &nLen);
37787 if( nLen < 1 ){
37788 /* Empty string, return zero */
37789 jx9_result_int(pCtx, 0);
37790 return JX9_OK;
37791 }
37792 /* Prepare our private data */
37793 sFdata.nCount = 0;
37794 sFdata.pIO = pDev;
37795 /* Format the string */
37796 jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, nArg - 1, &apArg[1], (void *)&sFdata, FALSE);
37797 /* Return total number of bytes written */
37798 jx9_result_int64(pCtx, sFdata.nCount);
37799 return JX9_OK;
37800}
37801/*
37802 * int vfprintf(resource $handle, string $format, array $args)
37803 * Write a formatted string to a stream.
37804 * Parameters
37805 * $handle
37806 * The file pointer.
37807 * $format
37808 * String format (see sprintf()).
37809 * $args
37810 * User arguments.
37811 * Return
37812 * The length of the written string.
37813 */
37814static int jx9Builtin_vfprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
37815{
37816 fprintf_data sFdata;
37817 const char *zFormat;
37818 jx9_hashmap *pMap;
37819 io_private *pDev;
37820 SySet sArg;
37821 int n, nLen;
37822 if( nArg < 3 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) || !jx9_value_is_json_array(apArg[2]) ){
37823 /* Missing/Invalid arguments, return zero */
37824 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
37825 jx9_result_int(pCtx, 0);
37826 return JX9_OK;
37827 }
37828 /* Extract our private data */
37829 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37830 /* Make sure we are dealing with a valid io_private instance */
37831 if( IO_PRIVATE_INVALID(pDev) ){
37832 /*Expecting an IO handle */
37833 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37834 jx9_result_int(pCtx, 0);
37835 return JX9_OK;
37836 }
37837 /* Point to the target IO stream device */
37838 if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){
37839 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37840 "IO routine(%s) not implemented in the underlying stream(%s) device",
37841 jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
37842 );
37843 jx9_result_int(pCtx, 0);
37844 return JX9_OK;
37845 }
37846 /* Extract the string format */
37847 zFormat = jx9_value_to_string(apArg[1], &nLen);
37848 if( nLen < 1 ){
37849 /* Empty string, return zero */
37850 jx9_result_int(pCtx, 0);
37851 return JX9_OK;
37852 }
37853 /* Point to hashmap */
37854 pMap = (jx9_hashmap *)apArg[2]->x.pOther;
37855 /* Extract arguments from the hashmap */
37856 n = jx9HashmapValuesToSet(pMap, &sArg);
37857 /* Prepare our private data */
37858 sFdata.nCount = 0;
37859 sFdata.pIO = pDev;
37860 /* Format the string */
37861 jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&sFdata, TRUE);
37862 /* Return total number of bytes written*/
37863 jx9_result_int64(pCtx, sFdata.nCount);
37864 SySetRelease(&sArg);
37865 return JX9_OK;
37866}
37867/*
37868 * Convert open modes (string passed to the fopen() function) [i.e: 'r', 'w+', 'a', ...] into JX9 flags.
37869 * According to the JX9 reference manual:
37870 * The mode parameter specifies the type of access you require to the stream. It may be any of the following
37871 * 'r' Open for reading only; place the file pointer at the beginning of the file.
37872 * 'r+' Open for reading and writing; place the file pointer at the beginning of the file.
37873 * 'w' Open for writing only; place the file pointer at the beginning of the file and truncate the file
37874 * to zero length. If the file does not exist, attempt to create it.
37875 * 'w+' Open for reading and writing; place the file pointer at the beginning of the file and truncate
37876 * the file to zero length. If the file does not exist, attempt to create it.
37877 * 'a' Open for writing only; place the file pointer at the end of the file. If the file does not
37878 * exist, attempt to create it.
37879 * 'a+' Open for reading and writing; place the file pointer at the end of the file. If the file does
37880 * not exist, attempt to create it.
37881 * 'x' Create and open for writing only; place the file pointer at the beginning of the file. If the file
37882 * already exists,
37883 * the fopen() call will fail by returning FALSE and generating an error of level E_WARNING. If the file
37884 * does not exist attempt to create it. This is equivalent to specifying O_EXCL|O_CREAT flags for
37885 * the underlying open(2) system call.
37886 * 'x+' Create and open for reading and writing; otherwise it has the same behavior as 'x'.
37887 * 'c' Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated
37888 * (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer
37889 * is positioned on the beginning of the file.
37890 * This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file
37891 * as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can
37892 * be used after the lock is requested).
37893 * 'c+' Open the file for reading and writing; otherwise it has the same behavior as 'c'.
37894 */
37895static int StrModeToFlags(jx9_context *pCtx, const char *zMode, int nLen)
37896{
37897 const char *zEnd = &zMode[nLen];
37898 int iFlag = 0;
37899 int c;
37900 if( nLen < 1 ){
37901 /* Open in a read-only mode */
37902 return JX9_IO_OPEN_RDONLY;
37903 }
37904 c = zMode[0];
37905 if( c == 'r' || c == 'R' ){
37906 /* Read-only access */
37907 iFlag = JX9_IO_OPEN_RDONLY;
37908 zMode++; /* Advance */
37909 if( zMode < zEnd ){
37910 c = zMode[0];
37911 if( c == '+' || c == 'w' || c == 'W' ){
37912 /* Read+Write access */
37913 iFlag = JX9_IO_OPEN_RDWR;
37914 }
37915 }
37916 }else if( c == 'w' || c == 'W' ){
37917 /* Overwrite mode.
37918 * If the file does not exists, try to create it
37919 */
37920 iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_CREATE;
37921 zMode++; /* Advance */
37922 if( zMode < zEnd ){
37923 c = zMode[0];
37924 if( c == '+' || c == 'r' || c == 'R' ){
37925 /* Read+Write access */
37926 iFlag &= ~JX9_IO_OPEN_WRONLY;
37927 iFlag |= JX9_IO_OPEN_RDWR;
37928 }
37929 }
37930 }else if( c == 'a' || c == 'A' ){
37931 /* Append mode (place the file pointer at the end of the file).
37932 * Create the file if it does not exists.
37933 */
37934 iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_APPEND|JX9_IO_OPEN_CREATE;
37935 zMode++; /* Advance */
37936 if( zMode < zEnd ){
37937 c = zMode[0];
37938 if( c == '+' ){
37939 /* Read-Write access */
37940 iFlag &= ~JX9_IO_OPEN_WRONLY;
37941 iFlag |= JX9_IO_OPEN_RDWR;
37942 }
37943 }
37944 }else if( c == 'x' || c == 'X' ){
37945 /* Exclusive access.
37946 * If the file already exists, return immediately with a failure code.
37947 * Otherwise create a new file.
37948 */
37949 iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_EXCL;
37950 zMode++; /* Advance */
37951 if( zMode < zEnd ){
37952 c = zMode[0];
37953 if( c == '+' || c == 'r' || c == 'R' ){
37954 /* Read-Write access */
37955 iFlag &= ~JX9_IO_OPEN_WRONLY;
37956 iFlag |= JX9_IO_OPEN_RDWR;
37957 }
37958 }
37959 }else if( c == 'c' || c == 'C' ){
37960 /* Overwrite mode.Create the file if it does not exists.*/
37961 iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_CREATE;
37962 zMode++; /* Advance */
37963 if( zMode < zEnd ){
37964 c = zMode[0];
37965 if( c == '+' ){
37966 /* Read-Write access */
37967 iFlag &= ~JX9_IO_OPEN_WRONLY;
37968 iFlag |= JX9_IO_OPEN_RDWR;
37969 }
37970 }
37971 }else{
37972 /* Invalid mode. Assume a read only open */
37973 jx9_context_throw_error(pCtx, JX9_CTX_NOTICE, "Invalid open mode, JX9 is assuming a Read-Only open");
37974 iFlag = JX9_IO_OPEN_RDONLY;
37975 }
37976 while( zMode < zEnd ){
37977 c = zMode[0];
37978 if( c == 'b' || c == 'B' ){
37979 iFlag &= ~JX9_IO_OPEN_TEXT;
37980 iFlag |= JX9_IO_OPEN_BINARY;
37981 }else if( c == 't' || c == 'T' ){
37982 iFlag &= ~JX9_IO_OPEN_BINARY;
37983 iFlag |= JX9_IO_OPEN_TEXT;
37984 }
37985 zMode++;
37986 }
37987 return iFlag;
37988}
37989/*
37990 * Initialize the IO private structure.
37991 */
37992static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut)
37993{
37994 pOut->pStream = pStream;
37995 SyBlobInit(&pOut->sBuffer, &pVm->sAllocator);
37996 pOut->nOfft = 0;
37997 /* Set the magic number */
37998 pOut->iMagic = IO_PRIVATE_MAGIC;
37999}
38000/*
38001 * Release the IO private structure.
38002 */
38003static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev)
38004{
38005 SyBlobRelease(&pDev->sBuffer);
38006 pDev->iMagic = 0x2126; /* Invalid magic number so we can detetct misuse */
38007 /* Release the whole structure */
38008 jx9_context_free_chunk(pCtx, pDev);
38009}
38010/*
38011 * Reset the IO private structure.
38012 */
38013static void ResetIOPrivate(io_private *pDev)
38014{
38015 SyBlobReset(&pDev->sBuffer);
38016 pDev->nOfft = 0;
38017}
38018/* Forward declaration */
38019static int is_jx9_stream(const jx9_io_stream *pStream);
38020/*
38021 * resource fopen(string $filename, string $mode [, bool $use_include_path = false[, resource $context ]])
38022 * Open a file, a URL or any other IO stream.
38023 * Parameters
38024 * $filename
38025 * If filename is of the form "scheme://...", it is assumed to be a URL and JX9 will search
38026 * for a protocol handler (also known as a wrapper) for that scheme. If no scheme is given
38027 * then a regular file is assumed.
38028 * $mode
38029 * The mode parameter specifies the type of access you require to the stream
38030 * See the block comment associated with the StrModeToFlags() for the supported
38031 * modes.
38032 * $use_include_path
38033 * You can use the optional second parameter and set it to
38034 * TRUE, if you want to search for the file in the include_path, too.
38035 * $context
38036 * A context stream resource.
38037 * Return
38038 * File handle on success or FALSE on failure.
38039 */
38040static int jx9Builtin_fopen(jx9_context *pCtx, int nArg, jx9_value **apArg)
38041{
38042 const jx9_io_stream *pStream;
38043 const char *zUri, *zMode;
38044 jx9_value *pResource;
38045 io_private *pDev;
38046 int iLen, imLen;
38047 int iOpenFlags;
38048 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
38049 /* Missing/Invalid arguments, return FALSE */
38050 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path or URL");
38051 jx9_result_bool(pCtx, 0);
38052 return JX9_OK;
38053 }
38054 /* Extract the URI and the desired access mode */
38055 zUri = jx9_value_to_string(apArg[0], &iLen);
38056 if( nArg > 1 ){
38057 zMode = jx9_value_to_string(apArg[1], &imLen);
38058 }else{
38059 /* Set a default read-only mode */
38060 zMode = "r";
38061 imLen = (int)sizeof(char);
38062 }
38063 /* Try to extract a stream */
38064 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zUri, iLen);
38065 if( pStream == 0 ){
38066 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
38067 "No stream device is associated with the given URI(%s)", zUri);
38068 jx9_result_bool(pCtx, 0);
38069 return JX9_OK;
38070 }
38071 /* Allocate a new IO private instance */
38072 pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
38073 if( pDev == 0 ){
38074 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
38075 jx9_result_bool(pCtx, 0);
38076 return JX9_OK;
38077 }
38078 pResource = 0;
38079 if( nArg > 3 ){
38080 pResource = apArg[3];
38081 }else if( is_jx9_stream(pStream) ){
38082 /* TICKET 1433-80: The jx9:// stream need a jx9_value to access the underlying
38083 * virtual machine.
38084 */
38085 pResource = apArg[0];
38086 }
38087 /* Initialize the structure */
38088 InitIOPrivate(pCtx->pVm, pStream, pDev);
38089 /* Convert open mode to JX9 flags */
38090 iOpenFlags = StrModeToFlags(pCtx, zMode, imLen);
38091 /* Try to get a handle */
38092 pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zUri, iOpenFlags,
38093 nArg > 2 ? jx9_value_to_bool(apArg[2]) : FALSE, pResource, FALSE, 0);
38094 if( pDev->pHandle == 0 ){
38095 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zUri);
38096 jx9_result_bool(pCtx, 0);
38097 jx9_context_free_chunk(pCtx, pDev);
38098 return JX9_OK;
38099 }
38100 /* All done, return the io_private instance as a resource */
38101 jx9_result_resource(pCtx, pDev);
38102 return JX9_OK;
38103}
38104/*
38105 * bool fclose(resource $handle)
38106 * Closes an open file pointer
38107 * Parameters
38108 * $handle
38109 * The file pointer.
38110 * Return
38111 * TRUE on success or FALSE on failure.
38112 */
38113static int jx9Builtin_fclose(jx9_context *pCtx, int nArg, jx9_value **apArg)
38114{
38115 const jx9_io_stream *pStream;
38116 io_private *pDev;
38117 jx9_vm *pVm;
38118 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38119 /* Missing/Invalid arguments, return FALSE */
38120 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
38121 jx9_result_bool(pCtx, 0);
38122 return JX9_OK;
38123 }
38124 /* Extract our private data */
38125 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
38126 /* Make sure we are dealing with a valid io_private instance */
38127 if( IO_PRIVATE_INVALID(pDev) ){
38128 /*Expecting an IO handle */
38129 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
38130 jx9_result_bool(pCtx, 0);
38131 return JX9_OK;
38132 }
38133 /* Point to the target IO stream device */
38134 pStream = pDev->pStream;
38135 if( pStream == 0 ){
38136 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
38137 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
38138 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
38139 );
38140 jx9_result_bool(pCtx, 0);
38141 return JX9_OK;
38142 }
38143 /* Point to the VM that own this context */
38144 pVm = pCtx->pVm;
38145 /* TICKET 1433-62: Keep the STDIN/STDOUT/STDERR handles open */
38146 if( pDev != pVm->pStdin && pDev != pVm->pStdout && pDev != pVm->pStderr ){
38147 /* Perform the requested operation */
38148 jx9StreamCloseHandle(pStream, pDev->pHandle);
38149 /* Release the IO private structure */
38150 ReleaseIOPrivate(pCtx, pDev);
38151 /* Invalidate the resource handle */
38152 jx9_value_release(apArg[0]);
38153 }
38154 /* Return TRUE */
38155 jx9_result_bool(pCtx, 1);
38156 return JX9_OK;
38157}
38158#if !defined(JX9_DISABLE_HASH_FUNC)
38159/*
38160 * MD5/SHA1 digest consumer.
38161 */
38162static int vfsHashConsumer(const void *pData, unsigned int nLen, void *pUserData)
38163{
38164 /* Append hex chunk verbatim */
38165 jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
38166 return SXRET_OK;
38167}
38168/*
38169 * string md5_file(string $uri[, bool $raw_output = false ])
38170 * Calculates the md5 hash of a given file.
38171 * Parameters
38172 * $uri
38173 * Target URI (file(/path/to/something) or URL(http://www.symisc.net/))
38174 * $raw_output
38175 * When TRUE, returns the digest in raw binary format with a length of 16.
38176 * Return
38177 * Return the MD5 digest on success or FALSE on failure.
38178 */
38179static int jx9Builtin_md5_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
38180{
38181 const jx9_io_stream *pStream;
38182 unsigned char zDigest[16];
38183 int raw_output = FALSE;
38184 const char *zFile;
38185 MD5Context sCtx;
38186 char zBuf[8192];
38187 void *pHandle;
38188 jx9_int64 n;
38189 int nLen;
38190 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
38191 /* Missing/Invalid arguments, return FALSE */
38192 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
38193 jx9_result_bool(pCtx, 0);
38194 return JX9_OK;
38195 }
38196 /* Extract the file path */
38197 zFile = jx9_value_to_string(apArg[0], &nLen);
38198 /* Point to the target IO stream device */
38199 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
38200 if( pStream == 0 ){
38201 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
38202 jx9_result_bool(pCtx, 0);
38203 return JX9_OK;
38204 }
38205 if( nArg > 1 ){
38206 raw_output = jx9_value_to_bool(apArg[1]);
38207 }
38208 /* Try to open the file in read-only mode */
38209 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
38210 if( pHandle == 0 ){
38211 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
38212 jx9_result_bool(pCtx, 0);
38213 return JX9_OK;
38214 }
38215 /* Init the MD5 context */
38216 MD5Init(&sCtx);
38217 /* Perform the requested operation */
38218 for(;;){
38219 n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
38220 if( n < 1 ){
38221 /* EOF or IO error, break immediately */
38222 break;
38223 }
38224 MD5Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n);
38225 }
38226 /* Close the stream */
38227 jx9StreamCloseHandle(pStream, pHandle);
38228 /* Extract the digest */
38229 MD5Final(zDigest, &sCtx);
38230 if( raw_output ){
38231 /* Output raw digest */
38232 jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
38233 }else{
38234 /* Perform a binary to hex conversion */
38235 SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
38236 }
38237 return JX9_OK;
38238}
38239/*
38240 * string sha1_file(string $uri[, bool $raw_output = false ])
38241 * Calculates the SHA1 hash of a given file.
38242 * Parameters
38243 * $uri
38244 * Target URI (file(/path/to/something) or URL(http://www.symisc.net/))
38245 * $raw_output
38246 * When TRUE, returns the digest in raw binary format with a length of 20.
38247 * Return
38248 * Return the SHA1 digest on success or FALSE on failure.
38249 */
38250static int jx9Builtin_sha1_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
38251{
38252 const jx9_io_stream *pStream;
38253 unsigned char zDigest[20];
38254 int raw_output = FALSE;
38255 const char *zFile;
38256 SHA1Context sCtx;
38257 char zBuf[8192];
38258 void *pHandle;
38259 jx9_int64 n;
38260 int nLen;
38261 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
38262 /* Missing/Invalid arguments, return FALSE */
38263 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
38264 jx9_result_bool(pCtx, 0);
38265 return JX9_OK;
38266 }
38267 /* Extract the file path */
38268 zFile = jx9_value_to_string(apArg[0], &nLen);
38269 /* Point to the target IO stream device */
38270 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
38271 if( pStream == 0 ){
38272 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
38273 jx9_result_bool(pCtx, 0);
38274 return JX9_OK;
38275 }
38276 if( nArg > 1 ){
38277 raw_output = jx9_value_to_bool(apArg[1]);
38278 }
38279 /* Try to open the file in read-only mode */
38280 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
38281 if( pHandle == 0 ){
38282 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
38283 jx9_result_bool(pCtx, 0);
38284 return JX9_OK;
38285 }
38286 /* Init the SHA1 context */
38287 SHA1Init(&sCtx);
38288 /* Perform the requested operation */
38289 for(;;){
38290 n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
38291 if( n < 1 ){
38292 /* EOF or IO error, break immediately */
38293 break;
38294 }
38295 SHA1Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n);
38296 }
38297 /* Close the stream */
38298 jx9StreamCloseHandle(pStream, pHandle);
38299 /* Extract the digest */
38300 SHA1Final(&sCtx, zDigest);
38301 if( raw_output ){
38302 /* Output raw digest */
38303 jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
38304 }else{
38305 /* Perform a binary to hex conversion */
38306 SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
38307 }
38308 return JX9_OK;
38309}
38310#endif /* JX9_DISABLE_HASH_FUNC */
38311/*
38312 * array parse_ini_file(string $filename[, bool $process_sections = false [, int $scanner_mode = INI_SCANNER_NORMAL ]] )
38313 * Parse a configuration file.
38314 * Parameters
38315 * $filename
38316 * The filename of the ini file being parsed.
38317 * $process_sections
38318 * By setting the process_sections parameter to TRUE, you get a multidimensional array
38319 * with the section names and settings included.
38320 * The default for process_sections is FALSE.
38321 * $scanner_mode
38322 * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW.
38323 * If INI_SCANNER_RAW is supplied, then option values will not be parsed.
38324 * Return
38325 * The settings are returned as an associative array on success.
38326 * Otherwise is returned.
38327 */
38328static int jx9Builtin_parse_ini_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
38329{
38330 const jx9_io_stream *pStream;
38331 const char *zFile;
38332 SyBlob sContents;
38333 void *pHandle;
38334 int nLen;
38335 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
38336 /* Missing/Invalid arguments, return FALSE */
38337 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
38338 jx9_result_bool(pCtx, 0);
38339 return JX9_OK;
38340 }
38341 /* Extract the file path */
38342 zFile = jx9_value_to_string(apArg[0], &nLen);
38343 /* Point to the target IO stream device */
38344 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
38345 if( pStream == 0 ){
38346 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
38347 jx9_result_bool(pCtx, 0);
38348 return JX9_OK;
38349 }
38350 /* Try to open the file in read-only mode */
38351 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
38352 if( pHandle == 0 ){
38353 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
38354 jx9_result_bool(pCtx, 0);
38355 return JX9_OK;
38356 }
38357 SyBlobInit(&sContents, &pCtx->pVm->sAllocator);
38358 /* Read the whole file */
38359 jx9StreamReadWholeFile(pHandle, pStream, &sContents);
38360 if( SyBlobLength(&sContents) < 1 ){
38361 /* Empty buffer, return FALSE */
38362 jx9_result_bool(pCtx, 0);
38363 }else{
38364 /* Process the raw INI buffer */
38365 jx9ParseIniString(pCtx, (const char *)SyBlobData(&sContents), SyBlobLength(&sContents),
38366 nArg > 1 ? jx9_value_to_bool(apArg[1]) : 0);
38367 }
38368 /* Close the stream */
38369 jx9StreamCloseHandle(pStream, pHandle);
38370 /* Release the working buffer */
38371 SyBlobRelease(&sContents);
38372 return JX9_OK;
38373}
38374/*
38375 * Section:
38376 * ZIP archive processing.
38377 * Authors:
38378 * Symisc Systems, devel@symisc.net.
38379 * Copyright (C) Symisc Systems, http://jx9.symisc.net
38380 * Status:
38381 * Stable.
38382 */
38383typedef struct zip_raw_data zip_raw_data;
38384struct zip_raw_data
38385{
38386 int iType; /* Where the raw data is stored */
38387 union raw_data{
38388 struct mmap_data{
38389 void *pMap; /* Memory mapped data */
38390 jx9_int64 nSize; /* Map size */
38391 const jx9_vfs *pVfs; /* Underlying vfs */
38392 }mmap;
38393 SyBlob sBlob; /* Memory buffer */
38394 }raw;
38395};
38396#define ZIP_RAW_DATA_MMAPED 1 /* Memory mapped ZIP raw data */
38397#define ZIP_RAW_DATA_MEMBUF 2 /* ZIP raw data stored in a dynamically
38398 * allocated memory chunk.
38399 */
38400 /*
38401 * mixed zip_open(string $filename)
38402 * Opens a new zip archive for reading.
38403 * Parameters
38404 * $filename
38405 * The file name of the ZIP archive to open.
38406 * Return
38407 * A resource handle for later use with zip_read() and zip_close() or FALSE on failure.
38408 */
38409static int jx9Builtin_zip_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
38410{
38411 const jx9_io_stream *pStream;
38412 SyArchive *pArchive;
38413 zip_raw_data *pRaw;
38414 const char *zFile;
38415 SyBlob *pContents;
38416 void *pHandle;
38417 int nLen;
38418 sxi32 rc;
38419 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
38420 /* Missing/Invalid arguments, return FALSE */
38421 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
38422 jx9_result_bool(pCtx, 0);
38423 return JX9_OK;
38424 }
38425 /* Extract the file path */
38426 zFile = jx9_value_to_string(apArg[0], &nLen);
38427 /* Point to the target IO stream device */
38428 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
38429 if( pStream == 0 ){
38430 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
38431 jx9_result_bool(pCtx, 0);
38432 return JX9_OK;
38433 }
38434 /* Create an in-memory archive */
38435 pArchive = (SyArchive *)jx9_context_alloc_chunk(pCtx, sizeof(SyArchive)+sizeof(zip_raw_data), TRUE, FALSE);
38436 if( pArchive == 0 ){
38437 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "JX9 is running out of memory");
38438 jx9_result_bool(pCtx, 0);
38439 return JX9_OK;
38440 }
38441 pRaw = (zip_raw_data *)&pArchive[1];
38442 /* Initialize the archive */
38443 SyArchiveInit(pArchive, &pCtx->pVm->sAllocator, 0, 0);
38444 /* Extract the default stream */
38445 if( pStream == pCtx->pVm->pDefStream /* file:// stream*/){
38446 const jx9_vfs *pVfs;
38447 /* Try to get a memory view of the whole file since ZIP files
38448 * tends to be very big this days, this is a huge performance win.
38449 */
38450 pVfs = jx9ExportBuiltinVfs();
38451 if( pVfs && pVfs->xMmap ){
38452 rc = pVfs->xMmap(zFile, &pRaw->raw.mmap.pMap, &pRaw->raw.mmap.nSize);
38453 if( rc == JX9_OK ){
38454 /* Nice, Extract the whole archive */
38455 rc = SyZipExtractFromBuf(pArchive, (const char *)pRaw->raw.mmap.pMap, (sxu32)pRaw->raw.mmap.nSize);
38456 if( rc != SXRET_OK ){
38457 if( pVfs->xUnmap ){
38458 pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize);
38459 }
38460 /* Release the allocated chunk */
38461 jx9_context_free_chunk(pCtx, pArchive);
38462 /* Something goes wrong with this ZIP archive, return FALSE */
38463 jx9_result_bool(pCtx, 0);
38464 return JX9_OK;
38465 }
38466 /* Archive successfully opened */
38467 pRaw->iType = ZIP_RAW_DATA_MMAPED;
38468 pRaw->raw.mmap.pVfs = pVfs;
38469 goto success;
38470 }
38471 }
38472 /* FALL THROUGH */
38473 }
38474 /* Try to open the file in read-only mode */
38475 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
38476 if( pHandle == 0 ){
38477 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
38478 jx9_result_bool(pCtx, 0);
38479 return JX9_OK;
38480 }
38481 pContents = &pRaw->raw.sBlob;
38482 SyBlobInit(pContents, &pCtx->pVm->sAllocator);
38483 /* Read the whole file */
38484 jx9StreamReadWholeFile(pHandle, pStream, pContents);
38485 /* Assume an invalid ZIP file */
38486 rc = SXERR_INVALID;
38487 if( SyBlobLength(pContents) > 0 ){
38488 /* Extract archive entries */
38489 rc = SyZipExtractFromBuf(pArchive, (const char *)SyBlobData(pContents), SyBlobLength(pContents));
38490 }
38491 pRaw->iType = ZIP_RAW_DATA_MEMBUF;
38492 /* Close the stream */
38493 jx9StreamCloseHandle(pStream, pHandle);
38494 if( rc != SXRET_OK ){
38495 /* Release the working buffer */
38496 SyBlobRelease(pContents);
38497 /* Release the allocated chunk */
38498 jx9_context_free_chunk(pCtx, pArchive);
38499 /* Something goes wrong with this ZIP archive, return FALSE */
38500 jx9_result_bool(pCtx, 0);
38501 return JX9_OK;
38502 }
38503success:
38504 /* Reset the loop cursor */
38505 SyArchiveResetLoopCursor(pArchive);
38506 /* Return the in-memory archive as a resource handle */
38507 jx9_result_resource(pCtx, pArchive);
38508 return JX9_OK;
38509}
38510/*
38511 * void zip_close(resource $zip)
38512 * Close an in-memory ZIP archive.
38513 * Parameters
38514 * $zip
38515 * A ZIP file previously opened with zip_open().
38516 * Return
38517 * null.
38518 */
38519static int jx9Builtin_zip_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
38520{
38521 SyArchive *pArchive;
38522 zip_raw_data *pRaw;
38523 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38524 /* Missing/Invalid arguments */
38525 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38526 return JX9_OK;
38527 }
38528 /* Point to the in-memory archive */
38529 pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
38530 /* Make sure we are dealing with a valid ZIP archive */
38531 if( SXARCH_INVALID(pArchive) ){
38532 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38533 return JX9_OK;
38534 }
38535 /* Release the archive */
38536 SyArchiveRelease(pArchive);
38537 pRaw = (zip_raw_data *)&pArchive[1];
38538 if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
38539 SyBlobRelease(&pRaw->raw.sBlob);
38540 }else{
38541 const jx9_vfs *pVfs = pRaw->raw.mmap.pVfs;
38542 if( pVfs->xUnmap ){
38543 /* Unmap the memory view */
38544 pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize);
38545 }
38546 }
38547 /* Release the memory chunk */
38548 jx9_context_free_chunk(pCtx, pArchive);
38549 return JX9_OK;
38550}
38551/*
38552 * mixed zip_read(resource $zip)
38553 * Reads the next entry from an in-memory ZIP archive.
38554 * Parameters
38555 * $zip
38556 * A ZIP file previously opened with zip_open().
38557 * Return
38558 * A directory entry resource for later use with the zip_entry_... functions
38559 * or FALSE if there are no more entries to read, or an error code if an error occurred.
38560 */
38561static int jx9Builtin_zip_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
38562{
38563 SyArchiveEntry *pNext = 0; /* cc warning */
38564 SyArchive *pArchive;
38565 sxi32 rc;
38566 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38567 /* Missing/Invalid arguments */
38568 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38569 /* return FALSE */
38570 jx9_result_bool(pCtx, 0);
38571 return JX9_OK;
38572 }
38573 /* Point to the in-memory archive */
38574 pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
38575 /* Make sure we are dealing with a valid ZIP archive */
38576 if( SXARCH_INVALID(pArchive) ){
38577 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38578 /* return FALSE */
38579 jx9_result_bool(pCtx, 0);
38580 return JX9_OK;
38581 }
38582 /* Extract the next entry */
38583 rc = SyArchiveGetNextEntry(pArchive, &pNext);
38584 if( rc != SXRET_OK ){
38585 /* No more entries in the central directory, return FALSE */
38586 jx9_result_bool(pCtx, 0);
38587 }else{
38588 /* Return as a resource handle */
38589 jx9_result_resource(pCtx, pNext);
38590 /* Point to the ZIP raw data */
38591 pNext->pUserData = (void *)&pArchive[1];
38592 }
38593 return JX9_OK;
38594}
38595/*
38596 * bool zip_entry_open(resource $zip, resource $zip_entry[, string $mode ])
38597 * Open a directory entry for reading
38598 * Parameters
38599 * $zip
38600 * A ZIP file previously opened with zip_open().
38601 * $zip_entry
38602 * A directory entry returned by zip_read().
38603 * $mode
38604 * Not used
38605 * Return
38606 * A directory entry resource for later use with the zip_entry_... functions
38607 * or FALSE if there are no more entries to read, or an error code if an error occurred.
38608 */
38609static int jx9Builtin_zip_entry_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
38610{
38611 SyArchiveEntry *pEntry;
38612 SyArchive *pArchive;
38613 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_resource(apArg[1]) ){
38614 /* Missing/Invalid arguments */
38615 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38616 /* return FALSE */
38617 jx9_result_bool(pCtx, 0);
38618 return JX9_OK;
38619 }
38620 /* Point to the in-memory archive */
38621 pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
38622 /* Make sure we are dealing with a valid ZIP archive */
38623 if( SXARCH_INVALID(pArchive) ){
38624 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38625 /* return FALSE */
38626 jx9_result_bool(pCtx, 0);
38627 return JX9_OK;
38628 }
38629 /* Make sure we are dealing with a valid ZIP archive entry */
38630 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[1]);
38631 if( SXARCH_ENTRY_INVALID(pEntry) ){
38632 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38633 /* return FALSE */
38634 jx9_result_bool(pCtx, 0);
38635 return JX9_OK;
38636 }
38637 /* All done. Actually this function is a no-op, return TRUE */
38638 jx9_result_bool(pCtx, 1);
38639 return JX9_OK;
38640}
38641/*
38642 * bool zip_entry_close(resource $zip_entry)
38643 * Close a directory entry.
38644 * Parameters
38645 * $zip_entry
38646 * A directory entry returned by zip_read().
38647 * Return
38648 * Returns TRUE on success or FALSE on failure.
38649 */
38650static int jx9Builtin_zip_entry_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
38651{
38652 SyArchiveEntry *pEntry;
38653 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38654 /* Missing/Invalid arguments */
38655 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38656 /* return FALSE */
38657 jx9_result_bool(pCtx, 0);
38658 return JX9_OK;
38659 }
38660 /* Make sure we are dealing with a valid ZIP archive entry */
38661 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38662 if( SXARCH_ENTRY_INVALID(pEntry) ){
38663 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38664 /* return FALSE */
38665 jx9_result_bool(pCtx, 0);
38666 return JX9_OK;
38667 }
38668 /* Reset the read cursor */
38669 pEntry->nReadCount = 0;
38670 /*All done. Actually this function is a no-op, return TRUE */
38671 jx9_result_bool(pCtx, 1);
38672 return JX9_OK;
38673}
38674/*
38675 * string zip_entry_name(resource $zip_entry)
38676 * Retrieve the name of a directory entry.
38677 * Parameters
38678 * $zip_entry
38679 * A directory entry returned by zip_read().
38680 * Return
38681 * The name of the directory entry.
38682 */
38683static int jx9Builtin_zip_entry_name(jx9_context *pCtx, int nArg, jx9_value **apArg)
38684{
38685 SyArchiveEntry *pEntry;
38686 SyString *pName;
38687 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38688 /* Missing/Invalid arguments */
38689 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38690 /* return FALSE */
38691 jx9_result_bool(pCtx, 0);
38692 return JX9_OK;
38693 }
38694 /* Make sure we are dealing with a valid ZIP archive entry */
38695 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38696 if( SXARCH_ENTRY_INVALID(pEntry) ){
38697 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38698 /* return FALSE */
38699 jx9_result_bool(pCtx, 0);
38700 return JX9_OK;
38701 }
38702 /* Return entry name */
38703 pName = &pEntry->sFileName;
38704 jx9_result_string(pCtx, pName->zString, (int)pName->nByte);
38705 return JX9_OK;
38706}
38707/*
38708 * int64 zip_entry_filesize(resource $zip_entry)
38709 * Retrieve the actual file size of a directory entry.
38710 * Parameters
38711 * $zip_entry
38712 * A directory entry returned by zip_read().
38713 * Return
38714 * The size of the directory entry.
38715 */
38716static int jx9Builtin_zip_entry_filesize(jx9_context *pCtx, int nArg, jx9_value **apArg)
38717{
38718 SyArchiveEntry *pEntry;
38719 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38720 /* Missing/Invalid arguments */
38721 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38722 /* return FALSE */
38723 jx9_result_bool(pCtx, 0);
38724 return JX9_OK;
38725 }
38726 /* Make sure we are dealing with a valid ZIP archive entry */
38727 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38728 if( SXARCH_ENTRY_INVALID(pEntry) ){
38729 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38730 /* return FALSE */
38731 jx9_result_bool(pCtx, 0);
38732 return JX9_OK;
38733 }
38734 /* Return entry size */
38735 jx9_result_int64(pCtx, (jx9_int64)pEntry->nByte);
38736 return JX9_OK;
38737}
38738/*
38739 * int64 zip_entry_compressedsize(resource $zip_entry)
38740 * Retrieve the compressed size of a directory entry.
38741 * Parameters
38742 * $zip_entry
38743 * A directory entry returned by zip_read().
38744 * Return
38745 * The compressed size.
38746 */
38747static int jx9Builtin_zip_entry_compressedsize(jx9_context *pCtx, int nArg, jx9_value **apArg)
38748{
38749 SyArchiveEntry *pEntry;
38750 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38751 /* Missing/Invalid arguments */
38752 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38753 /* return FALSE */
38754 jx9_result_bool(pCtx, 0);
38755 return JX9_OK;
38756 }
38757 /* Make sure we are dealing with a valid ZIP archive entry */
38758 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38759 if( SXARCH_ENTRY_INVALID(pEntry) ){
38760 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38761 /* return FALSE */
38762 jx9_result_bool(pCtx, 0);
38763 return JX9_OK;
38764 }
38765 /* Return entry compressed size */
38766 jx9_result_int64(pCtx, (jx9_int64)pEntry->nByteCompr);
38767 return JX9_OK;
38768}
38769/*
38770 * string zip_entry_read(resource $zip_entry[, int $length])
38771 * Reads from an open directory entry.
38772 * Parameters
38773 * $zip_entry
38774 * A directory entry returned by zip_read().
38775 * $length
38776 * The number of bytes to return. If not specified, this function
38777 * will attempt to read 1024 bytes.
38778 * Return
38779 * Returns the data read, or FALSE if the end of the file is reached.
38780 */
38781static int jx9Builtin_zip_entry_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
38782{
38783 SyArchiveEntry *pEntry;
38784 zip_raw_data *pRaw;
38785 const char *zData;
38786 int iLength;
38787 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38788 /* Missing/Invalid arguments */
38789 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38790 /* return FALSE */
38791 jx9_result_bool(pCtx, 0);
38792 return JX9_OK;
38793 }
38794 /* Make sure we are dealing with a valid ZIP archive entry */
38795 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38796 if( SXARCH_ENTRY_INVALID(pEntry) ){
38797 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38798 /* return FALSE */
38799 jx9_result_bool(pCtx, 0);
38800 return JX9_OK;
38801 }
38802 zData = 0;
38803 if( pEntry->nReadCount >= pEntry->nByteCompr ){
38804 /* No more data to read, return FALSE */
38805 jx9_result_bool(pCtx, 0);
38806 return JX9_OK;
38807 }
38808 /* Set a default read length */
38809 iLength = 1024;
38810 if( nArg > 1 ){
38811 iLength = jx9_value_to_int(apArg[1]);
38812 if( iLength < 1 ){
38813 iLength = 1024;
38814 }
38815 }
38816 if( (sxu32)iLength > pEntry->nByteCompr - pEntry->nReadCount ){
38817 iLength = (int)(pEntry->nByteCompr - pEntry->nReadCount);
38818 }
38819 /* Return the entry contents */
38820 pRaw = (zip_raw_data *)pEntry->pUserData;
38821 if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
38822 zData = (const char *)SyBlobDataAt(&pRaw->raw.sBlob, (pEntry->nOfft+pEntry->nReadCount));
38823 }else{
38824 const char *zMap = (const char *)pRaw->raw.mmap.pMap;
38825 /* Memory mmaped chunk */
38826 zData = &zMap[pEntry->nOfft+pEntry->nReadCount];
38827 }
38828 /* Increment the read counter */
38829 pEntry->nReadCount += iLength;
38830 /* Return the raw data */
38831 jx9_result_string(pCtx, zData, iLength);
38832 return JX9_OK;
38833}
38834/*
38835 * bool zip_entry_reset_cursor(resource $zip_entry)
38836 * Reset the read cursor of an open directory entry.
38837 * Parameters
38838 * $zip_entry
38839 * A directory entry returned by zip_read().
38840 * Return
38841 * TRUE on success, FALSE on failure.
38842 */
38843static int jx9Builtin_zip_entry_reset_cursor(jx9_context *pCtx, int nArg, jx9_value **apArg)
38844{
38845 SyArchiveEntry *pEntry;
38846 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38847 /* Missing/Invalid arguments */
38848 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38849 /* return FALSE */
38850 jx9_result_bool(pCtx, 0);
38851 return JX9_OK;
38852 }
38853 /* Make sure we are dealing with a valid ZIP archive entry */
38854 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38855 if( SXARCH_ENTRY_INVALID(pEntry) ){
38856 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38857 /* return FALSE */
38858 jx9_result_bool(pCtx, 0);
38859 return JX9_OK;
38860 }
38861 /* Reset the cursor */
38862 pEntry->nReadCount = 0;
38863 /* Return TRUE */
38864 jx9_result_bool(pCtx, 1);
38865 return JX9_OK;
38866}
38867/*
38868 * string zip_entry_compressionmethod(resource $zip_entry)
38869 * Retrieve the compression method of a directory entry.
38870 * Parameters
38871 * $zip_entry
38872 * A directory entry returned by zip_read().
38873 * Return
38874 * The compression method on success or FALSE on failure.
38875 */
38876static int jx9Builtin_zip_entry_compressionmethod(jx9_context *pCtx, int nArg, jx9_value **apArg)
38877{
38878 SyArchiveEntry *pEntry;
38879 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38880 /* Missing/Invalid arguments */
38881 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38882 /* return FALSE */
38883 jx9_result_bool(pCtx, 0);
38884 return JX9_OK;
38885 }
38886 /* Make sure we are dealing with a valid ZIP archive entry */
38887 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38888 if( SXARCH_ENTRY_INVALID(pEntry) ){
38889 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38890 /* return FALSE */
38891 jx9_result_bool(pCtx, 0);
38892 return JX9_OK;
38893 }
38894 switch(pEntry->nComprMeth){
38895 case 0:
38896 /* No compression;entry is stored */
38897 jx9_result_string(pCtx, "stored", (int)sizeof("stored")-1);
38898 break;
38899 case 8:
38900 /* Entry is deflated (Default compression algorithm) */
38901 jx9_result_string(pCtx, "deflate", (int)sizeof("deflate")-1);
38902 break;
38903 /* Exotic compression algorithms */
38904 case 1:
38905 jx9_result_string(pCtx, "shrunk", (int)sizeof("shrunk")-1);
38906 break;
38907 case 2:
38908 case 3:
38909 case 4:
38910 case 5:
38911 /* Entry is reduced */
38912 jx9_result_string(pCtx, "reduced", (int)sizeof("reduced")-1);
38913 break;
38914 case 6:
38915 /* Entry is imploded */
38916 jx9_result_string(pCtx, "implode", (int)sizeof("implode")-1);
38917 break;
38918 default:
38919 jx9_result_string(pCtx, "unknown", (int)sizeof("unknown")-1);
38920 break;
38921 }
38922 return JX9_OK;
38923}
38924#endif /* #ifndef JX9_DISABLE_BUILTIN_FUNC*/
38925/* NULL VFS [i.e: a no-op VFS]*/
38926static const jx9_vfs null_vfs = {
38927 "null_vfs",
38928 JX9_VFS_VERSION,
38929 0, /* int (*xChdir)(const char *) */
38930 0, /* int (*xChroot)(const char *); */
38931 0, /* int (*xGetcwd)(jx9_context *) */
38932 0, /* int (*xMkdir)(const char *, int, int) */
38933 0, /* int (*xRmdir)(const char *) */
38934 0, /* int (*xIsdir)(const char *) */
38935 0, /* int (*xRename)(const char *, const char *) */
38936 0, /*int (*xRealpath)(const char *, jx9_context *)*/
38937 0, /* int (*xSleep)(unsigned int) */
38938 0, /* int (*xUnlink)(const char *) */
38939 0, /* int (*xFileExists)(const char *) */
38940 0, /*int (*xChmod)(const char *, int)*/
38941 0, /*int (*xChown)(const char *, const char *)*/
38942 0, /*int (*xChgrp)(const char *, const char *)*/
38943 0, /* jx9_int64 (*xFreeSpace)(const char *) */
38944 0, /* jx9_int64 (*xTotalSpace)(const char *) */
38945 0, /* jx9_int64 (*xFileSize)(const char *) */
38946 0, /* jx9_int64 (*xFileAtime)(const char *) */
38947 0, /* jx9_int64 (*xFileMtime)(const char *) */
38948 0, /* jx9_int64 (*xFileCtime)(const char *) */
38949 0, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
38950 0, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
38951 0, /* int (*xIsfile)(const char *) */
38952 0, /* int (*xIslink)(const char *) */
38953 0, /* int (*xReadable)(const char *) */
38954 0, /* int (*xWritable)(const char *) */
38955 0, /* int (*xExecutable)(const char *) */
38956 0, /* int (*xFiletype)(const char *, jx9_context *) */
38957 0, /* int (*xGetenv)(const char *, jx9_context *) */
38958 0, /* int (*xSetenv)(const char *, const char *) */
38959 0, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
38960 0, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
38961 0, /* void (*xUnmap)(void *, jx9_int64); */
38962 0, /* int (*xLink)(const char *, const char *, int) */
38963 0, /* int (*xUmask)(int) */
38964 0, /* void (*xTempDir)(jx9_context *) */
38965 0, /* unsigned int (*xProcessId)(void) */
38966 0, /* int (*xUid)(void) */
38967 0, /* int (*xGid)(void) */
38968 0, /* void (*xUsername)(jx9_context *) */
38969 0 /* int (*xExec)(const char *, jx9_context *) */
38970};
38971#ifndef JX9_DISABLE_BUILTIN_FUNC
38972#ifndef JX9_DISABLE_DISK_IO
38973#ifdef __WINNT__
38974/*
38975 * Windows VFS implementation for the JX9 engine.
38976 * Authors:
38977 * Symisc Systems, devel@symisc.net.
38978 * Copyright (C) Symisc Systems, http://jx9.symisc.net
38979 * Status:
38980 * Stable.
38981 */
38982/* What follows here is code that is specific to windows systems. */
38983#include <Windows.h>
38984/*
38985** Convert a UTF-8 string to microsoft unicode (UTF-16?).
38986**
38987** Space to hold the returned string is obtained from HeapAlloc().
38988** Taken from the sqlite3 source tree
38989** status: Public Domain
38990*/
38991static WCHAR *jx9utf8ToUnicode(const char *zFilename){
38992 int nChar;
38993 WCHAR *zWideFilename;
38994
38995 nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0);
38996 zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, nChar*sizeof(zWideFilename[0]));
38997 if( zWideFilename == 0 ){
38998 return 0;
38999 }
39000 nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
39001 if( nChar==0 ){
39002 HeapFree(GetProcessHeap(), 0, zWideFilename);
39003 return 0;
39004 }
39005 return zWideFilename;
39006}
39007/*
39008** Convert a UTF-8 filename into whatever form the underlying
39009** operating system wants filenames in.Space to hold the result
39010** is obtained from HeapAlloc() and must be freed by the calling
39011** function.
39012** Taken from the sqlite3 source tree
39013** status: Public Domain
39014*/
39015static void *jx9convertUtf8Filename(const char *zFilename){
39016 void *zConverted;
39017 zConverted = jx9utf8ToUnicode(zFilename);
39018 return zConverted;
39019}
39020/*
39021** Convert microsoft unicode to UTF-8. Space to hold the returned string is
39022** obtained from HeapAlloc().
39023** Taken from the sqlite3 source tree
39024** status: Public Domain
39025*/
39026static char *jx9unicodeToUtf8(const WCHAR *zWideFilename){
39027 char *zFilename;
39028 int nByte;
39029
39030 nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
39031 zFilename = (char *)HeapAlloc(GetProcessHeap(), 0, nByte);
39032 if( zFilename == 0 ){
39033 return 0;
39034 }
39035 nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, 0, 0);
39036 if( nByte == 0 ){
39037 HeapFree(GetProcessHeap(), 0, zFilename);
39038 return 0;
39039 }
39040 return zFilename;
39041}
39042/* int (*xchdir)(const char *) */
39043static int WinVfs_chdir(const char *zPath)
39044{
39045 void * pConverted;
39046 BOOL rc;
39047 pConverted = jx9convertUtf8Filename(zPath);
39048 if( pConverted == 0 ){
39049 return -1;
39050 }
39051 rc = SetCurrentDirectoryW((LPCWSTR)pConverted);
39052 HeapFree(GetProcessHeap(), 0, pConverted);
39053 return rc ? JX9_OK : -1;
39054}
39055/* int (*xGetcwd)(jx9_context *) */
39056static int WinVfs_getcwd(jx9_context *pCtx)
39057{
39058 WCHAR zDir[2048];
39059 char *zConverted;
39060 DWORD rc;
39061 /* Get the current directory */
39062 rc = GetCurrentDirectoryW(sizeof(zDir), zDir);
39063 if( rc < 1 ){
39064 return -1;
39065 }
39066 zConverted = jx9unicodeToUtf8(zDir);
39067 if( zConverted == 0 ){
39068 return -1;
39069 }
39070 jx9_result_string(pCtx, zConverted, -1/*Compute length automatically*/); /* Will make it's own copy */
39071 HeapFree(GetProcessHeap(), 0, zConverted);
39072 return JX9_OK;
39073}
39074/* int (*xMkdir)(const char *, int, int) */
39075static int WinVfs_mkdir(const char *zPath, int mode, int recursive)
39076{
39077 void * pConverted;
39078 BOOL rc;
39079 pConverted = jx9convertUtf8Filename(zPath);
39080 if( pConverted == 0 ){
39081 return -1;
39082 }
39083 mode= 0; /* MSVC warning */
39084 recursive = 0;
39085 rc = CreateDirectoryW((LPCWSTR)pConverted, 0);
39086 HeapFree(GetProcessHeap(), 0, pConverted);
39087 return rc ? JX9_OK : -1;
39088}
39089/* int (*xRmdir)(const char *) */
39090static int WinVfs_rmdir(const char *zPath)
39091{
39092 void * pConverted;
39093 BOOL rc;
39094 pConverted = jx9convertUtf8Filename(zPath);
39095 if( pConverted == 0 ){
39096 return -1;
39097 }
39098 rc = RemoveDirectoryW((LPCWSTR)pConverted);
39099 HeapFree(GetProcessHeap(), 0, pConverted);
39100 return rc ? JX9_OK : -1;
39101}
39102/* int (*xIsdir)(const char *) */
39103static int WinVfs_isdir(const char *zPath)
39104{
39105 void * pConverted;
39106 DWORD dwAttr;
39107 pConverted = jx9convertUtf8Filename(zPath);
39108 if( pConverted == 0 ){
39109 return -1;
39110 }
39111 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39112 HeapFree(GetProcessHeap(), 0, pConverted);
39113 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39114 return -1;
39115 }
39116 return (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? JX9_OK : -1;
39117}
39118/* int (*xRename)(const char *, const char *) */
39119static int WinVfs_Rename(const char *zOld, const char *zNew)
39120{
39121 void *pOld, *pNew;
39122 BOOL rc = 0;
39123 pOld = jx9convertUtf8Filename(zOld);
39124 if( pOld == 0 ){
39125 return -1;
39126 }
39127 pNew = jx9convertUtf8Filename(zNew);
39128 if( pNew ){
39129 rc = MoveFileW((LPCWSTR)pOld, (LPCWSTR)pNew);
39130 }
39131 HeapFree(GetProcessHeap(), 0, pOld);
39132 if( pNew ){
39133 HeapFree(GetProcessHeap(), 0, pNew);
39134 }
39135 return rc ? JX9_OK : - 1;
39136}
39137/* int (*xRealpath)(const char *, jx9_context *) */
39138static int WinVfs_Realpath(const char *zPath, jx9_context *pCtx)
39139{
39140 WCHAR zTemp[2048];
39141 void *pPath;
39142 char *zReal;
39143 DWORD n;
39144 pPath = jx9convertUtf8Filename(zPath);
39145 if( pPath == 0 ){
39146 return -1;
39147 }
39148 n = GetFullPathNameW((LPCWSTR)pPath, 0, 0, 0);
39149 if( n > 0 ){
39150 if( n >= sizeof(zTemp) ){
39151 n = sizeof(zTemp) - 1;
39152 }
39153 GetFullPathNameW((LPCWSTR)pPath, n, zTemp, 0);
39154 }
39155 HeapFree(GetProcessHeap(), 0, pPath);
39156 if( !n ){
39157 return -1;
39158 }
39159 zReal = jx9unicodeToUtf8(zTemp);
39160 if( zReal == 0 ){
39161 return -1;
39162 }
39163 jx9_result_string(pCtx, zReal, -1); /* Will make it's own copy */
39164 HeapFree(GetProcessHeap(), 0, zReal);
39165 return JX9_OK;
39166}
39167/* int (*xSleep)(unsigned int) */
39168static int WinVfs_Sleep(unsigned int uSec)
39169{
39170 Sleep(uSec/1000/*uSec per Millisec */);
39171 return JX9_OK;
39172}
39173/* int (*xUnlink)(const char *) */
39174static int WinVfs_unlink(const char *zPath)
39175{
39176 void * pConverted;
39177 BOOL rc;
39178 pConverted = jx9convertUtf8Filename(zPath);
39179 if( pConverted == 0 ){
39180 return -1;
39181 }
39182 rc = DeleteFileW((LPCWSTR)pConverted);
39183 HeapFree(GetProcessHeap(), 0, pConverted);
39184 return rc ? JX9_OK : - 1;
39185}
39186/* jx9_int64 (*xFreeSpace)(const char *) */
39187static jx9_int64 WinVfs_DiskFreeSpace(const char *zPath)
39188{
39189#ifdef _WIN32_WCE
39190 /* GetDiskFreeSpace is not supported under WINCE */
39191 SXUNUSED(zPath);
39192 return 0;
39193#else
39194 DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
39195 void * pConverted;
39196 WCHAR *p;
39197 BOOL rc;
39198 pConverted = jx9convertUtf8Filename(zPath);
39199 if( pConverted == 0 ){
39200 return 0;
39201 }
39202 p = (WCHAR *)pConverted;
39203 for(;*p;p++){
39204 if( *p == '\\' || *p == '/'){
39205 *p = '\0';
39206 break;
39207 }
39208 }
39209 rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
39210 if( !rc ){
39211 return 0;
39212 }
39213 return (jx9_int64)dwFreeClusters * dwSectPerClust * dwBytesPerSect;
39214#endif
39215}
39216/* jx9_int64 (*xTotalSpace)(const char *) */
39217static jx9_int64 WinVfs_DiskTotalSpace(const char *zPath)
39218{
39219#ifdef _WIN32_WCE
39220 /* GetDiskFreeSpace is not supported under WINCE */
39221 SXUNUSED(zPath);
39222 return 0;
39223#else
39224 DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
39225 void * pConverted;
39226 WCHAR *p;
39227 BOOL rc;
39228 pConverted = jx9convertUtf8Filename(zPath);
39229 if( pConverted == 0 ){
39230 return 0;
39231 }
39232 p = (WCHAR *)pConverted;
39233 for(;*p;p++){
39234 if( *p == '\\' || *p == '/'){
39235 *p = '\0';
39236 break;
39237 }
39238 }
39239 rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
39240 if( !rc ){
39241 return 0;
39242 }
39243 return (jx9_int64)dwTotalClusters * dwSectPerClust * dwBytesPerSect;
39244#endif
39245}
39246/* int (*xFileExists)(const char *) */
39247static int WinVfs_FileExists(const char *zPath)
39248{
39249 void * pConverted;
39250 DWORD dwAttr;
39251 pConverted = jx9convertUtf8Filename(zPath);
39252 if( pConverted == 0 ){
39253 return -1;
39254 }
39255 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39256 HeapFree(GetProcessHeap(), 0, pConverted);
39257 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39258 return -1;
39259 }
39260 return JX9_OK;
39261}
39262/* Open a file in a read-only mode */
39263static HANDLE OpenReadOnly(LPCWSTR pPath)
39264{
39265 DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
39266 DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
39267 DWORD dwAccess = GENERIC_READ;
39268 DWORD dwCreate = OPEN_EXISTING;
39269 HANDLE pHandle;
39270 pHandle = CreateFileW(pPath, dwAccess, dwShare, 0, dwCreate, dwType, 0);
39271 if( pHandle == INVALID_HANDLE_VALUE){
39272 return 0;
39273 }
39274 return pHandle;
39275}
39276/* jx9_int64 (*xFileSize)(const char *) */
39277static jx9_int64 WinVfs_FileSize(const char *zPath)
39278{
39279 DWORD dwLow, dwHigh;
39280 void * pConverted;
39281 jx9_int64 nSize;
39282 HANDLE pHandle;
39283
39284 pConverted = jx9convertUtf8Filename(zPath);
39285 if( pConverted == 0 ){
39286 return -1;
39287 }
39288 /* Open the file in read-only mode */
39289 pHandle = OpenReadOnly((LPCWSTR)pConverted);
39290 HeapFree(GetProcessHeap(), 0, pConverted);
39291 if( pHandle ){
39292 dwLow = GetFileSize(pHandle, &dwHigh);
39293 nSize = dwHigh;
39294 nSize <<= 32;
39295 nSize += dwLow;
39296 CloseHandle(pHandle);
39297 }else{
39298 nSize = -1;
39299 }
39300 return nSize;
39301}
39302#define TICKS_PER_SECOND 10000000
39303#define EPOCH_DIFFERENCE 11644473600LL
39304/* Convert Windows timestamp to UNIX timestamp */
39305static jx9_int64 convertWindowsTimeToUnixTime(LPFILETIME pTime)
39306{
39307 jx9_int64 input, temp;
39308 input = pTime->dwHighDateTime;
39309 input <<= 32;
39310 input += pTime->dwLowDateTime;
39311 temp = input / TICKS_PER_SECOND; /*convert from 100ns intervals to seconds*/
39312 temp = temp - EPOCH_DIFFERENCE; /*subtract number of seconds between epochs*/
39313 return temp;
39314}
39315/* Convert UNIX timestamp to Windows timestamp */
39316static void convertUnixTimeToWindowsTime(jx9_int64 nUnixtime, LPFILETIME pOut)
39317{
39318 jx9_int64 result = EPOCH_DIFFERENCE;
39319 result += nUnixtime;
39320 result *= 10000000LL;
39321 pOut->dwHighDateTime = (DWORD)(nUnixtime>>32);
39322 pOut->dwLowDateTime = (DWORD)nUnixtime;
39323}
39324/* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
39325static int WinVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
39326{
39327 FILETIME sTouch, sAccess;
39328 void *pConverted;
39329 void *pHandle;
39330 BOOL rc = 0;
39331 pConverted = jx9convertUtf8Filename(zPath);
39332 if( pConverted == 0 ){
39333 return -1;
39334 }
39335 pHandle = OpenReadOnly((LPCWSTR)pConverted);
39336 if( pHandle ){
39337 if( touch_time < 0 ){
39338 GetSystemTimeAsFileTime(&sTouch);
39339 }else{
39340 convertUnixTimeToWindowsTime(touch_time, &sTouch);
39341 }
39342 if( access_time < 0 ){
39343 /* Use the touch time */
39344 sAccess = sTouch; /* Structure assignment */
39345 }else{
39346 convertUnixTimeToWindowsTime(access_time, &sAccess);
39347 }
39348 rc = SetFileTime(pHandle, &sTouch, &sAccess, 0);
39349 /* Close the handle */
39350 CloseHandle(pHandle);
39351 }
39352 HeapFree(GetProcessHeap(), 0, pConverted);
39353 return rc ? JX9_OK : -1;
39354}
39355/* jx9_int64 (*xFileAtime)(const char *) */
39356static jx9_int64 WinVfs_FileAtime(const char *zPath)
39357{
39358 BY_HANDLE_FILE_INFORMATION sInfo;
39359 void * pConverted;
39360 jx9_int64 atime;
39361 HANDLE pHandle;
39362 pConverted = jx9convertUtf8Filename(zPath);
39363 if( pConverted == 0 ){
39364 return -1;
39365 }
39366 /* Open the file in read-only mode */
39367 pHandle = OpenReadOnly((LPCWSTR)pConverted);
39368 if( pHandle ){
39369 BOOL rc;
39370 rc = GetFileInformationByHandle(pHandle, &sInfo);
39371 if( rc ){
39372 atime = convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime);
39373 }else{
39374 atime = -1;
39375 }
39376 CloseHandle(pHandle);
39377 }else{
39378 atime = -1;
39379 }
39380 HeapFree(GetProcessHeap(), 0, pConverted);
39381 return atime;
39382}
39383/* jx9_int64 (*xFileMtime)(const char *) */
39384static jx9_int64 WinVfs_FileMtime(const char *zPath)
39385{
39386 BY_HANDLE_FILE_INFORMATION sInfo;
39387 void * pConverted;
39388 jx9_int64 mtime;
39389 HANDLE pHandle;
39390 pConverted = jx9convertUtf8Filename(zPath);
39391 if( pConverted == 0 ){
39392 return -1;
39393 }
39394 /* Open the file in read-only mode */
39395 pHandle = OpenReadOnly((LPCWSTR)pConverted);
39396 if( pHandle ){
39397 BOOL rc;
39398 rc = GetFileInformationByHandle(pHandle, &sInfo);
39399 if( rc ){
39400 mtime = convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime);
39401 }else{
39402 mtime = -1;
39403 }
39404 CloseHandle(pHandle);
39405 }else{
39406 mtime = -1;
39407 }
39408 HeapFree(GetProcessHeap(), 0, pConverted);
39409 return mtime;
39410}
39411/* jx9_int64 (*xFileCtime)(const char *) */
39412static jx9_int64 WinVfs_FileCtime(const char *zPath)
39413{
39414 BY_HANDLE_FILE_INFORMATION sInfo;
39415 void * pConverted;
39416 jx9_int64 ctime;
39417 HANDLE pHandle;
39418 pConverted = jx9convertUtf8Filename(zPath);
39419 if( pConverted == 0 ){
39420 return -1;
39421 }
39422 /* Open the file in read-only mode */
39423 pHandle = OpenReadOnly((LPCWSTR)pConverted);
39424 if( pHandle ){
39425 BOOL rc;
39426 rc = GetFileInformationByHandle(pHandle, &sInfo);
39427 if( rc ){
39428 ctime = convertWindowsTimeToUnixTime(&sInfo.ftCreationTime);
39429 }else{
39430 ctime = -1;
39431 }
39432 CloseHandle(pHandle);
39433 }else{
39434 ctime = -1;
39435 }
39436 HeapFree(GetProcessHeap(), 0, pConverted);
39437 return ctime;
39438}
39439/* int (*xStat)(const char *, jx9_value *, jx9_value *) */
39440/* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
39441static int WinVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
39442{
39443 BY_HANDLE_FILE_INFORMATION sInfo;
39444 void *pConverted;
39445 HANDLE pHandle;
39446 BOOL rc;
39447 pConverted = jx9convertUtf8Filename(zPath);
39448 if( pConverted == 0 ){
39449 return -1;
39450 }
39451 /* Open the file in read-only mode */
39452 pHandle = OpenReadOnly((LPCWSTR)pConverted);
39453 HeapFree(GetProcessHeap(), 0, pConverted);
39454 if( pHandle == 0 ){
39455 return -1;
39456 }
39457 rc = GetFileInformationByHandle(pHandle, &sInfo);
39458 CloseHandle(pHandle);
39459 if( !rc ){
39460 return -1;
39461 }
39462 /* dev */
39463 jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
39464 jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
39465 /* ino */
39466 jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
39467 jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
39468 /* mode */
39469 jx9_value_int(pWorker, 0);
39470 jx9_array_add_strkey_elem(pArray, "mode", pWorker);
39471 /* nlink */
39472 jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
39473 jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
39474 /* uid, gid, rdev */
39475 jx9_value_int(pWorker, 0);
39476 jx9_array_add_strkey_elem(pArray, "uid", pWorker);
39477 jx9_array_add_strkey_elem(pArray, "gid", pWorker);
39478 jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
39479 /* size */
39480 jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
39481 jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
39482 /* atime */
39483 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
39484 jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
39485 /* mtime */
39486 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
39487 jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
39488 /* ctime */
39489 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
39490 jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
39491 /* blksize, blocks */
39492 jx9_value_int(pWorker, 0);
39493 jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
39494 jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
39495 return JX9_OK;
39496}
39497/* int (*xIsfile)(const char *) */
39498static int WinVfs_isfile(const char *zPath)
39499{
39500 void * pConverted;
39501 DWORD dwAttr;
39502 pConverted = jx9convertUtf8Filename(zPath);
39503 if( pConverted == 0 ){
39504 return -1;
39505 }
39506 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39507 HeapFree(GetProcessHeap(), 0, pConverted);
39508 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39509 return -1;
39510 }
39511 return (dwAttr & (FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ? JX9_OK : -1;
39512}
39513/* int (*xIslink)(const char *) */
39514static int WinVfs_islink(const char *zPath)
39515{
39516 void * pConverted;
39517 DWORD dwAttr;
39518 pConverted = jx9convertUtf8Filename(zPath);
39519 if( pConverted == 0 ){
39520 return -1;
39521 }
39522 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39523 HeapFree(GetProcessHeap(), 0, pConverted);
39524 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39525 return -1;
39526 }
39527 return (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) ? JX9_OK : -1;
39528}
39529/* int (*xWritable)(const char *) */
39530static int WinVfs_iswritable(const char *zPath)
39531{
39532 void * pConverted;
39533 DWORD dwAttr;
39534 pConverted = jx9convertUtf8Filename(zPath);
39535 if( pConverted == 0 ){
39536 return -1;
39537 }
39538 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39539 HeapFree(GetProcessHeap(), 0, pConverted);
39540 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39541 return -1;
39542 }
39543 if( (dwAttr & (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL)) == 0 ){
39544 /* Not a regular file */
39545 return -1;
39546 }
39547 if( dwAttr & FILE_ATTRIBUTE_READONLY ){
39548 /* Read-only file */
39549 return -1;
39550 }
39551 /* File is writable */
39552 return JX9_OK;
39553}
39554/* int (*xExecutable)(const char *) */
39555static int WinVfs_isexecutable(const char *zPath)
39556{
39557 void * pConverted;
39558 DWORD dwAttr;
39559 pConverted = jx9convertUtf8Filename(zPath);
39560 if( pConverted == 0 ){
39561 return -1;
39562 }
39563 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39564 HeapFree(GetProcessHeap(), 0, pConverted);
39565 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39566 return -1;
39567 }
39568 if( (dwAttr & FILE_ATTRIBUTE_NORMAL) == 0 ){
39569 /* Not a regular file */
39570 return -1;
39571 }
39572 /* File is executable */
39573 return JX9_OK;
39574}
39575/* int (*xFiletype)(const char *, jx9_context *) */
39576static int WinVfs_Filetype(const char *zPath, jx9_context *pCtx)
39577{
39578 void * pConverted;
39579 DWORD dwAttr;
39580 pConverted = jx9convertUtf8Filename(zPath);
39581 if( pConverted == 0 ){
39582 /* Expand 'unknown' */
39583 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
39584 return -1;
39585 }
39586 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39587 HeapFree(GetProcessHeap(), 0, pConverted);
39588 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39589 /* Expand 'unknown' */
39590 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
39591 return -1;
39592 }
39593 if(dwAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE) ){
39594 jx9_result_string(pCtx, "file", sizeof("file")-1);
39595 }else if(dwAttr & FILE_ATTRIBUTE_DIRECTORY){
39596 jx9_result_string(pCtx, "dir", sizeof("dir")-1);
39597 }else if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT){
39598 jx9_result_string(pCtx, "link", sizeof("link")-1);
39599 }else if(dwAttr & (FILE_ATTRIBUTE_DEVICE)){
39600 jx9_result_string(pCtx, "block", sizeof("block")-1);
39601 }else{
39602 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
39603 }
39604 return JX9_OK;
39605}
39606/* int (*xGetenv)(const char *, jx9_context *) */
39607static int WinVfs_Getenv(const char *zVar, jx9_context *pCtx)
39608{
39609 char zValue[1024];
39610 DWORD n;
39611 /*
39612 * According to MSDN
39613 * If lpBuffer is not large enough to hold the data, the return
39614 * value is the buffer size, in characters, required to hold the
39615 * string and its terminating null character and the contents
39616 * of lpBuffer are undefined.
39617 */
39618 n = sizeof(zValue);
39619 SyMemcpy("Undefined", zValue, sizeof("Undefined")-1);
39620 /* Extract the environment value */
39621 n = GetEnvironmentVariableA(zVar, zValue, sizeof(zValue));
39622 if( !n ){
39623 /* No such variable*/
39624 return -1;
39625 }
39626 jx9_result_string(pCtx, zValue, (int)n);
39627 return JX9_OK;
39628}
39629/* int (*xSetenv)(const char *, const char *) */
39630static int WinVfs_Setenv(const char *zName, const char *zValue)
39631{
39632 BOOL rc;
39633 rc = SetEnvironmentVariableA(zName, zValue);
39634 return rc ? JX9_OK : -1;
39635}
39636/* int (*xMmap)(const char *, void **, jx9_int64 *) */
39637static int WinVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
39638{
39639 DWORD dwSizeLow, dwSizeHigh;
39640 HANDLE pHandle, pMapHandle;
39641 void *pConverted, *pView;
39642
39643 pConverted = jx9convertUtf8Filename(zPath);
39644 if( pConverted == 0 ){
39645 return -1;
39646 }
39647 pHandle = OpenReadOnly((LPCWSTR)pConverted);
39648 HeapFree(GetProcessHeap(), 0, pConverted);
39649 if( pHandle == 0 ){
39650 return -1;
39651 }
39652 /* Get the file size */
39653 dwSizeLow = GetFileSize(pHandle, &dwSizeHigh);
39654 /* Create the mapping */
39655 pMapHandle = CreateFileMappingW(pHandle, 0, PAGE_READONLY, dwSizeHigh, dwSizeLow, 0);
39656 if( pMapHandle == 0 ){
39657 CloseHandle(pHandle);
39658 return -1;
39659 }
39660 *pSize = ((jx9_int64)dwSizeHigh << 32) | dwSizeLow;
39661 /* Obtain the view */
39662 pView = MapViewOfFile(pMapHandle, FILE_MAP_READ, 0, 0, (SIZE_T)(*pSize));
39663 if( pView ){
39664 /* Let the upper layer point to the view */
39665 *ppMap = pView;
39666 }
39667 /* Close the handle
39668 * According to MSDN it's OK the close the HANDLES.
39669 */
39670 CloseHandle(pMapHandle);
39671 CloseHandle(pHandle);
39672 return pView ? JX9_OK : -1;
39673}
39674/* void (*xUnmap)(void *, jx9_int64) */
39675static void WinVfs_Unmap(void *pView, jx9_int64 nSize)
39676{
39677 nSize = 0; /* Compiler warning */
39678 UnmapViewOfFile(pView);
39679}
39680/* void (*xTempDir)(jx9_context *) */
39681static void WinVfs_TempDir(jx9_context *pCtx)
39682{
39683 CHAR zTemp[1024];
39684 DWORD n;
39685 n = GetTempPathA(sizeof(zTemp), zTemp);
39686 if( n < 1 ){
39687 /* Assume the default windows temp directory */
39688 jx9_result_string(pCtx, "C:\\Windows\\Temp", -1/*Compute length automatically*/);
39689 }else{
39690 jx9_result_string(pCtx, zTemp, (int)n);
39691 }
39692}
39693/* unsigned int (*xProcessId)(void) */
39694static unsigned int WinVfs_ProcessId(void)
39695{
39696 DWORD nID = 0;
39697#ifndef __MINGW32__
39698 nID = GetProcessId(GetCurrentProcess());
39699#endif /* __MINGW32__ */
39700 return (unsigned int)nID;
39701}
39702
39703/* Export the windows vfs */
39704static const jx9_vfs sWinVfs = {
39705 "Windows_vfs",
39706 JX9_VFS_VERSION,
39707 WinVfs_chdir, /* int (*xChdir)(const char *) */
39708 0, /* int (*xChroot)(const char *); */
39709 WinVfs_getcwd, /* int (*xGetcwd)(jx9_context *) */
39710 WinVfs_mkdir, /* int (*xMkdir)(const char *, int, int) */
39711 WinVfs_rmdir, /* int (*xRmdir)(const char *) */
39712 WinVfs_isdir, /* int (*xIsdir)(const char *) */
39713 WinVfs_Rename, /* int (*xRename)(const char *, const char *) */
39714 WinVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
39715 WinVfs_Sleep, /* int (*xSleep)(unsigned int) */
39716 WinVfs_unlink, /* int (*xUnlink)(const char *) */
39717 WinVfs_FileExists, /* int (*xFileExists)(const char *) */
39718 0, /*int (*xChmod)(const char *, int)*/
39719 0, /*int (*xChown)(const char *, const char *)*/
39720 0, /*int (*xChgrp)(const char *, const char *)*/
39721 WinVfs_DiskFreeSpace, /* jx9_int64 (*xFreeSpace)(const char *) */
39722 WinVfs_DiskTotalSpace, /* jx9_int64 (*xTotalSpace)(const char *) */
39723 WinVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
39724 WinVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
39725 WinVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
39726 WinVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
39727 WinVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
39728 WinVfs_Stat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
39729 WinVfs_isfile, /* int (*xIsfile)(const char *) */
39730 WinVfs_islink, /* int (*xIslink)(const char *) */
39731 WinVfs_isfile, /* int (*xReadable)(const char *) */
39732 WinVfs_iswritable, /* int (*xWritable)(const char *) */
39733 WinVfs_isexecutable, /* int (*xExecutable)(const char *) */
39734 WinVfs_Filetype, /* int (*xFiletype)(const char *, jx9_context *) */
39735 WinVfs_Getenv, /* int (*xGetenv)(const char *, jx9_context *) */
39736 WinVfs_Setenv, /* int (*xSetenv)(const char *, const char *) */
39737 WinVfs_Touch, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
39738 WinVfs_Mmap, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
39739 WinVfs_Unmap, /* void (*xUnmap)(void *, jx9_int64); */
39740 0, /* int (*xLink)(const char *, const char *, int) */
39741 0, /* int (*xUmask)(int) */
39742 WinVfs_TempDir, /* void (*xTempDir)(jx9_context *) */
39743 WinVfs_ProcessId, /* unsigned int (*xProcessId)(void) */
39744 0, /* int (*xUid)(void) */
39745 0, /* int (*xGid)(void) */
39746 0, /* void (*xUsername)(jx9_context *) */
39747 0 /* int (*xExec)(const char *, jx9_context *) */
39748};
39749/* Windows file IO */
39750#ifndef INVALID_SET_FILE_POINTER
39751# define INVALID_SET_FILE_POINTER ((DWORD)-1)
39752#endif
39753/* int (*xOpen)(const char *, int, jx9_value *, void **) */
39754static int WinFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
39755{
39756 DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
39757 DWORD dwAccess = GENERIC_READ;
39758 DWORD dwShare, dwCreate;
39759 void *pConverted;
39760 HANDLE pHandle;
39761
39762 pConverted = jx9convertUtf8Filename(zPath);
39763 if( pConverted == 0 ){
39764 return -1;
39765 }
39766 /* Set the desired flags according to the open mode */
39767 if( iOpenMode & JX9_IO_OPEN_CREATE ){
39768 /* Open existing file, or create if it doesn't exist */
39769 dwCreate = OPEN_ALWAYS;
39770 if( iOpenMode & JX9_IO_OPEN_TRUNC ){
39771 /* If the specified file exists and is writable, the function overwrites the file */
39772 dwCreate = CREATE_ALWAYS;
39773 }
39774 }else if( iOpenMode & JX9_IO_OPEN_EXCL ){
39775 /* Creates a new file, only if it does not already exist.
39776 * If the file exists, it fails.
39777 */
39778 dwCreate = CREATE_NEW;
39779 }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
39780 /* Opens a file and truncates it so that its size is zero bytes
39781 * The file must exist.
39782 */
39783 dwCreate = TRUNCATE_EXISTING;
39784 }else{
39785 /* Opens a file, only if it exists. */
39786 dwCreate = OPEN_EXISTING;
39787 }
39788 if( iOpenMode & JX9_IO_OPEN_RDWR ){
39789 /* Read+Write access */
39790 dwAccess |= GENERIC_WRITE;
39791 }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
39792 /* Write only access */
39793 dwAccess = GENERIC_WRITE;
39794 }
39795 if( iOpenMode & JX9_IO_OPEN_APPEND ){
39796 /* Append mode */
39797 dwAccess = FILE_APPEND_DATA;
39798 }
39799 if( iOpenMode & JX9_IO_OPEN_TEMP ){
39800 /* File is temporary */
39801 dwType = FILE_ATTRIBUTE_TEMPORARY;
39802 }
39803 dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
39804 pHandle = CreateFileW((LPCWSTR)pConverted, dwAccess, dwShare, 0, dwCreate, dwType, 0);
39805 HeapFree(GetProcessHeap(), 0, pConverted);
39806 if( pHandle == INVALID_HANDLE_VALUE){
39807 SXUNUSED(pResource); /* MSVC warning */
39808 return -1;
39809 }
39810 /* Make the handle accessible to the upper layer */
39811 *ppHandle = (void *)pHandle;
39812 return JX9_OK;
39813}
39814/* An instance of the following structure is used to record state information
39815 * while iterating throw directory entries.
39816 */
39817typedef struct WinDir_Info WinDir_Info;
39818struct WinDir_Info
39819{
39820 HANDLE pDirHandle;
39821 void *pPath;
39822 WIN32_FIND_DATAW sInfo;
39823 int rc;
39824};
39825/* int (*xOpenDir)(const char *, jx9_value *, void **) */
39826static int WinDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
39827{
39828 WinDir_Info *pDirInfo;
39829 void *pConverted;
39830 char *zPrep;
39831 sxu32 n;
39832 /* Prepare the path */
39833 n = SyStrlen(zPath);
39834 zPrep = (char *)HeapAlloc(GetProcessHeap(), 0, n+sizeof("\\*")+4);
39835 if( zPrep == 0 ){
39836 return -1;
39837 }
39838 SyMemcpy((const void *)zPath, zPrep, n);
39839 zPrep[n] = '\\';
39840 zPrep[n+1] = '*';
39841 zPrep[n+2] = 0;
39842 pConverted = jx9convertUtf8Filename(zPrep);
39843 HeapFree(GetProcessHeap(), 0, zPrep);
39844 if( pConverted == 0 ){
39845 return -1;
39846 }
39847 /* Allocate a new instance */
39848 pDirInfo = (WinDir_Info *)HeapAlloc(GetProcessHeap(), 0, sizeof(WinDir_Info));
39849 if( pDirInfo == 0 ){
39850 pResource = 0; /* Compiler warning */
39851 return -1;
39852 }
39853 pDirInfo->rc = SXRET_OK;
39854 pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pConverted, &pDirInfo->sInfo);
39855 if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
39856 /* Cannot open directory */
39857 HeapFree(GetProcessHeap(), 0, pConverted);
39858 HeapFree(GetProcessHeap(), 0, pDirInfo);
39859 return -1;
39860 }
39861 /* Save the path */
39862 pDirInfo->pPath = pConverted;
39863 /* Save our structure */
39864 *ppHandle = pDirInfo;
39865 return JX9_OK;
39866}
39867/* void (*xCloseDir)(void *) */
39868static void WinDir_Close(void *pUserData)
39869{
39870 WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
39871 if( pDirInfo->pDirHandle != INVALID_HANDLE_VALUE ){
39872 FindClose(pDirInfo->pDirHandle);
39873 }
39874 HeapFree(GetProcessHeap(), 0, pDirInfo->pPath);
39875 HeapFree(GetProcessHeap(), 0, pDirInfo);
39876}
39877/* void (*xClose)(void *); */
39878static void WinFile_Close(void *pUserData)
39879{
39880 HANDLE pHandle = (HANDLE)pUserData;
39881 CloseHandle(pHandle);
39882}
39883/* int (*xReadDir)(void *, jx9_context *) */
39884static int WinDir_Read(void *pUserData, jx9_context *pCtx)
39885{
39886 WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
39887 LPWIN32_FIND_DATAW pData;
39888 char *zName;
39889 BOOL rc;
39890 sxu32 n;
39891 if( pDirInfo->rc != SXRET_OK ){
39892 /* No more entry to process */
39893 return -1;
39894 }
39895 pData = &pDirInfo->sInfo;
39896 for(;;){
39897 zName = jx9unicodeToUtf8(pData->cFileName);
39898 if( zName == 0 ){
39899 /* Out of memory */
39900 return -1;
39901 }
39902 n = SyStrlen(zName);
39903 /* Ignore '.' && '..' */
39904 if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
39905 break;
39906 }
39907 HeapFree(GetProcessHeap(), 0, zName);
39908 rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
39909 if( !rc ){
39910 return -1;
39911 }
39912 }
39913 /* Return the current file name */
39914 jx9_result_string(pCtx, zName, -1);
39915 HeapFree(GetProcessHeap(), 0, zName);
39916 /* Point to the next entry */
39917 rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
39918 if( !rc ){
39919 pDirInfo->rc = SXERR_EOF;
39920 }
39921 return JX9_OK;
39922}
39923/* void (*xRewindDir)(void *) */
39924static void WinDir_RewindDir(void *pUserData)
39925{
39926 WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
39927 FindClose(pDirInfo->pDirHandle);
39928 pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pDirInfo->pPath, &pDirInfo->sInfo);
39929 if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
39930 pDirInfo->rc = SXERR_EOF;
39931 }else{
39932 pDirInfo->rc = SXRET_OK;
39933 }
39934}
39935/* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
39936static jx9_int64 WinFile_Read(void *pOS, void *pBuffer, jx9_int64 nDatatoRead)
39937{
39938 HANDLE pHandle = (HANDLE)pOS;
39939 DWORD nRd;
39940 BOOL rc;
39941 rc = ReadFile(pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
39942 if( !rc ){
39943 /* EOF or IO error */
39944 return -1;
39945 }
39946 return (jx9_int64)nRd;
39947}
39948/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
39949static jx9_int64 WinFile_Write(void *pOS, const void *pBuffer, jx9_int64 nWrite)
39950{
39951 const char *zData = (const char *)pBuffer;
39952 HANDLE pHandle = (HANDLE)pOS;
39953 jx9_int64 nCount;
39954 DWORD nWr;
39955 BOOL rc;
39956 nWr = 0;
39957 nCount = 0;
39958 for(;;){
39959 if( nWrite < 1 ){
39960 break;
39961 }
39962 rc = WriteFile(pHandle, zData, (DWORD)nWrite, &nWr, 0);
39963 if( !rc ){
39964 /* IO error */
39965 break;
39966 }
39967 nWrite -= nWr;
39968 nCount += nWr;
39969 zData += nWr;
39970 }
39971 if( nWrite > 0 ){
39972 return -1;
39973 }
39974 return nCount;
39975}
39976/* int (*xSeek)(void *, jx9_int64, int) */
39977static int WinFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
39978{
39979 HANDLE pHandle = (HANDLE)pUserData;
39980 DWORD dwMove, dwNew;
39981 LONG nHighOfft;
39982 switch(whence){
39983 case 1:/*SEEK_CUR*/
39984 dwMove = FILE_CURRENT;
39985 break;
39986 case 2: /* SEEK_END */
39987 dwMove = FILE_END;
39988 break;
39989 case 0: /* SEEK_SET */
39990 default:
39991 dwMove = FILE_BEGIN;
39992 break;
39993 }
39994 nHighOfft = (LONG)(iOfft >> 32);
39995 dwNew = SetFilePointer(pHandle, (LONG)iOfft, &nHighOfft, dwMove);
39996 if( dwNew == INVALID_SET_FILE_POINTER ){
39997 return -1;
39998 }
39999 return JX9_OK;
40000}
40001/* int (*xLock)(void *, int) */
40002static int WinFile_Lock(void *pUserData, int lock_type)
40003{
40004 HANDLE pHandle = (HANDLE)pUserData;
40005 static DWORD dwLo = 0, dwHi = 0; /* xx: MT-SAFE */
40006 OVERLAPPED sDummy;
40007 BOOL rc;
40008 SyZero(&sDummy, sizeof(sDummy));
40009 /* Get the file size */
40010 if( lock_type < 1 ){
40011 /* Unlock the file */
40012 rc = UnlockFileEx(pHandle, 0, dwLo, dwHi, &sDummy);
40013 }else{
40014 DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; /* Shared non-blocking lock by default*/
40015 /* Lock the file */
40016 if( lock_type == 1 /* LOCK_EXCL */ ){
40017 dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
40018 }
40019 dwLo = GetFileSize(pHandle, &dwHi);
40020 rc = LockFileEx(pHandle, dwFlags, 0, dwLo, dwHi, &sDummy);
40021 }
40022 return rc ? JX9_OK : -1 /* Lock error */;
40023}
40024/* jx9_int64 (*xTell)(void *) */
40025static jx9_int64 WinFile_Tell(void *pUserData)
40026{
40027 HANDLE pHandle = (HANDLE)pUserData;
40028 DWORD dwNew;
40029 dwNew = SetFilePointer(pHandle, 0, 0, FILE_CURRENT/* SEEK_CUR */);
40030 if( dwNew == INVALID_SET_FILE_POINTER ){
40031 return -1;
40032 }
40033 return (jx9_int64)dwNew;
40034}
40035/* int (*xTrunc)(void *, jx9_int64) */
40036static int WinFile_Trunc(void *pUserData, jx9_int64 nOfft)
40037{
40038 HANDLE pHandle = (HANDLE)pUserData;
40039 LONG HighOfft;
40040 DWORD dwNew;
40041 BOOL rc;
40042 HighOfft = (LONG)(nOfft >> 32);
40043 dwNew = SetFilePointer(pHandle, (LONG)nOfft, &HighOfft, FILE_BEGIN);
40044 if( dwNew == INVALID_SET_FILE_POINTER ){
40045 return -1;
40046 }
40047 rc = SetEndOfFile(pHandle);
40048 return rc ? JX9_OK : -1;
40049}
40050/* int (*xSync)(void *); */
40051static int WinFile_Sync(void *pUserData)
40052{
40053 HANDLE pHandle = (HANDLE)pUserData;
40054 BOOL rc;
40055 rc = FlushFileBuffers(pHandle);
40056 return rc ? JX9_OK : - 1;
40057}
40058/* int (*xStat)(void *, jx9_value *, jx9_value *) */
40059static int WinFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
40060{
40061 BY_HANDLE_FILE_INFORMATION sInfo;
40062 HANDLE pHandle = (HANDLE)pUserData;
40063 BOOL rc;
40064 rc = GetFileInformationByHandle(pHandle, &sInfo);
40065 if( !rc ){
40066 return -1;
40067 }
40068 /* dev */
40069 jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
40070 jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
40071 /* ino */
40072 jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
40073 jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
40074 /* mode */
40075 jx9_value_int(pWorker, 0);
40076 jx9_array_add_strkey_elem(pArray, "mode", pWorker);
40077 /* nlink */
40078 jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
40079 jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
40080 /* uid, gid, rdev */
40081 jx9_value_int(pWorker, 0);
40082 jx9_array_add_strkey_elem(pArray, "uid", pWorker);
40083 jx9_array_add_strkey_elem(pArray, "gid", pWorker);
40084 jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
40085 /* size */
40086 jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
40087 jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
40088 /* atime */
40089 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
40090 jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
40091 /* mtime */
40092 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
40093 jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
40094 /* ctime */
40095 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
40096 jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
40097 /* blksize, blocks */
40098 jx9_value_int(pWorker, 0);
40099 jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
40100 jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
40101 return JX9_OK;
40102}
40103/* Export the file:// stream */
40104static const jx9_io_stream sWinFileStream = {
40105 "file", /* Stream name */
40106 JX9_IO_STREAM_VERSION,
40107 WinFile_Open, /* xOpen */
40108 WinDir_Open, /* xOpenDir */
40109 WinFile_Close, /* xClose */
40110 WinDir_Close, /* xCloseDir */
40111 WinFile_Read, /* xRead */
40112 WinDir_Read, /* xReadDir */
40113 WinFile_Write, /* xWrite */
40114 WinFile_Seek, /* xSeek */
40115 WinFile_Lock, /* xLock */
40116 WinDir_RewindDir, /* xRewindDir */
40117 WinFile_Tell, /* xTell */
40118 WinFile_Trunc, /* xTrunc */
40119 WinFile_Sync, /* xSeek */
40120 WinFile_Stat /* xStat */
40121};
40122#elif defined(__UNIXES__)
40123/*
40124 * UNIX VFS implementation for the JX9 engine.
40125 * Authors:
40126 * Symisc Systems, devel@symisc.net.
40127 * Copyright (C) Symisc Systems, http://jx9.symisc.net
40128 * Status:
40129 * Stable.
40130 */
40131#include <sys/types.h>
40132#include <limits.h>
40133#include <fcntl.h>
40134#include <unistd.h>
40135#include <sys/uio.h>
40136#include <sys/stat.h>
40137#include <sys/mman.h>
40138#include <sys/file.h>
40139#include <pwd.h>
40140#include <grp.h>
40141#include <dirent.h>
40142#include <utime.h>
40143#include <stdio.h>
40144#include <stdlib.h>
40145/* int (*xchdir)(const char *) */
40146static int UnixVfs_chdir(const char *zPath)
40147{
40148 int rc;
40149 rc = chdir(zPath);
40150 return rc == 0 ? JX9_OK : -1;
40151}
40152/* int (*xGetcwd)(jx9_context *) */
40153static int UnixVfs_getcwd(jx9_context *pCtx)
40154{
40155 char zBuf[4096];
40156 char *zDir;
40157 /* Get the current directory */
40158 zDir = getcwd(zBuf, sizeof(zBuf));
40159 if( zDir == 0 ){
40160 return -1;
40161 }
40162 jx9_result_string(pCtx, zDir, -1/*Compute length automatically*/);
40163 return JX9_OK;
40164}
40165/* int (*xMkdir)(const char *, int, int) */
40166static int UnixVfs_mkdir(const char *zPath, int mode, int recursive)
40167{
40168 int rc;
40169 rc = mkdir(zPath, mode);
40170 recursive = 0; /* cc warning */
40171 return rc == 0 ? JX9_OK : -1;
40172}
40173/* int (*xRmdir)(const char *) */
40174static int UnixVfs_rmdir(const char *zPath)
40175{
40176 int rc;
40177 rc = rmdir(zPath);
40178 return rc == 0 ? JX9_OK : -1;
40179}
40180/* int (*xIsdir)(const char *) */
40181static int UnixVfs_isdir(const char *zPath)
40182{
40183 struct stat st;
40184 int rc;
40185 rc = stat(zPath, &st);
40186 if( rc != 0 ){
40187 return -1;
40188 }
40189 rc = S_ISDIR(st.st_mode);
40190 return rc ? JX9_OK : -1 ;
40191}
40192/* int (*xRename)(const char *, const char *) */
40193static int UnixVfs_Rename(const char *zOld, const char *zNew)
40194{
40195 int rc;
40196 rc = rename(zOld, zNew);
40197 return rc == 0 ? JX9_OK : -1;
40198}
40199/* int (*xRealpath)(const char *, jx9_context *) */
40200static int UnixVfs_Realpath(const char *zPath, jx9_context *pCtx)
40201{
40202#ifndef JX9_UNIX_OLD_LIBC
40203 char *zReal;
40204 zReal = realpath(zPath, 0);
40205 if( zReal == 0 ){
40206 return -1;
40207 }
40208 jx9_result_string(pCtx, zReal, -1/*Compute length automatically*/);
40209 /* Release the allocated buffer */
40210 free(zReal);
40211 return JX9_OK;
40212#else
40213 zPath = 0; /* cc warning */
40214 pCtx = 0;
40215 return -1;
40216#endif
40217}
40218/* int (*xSleep)(unsigned int) */
40219static int UnixVfs_Sleep(unsigned int uSec)
40220{
40221 usleep(uSec);
40222 return JX9_OK;
40223}
40224/* int (*xUnlink)(const char *) */
40225static int UnixVfs_unlink(const char *zPath)
40226{
40227 int rc;
40228 rc = unlink(zPath);
40229 return rc == 0 ? JX9_OK : -1 ;
40230}
40231/* int (*xFileExists)(const char *) */
40232static int UnixVfs_FileExists(const char *zPath)
40233{
40234 int rc;
40235 rc = access(zPath, F_OK);
40236 return rc == 0 ? JX9_OK : -1;
40237}
40238/* jx9_int64 (*xFileSize)(const char *) */
40239static jx9_int64 UnixVfs_FileSize(const char *zPath)
40240{
40241 struct stat st;
40242 int rc;
40243 rc = stat(zPath, &st);
40244 if( rc != 0 ){
40245 return -1;
40246 }
40247 return (jx9_int64)st.st_size;
40248}
40249/* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
40250static int UnixVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
40251{
40252 struct utimbuf ut;
40253 int rc;
40254 ut.actime = (time_t)access_time;
40255 ut.modtime = (time_t)touch_time;
40256 rc = utime(zPath, &ut);
40257 if( rc != 0 ){
40258 return -1;
40259 }
40260 return JX9_OK;
40261}
40262/* jx9_int64 (*xFileAtime)(const char *) */
40263static jx9_int64 UnixVfs_FileAtime(const char *zPath)
40264{
40265 struct stat st;
40266 int rc;
40267 rc = stat(zPath, &st);
40268 if( rc != 0 ){
40269 return -1;
40270 }
40271 return (jx9_int64)st.st_atime;
40272}
40273/* jx9_int64 (*xFileMtime)(const char *) */
40274static jx9_int64 UnixVfs_FileMtime(const char *zPath)
40275{
40276 struct stat st;
40277 int rc;
40278 rc = stat(zPath, &st);
40279 if( rc != 0 ){
40280 return -1;
40281 }
40282 return (jx9_int64)st.st_mtime;
40283}
40284/* jx9_int64 (*xFileCtime)(const char *) */
40285static jx9_int64 UnixVfs_FileCtime(const char *zPath)
40286{
40287 struct stat st;
40288 int rc;
40289 rc = stat(zPath, &st);
40290 if( rc != 0 ){
40291 return -1;
40292 }
40293 return (jx9_int64)st.st_ctime;
40294}
40295/* int (*xStat)(const char *, jx9_value *, jx9_value *) */
40296static int UnixVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
40297{
40298 struct stat st;
40299 int rc;
40300 rc = stat(zPath, &st);
40301 if( rc != 0 ){
40302 return -1;
40303 }
40304 /* dev */
40305 jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
40306 jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
40307 /* ino */
40308 jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
40309 jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
40310 /* mode */
40311 jx9_value_int(pWorker, (int)st.st_mode);
40312 jx9_array_add_strkey_elem(pArray, "mode", pWorker);
40313 /* nlink */
40314 jx9_value_int(pWorker, (int)st.st_nlink);
40315 jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
40316 /* uid, gid, rdev */
40317 jx9_value_int(pWorker, (int)st.st_uid);
40318 jx9_array_add_strkey_elem(pArray, "uid", pWorker);
40319 jx9_value_int(pWorker, (int)st.st_gid);
40320 jx9_array_add_strkey_elem(pArray, "gid", pWorker);
40321 jx9_value_int(pWorker, (int)st.st_rdev);
40322 jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
40323 /* size */
40324 jx9_value_int64(pWorker, (jx9_int64)st.st_size);
40325 jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
40326 /* atime */
40327 jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
40328 jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
40329 /* mtime */
40330 jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
40331 jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
40332 /* ctime */
40333 jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
40334 jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
40335 /* blksize, blocks */
40336 jx9_value_int(pWorker, (int)st.st_blksize);
40337 jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
40338 jx9_value_int(pWorker, (int)st.st_blocks);
40339 jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
40340 return JX9_OK;
40341}
40342/* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
40343static int UnixVfs_lStat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
40344{
40345 struct stat st;
40346 int rc;
40347 rc = lstat(zPath, &st);
40348 if( rc != 0 ){
40349 return -1;
40350 }
40351 /* dev */
40352 jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
40353 jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
40354 /* ino */
40355 jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
40356 jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
40357 /* mode */
40358 jx9_value_int(pWorker, (int)st.st_mode);
40359 jx9_array_add_strkey_elem(pArray, "mode", pWorker);
40360 /* nlink */
40361 jx9_value_int(pWorker, (int)st.st_nlink);
40362 jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
40363 /* uid, gid, rdev */
40364 jx9_value_int(pWorker, (int)st.st_uid);
40365 jx9_array_add_strkey_elem(pArray, "uid", pWorker);
40366 jx9_value_int(pWorker, (int)st.st_gid);
40367 jx9_array_add_strkey_elem(pArray, "gid", pWorker);
40368 jx9_value_int(pWorker, (int)st.st_rdev);
40369 jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
40370 /* size */
40371 jx9_value_int64(pWorker, (jx9_int64)st.st_size);
40372 jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
40373 /* atime */
40374 jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
40375 jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
40376 /* mtime */
40377 jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
40378 jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
40379 /* ctime */
40380 jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
40381 jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
40382 /* blksize, blocks */
40383 jx9_value_int(pWorker, (int)st.st_blksize);
40384 jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
40385 jx9_value_int(pWorker, (int)st.st_blocks);
40386 jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
40387 return JX9_OK;
40388}
40389/* int (*xChmod)(const char *, int) */
40390static int UnixVfs_Chmod(const char *zPath, int mode)
40391{
40392 int rc;
40393 rc = chmod(zPath, (mode_t)mode);
40394 return rc == 0 ? JX9_OK : - 1;
40395}
40396/* int (*xChown)(const char *, const char *) */
40397static int UnixVfs_Chown(const char *zPath, const char *zUser)
40398{
40399#ifndef JX9_UNIX_STATIC_BUILD
40400 struct passwd *pwd;
40401 uid_t uid;
40402 int rc;
40403 pwd = getpwnam(zUser); /* Try getting UID for username */
40404 if (pwd == 0) {
40405 return -1;
40406 }
40407 uid = pwd->pw_uid;
40408 rc = chown(zPath, uid, -1);
40409 return rc == 0 ? JX9_OK : -1;
40410#else
40411 SXUNUSED(zPath);
40412 SXUNUSED(zUser);
40413 return -1;
40414#endif /* JX9_UNIX_STATIC_BUILD */
40415}
40416/* int (*xChgrp)(const char *, const char *) */
40417static int UnixVfs_Chgrp(const char *zPath, const char *zGroup)
40418{
40419#ifndef JX9_UNIX_STATIC_BUILD
40420 struct group *group;
40421 gid_t gid;
40422 int rc;
40423 group = getgrnam(zGroup);
40424 if (group == 0) {
40425 return -1;
40426 }
40427 gid = group->gr_gid;
40428 rc = chown(zPath, -1, gid);
40429 return rc == 0 ? JX9_OK : -1;
40430#else
40431 SXUNUSED(zPath);
40432 SXUNUSED(zGroup);
40433 return -1;
40434#endif /* JX9_UNIX_STATIC_BUILD */
40435}
40436/* int (*xIsfile)(const char *) */
40437static int UnixVfs_isfile(const char *zPath)
40438{
40439 struct stat st;
40440 int rc;
40441 rc = stat(zPath, &st);
40442 if( rc != 0 ){
40443 return -1;
40444 }
40445 rc = S_ISREG(st.st_mode);
40446 return rc ? JX9_OK : -1 ;
40447}
40448/* int (*xIslink)(const char *) */
40449static int UnixVfs_islink(const char *zPath)
40450{
40451 struct stat st;
40452 int rc;
40453 rc = stat(zPath, &st);
40454 if( rc != 0 ){
40455 return -1;
40456 }
40457 rc = S_ISLNK(st.st_mode);
40458 return rc ? JX9_OK : -1 ;
40459}
40460/* int (*xReadable)(const char *) */
40461static int UnixVfs_isreadable(const char *zPath)
40462{
40463 int rc;
40464 rc = access(zPath, R_OK);
40465 return rc == 0 ? JX9_OK : -1;
40466}
40467/* int (*xWritable)(const char *) */
40468static int UnixVfs_iswritable(const char *zPath)
40469{
40470 int rc;
40471 rc = access(zPath, W_OK);
40472 return rc == 0 ? JX9_OK : -1;
40473}
40474/* int (*xExecutable)(const char *) */
40475static int UnixVfs_isexecutable(const char *zPath)
40476{
40477 int rc;
40478 rc = access(zPath, X_OK);
40479 return rc == 0 ? JX9_OK : -1;
40480}
40481/* int (*xFiletype)(const char *, jx9_context *) */
40482static int UnixVfs_Filetype(const char *zPath, jx9_context *pCtx)
40483{
40484 struct stat st;
40485 int rc;
40486 rc = stat(zPath, &st);
40487 if( rc != 0 ){
40488 /* Expand 'unknown' */
40489 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
40490 return -1;
40491 }
40492 if(S_ISREG(st.st_mode) ){
40493 jx9_result_string(pCtx, "file", sizeof("file")-1);
40494 }else if(S_ISDIR(st.st_mode)){
40495 jx9_result_string(pCtx, "dir", sizeof("dir")-1);
40496 }else if(S_ISLNK(st.st_mode)){
40497 jx9_result_string(pCtx, "link", sizeof("link")-1);
40498 }else if(S_ISBLK(st.st_mode)){
40499 jx9_result_string(pCtx, "block", sizeof("block")-1);
40500 }else if(S_ISSOCK(st.st_mode)){
40501 jx9_result_string(pCtx, "socket", sizeof("socket")-1);
40502 }else if(S_ISFIFO(st.st_mode)){
40503 jx9_result_string(pCtx, "fifo", sizeof("fifo")-1);
40504 }else{
40505 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
40506 }
40507 return JX9_OK;
40508}
40509/* int (*xGetenv)(const char *, jx9_context *) */
40510static int UnixVfs_Getenv(const char *zVar, jx9_context *pCtx)
40511{
40512 char *zEnv;
40513 zEnv = getenv(zVar);
40514 if( zEnv == 0 ){
40515 return -1;
40516 }
40517 jx9_result_string(pCtx, zEnv, -1/*Compute length automatically*/);
40518 return JX9_OK;
40519}
40520/* int (*xSetenv)(const char *, const char *) */
40521static int UnixVfs_Setenv(const char *zName, const char *zValue)
40522{
40523 int rc;
40524 rc = setenv(zName, zValue, 1);
40525 return rc == 0 ? JX9_OK : -1;
40526}
40527/* int (*xMmap)(const char *, void **, jx9_int64 *) */
40528static int UnixVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
40529{
40530 struct stat st;
40531 void *pMap;
40532 int fd;
40533 int rc;
40534 /* Open the file in a read-only mode */
40535 fd = open(zPath, O_RDONLY);
40536 if( fd < 0 ){
40537 return -1;
40538 }
40539 /* stat the handle */
40540 fstat(fd, &st);
40541 /* Obtain a memory view of the whole file */
40542 pMap = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0);
40543 rc = JX9_OK;
40544 if( pMap == MAP_FAILED ){
40545 rc = -1;
40546 }else{
40547 /* Point to the memory view */
40548 *ppMap = pMap;
40549 *pSize = (jx9_int64)st.st_size;
40550 }
40551 close(fd);
40552 return rc;
40553}
40554/* void (*xUnmap)(void *, jx9_int64) */
40555static void UnixVfs_Unmap(void *pView, jx9_int64 nSize)
40556{
40557 munmap(pView, (size_t)nSize);
40558}
40559/* void (*xTempDir)(jx9_context *) */
40560static void UnixVfs_TempDir(jx9_context *pCtx)
40561{
40562 static const char *azDirs[] = {
40563 "/var/tmp",
40564 "/usr/tmp",
40565 "/usr/local/tmp"
40566 };
40567 unsigned int i;
40568 struct stat buf;
40569 const char *zDir;
40570 zDir = getenv("TMPDIR");
40571 if( zDir && zDir[0] != 0 && !access(zDir, 07) ){
40572 jx9_result_string(pCtx, zDir, -1);
40573 return;
40574 }
40575 for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
40576 zDir=azDirs[i];
40577 if( zDir==0 ) continue;
40578 if( stat(zDir, &buf) ) continue;
40579 if( !S_ISDIR(buf.st_mode) ) continue;
40580 if( access(zDir, 07) ) continue;
40581 /* Got one */
40582 jx9_result_string(pCtx, zDir, -1);
40583 return;
40584 }
40585 /* Default temp dir */
40586 jx9_result_string(pCtx, "/tmp", (int)sizeof("/tmp")-1);
40587}
40588/* unsigned int (*xProcessId)(void) */
40589static unsigned int UnixVfs_ProcessId(void)
40590{
40591 return (unsigned int)getpid();
40592}
40593/* int (*xUid)(void) */
40594static int UnixVfs_uid(void)
40595{
40596 return (int)getuid();
40597}
40598/* int (*xGid)(void) */
40599static int UnixVfs_gid(void)
40600{
40601 return (int)getgid();
40602}
40603/* int (*xUmask)(int) */
40604static int UnixVfs_Umask(int new_mask)
40605{
40606 int old_mask;
40607 old_mask = umask(new_mask);
40608 return old_mask;
40609}
40610/* void (*xUsername)(jx9_context *) */
40611static void UnixVfs_Username(jx9_context *pCtx)
40612{
40613#ifndef JX9_UNIX_STATIC_BUILD
40614 struct passwd *pwd;
40615 uid_t uid;
40616 uid = getuid();
40617 pwd = getpwuid(uid); /* Try getting UID for username */
40618 if (pwd == 0) {
40619 return;
40620 }
40621 /* Return the username */
40622 jx9_result_string(pCtx, pwd->pw_name, -1);
40623#else
40624 jx9_result_string(pCtx, "Unknown", -1);
40625#endif /* JX9_UNIX_STATIC_BUILD */
40626 return;
40627}
40628/* int (*xLink)(const char *, const char *, int) */
40629static int UnixVfs_link(const char *zSrc, const char *zTarget, int is_sym)
40630{
40631 int rc;
40632 if( is_sym ){
40633 /* Symbolic link */
40634 rc = symlink(zSrc, zTarget);
40635 }else{
40636 /* Hard link */
40637 rc = link(zSrc, zTarget);
40638 }
40639 return rc == 0 ? JX9_OK : -1;
40640}
40641/* int (*xChroot)(const char *) */
40642static int UnixVfs_chroot(const char *zRootDir)
40643{
40644 int rc;
40645 rc = chroot(zRootDir);
40646 return rc == 0 ? JX9_OK : -1;
40647}
40648/* Export the UNIX vfs */
40649static const jx9_vfs sUnixVfs = {
40650 "Unix_vfs",
40651 JX9_VFS_VERSION,
40652 UnixVfs_chdir, /* int (*xChdir)(const char *) */
40653 UnixVfs_chroot, /* int (*xChroot)(const char *); */
40654 UnixVfs_getcwd, /* int (*xGetcwd)(jx9_context *) */
40655 UnixVfs_mkdir, /* int (*xMkdir)(const char *, int, int) */
40656 UnixVfs_rmdir, /* int (*xRmdir)(const char *) */
40657 UnixVfs_isdir, /* int (*xIsdir)(const char *) */
40658 UnixVfs_Rename, /* int (*xRename)(const char *, const char *) */
40659 UnixVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
40660 UnixVfs_Sleep, /* int (*xSleep)(unsigned int) */
40661 UnixVfs_unlink, /* int (*xUnlink)(const char *) */
40662 UnixVfs_FileExists, /* int (*xFileExists)(const char *) */
40663 UnixVfs_Chmod, /*int (*xChmod)(const char *, int)*/
40664 UnixVfs_Chown, /*int (*xChown)(const char *, const char *)*/
40665 UnixVfs_Chgrp, /*int (*xChgrp)(const char *, const char *)*/
40666 0, /* jx9_int64 (*xFreeSpace)(const char *) */
40667 0, /* jx9_int64 (*xTotalSpace)(const char *) */
40668 UnixVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
40669 UnixVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
40670 UnixVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
40671 UnixVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
40672 UnixVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
40673 UnixVfs_lStat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
40674 UnixVfs_isfile, /* int (*xIsfile)(const char *) */
40675 UnixVfs_islink, /* int (*xIslink)(const char *) */
40676 UnixVfs_isreadable, /* int (*xReadable)(const char *) */
40677 UnixVfs_iswritable, /* int (*xWritable)(const char *) */
40678 UnixVfs_isexecutable, /* int (*xExecutable)(const char *) */
40679 UnixVfs_Filetype, /* int (*xFiletype)(const char *, jx9_context *) */
40680 UnixVfs_Getenv, /* int (*xGetenv)(const char *, jx9_context *) */
40681 UnixVfs_Setenv, /* int (*xSetenv)(const char *, const char *) */
40682 UnixVfs_Touch, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
40683 UnixVfs_Mmap, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
40684 UnixVfs_Unmap, /* void (*xUnmap)(void *, jx9_int64); */
40685 UnixVfs_link, /* int (*xLink)(const char *, const char *, int) */
40686 UnixVfs_Umask, /* int (*xUmask)(int) */
40687 UnixVfs_TempDir, /* void (*xTempDir)(jx9_context *) */
40688 UnixVfs_ProcessId, /* unsigned int (*xProcessId)(void) */
40689 UnixVfs_uid, /* int (*xUid)(void) */
40690 UnixVfs_gid, /* int (*xGid)(void) */
40691 UnixVfs_Username, /* void (*xUsername)(jx9_context *) */
40692 0 /* int (*xExec)(const char *, jx9_context *) */
40693};
40694/* UNIX File IO */
40695#define JX9_UNIX_OPEN_MODE 0640 /* Default open mode */
40696/* int (*xOpen)(const char *, int, jx9_value *, void **) */
40697static int UnixFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
40698{
40699 int iOpen = O_RDONLY;
40700 int fd;
40701 /* Set the desired flags according to the open mode */
40702 if( iOpenMode & JX9_IO_OPEN_CREATE ){
40703 /* Open existing file, or create if it doesn't exist */
40704 iOpen = O_CREAT;
40705 if( iOpenMode & JX9_IO_OPEN_TRUNC ){
40706 /* If the specified file exists and is writable, the function overwrites the file */
40707 iOpen |= O_TRUNC;
40708 SXUNUSED(pResource); /* cc warning */
40709 }
40710 }else if( iOpenMode & JX9_IO_OPEN_EXCL ){
40711 /* Creates a new file, only if it does not already exist.
40712 * If the file exists, it fails.
40713 */
40714 iOpen = O_CREAT|O_EXCL;
40715 }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
40716 /* Opens a file and truncates it so that its size is zero bytes
40717 * The file must exist.
40718 */
40719 iOpen = O_RDWR|O_TRUNC;
40720 }
40721 if( iOpenMode & JX9_IO_OPEN_RDWR ){
40722 /* Read+Write access */
40723 iOpen &= ~O_RDONLY;
40724 iOpen |= O_RDWR;
40725 }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
40726 /* Write only access */
40727 iOpen &= ~O_RDONLY;
40728 iOpen |= O_WRONLY;
40729 }
40730 if( iOpenMode & JX9_IO_OPEN_APPEND ){
40731 /* Append mode */
40732 iOpen |= O_APPEND;
40733 }
40734#ifdef O_TEMP
40735 if( iOpenMode & JX9_IO_OPEN_TEMP ){
40736 /* File is temporary */
40737 iOpen |= O_TEMP;
40738 }
40739#endif
40740 /* Open the file now */
40741 fd = open(zPath, iOpen, JX9_UNIX_OPEN_MODE);
40742 if( fd < 0 ){
40743 /* IO error */
40744 return -1;
40745 }
40746 /* Save the handle */
40747 *ppHandle = SX_INT_TO_PTR(fd);
40748 return JX9_OK;
40749}
40750/* int (*xOpenDir)(const char *, jx9_value *, void **) */
40751static int UnixDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
40752{
40753 DIR *pDir;
40754 /* Open the target directory */
40755 pDir = opendir(zPath);
40756 if( pDir == 0 ){
40757 pResource = 0; /* Compiler warning */
40758 return -1;
40759 }
40760 /* Save our structure */
40761 *ppHandle = pDir;
40762 return JX9_OK;
40763}
40764/* void (*xCloseDir)(void *) */
40765static void UnixDir_Close(void *pUserData)
40766{
40767 closedir((DIR *)pUserData);
40768}
40769/* void (*xClose)(void *); */
40770static void UnixFile_Close(void *pUserData)
40771{
40772 close(SX_PTR_TO_INT(pUserData));
40773}
40774/* int (*xReadDir)(void *, jx9_context *) */
40775static int UnixDir_Read(void *pUserData, jx9_context *pCtx)
40776{
40777 DIR *pDir = (DIR *)pUserData;
40778 struct dirent *pEntry;
40779 char *zName = 0; /* cc warning */
40780 sxu32 n = 0;
40781 for(;;){
40782 pEntry = readdir(pDir);
40783 if( pEntry == 0 ){
40784 /* No more entries to process */
40785 return -1;
40786 }
40787 zName = pEntry->d_name;
40788 n = SyStrlen(zName);
40789 /* Ignore '.' && '..' */
40790 if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
40791 break;
40792 }
40793 /* Next entry */
40794 }
40795 /* Return the current file name */
40796 jx9_result_string(pCtx, zName, (int)n);
40797 return JX9_OK;
40798}
40799/* void (*xRewindDir)(void *) */
40800static void UnixDir_Rewind(void *pUserData)
40801{
40802 rewinddir((DIR *)pUserData);
40803}
40804/* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
40805static jx9_int64 UnixFile_Read(void *pUserData, void *pBuffer, jx9_int64 nDatatoRead)
40806{
40807 ssize_t nRd;
40808 nRd = read(SX_PTR_TO_INT(pUserData), pBuffer, (size_t)nDatatoRead);
40809 if( nRd < 1 ){
40810 /* EOF or IO error */
40811 return -1;
40812 }
40813 return (jx9_int64)nRd;
40814}
40815/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
40816static jx9_int64 UnixFile_Write(void *pUserData, const void *pBuffer, jx9_int64 nWrite)
40817{
40818 const char *zData = (const char *)pBuffer;
40819 int fd = SX_PTR_TO_INT(pUserData);
40820 jx9_int64 nCount;
40821 ssize_t nWr;
40822 nCount = 0;
40823 for(;;){
40824 if( nWrite < 1 ){
40825 break;
40826 }
40827 nWr = write(fd, zData, (size_t)nWrite);
40828 if( nWr < 1 ){
40829 /* IO error */
40830 break;
40831 }
40832 nWrite -= nWr;
40833 nCount += nWr;
40834 zData += nWr;
40835 }
40836 if( nWrite > 0 ){
40837 return -1;
40838 }
40839 return nCount;
40840}
40841/* int (*xSeek)(void *, jx9_int64, int) */
40842static int UnixFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
40843{
40844 off_t iNew;
40845 switch(whence){
40846 case 1:/*SEEK_CUR*/
40847 whence = SEEK_CUR;
40848 break;
40849 case 2: /* SEEK_END */
40850 whence = SEEK_END;
40851 break;
40852 case 0: /* SEEK_SET */
40853 default:
40854 whence = SEEK_SET;
40855 break;
40856 }
40857 iNew = lseek(SX_PTR_TO_INT(pUserData), (off_t)iOfft, whence);
40858 if( iNew < 0 ){
40859 return -1;
40860 }
40861 return JX9_OK;
40862}
40863/* int (*xLock)(void *, int) */
40864static int UnixFile_Lock(void *pUserData, int lock_type)
40865{
40866 int fd = SX_PTR_TO_INT(pUserData);
40867 int rc = JX9_OK; /* cc warning */
40868 if( lock_type < 0 ){
40869 /* Unlock the file */
40870 rc = flock(fd, LOCK_UN);
40871 }else{
40872 if( lock_type == 1 ){
40873 /* Exculsive lock */
40874 rc = flock(fd, LOCK_EX);
40875 }else{
40876 /* Shared lock */
40877 rc = flock(fd, LOCK_SH);
40878 }
40879 }
40880 return !rc ? JX9_OK : -1;
40881}
40882/* jx9_int64 (*xTell)(void *) */
40883static jx9_int64 UnixFile_Tell(void *pUserData)
40884{
40885 off_t iNew;
40886 iNew = lseek(SX_PTR_TO_INT(pUserData), 0, SEEK_CUR);
40887 return (jx9_int64)iNew;
40888}
40889/* int (*xTrunc)(void *, jx9_int64) */
40890static int UnixFile_Trunc(void *pUserData, jx9_int64 nOfft)
40891{
40892 int rc;
40893 rc = ftruncate(SX_PTR_TO_INT(pUserData), (off_t)nOfft);
40894 if( rc != 0 ){
40895 return -1;
40896 }
40897 return JX9_OK;
40898}
40899/* int (*xSync)(void *); */
40900static int UnixFile_Sync(void *pUserData)
40901{
40902 int rc;
40903 rc = fsync(SX_PTR_TO_INT(pUserData));
40904 return rc == 0 ? JX9_OK : - 1;
40905}
40906/* int (*xStat)(void *, jx9_value *, jx9_value *) */
40907static int UnixFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
40908{
40909 struct stat st;
40910 int rc;
40911 rc = fstat(SX_PTR_TO_INT(pUserData), &st);
40912 if( rc != 0 ){
40913 return -1;
40914 }
40915 /* dev */
40916 jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
40917 jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
40918 /* ino */
40919 jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
40920 jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
40921 /* mode */
40922 jx9_value_int(pWorker, (int)st.st_mode);
40923 jx9_array_add_strkey_elem(pArray, "mode", pWorker);
40924 /* nlink */
40925 jx9_value_int(pWorker, (int)st.st_nlink);
40926 jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
40927 /* uid, gid, rdev */
40928 jx9_value_int(pWorker, (int)st.st_uid);
40929 jx9_array_add_strkey_elem(pArray, "uid", pWorker);
40930 jx9_value_int(pWorker, (int)st.st_gid);
40931 jx9_array_add_strkey_elem(pArray, "gid", pWorker);
40932 jx9_value_int(pWorker, (int)st.st_rdev);
40933 jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
40934 /* size */
40935 jx9_value_int64(pWorker, (jx9_int64)st.st_size);
40936 jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
40937 /* atime */
40938 jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
40939 jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
40940 /* mtime */
40941 jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
40942 jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
40943 /* ctime */
40944 jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
40945 jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
40946 /* blksize, blocks */
40947 jx9_value_int(pWorker, (int)st.st_blksize);
40948 jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
40949 jx9_value_int(pWorker, (int)st.st_blocks);
40950 jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
40951 return JX9_OK;
40952}
40953/* Export the file:// stream */
40954static const jx9_io_stream sUnixFileStream = {
40955 "file", /* Stream name */
40956 JX9_IO_STREAM_VERSION,
40957 UnixFile_Open, /* xOpen */
40958 UnixDir_Open, /* xOpenDir */
40959 UnixFile_Close, /* xClose */
40960 UnixDir_Close, /* xCloseDir */
40961 UnixFile_Read, /* xRead */
40962 UnixDir_Read, /* xReadDir */
40963 UnixFile_Write, /* xWrite */
40964 UnixFile_Seek, /* xSeek */
40965 UnixFile_Lock, /* xLock */
40966 UnixDir_Rewind, /* xRewindDir */
40967 UnixFile_Tell, /* xTell */
40968 UnixFile_Trunc, /* xTrunc */
40969 UnixFile_Sync, /* xSeek */
40970 UnixFile_Stat /* xStat */
40971};
40972#endif /* __WINNT__/__UNIXES__ */
40973#endif /* JX9_DISABLE_DISK_IO */
40974#endif /* JX9_DISABLE_BUILTIN_FUNC */
40975/*
40976 * Export the builtin vfs.
40977 * Return a pointer to the builtin vfs if available.
40978 * Otherwise return the null_vfs [i.e: a no-op vfs] instead.
40979 * Note:
40980 * The built-in vfs is always available for Windows/UNIX systems.
40981 * Note:
40982 * If the engine is compiled with the JX9_DISABLE_DISK_IO/JX9_DISABLE_BUILTIN_FUNC
40983 * directives defined then this function return the null_vfs instead.
40984 */
40985JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void)
40986{
40987#ifndef JX9_DISABLE_BUILTIN_FUNC
40988#ifdef JX9_DISABLE_DISK_IO
40989 return &null_vfs;
40990#else
40991#ifdef __WINNT__
40992 return &sWinVfs;
40993#elif defined(__UNIXES__)
40994 return &sUnixVfs;
40995#else
40996 return &null_vfs;
40997#endif /* __WINNT__/__UNIXES__ */
40998#endif /*JX9_DISABLE_DISK_IO*/
40999#else
41000 return &null_vfs;
41001#endif /* JX9_DISABLE_BUILTIN_FUNC */
41002}
41003#ifndef JX9_DISABLE_BUILTIN_FUNC
41004#ifndef JX9_DISABLE_DISK_IO
41005/*
41006 * The following defines are mostly used by the UNIX built and have
41007 * no particular meaning on windows.
41008 */
41009#ifndef STDIN_FILENO
41010#define STDIN_FILENO 0
41011#endif
41012#ifndef STDOUT_FILENO
41013#define STDOUT_FILENO 1
41014#endif
41015#ifndef STDERR_FILENO
41016#define STDERR_FILENO 2
41017#endif
41018/*
41019 * jx9:// Accessing various I/O streams
41020 * According to the JX9 langage reference manual
41021 * JX9 provides a number of miscellaneous I/O streams that allow access to JX9's own input
41022 * and output streams, the standard input, output and error file descriptors.
41023 * jx9://stdin, jx9://stdout and jx9://stderr:
41024 * Allow direct access to the corresponding input or output stream of the JX9 process.
41025 * The stream references a duplicate file descriptor, so if you open jx9://stdin and later
41026 * close it, you close only your copy of the descriptor-the actual stream referenced by STDIN is unaffected.
41027 * jx9://stdin is read-only, whereas jx9://stdout and jx9://stderr are write-only.
41028 * jx9://output
41029 * jx9://output is a write-only stream that allows you to write to the output buffer
41030 * mechanism in the same way as print and print.
41031 */
41032typedef struct jx9_stream_data jx9_stream_data;
41033/* Supported IO streams */
41034#define JX9_IO_STREAM_STDIN 1 /* jx9://stdin */
41035#define JX9_IO_STREAM_STDOUT 2 /* jx9://stdout */
41036#define JX9_IO_STREAM_STDERR 3 /* jx9://stderr */
41037#define JX9_IO_STREAM_OUTPUT 4 /* jx9://output */
41038 /* The following structure is the private data associated with the jx9:// stream */
41039struct jx9_stream_data
41040{
41041 jx9_vm *pVm; /* VM that own this instance */
41042 int iType; /* Stream type */
41043 union{
41044 void *pHandle; /* Stream handle */
41045 jx9_output_consumer sConsumer; /* VM output consumer */
41046 }x;
41047};
41048/*
41049 * Allocate a new instance of the jx9_stream_data structure.
41050 */
41051static jx9_stream_data * JX9StreamDataInit(jx9_vm *pVm, int iType)
41052{
41053 jx9_stream_data *pData;
41054 if( pVm == 0 ){
41055 return 0;
41056 }
41057 /* Allocate a new instance */
41058 pData = (jx9_stream_data *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(jx9_stream_data));
41059 if( pData == 0 ){
41060 return 0;
41061 }
41062 /* Zero the structure */
41063 SyZero(pData, sizeof(jx9_stream_data));
41064 /* Initialize fields */
41065 pData->iType = iType;
41066 if( iType == JX9_IO_STREAM_OUTPUT ){
41067 /* Point to the default VM consumer routine. */
41068 pData->x.sConsumer = pVm->sVmConsumer;
41069 }else{
41070#ifdef __WINNT__
41071 DWORD nChannel;
41072 switch(iType){
41073 case JX9_IO_STREAM_STDOUT: nChannel = STD_OUTPUT_HANDLE; break;
41074 case JX9_IO_STREAM_STDERR: nChannel = STD_ERROR_HANDLE; break;
41075 default:
41076 nChannel = STD_INPUT_HANDLE;
41077 break;
41078 }
41079 pData->x.pHandle = GetStdHandle(nChannel);
41080#else
41081 /* Assume an UNIX system */
41082 int ifd = STDIN_FILENO;
41083 switch(iType){
41084 case JX9_IO_STREAM_STDOUT: ifd = STDOUT_FILENO; break;
41085 case JX9_IO_STREAM_STDERR: ifd = STDERR_FILENO; break;
41086 default:
41087 break;
41088 }
41089 pData->x.pHandle = SX_INT_TO_PTR(ifd);
41090#endif
41091 }
41092 pData->pVm = pVm;
41093 return pData;
41094}
41095/*
41096 * Implementation of the jx9:// IO streams routines
41097 * Authors:
41098 * Symisc Systems, devel@symisc.net.
41099 * Copyright (C) Symisc Systems, http://jx9.symisc.net
41100 * Status:
41101 * Stable.
41102 */
41103/* int (*xOpen)(const char *, int, jx9_value *, void **) */
41104static int JX9StreamData_Open(const char *zName, int iMode, jx9_value *pResource, void ** ppHandle)
41105{
41106 jx9_stream_data *pData;
41107 SyString sStream;
41108 SyStringInitFromBuf(&sStream, zName, SyStrlen(zName));
41109 /* Trim leading and trailing white spaces */
41110 SyStringFullTrim(&sStream);
41111 /* Stream to open */
41112 if( SyStrnicmp(sStream.zString, "stdin", sizeof("stdin")-1) == 0 ){
41113 iMode = JX9_IO_STREAM_STDIN;
41114 }else if( SyStrnicmp(sStream.zString, "output", sizeof("output")-1) == 0 ){
41115 iMode = JX9_IO_STREAM_OUTPUT;
41116 }else if( SyStrnicmp(sStream.zString, "stdout", sizeof("stdout")-1) == 0 ){
41117 iMode = JX9_IO_STREAM_STDOUT;
41118 }else if( SyStrnicmp(sStream.zString, "stderr", sizeof("stderr")-1) == 0 ){
41119 iMode = JX9_IO_STREAM_STDERR;
41120 }else{
41121 /* unknown stream name */
41122 return -1;
41123 }
41124 /* Create our handle */
41125 pData = JX9StreamDataInit(pResource?pResource->pVm:0, iMode);
41126 if( pData == 0 ){
41127 return -1;
41128 }
41129 /* Make the handle public */
41130 *ppHandle = (void *)pData;
41131 return JX9_OK;
41132}
41133/* jx9_int64 (*xRead)(void *, void *, jx9_int64) */
41134static jx9_int64 JX9StreamData_Read(void *pHandle, void *pBuffer, jx9_int64 nDatatoRead)
41135{
41136 jx9_stream_data *pData = (jx9_stream_data *)pHandle;
41137 if( pData == 0 ){
41138 return -1;
41139 }
41140 if( pData->iType != JX9_IO_STREAM_STDIN ){
41141 /* Forbidden */
41142 return -1;
41143 }
41144#ifdef __WINNT__
41145 {
41146 DWORD nRd;
41147 BOOL rc;
41148 rc = ReadFile(pData->x.pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
41149 if( !rc ){
41150 /* IO error */
41151 return -1;
41152 }
41153 return (jx9_int64)nRd;
41154 }
41155#elif defined(__UNIXES__)
41156 {
41157 ssize_t nRd;
41158 int fd;
41159 fd = SX_PTR_TO_INT(pData->x.pHandle);
41160 nRd = read(fd, pBuffer, (size_t)nDatatoRead);
41161 if( nRd < 1 ){
41162 return -1;
41163 }
41164 return (jx9_int64)nRd;
41165 }
41166#else
41167 return -1;
41168#endif
41169}
41170/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64) */
41171static jx9_int64 JX9StreamData_Write(void *pHandle, const void *pBuf, jx9_int64 nWrite)
41172{
41173 jx9_stream_data *pData = (jx9_stream_data *)pHandle;
41174 if( pData == 0 ){
41175 return -1;
41176 }
41177 if( pData->iType == JX9_IO_STREAM_STDIN ){
41178 /* Forbidden */
41179 return -1;
41180 }else if( pData->iType == JX9_IO_STREAM_OUTPUT ){
41181 jx9_output_consumer *pCons = &pData->x.sConsumer;
41182 int rc;
41183 /* Call the vm output consumer */
41184 rc = pCons->xConsumer(pBuf, (unsigned int)nWrite, pCons->pUserData);
41185 if( rc == JX9_ABORT ){
41186 return -1;
41187 }
41188 return nWrite;
41189 }
41190#ifdef __WINNT__
41191 {
41192 DWORD nWr;
41193 BOOL rc;
41194 rc = WriteFile(pData->x.pHandle, pBuf, (DWORD)nWrite, &nWr, 0);
41195 if( !rc ){
41196 /* IO error */
41197 return -1;
41198 }
41199 return (jx9_int64)nWr;
41200 }
41201#elif defined(__UNIXES__)
41202 {
41203 ssize_t nWr;
41204 int fd;
41205 fd = SX_PTR_TO_INT(pData->x.pHandle);
41206 nWr = write(fd, pBuf, (size_t)nWrite);
41207 if( nWr < 1 ){
41208 return -1;
41209 }
41210 return (jx9_int64)nWr;
41211 }
41212#else
41213 return -1;
41214#endif
41215}
41216/* void (*xClose)(void *) */
41217static void JX9StreamData_Close(void *pHandle)
41218{
41219 jx9_stream_data *pData = (jx9_stream_data *)pHandle;
41220 jx9_vm *pVm;
41221 if( pData == 0 ){
41222 return;
41223 }
41224 pVm = pData->pVm;
41225 /* Free the instance */
41226 SyMemBackendFree(&pVm->sAllocator, pData);
41227}
41228/* Export the jx9:// stream */
41229static const jx9_io_stream sjx9Stream = {
41230 "jx9",
41231 JX9_IO_STREAM_VERSION,
41232 JX9StreamData_Open, /* xOpen */
41233 0, /* xOpenDir */
41234 JX9StreamData_Close, /* xClose */
41235 0, /* xCloseDir */
41236 JX9StreamData_Read, /* xRead */
41237 0, /* xReadDir */
41238 JX9StreamData_Write, /* xWrite */
41239 0, /* xSeek */
41240 0, /* xLock */
41241 0, /* xRewindDir */
41242 0, /* xTell */
41243 0, /* xTrunc */
41244 0, /* xSeek */
41245 0 /* xStat */
41246};
41247#endif /* JX9_DISABLE_DISK_IO */
41248/*
41249 * Return TRUE if we are dealing with the jx9:// stream.
41250 * FALSE otherwise.
41251 */
41252static int is_jx9_stream(const jx9_io_stream *pStream)
41253{
41254#ifndef JX9_DISABLE_DISK_IO
41255 return pStream == &sjx9Stream;
41256#else
41257 SXUNUSED(pStream); /* cc warning */
41258 return 0;
41259#endif /* JX9_DISABLE_DISK_IO */
41260}
41261
41262#endif /* JX9_DISABLE_BUILTIN_FUNC */
41263/*
41264 * Export the IO routines defined above and the built-in IO streams
41265 * [i.e: file://, jx9://].
41266 * Note:
41267 * If the engine is compiled with the JX9_DISABLE_BUILTIN_FUNC directive
41268 * defined then this function is a no-op.
41269 */
41270JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm)
41271{
41272#ifndef JX9_DISABLE_BUILTIN_FUNC
41273 /* VFS functions */
41274 static const jx9_builtin_func aVfsFunc[] = {
41275 {"chdir", jx9Vfs_chdir },
41276 {"chroot", jx9Vfs_chroot },
41277 {"getcwd", jx9Vfs_getcwd },
41278 {"rmdir", jx9Vfs_rmdir },
41279 {"is_dir", jx9Vfs_is_dir },
41280 {"mkdir", jx9Vfs_mkdir },
41281 {"rename", jx9Vfs_rename },
41282 {"realpath", jx9Vfs_realpath},
41283 {"sleep", jx9Vfs_sleep },
41284 {"usleep", jx9Vfs_usleep },
41285 {"unlink", jx9Vfs_unlink },
41286 {"delete", jx9Vfs_unlink },
41287 {"chmod", jx9Vfs_chmod },
41288 {"chown", jx9Vfs_chown },
41289 {"chgrp", jx9Vfs_chgrp },
41290 {"disk_free_space", jx9Vfs_disk_free_space },
41291 {"disk_total_space", jx9Vfs_disk_total_space},
41292 {"file_exists", jx9Vfs_file_exists },
41293 {"filesize", jx9Vfs_file_size },
41294 {"fileatime", jx9Vfs_file_atime },
41295 {"filemtime", jx9Vfs_file_mtime },
41296 {"filectime", jx9Vfs_file_ctime },
41297 {"is_file", jx9Vfs_is_file },
41298 {"is_link", jx9Vfs_is_link },
41299 {"is_readable", jx9Vfs_is_readable },
41300 {"is_writable", jx9Vfs_is_writable },
41301 {"is_executable", jx9Vfs_is_executable},
41302 {"filetype", jx9Vfs_filetype },
41303 {"stat", jx9Vfs_stat },
41304 {"lstat", jx9Vfs_lstat },
41305 {"getenv", jx9Vfs_getenv },
41306 {"setenv", jx9Vfs_putenv },
41307 {"putenv", jx9Vfs_putenv },
41308 {"touch", jx9Vfs_touch },
41309 {"link", jx9Vfs_link },
41310 {"symlink", jx9Vfs_symlink },
41311 {"umask", jx9Vfs_umask },
41312 {"sys_get_temp_dir", jx9Vfs_sys_get_temp_dir },
41313 {"get_current_user", jx9Vfs_get_current_user },
41314 {"getpid", jx9Vfs_getmypid },
41315 {"getuid", jx9Vfs_getmyuid },
41316 {"getgid", jx9Vfs_getmygid },
41317 {"uname", jx9Vfs_uname},
41318 /* Path processing */
41319 {"dirname", jx9Builtin_dirname },
41320 {"basename", jx9Builtin_basename },
41321 {"pathinfo", jx9Builtin_pathinfo },
41322 {"strglob", jx9Builtin_strglob },
41323 {"fnmatch", jx9Builtin_fnmatch },
41324 /* ZIP processing */
41325 {"zip_open", jx9Builtin_zip_open },
41326 {"zip_close", jx9Builtin_zip_close},
41327 {"zip_read", jx9Builtin_zip_read },
41328 {"zip_entry_open", jx9Builtin_zip_entry_open },
41329 {"zip_entry_close", jx9Builtin_zip_entry_close},
41330 {"zip_entry_name", jx9Builtin_zip_entry_name },
41331 {"zip_entry_filesize", jx9Builtin_zip_entry_filesize },
41332 {"zip_entry_compressedsize", jx9Builtin_zip_entry_compressedsize },
41333 {"zip_entry_read", jx9Builtin_zip_entry_read },
41334 {"zip_entry_reset_cursor", jx9Builtin_zip_entry_reset_cursor},
41335 {"zip_entry_compressionmethod", jx9Builtin_zip_entry_compressionmethod}
41336 };
41337 /* IO stream functions */
41338 static const jx9_builtin_func aIOFunc[] = {
41339 {"ftruncate", jx9Builtin_ftruncate },
41340 {"fseek", jx9Builtin_fseek },
41341 {"ftell", jx9Builtin_ftell },
41342 {"rewind", jx9Builtin_rewind },
41343 {"fflush", jx9Builtin_fflush },
41344 {"feof", jx9Builtin_feof },
41345 {"fgetc", jx9Builtin_fgetc },
41346 {"fgets", jx9Builtin_fgets },
41347 {"fread", jx9Builtin_fread },
41348 {"fgetcsv", jx9Builtin_fgetcsv},
41349 {"fgetss", jx9Builtin_fgetss },
41350 {"readdir", jx9Builtin_readdir},
41351 {"rewinddir", jx9Builtin_rewinddir },
41352 {"closedir", jx9Builtin_closedir},
41353 {"opendir", jx9Builtin_opendir },
41354 {"readfile", jx9Builtin_readfile},
41355 {"file_get_contents", jx9Builtin_file_get_contents},
41356 {"file_put_contents", jx9Builtin_file_put_contents},
41357 {"file", jx9Builtin_file },
41358 {"copy", jx9Builtin_copy },
41359 {"fstat", jx9Builtin_fstat },
41360 {"fwrite", jx9Builtin_fwrite },
41361 {"fputs", jx9Builtin_fwrite },
41362 {"flock", jx9Builtin_flock },
41363 {"fclose", jx9Builtin_fclose },
41364 {"fopen", jx9Builtin_fopen },
41365 {"fpassthru", jx9Builtin_fpassthru },
41366 {"fputcsv", jx9Builtin_fputcsv },
41367 {"fprintf", jx9Builtin_fprintf },
41368#if !defined(JX9_DISABLE_HASH_FUNC)
41369 {"md5_file", jx9Builtin_md5_file},
41370 {"sha1_file", jx9Builtin_sha1_file},
41371#endif /* JX9_DISABLE_HASH_FUNC */
41372 {"parse_ini_file", jx9Builtin_parse_ini_file},
41373 {"vfprintf", jx9Builtin_vfprintf}
41374 };
41375 const jx9_io_stream *pFileStream = 0;
41376 sxu32 n = 0;
41377 /* Register the functions defined above */
41378 for( n = 0 ; n < SX_ARRAYSIZE(aVfsFunc) ; ++n ){
41379 jx9_create_function(&(*pVm), aVfsFunc[n].zName, aVfsFunc[n].xFunc, (void *)pVm->pEngine->pVfs);
41380 }
41381 for( n = 0 ; n < SX_ARRAYSIZE(aIOFunc) ; ++n ){
41382 jx9_create_function(&(*pVm), aIOFunc[n].zName, aIOFunc[n].xFunc, pVm);
41383 }
41384#ifndef JX9_DISABLE_DISK_IO
41385 /* Register the file stream if available */
41386#ifdef __WINNT__
41387 pFileStream = &sWinFileStream;
41388#elif defined(__UNIXES__)
41389 pFileStream = &sUnixFileStream;
41390#endif
41391 /* Install the jx9:// stream */
41392 jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, &sjx9Stream);
41393#endif /* JX9_DISABLE_DISK_IO */
41394 if( pFileStream ){
41395 /* Install the file:// stream */
41396 jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, pFileStream);
41397 }
41398#else
41399 SXUNUSED(pVm); /* cc warning */
41400#endif /* JX9_DISABLE_BUILTIN_FUNC */
41401 return SXRET_OK;
41402}
41403/*
41404 * Export the STDIN handle.
41405 */
41406JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm)
41407{
41408#ifndef JX9_DISABLE_BUILTIN_FUNC
41409#ifndef JX9_DISABLE_DISK_IO
41410 if( pVm->pStdin == 0 ){
41411 io_private *pIn;
41412 /* Allocate an IO private instance */
41413 pIn = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
41414 if( pIn == 0 ){
41415 return 0;
41416 }
41417 InitIOPrivate(pVm, &sjx9Stream, pIn);
41418 /* Initialize the handle */
41419 pIn->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDIN);
41420 /* Install the STDIN stream */
41421 pVm->pStdin = pIn;
41422 return pIn;
41423 }else{
41424 /* NULL or STDIN */
41425 return pVm->pStdin;
41426 }
41427#else
41428 return 0;
41429#endif
41430#else
41431 SXUNUSED(pVm); /* cc warning */
41432 return 0;
41433#endif
41434}
41435/*
41436 * Export the STDOUT handle.
41437 */
41438JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm)
41439{
41440#ifndef JX9_DISABLE_BUILTIN_FUNC
41441#ifndef JX9_DISABLE_DISK_IO
41442 if( pVm->pStdout == 0 ){
41443 io_private *pOut;
41444 /* Allocate an IO private instance */
41445 pOut = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
41446 if( pOut == 0 ){
41447 return 0;
41448 }
41449 InitIOPrivate(pVm, &sjx9Stream, pOut);
41450 /* Initialize the handle */
41451 pOut->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDOUT);
41452 /* Install the STDOUT stream */
41453 pVm->pStdout = pOut;
41454 return pOut;
41455 }else{
41456 /* NULL or STDOUT */
41457 return pVm->pStdout;
41458 }
41459#else
41460 return 0;
41461#endif
41462#else
41463 SXUNUSED(pVm); /* cc warning */
41464 return 0;
41465#endif
41466}
41467/*
41468 * Export the STDERR handle.
41469 */
41470JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm)
41471{
41472#ifndef JX9_DISABLE_BUILTIN_FUNC
41473#ifndef JX9_DISABLE_DISK_IO
41474 if( pVm->pStderr == 0 ){
41475 io_private *pErr;
41476 /* Allocate an IO private instance */
41477 pErr = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
41478 if( pErr == 0 ){
41479 return 0;
41480 }
41481 InitIOPrivate(pVm, &sjx9Stream, pErr);
41482 /* Initialize the handle */
41483 pErr->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDERR);
41484 /* Install the STDERR stream */
41485 pVm->pStderr = pErr;
41486 return pErr;
41487 }else{
41488 /* NULL or STDERR */
41489 return pVm->pStderr;
41490 }
41491#else
41492 return 0;
41493#endif
41494#else
41495 SXUNUSED(pVm); /* cc warning */
41496 return 0;
41497#endif
41498}
41499
41500/*
41501 * ----------------------------------------------------------
41502 * File: jx9_vm.c
41503 * MD5: beca4be65a9a49c932c356d7680034c9
41504 * ----------------------------------------------------------
41505 */
41506/*
41507 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
41508 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
41509 * Version 1.7.2
41510 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
41511 * please contact Symisc Systems via:
41512 * legal@symisc.net
41513 * licensing@symisc.net
41514 * contact@symisc.net
41515 * or visit:
41516 * http://jx9.symisc.net/
41517 */
41518 /* $SymiscID: jx9_vm.c v1.0 FreeBSD 2012-12-09 00:19 stable <chm@symisc.net> $ */
41519#ifndef JX9_AMALGAMATION
41520#include "jx9Int.h"
41521#endif
41522/*
41523 * The code in this file implements execution method of the JX9 Virtual Machine.
41524 * The JX9 compiler (implemented in 'compiler.c' and 'parse.c') generates a bytecode program
41525 * which is then executed by the virtual machine implemented here to do the work of the JX9
41526 * statements.
41527 * JX9 bytecode programs are similar in form to assembly language. The program consists
41528 * of a linear sequence of operations .Each operation has an opcode and 3 operands.
41529 * Operands P1 and P2 are integers where the first is signed while the second is unsigned.
41530 * Operand P3 is an arbitrary pointer specific to each instruction. The P2 operand is usually
41531 * the jump destination used by the OP_JMP, OP_JZ, OP_JNZ, ... instructions.
41532 * Opcodes will typically ignore one or more operands. Many opcodes ignore all three operands.
41533 * Computation results are stored on a stack. Each entry on the stack is of type jx9_value.
41534 * JX9 uses the jx9_value object to represent all values that can be stored in a JX9 variable.
41535 * Since JX9 uses dynamic typing for the values it stores. Values stored in jx9_value objects
41536 * can be integers, floating point values, strings, arrays, object instances (object in the JX9 jargon)
41537 * and so on.
41538 * Internally, the JX9 virtual machine manipulates nearly all values as jx9_values structures.
41539 * Each jx9_value may cache multiple representations(string, integer etc.) of the same value.
41540 * An implicit conversion from one type to the other occurs as necessary.
41541 * Most of the code in this file is taken up by the [VmByteCodeExec()] function which does
41542 * the work of interpreting a JX9 bytecode program. But other routines are also provided
41543 * to help in building up a program instruction by instruction.
41544 */
41545/*
41546 * Each active virtual machine frame is represented by an instance
41547 * of the following structure.
41548 * VM Frame hold local variables and other stuff related to function call.
41549 */
41550struct VmFrame
41551{
41552 VmFrame *pParent; /* Parent frame or NULL if global scope */
41553 void *pUserData; /* Upper layer private data associated with this frame */
41554 SySet sLocal; /* Local variables container (VmSlot instance) */
41555 jx9_vm *pVm; /* VM that own this frame */
41556 SyHash hVar; /* Variable hashtable for fast lookup */
41557 SySet sArg; /* Function arguments container */
41558 sxi32 iFlags; /* Frame configuration flags (See below)*/
41559 sxu32 iExceptionJump; /* Exception jump destination */
41560};
41561/*
41562 * When a user defined variable is garbage collected, memory object index
41563 * is stored in an instance of the following structure and put in the free object
41564 * table so that it can be reused again without allocating a new memory object.
41565 */
41566typedef struct VmSlot VmSlot;
41567struct VmSlot
41568{
41569 sxu32 nIdx; /* Index in pVm->aMemObj[] */
41570 void *pUserData; /* Upper-layer private data */
41571};
41572/*
41573 * Each parsed URI is recorded and stored in an instance of the following structure.
41574 * This structure and it's related routines are taken verbatim from the xHT project
41575 * [A modern embeddable HTTP engine implementing all the RFC2616 methods]
41576 * the xHT project is developed internally by Symisc Systems.
41577 */
41578typedef struct SyhttpUri SyhttpUri;
41579struct SyhttpUri
41580{
41581 SyString sHost; /* Hostname or IP address */
41582 SyString sPort; /* Port number */
41583 SyString sPath; /* Mandatory resource path passed verbatim (Not decoded) */
41584 SyString sQuery; /* Query part */
41585 SyString sFragment; /* Fragment part */
41586 SyString sScheme; /* Scheme */
41587 SyString sUser; /* Username */
41588 SyString sPass; /* Password */
41589 SyString sRaw; /* Raw URI */
41590};
41591/*
41592 * An instance of the following structure is used to record all MIME headers seen
41593 * during a HTTP interaction.
41594 * This structure and it's related routines are taken verbatim from the xHT project
41595 * [A modern embeddable HTTP engine implementing all the RFC2616 methods]
41596 * the xHT project is developed internally by Symisc Systems.
41597 */
41598typedef struct SyhttpHeader SyhttpHeader;
41599struct SyhttpHeader
41600{
41601 SyString sName; /* Header name [i.e:"Content-Type", "Host", "User-Agent"]. NOT NUL TERMINATED */
41602 SyString sValue; /* Header values [i.e: "text/html"]. NOT NUL TERMINATED */
41603};
41604/*
41605 * Supported HTTP methods.
41606 */
41607#define HTTP_METHOD_GET 1 /* GET */
41608#define HTTP_METHOD_HEAD 2 /* HEAD */
41609#define HTTP_METHOD_POST 3 /* POST */
41610#define HTTP_METHOD_PUT 4 /* PUT */
41611#define HTTP_METHOD_OTHR 5 /* Other HTTP methods [i.e: DELETE, TRACE, OPTIONS...]*/
41612/*
41613 * Supported HTTP protocol version.
41614 */
41615#define HTTP_PROTO_10 1 /* HTTP/1.0 */
41616#define HTTP_PROTO_11 2 /* HTTP/1.1 */
41617/*
41618 * Register a constant and it's associated expansion callback so that
41619 * it can be expanded from the target JX9 program.
41620 * The constant expansion mechanism under JX9 is extremely powerful yet
41621 * simple and work as follows:
41622 * Each registered constant have a C procedure associated with it.
41623 * This procedure known as the constant expansion callback is responsible
41624 * of expanding the invoked constant to the desired value, for example:
41625 * The C procedure associated with the "__PI__" constant expands to 3.14 (the value of PI).
41626 * The "__OS__" constant procedure expands to the name of the host Operating Systems
41627 * (Windows, Linux, ...) and so on.
41628 * Please refer to the official documentation for additional information.
41629 */
41630JX9_PRIVATE sxi32 jx9VmRegisterConstant(
41631 jx9_vm *pVm, /* Target VM */
41632 const SyString *pName, /* Constant name */
41633 ProcConstant xExpand, /* Constant expansion callback */
41634 void *pUserData /* Last argument to xExpand() */
41635 )
41636{
41637 jx9_constant *pCons;
41638 SyHashEntry *pEntry;
41639 char *zDupName;
41640 sxi32 rc;
41641 pEntry = SyHashGet(&pVm->hConstant, (const void *)pName->zString, pName->nByte);
41642 if( pEntry ){
41643 /* Overwrite the old definition and return immediately */
41644 pCons = (jx9_constant *)pEntry->pUserData;
41645 pCons->xExpand = xExpand;
41646 pCons->pUserData = pUserData;
41647 return SXRET_OK;
41648 }
41649 /* Allocate a new constant instance */
41650 pCons = (jx9_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_constant));
41651 if( pCons == 0 ){
41652 return 0;
41653 }
41654 /* Duplicate constant name */
41655 zDupName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
41656 if( zDupName == 0 ){
41657 SyMemBackendPoolFree(&pVm->sAllocator, pCons);
41658 return 0;
41659 }
41660 /* Install the constant */
41661 SyStringInitFromBuf(&pCons->sName, zDupName, pName->nByte);
41662 pCons->xExpand = xExpand;
41663 pCons->pUserData = pUserData;
41664 rc = SyHashInsert(&pVm->hConstant, (const void *)zDupName, SyStringLength(&pCons->sName), pCons);
41665 if( rc != SXRET_OK ){
41666 SyMemBackendFree(&pVm->sAllocator, zDupName);
41667 SyMemBackendPoolFree(&pVm->sAllocator, pCons);
41668 return rc;
41669 }
41670 /* All done, constant can be invoked from JX9 code */
41671 return SXRET_OK;
41672}
41673/*
41674 * Allocate a new foreign function instance.
41675 * This function return SXRET_OK on success. Any other
41676 * return value indicates failure.
41677 * Please refer to the official documentation for an introduction to
41678 * the foreign function mechanism.
41679 */
41680static sxi32 jx9NewForeignFunction(
41681 jx9_vm *pVm, /* Target VM */
41682 const SyString *pName, /* Foreign function name */
41683 ProcHostFunction xFunc, /* Foreign function implementation */
41684 void *pUserData, /* Foreign function private data */
41685 jx9_user_func **ppOut /* OUT: VM image of the foreign function */
41686 )
41687{
41688 jx9_user_func *pFunc;
41689 char *zDup;
41690 /* Allocate a new user function */
41691 pFunc = (jx9_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_user_func));
41692 if( pFunc == 0 ){
41693 return SXERR_MEM;
41694 }
41695 /* Duplicate function name */
41696 zDup = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
41697 if( zDup == 0 ){
41698 SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
41699 return SXERR_MEM;
41700 }
41701 /* Zero the structure */
41702 SyZero(pFunc, sizeof(jx9_user_func));
41703 /* Initialize structure fields */
41704 SyStringInitFromBuf(&pFunc->sName, zDup, pName->nByte);
41705 pFunc->pVm = pVm;
41706 pFunc->xFunc = xFunc;
41707 pFunc->pUserData = pUserData;
41708 SySetInit(&pFunc->aAux, &pVm->sAllocator, sizeof(jx9_aux_data));
41709 /* Write a pointer to the new function */
41710 *ppOut = pFunc;
41711 return SXRET_OK;
41712}
41713/*
41714 * Install a foreign function and it's associated callback so that
41715 * it can be invoked from the target JX9 code.
41716 * This function return SXRET_OK on successful registration. Any other
41717 * return value indicates failure.
41718 * Please refer to the official documentation for an introduction to
41719 * the foreign function mechanism.
41720 */
41721JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(
41722 jx9_vm *pVm, /* Target VM */
41723 const SyString *pName, /* Foreign function name */
41724 ProcHostFunction xFunc, /* Foreign function implementation */
41725 void *pUserData /* Foreign function private data */
41726 )
41727{
41728 jx9_user_func *pFunc;
41729 SyHashEntry *pEntry;
41730 sxi32 rc;
41731 /* Overwrite any previously registered function with the same name */
41732 pEntry = SyHashGet(&pVm->hHostFunction, pName->zString, pName->nByte);
41733 if( pEntry ){
41734 pFunc = (jx9_user_func *)pEntry->pUserData;
41735 pFunc->pUserData = pUserData;
41736 pFunc->xFunc = xFunc;
41737 SySetReset(&pFunc->aAux);
41738 return SXRET_OK;
41739 }
41740 /* Create a new user function */
41741 rc = jx9NewForeignFunction(&(*pVm), &(*pName), xFunc, pUserData, &pFunc);
41742 if( rc != SXRET_OK ){
41743 return rc;
41744 }
41745 /* Install the function in the corresponding hashtable */
41746 rc = SyHashInsert(&pVm->hHostFunction, SyStringData(&pFunc->sName), pName->nByte, pFunc);
41747 if( rc != SXRET_OK ){
41748 SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
41749 SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
41750 return rc;
41751 }
41752 /* User function successfully installed */
41753 return SXRET_OK;
41754}
41755/*
41756 * Initialize a VM function.
41757 */
41758JX9_PRIVATE sxi32 jx9VmInitFuncState(
41759 jx9_vm *pVm, /* Target VM */
41760 jx9_vm_func *pFunc, /* Target Fucntion */
41761 const char *zName, /* Function name */
41762 sxu32 nByte, /* zName length */
41763 sxi32 iFlags, /* Configuration flags */
41764 void *pUserData /* Function private data */
41765 )
41766{
41767 /* Zero the structure */
41768 SyZero(pFunc, sizeof(jx9_vm_func));
41769 /* Initialize structure fields */
41770 /* Arguments container */
41771 SySetInit(&pFunc->aArgs, &pVm->sAllocator, sizeof(jx9_vm_func_arg));
41772 /* Static variable container */
41773 SySetInit(&pFunc->aStatic, &pVm->sAllocator, sizeof(jx9_vm_func_static_var));
41774 /* Bytecode container */
41775 SySetInit(&pFunc->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
41776 /* Preallocate some instruction slots */
41777 SySetAlloc(&pFunc->aByteCode, 0x10);
41778 pFunc->iFlags = iFlags;
41779 pFunc->pUserData = pUserData;
41780 SyStringInitFromBuf(&pFunc->sName, zName, nByte);
41781 return SXRET_OK;
41782}
41783/*
41784 * Install a user defined function in the corresponding VM container.
41785 */
41786JX9_PRIVATE sxi32 jx9VmInstallUserFunction(
41787 jx9_vm *pVm, /* Target VM */
41788 jx9_vm_func *pFunc, /* Target function */
41789 SyString *pName /* Function name */
41790 )
41791{
41792 SyHashEntry *pEntry;
41793 sxi32 rc;
41794 if( pName == 0 ){
41795 /* Use the built-in name */
41796 pName = &pFunc->sName;
41797 }
41798 /* Check for duplicates (functions with the same name) first */
41799 pEntry = SyHashGet(&pVm->hFunction, pName->zString, pName->nByte);
41800 if( pEntry ){
41801 jx9_vm_func *pLink = (jx9_vm_func *)pEntry->pUserData;
41802 if( pLink != pFunc ){
41803 /* Link */
41804 pFunc->pNextName = pLink;
41805 pEntry->pUserData = pFunc;
41806 }
41807 return SXRET_OK;
41808 }
41809 /* First time seen */
41810 pFunc->pNextName = 0;
41811 rc = SyHashInsert(&pVm->hFunction, pName->zString, pName->nByte, pFunc);
41812 return rc;
41813}
41814/*
41815 * Instruction builder interface.
41816 */
41817JX9_PRIVATE sxi32 jx9VmEmitInstr(
41818 jx9_vm *pVm, /* Target VM */
41819 sxi32 iOp, /* Operation to perform */
41820 sxi32 iP1, /* First operand */
41821 sxu32 iP2, /* Second operand */
41822 void *p3, /* Third operand */
41823 sxu32 *pIndex /* Instruction index. NULL otherwise */
41824 )
41825{
41826 VmInstr sInstr;
41827 sxi32 rc;
41828 /* Fill the VM instruction */
41829 sInstr.iOp = (sxu8)iOp;
41830 sInstr.iP1 = iP1;
41831 sInstr.iP2 = iP2;
41832 sInstr.p3 = p3;
41833 if( pIndex ){
41834 /* Instruction index in the bytecode array */
41835 *pIndex = SySetUsed(pVm->pByteContainer);
41836 }
41837 /* Finally, record the instruction */
41838 rc = SySetPut(pVm->pByteContainer, (const void *)&sInstr);
41839 if( rc != SXRET_OK ){
41840 jx9GenCompileError(&pVm->sCodeGen, E_ERROR, 1, "Fatal, Cannot emit instruction due to a memory failure");
41841 /* Fall throw */
41842 }
41843 return rc;
41844}
41845/*
41846 * Swap the current bytecode container with the given one.
41847 */
41848JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer)
41849{
41850 if( pContainer == 0 ){
41851 /* Point to the default container */
41852 pVm->pByteContainer = &pVm->aByteCode;
41853 }else{
41854 /* Change container */
41855 pVm->pByteContainer = &(*pContainer);
41856 }
41857 return SXRET_OK;
41858}
41859/*
41860 * Return the current bytecode container.
41861 */
41862JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm)
41863{
41864 return pVm->pByteContainer;
41865}
41866/*
41867 * Extract the VM instruction rooted at nIndex.
41868 */
41869JX9_PRIVATE VmInstr * jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex)
41870{
41871 VmInstr *pInstr;
41872 pInstr = (VmInstr *)SySetAt(pVm->pByteContainer, nIndex);
41873 return pInstr;
41874}
41875/*
41876 * Return the total number of VM instructions recorded so far.
41877 */
41878JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm)
41879{
41880 return SySetUsed(pVm->pByteContainer);
41881}
41882/*
41883 * Pop the last VM instruction.
41884 */
41885JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm)
41886{
41887 return (VmInstr *)SySetPop(pVm->pByteContainer);
41888}
41889/*
41890 * Peek the last VM instruction.
41891 */
41892JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm)
41893{
41894 return (VmInstr *)SySetPeek(pVm->pByteContainer);
41895}
41896/*
41897 * Allocate a new virtual machine frame.
41898 */
41899static VmFrame * VmNewFrame(
41900 jx9_vm *pVm, /* Target VM */
41901 void *pUserData /* Upper-layer private data */
41902 )
41903{
41904 VmFrame *pFrame;
41905 /* Allocate a new vm frame */
41906 pFrame = (VmFrame *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(VmFrame));
41907 if( pFrame == 0 ){
41908 return 0;
41909 }
41910 /* Zero the structure */
41911 SyZero(pFrame, sizeof(VmFrame));
41912 /* Initialize frame fields */
41913 pFrame->pUserData = pUserData;
41914 pFrame->pVm = pVm;
41915 SyHashInit(&pFrame->hVar, &pVm->sAllocator, 0, 0);
41916 SySetInit(&pFrame->sArg, &pVm->sAllocator, sizeof(VmSlot));
41917 SySetInit(&pFrame->sLocal, &pVm->sAllocator, sizeof(VmSlot));
41918 return pFrame;
41919}
41920/*
41921 * Enter a VM frame.
41922 */
41923static sxi32 VmEnterFrame(
41924 jx9_vm *pVm, /* Target VM */
41925 void *pUserData, /* Upper-layer private data */
41926 VmFrame **ppFrame /* OUT: Top most active frame */
41927 )
41928{
41929 VmFrame *pFrame;
41930 /* Allocate a new frame */
41931 pFrame = VmNewFrame(&(*pVm), pUserData);
41932 if( pFrame == 0 ){
41933 return SXERR_MEM;
41934 }
41935 /* Link to the list of active VM frame */
41936 pFrame->pParent = pVm->pFrame;
41937 pVm->pFrame = pFrame;
41938 if( ppFrame ){
41939 /* Write a pointer to the new VM frame */
41940 *ppFrame = pFrame;
41941 }
41942 return SXRET_OK;
41943}
41944/*
41945 * Link a foreign variable with the TOP most active frame.
41946 * Refer to the JX9_OP_UPLINK instruction implementation for more
41947 * information.
41948 */
41949static sxi32 VmFrameLink(jx9_vm *pVm,SyString *pName)
41950{
41951 VmFrame *pTarget, *pFrame;
41952 SyHashEntry *pEntry = 0;
41953 sxi32 rc;
41954 /* Point to the upper frame */
41955 pFrame = pVm->pFrame;
41956 pTarget = pFrame;
41957 pFrame = pTarget->pParent;
41958 while( pFrame ){
41959 /* Query the current frame */
41960 pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte);
41961 if( pEntry ){
41962 /* Variable found */
41963 break;
41964 }
41965 /* Point to the upper frame */
41966 pFrame = pFrame->pParent;
41967 }
41968 if( pEntry == 0 ){
41969 /* Inexistant variable */
41970 return SXERR_NOTFOUND;
41971 }
41972 /* Link to the current frame */
41973 rc = SyHashInsert(&pTarget->hVar, pEntry->pKey, pEntry->nKeyLen, pEntry->pUserData);
41974 return rc;
41975}
41976/*
41977 * Leave the top-most active frame.
41978 */
41979static void VmLeaveFrame(jx9_vm *pVm)
41980{
41981 VmFrame *pFrame = pVm->pFrame;
41982 if( pFrame ){
41983 /* Unlink from the list of active VM frame */
41984 pVm->pFrame = pFrame->pParent;
41985 if( pFrame->pParent ){
41986 VmSlot *aSlot;
41987 sxu32 n;
41988 /* Restore local variable to the free pool so that they can be reused again */
41989 aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal);
41990 for(n = 0 ; n < SySetUsed(&pFrame->sLocal) ; ++n ){
41991 /* Unset the local variable */
41992 jx9VmUnsetMemObj(&(*pVm), aSlot[n].nIdx);
41993 }
41994 }
41995 /* Release internal containers */
41996 SyHashRelease(&pFrame->hVar);
41997 SySetRelease(&pFrame->sArg);
41998 SySetRelease(&pFrame->sLocal);
41999 /* Release the whole structure */
42000 SyMemBackendPoolFree(&pVm->sAllocator, pFrame);
42001 }
42002}
42003/*
42004 * Compare two functions signature and return the comparison result.
42005 */
42006static int VmOverloadCompare(SyString *pFirst, SyString *pSecond)
42007{
42008 const char *zSend = &pSecond->zString[pSecond->nByte];
42009 const char *zFend = &pFirst->zString[pFirst->nByte];
42010 const char *zSin = pSecond->zString;
42011 const char *zFin = pFirst->zString;
42012 const char *zPtr = zFin;
42013 for(;;){
42014 if( zFin >= zFend || zSin >= zSend ){
42015 break;
42016 }
42017 if( zFin[0] != zSin[0] ){
42018 /* mismatch */
42019 break;
42020 }
42021 zFin++;
42022 zSin++;
42023 }
42024 return (int)(zFin-zPtr);
42025}
42026/*
42027 * Select the appropriate VM function for the current call context.
42028 * This is the implementation of the powerful 'function overloading' feature
42029 * introduced by the version 2 of the JX9 engine.
42030 * Refer to the official documentation for more information.
42031 */
42032static jx9_vm_func * VmOverload(
42033 jx9_vm *pVm, /* Target VM */
42034 jx9_vm_func *pList, /* Linked list of candidates for overloading */
42035 jx9_value *aArg, /* Array of passed arguments */
42036 int nArg /* Total number of passed arguments */
42037 )
42038{
42039 int iTarget, i, j, iCur, iMax;
42040 jx9_vm_func *apSet[10]; /* Maximum number of candidates */
42041 jx9_vm_func *pLink;
42042 SyString sArgSig;
42043 SyBlob sSig;
42044
42045 pLink = pList;
42046 i = 0;
42047 /* Put functions expecting the same number of passed arguments */
42048 while( i < (int)SX_ARRAYSIZE(apSet) ){
42049 if( pLink == 0 ){
42050 break;
42051 }
42052 if( (int)SySetUsed(&pLink->aArgs) == nArg ){
42053 /* Candidate for overloading */
42054 apSet[i++] = pLink;
42055 }
42056 /* Point to the next entry */
42057 pLink = pLink->pNextName;
42058 }
42059 if( i < 1 ){
42060 /* No candidates, return the head of the list */
42061 return pList;
42062 }
42063 if( nArg < 1 || i < 2 ){
42064 /* Return the only candidate */
42065 return apSet[0];
42066 }
42067 /* Calculate function signature */
42068 SyBlobInit(&sSig, &pVm->sAllocator);
42069 for( j = 0 ; j < nArg ; j++ ){
42070 int c = 'n'; /* null */
42071 if( aArg[j].iFlags & MEMOBJ_HASHMAP ){
42072 /* Hashmap */
42073 c = 'h';
42074 }else if( aArg[j].iFlags & MEMOBJ_BOOL ){
42075 /* bool */
42076 c = 'b';
42077 }else if( aArg[j].iFlags & MEMOBJ_INT ){
42078 /* int */
42079 c = 'i';
42080 }else if( aArg[j].iFlags & MEMOBJ_STRING ){
42081 /* String */
42082 c = 's';
42083 }else if( aArg[j].iFlags & MEMOBJ_REAL ){
42084 /* Float */
42085 c = 'f';
42086 }
42087 if( c > 0 ){
42088 SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
42089 }
42090 }
42091 SyStringInitFromBuf(&sArgSig, SyBlobData(&sSig), SyBlobLength(&sSig));
42092 iTarget = 0;
42093 iMax = -1;
42094 /* Select the appropriate function */
42095 for( j = 0 ; j < i ; j++ ){
42096 /* Compare the two signatures */
42097 iCur = VmOverloadCompare(&sArgSig, &apSet[j]->sSignature);
42098 if( iCur > iMax ){
42099 iMax = iCur;
42100 iTarget = j;
42101 }
42102 }
42103 SyBlobRelease(&sSig);
42104 /* Appropriate function for the current call context */
42105 return apSet[iTarget];
42106}
42107/*
42108 * Dummy read-only buffer used for slot reservation.
42109 */
42110static const char zDummy[sizeof(jx9_value)] = { 0 }; /* Must be >= sizeof(jx9_value) */
42111/*
42112 * Reserve a constant memory object.
42113 * Return a pointer to the raw jx9_value on success. NULL on failure.
42114 */
42115JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex)
42116{
42117 jx9_value *pObj;
42118 sxi32 rc;
42119 if( pIndex ){
42120 /* Object index in the object table */
42121 *pIndex = SySetUsed(&pVm->aLitObj);
42122 }
42123 /* Reserve a slot for the new object */
42124 rc = SySetPut(&pVm->aLitObj, (const void *)zDummy);
42125 if( rc != SXRET_OK ){
42126 /* If the supplied memory subsystem is so sick that we are unable to allocate
42127 * a tiny chunk of memory, there is no much we can do here.
42128 */
42129 return 0;
42130 }
42131 pObj = (jx9_value *)SySetPeek(&pVm->aLitObj);
42132 return pObj;
42133}
42134/*
42135 * Reserve a memory object.
42136 * Return a pointer to the raw jx9_value on success. NULL on failure.
42137 */
42138static jx9_value * VmReserveMemObj(jx9_vm *pVm, sxu32 *pIndex)
42139{
42140 jx9_value *pObj;
42141 sxi32 rc;
42142 if( pIndex ){
42143 /* Object index in the object table */
42144 *pIndex = SySetUsed(&pVm->aMemObj);
42145 }
42146 /* Reserve a slot for the new object */
42147 rc = SySetPut(&pVm->aMemObj, (const void *)zDummy);
42148 if( rc != SXRET_OK ){
42149 /* If the supplied memory subsystem is so sick that we are unable to allocate
42150 * a tiny chunk of memory, there is no much we can do here.
42151 */
42152 return 0;
42153 }
42154 pObj = (jx9_value *)SySetPeek(&pVm->aMemObj);
42155 return pObj;
42156}
42157/* Forward declaration */
42158static sxi32 VmEvalChunk(jx9_vm *pVm, jx9_context *pCtx, SyString *pChunk, int iFlags, int bTrueReturn);
42159/*
42160 * Built-in functions that cannot be implemented directly as foreign functions.
42161 */
42162#define JX9_BUILTIN_LIB \
42163 "function scandir(string $directory, int $sort_order = SCANDIR_SORT_ASCENDING)"\
42164 "{"\
42165 " if( func_num_args() < 1 ){ return FALSE; }"\
42166 " $aDir = [];"\
42167 " $pHandle = opendir($directory);"\
42168 " if( $pHandle == FALSE ){ return FALSE; }"\
42169 " while(FALSE !== ($pEntry = readdir($pHandle)) ){"\
42170 " $aDir[] = $pEntry;"\
42171 " }"\
42172 " closedir($pHandle);"\
42173 " if( $sort_order == SCANDIR_SORT_DESCENDING ){"\
42174 " rsort($aDir);"\
42175 " }else if( $sort_order == SCANDIR_SORT_ASCENDING ){"\
42176 " sort($aDir);"\
42177 " }"\
42178 " return $aDir;"\
42179 "}"\
42180 "function glob(string $pattern, int $iFlags = 0){"\
42181 "/* Open the target directory */"\
42182 "$zDir = dirname($pattern);"\
42183 "if(!is_string($zDir) ){ $zDir = './'; }"\
42184 "$pHandle = opendir($zDir);"\
42185 "if( $pHandle == FALSE ){"\
42186 " /* IO error while opening the current directory, return FALSE */"\
42187 " return FALSE;"\
42188 "}"\
42189 "$pattern = basename($pattern);"\
42190 "$pArray = []; /* Empty array */"\
42191 "/* Loop throw available entries */"\
42192 "while( FALSE !== ($pEntry = readdir($pHandle)) ){"\
42193 " /* Use the built-in strglob function which is a Symisc eXtension for wildcard comparison*/"\
42194 " $rc = strglob($pattern, $pEntry);"\
42195 " if( $rc ){"\
42196 " if( is_dir($pEntry) ){"\
42197 " if( $iFlags & GLOB_MARK ){"\
42198 " /* Adds a slash to each directory returned */"\
42199 " $pEntry .= DIRECTORY_SEPARATOR;"\
42200 " }"\
42201 " }else if( $iFlags & GLOB_ONLYDIR ){"\
42202 " /* Not a directory, ignore */"\
42203 " continue;"\
42204 " }"\
42205 " /* Add the entry */"\
42206 " $pArray[] = $pEntry;"\
42207 " }"\
42208 " }"\
42209 "/* Close the handle */"\
42210 "closedir($pHandle);"\
42211 "if( ($iFlags & GLOB_NOSORT) == 0 ){"\
42212 " /* Sort the array */"\
42213 " sort($pArray);"\
42214 "}"\
42215 "if( ($iFlags & GLOB_NOCHECK) && sizeof($pArray) < 1 ){"\
42216 " /* Return the search pattern if no files matching were found */"\
42217 " $pArray[] = $pattern;"\
42218 "}"\
42219 "/* Return the created array */"\
42220 "return $pArray;"\
42221 "}"\
42222 "/* Creates a temporary file */"\
42223 "function tmpfile(){"\
42224 " /* Extract the temp directory */"\
42225 " $zTempDir = sys_get_temp_dir();"\
42226 " if( strlen($zTempDir) < 1 ){"\
42227 " /* Use the current dir */"\
42228 " $zTempDir = '.';"\
42229 " }"\
42230 " /* Create the file */"\
42231 " $pHandle = fopen($zTempDir.DIRECTORY_SEPARATOR.'JX9'.rand_str(12), 'w+');"\
42232 " return $pHandle;"\
42233 "}"\
42234 "/* Creates a temporary filename */"\
42235 "function tempnam(string $zDir = sys_get_temp_dir() /* Symisc eXtension */, string $zPrefix = 'JX9')"\
42236 "{"\
42237 " return $zDir.DIRECTORY_SEPARATOR.$zPrefix.rand_str(12);"\
42238 "}"\
42239 "function max(){"\
42240 " $pArgs = func_get_args();"\
42241 " if( sizeof($pArgs) < 1 ){"\
42242 " return null;"\
42243 " }"\
42244 " if( sizeof($pArgs) < 2 ){"\
42245 " $pArg = $pArgs[0];"\
42246 " if( !is_array($pArg) ){"\
42247 " return $pArg; "\
42248 " }"\
42249 " if( sizeof($pArg) < 1 ){"\
42250 " return null;"\
42251 " }"\
42252 " $pArg = array_copy($pArgs[0]);"\
42253 " reset($pArg);"\
42254 " $max = current($pArg);"\
42255 " while( FALSE !== ($val = next($pArg)) ){"\
42256 " if( $val > $max ){"\
42257 " $max = $val;"\
42258 " }"\
42259 " }"\
42260 " return $max;"\
42261 " }"\
42262 " $max = $pArgs[0];"\
42263 " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\
42264 " $val = $pArgs[$i];"\
42265 "if( $val > $max ){"\
42266 " $max = $val;"\
42267 "}"\
42268 " }"\
42269 " return $max;"\
42270 "}"\
42271 "function min(){"\
42272 " $pArgs = func_get_args();"\
42273 " if( sizeof($pArgs) < 1 ){"\
42274 " return null;"\
42275 " }"\
42276 " if( sizeof($pArgs) < 2 ){"\
42277 " $pArg = $pArgs[0];"\
42278 " if( !is_array($pArg) ){"\
42279 " return $pArg; "\
42280 " }"\
42281 " if( sizeof($pArg) < 1 ){"\
42282 " return null;"\
42283 " }"\
42284 " $pArg = array_copy($pArgs[0]);"\
42285 " reset($pArg);"\
42286 " $min = current($pArg);"\
42287 " while( FALSE !== ($val = next($pArg)) ){"\
42288 " if( $val < $min ){"\
42289 " $min = $val;"\
42290 " }"\
42291 " }"\
42292 " return $min;"\
42293 " }"\
42294 " $min = $pArgs[0];"\
42295 " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\
42296 " $val = $pArgs[$i];"\
42297 "if( $val < $min ){"\
42298 " $min = $val;"\
42299 " }"\
42300 " }"\
42301 " return $min;"\
42302 "}"
42303/*
42304 * Initialize a freshly allocated JX9 Virtual Machine so that we can
42305 * start compiling the target JX9 program.
42306 */
42307JX9_PRIVATE sxi32 jx9VmInit(
42308 jx9_vm *pVm, /* Initialize this */
42309 jx9 *pEngine /* Master engine */
42310 )
42311{
42312 SyString sBuiltin;
42313 jx9_value *pObj;
42314 sxi32 rc;
42315 /* Zero the structure */
42316 SyZero(pVm, sizeof(jx9_vm));
42317 /* Initialize VM fields */
42318 pVm->pEngine = &(*pEngine);
42319 SyMemBackendInitFromParent(&pVm->sAllocator, &pEngine->sAllocator);
42320 /* Instructions containers */
42321 SySetInit(&pVm->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
42322 SySetAlloc(&pVm->aByteCode, 0xFF);
42323 pVm->pByteContainer = &pVm->aByteCode;
42324 /* Object containers */
42325 SySetInit(&pVm->aMemObj, &pVm->sAllocator, sizeof(jx9_value));
42326 SySetAlloc(&pVm->aMemObj, 0xFF);
42327 /* Virtual machine internal containers */
42328 SyBlobInit(&pVm->sConsumer, &pVm->sAllocator);
42329 SyBlobInit(&pVm->sWorker, &pVm->sAllocator);
42330 SyBlobInit(&pVm->sArgv, &pVm->sAllocator);
42331 SySetInit(&pVm->aLitObj, &pVm->sAllocator, sizeof(jx9_value));
42332 SySetAlloc(&pVm->aLitObj, 0xFF);
42333 SyHashInit(&pVm->hHostFunction, &pVm->sAllocator, 0, 0);
42334 SyHashInit(&pVm->hFunction, &pVm->sAllocator, 0, 0);
42335 SyHashInit(&pVm->hConstant, &pVm->sAllocator, 0, 0);
42336 SyHashInit(&pVm->hSuper, &pVm->sAllocator, 0, 0);
42337 SySetInit(&pVm->aFreeObj, &pVm->sAllocator, sizeof(VmSlot));
42338 /* Configuration containers */
42339 SySetInit(&pVm->aFiles, &pVm->sAllocator, sizeof(SyString));
42340 SySetInit(&pVm->aPaths, &pVm->sAllocator, sizeof(SyString));
42341 SySetInit(&pVm->aIncluded, &pVm->sAllocator, sizeof(SyString));
42342 SySetInit(&pVm->aIOstream, &pVm->sAllocator, sizeof(jx9_io_stream *));
42343 /* Error callbacks containers */
42344 jx9MemObjInit(&(*pVm), &pVm->sAssertCallback);
42345 /* Set a default recursion limit */
42346#if defined(__WINNT__) || defined(__UNIXES__)
42347 pVm->nMaxDepth = 32;
42348#else
42349 pVm->nMaxDepth = 16;
42350#endif
42351 /* Default assertion flags */
42352 pVm->iAssertFlags = JX9_ASSERT_WARNING; /* Issue a warning for each failed assertion */
42353 /* PRNG context */
42354 SyRandomnessInit(&pVm->sPrng, 0, 0);
42355 /* Install the null constant */
42356 pObj = jx9VmReserveConstObj(&(*pVm), 0);
42357 if( pObj == 0 ){
42358 rc = SXERR_MEM;
42359 goto Err;
42360 }
42361 jx9MemObjInit(pVm, pObj);
42362 /* Install the boolean TRUE constant */
42363 pObj = jx9VmReserveConstObj(&(*pVm), 0);
42364 if( pObj == 0 ){
42365 rc = SXERR_MEM;
42366 goto Err;
42367 }
42368 jx9MemObjInitFromBool(pVm, pObj, 1);
42369 /* Install the boolean FALSE constant */
42370 pObj = jx9VmReserveConstObj(&(*pVm), 0);
42371 if( pObj == 0 ){
42372 rc = SXERR_MEM;
42373 goto Err;
42374 }
42375 jx9MemObjInitFromBool(pVm, pObj, 0);
42376 /* Create the global frame */
42377 rc = VmEnterFrame(&(*pVm), 0, 0);
42378 if( rc != SXRET_OK ){
42379 goto Err;
42380 }
42381 /* Initialize the code generator */
42382 rc = jx9InitCodeGenerator(pVm, pEngine->xConf.xErr, pEngine->xConf.pErrData);
42383 if( rc != SXRET_OK ){
42384 goto Err;
42385 }
42386 /* VM correctly initialized, set the magic number */
42387 pVm->nMagic = JX9_VM_INIT;
42388 SyStringInitFromBuf(&sBuiltin,JX9_BUILTIN_LIB, sizeof(JX9_BUILTIN_LIB)-1);
42389 /* Compile the built-in library */
42390 VmEvalChunk(&(*pVm), 0, &sBuiltin, 0, FALSE);
42391 /* Reset the code generator */
42392 jx9ResetCodeGenerator(&(*pVm), pEngine->xConf.xErr, pEngine->xConf.pErrData);
42393 return SXRET_OK;
42394Err:
42395 SyMemBackendRelease(&pVm->sAllocator);
42396 return rc;
42397}
42398/*
42399 * Default VM output consumer callback.That is, all VM output is redirected to this
42400 * routine which store the output in an internal blob.
42401 * The output can be extracted later after program execution [jx9_vm_exec()] via
42402 * the [jx9_vm_config()] interface with a configuration verb set to
42403 * jx9VM_CONFIG_EXTRACT_OUTPUT.
42404 * Refer to the official docurmentation for additional information.
42405 * Note that for performance reason it's preferable to install a VM output
42406 * consumer callback via (jx9VM_CONFIG_OUTPUT) rather than waiting for the VM
42407 * to finish executing and extracting the output.
42408 */
42409JX9_PRIVATE sxi32 jx9VmBlobConsumer(
42410 const void *pOut, /* VM Generated output*/
42411 unsigned int nLen, /* Generated output length */
42412 void *pUserData /* User private data */
42413 )
42414{
42415 sxi32 rc;
42416 /* Store the output in an internal BLOB */
42417 rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen);
42418 return rc;
42419}
42420#define VM_STACK_GUARD 16
42421/*
42422 * Allocate a new operand stack so that we can start executing
42423 * our compiled JX9 program.
42424 * Return a pointer to the operand stack (array of jx9_values)
42425 * on success. NULL (Fatal error) on failure.
42426 */
42427static jx9_value * VmNewOperandStack(
42428 jx9_vm *pVm, /* Target VM */
42429 sxu32 nInstr /* Total numer of generated bytecode instructions */
42430 )
42431{
42432 jx9_value *pStack;
42433 /* No instruction ever pushes more than a single element onto the
42434 ** stack and the stack never grows on successive executions of the
42435 ** same loop. So the total number of instructions is an upper bound
42436 ** on the maximum stack depth required.
42437 **
42438 ** Allocation all the stack space we will ever need.
42439 */
42440 nInstr += VM_STACK_GUARD;
42441 pStack = (jx9_value *)SyMemBackendAlloc(&pVm->sAllocator, nInstr * sizeof(jx9_value));
42442 if( pStack == 0 ){
42443 return 0;
42444 }
42445 /* Initialize the operand stack */
42446 while( nInstr > 0 ){
42447 jx9MemObjInit(&(*pVm), &pStack[nInstr - 1]);
42448 --nInstr;
42449 }
42450 /* Ready for bytecode execution */
42451 return pStack;
42452}
42453/* Forward declaration */
42454static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm);
42455/*
42456 * Prepare the Virtual Machine for bytecode execution.
42457 * This routine gets called by the JX9 engine after
42458 * successful compilation of the target JX9 program.
42459 */
42460JX9_PRIVATE sxi32 jx9VmMakeReady(
42461 jx9_vm *pVm /* Target VM */
42462 )
42463{
42464 sxi32 rc;
42465 if( pVm->nMagic != JX9_VM_INIT ){
42466 /* Initialize your VM first */
42467 return SXERR_CORRUPT;
42468 }
42469 /* Mark the VM ready for bytecode execution */
42470 pVm->nMagic = JX9_VM_RUN;
42471 /* Release the code generator now we have compiled our program */
42472 jx9ResetCodeGenerator(pVm, 0, 0);
42473 /* Emit the DONE instruction */
42474 rc = jx9VmEmitInstr(&(*pVm), JX9_OP_DONE, 0, 0, 0, 0);
42475 if( rc != SXRET_OK ){
42476 return SXERR_MEM;
42477 }
42478 /* Script return value */
42479 jx9MemObjInit(&(*pVm), &pVm->sExec); /* Assume a NULL return value */
42480 /* Allocate a new operand stack */
42481 pVm->aOps = VmNewOperandStack(&(*pVm), SySetUsed(pVm->pByteContainer));
42482 if( pVm->aOps == 0 ){
42483 return SXERR_MEM;
42484 }
42485 /* Set the default VM output consumer callback and it's
42486 * private data. */
42487 pVm->sVmConsumer.xConsumer = jx9VmBlobConsumer;
42488 pVm->sVmConsumer.pUserData = &pVm->sConsumer;
42489 /* Register special functions first [i.e: print, func_get_args(), die, etc.] */
42490 rc = VmRegisterSpecialFunction(&(*pVm));
42491 if( rc != SXRET_OK ){
42492 /* Don't worry about freeing memory, everything will be released shortly */
42493 return rc;
42494 }
42495 /* Create superglobals [i.e: $GLOBALS, $_GET, $_POST...] */
42496 rc = jx9HashmapLoadBuiltin(&(*pVm));
42497 if( rc != SXRET_OK ){
42498 /* Don't worry about freeing memory, everything will be released shortly */
42499 return rc;
42500 }
42501 /* Register built-in constants [i.e: JX9_EOL, JX9_OS...] */
42502 jx9RegisterBuiltInConstant(&(*pVm));
42503 /* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */
42504 jx9RegisterBuiltInFunction(&(*pVm));
42505 /* VM is ready for bytecode execution */
42506 return SXRET_OK;
42507}
42508/*
42509 * Reset a Virtual Machine to it's initial state.
42510 */
42511JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm)
42512{
42513 if( pVm->nMagic != JX9_VM_RUN && pVm->nMagic != JX9_VM_EXEC ){
42514 return SXERR_CORRUPT;
42515 }
42516 /* TICKET 1433-003: As of this version, the VM is automatically reset */
42517 SyBlobReset(&pVm->sConsumer);
42518 jx9MemObjRelease(&pVm->sExec);
42519 /* Set the ready flag */
42520 pVm->nMagic = JX9_VM_RUN;
42521 return SXRET_OK;
42522}
42523/*
42524 * Release a Virtual Machine.
42525 * Every virtual machine must be destroyed in order to avoid memory leaks.
42526 */
42527JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm)
42528{
42529 /* Set the stale magic number */
42530 pVm->nMagic = JX9_VM_STALE;
42531 /* Release the private memory subsystem */
42532 SyMemBackendRelease(&pVm->sAllocator);
42533 return SXRET_OK;
42534}
42535/*
42536 * Initialize a foreign function call context.
42537 * The context in which a foreign function executes is stored in a jx9_context object.
42538 * A pointer to a jx9_context object is always first parameter to application-defined foreign
42539 * functions.
42540 * The application-defined foreign function implementation will pass this pointer through into
42541 * calls to dozens of interfaces, these includes jx9_result_int(), jx9_result_string(), jx9_result_value(),
42542 * jx9_context_new_scalar(), jx9_context_alloc_chunk(), jx9_context_output(), jx9_context_throw_error()
42543 * and many more. Refer to the C/C++ Interfaces documentation for additional information.
42544 */
42545static sxi32 VmInitCallContext(
42546 jx9_context *pOut, /* Call Context */
42547 jx9_vm *pVm, /* Target VM */
42548 jx9_user_func *pFunc, /* Foreign function to execute shortly */
42549 jx9_value *pRet, /* Store return value here*/
42550 sxi32 iFlags /* Control flags */
42551 )
42552{
42553 pOut->pFunc = pFunc;
42554 pOut->pVm = pVm;
42555 SySetInit(&pOut->sVar, &pVm->sAllocator, sizeof(jx9_value *));
42556 SySetInit(&pOut->sChunk, &pVm->sAllocator, sizeof(jx9_aux_data));
42557 /* Assume a null return value */
42558 MemObjSetType(pRet, MEMOBJ_NULL);
42559 pOut->pRet = pRet;
42560 pOut->iFlags = iFlags;
42561 return SXRET_OK;
42562}
42563/*
42564 * Release a foreign function call context and cleanup the mess
42565 * left behind.
42566 */
42567static void VmReleaseCallContext(jx9_context *pCtx)
42568{
42569 sxu32 n;
42570 if( SySetUsed(&pCtx->sVar) > 0 ){
42571 jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
42572 for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
42573 if( apObj[n] == 0 ){
42574 /* Already released */
42575 continue;
42576 }
42577 jx9MemObjRelease(apObj[n]);
42578 SyMemBackendPoolFree(&pCtx->pVm->sAllocator, apObj[n]);
42579 }
42580 SySetRelease(&pCtx->sVar);
42581 }
42582 if( SySetUsed(&pCtx->sChunk) > 0 ){
42583 jx9_aux_data *aAux;
42584 void *pChunk;
42585 /* Automatic release of dynamically allocated chunk
42586 * using [jx9_context_alloc_chunk()].
42587 */
42588 aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk);
42589 for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){
42590 pChunk = aAux[n].pAuxData;
42591 /* Release the chunk */
42592 if( pChunk ){
42593 SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk);
42594 }
42595 }
42596 SySetRelease(&pCtx->sChunk);
42597 }
42598}
42599/*
42600 * Release a jx9_value allocated from the body of a foreign function.
42601 * Refer to [jx9_context_release_value()] for additional information.
42602 */
42603JX9_PRIVATE void jx9VmReleaseContextValue(
42604 jx9_context *pCtx, /* Call context */
42605 jx9_value *pValue /* Release this value */
42606 )
42607{
42608 if( pValue == 0 ){
42609 /* NULL value is a harmless operation */
42610 return;
42611 }
42612 if( SySetUsed(&pCtx->sVar) > 0 ){
42613 jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
42614 sxu32 n;
42615 for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
42616 if( apObj[n] == pValue ){
42617 jx9MemObjRelease(pValue);
42618 SyMemBackendPoolFree(&pCtx->pVm->sAllocator, pValue);
42619 /* Mark as released */
42620 apObj[n] = 0;
42621 break;
42622 }
42623 }
42624 }
42625}
42626/*
42627 * Pop and release as many memory object from the operand stack.
42628 */
42629static void VmPopOperand(
42630 jx9_value **ppTos, /* Operand stack */
42631 sxi32 nPop /* Total number of memory objects to pop */
42632 )
42633{
42634 jx9_value *pTos = *ppTos;
42635 while( nPop > 0 ){
42636 jx9MemObjRelease(pTos);
42637 pTos--;
42638 nPop--;
42639 }
42640 /* Top of the stack */
42641 *ppTos = pTos;
42642}
42643/*
42644 * Reserve a memory object.
42645 * Return a pointer to the raw jx9_value on success. NULL on failure.
42646 */
42647JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIdx)
42648{
42649 jx9_value *pObj = 0;
42650 VmSlot *pSlot;
42651 sxu32 nIdx;
42652 /* Check for a free slot */
42653 nIdx = SXU32_HIGH; /* cc warning */
42654 pSlot = (VmSlot *)SySetPop(&pVm->aFreeObj);
42655 if( pSlot ){
42656 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx);
42657 nIdx = pSlot->nIdx;
42658 }
42659 if( pObj == 0 ){
42660 /* Reserve a new memory object */
42661 pObj = VmReserveMemObj(&(*pVm), &nIdx);
42662 if( pObj == 0 ){
42663 return 0;
42664 }
42665 }
42666 /* Set a null default value */
42667 jx9MemObjInit(&(*pVm), pObj);
42668 if( pIdx ){
42669 *pIdx = nIdx;
42670 }
42671 pObj->nIdx = nIdx;
42672 return pObj;
42673}
42674/*
42675 * Extract a variable value from the top active VM frame.
42676 * Return a pointer to the variable value on success.
42677 * NULL otherwise (non-existent variable/Out-of-memory, ...).
42678 */
42679static jx9_value * VmExtractMemObj(
42680 jx9_vm *pVm, /* Target VM */
42681 const SyString *pName, /* Variable name */
42682 int bDup, /* True to duplicate variable name */
42683 int bCreate /* True to create the variable if non-existent */
42684 )
42685{
42686 int bNullify = FALSE;
42687 SyHashEntry *pEntry;
42688 VmFrame *pFrame;
42689 jx9_value *pObj;
42690 sxu32 nIdx;
42691 sxi32 rc;
42692 /* Point to the top active frame */
42693 pFrame = pVm->pFrame;
42694 /* Perform the lookup */
42695 if( pName == 0 || pName->nByte < 1 ){
42696 static const SyString sAnnon = { " " , sizeof(char) };
42697 pName = &sAnnon;
42698 /* Always nullify the object */
42699 bNullify = TRUE;
42700 bDup = FALSE;
42701 }
42702 /* Check the superglobals table first */
42703 pEntry = SyHashGet(&pVm->hSuper, (const void *)pName->zString, pName->nByte);
42704 if( pEntry == 0 ){
42705 /* Query the top active frame */
42706 pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte);
42707 if( pEntry == 0 ){
42708 char *zName = (char *)pName->zString;
42709 VmSlot sLocal;
42710 if( !bCreate ){
42711 /* Do not create the variable, return NULL */
42712 return 0;
42713 }
42714 /* No such variable, automatically create a new one and install
42715 * it in the current frame.
42716 */
42717 pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
42718 if( pObj == 0 ){
42719 return 0;
42720 }
42721 if( bDup ){
42722 /* Duplicate name */
42723 zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
42724 if( zName == 0 ){
42725 return 0;
42726 }
42727 }
42728 /* Link to the top active VM frame */
42729 rc = SyHashInsert(&pFrame->hVar, zName, pName->nByte, SX_INT_TO_PTR(nIdx));
42730 if( rc != SXRET_OK ){
42731 /* Return the slot to the free pool */
42732 sLocal.nIdx = nIdx;
42733 sLocal.pUserData = 0;
42734 SySetPut(&pVm->aFreeObj, (const void *)&sLocal);
42735 return 0;
42736 }
42737 if( pFrame->pParent != 0 ){
42738 /* Local variable */
42739 sLocal.nIdx = nIdx;
42740 SySetPut(&pFrame->sLocal, (const void *)&sLocal);
42741 }
42742 }else{
42743 /* Extract variable contents */
42744 nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
42745 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
42746 if( bNullify && pObj ){
42747 jx9MemObjRelease(pObj);
42748 }
42749 }
42750 }else{
42751 /* Superglobal */
42752 nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
42753 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
42754 }
42755 return pObj;
42756}
42757/*
42758 * Extract a superglobal variable such as $_GET, $_POST, $_HEADERS, ....
42759 * Return a pointer to the variable value on success.NULL otherwise.
42760 */
42761static jx9_value * VmExtractSuper(
42762 jx9_vm *pVm, /* Target VM */
42763 const char *zName, /* Superglobal name: NOT NULL TERMINATED */
42764 sxu32 nByte /* zName length */
42765 )
42766{
42767 SyHashEntry *pEntry;
42768 jx9_value *pValue;
42769 sxu32 nIdx;
42770 /* Query the superglobal table */
42771 pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
42772 if( pEntry == 0 ){
42773 /* No such entry */
42774 return 0;
42775 }
42776 /* Extract the superglobal index in the global object pool */
42777 nIdx = SX_PTR_TO_INT(pEntry->pUserData);
42778 /* Extract the variable value */
42779 pValue = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
42780 return pValue;
42781}
42782/*
42783 * Perform a raw hashmap insertion.
42784 * Refer to the [jx9VmConfigure()] implementation for additional information.
42785 */
42786static sxi32 VmHashmapInsert(
42787 jx9_hashmap *pMap, /* Target hashmap */
42788 const char *zKey, /* Entry key */
42789 int nKeylen, /* zKey length*/
42790 const char *zData, /* Entry data */
42791 int nLen /* zData length */
42792 )
42793{
42794 jx9_value sKey,sValue;
42795 jx9_value *pKey;
42796 sxi32 rc;
42797 pKey = 0;
42798 jx9MemObjInit(pMap->pVm, &sKey);
42799 jx9MemObjInitFromString(pMap->pVm, &sValue, 0);
42800 if( zKey ){
42801 if( nKeylen < 0 ){
42802 nKeylen = (int)SyStrlen(zKey);
42803 }
42804 jx9MemObjStringAppend(&sKey, zKey, (sxu32)nKeylen);
42805 pKey = &sKey;
42806 }
42807 if( zData ){
42808 if( nLen < 0 ){
42809 /* Compute length automatically */
42810 nLen = (int)SyStrlen(zData);
42811 }
42812 jx9MemObjStringAppend(&sValue, zData, (sxu32)nLen);
42813 }
42814 /* Perform the insertion */
42815 rc = jx9HashmapInsert(&(*pMap),pKey,&sValue);
42816 jx9MemObjRelease(&sKey);
42817 jx9MemObjRelease(&sValue);
42818 return rc;
42819}
42820/* Forward declaration */
42821static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte);
42822/*
42823 * Configure a working virtual machine instance.
42824 *
42825 * This routine is used to configure a JX9 virtual machine obtained by a prior
42826 * successful call to one of the compile interface such as jx9_compile()
42827 * jx9_compile_v2() or jx9_compile_file().
42828 * The second argument to this function is an integer configuration option
42829 * that determines what property of the JX9 virtual machine is to be configured.
42830 * Subsequent arguments vary depending on the configuration option in the second
42831 * argument. There are many verbs but the most important are JX9_VM_CONFIG_OUTPUT,
42832 * JX9_VM_CONFIG_HTTP_REQUEST and JX9_VM_CONFIG_ARGV_ENTRY.
42833 * Refer to the official documentation for the list of allowed verbs.
42834 */
42835JX9_PRIVATE sxi32 jx9VmConfigure(
42836 jx9_vm *pVm, /* Target VM */
42837 sxi32 nOp, /* Configuration verb */
42838 va_list ap /* Subsequent option arguments */
42839 )
42840{
42841 sxi32 rc = SXRET_OK;
42842 switch(nOp){
42843 case JX9_VM_CONFIG_OUTPUT: {
42844 ProcConsumer xConsumer = va_arg(ap, ProcConsumer);
42845 void *pUserData = va_arg(ap, void *);
42846 /* VM output consumer callback */
42847#ifdef UNTRUST
42848 if( xConsumer == 0 ){
42849 rc = SXERR_CORRUPT;
42850 break;
42851 }
42852#endif
42853 /* Install the output consumer */
42854 pVm->sVmConsumer.xConsumer = xConsumer;
42855 pVm->sVmConsumer.pUserData = pUserData;
42856 break;
42857 }
42858 case JX9_VM_CONFIG_IMPORT_PATH: {
42859 /* Import path */
42860 const char *zPath;
42861 SyString sPath;
42862 zPath = va_arg(ap, const char *);
42863#if defined(UNTRUST)
42864 if( zPath == 0 ){
42865 rc = SXERR_EMPTY;
42866 break;
42867 }
42868#endif
42869 SyStringInitFromBuf(&sPath, zPath, SyStrlen(zPath));
42870 /* Remove trailing slashes and backslashes */
42871#ifdef __WINNT__
42872 SyStringTrimTrailingChar(&sPath, '\\');
42873#endif
42874 SyStringTrimTrailingChar(&sPath, '/');
42875 /* Remove leading and trailing white spaces */
42876 SyStringFullTrim(&sPath);
42877 if( sPath.nByte > 0 ){
42878 /* Store the path in the corresponding conatiner */
42879 rc = SySetPut(&pVm->aPaths, (const void *)&sPath);
42880 }
42881 break;
42882 }
42883 case JX9_VM_CONFIG_ERR_REPORT:
42884 /* Run-Time Error report */
42885 pVm->bErrReport = 1;
42886 break;
42887 case JX9_VM_CONFIG_RECURSION_DEPTH:{
42888 /* Recursion depth */
42889 int nDepth = va_arg(ap, int);
42890 if( nDepth > 2 && nDepth < 1024 ){
42891 pVm->nMaxDepth = nDepth;
42892 }
42893 break;
42894 }
42895 case JX9_VM_OUTPUT_LENGTH: {
42896 /* VM output length in bytes */
42897 sxu32 *pOut = va_arg(ap, sxu32 *);
42898#ifdef UNTRUST
42899 if( pOut == 0 ){
42900 rc = SXERR_CORRUPT;
42901 break;
42902 }
42903#endif
42904 *pOut = pVm->nOutputLen;
42905 break;
42906 }
42907 case JX9_VM_CONFIG_CREATE_VAR: {
42908 /* Create a new superglobal/global variable */
42909 const char *zName = va_arg(ap, const char *);
42910 jx9_value *pValue = va_arg(ap, jx9_value *);
42911 SyHashEntry *pEntry;
42912 jx9_value *pObj;
42913 sxu32 nByte;
42914 sxu32 nIdx;
42915#ifdef UNTRUST
42916 if( SX_EMPTY_STR(zName) || pValue == 0 ){
42917 rc = SXERR_CORRUPT;
42918 break;
42919 }
42920#endif
42921 nByte = SyStrlen(zName);
42922 /* Check if the superglobal is already installed */
42923 pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
42924 if( pEntry ){
42925 /* Variable already installed */
42926 nIdx = SX_PTR_TO_INT(pEntry->pUserData);
42927 /* Extract contents */
42928 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
42929 if( pObj ){
42930 /* Overwrite old contents */
42931 jx9MemObjStore(pValue, pObj);
42932 }
42933 }else{
42934 /* Install a new variable */
42935 pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
42936 if( pObj == 0 ){
42937 rc = SXERR_MEM;
42938 break;
42939 }
42940 /* Copy value */
42941 jx9MemObjStore(pValue, pObj);
42942 /* Install the superglobal */
42943 rc = SyHashInsert(&pVm->hSuper, (const void *)zName, nByte, SX_INT_TO_PTR(nIdx));
42944 }
42945 break;
42946 }
42947 case JX9_VM_CONFIG_SERVER_ATTR:
42948 case JX9_VM_CONFIG_ENV_ATTR: {
42949 const char *zKey = va_arg(ap, const char *);
42950 const char *zValue = va_arg(ap, const char *);
42951 int nLen = va_arg(ap, int);
42952 jx9_hashmap *pMap;
42953 jx9_value *pValue;
42954 if( nOp == JX9_VM_CONFIG_ENV_ATTR ){
42955 /* Extract the $_ENV superglobal */
42956 pValue = VmExtractSuper(&(*pVm), "_ENV", sizeof("_ENV")-1);
42957 }else{
42958 /* Extract the $_SERVER superglobal */
42959 pValue = VmExtractSuper(&(*pVm), "_SERVER", sizeof("_SERVER")-1);
42960 }
42961 if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){
42962 /* No such entry */
42963 rc = SXERR_NOTFOUND;
42964 break;
42965 }
42966 /* Point to the hashmap */
42967 pMap = (jx9_hashmap *)pValue->x.pOther;
42968 /* Perform the insertion */
42969 rc = VmHashmapInsert(pMap, zKey, -1, zValue, nLen);
42970 break;
42971 }
42972 case JX9_VM_CONFIG_ARGV_ENTRY:{
42973 /* Script arguments */
42974 const char *zValue = va_arg(ap, const char *);
42975 jx9_hashmap *pMap;
42976 jx9_value *pValue;
42977 /* Extract the $argv array */
42978 pValue = VmExtractSuper(&(*pVm), "argv", sizeof("argv")-1);
42979 if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){
42980 /* No such entry */
42981 rc = SXERR_NOTFOUND;
42982 break;
42983 }
42984 /* Point to the hashmap */
42985 pMap = (jx9_hashmap *)pValue->x.pOther;
42986 /* Perform the insertion */
42987 rc = VmHashmapInsert(pMap, 0, 0, zValue,-1);
42988 if( rc == SXRET_OK && zValue && zValue[0] != 0 ){
42989 if( pMap->nEntry > 1 ){
42990 /* Append space separator first */
42991 SyBlobAppend(&pVm->sArgv, (const void *)" ", sizeof(char));
42992 }
42993 SyBlobAppend(&pVm->sArgv, (const void *)zValue,SyStrlen(zValue));
42994 }
42995 break;
42996 }
42997 case JX9_VM_CONFIG_EXEC_VALUE: {
42998 /* Script return value */
42999 jx9_value **ppValue = va_arg(ap, jx9_value **);
43000#ifdef UNTRUST
43001 if( ppValue == 0 ){
43002 rc = SXERR_CORRUPT;
43003 break;
43004 }
43005#endif
43006 *ppValue = &pVm->sExec;
43007 break;
43008 }
43009 case JX9_VM_CONFIG_IO_STREAM: {
43010 /* Register an IO stream device */
43011 const jx9_io_stream *pStream = va_arg(ap, const jx9_io_stream *);
43012 /* Make sure we are dealing with a valid IO stream */
43013 if( pStream == 0 || pStream->zName == 0 || pStream->zName[0] == 0 ||
43014 pStream->xOpen == 0 || pStream->xRead == 0 ){
43015 /* Invalid stream */
43016 rc = SXERR_INVALID;
43017 break;
43018 }
43019 if( pVm->pDefStream == 0 && SyStrnicmp(pStream->zName, "file", sizeof("file")-1) == 0 ){
43020 /* Make the 'file://' stream the defaut stream device */
43021 pVm->pDefStream = pStream;
43022 }
43023 /* Insert in the appropriate container */
43024 rc = SySetPut(&pVm->aIOstream, (const void *)&pStream);
43025 break;
43026 }
43027 case JX9_VM_CONFIG_EXTRACT_OUTPUT: {
43028 /* Point to the VM internal output consumer buffer */
43029 const void **ppOut = va_arg(ap, const void **);
43030 unsigned int *pLen = va_arg(ap, unsigned int *);
43031#ifdef UNTRUST
43032 if( ppOut == 0 || pLen == 0 ){
43033 rc = SXERR_CORRUPT;
43034 break;
43035 }
43036#endif
43037 *ppOut = SyBlobData(&pVm->sConsumer);
43038 *pLen = SyBlobLength(&pVm->sConsumer);
43039 break;
43040 }
43041 case JX9_VM_CONFIG_HTTP_REQUEST:{
43042 /* Raw HTTP request*/
43043 const char *zRequest = va_arg(ap, const char *);
43044 int nByte = va_arg(ap, int);
43045 if( SX_EMPTY_STR(zRequest) ){
43046 rc = SXERR_EMPTY;
43047 break;
43048 }
43049 if( nByte < 0 ){
43050 /* Compute length automatically */
43051 nByte = (int)SyStrlen(zRequest);
43052 }
43053 /* Process the request */
43054 rc = VmHttpProcessRequest(&(*pVm), zRequest, nByte);
43055 break;
43056 }
43057 default:
43058 /* Unknown configuration option */
43059 rc = SXERR_UNKNOWN;
43060 break;
43061 }
43062 return rc;
43063}
43064/* Forward declaration */
43065static const char * VmInstrToString(sxi32 nOp);
43066/*
43067 * This routine is used to dump JX9 bytecode instructions to a human readable
43068 * format.
43069 * The dump is redirected to the given consumer callback which is responsible
43070 * of consuming the generated dump perhaps redirecting it to its standard output
43071 * (STDOUT).
43072 */
43073static sxi32 VmByteCodeDump(
43074 SySet *pByteCode, /* Bytecode container */
43075 ProcConsumer xConsumer, /* Dump consumer callback */
43076 void *pUserData /* Last argument to xConsumer() */
43077 )
43078{
43079 static const char zDump[] = {
43080 "====================================================\n"
43081 "JX9 VM Dump Copyright (C) 2012-2013 Symisc Systems\n"
43082 " http://jx9.symisc.net/\n"
43083 "====================================================\n"
43084 };
43085 VmInstr *pInstr, *pEnd;
43086 sxi32 rc = SXRET_OK;
43087 sxu32 n;
43088 /* Point to the JX9 instructions */
43089 pInstr = (VmInstr *)SySetBasePtr(pByteCode);
43090 pEnd = &pInstr[SySetUsed(pByteCode)];
43091 n = 0;
43092 xConsumer((const void *)zDump, sizeof(zDump)-1, pUserData);
43093 /* Dump instructions */
43094 for(;;){
43095 if( pInstr >= pEnd ){
43096 /* No more instructions */
43097 break;
43098 }
43099 /* Format and call the consumer callback */
43100 rc = SyProcFormat(xConsumer, pUserData, "%s %8d %8u %#8x [%u]\n",
43101 VmInstrToString(pInstr->iOp), pInstr->iP1, pInstr->iP2,
43102 SX_PTR_TO_INT(pInstr->p3), n);
43103 if( rc != SXRET_OK ){
43104 /* Consumer routine request an operation abort */
43105 return rc;
43106 }
43107 ++n;
43108 pInstr++; /* Next instruction in the stream */
43109 }
43110 return rc;
43111}
43112/*
43113 * Consume a generated run-time error message by invoking the VM output
43114 * consumer callback.
43115 */
43116static sxi32 VmCallErrorHandler(jx9_vm *pVm, SyBlob *pMsg)
43117{
43118 jx9_output_consumer *pCons = &pVm->sVmConsumer;
43119 sxi32 rc = SXRET_OK;
43120 /* Append a new line */
43121#ifdef __WINNT__
43122 SyBlobAppend(pMsg, "\r\n", sizeof("\r\n")-1);
43123#else
43124 SyBlobAppend(pMsg, "\n", sizeof(char));
43125#endif
43126 /* Invoke the output consumer callback */
43127 rc = pCons->xConsumer(SyBlobData(pMsg), SyBlobLength(pMsg), pCons->pUserData);
43128 /* Increment output length */
43129 pVm->nOutputLen += SyBlobLength(pMsg);
43130
43131 return rc;
43132}
43133/*
43134 * Throw a run-time error and invoke the supplied VM output consumer callback.
43135 * Refer to the implementation of [jx9_context_throw_error()] for additional
43136 * information.
43137 */
43138JX9_PRIVATE sxi32 jx9VmThrowError(
43139 jx9_vm *pVm, /* Target VM */
43140 SyString *pFuncName, /* Function name. NULL otherwise */
43141 sxi32 iErr, /* Severity level: [i.e: Error, Warning or Notice]*/
43142 const char *zMessage /* Null terminated error message */
43143 )
43144{
43145 SyBlob *pWorker = &pVm->sWorker;
43146 SyString *pFile;
43147 char *zErr;
43148 sxi32 rc;
43149 if( !pVm->bErrReport ){
43150 /* Don't bother reporting errors */
43151 return SXRET_OK;
43152 }
43153 /* Reset the working buffer */
43154 SyBlobReset(pWorker);
43155 /* Peek the processed file if available */
43156 pFile = (SyString *)SySetPeek(&pVm->aFiles);
43157 if( pFile ){
43158 /* Append file name */
43159 SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
43160 SyBlobAppend(pWorker, (const void *)" ", sizeof(char));
43161 }
43162 zErr = "Error: ";
43163 switch(iErr){
43164 case JX9_CTX_WARNING: zErr = "Warning: "; break;
43165 case JX9_CTX_NOTICE: zErr = "Notice: "; break;
43166 default:
43167 iErr = JX9_CTX_ERR;
43168 break;
43169 }
43170 SyBlobAppend(pWorker, zErr, SyStrlen(zErr));
43171 if( pFuncName ){
43172 /* Append function name first */
43173 SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte);
43174 SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1);
43175 }
43176 SyBlobAppend(pWorker, zMessage, SyStrlen(zMessage));
43177 /* Consume the error message */
43178 rc = VmCallErrorHandler(&(*pVm), pWorker);
43179 return rc;
43180}
43181/*
43182 * Format and throw a run-time error and invoke the supplied VM output consumer callback.
43183 * Refer to the implementation of [jx9_context_throw_error_format()] for additional
43184 * information.
43185 */
43186static sxi32 VmThrowErrorAp(
43187 jx9_vm *pVm, /* Target VM */
43188 SyString *pFuncName, /* Function name. NULL otherwise */
43189 sxi32 iErr, /* Severity level: [i.e: Error, Warning or Notice] */
43190 const char *zFormat, /* Format message */
43191 va_list ap /* Variable list of arguments */
43192 )
43193{
43194 SyBlob *pWorker = &pVm->sWorker;
43195 SyString *pFile;
43196 char *zErr;
43197 sxi32 rc;
43198 if( !pVm->bErrReport ){
43199 /* Don't bother reporting errors */
43200 return SXRET_OK;
43201 }
43202 /* Reset the working buffer */
43203 SyBlobReset(pWorker);
43204 /* Peek the processed file if available */
43205 pFile = (SyString *)SySetPeek(&pVm->aFiles);
43206 if( pFile ){
43207 /* Append file name */
43208 SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
43209 SyBlobAppend(pWorker, (const void *)" ", sizeof(char));
43210 }
43211 zErr = "Error: ";
43212 switch(iErr){
43213 case JX9_CTX_WARNING: zErr = "Warning: "; break;
43214 case JX9_CTX_NOTICE: zErr = "Notice: "; break;
43215 default:
43216 iErr = JX9_CTX_ERR;
43217 break;
43218 }
43219 SyBlobAppend(pWorker, zErr, SyStrlen(zErr));
43220 if( pFuncName ){
43221 /* Append function name first */
43222 SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte);
43223 SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1);
43224 }
43225 SyBlobFormatAp(pWorker, zFormat, ap);
43226 /* Consume the error message */
43227 rc = VmCallErrorHandler(&(*pVm), pWorker);
43228 return rc;
43229}
43230/*
43231 * Format and throw a run-time error and invoke the supplied VM output consumer callback.
43232 * Refer to the implementation of [jx9_context_throw_error_format()] for additional
43233 * information.
43234 * ------------------------------------
43235 * Simple boring wrapper function.
43236 * ------------------------------------
43237 */
43238static sxi32 VmErrorFormat(jx9_vm *pVm, sxi32 iErr, const char *zFormat, ...)
43239{
43240 va_list ap;
43241 sxi32 rc;
43242 va_start(ap, zFormat);
43243 rc = VmThrowErrorAp(&(*pVm), 0, iErr, zFormat, ap);
43244 va_end(ap);
43245 return rc;
43246}
43247/*
43248 * Format and throw a run-time error and invoke the supplied VM output consumer callback.
43249 * Refer to the implementation of [jx9_context_throw_error_format()] for additional
43250 * information.
43251 * ------------------------------------
43252 * Simple boring wrapper function.
43253 * ------------------------------------
43254 */
43255JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap)
43256{
43257 sxi32 rc;
43258 rc = VmThrowErrorAp(&(*pVm), &(*pFuncName), iErr, zFormat, ap);
43259 return rc;
43260}
43261/* Forward declaration */
43262static sxi32 VmLocalExec(jx9_vm *pVm,SySet *pByteCode,jx9_value *pResult);
43263/*
43264 * Execute as much of a JX9 bytecode program as we can then return.
43265 *
43266 * [jx9VmMakeReady()] must be called before this routine in order to
43267 * close the program with a final OP_DONE and to set up the default
43268 * consumer routines and other stuff. Refer to the implementation
43269 * of [jx9VmMakeReady()] for additional information.
43270 * If the installed VM output consumer callback ever returns JX9_ABORT
43271 * then the program execution is halted.
43272 * After this routine has finished, [jx9VmRelease()] or [jx9VmReset()]
43273 * should be used respectively to clean up the mess that was left behind
43274 * or to reset the VM to it's initial state.
43275 */
43276static sxi32 VmByteCodeExec(
43277 jx9_vm *pVm, /* Target VM */
43278 VmInstr *aInstr, /* JX9 bytecode program */
43279 jx9_value *pStack, /* Operand stack */
43280 int nTos, /* Top entry in the operand stack (usually -1) */
43281 jx9_value *pResult /* Store program return value here. NULL otherwise */
43282 )
43283{
43284 VmInstr *pInstr;
43285 jx9_value *pTos;
43286 SySet aArg;
43287 sxi32 pc;
43288 sxi32 rc;
43289 /* Argument container */
43290 SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
43291 if( nTos < 0 ){
43292 pTos = &pStack[-1];
43293 }else{
43294 pTos = &pStack[nTos];
43295 }
43296 pc = 0;
43297 /* Execute as much as we can */
43298 for(;;){
43299 /* Fetch the instruction to execute */
43300 pInstr = &aInstr[pc];
43301 rc = SXRET_OK;
43302/*
43303 * What follows here is a massive switch statement where each case implements a
43304 * separate instruction in the virtual machine. If we follow the usual
43305 * indentation convention each case should be indented by 6 spaces. But
43306 * that is a lot of wasted space on the left margin. So the code within
43307 * the switch statement will break with convention and be flush-left.
43308 */
43309 switch(pInstr->iOp){
43310/*
43311 * DONE: P1 * *
43312 *
43313 * Program execution completed: Clean up the mess left behind
43314 * and return immediately.
43315 */
43316case JX9_OP_DONE:
43317 if( pInstr->iP1 ){
43318#ifdef UNTRUST
43319 if( pTos < pStack ){
43320 goto Abort;
43321 }
43322#endif
43323 if( pResult ){
43324 /* Execution result */
43325 jx9MemObjStore(pTos, pResult);
43326 }
43327 VmPopOperand(&pTos, 1);
43328 }
43329 goto Done;
43330/*
43331 * HALT: P1 * *
43332 *
43333 * Program execution aborted: Clean up the mess left behind
43334 * and abort immediately.
43335 */
43336case JX9_OP_HALT:
43337 if( pInstr->iP1 ){
43338#ifdef UNTRUST
43339 if( pTos < pStack ){
43340 goto Abort;
43341 }
43342#endif
43343 if( pTos->iFlags & MEMOBJ_STRING ){
43344 if( SyBlobLength(&pTos->sBlob) > 0 ){
43345 /* Output the exit message */
43346 pVm->sVmConsumer.xConsumer(SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob),
43347 pVm->sVmConsumer.pUserData);
43348 /* Increment output length */
43349 pVm->nOutputLen += SyBlobLength(&pTos->sBlob);
43350 }
43351 }else if(pTos->iFlags & MEMOBJ_INT ){
43352 /* Record exit status */
43353 pVm->iExitStatus = (sxi32)pTos->x.iVal;
43354 }
43355 VmPopOperand(&pTos, 1);
43356 }
43357 goto Abort;
43358/*
43359 * JMP: * P2 *
43360 *
43361 * Unconditional jump: The next instruction executed will be
43362 * the one at index P2 from the beginning of the program.
43363 */
43364case JX9_OP_JMP:
43365 pc = pInstr->iP2 - 1;
43366 break;
43367/*
43368 * JZ: P1 P2 *
43369 *
43370 * Take the jump if the top value is zero (FALSE jump).Pop the top most
43371 * entry in the stack if P1 is zero.
43372 */
43373case JX9_OP_JZ:
43374#ifdef UNTRUST
43375 if( pTos < pStack ){
43376 goto Abort;
43377 }
43378#endif
43379 /* Get a boolean value */
43380 if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
43381 jx9MemObjToBool(pTos);
43382 }
43383 if( !pTos->x.iVal ){
43384 /* Take the jump */
43385 pc = pInstr->iP2 - 1;
43386 }
43387 if( !pInstr->iP1 ){
43388 VmPopOperand(&pTos, 1);
43389 }
43390 break;
43391/*
43392 * JNZ: P1 P2 *
43393 *
43394 * Take the jump if the top value is not zero (TRUE jump).Pop the top most
43395 * entry in the stack if P1 is zero.
43396 */
43397case JX9_OP_JNZ:
43398#ifdef UNTRUST
43399 if( pTos < pStack ){
43400 goto Abort;
43401 }
43402#endif
43403 /* Get a boolean value */
43404 if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
43405 jx9MemObjToBool(pTos);
43406 }
43407 if( pTos->x.iVal ){
43408 /* Take the jump */
43409 pc = pInstr->iP2 - 1;
43410 }
43411 if( !pInstr->iP1 ){
43412 VmPopOperand(&pTos, 1);
43413 }
43414 break;
43415/*
43416 * NOOP: * * *
43417 *
43418 * Do nothing. This instruction is often useful as a jump
43419 * destination.
43420 */
43421case JX9_OP_NOOP:
43422 break;
43423/*
43424 * POP: P1 * *
43425 *
43426 * Pop P1 elements from the operand stack.
43427 */
43428case JX9_OP_POP: {
43429 sxi32 n = pInstr->iP1;
43430 if( &pTos[-n+1] < pStack ){
43431 /* TICKET 1433-51 Stack underflow must be handled at run-time */
43432 n = (sxi32)(pTos - pStack);
43433 }
43434 VmPopOperand(&pTos, n);
43435 break;
43436 }
43437/*
43438 * CVT_INT: * * *
43439 *
43440 * Force the top of the stack to be an integer.
43441 */
43442case JX9_OP_CVT_INT:
43443#ifdef UNTRUST
43444 if( pTos < pStack ){
43445 goto Abort;
43446 }
43447#endif
43448 if((pTos->iFlags & MEMOBJ_INT) == 0 ){
43449 jx9MemObjToInteger(pTos);
43450 }
43451 /* Invalidate any prior representation */
43452 MemObjSetType(pTos, MEMOBJ_INT);
43453 break;
43454/*
43455 * CVT_REAL: * * *
43456 *
43457 * Force the top of the stack to be a real.
43458 */
43459case JX9_OP_CVT_REAL:
43460#ifdef UNTRUST
43461 if( pTos < pStack ){
43462 goto Abort;
43463 }
43464#endif
43465 if((pTos->iFlags & MEMOBJ_REAL) == 0 ){
43466 jx9MemObjToReal(pTos);
43467 }
43468 /* Invalidate any prior representation */
43469 MemObjSetType(pTos, MEMOBJ_REAL);
43470 break;
43471/*
43472 * CVT_STR: * * *
43473 *
43474 * Force the top of the stack to be a string.
43475 */
43476case JX9_OP_CVT_STR:
43477#ifdef UNTRUST
43478 if( pTos < pStack ){
43479 goto Abort;
43480 }
43481#endif
43482 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
43483 jx9MemObjToString(pTos);
43484 }
43485 break;
43486/*
43487 * CVT_BOOL: * * *
43488 *
43489 * Force the top of the stack to be a boolean.
43490 */
43491case JX9_OP_CVT_BOOL:
43492#ifdef UNTRUST
43493 if( pTos < pStack ){
43494 goto Abort;
43495 }
43496#endif
43497 if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
43498 jx9MemObjToBool(pTos);
43499 }
43500 break;
43501/*
43502 * CVT_NULL: * * *
43503 *
43504 * Nullify the top of the stack.
43505 */
43506case JX9_OP_CVT_NULL:
43507#ifdef UNTRUST
43508 if( pTos < pStack ){
43509 goto Abort;
43510 }
43511#endif
43512 jx9MemObjRelease(pTos);
43513 break;
43514/*
43515 * CVT_NUMC: * * *
43516 *
43517 * Force the top of the stack to be a numeric type (integer, real or both).
43518 */
43519case JX9_OP_CVT_NUMC:
43520#ifdef UNTRUST
43521 if( pTos < pStack ){
43522 goto Abort;
43523 }
43524#endif
43525 /* Force a numeric cast */
43526 jx9MemObjToNumeric(pTos);
43527 break;
43528/*
43529 * CVT_ARRAY: * * *
43530 *
43531 * Force the top of the stack to be a hashmap aka 'array'.
43532 */
43533case JX9_OP_CVT_ARRAY:
43534#ifdef UNTRUST
43535 if( pTos < pStack ){
43536 goto Abort;
43537 }
43538#endif
43539 /* Force a hashmap cast */
43540 rc = jx9MemObjToHashmap(pTos);
43541 if( rc != SXRET_OK ){
43542 /* Not so fatal, emit a simple warning */
43543 jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
43544 "JX9 engine is running out of memory while performing an array cast");
43545 }
43546 break;
43547/*
43548 * LOADC P1 P2 *
43549 *
43550 * Load a constant [i.e: JX9_EOL, JX9_OS, __TIME__, ...] indexed at P2 in the constant pool.
43551 * If P1 is set, then this constant is candidate for expansion via user installable callbacks.
43552 */
43553case JX9_OP_LOADC: {
43554 jx9_value *pObj;
43555 /* Reserve a room */
43556 pTos++;
43557 if( (pObj = (jx9_value *)SySetAt(&pVm->aLitObj, pInstr->iP2)) != 0 ){
43558 if( pInstr->iP1 == 1 && SyBlobLength(&pObj->sBlob) <= 64 ){
43559 SyHashEntry *pEntry;
43560 /* Candidate for expansion via user defined callbacks */
43561 pEntry = SyHashGet(&pVm->hConstant, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
43562 if( pEntry ){
43563 jx9_constant *pCons = (jx9_constant *)pEntry->pUserData;
43564 /* Set a NULL default value */
43565 MemObjSetType(pTos, MEMOBJ_NULL);
43566 SyBlobReset(&pTos->sBlob);
43567 /* Invoke the callback and deal with the expanded value */
43568 pCons->xExpand(pTos, pCons->pUserData);
43569 /* Mark as constant */
43570 pTos->nIdx = SXU32_HIGH;
43571 break;
43572 }
43573 }
43574 jx9MemObjLoad(pObj, pTos);
43575 }else{
43576 /* Set a NULL value */
43577 MemObjSetType(pTos, MEMOBJ_NULL);
43578 }
43579 /* Mark as constant */
43580 pTos->nIdx = SXU32_HIGH;
43581 break;
43582 }
43583/*
43584 * LOAD: P1 * P3
43585 *
43586 * Load a variable where it's name is taken from the top of the stack or
43587 * from the P3 operand.
43588 * If P1 is set, then perform a lookup only.In other words do not create
43589 * the variable if non existent and push the NULL constant instead.
43590 */
43591case JX9_OP_LOAD:{
43592 jx9_value *pObj;
43593 SyString sName;
43594 if( pInstr->p3 == 0 ){
43595 /* Take the variable name from the top of the stack */
43596#ifdef UNTRUST
43597 if( pTos < pStack ){
43598 goto Abort;
43599 }
43600#endif
43601 /* Force a string cast */
43602 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
43603 jx9MemObjToString(pTos);
43604 }
43605 SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
43606 }else{
43607 SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
43608 /* Reserve a room for the target object */
43609 pTos++;
43610 }
43611 /* Extract the requested memory object */
43612 pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, pInstr->iP1 != 1);
43613 if( pObj == 0 ){
43614 if( pInstr->iP1 ){
43615 /* Variable not found, load NULL */
43616 if( !pInstr->p3 ){
43617 jx9MemObjRelease(pTos);
43618 }else{
43619 MemObjSetType(pTos, MEMOBJ_NULL);
43620 }
43621 pTos->nIdx = SXU32_HIGH; /* Mark as constant */
43622 break;
43623 }else{
43624 /* Fatal error */
43625 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
43626 goto Abort;
43627 }
43628 }
43629 /* Load variable contents */
43630 jx9MemObjLoad(pObj, pTos);
43631 pTos->nIdx = pObj->nIdx;
43632 break;
43633 }
43634/*
43635 * LOAD_MAP P1 * *
43636 *
43637 * Allocate a new empty hashmap (array in the JX9 jargon) and push it on the stack.
43638 * If the P1 operand is greater than zero then pop P1 elements from the
43639 * stack and insert them (key => value pair) in the new hashmap.
43640 */
43641case JX9_OP_LOAD_MAP: {
43642 jx9_hashmap *pMap;
43643 int is_json_object; /* TRUE if we are dealing with a JSON object */
43644 int iIncr = 1;
43645 /* Allocate a new hashmap instance */
43646 pMap = jx9NewHashmap(&(*pVm), 0, 0);
43647 if( pMap == 0 ){
43648 VmErrorFormat(&(*pVm), JX9_CTX_ERR,
43649 "Fatal, JX9 engine is running out of memory while loading JSON array/object at instruction #:%d", pc);
43650 goto Abort;
43651 }
43652 is_json_object = 0;
43653 if( pInstr->iP2 ){
43654 /* JSON object, record that */
43655 pMap->iFlags |= HASHMAP_JSON_OBJECT;
43656 is_json_object = 1;
43657 iIncr = 2;
43658 }
43659 if( pInstr->iP1 > 0 ){
43660 jx9_value *pEntry = &pTos[-pInstr->iP1+1]; /* Point to the first entry */
43661 /* Perform the insertion */
43662 while( pEntry <= pTos ){
43663 /* Standard insertion */
43664 jx9HashmapInsert(pMap,
43665 is_json_object ? pEntry : 0 /* Automatic index assign */,
43666 is_json_object ? &pEntry[1] : pEntry
43667 );
43668 /* Next pair on the stack */
43669 pEntry += iIncr;
43670 }
43671 /* Pop P1 elements */
43672 VmPopOperand(&pTos, pInstr->iP1);
43673 }
43674 /* Push the hashmap */
43675 pTos++;
43676 pTos->x.pOther = pMap;
43677 MemObjSetType(pTos, MEMOBJ_HASHMAP);
43678 break;
43679 }
43680/*
43681 * LOAD_IDX: P1 P2 *
43682 *
43683 * Load a hasmap entry where it's index (either numeric or string) is taken
43684 * from the stack.
43685 * If the index does not refer to a valid element, then push the NULL constant
43686 * instead.
43687 */
43688case JX9_OP_LOAD_IDX: {
43689 jx9_hashmap_node *pNode = 0; /* cc warning */
43690 jx9_hashmap *pMap = 0;
43691 jx9_value *pIdx;
43692 pIdx = 0;
43693 if( pInstr->iP1 == 0 ){
43694 if( !pInstr->iP2){
43695 /* No available index, load NULL */
43696 if( pTos >= pStack ){
43697 jx9MemObjRelease(pTos);
43698 }else{
43699 /* TICKET 1433-020: Empty stack */
43700 pTos++;
43701 MemObjSetType(pTos, MEMOBJ_NULL);
43702 pTos->nIdx = SXU32_HIGH;
43703 }
43704 /* Emit a notice */
43705 jx9VmThrowError(&(*pVm), 0, JX9_CTX_NOTICE,
43706 "JSON Array/Object: Attempt to access an undefined member, JX9 is loading NULL");
43707 break;
43708 }
43709 }else{
43710 pIdx = pTos;
43711 pTos--;
43712 }
43713 if( pTos->iFlags & MEMOBJ_STRING ){
43714 /* String access */
43715 if( pIdx ){
43716 sxu32 nOfft;
43717 if( (pIdx->iFlags & MEMOBJ_INT) == 0 ){
43718 /* Force an int cast */
43719 jx9MemObjToInteger(pIdx);
43720 }
43721 nOfft = (sxu32)pIdx->x.iVal;
43722 if( nOfft >= SyBlobLength(&pTos->sBlob) ){
43723 /* Invalid offset, load null */
43724 jx9MemObjRelease(pTos);
43725 }else{
43726 const char *zData = (const char *)SyBlobData(&pTos->sBlob);
43727 int c = zData[nOfft];
43728 jx9MemObjRelease(pTos);
43729 MemObjSetType(pTos, MEMOBJ_STRING);
43730 SyBlobAppend(&pTos->sBlob, (const void *)&c, sizeof(char));
43731 }
43732 }else{
43733 /* No available index, load NULL */
43734 MemObjSetType(pTos, MEMOBJ_NULL);
43735 }
43736 break;
43737 }
43738 if( pInstr->iP2 && (pTos->iFlags & MEMOBJ_HASHMAP) == 0 ){
43739 if( pTos->nIdx != SXU32_HIGH ){
43740 jx9_value *pObj;
43741 if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
43742 jx9MemObjToHashmap(pObj);
43743 jx9MemObjLoad(pObj, pTos);
43744 }
43745 }
43746 }
43747 rc = SXERR_NOTFOUND; /* Assume the index is invalid */
43748 if( pTos->iFlags & MEMOBJ_HASHMAP ){
43749 /* Point to the hashmap */
43750 pMap = (jx9_hashmap *)pTos->x.pOther;
43751 if( pIdx ){
43752 /* Load the desired entry */
43753 rc = jx9HashmapLookup(pMap, pIdx, &pNode);
43754 }
43755 if( rc != SXRET_OK && pInstr->iP2 ){
43756 /* Create a new empty entry */
43757 rc = jx9HashmapInsert(pMap, pIdx, 0);
43758 if( rc == SXRET_OK ){
43759 /* Point to the last inserted entry */
43760 pNode = pMap->pLast;
43761 }
43762 }
43763 }
43764 if( pIdx ){
43765 jx9MemObjRelease(pIdx);
43766 }
43767 if( rc == SXRET_OK ){
43768 /* Load entry contents */
43769 if( pMap->iRef < 2 ){
43770 /* TICKET 1433-42: Array will be deleted shortly, so we will make a copy
43771 * of the entry value, rather than pointing to it.
43772 */
43773 pTos->nIdx = SXU32_HIGH;
43774 jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
43775 }else{
43776 pTos->nIdx = pNode->nValIdx;
43777 jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
43778 jx9HashmapUnref(pMap);
43779 }
43780 }else{
43781 /* No such entry, load NULL */
43782 jx9MemObjRelease(pTos);
43783 pTos->nIdx = SXU32_HIGH;
43784 }
43785 break;
43786 }
43787/*
43788 * STORE * P2 P3
43789 *
43790 * Perform a store (Assignment) operation.
43791 */
43792case JX9_OP_STORE: {
43793 jx9_value *pObj;
43794 SyString sName;
43795#ifdef UNTRUST
43796 if( pTos < pStack ){
43797 goto Abort;
43798 }
43799#endif
43800 if( pInstr->iP2 ){
43801 sxu32 nIdx;
43802 /* Member store operation */
43803 nIdx = pTos->nIdx;
43804 VmPopOperand(&pTos, 1);
43805 if( nIdx == SXU32_HIGH ){
43806 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR,
43807 "Cannot perform assignment on a constant object attribute, JX9 is loading NULL");
43808 pTos->nIdx = SXU32_HIGH;
43809 }else{
43810 /* Point to the desired memory object */
43811 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
43812 if( pObj ){
43813 /* Perform the store operation */
43814 jx9MemObjStore(pTos, pObj);
43815 }
43816 }
43817 break;
43818 }else if( pInstr->p3 == 0 ){
43819 /* Take the variable name from the next on the stack */
43820 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
43821 /* Force a string cast */
43822 jx9MemObjToString(pTos);
43823 }
43824 SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
43825 pTos--;
43826#ifdef UNTRUST
43827 if( pTos < pStack ){
43828 goto Abort;
43829 }
43830#endif
43831 }else{
43832 SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
43833 }
43834 /* Extract the desired variable and if not available dynamically create it */
43835 pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, TRUE);
43836 if( pObj == 0 ){
43837 VmErrorFormat(&(*pVm), JX9_CTX_ERR,
43838 "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
43839 goto Abort;
43840 }
43841 if( !pInstr->p3 ){
43842 jx9MemObjRelease(&pTos[1]);
43843 }
43844 /* Perform the store operation */
43845 jx9MemObjStore(pTos, pObj);
43846 break;
43847 }
43848/*
43849 * STORE_IDX: P1 * P3
43850 *
43851 * Perfrom a store operation an a hashmap entry.
43852 */
43853case JX9_OP_STORE_IDX: {
43854 jx9_hashmap *pMap = 0; /* cc warning */
43855 jx9_value *pKey;
43856 sxu32 nIdx;
43857 if( pInstr->iP1 ){
43858 /* Key is next on stack */
43859 pKey = pTos;
43860 pTos--;
43861 }else{
43862 pKey = 0;
43863 }
43864 nIdx = pTos->nIdx;
43865 if( pTos->iFlags & MEMOBJ_HASHMAP ){
43866 /* Hashmap already loaded */
43867 pMap = (jx9_hashmap *)pTos->x.pOther;
43868 if( pMap->iRef < 2 ){
43869 /* TICKET 1433-48: Prevent garbage collection */
43870 pMap->iRef = 2;
43871 }
43872 }else{
43873 jx9_value *pObj;
43874 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
43875 if( pObj == 0 ){
43876 if( pKey ){
43877 jx9MemObjRelease(pKey);
43878 }
43879 VmPopOperand(&pTos, 1);
43880 break;
43881 }
43882 /* Phase#1: Load the array */
43883 if( (pObj->iFlags & MEMOBJ_STRING) ){
43884 VmPopOperand(&pTos, 1);
43885 if( (pTos->iFlags&MEMOBJ_STRING) == 0 ){
43886 /* Force a string cast */
43887 jx9MemObjToString(pTos);
43888 }
43889 if( pKey == 0 ){
43890 /* Append string */
43891 if( SyBlobLength(&pTos->sBlob) > 0 ){
43892 SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
43893 }
43894 }else{
43895 sxu32 nOfft;
43896 if((pKey->iFlags & MEMOBJ_INT)){
43897 /* Force an int cast */
43898 jx9MemObjToInteger(pKey);
43899 }
43900 nOfft = (sxu32)pKey->x.iVal;
43901 if( nOfft < SyBlobLength(&pObj->sBlob) && SyBlobLength(&pTos->sBlob) > 0 ){
43902 const char *zBlob = (const char *)SyBlobData(&pTos->sBlob);
43903 char *zData = (char *)SyBlobData(&pObj->sBlob);
43904 zData[nOfft] = zBlob[0];
43905 }else{
43906 if( SyBlobLength(&pTos->sBlob) >= sizeof(char) ){
43907 /* Perform an append operation */
43908 SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), sizeof(char));
43909 }
43910 }
43911 }
43912 if( pKey ){
43913 jx9MemObjRelease(pKey);
43914 }
43915 break;
43916 }else if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
43917 /* Force a hashmap cast */
43918 rc = jx9MemObjToHashmap(pObj);
43919 if( rc != SXRET_OK ){
43920 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while creating a new array");
43921 goto Abort;
43922 }
43923 }
43924 pMap = (jx9_hashmap *)pObj->x.pOther;
43925 }
43926 VmPopOperand(&pTos, 1);
43927 /* Phase#2: Perform the insertion */
43928 jx9HashmapInsert(pMap, pKey, pTos);
43929 if( pKey ){
43930 jx9MemObjRelease(pKey);
43931 }
43932 break;
43933 }
43934/*
43935 * INCR: P1 * *
43936 *
43937 * Force a numeric cast and increment the top of the stack by 1.
43938 * If the P1 operand is set then perform a duplication of the top of
43939 * the stack and increment after that.
43940 */
43941case JX9_OP_INCR:
43942#ifdef UNTRUST
43943 if( pTos < pStack ){
43944 goto Abort;
43945 }
43946#endif
43947 if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){
43948 if( pTos->nIdx != SXU32_HIGH ){
43949 jx9_value *pObj;
43950 if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
43951 /* Force a numeric cast */
43952 jx9MemObjToNumeric(pObj);
43953 if( pObj->iFlags & MEMOBJ_REAL ){
43954 pObj->x.rVal++;
43955 /* Try to get an integer representation */
43956 jx9MemObjTryInteger(pTos);
43957 }else{
43958 pObj->x.iVal++;
43959 MemObjSetType(pTos, MEMOBJ_INT);
43960 }
43961 if( pInstr->iP1 ){
43962 /* Pre-icrement */
43963 jx9MemObjStore(pObj, pTos);
43964 }
43965 }
43966 }else{
43967 if( pInstr->iP1 ){
43968 /* Force a numeric cast */
43969 jx9MemObjToNumeric(pTos);
43970 /* Pre-increment */
43971 if( pTos->iFlags & MEMOBJ_REAL ){
43972 pTos->x.rVal++;
43973 /* Try to get an integer representation */
43974 jx9MemObjTryInteger(pTos);
43975 }else{
43976 pTos->x.iVal++;
43977 MemObjSetType(pTos, MEMOBJ_INT);
43978 }
43979 }
43980 }
43981 }
43982 break;
43983/*
43984 * DECR: P1 * *
43985 *
43986 * Force a numeric cast and decrement the top of the stack by 1.
43987 * If the P1 operand is set then perform a duplication of the top of the stack
43988 * and decrement after that.
43989 */
43990case JX9_OP_DECR:
43991#ifdef UNTRUST
43992 if( pTos < pStack ){
43993 goto Abort;
43994 }
43995#endif
43996 if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES|MEMOBJ_NULL)) == 0 ){
43997 /* Force a numeric cast */
43998 jx9MemObjToNumeric(pTos);
43999 if( pTos->nIdx != SXU32_HIGH ){
44000 jx9_value *pObj;
44001 if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44002 /* Force a numeric cast */
44003 jx9MemObjToNumeric(pObj);
44004 if( pObj->iFlags & MEMOBJ_REAL ){
44005 pObj->x.rVal--;
44006 /* Try to get an integer representation */
44007 jx9MemObjTryInteger(pTos);
44008 }else{
44009 pObj->x.iVal--;
44010 MemObjSetType(pTos, MEMOBJ_INT);
44011 }
44012 if( pInstr->iP1 ){
44013 /* Pre-icrement */
44014 jx9MemObjStore(pObj, pTos);
44015 }
44016 }
44017 }else{
44018 if( pInstr->iP1 ){
44019 /* Pre-increment */
44020 if( pTos->iFlags & MEMOBJ_REAL ){
44021 pTos->x.rVal--;
44022 /* Try to get an integer representation */
44023 jx9MemObjTryInteger(pTos);
44024 }else{
44025 pTos->x.iVal--;
44026 MemObjSetType(pTos, MEMOBJ_INT);
44027 }
44028 }
44029 }
44030 }
44031 break;
44032/*
44033 * UMINUS: * * *
44034 *
44035 * Perform a unary minus operation.
44036 */
44037case JX9_OP_UMINUS:
44038#ifdef UNTRUST
44039 if( pTos < pStack ){
44040 goto Abort;
44041 }
44042#endif
44043 /* Force a numeric (integer, real or both) cast */
44044 jx9MemObjToNumeric(pTos);
44045 if( pTos->iFlags & MEMOBJ_REAL ){
44046 pTos->x.rVal = -pTos->x.rVal;
44047 }
44048 if( pTos->iFlags & MEMOBJ_INT ){
44049 pTos->x.iVal = -pTos->x.iVal;
44050 }
44051 break;
44052/*
44053 * UPLUS: * * *
44054 *
44055 * Perform a unary plus operation.
44056 */
44057case JX9_OP_UPLUS:
44058#ifdef UNTRUST
44059 if( pTos < pStack ){
44060 goto Abort;
44061 }
44062#endif
44063 /* Force a numeric (integer, real or both) cast */
44064 jx9MemObjToNumeric(pTos);
44065 if( pTos->iFlags & MEMOBJ_REAL ){
44066 pTos->x.rVal = +pTos->x.rVal;
44067 }
44068 if( pTos->iFlags & MEMOBJ_INT ){
44069 pTos->x.iVal = +pTos->x.iVal;
44070 }
44071 break;
44072/*
44073 * OP_LNOT: * * *
44074 *
44075 * Interpret the top of the stack as a boolean value. Replace it
44076 * with its complement.
44077 */
44078case JX9_OP_LNOT:
44079#ifdef UNTRUST
44080 if( pTos < pStack ){
44081 goto Abort;
44082 }
44083#endif
44084 /* Force a boolean cast */
44085 if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
44086 jx9MemObjToBool(pTos);
44087 }
44088 pTos->x.iVal = !pTos->x.iVal;
44089 break;
44090/*
44091 * OP_BITNOT: * * *
44092 *
44093 * Interpret the top of the stack as an value.Replace it
44094 * with its ones-complement.
44095 */
44096case JX9_OP_BITNOT:
44097#ifdef UNTRUST
44098 if( pTos < pStack ){
44099 goto Abort;
44100 }
44101#endif
44102 /* Force an integer cast */
44103 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44104 jx9MemObjToInteger(pTos);
44105 }
44106 pTos->x.iVal = ~pTos->x.iVal;
44107 break;
44108/* OP_MUL * * *
44109 * OP_MUL_STORE * * *
44110 *
44111 * Pop the top two elements from the stack, multiply them together,
44112 * and push the result back onto the stack.
44113 */
44114case JX9_OP_MUL:
44115case JX9_OP_MUL_STORE: {
44116 jx9_value *pNos = &pTos[-1];
44117 /* Force the operand to be numeric */
44118#ifdef UNTRUST
44119 if( pNos < pStack ){
44120 goto Abort;
44121 }
44122#endif
44123 jx9MemObjToNumeric(pTos);
44124 jx9MemObjToNumeric(pNos);
44125 /* Perform the requested operation */
44126 if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
44127 /* Floating point arithemic */
44128 jx9_real a, b, r;
44129 if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
44130 jx9MemObjToReal(pTos);
44131 }
44132 if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
44133 jx9MemObjToReal(pNos);
44134 }
44135 a = pNos->x.rVal;
44136 b = pTos->x.rVal;
44137 r = a * b;
44138 /* Push the result */
44139 pNos->x.rVal = r;
44140 MemObjSetType(pNos, MEMOBJ_REAL);
44141 /* Try to get an integer representation */
44142 jx9MemObjTryInteger(pNos);
44143 }else{
44144 /* Integer arithmetic */
44145 sxi64 a, b, r;
44146 a = pNos->x.iVal;
44147 b = pTos->x.iVal;
44148 r = a * b;
44149 /* Push the result */
44150 pNos->x.iVal = r;
44151 MemObjSetType(pNos, MEMOBJ_INT);
44152 }
44153 if( pInstr->iOp == JX9_OP_MUL_STORE ){
44154 jx9_value *pObj;
44155 if( pTos->nIdx == SXU32_HIGH ){
44156 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44157 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44158 jx9MemObjStore(pNos, pObj);
44159 }
44160 }
44161 VmPopOperand(&pTos, 1);
44162 break;
44163 }
44164/* OP_ADD * * *
44165 *
44166 * Pop the top two elements from the stack, add them together,
44167 * and push the result back onto the stack.
44168 */
44169case JX9_OP_ADD:{
44170 jx9_value *pNos = &pTos[-1];
44171#ifdef UNTRUST
44172 if( pNos < pStack ){
44173 goto Abort;
44174 }
44175#endif
44176 /* Perform the addition */
44177 jx9MemObjAdd(pNos, pTos, FALSE);
44178 VmPopOperand(&pTos, 1);
44179 break;
44180 }
44181/*
44182 * OP_ADD_STORE * * *
44183 *
44184 * Pop the top two elements from the stack, add them together,
44185 * and push the result back onto the stack.
44186 */
44187case JX9_OP_ADD_STORE:{
44188 jx9_value *pNos = &pTos[-1];
44189 jx9_value *pObj;
44190 sxu32 nIdx;
44191#ifdef UNTRUST
44192 if( pNos < pStack ){
44193 goto Abort;
44194 }
44195#endif
44196 /* Perform the addition */
44197 nIdx = pTos->nIdx;
44198 jx9MemObjAdd(pTos, pNos, TRUE);
44199 /* Peform the store operation */
44200 if( nIdx == SXU32_HIGH ){
44201 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44202 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx)) != 0 ){
44203 jx9MemObjStore(pTos, pObj);
44204 }
44205 /* Ticket 1433-35: Perform a stack dup */
44206 jx9MemObjStore(pTos, pNos);
44207 VmPopOperand(&pTos, 1);
44208 break;
44209 }
44210/* OP_SUB * * *
44211 *
44212 * Pop the top two elements from the stack, subtract the
44213 * first (what was next on the stack) from the second (the
44214 * top of the stack) and push the result back onto the stack.
44215 */
44216case JX9_OP_SUB: {
44217 jx9_value *pNos = &pTos[-1];
44218#ifdef UNTRUST
44219 if( pNos < pStack ){
44220 goto Abort;
44221 }
44222#endif
44223 if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
44224 /* Floating point arithemic */
44225 jx9_real a, b, r;
44226 if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
44227 jx9MemObjToReal(pTos);
44228 }
44229 if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
44230 jx9MemObjToReal(pNos);
44231 }
44232 a = pNos->x.rVal;
44233 b = pTos->x.rVal;
44234 r = a - b;
44235 /* Push the result */
44236 pNos->x.rVal = r;
44237 MemObjSetType(pNos, MEMOBJ_REAL);
44238 /* Try to get an integer representation */
44239 jx9MemObjTryInteger(pNos);
44240 }else{
44241 /* Integer arithmetic */
44242 sxi64 a, b, r;
44243 a = pNos->x.iVal;
44244 b = pTos->x.iVal;
44245 r = a - b;
44246 /* Push the result */
44247 pNos->x.iVal = r;
44248 MemObjSetType(pNos, MEMOBJ_INT);
44249 }
44250 VmPopOperand(&pTos, 1);
44251 break;
44252 }
44253/* OP_SUB_STORE * * *
44254 *
44255 * Pop the top two elements from the stack, subtract the
44256 * first (what was next on the stack) from the second (the
44257 * top of the stack) and push the result back onto the stack.
44258 */
44259case JX9_OP_SUB_STORE: {
44260 jx9_value *pNos = &pTos[-1];
44261 jx9_value *pObj;
44262#ifdef UNTRUST
44263 if( pNos < pStack ){
44264 goto Abort;
44265 }
44266#endif
44267 if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
44268 /* Floating point arithemic */
44269 jx9_real a, b, r;
44270 if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
44271 jx9MemObjToReal(pTos);
44272 }
44273 if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
44274 jx9MemObjToReal(pNos);
44275 }
44276 a = pTos->x.rVal;
44277 b = pNos->x.rVal;
44278 r = a - b;
44279 /* Push the result */
44280 pNos->x.rVal = r;
44281 MemObjSetType(pNos, MEMOBJ_REAL);
44282 /* Try to get an integer representation */
44283 jx9MemObjTryInteger(pNos);
44284 }else{
44285 /* Integer arithmetic */
44286 sxi64 a, b, r;
44287 a = pTos->x.iVal;
44288 b = pNos->x.iVal;
44289 r = a - b;
44290 /* Push the result */
44291 pNos->x.iVal = r;
44292 MemObjSetType(pNos, MEMOBJ_INT);
44293 }
44294 if( pTos->nIdx == SXU32_HIGH ){
44295 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44296 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44297 jx9MemObjStore(pNos, pObj);
44298 }
44299 VmPopOperand(&pTos, 1);
44300 break;
44301 }
44302
44303/*
44304 * OP_MOD * * *
44305 *
44306 * Pop the top two elements from the stack, divide the
44307 * first (what was next on the stack) from the second (the
44308 * top of the stack) and push the remainder after division
44309 * onto the stack.
44310 * Note: Only integer arithemtic is allowed.
44311 */
44312case JX9_OP_MOD:{
44313 jx9_value *pNos = &pTos[-1];
44314 sxi64 a, b, r;
44315#ifdef UNTRUST
44316 if( pNos < pStack ){
44317 goto Abort;
44318 }
44319#endif
44320 /* Force the operands to be integer */
44321 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44322 jx9MemObjToInteger(pTos);
44323 }
44324 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44325 jx9MemObjToInteger(pNos);
44326 }
44327 /* Perform the requested operation */
44328 a = pNos->x.iVal;
44329 b = pTos->x.iVal;
44330 if( b == 0 ){
44331 r = 0;
44332 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a);
44333 /* goto Abort; */
44334 }else{
44335 r = a%b;
44336 }
44337 /* Push the result */
44338 pNos->x.iVal = r;
44339 MemObjSetType(pNos, MEMOBJ_INT);
44340 VmPopOperand(&pTos, 1);
44341 break;
44342 }
44343/*
44344 * OP_MOD_STORE * * *
44345 *
44346 * Pop the top two elements from the stack, divide the
44347 * first (what was next on the stack) from the second (the
44348 * top of the stack) and push the remainder after division
44349 * onto the stack.
44350 * Note: Only integer arithemtic is allowed.
44351 */
44352case JX9_OP_MOD_STORE: {
44353 jx9_value *pNos = &pTos[-1];
44354 jx9_value *pObj;
44355 sxi64 a, b, r;
44356#ifdef UNTRUST
44357 if( pNos < pStack ){
44358 goto Abort;
44359 }
44360#endif
44361 /* Force the operands to be integer */
44362 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44363 jx9MemObjToInteger(pTos);
44364 }
44365 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44366 jx9MemObjToInteger(pNos);
44367 }
44368 /* Perform the requested operation */
44369 a = pTos->x.iVal;
44370 b = pNos->x.iVal;
44371 if( b == 0 ){
44372 r = 0;
44373 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a);
44374 /* goto Abort; */
44375 }else{
44376 r = a%b;
44377 }
44378 /* Push the result */
44379 pNos->x.iVal = r;
44380 MemObjSetType(pNos, MEMOBJ_INT);
44381 if( pTos->nIdx == SXU32_HIGH ){
44382 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44383 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44384 jx9MemObjStore(pNos, pObj);
44385 }
44386 VmPopOperand(&pTos, 1);
44387 break;
44388 }
44389/*
44390 * OP_DIV * * *
44391 *
44392 * Pop the top two elements from the stack, divide the
44393 * first (what was next on the stack) from the second (the
44394 * top of the stack) and push the result onto the stack.
44395 * Note: Only floating point arithemtic is allowed.
44396 */
44397case JX9_OP_DIV:{
44398 jx9_value *pNos = &pTos[-1];
44399 jx9_real a, b, r;
44400#ifdef UNTRUST
44401 if( pNos < pStack ){
44402 goto Abort;
44403 }
44404#endif
44405 /* Force the operands to be real */
44406 if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
44407 jx9MemObjToReal(pTos);
44408 }
44409 if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
44410 jx9MemObjToReal(pNos);
44411 }
44412 /* Perform the requested operation */
44413 a = pNos->x.rVal;
44414 b = pTos->x.rVal;
44415 if( b == 0 ){
44416 /* Division by zero */
44417 r = 0;
44418 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Division by zero");
44419 /* goto Abort; */
44420 }else{
44421 r = a/b;
44422 /* Push the result */
44423 pNos->x.rVal = r;
44424 MemObjSetType(pNos, MEMOBJ_REAL);
44425 /* Try to get an integer representation */
44426 jx9MemObjTryInteger(pNos);
44427 }
44428 VmPopOperand(&pTos, 1);
44429 break;
44430 }
44431/*
44432 * OP_DIV_STORE * * *
44433 *
44434 * Pop the top two elements from the stack, divide the
44435 * first (what was next on the stack) from the second (the
44436 * top of the stack) and push the result onto the stack.
44437 * Note: Only floating point arithemtic is allowed.
44438 */
44439case JX9_OP_DIV_STORE:{
44440 jx9_value *pNos = &pTos[-1];
44441 jx9_value *pObj;
44442 jx9_real a, b, r;
44443#ifdef UNTRUST
44444 if( pNos < pStack ){
44445 goto Abort;
44446 }
44447#endif
44448 /* Force the operands to be real */
44449 if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
44450 jx9MemObjToReal(pTos);
44451 }
44452 if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
44453 jx9MemObjToReal(pNos);
44454 }
44455 /* Perform the requested operation */
44456 a = pTos->x.rVal;
44457 b = pNos->x.rVal;
44458 if( b == 0 ){
44459 /* Division by zero */
44460 r = 0;
44461 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd/0", a);
44462 /* goto Abort; */
44463 }else{
44464 r = a/b;
44465 /* Push the result */
44466 pNos->x.rVal = r;
44467 MemObjSetType(pNos, MEMOBJ_REAL);
44468 /* Try to get an integer representation */
44469 jx9MemObjTryInteger(pNos);
44470 }
44471 if( pTos->nIdx == SXU32_HIGH ){
44472 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44473 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44474 jx9MemObjStore(pNos, pObj);
44475 }
44476 VmPopOperand(&pTos, 1);
44477 break;
44478 }
44479/* OP_BAND * * *
44480 *
44481 * Pop the top two elements from the stack. Convert both elements
44482 * to integers. Push back onto the stack the bit-wise AND of the
44483 * two elements.
44484*/
44485/* OP_BOR * * *
44486 *
44487 * Pop the top two elements from the stack. Convert both elements
44488 * to integers. Push back onto the stack the bit-wise OR of the
44489 * two elements.
44490 */
44491/* OP_BXOR * * *
44492 *
44493 * Pop the top two elements from the stack. Convert both elements
44494 * to integers. Push back onto the stack the bit-wise XOR of the
44495 * two elements.
44496 */
44497case JX9_OP_BAND:
44498case JX9_OP_BOR:
44499case JX9_OP_BXOR:{
44500 jx9_value *pNos = &pTos[-1];
44501 sxi64 a, b, r;
44502#ifdef UNTRUST
44503 if( pNos < pStack ){
44504 goto Abort;
44505 }
44506#endif
44507 /* Force the operands to be integer */
44508 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44509 jx9MemObjToInteger(pTos);
44510 }
44511 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44512 jx9MemObjToInteger(pNos);
44513 }
44514 /* Perform the requested operation */
44515 a = pNos->x.iVal;
44516 b = pTos->x.iVal;
44517 switch(pInstr->iOp){
44518 case JX9_OP_BOR_STORE:
44519 case JX9_OP_BOR: r = a|b; break;
44520 case JX9_OP_BXOR_STORE:
44521 case JX9_OP_BXOR: r = a^b; break;
44522 case JX9_OP_BAND_STORE:
44523 case JX9_OP_BAND:
44524 default: r = a&b; break;
44525 }
44526 /* Push the result */
44527 pNos->x.iVal = r;
44528 MemObjSetType(pNos, MEMOBJ_INT);
44529 VmPopOperand(&pTos, 1);
44530 break;
44531 }
44532/* OP_BAND_STORE * * *
44533 *
44534 * Pop the top two elements from the stack. Convert both elements
44535 * to integers. Push back onto the stack the bit-wise AND of the
44536 * two elements.
44537*/
44538/* OP_BOR_STORE * * *
44539 *
44540 * Pop the top two elements from the stack. Convert both elements
44541 * to integers. Push back onto the stack the bit-wise OR of the
44542 * two elements.
44543 */
44544/* OP_BXOR_STORE * * *
44545 *
44546 * Pop the top two elements from the stack. Convert both elements
44547 * to integers. Push back onto the stack the bit-wise XOR of the
44548 * two elements.
44549 */
44550case JX9_OP_BAND_STORE:
44551case JX9_OP_BOR_STORE:
44552case JX9_OP_BXOR_STORE:{
44553 jx9_value *pNos = &pTos[-1];
44554 jx9_value *pObj;
44555 sxi64 a, b, r;
44556#ifdef UNTRUST
44557 if( pNos < pStack ){
44558 goto Abort;
44559 }
44560#endif
44561 /* Force the operands to be integer */
44562 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44563 jx9MemObjToInteger(pTos);
44564 }
44565 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44566 jx9MemObjToInteger(pNos);
44567 }
44568 /* Perform the requested operation */
44569 a = pTos->x.iVal;
44570 b = pNos->x.iVal;
44571 switch(pInstr->iOp){
44572 case JX9_OP_BOR_STORE:
44573 case JX9_OP_BOR: r = a|b; break;
44574 case JX9_OP_BXOR_STORE:
44575 case JX9_OP_BXOR: r = a^b; break;
44576 case JX9_OP_BAND_STORE:
44577 case JX9_OP_BAND:
44578 default: r = a&b; break;
44579 }
44580 /* Push the result */
44581 pNos->x.iVal = r;
44582 MemObjSetType(pNos, MEMOBJ_INT);
44583 if( pTos->nIdx == SXU32_HIGH ){
44584 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44585 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44586 jx9MemObjStore(pNos, pObj);
44587 }
44588 VmPopOperand(&pTos, 1);
44589 break;
44590 }
44591/* OP_SHL * * *
44592 *
44593 * Pop the top two elements from the stack. Convert both elements
44594 * to integers. Push back onto the stack the second element shifted
44595 * left by N bits where N is the top element on the stack.
44596 * Note: Only integer arithmetic is allowed.
44597 */
44598/* OP_SHR * * *
44599 *
44600 * Pop the top two elements from the stack. Convert both elements
44601 * to integers. Push back onto the stack the second element shifted
44602 * right by N bits where N is the top element on the stack.
44603 * Note: Only integer arithmetic is allowed.
44604 */
44605case JX9_OP_SHL:
44606case JX9_OP_SHR: {
44607 jx9_value *pNos = &pTos[-1];
44608 sxi64 a, r;
44609 sxi32 b;
44610#ifdef UNTRUST
44611 if( pNos < pStack ){
44612 goto Abort;
44613 }
44614#endif
44615 /* Force the operands to be integer */
44616 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44617 jx9MemObjToInteger(pTos);
44618 }
44619 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44620 jx9MemObjToInteger(pNos);
44621 }
44622 /* Perform the requested operation */
44623 a = pNos->x.iVal;
44624 b = (sxi32)pTos->x.iVal;
44625 if( pInstr->iOp == JX9_OP_SHL ){
44626 r = a << b;
44627 }else{
44628 r = a >> b;
44629 }
44630 /* Push the result */
44631 pNos->x.iVal = r;
44632 MemObjSetType(pNos, MEMOBJ_INT);
44633 VmPopOperand(&pTos, 1);
44634 break;
44635 }
44636/* OP_SHL_STORE * * *
44637 *
44638 * Pop the top two elements from the stack. Convert both elements
44639 * to integers. Push back onto the stack the second element shifted
44640 * left by N bits where N is the top element on the stack.
44641 * Note: Only integer arithmetic is allowed.
44642 */
44643/* OP_SHR_STORE * * *
44644 *
44645 * Pop the top two elements from the stack. Convert both elements
44646 * to integers. Push back onto the stack the second element shifted
44647 * right by N bits where N is the top element on the stack.
44648 * Note: Only integer arithmetic is allowed.
44649 */
44650case JX9_OP_SHL_STORE:
44651case JX9_OP_SHR_STORE: {
44652 jx9_value *pNos = &pTos[-1];
44653 jx9_value *pObj;
44654 sxi64 a, r;
44655 sxi32 b;
44656#ifdef UNTRUST
44657 if( pNos < pStack ){
44658 goto Abort;
44659 }
44660#endif
44661 /* Force the operands to be integer */
44662 if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44663 jx9MemObjToInteger(pTos);
44664 }
44665 if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44666 jx9MemObjToInteger(pNos);
44667 }
44668 /* Perform the requested operation */
44669 a = pTos->x.iVal;
44670 b = (sxi32)pNos->x.iVal;
44671 if( pInstr->iOp == JX9_OP_SHL_STORE ){
44672 r = a << b;
44673 }else{
44674 r = a >> b;
44675 }
44676 /* Push the result */
44677 pNos->x.iVal = r;
44678 MemObjSetType(pNos, MEMOBJ_INT);
44679 if( pTos->nIdx == SXU32_HIGH ){
44680 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44681 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44682 jx9MemObjStore(pNos, pObj);
44683 }
44684 VmPopOperand(&pTos, 1);
44685 break;
44686 }
44687/* CAT: P1 * *
44688 *
44689 * Pop P1 elements from the stack. Concatenate them togeher and push the result
44690 * back.
44691 */
44692case JX9_OP_CAT:{
44693 jx9_value *pNos, *pCur;
44694 if( pInstr->iP1 < 1 ){
44695 pNos = &pTos[-1];
44696 }else{
44697 pNos = &pTos[-pInstr->iP1+1];
44698 }
44699#ifdef UNTRUST
44700 if( pNos < pStack ){
44701 goto Abort;
44702 }
44703#endif
44704 /* Force a string cast */
44705 if( (pNos->iFlags & MEMOBJ_STRING) == 0 ){
44706 jx9MemObjToString(pNos);
44707 }
44708 pCur = &pNos[1];
44709 while( pCur <= pTos ){
44710 if( (pCur->iFlags & MEMOBJ_STRING) == 0 ){
44711 jx9MemObjToString(pCur);
44712 }
44713 /* Perform the concatenation */
44714 if( SyBlobLength(&pCur->sBlob) > 0 ){
44715 jx9MemObjStringAppend(pNos, (const char *)SyBlobData(&pCur->sBlob), SyBlobLength(&pCur->sBlob));
44716 }
44717 SyBlobRelease(&pCur->sBlob);
44718 pCur++;
44719 }
44720 pTos = pNos;
44721 break;
44722 }
44723/* CAT_STORE: * * *
44724 *
44725 * Pop two elements from the stack. Concatenate them togeher and push the result
44726 * back.
44727 */
44728case JX9_OP_CAT_STORE:{
44729 jx9_value *pNos = &pTos[-1];
44730 jx9_value *pObj;
44731#ifdef UNTRUST
44732 if( pNos < pStack ){
44733 goto Abort;
44734 }
44735#endif
44736 if((pTos->iFlags & MEMOBJ_STRING) == 0 ){
44737 /* Force a string cast */
44738 jx9MemObjToString(pTos);
44739 }
44740 if((pNos->iFlags & MEMOBJ_STRING) == 0 ){
44741 /* Force a string cast */
44742 jx9MemObjToString(pNos);
44743 }
44744 /* Perform the concatenation (Reverse order) */
44745 if( SyBlobLength(&pNos->sBlob) > 0 ){
44746 jx9MemObjStringAppend(pTos, (const char *)SyBlobData(&pNos->sBlob), SyBlobLength(&pNos->sBlob));
44747 }
44748 /* Perform the store operation */
44749 if( pTos->nIdx == SXU32_HIGH ){
44750 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44751 }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44752 jx9MemObjStore(pTos, pObj);
44753 }
44754 jx9MemObjStore(pTos, pNos);
44755 VmPopOperand(&pTos, 1);
44756 break;
44757 }
44758/* OP_AND: * * *
44759 *
44760 * Pop two values off the stack. Take the logical AND of the
44761 * two values and push the resulting boolean value back onto the
44762 * stack.
44763 */
44764/* OP_OR: * * *
44765 *
44766 * Pop two values off the stack. Take the logical OR of the
44767 * two values and push the resulting boolean value back onto the
44768 * stack.
44769 */
44770case JX9_OP_LAND:
44771case JX9_OP_LOR: {
44772 jx9_value *pNos = &pTos[-1];
44773 sxi32 v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */
44774#ifdef UNTRUST
44775 if( pNos < pStack ){
44776 goto Abort;
44777 }
44778#endif
44779 /* Force a boolean cast */
44780 if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
44781 jx9MemObjToBool(pTos);
44782 }
44783 if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
44784 jx9MemObjToBool(pNos);
44785 }
44786 v1 = pNos->x.iVal == 0 ? 1 : 0;
44787 v2 = pTos->x.iVal == 0 ? 1 : 0;
44788 if( pInstr->iOp == JX9_OP_LAND ){
44789 static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 };
44790 v1 = and_logic[v1*3+v2];
44791 }else{
44792 static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
44793 v1 = or_logic[v1*3+v2];
44794 }
44795 if( v1 == 2 ){
44796 v1 = 1;
44797 }
44798 VmPopOperand(&pTos, 1);
44799 pTos->x.iVal = v1 == 0 ? 1 : 0;
44800 MemObjSetType(pTos, MEMOBJ_BOOL);
44801 break;
44802 }
44803/* OP_LXOR: * * *
44804 *
44805 * Pop two values off the stack. Take the logical XOR of the
44806 * two values and push the resulting boolean value back onto the
44807 * stack.
44808 * According to the JX9 language reference manual:
44809 * $a xor $b is evaluated to TRUE if either $a or $b is
44810 * TRUE, but not both.
44811 */
44812case JX9_OP_LXOR:{
44813 jx9_value *pNos = &pTos[-1];
44814 sxi32 v = 0;
44815#ifdef UNTRUST
44816 if( pNos < pStack ){
44817 goto Abort;
44818 }
44819#endif
44820 /* Force a boolean cast */
44821 if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
44822 jx9MemObjToBool(pTos);
44823 }
44824 if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
44825 jx9MemObjToBool(pNos);
44826 }
44827 if( (pNos->x.iVal && !pTos->x.iVal) || (pTos->x.iVal && !pNos->x.iVal) ){
44828 v = 1;
44829 }
44830 VmPopOperand(&pTos, 1);
44831 pTos->x.iVal = v;
44832 MemObjSetType(pTos, MEMOBJ_BOOL);
44833 break;
44834 }
44835/* OP_EQ P1 P2 P3
44836 *
44837 * Pop the top two elements from the stack. If they are equal, then
44838 * jump to instruction P2. Otherwise, continue to the next instruction.
44839 * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
44840 * stack if the jump would have been taken, or a 0 (FALSE) if not.
44841 */
44842/* OP_NEQ P1 P2 P3
44843 *
44844 * Pop the top two elements from the stack. If they are not equal, then
44845 * jump to instruction P2. Otherwise, continue to the next instruction.
44846 * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
44847 * stack if the jump would have been taken, or a 0 (FALSE) if not.
44848 */
44849case JX9_OP_EQ:
44850case JX9_OP_NEQ: {
44851 jx9_value *pNos = &pTos[-1];
44852 /* Perform the comparison and act accordingly */
44853#ifdef UNTRUST
44854 if( pNos < pStack ){
44855 goto Abort;
44856 }
44857#endif
44858 rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
44859 if( pInstr->iOp == JX9_OP_EQ ){
44860 rc = rc == 0;
44861 }else{
44862 rc = rc != 0;
44863 }
44864 VmPopOperand(&pTos, 1);
44865 if( !pInstr->iP2 ){
44866 /* Push comparison result without taking the jump */
44867 jx9MemObjRelease(pTos);
44868 pTos->x.iVal = rc;
44869 /* Invalidate any prior representation */
44870 MemObjSetType(pTos, MEMOBJ_BOOL);
44871 }else{
44872 if( rc ){
44873 /* Jump to the desired location */
44874 pc = pInstr->iP2 - 1;
44875 VmPopOperand(&pTos, 1);
44876 }
44877 }
44878 break;
44879 }
44880/* OP_TEQ P1 P2 *
44881 *
44882 * Pop the top two elements from the stack. If they have the same type and are equal
44883 * then jump to instruction P2. Otherwise, continue to the next instruction.
44884 * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
44885 * stack if the jump would have been taken, or a 0 (FALSE) if not.
44886 */
44887case JX9_OP_TEQ: {
44888 jx9_value *pNos = &pTos[-1];
44889 /* Perform the comparison and act accordingly */
44890#ifdef UNTRUST
44891 if( pNos < pStack ){
44892 goto Abort;
44893 }
44894#endif
44895 rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) == 0;
44896 VmPopOperand(&pTos, 1);
44897 if( !pInstr->iP2 ){
44898 /* Push comparison result without taking the jump */
44899 jx9MemObjRelease(pTos);
44900 pTos->x.iVal = rc;
44901 /* Invalidate any prior representation */
44902 MemObjSetType(pTos, MEMOBJ_BOOL);
44903 }else{
44904 if( rc ){
44905 /* Jump to the desired location */
44906 pc = pInstr->iP2 - 1;
44907 VmPopOperand(&pTos, 1);
44908 }
44909 }
44910 break;
44911 }
44912/* OP_TNE P1 P2 *
44913 *
44914 * Pop the top two elements from the stack.If they are not equal an they are not
44915 * of the same type, then jump to instruction P2. Otherwise, continue to the next
44916 * instruction.
44917 * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
44918 * stack if the jump would have been taken, or a 0 (FALSE) if not.
44919 *
44920 */
44921case JX9_OP_TNE: {
44922 jx9_value *pNos = &pTos[-1];
44923 /* Perform the comparison and act accordingly */
44924#ifdef UNTRUST
44925 if( pNos < pStack ){
44926 goto Abort;
44927 }
44928#endif
44929 rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) != 0;
44930 VmPopOperand(&pTos, 1);
44931 if( !pInstr->iP2 ){
44932 /* Push comparison result without taking the jump */
44933 jx9MemObjRelease(pTos);
44934 pTos->x.iVal = rc;
44935 /* Invalidate any prior representation */
44936 MemObjSetType(pTos, MEMOBJ_BOOL);
44937 }else{
44938 if( rc ){
44939 /* Jump to the desired location */
44940 pc = pInstr->iP2 - 1;
44941 VmPopOperand(&pTos, 1);
44942 }
44943 }
44944 break;
44945 }
44946/* OP_LT P1 P2 P3
44947 *
44948 * Pop the top two elements from the stack. If the second element (the top of stack)
44949 * is less than the first (next on stack), then jump to instruction P2.Otherwise
44950 * continue to the next instruction. In other words, jump if pNos<pTos.
44951 * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
44952 * stack if the jump would have been taken, or a 0 (FALSE) if not.
44953 *
44954 */
44955/* OP_LE P1 P2 P3
44956 *
44957 * Pop the top two elements from the stack. If the second element (the top of stack)
44958 * is less than or equal to the first (next on stack), then jump to instruction P2.
44959 * Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
44960 * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
44961 * stack if the jump would have been taken, or a 0 (FALSE) if not.
44962 *
44963 */
44964case JX9_OP_LT:
44965case JX9_OP_LE: {
44966 jx9_value *pNos = &pTos[-1];
44967 /* Perform the comparison and act accordingly */
44968#ifdef UNTRUST
44969 if( pNos < pStack ){
44970 goto Abort;
44971 }
44972#endif
44973 rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
44974 if( pInstr->iOp == JX9_OP_LE ){
44975 rc = rc < 1;
44976 }else{
44977 rc = rc < 0;
44978 }
44979 VmPopOperand(&pTos, 1);
44980 if( !pInstr->iP2 ){
44981 /* Push comparison result without taking the jump */
44982 jx9MemObjRelease(pTos);
44983 pTos->x.iVal = rc;
44984 /* Invalidate any prior representation */
44985 MemObjSetType(pTos, MEMOBJ_BOOL);
44986 }else{
44987 if( rc ){
44988 /* Jump to the desired location */
44989 pc = pInstr->iP2 - 1;
44990 VmPopOperand(&pTos, 1);
44991 }
44992 }
44993 break;
44994 }
44995/* OP_GT P1 P2 P3
44996 *
44997 * Pop the top two elements from the stack. If the second element (the top of stack)
44998 * is greater than the first (next on stack), then jump to instruction P2.Otherwise
44999 * continue to the next instruction. In other words, jump if pNos<pTos.
45000 * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
45001 * stack if the jump would have been taken, or a 0 (FALSE) if not.
45002 *
45003 */
45004/* OP_GE P1 P2 P3
45005 *
45006 * Pop the top two elements from the stack. If the second element (the top of stack)
45007 * is greater than or equal to the first (next on stack), then jump to instruction P2.
45008 * Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
45009 * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
45010 * stack if the jump would have been taken, or a 0 (FALSE) if not.
45011 *
45012 */
45013case JX9_OP_GT:
45014case JX9_OP_GE: {
45015 jx9_value *pNos = &pTos[-1];
45016 /* Perform the comparison and act accordingly */
45017#ifdef UNTRUST
45018 if( pNos < pStack ){
45019 goto Abort;
45020 }
45021#endif
45022 rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
45023 if( pInstr->iOp == JX9_OP_GE ){
45024 rc = rc >= 0;
45025 }else{
45026 rc = rc > 0;
45027 }
45028 VmPopOperand(&pTos, 1);
45029 if( !pInstr->iP2 ){
45030 /* Push comparison result without taking the jump */
45031 jx9MemObjRelease(pTos);
45032 pTos->x.iVal = rc;
45033 /* Invalidate any prior representation */
45034 MemObjSetType(pTos, MEMOBJ_BOOL);
45035 }else{
45036 if( rc ){
45037 /* Jump to the desired location */
45038 pc = pInstr->iP2 - 1;
45039 VmPopOperand(&pTos, 1);
45040 }
45041 }
45042 break;
45043 }
45044/*
45045 * OP_FOREACH_INIT * P2 P3
45046 * Prepare a foreach step.
45047 */
45048case JX9_OP_FOREACH_INIT: {
45049 jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
45050 void *pName;
45051#ifdef UNTRUST
45052 if( pTos < pStack ){
45053 goto Abort;
45054 }
45055#endif
45056 if( SyStringLength(&pInfo->sValue) < 1 ){
45057 /* Take the variable name from the top of the stack */
45058 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
45059 /* Force a string cast */
45060 jx9MemObjToString(pTos);
45061 }
45062 /* Duplicate name */
45063 if( SyBlobLength(&pTos->sBlob) > 0 ){
45064 pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
45065 SyStringInitFromBuf(&pInfo->sValue, pName, SyBlobLength(&pTos->sBlob));
45066 }
45067 VmPopOperand(&pTos, 1);
45068 }
45069 if( (pInfo->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) < 1 ){
45070 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
45071 /* Force a string cast */
45072 jx9MemObjToString(pTos);
45073 }
45074 /* Duplicate name */
45075 if( SyBlobLength(&pTos->sBlob) > 0 ){
45076 pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
45077 SyStringInitFromBuf(&pInfo->sKey, pName, SyBlobLength(&pTos->sBlob));
45078 }
45079 VmPopOperand(&pTos, 1);
45080 }
45081 /* Make sure we are dealing with a hashmap [i.e. JSON array or object ]*/
45082 if( (pTos->iFlags & (MEMOBJ_HASHMAP)) == 0 || SyStringLength(&pInfo->sValue) < 1 ){
45083 /* Jump out of the loop */
45084 if( (pTos->iFlags & MEMOBJ_NULL) == 0 ){
45085 jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
45086 "Invalid argument supplied for the foreach statement, expecting JSON array or object instance");
45087 }
45088 pc = pInstr->iP2 - 1;
45089 }else{
45090 jx9_foreach_step *pStep;
45091 pStep = (jx9_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_foreach_step));
45092 if( pStep == 0 ){
45093 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step");
45094 /* Jump out of the loop */
45095 pc = pInstr->iP2 - 1;
45096 }else{
45097 /* Zero the structure */
45098 SyZero(pStep, sizeof(jx9_foreach_step));
45099 /* Prepare the step */
45100 pStep->iFlags = pInfo->iFlags;
45101 if( pTos->iFlags & MEMOBJ_HASHMAP ){
45102 jx9_hashmap *pMap = (jx9_hashmap *)pTos->x.pOther;
45103 /* Reset the internal loop cursor */
45104 jx9HashmapResetLoopCursor(pMap);
45105 /* Mark the step */
45106 pStep->pMap = pMap;
45107 pMap->iRef++;
45108 }
45109 }
45110 if( SXRET_OK != SySetPut(&pInfo->aStep, (const void *)&pStep) ){
45111 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step");
45112 SyMemBackendPoolFree(&pVm->sAllocator, pStep);
45113 /* Jump out of the loop */
45114 pc = pInstr->iP2 - 1;
45115 }
45116 }
45117 VmPopOperand(&pTos, 1);
45118 break;
45119 }
45120/*
45121 * OP_FOREACH_STEP * P2 P3
45122 * Perform a foreach step. Jump to P2 at the end of the step.
45123 */
45124case JX9_OP_FOREACH_STEP: {
45125 jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
45126 jx9_foreach_step **apStep, *pStep;
45127 jx9_hashmap_node *pNode;
45128 jx9_hashmap *pMap;
45129 jx9_value *pValue;
45130 /* Peek the last step */
45131 apStep = (jx9_foreach_step **)SySetBasePtr(&pInfo->aStep);
45132 pStep = apStep[SySetUsed(&pInfo->aStep) - 1];
45133 pMap = pStep->pMap;
45134 /* Extract the current node value */
45135 pNode = jx9HashmapGetNextEntry(pMap);
45136 if( pNode == 0 ){
45137 /* No more entry to process */
45138 pc = pInstr->iP2 - 1; /* Jump to this destination */
45139 /* Automatically reset the loop cursor */
45140 jx9HashmapResetLoopCursor(pMap);
45141 /* Cleanup the mess left behind */
45142 SyMemBackendPoolFree(&pVm->sAllocator, pStep);
45143 SySetPop(&pInfo->aStep);
45144 jx9HashmapUnref(pMap);
45145 }else{
45146 if( (pStep->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0 ){
45147 jx9_value *pKey = VmExtractMemObj(&(*pVm), &pInfo->sKey, FALSE, TRUE);
45148 if( pKey ){
45149 jx9HashmapExtractNodeKey(pNode, pKey);
45150 }
45151 }
45152 /* Make a copy of the entry value */
45153 pValue = VmExtractMemObj(&(*pVm), &pInfo->sValue, FALSE, TRUE);
45154 if( pValue ){
45155 jx9HashmapExtractNodeValue(pNode, pValue, TRUE);
45156 }
45157 }
45158 break;
45159 }
45160/*
45161 * OP_MEMBER P1 P2
45162 * Load JSON object entry on the stack.
45163 */
45164case JX9_OP_MEMBER: {
45165 jx9_hashmap_node *pNode = 0; /* cc warning */
45166 jx9_hashmap *pMap = 0;
45167 jx9_value *pIdx;
45168 pIdx = pTos;
45169 pTos--;
45170 rc = SXERR_NOTFOUND; /* Assume the index is invalid */
45171 if( pTos->iFlags & MEMOBJ_HASHMAP ){
45172 /* Point to the hashmap */
45173 pMap = (jx9_hashmap *)pTos->x.pOther;
45174 /* Load the desired entry */
45175 rc = jx9HashmapLookup(pMap, pIdx, &pNode);
45176 }
45177 jx9MemObjRelease(pIdx);
45178 if( rc == SXRET_OK ){
45179 /* Load entry contents */
45180 if( pMap->iRef < 2 ){
45181 /* TICKET 1433-42: Array will be deleted shortly, so we will make a copy
45182 * of the entry value, rather than pointing to it.
45183 */
45184 pTos->nIdx = SXU32_HIGH;
45185 jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
45186 }else{
45187 pTos->nIdx = pNode->nValIdx;
45188 jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
45189 jx9HashmapUnref(pMap);
45190 }
45191 }else{
45192 /* No such entry, load NULL */
45193 jx9MemObjRelease(pTos);
45194 pTos->nIdx = SXU32_HIGH;
45195 }
45196 break;
45197 }
45198/*
45199 * OP_SWITCH * * P3
45200 * This is the bytecode implementation of the complex switch() JX9 construct.
45201 */
45202case JX9_OP_SWITCH: {
45203 jx9_switch *pSwitch = (jx9_switch *)pInstr->p3;
45204 jx9_case_expr *aCase, *pCase;
45205 jx9_value sValue, sCaseValue;
45206 sxu32 n, nEntry;
45207#ifdef UNTRUST
45208 if( pSwitch == 0 || pTos < pStack ){
45209 goto Abort;
45210 }
45211#endif
45212 /* Point to the case table */
45213 aCase = (jx9_case_expr *)SySetBasePtr(&pSwitch->aCaseExpr);
45214 nEntry = SySetUsed(&pSwitch->aCaseExpr);
45215 /* Select the appropriate case block to execute */
45216 jx9MemObjInit(pVm, &sValue);
45217 jx9MemObjInit(pVm, &sCaseValue);
45218 for( n = 0 ; n < nEntry ; ++n ){
45219 pCase = &aCase[n];
45220 jx9MemObjLoad(pTos, &sValue);
45221 /* Execute the case expression first */
45222 VmLocalExec(pVm,&pCase->aByteCode, &sCaseValue);
45223 /* Compare the two expression */
45224 rc = jx9MemObjCmp(&sValue, &sCaseValue, FALSE, 0);
45225 jx9MemObjRelease(&sValue);
45226 jx9MemObjRelease(&sCaseValue);
45227 if( rc == 0 ){
45228 /* Value match, jump to this block */
45229 pc = pCase->nStart - 1;
45230 break;
45231 }
45232 }
45233 VmPopOperand(&pTos, 1);
45234 if( n >= nEntry ){
45235 /* No approprite case to execute, jump to the default case */
45236 if( pSwitch->nDefault > 0 ){
45237 pc = pSwitch->nDefault - 1;
45238 }else{
45239 /* No default case, jump out of this switch */
45240 pc = pSwitch->nOut - 1;
45241 }
45242 }
45243 break;
45244 }
45245/*
45246 * OP_UPLINK P1 * *
45247 * Link a variable to the top active VM frame.
45248 * This is used to implement the 'uplink' JX9 construct.
45249 */
45250case JX9_OP_UPLINK: {
45251 if( pVm->pFrame->pParent ){
45252 jx9_value *pLink = &pTos[-pInstr->iP1+1];
45253 SyString sName;
45254 /* Perform the link */
45255 while( pLink <= pTos ){
45256 if((pLink->iFlags & MEMOBJ_STRING) == 0 ){
45257 /* Force a string cast */
45258 jx9MemObjToString(pLink);
45259 }
45260 SyStringInitFromBuf(&sName, SyBlobData(&pLink->sBlob), SyBlobLength(&pLink->sBlob));
45261 if( sName.nByte > 0 ){
45262 VmFrameLink(&(*pVm), &sName);
45263 }
45264 pLink++;
45265 }
45266 }
45267 VmPopOperand(&pTos, pInstr->iP1);
45268 break;
45269 }
45270/*
45271 * OP_CALL P1 * *
45272 * Call a JX9 or a foreign function and push the return value of the called
45273 * function on the stack.
45274 */
45275case JX9_OP_CALL: {
45276 jx9_value *pArg = &pTos[-pInstr->iP1];
45277 SyHashEntry *pEntry;
45278 SyString sName;
45279 /* Extract function name */
45280 if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
45281 /* Raise exception: Invalid function name */
45282 VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Invalid function name, JX9 is returning NULL.");
45283 /* Pop given arguments */
45284 if( pInstr->iP1 > 0 ){
45285 VmPopOperand(&pTos, pInstr->iP1);
45286 }
45287 /* Assume a null return value so that the program continue it's execution normally */
45288 jx9MemObjRelease(pTos);
45289 break;
45290 }
45291 SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
45292 /* Check for a compiled function first */
45293 pEntry = SyHashGet(&pVm->hFunction, (const void *)sName.zString, sName.nByte);
45294 if( pEntry ){
45295 jx9_vm_func_arg *aFormalArg;
45296 jx9_value *pFrameStack;
45297 jx9_vm_func *pVmFunc;
45298 VmFrame *pFrame;
45299 jx9_value *pObj;
45300 VmSlot sArg;
45301 sxu32 n;
45302 pVmFunc = (jx9_vm_func *)pEntry->pUserData;
45303 /* Check The recursion limit */
45304 if( pVm->nRecursionDepth > pVm->nMaxDepth ){
45305 VmErrorFormat(&(*pVm), JX9_CTX_ERR,
45306 "Recursion limit reached while invoking user function '%z', JX9 will set a NULL return value",
45307 &pVmFunc->sName);
45308 /* Pop given arguments */
45309 if( pInstr->iP1 > 0 ){
45310 VmPopOperand(&pTos, pInstr->iP1);
45311 }
45312 /* Assume a null return value so that the program continue it's execution normally */
45313 jx9MemObjRelease(pTos);
45314 break;
45315 }
45316 if( pVmFunc->pNextName ){
45317 /* Function is candidate for overloading, select the appropriate function to call */
45318 pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (int)(pTos-pArg));
45319 }
45320 /* Extract the formal argument set */
45321 aFormalArg = (jx9_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs);
45322 /* Create a new VM frame */
45323 rc = VmEnterFrame(&(*pVm),pVmFunc,&pFrame);
45324 if( rc != SXRET_OK ){
45325 /* Raise exception: Out of memory */
45326 VmErrorFormat(&(*pVm), JX9_CTX_ERR,
45327 "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.",
45328 &pVmFunc->sName);
45329 /* Pop given arguments */
45330 if( pInstr->iP1 > 0 ){
45331 VmPopOperand(&pTos, pInstr->iP1);
45332 }
45333 /* Assume a null return value so that the program continue it's execution normally */
45334 jx9MemObjRelease(pTos);
45335 break;
45336 }
45337 if( SySetUsed(&pVmFunc->aStatic) > 0 ){
45338 jx9_vm_func_static_var *pStatic, *aStatic;
45339 /* Install static variables */
45340 aStatic = (jx9_vm_func_static_var *)SySetBasePtr(&pVmFunc->aStatic);
45341 for( n = 0 ; n < SySetUsed(&pVmFunc->aStatic) ; ++n ){
45342 pStatic = &aStatic[n];
45343 if( pStatic->nIdx == SXU32_HIGH ){
45344 /* Initialize the static variables */
45345 pObj = VmReserveMemObj(&(*pVm), &pStatic->nIdx);
45346 if( pObj ){
45347 /* Assume a NULL initialization value */
45348 jx9MemObjInit(&(*pVm), pObj);
45349 if( SySetUsed(&pStatic->aByteCode) > 0 ){
45350 /* Evaluate initialization expression (Any complex expression) */
45351 VmLocalExec(&(*pVm), &pStatic->aByteCode, pObj);
45352 }
45353 pObj->nIdx = pStatic->nIdx;
45354 }else{
45355 continue;
45356 }
45357 }
45358 /* Install in the current frame */
45359 SyHashInsert(&pFrame->hVar, SyStringData(&pStatic->sName), SyStringLength(&pStatic->sName),
45360 SX_INT_TO_PTR(pStatic->nIdx));
45361 }
45362 }
45363 /* Push arguments in the local frame */
45364 n = 0;
45365 while( pArg < pTos ){
45366 if( n < SySetUsed(&pVmFunc->aArgs) ){
45367 if( (pArg->iFlags & MEMOBJ_NULL) && SySetUsed(&aFormalArg[n].aByteCode) > 0 ){
45368 /* NULL values are redirected to default arguments */
45369 rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pArg);
45370 if( rc == JX9_ABORT ){
45371 goto Abort;
45372 }
45373 }
45374 /* Make sure the given arguments are of the correct type */
45375 if( aFormalArg[n].nType > 0 ){
45376 if( ((pArg->iFlags & aFormalArg[n].nType) == 0) ){
45377 ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
45378 /* Cast to the desired type */
45379 if( xCast ){
45380 xCast(pArg);
45381 }
45382 }
45383 }
45384 /* Pass by value, make a copy of the given argument */
45385 pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE);
45386 }else{
45387 char zName[32];
45388 SyString sName;
45389 /* Set a dummy name */
45390 sName.nByte = SyBufferFormat(zName, sizeof(zName), "[%u]apArg", n);
45391 sName.zString = zName;
45392 /* Annonymous argument */
45393 pObj = VmExtractMemObj(&(*pVm), &sName, TRUE, TRUE);
45394 }
45395 if( pObj ){
45396 jx9MemObjStore(pArg, pObj);
45397 /* Insert argument index */
45398 sArg.nIdx = pObj->nIdx;
45399 sArg.pUserData = 0;
45400 SySetPut(&pFrame->sArg, (const void *)&sArg);
45401 }
45402 jx9MemObjRelease(pArg);
45403 pArg++;
45404 ++n;
45405 }
45406 /* Process default values */
45407 while( n < SySetUsed(&pVmFunc->aArgs) ){
45408 if( SySetUsed(&aFormalArg[n].aByteCode) > 0 ){
45409 pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE);
45410 if( pObj ){
45411 /* Evaluate the default value and extract it's result */
45412 rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pObj);
45413 if( rc == JX9_ABORT ){
45414 goto Abort;
45415 }
45416 /* Insert argument index */
45417 sArg.nIdx = pObj->nIdx;
45418 sArg.pUserData = 0;
45419 SySetPut(&pFrame->sArg, (const void *)&sArg);
45420 /* Make sure the default argument is of the correct type */
45421 if( aFormalArg[n].nType > 0 && ((pObj->iFlags & aFormalArg[n].nType) == 0) ){
45422 ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
45423 /* Cast to the desired type */
45424 xCast(pObj);
45425 }
45426 }
45427 }
45428 ++n;
45429 }
45430 /* Pop arguments, function name from the operand stack and assume the function
45431 * does not return anything.
45432 */
45433 jx9MemObjRelease(pTos);
45434 pTos = &pTos[-pInstr->iP1];
45435 /* Allocate a new operand stack and evaluate the function body */
45436 pFrameStack = VmNewOperandStack(&(*pVm), SySetUsed(&pVmFunc->aByteCode));
45437 if( pFrameStack == 0 ){
45438 /* Raise exception: Out of memory */
45439 VmErrorFormat(&(*pVm), JX9_CTX_ERR, "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.",
45440 &pVmFunc->sName);
45441 if( pInstr->iP1 > 0 ){
45442 VmPopOperand(&pTos, pInstr->iP1);
45443 }
45444 break;
45445 }
45446 /* Increment nesting level */
45447 pVm->nRecursionDepth++;
45448 /* Execute function body */
45449 rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(&pVmFunc->aByteCode), pFrameStack, -1, pTos);
45450 /* Decrement nesting level */
45451 pVm->nRecursionDepth--;
45452 /* Free the operand stack */
45453 SyMemBackendFree(&pVm->sAllocator, pFrameStack);
45454 /* Leave the frame */
45455 VmLeaveFrame(&(*pVm));
45456 if( rc == JX9_ABORT ){
45457 /* Abort processing immeditaley */
45458 goto Abort;
45459 }
45460 }else{
45461 jx9_user_func *pFunc;
45462 jx9_context sCtx;
45463 jx9_value sRet;
45464 /* Look for an installed foreign function */
45465 pEntry = SyHashGet(&pVm->hHostFunction, (const void *)sName.zString, sName.nByte);
45466 if( pEntry == 0 ){
45467 /* Call to undefined function */
45468 VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Call to undefined function '%z', JX9 is returning NULL.", &sName);
45469 /* Pop given arguments */
45470 if( pInstr->iP1 > 0 ){
45471 VmPopOperand(&pTos, pInstr->iP1);
45472 }
45473 /* Assume a null return value so that the program continue it's execution normally */
45474 jx9MemObjRelease(pTos);
45475 break;
45476 }
45477 pFunc = (jx9_user_func *)pEntry->pUserData;
45478 /* Start collecting function arguments */
45479 SySetReset(&aArg);
45480 while( pArg < pTos ){
45481 SySetPut(&aArg, (const void *)&pArg);
45482 pArg++;
45483 }
45484 /* Assume a null return value */
45485 jx9MemObjInit(&(*pVm), &sRet);
45486 /* Init the call context */
45487 VmInitCallContext(&sCtx, &(*pVm), pFunc, &sRet, 0);
45488 /* Call the foreign function */
45489 rc = pFunc->xFunc(&sCtx, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg));
45490 /* Release the call context */
45491 VmReleaseCallContext(&sCtx);
45492 if( rc == JX9_ABORT ){
45493 goto Abort;
45494 }
45495 if( pInstr->iP1 > 0 ){
45496 /* Pop function name and arguments */
45497 VmPopOperand(&pTos, pInstr->iP1);
45498 }
45499 /* Save foreign function return value */
45500 jx9MemObjStore(&sRet, pTos);
45501 jx9MemObjRelease(&sRet);
45502 }
45503 break;
45504 }
45505/*
45506 * OP_CONSUME: P1 * *
45507 * Consume (Invoke the installed VM output consumer callback) and POP P1 elements from the stack.
45508 */
45509case JX9_OP_CONSUME: {
45510 jx9_output_consumer *pCons = &pVm->sVmConsumer;
45511 jx9_value *pCur, *pOut = pTos;
45512
45513 pOut = &pTos[-pInstr->iP1 + 1];
45514 pCur = pOut;
45515 /* Start the consume process */
45516 while( pOut <= pTos ){
45517 /* Force a string cast */
45518 if( (pOut->iFlags & MEMOBJ_STRING) == 0 ){
45519 jx9MemObjToString(pOut);
45520 }
45521 if( SyBlobLength(&pOut->sBlob) > 0 ){
45522 /*SyBlobNullAppend(&pOut->sBlob);*/
45523 /* Invoke the output consumer callback */
45524 rc = pCons->xConsumer(SyBlobData(&pOut->sBlob), SyBlobLength(&pOut->sBlob), pCons->pUserData);
45525 /* Increment output length */
45526 pVm->nOutputLen += SyBlobLength(&pOut->sBlob);
45527 SyBlobRelease(&pOut->sBlob);
45528 if( rc == SXERR_ABORT ){
45529 /* Output consumer callback request an operation abort. */
45530 goto Abort;
45531 }
45532 }
45533 pOut++;
45534 }
45535 pTos = &pCur[-1];
45536 break;
45537 }
45538
45539 } /* Switch() */
45540 pc++; /* Next instruction in the stream */
45541 } /* For(;;) */
45542Done:
45543 SySetRelease(&aArg);
45544 return SXRET_OK;
45545Abort:
45546 SySetRelease(&aArg);
45547 while( pTos >= pStack ){
45548 jx9MemObjRelease(pTos);
45549 pTos--;
45550 }
45551 return JX9_ABORT;
45552}
45553/*
45554 * Execute as much of a local JX9 bytecode program as we can then return.
45555 * This function is a wrapper around [VmByteCodeExec()].
45556 * See block-comment on that function for additional information.
45557 */
45558static sxi32 VmLocalExec(jx9_vm *pVm, SySet *pByteCode,jx9_value *pResult)
45559{
45560 jx9_value *pStack;
45561 sxi32 rc;
45562 /* Allocate a new operand stack */
45563 pStack = VmNewOperandStack(&(*pVm), SySetUsed(pByteCode));
45564 if( pStack == 0 ){
45565 return SXERR_MEM;
45566 }
45567 /* Execute the program */
45568 rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pByteCode), pStack, -1, &(*pResult));
45569 /* Free the operand stack */
45570 SyMemBackendFree(&pVm->sAllocator, pStack);
45571 /* Execution result */
45572 return rc;
45573}
45574/*
45575 * Execute as much of a JX9 bytecode program as we can then return.
45576 * This function is a wrapper around [VmByteCodeExec()].
45577 * See block-comment on that function for additional information.
45578 */
45579JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm)
45580{
45581 /* Make sure we are ready to execute this program */
45582 if( pVm->nMagic != JX9_VM_RUN ){
45583 return pVm->nMagic == JX9_VM_EXEC ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */
45584 }
45585 /* Set the execution magic number */
45586 pVm->nMagic = JX9_VM_EXEC;
45587 /* Execute the program */
45588 VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pVm->pByteContainer), pVm->aOps, -1, &pVm->sExec);
45589 /*
45590 * TICKET 1433-100: Do not remove the JX9_VM_EXEC magic number
45591 * so that any following call to [jx9_vm_exec()] without calling
45592 * [jx9_vm_reset()] first would fail.
45593 */
45594 return SXRET_OK;
45595}
45596/*
45597 * Extract a memory object (i.e. a variable) from the running script.
45598 * This function must be called after calling jx9_vm_exec(). Otherwise
45599 * NULL is returned.
45600 */
45601JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar)
45602{
45603 jx9_value *pValue;
45604 if( pVm->nMagic != JX9_VM_EXEC ){
45605 /* call jx9_vm_exec() first */
45606 return 0;
45607 }
45608 /* Perform the lookup */
45609 pValue = VmExtractMemObj(pVm,pVar,FALSE,FALSE);
45610 /* Lookup result */
45611 return pValue;
45612}
45613/*
45614 * Invoke the installed VM output consumer callback to consume
45615 * the desired message.
45616 * Refer to the implementation of [jx9_context_output()] defined
45617 * in 'api.c' for additional information.
45618 */
45619JX9_PRIVATE sxi32 jx9VmOutputConsume(
45620 jx9_vm *pVm, /* Target VM */
45621 SyString *pString /* Message to output */
45622 )
45623{
45624 jx9_output_consumer *pCons = &pVm->sVmConsumer;
45625 sxi32 rc = SXRET_OK;
45626 /* Call the output consumer */
45627 if( pString->nByte > 0 ){
45628 rc = pCons->xConsumer((const void *)pString->zString, pString->nByte, pCons->pUserData);
45629 /* Increment output length */
45630 pVm->nOutputLen += pString->nByte;
45631 }
45632 return rc;
45633}
45634/*
45635 * Format a message and invoke the installed VM output consumer
45636 * callback to consume the formatted message.
45637 * Refer to the implementation of [jx9_context_output_format()] defined
45638 * in 'api.c' for additional information.
45639 */
45640JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(
45641 jx9_vm *pVm, /* Target VM */
45642 const char *zFormat, /* Formatted message to output */
45643 va_list ap /* Variable list of arguments */
45644 )
45645{
45646 jx9_output_consumer *pCons = &pVm->sVmConsumer;
45647 sxi32 rc = SXRET_OK;
45648 SyBlob sWorker;
45649 /* Format the message and call the output consumer */
45650 SyBlobInit(&sWorker, &pVm->sAllocator);
45651 SyBlobFormatAp(&sWorker, zFormat, ap);
45652 if( SyBlobLength(&sWorker) > 0 ){
45653 /* Consume the formatted message */
45654 rc = pCons->xConsumer(SyBlobData(&sWorker), SyBlobLength(&sWorker), pCons->pUserData);
45655 }
45656 /* Increment output length */
45657 pVm->nOutputLen += SyBlobLength(&sWorker);
45658 /* Release the working buffer */
45659 SyBlobRelease(&sWorker);
45660 return rc;
45661}
45662/*
45663 * Return a string representation of the given JX9 OP code.
45664 * This function never fail and always return a pointer
45665 * to a null terminated string.
45666 */
45667static const char * VmInstrToString(sxi32 nOp)
45668{
45669 const char *zOp = "Unknown ";
45670 switch(nOp){
45671 case JX9_OP_DONE: zOp = "DONE "; break;
45672 case JX9_OP_HALT: zOp = "HALT "; break;
45673 case JX9_OP_LOAD: zOp = "LOAD "; break;
45674 case JX9_OP_LOADC: zOp = "LOADC "; break;
45675 case JX9_OP_LOAD_MAP: zOp = "LOAD_MAP "; break;
45676 case JX9_OP_LOAD_IDX: zOp = "LOAD_IDX "; break;
45677 case JX9_OP_NOOP: zOp = "NOOP "; break;
45678 case JX9_OP_JMP: zOp = "JMP "; break;
45679 case JX9_OP_JZ: zOp = "JZ "; break;
45680 case JX9_OP_JNZ: zOp = "JNZ "; break;
45681 case JX9_OP_POP: zOp = "POP "; break;
45682 case JX9_OP_CAT: zOp = "CAT "; break;
45683 case JX9_OP_CVT_INT: zOp = "CVT_INT "; break;
45684 case JX9_OP_CVT_STR: zOp = "CVT_STR "; break;
45685 case JX9_OP_CVT_REAL: zOp = "CVT_REAL "; break;
45686 case JX9_OP_CALL: zOp = "CALL "; break;
45687 case JX9_OP_UMINUS: zOp = "UMINUS "; break;
45688 case JX9_OP_UPLUS: zOp = "UPLUS "; break;
45689 case JX9_OP_BITNOT: zOp = "BITNOT "; break;
45690 case JX9_OP_LNOT: zOp = "LOGNOT "; break;
45691 case JX9_OP_MUL: zOp = "MUL "; break;
45692 case JX9_OP_DIV: zOp = "DIV "; break;
45693 case JX9_OP_MOD: zOp = "MOD "; break;
45694 case JX9_OP_ADD: zOp = "ADD "; break;
45695 case JX9_OP_SUB: zOp = "SUB "; break;
45696 case JX9_OP_SHL: zOp = "SHL "; break;
45697 case JX9_OP_SHR: zOp = "SHR "; break;
45698 case JX9_OP_LT: zOp = "LT "; break;
45699 case JX9_OP_LE: zOp = "LE "; break;
45700 case JX9_OP_GT: zOp = "GT "; break;
45701 case JX9_OP_GE: zOp = "GE "; break;
45702 case JX9_OP_EQ: zOp = "EQ "; break;
45703 case JX9_OP_NEQ: zOp = "NEQ "; break;
45704 case JX9_OP_TEQ: zOp = "TEQ "; break;
45705 case JX9_OP_TNE: zOp = "TNE "; break;
45706 case JX9_OP_BAND: zOp = "BITAND "; break;
45707 case JX9_OP_BXOR: zOp = "BITXOR "; break;
45708 case JX9_OP_BOR: zOp = "BITOR "; break;
45709 case JX9_OP_LAND: zOp = "LOGAND "; break;
45710 case JX9_OP_LOR: zOp = "LOGOR "; break;
45711 case JX9_OP_LXOR: zOp = "LOGXOR "; break;
45712 case JX9_OP_STORE: zOp = "STORE "; break;
45713 case JX9_OP_STORE_IDX: zOp = "STORE_IDX "; break;
45714 case JX9_OP_PULL: zOp = "PULL "; break;
45715 case JX9_OP_SWAP: zOp = "SWAP "; break;
45716 case JX9_OP_YIELD: zOp = "YIELD "; break;
45717 case JX9_OP_CVT_BOOL: zOp = "CVT_BOOL "; break;
45718 case JX9_OP_CVT_NULL: zOp = "CVT_NULL "; break;
45719 case JX9_OP_CVT_ARRAY: zOp = "CVT_JSON "; break;
45720 case JX9_OP_CVT_NUMC: zOp = "CVT_NUMC "; break;
45721 case JX9_OP_INCR: zOp = "INCR "; break;
45722 case JX9_OP_DECR: zOp = "DECR "; break;
45723 case JX9_OP_ADD_STORE: zOp = "ADD_STORE "; break;
45724 case JX9_OP_SUB_STORE: zOp = "SUB_STORE "; break;
45725 case JX9_OP_MUL_STORE: zOp = "MUL_STORE "; break;
45726 case JX9_OP_DIV_STORE: zOp = "DIV_STORE "; break;
45727 case JX9_OP_MOD_STORE: zOp = "MOD_STORE "; break;
45728 case JX9_OP_CAT_STORE: zOp = "CAT_STORE "; break;
45729 case JX9_OP_SHL_STORE: zOp = "SHL_STORE "; break;
45730 case JX9_OP_SHR_STORE: zOp = "SHR_STORE "; break;
45731 case JX9_OP_BAND_STORE: zOp = "BAND_STORE "; break;
45732 case JX9_OP_BOR_STORE: zOp = "BOR_STORE "; break;
45733 case JX9_OP_BXOR_STORE: zOp = "BXOR_STORE "; break;
45734 case JX9_OP_CONSUME: zOp = "CONSUME "; break;
45735 case JX9_OP_MEMBER: zOp = "MEMBER "; break;
45736 case JX9_OP_UPLINK: zOp = "UPLINK "; break;
45737 case JX9_OP_SWITCH: zOp = "SWITCH "; break;
45738 case JX9_OP_FOREACH_INIT:
45739 zOp = "4EACH_INIT "; break;
45740 case JX9_OP_FOREACH_STEP:
45741 zOp = "4EACH_STEP "; break;
45742 default:
45743 break;
45744 }
45745 return zOp;
45746}
45747/*
45748 * Dump JX9 bytecodes instructions to a human readable format.
45749 * The xConsumer() callback which is an used defined function
45750 * is responsible of consuming the generated dump.
45751 */
45752JX9_PRIVATE sxi32 jx9VmDump(
45753 jx9_vm *pVm, /* Target VM */
45754 ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */
45755 void *pUserData /* Last argument to xConsumer() */
45756 )
45757{
45758 sxi32 rc;
45759 rc = VmByteCodeDump(pVm->pByteContainer, xConsumer, pUserData);
45760 return rc;
45761}
45762/*
45763 * Default constant expansion callback used by the 'const' statement if used
45764 * outside a object body [i.e: global or function scope].
45765 * Refer to the implementation of [JX9_CompileConstant()] defined
45766 * in 'compile.c' for additional information.
45767 */
45768JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData)
45769{
45770 SySet *pByteCode = (SySet *)pUserData;
45771 /* Evaluate and expand constant value */
45772 VmLocalExec((jx9_vm *)SySetGetUserData(pByteCode), pByteCode, (jx9_value *)pVal);
45773}
45774/*
45775 * Section:
45776 * Function handling functions.
45777 * Authors:
45778 * Symisc Systems, devel@symisc.net.
45779 * Copyright (C) Symisc Systems, http://jx9.symisc.net
45780 * Status:
45781 * Stable.
45782 */
45783/*
45784 * int func_num_args(void)
45785 * Returns the number of arguments passed to the function.
45786 * Parameters
45787 * None.
45788 * Return
45789 * Total number of arguments passed into the current user-defined function
45790 * or -1 if called from the globe scope.
45791 */
45792static int vm_builtin_func_num_args(jx9_context *pCtx, int nArg, jx9_value **apArg)
45793{
45794 VmFrame *pFrame;
45795 jx9_vm *pVm;
45796 /* Point to the target VM */
45797 pVm = pCtx->pVm;
45798 /* Current frame */
45799 pFrame = pVm->pFrame;
45800 if( pFrame->pParent == 0 ){
45801 SXUNUSED(nArg);
45802 SXUNUSED(apArg);
45803 /* Global frame, return -1 */
45804 jx9_result_int(pCtx, -1);
45805 return SXRET_OK;
45806 }
45807 /* Total number of arguments passed to the enclosing function */
45808 nArg = (int)SySetUsed(&pFrame->sArg);
45809 jx9_result_int(pCtx, nArg);
45810 return SXRET_OK;
45811}
45812/*
45813 * value func_get_arg(int $arg_num)
45814 * Return an item from the argument list.
45815 * Parameters
45816 * Argument number(index start from zero).
45817 * Return
45818 * Returns the specified argument or FALSE on error.
45819 */
45820static int vm_builtin_func_get_arg(jx9_context *pCtx, int nArg, jx9_value **apArg)
45821{
45822 jx9_value *pObj = 0;
45823 VmSlot *pSlot = 0;
45824 VmFrame *pFrame;
45825 jx9_vm *pVm;
45826 /* Point to the target VM */
45827 pVm = pCtx->pVm;
45828 /* Current frame */
45829 pFrame = pVm->pFrame;
45830 if( nArg < 1 || pFrame->pParent == 0 ){
45831 /* Global frame or Missing arguments, return FALSE */
45832 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
45833 jx9_result_bool(pCtx, 0);
45834 return SXRET_OK;
45835 }
45836 /* Extract the desired index */
45837 nArg = jx9_value_to_int(apArg[0]);
45838 if( nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg) ){
45839 /* Invalid index, return FALSE */
45840 jx9_result_bool(pCtx, 0);
45841 return SXRET_OK;
45842 }
45843 /* Extract the desired argument */
45844 if( (pSlot = (VmSlot *)SySetAt(&pFrame->sArg, (sxu32)nArg)) != 0 ){
45845 if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx)) != 0 ){
45846 /* Return the desired argument */
45847 jx9_result_value(pCtx, (jx9_value *)pObj);
45848 }else{
45849 /* No such argument, return false */
45850 jx9_result_bool(pCtx, 0);
45851 }
45852 }else{
45853 /* CAN'T HAPPEN */
45854 jx9_result_bool(pCtx, 0);
45855 }
45856 return SXRET_OK;
45857}
45858/*
45859 * array func_get_args(void)
45860 * Returns an array comprising a copy of function's argument list.
45861 * Parameters
45862 * None.
45863 * Return
45864 * Returns an array in which each element is a copy of the corresponding
45865 * member of the current user-defined function's argument list.
45866 * Otherwise FALSE is returned on failure.
45867 */
45868static int vm_builtin_func_get_args(jx9_context *pCtx, int nArg, jx9_value **apArg)
45869{
45870 jx9_value *pObj = 0;
45871 jx9_value *pArray;
45872 VmFrame *pFrame;
45873 VmSlot *aSlot;
45874 sxu32 n;
45875 /* Point to the current frame */
45876 pFrame = pCtx->pVm->pFrame;
45877 if( pFrame->pParent == 0 ){
45878 /* Global frame, return FALSE */
45879 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
45880 jx9_result_bool(pCtx, 0);
45881 return SXRET_OK;
45882 }
45883 /* Create a new array */
45884 pArray = jx9_context_new_array(pCtx);
45885 if( pArray == 0 ){
45886 SXUNUSED(nArg); /* cc warning */
45887 SXUNUSED(apArg);
45888 jx9_result_bool(pCtx, 0);
45889 return SXRET_OK;
45890 }
45891 /* Start filling the array with the given arguments */
45892 aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg);
45893 for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){
45894 pObj = (jx9_value *)SySetAt(&pCtx->pVm->aMemObj, aSlot[n].nIdx);
45895 if( pObj ){
45896 jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pObj);
45897 }
45898 }
45899 /* Return the freshly created array */
45900 jx9_result_value(pCtx, pArray);
45901 return SXRET_OK;
45902}
45903/*
45904 * bool function_exists(string $name)
45905 * Return TRUE if the given function has been defined.
45906 * Parameters
45907 * The name of the desired function.
45908 * Return
45909 * Return TRUE if the given function has been defined.False otherwise
45910 */
45911static int vm_builtin_func_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
45912{
45913 const char *zName;
45914 jx9_vm *pVm;
45915 int nLen;
45916 int res;
45917 if( nArg < 1 ){
45918 /* Missing argument, return FALSE */
45919 jx9_result_bool(pCtx, 0);
45920 return SXRET_OK;
45921 }
45922 /* Point to the target VM */
45923 pVm = pCtx->pVm;
45924 /* Extract the function name */
45925 zName = jx9_value_to_string(apArg[0], &nLen);
45926 /* Assume the function is not defined */
45927 res = 0;
45928 /* Perform the lookup */
45929 if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 ||
45930 SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){
45931 /* Function is defined */
45932 res = 1;
45933 }
45934 jx9_result_bool(pCtx, res);
45935 return SXRET_OK;
45936}
45937/*
45938 * Verify that the contents of a variable can be called as a function.
45939 * [i.e: Whether it is callable or not].
45940 * Return TRUE if callable.FALSE otherwise.
45941 */
45942JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue)
45943{
45944 int res = 0;
45945 if( pValue->iFlags & MEMOBJ_STRING ){
45946 const char *zName;
45947 int nLen;
45948 /* Extract the name */
45949 zName = jx9_value_to_string(pValue, &nLen);
45950 /* Perform the lookup */
45951 if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 ||
45952 SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){
45953 /* Function is callable */
45954 res = 1;
45955 }
45956 }
45957 return res;
45958}
45959/*
45960 * bool is_callable(callable $name[, bool $syntax_only = false])
45961 * Verify that the contents of a variable can be called as a function.
45962 * Parameters
45963 * $name
45964 * The callback function to check
45965 * $syntax_only
45966 * If set to TRUE the function only verifies that name might be a function or method.
45967 * It will only reject simple variables that are not strings, or an array that does
45968 * not have a valid structure to be used as a callback. The valid ones are supposed
45969 * to have only 2 entries, the first of which is an object or a string, and the second
45970 * a string.
45971 * Return
45972 * TRUE if name is callable, FALSE otherwise.
45973 */
45974static int vm_builtin_is_callable(jx9_context *pCtx, int nArg, jx9_value **apArg)
45975{
45976 jx9_vm *pVm;
45977 int res;
45978 if( nArg < 1 ){
45979 /* Missing arguments, return FALSE */
45980 jx9_result_bool(pCtx, 0);
45981 return SXRET_OK;
45982 }
45983 /* Point to the target VM */
45984 pVm = pCtx->pVm;
45985 /* Perform the requested operation */
45986 res = jx9VmIsCallable(pVm, apArg[0]);
45987 jx9_result_bool(pCtx, res);
45988 return SXRET_OK;
45989}
45990/*
45991 * Hash walker callback used by the [get_defined_functions()] function
45992 * defined below.
45993 */
45994static int VmHashFuncStep(SyHashEntry *pEntry, void *pUserData)
45995{
45996 jx9_value *pArray = (jx9_value *)pUserData;
45997 jx9_value sName;
45998 sxi32 rc;
45999 /* Prepare the function name for insertion */
46000 jx9MemObjInitFromString(pArray->pVm, &sName, 0);
46001 jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
46002 /* Perform the insertion */
46003 rc = jx9_array_add_elem(pArray, 0/* Automatic index assign */, &sName); /* Will make it's own copy */
46004 jx9MemObjRelease(&sName);
46005 return rc;
46006}
46007/*
46008 * array get_defined_functions(void)
46009 * Returns an array of all defined functions.
46010 * Parameter
46011 * None.
46012 * Return
46013 * Returns an multidimensional array containing a list of all defined functions
46014 * both built-in (internal) and user-defined.
46015 * The internal functions will be accessible via $arr["internal"], and the user
46016 * defined ones using $arr["user"].
46017 * Note:
46018 * NULL is returned on failure.
46019 */
46020static int vm_builtin_get_defined_func(jx9_context *pCtx, int nArg, jx9_value **apArg)
46021{
46022 jx9_value *pArray;
46023 /* NOTE:
46024 * Don't worry about freeing memory here, every allocated resource will be released
46025 * automatically by the engine as soon we return from this foreign function.
46026 */
46027 pArray = jx9_context_new_array(pCtx);
46028 if( pArray == 0 ){
46029 SXUNUSED(nArg); /* cc warning */
46030 SXUNUSED(apArg);
46031 /* Return NULL */
46032 jx9_result_null(pCtx);
46033 return SXRET_OK;
46034 }
46035 /* Fill with the appropriate information */
46036 SyHashForEach(&pCtx->pVm->hHostFunction,VmHashFuncStep,pArray);
46037 /* Fill with the appropriate information */
46038 SyHashForEach(&pCtx->pVm->hFunction, VmHashFuncStep,pArray);
46039 /* Return a copy of the array array */
46040 jx9_result_value(pCtx, pArray);
46041 return SXRET_OK;
46042}
46043/*
46044 * Call a user defined or foreign function where the name of the function
46045 * is stored in the pFunc parameter and the given arguments are stored
46046 * in the apArg[] array.
46047 * Return SXRET_OK if the function was successfuly called.Any other
46048 * return value indicates failure.
46049 */
46050JX9_PRIVATE sxi32 jx9VmCallUserFunction(
46051 jx9_vm *pVm, /* Target VM */
46052 jx9_value *pFunc, /* Callback name */
46053 int nArg, /* Total number of given arguments */
46054 jx9_value **apArg, /* Callback arguments */
46055 jx9_value *pResult /* Store callback return value here. NULL otherwise */
46056 )
46057{
46058 jx9_value *aStack;
46059 VmInstr aInstr[2];
46060 int i;
46061 if((pFunc->iFlags & (MEMOBJ_STRING)) == 0 ){
46062 /* Don't bother processing, it's invalid anyway */
46063 if( pResult ){
46064 /* Assume a null return value */
46065 jx9MemObjRelease(pResult);
46066 }
46067 return SXERR_INVALID;
46068 }
46069 /* Create a new operand stack */
46070 aStack = VmNewOperandStack(&(*pVm), 1+nArg);
46071 if( aStack == 0 ){
46072 jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR,
46073 "JX9 is running out of memory while invoking user callback");
46074 if( pResult ){
46075 /* Assume a null return value */
46076 jx9MemObjRelease(pResult);
46077 }
46078 return SXERR_MEM;
46079 }
46080 /* Fill the operand stack with the given arguments */
46081 for( i = 0 ; i < nArg ; i++ ){
46082 jx9MemObjLoad(apArg[i], &aStack[i]);
46083 aStack[i].nIdx = apArg[i]->nIdx;
46084 }
46085 /* Push the function name */
46086 jx9MemObjLoad(pFunc, &aStack[i]);
46087 aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */
46088 /* Emit the CALL istruction */
46089 aInstr[0].iOp = JX9_OP_CALL;
46090 aInstr[0].iP1 = nArg; /* Total number of given arguments */
46091 aInstr[0].iP2 = 0;
46092 aInstr[0].p3 = 0;
46093 /* Emit the DONE instruction */
46094 aInstr[1].iOp = JX9_OP_DONE;
46095 aInstr[1].iP1 = 1; /* Extract function return value if available */
46096 aInstr[1].iP2 = 0;
46097 aInstr[1].p3 = 0;
46098 /* Execute the function body (if available) */
46099 VmByteCodeExec(&(*pVm), aInstr, aStack, nArg, pResult);
46100 /* Clean up the mess left behind */
46101 SyMemBackendFree(&pVm->sAllocator, aStack);
46102 return JX9_OK;
46103}
46104/*
46105 * Call a user defined or foreign function whith a varibale number
46106 * of arguments where the name of the function is stored in the pFunc
46107 * parameter.
46108 * Return SXRET_OK if the function was successfuly called.Any other
46109 * return value indicates failure.
46110 */
46111JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(
46112 jx9_vm *pVm, /* Target VM */
46113 jx9_value *pFunc, /* Callback name */
46114 jx9_value *pResult, /* Store callback return value here. NULL otherwise */
46115 ... /* 0 (Zero) or more Callback arguments */
46116 )
46117{
46118 jx9_value *pArg;
46119 SySet aArg;
46120 va_list ap;
46121 sxi32 rc;
46122 SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
46123 /* Copy arguments one after one */
46124 va_start(ap, pResult);
46125 for(;;){
46126 pArg = va_arg(ap, jx9_value *);
46127 if( pArg == 0 ){
46128 break;
46129 }
46130 SySetPut(&aArg, (const void *)&pArg);
46131 }
46132 /* Call the core routine */
46133 rc = jx9VmCallUserFunction(&(*pVm), pFunc, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg), pResult);
46134 /* Cleanup */
46135 SySetRelease(&aArg);
46136 return rc;
46137}
46138/*
46139 * bool defined(string $name)
46140 * Checks whether a given named constant exists.
46141 * Parameter:
46142 * Name of the desired constant.
46143 * Return
46144 * TRUE if the given constant exists.FALSE otherwise.
46145 */
46146static int vm_builtin_defined(jx9_context *pCtx, int nArg, jx9_value **apArg)
46147{
46148 const char *zName;
46149 int nLen = 0;
46150 int res = 0;
46151 if( nArg < 1 ){
46152 /* Missing constant name, return FALSE */
46153 jx9_context_throw_error(pCtx,JX9_CTX_NOTICE,"Missing constant name");
46154 jx9_result_bool(pCtx, 0);
46155 return SXRET_OK;
46156 }
46157 /* Extract constant name */
46158 zName = jx9_value_to_string(apArg[0], &nLen);
46159 /* Perform the lookup */
46160 if( nLen > 0 && SyHashGet(&pCtx->pVm->hConstant, (const void *)zName, (sxu32)nLen) != 0 ){
46161 /* Already defined */
46162 res = 1;
46163 }
46164 jx9_result_bool(pCtx, res);
46165 return SXRET_OK;
46166}
46167/*
46168 * Hash walker callback used by the [get_defined_constants()] function
46169 * defined below.
46170 */
46171static int VmHashConstStep(SyHashEntry *pEntry, void *pUserData)
46172{
46173 jx9_value *pArray = (jx9_value *)pUserData;
46174 jx9_value sName;
46175 sxi32 rc;
46176 /* Prepare the constant name for insertion */
46177 jx9MemObjInitFromString(pArray->pVm, &sName, 0);
46178 jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
46179 /* Perform the insertion */
46180 rc = jx9_array_add_elem(pArray, 0, &sName); /* Will make it's own copy */
46181 jx9MemObjRelease(&sName);
46182 return rc;
46183}
46184/*
46185 * array get_defined_constants(void)
46186 * Returns an associative array with the names of all defined
46187 * constants.
46188 * Parameters
46189 * NONE.
46190 * Returns
46191 * Returns the names of all the constants currently defined.
46192 */
46193static int vm_builtin_get_defined_constants(jx9_context *pCtx, int nArg, jx9_value **apArg)
46194{
46195 jx9_value *pArray;
46196 /* Create the array first*/
46197 pArray = jx9_context_new_array(pCtx);
46198 if( pArray == 0 ){
46199 SXUNUSED(nArg); /* cc warning */
46200 SXUNUSED(apArg);
46201 /* Return NULL */
46202 jx9_result_null(pCtx);
46203 return SXRET_OK;
46204 }
46205 /* Fill the array with the defined constants */
46206 SyHashForEach(&pCtx->pVm->hConstant, VmHashConstStep, pArray);
46207 /* Return the created array */
46208 jx9_result_value(pCtx, pArray);
46209 return SXRET_OK;
46210}
46211/*
46212 * Section:
46213 * Random numbers/string generators.
46214 * Authors:
46215 * Symisc Systems, devel@symisc.net.
46216 * Copyright (C) Symisc Systems, http://jx9.symisc.net
46217 * Status:
46218 * Stable.
46219 */
46220/*
46221 * Generate a random 32-bit unsigned integer.
46222 * JX9 use it's own private PRNG which is based on the one
46223 * used by te SQLite3 library.
46224 */
46225JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm)
46226{
46227 sxu32 iNum;
46228 SyRandomness(&pVm->sPrng, (void *)&iNum, sizeof(sxu32));
46229 return iNum;
46230}
46231/*
46232 * Generate a random string (English Alphabet) of length nLen.
46233 * Note that the generated string is NOT null terminated.
46234 * JX9 use it's own private PRNG which is based on the one used
46235 * by te SQLite3 library.
46236 */
46237JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen)
46238{
46239 static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */
46240 int i;
46241 /* Generate a binary string first */
46242 SyRandomness(&pVm->sPrng, zBuf, (sxu32)nLen);
46243 /* Turn the binary string into english based alphabet */
46244 for( i = 0 ; i < nLen ; ++i ){
46245 zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)];
46246 }
46247}
46248/*
46249 * int rand()
46250 * Generate a random (unsigned 32-bit) integer.
46251 * Parameter
46252 * $min
46253 * The lowest value to return (default: 0)
46254 * $max
46255 * The highest value to return (default: getrandmax())
46256 * Return
46257 * A pseudo random value between min (or 0) and max (or getrandmax(), inclusive).
46258 * Note:
46259 * JX9 use it's own private PRNG which is based on the one used
46260 * by te SQLite3 library.
46261 */
46262static int vm_builtin_rand(jx9_context *pCtx, int nArg, jx9_value **apArg)
46263{
46264 sxu32 iNum;
46265 /* Generate the random number */
46266 iNum = jx9VmRandomNum(pCtx->pVm);
46267 if( nArg > 1 ){
46268 sxu32 iMin, iMax;
46269 iMin = (sxu32)jx9_value_to_int(apArg[0]);
46270 iMax = (sxu32)jx9_value_to_int(apArg[1]);
46271 if( iMin < iMax ){
46272 sxu32 iDiv = iMax+1-iMin;
46273 if( iDiv > 0 ){
46274 iNum = (iNum % iDiv)+iMin;
46275 }
46276 }else if(iMax > 0 ){
46277 iNum %= iMax;
46278 }
46279 }
46280 /* Return the number */
46281 jx9_result_int64(pCtx, (jx9_int64)iNum);
46282 return SXRET_OK;
46283}
46284/*
46285 * int getrandmax(void)
46286 * Show largest possible random value
46287 * Return
46288 * The largest possible random value returned by rand() which is in
46289 * this implementation 0xFFFFFFFF.
46290 * Note:
46291 * JX9 use it's own private PRNG which is based on the one used
46292 * by te SQLite3 library.
46293 */
46294static int vm_builtin_getrandmax(jx9_context *pCtx, int nArg, jx9_value **apArg)
46295{
46296 SXUNUSED(nArg); /* cc warning */
46297 SXUNUSED(apArg);
46298 jx9_result_int64(pCtx, SXU32_HIGH);
46299 return SXRET_OK;
46300}
46301/*
46302 * string rand_str()
46303 * string rand_str(int $len)
46304 * Generate a random string (English alphabet).
46305 * Parameter
46306 * $len
46307 * Length of the desired string (default: 16, Min: 1, Max: 1024)
46308 * Return
46309 * A pseudo random string.
46310 * Note:
46311 * JX9 use it's own private PRNG which is based on the one used
46312 * by te SQLite3 library.
46313 */
46314static int vm_builtin_rand_str(jx9_context *pCtx, int nArg, jx9_value **apArg)
46315{
46316 char zString[1024];
46317 int iLen = 0x10;
46318 if( nArg > 0 ){
46319 /* Get the desired length */
46320 iLen = jx9_value_to_int(apArg[0]);
46321 if( iLen < 1 || iLen > 1024 ){
46322 /* Default length */
46323 iLen = 0x10;
46324 }
46325 }
46326 /* Generate the random string */
46327 jx9VmRandomString(pCtx->pVm, zString, iLen);
46328 /* Return the generated string */
46329 jx9_result_string(pCtx, zString, iLen); /* Will make it's own copy */
46330 return SXRET_OK;
46331}
46332/*
46333 * Section:
46334 * Language construct implementation as foreign functions.
46335 * Authors:
46336 * Symisc Systems, devel@symisc.net.
46337 * Copyright (C) Symisc Systems, http://jx9.symisc.net
46338 * Status:
46339 * Stable.
46340 */
46341/*
46342 * void print($string...)
46343 * Output one or more messages.
46344 * Parameters
46345 * $string
46346 * Message to output.
46347 * Return
46348 * NULL.
46349 */
46350static int vm_builtin_print(jx9_context *pCtx, int nArg,jx9_value **apArg)
46351{
46352 const char *zData;
46353 int nDataLen = 0;
46354 jx9_vm *pVm;
46355 int i, rc;
46356 /* Point to the target VM */
46357 pVm = pCtx->pVm;
46358 /* Output */
46359 for( i = 0 ; i < nArg ; ++i ){
46360 zData = jx9_value_to_string(apArg[i], &nDataLen);
46361 if( nDataLen > 0 ){
46362 rc = pVm->sVmConsumer.xConsumer((const void *)zData, (unsigned int)nDataLen, pVm->sVmConsumer.pUserData);
46363 /* Increment output length */
46364 pVm->nOutputLen += nDataLen;
46365 if( rc == SXERR_ABORT ){
46366 /* Output consumer callback request an operation abort */
46367 return JX9_ABORT;
46368 }
46369 }
46370 }
46371 return SXRET_OK;
46372}
46373/*
46374 * void exit(string $msg)
46375 * void exit(int $status)
46376 * void die(string $ms)
46377 * void die(int $status)
46378 * Output a message and terminate program execution.
46379 * Parameter
46380 * If status is a string, this function prints the status just before exiting.
46381 * If status is an integer, that value will be used as the exit status
46382 * and not printed
46383 * Return
46384 * NULL
46385 */
46386static int vm_builtin_exit(jx9_context *pCtx, int nArg, jx9_value **apArg)
46387{
46388 if( nArg > 0 ){
46389 if( jx9_value_is_string(apArg[0]) ){
46390 const char *zData;
46391 int iLen = 0;
46392 /* Print exit message */
46393 zData = jx9_value_to_string(apArg[0], &iLen);
46394 jx9_context_output(pCtx, zData, iLen);
46395 }else if(jx9_value_is_int(apArg[0]) ){
46396 sxi32 iExitStatus;
46397 /* Record exit status code */
46398 iExitStatus = jx9_value_to_int(apArg[0]);
46399 pCtx->pVm->iExitStatus = iExitStatus;
46400 }
46401 }
46402 /* Abort processing immediately */
46403 return JX9_ABORT;
46404}
46405/*
46406 * Unset a memory object [i.e: a jx9_value].
46407 */
46408JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm,sxu32 nObjIdx)
46409{
46410 jx9_value *pObj;
46411 pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nObjIdx);
46412 if( pObj ){
46413 VmSlot sFree;
46414 /* Release the object */
46415 jx9MemObjRelease(pObj);
46416 /* Restore to the free list */
46417 sFree.nIdx = nObjIdx;
46418 sFree.pUserData = 0;
46419 SySetPut(&pVm->aFreeObj, (const void *)&sFree);
46420 }
46421 return SXRET_OK;
46422}
46423/*
46424 * string gettype($var)
46425 * Get the type of a variable
46426 * Parameters
46427 * $var
46428 * The variable being type checked.
46429 * Return
46430 * String representation of the given variable type.
46431 */
46432static int vm_builtin_gettype(jx9_context *pCtx, int nArg, jx9_value **apArg)
46433{
46434 const char *zType = "null";
46435 if( nArg > 0 ){
46436 zType = jx9MemObjTypeDump(apArg[0]);
46437 }
46438 /* Return the variable type */
46439 jx9_result_string(pCtx, zType, -1/*Compute length automatically*/);
46440 return SXRET_OK;
46441}
46442/*
46443 * string get_resource_type(resource $handle)
46444 * This function gets the type of the given resource.
46445 * Parameters
46446 * $handle
46447 * The evaluated resource handle.
46448 * Return
46449 * If the given handle is a resource, this function will return a string
46450 * representing its type. If the type is not identified by this function
46451 * the return value will be the string Unknown.
46452 * This function will return FALSE and generate an error if handle
46453 * is not a resource.
46454 */
46455static int vm_builtin_get_resource_type(jx9_context *pCtx, int nArg, jx9_value **apArg)
46456{
46457 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
46458 /* Missing/Invalid arguments, return FALSE*/
46459 jx9_result_bool(pCtx, 0);
46460 return SXRET_OK;
46461 }
46462 jx9_result_string_format(pCtx, "resID_%#x", apArg[0]->x.pOther);
46463 return SXRET_OK;
46464}
46465/*
46466 * void dump(expression, ....)
46467 * dump — Dumps information about a variable
46468 * Parameters
46469 * One or more expression to dump.
46470 * Returns
46471 * Nothing.
46472 */
46473static int vm_builtin_dump(jx9_context *pCtx, int nArg, jx9_value **apArg)
46474{
46475 SyBlob sDump; /* Generated dump is stored here */
46476 int i;
46477 SyBlobInit(&sDump,&pCtx->pVm->sAllocator);
46478 /* Dump one or more expressions */
46479 for( i = 0 ; i < nArg ; i++ ){
46480 jx9_value *pObj = apArg[i];
46481 /* Reset the working buffer */
46482 SyBlobReset(&sDump);
46483 /* Dump the given expression */
46484 jx9MemObjDump(&sDump,pObj);
46485 /* Output */
46486 if( SyBlobLength(&sDump) > 0 ){
46487 jx9_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
46488 }
46489 }
46490 /* Release the working buffer */
46491 SyBlobRelease(&sDump);
46492 return SXRET_OK;
46493}
46494/*
46495 * Section:
46496 * Version, Credits and Copyright related functions.
46497 * Authors:
46498 * Symisc Systems, devel@symisc.net.
46499 * Copyright (C) Symisc Systems, http://jx9.symisc.net
46500 * Stable.
46501 */
46502/*
46503 * string jx9_version(void)
46504 * string jx9_credits(void)
46505 * Returns the running version of the jx9 version.
46506 * Parameters
46507 * None
46508 * Return
46509 * Current jx9 version.
46510 */
46511static int vm_builtin_jx9_version(jx9_context *pCtx, int nArg, jx9_value **apArg)
46512{
46513 SXUNUSED(nArg);
46514 SXUNUSED(apArg); /* cc warning */
46515 /* Current engine version, signature and cipyright notice */
46516 jx9_result_string_format(pCtx,"%s %s, %s",JX9_VERSION,JX9_SIG,JX9_COPYRIGHT);
46517 return JX9_OK;
46518}
46519/*
46520 * Section:
46521 * URL related routines.
46522 * Authors:
46523 * Symisc Systems, devel@symisc.net.
46524 * Copyright (C) Symisc Systems, http://jx9.symisc.net
46525 * Status:
46526 * Stable.
46527 */
46528/* Forward declaration */
46529static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen);
46530/*
46531 * value parse_url(string $url [, int $component = -1 ])
46532 * Parse a URL and return its fields.
46533 * Parameters
46534 * $url
46535 * The URL to parse.
46536 * $component
46537 * Specify one of JX9_URL_SCHEME, JX9_URL_HOST, JX9_URL_PORT, JX9_URL_USER
46538 * JX9_URL_PASS, JX9_URL_PATH, JX9_URL_QUERY or JX9_URL_FRAGMENT to retrieve
46539 * just a specific URL component as a string (except when JX9_URL_PORT is given
46540 * in which case the return value will be an integer).
46541 * Return
46542 * If the component parameter is omitted, an associative array is returned.
46543 * At least one element will be present within the array. Potential keys within
46544 * this array are:
46545 * scheme - e.g. http
46546 * host
46547 * port
46548 * user
46549 * pass
46550 * path
46551 * query - after the question mark ?
46552 * fragment - after the hashmark #
46553 * Note:
46554 * FALSE is returned on failure.
46555 * This function work with relative URL unlike the one shipped
46556 * with the standard JX9 engine.
46557 */
46558static int vm_builtin_parse_url(jx9_context *pCtx, int nArg, jx9_value **apArg)
46559{
46560 const char *zStr; /* Input string */
46561 SyString *pComp; /* Pointer to the URI component */
46562 SyhttpUri sURI; /* Parse of the given URI */
46563 int nLen;
46564 sxi32 rc;
46565 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
46566 /* Missing/Invalid arguments, return FALSE */
46567 jx9_result_bool(pCtx, 0);
46568 return JX9_OK;
46569 }
46570 /* Extract the given URI */
46571 zStr = jx9_value_to_string(apArg[0], &nLen);
46572 if( nLen < 1 ){
46573 /* Nothing to process, return FALSE */
46574 jx9_result_bool(pCtx, 0);
46575 return JX9_OK;
46576 }
46577 /* Get a parse */
46578 rc = VmHttpSplitURI(&sURI, zStr, (sxu32)nLen);
46579 if( rc != SXRET_OK ){
46580 /* Malformed input, return FALSE */
46581 jx9_result_bool(pCtx, 0);
46582 return JX9_OK;
46583 }
46584 if( nArg > 1 ){
46585 int nComponent = jx9_value_to_int(apArg[1]);
46586 /* Refer to constant.c for constants values */
46587 switch(nComponent){
46588 case 1: /* JX9_URL_SCHEME */
46589 pComp = &sURI.sScheme;
46590 if( pComp->nByte < 1 ){
46591 /* No available value, return NULL */
46592 jx9_result_null(pCtx);
46593 }else{
46594 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46595 }
46596 break;
46597 case 2: /* JX9_URL_HOST */
46598 pComp = &sURI.sHost;
46599 if( pComp->nByte < 1 ){
46600 /* No available value, return NULL */
46601 jx9_result_null(pCtx);
46602 }else{
46603 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46604 }
46605 break;
46606 case 3: /* JX9_URL_PORT */
46607 pComp = &sURI.sPort;
46608 if( pComp->nByte < 1 ){
46609 /* No available value, return NULL */
46610 jx9_result_null(pCtx);
46611 }else{
46612 int iPort = 0;
46613 /* Cast the value to integer */
46614 SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
46615 jx9_result_int(pCtx, iPort);
46616 }
46617 break;
46618 case 4: /* JX9_URL_USER */
46619 pComp = &sURI.sUser;
46620 if( pComp->nByte < 1 ){
46621 /* No available value, return NULL */
46622 jx9_result_null(pCtx);
46623 }else{
46624 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46625 }
46626 break;
46627 case 5: /* JX9_URL_PASS */
46628 pComp = &sURI.sPass;
46629 if( pComp->nByte < 1 ){
46630 /* No available value, return NULL */
46631 jx9_result_null(pCtx);
46632 }else{
46633 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46634 }
46635 break;
46636 case 7: /* JX9_URL_QUERY */
46637 pComp = &sURI.sQuery;
46638 if( pComp->nByte < 1 ){
46639 /* No available value, return NULL */
46640 jx9_result_null(pCtx);
46641 }else{
46642 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46643 }
46644 break;
46645 case 8: /* JX9_URL_FRAGMENT */
46646 pComp = &sURI.sFragment;
46647 if( pComp->nByte < 1 ){
46648 /* No available value, return NULL */
46649 jx9_result_null(pCtx);
46650 }else{
46651 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46652 }
46653 break;
46654 case 6: /* JX9_URL_PATH */
46655 pComp = &sURI.sPath;
46656 if( pComp->nByte < 1 ){
46657 /* No available value, return NULL */
46658 jx9_result_null(pCtx);
46659 }else{
46660 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46661 }
46662 break;
46663 default:
46664 /* No such entry, return NULL */
46665 jx9_result_null(pCtx);
46666 break;
46667 }
46668 }else{
46669 jx9_value *pArray, *pValue;
46670 /* Return an associative array */
46671 pArray = jx9_context_new_array(pCtx); /* Empty array */
46672 pValue = jx9_context_new_scalar(pCtx); /* Array value */
46673 if( pArray == 0 || pValue == 0 ){
46674 /* Out of memory */
46675 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "jx9 engine is running out of memory");
46676 /* Return false */
46677 jx9_result_bool(pCtx, 0);
46678 return JX9_OK;
46679 }
46680 /* Fill the array */
46681 pComp = &sURI.sScheme;
46682 if( pComp->nByte > 0 ){
46683 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46684 jx9_array_add_strkey_elem(pArray, "scheme", pValue); /* Will make it's own copy */
46685 }
46686 /* Reset the string cursor */
46687 jx9_value_reset_string_cursor(pValue);
46688 pComp = &sURI.sHost;
46689 if( pComp->nByte > 0 ){
46690 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46691 jx9_array_add_strkey_elem(pArray, "host", pValue); /* Will make it's own copy */
46692 }
46693 /* Reset the string cursor */
46694 jx9_value_reset_string_cursor(pValue);
46695 pComp = &sURI.sPort;
46696 if( pComp->nByte > 0 ){
46697 int iPort = 0;/* cc warning */
46698 /* Convert to integer */
46699 SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
46700 jx9_value_int(pValue, iPort);
46701 jx9_array_add_strkey_elem(pArray, "port", pValue); /* Will make it's own copy */
46702 }
46703 /* Reset the string cursor */
46704 jx9_value_reset_string_cursor(pValue);
46705 pComp = &sURI.sUser;
46706 if( pComp->nByte > 0 ){
46707 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46708 jx9_array_add_strkey_elem(pArray, "user", pValue); /* Will make it's own copy */
46709 }
46710 /* Reset the string cursor */
46711 jx9_value_reset_string_cursor(pValue);
46712 pComp = &sURI.sPass;
46713 if( pComp->nByte > 0 ){
46714 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46715 jx9_array_add_strkey_elem(pArray, "pass", pValue); /* Will make it's own copy */
46716 }
46717 /* Reset the string cursor */
46718 jx9_value_reset_string_cursor(pValue);
46719 pComp = &sURI.sPath;
46720 if( pComp->nByte > 0 ){
46721 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46722 jx9_array_add_strkey_elem(pArray, "path", pValue); /* Will make it's own copy */
46723 }
46724 /* Reset the string cursor */
46725 jx9_value_reset_string_cursor(pValue);
46726 pComp = &sURI.sQuery;
46727 if( pComp->nByte > 0 ){
46728 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46729 jx9_array_add_strkey_elem(pArray, "query", pValue); /* Will make it's own copy */
46730 }
46731 /* Reset the string cursor */
46732 jx9_value_reset_string_cursor(pValue);
46733 pComp = &sURI.sFragment;
46734 if( pComp->nByte > 0 ){
46735 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46736 jx9_array_add_strkey_elem(pArray, "fragment", pValue); /* Will make it's own copy */
46737 }
46738 /* Return the created array */
46739 jx9_result_value(pCtx, pArray);
46740 /* NOTE:
46741 * Don't worry about freeing 'pValue', everything will be released
46742 * automatically as soon we return from this function.
46743 */
46744 }
46745 /* All done */
46746 return JX9_OK;
46747}
46748/*
46749 * Section:
46750 * Array related routines.
46751 * Authors:
46752 * Symisc Systems, devel@symisc.net.
46753 * Copyright (C) Symisc Systems, http://jx9.symisc.net
46754 * Status:
46755 * Stable.
46756 * Note 2012-5-21 01:04:15:
46757 * Array related functions that need access to the underlying
46758 * virtual machine are implemented here rather than 'hashmap.c'
46759 */
46760/*
46761 * The [extract()] function store it's state information in an instance
46762 * of the following structure.
46763 */
46764typedef struct extract_aux_data extract_aux_data;
46765struct extract_aux_data
46766{
46767 jx9_vm *pVm; /* VM that own this instance */
46768 int iCount; /* Number of variables successfully imported */
46769 const char *zPrefix; /* Prefix name */
46770 int Prefixlen; /* Prefix length */
46771 int iFlags; /* Control flags */
46772 char zWorker[1024]; /* Working buffer */
46773};
46774/* Forward declaration */
46775static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData);
46776/*
46777 * int extract(array $var_array[, int $extract_type = EXTR_OVERWRITE[, string $prefix = NULL ]])
46778 * Import variables into the current symbol table from an array.
46779 * Parameters
46780 * $var_array
46781 * An associative array. This function treats keys as variable names and values
46782 * as variable values. For each key/value pair it will create a variable in the current symbol
46783 * table, subject to extract_type and prefix parameters.
46784 * You must use an associative array; a numerically indexed array will not produce results
46785 * unless you use EXTR_PREFIX_ALL or EXTR_PREFIX_INVALID.
46786 * $extract_type
46787 * The way invalid/numeric keys and collisions are treated is determined by the extract_type.
46788 * It can be one of the following values:
46789 * EXTR_OVERWRITE
46790 * If there is a collision, overwrite the existing variable.
46791 * EXTR_SKIP
46792 * If there is a collision, don't overwrite the existing variable.
46793 * EXTR_PREFIX_SAME
46794 * If there is a collision, prefix the variable name with prefix.
46795 * EXTR_PREFIX_ALL
46796 * Prefix all variable names with prefix.
46797 * EXTR_PREFIX_INVALID
46798 * Only prefix invalid/numeric variable names with prefix.
46799 * EXTR_IF_EXISTS
46800 * Only overwrite the variable if it already exists in the current symbol table
46801 * otherwise do nothing.
46802 * This is useful for defining a list of valid variables and then extracting only those
46803 * variables you have defined out of $_REQUEST, for example.
46804 * EXTR_PREFIX_IF_EXISTS
46805 * Only create prefixed variable names if the non-prefixed version of the same variable exists in
46806 * the current symbol table.
46807 * $prefix
46808 * Note that prefix is only required if extract_type is EXTR_PREFIX_SAME, EXTR_PREFIX_ALL
46809 * EXTR_PREFIX_INVALID or EXTR_PREFIX_IF_EXISTS. If the prefixed result is not a valid variable name
46810 * it is not imported into the symbol table. Prefixes are automatically separated from the array key by an
46811 * underscore character.
46812 * Return
46813 * Returns the number of variables successfully imported into the symbol table.
46814 */
46815static int vm_builtin_extract(jx9_context *pCtx, int nArg, jx9_value **apArg)
46816{
46817 extract_aux_data sAux;
46818 jx9_hashmap *pMap;
46819 if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
46820 /* Missing/Invalid arguments, return 0 */
46821 jx9_result_int(pCtx, 0);
46822 return JX9_OK;
46823 }
46824 /* Point to the target hashmap */
46825 pMap = (jx9_hashmap *)apArg[0]->x.pOther;
46826 if( pMap->nEntry < 1 ){
46827 /* Empty map, return 0 */
46828 jx9_result_int(pCtx, 0);
46829 return JX9_OK;
46830 }
46831 /* Prepare the aux data */
46832 SyZero(&sAux, sizeof(extract_aux_data)-sizeof(sAux.zWorker));
46833 if( nArg > 1 ){
46834 sAux.iFlags = jx9_value_to_int(apArg[1]);
46835 if( nArg > 2 ){
46836 sAux.zPrefix = jx9_value_to_string(apArg[2], &sAux.Prefixlen);
46837 }
46838 }
46839 sAux.pVm = pCtx->pVm;
46840 /* Invoke the worker callback */
46841 jx9HashmapWalk(pMap, VmExtractCallback, &sAux);
46842 /* Number of variables successfully imported */
46843 jx9_result_int(pCtx, sAux.iCount);
46844 return JX9_OK;
46845}
46846/*
46847 * Worker callback for the [extract()] function defined
46848 * below.
46849 */
46850static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
46851{
46852 extract_aux_data *pAux = (extract_aux_data *)pUserData;
46853 int iFlags = pAux->iFlags;
46854 jx9_vm *pVm = pAux->pVm;
46855 jx9_value *pObj;
46856 SyString sVar;
46857 if( (iFlags & 0x10/* EXTR_PREFIX_INVALID */) && (pKey->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL|MEMOBJ_REAL))){
46858 iFlags |= 0x08; /*EXTR_PREFIX_ALL*/
46859 }
46860 /* Perform a string cast */
46861 jx9MemObjToString(pKey);
46862 if( SyBlobLength(&pKey->sBlob) < 1 ){
46863 /* Unavailable variable name */
46864 return SXRET_OK;
46865 }
46866 sVar.nByte = 0; /* cc warning */
46867 if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/ ) && pAux->Prefixlen > 0 ){
46868 sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker, sizeof(pAux->zWorker), "%.*s_%.*s",
46869 pAux->Prefixlen, pAux->zPrefix,
46870 SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob)
46871 );
46872 }else{
46873 sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob), pAux->zWorker,
46874 SXMIN(SyBlobLength(&pKey->sBlob), sizeof(pAux->zWorker)));
46875 }
46876 sVar.zString = pAux->zWorker;
46877 /* Try to extract the variable */
46878 pObj = VmExtractMemObj(pVm, &sVar, TRUE, FALSE);
46879 if( pObj ){
46880 /* Collision */
46881 if( iFlags & 0x02 /* EXTR_SKIP */ ){
46882 return SXRET_OK;
46883 }
46884 if( iFlags & 0x04 /* EXTR_PREFIX_SAME */ ){
46885 if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/) || pAux->Prefixlen < 1){
46886 /* Already prefixed */
46887 return SXRET_OK;
46888 }
46889 sVar.nByte = SyBufferFormat(
46890 pAux->zWorker, sizeof(pAux->zWorker),
46891 "%.*s_%.*s",
46892 pAux->Prefixlen, pAux->zPrefix,
46893 SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob)
46894 );
46895 pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE);
46896 }
46897 }else{
46898 /* Create the variable */
46899 pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE);
46900 }
46901 if( pObj ){
46902 /* Overwrite the old value */
46903 jx9MemObjStore(pValue, pObj);
46904 /* Increment counter */
46905 pAux->iCount++;
46906 }
46907 return SXRET_OK;
46908}
46909/*
46910 * Compile and evaluate a JX9 chunk at run-time.
46911 * Refer to the include language construct implementation for more
46912 * information.
46913 */
46914static sxi32 VmEvalChunk(
46915 jx9_vm *pVm, /* Underlying Virtual Machine */
46916 jx9_context *pCtx, /* Call Context */
46917 SyString *pChunk, /* JX9 chunk to evaluate */
46918 int iFlags, /* Compile flag */
46919 int bTrueReturn /* TRUE to return execution result */
46920 )
46921{
46922 SySet *pByteCode, aByteCode;
46923 ProcConsumer xErr = 0;
46924 void *pErrData = 0;
46925 /* Initialize bytecode container */
46926 SySetInit(&aByteCode, &pVm->sAllocator, sizeof(VmInstr));
46927 SySetAlloc(&aByteCode, 0x20);
46928 /* Reset the code generator */
46929 if( bTrueReturn ){
46930 /* Included file, log compile-time errors */
46931 xErr = pVm->pEngine->xConf.xErr;
46932 pErrData = pVm->pEngine->xConf.pErrData;
46933 }
46934 jx9ResetCodeGenerator(pVm, xErr, pErrData);
46935 /* Swap bytecode container */
46936 pByteCode = pVm->pByteContainer;
46937 pVm->pByteContainer = &aByteCode;
46938 /* Compile the chunk */
46939 jx9CompileScript(pVm, pChunk, iFlags);
46940 if( pVm->sCodeGen.nErr > 0 ){
46941 /* Compilation error, return false */
46942 if( pCtx ){
46943 jx9_result_bool(pCtx, 0);
46944 }
46945 }else{
46946 jx9_value sResult; /* Return value */
46947 if( SXRET_OK != jx9VmEmitInstr(pVm, JX9_OP_DONE, 0, 0, 0, 0) ){
46948 /* Out of memory */
46949 if( pCtx ){
46950 jx9_result_bool(pCtx, 0);
46951 }
46952 goto Cleanup;
46953 }
46954 if( bTrueReturn ){
46955 /* Assume a boolean true return value */
46956 jx9MemObjInitFromBool(pVm, &sResult, 1);
46957 }else{
46958 /* Assume a null return value */
46959 jx9MemObjInit(pVm, &sResult);
46960 }
46961 /* Execute the compiled chunk */
46962 VmLocalExec(pVm, &aByteCode, &sResult);
46963 if( pCtx ){
46964 /* Set the execution result */
46965 jx9_result_value(pCtx, &sResult);
46966 }
46967 jx9MemObjRelease(&sResult);
46968 }
46969Cleanup:
46970 /* Cleanup the mess left behind */
46971 pVm->pByteContainer = pByteCode;
46972 SySetRelease(&aByteCode);
46973 return SXRET_OK;
46974}
46975/*
46976 * Check if a file path is already included.
46977 */
46978static int VmIsIncludedFile(jx9_vm *pVm, SyString *pFile)
46979{
46980 SyString *aEntries;
46981 sxu32 n;
46982 aEntries = (SyString *)SySetBasePtr(&pVm->aIncluded);
46983 /* Perform a linear search */
46984 for( n = 0 ; n < SySetUsed(&pVm->aIncluded) ; ++n ){
46985 if( SyStringCmp(pFile, &aEntries[n], SyMemcmp) == 0 ){
46986 /* Already included */
46987 return TRUE;
46988 }
46989 }
46990 return FALSE;
46991}
46992/*
46993 * Push a file path in the appropriate VM container.
46994 */
46995JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew)
46996{
46997 SyString sPath;
46998 char *zDup;
46999#ifdef __WINNT__
47000 char *zCur;
47001#endif
47002 sxi32 rc;
47003 if( nLen < 0 ){
47004 nLen = SyStrlen(zPath);
47005 }
47006 /* Duplicate the file path first */
47007 zDup = SyMemBackendStrDup(&pVm->sAllocator, zPath, nLen);
47008 if( zDup == 0 ){
47009 return SXERR_MEM;
47010 }
47011#ifdef __WINNT__
47012 /* Normalize path on windows
47013 * Example:
47014 * Path/To/File.jx9
47015 * becomes
47016 * path\to\file.jx9
47017 */
47018 zCur = zDup;
47019 while( zCur[0] != 0 ){
47020 if( zCur[0] == '/' ){
47021 zCur[0] = '\\';
47022 }else if( (unsigned char)zCur[0] < 0xc0 && SyisUpper(zCur[0]) ){
47023 int c = SyToLower(zCur[0]);
47024 zCur[0] = (char)c; /* MSVC stupidity */
47025 }
47026 zCur++;
47027 }
47028#endif
47029 /* Install the file path */
47030 SyStringInitFromBuf(&sPath, zDup, nLen);
47031 if( !bMain ){
47032 if( VmIsIncludedFile(&(*pVm), &sPath) ){
47033 /* Already included */
47034 *pNew = 0;
47035 }else{
47036 /* Insert in the corresponding container */
47037 rc = SySetPut(&pVm->aIncluded, (const void *)&sPath);
47038 if( rc != SXRET_OK ){
47039 SyMemBackendFree(&pVm->sAllocator, zDup);
47040 return rc;
47041 }
47042 *pNew = 1;
47043 }
47044 }
47045 SySetPut(&pVm->aFiles, (const void *)&sPath);
47046 return SXRET_OK;
47047}
47048/*
47049 * Compile and Execute a JX9 script at run-time.
47050 * SXRET_OK is returned on sucessful evaluation.Any other return values
47051 * indicates failure.
47052 * Note that the JX9 script to evaluate can be a local or remote file.In
47053 * either cases the [jx9StreamReadWholeFile()] function handle all the underlying
47054 * operations.
47055 * If the [jJX9_DISABLE_BUILTIN_FUNC] compile-time directive is defined, then
47056 * this function is a no-op.
47057 * Refer to the implementation of the include(), import() language
47058 * constructs for more information.
47059 */
47060static sxi32 VmExecIncludedFile(
47061 jx9_context *pCtx, /* Call Context */
47062 SyString *pPath, /* Script path or URL*/
47063 int IncludeOnce /* TRUE if called from import() or require_once() */
47064 )
47065{
47066 sxi32 rc;
47067#ifndef JX9_DISABLE_BUILTIN_FUNC
47068 const jx9_io_stream *pStream;
47069 SyBlob sContents;
47070 void *pHandle;
47071 jx9_vm *pVm;
47072 int isNew;
47073 /* Initialize fields */
47074 pVm = pCtx->pVm;
47075 SyBlobInit(&sContents, &pVm->sAllocator);
47076 isNew = 0;
47077 /* Extract the associated stream */
47078 pStream = jx9VmGetStreamDevice(pVm, &pPath->zString, pPath->nByte);
47079 /*
47080 * Open the file or the URL [i.e: http://jx9.symisc.net/example/hello.jx9.txt"]
47081 * in a read-only mode.
47082 */
47083 pHandle = jx9StreamOpenHandle(pVm, pStream,pPath->zString, JX9_IO_OPEN_RDONLY, TRUE, 0, TRUE, &isNew);
47084 if( pHandle == 0 ){
47085 return SXERR_IO;
47086 }
47087 rc = SXRET_OK; /* Stupid cc warning */
47088 if( IncludeOnce && !isNew ){
47089 /* Already included */
47090 rc = SXERR_EXISTS;
47091 }else{
47092 /* Read the whole file contents */
47093 rc = jx9StreamReadWholeFile(pHandle, pStream, &sContents);
47094 if( rc == SXRET_OK ){
47095 SyString sScript;
47096 /* Compile and execute the script */
47097 SyStringInitFromBuf(&sScript, SyBlobData(&sContents), SyBlobLength(&sContents));
47098 VmEvalChunk(pCtx->pVm, &(*pCtx), &sScript, 0, TRUE);
47099 }
47100 }
47101 /* Pop from the set of included file */
47102 (void)SySetPop(&pVm->aFiles);
47103 /* Close the handle */
47104 jx9StreamCloseHandle(pStream, pHandle);
47105 /* Release the working buffer */
47106 SyBlobRelease(&sContents);
47107#else
47108 pCtx = 0; /* cc warning */
47109 pPath = 0;
47110 IncludeOnce = 0;
47111 rc = SXERR_IO;
47112#endif /* JX9_DISABLE_BUILTIN_FUNC */
47113 return rc;
47114}
47115/* * include:
47116 * According to the JX9 reference manual.
47117 * The include() function includes and evaluates the specified file.
47118 * Files are included based on the file path given or, if none is given
47119 * the include_path specified.If the file isn't found in the include_path
47120 * include() will finally check in the calling script's own directory
47121 * and the current working directory before failing. The include()
47122 * construct will emit a warning if it cannot find a file; this is different
47123 * behavior from require(), which will emit a fatal error.
47124 * If a path is defined — whether absolute (starting with a drive letter
47125 * or \ on Windows, or / on Unix/Linux systems) or relative to the current
47126 * directory (starting with . or ..) — the include_path will be ignored altogether.
47127 * For example, if a filename begins with ../, the parser will look in the parent
47128 * directory to find the requested file.
47129 * When a file is included, the code it contains inherits the variable scope
47130 * of the line on which the include occurs. Any variables available at that line
47131 * in the calling file will be available within the called file, from that point forward.
47132 * However, all functions and objectes defined in the included file have the global scope.
47133 */
47134static int vm_builtin_include(jx9_context *pCtx, int nArg, jx9_value **apArg)
47135{
47136 SyString sFile;
47137 sxi32 rc;
47138 if( nArg < 1 ){
47139 /* Nothing to evaluate, return NULL */
47140 jx9_result_null(pCtx);
47141 return SXRET_OK;
47142 }
47143 /* File to include */
47144 sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
47145 if( sFile.nByte < 1 ){
47146 /* Empty string, return NULL */
47147 jx9_result_null(pCtx);
47148 return SXRET_OK;
47149 }
47150 /* Open, compile and execute the desired script */
47151 rc = VmExecIncludedFile(&(*pCtx), &sFile, FALSE);
47152 if( rc != SXRET_OK ){
47153 /* Emit a warning and return false */
47154 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
47155 jx9_result_bool(pCtx, 0);
47156 }
47157 return SXRET_OK;
47158}
47159/*
47160 * import:
47161 * According to the JX9 reference manual.
47162 * The import() statement includes and evaluates the specified file during
47163 * the execution of the script. This is a behavior similar to the include()
47164 * statement, with the only difference being that if the code from a file has already
47165 * been included, it will not be included again. As the name suggests, it will be included
47166 * just once.
47167 */
47168static int vm_builtin_import(jx9_context *pCtx, int nArg, jx9_value **apArg)
47169{
47170 SyString sFile;
47171 sxi32 rc;
47172 if( nArg < 1 ){
47173 /* Nothing to evaluate, return NULL */
47174 jx9_result_null(pCtx);
47175 return SXRET_OK;
47176 }
47177 /* File to include */
47178 sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
47179 if( sFile.nByte < 1 ){
47180 /* Empty string, return NULL */
47181 jx9_result_null(pCtx);
47182 return SXRET_OK;
47183 }
47184 /* Open, compile and execute the desired script */
47185 rc = VmExecIncludedFile(&(*pCtx), &sFile, TRUE);
47186 if( rc == SXERR_EXISTS ){
47187 /* File already included, return TRUE */
47188 jx9_result_bool(pCtx, 1);
47189 return SXRET_OK;
47190 }
47191 if( rc != SXRET_OK ){
47192 /* Emit a warning and return false */
47193 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
47194 jx9_result_bool(pCtx, 0);
47195 }
47196 return SXRET_OK;
47197}
47198/*
47199 * Section:
47200 * Command line arguments processing.
47201 * Authors:
47202 * Symisc Systems, devel@symisc.net.
47203 * Copyright (C) Symisc Systems, http://jx9.symisc.net
47204 * Status:
47205 * Stable.
47206 */
47207/*
47208 * Check if a short option argument [i.e: -c] is available in the command
47209 * line string. Return a pointer to the start of the stream on success.
47210 * NULL otherwise.
47211 */
47212static const char * VmFindShortOpt(int c, const char *zIn, const char *zEnd)
47213{
47214 while( zIn < zEnd ){
47215 if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == c ){
47216 /* Got one */
47217 return &zIn[1];
47218 }
47219 /* Advance the cursor */
47220 zIn++;
47221 }
47222 /* No such option */
47223 return 0;
47224}
47225/*
47226 * Check if a long option argument [i.e: --opt] is available in the command
47227 * line string. Return a pointer to the start of the stream on success.
47228 * NULL otherwise.
47229 */
47230static const char * VmFindLongOpt(const char *zLong, int nByte, const char *zIn, const char *zEnd)
47231{
47232 const char *zOpt;
47233 while( zIn < zEnd ){
47234 if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == '-' ){
47235 zIn += 2;
47236 zOpt = zIn;
47237 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
47238 if( zIn[0] == '=' /* --opt=val */){
47239 break;
47240 }
47241 zIn++;
47242 }
47243 /* Test */
47244 if( (int)(zIn-zOpt) == nByte && SyMemcmp(zOpt, zLong, nByte) == 0 ){
47245 /* Got one, return it's value */
47246 return zIn;
47247 }
47248
47249 }else{
47250 zIn++;
47251 }
47252 }
47253 /* No such option */
47254 return 0;
47255}
47256/*
47257 * Long option [i.e: --opt] arguments private data structure.
47258 */
47259struct getopt_long_opt
47260{
47261 const char *zArgIn, *zArgEnd; /* Command line arguments */
47262 jx9_value *pWorker; /* Worker variable*/
47263 jx9_value *pArray; /* getopt() return value */
47264 jx9_context *pCtx; /* Call Context */
47265};
47266/* Forward declaration */
47267static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData);
47268/*
47269 * Extract short or long argument option values.
47270 */
47271static void VmExtractOptArgValue(
47272 jx9_value *pArray, /* getopt() return value */
47273 jx9_value *pWorker, /* Worker variable */
47274 const char *zArg, /* Argument stream */
47275 const char *zArgEnd, /* End of the argument stream */
47276 int need_val, /* TRUE to fetch option argument */
47277 jx9_context *pCtx, /* Call Context */
47278 const char *zName /* Option name */)
47279{
47280 jx9_value_bool(pWorker, 0);
47281 if( !need_val ){
47282 /*
47283 * Option does not need arguments.
47284 * Insert the option name and a boolean FALSE.
47285 */
47286 jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
47287 }else{
47288 const char *zCur;
47289 /* Extract option argument */
47290 zArg++;
47291 if( zArg < zArgEnd && zArg[0] == '=' ){
47292 zArg++;
47293 }
47294 while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
47295 zArg++;
47296 }
47297 if( zArg >= zArgEnd || zArg[0] == '-' ){
47298 /*
47299 * Argument not found.
47300 * Insert the option name and a boolean FALSE.
47301 */
47302 jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
47303 return;
47304 }
47305 /* Delimit the value */
47306 zCur = zArg;
47307 if( zArg[0] == '\'' || zArg[0] == '"' ){
47308 int d = zArg[0];
47309 /* Delimt the argument */
47310 zArg++;
47311 zCur = zArg;
47312 while( zArg < zArgEnd ){
47313 if( zArg[0] == d && zArg[-1] != '\\' ){
47314 /* Delimiter found, exit the loop */
47315 break;
47316 }
47317 zArg++;
47318 }
47319 /* Save the value */
47320 jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
47321 if( zArg < zArgEnd ){ zArg++; }
47322 }else{
47323 while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
47324 zArg++;
47325 }
47326 /* Save the value */
47327 jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
47328 }
47329 /*
47330 * Check if we are dealing with multiple values.
47331 * If so, create an array to hold them, rather than a scalar variable.
47332 */
47333 while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
47334 zArg++;
47335 }
47336 if( zArg < zArgEnd && zArg[0] != '-' ){
47337 jx9_value *pOptArg; /* Array of option arguments */
47338 pOptArg = jx9_context_new_array(pCtx);
47339 if( pOptArg == 0 ){
47340 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
47341 }else{
47342 /* Insert the first value */
47343 jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */
47344 for(;;){
47345 if( zArg >= zArgEnd || zArg[0] == '-' ){
47346 /* No more value */
47347 break;
47348 }
47349 /* Delimit the value */
47350 zCur = zArg;
47351 if( zArg < zArgEnd && zArg[0] == '\\' ){
47352 zArg++;
47353 zCur = zArg;
47354 }
47355 while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
47356 zArg++;
47357 }
47358 /* Reset the string cursor */
47359 jx9_value_reset_string_cursor(pWorker);
47360 /* Save the value */
47361 jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
47362 /* Insert */
47363 jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */
47364 /* Jump trailing white spaces */
47365 while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
47366 zArg++;
47367 }
47368 }
47369 /* Insert the option arg array */
47370 jx9_array_add_strkey_elem(pArray, (const char *)zName, pOptArg); /* Will make it's own copy */
47371 /* Safely release */
47372 jx9_context_release_value(pCtx, pOptArg);
47373 }
47374 }else{
47375 /* Single value */
47376 jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
47377 }
47378 }
47379}
47380/*
47381 * array getopt(string $options[, array $longopts ])
47382 * Gets options from the command line argument list.
47383 * Parameters
47384 * $options
47385 * Each character in this string will be used as option characters
47386 * and matched against options passed to the script starting with
47387 * a single hyphen (-). For example, an option string "x" recognizes
47388 * an option -x. Only a-z, A-Z and 0-9 are allowed.
47389 * $longopts
47390 * An array of options. Each element in this array will be used as option
47391 * strings and matched against options passed to the script starting with
47392 * two hyphens (--). For example, an longopts element "opt" recognizes an
47393 * option --opt.
47394 * Return
47395 * This function will return an array of option / argument pairs or FALSE
47396 * on failure.
47397 */
47398static int vm_builtin_getopt(jx9_context *pCtx, int nArg, jx9_value **apArg)
47399{
47400 const char *zIn, *zEnd, *zArg, *zArgIn, *zArgEnd;
47401 struct getopt_long_opt sLong;
47402 jx9_value *pArray, *pWorker;
47403 SyBlob *pArg;
47404 int nByte;
47405 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
47406 /* Missing/Invalid arguments, return FALSE */
47407 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Missing/Invalid option arguments");
47408 jx9_result_bool(pCtx, 0);
47409 return JX9_OK;
47410 }
47411 /* Extract option arguments */
47412 zIn = jx9_value_to_string(apArg[0], &nByte);
47413 zEnd = &zIn[nByte];
47414 /* Point to the string representation of the $argv[] array */
47415 pArg = &pCtx->pVm->sArgv;
47416 /* Create a new empty array and a worker variable */
47417 pArray = jx9_context_new_array(pCtx);
47418 pWorker = jx9_context_new_scalar(pCtx);
47419 if( pArray == 0 || pWorker == 0 ){
47420 jx9_context_throw_error(pCtx,JX9_CTX_ERR, "JX9 is running out of memory");
47421 jx9_result_bool(pCtx, 0);
47422 return JX9_OK;
47423 }
47424 if( SyBlobLength(pArg) < 1 ){
47425 /* Empty command line, return the empty array*/
47426 jx9_result_value(pCtx, pArray);
47427 /* Everything will be released automatically when we return
47428 * from this function.
47429 */
47430 return JX9_OK;
47431 }
47432 zArgIn = (const char *)SyBlobData(pArg);
47433 zArgEnd = &zArgIn[SyBlobLength(pArg)];
47434 /* Fill the long option structure */
47435 sLong.pArray = pArray;
47436 sLong.pWorker = pWorker;
47437 sLong.zArgIn = zArgIn;
47438 sLong.zArgEnd = zArgEnd;
47439 sLong.pCtx = pCtx;
47440 /* Start processing */
47441 while( zIn < zEnd ){
47442 int c = zIn[0];
47443 int need_val = 0;
47444 /* Advance the stream cursor */
47445 zIn++;
47446 /* Ignore non-alphanum characters */
47447 if( !SyisAlphaNum(c) ){
47448 continue;
47449 }
47450 if( zIn < zEnd && zIn[0] == ':' ){
47451 zIn++;
47452 need_val = 1;
47453 if( zIn < zEnd && zIn[0] == ':' ){
47454 zIn++;
47455 }
47456 }
47457 /* Find option */
47458 zArg = VmFindShortOpt(c, zArgIn, zArgEnd);
47459 if( zArg == 0 ){
47460 /* No such option */
47461 continue;
47462 }
47463 /* Extract option argument value */
47464 VmExtractOptArgValue(pArray, pWorker, zArg, zArgEnd, need_val, pCtx, (const char *)&c);
47465 }
47466 if( nArg > 1 && jx9_value_is_json_array(apArg[1]) && jx9_array_count(apArg[1]) > 0 ){
47467 /* Process long options */
47468 jx9_array_walk(apArg[1], VmProcessLongOpt, &sLong);
47469 }
47470 /* Return the option array */
47471 jx9_result_value(pCtx, pArray);
47472 /*
47473 * Don't worry about freeing memory, everything will be released
47474 * automatically as soon we return from this foreign function.
47475 */
47476 return JX9_OK;
47477}
47478/*
47479 * Array walker callback used for processing long options values.
47480 */
47481static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData)
47482{
47483 struct getopt_long_opt *pOpt = (struct getopt_long_opt *)pUserData;
47484 const char *zArg, *zOpt, *zEnd;
47485 int need_value = 0;
47486 int nByte;
47487 /* Value must be of type string */
47488 if( !jx9_value_is_string(pValue) ){
47489 /* Simply ignore */
47490 return JX9_OK;
47491 }
47492 zOpt = jx9_value_to_string(pValue, &nByte);
47493 if( nByte < 1 ){
47494 /* Empty string, ignore */
47495 return JX9_OK;
47496 }
47497 zEnd = &zOpt[nByte - 1];
47498 if( zEnd[0] == ':' ){
47499 char *zTerm;
47500 /* Try to extract a value */
47501 need_value = 1;
47502 while( zEnd >= zOpt && zEnd[0] == ':' ){
47503 zEnd--;
47504 }
47505 if( zOpt >= zEnd ){
47506 /* Empty string, ignore */
47507 SXUNUSED(pKey);
47508 return JX9_OK;
47509 }
47510 zEnd++;
47511 zTerm = (char *)zEnd;
47512 zTerm[0] = 0;
47513 }else{
47514 zEnd = &zOpt[nByte];
47515 }
47516 /* Find the option */
47517 zArg = VmFindLongOpt(zOpt, (int)(zEnd-zOpt), pOpt->zArgIn, pOpt->zArgEnd);
47518 if( zArg == 0 ){
47519 /* No such option, return immediately */
47520 return JX9_OK;
47521 }
47522 /* Try to extract a value */
47523 VmExtractOptArgValue(pOpt->pArray, pOpt->pWorker, zArg, pOpt->zArgEnd, need_value, pOpt->pCtx, zOpt);
47524 return JX9_OK;
47525}
47526/*
47527 * int utf8_encode(string $input)
47528 * UTF-8 encoding.
47529 * This function encodes the string data to UTF-8, and returns the encoded version.
47530 * UTF-8 is a standard mechanism used by Unicode for encoding wide character values
47531 * into a byte stream. UTF-8 is transparent to plain ASCII characters, is self-synchronized
47532 * (meaning it is possible for a program to figure out where in the bytestream characters start)
47533 * and can be used with normal string comparison functions for sorting and such.
47534 * Notes on UTF-8 (According to SQLite3 authors):
47535 * Byte-0 Byte-1 Byte-2 Byte-3 Value
47536 * 0xxxxxxx 00000000 00000000 0xxxxxxx
47537 * 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx
47538 * 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx
47539 * 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx
47540 * Parameters
47541 * $input
47542 * String to encode or NULL on failure.
47543 * Return
47544 * An UTF-8 encoded string.
47545 */
47546static int vm_builtin_utf8_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
47547{
47548 const unsigned char *zIn, *zEnd;
47549 int nByte, c, e;
47550 if( nArg < 1 ){
47551 /* Missing arguments, return null */
47552 jx9_result_null(pCtx);
47553 return JX9_OK;
47554 }
47555 /* Extract the target string */
47556 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
47557 if( nByte < 1 ){
47558 /* Empty string, return null */
47559 jx9_result_null(pCtx);
47560 return JX9_OK;
47561 }
47562 zEnd = &zIn[nByte];
47563 /* Start the encoding process */
47564 for(;;){
47565 if( zIn >= zEnd ){
47566 /* End of input */
47567 break;
47568 }
47569 c = zIn[0];
47570 /* Advance the stream cursor */
47571 zIn++;
47572 /* Encode */
47573 if( c<0x00080 ){
47574 e = (c&0xFF);
47575 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47576 }else if( c<0x00800 ){
47577 e = 0xC0 + ((c>>6)&0x1F);
47578 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47579 e = 0x80 + (c & 0x3F);
47580 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47581 }else if( c<0x10000 ){
47582 e = 0xE0 + ((c>>12)&0x0F);
47583 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47584 e = 0x80 + ((c>>6) & 0x3F);
47585 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47586 e = 0x80 + (c & 0x3F);
47587 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47588 }else{
47589 e = 0xF0 + ((c>>18) & 0x07);
47590 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47591 e = 0x80 + ((c>>12) & 0x3F);
47592 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47593 e = 0x80 + ((c>>6) & 0x3F);
47594 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47595 e = 0x80 + (c & 0x3F);
47596 jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47597 }
47598 }
47599 /* All done */
47600 return JX9_OK;
47601}
47602/*
47603 * UTF-8 decoding routine extracted from the sqlite3 source tree.
47604 * Original author: D. Richard Hipp (http://www.sqlite.org)
47605 * Status: Public Domain
47606 */
47607/*
47608** This lookup table is used to help decode the first byte of
47609** a multi-byte UTF8 character.
47610*/
47611static const unsigned char UtfTrans1[] = {
47612 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
47613 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
47614 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
47615 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
47616 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
47617 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
47618 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
47619 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
47620};
47621/*
47622** Translate a single UTF-8 character. Return the unicode value.
47623**
47624** During translation, assume that the byte that zTerm points
47625** is a 0x00.
47626**
47627** Write a pointer to the next unread byte back into *pzNext.
47628**
47629** Notes On Invalid UTF-8:
47630**
47631** * This routine never allows a 7-bit character (0x00 through 0x7f) to
47632** be encoded as a multi-byte character. Any multi-byte character that
47633** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd.
47634**
47635** * This routine never allows a UTF16 surrogate value to be encoded.
47636** If a multi-byte character attempts to encode a value between
47637** 0xd800 and 0xe000 then it is rendered as 0xfffd.
47638**
47639** * Bytes in the range of 0x80 through 0xbf which occur as the first
47640** byte of a character are interpreted as single-byte characters
47641** and rendered as themselves even though they are technically
47642** invalid characters.
47643**
47644** * This routine accepts an infinite number of different UTF8 encodings
47645** for unicode values 0x80 and greater. It do not change over-length
47646** encodings to 0xfffd as some systems recommend.
47647*/
47648#define READ_UTF8(zIn, zTerm, c) \
47649 c = *(zIn++); \
47650 if( c>=0xc0 ){ \
47651 c = UtfTrans1[c-0xc0]; \
47652 while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
47653 c = (c<<6) + (0x3f & *(zIn++)); \
47654 } \
47655 if( c<0x80 \
47656 || (c&0xFFFFF800)==0xD800 \
47657 || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
47658 }
47659JX9_PRIVATE int jx9Utf8Read(
47660 const unsigned char *z, /* First byte of UTF-8 character */
47661 const unsigned char *zTerm, /* Pretend this byte is 0x00 */
47662 const unsigned char **pzNext /* Write first byte past UTF-8 char here */
47663){
47664 int c;
47665 READ_UTF8(z, zTerm, c);
47666 *pzNext = z;
47667 return c;
47668}
47669/*
47670 * string utf8_decode(string $data)
47671 * This function decodes data, assumed to be UTF-8 encoded, to unicode.
47672 * Parameters
47673 * data
47674 * An UTF-8 encoded string.
47675 * Return
47676 * Unicode decoded string or NULL on failure.
47677 */
47678static int vm_builtin_utf8_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
47679{
47680 const unsigned char *zIn, *zEnd;
47681 int nByte, c;
47682 if( nArg < 1 ){
47683 /* Missing arguments, return null */
47684 jx9_result_null(pCtx);
47685 return JX9_OK;
47686 }
47687 /* Extract the target string */
47688 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
47689 if( nByte < 1 ){
47690 /* Empty string, return null */
47691 jx9_result_null(pCtx);
47692 return JX9_OK;
47693 }
47694 zEnd = &zIn[nByte];
47695 /* Start the decoding process */
47696 while( zIn < zEnd ){
47697 c = jx9Utf8Read(zIn, zEnd, &zIn);
47698 if( c == 0x0 ){
47699 break;
47700 }
47701 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
47702 }
47703 return JX9_OK;
47704}
47705/*
47706 * string json_encode(mixed $value)
47707 * Returns a string containing the JSON representation of value.
47708 * Parameters
47709 * $value
47710 * The value being encoded. Can be any type except a resource.
47711 * Return
47712 * Returns a JSON encoded string on success. FALSE otherwise
47713 */
47714static int vm_builtin_json_encode(jx9_context *pCtx,int nArg,jx9_value **apArg)
47715{
47716 SyBlob sBlob;
47717 if( nArg < 1 ){
47718 /* Missing arguments, return FALSE */
47719 jx9_result_bool(pCtx, 0);
47720 return JX9_OK;
47721 }
47722 /* Init the working buffer */
47723 SyBlobInit(&sBlob,&pCtx->pVm->sAllocator);
47724 /* Perform the encoding operation */
47725 jx9JsonSerialize(apArg[0],&sBlob);
47726 /* Return the serialized value */
47727 jx9_result_string(pCtx,(const char *)SyBlobData(&sBlob),(int)SyBlobLength(&sBlob));
47728 /* Cleanup */
47729 SyBlobRelease(&sBlob);
47730 /* All done */
47731 return JX9_OK;
47732}
47733/*
47734 * mixed json_decode(string $json)
47735 * Takes a JSON encoded string and converts it into a JX9 variable.
47736 * Parameters
47737 * $json
47738 * The json string being decoded.
47739 * Return
47740 * The value encoded in json in appropriate JX9 type. Values true, false and null (case-insensitive)
47741 * are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded
47742 * or if the encoded data is deeper than the recursion limit.
47743 */
47744static int vm_builtin_json_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
47745{
47746 const char *zJSON;
47747 int nByte;
47748 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
47749 /* Missing/Invalid arguments, return NULL */
47750 jx9_result_null(pCtx);
47751 return JX9_OK;
47752 }
47753 /* Extract the JSON string */
47754 zJSON = jx9_value_to_string(apArg[0], &nByte);
47755 if( nByte < 1 ){
47756 /* Empty string, return NULL */
47757 jx9_result_null(pCtx);
47758 return JX9_OK;
47759 }
47760 /* Decode the raw JSON */
47761 jx9JsonDecode(pCtx,zJSON,nByte);
47762 return JX9_OK;
47763}
47764/* Table of built-in VM functions. */
47765static const jx9_builtin_func aVmFunc[] = {
47766 /* JSON Encoding/Decoding */
47767 { "json_encode", vm_builtin_json_encode },
47768 { "json_decode", vm_builtin_json_decode },
47769 /* Functions calls */
47770 { "func_num_args" , vm_builtin_func_num_args },
47771 { "func_get_arg" , vm_builtin_func_get_arg },
47772 { "func_get_args" , vm_builtin_func_get_args },
47773 { "function_exists", vm_builtin_func_exists },
47774 { "is_callable" , vm_builtin_is_callable },
47775 { "get_defined_functions", vm_builtin_get_defined_func },
47776 /* Constants management */
47777 { "defined", vm_builtin_defined },
47778 { "get_defined_constants", vm_builtin_get_defined_constants },
47779 /* Random numbers/strings generators */
47780 { "rand", vm_builtin_rand },
47781 { "rand_str", vm_builtin_rand_str },
47782 { "getrandmax", vm_builtin_getrandmax },
47783 /* Language constructs functions */
47784 { "print", vm_builtin_print },
47785 { "exit", vm_builtin_exit },
47786 { "die", vm_builtin_exit },
47787 /* Variable handling functions */
47788 { "gettype", vm_builtin_gettype },
47789 { "get_resource_type", vm_builtin_get_resource_type},
47790 /* Variable dumping */
47791 { "dump", vm_builtin_dump },
47792 /* Release info */
47793 {"jx9_version", vm_builtin_jx9_version },
47794 {"jx9_credits", vm_builtin_jx9_version },
47795 {"jx9_info", vm_builtin_jx9_version },
47796 {"jx9_copyright", vm_builtin_jx9_version },
47797 /* hashmap */
47798 {"extract", vm_builtin_extract },
47799 /* URL related function */
47800 {"parse_url", vm_builtin_parse_url },
47801 /* UTF-8 encoding/decoding */
47802 {"utf8_encode", vm_builtin_utf8_encode},
47803 {"utf8_decode", vm_builtin_utf8_decode},
47804 /* Command line processing */
47805 {"getopt", vm_builtin_getopt },
47806 /* Files/URI inclusion facility */
47807 { "include", vm_builtin_include },
47808 { "import", vm_builtin_import }
47809};
47810/*
47811 * Register the built-in VM functions defined above.
47812 */
47813static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm)
47814{
47815 sxi32 rc;
47816 sxu32 n;
47817 for( n = 0 ; n < SX_ARRAYSIZE(aVmFunc) ; ++n ){
47818 /* Note that these special functions have access
47819 * to the underlying virtual machine as their
47820 * private data.
47821 */
47822 rc = jx9_create_function(&(*pVm), aVmFunc[n].zName, aVmFunc[n].xFunc, &(*pVm));
47823 if( rc != SXRET_OK ){
47824 return rc;
47825 }
47826 }
47827 return SXRET_OK;
47828}
47829#ifndef JX9_DISABLE_BUILTIN_FUNC
47830/*
47831 * Extract the IO stream device associated with a given scheme.
47832 * Return a pointer to an instance of jx9_io_stream when the scheme
47833 * have an associated IO stream registered with it. NULL otherwise.
47834 * If no scheme:// is avalilable then the file:// scheme is assumed.
47835 * For more information on how to register IO stream devices, please
47836 * refer to the official documentation.
47837 */
47838JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(
47839 jx9_vm *pVm, /* Target VM */
47840 const char **pzDevice, /* Full path, URI, ... */
47841 int nByte /* *pzDevice length*/
47842 )
47843{
47844 const char *zIn, *zEnd, *zCur, *zNext;
47845 jx9_io_stream **apStream, *pStream;
47846 SyString sDev, sCur;
47847 sxu32 n, nEntry;
47848 int rc;
47849 /* Check if a scheme [i.e: file://, http://, zip://...] is available */
47850 zNext = zCur = zIn = *pzDevice;
47851 zEnd = &zIn[nByte];
47852 while( zIn < zEnd ){
47853 if( zIn < &zEnd[-3]/*://*/ && zIn[0] == ':' && zIn[1] == '/' && zIn[2] == '/' ){
47854 /* Got one */
47855 zNext = &zIn[sizeof("://")-1];
47856 break;
47857 }
47858 /* Advance the cursor */
47859 zIn++;
47860 }
47861 if( zIn >= zEnd ){
47862 /* No such scheme, return the default stream */
47863 return pVm->pDefStream;
47864 }
47865 SyStringInitFromBuf(&sDev, zCur, zIn-zCur);
47866 /* Remove leading and trailing white spaces */
47867 SyStringFullTrim(&sDev);
47868 /* Perform a linear lookup on the installed stream devices */
47869 apStream = (jx9_io_stream **)SySetBasePtr(&pVm->aIOstream);
47870 nEntry = SySetUsed(&pVm->aIOstream);
47871 for( n = 0 ; n < nEntry ; n++ ){
47872 pStream = apStream[n];
47873 SyStringInitFromBuf(&sCur, pStream->zName, SyStrlen(pStream->zName));
47874 /* Perfrom a case-insensitive comparison */
47875 rc = SyStringCmp(&sDev, &sCur, SyStrnicmp);
47876 if( rc == 0 ){
47877 /* Stream device found */
47878 *pzDevice = zNext;
47879 return pStream;
47880 }
47881 }
47882 /* No such stream, return NULL */
47883 return 0;
47884}
47885#endif /* JX9_DISABLE_BUILTIN_FUNC */
47886/*
47887 * Section:
47888 * HTTP/URI related routines.
47889 * Authors:
47890 * Symisc Systems, devel@symisc.net.
47891 * Copyright (C) Symisc Systems, http://jx9.symisc.net
47892 * Status:
47893 * Stable.
47894 */
47895 /*
47896 * URI Parser: Split an URI into components [i.e: Host, Path, Query, ...].
47897 * URI syntax: [method:/][/[user[:pwd]@]host[:port]/][document]
47898 * This almost, but not quite, RFC1738 URI syntax.
47899 * This routine is not a validator, it does not check for validity
47900 * nor decode URI parts, the only thing this routine does is splitting
47901 * the input to its fields.
47902 * Upper layer are responsible of decoding and validating URI parts.
47903 * On success, this function populate the "SyhttpUri" structure passed
47904 * as the first argument. Otherwise SXERR_* is returned when a malformed
47905 * input is encountered.
47906 */
47907 static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen)
47908 {
47909 const char *zEnd = &zUri[nLen];
47910 sxu8 bHostOnly = FALSE;
47911 sxu8 bIPv6 = FALSE ;
47912 const char *zCur;
47913 SyString *pComp;
47914 sxu32 nPos = 0;
47915 sxi32 rc;
47916 /* Zero the structure first */
47917 SyZero(pOut, sizeof(SyhttpUri));
47918 /* Remove leading and trailing white spaces */
47919 SyStringInitFromBuf(&pOut->sRaw, zUri, nLen);
47920 SyStringFullTrim(&pOut->sRaw);
47921 /* Find the first '/' separator */
47922 rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos);
47923 if( rc != SXRET_OK ){
47924 /* Assume a host name only */
47925 zCur = zEnd;
47926 bHostOnly = TRUE;
47927 goto ProcessHost;
47928 }
47929 zCur = &zUri[nPos];
47930 if( zUri != zCur && zCur[-1] == ':' ){
47931 /* Extract a scheme:
47932 * Not that we can get an invalid scheme here.
47933 * Fortunately the caller can discard any URI by comparing this scheme with its
47934 * registered schemes and will report the error as soon as his comparison function
47935 * fail.
47936 */
47937 pComp = &pOut->sScheme;
47938 SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri - 1));
47939 SyStringLeftTrim(pComp);
47940 }
47941 if( zCur[1] != '/' ){
47942 if( zCur == zUri || zCur[-1] == ':' ){
47943 /* No authority */
47944 goto PathSplit;
47945 }
47946 /* There is something here , we will assume its an authority
47947 * and someone has forgot the two prefix slashes "//",
47948 * sooner or later we will detect if we are dealing with a malicious
47949 * user or not, but now assume we are dealing with an authority
47950 * and let the caller handle all the validation process.
47951 */
47952 goto ProcessHost;
47953 }
47954 zUri = &zCur[2];
47955 zCur = zEnd;
47956 rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos);
47957 if( rc == SXRET_OK ){
47958 zCur = &zUri[nPos];
47959 }
47960 ProcessHost:
47961 /* Extract user information if present */
47962 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), '@', &nPos);
47963 if( rc == SXRET_OK ){
47964 if( nPos > 0 ){
47965 sxu32 nPassOfft; /* Password offset */
47966 pComp = &pOut->sUser;
47967 SyStringInitFromBuf(pComp, zUri, nPos);
47968 /* Extract the password if available */
47969 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPassOfft);
47970 if( rc == SXRET_OK && nPassOfft < nPos){
47971 pComp->nByte = nPassOfft;
47972 pComp = &pOut->sPass;
47973 pComp->zString = &zUri[nPassOfft+sizeof(char)];
47974 pComp->nByte = nPos - nPassOfft - 1;
47975 }
47976 /* Update the cursor */
47977 zUri = &zUri[nPos+1];
47978 }else{
47979 zUri++;
47980 }
47981 }
47982 pComp = &pOut->sHost;
47983 while( zUri < zCur && SyisSpace(zUri[0])){
47984 zUri++;
47985 }
47986 SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri));
47987 if( pComp->zString[0] == '[' ){
47988 /* An IPv6 Address: Make a simple naive test
47989 */
47990 zUri++; pComp->zString++; pComp->nByte = 0;
47991 while( ((unsigned char)zUri[0] < 0xc0 && SyisHex(zUri[0])) || zUri[0] == ':' ){
47992 zUri++; pComp->nByte++;
47993 }
47994 if( zUri[0] != ']' ){
47995 return SXERR_CORRUPT; /* Malformed IPv6 address */
47996 }
47997 zUri++;
47998 bIPv6 = TRUE;
47999 }
48000 /* Extract a port number if available */
48001 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPos);
48002 if( rc == SXRET_OK ){
48003 if( bIPv6 == FALSE ){
48004 pComp->nByte = (sxu32)(&zUri[nPos] - zUri);
48005 }
48006 pComp = &pOut->sPort;
48007 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zCur - &zUri[nPos+1]));
48008 }
48009 if( bHostOnly == TRUE ){
48010 return SXRET_OK;
48011 }
48012PathSplit:
48013 zUri = zCur;
48014 pComp = &pOut->sPath;
48015 SyStringInitFromBuf(pComp, zUri, (sxu32)(zEnd-zUri));
48016 if( pComp->nByte == 0 ){
48017 return SXRET_OK; /* Empty path */
48018 }
48019 if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '?', &nPos) ){
48020 pComp->nByte = nPos; /* Update path length */
48021 pComp = &pOut->sQuery;
48022 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1]));
48023 }
48024 if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '#', &nPos) ){
48025 /* Update path or query length */
48026 if( pComp == &pOut->sPath ){
48027 pComp->nByte = nPos;
48028 }else{
48029 if( &zUri[nPos] < (char *)SyStringData(pComp) ){
48030 /* Malformed syntax : Query must be present before fragment */
48031 return SXERR_SYNTAX;
48032 }
48033 pComp->nByte -= (sxu32)(zEnd - &zUri[nPos]);
48034 }
48035 pComp = &pOut->sFragment;
48036 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1]))
48037 }
48038 return SXRET_OK;
48039 }
48040 /*
48041 * Extract a single line from a raw HTTP request.
48042 * Return SXRET_OK on success, SXERR_EOF when end of input
48043 * and SXERR_MORE when more input is needed.
48044 */
48045static sxi32 VmGetNextLine(SyString *pCursor, SyString *pCurrent)
48046{
48047 const char *zIn;
48048 sxu32 nPos;
48049 /* Jump leading white spaces */
48050 SyStringLeftTrim(pCursor);
48051 if( pCursor->nByte < 1 ){
48052 SyStringInitFromBuf(pCurrent, 0, 0);
48053 return SXERR_EOF; /* End of input */
48054 }
48055 zIn = SyStringData(pCursor);
48056 if( SXRET_OK != SyByteListFind(pCursor->zString, pCursor->nByte, "\r\n", &nPos) ){
48057 /* Line not found, tell the caller to read more input from source */
48058 SyStringDupPtr(pCurrent, pCursor);
48059 return SXERR_MORE;
48060 }
48061 pCurrent->zString = zIn;
48062 pCurrent->nByte = nPos;
48063 /* advance the cursor so we can call this routine again */
48064 pCursor->zString = &zIn[nPos];
48065 pCursor->nByte -= nPos;
48066 return SXRET_OK;
48067 }
48068 /*
48069 * Split a single MIME header into a name value pair.
48070 * This function return SXRET_OK, SXERR_CONTINUE on success.
48071 * Otherwise SXERR_NEXT is returned when a malformed header
48072 * is encountered.
48073 * Note: This function handle also mult-line headers.
48074 */
48075 static sxi32 VmHttpProcessOneHeader(SyhttpHeader *pHdr, SyhttpHeader *pLast, const char *zLine, sxu32 nLen)
48076 {
48077 SyString *pName;
48078 sxu32 nPos;
48079 sxi32 rc;
48080 if( nLen < 1 ){
48081 return SXERR_NEXT;
48082 }
48083 /* Check for multi-line header */
48084 if( pLast && (zLine[-1] == ' ' || zLine[-1] == '\t') ){
48085 SyString *pTmp = &pLast->sValue;
48086 SyStringFullTrim(pTmp);
48087 if( pTmp->nByte == 0 ){
48088 SyStringInitFromBuf(pTmp, zLine, nLen);
48089 }else{
48090 /* Update header value length */
48091 pTmp->nByte = (sxu32)(&zLine[nLen] - pTmp->zString);
48092 }
48093 /* Simply tell the caller to reset its states and get another line */
48094 return SXERR_CONTINUE;
48095 }
48096 /* Split the header */
48097 pName = &pHdr->sName;
48098 rc = SyByteFind(zLine, nLen, ':', &nPos);
48099 if(rc != SXRET_OK ){
48100 return SXERR_NEXT; /* Malformed header;Check the next entry */
48101 }
48102 SyStringInitFromBuf(pName, zLine, nPos);
48103 SyStringFullTrim(pName);
48104 /* Extract a header value */
48105 SyStringInitFromBuf(&pHdr->sValue, &zLine[nPos + 1], nLen - nPos - 1);
48106 /* Remove leading and trailing whitespaces */
48107 SyStringFullTrim(&pHdr->sValue);
48108 return SXRET_OK;
48109 }
48110 /*
48111 * Extract all MIME headers associated with a HTTP request.
48112 * After processing the first line of a HTTP request, the following
48113 * routine is called in order to extract MIME headers.
48114 * This function return SXRET_OK on success, SXERR_MORE when it needs
48115 * more inputs.
48116 * Note: Any malformed header is simply discarded.
48117 */
48118 static sxi32 VmHttpExtractHeaders(SyString *pRequest, SySet *pOut)
48119 {
48120 SyhttpHeader *pLast = 0;
48121 SyString sCurrent;
48122 SyhttpHeader sHdr;
48123 sxu8 bEol;
48124 sxi32 rc;
48125 if( SySetUsed(pOut) > 0 ){
48126 pLast = (SyhttpHeader *)SySetAt(pOut, SySetUsed(pOut)-1);
48127 }
48128 bEol = FALSE;
48129 for(;;){
48130 SyZero(&sHdr, sizeof(SyhttpHeader));
48131 /* Extract a single line from the raw HTTP request */
48132 rc = VmGetNextLine(pRequest, &sCurrent);
48133 if(rc != SXRET_OK ){
48134 if( sCurrent.nByte < 1 ){
48135 break;
48136 }
48137 bEol = TRUE;
48138 }
48139 /* Process the header */
48140 if( SXRET_OK == VmHttpProcessOneHeader(&sHdr, pLast, sCurrent.zString, sCurrent.nByte)){
48141 if( SXRET_OK != SySetPut(pOut, (const void *)&sHdr) ){
48142 break;
48143 }
48144 /* Retrieve the last parsed header so we can handle multi-line header
48145 * in case we face one of them.
48146 */
48147 pLast = (SyhttpHeader *)SySetPeek(pOut);
48148 }
48149 if( bEol ){
48150 break;
48151 }
48152 } /* for(;;) */
48153 return SXRET_OK;
48154 }
48155 /*
48156 * Process the first line of a HTTP request.
48157 * This routine perform the following operations
48158 * 1) Extract the HTTP method.
48159 * 2) Split the request URI to it's fields [ie: host, path, query, ...].
48160 * 3) Extract the HTTP protocol version.
48161 */
48162 static sxi32 VmHttpProcessFirstLine(
48163 SyString *pRequest, /* Raw HTTP request */
48164 sxi32 *pMethod, /* OUT: HTTP method */
48165 SyhttpUri *pUri, /* OUT: Parse of the URI */
48166 sxi32 *pProto /* OUT: HTTP protocol */
48167 )
48168 {
48169 static const char *azMethods[] = { "get", "post", "head", "put"};
48170 static const sxi32 aMethods[] = { HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_HEAD, HTTP_METHOD_PUT};
48171 const char *zIn, *zEnd, *zPtr;
48172 SyString sLine;
48173 sxu32 nLen;
48174 sxi32 rc;
48175 /* Extract the first line and update the pointer */
48176 rc = VmGetNextLine(pRequest, &sLine);
48177 if( rc != SXRET_OK ){
48178 return rc;
48179 }
48180 if ( sLine.nByte < 1 ){
48181 /* Empty HTTP request */
48182 return SXERR_EMPTY;
48183 }
48184 /* Delimit the line and ignore trailing and leading white spaces */
48185 zIn = sLine.zString;
48186 zEnd = &zIn[sLine.nByte];
48187 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
48188 zIn++;
48189 }
48190 /* Extract the HTTP method */
48191 zPtr = zIn;
48192 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
48193 zIn++;
48194 }
48195 *pMethod = HTTP_METHOD_OTHR;
48196 if( zIn > zPtr ){
48197 sxu32 i;
48198 nLen = (sxu32)(zIn-zPtr);
48199 for( i = 0 ; i < SX_ARRAYSIZE(azMethods) ; ++i ){
48200 if( SyStrnicmp(azMethods[i], zPtr, nLen) == 0 ){
48201 *pMethod = aMethods[i];
48202 break;
48203 }
48204 }
48205 }
48206 /* Jump trailing white spaces */
48207 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
48208 zIn++;
48209 }
48210 /* Extract the request URI */
48211 zPtr = zIn;
48212 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
48213 zIn++;
48214 }
48215 if( zIn > zPtr ){
48216 nLen = (sxu32)(zIn-zPtr);
48217 /* Split raw URI to it's fields */
48218 VmHttpSplitURI(pUri, zPtr, nLen);
48219 }
48220 /* Jump trailing white spaces */
48221 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
48222 zIn++;
48223 }
48224 /* Extract the HTTP version */
48225 zPtr = zIn;
48226 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
48227 zIn++;
48228 }
48229 *pProto = HTTP_PROTO_11; /* HTTP/1.1 */
48230 rc = 1;
48231 if( zIn > zPtr ){
48232 rc = SyStrnicmp(zPtr, "http/1.0", (sxu32)(zIn-zPtr));
48233 }
48234 if( !rc ){
48235 *pProto = HTTP_PROTO_10; /* HTTP/1.0 */
48236 }
48237 return SXRET_OK;
48238 }
48239 /*
48240 * Tokenize, decode and split a raw query encoded as: "x-www-form-urlencoded"
48241 * into a name value pair.
48242 * Note that this encoding is implicit in GET based requests.
48243 * After the tokenization process, register the decoded queries
48244 * in the $_GET/$_POST/$_REQUEST superglobals arrays.
48245 */
48246 static sxi32 VmHttpSplitEncodedQuery(
48247 jx9_vm *pVm, /* Target VM */
48248 SyString *pQuery, /* Raw query to decode */
48249 SyBlob *pWorker, /* Working buffer */
48250 int is_post /* TRUE if we are dealing with a POST request */
48251 )
48252 {
48253 const char *zEnd = &pQuery->zString[pQuery->nByte];
48254 const char *zIn = pQuery->zString;
48255 jx9_value *pGet, *pRequest;
48256 SyString sName, sValue;
48257 const char *zPtr;
48258 sxu32 nBlobOfft;
48259 /* Extract superglobals */
48260 if( is_post ){
48261 /* $_POST superglobal */
48262 pGet = VmExtractSuper(&(*pVm), "_POST", sizeof("_POST")-1);
48263 }else{
48264 /* $_GET superglobal */
48265 pGet = VmExtractSuper(&(*pVm), "_GET", sizeof("_GET")-1);
48266 }
48267 pRequest = VmExtractSuper(&(*pVm), "_REQUEST", sizeof("_REQUEST")-1);
48268 /* Split up the raw query */
48269 for(;;){
48270 /* Jump leading white spaces */
48271 while(zIn < zEnd && SyisSpace(zIn[0]) ){
48272 zIn++;
48273 }
48274 if( zIn >= zEnd ){
48275 break;
48276 }
48277 zPtr = zIn;
48278 while( zPtr < zEnd && zPtr[0] != '=' && zPtr[0] != '&' && zPtr[0] != ';' ){
48279 zPtr++;
48280 }
48281 /* Reset the working buffer */
48282 SyBlobReset(pWorker);
48283 /* Decode the entry */
48284 SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
48285 /* Save the entry */
48286 sName.nByte = SyBlobLength(pWorker);
48287 sValue.zString = 0;
48288 sValue.nByte = 0;
48289 if( zPtr < zEnd && zPtr[0] == '=' ){
48290 zPtr++;
48291 zIn = zPtr;
48292 /* Store field value */
48293 while( zPtr < zEnd && zPtr[0] != '&' && zPtr[0] != ';' ){
48294 zPtr++;
48295 }
48296 if( zPtr > zIn ){
48297 /* Decode the value */
48298 nBlobOfft = SyBlobLength(pWorker);
48299 SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
48300 sValue.zString = (const char *)SyBlobDataAt(pWorker, nBlobOfft);
48301 sValue.nByte = SyBlobLength(pWorker) - nBlobOfft;
48302
48303 }
48304 /* Synchronize pointers */
48305 zIn = zPtr;
48306 }
48307 sName.zString = (const char *)SyBlobData(pWorker);
48308 /* Install the decoded query in the $_GET/$_REQUEST array */
48309 if( pGet && (pGet->iFlags & MEMOBJ_HASHMAP) ){
48310 VmHashmapInsert((jx9_hashmap *)pGet->x.pOther,
48311 sName.zString, (int)sName.nByte,
48312 sValue.zString, (int)sValue.nByte
48313 );
48314 }
48315 if( pRequest && (pRequest->iFlags & MEMOBJ_HASHMAP) ){
48316 VmHashmapInsert((jx9_hashmap *)pRequest->x.pOther,
48317 sName.zString, (int)sName.nByte,
48318 sValue.zString, (int)sValue.nByte
48319 );
48320 }
48321 /* Advance the pointer */
48322 zIn = &zPtr[1];
48323 }
48324 /* All done*/
48325 return SXRET_OK;
48326 }
48327 /*
48328 * Extract MIME header value from the given set.
48329 * Return header value on success. NULL otherwise.
48330 */
48331 static SyString * VmHttpExtractHeaderValue(SySet *pSet, const char *zMime, sxu32 nByte)
48332 {
48333 SyhttpHeader *aMime, *pMime;
48334 SyString sMime;
48335 sxu32 n;
48336 SyStringInitFromBuf(&sMime, zMime, nByte);
48337 /* Point to the MIME entries */
48338 aMime = (SyhttpHeader *)SySetBasePtr(pSet);
48339 /* Perform the lookup */
48340 for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
48341 pMime = &aMime[n];
48342 if( SyStringCmp(&sMime, &pMime->sName, SyStrnicmp) == 0 ){
48343 /* Header found, return it's associated value */
48344 return &pMime->sValue;
48345 }
48346 }
48347 /* No such MIME header */
48348 return 0;
48349 }
48350 /*
48351 * Tokenize and decode a raw "Cookie:" MIME header into a name value pair
48352 * and insert it's fields [i.e name, value] in the $_COOKIE superglobal.
48353 */
48354 static sxi32 VmHttpPorcessCookie(jx9_vm *pVm, SyBlob *pWorker, const char *zIn, sxu32 nByte)
48355 {
48356 const char *zPtr, *zDelimiter, *zEnd = &zIn[nByte];
48357 SyString sName, sValue;
48358 jx9_value *pCookie;
48359 sxu32 nOfft;
48360 /* Make sure the $_COOKIE superglobal is available */
48361 pCookie = VmExtractSuper(&(*pVm), "_COOKIE", sizeof("_COOKIE")-1);
48362 if( pCookie == 0 || (pCookie->iFlags & MEMOBJ_HASHMAP) == 0 ){
48363 /* $_COOKIE superglobal not available */
48364 return SXERR_NOTFOUND;
48365 }
48366 for(;;){
48367 /* Jump leading white spaces */
48368 while( zIn < zEnd && SyisSpace(zIn[0]) ){
48369 zIn++;
48370 }
48371 if( zIn >= zEnd ){
48372 break;
48373 }
48374 /* Reset the working buffer */
48375 SyBlobReset(pWorker);
48376 zDelimiter = zIn;
48377 /* Delimit the name[=value]; pair */
48378 while( zDelimiter < zEnd && zDelimiter[0] != ';' ){
48379 zDelimiter++;
48380 }
48381 zPtr = zIn;
48382 while( zPtr < zDelimiter && zPtr[0] != '=' ){
48383 zPtr++;
48384 }
48385 /* Decode the cookie */
48386 SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
48387 sName.nByte = SyBlobLength(pWorker);
48388 zPtr++;
48389 sValue.zString = 0;
48390 sValue.nByte = 0;
48391 if( zPtr < zDelimiter ){
48392 /* Got a Cookie value */
48393 nOfft = SyBlobLength(pWorker);
48394 SyUriDecode(zPtr, (sxu32)(zDelimiter-zPtr), jx9VmBlobConsumer, pWorker, TRUE);
48395 SyStringInitFromBuf(&sValue, SyBlobDataAt(pWorker, nOfft), SyBlobLength(pWorker)-nOfft);
48396 }
48397 /* Synchronize pointers */
48398 zIn = &zDelimiter[1];
48399 /* Perform the insertion */
48400 sName.zString = (const char *)SyBlobData(pWorker);
48401 VmHashmapInsert((jx9_hashmap *)pCookie->x.pOther,
48402 sName.zString, (int)sName.nByte,
48403 sValue.zString, (int)sValue.nByte
48404 );
48405 }
48406 return SXRET_OK;
48407 }
48408 /*
48409 * Process a full HTTP request and populate the appropriate arrays
48410 * such as $_SERVER, $_GET, $_POST, $_COOKIE, $_REQUEST, ... with the information
48411 * extracted from the raw HTTP request. As an extension Symisc introduced
48412 * the $_HEADER array which hold a copy of the processed HTTP MIME headers
48413 * and their associated values. [i.e: $_HEADER['Server'], $_HEADER['User-Agent'], ...].
48414 * This function return SXRET_OK on success. Any other return value indicates
48415 * a malformed HTTP request.
48416 */
48417 static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte)
48418 {
48419 SyString *pName, *pValue, sRequest; /* Raw HTTP request */
48420 jx9_value *pHeaderArray; /* $_HEADER superglobal (Symisc eXtension to the JX9 specification)*/
48421 SyhttpHeader *pHeader; /* MIME header */
48422 SyhttpUri sUri; /* Parse of the raw URI*/
48423 SyBlob sWorker; /* General purpose working buffer */
48424 SySet sHeader; /* MIME headers set */
48425 sxi32 iMethod; /* HTTP method [i.e: GET, POST, HEAD...]*/
48426 sxi32 iVer; /* HTTP protocol version */
48427 sxi32 rc;
48428 SyStringInitFromBuf(&sRequest, zRequest, nByte);
48429 SySetInit(&sHeader, &pVm->sAllocator, sizeof(SyhttpHeader));
48430 SyBlobInit(&sWorker, &pVm->sAllocator);
48431 /* Ignore leading and trailing white spaces*/
48432 SyStringFullTrim(&sRequest);
48433 /* Process the first line */
48434 rc = VmHttpProcessFirstLine(&sRequest, &iMethod, &sUri, &iVer);
48435 if( rc != SXRET_OK ){
48436 return rc;
48437 }
48438 /* Process MIME headers */
48439 VmHttpExtractHeaders(&sRequest, &sHeader);
48440 /*
48441 * Setup $_SERVER environments
48442 */
48443 /* 'SERVER_PROTOCOL': Name and revision of the information protocol via which the page was requested */
48444 jx9_vm_config(pVm,
48445 JX9_VM_CONFIG_SERVER_ATTR,
48446 "SERVER_PROTOCOL",
48447 iVer == HTTP_PROTO_10 ? "HTTP/1.0" : "HTTP/1.1",
48448 sizeof("HTTP/1.1")-1
48449 );
48450 /* 'REQUEST_METHOD': Which request method was used to access the page */
48451 jx9_vm_config(pVm,
48452 JX9_VM_CONFIG_SERVER_ATTR,
48453 "REQUEST_METHOD",
48454 iMethod == HTTP_METHOD_GET ? "GET" :
48455 (iMethod == HTTP_METHOD_POST ? "POST":
48456 (iMethod == HTTP_METHOD_PUT ? "PUT" :
48457 (iMethod == HTTP_METHOD_HEAD ? "HEAD" : "OTHER"))),
48458 -1 /* Compute attribute length automatically */
48459 );
48460 if( SyStringLength(&sUri.sQuery) > 0 && iMethod == HTTP_METHOD_GET ){
48461 pValue = &sUri.sQuery;
48462 /* 'QUERY_STRING': The query string, if any, via which the page was accessed */
48463 jx9_vm_config(pVm,
48464 JX9_VM_CONFIG_SERVER_ATTR,
48465 "QUERY_STRING",
48466 pValue->zString,
48467 pValue->nByte
48468 );
48469 /* Decoded the raw query */
48470 VmHttpSplitEncodedQuery(&(*pVm), pValue, &sWorker, FALSE);
48471 }
48472 /* REQUEST_URI: The URI which was given in order to access this page; for instance, '/index.html' */
48473 pValue = &sUri.sRaw;
48474 jx9_vm_config(pVm,
48475 JX9_VM_CONFIG_SERVER_ATTR,
48476 "REQUEST_URI",
48477 pValue->zString,
48478 pValue->nByte
48479 );
48480 /*
48481 * 'PATH_INFO'
48482 * 'ORIG_PATH_INFO'
48483 * Contains any client-provided pathname information trailing the actual script filename but preceding
48484 * the query string, if available. For instance, if the current script was accessed via the URL
48485 * http://www.example.com/jx9/path_info.jx9/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain
48486 * /some/stuff.
48487 */
48488 pValue = &sUri.sPath;
48489 jx9_vm_config(pVm,
48490 JX9_VM_CONFIG_SERVER_ATTR,
48491 "PATH_INFO",
48492 pValue->zString,
48493 pValue->nByte
48494 );
48495 jx9_vm_config(pVm,
48496 JX9_VM_CONFIG_SERVER_ATTR,
48497 "ORIG_PATH_INFO",
48498 pValue->zString,
48499 pValue->nByte
48500 );
48501 /* 'HTTP_ACCEPT': Contents of the Accept: header from the current request, if there is one */
48502 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept", sizeof("Accept")-1);
48503 if( pValue ){
48504 jx9_vm_config(pVm,
48505 JX9_VM_CONFIG_SERVER_ATTR,
48506 "HTTP_ACCEPT",
48507 pValue->zString,
48508 pValue->nByte
48509 );
48510 }
48511 /* 'HTTP_ACCEPT_CHARSET': Contents of the Accept-Charset: header from the current request, if there is one. */
48512 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Charset", sizeof("Accept-Charset")-1);
48513 if( pValue ){
48514 jx9_vm_config(pVm,
48515 JX9_VM_CONFIG_SERVER_ATTR,
48516 "HTTP_ACCEPT_CHARSET",
48517 pValue->zString,
48518 pValue->nByte
48519 );
48520 }
48521 /* 'HTTP_ACCEPT_ENCODING': Contents of the Accept-Encoding: header from the current request, if there is one. */
48522 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Encoding", sizeof("Accept-Encoding")-1);
48523 if( pValue ){
48524 jx9_vm_config(pVm,
48525 JX9_VM_CONFIG_SERVER_ATTR,
48526 "HTTP_ACCEPT_ENCODING",
48527 pValue->zString,
48528 pValue->nByte
48529 );
48530 }
48531 /* 'HTTP_ACCEPT_LANGUAGE': Contents of the Accept-Language: header from the current request, if there is one */
48532 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Language", sizeof("Accept-Language")-1);
48533 if( pValue ){
48534 jx9_vm_config(pVm,
48535 JX9_VM_CONFIG_SERVER_ATTR,
48536 "HTTP_ACCEPT_LANGUAGE",
48537 pValue->zString,
48538 pValue->nByte
48539 );
48540 }
48541 /* 'HTTP_CONNECTION': Contents of the Connection: header from the current request, if there is one. */
48542 pValue = VmHttpExtractHeaderValue(&sHeader, "Connection", sizeof("Connection")-1);
48543 if( pValue ){
48544 jx9_vm_config(pVm,
48545 JX9_VM_CONFIG_SERVER_ATTR,
48546 "HTTP_CONNECTION",
48547 pValue->zString,
48548 pValue->nByte
48549 );
48550 }
48551 /* 'HTTP_HOST': Contents of the Host: header from the current request, if there is one. */
48552 pValue = VmHttpExtractHeaderValue(&sHeader, "Host", sizeof("Host")-1);
48553 if( pValue ){
48554 jx9_vm_config(pVm,
48555 JX9_VM_CONFIG_SERVER_ATTR,
48556 "HTTP_HOST",
48557 pValue->zString,
48558 pValue->nByte
48559 );
48560 }
48561 /* 'HTTP_REFERER': Contents of the Referer: header from the current request, if there is one. */
48562 pValue = VmHttpExtractHeaderValue(&sHeader, "Referer", sizeof("Referer")-1);
48563 if( pValue ){
48564 jx9_vm_config(pVm,
48565 JX9_VM_CONFIG_SERVER_ATTR,
48566 "HTTP_REFERER",
48567 pValue->zString,
48568 pValue->nByte
48569 );
48570 }
48571 /* 'HTTP_USER_AGENT': Contents of the Referer: header from the current request, if there is one. */
48572 pValue = VmHttpExtractHeaderValue(&sHeader, "User-Agent", sizeof("User-Agent")-1);
48573 if( pValue ){
48574 jx9_vm_config(pVm,
48575 JX9_VM_CONFIG_SERVER_ATTR,
48576 "HTTP_USER_AGENT",
48577 pValue->zString,
48578 pValue->nByte
48579 );
48580 }
48581 /* 'JX9_AUTH_DIGEST': When doing Digest HTTP authentication this variable is set to the 'Authorization'
48582 * header sent by the client (which you should then use to make the appropriate validation).
48583 */
48584 pValue = VmHttpExtractHeaderValue(&sHeader, "Authorization", sizeof("Authorization")-1);
48585 if( pValue ){
48586 jx9_vm_config(pVm,
48587 JX9_VM_CONFIG_SERVER_ATTR,
48588 "JX9_AUTH_DIGEST",
48589 pValue->zString,
48590 pValue->nByte
48591 );
48592 jx9_vm_config(pVm,
48593 JX9_VM_CONFIG_SERVER_ATTR,
48594 "JX9_AUTH",
48595 pValue->zString,
48596 pValue->nByte
48597 );
48598 }
48599 /* Install all clients HTTP headers in the $_HEADER superglobal */
48600 pHeaderArray = VmExtractSuper(&(*pVm), "_HEADER", sizeof("_HEADER")-1);
48601 /* Iterate throw the available MIME headers*/
48602 SySetResetCursor(&sHeader);
48603 pHeader = 0; /* stupid cc warning */
48604 while( SXRET_OK == SySetGetNextEntry(&sHeader, (void **)&pHeader) ){
48605 pName = &pHeader->sName;
48606 pValue = &pHeader->sValue;
48607 if( pHeaderArray && (pHeaderArray->iFlags & MEMOBJ_HASHMAP)){
48608 /* Insert the MIME header and it's associated value */
48609 VmHashmapInsert((jx9_hashmap *)pHeaderArray->x.pOther,
48610 pName->zString, (int)pName->nByte,
48611 pValue->zString, (int)pValue->nByte
48612 );
48613 }
48614 if( pName->nByte == sizeof("Cookie")-1 && SyStrnicmp(pName->zString, "Cookie", sizeof("Cookie")-1) == 0
48615 && pValue->nByte > 0){
48616 /* Process the name=value pair and insert them in the $_COOKIE superglobal array */
48617 VmHttpPorcessCookie(&(*pVm), &sWorker, pValue->zString, pValue->nByte);
48618 }
48619 }
48620 if( iMethod == HTTP_METHOD_POST ){
48621 /* Extract raw POST data */
48622 pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Type", sizeof("Content-Type") - 1);
48623 if( pValue && pValue->nByte >= sizeof("application/x-www-form-urlencoded") - 1 &&
48624 SyMemcmp("application/x-www-form-urlencoded", pValue->zString, pValue->nByte) == 0 ){
48625 /* Extract POST data length */
48626 pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Length", sizeof("Content-Length") - 1);
48627 if( pValue ){
48628 sxi32 iLen = 0; /* POST data length */
48629 SyStrToInt32(pValue->zString, pValue->nByte, (void *)&iLen, 0);
48630 if( iLen > 0 ){
48631 /* Remove leading and trailing white spaces */
48632 SyStringFullTrim(&sRequest);
48633 if( (int)sRequest.nByte > iLen ){
48634 sRequest.nByte = (sxu32)iLen;
48635 }
48636 /* Decode POST data now */
48637 VmHttpSplitEncodedQuery(&(*pVm), &sRequest, &sWorker, TRUE);
48638 }
48639 }
48640 }
48641 }
48642 /* All done, clean-up the mess left behind */
48643 SySetRelease(&sHeader);
48644 SyBlobRelease(&sWorker);
48645 return SXRET_OK;
48646 }
48647
48648/*
48649 * ----------------------------------------------------------
48650 * File: lhash_kv.c
48651 * MD5: 581b07ce2984fd95740677285d8a11d3
48652 * ----------------------------------------------------------
48653 */
48654/*
48655 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
48656 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
48657 * Version 1.1.6
48658 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
48659 * please contact Symisc Systems via:
48660 * legal@symisc.net
48661 * licensing@symisc.net
48662 * contact@symisc.net
48663 * or visit:
48664 * http://unqlite.org/licensing.html
48665 */
48666 /* $SymiscID: lhash_kv.c v1.7 Solaris 2013-01-14 12:56 stable <chm@symisc.net> $ */
48667#ifndef UNQLITE_AMALGAMATION
48668#include "unqliteInt.h"
48669#endif
48670/*
48671 * This file implements disk based hashtable using the linear hashing algorithm.
48672 * This implementation is the one decribed in the paper:
48673 * LINEAR HASHING : A NEW TOOL FOR FILE AND TABLE ADDRESSING. Witold Litwin. I. N. Ft. I. A.. 78 150 Le Chesnay, France.
48674 * Plus a smart extension called Virtual Bucket Table. (contact devel@symisc.net for additional information).
48675 */
48676/* Magic number identifying a valid storage image */
48677#define L_HASH_MAGIC 0xFA782DCB
48678/*
48679 * Magic word to hash to identify a valid hash function.
48680 */
48681#define L_HASH_WORD "chm@symisc"
48682/*
48683 * Cell size on disk.
48684 */
48685#define L_HASH_CELL_SZ (4/*Hash*/+4/*Key*/+8/*Data*/+2/* Offset of the next cell */+8/*Overflow*/)
48686/*
48687 * Primary page (not overflow pages) header size on disk.
48688 */
48689#define L_HASH_PAGE_HDR_SZ (2/* Cell offset*/+2/* Free block offset*/+8/*Slave page number*/)
48690/*
48691 * The maximum amount of payload (in bytes) that can be stored locally for
48692 * a database entry. If the entry contains more data than this, the
48693 * extra goes onto overflow pages.
48694*/
48695#define L_HASH_MX_PAYLOAD(PageSize) (PageSize-(L_HASH_PAGE_HDR_SZ+L_HASH_CELL_SZ))
48696/*
48697 * Maxium free space on a single page.
48698 */
48699#define L_HASH_MX_FREE_SPACE(PageSize) (PageSize - (L_HASH_PAGE_HDR_SZ))
48700/*
48701** The maximum number of bytes of payload allowed on a single overflow page.
48702*/
48703#define L_HASH_OVERFLOW_SIZE(PageSize) (PageSize-8)
48704/* Forward declaration */
48705typedef struct lhash_kv_engine lhash_kv_engine;
48706typedef struct lhpage lhpage;
48707/*
48708 * Each record in the database is identified either in-memory or in
48709 * disk by an instance of the following structure.
48710 */
48711typedef struct lhcell lhcell;
48712struct lhcell
48713{
48714 /* Disk-data (Big-Endian) */
48715 sxu32 nHash; /* Hash of the key: 4 bytes */
48716 sxu32 nKey; /* Key length: 4 bytes */
48717 sxu64 nData; /* Data length: 8 bytes */
48718 sxu16 iNext; /* Offset of the next cell: 2 bytes */
48719 pgno iOvfl; /* Overflow page number if any: 8 bytes */
48720 /* In-memory data only */
48721 lhpage *pPage; /* Page this cell belongs */
48722 sxu16 iStart; /* Offset of this cell */
48723 pgno iDataPage; /* Data page number when overflow */
48724 sxu16 iDataOfft; /* Offset of the data in iDataPage */
48725 SyBlob sKey; /* Record key for fast lookup (Kept in-memory if < 256KB ) */
48726 lhcell *pNext,*pPrev; /* Linked list of the loaded memory cells */
48727 lhcell *pNextCol,*pPrevCol; /* Collison chain */
48728};
48729/*
48730** Each database page has a header that is an instance of this
48731** structure.
48732*/
48733typedef struct lhphdr lhphdr;
48734struct lhphdr
48735{
48736 sxu16 iOfft; /* Offset of the first cell */
48737 sxu16 iFree; /* Offset of the first free block*/
48738 pgno iSlave; /* Slave page number */
48739};
48740/*
48741 * Each loaded primary disk page is represented in-memory using
48742 * an instance of the following structure.
48743 */
48744struct lhpage
48745{
48746 lhash_kv_engine *pHash; /* KV Storage engine that own this page */
48747 unqlite_page *pRaw; /* Raw page contents */
48748 lhphdr sHdr; /* Processed page header */
48749 lhcell **apCell; /* Cell buckets */
48750 lhcell *pList,*pFirst; /* Linked list of cells */
48751 sxu32 nCell; /* Total number of cells */
48752 sxu32 nCellSize; /* apCell[] size */
48753 lhpage *pMaster; /* Master page in case we are dealing with a slave page */
48754 lhpage *pSlave; /* List of slave pages */
48755 lhpage *pNextSlave; /* Next slave page on the list */
48756 sxi32 iSlave; /* Total number of slave pages */
48757 sxu16 nFree; /* Amount of free space available in the page */
48758};
48759/*
48760 * A Bucket map record which is used to map logical bucket number to real
48761 * bucket number is represented by an instance of the following structure.
48762 */
48763typedef struct lhash_bmap_rec lhash_bmap_rec;
48764struct lhash_bmap_rec
48765{
48766 pgno iLogic; /* Logical bucket number */
48767 pgno iReal; /* Real bucket number */
48768 lhash_bmap_rec *pNext,*pPrev; /* Link to other bucket map */
48769 lhash_bmap_rec *pNextCol,*pPrevCol; /* Collision links */
48770};
48771typedef struct lhash_bmap_page lhash_bmap_page;
48772struct lhash_bmap_page
48773{
48774 pgno iNum; /* Page number where this entry is stored */
48775 sxu16 iPtr; /* Offset to start reading/writing from */
48776 sxu32 nRec; /* Total number of records in this page */
48777 pgno iNext; /* Next map page */
48778};
48779/*
48780 * An in memory linear hash implemenation is represented by in an isntance
48781 * of the following structure.
48782 */
48783struct lhash_kv_engine
48784{
48785 const unqlite_kv_io *pIo; /* IO methods: Must be first */
48786 /* Private fields */
48787 SyMemBackend sAllocator; /* Private memory backend */
48788 ProcHash xHash; /* Default hash function */
48789 ProcCmp xCmp; /* Default comparison function */
48790 unqlite_page *pHeader; /* Page one to identify a valid implementation */
48791 lhash_bmap_rec **apMap; /* Buckets map records */
48792 sxu32 nBuckRec; /* Total number of bucket map records */
48793 sxu32 nBuckSize; /* apMap[] size */
48794 lhash_bmap_rec *pList; /* List of bucket map records */
48795 lhash_bmap_rec *pFirst; /* First record*/
48796 lhash_bmap_page sPageMap; /* Primary bucket map */
48797 int iPageSize; /* Page size */
48798 pgno nFreeList; /* List of free pages */
48799 pgno split_bucket; /* Current split bucket: MUST BE A POWER OF TWO */
48800 pgno max_split_bucket; /* Maximum split bucket: MUST BE A POWER OF TWO */
48801 pgno nmax_split_nucket; /* Next maximum split bucket (1 << nMsb): In-memory only */
48802 sxu32 nMagic; /* Magic number to identify a valid linear hash disk database */
48803};
48804/*
48805 * Given a logical bucket number, return the record associated with it.
48806 */
48807static lhash_bmap_rec * lhMapFindBucket(lhash_kv_engine *pEngine,pgno iLogic)
48808{
48809 lhash_bmap_rec *pRec;
48810 if( pEngine->nBuckRec < 1 ){
48811 /* Don't bother */
48812 return 0;
48813 }
48814 pRec = pEngine->apMap[iLogic & (pEngine->nBuckSize - 1)];
48815 for(;;){
48816 if( pRec == 0 ){
48817 break;
48818 }
48819 if( pRec->iLogic == iLogic ){
48820 return pRec;
48821 }
48822 /* Point to the next entry */
48823 pRec = pRec->pNextCol;
48824 }
48825 /* No such record */
48826 return 0;
48827}
48828/*
48829 * Install a new bucket map record.
48830 */
48831static int lhMapInstallBucket(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal)
48832{
48833 lhash_bmap_rec *pRec;
48834 sxu32 iBucket;
48835 /* Allocate a new instance */
48836 pRec = (lhash_bmap_rec *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhash_bmap_rec));
48837 if( pRec == 0 ){
48838 return UNQLITE_NOMEM;
48839 }
48840 /* Zero the structure */
48841 SyZero(pRec,sizeof(lhash_bmap_rec));
48842 /* Fill in the structure */
48843 pRec->iLogic = iLogic;
48844 pRec->iReal = iReal;
48845 iBucket = iLogic & (pEngine->nBuckSize - 1);
48846 pRec->pNextCol = pEngine->apMap[iBucket];
48847 if( pEngine->apMap[iBucket] ){
48848 pEngine->apMap[iBucket]->pPrevCol = pRec;
48849 }
48850 pEngine->apMap[iBucket] = pRec;
48851 /* Link */
48852 if( pEngine->pFirst == 0 ){
48853 pEngine->pFirst = pEngine->pList = pRec;
48854 }else{
48855 MACRO_LD_PUSH(pEngine->pList,pRec);
48856 }
48857 pEngine->nBuckRec++;
48858 if( (pEngine->nBuckRec >= pEngine->nBuckSize * 3) && pEngine->nBuckRec < 100000 ){
48859 /* Allocate a new larger table */
48860 sxu32 nNewSize = pEngine->nBuckSize << 1;
48861 lhash_bmap_rec *pEntry;
48862 lhash_bmap_rec **apNew;
48863 sxu32 n;
48864
48865 apNew = (lhash_bmap_rec **)SyMemBackendAlloc(&pEngine->sAllocator, nNewSize * sizeof(lhash_bmap_rec *));
48866 if( apNew ){
48867 /* Zero the new table */
48868 SyZero((void *)apNew, nNewSize * sizeof(lhash_bmap_rec *));
48869 /* Rehash all entries */
48870 n = 0;
48871 pEntry = pEngine->pList;
48872 for(;;){
48873 /* Loop one */
48874 if( n >= pEngine->nBuckRec ){
48875 break;
48876 }
48877 pEntry->pNextCol = pEntry->pPrevCol = 0;
48878 /* Install in the new bucket */
48879 iBucket = pEntry->iLogic & (nNewSize - 1);
48880 pEntry->pNextCol = apNew[iBucket];
48881 if( apNew[iBucket] ){
48882 apNew[iBucket]->pPrevCol = pEntry;
48883 }
48884 apNew[iBucket] = pEntry;
48885 /* Point to the next entry */
48886 pEntry = pEntry->pNext;
48887 n++;
48888 }
48889 /* Release the old table and reflect the change */
48890 SyMemBackendFree(&pEngine->sAllocator,(void *)pEngine->apMap);
48891 pEngine->apMap = apNew;
48892 pEngine->nBuckSize = nNewSize;
48893 }
48894 }
48895 return UNQLITE_OK;
48896}
48897/*
48898 * Process a raw bucket map record.
48899 */
48900static int lhMapLoadPage(lhash_kv_engine *pEngine,lhash_bmap_page *pMap,const unsigned char *zRaw)
48901{
48902 const unsigned char *zEnd = &zRaw[pEngine->iPageSize];
48903 const unsigned char *zPtr = zRaw;
48904 pgno iLogic,iReal;
48905 sxu32 n;
48906 int rc;
48907 if( pMap->iPtr == 0 ){
48908 /* Read the map header */
48909 SyBigEndianUnpack64(zRaw,&pMap->iNext);
48910 zRaw += 8;
48911 SyBigEndianUnpack32(zRaw,&pMap->nRec);
48912 zRaw += 4;
48913 }else{
48914 /* Mostly page one of the database */
48915 zRaw += pMap->iPtr;
48916 }
48917 /* Start processing */
48918 for( n = 0; n < pMap->nRec ; ++n ){
48919 if( zRaw >= zEnd ){
48920 break;
48921 }
48922 /* Extract the logical and real bucket number */
48923 SyBigEndianUnpack64(zRaw,&iLogic);
48924 zRaw += 8;
48925 SyBigEndianUnpack64(zRaw,&iReal);
48926 zRaw += 8;
48927 /* Install the record in the map */
48928 rc = lhMapInstallBucket(pEngine,iLogic,iReal);
48929 if( rc != UNQLITE_OK ){
48930 return rc;
48931 }
48932 }
48933 pMap->iPtr = (sxu16)(zRaw-zPtr);
48934 /* All done */
48935 return UNQLITE_OK;
48936}
48937/*
48938 * Allocate a new cell instance.
48939 */
48940static lhcell * lhNewCell(lhash_kv_engine *pEngine,lhpage *pPage)
48941{
48942 lhcell *pCell;
48943 pCell = (lhcell *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhcell));
48944 if( pCell == 0 ){
48945 return 0;
48946 }
48947 /* Zero the structure */
48948 SyZero(pCell,sizeof(lhcell));
48949 /* Fill in the structure */
48950 SyBlobInit(&pCell->sKey,&pEngine->sAllocator);
48951 pCell->pPage = pPage;
48952 return pCell;
48953}
48954/*
48955 * Discard a cell from the page table.
48956 */
48957static void lhCellDiscard(lhcell *pCell)
48958{
48959 lhpage *pPage = pCell->pPage->pMaster;
48960
48961 if( pCell->pPrevCol ){
48962 pCell->pPrevCol->pNextCol = pCell->pNextCol;
48963 }else{
48964 pPage->apCell[pCell->nHash & (pPage->nCellSize - 1)] = pCell->pNextCol;
48965 }
48966 if( pCell->pNextCol ){
48967 pCell->pNextCol->pPrevCol = pCell->pPrevCol;
48968 }
48969 MACRO_LD_REMOVE(pPage->pList,pCell);
48970 if( pCell == pPage->pFirst ){
48971 pPage->pFirst = pCell->pPrev;
48972 }
48973 pPage->nCell--;
48974 /* Release the cell */
48975 SyBlobRelease(&pCell->sKey);
48976 SyMemBackendPoolFree(&pPage->pHash->sAllocator,pCell);
48977}
48978/*
48979 * Install a cell in the page table.
48980 */
48981static int lhInstallCell(lhcell *pCell)
48982{
48983 lhpage *pPage = pCell->pPage->pMaster;
48984 sxu32 iBucket;
48985 if( pPage->nCell < 1 ){
48986 sxu32 nTableSize = 32; /* Must be a power of two */
48987 lhcell **apTable;
48988 /* Allocate a new cell table */
48989 apTable = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nTableSize * sizeof(lhcell *));
48990 if( apTable == 0 ){
48991 return UNQLITE_NOMEM;
48992 }
48993 /* Zero the new table */
48994 SyZero((void *)apTable, nTableSize * sizeof(lhcell *));
48995 /* Install it */
48996 pPage->apCell = apTable;
48997 pPage->nCellSize = nTableSize;
48998 }
48999 iBucket = pCell->nHash & (pPage->nCellSize - 1);
49000 pCell->pNextCol = pPage->apCell[iBucket];
49001 if( pPage->apCell[iBucket] ){
49002 pPage->apCell[iBucket]->pPrevCol = pCell;
49003 }
49004 pPage->apCell[iBucket] = pCell;
49005 if( pPage->pFirst == 0 ){
49006 pPage->pFirst = pPage->pList = pCell;
49007 }else{
49008 MACRO_LD_PUSH(pPage->pList,pCell);
49009 }
49010 pPage->nCell++;
49011 if( (pPage->nCell >= pPage->nCellSize * 3) && pPage->nCell < 100000 ){
49012 /* Allocate a new larger table */
49013 sxu32 nNewSize = pPage->nCellSize << 1;
49014 lhcell *pEntry;
49015 lhcell **apNew;
49016 sxu32 n;
49017
49018 apNew = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nNewSize * sizeof(lhcell *));
49019 if( apNew ){
49020 /* Zero the new table */
49021 SyZero((void *)apNew, nNewSize * sizeof(lhcell *));
49022 /* Rehash all entries */
49023 n = 0;
49024 pEntry = pPage->pList;
49025 for(;;){
49026 /* Loop one */
49027 if( n >= pPage->nCell ){
49028 break;
49029 }
49030 pEntry->pNextCol = pEntry->pPrevCol = 0;
49031 /* Install in the new bucket */
49032 iBucket = pEntry->nHash & (nNewSize - 1);
49033 pEntry->pNextCol = apNew[iBucket];
49034 if( apNew[iBucket] ){
49035 apNew[iBucket]->pPrevCol = pEntry;
49036 }
49037 apNew[iBucket] = pEntry;
49038 /* Point to the next entry */
49039 pEntry = pEntry->pNext;
49040 n++;
49041 }
49042 /* Release the old table and reflect the change */
49043 SyMemBackendFree(&pPage->pHash->sAllocator,(void *)pPage->apCell);
49044 pPage->apCell = apNew;
49045 pPage->nCellSize = nNewSize;
49046 }
49047 }
49048 return UNQLITE_OK;
49049}
49050/*
49051 * Private data of lhKeyCmp().
49052 */
49053struct lhash_key_cmp
49054{
49055 const char *zIn; /* Start of the stream */
49056 const char *zEnd; /* End of the stream */
49057 ProcCmp xCmp; /* Comparison function */
49058};
49059/*
49060 * Comparsion callback for large key > 256 KB
49061 */
49062static int lhKeyCmp(const void *pData,sxu32 nLen,void *pUserData)
49063{
49064 struct lhash_key_cmp *pCmp = (struct lhash_key_cmp *)pUserData;
49065 int rc;
49066 if( pCmp->zIn >= pCmp->zEnd ){
49067 if( nLen > 0 ){
49068 return UNQLITE_ABORT;
49069 }
49070 return UNQLITE_OK;
49071 }
49072 /* Perform the comparison */
49073 rc = pCmp->xCmp((const void *)pCmp->zIn,pData,nLen);
49074 if( rc != 0 ){
49075 /* Abort comparison */
49076 return UNQLITE_ABORT;
49077 }
49078 /* Advance the cursor */
49079 pCmp->zIn += nLen;
49080 return UNQLITE_OK;
49081}
49082/* Forward declaration */
49083static int lhConsumeCellkey(lhcell *pCell,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData,int offt_only);
49084/*
49085 * given a key, return the cell associated with it on success. NULL otherwise.
49086 */
49087static lhcell * lhFindCell(
49088 lhpage *pPage, /* Target page */
49089 const void *pKey, /* Lookup key */
49090 sxu32 nByte, /* Key length */
49091 sxu32 nHash /* Hash of the key */
49092 )
49093{
49094 lhcell *pEntry;
49095 if( pPage->nCell < 1 ){
49096 /* Don't bother hashing */
49097 return 0;
49098 }
49099 /* Point to the corresponding bucket */
49100 pEntry = pPage->apCell[nHash & (pPage->nCellSize - 1)];
49101 for(;;){
49102 if( pEntry == 0 ){
49103 break;
49104 }
49105 if( pEntry->nHash == nHash && pEntry->nKey == nByte ){
49106 if( SyBlobLength(&pEntry->sKey) < 1 ){
49107 /* Large key (> 256 KB) are not kept in-memory */
49108 struct lhash_key_cmp sCmp;
49109 int rc;
49110 /* Fill-in the structure */
49111 sCmp.zIn = (const char *)pKey;
49112 sCmp.zEnd = &sCmp.zIn[nByte];
49113 sCmp.xCmp = pPage->pHash->xCmp;
49114 /* Fetch the key from disk and perform the comparison */
49115 rc = lhConsumeCellkey(pEntry,lhKeyCmp,&sCmp,0);
49116 if( rc == UNQLITE_OK ){
49117 /* Cell found */
49118 return pEntry;
49119 }
49120 }else if ( pPage->pHash->xCmp(pKey,SyBlobData(&pEntry->sKey),nByte) == 0 ){
49121 /* Cell found */
49122 return pEntry;
49123 }
49124 }
49125 /* Point to the next entry */
49126 pEntry = pEntry->pNextCol;
49127 }
49128 /* No such entry */
49129 return 0;
49130}
49131/*
49132 * Parse a raw cell fetched from disk.
49133 */
49134static int lhParseOneCell(lhpage *pPage,const unsigned char *zRaw,const unsigned char *zEnd,lhcell **ppOut)
49135{
49136 sxu16 iNext,iOfft;
49137 sxu32 iHash,nKey;
49138 lhcell *pCell;
49139 sxu64 nData;
49140 int rc;
49141 /* Offset this cell is stored */
49142 iOfft = (sxu16)(zRaw - (const unsigned char *)pPage->pRaw->zData);
49143 /* 4 byte hash number */
49144 SyBigEndianUnpack32(zRaw,&iHash);
49145 zRaw += 4;
49146 /* 4 byte key length */
49147 SyBigEndianUnpack32(zRaw,&nKey);
49148 zRaw += 4;
49149 /* 8 byte data length */
49150 SyBigEndianUnpack64(zRaw,&nData);
49151 zRaw += 8;
49152 /* 2 byte offset of the next cell */
49153 SyBigEndianUnpack16(zRaw,&iNext);
49154 /* Perform a sanity check */
49155 if( iNext > 0 && &pPage->pRaw->zData[iNext] >= zEnd ){
49156 return UNQLITE_CORRUPT;
49157 }
49158 zRaw += 2;
49159 pCell = lhNewCell(pPage->pHash,pPage);
49160 if( pCell == 0 ){
49161 return UNQLITE_NOMEM;
49162 }
49163 /* Fill in the structure */
49164 pCell->iNext = iNext;
49165 pCell->nKey = nKey;
49166 pCell->nData = nData;
49167 pCell->nHash = iHash;
49168 /* Overflow page if any */
49169 SyBigEndianUnpack64(zRaw,&pCell->iOvfl);
49170 zRaw += 8;
49171 /* Cell offset */
49172 pCell->iStart = iOfft;
49173 /* Consume the key */
49174 rc = lhConsumeCellkey(pCell,unqliteDataConsumer,&pCell->sKey,pCell->nKey > 262144 /* 256 KB */? 1 : 0);
49175 if( rc != UNQLITE_OK ){
49176 /* TICKET: 14-32-chm@symisc.net: Key too large for memory */
49177 SyBlobRelease(&pCell->sKey);
49178 }
49179 /* Finally install the cell */
49180 rc = lhInstallCell(pCell);
49181 if( rc != UNQLITE_OK ){
49182 return rc;
49183 }
49184 if( ppOut ){
49185 *ppOut = pCell;
49186 }
49187 return UNQLITE_OK;
49188}
49189/*
49190 * Compute the total number of free space on a given page.
49191 */
49192static int lhPageFreeSpace(lhpage *pPage)
49193{
49194 const unsigned char *zEnd,*zRaw = pPage->pRaw->zData;
49195 lhphdr *pHdr = &pPage->sHdr;
49196 sxu16 iNext,iAmount;
49197 sxu16 nFree = 0;
49198 if( pHdr->iFree < 1 ){
49199 /* Don't bother processing, the page is full */
49200 pPage->nFree = 0;
49201 return UNQLITE_OK;
49202 }
49203 /* Point to first free block */
49204 zEnd = &zRaw[pPage->pHash->iPageSize];
49205 zRaw += pHdr->iFree;
49206 for(;;){
49207 /* Offset of the next free block */
49208 SyBigEndianUnpack16(zRaw,&iNext);
49209 zRaw += 2;
49210 /* Available space on this block */
49211 SyBigEndianUnpack16(zRaw,&iAmount);
49212 nFree += iAmount;
49213 if( iNext < 1 ){
49214 /* No more free blocks */
49215 break;
49216 }
49217 /* Point to the next free block*/
49218 zRaw = &pPage->pRaw->zData[iNext];
49219 if( zRaw >= zEnd ){
49220 /* Corrupt page */
49221 return UNQLITE_CORRUPT;
49222 }
49223 }
49224 /* Save the amount of free space */
49225 pPage->nFree = nFree;
49226 return UNQLITE_OK;
49227}
49228/*
49229 * Given a primary page, load all its cell.
49230 */
49231static int lhLoadCells(lhpage *pPage)
49232{
49233 const unsigned char *zEnd,*zRaw = pPage->pRaw->zData;
49234 lhphdr *pHdr = &pPage->sHdr;
49235 lhcell *pCell = 0; /* cc warning */
49236 int rc;
49237 /* Calculate the amount of free space available first */
49238 rc = lhPageFreeSpace(pPage);
49239 if( rc != UNQLITE_OK ){
49240 return rc;
49241 }
49242 if( pHdr->iOfft < 1 ){
49243 /* Don't bother processing, the page is empty */
49244 return UNQLITE_OK;
49245 }
49246 /* Point to first cell */
49247 zRaw += pHdr->iOfft;
49248 zEnd = &zRaw[pPage->pHash->iPageSize];
49249 for(;;){
49250 /* Parse a single cell */
49251 rc = lhParseOneCell(pPage,zRaw,zEnd,&pCell);
49252 if( rc != UNQLITE_OK ){
49253 return rc;
49254 }
49255 if( pCell->iNext < 1 ){
49256 /* No more cells */
49257 break;
49258 }
49259 /* Point to the next cell */
49260 zRaw = &pPage->pRaw->zData[pCell->iNext];
49261 if( zRaw >= zEnd ){
49262 /* Corrupt page */
49263 return UNQLITE_CORRUPT;
49264 }
49265 }
49266 /* All done */
49267 return UNQLITE_OK;
49268}
49269/*
49270 * Given a page, parse its raw headers.
49271 */
49272static int lhParsePageHeader(lhpage *pPage)
49273{
49274 const unsigned char *zRaw = pPage->pRaw->zData;
49275 lhphdr *pHdr = &pPage->sHdr;
49276 /* Offset of the first cell */
49277 SyBigEndianUnpack16(zRaw,&pHdr->iOfft);
49278 zRaw += 2;
49279 /* Offset of the first free block */
49280 SyBigEndianUnpack16(zRaw,&pHdr->iFree);
49281 zRaw += 2;
49282 /* Slave page number */
49283 SyBigEndianUnpack64(zRaw,&pHdr->iSlave);
49284 /* All done */
49285 return UNQLITE_OK;
49286}
49287/*
49288 * Allocate a new page instance.
49289 */
49290static lhpage * lhNewPage(
49291 lhash_kv_engine *pEngine, /* KV store which own this instance */
49292 unqlite_page *pRaw, /* Raw page contents */
49293 lhpage *pMaster /* Master page in case we are dealing with a slave page */
49294 )
49295{
49296 lhpage *pPage;
49297 /* Allocate a new instance */
49298 pPage = (lhpage *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhpage));
49299 if( pPage == 0 ){
49300 return 0;
49301 }
49302 /* Zero the structure */
49303 SyZero(pPage,sizeof(lhpage));
49304 /* Fill-in the structure */
49305 pPage->pHash = pEngine;
49306 pPage->pRaw = pRaw;
49307 pPage->pMaster = pMaster ? pMaster /* Slave page */ : pPage /* Master page */ ;
49308 if( pPage->pMaster != pPage ){
49309 /* Slave page, attach it to its master */
49310 pPage->pNextSlave = pMaster->pSlave;
49311 pMaster->pSlave = pPage;
49312 pMaster->iSlave++;
49313 }
49314 /* Save this instance for future fast lookup */
49315 pRaw->pUserData = pPage;
49316 /* All done */
49317 return pPage;
49318}
49319/*
49320 * Load a primary and its associated slave pages from disk.
49321 */
49322static int lhLoadPage(lhash_kv_engine *pEngine,pgno pnum,lhpage *pMaster,lhpage **ppOut,int iNest)
49323{
49324 unqlite_page *pRaw;
49325 lhpage *pPage = 0; /* cc warning */
49326 int rc;
49327 /* Aquire the page from the pager first */
49328 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pnum,&pRaw);
49329 if( rc != UNQLITE_OK ){
49330 return rc;
49331 }
49332 if( pRaw->pUserData ){
49333 /* The page is already parsed and loaded in memory. Point to it */
49334 pPage = (lhpage *)pRaw->pUserData;
49335 }else{
49336 /* Allocate a new page */
49337 pPage = lhNewPage(pEngine,pRaw,pMaster);
49338 if( pPage == 0 ){
49339 return UNQLITE_NOMEM;
49340 }
49341 /* Process the page */
49342 rc = lhParsePageHeader(pPage);
49343 if( rc == UNQLITE_OK ){
49344 /* Load cells */
49345 rc = lhLoadCells(pPage);
49346 }
49347 if( rc != UNQLITE_OK ){
49348 pEngine->pIo->xPageUnref(pPage->pRaw); /* pPage will be released inside this call */
49349 return rc;
49350 }
49351 if( pPage->sHdr.iSlave > 0 && iNest < 128 ){
49352 if( pMaster == 0 ){
49353 pMaster = pPage;
49354 }
49355 /* Slave page. Not a fatal error if something goes wrong here */
49356 lhLoadPage(pEngine,pPage->sHdr.iSlave,pMaster,0,iNest++);
49357 }
49358 }
49359 if( ppOut ){
49360 *ppOut = pPage;
49361 }
49362 return UNQLITE_OK;
49363}
49364/*
49365 * Given a cell, Consume its key by invoking the given callback for each extracted chunk.
49366 */
49367static int lhConsumeCellkey(
49368 lhcell *pCell, /* Target cell */
49369 int (*xConsumer)(const void *,unsigned int,void *), /* Consumer callback */
49370 void *pUserData, /* Last argument to xConsumer() */
49371 int offt_only
49372 )
49373{
49374 lhpage *pPage = pCell->pPage;
49375 const unsigned char *zRaw = pPage->pRaw->zData;
49376 const unsigned char *zPayload;
49377 int rc;
49378 /* Point to the payload area */
49379 zPayload = &zRaw[pCell->iStart];
49380 if( pCell->iOvfl == 0 ){
49381 /* Best scenario, consume the key directly without any overflow page */
49382 zPayload += L_HASH_CELL_SZ;
49383 rc = xConsumer((const void *)zPayload,pCell->nKey,pUserData);
49384 if( rc != UNQLITE_OK ){
49385 rc = UNQLITE_ABORT;
49386 }
49387 }else{
49388 lhash_kv_engine *pEngine = pPage->pHash;
49389 sxu32 nByte,nData = pCell->nKey;
49390 unqlite_page *pOvfl;
49391 int data_offset = 0;
49392 pgno iOvfl;
49393 /* Overflow page */
49394 iOvfl = pCell->iOvfl;
49395 /* Total usable bytes in an overflow page */
49396 nByte = L_HASH_OVERFLOW_SIZE(pEngine->iPageSize);
49397 for(;;){
49398 if( iOvfl == 0 || nData < 1 ){
49399 /* no more overflow page */
49400 break;
49401 }
49402 /* Point to the overflow page */
49403 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOvfl);
49404 if( rc != UNQLITE_OK ){
49405 return rc;
49406 }
49407 zPayload = &pOvfl->zData[8];
49408 /* Point to the raw content */
49409 if( !data_offset ){
49410 /* Get the data page and offset */
49411 SyBigEndianUnpack64(zPayload,&pCell->iDataPage);
49412 zPayload += 8;
49413 SyBigEndianUnpack16(zPayload,&pCell->iDataOfft);
49414 zPayload += 2;
49415 if( offt_only ){
49416 /* Key too large, grab the data offset and return */
49417 pEngine->pIo->xPageUnref(pOvfl);
49418 return UNQLITE_OK;
49419 }
49420 data_offset = 1;
49421 }
49422 /* Consume the key */
49423 if( nData <= nByte ){
49424 rc = xConsumer((const void *)zPayload,nData,pUserData);
49425 if( rc != UNQLITE_OK ){
49426 pEngine->pIo->xPageUnref(pOvfl);
49427 return UNQLITE_ABORT;
49428 }
49429 nData = 0;
49430 }else{
49431 rc = xConsumer((const void *)zPayload,nByte,pUserData);
49432 if( rc != UNQLITE_OK ){
49433 pEngine->pIo->xPageUnref(pOvfl);
49434 return UNQLITE_ABORT;
49435 }
49436 nData -= nByte;
49437 }
49438 /* Next overflow page in the chain */
49439 SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
49440 /* Unref the page */
49441 pEngine->pIo->xPageUnref(pOvfl);
49442 }
49443 rc = UNQLITE_OK;
49444 }
49445 return rc;
49446}
49447/*
49448 * Given a cell, Consume its data by invoking the given callback for each extracted chunk.
49449 */
49450static int lhConsumeCellData(
49451 lhcell *pCell, /* Target cell */
49452 int (*xConsumer)(const void *,unsigned int,void *), /* Data consumer callback */
49453 void *pUserData /* Last argument to xConsumer() */
49454 )
49455{
49456 lhpage *pPage = pCell->pPage;
49457 const unsigned char *zRaw = pPage->pRaw->zData;
49458 const unsigned char *zPayload;
49459 int rc;
49460 /* Point to the payload area */
49461 zPayload = &zRaw[pCell->iStart];
49462 if( pCell->iOvfl == 0 ){
49463 /* Best scenario, consume the data directly without any overflow page */
49464 zPayload += L_HASH_CELL_SZ + pCell->nKey;
49465 rc = xConsumer((const void *)zPayload,(sxu32)pCell->nData,pUserData);
49466 if( rc != UNQLITE_OK ){
49467 rc = UNQLITE_ABORT;
49468 }
49469 }else{
49470 lhash_kv_engine *pEngine = pPage->pHash;
49471 sxu64 nData = pCell->nData;
49472 unqlite_page *pOvfl;
49473 int fix_offset = 0;
49474 sxu32 nByte;
49475 pgno iOvfl;
49476 /* Overflow page where data is stored */
49477 iOvfl = pCell->iDataPage;
49478 for(;;){
49479 if( iOvfl == 0 || nData < 1 ){
49480 /* no more overflow page */
49481 break;
49482 }
49483 /* Point to the overflow page */
49484 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOvfl);
49485 if( rc != UNQLITE_OK ){
49486 return rc;
49487 }
49488 /* Point to the raw content */
49489 zPayload = pOvfl->zData;
49490 if( !fix_offset ){
49491 /* Point to the data */
49492 zPayload += pCell->iDataOfft;
49493 nByte = pEngine->iPageSize - pCell->iDataOfft;
49494 fix_offset = 1;
49495 }else{
49496 zPayload += 8;
49497 /* Total usable bytes in an overflow page */
49498 nByte = L_HASH_OVERFLOW_SIZE(pEngine->iPageSize);
49499 }
49500 /* Consume the data */
49501 if( nData <= (sxu64)nByte ){
49502 rc = xConsumer((const void *)zPayload,(unsigned int)nData,pUserData);
49503 if( rc != UNQLITE_OK ){
49504 pEngine->pIo->xPageUnref(pOvfl);
49505 return UNQLITE_ABORT;
49506 }
49507 nData = 0;
49508 }else{
49509 if( nByte > 0 ){
49510 rc = xConsumer((const void *)zPayload,nByte,pUserData);
49511 if( rc != UNQLITE_OK ){
49512 pEngine->pIo->xPageUnref(pOvfl);
49513 return UNQLITE_ABORT;
49514 }
49515 nData -= nByte;
49516 }
49517 }
49518 /* Next overflow page in the chain */
49519 SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
49520 /* Unref the page */
49521 pEngine->pIo->xPageUnref(pOvfl);
49522 }
49523 rc = UNQLITE_OK;
49524 }
49525 return rc;
49526}
49527/*
49528 * Read the linear hash header (Page one of the database).
49529 */
49530static int lhash_read_header(lhash_kv_engine *pEngine,unqlite_page *pHeader)
49531{
49532 const unsigned char *zRaw = pHeader->zData;
49533 lhash_bmap_page *pMap;
49534 sxu32 nHash;
49535 int rc;
49536 pEngine->pHeader = pHeader;
49537 /* 4 byte magic number */
49538 SyBigEndianUnpack32(zRaw,&pEngine->nMagic);
49539 zRaw += 4;
49540 if( pEngine->nMagic != L_HASH_MAGIC ){
49541 /* Corrupt implementation */
49542 return UNQLITE_CORRUPT;
49543 }
49544 /* 4 byte hash value to identify a valid hash function */
49545 SyBigEndianUnpack32(zRaw,&nHash);
49546 zRaw += 4;
49547 /* Sanity check */
49548 if( pEngine->xHash(L_HASH_WORD,sizeof(L_HASH_WORD)-1) != nHash ){
49549 /* Different hash function */
49550 pEngine->pIo->xErr(pEngine->pIo->pHandle,"Invalid hash function");
49551 return UNQLITE_INVALID;
49552 }
49553 /* List of free pages */
49554 SyBigEndianUnpack64(zRaw,&pEngine->nFreeList);
49555 zRaw += 8;
49556 /* Current split bucket */
49557 SyBigEndianUnpack64(zRaw,&pEngine->split_bucket);
49558 zRaw += 8;
49559 /* Maximum split bucket */
49560 SyBigEndianUnpack64(zRaw,&pEngine->max_split_bucket);
49561 zRaw += 8;
49562 /* Next generation */
49563 pEngine->nmax_split_nucket = pEngine->max_split_bucket << 1;
49564 /* Initialiaze the bucket map */
49565 pMap = &pEngine->sPageMap;
49566 /* Fill in the structure */
49567 pMap->iNum = pHeader->pgno;
49568 /* Next page in the bucket map */
49569 SyBigEndianUnpack64(zRaw,&pMap->iNext);
49570 zRaw += 8;
49571 /* Total number of records in the bucket map (This page only) */
49572 SyBigEndianUnpack32(zRaw,&pMap->nRec);
49573 zRaw += 4;
49574 pMap->iPtr = (sxu16)(zRaw - pHeader->zData);
49575 /* Load the map in memory */
49576 rc = lhMapLoadPage(pEngine,pMap,pHeader->zData);
49577 if( rc != UNQLITE_OK ){
49578 return rc;
49579 }
49580 /* Load the bucket map chain if any */
49581 for(;;){
49582 pgno iNext = pMap->iNext;
49583 unqlite_page *pPage;
49584 if( iNext == 0 ){
49585 /* No more map pages */
49586 break;
49587 }
49588 /* Point to the target page */
49589 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iNext,&pPage);
49590 if( rc != UNQLITE_OK ){
49591 return rc;
49592 }
49593 /* Fill in the structure */
49594 pMap->iNum = iNext;
49595 pMap->iPtr = 0;
49596 /* Load the map in memory */
49597 rc = lhMapLoadPage(pEngine,pMap,pPage->zData);
49598 if( rc != UNQLITE_OK ){
49599 return rc;
49600 }
49601 }
49602 /* All done */
49603 return UNQLITE_OK;
49604}
49605/*
49606 * Perform a record lookup.
49607 */
49608static int lhRecordLookup(
49609 lhash_kv_engine *pEngine, /* KV storage engine */
49610 const void *pKey, /* Lookup key */
49611 sxu32 nByte, /* Key length */
49612 lhcell **ppCell /* OUT: Target cell on success */
49613 )
49614{
49615 lhash_bmap_rec *pRec;
49616 lhpage *pPage;
49617 lhcell *pCell;
49618 pgno iBucket;
49619 sxu32 nHash;
49620 int rc;
49621 /* Acquire the first page (hash Header) so that everything gets loaded autmatically */
49622 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
49623 if( rc != UNQLITE_OK ){
49624 return rc;
49625 }
49626 /* Compute the hash of the key first */
49627 nHash = pEngine->xHash(pKey,nByte);
49628 /* Extract the logical (i.e. not real) page number */
49629 iBucket = nHash & (pEngine->nmax_split_nucket - 1);
49630 if( iBucket >= (pEngine->split_bucket + pEngine->max_split_bucket) ){
49631 /* Low mask */
49632 iBucket = nHash & (pEngine->max_split_bucket - 1);
49633 }
49634 /* Map the logical bucket number to real page number */
49635 pRec = lhMapFindBucket(pEngine,iBucket);
49636 if( pRec == 0 ){
49637 /* No such entry */
49638 return UNQLITE_NOTFOUND;
49639 }
49640 /* Load the master page and it's slave page in-memory */
49641 rc = lhLoadPage(pEngine,pRec->iReal,0,&pPage,0);
49642 if( rc != UNQLITE_OK ){
49643 /* IO error, unlikely scenario */
49644 return rc;
49645 }
49646 /* Lookup for the cell */
49647 pCell = lhFindCell(pPage,pKey,nByte,nHash);
49648 if( pCell == 0 ){
49649 /* No such entry */
49650 return UNQLITE_NOTFOUND;
49651 }
49652 if( ppCell ){
49653 *ppCell = pCell;
49654 }
49655 return UNQLITE_OK;
49656}
49657/*
49658 * Acquire a new page either from the free list or ask the pager
49659 * for a new one.
49660 */
49661static int lhAcquirePage(lhash_kv_engine *pEngine,unqlite_page **ppOut)
49662{
49663 unqlite_page *pPage;
49664 int rc;
49665 if( pEngine->nFreeList != 0 ){
49666 /* Acquire one from the free list */
49667 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pEngine->nFreeList,&pPage);
49668 if( rc == UNQLITE_OK ){
49669 /* Point to the next free page */
49670 SyBigEndianUnpack64(pPage->zData,&pEngine->nFreeList);
49671 /* Update the database header */
49672 rc = pEngine->pIo->xWrite(pEngine->pHeader);
49673 if( rc != UNQLITE_OK ){
49674 return rc;
49675 }
49676 SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/],pEngine->nFreeList);
49677 /* Tell the pager do not journal this page */
49678 pEngine->pIo->xDontJournal(pPage);
49679 /* Return to the caller */
49680 *ppOut = pPage;
49681 /* All done */
49682 return UNQLITE_OK;
49683 }
49684 }
49685 /* Acquire a new page */
49686 rc = pEngine->pIo->xNew(pEngine->pIo->pHandle,&pPage);
49687 if( rc != UNQLITE_OK ){
49688 return rc;
49689 }
49690 /* Point to the target page */
49691 *ppOut = pPage;
49692 return UNQLITE_OK;
49693}
49694/*
49695 * Write a bucket map record to disk.
49696 */
49697static int lhMapWriteRecord(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal)
49698{
49699 lhash_bmap_page *pMap = &pEngine->sPageMap;
49700 unqlite_page *pPage = 0;
49701 int rc;
49702 if( pMap->iPtr > (pEngine->iPageSize - 16) /* 8 byte logical bucket number + 8 byte real bucket number */ ){
49703 unqlite_page *pOld;
49704 /* Point to the old page */
49705 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pMap->iNum,&pOld);
49706 if( rc != UNQLITE_OK ){
49707 return rc;
49708 }
49709 /* Acquire a new page */
49710 rc = lhAcquirePage(pEngine,&pPage);
49711 if( rc != UNQLITE_OK ){
49712 return rc;
49713 }
49714 /* Reflect the change */
49715 pMap->iNext = 0;
49716 pMap->iNum = pPage->pgno;
49717 pMap->nRec = 0;
49718 pMap->iPtr = 8/* Next page number */+4/* Total records in the map*/;
49719 /* Link this page */
49720 rc = pEngine->pIo->xWrite(pOld);
49721 if( rc != UNQLITE_OK ){
49722 return rc;
49723 }
49724 if( pOld->pgno == pEngine->pHeader->pgno ){
49725 /* First page (Hash header) */
49726 SyBigEndianPack64(&pOld->zData[4/*magic*/+4/*hash*/+8/* Free page */+8/*current split bucket*/+8/*Maximum split bucket*/],pPage->pgno);
49727 }else{
49728 /* Link the new page */
49729 SyBigEndianPack64(pOld->zData,pPage->pgno);
49730 /* Unref */
49731 pEngine->pIo->xPageUnref(pOld);
49732 }
49733 /* Assume the last bucket map page */
49734 rc = pEngine->pIo->xWrite(pPage);
49735 if( rc != UNQLITE_OK ){
49736 return rc;
49737 }
49738 SyBigEndianPack64(pPage->zData,0); /* Next bucket map page on the list */
49739 }
49740 if( pPage == 0){
49741 /* Point to the current map page */
49742 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pMap->iNum,&pPage);
49743 if( rc != UNQLITE_OK ){
49744 return rc;
49745 }
49746 }
49747 /* Make page writable */
49748 rc = pEngine->pIo->xWrite(pPage);
49749 if( rc != UNQLITE_OK ){
49750 return rc;
49751 }
49752 /* Write the data */
49753 SyBigEndianPack64(&pPage->zData[pMap->iPtr],iLogic);
49754 pMap->iPtr += 8;
49755 SyBigEndianPack64(&pPage->zData[pMap->iPtr],iReal);
49756 pMap->iPtr += 8;
49757 /* Install the bucket map */
49758 rc = lhMapInstallBucket(pEngine,iLogic,iReal);
49759 if( rc == UNQLITE_OK ){
49760 /* Total number of records */
49761 pMap->nRec++;
49762 if( pPage->pgno == pEngine->pHeader->pgno ){
49763 /* Page one: Always writable */
49764 SyBigEndianPack32(
49765 &pPage->zData[4/*magic*/+4/*hash*/+8/* Free page */+8/*current split bucket*/+8/*Maximum split bucket*/+8/*Next map page*/],
49766 pMap->nRec);
49767 }else{
49768 /* Make page writable */
49769 rc = pEngine->pIo->xWrite(pPage);
49770 if( rc != UNQLITE_OK ){
49771 return rc;
49772 }
49773 SyBigEndianPack32(&pPage->zData[8],pMap->nRec);
49774 }
49775 }
49776 return rc;
49777}
49778/*
49779 * Defragment a page.
49780 */
49781static int lhPageDefragment(lhpage *pPage)
49782{
49783 lhash_kv_engine *pEngine = pPage->pHash;
49784 unsigned char *zTmp,*zPtr,*zEnd,*zPayload;
49785 lhcell *pCell;
49786 /* Get a temporary page from the pager. This opertaion never fail */
49787 zTmp = pEngine->pIo->xTmpPage(pEngine->pIo->pHandle);
49788 /* Move the target cells to the begining */
49789 pCell = pPage->pList;
49790 /* Write the slave page number */
49791 SyBigEndianPack64(&zTmp[2/*Offset of the first cell */+2/*Offset of the first free block */],pPage->sHdr.iSlave);
49792 zPtr = &zTmp[L_HASH_PAGE_HDR_SZ]; /* Offset to start writing from */
49793 zEnd = &zTmp[pEngine->iPageSize];
49794 pPage->sHdr.iOfft = 0; /* Offset of the first cell */
49795 for(;;){
49796 if( pCell == 0 ){
49797 /* No more cells */
49798 break;
49799 }
49800 if( pCell->pPage->pRaw->pgno == pPage->pRaw->pgno ){
49801 /* Cell payload if locally stored */
49802 zPayload = 0;
49803 if( pCell->iOvfl == 0 ){
49804 zPayload = &pCell->pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ];
49805 }
49806 /* Move the cell */
49807 pCell->iNext = pPage->sHdr.iOfft;
49808 pCell->iStart = (sxu16)(zPtr - zTmp); /* Offset where this cell start */
49809 pPage->sHdr.iOfft = pCell->iStart;
49810 /* Write the cell header */
49811 /* 4 byte hash number */
49812 SyBigEndianPack32(zPtr,pCell->nHash);
49813 zPtr += 4;
49814 /* 4 byte ley length */
49815 SyBigEndianPack32(zPtr,pCell->nKey);
49816 zPtr += 4;
49817 /* 8 byte data length */
49818 SyBigEndianPack64(zPtr,pCell->nData);
49819 zPtr += 8;
49820 /* 2 byte offset of the next cell */
49821 SyBigEndianPack16(zPtr,pCell->iNext);
49822 zPtr += 2;
49823 /* 8 byte overflow page number */
49824 SyBigEndianPack64(zPtr,pCell->iOvfl);
49825 zPtr += 8;
49826 if( zPayload ){
49827 /* Local payload */
49828 SyMemcpy((const void *)zPayload,zPtr,(sxu32)(pCell->nKey + pCell->nData));
49829 zPtr += pCell->nKey + pCell->nData;
49830 }
49831 if( zPtr >= zEnd ){
49832 /* Can't happen */
49833 break;
49834 }
49835 }
49836 /* Point to the next page */
49837 pCell = pCell->pNext;
49838 }
49839 /* Mark the free block */
49840 pPage->nFree = (sxu16)(zEnd - zPtr); /* Block length */
49841 if( pPage->nFree > 3 ){
49842 pPage->sHdr.iFree = (sxu16)(zPtr - zTmp); /* Offset of the free block */
49843 /* Mark the block */
49844 SyBigEndianPack16(zPtr,0); /* Offset of the next free block */
49845 SyBigEndianPack16(&zPtr[2],pPage->nFree); /* Block length */
49846 }else{
49847 /* Block of length less than 4 bytes are simply discarded */
49848 pPage->nFree = 0;
49849 pPage->sHdr.iFree = 0;
49850 }
49851 /* Reflect the change */
49852 SyBigEndianPack16(zTmp,pPage->sHdr.iOfft); /* Offset of the first cell */
49853 SyBigEndianPack16(&zTmp[2],pPage->sHdr.iFree); /* Offset of the first free block */
49854 SyMemcpy((const void *)zTmp,pPage->pRaw->zData,pEngine->iPageSize);
49855 /* All done */
49856 return UNQLITE_OK;
49857}
49858/*
49859** Allocate nByte bytes of space on a page.
49860**
49861** Return the index into pPage->pRaw->zData[] of the first byte of
49862** the new allocation. Or return 0 if there is not enough free
49863** space on the page to satisfy the allocation request.
49864**
49865** If the page contains nBytes of free space but does not contain
49866** nBytes of contiguous free space, then this routine automatically
49867** calls defragementPage() to consolidate all free space before
49868** allocating the new chunk.
49869*/
49870static int lhAllocateSpace(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft)
49871{
49872 const unsigned char *zEnd,*zPtr;
49873 sxu16 iNext,iBlksz,nByte;
49874 unsigned char *zPrev;
49875 int rc;
49876 if( (sxu64)pPage->nFree < nAmount ){
49877 /* Don't bother looking for a free chunk */
49878 return UNQLITE_FULL;
49879 }
49880 if( pPage->nCell < 10 && ((int)nAmount >= (pPage->pHash->iPageSize / 2)) ){
49881 /* Big chunk need an overflow page for its data */
49882 return UNQLITE_FULL;
49883 }
49884 zPtr = &pPage->pRaw->zData[pPage->sHdr.iFree];
49885 zEnd = &pPage->pRaw->zData[pPage->pHash->iPageSize];
49886 nByte = (sxu16)nAmount;
49887 zPrev = 0;
49888 iBlksz = 0; /* cc warning */
49889 /* Perform the lookup */
49890 for(;;){
49891 if( zPtr >= zEnd ){
49892 return UNQLITE_FULL;
49893 }
49894 /* Offset of the next free block */
49895 SyBigEndianUnpack16(zPtr,&iNext);
49896 /* Block size */
49897 SyBigEndianUnpack16(&zPtr[2],&iBlksz);
49898 if( iBlksz >= nByte ){
49899 /* Got one */
49900 break;
49901 }
49902 zPrev = (unsigned char *)zPtr;
49903 if( iNext == 0 ){
49904 /* No more free blocks, defragment the page */
49905 rc = lhPageDefragment(pPage);
49906 if( rc == UNQLITE_OK && pPage->nFree >= nByte) {
49907 /* Free blocks are merged together */
49908 iNext = 0;
49909 zPtr = &pPage->pRaw->zData[pPage->sHdr.iFree];
49910 iBlksz = pPage->nFree;
49911 zPrev = 0;
49912 break;
49913 }else{
49914 return UNQLITE_FULL;
49915 }
49916 }
49917 /* Point to the next free block */
49918 zPtr = &pPage->pRaw->zData[iNext];
49919 }
49920 /* Acquire writer lock on this page */
49921 rc = pPage->pHash->pIo->xWrite(pPage->pRaw);
49922 if( rc != UNQLITE_OK ){
49923 return rc;
49924 }
49925 /* Save block offset */
49926 *pOfft = (sxu16)(zPtr - pPage->pRaw->zData);
49927 /* Fix pointers */
49928 if( iBlksz >= nByte && (iBlksz - nByte) > 3 ){
49929 unsigned char *zBlock = &pPage->pRaw->zData[(*pOfft) + nByte];
49930 /* Create a new block */
49931 zPtr = zBlock;
49932 SyBigEndianPack16(zBlock,iNext); /* Offset of the next block */
49933 SyBigEndianPack16(&zBlock[2],iBlksz-nByte); /* Block size*/
49934 /* Offset of the new block */
49935 iNext = (sxu16)(zPtr - pPage->pRaw->zData);
49936 }
49937 /* Fix offsets */
49938 if( zPrev ){
49939 SyBigEndianPack16(zPrev,iNext);
49940 }else{
49941 /* First block */
49942 pPage->sHdr.iFree = iNext;
49943 /* Reflect on the page header */
49944 SyBigEndianPack16(&pPage->pRaw->zData[2/* Offset of the first cell1*/],iNext);
49945 }
49946 /* All done */
49947 pPage->nFree -= nByte;
49948 return UNQLITE_OK;
49949}
49950/*
49951 * Write the cell header into the corresponding offset.
49952 */
49953static int lhCellWriteHeader(lhcell *pCell)
49954{
49955 lhpage *pPage = pCell->pPage;
49956 unsigned char *zRaw = pPage->pRaw->zData;
49957 /* Seek to the desired location */
49958 zRaw += pCell->iStart;
49959 /* 4 byte hash number */
49960 SyBigEndianPack32(zRaw,pCell->nHash);
49961 zRaw += 4;
49962 /* 4 byte key length */
49963 SyBigEndianPack32(zRaw,pCell->nKey);
49964 zRaw += 4;
49965 /* 8 byte data length */
49966 SyBigEndianPack64(zRaw,pCell->nData);
49967 zRaw += 8;
49968 /* 2 byte offset of the next cell */
49969 pCell->iNext = pPage->sHdr.iOfft;
49970 SyBigEndianPack16(zRaw,pCell->iNext);
49971 zRaw += 2;
49972 /* 8 byte overflow page number */
49973 SyBigEndianPack64(zRaw,pCell->iOvfl);
49974 /* Update the page header */
49975 pPage->sHdr.iOfft = pCell->iStart;
49976 /* pEngine->pIo->xWrite() has been successfully called on this page */
49977 SyBigEndianPack16(pPage->pRaw->zData,pCell->iStart);
49978 /* All done */
49979 return UNQLITE_OK;
49980}
49981/*
49982 * Write local payload.
49983 */
49984static int lhCellWriteLocalPayload(lhcell *pCell,
49985 const void *pKey,sxu32 nKeylen,
49986 const void *pData,unqlite_int64 nDatalen
49987 )
49988{
49989 /* A writer lock have been acquired on this page */
49990 lhpage *pPage = pCell->pPage;
49991 unsigned char *zRaw = pPage->pRaw->zData;
49992 /* Seek to the desired location */
49993 zRaw += pCell->iStart + L_HASH_CELL_SZ;
49994 /* Write the key */
49995 SyMemcpy(pKey,(void *)zRaw,nKeylen);
49996 zRaw += nKeylen;
49997 if( nDatalen > 0 ){
49998 /* Write the Data */
49999 SyMemcpy(pData,(void *)zRaw,(sxu32)nDatalen);
50000 }
50001 return UNQLITE_OK;
50002}
50003/*
50004 * Allocate as much overflow page we need to store the cell payload.
50005 */
50006static int lhCellWriteOvflPayload(lhcell *pCell,const void *pKey,sxu32 nKeylen,...)
50007{
50008 lhpage *pPage = pCell->pPage;
50009 lhash_kv_engine *pEngine = pPage->pHash;
50010 unqlite_page *pOvfl,*pFirst,*pNew;
50011 const unsigned char *zPtr,*zEnd;
50012 unsigned char *zRaw,*zRawEnd;
50013 sxu32 nAvail;
50014 va_list ap;
50015 int rc;
50016 /* Acquire a new overflow page */
50017 rc = lhAcquirePage(pEngine,&pOvfl);
50018 if( rc != UNQLITE_OK ){
50019 return rc;
50020 }
50021 /* Acquire a writer lock */
50022 rc = pEngine->pIo->xWrite(pOvfl);
50023 if( rc != UNQLITE_OK ){
50024 return rc;
50025 }
50026 pFirst = pOvfl;
50027 /* Link */
50028 pCell->iOvfl = pOvfl->pgno;
50029 /* Update the cell header */
50030 SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4/*Hash*/ + 4/*Key*/ + 8/*Data*/ + 2 /*Next cell*/],pCell->iOvfl);
50031 /* Start the write process */
50032 zPtr = (const unsigned char *)pKey;
50033 zEnd = &zPtr[nKeylen];
50034 SyBigEndianPack64(pOvfl->zData,0); /* Next overflow page on the chain */
50035 zRaw = &pOvfl->zData[8/* Next ovfl page*/ + 8 /* Data page */ + 2 /* Data offset*/];
50036 zRawEnd = &pOvfl->zData[pEngine->iPageSize];
50037 pNew = pOvfl;
50038 /* Write the key */
50039 for(;;){
50040 if( zPtr >= zEnd ){
50041 break;
50042 }
50043 if( zRaw >= zRawEnd ){
50044 /* Acquire a new page */
50045 rc = lhAcquirePage(pEngine,&pNew);
50046 if( rc != UNQLITE_OK ){
50047 return rc;
50048 }
50049 rc = pEngine->pIo->xWrite(pNew);
50050 if( rc != UNQLITE_OK ){
50051 return rc;
50052 }
50053 /* Link */
50054 SyBigEndianPack64(pOvfl->zData,pNew->pgno);
50055 pEngine->pIo->xPageUnref(pOvfl);
50056 SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
50057 pOvfl = pNew;
50058 zRaw = &pNew->zData[8];
50059 zRawEnd = &pNew->zData[pEngine->iPageSize];
50060 }
50061 nAvail = (sxu32)(zRawEnd-zRaw);
50062 nKeylen = (sxu32)(zEnd-zPtr);
50063 if( nKeylen > nAvail ){
50064 nKeylen = nAvail;
50065 }
50066 SyMemcpy((const void *)zPtr,(void *)zRaw,nKeylen);
50067 /* Synchronize pointers */
50068 zPtr += nKeylen;
50069 zRaw += nKeylen;
50070 }
50071 rc = UNQLITE_OK;
50072 va_start(ap,nKeylen);
50073 pCell->iDataPage = pNew->pgno;
50074 pCell->iDataOfft = (sxu16)(zRaw-pNew->zData);
50075 /* Write the data page and its offset */
50076 SyBigEndianPack64(&pFirst->zData[8/*Next ovfl*/],pCell->iDataPage);
50077 SyBigEndianPack16(&pFirst->zData[8/*Next ovfl*/+8/*Data page*/],pCell->iDataOfft);
50078 /* Write data */
50079 for(;;){
50080 const void *pData;
50081 sxu32 nDatalen;
50082 sxu64 nData;
50083 pData = va_arg(ap,const void *);
50084 nData = va_arg(ap,sxu64);
50085 if( pData == 0 ){
50086 /* No more chunks */
50087 break;
50088 }
50089 /* Write this chunk */
50090 zPtr = (const unsigned char *)pData;
50091 zEnd = &zPtr[nData];
50092 for(;;){
50093 if( zPtr >= zEnd ){
50094 break;
50095 }
50096 if( zRaw >= zRawEnd ){
50097 /* Acquire a new page */
50098 rc = lhAcquirePage(pEngine,&pNew);
50099 if( rc != UNQLITE_OK ){
50100 va_end(ap);
50101 return rc;
50102 }
50103 rc = pEngine->pIo->xWrite(pNew);
50104 if( rc != UNQLITE_OK ){
50105 va_end(ap);
50106 return rc;
50107 }
50108 /* Link */
50109 SyBigEndianPack64(pOvfl->zData,pNew->pgno);
50110 pEngine->pIo->xPageUnref(pOvfl);
50111 SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
50112 pOvfl = pNew;
50113 zRaw = &pNew->zData[8];
50114 zRawEnd = &pNew->zData[pEngine->iPageSize];
50115 }
50116 nAvail = (sxu32)(zRawEnd-zRaw);
50117 nDatalen = (sxu32)(zEnd-zPtr);
50118 if( nDatalen > nAvail ){
50119 nDatalen = nAvail;
50120 }
50121 SyMemcpy((const void *)zPtr,(void *)zRaw,nDatalen);
50122 /* Synchronize pointers */
50123 zPtr += nDatalen;
50124 zRaw += nDatalen;
50125 }
50126 }
50127 /* Unref the overflow page */
50128 pEngine->pIo->xPageUnref(pOvfl);
50129 va_end(ap);
50130 return UNQLITE_OK;
50131}
50132/*
50133 * Restore a page to the free list.
50134 */
50135static int lhRestorePage(lhash_kv_engine *pEngine,unqlite_page *pPage)
50136{
50137 int rc;
50138 rc = pEngine->pIo->xWrite(pEngine->pHeader);
50139 if( rc != UNQLITE_OK ){
50140 return rc;
50141 }
50142 rc = pEngine->pIo->xWrite(pPage);
50143 if( rc != UNQLITE_OK ){
50144 return rc;
50145 }
50146 /* Link to the list of free page */
50147 SyBigEndianPack64(pPage->zData,pEngine->nFreeList);
50148 pEngine->nFreeList = pPage->pgno;
50149 SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/],pEngine->nFreeList);
50150 /* All done */
50151 return UNQLITE_OK;
50152}
50153/*
50154 * Restore cell space and mark it as a free block.
50155 */
50156static int lhRestoreSpace(lhpage *pPage,sxu16 iOfft,sxu16 nByte)
50157{
50158 unsigned char *zRaw;
50159 if( nByte < 4 ){
50160 /* At least 4 bytes of freespace must form a valid block */
50161 return UNQLITE_OK;
50162 }
50163 /* pEngine->pIo->xWrite() has been successfully called on this page */
50164 zRaw = &pPage->pRaw->zData[iOfft];
50165 /* Mark as a free block */
50166 SyBigEndianPack16(zRaw,pPage->sHdr.iFree); /* Offset of the next free block */
50167 zRaw += 2;
50168 SyBigEndianPack16(zRaw,nByte);
50169 /* Link */
50170 SyBigEndianPack16(&pPage->pRaw->zData[2/* offset of the first cell */],iOfft);
50171 pPage->sHdr.iFree = iOfft;
50172 pPage->nFree += nByte;
50173 return UNQLITE_OK;
50174}
50175/* Forward declaration */
50176static lhcell * lhFindSibeling(lhcell *pCell);
50177/*
50178 * Unlink a cell.
50179 */
50180static int lhUnlinkCell(lhcell *pCell)
50181{
50182 lhash_kv_engine *pEngine = pCell->pPage->pHash;
50183 lhpage *pPage = pCell->pPage;
50184 sxu16 nByte = L_HASH_CELL_SZ;
50185 lhcell *pPrev;
50186 int rc;
50187 rc = pEngine->pIo->xWrite(pPage->pRaw);
50188 if( rc != UNQLITE_OK ){
50189 return rc;
50190 }
50191 /* Bring the link */
50192 pPrev = lhFindSibeling(pCell);
50193 if( pPrev ){
50194 pPrev->iNext = pCell->iNext;
50195 /* Fix offsets in the page header */
50196 SyBigEndianPack16(&pPage->pRaw->zData[pPrev->iStart + 4/*Hash*/+4/*Key*/+8/*Data*/],pCell->iNext);
50197 }else{
50198 /* First entry on this page (either master or slave) */
50199 pPage->sHdr.iOfft = pCell->iNext;
50200 /* Update the page header */
50201 SyBigEndianPack16(pPage->pRaw->zData,pCell->iNext);
50202 }
50203 /* Restore cell space */
50204 if( pCell->iOvfl == 0 ){
50205 nByte += (sxu16)(pCell->nData + pCell->nKey);
50206 }
50207 lhRestoreSpace(pPage,pCell->iStart,nByte);
50208 /* Discard the cell from the in-memory hashtable */
50209 lhCellDiscard(pCell);
50210 return UNQLITE_OK;
50211}
50212/*
50213 * Remove a cell and its paylod (key + data).
50214 */
50215static int lhRecordRemove(lhcell *pCell)
50216{
50217 lhash_kv_engine *pEngine = pCell->pPage->pHash;
50218 int rc;
50219 if( pCell->iOvfl > 0){
50220 /* Discard overflow pages */
50221 unqlite_page *pOvfl;
50222 pgno iNext = pCell->iOvfl;
50223 for(;;){
50224 /* Point to the overflow page */
50225 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iNext,&pOvfl);
50226 if( rc != UNQLITE_OK ){
50227 return rc;
50228 }
50229 /* Next page on the chain */
50230 SyBigEndianUnpack64(pOvfl->zData,&iNext);
50231 /* Restore the page to the free list */
50232 rc = lhRestorePage(pEngine,pOvfl);
50233 if( rc != UNQLITE_OK ){
50234 return rc;
50235 }
50236 /* Unref */
50237 pEngine->pIo->xPageUnref(pOvfl);
50238 if( iNext == 0 ){
50239 break;
50240 }
50241 }
50242 }
50243 /* Unlink the cell */
50244 rc = lhUnlinkCell(pCell);
50245 return rc;
50246}
50247/*
50248 * Find cell sibeling.
50249 */
50250static lhcell * lhFindSibeling(lhcell *pCell)
50251{
50252 lhpage *pPage = pCell->pPage->pMaster;
50253 lhcell *pEntry;
50254 pEntry = pPage->pFirst;
50255 while( pEntry ){
50256 if( pEntry->pPage == pCell->pPage && pEntry->iNext == pCell->iStart ){
50257 /* Sibeling found */
50258 return pEntry;
50259 }
50260 /* Point to the previous entry */
50261 pEntry = pEntry->pPrev;
50262 }
50263 /* Last inserted cell */
50264 return 0;
50265}
50266/*
50267 * Move a cell to a new location with its new data.
50268 */
50269static int lhMoveLocalCell(
50270 lhcell *pCell,
50271 sxu16 iOfft,
50272 const void *pData,
50273 unqlite_int64 nData
50274 )
50275{
50276 sxu16 iKeyOfft = pCell->iStart + L_HASH_CELL_SZ;
50277 lhpage *pPage = pCell->pPage;
50278 lhcell *pSibeling;
50279 pSibeling = lhFindSibeling(pCell);
50280 if( pSibeling ){
50281 /* Fix link */
50282 SyBigEndianPack16(&pPage->pRaw->zData[pSibeling->iStart + 4/*Hash*/+4/*Key*/+8/*Data*/],pCell->iNext);
50283 pSibeling->iNext = pCell->iNext;
50284 }else{
50285 /* First cell, update page header only */
50286 SyBigEndianPack16(pPage->pRaw->zData,pCell->iNext);
50287 pPage->sHdr.iOfft = pCell->iNext;
50288 }
50289 /* Set the new offset */
50290 pCell->iStart = iOfft;
50291 pCell->nData = (sxu64)nData;
50292 /* Write the cell payload */
50293 lhCellWriteLocalPayload(pCell,(const void *)&pPage->pRaw->zData[iKeyOfft],pCell->nKey,pData,nData);
50294 /* Finally write the cell header */
50295 lhCellWriteHeader(pCell);
50296 /* All done */
50297 return UNQLITE_OK;
50298}
50299/*
50300 * Overwrite an existing record.
50301 */
50302static int lhRecordOverwrite(
50303 lhcell *pCell,
50304 const void *pData,unqlite_int64 nByte
50305 )
50306{
50307 lhash_kv_engine *pEngine = pCell->pPage->pHash;
50308 unsigned char *zRaw,*zRawEnd,*zPayload;
50309 const unsigned char *zPtr,*zEnd;
50310 unqlite_page *pOvfl,*pOld,*pNew;
50311 lhpage *pPage = pCell->pPage;
50312 sxu32 nAvail;
50313 pgno iOvfl;
50314 int rc;
50315 /* Acquire a writer lock on this page */
50316 rc = pEngine->pIo->xWrite(pPage->pRaw);
50317 if( rc != UNQLITE_OK ){
50318 return rc;
50319 }
50320 if( pCell->iOvfl == 0 ){
50321 /* Local payload, try to deal with the free space issues */
50322 zPayload = &pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey];
50323 if( pCell->nData == (sxu64)nByte ){
50324 /* Best scenario, simply a memcpy operation */
50325 SyMemcpy(pData,(void *)zPayload,(sxu32)nByte);
50326 }else if( (sxu64)nByte < pCell->nData ){
50327 /* Shorter data, not so ugly */
50328 SyMemcpy(pData,(void *)zPayload,(sxu32)nByte);
50329 /* Update the cell header */
50330 SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],nByte);
50331 /* Restore freespace */
50332 lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ + pCell->nKey + nByte),(sxu16)(pCell->nData - nByte));
50333 /* New data size */
50334 pCell->nData = (sxu64)nByte;
50335 }else{
50336 sxu16 iOfft = 0; /* cc warning */
50337 /* Check if another chunk is available for this cell */
50338 rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ + pCell->nKey + nByte,&iOfft);
50339 if( rc != UNQLITE_OK ){
50340 /* Transfer the payload to an overflow page */
50341 rc = lhCellWriteOvflPayload(pCell,&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ],pCell->nKey,pData,nByte,(const void *)0);
50342 if( rc != UNQLITE_OK ){
50343 return rc;
50344 }
50345 /* Update the cell header */
50346 SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],(sxu64)nByte);
50347 /* Restore freespace */
50348 lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ),(sxu16)(pCell->nKey + pCell->nData));
50349 /* New data size */
50350 pCell->nData = (sxu64)nByte;
50351 }else{
50352 sxu16 iOldOfft = pCell->iStart;
50353 sxu32 iOld = (sxu32)pCell->nData;
50354 /* Space is available, transfer the cell */
50355 lhMoveLocalCell(pCell,iOfft,pData,nByte);
50356 /* Restore cell space */
50357 lhRestoreSpace(pPage,iOldOfft,(sxu16)(L_HASH_CELL_SZ + pCell->nKey + iOld));
50358 }
50359 }
50360 return UNQLITE_OK;
50361 }
50362 /* Point to the overflow page */
50363 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pCell->iDataPage,&pOvfl);
50364 if( rc != UNQLITE_OK ){
50365 return rc;
50366 }
50367 /* Relase all old overflow pages first */
50368 SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
50369 pOld = pOvfl;
50370 for(;;){
50371 if( iOvfl == 0 ){
50372 /* No more overflow pages on the chain */
50373 break;
50374 }
50375 /* Point to the target page */
50376 if( UNQLITE_OK != pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOld) ){
50377 /* Not so fatal if something goes wrong here */
50378 break;
50379 }
50380 /* Next overflow page to be released */
50381 SyBigEndianUnpack64(pOld->zData,&iOvfl);
50382 if( pOld != pOvfl ){ /* xx: chm is maniac */
50383 /* Restore the page to the free list */
50384 lhRestorePage(pEngine,pOld);
50385 /* Unref */
50386 pEngine->pIo->xPageUnref(pOld);
50387 }
50388 }
50389 /* Point to the data offset */
50390 zRaw = &pOvfl->zData[pCell->iDataOfft];
50391 zRawEnd = &pOvfl->zData[pEngine->iPageSize];
50392 /* The data to be stored */
50393 zPtr = (const unsigned char *)pData;
50394 zEnd = &zPtr[nByte];
50395 /* Start the overwrite process */
50396 /* Acquire a writer lock */
50397 rc = pEngine->pIo->xWrite(pOvfl);
50398 if( rc != UNQLITE_OK ){
50399 return rc;
50400 }
50401 SyBigEndianPack64(pOvfl->zData,0);
50402 for(;;){
50403 sxu32 nLen;
50404 if( zPtr >= zEnd ){
50405 break;
50406 }
50407 if( zRaw >= zRawEnd ){
50408 /* Acquire a new page */
50409 rc = lhAcquirePage(pEngine,&pNew);
50410 if( rc != UNQLITE_OK ){
50411 return rc;
50412 }
50413 rc = pEngine->pIo->xWrite(pNew);
50414 if( rc != UNQLITE_OK ){
50415 return rc;
50416 }
50417 /* Link */
50418 SyBigEndianPack64(pOvfl->zData,pNew->pgno);
50419 pEngine->pIo->xPageUnref(pOvfl);
50420 SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
50421 pOvfl = pNew;
50422 zRaw = &pNew->zData[8];
50423 zRawEnd = &pNew->zData[pEngine->iPageSize];
50424 }
50425 nAvail = (sxu32)(zRawEnd-zRaw);
50426 nLen = (sxu32)(zEnd-zPtr);
50427 if( nLen > nAvail ){
50428 nLen = nAvail;
50429 }
50430 SyMemcpy((const void *)zPtr,(void *)zRaw,nLen);
50431 /* Synchronize pointers */
50432 zPtr += nLen;
50433 zRaw += nLen;
50434 }
50435 /* Unref the last overflow page */
50436 pEngine->pIo->xPageUnref(pOvfl);
50437 /* Finally, update the cell header */
50438 pCell->nData = (sxu64)nByte;
50439 SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData);
50440 /* All done */
50441 return UNQLITE_OK;
50442}
50443/*
50444 * Append data to an existing record.
50445 */
50446static int lhRecordAppend(
50447 lhcell *pCell,
50448 const void *pData,unqlite_int64 nByte
50449 )
50450{
50451 lhash_kv_engine *pEngine = pCell->pPage->pHash;
50452 const unsigned char *zPtr,*zEnd;
50453 lhpage *pPage = pCell->pPage;
50454 unsigned char *zRaw,*zRawEnd;
50455 unqlite_page *pOvfl,*pNew;
50456 sxu64 nDatalen;
50457 sxu32 nAvail;
50458 pgno iOvfl;
50459 int rc;
50460 if( pCell->nData + nByte < pCell->nData ){
50461 /* Overflow */
50462 pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow");
50463 return UNQLITE_LIMIT;
50464 }
50465 /* Acquire a writer lock on this page */
50466 rc = pEngine->pIo->xWrite(pPage->pRaw);
50467 if( rc != UNQLITE_OK ){
50468 return rc;
50469 }
50470 if( pCell->iOvfl == 0 ){
50471 sxu16 iOfft = 0; /* cc warning */
50472 /* Local payload, check for a bigger place */
50473 rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ + pCell->nKey + pCell->nData + nByte,&iOfft);
50474 if( rc != UNQLITE_OK ){
50475 /* Transfer the payload to an overflow page */
50476 rc = lhCellWriteOvflPayload(pCell,
50477 &pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ],pCell->nKey,
50478 (const void *)&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey],pCell->nData,
50479 pData,nByte,
50480 (const void *)0);
50481 if( rc != UNQLITE_OK ){
50482 return rc;
50483 }
50484 /* Update the cell header */
50485 SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData + nByte);
50486 /* Restore freespace */
50487 lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ),(sxu16)(pCell->nKey + pCell->nData));
50488 /* New data size */
50489 pCell->nData += nByte;
50490 }else{
50491 sxu16 iOldOfft = pCell->iStart;
50492 sxu32 iOld = (sxu32)pCell->nData;
50493 SyBlob sWorker;
50494 SyBlobInit(&sWorker,&pEngine->sAllocator);
50495 /* Copy the old data */
50496 rc = SyBlobAppend(&sWorker,(const void *)&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey],(sxu32)pCell->nData);
50497 if( rc == SXRET_OK ){
50498 /* Append the new data */
50499 rc = SyBlobAppend(&sWorker,pData,(sxu32)nByte);
50500 }
50501 if( rc != UNQLITE_OK ){
50502 SyBlobRelease(&sWorker);
50503 return rc;
50504 }
50505 /* Space is available, transfer the cell */
50506 lhMoveLocalCell(pCell,iOfft,SyBlobData(&sWorker),(unqlite_int64)SyBlobLength(&sWorker));
50507 /* Restore cell space */
50508 lhRestoreSpace(pPage,iOldOfft,(sxu16)(L_HASH_CELL_SZ + pCell->nKey + iOld));
50509 /* All done */
50510 SyBlobRelease(&sWorker);
50511 }
50512 return UNQLITE_OK;
50513 }
50514 /* Point to the overflow page which hold the data */
50515 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pCell->iDataPage,&pOvfl);
50516 if( rc != UNQLITE_OK ){
50517 return rc;
50518 }
50519 /* Next overflow page in the chain */
50520 SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
50521 /* Point to the end of the chunk */
50522 zRaw = &pOvfl->zData[pCell->iDataOfft];
50523 zRawEnd = &pOvfl->zData[pEngine->iPageSize];
50524 nDatalen = pCell->nData;
50525 nAvail = (sxu32)(zRawEnd - zRaw);
50526 for(;;){
50527 if( zRaw >= zRawEnd ){
50528 if( iOvfl == 0 ){
50529 /* Cant happen */
50530 pEngine->pIo->xErr(pEngine->pIo->pHandle,"Corrupt overflow page");
50531 return UNQLITE_CORRUPT;
50532 }
50533 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pNew);
50534 if( rc != UNQLITE_OK ){
50535 return rc;
50536 }
50537 /* Next overflow page on the chain */
50538 SyBigEndianUnpack64(pNew->zData,&iOvfl);
50539 /* Unref the previous overflow page */
50540 pEngine->pIo->xPageUnref(pOvfl);
50541 /* Point to the new chunk */
50542 zRaw = &pNew->zData[8];
50543 zRawEnd = &pNew->zData[pCell->pPage->pHash->iPageSize];
50544 nAvail = L_HASH_OVERFLOW_SIZE(pCell->pPage->pHash->iPageSize);
50545 pOvfl = pNew;
50546 }
50547 if( (sxu64)nAvail > nDatalen ){
50548 zRaw += nDatalen;
50549 break;
50550 }else{
50551 nDatalen -= nAvail;
50552 }
50553 zRaw += nAvail;
50554 }
50555 /* Start the append process */
50556 zPtr = (const unsigned char *)pData;
50557 zEnd = &zPtr[nByte];
50558 /* Acquire a writer lock */
50559 rc = pEngine->pIo->xWrite(pOvfl);
50560 if( rc != UNQLITE_OK ){
50561 return rc;
50562 }
50563 for(;;){
50564 sxu32 nLen;
50565 if( zPtr >= zEnd ){
50566 break;
50567 }
50568 if( zRaw >= zRawEnd ){
50569 /* Acquire a new page */
50570 rc = lhAcquirePage(pEngine,&pNew);
50571 if( rc != UNQLITE_OK ){
50572 return rc;
50573 }
50574 rc = pEngine->pIo->xWrite(pNew);
50575 if( rc != UNQLITE_OK ){
50576 return rc;
50577 }
50578 /* Link */
50579 SyBigEndianPack64(pOvfl->zData,pNew->pgno);
50580 pEngine->pIo->xPageUnref(pOvfl);
50581 SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
50582 pOvfl = pNew;
50583 zRaw = &pNew->zData[8];
50584 zRawEnd = &pNew->zData[pEngine->iPageSize];
50585 }
50586 nAvail = (sxu32)(zRawEnd-zRaw);
50587 nLen = (sxu32)(zEnd-zPtr);
50588 if( nLen > nAvail ){
50589 nLen = nAvail;
50590 }
50591 SyMemcpy((const void *)zPtr,(void *)zRaw,nLen);
50592 /* Synchronize pointers */
50593 zPtr += nLen;
50594 zRaw += nLen;
50595 }
50596 /* Unref the last overflow page */
50597 pEngine->pIo->xPageUnref(pOvfl);
50598 /* Finally, update the cell header */
50599 pCell->nData += nByte;
50600 SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData);
50601 /* All done */
50602 return UNQLITE_OK;
50603}
50604/*
50605 * A write privilege have been acquired on this page.
50606 * Mark it as an empty page (No cells).
50607 */
50608static int lhSetEmptyPage(lhpage *pPage)
50609{
50610 unsigned char *zRaw = pPage->pRaw->zData;
50611 lhphdr *pHeader = &pPage->sHdr;
50612 sxu16 nByte;
50613 int rc;
50614 /* Acquire a writer lock */
50615 rc = pPage->pHash->pIo->xWrite(pPage->pRaw);
50616 if( rc != UNQLITE_OK ){
50617 return rc;
50618 }
50619 /* Offset of the first cell */
50620 SyBigEndianPack16(zRaw,0);
50621 zRaw += 2;
50622 /* Offset of the first free block */
50623 pHeader->iFree = L_HASH_PAGE_HDR_SZ;
50624 SyBigEndianPack16(zRaw,L_HASH_PAGE_HDR_SZ);
50625 zRaw += 2;
50626 /* Slave page number */
50627 SyBigEndianPack64(zRaw,0);
50628 zRaw += 8;
50629 /* Fill the free block */
50630 SyBigEndianPack16(zRaw,0); /* Offset of the next free block */
50631 zRaw += 2;
50632 nByte = (sxu16)L_HASH_MX_FREE_SPACE(pPage->pHash->iPageSize);
50633 SyBigEndianPack16(zRaw,nByte);
50634 pPage->nFree = nByte;
50635 /* Do not add this page to the hot dirty list */
50636 pPage->pHash->pIo->xDontMkHot(pPage->pRaw);
50637 return UNQLITE_OK;
50638}
50639/* Forward declaration */
50640static int lhSlaveStore(
50641 lhpage *pPage,
50642 const void *pKey,sxu32 nKeyLen,
50643 const void *pData,unqlite_int64 nDataLen,
50644 sxu32 nHash
50645 );
50646/*
50647 * Store a cell and its payload in a given page.
50648 */
50649static int lhStoreCell(
50650 lhpage *pPage, /* Target page */
50651 const void *pKey,sxu32 nKeyLen, /* Payload: Key */
50652 const void *pData,unqlite_int64 nDataLen, /* Payload: Data */
50653 sxu32 nHash, /* Hash of the key */
50654 int auto_append /* Auto append a slave page if full */
50655 )
50656{
50657 lhash_kv_engine *pEngine = pPage->pHash;
50658 int iNeedOvfl = 0; /* Need overflow page for this cell and its payload*/
50659 lhcell *pCell;
50660 sxu16 nOfft;
50661 int rc;
50662 /* Acquire a writer lock on this page first */
50663 rc = pEngine->pIo->xWrite(pPage->pRaw);
50664 if( rc != UNQLITE_OK ){
50665 return rc;
50666 }
50667 /* Check for a free block */
50668 rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ+nKeyLen+nDataLen,&nOfft);
50669 if( rc != UNQLITE_OK ){
50670 /* Check for a free block to hold a single cell only (without payload) */
50671 rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ,&nOfft);
50672 if( rc != UNQLITE_OK ){
50673 if( !auto_append ){
50674 /* A split must be done */
50675 return UNQLITE_FULL;
50676 }else{
50677 /* Store this record in a slave page */
50678 rc = lhSlaveStore(pPage,pKey,nKeyLen,pData,nDataLen,nHash);
50679 return rc;
50680 }
50681 }
50682 iNeedOvfl = 1;
50683 }
50684 /* Allocate a new cell instance */
50685 pCell = lhNewCell(pEngine,pPage);
50686 if( pCell == 0 ){
50687 pEngine->pIo->xErr(pEngine->pIo->pHandle,"KV store is running out of memory");
50688 return UNQLITE_NOMEM;
50689 }
50690 /* Fill-in the structure */
50691 pCell->iStart = nOfft;
50692 pCell->nKey = nKeyLen;
50693 pCell->nData = (sxu64)nDataLen;
50694 pCell->nHash = nHash;
50695 if( nKeyLen < 262144 /* 256 KB */ ){
50696 /* Keep the key in-memory for fast lookup */
50697 SyBlobAppend(&pCell->sKey,pKey,nKeyLen);
50698 }
50699 /* Link the cell */
50700 rc = lhInstallCell(pCell);
50701 if( rc != UNQLITE_OK ){
50702 return rc;
50703 }
50704 /* Write the payload */
50705 if( iNeedOvfl ){
50706 rc = lhCellWriteOvflPayload(pCell,pKey,nKeyLen,pData,nDataLen,(const void *)0);
50707 if( rc != UNQLITE_OK ){
50708 lhCellDiscard(pCell);
50709 return rc;
50710 }
50711 }else{
50712 lhCellWriteLocalPayload(pCell,pKey,nKeyLen,pData,nDataLen);
50713 }
50714 /* Finally, Write the cell header */
50715 lhCellWriteHeader(pCell);
50716 /* All done */
50717 return UNQLITE_OK;
50718}
50719/*
50720 * Find a slave page capable of hosting the given amount.
50721 */
50722static int lhFindSlavePage(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft,lhpage **ppSlave)
50723{
50724 lhash_kv_engine *pEngine = pPage->pHash;
50725 lhpage *pMaster = pPage->pMaster;
50726 lhpage *pSlave = pMaster->pSlave;
50727 unqlite_page *pRaw;
50728 lhpage *pNew;
50729 sxu16 iOfft;
50730 sxi32 i;
50731 int rc;
50732 /* Look for an already attached slave page */
50733 for( i = 0 ; i < pMaster->iSlave ; ++i ){
50734 /* Find a free chunk big enough */
50735 rc = lhAllocateSpace(pSlave,L_HASH_CELL_SZ+nAmount,&iOfft);
50736 if( rc != UNQLITE_OK ){
50737 /* A space for cell header only */
50738 rc = lhAllocateSpace(pSlave,L_HASH_CELL_SZ,&iOfft);
50739 }
50740 if( rc == UNQLITE_OK ){
50741 /* All done */
50742 if( pOfft ){
50743 *pOfft = iOfft;
50744 }
50745 *ppSlave = pSlave;
50746 return UNQLITE_OK;
50747 }
50748 /* Point to the next slave page */
50749 pSlave = pSlave->pNextSlave;
50750 }
50751 /* Acquire a new slave page */
50752 rc = lhAcquirePage(pEngine,&pRaw);
50753 if( rc != UNQLITE_OK ){
50754 return rc;
50755 }
50756 /* Last slave page */
50757 pSlave = pMaster->pSlave;
50758 if( pSlave == 0 ){
50759 /* First slave page */
50760 pSlave = pMaster;
50761 }
50762 /* Initialize the page */
50763 pNew = lhNewPage(pEngine,pRaw,pMaster);
50764 if( pNew == 0 ){
50765 return UNQLITE_NOMEM;
50766 }
50767 /* Mark as an empty page */
50768 rc = lhSetEmptyPage(pNew);
50769 if( rc != UNQLITE_OK ){
50770 goto fail;
50771 }
50772 if( pOfft ){
50773 /* Look for a free block */
50774 if( UNQLITE_OK != lhAllocateSpace(pNew,L_HASH_CELL_SZ+nAmount,&iOfft) ){
50775 /* Cell header only */
50776 lhAllocateSpace(pNew,L_HASH_CELL_SZ,&iOfft); /* Never fail */
50777 }
50778 *pOfft = iOfft;
50779 }
50780 /* Link this page to the previous slave page */
50781 rc = pEngine->pIo->xWrite(pSlave->pRaw);
50782 if( rc != UNQLITE_OK ){
50783 goto fail;
50784 }
50785 /* Reflect in the page header */
50786 SyBigEndianPack64(&pSlave->pRaw->zData[2/*Cell offset*/+2/*Free block offset*/],pRaw->pgno);
50787 pSlave->sHdr.iSlave = pRaw->pgno;
50788 /* All done */
50789 *ppSlave = pNew;
50790 return UNQLITE_OK;
50791fail:
50792 pEngine->pIo->xPageUnref(pNew->pRaw); /* pNew will be released in this call */
50793 return rc;
50794
50795}
50796/*
50797 * Perform a store operation in a slave page.
50798 */
50799static int lhSlaveStore(
50800 lhpage *pPage, /* Master page */
50801 const void *pKey,sxu32 nKeyLen, /* Payload: key */
50802 const void *pData,unqlite_int64 nDataLen, /* Payload: data */
50803 sxu32 nHash /* Hash of the key */
50804 )
50805{
50806 lhpage *pSlave;
50807 int rc;
50808 /* Find a slave page */
50809 rc = lhFindSlavePage(pPage,nKeyLen + nDataLen,0,&pSlave);
50810 if( rc != UNQLITE_OK ){
50811 return rc;
50812 }
50813 /* Perform the insertion in the slave page */
50814 rc = lhStoreCell(pSlave,pKey,nKeyLen,pData,nDataLen,nHash,1);
50815 return rc;
50816}
50817/*
50818 * Transfer a cell to a new page (either a master or slave).
50819 */
50820static int lhTransferCell(lhcell *pTarget,lhpage *pPage)
50821{
50822 lhcell *pCell;
50823 sxu16 nOfft;
50824 int rc;
50825 /* Check for a free block to hold a single cell only */
50826 rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ,&nOfft);
50827 if( rc != UNQLITE_OK ){
50828 /* Store in a slave page */
50829 rc = lhFindSlavePage(pPage,L_HASH_CELL_SZ,&nOfft,&pPage);
50830 if( rc != UNQLITE_OK ){
50831 return rc;
50832 }
50833 }
50834 /* Allocate a new cell instance */
50835 pCell = lhNewCell(pPage->pHash,pPage);
50836 if( pCell == 0 ){
50837 return UNQLITE_NOMEM;
50838 }
50839 /* Fill-in the structure */
50840 pCell->iStart = nOfft;
50841 pCell->nData = pTarget->nData;
50842 pCell->nKey = pTarget->nKey;
50843 pCell->iOvfl = pTarget->iOvfl;
50844 pCell->iDataOfft = pTarget->iDataOfft;
50845 pCell->iDataPage = pTarget->iDataPage;
50846 pCell->nHash = pTarget->nHash;
50847 SyBlobDup(&pTarget->sKey,&pCell->sKey);
50848 /* Link the cell */
50849 rc = lhInstallCell(pCell);
50850 if( rc != UNQLITE_OK ){
50851 return rc;
50852 }
50853 /* Finally, Write the cell header */
50854 lhCellWriteHeader(pCell);
50855 /* All done */
50856 return UNQLITE_OK;
50857}
50858/*
50859 * Perform a page split.
50860 */
50861static int lhPageSplit(
50862 lhpage *pOld, /* Page to be split */
50863 lhpage *pNew, /* New page */
50864 pgno split_bucket, /* Current split bucket */
50865 pgno high_mask /* High mask (Max split bucket - 1) */
50866 )
50867{
50868 lhcell *pCell,*pNext;
50869 SyBlob sWorker;
50870 pgno iBucket;
50871 int rc;
50872 SyBlobInit(&sWorker,&pOld->pHash->sAllocator);
50873 /* Perform the split */
50874 pCell = pOld->pList;
50875 for( ;; ){
50876 if( pCell == 0 ){
50877 /* No more cells */
50878 break;
50879 }
50880 /* Obtain the new logical bucket */
50881 iBucket = pCell->nHash & high_mask;
50882 pNext = pCell->pNext;
50883 if( iBucket != split_bucket){
50884 rc = UNQLITE_OK;
50885 if( pCell->iOvfl ){
50886 /* Transfer the cell only */
50887 rc = lhTransferCell(pCell,pNew);
50888 }else{
50889 /* Transfer the cell and its payload */
50890 SyBlobReset(&sWorker);
50891 if( SyBlobLength(&pCell->sKey) < 1 ){
50892 /* Consume the key */
50893 rc = lhConsumeCellkey(pCell,unqliteDataConsumer,&pCell->sKey,0);
50894 if( rc != UNQLITE_OK ){
50895 goto fail;
50896 }
50897 }
50898 /* Consume the data (Very small data < 65k) */
50899 rc = lhConsumeCellData(pCell,unqliteDataConsumer,&sWorker);
50900 if( rc != UNQLITE_OK ){
50901 goto fail;
50902 }
50903 /* Perform the transfer */
50904 rc = lhStoreCell(
50905 pNew,
50906 SyBlobData(&pCell->sKey),(int)SyBlobLength(&pCell->sKey),
50907 SyBlobData(&sWorker),SyBlobLength(&sWorker),
50908 pCell->nHash,
50909 1
50910 );
50911 }
50912 if( rc != UNQLITE_OK ){
50913 goto fail;
50914 }
50915 /* Discard the cell from the old page */
50916 lhUnlinkCell(pCell);
50917 }
50918 /* Point to the next cell */
50919 pCell = pNext;
50920 }
50921 /* All done */
50922 rc = UNQLITE_OK;
50923fail:
50924 SyBlobRelease(&sWorker);
50925 return rc;
50926}
50927/*
50928 * Perform the infamous linear hash split operation.
50929 */
50930static int lhSplit(lhpage *pTarget,int *pRetry)
50931{
50932 lhash_kv_engine *pEngine = pTarget->pHash;
50933 lhash_bmap_rec *pRec;
50934 lhpage *pOld,*pNew;
50935 unqlite_page *pRaw;
50936 int rc;
50937 /* Get the real page number of the bucket to split */
50938 pRec = lhMapFindBucket(pEngine,pEngine->split_bucket);
50939 if( pRec == 0 ){
50940 /* Can't happen */
50941 return UNQLITE_CORRUPT;
50942 }
50943 /* Load the page to be split */
50944 rc = lhLoadPage(pEngine,pRec->iReal,0,&pOld,0);
50945 if( rc != UNQLITE_OK ){
50946 return rc;
50947 }
50948 /* Request a new page */
50949 rc = lhAcquirePage(pEngine,&pRaw);
50950 if( rc != UNQLITE_OK ){
50951 return rc;
50952 }
50953 /* Initialize the page */
50954 pNew = lhNewPage(pEngine,pRaw,0);
50955 if( pNew == 0 ){
50956 return UNQLITE_NOMEM;
50957 }
50958 /* Mark as an empty page */
50959 rc = lhSetEmptyPage(pNew);
50960 if( rc != UNQLITE_OK ){
50961 goto fail;
50962 }
50963 /* Install and write the logical map record */
50964 rc = lhMapWriteRecord(pEngine,
50965 pEngine->split_bucket + pEngine->max_split_bucket,
50966 pRaw->pgno
50967 );
50968 if( rc != UNQLITE_OK ){
50969 goto fail;
50970 }
50971 if( pTarget->pRaw->pgno == pOld->pRaw->pgno ){
50972 *pRetry = 1;
50973 }
50974 /* Perform the split */
50975 rc = lhPageSplit(pOld,pNew,pEngine->split_bucket,pEngine->nmax_split_nucket - 1);
50976 if( rc != UNQLITE_OK ){
50977 goto fail;
50978 }
50979 /* Update the database header */
50980 pEngine->split_bucket++;
50981 /* Acquire a writer lock on the first page */
50982 rc = pEngine->pIo->xWrite(pEngine->pHeader);
50983 if( rc != UNQLITE_OK ){
50984 return rc;
50985 }
50986 if( pEngine->split_bucket >= pEngine->max_split_bucket ){
50987 /* Increment the generation number */
50988 pEngine->split_bucket = 0;
50989 pEngine->max_split_bucket = pEngine->nmax_split_nucket;
50990 pEngine->nmax_split_nucket <<= 1;
50991 if( !pEngine->nmax_split_nucket ){
50992 /* If this happen to your installation, please tell us <chm@symisc.net> */
50993 pEngine->pIo->xErr(pEngine->pIo->pHandle,"Database page (64-bit integer) limit reached");
50994 return UNQLITE_LIMIT;
50995 }
50996 /* Reflect in the page header */
50997 SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/],pEngine->split_bucket);
50998 SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/+8/*Split bucket*/],pEngine->max_split_bucket);
50999 }else{
51000 /* Modify only the split bucket */
51001 SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/],pEngine->split_bucket);
51002 }
51003 /* All done */
51004 return UNQLITE_OK;
51005fail:
51006 pEngine->pIo->xPageUnref(pNew->pRaw);
51007 return rc;
51008}
51009/*
51010 * Store a record in the target page.
51011 */
51012static int lhRecordInstall(
51013 lhpage *pPage, /* Target page */
51014 sxu32 nHash, /* Hash of the key */
51015 const void *pKey,sxu32 nKeyLen, /* Payload: Key */
51016 const void *pData,unqlite_int64 nDataLen /* Payload: Data */
51017 )
51018{
51019 int rc;
51020 rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,0);
51021 if( rc == UNQLITE_FULL ){
51022 int do_retry = 0;
51023 /* Split */
51024 rc = lhSplit(pPage,&do_retry);
51025 if( rc == UNQLITE_OK ){
51026 if( do_retry ){
51027 /* Re-calculate logical bucket number */
51028 return SXERR_RETRY;
51029 }
51030 /* Perform the store */
51031 rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,1);
51032 }
51033 }
51034 return rc;
51035}
51036/*
51037 * Insert a record (Either overwrite or append operation) in our database.
51038 */
51039static int lh_record_insert(
51040 unqlite_kv_engine *pKv, /* KV store */
51041 const void *pKey,sxu32 nKeyLen, /* Payload: Key */
51042 const void *pData,unqlite_int64 nDataLen, /* Payload: data */
51043 int is_append /* True for an append operation */
51044 )
51045{
51046 lhash_kv_engine *pEngine = (lhash_kv_engine *)pKv;
51047 lhash_bmap_rec *pRec;
51048 unqlite_page *pRaw;
51049 lhpage *pPage;
51050 lhcell *pCell;
51051 pgno iBucket;
51052 sxu32 nHash;
51053 int iCnt;
51054 int rc;
51055
51056 /* Acquire the first page (DB hash Header) so that everything gets loaded autmatically */
51057 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
51058 if( rc != UNQLITE_OK ){
51059 return rc;
51060 }
51061 iCnt = 0;
51062 /* Compute the hash of the key first */
51063 nHash = pEngine->xHash(pKey,(sxu32)nKeyLen);
51064retry:
51065 /* Extract the logical bucket number */
51066 iBucket = nHash & (pEngine->nmax_split_nucket - 1);
51067 if( iBucket >= pEngine->split_bucket + pEngine->max_split_bucket ){
51068 /* Low mask */
51069 iBucket = nHash & (pEngine->max_split_bucket - 1);
51070 }
51071 /* Map the logical bucket number to real page number */
51072 pRec = lhMapFindBucket(pEngine,iBucket);
51073 if( pRec == 0 ){
51074 /* Request a new page */
51075 rc = lhAcquirePage(pEngine,&pRaw);
51076 if( rc != UNQLITE_OK ){
51077 return rc;
51078 }
51079 /* Initialize the page */
51080 pPage = lhNewPage(pEngine,pRaw,0);
51081 if( pPage == 0 ){
51082 return UNQLITE_NOMEM;
51083 }
51084 /* Mark as an empty page */
51085 rc = lhSetEmptyPage(pPage);
51086 if( rc != UNQLITE_OK ){
51087 pEngine->pIo->xPageUnref(pRaw); /* pPage will be released during this call */
51088 return rc;
51089 }
51090 /* Store the cell */
51091 rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,1);
51092 if( rc == UNQLITE_OK ){
51093 /* Install and write the logical map record */
51094 rc = lhMapWriteRecord(pEngine,iBucket,pRaw->pgno);
51095 }
51096 pEngine->pIo->xPageUnref(pRaw);
51097 return rc;
51098 }else{
51099 /* Load the page */
51100 rc = lhLoadPage(pEngine,pRec->iReal,0,&pPage,0);
51101 if( rc != UNQLITE_OK ){
51102 /* IO error, unlikely scenario */
51103 return rc;
51104 }
51105 /* Do not add this page to the hot dirty list */
51106 pEngine->pIo->xDontMkHot(pPage->pRaw);
51107 /* Lookup for the cell */
51108 pCell = lhFindCell(pPage,pKey,(sxu32)nKeyLen,nHash);
51109 if( pCell == 0 ){
51110 /* Create the record */
51111 rc = lhRecordInstall(pPage,nHash,pKey,nKeyLen,pData,nDataLen);
51112 if( rc == SXERR_RETRY && iCnt++ < 2 ){
51113 rc = UNQLITE_OK;
51114 goto retry;
51115 }
51116 }else{
51117 if( is_append ){
51118 /* Append operation */
51119 rc = lhRecordAppend(pCell,pData,nDataLen);
51120 }else{
51121 /* Overwrite old value */
51122 rc = lhRecordOverwrite(pCell,pData,nDataLen);
51123 }
51124 }
51125 pEngine->pIo->xPageUnref(pPage->pRaw);
51126 }
51127 return rc;
51128}
51129/*
51130 * Replace method.
51131 */
51132static int lhash_kv_replace(
51133 unqlite_kv_engine *pKv,
51134 const void *pKey,int nKeyLen,
51135 const void *pData,unqlite_int64 nDataLen
51136 )
51137{
51138 int rc;
51139 rc = lh_record_insert(pKv,pKey,(sxu32)nKeyLen,pData,nDataLen,0);
51140 return rc;
51141}
51142/*
51143 * Append method.
51144 */
51145static int lhash_kv_append(
51146 unqlite_kv_engine *pKv,
51147 const void *pKey,int nKeyLen,
51148 const void *pData,unqlite_int64 nDataLen
51149 )
51150{
51151 int rc;
51152 rc = lh_record_insert(pKv,pKey,(sxu32)nKeyLen,pData,nDataLen,1);
51153 return rc;
51154}
51155/*
51156 * Write the hash header (Page one).
51157 */
51158static int lhash_write_header(lhash_kv_engine *pEngine,unqlite_page *pHeader)
51159{
51160 unsigned char *zRaw = pHeader->zData;
51161 lhash_bmap_page *pMap;
51162
51163 pEngine->pHeader = pHeader;
51164 /* 4 byte magic number */
51165 SyBigEndianPack32(zRaw,pEngine->nMagic);
51166 zRaw += 4;
51167 /* 4 byte hash value to identify a valid hash function */
51168 SyBigEndianPack32(zRaw,pEngine->xHash(L_HASH_WORD,sizeof(L_HASH_WORD)-1));
51169 zRaw += 4;
51170 /* List of free pages: Empty */
51171 SyBigEndianPack64(zRaw,0);
51172 zRaw += 8;
51173 /* Current split bucket */
51174 SyBigEndianPack64(zRaw,pEngine->split_bucket);
51175 zRaw += 8;
51176 /* Maximum split bucket */
51177 SyBigEndianPack64(zRaw,pEngine->max_split_bucket);
51178 zRaw += 8;
51179 /* Initialiaze the bucket map */
51180 pMap = &pEngine->sPageMap;
51181 /* Fill in the structure */
51182 pMap->iNum = pHeader->pgno;
51183 /* Next page in the bucket map */
51184 SyBigEndianPack64(zRaw,0);
51185 zRaw += 8;
51186 /* Total number of records in the bucket map */
51187 SyBigEndianPack32(zRaw,0);
51188 zRaw += 4;
51189 pMap->iPtr = (sxu16)(zRaw - pHeader->zData);
51190 /* All done */
51191 return UNQLITE_OK;
51192 }
51193/*
51194 * Exported: xOpen() method.
51195 */
51196static int lhash_kv_open(unqlite_kv_engine *pEngine,pgno dbSize)
51197{
51198 lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
51199 unqlite_page *pHeader;
51200 int rc;
51201 if( dbSize < 1 ){
51202 /* A new database, create the header */
51203 rc = pEngine->pIo->xNew(pEngine->pIo->pHandle,&pHeader);
51204 if( rc != UNQLITE_OK ){
51205 return rc;
51206 }
51207 /* Acquire a writer lock */
51208 rc = pEngine->pIo->xWrite(pHeader);
51209 if( rc != UNQLITE_OK ){
51210 return rc;
51211 }
51212 /* Write the hash header */
51213 rc = lhash_write_header(pHash,pHeader);
51214 if( rc != UNQLITE_OK ){
51215 return rc;
51216 }
51217 }else{
51218 /* Acquire the page one of the database */
51219 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,&pHeader);
51220 if( rc != UNQLITE_OK ){
51221 return rc;
51222 }
51223 /* Read the database header */
51224 rc = lhash_read_header(pHash,pHeader);
51225 if( rc != UNQLITE_OK ){
51226 return rc;
51227 }
51228 }
51229 return UNQLITE_OK;
51230}
51231/*
51232 * Release a master or slave page. (xUnpin callback).
51233 */
51234static void lhash_page_release(void *pUserData)
51235{
51236 lhpage *pPage = (lhpage *)pUserData;
51237 lhash_kv_engine *pEngine = pPage->pHash;
51238 lhcell *pNext,*pCell = pPage->pList;
51239 unqlite_page *pRaw = pPage->pRaw;
51240 sxu32 n;
51241 /* Drop in-memory cells */
51242 for( n = 0 ; n < pPage->nCell ; ++n ){
51243 pNext = pCell->pNext;
51244 SyBlobRelease(&pCell->sKey);
51245 /* Release the cell instance */
51246 SyMemBackendPoolFree(&pEngine->sAllocator,(void *)pCell);
51247 /* Point to the next entry */
51248 pCell = pNext;
51249 }
51250 if( pPage->apCell ){
51251 /* Release the cell table */
51252 SyMemBackendFree(&pEngine->sAllocator,(void *)pPage->apCell);
51253 }
51254 /* Finally, release the whole page */
51255 SyMemBackendPoolFree(&pEngine->sAllocator,pPage);
51256 pRaw->pUserData = 0;
51257}
51258/*
51259 * Default hash function (DJB).
51260 */
51261static sxu32 lhash_bin_hash(const void *pSrc,sxu32 nLen)
51262{
51263 register unsigned char *zIn = (unsigned char *)pSrc;
51264 unsigned char *zEnd;
51265 sxu32 nH = 5381;
51266 if( nLen > 2048 /* 2K */ ){
51267 nLen = 2048;
51268 }
51269 zEnd = &zIn[nLen];
51270 for(;;){
51271 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
51272 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
51273 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
51274 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
51275 }
51276 return nH;
51277}
51278/*
51279 * Exported: xInit() method.
51280 * Initialize the Key value storage engine.
51281 */
51282static int lhash_kv_init(unqlite_kv_engine *pEngine,int iPageSize)
51283{
51284 lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
51285 int rc;
51286
51287 /* This structure is always zeroed, go to the initialization directly */
51288 SyMemBackendInitFromParent(&pHash->sAllocator,unqliteExportMemBackend());
51289#if defined(UNQLITE_ENABLE_THREADS)
51290 /* Already protected by the upper layers */
51291 SyMemBackendDisbaleMutexing(&pHash->sAllocator);
51292#endif
51293 pHash->iPageSize = iPageSize;
51294 /* Default hash function */
51295 pHash->xHash = lhash_bin_hash;
51296 /* Default comparison function */
51297 pHash->xCmp = SyMemcmp;
51298 /* Allocate a new record map */
51299 pHash->nBuckSize = 32;
51300 pHash->apMap = (lhash_bmap_rec **)SyMemBackendAlloc(&pHash->sAllocator,pHash->nBuckSize *sizeof(lhash_bmap_rec *));
51301 if( pHash->apMap == 0 ){
51302 rc = UNQLITE_NOMEM;
51303 goto err;
51304 }
51305 /* Zero the table */
51306 SyZero(pHash->apMap,pHash->nBuckSize * sizeof(lhash_bmap_rec *));
51307 /* Linear hashing components */
51308 pHash->split_bucket = 0; /* Logical not real bucket number */
51309 pHash->max_split_bucket = 1;
51310 pHash->nmax_split_nucket = 2;
51311 pHash->nMagic = L_HASH_MAGIC;
51312 /* Install the cache unpin and reload callbacks */
51313 pHash->pIo->xSetUnpin(pHash->pIo->pHandle,lhash_page_release);
51314 pHash->pIo->xSetReload(pHash->pIo->pHandle,lhash_page_release);
51315 return UNQLITE_OK;
51316err:
51317 SyMemBackendRelease(&pHash->sAllocator);
51318 return rc;
51319}
51320/*
51321 * Exported: xRelease() method.
51322 * Release the Key value storage engine.
51323 */
51324static void lhash_kv_release(unqlite_kv_engine *pEngine)
51325{
51326 lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
51327 /* Release the private memory backend */
51328 SyMemBackendRelease(&pHash->sAllocator);
51329}
51330/*
51331 * Exported: xConfig() method.
51332 * Configure the linear hash KV store.
51333 */
51334static int lhash_kv_config(unqlite_kv_engine *pEngine,int op,va_list ap)
51335{
51336 lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
51337 int rc = UNQLITE_OK;
51338 switch(op){
51339 case UNQLITE_KV_CONFIG_HASH_FUNC: {
51340 /* Default hash function */
51341 if( pHash->nBuckRec > 0 ){
51342 /* Locked operation */
51343 rc = UNQLITE_LOCKED;
51344 }else{
51345 ProcHash xHash = va_arg(ap,ProcHash);
51346 if( xHash ){
51347 pHash->xHash = xHash;
51348 }
51349 }
51350 break;
51351 }
51352 case UNQLITE_KV_CONFIG_CMP_FUNC: {
51353 /* Default comparison function */
51354 ProcCmp xCmp = va_arg(ap,ProcCmp);
51355 if( xCmp ){
51356 pHash->xCmp = xCmp;
51357 }
51358 break;
51359 }
51360 default:
51361 /* Unknown OP */
51362 rc = UNQLITE_UNKNOWN;
51363 break;
51364 }
51365 return rc;
51366}
51367/*
51368 * Each public cursor is identified by an instance of this structure.
51369 */
51370typedef struct lhash_kv_cursor lhash_kv_cursor;
51371struct lhash_kv_cursor
51372{
51373 unqlite_kv_engine *pStore; /* Must be first */
51374 /* Private fields */
51375 int iState; /* Current state of the cursor */
51376 int is_first; /* True to read the database header */
51377 lhcell *pCell; /* Current cell we are processing */
51378 unqlite_page *pRaw; /* Raw disk page */
51379 lhash_bmap_rec *pRec; /* Logical to real bucket map */
51380};
51381/*
51382 * Possible state of the cursor
51383 */
51384#define L_HASH_CURSOR_STATE_NEXT_PAGE 1 /* Next page in the list */
51385#define L_HASH_CURSOR_STATE_CELL 2 /* Processing Cell */
51386#define L_HASH_CURSOR_STATE_DONE 3 /* Cursor does not point to anything */
51387/*
51388 * Initialize the cursor.
51389 */
51390static void lhInitCursor(unqlite_kv_cursor *pPtr)
51391{
51392 lhash_kv_engine *pEngine = (lhash_kv_engine *)pPtr->pStore;
51393 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
51394 /* Init */
51395 pCur->iState = L_HASH_CURSOR_STATE_NEXT_PAGE;
51396 pCur->pCell = 0;
51397 pCur->pRec = pEngine->pFirst;
51398 pCur->pRaw = 0;
51399 pCur->is_first = 1;
51400}
51401/*
51402 * Point to the next page on the database.
51403 */
51404static int lhCursorNextPage(lhash_kv_cursor *pPtr)
51405{
51406 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
51407 lhash_bmap_rec *pRec;
51408 lhpage *pPage;
51409 int rc;
51410 for(;;){
51411 pRec = pCur->pRec;
51412 if( pRec == 0 ){
51413 pCur->iState = L_HASH_CURSOR_STATE_DONE;
51414 return UNQLITE_DONE;
51415 }
51416 if( pPtr->iState == L_HASH_CURSOR_STATE_CELL && pPtr->pRaw ){
51417 /* Unref this page */
51418 pCur->pStore->pIo->xPageUnref(pPtr->pRaw);
51419 pPtr->pRaw = 0;
51420 }
51421 /* Advance the map cursor */
51422 pCur->pRec = pRec->pPrev; /* Not a bug, reverse link */
51423 /* Load the next page on the list */
51424 rc = lhLoadPage((lhash_kv_engine *)pCur->pStore,pRec->iReal,0,&pPage,0);
51425 if( rc != UNQLITE_OK ){
51426 return rc;
51427 }
51428 if( pPage->pList ){
51429 /* Reflect the change */
51430 pCur->pCell = pPage->pList;
51431 pCur->iState = L_HASH_CURSOR_STATE_CELL;
51432 pCur->pRaw = pPage->pRaw;
51433 break;
51434 }
51435 /* Empty page, discard this page and continue */
51436 pPage->pHash->pIo->xPageUnref(pPage->pRaw);
51437 }
51438 return UNQLITE_OK;
51439}
51440/*
51441 * Point to the previous page on the database.
51442 */
51443static int lhCursorPrevPage(lhash_kv_cursor *pPtr)
51444{
51445 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
51446 lhash_bmap_rec *pRec;
51447 lhpage *pPage;
51448 int rc;
51449 for(;;){
51450 pRec = pCur->pRec;
51451 if( pRec == 0 ){
51452 pCur->iState = L_HASH_CURSOR_STATE_DONE;
51453 return UNQLITE_DONE;
51454 }
51455 if( pPtr->iState == L_HASH_CURSOR_STATE_CELL && pPtr->pRaw ){
51456 /* Unref this page */
51457 pCur->pStore->pIo->xPageUnref(pPtr->pRaw);
51458 pPtr->pRaw = 0;
51459 }
51460 /* Advance the map cursor */
51461 pCur->pRec = pRec->pNext; /* Not a bug, reverse link */
51462 /* Load the previous page on the list */
51463 rc = lhLoadPage((lhash_kv_engine *)pCur->pStore,pRec->iReal,0,&pPage,0);
51464 if( rc != UNQLITE_OK ){
51465 return rc;
51466 }
51467 if( pPage->pFirst ){
51468 /* Reflect the change */
51469 pCur->pCell = pPage->pFirst;
51470 pCur->iState = L_HASH_CURSOR_STATE_CELL;
51471 pCur->pRaw = pPage->pRaw;
51472 break;
51473 }
51474 /* Discard this page and continue */
51475 pPage->pHash->pIo->xPageUnref(pPage->pRaw);
51476 }
51477 return UNQLITE_OK;
51478}
51479/*
51480 * Is a valid cursor.
51481 */
51482static int lhCursorValid(unqlite_kv_cursor *pPtr)
51483{
51484 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
51485 return (pCur->iState == L_HASH_CURSOR_STATE_CELL) && pCur->pCell;
51486}
51487/*
51488 * Point to the first record.
51489 */
51490static int lhCursorFirst(unqlite_kv_cursor *pCursor)
51491{
51492 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51493 lhash_kv_engine *pEngine = (lhash_kv_engine *)pCursor->pStore;
51494 int rc;
51495 if( pCur->is_first ){
51496 /* Read the database header first */
51497 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
51498 if( rc != UNQLITE_OK ){
51499 return rc;
51500 }
51501 pCur->is_first = 0;
51502 }
51503 /* Point to the first map record */
51504 pCur->pRec = pEngine->pFirst;
51505 /* Load the cells */
51506 rc = lhCursorNextPage(pCur);
51507 return rc;
51508}
51509/*
51510 * Point to the last record.
51511 */
51512static int lhCursorLast(unqlite_kv_cursor *pCursor)
51513{
51514 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51515 lhash_kv_engine *pEngine = (lhash_kv_engine *)pCursor->pStore;
51516 int rc;
51517 if( pCur->is_first ){
51518 /* Read the database header first */
51519 rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
51520 if( rc != UNQLITE_OK ){
51521 return rc;
51522 }
51523 pCur->is_first = 0;
51524 }
51525 /* Point to the last map record */
51526 pCur->pRec = pEngine->pList;
51527 /* Load the cells */
51528 rc = lhCursorPrevPage(pCur);
51529 return rc;
51530}
51531/*
51532 * Reset the cursor.
51533 */
51534static void lhCursorReset(unqlite_kv_cursor *pCursor)
51535{
51536 lhCursorFirst(pCursor);
51537}
51538/*
51539 * Point to the next record.
51540 */
51541static int lhCursorNext(unqlite_kv_cursor *pCursor)
51542{
51543 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51544 lhcell *pCell;
51545 int rc;
51546 if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51547 /* Load the cells of the next page */
51548 rc = lhCursorNextPage(pCur);
51549 return rc;
51550 }
51551 pCell = pCur->pCell;
51552 pCur->pCell = pCell->pNext;
51553 if( pCur->pCell == 0 ){
51554 /* Load the cells of the next page */
51555 rc = lhCursorNextPage(pCur);
51556 return rc;
51557 }
51558 return UNQLITE_OK;
51559}
51560/*
51561 * Point to the previous record.
51562 */
51563static int lhCursorPrev(unqlite_kv_cursor *pCursor)
51564{
51565 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51566 lhcell *pCell;
51567 int rc;
51568 if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51569 /* Load the cells of the previous page */
51570 rc = lhCursorPrevPage(pCur);
51571 return rc;
51572 }
51573 pCell = pCur->pCell;
51574 pCur->pCell = pCell->pPrev;
51575 if( pCur->pCell == 0 ){
51576 /* Load the cells of the previous page */
51577 rc = lhCursorPrevPage(pCur);
51578 return rc;
51579 }
51580 return UNQLITE_OK;
51581}
51582/*
51583 * Return key length.
51584 */
51585static int lhCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen)
51586{
51587 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51588 lhcell *pCell;
51589
51590 if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51591 /* Invalid state */
51592 return UNQLITE_INVALID;
51593 }
51594 /* Point to the target cell */
51595 pCell = pCur->pCell;
51596 /* Return key length */
51597 *pLen = (int)pCell->nKey;
51598 return UNQLITE_OK;
51599}
51600/*
51601 * Return data length.
51602 */
51603static int lhCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen)
51604{
51605 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51606 lhcell *pCell;
51607
51608 if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51609 /* Invalid state */
51610 return UNQLITE_INVALID;
51611 }
51612 /* Point to the target cell */
51613 pCell = pCur->pCell;
51614 /* Return data length */
51615 *pLen = (unqlite_int64)pCell->nData;
51616 return UNQLITE_OK;
51617}
51618/*
51619 * Consume the key.
51620 */
51621static int lhCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
51622{
51623 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51624 lhcell *pCell;
51625 int rc;
51626 if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51627 /* Invalid state */
51628 return UNQLITE_INVALID;
51629 }
51630 /* Point to the target cell */
51631 pCell = pCur->pCell;
51632 if( SyBlobLength(&pCell->sKey) > 0 ){
51633 /* Consume the key directly */
51634 rc = xConsumer(SyBlobData(&pCell->sKey),SyBlobLength(&pCell->sKey),pUserData);
51635 }else{
51636 /* Very large key */
51637 rc = lhConsumeCellkey(pCell,xConsumer,pUserData,0);
51638 }
51639 return rc;
51640}
51641/*
51642 * Consume the data.
51643 */
51644static int lhCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
51645{
51646 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51647 lhcell *pCell;
51648 int rc;
51649 if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51650 /* Invalid state */
51651 return UNQLITE_INVALID;
51652 }
51653 /* Point to the target cell */
51654 pCell = pCur->pCell;
51655 /* Consume the data */
51656 rc = lhConsumeCellData(pCell,xConsumer,pUserData);
51657 return rc;
51658}
51659/*
51660 * Find a partiuclar record.
51661 */
51662static int lhCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos)
51663{
51664 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51665 int rc;
51666 /* Perform a lookup */
51667 rc = lhRecordLookup((lhash_kv_engine *)pCur->pStore,pKey,nByte,&pCur->pCell);
51668 if( rc != UNQLITE_OK ){
51669 SXUNUSED(iPos);
51670 pCur->pCell = 0;
51671 pCur->iState = L_HASH_CURSOR_STATE_DONE;
51672 return rc;
51673 }
51674 pCur->iState = L_HASH_CURSOR_STATE_CELL;
51675 return UNQLITE_OK;
51676}
51677/*
51678 * Remove a particular record.
51679 */
51680static int lhCursorDelete(unqlite_kv_cursor *pCursor)
51681{
51682 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51683 lhcell *pCell;
51684 int rc;
51685 if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51686 /* Invalid state */
51687 return UNQLITE_INVALID;
51688 }
51689 /* Point to the target cell */
51690 pCell = pCur->pCell;
51691 /* Point to the next entry */
51692 pCur->pCell = pCell->pNext;
51693 /* Perform the deletion */
51694 rc = lhRecordRemove(pCell);
51695 return rc;
51696}
51697/*
51698 * Export the linear-hash storage engine.
51699 */
51700UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void)
51701{
51702 static const unqlite_kv_methods sDiskStore = {
51703 "hash", /* zName */
51704 sizeof(lhash_kv_engine), /* szKv */
51705 sizeof(lhash_kv_cursor), /* szCursor */
51706 1, /* iVersion */
51707 lhash_kv_init, /* xInit */
51708 lhash_kv_release, /* xRelease */
51709 lhash_kv_config, /* xConfig */
51710 lhash_kv_open, /* xOpen */
51711 lhash_kv_replace, /* xReplace */
51712 lhash_kv_append, /* xAppend */
51713 lhInitCursor, /* xCursorInit */
51714 lhCursorSeek, /* xSeek */
51715 lhCursorFirst, /* xFirst */
51716 lhCursorLast, /* xLast */
51717 lhCursorValid, /* xValid */
51718 lhCursorNext, /* xNext */
51719 lhCursorPrev, /* xPrev */
51720 lhCursorDelete, /* xDelete */
51721 lhCursorKeyLength, /* xKeyLength */
51722 lhCursorKey, /* xKey */
51723 lhCursorDataLength, /* xDataLength */
51724 lhCursorData, /* xData */
51725 lhCursorReset, /* xReset */
51726 0 /* xRelease */
51727 };
51728 return &sDiskStore;
51729}
51730/*
51731 * ----------------------------------------------------------
51732 * File: mem_kv.c
51733 * MD5: 32e2610c95f53038114d9566f0d0489e
51734 * ----------------------------------------------------------
51735 */
51736/*
51737 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
51738 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
51739 * Version 1.1.6
51740 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
51741 * please contact Symisc Systems via:
51742 * legal@symisc.net
51743 * licensing@symisc.net
51744 * contact@symisc.net
51745 * or visit:
51746 * http://unqlite.org/licensing.html
51747 */
51748 /* $SymiscID: mem_kv.c v1.7 Win7 2012-11-28 01:41 stable <chm@symisc.net> $ */
51749#ifndef UNQLITE_AMALGAMATION
51750#include "unqliteInt.h"
51751#endif
51752/*
51753 * This file implements an in-memory key value storage engine for unQLite.
51754 * Note that this storage engine does not support transactions.
51755 *
51756 * Normaly, I (chm@symisc.net) planned to implement a red-black tree
51757 * which is suitable for this kind of operation, but due to the lack
51758 * of time, I decided to implement a tunned hashtable which everybody
51759 * know works very well for this kind of operation.
51760 * Again, I insist on a red-black tree implementation for future version
51761 * of Unqlite.
51762 */
51763/* Forward declaration */
51764typedef struct mem_hash_kv_engine mem_hash_kv_engine;
51765/*
51766 * Each record is storead in an instance of the following structure.
51767 */
51768typedef struct mem_hash_record mem_hash_record;
51769struct mem_hash_record
51770{
51771 mem_hash_kv_engine *pEngine; /* Storage engine */
51772 sxu32 nHash; /* Hash of the key */
51773 const void *pKey; /* Key */
51774 sxu32 nKeyLen; /* Key size (Max 1GB) */
51775 const void *pData; /* Data */
51776 sxu32 nDataLen; /* Data length (Max 4GB) */
51777 mem_hash_record *pNext,*pPrev; /* Link to other records */
51778 mem_hash_record *pNextHash,*pPrevHash; /* Collision link */
51779};
51780/*
51781 * Each in-memory KV engine is represented by an instance
51782 * of the following structure.
51783 */
51784struct mem_hash_kv_engine
51785{
51786 const unqlite_kv_io *pIo; /* IO methods: MUST be first */
51787 /* Private data */
51788 SyMemBackend sAlloc; /* Private memory allocator */
51789 ProcHash xHash; /* Default hash function */
51790 ProcCmp xCmp; /* Default comparison function */
51791 sxu32 nRecord; /* Total number of records */
51792 sxu32 nBucket; /* Bucket size: Must be a power of two */
51793 mem_hash_record **apBucket; /* Hash bucket */
51794 mem_hash_record *pFirst; /* First inserted entry */
51795 mem_hash_record *pLast; /* Last inserted entry */
51796};
51797/*
51798 * Allocate a new hash record.
51799 */
51800static mem_hash_record * MemHashNewRecord(
51801 mem_hash_kv_engine *pEngine,
51802 const void *pKey,int nKey,
51803 const void *pData,unqlite_int64 nData,
51804 sxu32 nHash
51805 )
51806{
51807 SyMemBackend *pAlloc = &pEngine->sAlloc;
51808 mem_hash_record *pRecord;
51809 void *pDupData;
51810 sxu32 nByte;
51811 char *zPtr;
51812
51813 /* Total number of bytes to alloc */
51814 nByte = sizeof(mem_hash_record) + nKey;
51815 /* Allocate a new instance */
51816 pRecord = (mem_hash_record *)SyMemBackendAlloc(pAlloc,nByte);
51817 if( pRecord == 0 ){
51818 return 0;
51819 }
51820 pDupData = (void *)SyMemBackendAlloc(pAlloc,(sxu32)nData);
51821 if( pDupData == 0 ){
51822 SyMemBackendFree(pAlloc,pRecord);
51823 return 0;
51824 }
51825 zPtr = (char *)pRecord;
51826 zPtr += sizeof(mem_hash_record);
51827 /* Zero the structure */
51828 SyZero(pRecord,sizeof(mem_hash_record));
51829 /* Fill in the structure */
51830 pRecord->pEngine = pEngine;
51831 pRecord->nDataLen = (sxu32)nData;
51832 pRecord->nKeyLen = (sxu32)nKey;
51833 pRecord->nHash = nHash;
51834 SyMemcpy(pKey,zPtr,pRecord->nKeyLen);
51835 pRecord->pKey = (const void *)zPtr;
51836 SyMemcpy(pData,pDupData,pRecord->nDataLen);
51837 pRecord->pData = pDupData;
51838 /* All done */
51839 return pRecord;
51840}
51841/*
51842 * Install a given record in the hashtable.
51843 */
51844static void MemHashLinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pRecord)
51845{
51846 sxu32 nBucket = pRecord->nHash & (pEngine->nBucket - 1);
51847 pRecord->pNextHash = pEngine->apBucket[nBucket];
51848 if( pEngine->apBucket[nBucket] ){
51849 pEngine->apBucket[nBucket]->pPrevHash = pRecord;
51850 }
51851 pEngine->apBucket[nBucket] = pRecord;
51852 if( pEngine->pFirst == 0 ){
51853 pEngine->pFirst = pEngine->pLast = pRecord;
51854 }else{
51855 MACRO_LD_PUSH(pEngine->pLast,pRecord);
51856 }
51857 pEngine->nRecord++;
51858}
51859/*
51860 * Unlink a given record from the hashtable.
51861 */
51862static void MemHashUnlinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pEntry)
51863{
51864 sxu32 nBucket = pEntry->nHash & (pEngine->nBucket - 1);
51865 SyMemBackend *pAlloc = &pEngine->sAlloc;
51866 if( pEntry->pPrevHash == 0 ){
51867 pEngine->apBucket[nBucket] = pEntry->pNextHash;
51868 }else{
51869 pEntry->pPrevHash->pNextHash = pEntry->pNextHash;
51870 }
51871 if( pEntry->pNextHash ){
51872 pEntry->pNextHash->pPrevHash = pEntry->pPrevHash;
51873 }
51874 MACRO_LD_REMOVE(pEngine->pLast,pEntry);
51875 if( pEntry == pEngine->pFirst ){
51876 pEngine->pFirst = pEntry->pPrev;
51877 }
51878 pEngine->nRecord--;
51879 /* Release the entry */
51880 SyMemBackendFree(pAlloc,(void *)pEntry->pData);
51881 SyMemBackendFree(pAlloc,pEntry); /* Key is also stored here */
51882}
51883/*
51884 * Perform a lookup for a given entry.
51885 */
51886static mem_hash_record * MemHashGetEntry(
51887 mem_hash_kv_engine *pEngine,
51888 const void *pKey,int nKeyLen
51889 )
51890{
51891 mem_hash_record *pEntry;
51892 sxu32 nHash,nBucket;
51893 /* Hash the entry */
51894 nHash = pEngine->xHash(pKey,(sxu32)nKeyLen);
51895 nBucket = nHash & (pEngine->nBucket - 1);
51896 pEntry = pEngine->apBucket[nBucket];
51897 for(;;){
51898 if( pEntry == 0 ){
51899 break;
51900 }
51901 if( pEntry->nHash == nHash && pEntry->nKeyLen == (sxu32)nKeyLen &&
51902 pEngine->xCmp(pEntry->pKey,pKey,pEntry->nKeyLen) == 0 ){
51903 return pEntry;
51904 }
51905 pEntry = pEntry->pNextHash;
51906 }
51907 /* No such entry */
51908 return 0;
51909}
51910/*
51911 * Rehash all the entries in the given table.
51912 */
51913static int MemHashGrowTable(mem_hash_kv_engine *pEngine)
51914{
51915 sxu32 nNewSize = pEngine->nBucket << 1;
51916 mem_hash_record *pEntry;
51917 mem_hash_record **apNew;
51918 sxu32 n,iBucket;
51919 /* Allocate a new larger table */
51920 apNew = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc, nNewSize * sizeof(mem_hash_record *));
51921 if( apNew == 0 ){
51922 /* Not so fatal, simply a performance hit */
51923 return UNQLITE_OK;
51924 }
51925 /* Zero the new table */
51926 SyZero((void *)apNew, nNewSize * sizeof(mem_hash_record *));
51927 /* Rehash all entries */
51928 n = 0;
51929 pEntry = pEngine->pLast;
51930 for(;;){
51931
51932 /* Loop one */
51933 if( n >= pEngine->nRecord ){
51934 break;
51935 }
51936 pEntry->pNextHash = pEntry->pPrevHash = 0;
51937 /* Install in the new bucket */
51938 iBucket = pEntry->nHash & (nNewSize - 1);
51939 pEntry->pNextHash = apNew[iBucket];
51940 if( apNew[iBucket] ){
51941 apNew[iBucket]->pPrevHash = pEntry;
51942 }
51943 apNew[iBucket] = pEntry;
51944 /* Point to the next entry */
51945 pEntry = pEntry->pNext;
51946 n++;
51947
51948 /* Loop two */
51949 if( n >= pEngine->nRecord ){
51950 break;
51951 }
51952 pEntry->pNextHash = pEntry->pPrevHash = 0;
51953 /* Install in the new bucket */
51954 iBucket = pEntry->nHash & (nNewSize - 1);
51955 pEntry->pNextHash = apNew[iBucket];
51956 if( apNew[iBucket] ){
51957 apNew[iBucket]->pPrevHash = pEntry;
51958 }
51959 apNew[iBucket] = pEntry;
51960 /* Point to the next entry */
51961 pEntry = pEntry->pNext;
51962 n++;
51963
51964 /* Loop three */
51965 if( n >= pEngine->nRecord ){
51966 break;
51967 }
51968 pEntry->pNextHash = pEntry->pPrevHash = 0;
51969 /* Install in the new bucket */
51970 iBucket = pEntry->nHash & (nNewSize - 1);
51971 pEntry->pNextHash = apNew[iBucket];
51972 if( apNew[iBucket] ){
51973 apNew[iBucket]->pPrevHash = pEntry;
51974 }
51975 apNew[iBucket] = pEntry;
51976 /* Point to the next entry */
51977 pEntry = pEntry->pNext;
51978 n++;
51979
51980 /* Loop four */
51981 if( n >= pEngine->nRecord ){
51982 break;
51983 }
51984 pEntry->pNextHash = pEntry->pPrevHash = 0;
51985 /* Install in the new bucket */
51986 iBucket = pEntry->nHash & (nNewSize - 1);
51987 pEntry->pNextHash = apNew[iBucket];
51988 if( apNew[iBucket] ){
51989 apNew[iBucket]->pPrevHash = pEntry;
51990 }
51991 apNew[iBucket] = pEntry;
51992 /* Point to the next entry */
51993 pEntry = pEntry->pNext;
51994 n++;
51995 }
51996 /* Release the old table and reflect the change */
51997 SyMemBackendFree(&pEngine->sAlloc,(void *)pEngine->apBucket);
51998 pEngine->apBucket = apNew;
51999 pEngine->nBucket = nNewSize;
52000 return UNQLITE_OK;
52001}
52002/*
52003 * Exported Interfaces.
52004 */
52005/*
52006 * Each public cursor is identified by an instance of this structure.
52007 */
52008typedef struct mem_hash_cursor mem_hash_cursor;
52009struct mem_hash_cursor
52010{
52011 unqlite_kv_engine *pStore; /* Must be first */
52012 /* Private fields */
52013 mem_hash_record *pCur; /* Current hash record */
52014};
52015/*
52016 * Initialize the cursor.
52017 */
52018static void MemHashInitCursor(unqlite_kv_cursor *pCursor)
52019{
52020 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
52021 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52022 /* Point to the first inserted entry */
52023 pMem->pCur = pEngine->pFirst;
52024}
52025/*
52026 * Point to the first entry.
52027 */
52028static int MemHashCursorFirst(unqlite_kv_cursor *pCursor)
52029{
52030 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
52031 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52032 pMem->pCur = pEngine->pFirst;
52033 return UNQLITE_OK;
52034}
52035/*
52036 * Point to the last entry.
52037 */
52038static int MemHashCursorLast(unqlite_kv_cursor *pCursor)
52039{
52040 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
52041 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52042 pMem->pCur = pEngine->pLast;
52043 return UNQLITE_OK;
52044}
52045/*
52046 * is a Valid Cursor.
52047 */
52048static int MemHashCursorValid(unqlite_kv_cursor *pCursor)
52049{
52050 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52051 return pMem->pCur != 0 ? 1 : 0;
52052}
52053/*
52054 * Point to the next entry.
52055 */
52056static int MemHashCursorNext(unqlite_kv_cursor *pCursor)
52057{
52058 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52059 if( pMem->pCur == 0){
52060 return UNQLITE_EOF;
52061 }
52062 pMem->pCur = pMem->pCur->pPrev; /* Reverse link: Not a Bug */
52063 return UNQLITE_OK;
52064}
52065/*
52066 * Point to the previous entry.
52067 */
52068static int MemHashCursorPrev(unqlite_kv_cursor *pCursor)
52069{
52070 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52071 if( pMem->pCur == 0){
52072 return UNQLITE_EOF;
52073 }
52074 pMem->pCur = pMem->pCur->pNext; /* Reverse link: Not a Bug */
52075 return UNQLITE_OK;
52076}
52077/*
52078 * Return key length.
52079 */
52080static int MemHashCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen)
52081{
52082 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52083 if( pMem->pCur == 0){
52084 return UNQLITE_EOF;
52085 }
52086 *pLen = (int)pMem->pCur->nKeyLen;
52087 return UNQLITE_OK;
52088}
52089/*
52090 * Return data length.
52091 */
52092static int MemHashCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen)
52093{
52094 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52095 if( pMem->pCur == 0 ){
52096 return UNQLITE_EOF;
52097 }
52098 *pLen = pMem->pCur->nDataLen;
52099 return UNQLITE_OK;
52100}
52101/*
52102 * Consume the key.
52103 */
52104static int MemHashCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
52105{
52106 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52107 int rc;
52108 if( pMem->pCur == 0){
52109 return UNQLITE_EOF;
52110 }
52111 /* Invoke the callback */
52112 rc = xConsumer(pMem->pCur->pKey,pMem->pCur->nKeyLen,pUserData);
52113 /* Callback result */
52114 return rc;
52115}
52116/*
52117 * Consume the data.
52118 */
52119static int MemHashCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
52120{
52121 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52122 int rc;
52123 if( pMem->pCur == 0){
52124 return UNQLITE_EOF;
52125 }
52126 /* Invoke the callback */
52127 rc = xConsumer(pMem->pCur->pData,pMem->pCur->nDataLen,pUserData);
52128 /* Callback result */
52129 return rc;
52130}
52131/*
52132 * Reset the cursor.
52133 */
52134static void MemHashCursorReset(unqlite_kv_cursor *pCursor)
52135{
52136 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52137 pMem->pCur = ((mem_hash_kv_engine *)pCursor->pStore)->pFirst;
52138}
52139/*
52140 * Remove a particular record.
52141 */
52142static int MemHashCursorDelete(unqlite_kv_cursor *pCursor)
52143{
52144 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52145 mem_hash_record *pNext;
52146 if( pMem->pCur == 0 ){
52147 /* Cursor does not point to anything */
52148 return UNQLITE_NOTFOUND;
52149 }
52150 pNext = pMem->pCur->pPrev;
52151 /* Perform the deletion */
52152 MemHashUnlinkRecord(pMem->pCur->pEngine,pMem->pCur);
52153 /* Point to the next entry */
52154 pMem->pCur = pNext;
52155 return UNQLITE_OK;
52156}
52157/*
52158 * Find a particular record.
52159 */
52160static int MemHashCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos)
52161{
52162 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
52163 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52164 /* Perform the lookup */
52165 pMem->pCur = MemHashGetEntry(pEngine,pKey,nByte);
52166 if( pMem->pCur == 0 ){
52167 if( iPos != UNQLITE_CURSOR_MATCH_EXACT ){
52168 /* noop; */
52169 }
52170 /* No such record */
52171 return UNQLITE_NOTFOUND;
52172 }
52173 return UNQLITE_OK;
52174}
52175/*
52176 * Builtin hash function.
52177 */
52178static sxu32 MemHashFunc(const void *pSrc,sxu32 nLen)
52179{
52180 register unsigned char *zIn = (unsigned char *)pSrc;
52181 unsigned char *zEnd;
52182 sxu32 nH = 5381;
52183 zEnd = &zIn[nLen];
52184 for(;;){
52185 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
52186 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
52187 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
52188 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
52189 }
52190 return nH;
52191}
52192/* Default bucket size */
52193#define MEM_HASH_BUCKET_SIZE 64
52194/* Default fill factor */
52195#define MEM_HASH_FILL_FACTOR 4 /* or 3 */
52196/*
52197 * Initialize the in-memory storage engine.
52198 */
52199static int MemHashInit(unqlite_kv_engine *pKvEngine,int iPageSize)
52200{
52201 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
52202 /* Note that this instance is already zeroed */
52203 /* Memory backend */
52204 SyMemBackendInitFromParent(&pEngine->sAlloc,unqliteExportMemBackend());
52205#if defined(UNQLITE_ENABLE_THREADS)
52206 /* Already protected by the upper layers */
52207 SyMemBackendDisbaleMutexing(&pEngine->sAlloc);
52208#endif
52209 /* Default hash & comparison function */
52210 pEngine->xHash = MemHashFunc;
52211 pEngine->xCmp = SyMemcmp;
52212 /* Allocate a new bucket */
52213 pEngine->apBucket = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *));
52214 if( pEngine->apBucket == 0 ){
52215 SXUNUSED(iPageSize); /* cc warning */
52216 return UNQLITE_NOMEM;
52217 }
52218 /* Zero the bucket */
52219 SyZero(pEngine->apBucket,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *));
52220 pEngine->nRecord = 0;
52221 pEngine->nBucket = MEM_HASH_BUCKET_SIZE;
52222 return UNQLITE_OK;
52223}
52224/*
52225 * Release the in-memory storage engine.
52226 */
52227static void MemHashRelease(unqlite_kv_engine *pKvEngine)
52228{
52229 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
52230 /* Release the private memory backend */
52231 SyMemBackendRelease(&pEngine->sAlloc);
52232}
52233/*
52234 * Configure the in-memory storage engine.
52235 */
52236static int MemHashConfigure(unqlite_kv_engine *pKvEngine,int iOp,va_list ap)
52237{
52238 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
52239 int rc = UNQLITE_OK;
52240 switch(iOp){
52241 case UNQLITE_KV_CONFIG_HASH_FUNC:{
52242 /* Use a default hash function */
52243 if( pEngine->nRecord > 0 ){
52244 rc = UNQLITE_LOCKED;
52245 }else{
52246 ProcHash xHash = va_arg(ap,ProcHash);
52247 if( xHash ){
52248 pEngine->xHash = xHash;
52249 }
52250 }
52251 break;
52252 }
52253 case UNQLITE_KV_CONFIG_CMP_FUNC: {
52254 /* Default comparison function */
52255 ProcCmp xCmp = va_arg(ap,ProcCmp);
52256 if( xCmp ){
52257 pEngine->xCmp = xCmp;
52258 }
52259 break;
52260 }
52261 default:
52262 /* Unknown configuration option */
52263 rc = UNQLITE_UNKNOWN;
52264 }
52265 return rc;
52266}
52267/*
52268 * Replace method.
52269 */
52270static int MemHashReplace(
52271 unqlite_kv_engine *pKv,
52272 const void *pKey,int nKeyLen,
52273 const void *pData,unqlite_int64 nDataLen
52274 )
52275{
52276 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv;
52277 mem_hash_record *pRecord;
52278 if( nDataLen > SXU32_HIGH ){
52279 /* Database limit */
52280 pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached");
52281 return UNQLITE_LIMIT;
52282 }
52283 /* Fetch the record first */
52284 pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen);
52285 if( pRecord == 0 ){
52286 /* Allocate a new record */
52287 pRecord = MemHashNewRecord(pEngine,
52288 pKey,nKeyLen,
52289 pData,nDataLen,
52290 pEngine->xHash(pKey,nKeyLen)
52291 );
52292 if( pRecord == 0 ){
52293 return UNQLITE_NOMEM;
52294 }
52295 /* Link the entry */
52296 MemHashLinkRecord(pEngine,pRecord);
52297 if( (pEngine->nRecord >= pEngine->nBucket * MEM_HASH_FILL_FACTOR) && pEngine->nRecord < 100000 ){
52298 /* Rehash the table */
52299 MemHashGrowTable(pEngine);
52300 }
52301 }else{
52302 sxu32 nData = (sxu32)nDataLen;
52303 void *pNew;
52304 /* Replace an existing record */
52305 if( nData == pRecord->nDataLen ){
52306 /* No need to free the old chunk */
52307 pNew = (void *)pRecord->pData;
52308 }else{
52309 pNew = SyMemBackendAlloc(&pEngine->sAlloc,nData);
52310 if( pNew == 0 ){
52311 return UNQLITE_NOMEM;
52312 }
52313 /* Release the old data */
52314 SyMemBackendFree(&pEngine->sAlloc,(void *)pRecord->pData);
52315 }
52316 /* Reflect the change */
52317 pRecord->nDataLen = nData;
52318 SyMemcpy(pData,pNew,nData);
52319 pRecord->pData = pNew;
52320 }
52321 return UNQLITE_OK;
52322}
52323/*
52324 * Append method.
52325 */
52326static int MemHashAppend(
52327 unqlite_kv_engine *pKv,
52328 const void *pKey,int nKeyLen,
52329 const void *pData,unqlite_int64 nDataLen
52330 )
52331{
52332 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv;
52333 mem_hash_record *pRecord;
52334 if( nDataLen > SXU32_HIGH ){
52335 /* Database limit */
52336 pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached");
52337 return UNQLITE_LIMIT;
52338 }
52339 /* Fetch the record first */
52340 pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen);
52341 if( pRecord == 0 ){
52342 /* Allocate a new record */
52343 pRecord = MemHashNewRecord(pEngine,
52344 pKey,nKeyLen,
52345 pData,nDataLen,
52346 pEngine->xHash(pKey,nKeyLen)
52347 );
52348 if( pRecord == 0 ){
52349 return UNQLITE_NOMEM;
52350 }
52351 /* Link the entry */
52352 MemHashLinkRecord(pEngine,pRecord);
52353 if( pEngine->nRecord * MEM_HASH_FILL_FACTOR >= pEngine->nBucket && pEngine->nRecord < 100000 ){
52354 /* Rehash the table */
52355 MemHashGrowTable(pEngine);
52356 }
52357 }else{
52358 unqlite_int64 nNew = pRecord->nDataLen + nDataLen;
52359 void *pOld = (void *)pRecord->pData;
52360 sxu32 nData;
52361 char *zNew;
52362 /* Append data to the existing record */
52363 if( nNew > SXU32_HIGH ){
52364 /* Overflow */
52365 pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow");
52366 return UNQLITE_LIMIT;
52367 }
52368 nData = (sxu32)nNew;
52369 /* Allocate bigger chunk */
52370 zNew = (char *)SyMemBackendRealloc(&pEngine->sAlloc,pOld,nData);
52371 if( zNew == 0 ){
52372 return UNQLITE_NOMEM;
52373 }
52374 /* Reflect the change */
52375 SyMemcpy(pData,&zNew[pRecord->nDataLen],(sxu32)nDataLen);
52376 pRecord->pData = (const void *)zNew;
52377 pRecord->nDataLen = nData;
52378 }
52379 return UNQLITE_OK;
52380}
52381/*
52382 * Export the in-memory storage engine.
52383 */
52384UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void)
52385{
52386 static const unqlite_kv_methods sMemStore = {
52387 "mem", /* zName */
52388 sizeof(mem_hash_kv_engine), /* szKv */
52389 sizeof(mem_hash_cursor), /* szCursor */
52390 1, /* iVersion */
52391 MemHashInit, /* xInit */
52392 MemHashRelease, /* xRelease */
52393 MemHashConfigure, /* xConfig */
52394 0, /* xOpen */
52395 MemHashReplace, /* xReplace */
52396 MemHashAppend, /* xAppend */
52397 MemHashInitCursor, /* xCursorInit */
52398 MemHashCursorSeek, /* xSeek */
52399 MemHashCursorFirst, /* xFirst */
52400 MemHashCursorLast, /* xLast */
52401 MemHashCursorValid, /* xValid */
52402 MemHashCursorNext, /* xNext */
52403 MemHashCursorPrev, /* xPrev */
52404 MemHashCursorDelete, /* xDelete */
52405 MemHashCursorKeyLength, /* xKeyLength */
52406 MemHashCursorKey, /* xKey */
52407 MemHashCursorDataLength, /* xDataLength */
52408 MemHashCursorData, /* xData */
52409 MemHashCursorReset, /* xReset */
52410 0 /* xRelease */
52411 };
52412 return &sMemStore;
52413}
52414/*
52415 * ----------------------------------------------------------
52416 * File: os.c
52417 * MD5: e7ad243c3cd9e6aac5fba406eedb7766
52418 * ----------------------------------------------------------
52419 */
52420/*
52421 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
52422 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
52423 * Version 1.1.6
52424 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
52425 * please contact Symisc Systems via:
52426 * legal@symisc.net
52427 * licensing@symisc.net
52428 * contact@symisc.net
52429 * or visit:
52430 * http://unqlite.org/licensing.html
52431 */
52432 /* $SymiscID: os.c v1.0 FreeBSD 2012-11-12 21:27 devel <chm@symisc.net> $ */
52433#ifndef UNQLITE_AMALGAMATION
52434#include "unqliteInt.h"
52435#endif
52436/* OS interfaces abstraction layers: Mostly SQLite3 source tree */
52437/*
52438** The following routines are convenience wrappers around methods
52439** of the unqlite_file object. This is mostly just syntactic sugar. All
52440** of this would be completely automatic if UnQLite were coded using
52441** C++ instead of plain old C.
52442*/
52443UNQLITE_PRIVATE int unqliteOsRead(unqlite_file *id, void *pBuf, unqlite_int64 amt, unqlite_int64 offset)
52444{
52445 return id->pMethods->xRead(id, pBuf, amt, offset);
52446}
52447UNQLITE_PRIVATE int unqliteOsWrite(unqlite_file *id, const void *pBuf, unqlite_int64 amt, unqlite_int64 offset)
52448{
52449 return id->pMethods->xWrite(id, pBuf, amt, offset);
52450}
52451UNQLITE_PRIVATE int unqliteOsTruncate(unqlite_file *id, unqlite_int64 size)
52452{
52453 return id->pMethods->xTruncate(id, size);
52454}
52455UNQLITE_PRIVATE int unqliteOsSync(unqlite_file *id, int flags)
52456{
52457 return id->pMethods->xSync(id, flags);
52458}
52459UNQLITE_PRIVATE int unqliteOsFileSize(unqlite_file *id, unqlite_int64 *pSize)
52460{
52461 return id->pMethods->xFileSize(id, pSize);
52462}
52463UNQLITE_PRIVATE int unqliteOsLock(unqlite_file *id, int lockType)
52464{
52465 return id->pMethods->xLock(id, lockType);
52466}
52467UNQLITE_PRIVATE int unqliteOsUnlock(unqlite_file *id, int lockType)
52468{
52469 return id->pMethods->xUnlock(id, lockType);
52470}
52471UNQLITE_PRIVATE int unqliteOsCheckReservedLock(unqlite_file *id, int *pResOut)
52472{
52473 return id->pMethods->xCheckReservedLock(id, pResOut);
52474}
52475UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id)
52476{
52477 if( id->pMethods->xSectorSize ){
52478 return id->pMethods->xSectorSize(id);
52479 }
52480 return UNQLITE_DEFAULT_SECTOR_SIZE;
52481}
52482/*
52483** The next group of routines are convenience wrappers around the
52484** VFS methods.
52485*/
52486UNQLITE_PRIVATE int unqliteOsOpen(
52487 unqlite_vfs *pVfs,
52488 SyMemBackend *pAlloc,
52489 const char *zPath,
52490 unqlite_file **ppOut,
52491 unsigned int flags
52492)
52493{
52494 unqlite_file *pFile;
52495 int rc;
52496 *ppOut = 0;
52497 if( zPath == 0 ){
52498 /* May happen if dealing with an in-memory database */
52499 return SXERR_EMPTY;
52500 }
52501 /* Allocate a new instance */
52502 pFile = (unqlite_file *)SyMemBackendAlloc(pAlloc,sizeof(unqlite_file)+pVfs->szOsFile);
52503 if( pFile == 0 ){
52504 return UNQLITE_NOMEM;
52505 }
52506 /* Zero the structure */
52507 SyZero(pFile,sizeof(unqlite_file)+pVfs->szOsFile);
52508 /* Invoke the xOpen method of the underlying VFS */
52509 rc = pVfs->xOpen(pVfs, zPath, pFile, flags);
52510 if( rc != UNQLITE_OK ){
52511 SyMemBackendFree(pAlloc,pFile);
52512 pFile = 0;
52513 }
52514 *ppOut = pFile;
52515 return rc;
52516}
52517UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId)
52518{
52519 int rc = UNQLITE_OK;
52520 if( pId ){
52521 rc = pId->pMethods->xClose(pId);
52522 SyMemBackendFree(pAlloc,pId);
52523 }
52524 return rc;
52525}
52526UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int dirSync){
52527 return pVfs->xDelete(pVfs, zPath, dirSync);
52528}
52529UNQLITE_PRIVATE int unqliteOsAccess(
52530 unqlite_vfs *pVfs,
52531 const char *zPath,
52532 int flags,
52533 int *pResOut
52534){
52535 return pVfs->xAccess(pVfs, zPath, flags, pResOut);
52536}
52537/*
52538 * ----------------------------------------------------------
52539 * File: os_unix.c
52540 * MD5: 5efd57d03f8fb988d081c5bcf5cc2998
52541 * ----------------------------------------------------------
52542 */
52543/*
52544 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
52545 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
52546 * Version 1.1.6
52547 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
52548 * please contact Symisc Systems via:
52549 * legal@symisc.net
52550 * licensing@symisc.net
52551 * contact@symisc.net
52552 * or visit:
52553 * http://unqlite.org/licensing.html
52554 */
52555 /* $SymiscID: os_unix.c v1.3 FreeBSD 2013-04-05 01:10 devel <chm@symisc.net> $ */
52556#ifndef UNQLITE_AMALGAMATION
52557#include "unqliteInt.h"
52558#endif
52559/*
52560 * Omit the whole layer from the build if compiling for platforms other than Unix (Linux, BSD, Solaris, OS X, etc.).
52561 * Note: Mostly SQLite3 source tree.
52562 */
52563#if defined(__UNIXES__)
52564/** This file contains the VFS implementation for unix-like operating systems
52565** include Linux, MacOSX, *BSD, QNX, VxWorks, AIX, HPUX, and others.
52566**
52567** There are actually several different VFS implementations in this file.
52568** The differences are in the way that file locking is done. The default
52569** implementation uses Posix Advisory Locks. Alternative implementations
52570** use flock(), dot-files, various proprietary locking schemas, or simply
52571** skip locking all together.
52572**
52573** This source file is organized into divisions where the logic for various
52574** subfunctions is contained within the appropriate division. PLEASE
52575** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed
52576** in the correct division and should be clearly labeled.
52577**
52578*/
52579/*
52580** standard include files.
52581*/
52582#include <sys/types.h>
52583#include <sys/stat.h>
52584#include <sys/uio.h>
52585#include <sys/file.h>
52586#include <fcntl.h>
52587#include <unistd.h>
52588#include <time.h>
52589#include <sys/time.h>
52590#include <errno.h>
52591#if defined(__APPLE__)
52592# include <sys/mount.h>
52593#endif
52594/*
52595** Allowed values of unixFile.fsFlags
52596*/
52597#define UNQLITE_FSFLAGS_IS_MSDOS 0x1
52598
52599/*
52600** Default permissions when creating a new file
52601*/
52602#ifndef UNQLITE_DEFAULT_FILE_PERMISSIONS
52603# define UNQLITE_DEFAULT_FILE_PERMISSIONS 0644
52604#endif
52605/*
52606 ** Default permissions when creating auto proxy dir
52607 */
52608#ifndef UNQLITE_DEFAULT_PROXYDIR_PERMISSIONS
52609# define UNQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755
52610#endif
52611/*
52612** Maximum supported path-length.
52613*/
52614#define MAX_PATHNAME 512
52615/*
52616** Only set the lastErrno if the error code is a real error and not
52617** a normal expected return code of UNQLITE_BUSY or UNQLITE_OK
52618*/
52619#define IS_LOCK_ERROR(x) ((x != UNQLITE_OK) && (x != UNQLITE_BUSY))
52620/* Forward references */
52621typedef struct unixInodeInfo unixInodeInfo; /* An i-node */
52622typedef struct UnixUnusedFd UnixUnusedFd; /* An unused file descriptor */
52623/*
52624** Sometimes, after a file handle is closed by SQLite, the file descriptor
52625** cannot be closed immediately. In these cases, instances of the following
52626** structure are used to store the file descriptor while waiting for an
52627** opportunity to either close or reuse it.
52628*/
52629struct UnixUnusedFd {
52630 int fd; /* File descriptor to close */
52631 int flags; /* Flags this file descriptor was opened with */
52632 UnixUnusedFd *pNext; /* Next unused file descriptor on same file */
52633};
52634/*
52635** The unixFile structure is subclass of unqlite3_file specific to the unix
52636** VFS implementations.
52637*/
52638typedef struct unixFile unixFile;
52639struct unixFile {
52640 const unqlite_io_methods *pMethod; /* Always the first entry */
52641 unixInodeInfo *pInode; /* Info about locks on this inode */
52642 int h; /* The file descriptor */
52643 int dirfd; /* File descriptor for the directory */
52644 unsigned char eFileLock; /* The type of lock held on this fd */
52645 int lastErrno; /* The unix errno from last I/O error */
52646 void *lockingContext; /* Locking style specific state */
52647 UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
52648 int fileFlags; /* Miscellanous flags */
52649 const char *zPath; /* Name of the file */
52650 unsigned fsFlags; /* cached details from statfs() */
52651};
52652/*
52653** The following macros define bits in unixFile.fileFlags
52654*/
52655#define UNQLITE_WHOLE_FILE_LOCKING 0x0001 /* Use whole-file locking */
52656/*
52657** Define various macros that are missing from some systems.
52658*/
52659#ifndef O_LARGEFILE
52660# define O_LARGEFILE 0
52661#endif
52662#ifndef O_NOFOLLOW
52663# define O_NOFOLLOW 0
52664#endif
52665#ifndef O_BINARY
52666# define O_BINARY 0
52667#endif
52668/*
52669** Helper functions to obtain and relinquish the global mutex. The
52670** global mutex is used to protect the unixInodeInfo and
52671** vxworksFileId objects used by this file, all of which may be
52672** shared by multiple threads.
52673**
52674** Function unixMutexHeld() is used to assert() that the global mutex
52675** is held when required. This function is only used as part of assert()
52676** statements. e.g.
52677**
52678** unixEnterMutex()
52679** assert( unixMutexHeld() );
52680** unixEnterLeave()
52681*/
52682static void unixEnterMutex(void){
52683#ifdef UNQLITE_ENABLE_THREADS
52684 const SyMutexMethods *pMutexMethods = SyMutexExportMethods();
52685 if( pMutexMethods ){
52686 SyMutex *pMutex = pMutexMethods->xNew(SXMUTEX_TYPE_STATIC_2); /* pre-allocated, never fail */
52687 SyMutexEnter(pMutexMethods,pMutex);
52688 }
52689#endif /* UNQLITE_ENABLE_THREADS */
52690}
52691static void unixLeaveMutex(void){
52692#ifdef UNQLITE_ENABLE_THREADS
52693 const SyMutexMethods *pMutexMethods = SyMutexExportMethods();
52694 if( pMutexMethods ){
52695 SyMutex *pMutex = pMutexMethods->xNew(SXMUTEX_TYPE_STATIC_2); /* pre-allocated, never fail */
52696 SyMutexLeave(pMutexMethods,pMutex);
52697 }
52698#endif /* UNQLITE_ENABLE_THREADS */
52699}
52700/*
52701** This routine translates a standard POSIX errno code into something
52702** useful to the clients of the unqlite3 functions. Specifically, it is
52703** intended to translate a variety of "try again" errors into UNQLITE_BUSY
52704** and a variety of "please close the file descriptor NOW" errors into
52705** UNQLITE_IOERR
52706**
52707** Errors during initialization of locks, or file system support for locks,
52708** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
52709*/
52710static int unqliteErrorFromPosixError(int posixError, int unqliteIOErr) {
52711 switch (posixError) {
52712 case 0:
52713 return UNQLITE_OK;
52714
52715 case EAGAIN:
52716 case ETIMEDOUT:
52717 case EBUSY:
52718 case EINTR:
52719 case ENOLCK:
52720 /* random NFS retry error, unless during file system support
52721 * introspection, in which it actually means what it says */
52722 return UNQLITE_BUSY;
52723
52724 case EACCES:
52725 /* EACCES is like EAGAIN during locking operations, but not any other time*/
52726 return UNQLITE_BUSY;
52727
52728 case EPERM:
52729 return UNQLITE_PERM;
52730
52731 case EDEADLK:
52732 return UNQLITE_IOERR;
52733
52734#if EOPNOTSUPP!=ENOTSUP
52735 case EOPNOTSUPP:
52736 /* something went terribly awry, unless during file system support
52737 * introspection, in which it actually means what it says */
52738#endif
52739#ifdef ENOTSUP
52740 case ENOTSUP:
52741 /* invalid fd, unless during file system support introspection, in which
52742 * it actually means what it says */
52743#endif
52744 case EIO:
52745 case EBADF:
52746 case EINVAL:
52747 case ENOTCONN:
52748 case ENODEV:
52749 case ENXIO:
52750 case ENOENT:
52751 case ESTALE:
52752 case ENOSYS:
52753 /* these should force the client to close the file and reconnect */
52754
52755 default:
52756 return unqliteIOErr;
52757 }
52758}
52759/******************************************************************************
52760*************************** Posix Advisory Locking ****************************
52761**
52762** POSIX advisory locks are broken by design. ANSI STD 1003.1 (1996)
52763** section 6.5.2.2 lines 483 through 490 specify that when a process
52764** sets or clears a lock, that operation overrides any prior locks set
52765** by the same process. It does not explicitly say so, but this implies
52766** that it overrides locks set by the same process using a different
52767** file descriptor. Consider this test case:
52768**
52769** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644);
52770** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);
52771**
52772** Suppose ./file1 and ./file2 are really the same file (because
52773** one is a hard or symbolic link to the other) then if you set
52774** an exclusive lock on fd1, then try to get an exclusive lock
52775** on fd2, it works. I would have expected the second lock to
52776** fail since there was already a lock on the file due to fd1.
52777** But not so. Since both locks came from the same process, the
52778** second overrides the first, even though they were on different
52779** file descriptors opened on different file names.
52780**
52781** This means that we cannot use POSIX locks to synchronize file access
52782** among competing threads of the same process. POSIX locks will work fine
52783** to synchronize access for threads in separate processes, but not
52784** threads within the same process.
52785**
52786** To work around the problem, SQLite has to manage file locks internally
52787** on its own. Whenever a new database is opened, we have to find the
52788** specific inode of the database file (the inode is determined by the
52789** st_dev and st_ino fields of the stat structure that fstat() fills in)
52790** and check for locks already existing on that inode. When locks are
52791** created or removed, we have to look at our own internal record of the
52792** locks to see if another thread has previously set a lock on that same
52793** inode.
52794**
52795** (Aside: The use of inode numbers as unique IDs does not work on VxWorks.
52796** For VxWorks, we have to use the alternative unique ID system based on
52797** canonical filename and implemented in the previous division.)
52798**
52799** There is one locking structure
52800** per inode, so if the same inode is opened twice, both unixFile structures
52801** point to the same locking structure. The locking structure keeps
52802** a reference count (so we will know when to delete it) and a "cnt"
52803** field that tells us its internal lock status. cnt==0 means the
52804** file is unlocked. cnt==-1 means the file has an exclusive lock.
52805** cnt>0 means there are cnt shared locks on the file.
52806**
52807** Any attempt to lock or unlock a file first checks the locking
52808** structure. The fcntl() system call is only invoked to set a
52809** POSIX lock if the internal lock structure transitions between
52810** a locked and an unlocked state.
52811**
52812** But wait: there are yet more problems with POSIX advisory locks.
52813**
52814** If you close a file descriptor that points to a file that has locks,
52815** all locks on that file that are owned by the current process are
52816** released. To work around this problem, each unixInodeInfo object
52817** maintains a count of the number of pending locks on that inode.
52818** When an attempt is made to close an unixFile, if there are
52819** other unixFile open on the same inode that are holding locks, the call
52820** to close() the file descriptor is deferred until all of the locks clear.
52821** The unixInodeInfo structure keeps a list of file descriptors that need to
52822** be closed and that list is walked (and cleared) when the last lock
52823** clears.
52824**
52825** Yet another problem: LinuxThreads do not play well with posix locks.
52826**
52827** Many older versions of linux use the LinuxThreads library which is
52828** not posix compliant. Under LinuxThreads, a lock created by thread
52829** A cannot be modified or overridden by a different thread B.
52830** Only thread A can modify the lock. Locking behavior is correct
52831** if the appliation uses the newer Native Posix Thread Library (NPTL)
52832** on linux - with NPTL a lock created by thread A can override locks
52833** in thread B. But there is no way to know at compile-time which
52834** threading library is being used. So there is no way to know at
52835** compile-time whether or not thread A can override locks on thread B.
52836** One has to do a run-time check to discover the behavior of the
52837** current process.
52838**
52839*/
52840
52841/*
52842** An instance of the following structure serves as the key used
52843** to locate a particular unixInodeInfo object.
52844*/
52845struct unixFileId {
52846 dev_t dev; /* Device number */
52847 ino_t ino; /* Inode number */
52848};
52849/*
52850** An instance of the following structure is allocated for each open
52851** inode. Or, on LinuxThreads, there is one of these structures for
52852** each inode opened by each thread.
52853**
52854** A single inode can have multiple file descriptors, so each unixFile
52855** structure contains a pointer to an instance of this object and this
52856** object keeps a count of the number of unixFile pointing to it.
52857*/
52858struct unixInodeInfo {
52859 struct unixFileId fileId; /* The lookup key */
52860 int nShared; /* Number of SHARED locks held */
52861 int eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
52862 int nRef; /* Number of pointers to this structure */
52863 int nLock; /* Number of outstanding file locks */
52864 UnixUnusedFd *pUnused; /* Unused file descriptors to close */
52865 unixInodeInfo *pNext; /* List of all unixInodeInfo objects */
52866 unixInodeInfo *pPrev; /* .... doubly linked */
52867};
52868
52869static unixInodeInfo *inodeList = 0;
52870/*
52871 * Local memory allocation stuff.
52872 */
52873static void * unqlite_malloc(sxu32 nByte)
52874{
52875 SyMemBackend *pAlloc;
52876 void *p;
52877 pAlloc = (SyMemBackend *)unqliteExportMemBackend();
52878 p = SyMemBackendAlloc(pAlloc,nByte);
52879 return p;
52880}
52881static void unqlite_free(void *p)
52882{
52883 SyMemBackend *pAlloc;
52884 pAlloc = (SyMemBackend *)unqliteExportMemBackend();
52885 SyMemBackendFree(pAlloc,p);
52886}
52887/*
52888** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
52889** If all such file descriptors are closed without error, the list is
52890** cleared and UNQLITE_OK returned.
52891**
52892** Otherwise, if an error occurs, then successfully closed file descriptor
52893** entries are removed from the list, and UNQLITE_IOERR_CLOSE returned.
52894** not deleted and UNQLITE_IOERR_CLOSE returned.
52895*/
52896static int closePendingFds(unixFile *pFile){
52897 int rc = UNQLITE_OK;
52898 unixInodeInfo *pInode = pFile->pInode;
52899 UnixUnusedFd *pError = 0;
52900 UnixUnusedFd *p;
52901 UnixUnusedFd *pNext;
52902 for(p=pInode->pUnused; p; p=pNext){
52903 pNext = p->pNext;
52904 if( close(p->fd) ){
52905 pFile->lastErrno = errno;
52906 rc = UNQLITE_IOERR;
52907 p->pNext = pError;
52908 pError = p;
52909 }else{
52910 unqlite_free(p);
52911 }
52912 }
52913 pInode->pUnused = pError;
52914 return rc;
52915}
52916/*
52917** Release a unixInodeInfo structure previously allocated by findInodeInfo().
52918**
52919** The mutex entered using the unixEnterMutex() function must be held
52920** when this function is called.
52921*/
52922static void releaseInodeInfo(unixFile *pFile){
52923 unixInodeInfo *pInode = pFile->pInode;
52924 if( pInode ){
52925 pInode->nRef--;
52926 if( pInode->nRef==0 ){
52927 closePendingFds(pFile);
52928 if( pInode->pPrev ){
52929 pInode->pPrev->pNext = pInode->pNext;
52930 }else{
52931 inodeList = pInode->pNext;
52932 }
52933 if( pInode->pNext ){
52934 pInode->pNext->pPrev = pInode->pPrev;
52935 }
52936 unqlite_free(pInode);
52937 }
52938 }
52939}
52940/*
52941** Given a file descriptor, locate the unixInodeInfo object that
52942** describes that file descriptor. Create a new one if necessary. The
52943** return value might be uninitialized if an error occurs.
52944**
52945** The mutex entered using the unixEnterMutex() function must be held
52946** when this function is called.
52947**
52948** Return an appropriate error code.
52949*/
52950static int findInodeInfo(
52951 unixFile *pFile, /* Unix file with file desc used in the key */
52952 unixInodeInfo **ppInode /* Return the unixInodeInfo object here */
52953){
52954 int rc; /* System call return code */
52955 int fd; /* The file descriptor for pFile */
52956 struct unixFileId fileId; /* Lookup key for the unixInodeInfo */
52957 struct stat statbuf; /* Low-level file information */
52958 unixInodeInfo *pInode = 0; /* Candidate unixInodeInfo object */
52959
52960 /* Get low-level information about the file that we can used to
52961 ** create a unique name for the file.
52962 */
52963 fd = pFile->h;
52964 rc = fstat(fd, &statbuf);
52965 if( rc!=0 ){
52966 pFile->lastErrno = errno;
52967#ifdef EOVERFLOW
52968 if( pFile->lastErrno==EOVERFLOW ) return UNQLITE_NOTIMPLEMENTED;
52969#endif
52970 return UNQLITE_IOERR;
52971 }
52972
52973#ifdef __APPLE__
52974 /* On OS X on an msdos filesystem, the inode number is reported
52975 ** incorrectly for zero-size files. See ticket #3260. To work
52976 ** around this problem (we consider it a bug in OS X, not SQLite)
52977 ** we always increase the file size to 1 by writing a single byte
52978 ** prior to accessing the inode number. The one byte written is
52979 ** an ASCII 'S' character which also happens to be the first byte
52980 ** in the header of every SQLite database. In this way, if there
52981 ** is a race condition such that another thread has already populated
52982 ** the first page of the database, no damage is done.
52983 */
52984 if( statbuf.st_size==0 && (pFile->fsFlags & UNQLITE_FSFLAGS_IS_MSDOS)!=0 ){
52985 rc = write(fd, "S", 1);
52986 if( rc!=1 ){
52987 pFile->lastErrno = errno;
52988 return UNQLITE_IOERR;
52989 }
52990 rc = fstat(fd, &statbuf);
52991 if( rc!=0 ){
52992 pFile->lastErrno = errno;
52993 return UNQLITE_IOERR;
52994 }
52995 }
52996#endif
52997 SyZero(&fileId,sizeof(fileId));
52998 fileId.dev = statbuf.st_dev;
52999 fileId.ino = statbuf.st_ino;
53000 pInode = inodeList;
53001 while( pInode && SyMemcmp((const void *)&fileId,(const void *)&pInode->fileId, sizeof(fileId)) ){
53002 pInode = pInode->pNext;
53003 }
53004 if( pInode==0 ){
53005 pInode = (unixInodeInfo *)unqlite_malloc( sizeof(*pInode) );
53006 if( pInode==0 ){
53007 return UNQLITE_NOMEM;
53008 }
53009 SyZero(pInode,sizeof(*pInode));
53010 SyMemcpy((const void *)&fileId,(void *)&pInode->fileId,sizeof(fileId));
53011 pInode->nRef = 1;
53012 pInode->pNext = inodeList;
53013 pInode->pPrev = 0;
53014 if( inodeList ) inodeList->pPrev = pInode;
53015 inodeList = pInode;
53016 }else{
53017 pInode->nRef++;
53018 }
53019 *ppInode = pInode;
53020 return UNQLITE_OK;
53021}
53022/*
53023** This routine checks if there is a RESERVED lock held on the specified
53024** file by this or any other process. If such a lock is held, set *pResOut
53025** to a non-zero value otherwise *pResOut is set to zero. The return value
53026** is set to UNQLITE_OK unless an I/O error occurs during lock checking.
53027*/
53028static int unixCheckReservedLock(unqlite_file *id, int *pResOut){
53029 int rc = UNQLITE_OK;
53030 int reserved = 0;
53031 unixFile *pFile = (unixFile*)id;
53032
53033
53034 unixEnterMutex(); /* Because pFile->pInode is shared across threads */
53035
53036 /* Check if a thread in this process holds such a lock */
53037 if( pFile->pInode->eFileLock>SHARED_LOCK ){
53038 reserved = 1;
53039 }
53040
53041 /* Otherwise see if some other process holds it.
53042 */
53043 if( !reserved ){
53044 struct flock lock;
53045 lock.l_whence = SEEK_SET;
53046 lock.l_start = RESERVED_BYTE;
53047 lock.l_len = 1;
53048 lock.l_type = F_WRLCK;
53049 if (-1 == fcntl(pFile->h, F_GETLK, &lock)) {
53050 int tErrno = errno;
53051 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53052 pFile->lastErrno = tErrno;
53053 } else if( lock.l_type!=F_UNLCK ){
53054 reserved = 1;
53055 }
53056 }
53057
53058 unixLeaveMutex();
53059
53060 *pResOut = reserved;
53061 return rc;
53062}
53063/*
53064** Lock the file with the lock specified by parameter eFileLock - one
53065** of the following:
53066**
53067** (1) SHARED_LOCK
53068** (2) RESERVED_LOCK
53069** (3) PENDING_LOCK
53070** (4) EXCLUSIVE_LOCK
53071**
53072** Sometimes when requesting one lock state, additional lock states
53073** are inserted in between. The locking might fail on one of the later
53074** transitions leaving the lock state different from what it started but
53075** still short of its goal. The following chart shows the allowed
53076** transitions and the inserted intermediate states:
53077**
53078** UNLOCKED -> SHARED
53079** SHARED -> RESERVED
53080** SHARED -> (PENDING) -> EXCLUSIVE
53081** RESERVED -> (PENDING) -> EXCLUSIVE
53082** PENDING -> EXCLUSIVE
53083**
53084** This routine will only increase a lock. Use the unqliteOsUnlock()
53085** routine to lower a locking level.
53086*/
53087static int unixLock(unqlite_file *id, int eFileLock){
53088 /* The following describes the implementation of the various locks and
53089 ** lock transitions in terms of the POSIX advisory shared and exclusive
53090 ** lock primitives (called read-locks and write-locks below, to avoid
53091 ** confusion with SQLite lock names). The algorithms are complicated
53092 ** slightly in order to be compatible with unixdows systems simultaneously
53093 ** accessing the same database file, in case that is ever required.
53094 **
53095 ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved
53096 ** byte', each single bytes at well known offsets, and the 'shared byte
53097 ** range', a range of 510 bytes at a well known offset.
53098 **
53099 ** To obtain a SHARED lock, a read-lock is obtained on the 'pending
53100 ** byte'. If this is successful, a random byte from the 'shared byte
53101 ** range' is read-locked and the lock on the 'pending byte' released.
53102 **
53103 ** A process may only obtain a RESERVED lock after it has a SHARED lock.
53104 ** A RESERVED lock is implemented by grabbing a write-lock on the
53105 ** 'reserved byte'.
53106 **
53107 ** A process may only obtain a PENDING lock after it has obtained a
53108 ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock
53109 ** on the 'pending byte'. This ensures that no new SHARED locks can be
53110 ** obtained, but existing SHARED locks are allowed to persist. A process
53111 ** does not have to obtain a RESERVED lock on the way to a PENDING lock.
53112 ** This property is used by the algorithm for rolling back a journal file
53113 ** after a crash.
53114 **
53115 ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is
53116 ** implemented by obtaining a write-lock on the entire 'shared byte
53117 ** range'. Since all other locks require a read-lock on one of the bytes
53118 ** within this range, this ensures that no other locks are held on the
53119 ** database.
53120 **
53121 ** The reason a single byte cannot be used instead of the 'shared byte
53122 ** range' is that some versions of unixdows do not support read-locks. By
53123 ** locking a random byte from a range, concurrent SHARED locks may exist
53124 ** even if the locking primitive used is always a write-lock.
53125 */
53126 int rc = UNQLITE_OK;
53127 unixFile *pFile = (unixFile*)id;
53128 unixInodeInfo *pInode = pFile->pInode;
53129 struct flock lock;
53130 int s = 0;
53131 int tErrno = 0;
53132
53133 /* If there is already a lock of this type or more restrictive on the
53134 ** unixFile, do nothing. Don't use the end_lock: exit path, as
53135 ** unixEnterMutex() hasn't been called yet.
53136 */
53137 if( pFile->eFileLock>=eFileLock ){
53138 return UNQLITE_OK;
53139 }
53140 /* This mutex is needed because pFile->pInode is shared across threads
53141 */
53142 unixEnterMutex();
53143 pInode = pFile->pInode;
53144
53145 /* If some thread using this PID has a lock via a different unixFile*
53146 ** handle that precludes the requested lock, return BUSY.
53147 */
53148 if( (pFile->eFileLock!=pInode->eFileLock &&
53149 (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
53150 ){
53151 rc = UNQLITE_BUSY;
53152 goto end_lock;
53153 }
53154
53155 /* If a SHARED lock is requested, and some thread using this PID already
53156 ** has a SHARED or RESERVED lock, then increment reference counts and
53157 ** return UNQLITE_OK.
53158 */
53159 if( eFileLock==SHARED_LOCK &&
53160 (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){
53161 pFile->eFileLock = SHARED_LOCK;
53162 pInode->nShared++;
53163 pInode->nLock++;
53164 goto end_lock;
53165 }
53166 /* A PENDING lock is needed before acquiring a SHARED lock and before
53167 ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will
53168 ** be released.
53169 */
53170 lock.l_len = 1L;
53171 lock.l_whence = SEEK_SET;
53172 if( eFileLock==SHARED_LOCK
53173 || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
53174 ){
53175 lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK);
53176 lock.l_start = PENDING_BYTE;
53177 s = fcntl(pFile->h, F_SETLK, &lock);
53178 if( s==(-1) ){
53179 tErrno = errno;
53180 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53181 if( IS_LOCK_ERROR(rc) ){
53182 pFile->lastErrno = tErrno;
53183 }
53184 goto end_lock;
53185 }
53186 }
53187 /* If control gets to this point, then actually go ahead and make
53188 ** operating system calls for the specified lock.
53189 */
53190 if( eFileLock==SHARED_LOCK ){
53191 /* Now get the read-lock */
53192 lock.l_start = SHARED_FIRST;
53193 lock.l_len = SHARED_SIZE;
53194 if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){
53195 tErrno = errno;
53196 }
53197 /* Drop the temporary PENDING lock */
53198 lock.l_start = PENDING_BYTE;
53199 lock.l_len = 1L;
53200 lock.l_type = F_UNLCK;
53201 if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){
53202 if( s != -1 ){
53203 /* This could happen with a network mount */
53204 tErrno = errno;
53205 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53206 if( IS_LOCK_ERROR(rc) ){
53207 pFile->lastErrno = tErrno;
53208 }
53209 goto end_lock;
53210 }
53211 }
53212 if( s==(-1) ){
53213 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53214 if( IS_LOCK_ERROR(rc) ){
53215 pFile->lastErrno = tErrno;
53216 }
53217 }else{
53218 pFile->eFileLock = SHARED_LOCK;
53219 pInode->nLock++;
53220 pInode->nShared = 1;
53221 }
53222 }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){
53223 /* We are trying for an exclusive lock but another thread in this
53224 ** same process is still holding a shared lock. */
53225 rc = UNQLITE_BUSY;
53226 }else{
53227 /* The request was for a RESERVED or EXCLUSIVE lock. It is
53228 ** assumed that there is a SHARED or greater lock on the file
53229 ** already.
53230 */
53231 lock.l_type = F_WRLCK;
53232 switch( eFileLock ){
53233 case RESERVED_LOCK:
53234 lock.l_start = RESERVED_BYTE;
53235 break;
53236 case EXCLUSIVE_LOCK:
53237 lock.l_start = SHARED_FIRST;
53238 lock.l_len = SHARED_SIZE;
53239 break;
53240 default:
53241 /* Can't happen */
53242 break;
53243 }
53244 s = fcntl(pFile->h, F_SETLK, &lock);
53245 if( s==(-1) ){
53246 tErrno = errno;
53247 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53248 if( IS_LOCK_ERROR(rc) ){
53249 pFile->lastErrno = tErrno;
53250 }
53251 }
53252 }
53253 if( rc==UNQLITE_OK ){
53254 pFile->eFileLock = eFileLock;
53255 pInode->eFileLock = eFileLock;
53256 }else if( eFileLock==EXCLUSIVE_LOCK ){
53257 pFile->eFileLock = PENDING_LOCK;
53258 pInode->eFileLock = PENDING_LOCK;
53259 }
53260end_lock:
53261 unixLeaveMutex();
53262 return rc;
53263}
53264/*
53265** Add the file descriptor used by file handle pFile to the corresponding
53266** pUnused list.
53267*/
53268static void setPendingFd(unixFile *pFile){
53269 unixInodeInfo *pInode = pFile->pInode;
53270 UnixUnusedFd *p = pFile->pUnused;
53271 p->pNext = pInode->pUnused;
53272 pInode->pUnused = p;
53273 pFile->h = -1;
53274 pFile->pUnused = 0;
53275}
53276/*
53277** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
53278** must be either NO_LOCK or SHARED_LOCK.
53279**
53280** If the locking level of the file descriptor is already at or below
53281** the requested locking level, this routine is a no-op.
53282**
53283** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED
53284** the byte range is divided into 2 parts and the first part is unlocked then
53285** set to a read lock, then the other part is simply unlocked. This works
53286** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to
53287** remove the write lock on a region when a read lock is set.
53288*/
53289static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){
53290 unixFile *pFile = (unixFile*)id;
53291 unixInodeInfo *pInode;
53292 struct flock lock;
53293 int rc = UNQLITE_OK;
53294 int h;
53295 int tErrno; /* Error code from system call errors */
53296
53297 if( pFile->eFileLock<=eFileLock ){
53298 return UNQLITE_OK;
53299 }
53300 unixEnterMutex();
53301
53302 h = pFile->h;
53303 pInode = pFile->pInode;
53304
53305 if( pFile->eFileLock>SHARED_LOCK ){
53306 /* downgrading to a shared lock on NFS involves clearing the write lock
53307 ** before establishing the readlock - to avoid a race condition we downgrade
53308 ** the lock in 2 blocks, so that part of the range will be covered by a
53309 ** write lock until the rest is covered by a read lock:
53310 ** 1: [WWWWW]
53311 ** 2: [....W]
53312 ** 3: [RRRRW]
53313 ** 4: [RRRR.]
53314 */
53315 if( eFileLock==SHARED_LOCK ){
53316 if( handleNFSUnlock ){
53317 off_t divSize = SHARED_SIZE - 1;
53318
53319 lock.l_type = F_UNLCK;
53320 lock.l_whence = SEEK_SET;
53321 lock.l_start = SHARED_FIRST;
53322 lock.l_len = divSize;
53323 if( fcntl(h, F_SETLK, &lock)==(-1) ){
53324 tErrno = errno;
53325 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53326 if( IS_LOCK_ERROR(rc) ){
53327 pFile->lastErrno = tErrno;
53328 }
53329 goto end_unlock;
53330 }
53331 lock.l_type = F_RDLCK;
53332 lock.l_whence = SEEK_SET;
53333 lock.l_start = SHARED_FIRST;
53334 lock.l_len = divSize;
53335 if( fcntl(h, F_SETLK, &lock)==(-1) ){
53336 tErrno = errno;
53337 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53338 if( IS_LOCK_ERROR(rc) ){
53339 pFile->lastErrno = tErrno;
53340 }
53341 goto end_unlock;
53342 }
53343 lock.l_type = F_UNLCK;
53344 lock.l_whence = SEEK_SET;
53345 lock.l_start = SHARED_FIRST+divSize;
53346 lock.l_len = SHARED_SIZE-divSize;
53347 if( fcntl(h, F_SETLK, &lock)==(-1) ){
53348 tErrno = errno;
53349 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53350 if( IS_LOCK_ERROR(rc) ){
53351 pFile->lastErrno = tErrno;
53352 }
53353 goto end_unlock;
53354 }
53355 }else{
53356 lock.l_type = F_RDLCK;
53357 lock.l_whence = SEEK_SET;
53358 lock.l_start = SHARED_FIRST;
53359 lock.l_len = SHARED_SIZE;
53360 if( fcntl(h, F_SETLK, &lock)==(-1) ){
53361 tErrno = errno;
53362 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53363 if( IS_LOCK_ERROR(rc) ){
53364 pFile->lastErrno = tErrno;
53365 }
53366 goto end_unlock;
53367 }
53368 }
53369 }
53370 lock.l_type = F_UNLCK;
53371 lock.l_whence = SEEK_SET;
53372 lock.l_start = PENDING_BYTE;
53373 lock.l_len = 2L;
53374 if( fcntl(h, F_SETLK, &lock)!=(-1) ){
53375 pInode->eFileLock = SHARED_LOCK;
53376 }else{
53377 tErrno = errno;
53378 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53379 if( IS_LOCK_ERROR(rc) ){
53380 pFile->lastErrno = tErrno;
53381 }
53382 goto end_unlock;
53383 }
53384 }
53385 if( eFileLock==NO_LOCK ){
53386 /* Decrement the shared lock counter. Release the lock using an
53387 ** OS call only when all threads in this same process have released
53388 ** the lock.
53389 */
53390 pInode->nShared--;
53391 if( pInode->nShared==0 ){
53392 lock.l_type = F_UNLCK;
53393 lock.l_whence = SEEK_SET;
53394 lock.l_start = lock.l_len = 0L;
53395
53396 if( fcntl(h, F_SETLK, &lock)!=(-1) ){
53397 pInode->eFileLock = NO_LOCK;
53398 }else{
53399 tErrno = errno;
53400 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53401 if( IS_LOCK_ERROR(rc) ){
53402 pFile->lastErrno = tErrno;
53403 }
53404 pInode->eFileLock = NO_LOCK;
53405 pFile->eFileLock = NO_LOCK;
53406 }
53407 }
53408
53409 /* Decrement the count of locks against this same file. When the
53410 ** count reaches zero, close any other file descriptors whose close
53411 ** was deferred because of outstanding locks.
53412 */
53413 pInode->nLock--;
53414
53415 if( pInode->nLock==0 ){
53416 int rc2 = closePendingFds(pFile);
53417 if( rc==UNQLITE_OK ){
53418 rc = rc2;
53419 }
53420 }
53421 }
53422
53423end_unlock:
53424
53425 unixLeaveMutex();
53426
53427 if( rc==UNQLITE_OK ) pFile->eFileLock = eFileLock;
53428 return rc;
53429}
53430/*
53431** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
53432** must be either NO_LOCK or SHARED_LOCK.
53433**
53434** If the locking level of the file descriptor is already at or below
53435** the requested locking level, this routine is a no-op.
53436*/
53437static int unixUnlock(unqlite_file *id, int eFileLock){
53438 return _posixUnlock(id, eFileLock, 0);
53439}
53440/*
53441** This function performs the parts of the "close file" operation
53442** common to all locking schemes. It closes the directory and file
53443** handles, if they are valid, and sets all fields of the unixFile
53444** structure to 0.
53445**
53446*/
53447static int closeUnixFile(unqlite_file *id){
53448 unixFile *pFile = (unixFile*)id;
53449 if( pFile ){
53450 if( pFile->dirfd>=0 ){
53451 int err = close(pFile->dirfd);
53452 if( err ){
53453 pFile->lastErrno = errno;
53454 return UNQLITE_IOERR;
53455 }else{
53456 pFile->dirfd=-1;
53457 }
53458 }
53459 if( pFile->h>=0 ){
53460 int err = close(pFile->h);
53461 if( err ){
53462 pFile->lastErrno = errno;
53463 return UNQLITE_IOERR;
53464 }
53465 }
53466 unqlite_free(pFile->pUnused);
53467 SyZero(pFile,sizeof(unixFile));
53468 }
53469 return UNQLITE_OK;
53470}
53471/*
53472** Close a file.
53473*/
53474static int unixClose(unqlite_file *id){
53475 int rc = UNQLITE_OK;
53476 if( id ){
53477 unixFile *pFile = (unixFile *)id;
53478 unixUnlock(id, NO_LOCK);
53479 unixEnterMutex();
53480 if( pFile->pInode && pFile->pInode->nLock ){
53481 /* If there are outstanding locks, do not actually close the file just
53482 ** yet because that would clear those locks. Instead, add the file
53483 ** descriptor to pInode->pUnused list. It will be automatically closed
53484 ** when the last lock is cleared.
53485 */
53486 setPendingFd(pFile);
53487 }
53488 releaseInodeInfo(pFile);
53489 rc = closeUnixFile(id);
53490 unixLeaveMutex();
53491 }
53492 return rc;
53493}
53494/************** End of the posix advisory lock implementation *****************
53495******************************************************************************/
53496/*
53497**
53498** The next division contains implementations for all methods of the
53499** unqlite_file object other than the locking methods. The locking
53500** methods were defined in divisions above (one locking method per
53501** division). Those methods that are common to all locking modes
53502** are gather together into this division.
53503*/
53504/*
53505** Seek to the offset passed as the second argument, then read cnt
53506** bytes into pBuf. Return the number of bytes actually read.
53507**
53508** NB: If you define USE_PREAD or USE_PREAD64, then it might also
53509** be necessary to define _XOPEN_SOURCE to be 500. This varies from
53510** one system to another. Since SQLite does not define USE_PREAD
53511** any form by default, we will not attempt to define _XOPEN_SOURCE.
53512** See tickets #2741 and #2681.
53513**
53514** To avoid stomping the errno value on a failed read the lastErrno value
53515** is set before returning.
53516*/
53517static int seekAndRead(unixFile *id, unqlite_int64 offset, void *pBuf, int cnt){
53518 int got;
53519#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
53520 unqlite_int64 newOffset;
53521#endif
53522
53523#if defined(USE_PREAD)
53524 got = pread(id->h, pBuf, cnt, offset);
53525#elif defined(USE_PREAD64)
53526 got = pread64(id->h, pBuf, cnt, offset);
53527#else
53528 newOffset = lseek(id->h, offset, SEEK_SET);
53529
53530 if( newOffset!=offset ){
53531 if( newOffset == -1 ){
53532 ((unixFile*)id)->lastErrno = errno;
53533 }else{
53534 ((unixFile*)id)->lastErrno = 0;
53535 }
53536 return -1;
53537 }
53538 got = read(id->h, pBuf, cnt);
53539#endif
53540 if( got<0 ){
53541 ((unixFile*)id)->lastErrno = errno;
53542 }
53543 return got;
53544}
53545/*
53546** Read data from a file into a buffer. Return UNQLITE_OK if all
53547** bytes were read successfully and UNQLITE_IOERR if anything goes
53548** wrong.
53549*/
53550static int unixRead(
53551 unqlite_file *id,
53552 void *pBuf,
53553 unqlite_int64 amt,
53554 unqlite_int64 offset
53555){
53556 unixFile *pFile = (unixFile *)id;
53557 int got;
53558
53559 got = seekAndRead(pFile, offset, pBuf, (int)amt);
53560 if( got==(int)amt ){
53561 return UNQLITE_OK;
53562 }else if( got<0 ){
53563 /* lastErrno set by seekAndRead */
53564 return UNQLITE_IOERR;
53565 }else{
53566 pFile->lastErrno = 0; /* not a system error */
53567 /* Unread parts of the buffer must be zero-filled */
53568 SyZero(&((char*)pBuf)[got],(sxu32)amt-got);
53569 return UNQLITE_IOERR;
53570 }
53571}
53572/*
53573** Seek to the offset in id->offset then read cnt bytes into pBuf.
53574** Return the number of bytes actually read. Update the offset.
53575**
53576** To avoid stomping the errno value on a failed write the lastErrno value
53577** is set before returning.
53578*/
53579static int seekAndWrite(unixFile *id, unqlite_int64 offset, const void *pBuf, unqlite_int64 cnt){
53580 int got;
53581#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
53582 unqlite_int64 newOffset;
53583#endif
53584
53585#if defined(USE_PREAD)
53586 got = pwrite(id->h, pBuf, cnt, offset);
53587#elif defined(USE_PREAD64)
53588 got = pwrite64(id->h, pBuf, cnt, offset);
53589#else
53590 newOffset = lseek(id->h, offset, SEEK_SET);
53591 if( newOffset!=offset ){
53592 if( newOffset == -1 ){
53593 ((unixFile*)id)->lastErrno = errno;
53594 }else{
53595 ((unixFile*)id)->lastErrno = 0;
53596 }
53597 return -1;
53598 }
53599 got = write(id->h, pBuf, cnt);
53600#endif
53601 if( got<0 ){
53602 ((unixFile*)id)->lastErrno = errno;
53603 }
53604 return got;
53605}
53606/*
53607** Write data from a buffer into a file. Return UNQLITE_OK on success
53608** or some other error code on failure.
53609*/
53610static int unixWrite(
53611 unqlite_file *id,
53612 const void *pBuf,
53613 unqlite_int64 amt,
53614 unqlite_int64 offset
53615){
53616 unixFile *pFile = (unixFile*)id;
53617 int wrote = 0;
53618
53619 while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
53620 amt -= wrote;
53621 offset += wrote;
53622 pBuf = &((char*)pBuf)[wrote];
53623 }
53624
53625 if( amt>0 ){
53626 if( wrote<0 ){
53627 /* lastErrno set by seekAndWrite */
53628 return UNQLITE_IOERR;
53629 }else{
53630 pFile->lastErrno = 0; /* not a system error */
53631 return UNQLITE_FULL;
53632 }
53633 }
53634 return UNQLITE_OK;
53635}
53636/*
53637** We do not trust systems to provide a working fdatasync(). Some do.
53638** Others do no. To be safe, we will stick with the (slower) fsync().
53639** If you know that your system does support fdatasync() correctly,
53640** then simply compile with -Dfdatasync=fdatasync
53641*/
53642#if !defined(fdatasync) && !defined(__linux__)
53643# define fdatasync fsync
53644#endif
53645
53646/*
53647** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not
53648** the F_FULLFSYNC macro is defined. F_FULLFSYNC is currently
53649** only available on Mac OS X. But that could change.
53650*/
53651#ifdef F_FULLFSYNC
53652# define HAVE_FULLFSYNC 1
53653#else
53654# define HAVE_FULLFSYNC 0
53655#endif
53656/*
53657** The fsync() system call does not work as advertised on many
53658** unix systems. The following procedure is an attempt to make
53659** it work better.
53660**
53661**
53662** SQLite sets the dataOnly flag if the size of the file is unchanged.
53663** The idea behind dataOnly is that it should only write the file content
53664** to disk, not the inode. We only set dataOnly if the file size is
53665** unchanged since the file size is part of the inode. However,
53666** Ted Ts'o tells us that fdatasync() will also write the inode if the
53667** file size has changed. The only real difference between fdatasync()
53668** and fsync(), Ted tells us, is that fdatasync() will not flush the
53669** inode if the mtime or owner or other inode attributes have changed.
53670** We only care about the file size, not the other file attributes, so
53671** as far as SQLite is concerned, an fdatasync() is always adequate.
53672** So, we always use fdatasync() if it is available, regardless of
53673** the value of the dataOnly flag.
53674*/
53675static int full_fsync(int fd, int fullSync, int dataOnly){
53676 int rc;
53677#if HAVE_FULLFSYNC
53678 SXUNUSED(dataOnly);
53679#else
53680 SXUNUSED(fullSync);
53681 SXUNUSED(dataOnly);
53682#endif
53683
53684 /* If we compiled with the UNQLITE_NO_SYNC flag, then syncing is a
53685 ** no-op
53686 */
53687#if HAVE_FULLFSYNC
53688 if( fullSync ){
53689 rc = fcntl(fd, F_FULLFSYNC, 0);
53690 }else{
53691 rc = 1;
53692 }
53693 /* If the FULLFSYNC failed, fall back to attempting an fsync().
53694 ** It shouldn't be possible for fullfsync to fail on the local
53695 ** file system (on OSX), so failure indicates that FULLFSYNC
53696 ** isn't supported for this file system. So, attempt an fsync
53697 ** and (for now) ignore the overhead of a superfluous fcntl call.
53698 ** It'd be better to detect fullfsync support once and avoid
53699 ** the fcntl call every time sync is called.
53700 */
53701 if( rc ) rc = fsync(fd);
53702
53703#elif defined(__APPLE__)
53704 /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly
53705 ** so currently we default to the macro that redefines fdatasync to fsync
53706 */
53707 rc = fsync(fd);
53708#else
53709 rc = fdatasync(fd);
53710#endif /* ifdef UNQLITE_NO_SYNC elif HAVE_FULLFSYNC */
53711 if( rc!= -1 ){
53712 rc = 0;
53713 }
53714 return rc;
53715}
53716/*
53717** Make sure all writes to a particular file are committed to disk.
53718**
53719** If dataOnly==0 then both the file itself and its metadata (file
53720** size, access time, etc) are synced. If dataOnly!=0 then only the
53721** file data is synced.
53722**
53723** Under Unix, also make sure that the directory entry for the file
53724** has been created by fsync-ing the directory that contains the file.
53725** If we do not do this and we encounter a power failure, the directory
53726** entry for the journal might not exist after we reboot. The next
53727** SQLite to access the file will not know that the journal exists (because
53728** the directory entry for the journal was never created) and the transaction
53729** will not roll back - possibly leading to database corruption.
53730*/
53731static int unixSync(unqlite_file *id, int flags){
53732 int rc;
53733 unixFile *pFile = (unixFile*)id;
53734
53735 int isDataOnly = (flags&UNQLITE_SYNC_DATAONLY);
53736 int isFullsync = (flags&0x0F)==UNQLITE_SYNC_FULL;
53737
53738 rc = full_fsync(pFile->h, isFullsync, isDataOnly);
53739
53740 if( rc ){
53741 pFile->lastErrno = errno;
53742 return UNQLITE_IOERR;
53743 }
53744 if( pFile->dirfd>=0 ){
53745 int err;
53746#ifndef UNQLITE_DISABLE_DIRSYNC
53747 /* The directory sync is only attempted if full_fsync is
53748 ** turned off or unavailable. If a full_fsync occurred above,
53749 ** then the directory sync is superfluous.
53750 */
53751 if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){
53752 /*
53753 ** We have received multiple reports of fsync() returning
53754 ** errors when applied to directories on certain file systems.
53755 ** A failed directory sync is not a big deal. So it seems
53756 ** better to ignore the error. Ticket #1657
53757 */
53758 /* pFile->lastErrno = errno; */
53759 /* return UNQLITE_IOERR; */
53760 }
53761#endif
53762 err = close(pFile->dirfd); /* Only need to sync once, so close the */
53763 if( err==0 ){ /* directory when we are done */
53764 pFile->dirfd = -1;
53765 }else{
53766 pFile->lastErrno = errno;
53767 rc = UNQLITE_IOERR;
53768 }
53769 }
53770 return rc;
53771}
53772/*
53773** Truncate an open file to a specified size
53774*/
53775static int unixTruncate(unqlite_file *id, sxi64 nByte){
53776 unixFile *pFile = (unixFile *)id;
53777 int rc;
53778
53779 rc = ftruncate(pFile->h, (off_t)nByte);
53780 if( rc ){
53781 pFile->lastErrno = errno;
53782 return UNQLITE_IOERR;
53783 }else{
53784 return UNQLITE_OK;
53785 }
53786}
53787/*
53788** Determine the current size of a file in bytes
53789*/
53790static int unixFileSize(unqlite_file *id,sxi64 *pSize){
53791 int rc;
53792 struct stat buf;
53793
53794 rc = fstat(((unixFile*)id)->h, &buf);
53795
53796 if( rc!=0 ){
53797 ((unixFile*)id)->lastErrno = errno;
53798 return UNQLITE_IOERR;
53799 }
53800 *pSize = buf.st_size;
53801
53802 /* When opening a zero-size database, the findInodeInfo() procedure
53803 ** writes a single byte into that file in order to work around a bug
53804 ** in the OS-X msdos filesystem. In order to avoid problems with upper
53805 ** layers, we need to report this file size as zero even though it is
53806 ** really 1. Ticket #3260.
53807 */
53808 if( *pSize==1 ) *pSize = 0;
53809
53810 return UNQLITE_OK;
53811}
53812/*
53813** Return the sector size in bytes of the underlying block device for
53814** the specified file. This is almost always 512 bytes, but may be
53815** larger for some devices.
53816**
53817** SQLite code assumes this function cannot fail. It also assumes that
53818** if two files are created in the same file-system directory (i.e.
53819** a database and its journal file) that the sector size will be the
53820** same for both.
53821*/
53822static int unixSectorSize(unqlite_file *NotUsed){
53823 SXUNUSED(NotUsed);
53824 return UNQLITE_DEFAULT_SECTOR_SIZE;
53825}
53826/*
53827** This vector defines all the methods that can operate on an
53828** unqlite_file for Windows systems.
53829*/
53830static const unqlite_io_methods unixIoMethod = {
53831 1, /* iVersion */
53832 unixClose, /* xClose */
53833 unixRead, /* xRead */
53834 unixWrite, /* xWrite */
53835 unixTruncate, /* xTruncate */
53836 unixSync, /* xSync */
53837 unixFileSize, /* xFileSize */
53838 unixLock, /* xLock */
53839 unixUnlock, /* xUnlock */
53840 unixCheckReservedLock, /* xCheckReservedLock */
53841 unixSectorSize, /* xSectorSize */
53842};
53843/****************************************************************************
53844**************************** unqlite_vfs methods ****************************
53845**
53846** This division contains the implementation of methods on the
53847** unqlite_vfs object.
53848*/
53849/*
53850** Initialize the contents of the unixFile structure pointed to by pId.
53851*/
53852static int fillInUnixFile(
53853 unqlite_vfs *pVfs, /* Pointer to vfs object */
53854 int h, /* Open file descriptor of file being opened */
53855 int dirfd, /* Directory file descriptor */
53856 unqlite_file *pId, /* Write to the unixFile structure here */
53857 const char *zFilename, /* Name of the file being opened */
53858 int noLock, /* Omit locking if true */
53859 int isDelete /* Delete on close if true */
53860){
53861 const unqlite_io_methods *pLockingStyle = &unixIoMethod;
53862 unixFile *pNew = (unixFile *)pId;
53863 int rc = UNQLITE_OK;
53864
53865 /* Parameter isDelete is only used on vxworks. Express this explicitly
53866 ** here to prevent compiler warnings about unused parameters.
53867 */
53868 SXUNUSED(isDelete);
53869 SXUNUSED(noLock);
53870 SXUNUSED(pVfs);
53871
53872 pNew->h = h;
53873 pNew->dirfd = dirfd;
53874 pNew->fileFlags = 0;
53875 pNew->zPath = zFilename;
53876
53877 unixEnterMutex();
53878 rc = findInodeInfo(pNew, &pNew->pInode);
53879 if( rc!=UNQLITE_OK ){
53880 /* If an error occured in findInodeInfo(), close the file descriptor
53881 ** immediately, before releasing the mutex. findInodeInfo() may fail
53882 ** in two scenarios:
53883 **
53884 ** (a) A call to fstat() failed.
53885 ** (b) A malloc failed.
53886 **
53887 ** Scenario (b) may only occur if the process is holding no other
53888 ** file descriptors open on the same file. If there were other file
53889 ** descriptors on this file, then no malloc would be required by
53890 ** findInodeInfo(). If this is the case, it is quite safe to close
53891 ** handle h - as it is guaranteed that no posix locks will be released
53892 ** by doing so.
53893 **
53894 ** If scenario (a) caused the error then things are not so safe. The
53895 ** implicit assumption here is that if fstat() fails, things are in
53896 ** such bad shape that dropping a lock or two doesn't matter much.
53897 */
53898 close(h);
53899 h = -1;
53900 }
53901 unixLeaveMutex();
53902
53903 pNew->lastErrno = 0;
53904 if( rc!=UNQLITE_OK ){
53905 if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */
53906 if( h>=0 ) close(h);
53907 }else{
53908 pNew->pMethod = pLockingStyle;
53909 }
53910 return rc;
53911}
53912/*
53913** Open a file descriptor to the directory containing file zFilename.
53914** If successful, *pFd is set to the opened file descriptor and
53915** UNQLITE_OK is returned. If an error occurs, either UNQLITE_NOMEM
53916** or UNQLITE_CANTOPEN is returned and *pFd is set to an undefined
53917** value.
53918**
53919** If UNQLITE_OK is returned, the caller is responsible for closing
53920** the file descriptor *pFd using close().
53921*/
53922static int openDirectory(const char *zFilename, int *pFd){
53923 sxu32 ii;
53924 int fd = -1;
53925 char zDirname[MAX_PATHNAME+1];
53926 sxu32 n;
53927 n = Systrcpy(zDirname,sizeof(zDirname),zFilename,0);
53928 for(ii=n; ii>1 && zDirname[ii]!='/'; ii--);
53929 if( ii>0 ){
53930 zDirname[ii] = '\0';
53931 fd = open(zDirname, O_RDONLY|O_BINARY, 0);
53932 if( fd>=0 ){
53933#ifdef FD_CLOEXEC
53934 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
53935#endif
53936 }
53937 }
53938 *pFd = fd;
53939 return (fd>=0?UNQLITE_OK: UNQLITE_IOERR );
53940}
53941/*
53942** Search for an unused file descriptor that was opened on the database
53943** file (not a journal or master-journal file) identified by pathname
53944** zPath with UNQLITE_OPEN_XXX flags matching those passed as the second
53945** argument to this function.
53946**
53947** Such a file descriptor may exist if a database connection was closed
53948** but the associated file descriptor could not be closed because some
53949** other file descriptor open on the same file is holding a file-lock.
53950** Refer to comments in the unixClose() function and the lengthy comment
53951** describing "Posix Advisory Locking" at the start of this file for
53952** further details. Also, ticket #4018.
53953**
53954** If a suitable file descriptor is found, then it is returned. If no
53955** such file descriptor is located, -1 is returned.
53956*/
53957static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
53958 UnixUnusedFd *pUnused = 0;
53959 struct stat sStat; /* Results of stat() call */
53960 /* A stat() call may fail for various reasons. If this happens, it is
53961 ** almost certain that an open() call on the same path will also fail.
53962 ** For this reason, if an error occurs in the stat() call here, it is
53963 ** ignored and -1 is returned. The caller will try to open a new file
53964 ** descriptor on the same path, fail, and return an error to SQLite.
53965 **
53966 ** Even if a subsequent open() call does succeed, the consequences of
53967 ** not searching for a resusable file descriptor are not dire. */
53968 if( 0==stat(zPath, &sStat) ){
53969 unixInodeInfo *pInode;
53970
53971 unixEnterMutex();
53972 pInode = inodeList;
53973 while( pInode && (pInode->fileId.dev!=sStat.st_dev
53974 || pInode->fileId.ino!=sStat.st_ino) ){
53975 pInode = pInode->pNext;
53976 }
53977 if( pInode ){
53978 UnixUnusedFd **pp;
53979 for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
53980 pUnused = *pp;
53981 if( pUnused ){
53982 *pp = pUnused->pNext;
53983 }
53984 }
53985 unixLeaveMutex();
53986 }
53987 return pUnused;
53988}
53989/*
53990** This function is called by unixOpen() to determine the unix permissions
53991** to create new files with. If no error occurs, then UNQLITE_OK is returned
53992** and a value suitable for passing as the third argument to open(2) is
53993** written to *pMode. If an IO error occurs, an SQLite error code is
53994** returned and the value of *pMode is not modified.
53995**
53996** If the file being opened is a temporary file, it is always created with
53997** the octal permissions 0600 (read/writable by owner only). If the file
53998** is a database or master journal file, it is created with the permissions
53999** mask UNQLITE_DEFAULT_FILE_PERMISSIONS.
54000**
54001** Finally, if the file being opened is a WAL or regular journal file, then
54002** this function queries the file-system for the permissions on the
54003** corresponding database file and sets *pMode to this value. Whenever
54004** possible, WAL and journal files are created using the same permissions
54005** as the associated database file.
54006*/
54007static int findCreateFileMode(
54008 const char *zPath, /* Path of file (possibly) being created */
54009 int flags, /* Flags passed as 4th argument to xOpen() */
54010 mode_t *pMode /* OUT: Permissions to open file with */
54011){
54012 int rc = UNQLITE_OK; /* Return Code */
54013 if( flags & UNQLITE_OPEN_TEMP_DB ){
54014 *pMode = 0600;
54015 SXUNUSED(zPath);
54016 }else{
54017 *pMode = UNQLITE_DEFAULT_FILE_PERMISSIONS;
54018 }
54019 return rc;
54020}
54021/*
54022** Open the file zPath.
54023**
54024** Previously, the SQLite OS layer used three functions in place of this
54025** one:
54026**
54027** unqliteOsOpenReadWrite();
54028** unqliteOsOpenReadOnly();
54029** unqliteOsOpenExclusive();
54030**
54031** These calls correspond to the following combinations of flags:
54032**
54033** ReadWrite() -> (READWRITE | CREATE)
54034** ReadOnly() -> (READONLY)
54035** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE)
54036**
54037** The old OpenExclusive() accepted a boolean argument - "delFlag". If
54038** true, the file was configured to be automatically deleted when the
54039** file handle closed. To achieve the same effect using this new
54040** interface, add the DELETEONCLOSE flag to those specified above for
54041** OpenExclusive().
54042*/
54043static int unixOpen(
54044 unqlite_vfs *pVfs, /* The VFS for which this is the xOpen method */
54045 const char *zPath, /* Pathname of file to be opened */
54046 unqlite_file *pFile, /* The file descriptor to be filled in */
54047 unsigned int flags /* Input flags to control the opening */
54048){
54049 unixFile *p = (unixFile *)pFile;
54050 int fd = -1; /* File descriptor returned by open() */
54051 int dirfd = -1; /* Directory file descriptor */
54052 int openFlags = 0; /* Flags to pass to open() */
54053 int noLock; /* True to omit locking primitives */
54054 int rc = UNQLITE_OK; /* Function Return Code */
54055 UnixUnusedFd *pUnused;
54056 int isExclusive = (flags & UNQLITE_OPEN_EXCLUSIVE);
54057 int isDelete = (flags & UNQLITE_OPEN_TEMP_DB);
54058 int isCreate = (flags & UNQLITE_OPEN_CREATE);
54059 int isReadonly = (flags & UNQLITE_OPEN_READONLY);
54060 int isReadWrite = (flags & UNQLITE_OPEN_READWRITE);
54061 /* If creating a master or main-file journal, this function will open
54062 ** a file-descriptor on the directory too. The first time unixSync()
54063 ** is called the directory file descriptor will be fsync()ed and close()d.
54064 */
54065 int isOpenDirectory = isCreate ;
54066 const char *zName = zPath;
54067
54068 SyZero(p,sizeof(unixFile));
54069
54070 pUnused = findReusableFd(zName, flags);
54071 if( pUnused ){
54072 fd = pUnused->fd;
54073 }else{
54074 pUnused = unqlite_malloc(sizeof(*pUnused));
54075 if( !pUnused ){
54076 return UNQLITE_NOMEM;
54077 }
54078 }
54079 p->pUnused = pUnused;
54080
54081 /* Determine the value of the flags parameter passed to POSIX function
54082 ** open(). These must be calculated even if open() is not called, as
54083 ** they may be stored as part of the file handle and used by the
54084 ** 'conch file' locking functions later on. */
54085 if( isReadonly ) openFlags |= O_RDONLY;
54086 if( isReadWrite ) openFlags |= O_RDWR;
54087 if( isCreate ) openFlags |= O_CREAT;
54088 if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW);
54089 openFlags |= (O_LARGEFILE|O_BINARY);
54090
54091 if( fd<0 ){
54092 mode_t openMode; /* Permissions to create file with */
54093 rc = findCreateFileMode(zName, flags, &openMode);
54094 if( rc!=UNQLITE_OK ){
54095 return rc;
54096 }
54097 fd = open(zName, openFlags, openMode);
54098 if( fd<0 ){
54099 rc = UNQLITE_IOERR;
54100 goto open_finished;
54101 }
54102 }
54103
54104 if( p->pUnused ){
54105 p->pUnused->fd = fd;
54106 p->pUnused->flags = flags;
54107 }
54108
54109 if( isDelete ){
54110 unlink(zName);
54111 }
54112
54113 if( isOpenDirectory ){
54114 rc = openDirectory(zPath, &dirfd);
54115 if( rc!=UNQLITE_OK ){
54116 /* It is safe to close fd at this point, because it is guaranteed not
54117 ** to be open on a database file. If it were open on a database file,
54118 ** it would not be safe to close as this would release any locks held
54119 ** on the file by this process. */
54120 close(fd); /* silently leak if fail, already in error */
54121 goto open_finished;
54122 }
54123 }
54124
54125#ifdef FD_CLOEXEC
54126 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
54127#endif
54128
54129 noLock = 0;
54130
54131#if defined(__APPLE__)
54132 struct statfs fsInfo;
54133 if( fstatfs(fd, &fsInfo) == -1 ){
54134 ((unixFile*)pFile)->lastErrno = errno;
54135 if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */
54136 close(fd); /* silently leak if fail, in error */
54137 return UNQLITE_IOERR;
54138 }
54139 if (0 == SyStrncmp("msdos", fsInfo.f_fstypename, 5)) {
54140 ((unixFile*)pFile)->fsFlags |= UNQLITE_FSFLAGS_IS_MSDOS;
54141 }
54142#endif
54143
54144 rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete);
54145open_finished:
54146 if( rc!=UNQLITE_OK ){
54147 unqlite_free(p->pUnused);
54148 }
54149 return rc;
54150}
54151/*
54152** Delete the file at zPath. If the dirSync argument is true, fsync()
54153** the directory after deleting the file.
54154*/
54155static int unixDelete(
54156 unqlite_vfs *NotUsed, /* VFS containing this as the xDelete method */
54157 const char *zPath, /* Name of file to be deleted */
54158 int dirSync /* If true, fsync() directory after deleting file */
54159){
54160 int rc = UNQLITE_OK;
54161 SXUNUSED(NotUsed);
54162
54163 if( unlink(zPath)==(-1) && errno!=ENOENT ){
54164 return UNQLITE_IOERR;
54165 }
54166#ifndef UNQLITE_DISABLE_DIRSYNC
54167 if( dirSync ){
54168 int fd;
54169 rc = openDirectory(zPath, &fd);
54170 if( rc==UNQLITE_OK ){
54171 if( fsync(fd) )
54172 {
54173 rc = UNQLITE_IOERR;
54174 }
54175 if( close(fd) && !rc ){
54176 rc = UNQLITE_IOERR;
54177 }
54178 }
54179 }
54180#endif
54181 return rc;
54182}
54183/*
54184** Sleep for a little while. Return the amount of time slept.
54185** The argument is the number of microseconds we want to sleep.
54186** The return value is the number of microseconds of sleep actually
54187** requested from the underlying operating system, a number which
54188** might be greater than or equal to the argument, but not less
54189** than the argument.
54190*/
54191static int unixSleep(unqlite_vfs *NotUsed, int microseconds)
54192{
54193#if defined(HAVE_USLEEP) && HAVE_USLEEP
54194 usleep(microseconds);
54195 SXUNUSED(NotUsed);
54196 return microseconds;
54197#else
54198 int seconds = (microseconds+999999)/1000000;
54199 SXUNUSED(NotUsed);
54200 sleep(seconds);
54201 return seconds*1000000;
54202#endif
54203}
54204/*
54205 * Export the current system time.
54206 */
54207static int unixCurrentTime(unqlite_vfs *pVfs,Sytm *pOut)
54208{
54209 struct tm *pTm;
54210 time_t tt;
54211 SXUNUSED(pVfs);
54212 time(&tt);
54213 pTm = gmtime(&tt);
54214 if( pTm ){ /* Yes, it can fail */
54215 STRUCT_TM_TO_SYTM(pTm,pOut);
54216 }
54217 return UNQLITE_OK;
54218}
54219/*
54220** Test the existance of or access permissions of file zPath. The
54221** test performed depends on the value of flags:
54222**
54223** UNQLITE_ACCESS_EXISTS: Return 1 if the file exists
54224** UNQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable.
54225** UNQLITE_ACCESS_READONLY: Return 1 if the file is readable.
54226**
54227** Otherwise return 0.
54228*/
54229static int unixAccess(
54230 unqlite_vfs *NotUsed, /* The VFS containing this xAccess method */
54231 const char *zPath, /* Path of the file to examine */
54232 int flags, /* What do we want to learn about the zPath file? */
54233 int *pResOut /* Write result boolean here */
54234){
54235 int amode = 0;
54236 SXUNUSED(NotUsed);
54237 switch( flags ){
54238 case UNQLITE_ACCESS_EXISTS:
54239 amode = F_OK;
54240 break;
54241 case UNQLITE_ACCESS_READWRITE:
54242 amode = W_OK|R_OK;
54243 break;
54244 case UNQLITE_ACCESS_READ:
54245 amode = R_OK;
54246 break;
54247 default:
54248 /* Can't happen */
54249 break;
54250 }
54251 *pResOut = (access(zPath, amode)==0);
54252 if( flags==UNQLITE_ACCESS_EXISTS && *pResOut ){
54253 struct stat buf;
54254 if( 0==stat(zPath, &buf) && buf.st_size==0 ){
54255 *pResOut = 0;
54256 }
54257 }
54258 return UNQLITE_OK;
54259}
54260/*
54261** Turn a relative pathname into a full pathname. The relative path
54262** is stored as a nul-terminated string in the buffer pointed to by
54263** zPath.
54264**
54265** zOut points to a buffer of at least unqlite_vfs.mxPathname bytes
54266** (in this case, MAX_PATHNAME bytes). The full-path is written to
54267** this buffer before returning.
54268*/
54269static int unixFullPathname(
54270 unqlite_vfs *pVfs, /* Pointer to vfs object */
54271 const char *zPath, /* Possibly relative input path */
54272 int nOut, /* Size of output buffer in bytes */
54273 char *zOut /* Output buffer */
54274){
54275 if( zPath[0]=='/' ){
54276 Systrcpy(zOut,(sxu32)nOut,zPath,0);
54277 SXUNUSED(pVfs);
54278 }else{
54279 sxu32 nCwd;
54280 zOut[nOut-1] = '\0';
54281 if( getcwd(zOut, nOut-1)==0 ){
54282 return UNQLITE_IOERR;
54283 }
54284 nCwd = SyStrlen(zOut);
54285 SyBufferFormat(&zOut[nCwd],(sxu32)nOut-nCwd,"/%s",zPath);
54286 }
54287 return UNQLITE_OK;
54288}
54289/*
54290 * Export the Unix Vfs.
54291 */
54292UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void)
54293{
54294 static const unqlite_vfs sUnixvfs = {
54295 "Unix", /* Vfs name */
54296 1, /* Vfs structure version */
54297 sizeof(unixFile), /* szOsFile */
54298 MAX_PATHNAME, /* mxPathName */
54299 unixOpen, /* xOpen */
54300 unixDelete, /* xDelete */
54301 unixAccess, /* xAccess */
54302 unixFullPathname, /* xFullPathname */
54303 0, /* xTmp */
54304 unixSleep, /* xSleep */
54305 unixCurrentTime, /* xCurrentTime */
54306 0, /* xGetLastError */
54307 };
54308 return &sUnixvfs;
54309}
54310
54311#endif /* __UNIXES__ */
54312
54313/*
54314 * ----------------------------------------------------------
54315 * File: os_win.c
54316 * MD5: ab70fb386c21b39a08b0eb776a8391ab
54317 * ----------------------------------------------------------
54318 */
54319/*
54320 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
54321 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
54322 * Version 1.1.6
54323 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
54324 * please contact Symisc Systems via:
54325 * legal@symisc.net
54326 * licensing@symisc.net
54327 * contact@symisc.net
54328 * or visit:
54329 * http://unqlite.org/licensing.html
54330 */
54331 /* $SymiscID: os_win.c v1.2 Win7 2012-11-10 12:10 devel <chm@symisc.net> $ */
54332#ifndef UNQLITE_AMALGAMATION
54333#include "unqliteInt.h"
54334#endif
54335/* Omit the whole layer from the build if compiling for platforms other than Windows */
54336#ifdef __WINNT__
54337/* This file contains code that is specific to windows. (Mostly SQLite3 source tree) */
54338#include <Windows.h>
54339/*
54340** Some microsoft compilers lack this definition.
54341*/
54342#ifndef INVALID_FILE_ATTRIBUTES
54343# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
54344#endif
54345/*
54346** WinCE lacks native support for file locking so we have to fake it
54347** with some code of our own.
54348*/
54349#ifdef __WIN_CE__
54350typedef struct winceLock {
54351 int nReaders; /* Number of reader locks obtained */
54352 BOOL bPending; /* Indicates a pending lock has been obtained */
54353 BOOL bReserved; /* Indicates a reserved lock has been obtained */
54354 BOOL bExclusive; /* Indicates an exclusive lock has been obtained */
54355} winceLock;
54356#define AreFileApisANSI() 1
54357#define FormatMessageW(a,b,c,d,e,f,g) 0
54358#endif
54359
54360/*
54361** The winFile structure is a subclass of unqlite_file* specific to the win32
54362** portability layer.
54363*/
54364typedef struct winFile winFile;
54365struct winFile {
54366 const unqlite_io_methods *pMethod; /*** Must be first ***/
54367 unqlite_vfs *pVfs; /* The VFS used to open this file */
54368 HANDLE h; /* Handle for accessing the file */
54369 sxu8 locktype; /* Type of lock currently held on this file */
54370 short sharedLockByte; /* Randomly chosen byte used as a shared lock */
54371 DWORD lastErrno; /* The Windows errno from the last I/O error */
54372 DWORD sectorSize; /* Sector size of the device file is on */
54373 int szChunk; /* Chunk size */
54374#ifdef __WIN_CE__
54375 WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
54376 HANDLE hMutex; /* Mutex used to control access to shared lock */
54377 HANDLE hShared; /* Shared memory segment used for locking */
54378 winceLock local; /* Locks obtained by this instance of winFile */
54379 winceLock *shared; /* Global shared lock memory for the file */
54380#endif
54381};
54382/*
54383** Convert a UTF-8 string to microsoft unicode (UTF-16?).
54384**
54385** Space to hold the returned string is obtained from HeapAlloc().
54386*/
54387static WCHAR *utf8ToUnicode(const char *zFilename){
54388 int nChar;
54389 WCHAR *zWideFilename;
54390
54391 nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0);
54392 zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nChar*sizeof(zWideFilename[0]) );
54393 if( zWideFilename==0 ){
54394 return 0;
54395 }
54396 nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
54397 if( nChar==0 ){
54398 HeapFree(GetProcessHeap(),0,zWideFilename);
54399 zWideFilename = 0;
54400 }
54401 return zWideFilename;
54402}
54403
54404/*
54405** Convert microsoft unicode to UTF-8. Space to hold the returned string is
54406** obtained from malloc().
54407*/
54408static char *unicodeToUtf8(const WCHAR *zWideFilename){
54409 int nByte;
54410 char *zFilename;
54411
54412 nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
54413 zFilename = (char *)HeapAlloc(GetProcessHeap(),0,nByte );
54414 if( zFilename==0 ){
54415 return 0;
54416 }
54417 nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte,
54418 0, 0);
54419 if( nByte == 0 ){
54420 HeapFree(GetProcessHeap(),0,zFilename);
54421 zFilename = 0;
54422 }
54423 return zFilename;
54424}
54425
54426/*
54427** Convert an ansi string to microsoft unicode, based on the
54428** current codepage settings for file apis.
54429**
54430** Space to hold the returned string is obtained
54431** from malloc.
54432*/
54433static WCHAR *mbcsToUnicode(const char *zFilename){
54434 int nByte;
54435 WCHAR *zMbcsFilename;
54436 int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
54437
54438 nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, 0,0)*sizeof(WCHAR);
54439 zMbcsFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zMbcsFilename[0]) );
54440 if( zMbcsFilename==0 ){
54441 return 0;
54442 }
54443 nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte);
54444 if( nByte==0 ){
54445 HeapFree(GetProcessHeap(),0,zMbcsFilename);
54446 zMbcsFilename = 0;
54447 }
54448 return zMbcsFilename;
54449}
54450/*
54451** Convert multibyte character string to UTF-8. Space to hold the
54452** returned string is obtained from malloc().
54453*/
54454char *unqlite_win32_mbcs_to_utf8(const char *zFilename){
54455 char *zFilenameUtf8;
54456 WCHAR *zTmpWide;
54457
54458 zTmpWide = mbcsToUnicode(zFilename);
54459 if( zTmpWide==0 ){
54460 return 0;
54461 }
54462 zFilenameUtf8 = unicodeToUtf8(zTmpWide);
54463 HeapFree(GetProcessHeap(),0,zTmpWide);
54464 return zFilenameUtf8;
54465}
54466/*
54467** Some microsoft compilers lack this definition.
54468*/
54469#ifndef INVALID_SET_FILE_POINTER
54470# define INVALID_SET_FILE_POINTER ((DWORD)-1)
54471#endif
54472
54473/*
54474** Move the current position of the file handle passed as the first
54475** argument to offset iOffset within the file. If successful, return 0.
54476** Otherwise, set pFile->lastErrno and return non-zero.
54477*/
54478static int seekWinFile(winFile *pFile, unqlite_int64 iOffset){
54479 LONG upperBits; /* Most sig. 32 bits of new offset */
54480 LONG lowerBits; /* Least sig. 32 bits of new offset */
54481 DWORD dwRet; /* Value returned by SetFilePointer() */
54482
54483 upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
54484 lowerBits = (LONG)(iOffset & 0xffffffff);
54485
54486 /* API oddity: If successful, SetFilePointer() returns a dword
54487 ** containing the lower 32-bits of the new file-offset. Or, if it fails,
54488 ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
54489 ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
54490 ** whether an error has actually occured, it is also necessary to call
54491 ** GetLastError().
54492 */
54493 dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
54494 if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
54495 pFile->lastErrno = GetLastError();
54496 return 1;
54497 }
54498 return 0;
54499}
54500/*
54501** Close a file.
54502**
54503** It is reported that an attempt to close a handle might sometimes
54504** fail. This is a very unreasonable result, but windows is notorious
54505** for being unreasonable so I do not doubt that it might happen. If
54506** the close fails, we pause for 100 milliseconds and try again. As
54507** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before
54508** giving up and returning an error.
54509*/
54510#define MX_CLOSE_ATTEMPT 3
54511static int winClose(unqlite_file *id)
54512{
54513 int rc, cnt = 0;
54514 winFile *pFile = (winFile*)id;
54515 do{
54516 rc = CloseHandle(pFile->h);
54517 }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
54518
54519 return rc ? UNQLITE_OK : UNQLITE_IOERR;
54520}
54521/*
54522** Read data from a file into a buffer. Return UNQLITE_OK if all
54523** bytes were read successfully and UNQLITE_IOERR if anything goes
54524** wrong.
54525*/
54526static int winRead(
54527 unqlite_file *id, /* File to read from */
54528 void *pBuf, /* Write content into this buffer */
54529 unqlite_int64 amt, /* Number of bytes to read */
54530 unqlite_int64 offset /* Begin reading at this offset */
54531){
54532 winFile *pFile = (winFile*)id; /* file handle */
54533 DWORD nRead; /* Number of bytes actually read from file */
54534
54535 if( seekWinFile(pFile, offset) ){
54536 return UNQLITE_FULL;
54537 }
54538 if( !ReadFile(pFile->h, pBuf, (DWORD)amt, &nRead, 0) ){
54539 pFile->lastErrno = GetLastError();
54540 return UNQLITE_IOERR;
54541 }
54542 if( nRead<(DWORD)amt ){
54543 /* Unread parts of the buffer must be zero-filled */
54544 SyZero(&((char*)pBuf)[nRead],(sxu32)(amt-nRead));
54545 return UNQLITE_IOERR;
54546 }
54547
54548 return UNQLITE_OK;
54549}
54550
54551/*
54552** Write data from a buffer into a file. Return UNQLITE_OK on success
54553** or some other error code on failure.
54554*/
54555static int winWrite(
54556 unqlite_file *id, /* File to write into */
54557 const void *pBuf, /* The bytes to be written */
54558 unqlite_int64 amt, /* Number of bytes to write */
54559 unqlite_int64 offset /* Offset into the file to begin writing at */
54560){
54561 int rc; /* True if error has occured, else false */
54562 winFile *pFile = (winFile*)id; /* File handle */
54563
54564 rc = seekWinFile(pFile, offset);
54565 if( rc==0 ){
54566 sxu8 *aRem = (sxu8 *)pBuf; /* Data yet to be written */
54567 unqlite_int64 nRem = amt; /* Number of bytes yet to be written */
54568 DWORD nWrite; /* Bytes written by each WriteFile() call */
54569
54570 while( nRem>0 && WriteFile(pFile->h, aRem, (DWORD)nRem, &nWrite, 0) && nWrite>0 ){
54571 aRem += nWrite;
54572 nRem -= nWrite;
54573 }
54574 if( nRem>0 ){
54575 pFile->lastErrno = GetLastError();
54576 rc = 1;
54577 }
54578 }
54579 if( rc ){
54580 if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
54581 return UNQLITE_FULL;
54582 }
54583 return UNQLITE_IOERR;
54584 }
54585 return UNQLITE_OK;
54586}
54587
54588/*
54589** Truncate an open file to a specified size
54590*/
54591static int winTruncate(unqlite_file *id, unqlite_int64 nByte){
54592 winFile *pFile = (winFile*)id; /* File handle object */
54593 int rc = UNQLITE_OK; /* Return code for this function */
54594
54595
54596 /* If the user has configured a chunk-size for this file, truncate the
54597 ** file so that it consists of an integer number of chunks (i.e. the
54598 ** actual file size after the operation may be larger than the requested
54599 ** size).
54600 */
54601 if( pFile->szChunk ){
54602 nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
54603 }
54604
54605 /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
54606 if( seekWinFile(pFile, nByte) ){
54607 rc = UNQLITE_IOERR;
54608 }else if( 0==SetEndOfFile(pFile->h) ){
54609 pFile->lastErrno = GetLastError();
54610 rc = UNQLITE_IOERR;
54611 }
54612 return rc;
54613}
54614/*
54615** Make sure all writes to a particular file are committed to disk.
54616*/
54617static int winSync(unqlite_file *id, int flags){
54618 winFile *pFile = (winFile*)id;
54619 SXUNUSED(flags); /* MSVC warning */
54620 if( FlushFileBuffers(pFile->h) ){
54621 return UNQLITE_OK;
54622 }else{
54623 pFile->lastErrno = GetLastError();
54624 return UNQLITE_IOERR;
54625 }
54626}
54627/*
54628** Determine the current size of a file in bytes
54629*/
54630static int winFileSize(unqlite_file *id, unqlite_int64 *pSize){
54631 DWORD upperBits;
54632 DWORD lowerBits;
54633 winFile *pFile = (winFile*)id;
54634 DWORD error;
54635 lowerBits = GetFileSize(pFile->h, &upperBits);
54636 if( (lowerBits == INVALID_FILE_SIZE)
54637 && ((error = GetLastError()) != NO_ERROR) )
54638 {
54639 pFile->lastErrno = error;
54640 return UNQLITE_IOERR;
54641 }
54642 *pSize = (((unqlite_int64)upperBits)<<32) + lowerBits;
54643 return UNQLITE_OK;
54644}
54645/*
54646** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems.
54647*/
54648#ifndef LOCKFILE_FAIL_IMMEDIATELY
54649# define LOCKFILE_FAIL_IMMEDIATELY 1
54650#endif
54651
54652/*
54653** Acquire a reader lock.
54654*/
54655static int getReadLock(winFile *pFile){
54656 int res;
54657 OVERLAPPED ovlp;
54658 ovlp.Offset = SHARED_FIRST;
54659 ovlp.OffsetHigh = 0;
54660 ovlp.hEvent = 0;
54661 res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,0, SHARED_SIZE, 0, &ovlp);
54662 if( res == 0 ){
54663 pFile->lastErrno = GetLastError();
54664 }
54665 return res;
54666}
54667/*
54668** Undo a readlock
54669*/
54670static int unlockReadLock(winFile *pFile){
54671 int res;
54672 res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
54673 if( res == 0 ){
54674 pFile->lastErrno = GetLastError();
54675 }
54676 return res;
54677}
54678/*
54679** Lock the file with the lock specified by parameter locktype - one
54680** of the following:
54681**
54682** (1) SHARED_LOCK
54683** (2) RESERVED_LOCK
54684** (3) PENDING_LOCK
54685** (4) EXCLUSIVE_LOCK
54686**
54687** Sometimes when requesting one lock state, additional lock states
54688** are inserted in between. The locking might fail on one of the later
54689** transitions leaving the lock state different from what it started but
54690** still short of its goal. The following chart shows the allowed
54691** transitions and the inserted intermediate states:
54692**
54693** UNLOCKED -> SHARED
54694** SHARED -> RESERVED
54695** SHARED -> (PENDING) -> EXCLUSIVE
54696** RESERVED -> (PENDING) -> EXCLUSIVE
54697** PENDING -> EXCLUSIVE
54698**
54699** This routine will only increase a lock. The winUnlock() routine
54700** erases all locks at once and returns us immediately to locking level 0.
54701** It is not possible to lower the locking level one step at a time. You
54702** must go straight to locking level 0.
54703*/
54704static int winLock(unqlite_file *id, int locktype){
54705 int rc = UNQLITE_OK; /* Return code from subroutines */
54706 int res = 1; /* Result of a windows lock call */
54707 int newLocktype; /* Set pFile->locktype to this value before exiting */
54708 int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
54709 winFile *pFile = (winFile*)id;
54710 DWORD error = NO_ERROR;
54711
54712 /* If there is already a lock of this type or more restrictive on the
54713 ** OsFile, do nothing.
54714 */
54715 if( pFile->locktype>=locktype ){
54716 return UNQLITE_OK;
54717 }
54718
54719 /* Make sure the locking sequence is correct
54720 assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
54721 assert( locktype!=PENDING_LOCK );
54722 assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
54723 */
54724 /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
54725 ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
54726 ** the PENDING_LOCK byte is temporary.
54727 */
54728 newLocktype = pFile->locktype;
54729 if( (pFile->locktype==NO_LOCK)
54730 || ( (locktype==EXCLUSIVE_LOCK)
54731 && (pFile->locktype==RESERVED_LOCK))
54732 ){
54733 int cnt = 3;
54734 while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){
54735 /* Try 3 times to get the pending lock. The pending lock might be
54736 ** held by another reader process who will release it momentarily.
54737 */
54738 Sleep(1);
54739 }
54740 gotPendingLock = res;
54741 if( !res ){
54742 error = GetLastError();
54743 }
54744 }
54745
54746 /* Acquire a shared lock
54747 */
54748 if( locktype==SHARED_LOCK && res ){
54749 /* assert( pFile->locktype==NO_LOCK ); */
54750 res = getReadLock(pFile);
54751 if( res ){
54752 newLocktype = SHARED_LOCK;
54753 }else{
54754 error = GetLastError();
54755 }
54756 }
54757
54758 /* Acquire a RESERVED lock
54759 */
54760 if( locktype==RESERVED_LOCK && res ){
54761 /* assert( pFile->locktype==SHARED_LOCK ); */
54762 res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
54763 if( res ){
54764 newLocktype = RESERVED_LOCK;
54765 }else{
54766 error = GetLastError();
54767 }
54768 }
54769
54770 /* Acquire a PENDING lock
54771 */
54772 if( locktype==EXCLUSIVE_LOCK && res ){
54773 newLocktype = PENDING_LOCK;
54774 gotPendingLock = 0;
54775 }
54776
54777 /* Acquire an EXCLUSIVE lock
54778 */
54779 if( locktype==EXCLUSIVE_LOCK && res ){
54780 /* assert( pFile->locktype>=SHARED_LOCK ); */
54781 res = unlockReadLock(pFile);
54782 res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
54783 if( res ){
54784 newLocktype = EXCLUSIVE_LOCK;
54785 }else{
54786 error = GetLastError();
54787 getReadLock(pFile);
54788 }
54789 }
54790
54791 /* If we are holding a PENDING lock that ought to be released, then
54792 ** release it now.
54793 */
54794 if( gotPendingLock && locktype==SHARED_LOCK ){
54795 UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
54796 }
54797
54798 /* Update the state of the lock has held in the file descriptor then
54799 ** return the appropriate result code.
54800 */
54801 if( res ){
54802 rc = UNQLITE_OK;
54803 }else{
54804 pFile->lastErrno = error;
54805 rc = UNQLITE_BUSY;
54806 }
54807 pFile->locktype = (sxu8)newLocktype;
54808 return rc;
54809}
54810/*
54811** This routine checks if there is a RESERVED lock held on the specified
54812** file by this or any other process. If such a lock is held, return
54813** non-zero, otherwise zero.
54814*/
54815static int winCheckReservedLock(unqlite_file *id, int *pResOut){
54816 int rc;
54817 winFile *pFile = (winFile*)id;
54818 if( pFile->locktype>=RESERVED_LOCK ){
54819 rc = 1;
54820 }else{
54821 rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
54822 if( rc ){
54823 UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
54824 }
54825 rc = !rc;
54826 }
54827 *pResOut = rc;
54828 return UNQLITE_OK;
54829}
54830/*
54831** Lower the locking level on file descriptor id to locktype. locktype
54832** must be either NO_LOCK or SHARED_LOCK.
54833**
54834** If the locking level of the file descriptor is already at or below
54835** the requested locking level, this routine is a no-op.
54836**
54837** It is not possible for this routine to fail if the second argument
54838** is NO_LOCK. If the second argument is SHARED_LOCK then this routine
54839** might return UNQLITE_IOERR;
54840*/
54841static int winUnlock(unqlite_file *id, int locktype){
54842 int type;
54843 winFile *pFile = (winFile*)id;
54844 int rc = UNQLITE_OK;
54845
54846 type = pFile->locktype;
54847 if( type>=EXCLUSIVE_LOCK ){
54848 UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
54849 if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
54850 /* This should never happen. We should always be able to
54851 ** reacquire the read lock */
54852 rc = UNQLITE_IOERR;
54853 }
54854 }
54855 if( type>=RESERVED_LOCK ){
54856 UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
54857 }
54858 if( locktype==NO_LOCK && type>=SHARED_LOCK ){
54859 unlockReadLock(pFile);
54860 }
54861 if( type>=PENDING_LOCK ){
54862 UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
54863 }
54864 pFile->locktype = (sxu8)locktype;
54865 return rc;
54866}
54867/*
54868** Return the sector size in bytes of the underlying block device for
54869** the specified file. This is almost always 512 bytes, but may be
54870** larger for some devices.
54871**
54872*/
54873static int winSectorSize(unqlite_file *id){
54874 return (int)(((winFile*)id)->sectorSize);
54875}
54876/*
54877** This vector defines all the methods that can operate on an
54878** unqlite_file for Windows systems.
54879*/
54880static const unqlite_io_methods winIoMethod = {
54881 1, /* iVersion */
54882 winClose, /* xClose */
54883 winRead, /* xRead */
54884 winWrite, /* xWrite */
54885 winTruncate, /* xTruncate */
54886 winSync, /* xSync */
54887 winFileSize, /* xFileSize */
54888 winLock, /* xLock */
54889 winUnlock, /* xUnlock */
54890 winCheckReservedLock, /* xCheckReservedLock */
54891 winSectorSize, /* xSectorSize */
54892};
54893/*
54894 * Windows VFS Methods.
54895 */
54896/*
54897** Convert a UTF-8 filename into whatever form the underlying
54898** operating system wants filenames in. Space to hold the result
54899** is obtained from malloc and must be freed by the calling
54900** function.
54901*/
54902static void *convertUtf8Filename(const char *zFilename)
54903{
54904 void *zConverted;
54905 zConverted = utf8ToUnicode(zFilename);
54906 /* caller will handle out of memory */
54907 return zConverted;
54908}
54909/*
54910** Delete the named file.
54911**
54912** Note that windows does not allow a file to be deleted if some other
54913** process has it open. Sometimes a virus scanner or indexing program
54914** will open a journal file shortly after it is created in order to do
54915** whatever it does. While this other process is holding the
54916** file open, we will be unable to delete it. To work around this
54917** problem, we delay 100 milliseconds and try to delete again. Up
54918** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
54919** up and returning an error.
54920*/
54921#define MX_DELETION_ATTEMPTS 5
54922static int winDelete(
54923 unqlite_vfs *pVfs, /* Not used on win32 */
54924 const char *zFilename, /* Name of file to delete */
54925 int syncDir /* Not used on win32 */
54926){
54927 int cnt = 0;
54928 DWORD rc;
54929 DWORD error = 0;
54930 void *zConverted;
54931 zConverted = convertUtf8Filename(zFilename);
54932 if( zConverted==0 ){
54933 SXUNUSED(pVfs);
54934 SXUNUSED(syncDir);
54935 return UNQLITE_NOMEM;
54936 }
54937 do{
54938 DeleteFileW((LPCWSTR)zConverted);
54939 }while( ( ((rc = GetFileAttributesW((LPCWSTR)zConverted)) != INVALID_FILE_ATTRIBUTES)
54940 || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
54941 && (++cnt < MX_DELETION_ATTEMPTS)
54942 && (Sleep(100), 1)
54943 );
54944 HeapFree(GetProcessHeap(),0,zConverted);
54945
54946 return ( (rc == INVALID_FILE_ATTRIBUTES)
54947 && (error == ERROR_FILE_NOT_FOUND)) ? UNQLITE_OK : UNQLITE_IOERR;
54948}
54949/*
54950** Check the existance and status of a file.
54951*/
54952static int winAccess(
54953 unqlite_vfs *pVfs, /* Not used */
54954 const char *zFilename, /* Name of file to check */
54955 int flags, /* Type of test to make on this file */
54956 int *pResOut /* OUT: Result */
54957){
54958 WIN32_FILE_ATTRIBUTE_DATA sAttrData;
54959 DWORD attr;
54960 int rc = 0;
54961 void *zConverted;
54962 SXUNUSED(pVfs);
54963
54964 zConverted = convertUtf8Filename(zFilename);
54965 if( zConverted==0 ){
54966 return UNQLITE_NOMEM;
54967 }
54968 SyZero(&sAttrData,sizeof(sAttrData));
54969 if( GetFileAttributesExW((WCHAR*)zConverted,
54970 GetFileExInfoStandard,
54971 &sAttrData) ){
54972 /* For an UNQLITE_ACCESS_EXISTS query, treat a zero-length file
54973 ** as if it does not exist.
54974 */
54975 if( flags==UNQLITE_ACCESS_EXISTS
54976 && sAttrData.nFileSizeHigh==0
54977 && sAttrData.nFileSizeLow==0 ){
54978 attr = INVALID_FILE_ATTRIBUTES;
54979 }else{
54980 attr = sAttrData.dwFileAttributes;
54981 }
54982 }else{
54983 if( GetLastError()!=ERROR_FILE_NOT_FOUND ){
54984 HeapFree(GetProcessHeap(),0,zConverted);
54985 return UNQLITE_IOERR;
54986 }else{
54987 attr = INVALID_FILE_ATTRIBUTES;
54988 }
54989 }
54990 HeapFree(GetProcessHeap(),0,zConverted);
54991 switch( flags ){
54992 case UNQLITE_ACCESS_READWRITE:
54993 rc = (attr & FILE_ATTRIBUTE_READONLY)==0;
54994 break;
54995 case UNQLITE_ACCESS_READ:
54996 case UNQLITE_ACCESS_EXISTS:
54997 default:
54998 rc = attr!=INVALID_FILE_ATTRIBUTES;
54999 break;
55000 }
55001 *pResOut = rc;
55002 return UNQLITE_OK;
55003}
55004/*
55005** Turn a relative pathname into a full pathname. Write the full
55006** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
55007** bytes in size.
55008*/
55009static int winFullPathname(
55010 unqlite_vfs *pVfs, /* Pointer to vfs object */
55011 const char *zRelative, /* Possibly relative input path */
55012 int nFull, /* Size of output buffer in bytes */
55013 char *zFull /* Output buffer */
55014){
55015 int nByte;
55016 void *zConverted;
55017 WCHAR *zTemp;
55018 char *zOut;
55019 SXUNUSED(nFull);
55020 zConverted = convertUtf8Filename(zRelative);
55021 if( zConverted == 0 ){
55022 return UNQLITE_NOMEM;
55023 }
55024 nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
55025 zTemp = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zTemp[0]) );
55026 if( zTemp==0 ){
55027 HeapFree(GetProcessHeap(),0,zConverted);
55028 return UNQLITE_NOMEM;
55029 }
55030 GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
55031 HeapFree(GetProcessHeap(),0,zConverted);
55032 zOut = unicodeToUtf8(zTemp);
55033 HeapFree(GetProcessHeap(),0,zTemp);
55034 if( zOut == 0 ){
55035 return UNQLITE_NOMEM;
55036 }
55037 Systrcpy(zFull,(sxu32)pVfs->mxPathname,zOut,0);
55038 HeapFree(GetProcessHeap(),0,zOut);
55039 return UNQLITE_OK;
55040}
55041/*
55042** Get the sector size of the device used to store
55043** file.
55044*/
55045static int getSectorSize(
55046 unqlite_vfs *pVfs,
55047 const char *zRelative /* UTF-8 file name */
55048){
55049 DWORD bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE;
55050 char zFullpath[MAX_PATH+1];
55051 int rc;
55052 DWORD dwRet = 0;
55053 DWORD dwDummy;
55054 /*
55055 ** We need to get the full path name of the file
55056 ** to get the drive letter to look up the sector
55057 ** size.
55058 */
55059 rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath);
55060 if( rc == UNQLITE_OK )
55061 {
55062 void *zConverted = convertUtf8Filename(zFullpath);
55063 if( zConverted ){
55064 /* trim path to just drive reference */
55065 WCHAR *p = (WCHAR *)zConverted;
55066 for(;*p;p++){
55067 if( *p == '\\' ){
55068 *p = '\0';
55069 break;
55070 }
55071 }
55072 dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted,
55073 &dwDummy,
55074 &bytesPerSector,
55075 &dwDummy,
55076 &dwDummy);
55077 HeapFree(GetProcessHeap(),0,zConverted);
55078 }
55079 if( !dwRet ){
55080 bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE;
55081 }
55082 }
55083 return (int) bytesPerSector;
55084}
55085/*
55086** Sleep for a little while. Return the amount of time slept.
55087*/
55088static int winSleep(unqlite_vfs *pVfs, int microsec){
55089 Sleep((microsec+999)/1000);
55090 SXUNUSED(pVfs);
55091 return ((microsec+999)/1000)*1000;
55092}
55093/*
55094 * Export the current system time.
55095 */
55096static int winCurrentTime(unqlite_vfs *pVfs,Sytm *pOut)
55097{
55098 SYSTEMTIME sSys;
55099 SXUNUSED(pVfs);
55100 GetSystemTime(&sSys);
55101 SYSTEMTIME_TO_SYTM(&sSys,pOut);
55102 return UNQLITE_OK;
55103}
55104/*
55105** The idea is that this function works like a combination of
55106** GetLastError() and FormatMessage() on windows (or errno and
55107** strerror_r() on unix). After an error is returned by an OS
55108** function, UnQLite calls this function with zBuf pointing to
55109** a buffer of nBuf bytes. The OS layer should populate the
55110** buffer with a nul-terminated UTF-8 encoded error message
55111** describing the last IO error to have occurred within the calling
55112** thread.
55113**
55114** If the error message is too large for the supplied buffer,
55115** it should be truncated. The return value of xGetLastError
55116** is zero if the error message fits in the buffer, or non-zero
55117** otherwise (if the message was truncated). If non-zero is returned,
55118** then it is not necessary to include the nul-terminator character
55119** in the output buffer.
55120*/
55121static int winGetLastError(unqlite_vfs *pVfs, int nBuf, char *zBuf)
55122{
55123 /* FormatMessage returns 0 on failure. Otherwise it
55124 ** returns the number of TCHARs written to the output
55125 ** buffer, excluding the terminating null char.
55126 */
55127 DWORD error = GetLastError();
55128 WCHAR *zTempWide = 0;
55129 DWORD dwLen;
55130 char *zOut = 0;
55131
55132 SXUNUSED(pVfs);
55133 dwLen = FormatMessageW(
55134 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
55135 0,
55136 error,
55137 0,
55138 (LPWSTR) &zTempWide,
55139 0,
55140 0
55141 );
55142 if( dwLen > 0 ){
55143 /* allocate a buffer and convert to UTF8 */
55144 zOut = unicodeToUtf8(zTempWide);
55145 /* free the system buffer allocated by FormatMessage */
55146 LocalFree(zTempWide);
55147 }
55148 if( 0 == dwLen ){
55149 Systrcpy(zBuf,(sxu32)nBuf,"OS Error",sizeof("OS Error")-1);
55150 }else{
55151 /* copy a maximum of nBuf chars to output buffer */
55152 Systrcpy(zBuf,(sxu32)nBuf,zOut,0 /* Compute input length automatically */);
55153 /* free the UTF8 buffer */
55154 HeapFree(GetProcessHeap(),0,zOut);
55155 }
55156 return 0;
55157}
55158/*
55159** Open a file.
55160*/
55161static int winOpen(
55162 unqlite_vfs *pVfs, /* Not used */
55163 const char *zName, /* Name of the file (UTF-8) */
55164 unqlite_file *id, /* Write the UnQLite file handle here */
55165 unsigned int flags /* Open mode flags */
55166){
55167 HANDLE h;
55168 DWORD dwDesiredAccess;
55169 DWORD dwShareMode;
55170 DWORD dwCreationDisposition;
55171 DWORD dwFlagsAndAttributes = 0;
55172 winFile *pFile = (winFile*)id;
55173 void *zConverted; /* Filename in OS encoding */
55174 const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
55175 int isExclusive = (flags & UNQLITE_OPEN_EXCLUSIVE);
55176 int isDelete = (flags & UNQLITE_OPEN_TEMP_DB);
55177 int isCreate = (flags & UNQLITE_OPEN_CREATE);
55178 int isReadWrite = (flags & UNQLITE_OPEN_READWRITE);
55179
55180 pFile->h = INVALID_HANDLE_VALUE;
55181 /* Convert the filename to the system encoding. */
55182 zConverted = convertUtf8Filename(zUtf8Name);
55183 if( zConverted==0 ){
55184 return UNQLITE_NOMEM;
55185 }
55186 if( isReadWrite ){
55187 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
55188 }else{
55189 dwDesiredAccess = GENERIC_READ;
55190 }
55191 /* UNQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
55192 ** created.
55193 */
55194 if( isExclusive ){
55195 /* Creates a new file, only if it does not already exist. */
55196 /* If the file exists, it fails. */
55197 dwCreationDisposition = CREATE_NEW;
55198 }else if( isCreate ){
55199 /* Open existing file, or create if it doesn't exist */
55200 dwCreationDisposition = OPEN_ALWAYS;
55201 }else{
55202 /* Opens a file, only if it exists. */
55203 dwCreationDisposition = OPEN_EXISTING;
55204 }
55205
55206 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
55207
55208 if( isDelete ){
55209 dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
55210 | FILE_ATTRIBUTE_HIDDEN
55211 | FILE_FLAG_DELETE_ON_CLOSE;
55212 }else{
55213 dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
55214 }
55215 h = CreateFileW((WCHAR*)zConverted,
55216 dwDesiredAccess,
55217 dwShareMode,
55218 NULL,
55219 dwCreationDisposition,
55220 dwFlagsAndAttributes,
55221 NULL
55222 );
55223 if( h==INVALID_HANDLE_VALUE ){
55224 pFile->lastErrno = GetLastError();
55225 HeapFree(GetProcessHeap(),0,zConverted);
55226 return UNQLITE_IOERR;
55227 }
55228 SyZero(pFile,sizeof(*pFile));
55229 pFile->pMethod = &winIoMethod;
55230 pFile->h = h;
55231 pFile->lastErrno = NO_ERROR;
55232 pFile->pVfs = pVfs;
55233 pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
55234 HeapFree(GetProcessHeap(),0,zConverted);
55235 return UNQLITE_OK;
55236}
55237/*
55238 * Export the Windows Vfs.
55239 */
55240UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void)
55241{
55242 static const unqlite_vfs sWinvfs = {
55243 "Windows", /* Vfs name */
55244 1, /* Vfs structure version */
55245 sizeof(winFile), /* szOsFile */
55246 MAX_PATH, /* mxPathName */
55247 winOpen, /* xOpen */
55248 winDelete, /* xDelete */
55249 winAccess, /* xAccess */
55250 winFullPathname, /* xFullPathname */
55251 0, /* xTmp */
55252 winSleep, /* xSleep */
55253 winCurrentTime, /* xCurrentTime */
55254 winGetLastError, /* xGetLastError */
55255 };
55256 return &sWinvfs;
55257}
55258#endif /* __WINNT__ */
55259/*
55260 * ----------------------------------------------------------
55261 * File: pager.c
55262 * MD5: 57ff77347402fbf6892af589ff8a5df7
55263 * ----------------------------------------------------------
55264 */
55265/*
55266 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
55267 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
55268 * Version 1.1.6
55269 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
55270 * please contact Symisc Systems via:
55271 * legal@symisc.net
55272 * licensing@symisc.net
55273 * contact@symisc.net
55274 * or visit:
55275 * http://unqlite.org/licensing.html
55276 */
55277 /* $SymiscID: pager.c v1.1 Win7 2012-11-29 03:46 stable <chm@symisc.net> $ */
55278#ifndef UNQLITE_AMALGAMATION
55279#include "unqliteInt.h"
55280#endif
55281/*
55282** This file implements the pager and the transaction manager for UnQLite (Mostly inspired from the SQLite3 Source tree).
55283**
55284** The Pager.eState variable stores the current 'state' of a pager. A
55285** pager may be in any one of the seven states shown in the following
55286** state diagram.
55287**
55288** OPEN <------+------+
55289** | | |
55290** V | |
55291** +---------> READER-------+ |
55292** | | |
55293** | V |
55294** |<-------WRITER_LOCKED--------->|
55295** | | |
55296** | V |
55297** |<------WRITER_CACHEMOD-------->|
55298** | | |
55299** | V |
55300** |<-------WRITER_DBMOD---------->|
55301** | | |
55302** | V |
55303** +<------WRITER_FINISHED-------->+
55304**
55305** OPEN:
55306**
55307** The pager starts up in this state. Nothing is guaranteed in this
55308** state - the file may or may not be locked and the database size is
55309** unknown. The database may not be read or written.
55310**
55311** * No read or write transaction is active.
55312** * Any lock, or no lock at all, may be held on the database file.
55313** * The dbSize and dbOrigSize variables may not be trusted.
55314**
55315** READER:
55316**
55317** In this state all the requirements for reading the database in
55318** rollback mode are met. Unless the pager is (or recently
55319** was) in exclusive-locking mode, a user-level read transaction is
55320** open. The database size is known in this state.
55321**
55322** * A read transaction may be active (but a write-transaction cannot).
55323** * A SHARED or greater lock is held on the database file.
55324** * The dbSize variable may be trusted (even if a user-level read
55325** transaction is not active). The dbOrigSize variables
55326** may not be trusted at this point.
55327** * Even if a read-transaction is not open, it is guaranteed that
55328** there is no hot-journal in the file-system.
55329**
55330** WRITER_LOCKED:
55331**
55332** The pager moves to this state from READER when a write-transaction
55333** is first opened on the database. In WRITER_LOCKED state, all locks
55334** required to start a write-transaction are held, but no actual
55335** modifications to the cache or database have taken place.
55336**
55337** In rollback mode, a RESERVED or (if the transaction was opened with
55338** EXCLUSIVE flag) EXCLUSIVE lock is obtained on the database file when
55339** moving to this state, but the journal file is not written to or opened
55340** to in this state. If the transaction is committed or rolled back while
55341** in WRITER_LOCKED state, all that is required is to unlock the database
55342** file.
55343**
55344** * A write transaction is active.
55345** * If the connection is open in rollback-mode, a RESERVED or greater
55346** lock is held on the database file.
55347** * The dbSize and dbOrigSize variables are all valid.
55348** * The contents of the pager cache have not been modified.
55349** * The journal file may or may not be open.
55350** * Nothing (not even the first header) has been written to the journal.
55351**
55352** WRITER_CACHEMOD:
55353**
55354** A pager moves from WRITER_LOCKED state to this state when a page is
55355** first modified by the upper layer. In rollback mode the journal file
55356** is opened (if it is not already open) and a header written to the
55357** start of it. The database file on disk has not been modified.
55358**
55359** * A write transaction is active.
55360** * A RESERVED or greater lock is held on the database file.
55361** * The journal file is open and the first header has been written
55362** to it, but the header has not been synced to disk.
55363** * The contents of the page cache have been modified.
55364**
55365** WRITER_DBMOD:
55366**
55367** The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state
55368** when it modifies the contents of the database file.
55369**
55370** * A write transaction is active.
55371** * An EXCLUSIVE or greater lock is held on the database file.
55372** * The journal file is open and the first header has been written
55373** and synced to disk.
55374** * The contents of the page cache have been modified (and possibly
55375** written to disk).
55376**
55377** WRITER_FINISHED:
55378**
55379** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD
55380** state after the entire transaction has been successfully written into the
55381** database file. In this state the transaction may be committed simply
55382** by finalizing the journal file. Once in WRITER_FINISHED state, it is
55383** not possible to modify the database further. At this point, the upper
55384** layer must either commit or rollback the transaction.
55385**
55386** * A write transaction is active.
55387** * An EXCLUSIVE or greater lock is held on the database file.
55388** * All writing and syncing of journal and database data has finished.
55389** If no error occured, all that remains is to finalize the journal to
55390** commit the transaction. If an error did occur, the caller will need
55391** to rollback the transaction.
55392**
55393**
55394*/
55395#define PAGER_OPEN 0
55396#define PAGER_READER 1
55397#define PAGER_WRITER_LOCKED 2
55398#define PAGER_WRITER_CACHEMOD 3
55399#define PAGER_WRITER_DBMOD 4
55400#define PAGER_WRITER_FINISHED 5
55401/*
55402** Journal files begin with the following magic string. The data
55403** was obtained from /dev/random. It is used only as a sanity check.
55404**
55405** NOTE: These values must be different from the one used by SQLite3
55406** to avoid journal file collision.
55407**
55408*/
55409static const unsigned char aJournalMagic[] = {
55410 0xa6, 0xe8, 0xcd, 0x2b, 0x1c, 0x92, 0xdb, 0x9f,
55411};
55412/*
55413** The journal header size for this pager. This is usually the same
55414** size as a single disk sector. See also setSectorSize().
55415*/
55416#define JOURNAL_HDR_SZ(pPager) (pPager->iSectorSize)
55417/*
55418 * Database page handle.
55419 * Each raw disk page is represented in memory by an instance
55420 * of the following structure.
55421 */
55422typedef struct Page Page;
55423struct Page {
55424 /* Must correspond to unqlite_page */
55425 unsigned char *zData; /* Content of this page */
55426 void *pUserData; /* Extra content */
55427 pgno pgno; /* Page number for this page */
55428 /**********************************************************************
55429 ** Elements above are public. All that follows is private to pcache.c
55430 ** and should not be accessed by other modules.
55431 */
55432 Pager *pPager; /* The pager this page is part of */
55433 int flags; /* Page flags defined below */
55434 int nRef; /* Number of users of this page */
55435 Page *pNext, *pPrev; /* A list of all pages */
55436 Page *pDirtyNext; /* Next element in list of dirty pages */
55437 Page *pDirtyPrev; /* Previous element in list of dirty pages */
55438 Page *pNextCollide,*pPrevCollide; /* Collission chain */
55439 Page *pNextHot,*pPrevHot; /* Hot dirty pages chain */
55440};
55441/* Bit values for Page.flags */
55442#define PAGE_DIRTY 0x002 /* Page has changed */
55443#define PAGE_NEED_SYNC 0x004 /* fsync the rollback journal before
55444 ** writing this page to the database */
55445#define PAGE_DONT_WRITE 0x008 /* Dont write page content to disk */
55446#define PAGE_NEED_READ 0x010 /* Content is unread */
55447#define PAGE_IN_JOURNAL 0x020 /* Page written to the journal */
55448#define PAGE_HOT_DIRTY 0x040 /* Hot dirty page */
55449#define PAGE_DONT_MAKE_HOT 0x080 /* Dont make this page Hot. In other words,
55450 * do not link it to the hot dirty list.
55451 */
55452/*
55453 * Each active database pager is represented by an instance of
55454 * the following structure.
55455 */
55456struct Pager
55457{
55458 SyMemBackend *pAllocator; /* Memory backend */
55459 unqlite *pDb; /* DB handle that own this instance */
55460 unqlite_kv_engine *pEngine; /* Underlying KV storage engine */
55461 char *zFilename; /* Name of the database file */
55462 char *zJournal; /* Name of the journal file */
55463 unqlite_vfs *pVfs; /* Underlying virtual file system */
55464 unqlite_file *pfd,*pjfd; /* File descriptors for database and journal */
55465 pgno dbSize; /* Number of pages in the file */
55466 pgno dbOrigSize; /* dbSize before the current change */
55467 sxi64 dbByteSize; /* Database size in bytes */
55468 void *pMmap; /* Read-only Memory view (mmap) of the whole file if requested (UNQLITE_OPEN_MMAP). */
55469 sxu32 nRec; /* Number of pages written to the journal */
55470 SyPRNGCtx sPrng; /* PRNG Context */
55471 sxu32 cksumInit; /* Quasi-random value added to every checksum */
55472 sxu32 iOpenFlags; /* Flag passed to unqlite_open() after processing */
55473 sxi64 iJournalOfft; /* Journal offset we are reading from */
55474 int (*xBusyHandler)(void *); /* Busy handler */
55475 void *pBusyHandlerArg; /* First arg to xBusyHandler() */
55476 void (*xPageUnpin)(void *); /* Page Unpin callback */
55477 void (*xPageReload)(void *); /* Page Reload callback */
55478 Bitvec *pVec; /* Bitmap */
55479 Page *pHeader; /* Page one of the database (Unqlite header) */
55480 Sytm tmCreate; /* Database creation time */
55481 SyString sKv; /* Underlying Key/Value storage engine name */
55482 int iState; /* Pager state */
55483 int iLock; /* Lock state */
55484 sxi32 iFlags; /* Control flags (see below) */
55485 int is_mem; /* True for an in-memory database */
55486 int is_rdonly; /* True for a read-only database */
55487 int no_jrnl; /* TRUE to omit journaling */
55488 int iPageSize; /* Page size in bytes (default 4K) */
55489 int iSectorSize; /* Size of a single sector on disk */
55490 unsigned char *zTmpPage; /* Temporary page */
55491 Page *pFirstDirty; /* First dirty pages */
55492 Page *pDirty; /* Transient list of dirty pages */
55493 Page *pAll; /* List of all pages */
55494 Page *pHotDirty; /* List of hot dirty pages */
55495 Page *pFirstHot; /* First hot dirty page */
55496 sxu32 nHot; /* Total number of hot dirty pages */
55497 Page **apHash; /* Page table */
55498 sxu32 nSize; /* apHash[] size: Must be a power of two */
55499 sxu32 nPage; /* Total number of page loaded in memory */
55500 sxu32 nCacheMax; /* Maximum page to cache*/
55501};
55502/* Control flags */
55503#define PAGER_CTRL_COMMIT_ERR 0x001 /* Commit error */
55504#define PAGER_CTRL_DIRTY_COMMIT 0x002 /* Dirty commit has been applied */
55505/*
55506** Read a 32-bit integer from the given file descriptor.
55507** All values are stored on disk as big-endian.
55508*/
55509static int ReadInt32(unqlite_file *pFd,sxu32 *pOut,sxi64 iOfft)
55510{
55511 unsigned char zBuf[4];
55512 int rc;
55513 rc = unqliteOsRead(pFd,zBuf,sizeof(zBuf),iOfft);
55514 if( rc != UNQLITE_OK ){
55515 return rc;
55516 }
55517 SyBigEndianUnpack32(zBuf,pOut);
55518 return UNQLITE_OK;
55519}
55520/*
55521** Read a 64-bit integer from the given file descriptor.
55522** All values are stored on disk as big-endian.
55523*/
55524static int ReadInt64(unqlite_file *pFd,sxu64 *pOut,sxi64 iOfft)
55525{
55526 unsigned char zBuf[8];
55527 int rc;
55528 rc = unqliteOsRead(pFd,zBuf,sizeof(zBuf),iOfft);
55529 if( rc != UNQLITE_OK ){
55530 return rc;
55531 }
55532 SyBigEndianUnpack64(zBuf,pOut);
55533 return UNQLITE_OK;
55534}
55535/*
55536** Write a 32-bit integer into the given file descriptor.
55537*/
55538static int WriteInt32(unqlite_file *pFd,sxu32 iNum,sxi64 iOfft)
55539{
55540 unsigned char zBuf[4];
55541 int rc;
55542 SyBigEndianPack32(zBuf,iNum);
55543 rc = unqliteOsWrite(pFd,zBuf,sizeof(zBuf),iOfft);
55544 return rc;
55545}
55546/*
55547** Write a 64-bit integer into the given file descriptor.
55548*/
55549static int WriteInt64(unqlite_file *pFd,sxu64 iNum,sxi64 iOfft)
55550{
55551 unsigned char zBuf[8];
55552 int rc;
55553 SyBigEndianPack64(zBuf,iNum);
55554 rc = unqliteOsWrite(pFd,zBuf,sizeof(zBuf),iOfft);
55555 return rc;
55556}
55557/*
55558** The maximum allowed sector size. 64KiB. If the xSectorsize() method
55559** returns a value larger than this, then MAX_SECTOR_SIZE is used instead.
55560** This could conceivably cause corruption following a power failure on
55561** such a system. This is currently an undocumented limit.
55562*/
55563#define MAX_SECTOR_SIZE 0x10000
55564/*
55565** Get the size of a single sector on disk.
55566** The sector size will be used used to determine the size
55567** and alignment of journal header and within created journal files.
55568**
55569** The default sector size is set to 512.
55570*/
55571static int GetSectorSize(unqlite_file *pFd)
55572{
55573 int iSectorSize = UNQLITE_DEFAULT_SECTOR_SIZE;
55574 if( pFd ){
55575 iSectorSize = unqliteOsSectorSize(pFd);
55576 if( iSectorSize < 32 ){
55577 iSectorSize = 512;
55578 }
55579 if( iSectorSize > MAX_SECTOR_SIZE ){
55580 iSectorSize = MAX_SECTOR_SIZE;
55581 }
55582 }
55583 return iSectorSize;
55584}
55585/* Hash function for page number */
55586#define PAGE_HASH(PNUM) (PNUM)
55587/*
55588 * Fetch a page from the cache.
55589 */
55590static Page * pager_fetch_page(Pager *pPager,pgno page_num)
55591{
55592 Page *pEntry;
55593 if( pPager->nPage < 1 ){
55594 /* Don't bother hashing */
55595 return 0;
55596 }
55597 /* Perform the lookup */
55598 pEntry = pPager->apHash[PAGE_HASH(page_num) & (pPager->nSize - 1)];
55599 for(;;){
55600 if( pEntry == 0 ){
55601 break;
55602 }
55603 if( pEntry->pgno == page_num ){
55604 return pEntry;
55605 }
55606 /* Point to the next entry in the colission chain */
55607 pEntry = pEntry->pNextCollide;
55608 }
55609 /* No such page */
55610 return 0;
55611}
55612/*
55613 * Allocate and initialize a new page.
55614 */
55615static Page * pager_alloc_page(Pager *pPager,pgno num_page)
55616{
55617 Page *pNew;
55618
55619 pNew = (Page *)SyMemBackendPoolAlloc(pPager->pAllocator,sizeof(Page)+pPager->iPageSize);
55620 if( pNew == 0 ){
55621 return 0;
55622 }
55623 /* Zero the structure */
55624 SyZero(pNew,sizeof(Page)+pPager->iPageSize);
55625 /* Page data */
55626 pNew->zData = (unsigned char *)&pNew[1];
55627 /* Fill in the structure */
55628 pNew->pPager = pPager;
55629 pNew->nRef = 1;
55630 pNew->pgno = num_page;
55631 return pNew;
55632}
55633/*
55634 * Increment the reference count of a given page.
55635 */
55636static void page_ref(Page *pPage)
55637{
55638 pPage->nRef++;
55639}
55640/*
55641 * Release an in-memory page after its reference count reach zero.
55642 */
55643static int pager_release_page(Pager *pPager,Page *pPage)
55644{
55645 int rc = UNQLITE_OK;
55646 if( !(pPage->flags & PAGE_DIRTY)){
55647 /* Invoke the unpin callback if available */
55648 if( pPager->xPageUnpin && pPage->pUserData ){
55649 pPager->xPageUnpin(pPage->pUserData);
55650 }
55651 pPage->pUserData = 0;
55652 SyMemBackendPoolFree(pPager->pAllocator,pPage);
55653 }else{
55654 /* Dirty page, it will be released later when a dirty commit
55655 * or the final commit have been applied.
55656 */
55657 rc = UNQLITE_LOCKED;
55658 }
55659 return rc;
55660}
55661/* Forward declaration */
55662static int pager_unlink_page(Pager *pPager,Page *pPage);
55663/*
55664 * Decrement the reference count of a given page.
55665 */
55666static void page_unref(Page *pPage)
55667{
55668 pPage->nRef--;
55669 if( pPage->nRef < 1 ){
55670 Pager *pPager = pPage->pPager;
55671 if( !(pPage->flags & PAGE_DIRTY) ){
55672 pager_unlink_page(pPager,pPage);
55673 /* Release the page */
55674 pager_release_page(pPager,pPage);
55675 }else{
55676 if( pPage->flags & PAGE_DONT_MAKE_HOT ){
55677 /* Do not add this page to the hot dirty list */
55678 return;
55679 }
55680 if( !(pPage->flags & PAGE_HOT_DIRTY) ){
55681 /* Add to the hot dirty list */
55682 pPage->pPrevHot = 0;
55683 if( pPager->pFirstHot == 0 ){
55684 pPager->pFirstHot = pPager->pHotDirty = pPage;
55685 }else{
55686 pPage->pNextHot = pPager->pHotDirty;
55687 if( pPager->pHotDirty ){
55688 pPager->pHotDirty->pPrevHot = pPage;
55689 }
55690 pPager->pHotDirty = pPage;
55691 }
55692 pPager->nHot++;
55693 pPage->flags |= PAGE_HOT_DIRTY;
55694 }
55695 }
55696 }
55697}
55698/*
55699 * Link a freshly created page to the list of active page.
55700 */
55701static int pager_link_page(Pager *pPager,Page *pPage)
55702{
55703 sxu32 nBucket;
55704 /* Install in the corresponding bucket */
55705 nBucket = PAGE_HASH(pPage->pgno) & (pPager->nSize - 1);
55706 pPage->pNextCollide = pPager->apHash[nBucket];
55707 if( pPager->apHash[nBucket] ){
55708 pPager->apHash[nBucket]->pPrevCollide = pPage;
55709 }
55710 pPager->apHash[nBucket] = pPage;
55711 /* Link to the list of active pages */
55712 MACRO_LD_PUSH(pPager->pAll,pPage);
55713 pPager->nPage++;
55714 if( (pPager->nPage >= pPager->nSize * 4) && pPager->nPage < 100000 ){
55715 /* Grow the hashtable */
55716 sxu32 nNewSize = pPager->nSize << 1;
55717 Page *pEntry,**apNew;
55718 sxu32 n;
55719 apNew = (Page **)SyMemBackendAlloc(pPager->pAllocator, nNewSize * sizeof(Page *));
55720 if( apNew ){
55721 sxu32 iBucket;
55722 /* Zero the new table */
55723 SyZero((void *)apNew, nNewSize * sizeof(Page *));
55724 /* Rehash all entries */
55725 n = 0;
55726 pEntry = pPager->pAll;
55727 for(;;){
55728 /* Loop one */
55729 if( n >= pPager->nPage ){
55730 break;
55731 }
55732 pEntry->pNextCollide = pEntry->pPrevCollide = 0;
55733 /* Install in the new bucket */
55734 iBucket = PAGE_HASH(pEntry->pgno) & (nNewSize - 1);
55735 pEntry->pNextCollide = apNew[iBucket];
55736 if( apNew[iBucket] ){
55737 apNew[iBucket]->pPrevCollide = pEntry;
55738 }
55739 apNew[iBucket] = pEntry;
55740 /* Point to the next entry */
55741 pEntry = pEntry->pNext;
55742 n++;
55743 }
55744 /* Release the old table and reflect the change */
55745 SyMemBackendFree(pPager->pAllocator,(void *)pPager->apHash);
55746 pPager->apHash = apNew;
55747 pPager->nSize = nNewSize;
55748 }
55749 }
55750 return UNQLITE_OK;
55751}
55752/*
55753 * Unlink a page from the list of active pages.
55754 */
55755static int pager_unlink_page(Pager *pPager,Page *pPage)
55756{
55757 if( pPage->pNextCollide ){
55758 pPage->pNextCollide->pPrevCollide = pPage->pPrevCollide;
55759 }
55760 if( pPage->pPrevCollide ){
55761 pPage->pPrevCollide->pNextCollide = pPage->pNextCollide;
55762 }else{
55763 sxu32 nBucket = PAGE_HASH(pPage->pgno) & (pPager->nSize - 1);
55764 pPager->apHash[nBucket] = pPage->pNextCollide;
55765 }
55766 MACRO_LD_REMOVE(pPager->pAll,pPage);
55767 pPager->nPage--;
55768 return UNQLITE_OK;
55769}
55770/*
55771 * Update the content of a cached page.
55772 */
55773static int pager_fill_page(Pager *pPager,pgno iNum,void *pContents)
55774{
55775 Page *pPage;
55776 /* Fetch the page from the catch */
55777 pPage = pager_fetch_page(pPager,iNum);
55778 if( pPage == 0 ){
55779 return SXERR_NOTFOUND;
55780 }
55781 /* Reflect the change */
55782 SyMemcpy(pContents,pPage->zData,pPager->iPageSize);
55783
55784 return UNQLITE_OK;
55785}
55786/*
55787 * Read the content of a page from disk.
55788 */
55789static int pager_get_page_contents(Pager *pPager,Page *pPage,int noContent)
55790{
55791 int rc = UNQLITE_OK;
55792 if( pPager->is_mem || noContent || pPage->pgno >= pPager->dbSize ){
55793 /* Do not bother reading, zero the page contents only */
55794 SyZero(pPage->zData,pPager->iPageSize);
55795 return UNQLITE_OK;
55796 }
55797 if( (pPager->iOpenFlags & UNQLITE_OPEN_MMAP) && (pPager->pMmap /* Paranoid edition */) ){
55798 unsigned char *zMap = (unsigned char *)pPager->pMmap;
55799 pPage->zData = &zMap[pPage->pgno * pPager->iPageSize];
55800 }else{
55801 /* Read content */
55802 rc = unqliteOsRead(pPager->pfd,pPage->zData,pPager->iPageSize,pPage->pgno * pPager->iPageSize);
55803 }
55804 return rc;
55805}
55806/*
55807 * Add a page to the dirty list.
55808 */
55809static void pager_page_to_dirty_list(Pager *pPager,Page *pPage)
55810{
55811 if( pPage->flags & PAGE_DIRTY ){
55812 /* Already set */
55813 return;
55814 }
55815 /* Mark the page as dirty */
55816 pPage->flags |= PAGE_DIRTY|PAGE_NEED_SYNC|PAGE_IN_JOURNAL;
55817 /* Link to the list */
55818 pPage->pDirtyPrev = 0;
55819 pPage->pDirtyNext = pPager->pDirty;
55820 if( pPager->pDirty ){
55821 pPager->pDirty->pDirtyPrev = pPage;
55822 }
55823 pPager->pDirty = pPage;
55824 if( pPager->pFirstDirty == 0 ){
55825 pPager->pFirstDirty = pPage;
55826 }
55827}
55828/*
55829 * Merge sort.
55830 * The merge sort implementation is based on the one used by
55831 * the PH7 Embeddable PHP Engine (http://ph7.symisc.net/).
55832 */
55833/*
55834** Inputs:
55835** a: A sorted, null-terminated linked list. (May be null).
55836** b: A sorted, null-terminated linked list. (May be null).
55837** cmp: A pointer to the comparison function.
55838**
55839** Return Value:
55840** A pointer to the head of a sorted list containing the elements
55841** of both a and b.
55842**
55843** Side effects:
55844** The "next", "prev" pointers for elements in the lists a and b are
55845** changed.
55846*/
55847static Page * page_merge_dirty(Page *pA, Page *pB)
55848{
55849 Page result, *pTail;
55850 /* Prevent compiler warning */
55851 result.pDirtyNext = result.pDirtyPrev = 0;
55852 pTail = &result;
55853 while( pA && pB ){
55854 if( pA->pgno < pB->pgno ){
55855 pTail->pDirtyPrev = pA;
55856 pA->pDirtyNext = pTail;
55857 pTail = pA;
55858 pA = pA->pDirtyPrev;
55859 }else{
55860 pTail->pDirtyPrev = pB;
55861 pB->pDirtyNext = pTail;
55862 pTail = pB;
55863 pB = pB->pDirtyPrev;
55864 }
55865 }
55866 if( pA ){
55867 pTail->pDirtyPrev = pA;
55868 pA->pDirtyNext = pTail;
55869 }else if( pB ){
55870 pTail->pDirtyPrev = pB;
55871 pB->pDirtyNext = pTail;
55872 }else{
55873 pTail->pDirtyPrev = pTail->pDirtyNext = 0;
55874 }
55875 return result.pDirtyPrev;
55876}
55877/*
55878** Inputs:
55879** Map: Input hashmap
55880** cmp: A comparison function.
55881**
55882** Return Value:
55883** Sorted hashmap.
55884**
55885** Side effects:
55886** The "next" pointers for elements in list are changed.
55887*/
55888#define N_SORT_BUCKET 32
55889static Page * pager_get_dirty_pages(Pager *pPager)
55890{
55891 Page *a[N_SORT_BUCKET], *p, *pIn;
55892 sxu32 i;
55893 if( pPager->pFirstDirty == 0 ){
55894 /* Don't bother sorting, the list is already empty */
55895 return 0;
55896 }
55897 SyZero(a, sizeof(a));
55898 /* Point to the first inserted entry */
55899 pIn = pPager->pFirstDirty;
55900 while( pIn ){
55901 p = pIn;
55902 pIn = p->pDirtyPrev;
55903 p->pDirtyPrev = 0;
55904 for(i=0; i<N_SORT_BUCKET-1; i++){
55905 if( a[i]==0 ){
55906 a[i] = p;
55907 break;
55908 }else{
55909 p = page_merge_dirty(a[i], p);
55910 a[i] = 0;
55911 }
55912 }
55913 if( i==N_SORT_BUCKET-1 ){
55914 /* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
55915 * But that is impossible.
55916 */
55917 a[i] = page_merge_dirty(a[i], p);
55918 }
55919 }
55920 p = a[0];
55921 for(i=1; i<N_SORT_BUCKET; i++){
55922 p = page_merge_dirty(p,a[i]);
55923 }
55924 p->pDirtyNext = 0;
55925 return p;
55926}
55927/*
55928 * See block comment above.
55929 */
55930static Page * page_merge_hot(Page *pA, Page *pB)
55931{
55932 Page result, *pTail;
55933 /* Prevent compiler warning */
55934 result.pNextHot = result.pPrevHot = 0;
55935 pTail = &result;
55936 while( pA && pB ){
55937 if( pA->pgno < pB->pgno ){
55938 pTail->pPrevHot = pA;
55939 pA->pNextHot = pTail;
55940 pTail = pA;
55941 pA = pA->pPrevHot;
55942 }else{
55943 pTail->pPrevHot = pB;
55944 pB->pNextHot = pTail;
55945 pTail = pB;
55946 pB = pB->pPrevHot;
55947 }
55948 }
55949 if( pA ){
55950 pTail->pPrevHot = pA;
55951 pA->pNextHot = pTail;
55952 }else if( pB ){
55953 pTail->pPrevHot = pB;
55954 pB->pNextHot = pTail;
55955 }else{
55956 pTail->pPrevHot = pTail->pNextHot = 0;
55957 }
55958 return result.pPrevHot;
55959}
55960/*
55961** Inputs:
55962** Map: Input hashmap
55963** cmp: A comparison function.
55964**
55965** Return Value:
55966** Sorted hashmap.
55967**
55968** Side effects:
55969** The "next" pointers for elements in list are changed.
55970*/
55971#define N_SORT_BUCKET 32
55972static Page * pager_get_hot_pages(Pager *pPager)
55973{
55974 Page *a[N_SORT_BUCKET], *p, *pIn;
55975 sxu32 i;
55976 if( pPager->pFirstHot == 0 ){
55977 /* Don't bother sorting, the list is already empty */
55978 return 0;
55979 }
55980 SyZero(a, sizeof(a));
55981 /* Point to the first inserted entry */
55982 pIn = pPager->pFirstHot;
55983 while( pIn ){
55984 p = pIn;
55985 pIn = p->pPrevHot;
55986 p->pPrevHot = 0;
55987 for(i=0; i<N_SORT_BUCKET-1; i++){
55988 if( a[i]==0 ){
55989 a[i] = p;
55990 break;
55991 }else{
55992 p = page_merge_hot(a[i], p);
55993 a[i] = 0;
55994 }
55995 }
55996 if( i==N_SORT_BUCKET-1 ){
55997 /* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
55998 * But that is impossible.
55999 */
56000 a[i] = page_merge_hot(a[i], p);
56001 }
56002 }
56003 p = a[0];
56004 for(i=1; i<N_SORT_BUCKET; i++){
56005 p = page_merge_hot(p,a[i]);
56006 }
56007 p->pNextHot = 0;
56008 return p;
56009}
56010/*
56011** The format for the journal header is as follows:
56012** - 8 bytes: Magic identifying journal format.
56013** - 4 bytes: Number of records in journal.
56014** - 4 bytes: Random number used for page hash.
56015** - 8 bytes: Initial database page count.
56016** - 4 bytes: Sector size used by the process that wrote this journal.
56017** - 4 bytes: Database page size.
56018**
56019** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
56020*/
56021/*
56022** Open the journal file and extract its header information.
56023**
56024** If the header is read successfully, *pNRec is set to the number of
56025** page records following this header and *pDbSize is set to the size of the
56026** database before the transaction began, in pages. Also, pPager->cksumInit
56027** is set to the value read from the journal header. UNQLITE_OK is returned
56028** in this case.
56029**
56030** If the journal header file appears to be corrupted, UNQLITE_DONE is
56031** returned and *pNRec and *PDbSize are undefined. If JOURNAL_HDR_SZ bytes
56032** cannot be read from the journal file an error code is returned.
56033*/
56034static int pager_read_journal_header(
56035 Pager *pPager, /* Pager object */
56036 sxu32 *pNRec, /* OUT: Value read from the nRec field */
56037 pgno *pDbSize /* OUT: Value of original database size field */
56038)
56039{
56040 sxu32 iPageSize,iSectorSize;
56041 unsigned char zMagic[8];
56042 sxi64 iHdrOfft;
56043 sxi64 iSize;
56044 int rc;
56045 /* Offset to start reading from */
56046 iHdrOfft = 0;
56047 /* Get the size of the journal */
56048 rc = unqliteOsFileSize(pPager->pjfd,&iSize);
56049 if( rc != UNQLITE_OK ){
56050 return UNQLITE_DONE;
56051 }
56052 /* If the journal file is too small, return UNQLITE_DONE. */
56053 if( 32 /* Minimum sector size */> iSize ){
56054 return UNQLITE_DONE;
56055 }
56056 /* Make sure we are dealing with a valid journal */
56057 rc = unqliteOsRead(pPager->pjfd,zMagic,sizeof(zMagic),iHdrOfft);
56058 if( rc != UNQLITE_OK ){
56059 return rc;
56060 }
56061 if( SyMemcmp(zMagic,aJournalMagic,sizeof(zMagic)) != 0 ){
56062 return UNQLITE_DONE;
56063 }
56064 iHdrOfft += sizeof(zMagic);
56065 /* Read the first three 32-bit fields of the journal header: The nRec
56066 ** field, the checksum-initializer and the database size at the start
56067 ** of the transaction. Return an error code if anything goes wrong.
56068 */
56069 rc = ReadInt32(pPager->pjfd,pNRec,iHdrOfft);
56070 if( rc != UNQLITE_OK ){
56071 return rc;
56072 }
56073 iHdrOfft += 4;
56074 rc = ReadInt32(pPager->pjfd,&pPager->cksumInit,iHdrOfft);
56075 if( rc != UNQLITE_OK ){
56076 return rc;
56077 }
56078 iHdrOfft += 4;
56079 rc = ReadInt64(pPager->pjfd,pDbSize,iHdrOfft);
56080 if( rc != UNQLITE_OK ){
56081 return rc;
56082 }
56083 iHdrOfft += 8;
56084 /* Read the page-size and sector-size journal header fields. */
56085 rc = ReadInt32(pPager->pjfd,&iSectorSize,iHdrOfft);
56086 if( rc != UNQLITE_OK ){
56087 return rc;
56088 }
56089 iHdrOfft += 4;
56090 rc = ReadInt32(pPager->pjfd,&iPageSize,iHdrOfft);
56091 if( rc != UNQLITE_OK ){
56092 return rc;
56093 }
56094 /* Check that the values read from the page-size and sector-size fields
56095 ** are within range. To be 'in range', both values need to be a power
56096 ** of two greater than or equal to 512 or 32, and not greater than their
56097 ** respective compile time maximum limits.
56098 */
56099 if( iPageSize < UNQLITE_MIN_PAGE_SIZE || iSectorSize<32
56100 || iPageSize > UNQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE
56101 || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0
56102 ){
56103 /* If the either the page-size or sector-size in the journal-header is
56104 ** invalid, then the process that wrote the journal-header must have
56105 ** crashed before the header was synced. In this case stop reading
56106 ** the journal file here.
56107 */
56108 return UNQLITE_DONE;
56109 }
56110 /* Update the assumed sector-size to match the value used by
56111 ** the process that created this journal. If this journal was
56112 ** created by a process other than this one, then this routine
56113 ** is being called from within pager_playback(). The local value
56114 ** of Pager.sectorSize is restored at the end of that routine.
56115 */
56116 pPager->iSectorSize = iSectorSize;
56117 pPager->iPageSize = iPageSize;
56118 /* Ready to rollback */
56119 pPager->iJournalOfft = JOURNAL_HDR_SZ(pPager);
56120 /* All done */
56121 return UNQLITE_OK;
56122}
56123/*
56124 * Write the journal header in the given memory buffer.
56125 * The given buffer is big enough to hold the whole header.
56126 */
56127static int pager_write_journal_header(Pager *pPager,unsigned char *zBuf)
56128{
56129 unsigned char *zPtr = zBuf;
56130 /* 8 bytes magic number */
56131 SyMemcpy(aJournalMagic,zPtr,sizeof(aJournalMagic));
56132 zPtr += sizeof(aJournalMagic);
56133 /* 4 bytes: Number of records in journal. */
56134 SyBigEndianPack32(zPtr,0);
56135 zPtr += 4;
56136 /* 4 bytes: Random number used to compute page checksum. */
56137 SyBigEndianPack32(zPtr,pPager->cksumInit);
56138 zPtr += 4;
56139 /* 8 bytes: Initial database page count. */
56140 SyBigEndianPack64(zPtr,pPager->dbOrigSize);
56141 zPtr += 8;
56142 /* 4 bytes: Sector size used by the process that wrote this journal. */
56143 SyBigEndianPack32(zPtr,(sxu32)pPager->iSectorSize);
56144 zPtr += 4;
56145 /* 4 bytes: Database page size. */
56146 SyBigEndianPack32(zPtr,(sxu32)pPager->iPageSize);
56147 return UNQLITE_OK;
56148}
56149/*
56150** Parameter aData must point to a buffer of pPager->pageSize bytes
56151** of data. Compute and return a checksum based ont the contents of the
56152** page of data and the current value of pPager->cksumInit.
56153**
56154** This is not a real checksum. It is really just the sum of the
56155** random initial value (pPager->cksumInit) and every 200th byte
56156** of the page data, starting with byte offset (pPager->pageSize%200).
56157** Each byte is interpreted as an 8-bit unsigned integer.
56158**
56159** Changing the formula used to compute this checksum results in an
56160** incompatible journal file format.
56161**
56162** If journal corruption occurs due to a power failure, the most likely
56163** scenario is that one end or the other of the record will be changed.
56164** It is much less likely that the two ends of the journal record will be
56165** correct and the middle be corrupt. Thus, this "checksum" scheme,
56166** though fast and simple, catches the mostly likely kind of corruption.
56167*/
56168static sxu32 pager_cksum(Pager *pPager,const unsigned char *zData)
56169{
56170 sxu32 cksum = pPager->cksumInit; /* Checksum value to return */
56171 int i = pPager->iPageSize-200; /* Loop counter */
56172 while( i>0 ){
56173 cksum += zData[i];
56174 i -= 200;
56175 }
56176 return cksum;
56177}
56178/*
56179** Read a single page from the journal file opened on file descriptor
56180** jfd. Playback this one page. Update the offset to read from.
56181*/
56182static int pager_play_back_one_page(Pager *pPager,sxi64 *pOfft,unsigned char *zTmp)
56183{
56184 unsigned char *zData = zTmp;
56185 sxi64 iOfft; /* Offset to read from */
56186 pgno iNum; /* Pager number */
56187 sxu32 ckSum; /* Sanity check */
56188 int rc;
56189 /* Offset to start reading from */
56190 iOfft = *pOfft;
56191 /* Database page number */
56192 rc = ReadInt64(pPager->pjfd,&iNum,iOfft);
56193 if( rc != UNQLITE_OK ){ return rc; }
56194 iOfft += 8;
56195 /* Page data */
56196 rc = unqliteOsRead(pPager->pjfd,zData,pPager->iPageSize,iOfft);
56197 if( rc != UNQLITE_OK ){ return rc; }
56198 iOfft += pPager->iPageSize;
56199 /* Page cksum */
56200 rc = ReadInt32(pPager->pjfd,&ckSum,iOfft);
56201 if( rc != UNQLITE_OK ){ return rc; }
56202 iOfft += 4;
56203 /* Synchronize pointers */
56204 *pOfft = iOfft;
56205 /* Make sure we are dealing with a valid page */
56206 if( ckSum != pager_cksum(pPager,zData) ){
56207 /* Ignore that page */
56208 return SXERR_IGNORE;
56209 }
56210 if( iNum >= pPager->dbSize ){
56211 /* Ignore that page */
56212 return UNQLITE_OK;
56213 }
56214 /* playback */
56215 rc = unqliteOsWrite(pPager->pfd,zData,pPager->iPageSize,iNum * pPager->iPageSize);
56216 if( rc == UNQLITE_OK ){
56217 /* Flush the cache */
56218 pager_fill_page(pPager,iNum,zData);
56219 }
56220 return rc;
56221}
56222/*
56223** Playback the journal and thus restore the database file to
56224** the state it was in before we started making changes.
56225**
56226** The journal file format is as follows:
56227**
56228** (1) 8 byte prefix. A copy of aJournalMagic[].
56229** (2) 4 byte big-endian integer which is the number of valid page records
56230** in the journal.
56231** (3) 4 byte big-endian integer which is the initial value for the
56232** sanity checksum.
56233** (4) 8 byte integer which is the number of pages to truncate the
56234** database to during a rollback.
56235** (5) 4 byte big-endian integer which is the sector size. The header
56236** is this many bytes in size.
56237** (6) 4 byte big-endian integer which is the page size.
56238** (7) zero padding out to the next sector size.
56239** (8) Zero or more pages instances, each as follows:
56240** + 4 byte page number.
56241** + pPager->pageSize bytes of data.
56242** + 4 byte checksum
56243**
56244** When we speak of the journal header, we mean the first 7 items above.
56245** Each entry in the journal is an instance of the 8th item.
56246**
56247** Call the value from the second bullet "nRec". nRec is the number of
56248** valid page entries in the journal. In most cases, you can compute the
56249** value of nRec from the size of the journal file. But if a power
56250** failure occurred while the journal was being written, it could be the
56251** case that the size of the journal file had already been increased but
56252** the extra entries had not yet made it safely to disk. In such a case,
56253** the value of nRec computed from the file size would be too large. For
56254** that reason, we always use the nRec value in the header.
56255**
56256** If the file opened as the journal file is not a well-formed
56257** journal file then all pages up to the first corrupted page are rolled
56258** back (or no pages if the journal header is corrupted). The journal file
56259** is then deleted and SQLITE_OK returned, just as if no corruption had
56260** been encountered.
56261**
56262** If an I/O or malloc() error occurs, the journal-file is not deleted
56263** and an error code is returned.
56264**
56265*/
56266static int pager_playback(Pager *pPager)
56267{
56268 unsigned char *zTmp = 0; /* cc warning */
56269 sxu32 n,nRec;
56270 sxi64 iOfft;
56271 int rc;
56272 /* Read the journal header*/
56273 rc = pager_read_journal_header(pPager,&nRec,&pPager->dbSize);
56274 if( rc != UNQLITE_OK ){
56275 if( rc == UNQLITE_DONE ){
56276 goto end_playback;
56277 }
56278 unqliteGenErrorFormat(pPager->pDb,"IO error while reading journal file '%s' header",pPager->zJournal);
56279 return rc;
56280 }
56281 /* Truncate the database back to its original size */
56282 rc = unqliteOsTruncate(pPager->pfd,pPager->iPageSize * pPager->dbSize);
56283 if( rc != UNQLITE_OK ){
56284 unqliteGenError(pPager->pDb,"IO error while truncating database file");
56285 return rc;
56286 }
56287 /* Allocate a temporary page */
56288 zTmp = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iPageSize);
56289 if( zTmp == 0 ){
56290 unqliteGenOutofMem(pPager->pDb);
56291 return UNQLITE_NOMEM;
56292 }
56293 SyZero((void *)zTmp,(sxu32)pPager->iPageSize);
56294 /* Copy original pages out of the journal and back into the
56295 ** database file and/or page cache.
56296 */
56297 iOfft = pPager->iJournalOfft;
56298 for( n = 0 ; n < nRec ; ++n ){
56299 rc = pager_play_back_one_page(pPager,&iOfft,zTmp);
56300 if( rc != UNQLITE_OK ){
56301 if( rc != SXERR_IGNORE ){
56302 unqliteGenError(pPager->pDb,"Page playback error");
56303 goto end_playback;
56304 }
56305 }
56306 }
56307end_playback:
56308 /* Release the temp page */
56309 SyMemBackendFree(pPager->pAllocator,(void *)zTmp);
56310 if( rc == UNQLITE_OK ){
56311 /* Sync the database file */
56312 unqliteOsSync(pPager->pfd,UNQLITE_SYNC_FULL);
56313 }
56314 if( rc == UNQLITE_DONE ){
56315 rc = UNQLITE_OK;
56316 }
56317 /* Return to the caller */
56318 return rc;
56319}
56320/*
56321** Unlock the database file to level eLock, which must be either NO_LOCK
56322** or SHARED_LOCK. Regardless of whether or not the call to xUnlock()
56323** succeeds, set the Pager.iLock variable to match the (attempted) new lock.
56324**
56325** Except, if Pager.iLock is set to NO_LOCK when this function is
56326** called, do not modify it. See the comment above the #define of
56327** NO_LOCK for an explanation of this.
56328*/
56329static int pager_unlock_db(Pager *pPager, int eLock)
56330{
56331 int rc = UNQLITE_OK;
56332 if( pPager->iLock != NO_LOCK ){
56333 rc = unqliteOsUnlock(pPager->pfd,eLock);
56334 pPager->iLock = eLock;
56335 }
56336 return rc;
56337}
56338/*
56339** Lock the database file to level eLock, which must be either SHARED_LOCK,
56340** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the
56341** Pager.eLock variable to the new locking state.
56342**
56343** Except, if Pager.eLock is set to NO_LOCK when this function is
56344** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK.
56345** See the comment above the #define of NO_LOCK for an explanation
56346** of this.
56347*/
56348static int pager_lock_db(Pager *pPager, int eLock){
56349 int rc = UNQLITE_OK;
56350 if( pPager->iLock < eLock || pPager->iLock == NO_LOCK ){
56351 rc = unqliteOsLock(pPager->pfd, eLock);
56352 if( rc==UNQLITE_OK ){
56353 pPager->iLock = eLock;
56354 }else{
56355 unqliteGenError(pPager->pDb,
56356 rc == UNQLITE_BUSY ? "Another process or thread hold the requested lock" : "Error while requesting database lock"
56357 );
56358 }
56359 }
56360 return rc;
56361}
56362/*
56363** Try to obtain a lock of type locktype on the database file. If
56364** a similar or greater lock is already held, this function is a no-op
56365** (returning UNQLITE_OK immediately).
56366**
56367** Otherwise, attempt to obtain the lock using unqliteOsLock(). Invoke
56368** the busy callback if the lock is currently not available. Repeat
56369** until the busy callback returns false or until the attempt to
56370** obtain the lock succeeds.
56371**
56372** Return UNQLITE_OK on success and an error code if we cannot obtain
56373** the lock. If the lock is obtained successfully, set the Pager.state
56374** variable to locktype before returning.
56375*/
56376static int pager_wait_on_lock(Pager *pPager, int locktype){
56377 int rc; /* Return code */
56378 do {
56379 rc = pager_lock_db(pPager,locktype);
56380 }while( rc==UNQLITE_BUSY && pPager->xBusyHandler && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
56381 return rc;
56382}
56383/*
56384** This function is called after transitioning from PAGER_OPEN to
56385** PAGER_SHARED state. It tests if there is a hot journal present in
56386** the file-system for the given pager. A hot journal is one that
56387** needs to be played back. According to this function, a hot-journal
56388** file exists if the following criteria are met:
56389**
56390** * The journal file exists in the file system, and
56391** * No process holds a RESERVED or greater lock on the database file, and
56392** * The database file itself is greater than 0 bytes in size, and
56393** * The first byte of the journal file exists and is not 0x00.
56394**
56395** If the current size of the database file is 0 but a journal file
56396** exists, that is probably an old journal left over from a prior
56397** database with the same name. In this case the journal file is
56398** just deleted using OsDelete, *pExists is set to 0 and UNQLITE_OK
56399** is returned.
56400**
56401** If a hot-journal file is found to exist, *pExists is set to 1 and
56402** UNQLITE_OK returned. If no hot-journal file is present, *pExists is
56403** set to 0 and UNQLITE_OK returned. If an IO error occurs while trying
56404** to determine whether or not a hot-journal file exists, the IO error
56405** code is returned and the value of *pExists is undefined.
56406*/
56407static int pager_has_hot_journal(Pager *pPager, int *pExists)
56408{
56409 unqlite_vfs *pVfs = pPager->pVfs;
56410 int rc = UNQLITE_OK; /* Return code */
56411 int exists = 1; /* True if a journal file is present */
56412
56413 *pExists = 0;
56414 rc = unqliteOsAccess(pVfs, pPager->zJournal, UNQLITE_ACCESS_EXISTS, &exists);
56415 if( rc==UNQLITE_OK && exists ){
56416 int locked = 0; /* True if some process holds a RESERVED lock */
56417
56418 /* Race condition here: Another process might have been holding the
56419 ** the RESERVED lock and have a journal open at the unqliteOsAccess()
56420 ** call above, but then delete the journal and drop the lock before
56421 ** we get to the following unqliteOsCheckReservedLock() call. If that
56422 ** is the case, this routine might think there is a hot journal when
56423 ** in fact there is none. This results in a false-positive which will
56424 ** be dealt with by the playback routine.
56425 */
56426 rc = unqliteOsCheckReservedLock(pPager->pfd, &locked);
56427 if( rc==UNQLITE_OK && !locked ){
56428 sxi64 n = 0; /* Size of db file in bytes */
56429
56430 /* Check the size of the database file. If it consists of 0 pages,
56431 ** then delete the journal file. See the header comment above for
56432 ** the reasoning here. Delete the obsolete journal file under
56433 ** a RESERVED lock to avoid race conditions.
56434 */
56435 rc = unqliteOsFileSize(pPager->pfd,&n);
56436 if( rc==UNQLITE_OK ){
56437 if( n < 1 ){
56438 if( pager_lock_db(pPager, RESERVED_LOCK)==UNQLITE_OK ){
56439 unqliteOsDelete(pVfs, pPager->zJournal, 0);
56440 pager_unlock_db(pPager, SHARED_LOCK);
56441 }
56442 }else{
56443 /* The journal file exists and no other connection has a reserved
56444 ** or greater lock on the database file. */
56445 *pExists = 1;
56446 }
56447 }
56448 }
56449 }
56450 return rc;
56451}
56452/*
56453 * Rollback a journal file. (See block-comment above).
56454 */
56455static int pager_journal_rollback(Pager *pPager,int check_hot)
56456{
56457 int rc;
56458 if( check_hot ){
56459 int iExists = 0; /* cc warning */
56460 /* Check if the journal file exists */
56461 rc = pager_has_hot_journal(pPager,&iExists);
56462 if( rc != UNQLITE_OK ){
56463 /* IO error */
56464 return rc;
56465 }
56466 if( !iExists ){
56467 /* Journal file does not exists */
56468 return UNQLITE_OK;
56469 }
56470 }
56471 if( pPager->is_rdonly ){
56472 unqliteGenErrorFormat(pPager->pDb,
56473 "Cannot rollback journal file '%s' due to a read-only database handle",pPager->zJournal);
56474 return UNQLITE_READ_ONLY;
56475 }
56476 /* Get an EXCLUSIVE lock on the database file. At this point it is
56477 ** important that a RESERVED lock is not obtained on the way to the
56478 ** EXCLUSIVE lock. If it were, another process might open the
56479 ** database file, detect the RESERVED lock, and conclude that the
56480 ** database is safe to read while this process is still rolling the
56481 ** hot-journal back.
56482 **
56483 ** Because the intermediate RESERVED lock is not requested, any
56484 ** other process attempting to access the database file will get to
56485 ** this point in the code and fail to obtain its own EXCLUSIVE lock
56486 ** on the database file.
56487 **
56488 ** Unless the pager is in locking_mode=exclusive mode, the lock is
56489 ** downgraded to SHARED_LOCK before this function returns.
56490 */
56491 /* Open the journal file */
56492 rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zJournal,&pPager->pjfd,UNQLITE_OPEN_READWRITE);
56493 if( rc != UNQLITE_OK ){
56494 unqliteGenErrorFormat(pPager->pDb,"IO error while opening journal file: '%s'",pPager->zJournal);
56495 goto fail;
56496 }
56497 rc = pager_lock_db(pPager,EXCLUSIVE_LOCK);
56498 if( rc != UNQLITE_OK ){
56499 unqliteGenError(pPager->pDb,"Cannot acquire an exclusive lock on the database while journal rollback");
56500 goto fail;
56501 }
56502 /* Sync the journal file */
56503 unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
56504 /* Finally rollback the database */
56505 rc = pager_playback(pPager);
56506 /* Switch back to shared lock */
56507 pager_unlock_db(pPager,SHARED_LOCK);
56508fail:
56509 /* Close the journal handle */
56510 unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
56511 pPager->pjfd = 0;
56512 if( rc == UNQLITE_OK ){
56513 /* Delete the journal file */
56514 unqliteOsDelete(pPager->pVfs,pPager->zJournal,TRUE);
56515 }
56516 return rc;
56517}
56518/*
56519 * Write the unqlite header (First page). (Big-Endian)
56520 */
56521static int pager_write_db_header(Pager *pPager)
56522{
56523 unsigned char *zRaw = pPager->pHeader->zData;
56524 unqlite_kv_engine *pEngine = pPager->pEngine;
56525 sxu32 nDos;
56526 sxu16 nLen;
56527 /* Database signature */
56528 SyMemcpy(UNQLITE_DB_SIG,zRaw,sizeof(UNQLITE_DB_SIG)-1);
56529 zRaw += sizeof(UNQLITE_DB_SIG)-1;
56530 /* Database magic number */
56531 SyBigEndianPack32(zRaw,UNQLITE_DB_MAGIC);
56532 zRaw += 4; /* 4 byte magic number */
56533 /* Database creation time */
56534 SyZero(&pPager->tmCreate,sizeof(Sytm));
56535 if( pPager->pVfs->xCurrentTime ){
56536 pPager->pVfs->xCurrentTime(pPager->pVfs,&pPager->tmCreate);
56537 }
56538 /* DOS time format (4 bytes) */
56539 SyTimeFormatToDos(&pPager->tmCreate,&nDos);
56540 SyBigEndianPack32(zRaw,nDos);
56541 zRaw += 4; /* 4 byte DOS time */
56542 /* Sector size */
56543 SyBigEndianPack32(zRaw,(sxu32)pPager->iSectorSize);
56544 zRaw += 4; /* 4 byte sector size */
56545 /* Page size */
56546 SyBigEndianPack32(zRaw,(sxu32)pPager->iPageSize);
56547 zRaw += 4; /* 4 byte page size */
56548 /* Key value storage engine */
56549 nLen = (sxu16)SyStrlen(pEngine->pIo->pMethods->zName);
56550 SyBigEndianPack16(zRaw,nLen); /* 2 byte storage engine name */
56551 zRaw += 2;
56552 SyMemcpy((const void *)pEngine->pIo->pMethods->zName,(void *)zRaw,nLen);
56553 zRaw += nLen;
56554 /* All rest are meta-data available to the host application */
56555 return UNQLITE_OK;
56556}
56557/*
56558 * Read the unqlite header (first page). (Big-Endian)
56559 */
56560static int pager_extract_header(Pager *pPager,const unsigned char *zRaw,sxu32 nByte)
56561{
56562 const unsigned char *zEnd = &zRaw[nByte];
56563 sxu32 nDos,iMagic;
56564 sxu16 nLen;
56565 char *zKv;
56566 /* Database signature */
56567 if( SyMemcmp(UNQLITE_DB_SIG,zRaw,sizeof(UNQLITE_DB_SIG)-1) != 0 ){
56568 /* Corrupt database */
56569 return UNQLITE_CORRUPT;
56570 }
56571 zRaw += sizeof(UNQLITE_DB_SIG)-1;
56572 /* Database magic number */
56573 SyBigEndianUnpack32(zRaw,&iMagic);
56574 zRaw += 4; /* 4 byte magic number */
56575 if( iMagic != UNQLITE_DB_MAGIC ){
56576 /* Corrupt database */
56577 return UNQLITE_CORRUPT;
56578 }
56579 /* Database creation time */
56580 SyBigEndianUnpack32(zRaw,&nDos);
56581 zRaw += 4; /* 4 byte DOS time format */
56582 SyDosTimeFormat(nDos,&pPager->tmCreate);
56583 /* Sector size */
56584 SyBigEndianUnpack32(zRaw,(sxu32 *)&pPager->iSectorSize);
56585 zRaw += 4; /* 4 byte sector size */
56586 /* Page size */
56587 SyBigEndianUnpack32(zRaw,(sxu32 *)&pPager->iPageSize);
56588 zRaw += 4; /* 4 byte page size */
56589 /* Check that the values read from the page-size and sector-size fields
56590 ** are within range. To be 'in range', both values need to be a power
56591 ** of two greater than or equal to 512 or 32, and not greater than their
56592 ** respective compile time maximum limits.
56593 */
56594 if( pPager->iPageSize<UNQLITE_MIN_PAGE_SIZE || pPager->iSectorSize<32
56595 || pPager->iPageSize>UNQLITE_MAX_PAGE_SIZE || pPager->iSectorSize>MAX_SECTOR_SIZE
56596 || ((pPager->iPageSize<-1)&pPager->iPageSize)!=0 || ((pPager->iSectorSize-1)&pPager->iSectorSize)!=0
56597 ){
56598 return UNQLITE_CORRUPT;
56599 }
56600 /* Key value storage engine */
56601 SyBigEndianUnpack16(zRaw,&nLen); /* 2 byte storage engine length */
56602 zRaw += 2;
56603 if( nLen > (sxu16)(zEnd - zRaw) ){
56604 nLen = (sxu16)(zEnd - zRaw);
56605 }
56606 zKv = (char *)SyMemBackendDup(pPager->pAllocator,(const char *)zRaw,nLen);
56607 if( zKv == 0 ){
56608 return UNQLITE_NOMEM;
56609 }
56610 SyStringInitFromBuf(&pPager->sKv,zKv,nLen);
56611 return UNQLITE_OK;
56612}
56613/*
56614 * Read the database header.
56615 */
56616static int pager_read_db_header(Pager *pPager)
56617{
56618 unsigned char zRaw[UNQLITE_MIN_PAGE_SIZE]; /* Minimum page size */
56619 sxi64 n = 0; /* Size of db file in bytes */
56620 int rc;
56621 /* Get the file size first */
56622 rc = unqliteOsFileSize(pPager->pfd,&n);
56623 if( rc != UNQLITE_OK ){
56624 return rc;
56625 }
56626 pPager->dbByteSize = n;
56627 if( n > 0 ){
56628 unqlite_kv_methods *pMethods;
56629 SyString *pKv;
56630 pgno nPage;
56631 if( n < UNQLITE_MIN_PAGE_SIZE ){
56632 /* A valid unqlite database must be at least 512 bytes long */
56633 unqliteGenError(pPager->pDb,"Malformed database image");
56634 return UNQLITE_CORRUPT;
56635 }
56636 /* Read the database header */
56637 rc = unqliteOsRead(pPager->pfd,zRaw,sizeof(zRaw),0);
56638 if( rc != UNQLITE_OK ){
56639 unqliteGenError(pPager->pDb,"IO error while reading database header");
56640 return rc;
56641 }
56642 /* Extract the header */
56643 rc = pager_extract_header(pPager,zRaw,sizeof(zRaw));
56644 if( rc != UNQLITE_OK ){
56645 unqliteGenError(pPager->pDb,rc == UNQLITE_NOMEM ? "Unqlite is running out of memory" : "Malformed database image");
56646 return rc;
56647 }
56648 /* Update pager state */
56649 nPage = (pgno)(n / pPager->iPageSize);
56650 if( nPage==0 && n>0 ){
56651 nPage = 1;
56652 }
56653 pPager->dbSize = nPage;
56654 /* Laod the target Key/Value storage engine */
56655 pKv = &pPager->sKv;
56656 pMethods = unqliteFindKVStore(pKv->zString,pKv->nByte);
56657 if( pMethods == 0 ){
56658 unqliteGenErrorFormat(pPager->pDb,"No such Key/Value storage engine '%z'",pKv);
56659 return UNQLITE_NOTIMPLEMENTED;
56660 }
56661 /* Install the new KV storage engine */
56662 rc = unqlitePagerRegisterKvEngine(pPager,pMethods);
56663 if( rc != UNQLITE_OK ){
56664 return rc;
56665 }
56666 }else{
56667 /* Set a default page and sector size */
56668 pPager->iSectorSize = GetSectorSize(pPager->pfd);
56669 pPager->iPageSize = unqliteGetPageSize();
56670 SyStringInitFromBuf(&pPager->sKv,pPager->pEngine->pIo->pMethods->zName,SyStrlen(pPager->pEngine->pIo->pMethods->zName));
56671 pPager->dbSize = 0;
56672 }
56673 /* Allocate a temporary page size */
56674 pPager->zTmpPage = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iPageSize);
56675 if( pPager->zTmpPage == 0 ){
56676 unqliteGenOutofMem(pPager->pDb);
56677 return UNQLITE_NOMEM;
56678 }
56679 SyZero(pPager->zTmpPage,(sxu32)pPager->iPageSize);
56680 return UNQLITE_OK;
56681}
56682/*
56683 * Write the database header.
56684 */
56685static int pager_create_header(Pager *pPager)
56686{
56687 Page *pHeader;
56688 int rc;
56689 /* Allocate a new page */
56690 pHeader = pager_alloc_page(pPager,0);
56691 if( pHeader == 0 ){
56692 return UNQLITE_NOMEM;
56693 }
56694 pPager->pHeader = pHeader;
56695 /* Link the page */
56696 pager_link_page(pPager,pHeader);
56697 /* Add to the dirty list */
56698 pager_page_to_dirty_list(pPager,pHeader);
56699 /* Write the database header */
56700 rc = pager_write_db_header(pPager);
56701 return rc;
56702}
56703/*
56704** This function is called to obtain a shared lock on the database file.
56705** It is illegal to call unqlitePagerAcquire() until after this function
56706** has been successfully called. If a shared-lock is already held when
56707** this function is called, it is a no-op.
56708**
56709** The following operations are also performed by this function.
56710**
56711** 1) If the pager is currently in PAGER_OPEN state (no lock held
56712** on the database file), then an attempt is made to obtain a
56713** SHARED lock on the database file. Immediately after obtaining
56714** the SHARED lock, the file-system is checked for a hot-journal,
56715** which is played back if present.
56716**
56717** If everything is successful, UNQLITE_OK is returned. If an IO error
56718** occurs while locking the database, checking for a hot-journal file or
56719** rolling back a journal file, the IO error code is returned.
56720*/
56721static int pager_shared_lock(Pager *pPager)
56722{
56723 int rc = UNQLITE_OK;
56724 if( pPager->iState == PAGER_OPEN ){
56725 unqlite_kv_methods *pMethods;
56726 /* Open the target database */
56727 rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zFilename,&pPager->pfd,pPager->iOpenFlags);
56728 if( rc != UNQLITE_OK ){
56729 unqliteGenErrorFormat(pPager->pDb,
56730 "IO error while opening the target database file: %s",pPager->zFilename
56731 );
56732 return rc;
56733 }
56734 /* Try to obtain a shared lock */
56735 rc = pager_wait_on_lock(pPager,SHARED_LOCK);
56736 if( rc == UNQLITE_OK ){
56737 if( pPager->iLock <= SHARED_LOCK ){
56738 /* Rollback any hot journal */
56739 rc = pager_journal_rollback(pPager,1);
56740 if( rc != UNQLITE_OK ){
56741 return rc;
56742 }
56743 }
56744 /* Read the database header */
56745 rc = pager_read_db_header(pPager);
56746 if( rc != UNQLITE_OK ){
56747 return rc;
56748 }
56749 if(pPager->dbSize > 0 ){
56750 if( pPager->iOpenFlags & UNQLITE_OPEN_MMAP ){
56751 const jx9_vfs *pVfs = jx9ExportBuiltinVfs();
56752 /* Obtain a read-only memory view of the whole file */
56753 if( pVfs && pVfs->xMmap ){
56754 int vr;
56755 vr = pVfs->xMmap(pPager->zFilename,&pPager->pMmap,&pPager->dbByteSize);
56756 if( vr != JX9_OK ){
56757 /* Generate a warning */
56758 unqliteGenError(pPager->pDb,"Cannot obtain a read-only memory view of the target database");
56759 pPager->iOpenFlags &= ~UNQLITE_OPEN_MMAP;
56760 }
56761 }else{
56762 /* Generate a warning */
56763 unqliteGenError(pPager->pDb,"Cannot obtain a read-only memory view of the target database");
56764 pPager->iOpenFlags &= ~UNQLITE_OPEN_MMAP;
56765 }
56766 }
56767 }
56768 /* Update the pager state */
56769 pPager->iState = PAGER_READER;
56770 /* Invoke the xOpen methods if available */
56771 pMethods = pPager->pEngine->pIo->pMethods;
56772 if( pMethods->xOpen ){
56773 rc = pMethods->xOpen(pPager->pEngine,pPager->dbSize);
56774 if( rc != UNQLITE_OK ){
56775 unqliteGenErrorFormat(pPager->pDb,
56776 "xOpen() method of the underlying KV engine '%z' failed",
56777 &pPager->sKv
56778 );
56779 pager_unlock_db(pPager,NO_LOCK);
56780 pPager->iState = PAGER_OPEN;
56781 return rc;
56782 }
56783 }
56784 }else if( rc == UNQLITE_BUSY ){
56785 unqliteGenError(pPager->pDb,"Another process or thread have a reserved or exclusive lock on this database");
56786 }
56787 }
56788 return rc;
56789}
56790/*
56791** Begin a write-transaction on the specified pager object. If a
56792** write-transaction has already been opened, this function is a no-op.
56793*/
56794UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager)
56795{
56796 int rc;
56797 /* Obtain a shared lock on the database first */
56798 rc = pager_shared_lock(pPager);
56799 if( rc != UNQLITE_OK ){
56800 return rc;
56801 }
56802 if( pPager->iState >= PAGER_WRITER_LOCKED ){
56803 return UNQLITE_OK;
56804 }
56805 if( pPager->is_rdonly ){
56806 unqliteGenError(pPager->pDb,"Read-only database");
56807 /* Read only database */
56808 return UNQLITE_READ_ONLY;
56809 }
56810 /* Obtain a reserved lock on the database */
56811 rc = pager_wait_on_lock(pPager,RESERVED_LOCK);
56812 if( rc == UNQLITE_OK ){
56813 /* Create the bitvec */
56814 pPager->pVec = unqliteBitvecCreate(pPager->pAllocator,pPager->dbSize);
56815 if( pPager->pVec == 0 ){
56816 unqliteGenOutofMem(pPager->pDb);
56817 rc = UNQLITE_NOMEM;
56818 goto fail;
56819 }
56820 /* Change to the WRITER_LOCK state */
56821 pPager->iState = PAGER_WRITER_LOCKED;
56822 pPager->dbOrigSize = pPager->dbSize;
56823 pPager->iJournalOfft = 0;
56824 pPager->nRec = 0;
56825 if( pPager->dbSize < 1 ){
56826 /* Write the database header */
56827 rc = pager_create_header(pPager);
56828 if( rc != UNQLITE_OK ){
56829 goto fail;
56830 }
56831 pPager->dbSize = 1;
56832 }
56833 }else if( rc == UNQLITE_BUSY ){
56834 unqliteGenError(pPager->pDb,"Another process or thread have a reserved lock on this database");
56835 }
56836 return rc;
56837fail:
56838 /* Downgrade to shared lock */
56839 pager_unlock_db(pPager,SHARED_LOCK);
56840 return rc;
56841}
56842/*
56843** This function is called at the start of every write transaction.
56844** There must already be a RESERVED or EXCLUSIVE lock on the database
56845** file when this routine is called.
56846**
56847*/
56848static int unqliteOpenJournal(Pager *pPager)
56849{
56850 unsigned char *zHeader;
56851 int rc = UNQLITE_OK;
56852 if( pPager->is_mem || pPager->no_jrnl ){
56853 /* Journaling is omitted for this database */
56854 goto finish;
56855 }
56856 if( pPager->iState >= PAGER_WRITER_CACHEMOD ){
56857 /* Already opened */
56858 return UNQLITE_OK;
56859 }
56860 /* Delete any previously journal with the same name */
56861 unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
56862 /* Open the journal file */
56863 rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zJournal,
56864 &pPager->pjfd,UNQLITE_OPEN_CREATE|UNQLITE_OPEN_READWRITE);
56865 if( rc != UNQLITE_OK ){
56866 unqliteGenErrorFormat(pPager->pDb,"IO error while opening journal file: %s",pPager->zJournal);
56867 return rc;
56868 }
56869 /* Write the journal header */
56870 zHeader = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iSectorSize);
56871 if( zHeader == 0 ){
56872 rc = UNQLITE_NOMEM;
56873 goto fail;
56874 }
56875 pager_write_journal_header(pPager,zHeader);
56876 /* Perform the disk write */
56877 rc = unqliteOsWrite(pPager->pjfd,zHeader,pPager->iSectorSize,0);
56878 /* Offset to start writing from */
56879 pPager->iJournalOfft = pPager->iSectorSize;
56880 /* All done, journal will be synced later */
56881 SyMemBackendFree(pPager->pAllocator,zHeader);
56882finish:
56883 if( rc == UNQLITE_OK ){
56884 pPager->iState = PAGER_WRITER_CACHEMOD;
56885 return UNQLITE_OK;
56886 }
56887fail:
56888 /* Unlink the journal file if something goes wrong */
56889 unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
56890 unqliteOsDelete(pPager->pVfs,pPager->zJournal,0);
56891 pPager->pjfd = 0;
56892 return rc;
56893}
56894/*
56895** Sync the journal. In other words, make sure all the pages that have
56896** been written to the journal have actually reached the surface of the
56897** disk and can be restored in the event of a hot-journal rollback.
56898*
56899* This routine try also to obtain an exlusive lock on the database.
56900*/
56901static int unqliteFinalizeJournal(Pager *pPager,int *pRetry,int close_jrnl)
56902{
56903 int rc;
56904 *pRetry = 0;
56905 /* Grab the exclusive lock first */
56906 rc = pager_lock_db(pPager,EXCLUSIVE_LOCK);
56907 if( rc != UNQLITE_OK ){
56908 /* Retry the excusive lock process */
56909 *pRetry = 1;
56910 rc = UNQLITE_OK;
56911 }
56912 if( pPager->no_jrnl ){
56913 /* Journaling is omitted, return immediately */
56914 return UNQLITE_OK;
56915 }
56916 /* Write the total number of database records */
56917 rc = WriteInt32(pPager->pjfd,pPager->nRec,8 /* sizeof(aJournalRec) */);
56918 if( rc != UNQLITE_OK ){
56919 if( pPager->nRec > 0 ){
56920 return rc;
56921 }else{
56922 /* Not so fatal */
56923 rc = UNQLITE_OK;
56924 }
56925 }
56926 /* Sync the journal and close it */
56927 rc = unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
56928 if( close_jrnl ){
56929 /* close the journal file */
56930 if( UNQLITE_OK != unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd) ){
56931 if( rc != UNQLITE_OK /* unqliteOsSync */ ){
56932 return rc;
56933 }
56934 }
56935 pPager->pjfd = 0;
56936 }
56937 if( (*pRetry) == 1 ){
56938 if( pager_lock_db(pPager,EXCLUSIVE_LOCK) == UNQLITE_OK ){
56939 /* Got exclusive lock */
56940 *pRetry = 0;
56941 }
56942 }
56943 return UNQLITE_OK;
56944}
56945/*
56946 * Mark a single data page as writeable. The page is written into the
56947 * main journal as required.
56948 */
56949static int page_write(Pager *pPager,Page *pPage)
56950{
56951 int rc;
56952 if( !pPager->is_mem && !pPager->no_jrnl ){
56953 /* Write the page to the transaction journal */
56954 if( pPage->pgno < pPager->dbOrigSize && !unqliteBitvecTest(pPager->pVec,pPage->pgno) ){
56955 sxu32 cksum;
56956 if( pPager->nRec == SXU32_HIGH ){
56957 /* Journal Limit reached */
56958 unqliteGenError(pPager->pDb,"Journal record limit reached, commit your changes");
56959 return UNQLITE_LIMIT;
56960 }
56961 /* Write the page number */
56962 rc = WriteInt64(pPager->pjfd,pPage->pgno,pPager->iJournalOfft);
56963 if( rc != UNQLITE_OK ){ return rc; }
56964 /* Write the raw page */
56965 /** CODEC */
56966 rc = unqliteOsWrite(pPager->pjfd,pPage->zData,pPager->iPageSize,pPager->iJournalOfft + 8);
56967 if( rc != UNQLITE_OK ){ return rc; }
56968 /* Compute the checksum */
56969 cksum = pager_cksum(pPager,pPage->zData);
56970 rc = WriteInt32(pPager->pjfd,cksum,pPager->iJournalOfft + 8 + pPager->iPageSize);
56971 if( rc != UNQLITE_OK ){ return rc; }
56972 /* Update the journal offset */
56973 pPager->iJournalOfft += 8 /* page num */ + pPager->iPageSize + 4 /* cksum */;
56974 pPager->nRec++;
56975 /* Mark as journalled */
56976 unqliteBitvecSet(pPager->pVec,pPage->pgno);
56977 }
56978 }
56979 /* Add the page to the dirty list */
56980 pager_page_to_dirty_list(pPager,pPage);
56981 /* Update the database size and return. */
56982 if( (1 + pPage->pgno) > pPager->dbSize ){
56983 pPager->dbSize = 1 + pPage->pgno;
56984 if( pPager->dbSize == SXU64_HIGH ){
56985 unqliteGenError(pPager->pDb,"Database maximum page limit (64-bit) reached");
56986 return UNQLITE_LIMIT;
56987 }
56988 }
56989 return UNQLITE_OK;
56990}
56991/*
56992** The argument is the first in a linked list of dirty pages connected
56993** by the PgHdr.pDirty pointer. This function writes each one of the
56994** in-memory pages in the list to the database file. The argument may
56995** be NULL, representing an empty list. In this case this function is
56996** a no-op.
56997**
56998** The pager must hold at least a RESERVED lock when this function
56999** is called. Before writing anything to the database file, this lock
57000** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained,
57001** UNQLITE_BUSY is returned and no data is written to the database file.
57002*/
57003static int pager_write_dirty_pages(Pager *pPager,Page *pDirty)
57004{
57005 int rc = UNQLITE_OK;
57006 Page *pNext;
57007 for(;;){
57008 if( pDirty == 0 ){
57009 break;
57010 }
57011 /* Point to the next dirty page */
57012 pNext = pDirty->pDirtyPrev; /* Not a bug: Reverse link */
57013 if( (pDirty->flags & PAGE_DONT_WRITE) == 0 ){
57014 rc = unqliteOsWrite(pPager->pfd,pDirty->zData,pPager->iPageSize,pDirty->pgno * pPager->iPageSize);
57015 if( rc != UNQLITE_OK ){
57016 /* A rollback should be done */
57017 break;
57018 }
57019 }
57020 /* Remove stale flags */
57021 pDirty->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
57022 if( pDirty->nRef < 1 ){
57023 /* Unlink the page now it is unused */
57024 pager_unlink_page(pPager,pDirty);
57025 /* Release the page */
57026 pager_release_page(pPager,pDirty);
57027 }
57028 /* Point to the next page */
57029 pDirty = pNext;
57030 }
57031 pPager->pDirty = pPager->pFirstDirty = 0;
57032 pPager->pHotDirty = pPager->pFirstHot = 0;
57033 pPager->nHot = 0;
57034 return rc;
57035}
57036/*
57037** The argument is the first in a linked list of hot dirty pages connected
57038** by the PgHdr.pHotDirty pointer. This function writes each one of the
57039** in-memory pages in the list to the database file. The argument may
57040** be NULL, representing an empty list. In this case this function is
57041** a no-op.
57042**
57043** The pager must hold at least a RESERVED lock when this function
57044** is called. Before writing anything to the database file, this lock
57045** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained,
57046** UNQLITE_BUSY is returned and no data is written to the database file.
57047*/
57048static int pager_write_hot_dirty_pages(Pager *pPager,Page *pDirty)
57049{
57050 int rc = UNQLITE_OK;
57051 Page *pNext;
57052 for(;;){
57053 if( pDirty == 0 ){
57054 break;
57055 }
57056 /* Point to the next page */
57057 pNext = pDirty->pPrevHot; /* Not a bug: Reverse link */
57058 if( (pDirty->flags & PAGE_DONT_WRITE) == 0 ){
57059 rc = unqliteOsWrite(pPager->pfd,pDirty->zData,pPager->iPageSize,pDirty->pgno * pPager->iPageSize);
57060 if( rc != UNQLITE_OK ){
57061 break;
57062 }
57063 }
57064 /* Remove stale flags */
57065 pDirty->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
57066 /* Unlink from the list of dirty pages */
57067 if( pDirty->pDirtyPrev ){
57068 pDirty->pDirtyPrev->pDirtyNext = pDirty->pDirtyNext;
57069 }else{
57070 pPager->pDirty = pDirty->pDirtyNext;
57071 }
57072 if( pDirty->pDirtyNext ){
57073 pDirty->pDirtyNext->pDirtyPrev = pDirty->pDirtyPrev;
57074 }else{
57075 pPager->pFirstDirty = pDirty->pDirtyPrev;
57076 }
57077 /* Discard */
57078 pager_unlink_page(pPager,pDirty);
57079 /* Release the page */
57080 pager_release_page(pPager,pDirty);
57081 /* Next hot page */
57082 pDirty = pNext;
57083 }
57084 return rc;
57085}
57086/*
57087 * Commit a transaction: Phase one.
57088 */
57089static int pager_commit_phase1(Pager *pPager)
57090{
57091 int get_excl = 0;
57092 Page *pDirty;
57093 int rc;
57094 /* If no database changes have been made, return early. */
57095 if( pPager->iState < PAGER_WRITER_CACHEMOD ){
57096 return UNQLITE_OK;
57097 }
57098 if( pPager->is_mem ){
57099 /* An in-memory database */
57100 return UNQLITE_OK;
57101 }
57102 if( pPager->is_rdonly ){
57103 /* Read-Only DB */
57104 unqliteGenError(pPager->pDb,"Read-Only database");
57105 return UNQLITE_READ_ONLY;
57106 }
57107 /* Finalize the journal file */
57108 rc = unqliteFinalizeJournal(pPager,&get_excl,1);
57109 if( rc != UNQLITE_OK ){
57110 return rc;
57111 }
57112 /* Get the dirty pages */
57113 pDirty = pager_get_dirty_pages(pPager);
57114 if( get_excl ){
57115 /* Wait one last time for the exclusive lock */
57116 rc = pager_wait_on_lock(pPager,EXCLUSIVE_LOCK);
57117 if( rc != UNQLITE_OK ){
57118 unqliteGenError(pPager->pDb,"Cannot obtain an Exclusive lock on the target database");
57119 return rc;
57120 }
57121 }
57122 if( pPager->iFlags & PAGER_CTRL_DIRTY_COMMIT ){
57123 /* Synce the database first if a dirty commit have been applied */
57124 unqliteOsSync(pPager->pfd,UNQLITE_SYNC_NORMAL);
57125 }
57126 /* Write the dirty pages */
57127 rc = pager_write_dirty_pages(pPager,pDirty);
57128 if( rc != UNQLITE_OK ){
57129 /* Rollback your DB */
57130 pPager->iFlags |= PAGER_CTRL_COMMIT_ERR;
57131 pPager->pFirstDirty = pDirty;
57132 unqliteGenError(pPager->pDb,"IO error while writing dirty pages, rollback your database");
57133 return rc;
57134 }
57135 /* If the file on disk is not the same size as the database image,
57136 * then use unqliteOsTruncate to grow or shrink the file here.
57137 */
57138 if( pPager->dbSize != pPager->dbOrigSize ){
57139 unqliteOsTruncate(pPager->pfd,pPager->iPageSize * pPager->dbSize);
57140 }
57141 /* Sync the database file */
57142 unqliteOsSync(pPager->pfd,UNQLITE_SYNC_FULL);
57143 /* Remove stale flags */
57144 pPager->iJournalOfft = 0;
57145 pPager->nRec = 0;
57146 return UNQLITE_OK;
57147}
57148/*
57149 * Commit a transaction: Phase two.
57150 */
57151static int pager_commit_phase2(Pager *pPager)
57152{
57153 if( !pPager->is_mem ){
57154 if( pPager->iState == PAGER_OPEN ){
57155 return UNQLITE_OK;
57156 }
57157 if( pPager->iState != PAGER_READER ){
57158 if( !pPager->no_jrnl ){
57159 /* Finally, unlink the journal file */
57160 unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
57161 }
57162 /* Downgrade to shraed lock */
57163 pager_unlock_db(pPager,SHARED_LOCK);
57164 pPager->iState = PAGER_READER;
57165 if( pPager->pVec ){
57166 unqliteBitvecDestroy(pPager->pVec);
57167 pPager->pVec = 0;
57168 }
57169 }
57170 }
57171 return UNQLITE_OK;
57172}
57173/*
57174 * Perform a dirty commit.
57175 */
57176static int pager_dirty_commit(Pager *pPager)
57177{
57178 int get_excl = 0;
57179 Page *pHot;
57180 int rc;
57181 /* Finalize the journal file without closing it */
57182 rc = unqliteFinalizeJournal(pPager,&get_excl,0);
57183 if( rc != UNQLITE_OK ){
57184 /* It's not a fatal error if something goes wrong here since
57185 * its not the final commit.
57186 */
57187 return UNQLITE_OK;
57188 }
57189 /* Point to the list of hot pages */
57190 pHot = pager_get_hot_pages(pPager);
57191 if( pHot == 0 ){
57192 return UNQLITE_OK;
57193 }
57194 if( get_excl ){
57195 /* Wait one last time for the exclusive lock */
57196 rc = pager_wait_on_lock(pPager,EXCLUSIVE_LOCK);
57197 if( rc != UNQLITE_OK ){
57198 /* Not so fatal, will try another time */
57199 return UNQLITE_OK;
57200 }
57201 }
57202 /* Tell that a dirty commit happen */
57203 pPager->iFlags |= PAGER_CTRL_DIRTY_COMMIT;
57204 /* Write the hot pages now */
57205 rc = pager_write_hot_dirty_pages(pPager,pHot);
57206 if( rc != UNQLITE_OK ){
57207 pPager->iFlags |= PAGER_CTRL_COMMIT_ERR;
57208 unqliteGenError(pPager->pDb,"IO error while writing hot dirty pages, rollback your database");
57209 return rc;
57210 }
57211 pPager->pFirstHot = pPager->pHotDirty = 0;
57212 pPager->nHot = 0;
57213 /* No need to sync the database file here, since the journal is already
57214 * open here and this is not the final commit.
57215 */
57216 return UNQLITE_OK;
57217}
57218/*
57219** Commit a transaction and sync the database file for the pager pPager.
57220**
57221** This routine ensures that:
57222**
57223** * the journal is synced,
57224** * all dirty pages are written to the database file,
57225** * the database file is truncated (if required), and
57226** * the database file synced.
57227** * the journal file is deleted.
57228*/
57229UNQLITE_PRIVATE int unqlitePagerCommit(Pager *pPager)
57230{
57231 int rc;
57232 /* Commit: Phase One */
57233 rc = pager_commit_phase1(pPager);
57234 if( rc != UNQLITE_OK ){
57235 goto fail;
57236 }
57237 /* Commit: Phase Two */
57238 rc = pager_commit_phase2(pPager);
57239 if( rc != UNQLITE_OK ){
57240 goto fail;
57241 }
57242 /* Remove stale flags */
57243 pPager->iFlags &= ~PAGER_CTRL_COMMIT_ERR;
57244 /* All done */
57245 return UNQLITE_OK;
57246fail:
57247 /* Disable the auto-commit flag */
57248 pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
57249 return rc;
57250}
57251/*
57252 * Reset the pager to its initial state. This is caused by
57253 * a rollback operation.
57254 */
57255static int pager_reset_state(Pager *pPager,int bResetKvEngine)
57256{
57257 unqlite_kv_engine *pEngine = pPager->pEngine;
57258 Page *pNext,*pPtr = pPager->pAll;
57259 const unqlite_kv_io *pIo;
57260 int rc;
57261 /* Remove stale flags */
57262 pPager->iFlags &= ~(PAGER_CTRL_COMMIT_ERR|PAGER_CTRL_DIRTY_COMMIT);
57263 pPager->iJournalOfft = 0;
57264 pPager->nRec = 0;
57265 /* Database original size */
57266 pPager->dbSize = pPager->dbOrigSize;
57267 /* Discard all in-memory pages */
57268 for(;;){
57269 if( pPtr == 0 ){
57270 break;
57271 }
57272 pNext = pPtr->pNext; /* Reverse link */
57273 /* Remove stale flags */
57274 pPtr->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
57275 /* Release the page */
57276 pager_release_page(pPager,pPtr);
57277 /* Point to the next page */
57278 pPtr = pNext;
57279 }
57280 pPager->pAll = 0;
57281 pPager->nPage = 0;
57282 pPager->pDirty = pPager->pFirstDirty = 0;
57283 pPager->pHotDirty = pPager->pFirstHot = 0;
57284 pPager->nHot = 0;
57285 if( pPager->apHash ){
57286 /* Zero the table */
57287 SyZero((void *)pPager->apHash,sizeof(Page *) * pPager->nSize);
57288 }
57289 if( pPager->pVec ){
57290 unqliteBitvecDestroy(pPager->pVec);
57291 pPager->pVec = 0;
57292 }
57293 /* Switch back to shared lock */
57294 pager_unlock_db(pPager,SHARED_LOCK);
57295 pPager->iState = PAGER_READER;
57296 if( bResetKvEngine ){
57297 /* Reset the underlying KV engine */
57298 pIo = pEngine->pIo;
57299 if( pIo->pMethods->xRelease ){
57300 /* Call the release callback */
57301 pIo->pMethods->xRelease(pEngine);
57302 }
57303 /* Zero the structure */
57304 SyZero(pEngine,(sxu32)pIo->pMethods->szKv);
57305 /* Fill in */
57306 pEngine->pIo = pIo;
57307 if( pIo->pMethods->xInit ){
57308 /* Call the init method */
57309 rc = pIo->pMethods->xInit(pEngine,pPager->iPageSize);
57310 if( rc != UNQLITE_OK ){
57311 return rc;
57312 }
57313 }
57314 if( pIo->pMethods->xOpen ){
57315 /* Call the xOpen method */
57316 rc = pIo->pMethods->xOpen(pEngine,pPager->dbSize);
57317 if( rc != UNQLITE_OK ){
57318 return rc;
57319 }
57320 }
57321 }
57322 /* All done */
57323 return UNQLITE_OK;
57324}
57325/*
57326** If a write transaction is open, then all changes made within the
57327** transaction are reverted and the current write-transaction is closed.
57328** The pager falls back to PAGER_READER state if successful.
57329**
57330** Otherwise, in rollback mode, this function performs two functions:
57331**
57332** 1) It rolls back the journal file, restoring all database file and
57333** in-memory cache pages to the state they were in when the transaction
57334** was opened, and
57335**
57336** 2) It finalizes the journal file, so that it is not used for hot
57337** rollback at any point in the future (i.e. deletion).
57338**
57339** Finalization of the journal file (task 2) is only performed if the
57340** rollback is successful.
57341**
57342*/
57343UNQLITE_PRIVATE int unqlitePagerRollback(Pager *pPager,int bResetKvEngine)
57344{
57345 int rc = UNQLITE_OK;
57346 if( pPager->iState < PAGER_WRITER_LOCKED ){
57347 /* A write transaction must be opened */
57348 return UNQLITE_OK;
57349 }
57350 if( pPager->is_mem ){
57351 /* As of this release 1.1.6: Transactions are not supported for in-memory databases */
57352 return UNQLITE_OK;
57353 }
57354 if( pPager->is_rdonly ){
57355 /* Read-Only DB */
57356 unqliteGenError(pPager->pDb,"Read-Only database");
57357 return UNQLITE_READ_ONLY;
57358 }
57359 if( pPager->iState >= PAGER_WRITER_CACHEMOD ){
57360 if( !pPager->no_jrnl ){
57361 /* Close any outstanding joural file */
57362 if( pPager->pjfd ){
57363 /* Sync the journal file */
57364 unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
57365 }
57366 unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
57367 pPager->pjfd = 0;
57368 if( pPager->iFlags & (PAGER_CTRL_COMMIT_ERR|PAGER_CTRL_DIRTY_COMMIT) ){
57369 /* Perform the rollback */
57370 rc = pager_journal_rollback(pPager,0);
57371 if( rc != UNQLITE_OK ){
57372 /* Set the auto-commit flag */
57373 pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
57374 return rc;
57375 }
57376 }
57377 }
57378 /* Unlink the journal file */
57379 unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
57380 /* Reset the pager state */
57381 rc = pager_reset_state(pPager,bResetKvEngine);
57382 if( rc != UNQLITE_OK ){
57383 /* Mostly an unlikely scenario */
57384 pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT; /* Set the auto-commit flag */
57385 unqliteGenError(pPager->pDb,"Error while reseting pager to its initial state");
57386 return rc;
57387 }
57388 }else{
57389 /* Downgrade to shared lock */
57390 pager_unlock_db(pPager,SHARED_LOCK);
57391 pPager->iState = PAGER_READER;
57392 }
57393 return UNQLITE_OK;
57394}
57395/*
57396 * Mark a data page as non writeable.
57397 */
57398static int unqlitePagerDontWrite(unqlite_page *pMyPage)
57399{
57400 Page *pPage = (Page *)pMyPage;
57401 if( pPage->pgno > 0 /* Page 0 is always writeable */ ){
57402 pPage->flags |= PAGE_DONT_WRITE;
57403 }
57404 return UNQLITE_OK;
57405}
57406/*
57407** Mark a data page as writeable. This routine must be called before
57408** making changes to a page. The caller must check the return value
57409** of this function and be careful not to change any page data unless
57410** this routine returns UNQLITE_OK.
57411*/
57412static int unqlitePageWrite(unqlite_page *pMyPage)
57413{
57414 Page *pPage = (Page *)pMyPage;
57415 Pager *pPager = pPage->pPager;
57416 int rc;
57417 /* Begin the write transaction */
57418 rc = unqlitePagerBegin(pPager);
57419 if( rc != UNQLITE_OK ){
57420 return rc;
57421 }
57422 if( pPager->iState == PAGER_WRITER_LOCKED ){
57423 /* The journal file needs to be opened. Higher level routines have already
57424 ** obtained the necessary locks to begin the write-transaction, but the
57425 ** rollback journal might not yet be open. Open it now if this is the case.
57426 */
57427 rc = unqliteOpenJournal(pPager);
57428 if( rc != UNQLITE_OK ){
57429 return rc;
57430 }
57431 }
57432 if( pPager->nHot > 127 ){
57433 /* Write hot dirty pages */
57434 rc = pager_dirty_commit(pPager);
57435 if( rc != UNQLITE_OK ){
57436 /* A rollback must be done */
57437 unqliteGenError(pPager->pDb,"Please perform a rollback");
57438 return rc;
57439 }
57440 }
57441 /* Write the page to the journal file */
57442 rc = page_write(pPager,pPage);
57443 return rc;
57444}
57445/*
57446** Acquire a reference to page number pgno in pager pPager (a page
57447** reference has type unqlite_page*). If the requested reference is
57448** successfully obtained, it is copied to *ppPage and UNQLITE_OK returned.
57449**
57450** If the requested page is already in the cache, it is returned.
57451** Otherwise, a new page object is allocated and populated with data
57452** read from the database file.
57453*/
57454static int unqlitePagerAcquire(
57455 Pager *pPager, /* The pager open on the database file */
57456 pgno pgno, /* Page number to fetch */
57457 unqlite_page **ppPage, /* OUT: Acquired page */
57458 int fetchOnly, /* Cache lookup only */
57459 int noContent /* Do not bother reading content from disk if true */
57460)
57461{
57462 Page *pPage;
57463 int rc;
57464 /* Acquire a shared lock (if not yet done) on the database and rollback any hot-journal if present */
57465 rc = pager_shared_lock(pPager);
57466 if( rc != UNQLITE_OK ){
57467 return rc;
57468 }
57469 /* Fetch the page from the cache */
57470 pPage = pager_fetch_page(pPager,pgno);
57471 if( fetchOnly ){
57472 if( ppPage ){
57473 *ppPage = (unqlite_page *)pPage;
57474 }
57475 return pPage ? UNQLITE_OK : UNQLITE_NOTFOUND;
57476 }
57477 if( pPage == 0 ){
57478 /* Allocate a new page */
57479 pPage = pager_alloc_page(pPager,pgno);
57480 if( pPage == 0 ){
57481 unqliteGenOutofMem(pPager->pDb);
57482 return UNQLITE_NOMEM;
57483 }
57484 /* Read page contents */
57485 rc = pager_get_page_contents(pPager,pPage,noContent);
57486 if( rc != UNQLITE_OK ){
57487 SyMemBackendPoolFree(pPager->pAllocator,pPage);
57488 return rc;
57489 }
57490 /* Link the page */
57491 pager_link_page(pPager,pPage);
57492 }else{
57493 if( ppPage ){
57494 page_ref(pPage);
57495 }
57496 }
57497 /* All done, page is loaded in memeory */
57498 if( ppPage ){
57499 *ppPage = (unqlite_page *)pPage;
57500 }
57501 return UNQLITE_OK;
57502}
57503/*
57504 * Return true if we are dealing with an in-memory database.
57505 */
57506static int unqliteInMemory(const char *zFilename)
57507{
57508 sxu32 n;
57509 if( SX_EMPTY_STR(zFilename) ){
57510 /* NULL or the empty string means an in-memory database */
57511 return TRUE;
57512 }
57513 n = SyStrlen(zFilename);
57514 if( n == sizeof(":mem:") - 1 &&
57515 SyStrnicmp(zFilename,":mem:",sizeof(":mem:") - 1) == 0 ){
57516 return TRUE;
57517 }
57518 if( n == sizeof(":memory:") - 1 &&
57519 SyStrnicmp(zFilename,":memory:",sizeof(":memory:") - 1) == 0 ){
57520 return TRUE;
57521 }
57522 return FALSE;
57523}
57524/*
57525 * Allocate a new KV cursor.
57526 */
57527UNQLITE_PRIVATE int unqliteInitCursor(unqlite *pDb,unqlite_kv_cursor **ppOut)
57528{
57529 unqlite_kv_methods *pMethods;
57530 unqlite_kv_cursor *pCur;
57531 sxu32 nByte;
57532 /* Storage engine methods */
57533 pMethods = pDb->sDB.pPager->pEngine->pIo->pMethods;
57534 if( pMethods->szCursor < 1 ){
57535 /* Implementation does not supprt cursors */
57536 unqliteGenErrorFormat(pDb,"Storage engine '%s' does not support cursors",pMethods->zName);
57537 return UNQLITE_NOTIMPLEMENTED;
57538 }
57539 nByte = pMethods->szCursor;
57540 if( nByte < sizeof(unqlite_kv_cursor) ){
57541 nByte += sizeof(unqlite_kv_cursor);
57542 }
57543 pCur = (unqlite_kv_cursor *)SyMemBackendPoolAlloc(&pDb->sMem,nByte);
57544 if( pCur == 0 ){
57545 unqliteGenOutofMem(pDb);
57546 return UNQLITE_NOMEM;
57547 }
57548 /* Zero the structure */
57549 SyZero(pCur,nByte);
57550 /* Save the cursor */
57551 pCur->pStore = pDb->sDB.pPager->pEngine;
57552 /* Invoke the initialization callback if any */
57553 if( pMethods->xCursorInit ){
57554 pMethods->xCursorInit(pCur);
57555 }
57556 /* All done */
57557 *ppOut = pCur;
57558 return UNQLITE_OK;
57559}
57560/*
57561 * Release a cursor.
57562 */
57563UNQLITE_PRIVATE int unqliteReleaseCursor(unqlite *pDb,unqlite_kv_cursor *pCur)
57564{
57565 unqlite_kv_methods *pMethods;
57566 /* Storage engine methods */
57567 pMethods = pDb->sDB.pPager->pEngine->pIo->pMethods;
57568 /* Invoke the release callback if available */
57569 if( pMethods->xCursorRelease ){
57570 pMethods->xCursorRelease(pCur);
57571 }
57572 /* Finally, free the whole instance */
57573 SyMemBackendPoolFree(&pDb->sMem,pCur);
57574 return UNQLITE_OK;
57575}
57576/*
57577 * Release the underlying KV storage engine and invoke
57578 * its associated callbacks if available.
57579 */
57580static void pager_release_kv_engine(Pager *pPager)
57581{
57582 unqlite_kv_engine *pEngine = pPager->pEngine;
57583 unqlite_db *pStorage = &pPager->pDb->sDB;
57584 if( pStorage->pCursor ){
57585 /* Release the associated cursor */
57586 unqliteReleaseCursor(pPager->pDb,pStorage->pCursor);
57587 pStorage->pCursor = 0;
57588 }
57589 if( pEngine->pIo->pMethods->xRelease ){
57590 pEngine->pIo->pMethods->xRelease(pEngine);
57591 }
57592 /* Release the whole instance */
57593 SyMemBackendFree(&pPager->pDb->sMem,(void *)pEngine->pIo);
57594 SyMemBackendFree(&pPager->pDb->sMem,(void *)pEngine);
57595 pPager->pEngine = 0;
57596}
57597/* Forward declaration */
57598static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_kv_io *pIo);
57599/*
57600 * Allocate, initialize and register a new KV storage engine
57601 * within this database instance.
57602 */
57603UNQLITE_PRIVATE int unqlitePagerRegisterKvEngine(Pager *pPager,unqlite_kv_methods *pMethods)
57604{
57605 unqlite_db *pStorage = &pPager->pDb->sDB;
57606 unqlite *pDb = pPager->pDb;
57607 unqlite_kv_engine *pEngine;
57608 unqlite_kv_io *pIo;
57609 sxu32 nByte;
57610 int rc;
57611 if( pPager->pEngine ){
57612 if( pMethods == pPager->pEngine->pIo->pMethods ){
57613 /* Ticket 1432: Same implementation */
57614 return UNQLITE_OK;
57615 }
57616 /* Release the old KV engine */
57617 pager_release_kv_engine(pPager);
57618 }
57619 /* Allocate a new KV engine instance */
57620 nByte = (sxu32)pMethods->szKv;
57621 pEngine = (unqlite_kv_engine *)SyMemBackendAlloc(&pDb->sMem,nByte);
57622 if( pEngine == 0 ){
57623 unqliteGenOutofMem(pDb);
57624 return UNQLITE_NOMEM;
57625 }
57626 pIo = (unqlite_kv_io *)SyMemBackendAlloc(&pDb->sMem,sizeof(unqlite_kv_io));
57627 if( pIo == 0 ){
57628 SyMemBackendFree(&pDb->sMem,pEngine);
57629 unqliteGenOutofMem(pDb);
57630 return UNQLITE_NOMEM;
57631 }
57632 /* Zero the structure */
57633 SyZero(pIo,sizeof(unqlite_io_methods));
57634 SyZero(pEngine,nByte);
57635 /* Populate the IO structure */
57636 pager_kv_io_init(pPager,pMethods,pIo);
57637 pEngine->pIo = pIo;
57638 /* Invoke the init callback if avaialble */
57639 if( pMethods->xInit ){
57640 rc = pMethods->xInit(pEngine,unqliteGetPageSize());
57641 if( rc != UNQLITE_OK ){
57642 unqliteGenErrorFormat(pDb,
57643 "xInit() method of the underlying KV engine '%z' failed",&pPager->sKv);
57644 goto fail;
57645 }
57646 pEngine->pIo = pIo;
57647 }
57648 pPager->pEngine = pEngine;
57649 /* Allocate a new cursor */
57650 rc = unqliteInitCursor(pDb,&pStorage->pCursor);
57651 if( rc != UNQLITE_OK ){
57652 goto fail;
57653 }
57654 return UNQLITE_OK;
57655fail:
57656 SyMemBackendFree(&pDb->sMem,pEngine);
57657 SyMemBackendFree(&pDb->sMem,pIo);
57658 return rc;
57659}
57660/*
57661 * Return the underlying KV storage engine instance.
57662 */
57663UNQLITE_PRIVATE unqlite_kv_engine * unqlitePagerGetKvEngine(unqlite *pDb)
57664{
57665 return pDb->sDB.pPager->pEngine;
57666}
57667/*
57668* Allocate and initialize a new Pager object. The pager should
57669* eventually be freed by passing it to unqlitePagerClose().
57670*
57671* The zFilename argument is the path to the database file to open.
57672* If zFilename is NULL or ":memory:" then all information is held
57673* in cache. It is never written to disk. This can be used to implement
57674* an in-memory database.
57675*/
57676UNQLITE_PRIVATE int unqlitePagerOpen(
57677 unqlite_vfs *pVfs, /* The virtual file system to use */
57678 unqlite *pDb, /* Database handle */
57679 const char *zFilename, /* Name of the database file to open */
57680 unsigned int iFlags /* flags controlling this file */
57681 )
57682{
57683 unqlite_kv_methods *pMethods = 0;
57684 int is_mem,rd_only,no_jrnl;
57685 Pager *pPager;
57686 sxu32 nByte;
57687 sxu32 nLen;
57688 int rc;
57689
57690 /* Select the appropriate KV storage subsytem */
57691 if( (iFlags & UNQLITE_OPEN_IN_MEMORY) || unqliteInMemory(zFilename) ){
57692 /* An in-memory database, record that */
57693 pMethods = unqliteFindKVStore("mem",sizeof("mem") - 1); /* Always available */
57694 iFlags |= UNQLITE_OPEN_IN_MEMORY;
57695 }else{
57696 /* Install the default key value storage subsystem [i.e. Linear Hash] */
57697 pMethods = unqliteFindKVStore("hash",sizeof("hash")-1);
57698 if( pMethods == 0 ){
57699 /* Use the b+tree storage backend if the linear hash storage is not available */
57700 pMethods = unqliteFindKVStore("btree",sizeof("btree")-1);
57701 }
57702 }
57703 if( pMethods == 0 ){
57704 /* Can't happen */
57705 unqliteGenError(pDb,"Cannot install a default Key/Value storage engine");
57706 return UNQLITE_NOTIMPLEMENTED;
57707 }
57708 is_mem = (iFlags & UNQLITE_OPEN_IN_MEMORY) != 0;
57709 rd_only = (iFlags & UNQLITE_OPEN_READONLY) != 0;
57710 no_jrnl = (iFlags & UNQLITE_OPEN_OMIT_JOURNALING) != 0;
57711 rc = UNQLITE_OK;
57712 if( is_mem ){
57713 /* Omit journaling for in-memory database */
57714 no_jrnl = 1;
57715 }
57716 /* Total number of bytes to allocate */
57717 nByte = sizeof(Pager);
57718 nLen = 0;
57719 if( !is_mem ){
57720 nLen = SyStrlen(zFilename);
57721 nByte += pVfs->mxPathname + nLen + sizeof(char) /* null termniator */;
57722 }
57723 /* Allocate */
57724 pPager = (Pager *)SyMemBackendAlloc(&pDb->sMem,nByte);
57725 if( pPager == 0 ){
57726 return UNQLITE_NOMEM;
57727 }
57728 /* Zero the structure */
57729 SyZero(pPager,nByte);
57730 /* Fill-in the structure */
57731 pPager->pAllocator = &pDb->sMem;
57732 pPager->pDb = pDb;
57733 pDb->sDB.pPager = pPager;
57734 /* Allocate page table */
57735 pPager->nSize = 128; /* Must be a power of two */
57736 nByte = pPager->nSize * sizeof(Page *);
57737 pPager->apHash = (Page **)SyMemBackendAlloc(pPager->pAllocator,nByte);
57738 if( pPager->apHash == 0 ){
57739 rc = UNQLITE_NOMEM;
57740 goto fail;
57741 }
57742 SyZero(pPager->apHash,nByte);
57743 pPager->is_mem = is_mem;
57744 pPager->no_jrnl = no_jrnl;
57745 pPager->is_rdonly = rd_only;
57746 pPager->iOpenFlags = iFlags;
57747 pPager->pVfs = pVfs;
57748 SyRandomnessInit(&pPager->sPrng,0,0);
57749 SyRandomness(&pPager->sPrng,(void *)&pPager->cksumInit,sizeof(sxu32));
57750 /* Unlimited cache size */
57751 pPager->nCacheMax = SXU32_HIGH;
57752 /* Copy filename and journal name */
57753 if( !is_mem ){
57754 pPager->zFilename = (char *)&pPager[1];
57755 rc = UNQLITE_OK;
57756 if( pVfs->xFullPathname ){
57757 rc = pVfs->xFullPathname(pVfs,zFilename,pVfs->mxPathname + nLen,pPager->zFilename);
57758 }
57759 if( rc != UNQLITE_OK ){
57760 /* Simple filename copy */
57761 SyMemcpy(zFilename,pPager->zFilename,nLen);
57762 pPager->zFilename[nLen] = 0;
57763 rc = UNQLITE_OK;
57764 }else{
57765 nLen = SyStrlen(pPager->zFilename);
57766 }
57767 pPager->zJournal = (char *) SyMemBackendAlloc(pPager->pAllocator,nLen + sizeof(UNQLITE_JOURNAL_FILE_SUFFIX) + sizeof(char));
57768 if( pPager->zJournal == 0 ){
57769 rc = UNQLITE_NOMEM;
57770 goto fail;
57771 }
57772 /* Copy filename */
57773 SyMemcpy(pPager->zFilename,pPager->zJournal,nLen);
57774 /* Copy journal suffix */
57775 SyMemcpy(UNQLITE_JOURNAL_FILE_SUFFIX,&pPager->zJournal[nLen],sizeof(UNQLITE_JOURNAL_FILE_SUFFIX)-1);
57776 /* Append the nul terminator to the journal path */
57777 pPager->zJournal[nLen + ( sizeof(UNQLITE_JOURNAL_FILE_SUFFIX) - 1)] = 0;
57778 }
57779 /* Finally, register the selected KV engine */
57780 rc = unqlitePagerRegisterKvEngine(pPager,pMethods);
57781 if( rc != UNQLITE_OK ){
57782 goto fail;
57783 }
57784 /* Set the pager state */
57785 if( pPager->is_mem ){
57786 pPager->iState = PAGER_WRITER_FINISHED;
57787 pPager->iLock = EXCLUSIVE_LOCK;
57788 }else{
57789 pPager->iState = PAGER_OPEN;
57790 pPager->iLock = NO_LOCK;
57791 }
57792 /* All done, ready for processing */
57793 return UNQLITE_OK;
57794fail:
57795 SyMemBackendFree(&pDb->sMem,pPager);
57796 return rc;
57797}
57798/*
57799 * Set a cache limit. Note that, this is a simple hint, the pager is not
57800 * forced to honor this limit.
57801 */
57802UNQLITE_PRIVATE int unqlitePagerSetCachesize(Pager *pPager,int mxPage)
57803{
57804 if( mxPage < 256 ){
57805 return UNQLITE_INVALID;
57806 }
57807 pPager->nCacheMax = mxPage;
57808 return UNQLITE_OK;
57809}
57810/*
57811 * Shutdown the page cache. Free all memory and close the database file.
57812 */
57813UNQLITE_PRIVATE int unqlitePagerClose(Pager *pPager)
57814{
57815 /* Release the KV engine */
57816 pager_release_kv_engine(pPager);
57817 if( pPager->iOpenFlags & UNQLITE_OPEN_MMAP ){
57818 const jx9_vfs *pVfs = jx9ExportBuiltinVfs();
57819 if( pVfs && pVfs->xUnmap && pPager->pMmap ){
57820 pVfs->xUnmap(pPager->pMmap,pPager->dbByteSize);
57821 }
57822 }
57823 if( !pPager->is_mem && pPager->iState > PAGER_OPEN ){
57824 /* Release all lock on this database handle */
57825 pager_unlock_db(pPager,NO_LOCK);
57826 /* Close the file */
57827 unqliteOsCloseFree(pPager->pAllocator,pPager->pfd);
57828 }
57829 if( pPager->pVec ){
57830 unqliteBitvecDestroy(pPager->pVec);
57831 pPager->pVec = 0;
57832 }
57833 return UNQLITE_OK;
57834}
57835/*
57836 * Generate a random string.
57837 */
57838UNQLITE_PRIVATE void unqlitePagerRandomString(Pager *pPager,char *zBuf,sxu32 nLen)
57839{
57840 static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */
57841 sxu32 i;
57842 /* Generate a binary string first */
57843 SyRandomness(&pPager->sPrng,zBuf,nLen);
57844 /* Turn the binary string into english based alphabet */
57845 for( i = 0 ; i < nLen ; ++i ){
57846 zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)];
57847 }
57848}
57849/*
57850 * Generate a random number.
57851 */
57852UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager)
57853{
57854 sxu32 iNum;
57855 SyRandomness(&pPager->sPrng,(void *)&iNum,sizeof(iNum));
57856 return iNum;
57857}
57858/* Exported KV IO Methods */
57859/*
57860 * Refer to [unqlitePagerAcquire()]
57861 */
57862static int unqliteKvIoPageGet(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage)
57863{
57864 int rc;
57865 rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,0,0);
57866 return rc;
57867}
57868/*
57869 * Refer to [unqlitePagerAcquire()]
57870 */
57871static int unqliteKvIoPageLookup(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage)
57872{
57873 int rc;
57874 rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,1,0);
57875 return rc;
57876}
57877/*
57878 * Refer to [unqlitePagerAcquire()]
57879 */
57880static int unqliteKvIoNewPage(unqlite_kv_handle pHandle,unqlite_page **ppPage)
57881{
57882 Pager *pPager = (Pager *)pHandle;
57883 int rc;
57884 /*
57885 * Acquire a reader-lock first so that pPager->dbSize get initialized.
57886 */
57887 rc = pager_shared_lock(pPager);
57888 if( rc == UNQLITE_OK ){
57889 rc = unqlitePagerAcquire(pPager,pPager->dbSize == 0 ? /* Page 0 is reserved */ 1 : pPager->dbSize ,ppPage,0,0);
57890 }
57891 return rc;
57892}
57893/*
57894 * Refer to [unqlitePageWrite()]
57895 */
57896static int unqliteKvIopageWrite(unqlite_page *pPage)
57897{
57898 int rc;
57899 if( pPage == 0 ){
57900 /* TICKET 1433-0348 */
57901 return UNQLITE_OK;
57902 }
57903 rc = unqlitePageWrite(pPage);
57904 return rc;
57905}
57906/*
57907 * Refer to [unqlitePagerDontWrite()]
57908 */
57909static int unqliteKvIoPageDontWrite(unqlite_page *pPage)
57910{
57911 int rc;
57912 if( pPage == 0 ){
57913 /* TICKET 1433-0348 */
57914 return UNQLITE_OK;
57915 }
57916 rc = unqlitePagerDontWrite(pPage);
57917 return rc;
57918}
57919/*
57920 * Refer to [unqliteBitvecSet()]
57921 */
57922static int unqliteKvIoPageDontJournal(unqlite_page *pRaw)
57923{
57924 Page *pPage = (Page *)pRaw;
57925 Pager *pPager;
57926 if( pPage == 0 ){
57927 /* TICKET 1433-0348 */
57928 return UNQLITE_OK;
57929 }
57930 pPager = pPage->pPager;
57931 if( pPager->iState >= PAGER_WRITER_LOCKED ){
57932 if( !pPager->no_jrnl && pPager->pVec && !unqliteBitvecTest(pPager->pVec,pPage->pgno) ){
57933 unqliteBitvecSet(pPager->pVec,pPage->pgno);
57934 }
57935 }
57936 return UNQLITE_OK;
57937}
57938/*
57939 * Do not add a page to the hot dirty list.
57940 */
57941static int unqliteKvIoPageDontMakeHot(unqlite_page *pRaw)
57942{
57943 Page *pPage = (Page *)pRaw;
57944
57945 if( pPage == 0 ){
57946 /* TICKET 1433-0348 */
57947 return UNQLITE_OK;
57948 }
57949 pPage->flags |= PAGE_DONT_MAKE_HOT;
57950 return UNQLITE_OK;
57951}
57952/*
57953 * Refer to [page_ref()]
57954 */
57955static int unqliteKvIopage_ref(unqlite_page *pPage)
57956{
57957 if( pPage ){
57958 page_ref((Page *)pPage);
57959 }
57960 return UNQLITE_OK;
57961}
57962/*
57963 * Refer to [page_unref()]
57964 */
57965static int unqliteKvIoPageUnRef(unqlite_page *pPage)
57966{
57967 if( pPage ){
57968 page_unref((Page *)pPage);
57969 }
57970 return UNQLITE_OK;
57971}
57972/*
57973 * Refer to the declaration of the [Pager] structure
57974 */
57975static int unqliteKvIoReadOnly(unqlite_kv_handle pHandle)
57976{
57977 return ((Pager *)pHandle)->is_rdonly;
57978}
57979/*
57980 * Refer to the declaration of the [Pager] structure
57981 */
57982static int unqliteKvIoPageSize(unqlite_kv_handle pHandle)
57983{
57984 return ((Pager *)pHandle)->iPageSize;
57985}
57986/*
57987 * Refer to the declaration of the [Pager] structure
57988 */
57989static unsigned char * unqliteKvIoTempPage(unqlite_kv_handle pHandle)
57990{
57991 return ((Pager *)pHandle)->zTmpPage;
57992}
57993/*
57994 * Set a page unpin callback.
57995 * Refer to the declaration of the [Pager] structure
57996 */
57997static void unqliteKvIoPageUnpin(unqlite_kv_handle pHandle,void (*xPageUnpin)(void *))
57998{
57999 Pager *pPager = (Pager *)pHandle;
58000 pPager->xPageUnpin = xPageUnpin;
58001}
58002/*
58003 * Set a page reload callback.
58004 * Refer to the declaration of the [Pager] structure
58005 */
58006static void unqliteKvIoPageReload(unqlite_kv_handle pHandle,void (*xPageReload)(void *))
58007{
58008 Pager *pPager = (Pager *)pHandle;
58009 pPager->xPageReload = xPageReload;
58010}
58011/*
58012 * Log an error.
58013 * Refer to the declaration of the [Pager] structure
58014 */
58015static void unqliteKvIoErr(unqlite_kv_handle pHandle,const char *zErr)
58016{
58017 Pager *pPager = (Pager *)pHandle;
58018 unqliteGenError(pPager->pDb,zErr);
58019}
58020/*
58021 * Init an instance of the [unqlite_kv_io] structure.
58022 */
58023static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_kv_io *pIo)
58024{
58025 pIo->pHandle = pPager;
58026 pIo->pMethods = pMethods;
58027
58028 pIo->xGet = unqliteKvIoPageGet;
58029 pIo->xLookup = unqliteKvIoPageLookup;
58030 pIo->xNew = unqliteKvIoNewPage;
58031
58032 pIo->xWrite = unqliteKvIopageWrite;
58033 pIo->xDontWrite = unqliteKvIoPageDontWrite;
58034 pIo->xDontJournal = unqliteKvIoPageDontJournal;
58035 pIo->xDontMkHot = unqliteKvIoPageDontMakeHot;
58036
58037 pIo->xPageRef = unqliteKvIopage_ref;
58038 pIo->xPageUnref = unqliteKvIoPageUnRef;
58039
58040 pIo->xPageSize = unqliteKvIoPageSize;
58041 pIo->xReadOnly = unqliteKvIoReadOnly;
58042
58043 pIo->xTmpPage = unqliteKvIoTempPage;
58044
58045 pIo->xSetUnpin = unqliteKvIoPageUnpin;
58046 pIo->xSetReload = unqliteKvIoPageReload;
58047
58048 pIo->xErr = unqliteKvIoErr;
58049
58050 return UNQLITE_OK;
58051}
58052/*
58053 * ----------------------------------------------------------
58054 * File: unqlite_vm.c
58055 * MD5: 2a0c56efb2ab87d3e52d0d7c3147c53b
58056 * ----------------------------------------------------------
58057 */
58058/*
58059 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
58060 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
58061 * Version 1.1.6
58062 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
58063 * please contact Symisc Systems via:
58064 * legal@symisc.net
58065 * licensing@symisc.net
58066 * contact@symisc.net
58067 * or visit:
58068 * http://unqlite.org/licensing.html
58069 */
58070 /* $SymiscID: unqlite_vm.c v1.0 Win7 2013-01-29 23:37 stable <chm@symisc.net> $ */
58071#ifndef UNQLITE_AMALGAMATION
58072#include "unqliteInt.h"
58073#endif
58074/* This file deals with low level stuff related to the unQLite Virtual Machine */
58075
58076/* Record ID as a hash value */
58077#define COL_RECORD_HASH(RID) (RID)
58078/*
58079 * Fetch a record from a given collection.
58080 */
58081static unqlite_col_record * CollectionCacheFetchRecord(
58082 unqlite_col *pCol, /* Target collection */
58083 jx9_int64 nId /* Unique record ID */
58084 )
58085{
58086 unqlite_col_record *pEntry;
58087 if( pCol->nRec < 1 ){
58088 /* Don't bother hashing */
58089 return 0;
58090 }
58091 pEntry = pCol->apRecord[COL_RECORD_HASH(nId) & (pCol->nRecSize - 1)];
58092 for(;;){
58093 if( pEntry == 0 ){
58094 break;
58095 }
58096 if( pEntry->nId == nId ){
58097 /* Record found */
58098 return pEntry;
58099 }
58100 /* Point to the next entry */
58101 pEntry = pEntry->pNextCol;
58102
58103 }
58104 /* No such record */
58105 return 0;
58106}
58107/*
58108 * Install a freshly created record in a given collection.
58109 */
58110static int CollectionCacheInstallRecord(
58111 unqlite_col *pCol, /* Target collection */
58112 jx9_int64 nId, /* Unique record ID */
58113 jx9_value *pValue /* JSON value */
58114 )
58115{
58116 unqlite_col_record *pRecord;
58117 sxu32 iBucket;
58118 /* Fetch the record first */
58119 pRecord = CollectionCacheFetchRecord(pCol,nId);
58120 if( pRecord ){
58121 /* Record already installed, overwrite its old value */
58122 jx9MemObjStore(pValue,&pRecord->sValue);
58123 return UNQLITE_OK;
58124 }
58125 /* Allocate a new instance */
58126 pRecord = (unqlite_col_record *)SyMemBackendPoolAlloc(&pCol->pVm->sAlloc,sizeof(unqlite_col_record));
58127 if( pRecord == 0 ){
58128 return UNQLITE_NOMEM;
58129 }
58130 /* Zero the structure */
58131 SyZero(pRecord,sizeof(unqlite_col_record));
58132 /* Fill in the structure */
58133 jx9MemObjInit(pCol->pVm->pJx9Vm,&pRecord->sValue);
58134 jx9MemObjStore(pValue,&pRecord->sValue);
58135 pRecord->nId = nId;
58136 pRecord->pCol = pCol;
58137 /* Install in the corresponding bucket */
58138 iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1);
58139 pRecord->pNextCol = pCol->apRecord[iBucket];
58140 if( pCol->apRecord[iBucket] ){
58141 pCol->apRecord[iBucket]->pPrevCol = pRecord;
58142 }
58143 pCol->apRecord[iBucket] = pRecord;
58144 /* Link */
58145 MACRO_LD_PUSH(pCol->pList,pRecord);
58146 pCol->nRec++;
58147 if( (pCol->nRec >= pCol->nRecSize * 3) && pCol->nRec < 100000 ){
58148 /* Allocate a new larger table */
58149 sxu32 nNewSize = pCol->nRecSize << 1;
58150 unqlite_col_record *pEntry;
58151 unqlite_col_record **apNew;
58152 sxu32 n;
58153
58154 apNew = (unqlite_col_record **)SyMemBackendAlloc(&pCol->pVm->sAlloc, nNewSize * sizeof(unqlite_col_record *));
58155 if( apNew ){
58156 /* Zero the new table */
58157 SyZero((void *)apNew, nNewSize * sizeof(unqlite_col_record *));
58158 /* Rehash all entries */
58159 n = 0;
58160 pEntry = pCol->pList;
58161 for(;;){
58162 /* Loop one */
58163 if( n >= pCol->nRec ){
58164 break;
58165 }
58166 pEntry->pNextCol = pEntry->pPrevCol = 0;
58167 /* Install in the new bucket */
58168 iBucket = COL_RECORD_HASH(pEntry->nId) & (nNewSize - 1);
58169 pEntry->pNextCol = apNew[iBucket];
58170 if( apNew[iBucket] ){
58171 apNew[iBucket]->pPrevCol = pEntry;
58172 }
58173 apNew[iBucket] = pEntry;
58174 /* Point to the next entry */
58175 pEntry = pEntry->pNext;
58176 n++;
58177 }
58178 /* Release the old table and reflect the change */
58179 SyMemBackendFree(&pCol->pVm->sAlloc,(void *)pCol->apRecord);
58180 pCol->apRecord = apNew;
58181 pCol->nRecSize = nNewSize;
58182 }
58183 }
58184 /* All done */
58185 return UNQLITE_OK;
58186}
58187/*
58188 * Remove a record from the collection table.
58189 */
58190UNQLITE_PRIVATE int unqliteCollectionCacheRemoveRecord(
58191 unqlite_col *pCol, /* Target collection */
58192 jx9_int64 nId /* Unique record ID */
58193 )
58194{
58195 unqlite_col_record *pRecord;
58196 /* Fetch the record first */
58197 pRecord = CollectionCacheFetchRecord(pCol,nId);
58198 if( pRecord == 0 ){
58199 /* No such record */
58200 return UNQLITE_NOTFOUND;
58201 }
58202 if( pRecord->pPrevCol ){
58203 pRecord->pPrevCol->pNextCol = pRecord->pNextCol;
58204 }else{
58205 sxu32 iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1);
58206 pCol->apRecord[iBucket] = pRecord->pNextCol;
58207 }
58208 if( pRecord->pNextCol ){
58209 pRecord->pNextCol->pPrevCol = pRecord->pPrevCol;
58210 }
58211 /* Unlink */
58212 MACRO_LD_REMOVE(pCol->pList,pRecord);
58213 pCol->nRec--;
58214 return UNQLITE_OK;
58215}
58216/*
58217 * Discard a collection and its records.
58218 */
58219static int CollectionCacheRelease(unqlite_col *pCol)
58220{
58221 unqlite_col_record *pNext,*pRec = pCol->pList;
58222 unqlite_vm *pVm = pCol->pVm;
58223 sxu32 n;
58224 /* Discard all records */
58225 for( n = 0 ; n < pCol->nRec ; ++n ){
58226 pNext = pRec->pNext;
58227 jx9MemObjRelease(&pRec->sValue);
58228 SyMemBackendPoolFree(&pVm->sAlloc,(void *)pRec);
58229 /* Point to the next record */
58230 pRec = pNext;
58231 }
58232 SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord);
58233 pCol->nRec = pCol->nRecSize = 0;
58234 pCol->pList = 0;
58235 return UNQLITE_OK;
58236}
58237/*
58238 * Install a freshly created collection in the unqlite VM.
58239 */
58240static int unqliteVmInstallCollection(
58241 unqlite_vm *pVm, /* Target VM */
58242 unqlite_col *pCol /* Collection to install */
58243 )
58244{
58245 SyString *pName = &pCol->sName;
58246 sxu32 iBucket;
58247 /* Hash the collection name */
58248 pCol->nHash = SyBinHash((const void *)pName->zString,pName->nByte);
58249 /* Install it in the corresponding bucket */
58250 iBucket = pCol->nHash & (pVm->iColSize - 1);
58251 pCol->pNextCol = pVm->apCol[iBucket];
58252 if( pVm->apCol[iBucket] ){
58253 pVm->apCol[iBucket]->pPrevCol = pCol;
58254 }
58255 pVm->apCol[iBucket] = pCol;
58256 /* Link to the list of active collections */
58257 MACRO_LD_PUSH(pVm->pCol,pCol);
58258 pVm->iCol++;
58259 if( (pVm->iCol >= pVm->iColSize * 4) && pVm->iCol < 10000 ){
58260 /* Grow the hashtable */
58261 sxu32 nNewSize = pVm->iColSize << 1;
58262 unqlite_col *pEntry;
58263 unqlite_col **apNew;
58264 sxu32 n;
58265
58266 apNew = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc, nNewSize * sizeof(unqlite_col *));
58267 if( apNew ){
58268 /* Zero the new table */
58269 SyZero((void *)apNew, nNewSize * sizeof(unqlite_col *));
58270 /* Rehash all entries */
58271 n = 0;
58272 pEntry = pVm->pCol;
58273 for(;;){
58274 /* Loop one */
58275 if( n >= pVm->iCol ){
58276 break;
58277 }
58278 pEntry->pNextCol = pEntry->pPrevCol = 0;
58279 /* Install in the new bucket */
58280 iBucket = pEntry->nHash & (nNewSize - 1);
58281 pEntry->pNextCol = apNew[iBucket];
58282 if( apNew[iBucket] ){
58283 apNew[iBucket]->pPrevCol = pEntry;
58284 }
58285 apNew[iBucket] = pEntry;
58286 /* Point to the next entry */
58287 pEntry = pEntry->pNext;
58288 n++;
58289 }
58290 /* Release the old table and reflect the change */
58291 SyMemBackendFree(&pVm->sAlloc,(void *)pVm->apCol);
58292 pVm->apCol = apNew;
58293 pVm->iColSize = nNewSize;
58294 }
58295 }
58296 return UNQLITE_OK;
58297}
58298/*
58299 * Fetch a collection from the target VM.
58300 */
58301static unqlite_col * unqliteVmFetchCollection(
58302 unqlite_vm *pVm, /* Target VM */
58303 SyString *pName /* Lookup name */
58304 )
58305{
58306 unqlite_col *pCol;
58307 sxu32 nHash;
58308 if( pVm->iCol < 1 ){
58309 /* Don't bother hashing */
58310 return 0;
58311 }
58312 nHash = SyBinHash((const void *)pName->zString,pName->nByte);
58313 /* Perform the lookup */
58314 pCol = pVm->apCol[nHash & ( pVm->iColSize - 1)];
58315 for(;;){
58316 if( pCol == 0 ){
58317 break;
58318 }
58319 if( nHash == pCol->nHash && SyStringCmp(pName,&pCol->sName,SyMemcmp) == 0 ){
58320 /* Collection found */
58321 return pCol;
58322 }
58323 /* Point to the next entry */
58324 pCol = pCol->pNextCol;
58325 }
58326 /* No such collection */
58327 return 0;
58328}
58329/*
58330 * Write and/or alter collection binary header.
58331 */
58332static int CollectionSetHeader(
58333 unqlite_kv_engine *pEngine, /* Underlying KV storage engine */
58334 unqlite_col *pCol, /* Target collection */
58335 jx9_int64 iRec, /* Last record ID */
58336 jx9_int64 iTotal, /* Total number of records in this collection */
58337 jx9_value *pSchema /* Collection schema */
58338 )
58339{
58340 SyBlob *pHeader = &pCol->sHeader;
58341 unqlite_kv_methods *pMethods;
58342 int iWrite = 0;
58343 int rc;
58344 if( pEngine == 0 ){
58345 /* Default storage engine */
58346 pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb);
58347 }
58348 pMethods = pEngine->pIo->pMethods;
58349 if( SyBlobLength(pHeader) < 1 ){
58350 Sytm *pCreate = &pCol->sCreation; /* Creation time */
58351 unqlite_vfs *pVfs;
58352 sxu32 iDos;
58353 /* Magic number */
58354 rc = SyBlobAppendBig16(pHeader,UNQLITE_COLLECTION_MAGIC);
58355 if( rc != UNQLITE_OK ){
58356 return rc;
58357 }
58358 /* Initial record ID */
58359 rc = SyBlobAppendBig64(pHeader,0);
58360 if( rc != UNQLITE_OK ){
58361 return rc;
58362 }
58363 /* Total records in the collection */
58364 rc = SyBlobAppendBig64(pHeader,0);
58365 if( rc != UNQLITE_OK ){
58366 return rc;
58367 }
58368 pVfs = (unqlite_vfs *)unqliteExportBuiltinVfs();
58369 /* Creation time of the collection */
58370 if( pVfs->xCurrentTime ){
58371 /* Get the creation time */
58372 pVfs->xCurrentTime(pVfs,pCreate);
58373 }else{
58374 /* Zero the structure */
58375 SyZero(pCreate,sizeof(Sytm));
58376 }
58377 /* Convert to DOS time */
58378 SyTimeFormatToDos(pCreate,&iDos);
58379 rc = SyBlobAppendBig32(pHeader,iDos);
58380 if( rc != UNQLITE_OK ){
58381 return rc;
58382 }
58383 /* Offset to start writing collection schema */
58384 pCol->nSchemaOfft = SyBlobLength(pHeader);
58385 iWrite = 1;
58386 }else{
58387 unsigned char *zBinary = (unsigned char *)SyBlobData(pHeader);
58388 /* Header update */
58389 if( iRec >= 0 ){
58390 /* Update record ID */
58391 SyBigEndianPack64(&zBinary[2/* Magic number*/],(sxu64)iRec);
58392 iWrite = 1;
58393 }
58394 if( iTotal >= 0 ){
58395 /* Total records */
58396 SyBigEndianPack64(&zBinary[2/* Magic number*/+8/* Record ID*/],(sxu64)iTotal);
58397 iWrite = 1;
58398 }
58399 if( pSchema ){
58400 /* Collection Schema */
58401 SyBlobTruncate(pHeader,pCol->nSchemaOfft);
58402 /* Encode the schema to FastJson */
58403 rc = FastJsonEncode(pSchema,pHeader,0);
58404 if( rc != UNQLITE_OK ){
58405 return rc;
58406 }
58407 /* Copy the collection schema */
58408 jx9MemObjStore(pSchema,&pCol->sSchema);
58409 iWrite = 1;
58410 }
58411 }
58412 if( iWrite ){
58413 SyString *pId = &pCol->sName;
58414 /* Reflect the disk and/or in-memory image */
58415 rc = pMethods->xReplace(pEngine,
58416 (const void *)pId->zString,pId->nByte,
58417 SyBlobData(pHeader),SyBlobLength(pHeader)
58418 );
58419 if( rc != UNQLITE_OK ){
58420 unqliteGenErrorFormat(pCol->pVm->pDb,
58421 "Cannot save collection '%z' header in the underlying storage engine",
58422 pId
58423 );
58424 return rc;
58425 }
58426 }
58427 return UNQLITE_OK;
58428}
58429/*
58430 * Load a binary collection from disk.
58431 */
58432static int CollectionLoadHeader(unqlite_col *pCol)
58433{
58434 SyBlob *pHeader = &pCol->sHeader;
58435 unsigned char *zRaw,*zEnd;
58436 sxu16 nMagic;
58437 sxu32 iDos;
58438 int rc;
58439 SyBlobReset(pHeader);
58440 /* Read the binary header */
58441 rc = unqlite_kv_cursor_data_callback(pCol->pCursor,unqliteDataConsumer,pHeader);
58442 if( rc != UNQLITE_OK ){
58443 return rc;
58444 }
58445 /* Perform a sanity check */
58446 if( SyBlobLength(pHeader) < (2 /* magic */ + 8 /* record_id */ + 8 /* total_records */+ 4 /* DOS creation time*/) ){
58447 return UNQLITE_CORRUPT;
58448 }
58449 zRaw = (unsigned char *)SyBlobData(pHeader);
58450 zEnd = &zRaw[SyBlobLength(pHeader)];
58451 /* Extract the magic number */
58452 SyBigEndianUnpack16(zRaw,&nMagic);
58453 if( nMagic != UNQLITE_COLLECTION_MAGIC ){
58454 return UNQLITE_CORRUPT;
58455 }
58456 zRaw += 2; /* sizeof(sxu16) */
58457 /* Extract the record ID */
58458 SyBigEndianUnpack64(zRaw,(sxu64 *)&pCol->nLastid);
58459 zRaw += 8; /* sizeof(sxu64) */
58460 /* Total records in the collection */
58461 SyBigEndianUnpack64(zRaw,(sxu64 *)&pCol->nTotRec);
58462 /* Extract the collection creation date (DOS) */
58463 zRaw += 8; /* sizeof(sxu64) */
58464 SyBigEndianUnpack32(zRaw,&iDos);
58465 SyDosTimeFormat(iDos,&pCol->sCreation);
58466 zRaw += 4;
58467 /* Check for a collection schema */
58468 pCol->nSchemaOfft = (sxu32)(zRaw - (unsigned char *)SyBlobData(pHeader));
58469 if( zRaw < zEnd ){
58470 /* Decode the FastJson value */
58471 FastJsonDecode((const void *)zRaw,(sxu32)(zEnd-zRaw),&pCol->sSchema,0,0);
58472 }
58473 return UNQLITE_OK;
58474}
58475/*
58476 * Load or create a binary collection.
58477 */
58478static int unqliteVmLoadCollection(
58479 unqlite_vm *pVm, /* Target VM */
58480 const char *zName, /* Collection name */
58481 sxu32 nByte, /* zName length */
58482 int iFlag, /* Control flag */
58483 unqlite_col **ppOut /* OUT: in-memory collection */
58484 )
58485{
58486 unqlite_kv_methods *pMethods;
58487 unqlite_kv_engine *pEngine;
58488 unqlite_kv_cursor *pCursor;
58489 unqlite *pDb = pVm->pDb;
58490 unqlite_col *pCol = 0; /* cc warning */
58491 int rc = SXERR_MEM;
58492 char *zDup = 0;
58493 /* Point to the underlying KV store */
58494 pEngine = unqlitePagerGetKvEngine(pVm->pDb);
58495 pMethods = pEngine->pIo->pMethods;
58496 /* Allocate a new cursor */
58497 rc = unqliteInitCursor(pDb,&pCursor);
58498 if( rc != UNQLITE_OK ){
58499 return rc;
58500 }
58501 if( (iFlag & UNQLITE_VM_COLLECTION_CREATE) == 0 ){
58502 /* Seek to the desired location */
58503 rc = pMethods->xSeek(pCursor,(const void *)zName,(unqlite_int64)nByte,UNQLITE_CURSOR_MATCH_EXACT);
58504 if( rc != UNQLITE_OK ){
58505 unqliteGenErrorFormat(pDb,"Collection '%.*s' not defined in the underlying database",nByte,zName);
58506 unqliteReleaseCursor(pDb,pCursor);
58507 return rc;
58508 }
58509 }
58510 /* Allocate a new instance */
58511 pCol = (unqlite_col *)SyMemBackendPoolAlloc(&pVm->sAlloc,sizeof(unqlite_col));
58512 if( pCol == 0 ){
58513 unqliteGenOutofMem(pDb);
58514 rc = UNQLITE_NOMEM;
58515 goto fail;
58516 }
58517 SyZero(pCol,sizeof(unqlite_col));
58518 /* Fill in the structure */
58519 SyBlobInit(&pCol->sWorker,&pVm->sAlloc);
58520 SyBlobInit(&pCol->sHeader,&pVm->sAlloc);
58521 pCol->pVm = pVm;
58522 pCol->pCursor = pCursor;
58523 /* Duplicate collection name */
58524 zDup = SyMemBackendStrDup(&pVm->sAlloc,zName,nByte);
58525 if( zDup == 0 ){
58526 unqliteGenOutofMem(pDb);
58527 rc = UNQLITE_NOMEM;
58528 goto fail;
58529 }
58530 pCol->nRecSize = 64; /* Must be a power of two */
58531 pCol->apRecord = (unqlite_col_record **)SyMemBackendAlloc(&pVm->sAlloc,pCol->nRecSize * sizeof(unqlite_col_record *));
58532 if( pCol->apRecord == 0 ){
58533 unqliteGenOutofMem(pDb);
58534 rc = UNQLITE_NOMEM;
58535 goto fail;
58536 }
58537 /* Zero the table */
58538 SyZero((void *)pCol->apRecord,pCol->nRecSize * sizeof(unqlite_col_record *));
58539 SyStringInitFromBuf(&pCol->sName,zDup,nByte);
58540 jx9MemObjInit(pVm->pJx9Vm,&pCol->sSchema);
58541 if( iFlag & UNQLITE_VM_COLLECTION_CREATE ){
58542 /* Create a new collection */
58543 if( pMethods->xReplace == 0 ){
58544 /* Read-only KV engine: Generate an error message and return */
58545 unqliteGenErrorFormat(pDb,
58546 "Cannot create new collection '%z' due to a read-only Key/Value storage engine",
58547 &pCol->sName
58548 );
58549 rc = UNQLITE_ABORT; /* Abort VM execution */
58550 goto fail;
58551 }
58552 /* Write the collection header */
58553 rc = CollectionSetHeader(pEngine,pCol,0,0,0);
58554 if( rc != UNQLITE_OK ){
58555 rc = UNQLITE_ABORT; /* Abort VM execution */
58556 goto fail;
58557 }
58558 }else{
58559 /* Read the collection header */
58560 rc = CollectionLoadHeader(pCol);
58561 if( rc != UNQLITE_OK ){
58562 unqliteGenErrorFormat(pDb,"Corrupt collection '%z' header",&pCol->sName);
58563 goto fail;
58564 }
58565 }
58566 /* Finally install the collection */
58567 unqliteVmInstallCollection(pVm,pCol);
58568 /* All done */
58569 if( ppOut ){
58570 *ppOut = pCol;
58571 }
58572 return UNQLITE_OK;
58573fail:
58574 unqliteReleaseCursor(pDb,pCursor);
58575 if( zDup ){
58576 SyMemBackendFree(&pVm->sAlloc,zDup);
58577 }
58578 if( pCol ){
58579 if( pCol->apRecord ){
58580 SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord);
58581 }
58582 SyBlobRelease(&pCol->sHeader);
58583 SyBlobRelease(&pCol->sWorker);
58584 jx9MemObjRelease(&pCol->sSchema);
58585 SyMemBackendPoolFree(&pVm->sAlloc,pCol);
58586 }
58587 return rc;
58588}
58589/*
58590 * Fetch a collection.
58591 */
58592UNQLITE_PRIVATE unqlite_col * unqliteCollectionFetch(
58593 unqlite_vm *pVm, /* Target VM */
58594 SyString *pName, /* Lookup key */
58595 int iFlag /* Control flag */
58596 )
58597{
58598 unqlite_col *pCol = 0; /* cc warning */
58599 int rc;
58600 /* Check if the collection is already loaded in memory */
58601 pCol = unqliteVmFetchCollection(pVm,pName);
58602 if( pCol ){
58603 /* Already loaded in memory*/
58604 return pCol;
58605 }
58606 if( (iFlag & UNQLITE_VM_AUTO_LOAD) == 0 ){
58607 return 0;
58608 }
58609 /* Ask the storage engine for the collection */
58610 rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,0,&pCol);
58611 /* Return to the caller */
58612 return rc == UNQLITE_OK ? pCol : 0;
58613}
58614/*
58615 * Return the unique ID of the last inserted record.
58616 */
58617UNQLITE_PRIVATE jx9_int64 unqliteCollectionLastRecordId(unqlite_col *pCol)
58618{
58619 return pCol->nLastid == 0 ? 0 : (pCol->nLastid - 1);
58620}
58621/*
58622 * Return the current record ID.
58623 */
58624UNQLITE_PRIVATE jx9_int64 unqliteCollectionCurrentRecordId(unqlite_col *pCol)
58625{
58626 return pCol->nCurid;
58627}
58628/*
58629 * Return the total number of records in a given collection.
58630 */
58631UNQLITE_PRIVATE jx9_int64 unqliteCollectionTotalRecords(unqlite_col *pCol)
58632{
58633 return pCol->nTotRec;
58634}
58635/*
58636 * Reset the record cursor.
58637 */
58638UNQLITE_PRIVATE void unqliteCollectionResetRecordCursor(unqlite_col *pCol)
58639{
58640 pCol->nCurid = 0;
58641}
58642/*
58643 * Fetch a record by its unique ID.
58644 */
58645UNQLITE_PRIVATE int unqliteCollectionFetchRecordById(
58646 unqlite_col *pCol, /* Target collection */
58647 jx9_int64 nId, /* Unique record ID */
58648 jx9_value *pValue /* OUT: record value */
58649 )
58650{
58651 SyBlob *pWorker = &pCol->sWorker;
58652 unqlite_col_record *pRec;
58653 int rc;
58654 jx9_value_null(pValue);
58655 /* Perform a cache lookup first */
58656 pRec = CollectionCacheFetchRecord(pCol,nId);
58657 if( pRec ){
58658 /* Copy record value */
58659 jx9MemObjStore(&pRec->sValue,pValue);
58660 return UNQLITE_OK;
58661 }
58662 /* Reset the working buffer */
58663 SyBlobReset(pWorker);
58664 /* Generate the unique ID */
58665 SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,nId);
58666 /* Reset the cursor */
58667 unqlite_kv_cursor_reset(pCol->pCursor);
58668 /* Seek the cursor to the desired location */
58669 rc = unqlite_kv_cursor_seek(pCol->pCursor,
58670 SyBlobData(pWorker),SyBlobLength(pWorker),
58671 UNQLITE_CURSOR_MATCH_EXACT
58672 );
58673 if( rc != UNQLITE_OK ){
58674 return rc;
58675 }
58676 /* Consume the binary JSON */
58677 SyBlobReset(pWorker);
58678 unqlite_kv_cursor_data_callback(pCol->pCursor,unqliteDataConsumer,pWorker);
58679 if( SyBlobLength(pWorker) < 1 ){
58680 unqliteGenErrorFormat(pCol->pVm->pDb,
58681 "Empty record '%qd'",nId
58682 );
58683 jx9_value_null(pValue);
58684 }else{
58685 /* Decode the binary JSON */
58686 rc = FastJsonDecode(SyBlobData(pWorker),SyBlobLength(pWorker),pValue,0,0);
58687 if( rc == UNQLITE_OK ){
58688 /* Install the record in the cache */
58689 CollectionCacheInstallRecord(pCol,nId,pValue);
58690 }
58691 }
58692 return rc;
58693}
58694/*
58695 * Fetch the next record from a given collection.
58696 */
58697UNQLITE_PRIVATE int unqliteCollectionFetchNextRecord(unqlite_col *pCol,jx9_value *pValue)
58698{
58699 int rc;
58700 for(;;){
58701 if( pCol->nCurid >= pCol->nLastid ){
58702 /* No more records, reset the record cursor ID */
58703 pCol->nCurid = 0;
58704 /* Return to the caller */
58705 return SXERR_EOF;
58706 }
58707 rc = unqliteCollectionFetchRecordById(pCol,pCol->nCurid,pValue);
58708 /* Increment the record ID */
58709 pCol->nCurid++;
58710 /* Lookup result */
58711 if( rc == UNQLITE_OK || rc != UNQLITE_NOTFOUND ){
58712 break;
58713 }
58714 }
58715 return rc;
58716}
58717/*
58718 * Create a new collection.
58719 */
58720UNQLITE_PRIVATE int unqliteCreateCollection(
58721 unqlite_vm *pVm, /* Target VM */
58722 SyString *pName /* Collection name */
58723 )
58724{
58725 unqlite_col *pCol;
58726 int rc;
58727 /* Perform a lookup first */
58728 pCol = unqliteCollectionFetch(pVm,pName,UNQLITE_VM_AUTO_LOAD);
58729 if( pCol ){
58730 return UNQLITE_EXISTS;
58731 }
58732 /* Now, safely create the collection */
58733 rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,UNQLITE_VM_COLLECTION_CREATE,0);
58734 return rc;
58735}
58736/*
58737 * Set a schema (JSON object) for a given collection.
58738 */
58739UNQLITE_PRIVATE int unqliteCollectionSetSchema(unqlite_col *pCol,jx9_value *pValue)
58740{
58741 int rc;
58742 if( !jx9_value_is_json_object(pValue) ){
58743 /* Must be a JSON object */
58744 return SXERR_INVALID;
58745 }
58746 rc = CollectionSetHeader(0,pCol,-1,-1,pValue);
58747 return rc;
58748}
58749/*
58750 * Perform a store operation on a given collection.
58751 */
58752static int CollectionStore(
58753 unqlite_col *pCol, /* Target collection */
58754 jx9_value *pValue /* JSON value to be stored */
58755 )
58756{
58757 SyBlob *pWorker = &pCol->sWorker;
58758 unqlite_kv_methods *pMethods;
58759 unqlite_kv_engine *pEngine;
58760 sxu32 nKeyLen;
58761 int rc;
58762 /* Point to the underlying KV store */
58763 pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb);
58764 pMethods = pEngine->pIo->pMethods;
58765 if( pCol->nTotRec >= SXI64_HIGH ){
58766 /* Collection limit reached. No more records */
58767 unqliteGenErrorFormat(pCol->pVm->pDb,
58768 "Collection '%z': Records limit reached",
58769 &pCol->sName
58770 );
58771 return UNQLITE_LIMIT;
58772 }
58773 if( pMethods->xReplace == 0 ){
58774 unqliteGenErrorFormat(pCol->pVm->pDb,
58775 "Cannot store record into collection '%z' due to a read-only Key/Value storage engine",
58776 &pCol->sName
58777 );
58778 return UNQLITE_READ_ONLY;
58779 }
58780 /* Reset the working buffer */
58781 SyBlobReset(pWorker);
58782 if( jx9_value_is_json_object(pValue) ){
58783 jx9_value sId;
58784 /* If the given type is a JSON object, then add the special __id field */
58785 jx9MemObjInitFromInt(pCol->pVm->pJx9Vm,&sId,pCol->nLastid);
58786 jx9_array_add_strkey_elem(pValue,"__id",&sId);
58787 jx9MemObjRelease(&sId);
58788 }
58789 /* Prepare the unique ID for this record */
58790 SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,pCol->nLastid);
58791 nKeyLen = SyBlobLength(pWorker);
58792 if( nKeyLen < 1 ){
58793 unqliteGenOutofMem(pCol->pVm->pDb);
58794 return UNQLITE_NOMEM;
58795 }
58796 /* Turn to FastJson */
58797 rc = FastJsonEncode(pValue,pWorker,0);
58798 if( rc != UNQLITE_OK ){
58799 return rc;
58800 }
58801 /* Finally perform the insertion */
58802 rc = pMethods->xReplace(
58803 pEngine,
58804 SyBlobData(pWorker),nKeyLen,
58805 SyBlobDataAt(pWorker,nKeyLen),SyBlobLength(pWorker)-nKeyLen
58806 );
58807 if( rc == UNQLITE_OK ){
58808 /* Save the value in the cache */
58809 CollectionCacheInstallRecord(pCol,pCol->nLastid,pValue);
58810 /* Increment the unique __id */
58811 pCol->nLastid++;
58812 pCol->nTotRec++;
58813 /* Reflect the change */
58814 rc = CollectionSetHeader(0,pCol,pCol->nLastid,pCol->nTotRec,0);
58815 }
58816 if( rc != UNQLITE_OK ){
58817 unqliteGenErrorFormat(pCol->pVm->pDb,
58818 "IO error while storing record into collection '%z'",
58819 &pCol->sName
58820 );
58821 return rc;
58822 }
58823 return UNQLITE_OK;
58824}
58825/*
58826 * Array walker callback (Refer to jx9_array_walk()).
58827 */
58828static int CollectionRecordArrayWalker(jx9_value *pKey,jx9_value *pData,void *pUserData)
58829{
58830 unqlite_col *pCol = (unqlite_col *)pUserData;
58831 int rc;
58832 /* Perform the insertion */
58833 rc = CollectionStore(pCol,pData);
58834 if( rc != UNQLITE_OK ){
58835 SXUNUSED(pKey); /* cc warning */
58836 }
58837 return rc;
58838}
58839/*
58840 * Perform a store operation on a given collection.
58841 */
58842UNQLITE_PRIVATE int unqliteCollectionPut(unqlite_col *pCol,jx9_value *pValue,int iFlag)
58843{
58844 int rc;
58845 if( !jx9_value_is_json_object(pValue) && jx9_value_is_json_array(pValue) ){
58846 /* Iterate over the array and store its members in the collection */
58847 rc = jx9_array_walk(pValue,CollectionRecordArrayWalker,pCol);
58848 SXUNUSED(iFlag); /* cc warning */
58849 }else{
58850 rc = CollectionStore(pCol,pValue);
58851 }
58852 return rc;
58853}
58854/*
58855 * Drop a record from a given collection.
58856 */
58857UNQLITE_PRIVATE int unqliteCollectionDropRecord(
58858 unqlite_col *pCol, /* Target collection */
58859 jx9_int64 nId, /* Unique ID of the record to be droped */
58860 int wr_header, /* True to alter collection header */
58861 int log_err /* True to log error */
58862 )
58863{
58864 SyBlob *pWorker = &pCol->sWorker;
58865 int rc;
58866 /* Reset the working buffer */
58867 SyBlobReset(pWorker);
58868 /* Prepare the unique ID for this record */
58869 SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,nId);
58870 /* Reset the cursor */
58871 unqlite_kv_cursor_reset(pCol->pCursor);
58872 /* Seek the cursor to the desired location */
58873 rc = unqlite_kv_cursor_seek(pCol->pCursor,
58874 SyBlobData(pWorker),SyBlobLength(pWorker),
58875 UNQLITE_CURSOR_MATCH_EXACT
58876 );
58877 if( rc != UNQLITE_OK ){
58878 return rc;
58879 }
58880 /* Remove the record from the storage engine */
58881 rc = unqlite_kv_cursor_delete_entry(pCol->pCursor);
58882 /* Finally, Remove the record from the cache */
58883 unqliteCollectionCacheRemoveRecord(pCol,nId);
58884 if( rc == UNQLITE_OK ){
58885 pCol->nTotRec--;
58886 if( wr_header ){
58887 /* Relect in the collection header */
58888 rc = CollectionSetHeader(0,pCol,-1,pCol->nTotRec,0);
58889 }
58890 }else if( rc == UNQLITE_NOTIMPLEMENTED ){
58891 if( log_err ){
58892 unqliteGenErrorFormat(pCol->pVm->pDb,
58893 "Cannot delete record from collection '%z' due to a read-only Key/Value storage engine",
58894 &pCol->sName
58895 );
58896 }
58897 }
58898 return rc;
58899}
58900/*
58901 * Drop a collection from the KV storage engine and the underlying
58902 * unqlite VM.
58903 */
58904UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol)
58905{
58906 unqlite_vm *pVm = pCol->pVm;
58907 jx9_int64 nId;
58908 int rc;
58909 /* Reset the cursor */
58910 unqlite_kv_cursor_reset(pCol->pCursor);
58911 /* Seek the cursor to the desired location */
58912 rc = unqlite_kv_cursor_seek(pCol->pCursor,
58913 SyStringData(&pCol->sName),SyStringLength(&pCol->sName),
58914 UNQLITE_CURSOR_MATCH_EXACT
58915 );
58916 if( rc == UNQLITE_OK ){
58917 /* Remove the record from the storage engine */
58918 rc = unqlite_kv_cursor_delete_entry(pCol->pCursor);
58919 }
58920 if( rc != UNQLITE_OK ){
58921 unqliteGenErrorFormat(pCol->pVm->pDb,
58922 "Cannot remove collection '%z' due to a read-only Key/Value storage engine",
58923 &pCol->sName
58924 );
58925 return rc;
58926 }
58927 /* Drop collection records */
58928 for( nId = 0 ; nId < pCol->nLastid ; ++nId ){
58929 unqliteCollectionDropRecord(pCol,nId,0,0);
58930 }
58931 /* Cleanup */
58932 CollectionCacheRelease(pCol);
58933 SyBlobRelease(&pCol->sHeader);
58934 SyBlobRelease(&pCol->sWorker);
58935 SyMemBackendFree(&pVm->sAlloc,(void *)SyStringData(&pCol->sName));
58936 unqliteReleaseCursor(pVm->pDb,pCol->pCursor);
58937 /* Unlink */
58938 if( pCol->pPrevCol ){
58939 pCol->pPrevCol->pNextCol = pCol->pNextCol;
58940 }else{
58941 sxu32 iBucket = pCol->nHash & (pVm->iColSize - 1);
58942 pVm->apCol[iBucket] = pCol->pNextCol;
58943 }
58944 if( pCol->pNextCol ){
58945 pCol->pNextCol->pPrevCol = pCol->pPrevCol;
58946 }
58947 MACRO_LD_REMOVE(pVm->pCol,pCol);
58948 pVm->iCol--;
58949 SyMemBackendPoolFree(&pVm->sAlloc,pCol);
58950 return UNQLITE_OK;
58951}
58952/*
58953 * ----------------------------------------------------------
58954 * File: unqlite_jx9.c
58955 * MD5: 8fddc15b667e85d7b5df5367132518fb
58956 * ----------------------------------------------------------
58957 */
58958/*
58959 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
58960 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
58961 * Version 1.1.6
58962 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
58963 * please contact Symisc Systems via:
58964 * legal@symisc.net
58965 * licensing@symisc.net
58966 * contact@symisc.net
58967 * or visit:
58968 * http://unqlite.org/licensing.html
58969 */
58970 /* $SymiscID: unql_jx9.c v1.2 FreeBSD 2013-01-24 22:45 stable <chm@symisc.net> $ */
58971#ifndef UNQLITE_AMALGAMATION
58972#include "unqliteInt.h"
58973#endif
58974/*
58975 * This file implements UnQLite functions (db_exists(), db_create(), db_put(), db_get(), etc.) for the
58976 * underlying Jx9 Virtual Machine.
58977 */
58978/*
58979 * string db_version(void)
58980 * Return the current version of the unQLite database engine.
58981 * Parameter
58982 * None
58983 * Return
58984 * unQLite version number (string).
58985 */
58986static int unqliteBuiltin_db_version(jx9_context *pCtx,int argc,jx9_value **argv)
58987{
58988 SXUNUSED(argc); /* cc warning */
58989 SXUNUSED(argv);
58990 jx9_result_string(pCtx,UNQLITE_VERSION,(int)sizeof(UNQLITE_VERSION)-1);
58991 return JX9_OK;
58992}
58993/*
58994 * string db_errlog(void)
58995 * Return the database error log.
58996 * Parameter
58997 * None
58998 * Return
58999 * Database error log (string).
59000 */
59001static int unqliteBuiltin_db_errlog(jx9_context *pCtx,int argc,jx9_value **argv)
59002{
59003 unqlite_vm *pVm;
59004 SyBlob *pErr;
59005
59006 SXUNUSED(argc); /* cc warning */
59007 SXUNUSED(argv);
59008
59009 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59010 /* Point to the error log */
59011 pErr = &pVm->pDb->sErr;
59012 /* Return the log */
59013 jx9_result_string(pCtx,(const char *)SyBlobData(pErr),(int)SyBlobLength(pErr));
59014 return JX9_OK;
59015}
59016/*
59017 * string db_copyright(void)
59018 * string db_credits(void)
59019 * Return the unQLite database engine copyright notice.
59020 * Parameter
59021 * None
59022 * Return
59023 * Copyright notice.
59024 */
59025static int unqliteBuiltin_db_credits(jx9_context *pCtx,int argc,jx9_value **argv)
59026{
59027 SXUNUSED(argc); /* cc warning */
59028 SXUNUSED(argv);
59029 jx9_result_string(pCtx,UNQLITE_COPYRIGHT,(int)sizeof(UNQLITE_COPYRIGHT)-1);
59030 return JX9_OK;
59031}
59032/*
59033 * string db_sig(void)
59034 * Return the unQLite database engine unique signature.
59035 * Parameter
59036 * None
59037 * Return
59038 * unQLite signature.
59039 */
59040static int unqliteBuiltin_db_sig(jx9_context *pCtx,int argc,jx9_value **argv)
59041{
59042 SXUNUSED(argc); /* cc warning */
59043 SXUNUSED(argv);
59044 jx9_result_string(pCtx,UNQLITE_IDENT,sizeof(UNQLITE_IDENT)-1);
59045 return JX9_OK;
59046}
59047/*
59048 * bool collection_exists(string $name)
59049 * bool db_exits(string $name)
59050 * Check if a given collection exists in the underlying database.
59051 * Parameter
59052 * name: Lookup name
59053 * Return
59054 * TRUE if the collection exits. FALSE otherwise.
59055 */
59056static int unqliteBuiltin_collection_exists(jx9_context *pCtx,int argc,jx9_value **argv)
59057{
59058 unqlite_col *pCol;
59059 const char *zName;
59060 unqlite_vm *pVm;
59061 SyString sName;
59062 int nByte;
59063 /* Extract collection name */
59064 if( argc < 1 ){
59065 /* Missing arguments */
59066 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
59067 /* Return false */
59068 jx9_result_bool(pCtx,0);
59069 return JX9_OK;
59070 }
59071 zName = jx9_value_to_string(argv[0],&nByte);
59072 if( nByte < 1){
59073 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59074 /* Return false */
59075 jx9_result_bool(pCtx,0);
59076 return JX9_OK;
59077 }
59078 SyStringInitFromBuf(&sName,zName,nByte);
59079 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59080 /* Perform the lookup */
59081 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59082 /* Lookup result */
59083 jx9_result_bool(pCtx,pCol ? 1 : 0);
59084 return JX9_OK;
59085}
59086/*
59087 * bool collection_create(string $name)
59088 * bool db_create(string $name)
59089 * Create a new collection.
59090 * Parameter
59091 * name: Collection name
59092 * Return
59093 * TRUE if the collection was successfuly created. FALSE otherwise.
59094 */
59095static int unqliteBuiltin_collection_create(jx9_context *pCtx,int argc,jx9_value **argv)
59096{
59097 const char *zName;
59098 unqlite_vm *pVm;
59099 SyString sName;
59100 int nByte;
59101 int rc;
59102 /* Extract collection name */
59103 if( argc < 1 ){
59104 /* Missing arguments */
59105 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
59106 /* Return false */
59107 jx9_result_bool(pCtx,0);
59108 return JX9_OK;
59109 }
59110 zName = jx9_value_to_string(argv[0],&nByte);
59111 if( nByte < 1){
59112 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59113 /* Return false */
59114 jx9_result_bool(pCtx,0);
59115 return JX9_OK;
59116 }
59117 SyStringInitFromBuf(&sName,zName,nByte);
59118 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59119 /* Try to create the collection */
59120 rc = unqliteCreateCollection(pVm,&sName);
59121 /* Return the result to the caller */
59122 jx9_result_bool(pCtx,rc == UNQLITE_OK ? 1 : 0);
59123 return JX9_OK;
59124}
59125/*
59126 * value db_fetch(string $col_name)
59127 * value db_get(string $col_name)
59128 * Fetch the current record from a given collection and advance
59129 * the record cursor.
59130 * Parameter
59131 * col_name: Collection name
59132 * Return
59133 * Record content success. NULL on failure (No more records to retrieve).
59134 */
59135static int unqliteBuiltin_db_fetch_next(jx9_context *pCtx,int argc,jx9_value **argv)
59136{
59137 unqlite_col *pCol;
59138 const char *zName;
59139 unqlite_vm *pVm;
59140 SyString sName;
59141 int nByte;
59142 int rc;
59143 /* Extract collection name */
59144 if( argc < 1 ){
59145 /* Missing arguments */
59146 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
59147 /* Return null */
59148 jx9_result_null(pCtx);
59149 return JX9_OK;
59150 }
59151 zName = jx9_value_to_string(argv[0],&nByte);
59152 if( nByte < 1){
59153 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59154 /* Return null */
59155 jx9_result_null(pCtx);
59156 return JX9_OK;
59157 }
59158 SyStringInitFromBuf(&sName,zName,nByte);
59159 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59160 /* Fetch the collection */
59161 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59162 if( pCol ){
59163 /* Fetch the current record */
59164 jx9_value *pValue;
59165 pValue = jx9_context_new_scalar(pCtx);
59166 if( pValue == 0 ){
59167 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
59168 jx9_result_null(pCtx);
59169 return JX9_OK;
59170 }else{
59171 rc = unqliteCollectionFetchNextRecord(pCol,pValue);
59172 if( rc == UNQLITE_OK ){
59173 jx9_result_value(pCtx,pValue);
59174 /* pValue will be automatically released as soon we return from this function */
59175 }else{
59176 /* Return null */
59177 jx9_result_null(pCtx);
59178 }
59179 }
59180 }else{
59181 /* No such collection, return null */
59182 jx9_result_null(pCtx);
59183 }
59184 return JX9_OK;
59185}
59186/*
59187 * value db_fetch_by_id(string $col_name,int64 $record_id)
59188 * value db_get_by_id(string $col_name,int64 $record_id)
59189 * Fetch a record using its unique ID from a given collection.
59190 * Parameter
59191 * col_name: Collection name
59192 * record_id: Record number (__id field of a JSON object)
59193 * Return
59194 * Record content success. NULL on failure (No such record).
59195 */
59196static int unqliteBuiltin_db_fetch_by_id(jx9_context *pCtx,int argc,jx9_value **argv)
59197{
59198 unqlite_col *pCol;
59199 const char *zName;
59200 unqlite_vm *pVm;
59201 SyString sName;
59202 jx9_int64 nId;
59203 int nByte;
59204 int rc;
59205 /* Extract collection name */
59206 if( argc < 2 ){
59207 /* Missing arguments */
59208 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or record ID");
59209 /* Return NULL */
59210 jx9_result_null(pCtx);
59211 return JX9_OK;
59212 }
59213 zName = jx9_value_to_string(argv[0],&nByte);
59214 if( nByte < 1){
59215 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59216 /* Return NULL */
59217 jx9_result_null(pCtx);
59218 return JX9_OK;
59219 }
59220 /* Extract the record ID */
59221 nId = jx9_value_to_int(argv[1]);
59222 SyStringInitFromBuf(&sName,zName,nByte);
59223 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59224 /* Fetch the collection */
59225 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59226 if( pCol ){
59227 /* Fetch the desired record */
59228 jx9_value *pValue;
59229 pValue = jx9_context_new_scalar(pCtx);
59230 if( pValue == 0 ){
59231 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
59232 jx9_result_null(pCtx);
59233 return JX9_OK;
59234 }else{
59235 rc = unqliteCollectionFetchRecordById(pCol,nId,pValue);
59236 if( rc == UNQLITE_OK ){
59237 jx9_result_value(pCtx,pValue);
59238 /* pValue will be automatically released as soon we return from this function */
59239 }else{
59240 /* No such record, return null */
59241 jx9_result_null(pCtx);
59242 }
59243 }
59244 }else{
59245 /* No such collection, return null */
59246 jx9_result_null(pCtx);
59247 }
59248 return JX9_OK;
59249}
59250/*
59251 * array db_fetch_all(string $col_name,[callback filter_callback])
59252 * array db_get_all(string $col_name,[callback filter_callback])
59253 * Retrieve all records of a given collection and apply the given
59254 * callback if available to filter records.
59255 * Parameter
59256 * col_name: Collection name
59257 * Return
59258 * Contents of the collection (JSON array) on success. NULL on failure.
59259 */
59260static int unqliteBuiltin_db_fetch_all(jx9_context *pCtx,int argc,jx9_value **argv)
59261{
59262 unqlite_col *pCol;
59263 const char *zName;
59264 unqlite_vm *pVm;
59265 SyString sName;
59266 int nByte;
59267 int rc;
59268 /* Extract collection name */
59269 if( argc < 1 ){
59270 /* Missing arguments */
59271 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
59272 /* Return NULL */
59273 jx9_result_null(pCtx);
59274 return JX9_OK;
59275 }
59276 zName = jx9_value_to_string(argv[0],&nByte);
59277 if( nByte < 1){
59278 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59279 /* Return NULL */
59280 jx9_result_null(pCtx);
59281 return JX9_OK;
59282 }
59283 SyStringInitFromBuf(&sName,zName,nByte);
59284 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59285 /* Fetch the collection */
59286 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59287 if( pCol ){
59288 jx9_value *pValue,*pArray,*pCallback = 0;
59289 jx9_value sResult; /* Callback result */
59290 /* Allocate an empty scalar value and an empty JSON array */
59291 pArray = jx9_context_new_array(pCtx);
59292 pValue = jx9_context_new_scalar(pCtx);
59293 jx9MemObjInit(pCtx->pVm,&sResult);
59294 if( pValue == 0 || pArray == 0 ){
59295 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
59296 jx9_result_null(pCtx);
59297 return JX9_OK;
59298 }
59299 if( argc > 1 && jx9_value_is_callable(argv[1]) ){
59300 pCallback = argv[1];
59301 }
59302 unqliteCollectionResetRecordCursor(pCol);
59303 /* Fetch collection records one after one */
59304 while( UNQLITE_OK == unqliteCollectionFetchNextRecord(pCol,pValue) ){
59305 if( pCallback ){
59306 jx9_value *apArg[2];
59307 /* Invoke the filter callback */
59308 apArg[0] = pValue;
59309 rc = jx9VmCallUserFunction(pCtx->pVm,pCallback,1,apArg,&sResult);
59310 if( rc == JX9_OK ){
59311 int iResult; /* Callback result */
59312 /* Extract callback result */
59313 iResult = jx9_value_to_bool(&sResult);
59314 if( !iResult ){
59315 /* Discard the result */
59316 unqliteCollectionCacheRemoveRecord(pCol,unqliteCollectionCurrentRecordId(pCol) - 1);
59317 continue;
59318 }
59319 }
59320 }
59321 /* Put the value in the JSON array */
59322 jx9_array_add_elem(pArray,0,pValue);
59323 /* Release the value */
59324 jx9_value_null(pValue);
59325 }
59326 jx9MemObjRelease(&sResult);
59327 /* Finally, return our array */
59328 jx9_result_value(pCtx,pArray);
59329 /* pValue will be automatically released as soon we return from
59330 * this foreign function.
59331 */
59332 }else{
59333 /* No such collection, return null */
59334 jx9_result_null(pCtx);
59335 }
59336 return JX9_OK;
59337}
59338/*
59339 * int64 db_last_record_id(string $col_name)
59340 * Return the ID of the last inserted record.
59341 * Parameter
59342 * col_name: Collection name
59343 * Return
59344 * Record ID (64-bit integer) on success. FALSE on failure.
59345 */
59346static int unqliteBuiltin_db_last_record_id(jx9_context *pCtx,int argc,jx9_value **argv)
59347{
59348 unqlite_col *pCol;
59349 const char *zName;
59350 unqlite_vm *pVm;
59351 SyString sName;
59352 int nByte;
59353 /* Extract collection name */
59354 if( argc < 1 ){
59355 /* Missing arguments */
59356 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
59357 /* Return false */
59358 jx9_result_bool(pCtx,0);
59359 return JX9_OK;
59360 }
59361 zName = jx9_value_to_string(argv[0],&nByte);
59362 if( nByte < 1){
59363 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59364 /* Return false */
59365 jx9_result_bool(pCtx,0);
59366 return JX9_OK;
59367 }
59368 SyStringInitFromBuf(&sName,zName,nByte);
59369 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59370 /* Fetch the collection */
59371 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59372 if( pCol ){
59373 jx9_result_int64(pCtx,unqliteCollectionLastRecordId(pCol));
59374 }else{
59375 /* No such collection, return FALSE */
59376 jx9_result_bool(pCtx,0);
59377 }
59378 return JX9_OK;
59379}
59380/*
59381 * inr64 db_current_record_id(string $col_name)
59382 * Return the current record ID.
59383 * Parameter
59384 * col_name: Collection name
59385 * Return
59386 * Current record ID (64-bit integer) on success. FALSE on failure.
59387 */
59388static int unqliteBuiltin_db_current_record_id(jx9_context *pCtx,int argc,jx9_value **argv)
59389{
59390 unqlite_col *pCol;
59391 const char *zName;
59392 unqlite_vm *pVm;
59393 SyString sName;
59394 int nByte;
59395 /* Extract collection name */
59396 if( argc < 1 ){
59397 /* Missing arguments */
59398 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
59399 /* Return false */
59400 jx9_result_bool(pCtx,0);
59401 return JX9_OK;
59402 }
59403 zName = jx9_value_to_string(argv[0],&nByte);
59404 if( nByte < 1){
59405 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59406 /* Return false */
59407 jx9_result_bool(pCtx,0);
59408 return JX9_OK;
59409 }
59410 SyStringInitFromBuf(&sName,zName,nByte);
59411 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59412 /* Fetch the collection */
59413 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59414 if( pCol ){
59415 jx9_result_int64(pCtx,unqliteCollectionCurrentRecordId(pCol));
59416 }else{
59417 /* No such collection, return FALSE */
59418 jx9_result_bool(pCtx,0);
59419 }
59420 return JX9_OK;
59421}
59422/*
59423 * bool db_reset_record_cursor(string $col_name)
59424 * Reset the record ID cursor.
59425 * Parameter
59426 * col_name: Collection name
59427 * Return
59428 * TRUE on success. FALSE on failure.
59429 */
59430static int unqliteBuiltin_db_reset_record_cursor(jx9_context *pCtx,int argc,jx9_value **argv)
59431{
59432 unqlite_col *pCol;
59433 const char *zName;
59434 unqlite_vm *pVm;
59435 SyString sName;
59436 int nByte;
59437 /* Extract collection name */
59438 if( argc < 1 ){
59439 /* Missing arguments */
59440 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
59441 /* Return false */
59442 jx9_result_bool(pCtx,0);
59443 return JX9_OK;
59444 }
59445 zName = jx9_value_to_string(argv[0],&nByte);
59446 if( nByte < 1){
59447 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59448 /* Return false */
59449 jx9_result_bool(pCtx,0);
59450 return JX9_OK;
59451 }
59452 SyStringInitFromBuf(&sName,zName,nByte);
59453 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59454 /* Fetch the collection */
59455 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59456 if( pCol ){
59457 unqliteCollectionResetRecordCursor(pCol);
59458 jx9_result_bool(pCtx,1);
59459 }else{
59460 /* No such collection */
59461 jx9_result_bool(pCtx,0);
59462 }
59463 return JX9_OK;
59464}
59465/*
59466 * int64 db_total_records(string $col_name)
59467 * Return the total number of inserted records in the given collection.
59468 * Parameter
59469 * col_name: Collection name
59470 * Return
59471 * Total number of records on success. FALSE on failure.
59472 */
59473static int unqliteBuiltin_db_total_records(jx9_context *pCtx,int argc,jx9_value **argv)
59474{
59475 unqlite_col *pCol;
59476 const char *zName;
59477 unqlite_vm *pVm;
59478 SyString sName;
59479 int nByte;
59480 /* Extract collection name */
59481 if( argc < 1 ){
59482 /* Missing arguments */
59483 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
59484 /* Return false */
59485 jx9_result_bool(pCtx,0);
59486 return JX9_OK;
59487 }
59488 zName = jx9_value_to_string(argv[0],&nByte);
59489 if( nByte < 1){
59490 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59491 /* Return false */
59492 jx9_result_bool(pCtx,0);
59493 return JX9_OK;
59494 }
59495 SyStringInitFromBuf(&sName,zName,nByte);
59496 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59497 /* Fetch the collection */
59498 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59499 if( pCol ){
59500 unqlite_int64 nRec;
59501 nRec = unqliteCollectionTotalRecords(pCol);
59502 jx9_result_int64(pCtx,nRec);
59503 }else{
59504 /* No such collection */
59505 jx9_result_bool(pCtx,0);
59506 }
59507 return JX9_OK;
59508}
59509/*
59510 * string db_creation_date(string $col_name)
59511 * Return the creation date of the given collection.
59512 * Parameter
59513 * col_name: Collection name
59514 * Return
59515 * Creation date on success. FALSE on failure.
59516 */
59517static int unqliteBuiltin_db_creation_date(jx9_context *pCtx,int argc,jx9_value **argv)
59518{
59519 unqlite_col *pCol;
59520 const char *zName;
59521 unqlite_vm *pVm;
59522 SyString sName;
59523 int nByte;
59524 /* Extract collection name */
59525 if( argc < 1 ){
59526 /* Missing arguments */
59527 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
59528 /* Return false */
59529 jx9_result_bool(pCtx,0);
59530 return JX9_OK;
59531 }
59532 zName = jx9_value_to_string(argv[0],&nByte);
59533 if( nByte < 1){
59534 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59535 /* Return false */
59536 jx9_result_bool(pCtx,0);
59537 return JX9_OK;
59538 }
59539 SyStringInitFromBuf(&sName,zName,nByte);
59540 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59541 /* Fetch the collection */
59542 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59543 if( pCol ){
59544 Sytm *pTm = &pCol->sCreation;
59545 jx9_result_string_format(pCtx,"%d-%d-%d %02d:%02d:%02d",
59546 pTm->tm_year,pTm->tm_mon,pTm->tm_mday,
59547 pTm->tm_hour,pTm->tm_min,pTm->tm_sec
59548 );
59549 }else{
59550 /* No such collection */
59551 jx9_result_bool(pCtx,0);
59552 }
59553 return JX9_OK;
59554}
59555/*
59556 * bool db_store(string $col_name,...)
59557 * bool db_put(string $col_name,...)
59558 * Store one or more JSON values in a given collection.
59559 * Parameter
59560 * col_name: Collection name
59561 * Return
59562 * TRUE on success. FALSE on failure.
59563 */
59564static int unqliteBuiltin_db_store(jx9_context *pCtx,int argc,jx9_value **argv)
59565{
59566 unqlite_col *pCol;
59567 const char *zName;
59568 unqlite_vm *pVm;
59569 SyString sName;
59570 int nByte;
59571 int rc;
59572 int i;
59573 /* Extract collection name */
59574 if( argc < 2 ){
59575 /* Missing arguments */
59576 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records");
59577 /* Return false */
59578 jx9_result_bool(pCtx,0);
59579 return JX9_OK;
59580 }
59581 zName = jx9_value_to_string(argv[0],&nByte);
59582 if( nByte < 1){
59583 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59584 /* Return false */
59585 jx9_result_bool(pCtx,0);
59586 return JX9_OK;
59587 }
59588 SyStringInitFromBuf(&sName,zName,nByte);
59589 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59590 /* Fetch the collection */
59591 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59592 if( pCol == 0 ){
59593 jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
59594 /* Return false */
59595 jx9_result_bool(pCtx,0);
59596 return JX9_OK;
59597 }
59598 /* Store the given values */
59599 for( i = 1 ; i < argc ; ++i ){
59600 rc = unqliteCollectionPut(pCol,argv[i],0);
59601 if( rc != UNQLITE_OK){
59602 jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,
59603 "Error while storing record %d in collection '%z'",i,&sName
59604 );
59605 /* Return false */
59606 jx9_result_bool(pCtx,0);
59607 return JX9_OK;
59608 }
59609 }
59610 /* All done, return TRUE */
59611 jx9_result_bool(pCtx,1);
59612 return JX9_OK;
59613}
59614/*
59615 * bool db_drop_collection(string $col_name)
59616 * bool collection_delete(string $col_name)
59617 * Remove a given collection from the database.
59618 * Parameter
59619 * col_name: Collection name
59620 * Return
59621 * TRUE on success. FALSE on failure.
59622 */
59623static int unqliteBuiltin_db_drop_col(jx9_context *pCtx,int argc,jx9_value **argv)
59624{
59625 unqlite_col *pCol;
59626 const char *zName;
59627 unqlite_vm *pVm;
59628 SyString sName;
59629 int nByte;
59630 int rc;
59631 /* Extract collection name */
59632 if( argc < 1 ){
59633 /* Missing arguments */
59634 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
59635 /* Return false */
59636 jx9_result_bool(pCtx,0);
59637 return JX9_OK;
59638 }
59639 zName = jx9_value_to_string(argv[0],&nByte);
59640 if( nByte < 1){
59641 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59642 /* Return false */
59643 jx9_result_bool(pCtx,0);
59644 return JX9_OK;
59645 }
59646 SyStringInitFromBuf(&sName,zName,nByte);
59647 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59648 /* Fetch the collection */
59649 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59650 if( pCol == 0 ){
59651 jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
59652 /* Return false */
59653 jx9_result_bool(pCtx,0);
59654 return JX9_OK;
59655 }
59656 /* Drop the collection */
59657 rc = unqliteDropCollection(pCol);
59658 /* Processing result */
59659 jx9_result_bool(pCtx,rc == UNQLITE_OK);
59660 return JX9_OK;
59661}
59662/*
59663 * bool db_drop_record(string $col_name,int64 record_id)
59664 * Remove a given record from a collection.
59665 * Parameter
59666 * col_name: Collection name.
59667 * record_id: ID of the record.
59668 * Return
59669 * TRUE on success. FALSE on failure.
59670 */
59671static int unqliteBuiltin_db_drop_record(jx9_context *pCtx,int argc,jx9_value **argv)
59672{
59673 unqlite_col *pCol;
59674 const char *zName;
59675 unqlite_vm *pVm;
59676 SyString sName;
59677 jx9_int64 nId;
59678 int nByte;
59679 int rc;
59680 /* Extract collection name */
59681 if( argc < 2 ){
59682 /* Missing arguments */
59683 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records");
59684 /* Return false */
59685 jx9_result_bool(pCtx,0);
59686 return JX9_OK;
59687 }
59688 zName = jx9_value_to_string(argv[0],&nByte);
59689 if( nByte < 1){
59690 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59691 /* Return false */
59692 jx9_result_bool(pCtx,0);
59693 return JX9_OK;
59694 }
59695 SyStringInitFromBuf(&sName,zName,nByte);
59696 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59697 /* Fetch the collection */
59698 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59699 if( pCol == 0 ){
59700 jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
59701 /* Return false */
59702 jx9_result_bool(pCtx,0);
59703 return JX9_OK;
59704 }
59705 /* Extract the record ID */
59706 nId = jx9_value_to_int64(argv[1]);
59707 /* Drop the record */
59708 rc = unqliteCollectionDropRecord(pCol,nId,1,1);
59709 /* Processing result */
59710 jx9_result_bool(pCtx,rc == UNQLITE_OK);
59711 return JX9_OK;
59712}
59713/*
59714 * bool db_set_schema(string $col_name, object $json_object)
59715 * Set a schema for a given collection.
59716 * Parameter
59717 * col_name: Collection name.
59718 * json_object: Collection schema (Must be a JSON object).
59719 * Return
59720 * TRUE on success. FALSE on failure.
59721 */
59722static int unqliteBuiltin_db_set_schema(jx9_context *pCtx,int argc,jx9_value **argv)
59723{
59724 unqlite_col *pCol;
59725 const char *zName;
59726 unqlite_vm *pVm;
59727 SyString sName;
59728 int nByte;
59729 int rc;
59730 /* Extract collection name */
59731 if( argc < 2 ){
59732 /* Missing arguments */
59733 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or db scheme");
59734 /* Return false */
59735 jx9_result_bool(pCtx,0);
59736 return JX9_OK;
59737 }
59738 if( !jx9_value_is_json_object(argv[1]) ){
59739 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection scheme");
59740 /* Return false */
59741 jx9_result_bool(pCtx,0);
59742 return JX9_OK;
59743 }
59744 zName = jx9_value_to_string(argv[0],&nByte);
59745 if( nByte < 1){
59746 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59747 /* Return false */
59748 jx9_result_bool(pCtx,0);
59749 return JX9_OK;
59750 }
59751 SyStringInitFromBuf(&sName,zName,nByte);
59752 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59753 /* Fetch the collection */
59754 rc = UNQLITE_NOOP;
59755 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59756 if( pCol ){
59757 /* Set the collection scheme */
59758 rc = unqliteCollectionSetSchema(pCol,argv[1]);
59759 }else{
59760 jx9_context_throw_error_format(pCtx,JX9_CTX_WARNING,
59761 "No such collection '%z'",
59762 &sName
59763 );
59764 }
59765 /* Processing result */
59766 jx9_result_bool(pCtx,rc == UNQLITE_OK);
59767 return JX9_OK;
59768}
59769/*
59770 * object db_get_schema(string $col_name)
59771 * Return the schema associated with a given collection.
59772 * Parameter
59773 * col_name: Collection name
59774 * Return
59775 * Collection schema on success. null otherwise.
59776 */
59777static int unqliteBuiltin_db_get_schema(jx9_context *pCtx,int argc,jx9_value **argv)
59778{
59779 unqlite_col *pCol;
59780 const char *zName;
59781 unqlite_vm *pVm;
59782 SyString sName;
59783 int nByte;
59784 /* Extract collection name */
59785 if( argc < 1 ){
59786 /* Missing arguments */
59787 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or db scheme");
59788 /* Return false */
59789 jx9_result_bool(pCtx,0);
59790 return JX9_OK;
59791 }
59792 zName = jx9_value_to_string(argv[0],&nByte);
59793 if( nByte < 1){
59794 jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
59795 /* Return false */
59796 jx9_result_bool(pCtx,0);
59797 return JX9_OK;
59798 }
59799 SyStringInitFromBuf(&sName,zName,nByte);
59800 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59801 /* Fetch the collection */
59802 pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
59803 if( pCol ){
59804 /* Return the collection schema */
59805 jx9_result_value(pCtx,&pCol->sSchema);
59806 }else{
59807 jx9_context_throw_error_format(pCtx,JX9_CTX_WARNING,
59808 "No such collection '%z'",
59809 &sName
59810 );
59811 jx9_result_null(pCtx);
59812 }
59813 return JX9_OK;
59814}
59815/*
59816 * bool db_begin(void)
59817 * Manually begin a write transaction.
59818 * Parameter
59819 * None
59820 * Return
59821 * TRUE on success. FALSE otherwise.
59822 */
59823static int unqliteBuiltin_db_begin(jx9_context *pCtx,int argc,jx9_value **argv)
59824{
59825 unqlite_vm *pVm;
59826 unqlite *pDb;
59827 int rc;
59828 SXUNUSED(argc); /* cc warning */
59829 SXUNUSED(argv);
59830 /* Point to the unqlite Vm */
59831 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59832 /* Point to the underlying database handle */
59833 pDb = pVm->pDb;
59834 /* Begin the transaction */
59835 rc = unqlitePagerBegin(pDb->sDB.pPager);
59836 /* result */
59837 jx9_result_bool(pCtx,rc == UNQLITE_OK );
59838 return JX9_OK;
59839}
59840/*
59841 * bool db_commit(void)
59842 * Manually commit a transaction.
59843 * Parameter
59844 * None
59845 * Return
59846 * TRUE if the transaction was successfuly commited. FALSE otherwise.
59847 */
59848static int unqliteBuiltin_db_commit(jx9_context *pCtx,int argc,jx9_value **argv)
59849{
59850 unqlite_vm *pVm;
59851 unqlite *pDb;
59852 int rc;
59853 SXUNUSED(argc); /* cc warning */
59854 SXUNUSED(argv);
59855 /* Point to the unqlite Vm */
59856 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59857 /* Point to the underlying database handle */
59858 pDb = pVm->pDb;
59859 /* Commit the transaction if any */
59860 rc = unqlitePagerCommit(pDb->sDB.pPager);
59861 /* Commit result */
59862 jx9_result_bool(pCtx,rc == UNQLITE_OK );
59863 return JX9_OK;
59864}
59865/*
59866 * bool db_rollback(void)
59867 * Manually rollback a transaction.
59868 * Parameter
59869 * None
59870 * Return
59871 * TRUE if the transaction was successfuly rolled back. FALSE otherwise
59872 */
59873static int unqliteBuiltin_db_rollback(jx9_context *pCtx,int argc,jx9_value **argv)
59874{
59875 unqlite_vm *pVm;
59876 unqlite *pDb;
59877 int rc;
59878 SXUNUSED(argc); /* cc warning */
59879 SXUNUSED(argv);
59880 /* Point to the unqlite Vm */
59881 pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
59882 /* Point to the underlying database handle */
59883 pDb = pVm->pDb;
59884 /* Rollback the transaction if any */
59885 rc = unqlitePagerRollback(pDb->sDB.pPager,TRUE);
59886 /* Rollback result */
59887 jx9_result_bool(pCtx,rc == UNQLITE_OK );
59888 return JX9_OK;
59889}
59890/*
59891 * Register all the UnQLite foreign functions defined above.
59892 */
59893UNQLITE_PRIVATE int unqliteRegisterJx9Functions(unqlite_vm *pVm)
59894{
59895 static const jx9_builtin_func aBuiltin[] = {
59896 { "db_version" , unqliteBuiltin_db_version },
59897 { "db_copyright", unqliteBuiltin_db_credits },
59898 { "db_credits" , unqliteBuiltin_db_credits },
59899 { "db_sig" , unqliteBuiltin_db_sig },
59900 { "db_errlog", unqliteBuiltin_db_errlog },
59901 { "collection_exists", unqliteBuiltin_collection_exists },
59902 { "db_exists", unqliteBuiltin_collection_exists },
59903 { "collection_create", unqliteBuiltin_collection_create },
59904 { "db_create", unqliteBuiltin_collection_create },
59905 { "db_fetch", unqliteBuiltin_db_fetch_next },
59906 { "db_get", unqliteBuiltin_db_fetch_next },
59907 { "db_fetch_by_id", unqliteBuiltin_db_fetch_by_id },
59908 { "db_get_by_id", unqliteBuiltin_db_fetch_by_id },
59909 { "db_fetch_all", unqliteBuiltin_db_fetch_all },
59910 { "db_get_all", unqliteBuiltin_db_fetch_all },
59911 { "db_last_record_id", unqliteBuiltin_db_last_record_id },
59912 { "db_current_record_id", unqliteBuiltin_db_current_record_id },
59913 { "db_reset_record_cursor", unqliteBuiltin_db_reset_record_cursor },
59914 { "db_total_records", unqliteBuiltin_db_total_records },
59915 { "db_creation_date", unqliteBuiltin_db_creation_date },
59916 { "db_store", unqliteBuiltin_db_store },
59917 { "db_put", unqliteBuiltin_db_store },
59918 { "db_drop_collection", unqliteBuiltin_db_drop_col },
59919 { "collection_delete", unqliteBuiltin_db_drop_col },
59920 { "db_drop_record", unqliteBuiltin_db_drop_record },
59921 { "db_set_schema", unqliteBuiltin_db_set_schema },
59922 { "db_get_schema", unqliteBuiltin_db_get_schema },
59923 { "db_begin", unqliteBuiltin_db_begin },
59924 { "db_commit", unqliteBuiltin_db_commit },
59925 { "db_rollback", unqliteBuiltin_db_rollback },
59926 };
59927 int rc = UNQLITE_OK;
59928 sxu32 n;
59929 /* Register the unQLite functions defined above in the Jx9 call table */
59930 for( n = 0 ; n < SX_ARRAYSIZE(aBuiltin) ; ++n ){
59931 rc = jx9_create_function(pVm->pJx9Vm,aBuiltin[n].zName,aBuiltin[n].xFunc,pVm);
59932 }
59933 return rc;
59934}
59935/* END-OF-IMPLEMENTATION: unqlite@embedded@symisc 34-09-46 */
59936/*
59937 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
59938 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
59939 * Version 1.1.6
59940 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
59941 * please contact Symisc Systems via:
59942 * legal@symisc.net
59943 * licensing@symisc.net
59944 * contact@symisc.net
59945 * or visit:
59946 * http://unqlite.org/licensing.html
59947 */
59948/*
59949 * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
59950 * All rights reserved.
59951 *
59952 * Redistribution and use in source and binary forms, with or without
59953 * modification, are permitted provided that the following conditions
59954 * are met:
59955 * 1. Redistributions of source code must retain the above copyright
59956 * notice, this list of conditions and the following disclaimer.
59957 * 2. Redistributions in binary form must reproduce the above copyright
59958 * notice, this list of conditions and the following disclaimer in the
59959 * documentation and/or other materials provided with the distribution.
59960 *
59961 * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
59962 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
59963 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
59964 * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS
59965 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
59966 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
59967 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
59968 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59969 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
59970 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
59971 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59972 */
diff --git a/common/unqlite/unqlite.h b/common/unqlite/unqlite.h
deleted file mode 100644
index bf7ee50..0000000
--- a/common/unqlite/unqlite.h
+++ /dev/null
@@ -1,949 +0,0 @@
1/* This file was automatically generated. Do not edit (Except for compile time directives)! */
2#ifndef _UNQLITE_H_
3#define _UNQLITE_H_
4/*
5 * Symisc UnQLite: An Embeddable NoSQL (Post Modern) Database Engine.
6 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
7 * Version 1.1.6
8 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
9 * please contact Symisc Systems via:
10 * legal@symisc.net
11 * licensing@symisc.net
12 * contact@symisc.net
13 * or visit:
14 * http://unqlite.org/licensing.html
15 */
16/*
17 * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
18 * All rights reserved.
19 *
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions
22 * are met:
23 * 1. Redistributions of source code must retain the above copyright
24 * notice, this list of conditions and the following disclaimer.
25 * 2. Redistributions in binary form must reproduce the above copyright
26 * notice, this list of conditions and the following disclaimer in the
27 * documentation and/or other materials provided with the distribution.
28 *
29 * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
30 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
32 * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS
33 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
36 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
37 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
38 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
39 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 */
41 /* $SymiscID: unqlite.h v1.1 UNIX|WIN32/64 2012-11-02 02:10 stable <chm@symisc.net> $ */
42#include <stdarg.h> /* needed for the definition of va_list */
43/*
44 * Compile time engine version, signature, identification in the symisc source tree
45 * and copyright notice.
46 * Each macro have an equivalent C interface associated with it that provide the same
47 * information but are associated with the library instead of the header file.
48 * Refer to [unqlite_lib_version()], [unqlite_lib_signature()], [unqlite_lib_ident()] and
49 * [unqlite_lib_copyright()] for more information.
50 */
51/*
52 * The UNQLITE_VERSION C preprocessor macroevaluates to a string literal
53 * that is the unqlite version in the format "X.Y.Z" where X is the major
54 * version number and Y is the minor version number and Z is the release
55 * number.
56 */
57#define UNQLITE_VERSION "1.1.6"
58/*
59 * The UNQLITE_VERSION_NUMBER C preprocessor macro resolves to an integer
60 * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same
61 * numbers used in [UNQLITE_VERSION].
62 */
63#define UNQLITE_VERSION_NUMBER 1001006
64/*
65 * The UNQLITE_SIG C preprocessor macro evaluates to a string
66 * literal which is the public signature of the unqlite engine.
67 * This signature could be included for example in a host-application
68 * generated Server MIME header as follows:
69 * Server: YourWebServer/x.x unqlite/x.x.x \r\n
70 */
71#define UNQLITE_SIG "unqlite/1.1.6"
72/*
73 * UnQLite identification in the Symisc source tree:
74 * Each particular check-in of a particular software released
75 * by symisc systems have an unique identifier associated with it.
76 * This macro hold the one associated with unqlite.
77 */
78#define UNQLITE_IDENT "unqlite:b172a1e2c3f62fb35c8e1fb2795121f82356cad6"
79/*
80 * Copyright notice.
81 * If you have any questions about the licensing situation, please
82 * visit http://unqlite.org/licensing.html
83 * or contact Symisc Systems via:
84 * legal@symisc.net
85 * licensing@symisc.net
86 * contact@symisc.net
87 */
88#define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine <chm@symisc.net>] 2012-2013, http://unqlite.org/"
89
90/* Forward declaration to public objects */
91typedef struct unqlite_io_methods unqlite_io_methods;
92typedef struct unqlite_kv_methods unqlite_kv_methods;
93typedef struct unqlite_kv_engine unqlite_kv_engine;
94typedef struct jx9_io_stream unqlite_io_stream;
95typedef struct jx9_context unqlite_context;
96typedef struct jx9_value unqlite_value;
97typedef struct unqlite_vfs unqlite_vfs;
98typedef struct unqlite_vm unqlite_vm;
99typedef struct unqlite unqlite;
100/*
101 * ------------------------------
102 * Compile time directives
103 * ------------------------------
104 * For most purposes, UnQLite can be built just fine using the default compilation options.
105 * However, if required, the compile-time options documented below can be used to omit UnQLite
106 * features (resulting in a smaller compiled library size) or to change the default values
107 * of some parameters.
108 * Every effort has been made to ensure that the various combinations of compilation options
109 * work harmoniously and produce a working library.
110 *
111 * UNQLITE_ENABLE_THREADS
112 * This option controls whether or not code is included in UnQLite to enable it to operate
113 * safely in a multithreaded environment. The default is not. All mutexing code is omitted
114 * and it is unsafe to use UnQLite in a multithreaded program. When compiled with the
115 * UNQLITE_ENABLE_THREADS directive enabled, UnQLite can be used in a multithreaded program
116 * and it is safe to share the same virtual machine and engine handle between two or more threads.
117 * The value of UNQLITE_ENABLE_THREADS can be determined at run-time using the unqlite_lib_is_threadsafe()
118 * interface.
119 * When UnQLite has been compiled with threading support then the threading mode can be altered
120 * at run-time using the unqlite_lib_config() interface together with one of these verbs:
121 * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE
122 * UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI
123 * Platforms others than Windows and UNIX systems must install their own mutex subsystem via
124 * unqlite_lib_config() with a configuration verb set to UNQLITE_LIB_CONFIG_USER_MUTEX.
125 * Otherwise the library is not threadsafe.
126 * Note that you must link UnQLite with the POSIX threads library under UNIX systems (i.e: -lpthread).
127 *
128 * Options To Omit/Enable Features
129 *
130 * The following options can be used to reduce the size of the compiled library by omitting optional
131 * features. This is probably only useful in embedded systems where space is especially tight, as even
132 * with all features included the UnQLite library is relatively small. Don't forget to tell your
133 * compiler to optimize for binary size! (the -Os option if using GCC). Telling your compiler
134 * to optimize for size usually has a much larger impact on library footprint than employing
135 * any of these compile-time options.
136 *
137 * JX9_DISABLE_BUILTIN_FUNC
138 * Jx9 is shipped with more than 312 built-in functions suitable for most purposes like
139 * string and INI processing, ZIP extracting, Base64 encoding/decoding, JSON encoding/decoding
140 * and so forth.
141 * If this directive is enabled, then all built-in Jx9 functions are omitted from the build.
142 * Note that special functions such as db_create(), db_store(), db_fetch(), etc. are not omitted
143 * from the build and are not affected by this directive.
144 *
145 * JX9_ENABLE_MATH_FUNC
146 * If this directive is enabled, built-in math functions such as sqrt(), abs(), log(), ceil(), etc.
147 * are included in the build. Note that you may need to link UnQLite with the math library in same
148 * Linux/BSD flavor (i.e: -lm).
149 *
150 * JX9_DISABLE_DISK_IO
151 * If this directive is enabled, built-in VFS functions such as chdir(), mkdir(), chroot(), unlink(),
152 * sleep(), etc. are omitted from the build.
153 *
154 * UNQLITE_ENABLE_JX9_HASH_IO
155 * If this directive is enabled, built-in hash functions such as md5(), sha1(), md5_file(), crc32(), etc.
156 * are included in the build.
157 */
158/* Symisc public definitions */
159#if !defined(SYMISC_STANDARD_DEFS)
160#define SYMISC_STANDARD_DEFS
161#if defined (_WIN32) || defined (WIN32) || defined(__MINGW32__) || defined (_MSC_VER) || defined (_WIN32_WCE)
162/* Windows Systems */
163#if !defined(__WINNT__)
164#define __WINNT__
165#endif
166/*
167 * Determine if we are dealing with WindowsCE - which has a much
168 * reduced API.
169 */
170#if defined(_WIN32_WCE)
171#ifndef __WIN_CE__
172#define __WIN_CE__
173#endif /* __WIN_CE__ */
174#endif /* _WIN32_WCE */
175#else
176/*
177 * By default we will assume that we are compiling on a UNIX systems.
178 * Otherwise the OS_OTHER directive must be defined.
179 */
180#if !defined(OS_OTHER)
181#if !defined(__UNIXES__)
182#define __UNIXES__
183#endif /* __UNIXES__ */
184#else
185#endif /* OS_OTHER */
186#endif /* __WINNT__/__UNIXES__ */
187#if defined(_MSC_VER) || defined(__BORLANDC__)
188typedef signed __int64 sxi64; /* 64 bits(8 bytes) signed int64 */
189typedef unsigned __int64 sxu64; /* 64 bits(8 bytes) unsigned int64 */
190#else
191typedef signed long long int sxi64; /* 64 bits(8 bytes) signed int64 */
192typedef unsigned long long int sxu64; /* 64 bits(8 bytes) unsigned int64 */
193#endif /* _MSC_VER */
194/* Signature of the consumer routine */
195typedef int (*ProcConsumer)(const void *, unsigned int, void *);
196/* Forward reference */
197typedef struct SyMutexMethods SyMutexMethods;
198typedef struct SyMemMethods SyMemMethods;
199typedef struct SyString SyString;
200typedef struct syiovec syiovec;
201typedef struct SyMutex SyMutex;
202typedef struct Sytm Sytm;
203/* Scatter and gather array. */
204struct syiovec
205{
206#if defined (__WINNT__)
207 /* Same fields type and offset as WSABUF structure defined one winsock2 header */
208 unsigned long nLen;
209 char *pBase;
210#else
211 void *pBase;
212 unsigned long nLen;
213#endif
214};
215struct SyString
216{
217 const char *zString; /* Raw string (may not be null terminated) */
218 unsigned int nByte; /* Raw string length */
219};
220/* Time structure. */
221struct Sytm
222{
223 int tm_sec; /* seconds (0 - 60) */
224 int tm_min; /* minutes (0 - 59) */
225 int tm_hour; /* hours (0 - 23) */
226 int tm_mday; /* day of month (1 - 31) */
227 int tm_mon; /* month of year (0 - 11) */
228 int tm_year; /* year + 1900 */
229 int tm_wday; /* day of week (Sunday = 0) */
230 int tm_yday; /* day of year (0 - 365) */
231 int tm_isdst; /* is summer time in effect? */
232 char *tm_zone; /* abbreviation of timezone name */
233 long tm_gmtoff; /* offset from UTC in seconds */
234};
235/* Convert a tm structure (struct tm *) found in <time.h> to a Sytm structure */
236#define STRUCT_TM_TO_SYTM(pTM, pSYTM) \
237 (pSYTM)->tm_hour = (pTM)->tm_hour;\
238 (pSYTM)->tm_min = (pTM)->tm_min;\
239 (pSYTM)->tm_sec = (pTM)->tm_sec;\
240 (pSYTM)->tm_mon = (pTM)->tm_mon;\
241 (pSYTM)->tm_mday = (pTM)->tm_mday;\
242 (pSYTM)->tm_year = (pTM)->tm_year + 1900;\
243 (pSYTM)->tm_yday = (pTM)->tm_yday;\
244 (pSYTM)->tm_wday = (pTM)->tm_wday;\
245 (pSYTM)->tm_isdst = (pTM)->tm_isdst;\
246 (pSYTM)->tm_gmtoff = 0;\
247 (pSYTM)->tm_zone = 0;
248
249/* Convert a SYSTEMTIME structure (LPSYSTEMTIME: Windows Systems only ) to a Sytm structure */
250#define SYSTEMTIME_TO_SYTM(pSYSTIME, pSYTM) \
251 (pSYTM)->tm_hour = (pSYSTIME)->wHour;\
252 (pSYTM)->tm_min = (pSYSTIME)->wMinute;\
253 (pSYTM)->tm_sec = (pSYSTIME)->wSecond;\
254 (pSYTM)->tm_mon = (pSYSTIME)->wMonth - 1;\
255 (pSYTM)->tm_mday = (pSYSTIME)->wDay;\
256 (pSYTM)->tm_year = (pSYSTIME)->wYear;\
257 (pSYTM)->tm_yday = 0;\
258 (pSYTM)->tm_wday = (pSYSTIME)->wDayOfWeek;\
259 (pSYTM)->tm_gmtoff = 0;\
260 (pSYTM)->tm_isdst = -1;\
261 (pSYTM)->tm_zone = 0;
262
263/* Dynamic memory allocation methods. */
264struct SyMemMethods
265{
266 void * (*xAlloc)(unsigned int); /* [Required:] Allocate a memory chunk */
267 void * (*xRealloc)(void *, unsigned int); /* [Required:] Re-allocate a memory chunk */
268 void (*xFree)(void *); /* [Required:] Release a memory chunk */
269 unsigned int (*xChunkSize)(void *); /* [Optional:] Return chunk size */
270 int (*xInit)(void *); /* [Optional:] Initialization callback */
271 void (*xRelease)(void *); /* [Optional:] Release callback */
272 void *pUserData; /* [Optional:] First argument to xInit() and xRelease() */
273};
274/* Out of memory callback signature. */
275typedef int (*ProcMemError)(void *);
276/* Mutex methods. */
277struct SyMutexMethods
278{
279 int (*xGlobalInit)(void); /* [Optional:] Global mutex initialization */
280 void (*xGlobalRelease)(void); /* [Optional:] Global Release callback () */
281 SyMutex * (*xNew)(int); /* [Required:] Request a new mutex */
282 void (*xRelease)(SyMutex *); /* [Optional:] Release a mutex */
283 void (*xEnter)(SyMutex *); /* [Required:] Enter mutex */
284 int (*xTryEnter)(SyMutex *); /* [Optional:] Try to enter a mutex */
285 void (*xLeave)(SyMutex *); /* [Required:] Leave a locked mutex */
286};
287#if defined (_MSC_VER) || defined (__MINGW32__) || defined (__GNUC__) && defined (__declspec)
288#define SX_APIIMPORT __declspec(dllimport)
289#define SX_APIEXPORT __declspec(dllexport)
290#else
291#define SX_APIIMPORT
292#define SX_APIEXPORT
293#endif
294/* Standard return values from Symisc public interfaces */
295#define SXRET_OK 0 /* Not an error */
296#define SXERR_MEM (-1) /* Out of memory */
297#define SXERR_IO (-2) /* IO error */
298#define SXERR_EMPTY (-3) /* Empty field */
299#define SXERR_LOCKED (-4) /* Locked operation */
300#define SXERR_ORANGE (-5) /* Out of range value */
301#define SXERR_NOTFOUND (-6) /* Item not found */
302#define SXERR_LIMIT (-7) /* Limit reached */
303#define SXERR_MORE (-8) /* Need more input */
304#define SXERR_INVALID (-9) /* Invalid parameter */
305#define SXERR_ABORT (-10) /* User callback request an operation abort */
306#define SXERR_EXISTS (-11) /* Item exists */
307#define SXERR_SYNTAX (-12) /* Syntax error */
308#define SXERR_UNKNOWN (-13) /* Unknown error */
309#define SXERR_BUSY (-14) /* Busy operation */
310#define SXERR_OVERFLOW (-15) /* Stack or buffer overflow */
311#define SXERR_WILLBLOCK (-16) /* Operation will block */
312#define SXERR_NOTIMPLEMENTED (-17) /* Operation not implemented */
313#define SXERR_EOF (-18) /* End of input */
314#define SXERR_PERM (-19) /* Permission error */
315#define SXERR_NOOP (-20) /* No-op */
316#define SXERR_FORMAT (-21) /* Invalid format */
317#define SXERR_NEXT (-22) /* Not an error */
318#define SXERR_OS (-23) /* System call return an error */
319#define SXERR_CORRUPT (-24) /* Corrupted pointer */
320#define SXERR_CONTINUE (-25) /* Not an error: Operation in progress */
321#define SXERR_NOMATCH (-26) /* No match */
322#define SXERR_RESET (-27) /* Operation reset */
323#define SXERR_DONE (-28) /* Not an error */
324#define SXERR_SHORT (-29) /* Buffer too short */
325#define SXERR_PATH (-30) /* Path error */
326#define SXERR_TIMEOUT (-31) /* Timeout */
327#define SXERR_BIG (-32) /* Too big for processing */
328#define SXERR_RETRY (-33) /* Retry your call */
329#define SXERR_IGNORE (-63) /* Ignore */
330#endif /* SYMISC_PUBLIC_DEFS */
331/*
332 * Marker for exported interfaces.
333 */
334#define UNQLITE_APIEXPORT SX_APIEXPORT
335/*
336 * If compiling for a processor that lacks floating point
337 * support, substitute integer for floating-point.
338 */
339#ifdef UNQLITE_OMIT_FLOATING_POINT
340typedef sxi64 uqlite_real;
341#else
342typedef double unqlite_real;
343#endif
344typedef sxi64 unqlite_int64;
345/* Standard UnQLite return values */
346#define UNQLITE_OK SXRET_OK /* Successful result */
347/* Beginning of error codes */
348#define UNQLITE_NOMEM SXERR_MEM /* Out of memory */
349#define UNQLITE_ABORT SXERR_ABORT /* Another thread have released this instance */
350#define UNQLITE_IOERR SXERR_IO /* IO error */
351#define UNQLITE_CORRUPT SXERR_CORRUPT /* Corrupt pointer */
352#define UNQLITE_LOCKED SXERR_LOCKED /* Forbidden Operation */
353#define UNQLITE_BUSY SXERR_BUSY /* The database file is locked */
354#define UNQLITE_DONE SXERR_DONE /* Operation done */
355#define UNQLITE_PERM SXERR_PERM /* Permission error */
356#define UNQLITE_NOTIMPLEMENTED SXERR_NOTIMPLEMENTED /* Method not implemented by the underlying Key/Value storage engine */
357#define UNQLITE_NOTFOUND SXERR_NOTFOUND /* No such record */
358#define UNQLITE_NOOP SXERR_NOOP /* No such method */
359#define UNQLITE_INVALID SXERR_INVALID /* Invalid parameter */
360#define UNQLITE_EOF SXERR_EOF /* End Of Input */
361#define UNQLITE_UNKNOWN SXERR_UNKNOWN /* Unknown configuration option */
362#define UNQLITE_LIMIT SXERR_LIMIT /* Database limit reached */
363#define UNQLITE_EXISTS SXERR_EXISTS /* Record exists */
364#define UNQLITE_EMPTY SXERR_EMPTY /* Empty record */
365#define UNQLITE_COMPILE_ERR (-70) /* Compilation error */
366#define UNQLITE_VM_ERR (-71) /* Virtual machine error */
367#define UNQLITE_FULL (-73) /* Full database (unlikely) */
368#define UNQLITE_CANTOPEN (-74) /* Unable to open the database file */
369#define UNQLITE_READ_ONLY (-75) /* Read only Key/Value storage engine */
370#define UNQLITE_LOCKERR (-76) /* Locking protocol error */
371/* end-of-error-codes */
372/*
373 * Database Handle Configuration Commands.
374 *
375 * The following set of constants are the available configuration verbs that can
376 * be used by the host-application to configure an UnQLite database handle.
377 * These constants must be passed as the second argument to [unqlite_config()].
378 *
379 * Each options require a variable number of arguments.
380 * The [unqlite_config()] interface will return UNQLITE_OK on success, any other
381 * return value indicates failure.
382 * For a full discussion on the configuration verbs and their expected
383 * parameters, please refer to this page:
384 * http://unqlite.org/c_api/unqlite_config.html
385 */
386#define UNQLITE_CONFIG_JX9_ERR_LOG 1 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
387#define UNQLITE_CONFIG_MAX_PAGE_CACHE 2 /* ONE ARGUMENT: int nMaxPage */
388#define UNQLITE_CONFIG_ERR_LOG 3 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
389#define UNQLITE_CONFIG_KV_ENGINE 4 /* ONE ARGUMENT: const char *zKvName */
390#define UNQLITE_CONFIG_DISABLE_AUTO_COMMIT 5 /* NO ARGUMENTS */
391#define UNQLITE_CONFIG_GET_KV_NAME 6 /* ONE ARGUMENT: const char **pzPtr */
392/*
393 * UnQLite/Jx9 Virtual Machine Configuration Commands.
394 *
395 * The following set of constants are the available configuration verbs that can
396 * be used by the host-application to configure the Jx9 (Via UnQLite) Virtual machine.
397 * These constants must be passed as the second argument to the [unqlite_vm_config()]
398 * interface.
399 * Each options require a variable number of arguments.
400 * The [unqlite_vm_config()] interface will return UNQLITE_OK on success, any other return
401 * value indicates failure.
402 * There are many options but the most importants are: UNQLITE_VM_CONFIG_OUTPUT which install
403 * a VM output consumer callback, UNQLITE_VM_CONFIG_HTTP_REQUEST which parse and register
404 * a HTTP request and UNQLITE_VM_CONFIG_ARGV_ENTRY which populate the $argv array.
405 * For a full discussion on the configuration verbs and their expected parameters, please
406 * refer to this page:
407 * http://unqlite.org/c_api/unqlite_vm_config.html
408 */
409#define UNQLITE_VM_CONFIG_OUTPUT 1 /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
410#define UNQLITE_VM_CONFIG_IMPORT_PATH 2 /* ONE ARGUMENT: const char *zIncludePath */
411#define UNQLITE_VM_CONFIG_ERR_REPORT 3 /* NO ARGUMENTS: Report all run-time errors in the VM output */
412#define UNQLITE_VM_CONFIG_RECURSION_DEPTH 4 /* ONE ARGUMENT: int nMaxDepth */
413#define UNQLITE_VM_OUTPUT_LENGTH 5 /* ONE ARGUMENT: unsigned int *pLength */
414#define UNQLITE_VM_CONFIG_CREATE_VAR 6 /* TWO ARGUMENTS: const char *zName, unqlite_value *pValue */
415#define UNQLITE_VM_CONFIG_HTTP_REQUEST 7 /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
416#define UNQLITE_VM_CONFIG_SERVER_ATTR 8 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
417#define UNQLITE_VM_CONFIG_ENV_ATTR 9 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
418#define UNQLITE_VM_CONFIG_EXEC_VALUE 10 /* ONE ARGUMENT: unqlite_value **ppValue */
419#define UNQLITE_VM_CONFIG_IO_STREAM 11 /* ONE ARGUMENT: const unqlite_io_stream *pStream */
420#define UNQLITE_VM_CONFIG_ARGV_ENTRY 12 /* ONE ARGUMENT: const char *zValue */
421#define UNQLITE_VM_CONFIG_EXTRACT_OUTPUT 13 /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
422/*
423 * Storage engine configuration commands.
424 *
425 * The following set of constants are the available configuration verbs that can
426 * be used by the host-application to configure the underlying storage engine (i.e Hash, B+tree, R+tree).
427 * These constants must be passed as the first argument to [unqlite_kv_config()].
428 * Each options require a variable number of arguments.
429 * The [unqlite_kv_config()] interface will return UNQLITE_OK on success, any other return
430 * value indicates failure.
431 * For a full discussion on the configuration verbs and their expected parameters, please
432 * refer to this page:
433 * http://unqlite.org/c_api/unqlite_kv_config.html
434 */
435#define UNQLITE_KV_CONFIG_HASH_FUNC 1 /* ONE ARGUMENT: unsigned int (*xHash)(const void *,unsigned int) */
436#define UNQLITE_KV_CONFIG_CMP_FUNC 2 /* ONE ARGUMENT: int (*xCmp)(const void *,const void *,unsigned int) */
437/*
438 * Global Library Configuration Commands.
439 *
440 * The following set of constants are the available configuration verbs that can
441 * be used by the host-application to configure the whole library.
442 * These constants must be passed as the first argument to [unqlite_lib_config()].
443 *
444 * Each options require a variable number of arguments.
445 * The [unqlite_lib_config()] interface will return UNQLITE_OK on success, any other return
446 * value indicates failure.
447 * Notes:
448 * The default configuration is recommended for most applications and so the call to
449 * [unqlite_lib_config()] is usually not necessary. It is provided to support rare
450 * applications with unusual needs.
451 * The [unqlite_lib_config()] interface is not threadsafe. The application must insure that
452 * no other [unqlite_*()] interfaces are invoked by other threads while [unqlite_lib_config()]
453 * is running. Furthermore, [unqlite_lib_config()] may only be invoked prior to library
454 * initialization using [unqlite_lib_init()] or [unqlite_init()] or after shutdown
455 * by [unqlite_lib_shutdown()]. If [unqlite_lib_config()] is called after [unqlite_lib_init()]
456 * or [unqlite_init()] and before [unqlite_lib_shutdown()] then it will return UNQLITE_LOCKED.
457 * For a full discussion on the configuration verbs and their expected parameters, please
458 * refer to this page:
459 * http://unqlite.org/c_api/unqlite_lib.html
460 */
461#define UNQLITE_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */
462#define UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
463#define UNQLITE_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */
464#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */
465#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */
466#define UNQLITE_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const unqlite_vfs *pVfs */
467#define UNQLITE_LIB_CONFIG_STORAGE_ENGINE 7 /* ONE ARGUMENT: unqlite_kv_methods *pStorage */
468#define UNQLITE_LIB_CONFIG_PAGE_SIZE 8 /* ONE ARGUMENT: int iPageSize */
469/*
470 * These bit values are intended for use in the 3rd parameter to the [unqlite_open()] interface
471 * and in the 4th parameter to the xOpen method of the [unqlite_vfs] object.
472 */
473#define UNQLITE_OPEN_READONLY 0x00000001 /* Read only mode. Ok for [unqlite_open] */
474#define UNQLITE_OPEN_READWRITE 0x00000002 /* Ok for [unqlite_open] */
475#define UNQLITE_OPEN_CREATE 0x00000004 /* Ok for [unqlite_open] */
476#define UNQLITE_OPEN_EXCLUSIVE 0x00000008 /* VFS only */
477#define UNQLITE_OPEN_TEMP_DB 0x00000010 /* VFS only */
478#define UNQLITE_OPEN_NOMUTEX 0x00000020 /* Ok for [unqlite_open] */
479#define UNQLITE_OPEN_OMIT_JOURNALING 0x00000040 /* Omit journaling for this database. Ok for [unqlite_open] */
480#define UNQLITE_OPEN_IN_MEMORY 0x00000080 /* An in memory database. Ok for [unqlite_open]*/
481#define UNQLITE_OPEN_MMAP 0x00000100 /* Obtain a memory view of the whole file. Ok for [unqlite_open] */
482/*
483 * Synchronization Type Flags
484 *
485 * When UnQLite invokes the xSync() method of an [unqlite_io_methods] object it uses
486 * a combination of these integer values as the second argument.
487 *
488 * When the UNQLITE_SYNC_DATAONLY flag is used, it means that the sync operation only
489 * needs to flush data to mass storage. Inode information need not be flushed.
490 * If the lower four bits of the flag equal UNQLITE_SYNC_NORMAL, that means to use normal
491 * fsync() semantics. If the lower four bits equal UNQLITE_SYNC_FULL, that means to use
492 * Mac OS X style fullsync instead of fsync().
493 */
494#define UNQLITE_SYNC_NORMAL 0x00002
495#define UNQLITE_SYNC_FULL 0x00003
496#define UNQLITE_SYNC_DATAONLY 0x00010
497/*
498 * File Locking Levels
499 *
500 * UnQLite uses one of these integer values as the second
501 * argument to calls it makes to the xLock() and xUnlock() methods
502 * of an [unqlite_io_methods] object.
503 */
504#define UNQLITE_LOCK_NONE 0
505#define UNQLITE_LOCK_SHARED 1
506#define UNQLITE_LOCK_RESERVED 2
507#define UNQLITE_LOCK_PENDING 3
508#define UNQLITE_LOCK_EXCLUSIVE 4
509/*
510 * CAPIREF: OS Interface: Open File Handle
511 *
512 * An [unqlite_file] object represents an open file in the [unqlite_vfs] OS interface
513 * layer.
514 * Individual OS interface implementations will want to subclass this object by appending
515 * additional fields for their own use. The pMethods entry is a pointer to an
516 * [unqlite_io_methods] object that defines methods for performing
517 * I/O operations on the open file.
518*/
519typedef struct unqlite_file unqlite_file;
520struct unqlite_file {
521 const unqlite_io_methods *pMethods; /* Methods for an open file. MUST BE FIRST */
522};
523/*
524 * CAPIREF: OS Interface: File Methods Object
525 *
526 * Every file opened by the [unqlite_vfs] xOpen method populates an
527 * [unqlite_file] object (or, more commonly, a subclass of the
528 * [unqlite_file] object) with a pointer to an instance of this object.
529 * This object defines the methods used to perform various operations
530 * against the open file represented by the [unqlite_file] object.
531 *
532 * If the xOpen method sets the unqlite_file.pMethods element
533 * to a non-NULL pointer, then the unqlite_io_methods.xClose method
534 * may be invoked even if the xOpen reported that it failed. The
535 * only way to prevent a call to xClose following a failed xOpen
536 * is for the xOpen to set the unqlite_file.pMethods element to NULL.
537 *
538 * The flags argument to xSync may be one of [UNQLITE_SYNC_NORMAL] or
539 * [UNQLITE_SYNC_FULL]. The first choice is the normal fsync().
540 * The second choice is a Mac OS X style fullsync. The [UNQLITE_SYNC_DATAONLY]
541 * flag may be ORed in to indicate that only the data of the file
542 * and not its inode needs to be synced.
543 *
544 * The integer values to xLock() and xUnlock() are one of
545 *
546 * UNQLITE_LOCK_NONE
547 * UNQLITE_LOCK_SHARED
548 * UNQLITE_LOCK_RESERVED
549 * UNQLITE_LOCK_PENDING
550 * UNQLITE_LOCK_EXCLUSIVE
551 *
552 * xLock() increases the lock. xUnlock() decreases the lock.
553 * The xCheckReservedLock() method checks whether any database connection,
554 * either in this process or in some other process, is holding a RESERVED,
555 * PENDING, or EXCLUSIVE lock on the file. It returns true if such a lock exists
556 * and false otherwise.
557 *
558 * The xSectorSize() method returns the sector size of the device that underlies
559 * the file. The sector size is the minimum write that can be performed without
560 * disturbing other bytes in the file.
561 *
562 */
563struct unqlite_io_methods {
564 int iVersion; /* Structure version number (currently 1) */
565 int (*xClose)(unqlite_file*);
566 int (*xRead)(unqlite_file*, void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
567 int (*xWrite)(unqlite_file*, const void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
568 int (*xTruncate)(unqlite_file*, unqlite_int64 size);
569 int (*xSync)(unqlite_file*, int flags);
570 int (*xFileSize)(unqlite_file*, unqlite_int64 *pSize);
571 int (*xLock)(unqlite_file*, int);
572 int (*xUnlock)(unqlite_file*, int);
573 int (*xCheckReservedLock)(unqlite_file*, int *pResOut);
574 int (*xSectorSize)(unqlite_file*);
575};
576/*
577 * CAPIREF: OS Interface Object
578 *
579 * An instance of the unqlite_vfs object defines the interface between
580 * the UnQLite core and the underlying operating system. The "vfs"
581 * in the name of the object stands for "Virtual File System".
582 *
583 * Only a single vfs can be registered within the UnQLite core.
584 * Vfs registration is done using the [unqlite_lib_config()] interface
585 * with a configuration verb set to UNQLITE_LIB_CONFIG_VFS.
586 * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users
587 * does not have to worry about registering and installing a vfs since UnQLite
588 * come with a built-in vfs for these platforms that implements most the methods
589 * defined below.
590 *
591 * Clients running on exotic systems (ie: Other than Windows and UNIX systems)
592 * must register their own vfs in order to be able to use the UnQLite library.
593 *
594 * The value of the iVersion field is initially 1 but may be larger in
595 * future versions of UnQLite.
596 *
597 * The szOsFile field is the size of the subclassed [unqlite_file] structure
598 * used by this VFS. mxPathname is the maximum length of a pathname in this VFS.
599 *
600 * At least szOsFile bytes of memory are allocated by UnQLite to hold the [unqlite_file]
601 * structure passed as the third argument to xOpen. The xOpen method does not have to
602 * allocate the structure; it should just fill it in. Note that the xOpen method must
603 * set the unqlite_file.pMethods to either a valid [unqlite_io_methods] object or to NULL.
604 * xOpen must do this even if the open fails. UnQLite expects that the unqlite_file.pMethods
605 * element will be valid after xOpen returns regardless of the success or failure of the
606 * xOpen call.
607 *
608 */
609struct unqlite_vfs {
610 const char *zName; /* Name of this virtual file system [i.e: Windows, UNIX, etc.] */
611 int iVersion; /* Structure version number (currently 1) */
612 int szOsFile; /* Size of subclassed unqlite_file */
613 int mxPathname; /* Maximum file pathname length */
614 int (*xOpen)(unqlite_vfs*, const char *zName, unqlite_file*,unsigned int flags);
615 int (*xDelete)(unqlite_vfs*, const char *zName, int syncDir);
616 int (*xAccess)(unqlite_vfs*, const char *zName, int flags, int *pResOut);
617 int (*xFullPathname)(unqlite_vfs*, const char *zName,int buf_len,char *zBuf);
618 int (*xTmpDir)(unqlite_vfs*,char *zBuf,int buf_len);
619 int (*xSleep)(unqlite_vfs*, int microseconds);
620 int (*xCurrentTime)(unqlite_vfs*,Sytm *pOut);
621 int (*xGetLastError)(unqlite_vfs*, int, char *);
622};
623/*
624 * Flags for the xAccess VFS method
625 *
626 * These integer constants can be used as the third parameter to
627 * the xAccess method of an [unqlite_vfs] object. They determine
628 * what kind of permissions the xAccess method is looking for.
629 * With UNQLITE_ACCESS_EXISTS, the xAccess method
630 * simply checks whether the file exists.
631 * With UNQLITE_ACCESS_READWRITE, the xAccess method
632 * checks whether the named directory is both readable and writable
633 * (in other words, if files can be added, removed, and renamed within
634 * the directory).
635 * The UNQLITE_ACCESS_READWRITE constant is currently used only by the
636 * [temp_store_directory pragma], though this could change in a future
637 * release of UnQLite.
638 * With UNQLITE_ACCESS_READ, the xAccess method
639 * checks whether the file is readable. The UNQLITE_ACCESS_READ constant is
640 * currently unused, though it might be used in a future release of
641 * UnQLite.
642 */
643#define UNQLITE_ACCESS_EXISTS 0
644#define UNQLITE_ACCESS_READWRITE 1
645#define UNQLITE_ACCESS_READ 2
646/*
647 * The type used to represent a page number. The first page in a file
648 * is called page 1. 0 is used to represent "not a page".
649 * A page number is an unsigned 64-bit integer.
650 */
651typedef sxu64 pgno;
652/*
653 * A database disk page is represented by an instance
654 * of the follwoing structure.
655 */
656typedef struct unqlite_page unqlite_page;
657struct unqlite_page
658{
659 unsigned char *zData; /* Content of this page */
660 void *pUserData; /* Extra content */
661 pgno pgno; /* Page number for this page */
662};
663/*
664 * UnQLite handle to the underlying Key/Value Storage Engine (See below).
665 */
666typedef void * unqlite_kv_handle;
667/*
668 * UnQLite pager IO methods.
669 *
670 * An instance of the following structure define the exported methods of the UnQLite pager
671 * to the underlying Key/Value storage engine.
672 */
673typedef struct unqlite_kv_io unqlite_kv_io;
674struct unqlite_kv_io
675{
676 unqlite_kv_handle pHandle; /* UnQLite handle passed as the first parameter to the
677 * method defined below.
678 */
679 unqlite_kv_methods *pMethods; /* Underlying storage engine */
680 /* Pager methods */
681 int (*xGet)(unqlite_kv_handle,pgno,unqlite_page **);
682 int (*xLookup)(unqlite_kv_handle,pgno,unqlite_page **);
683 int (*xNew)(unqlite_kv_handle,unqlite_page **);
684 int (*xWrite)(unqlite_page *);
685 int (*xDontWrite)(unqlite_page *);
686 int (*xDontJournal)(unqlite_page *);
687 int (*xDontMkHot)(unqlite_page *);
688 int (*xPageRef)(unqlite_page *);
689 int (*xPageUnref)(unqlite_page *);
690 int (*xPageSize)(unqlite_kv_handle);
691 int (*xReadOnly)(unqlite_kv_handle);
692 unsigned char * (*xTmpPage)(unqlite_kv_handle);
693 void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *));
694 void (*xSetReload)(unqlite_kv_handle,void (*xPageReload)(void *));
695 void (*xErr)(unqlite_kv_handle,const char *);
696};
697/*
698 * Key/Value Storage Engine Cursor Object
699 *
700 * An instance of a subclass of the following object defines a cursor
701 * used to scan through a key-value storage engine.
702 */
703typedef struct unqlite_kv_cursor unqlite_kv_cursor;
704struct unqlite_kv_cursor
705{
706 unqlite_kv_engine *pStore; /* Must be first */
707 /* Subclasses will typically add additional fields */
708};
709/*
710 * Possible seek positions.
711 */
712#define UNQLITE_CURSOR_MATCH_EXACT 1
713#define UNQLITE_CURSOR_MATCH_LE 2
714#define UNQLITE_CURSOR_MATCH_GE 3
715/*
716 * Key/Value Storage Engine.
717 *
718 * A Key-Value storage engine is defined by an instance of the following
719 * object.
720 * UnQLite works with run-time interchangeable storage engines (i.e. Hash, B+Tree, R+Tree, LSM, etc.).
721 * The storage engine works with key/value pairs where both the key
722 * and the value are byte arrays of arbitrary length and with no restrictions on content.
723 * UnQLite come with two built-in KV storage engine: A Virtual Linear Hash (VLH) storage
724 * engine is used for persistent on-disk databases with O(1) lookup time and an in-memory
725 * hash-table or Red-black tree storage engine is used for in-memory databases.
726 * Future versions of UnQLite might add other built-in storage engines (i.e. LSM).
727 * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
728 * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
729 */
730struct unqlite_kv_engine
731{
732 const unqlite_kv_io *pIo; /* IO methods: MUST be first */
733 /* Subclasses will typically add additional fields */
734};
735/*
736 * Key/Value Storage Engine Virtual Method Table.
737 *
738 * Key/Value storage engine methods is defined by an instance of the following
739 * object.
740 * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
741 * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
742 */
743struct unqlite_kv_methods
744{
745 const char *zName; /* Storage engine name [i.e. Hash, B+tree, LSM, R-tree, Mem, etc.]*/
746 int szKv; /* 'unqlite_kv_engine' subclass size */
747 int szCursor; /* 'unqlite_kv_cursor' subclass size */
748 int iVersion; /* Structure version, currently 1 */
749 /* Storage engine methods */
750 int (*xInit)(unqlite_kv_engine *,int iPageSize);
751 void (*xRelease)(unqlite_kv_engine *);
752 int (*xConfig)(unqlite_kv_engine *,int op,va_list ap);
753 int (*xOpen)(unqlite_kv_engine *,pgno);
754 int (*xReplace)(
755 unqlite_kv_engine *,
756 const void *pKey,int nKeyLen,
757 const void *pData,unqlite_int64 nDataLen
758 );
759 int (*xAppend)(
760 unqlite_kv_engine *,
761 const void *pKey,int nKeyLen,
762 const void *pData,unqlite_int64 nDataLen
763 );
764 void (*xCursorInit)(unqlite_kv_cursor *);
765 int (*xSeek)(unqlite_kv_cursor *,const void *pKey,int nByte,int iPos); /* Mandatory */
766 int (*xFirst)(unqlite_kv_cursor *);
767 int (*xLast)(unqlite_kv_cursor *);
768 int (*xValid)(unqlite_kv_cursor *);
769 int (*xNext)(unqlite_kv_cursor *);
770 int (*xPrev)(unqlite_kv_cursor *);
771 int (*xDelete)(unqlite_kv_cursor *);
772 int (*xKeyLength)(unqlite_kv_cursor *,int *);
773 int (*xKey)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
774 int (*xDataLength)(unqlite_kv_cursor *,unqlite_int64 *);
775 int (*xData)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
776 void (*xReset)(unqlite_kv_cursor *);
777 void (*xCursorRelease)(unqlite_kv_cursor *);
778};
779/*
780 * UnQLite journal file suffix.
781 */
782#ifndef UNQLITE_JOURNAL_FILE_SUFFIX
783#define UNQLITE_JOURNAL_FILE_SUFFIX "_unqlite_journal"
784#endif
785/*
786 * Call Context - Error Message Serverity Level.
787 *
788 * The following constans are the allowed severity level that can
789 * passed as the second argument to the [unqlite_context_throw_error()] or
790 * [unqlite_context_throw_error_format()] interfaces.
791 * Refer to the official documentation for additional information.
792 */
793#define UNQLITE_CTX_ERR 1 /* Call context error such as unexpected number of arguments, invalid types and so on. */
794#define UNQLITE_CTX_WARNING 2 /* Call context Warning */
795#define UNQLITE_CTX_NOTICE 3 /* Call context Notice */
796/*
797 * C-API-REF: Please refer to the official documentation for interfaces
798 * purpose and expected parameters.
799 */
800
801/* Database Engine Handle */
802UNQLITE_APIEXPORT int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode);
803UNQLITE_APIEXPORT int unqlite_config(unqlite *pDb,int nOp,...);
804UNQLITE_APIEXPORT int unqlite_close(unqlite *pDb);
805
806/* Key/Value (KV) Store Interfaces */
807UNQLITE_APIEXPORT int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
808UNQLITE_APIEXPORT int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
809UNQLITE_APIEXPORT int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
810UNQLITE_APIEXPORT int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
811UNQLITE_APIEXPORT int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 /* in|out */*pBufLen);
812UNQLITE_APIEXPORT int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,
813 int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
814UNQLITE_APIEXPORT int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen);
815UNQLITE_APIEXPORT int unqlite_kv_config(unqlite *pDb,int iOp,...);
816
817/* Document (JSON) Store Interfaces powered by the Jx9 Scripting Language */
818UNQLITE_APIEXPORT int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut);
819UNQLITE_APIEXPORT int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut);
820UNQLITE_APIEXPORT int unqlite_vm_config(unqlite_vm *pVm,int iOp,...);
821UNQLITE_APIEXPORT int unqlite_vm_exec(unqlite_vm *pVm);
822UNQLITE_APIEXPORT int unqlite_vm_reset(unqlite_vm *pVm);
823UNQLITE_APIEXPORT int unqlite_vm_release(unqlite_vm *pVm);
824UNQLITE_APIEXPORT int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);
825UNQLITE_APIEXPORT unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname);
826
827/* Cursor Iterator Interfaces */
828UNQLITE_APIEXPORT int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut);
829UNQLITE_APIEXPORT int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur);
830UNQLITE_APIEXPORT int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos);
831UNQLITE_APIEXPORT int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor);
832UNQLITE_APIEXPORT int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor);
833UNQLITE_APIEXPORT int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor);
834UNQLITE_APIEXPORT int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor);
835UNQLITE_APIEXPORT int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor);
836UNQLITE_APIEXPORT int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte);
837UNQLITE_APIEXPORT int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
838UNQLITE_APIEXPORT int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnData);
839UNQLITE_APIEXPORT int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
840UNQLITE_APIEXPORT int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor);
841UNQLITE_APIEXPORT int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor);
842
843/* Manual Transaction Manager */
844UNQLITE_APIEXPORT int unqlite_begin(unqlite *pDb);
845UNQLITE_APIEXPORT int unqlite_commit(unqlite *pDb);
846UNQLITE_APIEXPORT int unqlite_rollback(unqlite *pDb);
847
848/* Utility interfaces */
849UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize);
850UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize);
851UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size);
852UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb);
853
854/* In-process extending interfaces */
855UNQLITE_APIEXPORT int unqlite_create_function(unqlite_vm *pVm,const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData);
856UNQLITE_APIEXPORT int unqlite_delete_function(unqlite_vm *pVm, const char *zName);
857UNQLITE_APIEXPORT int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData);
858UNQLITE_APIEXPORT int unqlite_delete_constant(unqlite_vm *pVm, const char *zName);
859
860/* On Demand Object allocation interfaces */
861UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm);
862UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm);
863UNQLITE_APIEXPORT int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue);
864UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx);
865UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_array(unqlite_context *pCtx);
866UNQLITE_APIEXPORT void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue);
867
868/* Dynamically Typed Value Object Management Interfaces */
869UNQLITE_APIEXPORT int unqlite_value_int(unqlite_value *pVal, int iValue);
870UNQLITE_APIEXPORT int unqlite_value_int64(unqlite_value *pVal, unqlite_int64 iValue);
871UNQLITE_APIEXPORT int unqlite_value_bool(unqlite_value *pVal, int iBool);
872UNQLITE_APIEXPORT int unqlite_value_null(unqlite_value *pVal);
873UNQLITE_APIEXPORT int unqlite_value_double(unqlite_value *pVal, double Value);
874UNQLITE_APIEXPORT int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen);
875UNQLITE_APIEXPORT int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...);
876UNQLITE_APIEXPORT int unqlite_value_reset_string_cursor(unqlite_value *pVal);
877UNQLITE_APIEXPORT int unqlite_value_resource(unqlite_value *pVal, void *pUserData);
878UNQLITE_APIEXPORT int unqlite_value_release(unqlite_value *pVal);
879
880/* Foreign Function Parameter Values */
881UNQLITE_APIEXPORT int unqlite_value_to_int(unqlite_value *pValue);
882UNQLITE_APIEXPORT int unqlite_value_to_bool(unqlite_value *pValue);
883UNQLITE_APIEXPORT unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue);
884UNQLITE_APIEXPORT double unqlite_value_to_double(unqlite_value *pValue);
885UNQLITE_APIEXPORT const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen);
886UNQLITE_APIEXPORT void * unqlite_value_to_resource(unqlite_value *pValue);
887UNQLITE_APIEXPORT int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict);
888
889/* Setting The Result Of A Foreign Function */
890UNQLITE_APIEXPORT int unqlite_result_int(unqlite_context *pCtx, int iValue);
891UNQLITE_APIEXPORT int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue);
892UNQLITE_APIEXPORT int unqlite_result_bool(unqlite_context *pCtx, int iBool);
893UNQLITE_APIEXPORT int unqlite_result_double(unqlite_context *pCtx, double Value);
894UNQLITE_APIEXPORT int unqlite_result_null(unqlite_context *pCtx);
895UNQLITE_APIEXPORT int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen);
896UNQLITE_APIEXPORT int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...);
897UNQLITE_APIEXPORT int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue);
898UNQLITE_APIEXPORT int unqlite_result_resource(unqlite_context *pCtx, void *pUserData);
899
900/* Dynamically Typed Value Object Query Interfaces */
901UNQLITE_APIEXPORT int unqlite_value_is_int(unqlite_value *pVal);
902UNQLITE_APIEXPORT int unqlite_value_is_float(unqlite_value *pVal);
903UNQLITE_APIEXPORT int unqlite_value_is_bool(unqlite_value *pVal);
904UNQLITE_APIEXPORT int unqlite_value_is_string(unqlite_value *pVal);
905UNQLITE_APIEXPORT int unqlite_value_is_null(unqlite_value *pVal);
906UNQLITE_APIEXPORT int unqlite_value_is_numeric(unqlite_value *pVal);
907UNQLITE_APIEXPORT int unqlite_value_is_callable(unqlite_value *pVal);
908UNQLITE_APIEXPORT int unqlite_value_is_scalar(unqlite_value *pVal);
909UNQLITE_APIEXPORT int unqlite_value_is_json_array(unqlite_value *pVal);
910UNQLITE_APIEXPORT int unqlite_value_is_json_object(unqlite_value *pVal);
911UNQLITE_APIEXPORT int unqlite_value_is_resource(unqlite_value *pVal);
912UNQLITE_APIEXPORT int unqlite_value_is_empty(unqlite_value *pVal);
913
914/* JSON Array/Object Management Interfaces */
915UNQLITE_APIEXPORT unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte);
916UNQLITE_APIEXPORT int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData);
917UNQLITE_APIEXPORT int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue);
918UNQLITE_APIEXPORT int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue);
919UNQLITE_APIEXPORT int unqlite_array_count(unqlite_value *pArray);
920
921/* Call Context Handling Interfaces */
922UNQLITE_APIEXPORT int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen);
923UNQLITE_APIEXPORT int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...);
924UNQLITE_APIEXPORT int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr);
925UNQLITE_APIEXPORT int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...);
926UNQLITE_APIEXPORT unsigned int unqlite_context_random_num(unqlite_context *pCtx);
927UNQLITE_APIEXPORT int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen);
928UNQLITE_APIEXPORT void * unqlite_context_user_data(unqlite_context *pCtx);
929UNQLITE_APIEXPORT int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData);
930UNQLITE_APIEXPORT void * unqlite_context_peek_aux_data(unqlite_context *pCtx);
931UNQLITE_APIEXPORT unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx);
932UNQLITE_APIEXPORT const char * unqlite_function_name(unqlite_context *pCtx);
933
934/* Call Context Memory Management Interfaces */
935UNQLITE_APIEXPORT void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease);
936UNQLITE_APIEXPORT void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte);
937UNQLITE_APIEXPORT void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk);
938
939/* Global Library Management Interfaces */
940UNQLITE_APIEXPORT int unqlite_lib_config(int nConfigOp,...);
941UNQLITE_APIEXPORT int unqlite_lib_init(void);
942UNQLITE_APIEXPORT int unqlite_lib_shutdown(void);
943UNQLITE_APIEXPORT int unqlite_lib_is_threadsafe(void);
944UNQLITE_APIEXPORT const char * unqlite_lib_version(void);
945UNQLITE_APIEXPORT const char * unqlite_lib_signature(void);
946UNQLITE_APIEXPORT const char * unqlite_lib_ident(void);
947UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void);
948
949#endif /* _UNQLITE_H_ */
diff --git a/docs/applicationdomaintypes.md b/docs/applicationdomaintypes.md
index 502a349..9a50940 100644
--- a/docs/applicationdomaintypes.md
+++ b/docs/applicationdomaintypes.md
@@ -31,18 +31,19 @@ This is a proposed set of types that we will need to evolve into what we actuall
31 * Tag 31 * Tag
32 * Contact Group 32 * Contact Group
33 * Thread 33 * Thread
34 * Akonadi Resource 34 * Akonadi Resource
35 * Maildir Resource
35 36
36#### Properties 37#### Properties
37```no-highlight 38```no-highlight
38Entity: The smallest unit in the akonadi universe 39Entity: The smallest unit in the akonadi universe
39 id: unique identifier in the akonadi storage. Not persistant over db recreations and can therefore only be referenced from within the akonadi database. 40 id: unique identifier in the akonadi storage. Not persistant over db recreations and can therefore only be referenced from within the akonadi database.
40 revision: revision of the entity
41 resource: reference to AkonadiResource:id
42``` 41```
43```no-highlight 42```no-highlight
44Domain Object: 43Domain Object:
45 uid: unique identifier of the domain object. 44 uid: unique identifier of the domain object.
45 revision: revision of the entity
46 resource: reference to AkonadiResource:id of the parent resource.
46``` 47```
47```no-highlight 48```no-highlight
48Event: 49Event:
@@ -61,6 +62,7 @@ Mail:
61```no-highlight 62```no-highlight
62Akonadi Resource: 63Akonadi Resource:
63 type: The type of the resource. 64 type: The type of the resource.
65 name: The name of the resource.
64``` 66```
65```no-highlight 67```no-highlight
66Maildir Resource: 68Maildir Resource:
diff --git a/docs/storage.md b/docs/storage.md
index 26469a7..b6d73fe 100644
--- a/docs/storage.md
+++ b/docs/storage.md
@@ -86,7 +86,7 @@ Storage is split up in multiple named databases that reside in the same database
86``` 86```
87 87
88The resource can be effectively removed from disk (besides configuration), 88The resource can be effectively removed from disk (besides configuration),
89by deleting the `$RESOURCE_IDENTIFIER` directory and everything it contains. 89by deleting the directories matching `$RESOURCE_IDENTIFIER*` and everything they contain.
90 90
91#### Design Considerations 91#### Design Considerations
92* The stores are split by buffertype, so a full scan (which is done by type), doesn't require filtering by type first. The downside is that an additional lookup is required to get from revision to the data. 92* The stores are split by buffertype, so a full scan (which is done by type), doesn't require filtering by type first. The downside is that an additional lookup is required to get from revision to the data.
@@ -95,9 +95,43 @@ by deleting the `$RESOURCE_IDENTIFIER` directory and everything it contains.
95Every operation (create/delete/modify), leads to a new revision. The revision is an ever increasing number for the complete store. 95Every operation (create/delete/modify), leads to a new revision. The revision is an ever increasing number for the complete store.
96 96
97#### Design Considerations 97#### Design Considerations
98By having one revision for the complete store instead of one per type, the change replay always works across all types. This is especially important in the write-back 98By having one revision for the complete store instead of one per type, the change replay always works across all types. This is especially important in the write-back mechanism that replays the changes to the source.
99mechanism that replays the changes to the source.
100 99
100### BLOB properties
101Files are used to handle opaque large properties that should not end up in memory. BLOB properties are in their nature never queriable (extract parts of it to other properties if indexes are required).
102
103For reading:
104
105Resources...
106* store the file in ~/akonadi2/storage/$RESOURCE_IDENTIFIER_files/
107* store the filename in the blob property.
108* delete the file when the corresponding entity is deleted.
109
110Queries...
111* Copy the requested property to /tmp/akonadi2/client_files/ and provide the path in the property
112* The file is guaranteed to exist for the lifetime of the query result.
113
114Clients..
115* Load the file from disk and use it as they wish (moving is fine too)
116
117For writing:
118
119Clients..
120* Request a path from akonadi2 and store the file there.
121* Store the path of the written file in the property.
122
123Resources..
124* move the file to ~/akonadi2/storage/$RESOURCE_IDENTIFIER_files/
125* store the new path in the entity
126
127#### Design Considerations
128Using regular files as the interface has the advantages:
129* Existing mechanisms can be used to stream data directly to disk.
130* The necessary file operations can be efficiently handled depending on OS and filesystem.
131* We avoid reinventing the wheel.
132
133The copy is necessary to guarantee that the file remains for the client/resource even if the resource removes the file on it's side as part of a sync.
134The copy could be optimized by using hardlinks, which is not a portable solution though. For some next-gen copy-on-write filesystems copying is a very cheap operation.
101 135
102### Database choice 136### Database choice
103By design we're interested in key-value stores or perhaps document databases. This is because a fixed schema is not useful for this design, which makes 137By design we're interested in key-value stores or perhaps document databases. This is because a fixed schema is not useful for this design, which makes
diff --git a/examples/client/main.cpp b/examples/client/main.cpp
index 946557f..ad65e44 100644
--- a/examples/client/main.cpp
+++ b/examples/client/main.cpp
@@ -21,6 +21,7 @@
21#include <QCommandLineParser> 21#include <QCommandLineParser>
22#include <QCommandLineOption> 22#include <QCommandLineOption>
23#include <QElapsedTimer> 23#include <QElapsedTimer>
24#include <QDir>
24 25
25#include "common/clientapi.h" 26#include "common/clientapi.h"
26#include "common/resource.h" 27#include "common/resource.h"
@@ -407,6 +408,12 @@ int main(int argc, char *argv[])
407 total += size; 408 total += size;
408 } 409 }
409 std::cout << "Total [kb]: " << total / 1024 << std::endl; 410 std::cout << "Total [kb]: " << total / 1024 << std::endl;
411 int diskUsage = 0;
412 QDir dir(Akonadi2::storageLocation());
413 for (const auto &folder : dir.entryList(QStringList() << resource + "*")) {
414 diskUsage += Akonadi2::Storage(Akonadi2::storageLocation(), folder, Akonadi2::Storage::ReadOnly).diskUsage();
415 }
416 std::cout << "Disk usage [kb]: " << diskUsage / 1024 << std::endl;
410 } 417 }
411 } else { 418 } else {
412 qWarning() << "Unknown command " << command; 419 qWarning() << "Unknown command " << command;
diff --git a/tests/dummyresourcewritebenchmark.cpp b/tests/dummyresourcewritebenchmark.cpp
index 3e8f4c5..1e7f1ef 100644
--- a/tests/dummyresourcewritebenchmark.cpp
+++ b/tests/dummyresourcewritebenchmark.cpp
@@ -213,6 +213,11 @@ private Q_SLOTS:
213 QVERIFY(timeStandardDeviation < 1); 213 QVERIFY(timeStandardDeviation < 1);
214 } 214 }
215 215
216 void getFreePages()
217 {
218 std::system(QString("mdb_stat %1/%2 -ff").arg(Akonadi2::storageLocation()).arg("org.kde.dummy.instance1").toLatin1().constData());
219 }
220
216 //This allows to run individual parts without doing a cleanup, but still cleaning up normally 221 //This allows to run individual parts without doing a cleanup, but still cleaning up normally
217 void testCleanupForCompleteTest() 222 void testCleanupForCompleteTest()
218 { 223 {