summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct4
-rw-r--r--bin/tests/test_string.cpp73
-rw-r--r--core/io/http_client.cpp7
-rw-r--r--core/io/http_client.h2
-rw-r--r--core/io/ip.cpp37
-rw-r--r--core/io/ip.h16
-rw-r--r--core/io/ip_address.cpp186
-rw-r--r--core/io/ip_address.h38
-rw-r--r--core/io/packet_peer_udp.cpp9
-rw-r--r--core/io/packet_peer_udp.h3
-rw-r--r--core/io/tcp_server.cpp8
-rw-r--r--core/io/tcp_server.h4
-rw-r--r--core/math/math_funcs.cpp37
-rw-r--r--core/os/input_event.cpp3
-rw-r--r--core/translation.cpp39
-rw-r--r--core/ustring.cpp90
-rw-r--r--core/ustring.h3
-rw-r--r--core/variant_call.cpp5
-rw-r--r--doc/base/classes.xml122
-rw-r--r--drivers/SCsub1
-rw-r--r--drivers/nrex/README.md75
-rw-r--r--drivers/nrex/nrex.cpp1496
-rw-r--r--drivers/nrex/nrex.hpp176
-rw-r--r--drivers/nrex/nrex_config.h12
-rw-r--r--drivers/nrex/regex.cpp142
-rw-r--r--drivers/register_driver_types.cpp4
-rw-r--r--drivers/unix/ip_unix.cpp89
-rw-r--r--drivers/unix/ip_unix.h2
-rw-r--r--drivers/unix/packet_peer_udp_posix.cpp80
-rw-r--r--drivers/unix/packet_peer_udp_posix.h8
-rw-r--r--drivers/unix/socket_helpers.h70
-rw-r--r--drivers/unix/stream_peer_tcp_posix.cpp26
-rw-r--r--drivers/unix/tcp_server_posix.cpp32
-rw-r--r--drivers/unix/tcp_server_posix.h2
-rwxr-xr-xmethods.py5
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp9
-rw-r--r--modules/openssl/SCsub2
-rw-r--r--modules/regex/SCsub (renamed from drivers/nrex/SCsub)2
-rw-r--r--modules/regex/config.py8
-rw-r--r--modules/regex/regex.cpp1502
-rw-r--r--modules/regex/regex.h114
-rw-r--r--modules/regex/register_types.cpp (renamed from drivers/nrex/regex.h)42
-rw-r--r--modules/regex/register_types.h31
-rw-r--r--platform/javascript/export/export.cpp4
-rw-r--r--platform/windows/detect.py26
-rw-r--r--platform/windows/packet_peer_udp_winsock.cpp78
-rw-r--r--platform/windows/packet_peer_udp_winsock.h8
-rw-r--r--platform/windows/stream_peer_winsock.cpp28
-rw-r--r--platform/windows/tcp_server_winsock.cpp21
-rw-r--r--platform/windows/tcp_server_winsock.h2
-rw-r--r--scene/gui/file_dialog.cpp34
-rw-r--r--tools/editor/editor_file_dialog.cpp47
-rw-r--r--tools/editor/filesystem_dock.cpp7
-rw-r--r--tools/editor/scene_tree_dock.cpp64
-rw-r--r--tools/editor/scene_tree_dock.h6
55 files changed, 2649 insertions, 2292 deletions
diff --git a/SConstruct b/SConstruct
index c28b40652a..365463ebad 100644
--- a/SConstruct
+++ b/SConstruct
@@ -61,7 +61,7 @@ platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False))
if (os.name=="posix"):
pass
elif (os.name=="nt"):
- if (not methods.msvc_is_detected() or platform_arg=="android"):
+ if ( os.getenv("VCINSTALLDIR")==None or platform_arg=="android"):
custom_tools=['mingw']
env_base=Environment(tools=custom_tools);
@@ -139,7 +139,7 @@ opts.Add('glew','GLEW library for the gl_context (system/builtin)','builtin')
opts.Add('xaudio2','XAudio2 audio driver (yes/no)','no')
opts.Add("CXX", "C++ Compiler")
opts.Add("CC", "C Compiler")
-opts.Add("CCFLAGS", "Custom flags for the C++ compiler");
+opts.Add("CCFLAGS", "Custom flags for the C/C++ compiler");
opts.Add("CFLAGS", "Custom flags for the C compiler");
opts.Add("LINKFLAGS", "Custom flags for the linker");
opts.Add('unix_global_settings_path', 'unix-specific path to system-wide settings. Currently only used by templates.','')
diff --git a/bin/tests/test_string.cpp b/bin/tests/test_string.cpp
index be37ce118f..4990c58896 100644
--- a/bin/tests/test_string.cpp
+++ b/bin/tests/test_string.cpp
@@ -31,7 +31,7 @@
//#include "math_funcs.h"
#include <stdio.h>
#include "os/os.h"
-#include "drivers/nrex/regex.h"
+#include "core/io/ip_address.h"
#include "test_string.h"
@@ -461,18 +461,8 @@ bool test_25() {
bool test_26() {
- OS::get_singleton()->print("\n\nTest 26: RegEx\n");
- RegEx regexp("(.*):(.*)");
-
- int res = regexp.find("name:password");
- printf("\tmatch: %s\n", (res>=0)?"true":"false");
-
- printf("\t%i captures:\n", regexp.get_capture_count());
- for (int i = 0; i<regexp.get_capture_count(); i++)
- {
- printf("%ls\n", regexp.get_capture(i).c_str());
- }
- return (res>=0);
+ //TODO: Do replacement RegEx test
+ return true;
};
struct test_27_data {
@@ -843,6 +833,62 @@ bool test_28() {
return state;
}
+bool test_29() {
+
+ bool error = false;
+ bool state = true;
+ bool success = false;
+
+ IP_Address ip0("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ OS::get_singleton()->print("ip0 is %ls\n", String(ip0).c_str());
+
+ IP_Address ip(0x0123, 0x4567, 0x89ab, 0xcdef, IP_Address::TYPE_IPV6);
+ OS::get_singleton()->print("ip6 is %ls\n", String(ip).c_str());
+
+ IP_Address ip2("fe80::52e5:49ff:fe93:1baf");
+ OS::get_singleton()->print("ip6 is %ls\n", String(ip2).c_str());
+
+ IP_Address ip3("::ffff:192.168.0.1");
+ OS::get_singleton()->print("ip6 is %ls\n", String(ip3).c_str());
+
+ String ip4 = "192.168.0.1";
+ success = ip4.is_valid_ip_address();
+ OS::get_singleton()->print("Is valid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ ip4 = "192.368.0.1";
+ success = (!ip4.is_valid_ip_address());
+ OS::get_singleton()->print("Is invalid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ String ip6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+ success = ip6.is_valid_ip_address();
+ OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ ip6 = "2001:0db8:85j3:0000:0000:8a2e:0370:7334";
+ success = (!ip6.is_valid_ip_address());
+ OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ ip6 = "2001:0db8:85f345:0000:0000:8a2e:0370:7334";
+ success = (!ip6.is_valid_ip_address());
+ OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ ip6 = "2001:0db8::0:8a2e:370:7334";
+ success = (ip6.is_valid_ip_address());
+ OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ ip6 = "::ffff:192.168.0.1";
+ success = (ip6.is_valid_ip_address());
+ OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ return state;
+};
+
typedef bool (*TestFunc)(void);
TestFunc test_funcs[] = {
@@ -875,6 +921,7 @@ TestFunc test_funcs[] = {
test_26,
test_27,
test_28,
+ test_29,
0
};
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 2a831dd992..e3289b452c 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -29,8 +29,9 @@
#include "http_client.h"
#include "io/stream_peer_ssl.h"
+VARIANT_ENUM_CAST(IP_Address::AddrType);
-Error HTTPClient::connect(const String &p_host, int p_port, bool p_ssl,bool p_verify_host){
+Error HTTPClient::connect(const String &p_host, int p_port, bool p_ssl,bool p_verify_host, IP_Address::AddrType p_addr_type){
close();
conn_port=p_port;
@@ -62,7 +63,7 @@ Error HTTPClient::connect(const String &p_host, int p_port, bool p_ssl,bool p_ve
status=STATUS_CONNECTING;
} else {
//is hostname
- resolving=IP::get_singleton()->resolve_hostname_queue_item(conn_host);
+ resolving=IP::get_singleton()->resolve_hostname_queue_item(conn_host, p_addr_type);
status=STATUS_RESOLVING;
}
@@ -635,7 +636,7 @@ Error HTTPClient::_get_http_data(uint8_t* p_buffer, int p_bytes,int &r_received)
void HTTPClient::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("connect:Error","host","port","use_ssl","verify_host"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true));
+ ObjectTypeDB::bind_method(_MD("connect:Error","host","port","use_ssl","verify_host"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true),DEFVAL(IP_Address::TYPE_ANY));
ObjectTypeDB::bind_method(_MD("set_connection","connection:StreamPeer"),&HTTPClient::set_connection);
ObjectTypeDB::bind_method(_MD("get_connection:StreamPeer"),&HTTPClient::get_connection);
ObjectTypeDB::bind_method(_MD("request_raw","method","url","headers","body"),&HTTPClient::request_raw);
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 32d2e72101..ba464c34c7 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -165,7 +165,7 @@ public:
//Error connect_and_get(const String& p_url,bool p_verify_host=true); //connects to a full url and perform request
- Error connect(const String &p_host,int p_port,bool p_ssl=false,bool p_verify_host=true);
+ Error connect(const String &p_host,int p_port,bool p_ssl=false,bool p_verify_host=true, IP_Address::AddrType p_addr_type = IP_Address::TYPE_ANY);
void set_connection(const Ref<StreamPeer>& p_connection);
Ref<StreamPeer> get_connection() const;
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index a77aace07f..4ee1b281c4 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -32,6 +32,7 @@
#include "hash_map.h"
VARIANT_ENUM_CAST(IP::ResolverStatus);
+VARIANT_ENUM_CAST(IP_Address::AddrType);
/************* RESOLVER ******************/
@@ -43,10 +44,12 @@ struct _IP_ResolverPrivate {
volatile IP::ResolverStatus status;
IP_Address response;
String hostname;
+ IP_Address::AddrType type;
void clear() {
status = IP::RESOLVER_STATUS_NONE;
response = IP_Address();
+ type = IP_Address::TYPE_NONE;
hostname="";
};
@@ -78,9 +81,9 @@ struct _IP_ResolverPrivate {
if (queue[i].status!=IP::RESOLVER_STATUS_WAITING)
continue;
- queue[i].response=IP::get_singleton()->resolve_hostname(queue[i].hostname);
+ queue[i].response=IP::get_singleton()->resolve_hostname(queue[i].hostname, queue[i].type);
- if (queue[i].response.host==0)
+ if (queue[i].response.type==IP_Address::TYPE_NONE)
queue[i].status=IP::RESOLVER_STATUS_ERROR;
else
queue[i].status=IP::RESOLVER_STATUS_DONE;
@@ -109,21 +112,23 @@ struct _IP_ResolverPrivate {
-IP_Address IP::resolve_hostname(const String& p_hostname) {
+IP_Address IP::resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type) {
- GLOBAL_LOCK_FUNCTION
+ GLOBAL_LOCK_FUNCTION;
if (resolver->cache.has(p_hostname))
- return resolver->cache[p_hostname];
+ if (resolver->cache[p_hostname].type & p_type != 0)
+ return resolver->cache[p_hostname];
+ // requested type is different from type in cache. continue resolution, if successful it'll overwrite cache
- IP_Address res = _resolve_hostname(p_hostname);
+ IP_Address res = _resolve_hostname(p_hostname, p_type);
resolver->cache[p_hostname]=res;
return res;
}
-IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
+IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname, IP_Address::AddrType p_type) {
- GLOBAL_LOCK_FUNCTION
+ GLOBAL_LOCK_FUNCTION;
ResolverID id = resolver->find_empty_id();
@@ -133,7 +138,8 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
}
resolver->queue[id].hostname=p_hostname;
- if (resolver->cache.has(p_hostname)) {
+ resolver->queue[id].type = p_type;
+ if (resolver->cache.has(p_hostname) && (resolver->cache[p_hostname].type & p_type) != 0) {
resolver->queue[id].response=resolver->cache[p_hostname];
resolver->queue[id].status=IP::RESOLVER_STATUS_DONE;
} else {
@@ -145,10 +151,6 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
resolver->resolve_queues();
}
-
-
-
-
return id;
}
@@ -187,6 +189,14 @@ void IP::erase_resolve_item(ResolverID p_id) {
}
+void IP::clear_cache(const String &p_hostname) {
+
+ if (p_hostname.empty()) {
+ resolver->cache.clear();
+ } else {
+ resolver->cache.erase(p_hostname);
+ }
+};
Array IP::_get_local_addresses() const {
@@ -208,6 +218,7 @@ void IP::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_resolve_item_address","id"),&IP::get_resolve_item_address);
ObjectTypeDB::bind_method(_MD("erase_resolve_item","id"),&IP::erase_resolve_item);
ObjectTypeDB::bind_method(_MD("get_local_addresses"),&IP::_get_local_addresses);
+ ObjectTypeDB::bind_method(_MD("clear_cache"),&IP::clear_cache, DEFVAL(""));
BIND_CONSTANT( RESOLVER_STATUS_NONE );
BIND_CONSTANT( RESOLVER_STATUS_WAITING );
diff --git a/core/io/ip.h b/core/io/ip.h
index 38c86e7ba3..742dd0e740 100644
--- a/core/io/ip.h
+++ b/core/io/ip.h
@@ -48,6 +48,14 @@ public:
RESOLVER_STATUS_ERROR,
};
+ enum AddressType {
+
+ ADDRESS_IPV4 = 1,
+ ADDRESS_IPV6 = 2,
+
+ ADDRESS_ANY = 3,
+ };
+
enum {
RESOLVER_MAX_QUERIES = 32,
RESOLVER_INVALID_ID=-1
@@ -65,7 +73,7 @@ protected:
static IP*singleton;
static void _bind_methods();
- virtual IP_Address _resolve_hostname(const String& p_hostname)=0;
+ virtual IP_Address _resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type = IP_Address::TYPE_ANY)=0;
Array _get_local_addresses() const;
static IP* (*_create)();
@@ -73,14 +81,16 @@ public:
- IP_Address resolve_hostname(const String& p_hostname);
+ IP_Address resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type = IP_Address::TYPE_ANY);
// async resolver hostname
- ResolverID resolve_hostname_queue_item(const String& p_hostname);
+ ResolverID resolve_hostname_queue_item(const String& p_hostname, IP_Address::AddrType p_type = IP_Address::TYPE_ANY);
ResolverStatus get_resolve_item_status(ResolverID p_id) const;
IP_Address get_resolve_item_address(ResolverID p_id) const;
virtual void get_local_addresses(List<IP_Address> *r_addresses) const=0;
void erase_resolve_item(ResolverID p_id);
+ void clear_cache(const String& p_hostname = "");
+
static IP* get_singleton();
static IP* create();
diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp
index 7a51bce7c6..9887cd132b 100644
--- a/core/io/ip_address.cpp
+++ b/core/io/ip_address.cpp
@@ -32,29 +32,191 @@ IP_Address::operator Variant() const {
return operator String();
}*/
+
+#include <string.h>
+#include <stdio.h>
+
IP_Address::operator String() const {
- return itos(field[0])+"."+itos(field[1])+"."+itos(field[2])+"."+itos(field[3]);
+ if (type == TYPE_NONE)
+ return "0.0.0.0";
+ if (type == TYPE_IPV4)
+ return itos(field8[0])+"."+itos(field8[1])+"."+itos(field8[2])+"."+itos(field8[3]);
+ else {
+ String ret;
+ for (int i=0; i<8; i++) {
+ if (i > 0)
+ ret = ret + ":";
+ uint16_t num = (field8[i*2] << 8) + field8[i*2+1];
+ ret = ret + String::num_int64(num, 16);
+ };
+
+ return ret;
+ };
}
-IP_Address::IP_Address(const String& p_string) {
+static void _parse_hex(const String& p_string, int p_start, uint8_t* p_dst) {
+
+ uint16_t ret = 0;
+ for (int i=p_start; i<p_start + 4; i++) {
+
+ if (i >= p_string.length()) {
+ break;
+ };
+
+ int n = 0;
+ CharType c = p_string[i];
+ if (c >= '0' && c <= '9') {
+
+ n = c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ n = 10 + (c - 'a');
+ } else if (c >= 'A' && c <= 'F') {
+ n = 10 + (c - 'A');
+ } else if (c == ':') {
+ break;
+ } else {
+ ERR_EXPLAIN("Invalid character in ipv6 address: " + p_string);
+ ERR_FAIL();
+ };
+ ret = ret << 4;
+ ret += n;
+ };
+
+ p_dst[0] = ret >> 8;
+ p_dst[1] = ret & 0xff;
+};
+
+void IP_Address::_parse_ipv6(const String& p_string) {
+
+ static const int parts_total = 8;
+ int parts[parts_total] = {0};
+ int parts_count = 0;
+ bool part_found = false;
+ bool part_skip = false;
+ bool part_ipv4 = false;
+ int parts_idx = 0;
+
+ for (int i=0; i<p_string.length(); i++) {
+
+ CharType c = p_string[i];
+ if (c == ':') {
+
+ if (i == 0) {
+ continue; // next must be a ":"
+ };
+ if (!part_found) {
+ part_skip = true;
+ parts[parts_idx++] = -1;
+ };
+ part_found = false;
+ } else if (c == '.') {
+
+ part_ipv4 = true;
+
+ } else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+ if (!part_found) {
+ parts[parts_idx++] = i;
+ part_found = true;
+ ++parts_count;
+ };
+ } else {
+
+ ERR_EXPLAIN("Invalid character in IPv6 address: " + p_string);
+ ERR_FAIL();
+ };
+ };
+
+ int parts_extra = 0;
+ if (part_skip) {
+ parts_extra = parts_total - parts_count;
+ };
+
+ int idx = 0;
+ for (int i=0; i<parts_idx; i++) {
- host=0;
- int slices = p_string.get_slice_count(".");
+ if (parts[i] == -1) {
+
+ for (int j=0; j<parts_extra; j++) {
+ field16[idx++] = 0;
+ };
+ continue;
+ };
+
+ if (part_ipv4 && i == parts_idx - 1) {
+ _parse_ipv4(p_string, parts[i], (uint8_t*)&field16[idx]); // should be the last one
+ } else {
+ _parse_hex(p_string, parts[i], (uint8_t*)&(field16[idx++]));
+ };
+ };
+
+};
+
+void IP_Address::_parse_ipv4(const String& p_string, int p_start, uint8_t* p_ret) {
+
+ String ip;
+ if (p_start != 0) {
+ ip = p_string.substr(p_start, p_string.length() - p_start);
+ } else {
+ ip = p_string;
+ };
+
+ int slices = ip.get_slice_count(".");
if (slices!=4) {
- ERR_EXPLAIN("Invalid IP Address String: "+p_string);
+ ERR_EXPLAIN("Invalid IP Address String: "+ip);
ERR_FAIL();
}
for(int i=0;i<4;i++) {
-
- field[i]=p_string.get_slicec('.',i).to_int();
+ p_ret[i]=ip.get_slicec('.',i).to_int();
}
+};
+
+void IP_Address::clear() {
+
+ memset(&field8[0], 0, sizeof(field8));
+};
+
+IP_Address::IP_Address(const String& p_string) {
+
+ clear();
+ if (p_string.find(":") >= 0) {
+
+ _parse_ipv6(p_string);
+ type = TYPE_IPV6;
+ } else {
+
+ _parse_ipv4(p_string, 0, &field8[0]);
+ type = TYPE_IPV4;
+ };
}
-IP_Address::IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d) {
+_FORCE_INLINE_ static void _32_to_buf(uint8_t* p_dst, uint32_t p_n) {
+
+ p_dst[0] = (p_n >> 24) & 0xff;
+ p_dst[1] = (p_n >> 16) & 0xff;
+ p_dst[2] = (p_n >> 8) & 0xff;
+ p_dst[3] = (p_n >> 0) & 0xff;
+};
+
+IP_Address::IP_Address(uint32_t p_a,uint32_t p_b,uint32_t p_c,uint32_t p_d, IP_Address::AddrType p_type) {
+
+ type = p_type;
+ memset(&field8[0], 0, sizeof(field8));
+ if (p_type == TYPE_IPV4) {
+ field8[0]=p_a;
+ field8[1]=p_b;
+ field8[2]=p_c;
+ field8[3]=p_d;
+ } else if (type == TYPE_IPV6) {
+
+ _32_to_buf(&field8[0], p_a);
+ _32_to_buf(&field8[4], p_b);
+ _32_to_buf(&field8[8], p_c);
+ _32_to_buf(&field8[12], p_d);
+ } else {
+ type = TYPE_NONE;
+ ERR_EXPLAIN("Invalid type specified for IP_Address (use TYPE_IPV4 or TYPE_IPV6");
+ ERR_FAIL();
+ };
- field[0]=p_a;
- field[1]=p_b;
- field[2]=p_c;
- field[3]=p_d;
}
diff --git a/core/io/ip_address.h b/core/io/ip_address.h
index 1292311729..fe13d70611 100644
--- a/core/io/ip_address.h
+++ b/core/io/ip_address.h
@@ -33,22 +33,48 @@
struct IP_Address {
+public:
+ enum AddrType {
+ TYPE_NONE = 0,
+ TYPE_IPV4 = 1,
+ TYPE_IPV6 = 2,
+
+ TYPE_ANY = 3,
+ };
+
+ AddrType type;
+
union {
- uint8_t field[4];
- uint32_t host;
+ uint8_t field8[16];
+ uint16_t field16[8];
+ uint32_t field32[4];
};
+protected:
+ void _parse_ipv6(const String& p_string);
+ void _parse_ipv4(const String& p_string, int p_start, uint8_t* p_ret);
+
+public:
//operator Variant() const;
bool operator==(const IP_Address& p_ip) const {
- return host==p_ip.host;
+ for (int i=0; i<4; i++)
+ if (field32[i] != p_ip.field32[i])
+ return false;
+ return true;
}
bool operator!=(const IP_Address& p_ip) const {
- return host!=p_ip.host;
+ for (int i=0; i<4; i++)
+ if (field32[i] != p_ip.field32[i])
+ return true;
+ return false;
}
+
+ void clear();
+
operator String() const;
IP_Address(const String& p_string);
- IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d);
- IP_Address() { host=0; }
+ IP_Address(uint32_t p_a,uint32_t p_b,uint32_t p_c,uint32_t p_d, AddrType p_type=TYPE_IPV4);
+ IP_Address() { clear(); type=TYPE_NONE; }
};
diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp
index efc619e414..018dc77d91 100644
--- a/core/io/packet_peer_udp.cpp
+++ b/core/io/packet_peer_udp.cpp
@@ -29,14 +29,9 @@
#include "packet_peer_udp.h"
#include "io/ip.h"
-
PacketPeerUDP* (*PacketPeerUDP::_create)()=NULL;
-int PacketPeerUDP::_get_packet_address() const {
-
- IP_Address ip = get_packet_address();
- return ip.host;
-}
+VARIANT_ENUM_CAST(IP_Address::AddrType);
String PacketPeerUDP::_get_packet_ip() const {
@@ -65,7 +60,7 @@ void PacketPeerUDP::_bind_methods() {
ObjectTypeDB::bind_method(_MD("wait:Error"),&PacketPeerUDP::wait);
ObjectTypeDB::bind_method(_MD("is_listening"),&PacketPeerUDP::is_listening);
ObjectTypeDB::bind_method(_MD("get_packet_ip"),&PacketPeerUDP::_get_packet_ip);
- ObjectTypeDB::bind_method(_MD("get_packet_address"),&PacketPeerUDP::_get_packet_address);
+ //ObjectTypeDB::bind_method(_MD("get_packet_address"),&PacketPeerUDP::_get_packet_address);
ObjectTypeDB::bind_method(_MD("get_packet_port"),&PacketPeerUDP::get_packet_port);
ObjectTypeDB::bind_method(_MD("set_send_address","host","port"),&PacketPeerUDP::_set_send_address);
diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h
index 70d92834fc..c0806a9e6a 100644
--- a/core/io/packet_peer_udp.h
+++ b/core/io/packet_peer_udp.h
@@ -40,14 +40,13 @@ protected:
static PacketPeerUDP* (*_create)();
static void _bind_methods();
- int _get_packet_address() const;
String _get_packet_ip() const;
virtual Error _set_send_address(const String& p_address,int p_port);
public:
- virtual Error listen(int p_port,int p_recv_buffer_size=65536)=0;
+ virtual Error listen(int p_port, IP_Address::AddrType p_address_type = IP_Address::TYPE_IPV4, int p_recv_buffer_size=65536)=0;
virtual void close()=0;
virtual Error wait()=0;
virtual bool is_listening() const=0;
diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp
index 274d20a48a..53d6e900f3 100644
--- a/core/io/tcp_server.cpp
+++ b/core/io/tcp_server.cpp
@@ -30,6 +30,8 @@
TCP_Server* (*TCP_Server::_create)()=NULL;
+VARIANT_ENUM_CAST(IP_Address::AddrType);
+
Ref<TCP_Server> TCP_Server::create_ref() {
if (!_create)
@@ -44,19 +46,19 @@ TCP_Server* TCP_Server::create() {
return _create();
}
-Error TCP_Server::_listen(uint16_t p_port,DVector<String> p_accepted_hosts) {
+Error TCP_Server::_listen(uint16_t p_port, IP_Address::AddrType p_type, DVector<String> p_accepted_hosts) {
List<String> hosts;
for(int i=0;i<p_accepted_hosts.size();i++)
hosts.push_back(p_accepted_hosts.get(i));
- return listen(p_port,hosts.size()?&hosts:NULL);
+ return listen(p_port,p_type, hosts.size()?&hosts:NULL);
}
void TCP_Server::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("listen","port","accepted_hosts"),&TCP_Server::_listen,DEFVAL(DVector<String>()));
+ ObjectTypeDB::bind_method(_MD("listen","port","accepted_hosts"),&TCP_Server::_listen,DEFVAL(IP_Address::TYPE_IPV4), DEFVAL(DVector<String>()));
ObjectTypeDB::bind_method(_MD("is_connection_available"),&TCP_Server::is_connection_available);
ObjectTypeDB::bind_method(_MD("take_connection"),&TCP_Server::take_connection);
ObjectTypeDB::bind_method(_MD("stop"),&TCP_Server::stop);
diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h
index 512a7e640a..883a3ef2f6 100644
--- a/core/io/tcp_server.h
+++ b/core/io/tcp_server.h
@@ -41,11 +41,11 @@ protected:
static TCP_Server* (*_create)();
//bind helper
- Error _listen(uint16_t p_port,DVector<String> p_accepted_hosts=DVector<String>());
+ Error _listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4 ,DVector<String> p_accepted_hosts=DVector<String>());
static void _bind_methods();
public:
- virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL)=0;
+ virtual Error listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4, const List<String> *p_accepted_hosts=NULL)=0;
virtual bool is_connection_available() const=0;
virtual Ref<StreamPeerTCP> take_connection()=0;
diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp
index 64615fe6b4..46c0218707 100644
--- a/core/math/math_funcs.cpp
+++ b/core/math/math_funcs.cpp
@@ -41,37 +41,16 @@ static uint32_t Q[4096];
#endif
uint32_t Math::rand_from_seed(uint32_t *seed) {
-
-#if 1
- uint32_t k;
- uint32_t s = (*seed);
- if (s == 0)
- s = 0x12345987;
- k = s / 127773;
- s = 16807 * (s - k * 127773) - 2836 * k;
-// if (s < 0)
-// s += 2147483647;
- (*seed) = s;
- return (s & Math::RANDOM_MAX);
-#else
- *seed = *seed * 1103515245 + 12345;
- return (*seed % ((unsigned int)RANDOM_MAX + 1));
-#endif
+ // Xorshift31 PRNG
+ if ( *seed == 0 ) *seed = Math::RANDOM_MAX;
+ (*seed) ^= (*seed) << 13;
+ (*seed) ^= (*seed) >> 17;
+ (*seed) ^= (*seed) << 5;
+ return (*seed) & Math::RANDOM_MAX;
}
void Math::seed(uint32_t x) {
-#if 0
- int i;
-
- Q[0] = x;
- Q[1] = x + PHI;
- Q[2] = x + PHI + PHI;
-
- for (i = 3; i < 4096; i++)
- Q[i] = Q[i - 3] ^ Q[i - 2] ^ PHI ^ i;
-#else
default_seed=x;
-#endif
}
void Math::randomize() {
@@ -82,12 +61,12 @@ void Math::randomize() {
uint32_t Math::rand() {
- return rand_from_seed(&default_seed)&0x7FFFFFFF;
+ return rand_from_seed(&default_seed);
}
double Math::randf() {
- return (double)rand() / (double)RANDOM_MAX;
+ return (double)rand() / (double)Math::RANDOM_MAX;
}
double Math::sin(double p_x) {
diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp
index 9982767be1..7350be824a 100644
--- a/core/os/input_event.cpp
+++ b/core/os/input_event.cpp
@@ -50,7 +50,8 @@ bool InputEvent::operator==(const InputEvent &p_event) const {
case MOUSE_MOTION:
return mouse_motion.x == p_event.mouse_motion.x
&& mouse_motion.y == p_event.mouse_motion.y
- && mouse_motion.relative_x == p_event.mouse_motion.relative_y
+ && mouse_motion.relative_x == p_event.mouse_motion.relative_x
+ && mouse_motion.relative_y == p_event.mouse_motion.relative_y
&& mouse_motion.button_mask == p_event.mouse_motion.button_mask
&& key.mod == p_event.key.mod;
case MOUSE_BUTTON:
diff --git a/core/translation.cpp b/core/translation.cpp
index 4592d00598..4d81073fe6 100644
--- a/core/translation.cpp
+++ b/core/translation.cpp
@@ -779,6 +779,11 @@ Vector<String> TranslationServer::get_all_locale_names(){
}
+static String get_trimmed_locale(const String& p_locale) {
+
+ return p_locale.substr(0,2);
+}
+
static bool is_valid_locale(const String& p_locale) {
const char **ptr=locale_list;
@@ -839,9 +844,20 @@ void Translation::_set_messages(const DVector<String>& p_messages){
void Translation::set_locale(const String& p_locale) {
- ERR_EXPLAIN("Invalid Locale: "+p_locale);
- ERR_FAIL_COND(!is_valid_locale(p_locale));
- locale=p_locale;
+ // replaces '-' with '_' for macOS Sierra-style locales
+ String univ_locale = p_locale.replace("-", "_");
+
+ if(!is_valid_locale(univ_locale)) {
+ String trimmed_locale = get_trimmed_locale(univ_locale);
+
+ ERR_EXPLAIN("Invalid Locale: "+trimmed_locale);
+ ERR_FAIL_COND(!is_valid_locale(trimmed_locale));
+
+ locale=trimmed_locale;
+ }
+ else {
+ locale=univ_locale;
+ }
}
void Translation::add_message( const StringName& p_src_text, const StringName& p_xlated_text ) {
@@ -906,9 +922,20 @@ Translation::Translation() {
void TranslationServer::set_locale(const String& p_locale) {
- ERR_EXPLAIN("Invalid Locale: "+p_locale);
- ERR_FAIL_COND(!is_valid_locale(p_locale));
- locale=p_locale;
+ // replaces '-' with '_' for macOS Sierra-style locales
+ String univ_locale = p_locale.replace("-", "_");
+
+ if(!is_valid_locale(univ_locale)) {
+ String trimmed_locale = get_trimmed_locale(univ_locale);
+
+ ERR_EXPLAIN("Invalid Locale: "+trimmed_locale);
+ ERR_FAIL_COND(!is_valid_locale(trimmed_locale));
+
+ locale=trimmed_locale;
+ }
+ else {
+ locale=univ_locale;
+ }
}
String TranslationServer::get_locale() const {
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 2e907381f7..f9c10615b3 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -1543,11 +1543,11 @@ String::String(const StrRange& p_range) {
copy_from(p_range.c_str,p_range.len);
}
-int String::hex_to_int() const {
+int String::hex_to_int(bool p_with_prefix) const {
int l = length();
- if (l<3)
- return 0;
+ if (p_with_prefix && l<3)
+ return 0;
const CharType *s=ptr();
@@ -1556,15 +1556,16 @@ int String::hex_to_int() const {
if (sign<0) {
s++;
l--;
- if (l<2)
+ if (p_with_prefix && l<2)
return 0;
}
- if (s[0]!='0' || s[1]!='x')
- return 0;
-
- s+=2;
- l-=2;
+ if (p_with_prefix) {
+ if (s[0]!='0' || s[1]!='x')
+ return 0;
+ s+=2;
+ l-=2;
+ };
int hex=0;
@@ -3510,6 +3511,36 @@ bool String::is_valid_integer() const {
}
+bool String::is_valid_hex_number(bool p_with_prefix) const {
+
+ int from = 0;
+ int len = length();
+
+ if (len!=1 && (operator[](0)=='+' || operator[](0)=='-'))
+ from++;
+
+ if (p_with_prefix) {
+
+ if (len < 2)
+ return false;
+ if (operator[](from) != '0' || operator[](from+1) != 'x') {
+ return false;
+ };
+ from += 2;
+ };
+
+ for (int i=from; i<len; i++) {
+
+ CharType c = operator[](i);
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
+ continue;
+ return false;
+ };
+
+ return true;
+};
+
+
bool String::is_valid_float() const {
int len = length();
@@ -3646,20 +3677,41 @@ bool String::is_valid_html_color() const {
}
+
bool String::is_valid_ip_address() const {
- Vector<String> ip = split(".");
- if (ip.size()!=4)
- return false;
- for(int i=0;i<ip.size();i++) {
+ if (find(":") >= 0) {
- String n = ip[i];
- if (!n.is_valid_integer())
- return false;
- int val = n.to_int();
- if (val<0 || val>255)
+ Vector<String> ip = split(":");
+ for (int i=0; i<ip.size(); i++) {
+
+ String n = ip[i];
+ if (n.empty())
+ continue;
+ if (n.is_valid_hex_number(false)) {
+ int nint = n.hex_to_int(false);
+ if (nint < 0 || nint > 0xffff)
+ return false;
+ continue;
+ };
+ if (!n.is_valid_ip_address())
+ return false;
+ };
+
+ } else {
+ Vector<String> ip = split(".");
+ if (ip.size()!=4)
return false;
- }
+ for(int i=0;i<ip.size();i++) {
+
+ String n = ip[i];
+ if (!n.is_valid_integer())
+ return false;
+ int val = n.to_int();
+ if (val<0 || val>255)
+ return false;
+ }
+ };
return true;
}
diff --git a/core/ustring.h b/core/ustring.h
index 09d13a9e64..452f252857 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -144,7 +144,7 @@ public:
bool is_numeric() const;
double to_double() const;
float to_float() const;
- int hex_to_int() const;
+ int hex_to_int(bool p_with_prefix = true) const;
int to_int() const;
int64_t to_int64() const;
@@ -226,6 +226,7 @@ public:
bool is_valid_identifier() const;
bool is_valid_integer() const;
bool is_valid_float() const;
+ bool is_valid_hex_number(bool p_with_prefix) const;
bool is_valid_html_color() const;
bool is_valid_ip_address() const;
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 51cd4c2399..9b6fa27cf4 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -1820,6 +1820,11 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl
_VariantCall::add_constant(Variant::IMAGE,"INTERPOLATE_NEAREST",Image::INTERPOLATE_NEAREST);
_VariantCall::add_constant(Variant::IMAGE,"INTERPOLATE_BILINEAR",Image::INTERPOLATE_BILINEAR);
_VariantCall::add_constant(Variant::IMAGE,"INTERPOLATE_CUBIC",Image::INTERPOLATE_CUBIC);
+
+ _VariantCall::add_constant(Variant::INT, "IP_TYPE_NONE", IP_Address::TYPE_NONE);
+ _VariantCall::add_constant(Variant::INT, "IP_TYPE_IPV4", IP_Address::TYPE_IPV4);
+ _VariantCall::add_constant(Variant::INT, "IP_TYPE_IPV6", IP_Address::TYPE_IPV6);
+ _VariantCall::add_constant(Variant::INT, "IP_TYPE_ANY", IP_Address::TYPE_ANY);
}
void unregister_variant_methods() {
diff --git a/doc/base/classes.xml b/doc/base/classes.xml
index 5eb021f6c0..47fa1deb55 100644
--- a/doc/base/classes.xml
+++ b/doc/base/classes.xml
@@ -32514,6 +32514,7 @@
would be read as [code]"(?:\\.|[^"])*"[/code]
Currently supported features:
* Capturing [code]()[/code] and non-capturing [code](?:)[/code] groups
+ * Named capturing groups [code](?P&lt;name&gt;)[/code]
* Any character [code].[/code]
* Shorthand character classes [code]\w \W \s \S \d \D[/code]
* User-defined character classes such as [code][A-Za-z][/code]
@@ -32522,7 +32523,7 @@
* Lazy (non-greedy) quantifiers [code]*?[/code]
* Beginning [code]^[/code] and end [code]$[/code] anchors
* Alternation [code]|[/code]
- * Backreferences [code]\1[/code] and [code]\g{1}[/code]
+ * Backreferences [code]\1[/code], [code]\g{1}[/code], and [code]\g&lt;name&gt;[/code]
* POSIX character classes [code][[:alnum:]][/code]
* Lookahead [code](?=)[/code], [code](?!)[/code] and lookbehind [code](?&lt;=)[/code], [code](?&lt;!)[/code]
* ASCII [code]\xFF[/code] and Unicode [code]\uFFFF[/code] code points (in a style similar to Python)
@@ -32531,7 +32532,7 @@
<methods>
<method name="clear">
<description>
- This method resets the state of the object, as it was freshly created. Namely, it unassigns the regular expression of this object, and forgets all captures made by the last [method find].
+ This method resets the state of the object, as it was freshly created. Namely, it unassigns the regular expression of this object.
</description>
</method>
<method name="compile">
@@ -32539,15 +32540,41 @@
</return>
<argument index="0" name="pattern" type="String">
</argument>
- <argument index="1" name="capture" type="int" default="9">
- </argument>
<description>
- Compiles and assign the regular expression pattern to use. The limit on the number of capturing groups can be specified or made unlimited if negative.
+ Compiles and assign the regular expression pattern to use.
</description>
</method>
- <method name="find" qualifiers="const">
+ <method name="get_group_count" qualifiers="const">
<return type="int">
</return>
+ <description>
+ Returns the number of numeric capturing groups.
+ </description>
+ </method>
+ <method name="get_names" qualifiers="const">
+ <return type="Array">
+ </return>
+ <description>
+ Returns an array of names of named capturing groups.
+ </description>
+ </method>
+ <method name="get_pattern" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Returns the expression used to compile the code.
+ </description>
+ </method>
+ <method name="is_valid" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns whether this object has a valid regular expression assigned.
+ </description>
+ </method>
+ <method name="search" qualifiers="const">
+ <return type="Object">
+ </return>
<argument index="0" name="text" type="String">
</argument>
<argument index="1" name="start" type="int" default="0">
@@ -32555,45 +32582,98 @@
<argument index="2" name="end" type="int" default="-1">
</argument>
<description>
- This method tries to find the pattern within the string, and returns the position where it was found. It also stores any capturing group (see [method get_capture]) for further retrieval.
+ Searches the text for the compiled pattern. Returns a [RegExMatch] container of the first matching reult if found, otherwise null. The region to search within can be specified without modifying where the start and end anchor would be.
</description>
</method>
- <method name="get_capture" qualifiers="const">
+ <method name="sub" qualifiers="const">
<return type="String">
</return>
- <argument index="0" name="capture" type="int">
+ <argument index="0" name="text" type="String">
+ </argument>
+ <argument index="1" name="replacement" type="String">
+ </argument>
+ <argument index="2" name="all" type="bool" default="false">
+ </argument>
+ <argument index="3" name="start" type="int" default="0">
+ </argument>
+ <argument index="4" name="end" type="int" default="-1">
</argument>
<description>
- Returns a captured group. A captured group is the part of a string that matches a part of the pattern delimited by parentheses (unless they are non-capturing parentheses [i](?:)[/i]).
+ Searches the text for the compiled pattern and replaces it with the specified string. Escapes and backreferences such as [code]\1[/code] and [code]\g&lt;name&gt;[/code] expanded and resolved. By default only the first instance is replaced but it can be changed for all instances (global replacement). The region to search within can be specified without modifying where the start and end anchor would be.
</description>
</method>
- <method name="get_capture_count" qualifiers="const">
- <return type="int">
+ </methods>
+ <constants>
+ </constants>
+</class>
+<class name="RegExMatch" inherits="Reference" category="Core">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <methods>
+ <method name="expand" qualifiers="const">
+ <return type="String">
</return>
+ <argument index="0" name="template" type="String">
+ </argument>
<description>
- Returns the number of capturing groups. A captured group is the part of a string that matches a part of the pattern delimited by parentheses (unless they are non-capturing parentheses [i](?:)[/i]).
+ Using results from the search, returns the specified string with escapes and backreferences such as [code]\1[/code] and [code]\g&lt;name&gt;[/code] expanded and resolved.
</description>
</method>
- <method name="get_capture_start" qualifiers="const">
+ <method name="get_end" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="capture" type="int">
+ <argument index="0" name="name" type="Variant" default="0">
</argument>
<description>
+ Returns the end position of the match in the string. An interger can be specified for numeric groups or a string for named groups. Returns -1 if that group wasn't found or doesn't exist. Defaults to 0 (whole pattern).
</description>
</method>
- <method name="get_captures" qualifiers="const">
- <return type="StringArray">
+ <method name="get_group_array" qualifiers="const">
+ <return type="Array">
</return>
<description>
- Return a list of all the captures made by the regular expression.
+ Returns an array of the results of the numeric groups.
</description>
</method>
- <method name="is_valid" qualifiers="const">
- <return type="bool">
+ <method name="get_group_count" qualifiers="const">
+ <return type="int">
</return>
<description>
- Returns whether this object has a valid regular expression assigned.
+ Returns the number of numeric capturing groups.
+ </description>
+ </method>
+ <method name="get_name_dict" qualifiers="const">
+ <return type="Dictionary">
+ </return>
+ <description>
+ Returns a dictionary containing the named capturing groups and their results.
+ </description>
+ </method>
+ <method name="get_names" qualifiers="const">
+ <return type="Array">
+ </return>
+ <description>
+ Returns an array of names of named capturing groups.
+ </description>
+ </method>
+ <method name="get_start" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="name" type="Variant" default="0">
+ </argument>
+ <description>
+ Returns the starting position of the match in the string. An interger can be specified for numeric groups or a string for named groups. Returns -1 if that group wasn't found or doesn't exist. Defaults to 0 (whole pattern).
+ </description>
+ </method>
+ <method name="get_string" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="name" type="Variant" default="0">
+ </argument>
+ <description>
+ Returns the result of the match in the string. An interger can be specified for numeric groups or a string for named groups. Returns -1 if that group wasn't found or doesn't exist. Defaults to 0 (whole pattern).
</description>
</method>
</methods>
diff --git a/drivers/SCsub b/drivers/SCsub
index 1f1509efa8..ab2c991f24 100644
--- a/drivers/SCsub
+++ b/drivers/SCsub
@@ -25,7 +25,6 @@ SConscript('gl_context/SCsub');
# Core dependencies
SConscript("png/SCsub");
-SConscript("nrex/SCsub");
# Tools override
# FIXME: Should likely be integrated in the tools/ codebase
diff --git a/drivers/nrex/README.md b/drivers/nrex/README.md
deleted file mode 100644
index 7a942b2452..0000000000
--- a/drivers/nrex/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# NREX: Node RegEx
-
-[![Build Status](https://travis-ci.org/leezh/nrex.svg?branch=master)](https://travis-ci.org/leezh/nrex)
-
-** Version 0.2 **
-
-Small node-based regular expression library. It only does text pattern
-matchhing, not replacement. To use add the files `nrex.hpp`, `nrex.cpp`
-and `nrex_config.h` to your project and follow the example:
-
- nrex regex;
- regex.compile("^(fo+)bar$");
-
- nrex_result captures[regex.capture_size()];
- if (regex.match("foobar", captures))
- {
- std::cout << captures[0].start << std::endl;
- std::cout << captures[0].length << std::endl;
- }
-
-More details about its use is documented in `nrex.hpp`
-
-Currently supported features:
- * Capturing `()` and non-capturing `(?:)` groups
- * Any character `.` (includes newlines)
- * Shorthand caracter classes `\w\W\s\S\d\D`
- * POSIX character classes such as `[[:alnum:]]`
- * Bracket expressions such as `[A-Za-z]`
- * Simple quantifiers `?`, `*` and `+`
- * Range quantifiers `{0,1}`
- * Lazy (non-greedy) quantifiers `*?`
- * Begining `^` and end `$` anchors
- * Word boundaries `\b`
- * Alternation `|`
- * ASCII `\xFF` code points
- * Unicode `\uFFFF` code points
- * Positive `(?=)` and negative `(?!)` lookahead
- * Positive `(?<=)` and negative `(?<!)` lookbehind (fixed length and no alternations)
- * Backreferences `\1` and `\g{1}` (limited by default to 9 - can be unlimited)
-
-## License
-
-Copyright (c) 2015-2016, Zher Huei Lee
-All rights reserved.
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would
- be appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not
- be misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
-
-
-# Changes
-
-## Version 0.2 (2016-08-04)
- * Fixed capturing groups matching to invalid results
- * Fixed parents of recursive quantifiers not expanding properly
- * Fixed LookAhead sometimes adding to result
- * More verbose unit testing
-
-## Version 0.1 (2015-12-04)
- * Initial release
diff --git a/drivers/nrex/nrex.cpp b/drivers/nrex/nrex.cpp
deleted file mode 100644
index 69e04285e3..0000000000
--- a/drivers/nrex/nrex.cpp
+++ /dev/null
@@ -1,1496 +0,0 @@
-// NREX: Node RegEx
-// Version 0.2
-//
-// Copyright (c) 2015-2016, Zher Huei Lee
-// All rights reserved.
-//
-// This software is provided 'as-is', without any express or implied
-// warranty. In no event will the authors be held liable for any damages
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would
-// be appreciated but is not required.
-//
-// 2. Altered source versions must be plainly marked as such, and must not
-// be misrepresented as being the original software.
-//
-// 3. This notice may not be removed or altered from any source
-// distribution.
-//
-
-#include "nrex.hpp"
-
-#ifdef NREX_UNICODE
-#include <wctype.h>
-#include <wchar.h>
-#define NREX_ISALPHANUM iswalnum
-#define NREX_ISSPACE iswspace
-#define NREX_STRLEN wcslen
-#else
-#include <ctype.h>
-#include <string.h>
-#define NREX_ISALPHANUM isalnum
-#define NREX_ISSPACE isspace
-#define NREX_STRLEN strlen
-#endif
-
-#ifdef NREX_THROW_ERROR
-#define NREX_COMPILE_ERROR(M) throw nrex_compile_error(M)
-#else
-#define NREX_COMPILE_ERROR(M) reset(); return false
-#endif
-
-#ifndef NREX_NEW
-#define NREX_NEW(X) new X
-#define NREX_NEW_ARRAY(X, N) new X[N]
-#define NREX_DELETE(X) delete X
-#define NREX_DELETE_ARRAY(X) delete[] X
-#endif
-
-template<typename T>
-class nrex_array
-{
- private:
- T* _data;
- unsigned int _reserved;
- unsigned int _size;
- public:
- nrex_array()
- : _data(NREX_NEW_ARRAY(T, 2))
- , _reserved(2)
- , _size(0)
- {
- }
-
- nrex_array(unsigned int reserved)
- : _data(NREX_NEW_ARRAY(T, reserved ? reserved : 1))
- , _reserved(reserved ? reserved : 1)
- , _size(0)
- {
- }
-
- ~nrex_array()
- {
- NREX_DELETE_ARRAY(_data);
- }
-
- unsigned int size() const
- {
- return _size;
- }
-
- void reserve(unsigned int size)
- {
- if (size < _size) {
- size = _size;
- }
- if (size == 0) {
- size = 1;
- }
- T* old = _data;
- _data = NREX_NEW_ARRAY(T, size);
- _reserved = size;
- for (unsigned int i = 0; i < _size; ++i)
- {
- _data[i] = old[i];
- }
- NREX_DELETE_ARRAY(old);
- }
-
- void push(T item)
- {
- if (_size == _reserved)
- {
- reserve(_reserved * 2);
- }
- _data[_size] = item;
- _size++;
- }
-
- const T& top() const
- {
- return _data[_size - 1];
- }
-
- const T& operator[] (unsigned int i) const
- {
- return _data[i];
- }
-
- void pop()
- {
- if (_size > 0)
- {
- --_size;
- }
- }
-};
-
-static int nrex_parse_hex(nrex_char c)
-{
- if ('0' <= c && c <= '9')
- {
- return int(c - '0');
- }
- else if ('a' <= c && c <= 'f')
- {
- return int(c - 'a') + 10;
- }
- else if ('A' <= c && c <= 'F')
- {
- return int(c - 'A') + 10;
- }
- return -1;
-}
-
-static nrex_char nrex_unescape(const nrex_char*& c)
-{
- switch (c[1])
- {
- case '0': ++c; return '\0';
- case 'a': ++c; return '\a';
- case 'e': ++c; return '\e';
- case 'f': ++c; return '\f';
- case 'n': ++c; return '\n';
- case 'r': ++c; return '\r';
- case 't': ++c; return '\t';
- case 'v': ++c; return '\v';
- case 'b': ++c; return '\b';
- case 'x':
- {
- int point = 0;
- for (int i = 2; i <= 3; ++i)
- {
- int res = nrex_parse_hex(c[i]);
- if (res == -1)
- {
- return '\0';
- }
- point = (point << 4) + res;
- }
- c = &c[3];
- return nrex_char(point);
- }
- case 'u':
- {
- int point = 0;
- for (int i = 2; i <= 5; ++i)
- {
- int res = nrex_parse_hex(c[i]);
- if (res == -1)
- {
- return '\0';
- }
- point = (point << 4) + res;
- }
- c = &c[5];
- return nrex_char(point);
- }
- }
- return (++c)[0];
-}
-
-struct nrex_search
-{
- const nrex_char* str;
- nrex_result* captures;
- int end;
- bool complete;
- nrex_array<int> lookahead_pos;
-
- nrex_char at(int pos)
- {
- return str[pos];
- }
-
- nrex_search(const nrex_char* str, nrex_result* captures, int lookahead)
- : str(str)
- , captures(captures)
- , end(0)
- , lookahead_pos(lookahead)
- {
- }
-};
-
-struct nrex_node
-{
- nrex_node* next;
- nrex_node* previous;
- nrex_node* parent;
- bool quantifiable;
- int length;
-
- nrex_node(bool quantify = false)
- : next(NULL)
- , previous(NULL)
- , parent(NULL)
- , quantifiable(quantify)
- , length(-1)
- {
- }
-
- virtual ~nrex_node()
- {
- if (next)
- {
- NREX_DELETE(next);
- }
- }
-
- virtual int test(nrex_search* s, int pos) const
- {
- return next ? next->test(s, pos) : -1;
- }
-
- virtual int test_parent(nrex_search* s, int pos) const
- {
- if (next)
- {
- pos = next->test(s, pos);
- }
- if (pos >= 0)
- {
- s->complete = true;
- }
- if (parent && pos >= 0)
- {
- pos = parent->test_parent(s, pos);
- }
- if (pos < 0)
- {
- s->complete = false;
- }
- return pos;
- }
-
- void increment_length(int amount, bool subtract = false)
- {
- if (amount >= 0 && length >= 0)
- {
- if (!subtract)
- {
- length += amount;
- }
- else
- {
- length -= amount;
- }
- }
- else
- {
- length = -1;
- }
- if (parent)
- {
- parent->increment_length(amount, subtract);
- }
- }
-};
-
-enum nrex_group_type
-{
- nrex_group_capture,
- nrex_group_non_capture,
- nrex_group_bracket,
- nrex_group_look_ahead,
- nrex_group_look_behind,
-};
-
-struct nrex_node_group : public nrex_node
-{
- nrex_group_type type;
- int id;
- bool negate;
- nrex_array<nrex_node*> childset;
- nrex_node* back;
-
- nrex_node_group(nrex_group_type type, int id = 0)
- : nrex_node(true)
- , type(type)
- , id(id)
- , negate(false)
- , back(NULL)
- {
- if (type != nrex_group_bracket)
- {
- length = 0;
- }
- else
- {
- length = 1;
- }
- if (type == nrex_group_look_ahead || type == nrex_group_look_behind)
- {
- quantifiable = false;
- }
- }
-
- virtual ~nrex_node_group()
- {
- for (unsigned int i = 0; i < childset.size(); ++i)
- {
- NREX_DELETE(childset[i]);
- }
-
- }
-
- int test(nrex_search* s, int pos) const
- {
- int old_start;
- if (type == nrex_group_capture)
- {
- old_start = s->captures[id].start;
- s->captures[id].start = pos;
- }
- for (unsigned int i = 0; i < childset.size(); ++i)
- {
- s->complete = false;
- int offset = 0;
- if (type == nrex_group_look_behind)
- {
- if (pos < length)
- {
- return -1;
- }
- offset = length;
- }
- if (type == nrex_group_look_ahead)
- {
- s->lookahead_pos.push(pos);
- }
- int res = childset[i]->test(s, pos - offset);
- if (type == nrex_group_look_ahead)
- {
- s->lookahead_pos.pop();
- }
- if (s->complete)
- {
- return res;
- }
- if (negate)
- {
- if (res < 0)
- {
- res = pos + 1;
- }
- else
- {
- return -1;
- }
- if (i + 1 < childset.size())
- {
- continue;
- }
- }
- if (res >= 0)
- {
- if (type == nrex_group_capture)
- {
- s->captures[id].length = res - pos;
- }
- else if (type == nrex_group_look_ahead || type == nrex_group_look_behind)
- {
- res = pos;
- }
- return next ? next->test(s, res) : res;
- }
- }
- if (type == nrex_group_capture)
- {
- s->captures[id].start = old_start;
- }
- return -1;
- }
-
- virtual int test_parent(nrex_search* s, int pos) const
- {
- if (type == nrex_group_capture)
- {
- s->captures[id].length = pos - s->captures[id].start;
- }
- if (type == nrex_group_look_ahead)
- {
- pos = s->lookahead_pos[id];
- }
- return nrex_node::test_parent(s, pos);
- }
-
- void add_childset()
- {
- if (childset.size() > 0 && type != nrex_group_bracket)
- {
- length = -1;
- }
- back = NULL;
- }
-
- void add_child(nrex_node* node)
- {
- node->parent = this;
- node->previous = back;
- if (back && type != nrex_group_bracket)
- {
- back->next = node;
- }
- else
- {
- childset.push(node);
- }
- if (type != nrex_group_bracket)
- {
- increment_length(node->length);
- }
- back = node;
- }
-
- nrex_node* swap_back(nrex_node* node)
- {
- if (!back)
- {
- add_child(node);
- return NULL;
- }
- nrex_node* old = back;
- if (!old->previous)
- {
- childset.pop();
- }
- if (type != nrex_group_bracket)
- {
- increment_length(old->length, true);
- }
- back = old->previous;
- add_child(node);
- return old;
- }
-
- void pop_back()
- {
- if (back)
- {
- nrex_node* old = back;
- if (!old->previous)
- {
- childset.pop();
- }
- if (type != nrex_group_bracket)
- {
- increment_length(old->length, true);
- }
- back = old->previous;
- NREX_DELETE(old);
- }
- }
-};
-
-struct nrex_node_char : public nrex_node
-{
- nrex_char ch;
-
- nrex_node_char(nrex_char c)
- : nrex_node(true)
- , ch(c)
- {
- length = 1;
- }
-
- int test(nrex_search* s, int pos) const
- {
- if (s->end <= pos || 0 > pos || s->at(pos) != ch)
- {
- return -1;
- }
- return next ? next->test(s, pos + 1) : pos + 1;
- }
-};
-
-struct nrex_node_range : public nrex_node
-{
- nrex_char start;
- nrex_char end;
-
- nrex_node_range(nrex_char s, nrex_char e)
- : nrex_node(true)
- , start(s)
- , end(e)
- {
- length = 1;
- }
-
- int test(nrex_search* s, int pos) const
- {
- if (s->end <= pos || 0 > pos)
- {
- return -1;
- }
- nrex_char c = s->at(pos);
- if (c < start || end < c)
- {
- return -1;
- }
- return next ? next->test(s, pos + 1) : pos + 1;
- }
-};
-
-enum nrex_class_type
-{
- nrex_class_none,
- nrex_class_alnum,
- nrex_class_alpha,
- nrex_class_blank,
- nrex_class_cntrl,
- nrex_class_digit,
- nrex_class_graph,
- nrex_class_lower,
- nrex_class_print,
- nrex_class_punct,
- nrex_class_space,
- nrex_class_upper,
- nrex_class_xdigit,
- nrex_class_word
-};
-
-static bool nrex_compare_class(const nrex_char** pos, const char* text)
-{
- unsigned int i = 0;
- for (i = 0; text[i] != '\0'; ++i)
- {
- if ((*pos)[i] != text[i])
- {
- return false;
- }
- }
- if ((*pos)[i++] != ':' || (*pos)[i] != ']')
- {
- return false;
- }
- *pos = &(*pos)[i];
- return true;
-}
-
-#define NREX_COMPARE_CLASS(POS, NAME) if (nrex_compare_class(POS, #NAME)) return nrex_class_ ## NAME
-
-static nrex_class_type nrex_parse_class(const nrex_char** pos)
-{
- NREX_COMPARE_CLASS(pos, alnum);
- NREX_COMPARE_CLASS(pos, alpha);
- NREX_COMPARE_CLASS(pos, blank);
- NREX_COMPARE_CLASS(pos, cntrl);
- NREX_COMPARE_CLASS(pos, digit);
- NREX_COMPARE_CLASS(pos, graph);
- NREX_COMPARE_CLASS(pos, lower);
- NREX_COMPARE_CLASS(pos, print);
- NREX_COMPARE_CLASS(pos, punct);
- NREX_COMPARE_CLASS(pos, space);
- NREX_COMPARE_CLASS(pos, upper);
- NREX_COMPARE_CLASS(pos, xdigit);
- NREX_COMPARE_CLASS(pos, word);
- return nrex_class_none;
-}
-
-struct nrex_node_class : public nrex_node
-{
- nrex_class_type type;
-
- nrex_node_class(nrex_class_type t)
- : nrex_node(true)
- , type(t)
- {
- length = 1;
- }
-
- int test(nrex_search* s, int pos) const
- {
- if (s->end <= pos || 0 > pos)
- {
- return -1;
- }
- if (!test_class(s->at(pos)))
- {
- return -1;
- }
- return next ? next->test(s, pos + 1) : pos + 1;
- }
-
- bool test_class(nrex_char c) const
- {
- if ((0 <= c && c <= 0x1F) || c == 0x7F)
- {
- if (type == nrex_class_cntrl)
- {
- return true;
- }
- }
- else if (c < 0x7F)
- {
- if (type == nrex_class_print)
- {
- return true;
- }
- else if (type == nrex_class_graph && c != ' ')
- {
- return true;
- }
- else if ('0' <= c && c <= '9')
- {
- switch (type)
- {
- case nrex_class_alnum:
- case nrex_class_digit:
- case nrex_class_xdigit:
- case nrex_class_word:
- return true;
- default:
- break;
- }
- }
- else if ('A' <= c && c <= 'Z')
- {
- switch (type)
- {
- case nrex_class_alnum:
- case nrex_class_alpha:
- case nrex_class_upper:
- case nrex_class_word:
- return true;
- case nrex_class_xdigit:
- if (c <= 'F')
- {
- return true;
- }
- default:
- break;
- }
- }
- else if ('a' <= c && c <= 'z')
- {
- switch (type)
- {
- case nrex_class_alnum:
- case nrex_class_alpha:
- case nrex_class_lower:
- case nrex_class_word:
- return true;
- case nrex_class_xdigit:
- if (c <= 'f')
- {
- return true;
- }
- default:
- break;
- }
- }
- }
- switch (c)
- {
- case ' ':
- case '\t':
- if (type == nrex_class_blank)
- {
- return true;
- }
- case '\r':
- case '\n':
- case '\f':
- if (type == nrex_class_space)
- {
- return true;
- }
- break;
- case '_':
- if (type == nrex_class_word)
- {
- return true;
- }
- case ']':
- case '[':
- case '!':
- case '"':
- case '#':
- case '$':
- case '%':
- case '&':
- case '\'':
- case '(':
- case ')':
- case '*':
- case '+':
- case ',':
- case '.':
- case '/':
- case ':':
- case ';':
- case '<':
- case '=':
- case '>':
- case '?':
- case '@':
- case '\\':
- case '^':
- case '`':
- case '{':
- case '|':
- case '}':
- case '~':
- case '-':
- if (type == nrex_class_punct)
- {
- return true;
- }
- break;
- default:
- break;
- }
- return false;
- }
-};
-
-static bool nrex_is_shorthand(nrex_char repr)
-{
- switch (repr)
- {
- case 'W':
- case 'w':
- case 'D':
- case 'd':
- case 'S':
- case 's':
- return true;
- }
- return false;
-}
-
-struct nrex_node_shorthand : public nrex_node
-{
- nrex_char repr;
-
- nrex_node_shorthand(nrex_char c)
- : nrex_node(true)
- , repr(c)
- {
- length = 1;
- }
-
- int test(nrex_search* s, int pos) const
- {
- if (s->end <= pos || 0 > pos)
- {
- return -1;
- }
- bool found = false;
- bool invert = false;
- nrex_char c = s->at(pos);
- switch (repr)
- {
- case '.':
- found = true;
- break;
- case 'W':
- invert = true;
- case 'w':
- if (c == '_' || NREX_ISALPHANUM(c))
- {
- found = true;
- }
- break;
- case 'D':
- invert = true;
- case 'd':
- if ('0' <= c && c <= '9')
- {
- found = true;
- }
- break;
- case 'S':
- invert = true;
- case 's':
- if (NREX_ISSPACE(c))
- {
- found = true;
- }
- break;
- }
- if (found == invert)
- {
- return -1;
- }
- return next ? next->test(s, pos + 1) : pos + 1;
- }
-};
-
-static bool nrex_is_quantifier(nrex_char repr)
-{
- switch (repr)
- {
- case '?':
- case '*':
- case '+':
- case '{':
- return true;
- }
- return false;
-}
-
-struct nrex_node_quantifier : public nrex_node
-{
- int min;
- int max;
- bool greedy;
- nrex_node* child;
-
- nrex_node_quantifier(int min, int max)
- : nrex_node()
- , min(min)
- , max(max)
- , greedy(true)
- , child(NULL)
- {
- }
-
- virtual ~nrex_node_quantifier()
- {
- if (child)
- {
- NREX_DELETE(child);
- }
- }
-
- int test(nrex_search* s, int pos) const
- {
- return test_step(s, pos, 0, pos);
- }
-
- int test_step(nrex_search* s, int pos, int level, int start) const
- {
- if (pos > s->end)
- {
- return -1;
- }
- if (!greedy && level > min)
- {
- int res = pos;
- if (next)
- {
- res = next->test(s, res);
- }
- if (s->complete)
- {
- return res;
- }
- if (res >= 0 && parent->test_parent(s, res) >= 0)
- {
- return res;
- }
- }
- if (max >= 0 && level > max)
- {
- return -1;
- }
- if (level > 1 && level > min + 1 && pos == start)
- {
- return -1;
- }
- int res = pos;
- if (level >= 1)
- {
- res = child->test(s, pos);
- if (s->complete)
- {
- return res;
- }
- }
- if (res >= 0)
- {
- int res_step = test_step(s, res, level + 1, start);
- if (res_step >= 0)
- {
- return res_step;
- }
- else if (greedy && level >= min)
- {
- if (next)
- {
- res = next->test(s, res);
- }
- if (s->complete)
- {
- return res;
- }
- if (res >= 0 && parent->test_parent(s, res) >= 0)
- {
- return res;
- }
- }
- }
- return -1;
- }
-
- virtual int test_parent(nrex_search* s, int pos) const
- {
- s->complete = false;
- return pos;
- }
-};
-
-struct nrex_node_anchor : public nrex_node
-{
- bool end;
-
- nrex_node_anchor(bool end)
- : nrex_node()
- , end(end)
- {
- length = 0;
- }
-
- int test(nrex_search* s, int pos) const
- {
- if (!end && pos != 0)
- {
- return -1;
- }
- else if (end && pos != s->end)
- {
- return -1;
- }
- return next ? next->test(s, pos) : pos;
- }
-};
-
-struct nrex_node_word_boundary : public nrex_node
-{
- bool inverse;
-
- nrex_node_word_boundary(bool inverse)
- : nrex_node()
- , inverse(inverse)
- {
- length = 0;
- }
-
- int test(nrex_search* s, int pos) const
- {
- bool left = false;
- bool right = false;
- if (pos != 0)
- {
- nrex_char c = s->at(pos - 1);
- if (c == '_' || NREX_ISALPHANUM(c))
- {
- left = true;
- }
- }
- if (pos != s->end)
- {
- nrex_char c = s->at(pos);
- if (c == '_' || NREX_ISALPHANUM(c))
- {
- right = true;
- }
- }
- if ((left != right) == inverse)
- {
- return -1;
- }
- return next ? next->test(s, pos) : pos;
- }
-};
-
-struct nrex_node_backreference : public nrex_node
-{
- int ref;
-
- nrex_node_backreference(int ref)
- : nrex_node(true)
- , ref(ref)
- {
- length = -1;
- }
-
- int test(nrex_search* s, int pos) const
- {
- nrex_result& r = s->captures[ref];
- for (int i = 0; i < r.length; ++i)
- {
- if (pos + i >= s->end)
- {
- return -1;
- }
- if (s->at(r.start + i) != s->at(pos + i))
- {
- return -1;
- }
- }
- return next ? next->test(s, pos + r.length) : pos + r.length;
- }
-};
-
-bool nrex_has_lookbehind(nrex_array<nrex_node_group*>& stack)
-{
- for (unsigned int i = 0; i < stack.size(); i++)
- {
- if (stack[i]->type == nrex_group_look_behind)
- {
- return true;
- }
- }
- return false;
-}
-
-nrex::nrex()
- : _capturing(0)
- , _lookahead_depth(0)
- , _root(NULL)
-{
-}
-
-nrex::nrex(const nrex_char* pattern, int captures)
- : _capturing(0)
- , _lookahead_depth(0)
- , _root(NULL)
-{
- compile(pattern, captures);
-}
-
-nrex::~nrex()
-{
- if (_root)
- {
- NREX_DELETE(_root);
- }
-}
-
-bool nrex::valid() const
-{
- return (_root != NULL);
-}
-
-void nrex::reset()
-{
- _capturing = 0;
- _lookahead_depth = 0;
- if (_root)
- {
- NREX_DELETE(_root);
- }
- _root = NULL;
-}
-
-int nrex::capture_size() const
-{
- if (_root)
- {
- return _capturing + 1;
- }
- return 0;
-}
-
-bool nrex::compile(const nrex_char* pattern, int captures)
-{
- reset();
- nrex_node_group* root = NREX_NEW(nrex_node_group(nrex_group_capture, _capturing));
- nrex_array<nrex_node_group*> stack;
- stack.push(root);
- unsigned int lookahead_level = 0;
- _root = root;
-
- for (const nrex_char* c = pattern; c[0] != '\0'; ++c)
- {
- if (c[0] == '(')
- {
- if (c[1] == '?')
- {
- if (c[2] == ':')
- {
- c = &c[2];
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_non_capture));
- stack.top()->add_child(group);
- stack.push(group);
- }
- else if (c[2] == '!' || c[2] == '=')
- {
- c = &c[2];
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_look_ahead, lookahead_level++));
- group->negate = (c[0] == '!');
- stack.top()->add_child(group);
- stack.push(group);
- if (lookahead_level > _lookahead_depth)
- {
- _lookahead_depth = lookahead_level;
- }
- }
- else if (c[2] == '<' && (c[3] == '!' || c[3] == '='))
- {
- c = &c[3];
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_look_behind));
- group->negate = (c[0] == '!');
- stack.top()->add_child(group);
- stack.push(group);
- }
- else
- {
- NREX_COMPILE_ERROR("unrecognised qualifier for group");
- }
- }
- else if (captures >= 0 && _capturing < captures)
- {
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_capture, ++_capturing));
- stack.top()->add_child(group);
- stack.push(group);
- }
- else
- {
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_non_capture));
- stack.top()->add_child(group);
- stack.push(group);
- }
- }
- else if (c[0] == ')')
- {
- if (stack.size() > 1)
- {
- if (stack.top()->type == nrex_group_look_ahead)
- {
- --lookahead_level;
- }
- stack.pop();
- }
- else
- {
- NREX_COMPILE_ERROR("unexpected ')'");
- }
- }
- else if (c[0] == '[')
- {
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_bracket));
- stack.top()->add_child(group);
- if (c[1] == '^')
- {
- group->negate = true;
- ++c;
- }
- bool first_child = true;
- nrex_char previous_child;
- bool previous_child_single = false;
- while (true)
- {
- group->add_childset();
- ++c;
- if (c[0] == '\0')
- {
- NREX_COMPILE_ERROR("unclosed bracket expression '['");
- }
- if (c[0] == '[' && c[1] == ':')
- {
- const nrex_char* d = &c[2];
- nrex_class_type cls = nrex_parse_class(&d);
- if (cls != nrex_class_none)
- {
- c = d;
- group->add_child(NREX_NEW(nrex_node_class(cls)));
- previous_child_single = false;
- }
- else
- {
- group->add_child(NREX_NEW(nrex_node_char('[')));
- previous_child = '[';
- previous_child_single = true;
- }
- }
- else if (c[0] == ']' && !first_child)
- {
- break;
- }
- else if (c[0] == '\\')
- {
- if (nrex_is_shorthand(c[1]))
- {
- group->add_child(NREX_NEW(nrex_node_shorthand(c[1])));
- ++c;
- previous_child_single = false;
- }
- else
- {
- const nrex_char* d = c;
- nrex_char unescaped = nrex_unescape(d);
- if (c == d)
- {
- NREX_COMPILE_ERROR("invalid escape token");
- }
- group->add_child(NREX_NEW(nrex_node_char(unescaped)));
- c = d;
- previous_child = unescaped;
- previous_child_single = true;
- }
- }
- else if (previous_child_single && c[0] == '-')
- {
- bool is_range = false;
- nrex_char next;
- if (c[1] != '\0' && c[1] != ']')
- {
- if (c[1] == '\\')
- {
- const nrex_char* d = ++c;
- next = nrex_unescape(d);
- if (c == d)
- {
- NREX_COMPILE_ERROR("invalid escape token in range");
- }
- }
- else
- {
- next = c[1];
- ++c;
- }
- is_range = true;
- }
- if (is_range)
- {
- if (next < previous_child)
- {
- NREX_COMPILE_ERROR("text range out of order");
- }
- group->pop_back();
- group->add_child(NREX_NEW(nrex_node_range(previous_child, next)));
- previous_child_single = false;
- }
- else
- {
- group->add_child(NREX_NEW(nrex_node_char(c[0])));
- previous_child = c[0];
- previous_child_single = true;
- }
- }
- else
- {
- group->add_child(NREX_NEW(nrex_node_char(c[0])));
- previous_child = c[0];
- previous_child_single = true;
- }
- first_child = false;
- }
- }
- else if (nrex_is_quantifier(c[0]))
- {
- int min = 0;
- int max = -1;
- bool valid_quantifier = true;
- if (c[0] == '?')
- {
- min = 0;
- max = 1;
- }
- else if (c[0] == '+')
- {
- min = 1;
- max = -1;
- }
- else if (c[0] == '*')
- {
- min = 0;
- max = -1;
- }
- else if (c[0] == '{')
- {
- bool max_set = false;
- const nrex_char* d = c;
- while (true)
- {
- ++d;
- if (d[0] == '\0')
- {
- valid_quantifier = false;
- break;
- }
- else if (d[0] == '}')
- {
- break;
- }
- else if (d[0] == ',')
- {
- max_set = true;
- continue;
- }
- else if (d[0] < '0' || '9' < d[0])
- {
- valid_quantifier = false;
- break;
- }
- if (max_set)
- {
- if (max < 0)
- {
- max = int(d[0] - '0');
- }
- else
- {
- max = max * 10 + int(d[0] - '0');
- }
- }
- else
- {
- min = min * 10 + int(d[0] - '0');
- }
- }
- if (!max_set)
- {
- max = min;
- }
- if (valid_quantifier)
- {
- c = d;
- }
- }
- if (valid_quantifier)
- {
- if (stack.top()->back == NULL || !stack.top()->back->quantifiable)
- {
- NREX_COMPILE_ERROR("element not quantifiable");
- }
- nrex_node_quantifier* quant = NREX_NEW(nrex_node_quantifier(min, max));
- if (min == max)
- {
- if (stack.top()->back->length >= 0)
- {
- quant->length = max * stack.top()->back->length;
- }
- }
- else
- {
- if (nrex_has_lookbehind(stack))
- {
- NREX_COMPILE_ERROR("variable length quantifiers inside lookbehind not supported");
- }
- }
- quant->child = stack.top()->swap_back(quant);
- quant->child->previous = NULL;
- quant->child->next = NULL;
- quant->child->parent = quant;
- if (c[1] == '?')
- {
- quant->greedy = false;
- ++c;
- }
- }
- else
- {
- stack.top()->add_child(NREX_NEW(nrex_node_char(c[0])));
- }
- }
- else if (c[0] == '|')
- {
- if (nrex_has_lookbehind(stack))
- {
- NREX_COMPILE_ERROR("alternations inside lookbehind not supported");
- }
- stack.top()->add_childset();
- }
- else if (c[0] == '^' || c[0] == '$')
- {
- stack.top()->add_child(NREX_NEW(nrex_node_anchor((c[0] == '$'))));
- }
- else if (c[0] == '.')
- {
- stack.top()->add_child(NREX_NEW(nrex_node_shorthand('.')));
- }
- else if (c[0] == '\\')
- {
- if (nrex_is_shorthand(c[1]))
- {
- stack.top()->add_child(NREX_NEW(nrex_node_shorthand(c[1])));
- ++c;
- }
- else if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{'))
- {
- int ref = 0;
- bool unclosed = false;
- if (c[1] == 'g')
- {
- unclosed = true;
- c = &c[2];
- }
- while ('0' <= c[1] && c[1] <= '9')
- {
- ref = ref * 10 + int(c[1] - '0');
- ++c;
- }
- if (c[1] == '}')
- {
- unclosed = false;
- ++c;
- }
- if (ref > _capturing || ref <= 0 || unclosed)
- {
- NREX_COMPILE_ERROR("backreference to non-existent capture");
- }
- if (nrex_has_lookbehind(stack))
- {
- NREX_COMPILE_ERROR("backreferences inside lookbehind not supported");
- }
- stack.top()->add_child(NREX_NEW(nrex_node_backreference(ref)));
- }
- else if (c[1] == 'b' || c[1] == 'B')
- {
- stack.top()->add_child(NREX_NEW(nrex_node_word_boundary(c[1] == 'B')));
- ++c;
- }
- else
- {
- const nrex_char* d = c;
- nrex_char unescaped = nrex_unescape(d);
- if (c == d)
- {
- NREX_COMPILE_ERROR("invalid escape token");
- }
- stack.top()->add_child(NREX_NEW(nrex_node_char(unescaped)));
- c = d;
- }
- }
- else
- {
- stack.top()->add_child(NREX_NEW(nrex_node_char(c[0])));
- }
- }
- if (stack.size() > 1)
- {
- NREX_COMPILE_ERROR("unclosed group '('");
- }
- return true;
-}
-
-bool nrex::match(const nrex_char* str, nrex_result* captures, int offset, int end) const
-{
- if (!_root)
- {
- return false;
- }
- nrex_search s(str, captures, _lookahead_depth);
- if (end >= offset)
- {
- s.end = end;
- }
- else
- {
- s.end = NREX_STRLEN(str);
- }
- for (int i = offset; i <= s.end; ++i)
- {
- for (int c = 0; c <= _capturing; ++c)
- {
- captures[c].start = 0;
- captures[c].length = 0;
- }
- if (_root->test(&s, i) >= 0)
- {
- return true;
- }
- }
- return false;
-}
diff --git a/drivers/nrex/nrex.hpp b/drivers/nrex/nrex.hpp
deleted file mode 100644
index d30b7d0102..0000000000
--- a/drivers/nrex/nrex.hpp
+++ /dev/null
@@ -1,176 +0,0 @@
-// NREX: Node RegEx
-// Version 0.2
-//
-// Copyright (c) 2015-2016, Zher Huei Lee
-// All rights reserved.
-//
-// This software is provided 'as-is', without any express or implied
-// warranty. In no event will the authors be held liable for any damages
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would
-// be appreciated but is not required.
-//
-// 2. Altered source versions must be plainly marked as such, and must not
-// be misrepresented as being the original software.
-//
-// 3. This notice may not be removed or altered from any source
-// distribution.
-//
-
-#ifndef NREX_HPP
-#define NREX_HPP
-
-#include "nrex_config.h"
-
-#ifdef NREX_UNICODE
-typedef wchar_t nrex_char;
-#else
-typedef char nrex_char;
-#endif
-
-/*!
- * \brief Struct to contain the range of a capture result
- *
- * The range provided is relative to the begining of the searched string.
- *
- * \see nrex_node::match()
- */
-struct nrex_result
-{
- public:
- int start; /*!< Start of text range */
- int length; /*!< Length of text range */
-};
-
-class nrex_node;
-
-/*!
- * \brief Holds the compiled regex pattern
- */
-class nrex
-{
- private:
- unsigned int _capturing;
- unsigned int _lookahead_depth;
- nrex_node* _root;
- public:
-
- /*!
- * \brief Initialises an empty regex container
- */
- nrex();
-
- /*!
- * \brief Initialises and compiles the regex pattern
- *
- * This calls nrex::compile() with the same arguments. To check whether
- * the compilation was successfull, use nrex::valid().
- *
- * If the NREX_THROW_ERROR was defined it would automatically throw a
- * runtime error nrex_compile_error if it encounters a problem when
- * parsing the pattern.
- *
- * \param pattern The regex pattern
- * \param captures The maximum number of capture groups to allow. Any
- * extra would be converted to non-capturing groups.
- * If negative, no limit would be imposed. Defaults
- * to 9.
- *
- * \see nrex::compile()
- */
- nrex(const nrex_char* pattern, int captures = 9);
-
- ~nrex();
-
- /*!
- * \brief Removes the compiled regex and frees up the memory
- */
- void reset();
-
- /*!
- * \brief Checks if there is a compiled regex being stored
- * \return True if present, False if not present
- */
- bool valid() const;
-
- /*!
- * \brief Provides number of captures the compiled regex uses
- *
- * This is used to provide the array size of the captures needed for
- * nrex::match() to work. The size is actually the number of capture
- * groups + one for the matching of the entire pattern. This can be
- * capped using the extra argument given in nrex::compile()
- * (default 10).
- *
- * \return The number of captures
- */
- int capture_size() const;
-
- /*!
- * \brief Compiles the provided regex pattern
- *
- * This automatically removes the existing compiled regex if already
- * present.
- *
- * If the NREX_THROW_ERROR was defined it would automatically throw a
- * runtime error nrex_compile_error if it encounters a problem when
- * parsing the pattern.
- *
- * \param pattern The regex pattern
- * \param captures The maximum number of capture groups to allow. Any
- * extra would be converted to non-capturing groups.
- * If negative, no limit would be imposed. Defaults
- * to 9.
- * \return True if the pattern was succesfully compiled
- */
- bool compile(const nrex_char* pattern, int captures = 9);
-
- /*!
- * \brief Uses the pattern to search through the provided string
- * \param str The text to search through. It only needs to be
- * null terminated if the end point is not provided.
- * This also determines the starting anchor.
- * \param captures The array of results to store the capture results.
- * The size of that array needs to be the same as the
- * size given in nrex::capture_size(). As it matches
- * the function fills the array with the results. 0 is
- * the result for the entire pattern, 1 and above
- * corresponds to the regex capture group if present.
- * \param offset The starting point of the search. This does not move
- * the starting anchor. Defaults to 0.
- * \param end The end point of the search. This also determines
- * the ending anchor. If a number less than the offset
- * is provided, the search would be done until null
- * termination. Defaults to -1.
- * \return True if a match was found. False otherwise.
- */
- bool match(const nrex_char* str, nrex_result* captures, int offset = 0, int end = -1) const;
-};
-
-#ifdef NREX_THROW_ERROR
-
-#include <stdexcept>
-
-class nrex_compile_error : std::runtime_error
-{
- public:
- nrex_compile_error(const char* message)
- : std::runtime_error(message)
- {
- }
-
- ~nrex_compile_error() throw()
- {
- }
-};
-
-#endif
-
-#endif // NREX_HPP
diff --git a/drivers/nrex/nrex_config.h b/drivers/nrex/nrex_config.h
deleted file mode 100644
index 540f34f8b4..0000000000
--- a/drivers/nrex/nrex_config.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Godot-specific configuration
-// To use this, replace nrex_config.h
-
-#include "core/os/memory.h"
-
-#define NREX_UNICODE
-//#define NREX_THROW_ERROR
-
-#define NREX_NEW(X) memnew(X)
-#define NREX_NEW_ARRAY(X, N) memnew_arr(X, N)
-#define NREX_DELETE(X) memdelete(X)
-#define NREX_DELETE_ARRAY(X) memdelete_arr(X)
diff --git a/drivers/nrex/regex.cpp b/drivers/nrex/regex.cpp
deleted file mode 100644
index 7bf14d14ad..0000000000
--- a/drivers/nrex/regex.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*************************************************************************/
-/* regex.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* http://www.godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#include "regex.h"
-#include "nrex.hpp"
-#include "core/os/memory.h"
-
-void RegEx::_bind_methods() {
-
- ObjectTypeDB::bind_method(_MD("compile","pattern", "capture"),&RegEx::compile, DEFVAL(9));
- ObjectTypeDB::bind_method(_MD("find","text","start","end"),&RegEx::find, DEFVAL(0), DEFVAL(-1));
- ObjectTypeDB::bind_method(_MD("clear"),&RegEx::clear);
- ObjectTypeDB::bind_method(_MD("is_valid"),&RegEx::is_valid);
- ObjectTypeDB::bind_method(_MD("get_capture_count"),&RegEx::get_capture_count);
- ObjectTypeDB::bind_method(_MD("get_capture","capture"),&RegEx::get_capture);
- ObjectTypeDB::bind_method(_MD("get_capture_start","capture"),&RegEx::get_capture_start);
- ObjectTypeDB::bind_method(_MD("get_captures"),&RegEx::_bind_get_captures);
-
-};
-
-StringArray RegEx::_bind_get_captures() const {
-
- StringArray ret;
- int count = get_capture_count();
- for (int i=0; i<count; i++) {
-
- String c = get_capture(i);
- ret.push_back(c);
- };
-
- return ret;
-
-};
-
-void RegEx::clear() {
-
- text.clear();
- captures.clear();
- exp.reset();
-
-};
-
-bool RegEx::is_valid() const {
-
- return exp.valid();
-
-};
-
-int RegEx::get_capture_count() const {
-
- ERR_FAIL_COND_V( !exp.valid(), 0 );
-
- return exp.capture_size();
-}
-
-String RegEx::get_capture(int capture) const {
-
- ERR_FAIL_COND_V( get_capture_count() <= capture, String() );
-
- return text.substr(captures[capture].start, captures[capture].length);
-
-}
-
-int RegEx::get_capture_start(int capture) const {
-
- ERR_FAIL_COND_V( get_capture_count() <= capture, -1 );
-
- return captures[capture].start;
-
-}
-
-Error RegEx::compile(const String& p_pattern, int capture) {
-
- clear();
-
- exp.compile(p_pattern.c_str(), capture);
-
- ERR_FAIL_COND_V( !exp.valid(), FAILED );
-
- captures.resize(exp.capture_size());
-
- return OK;
-
-};
-
-int RegEx::find(const String& p_text, int p_start, int p_end) const {
-
- ERR_FAIL_COND_V( !exp.valid(), -1 );
- ERR_FAIL_COND_V( p_text.length() < p_start, -1 );
- ERR_FAIL_COND_V( p_text.length() < p_end, -1 );
-
- bool res = exp.match(p_text.c_str(), &captures[0], p_start, p_end);
-
- if (res) {
- text = p_text;
- return captures[0].start;
- }
- text.clear();
- return -1;
-
-};
-
-RegEx::RegEx(const String& p_pattern) {
-
- compile(p_pattern);
-
-};
-
-RegEx::RegEx() {
-
-};
-
-RegEx::~RegEx() {
-
- clear();
-
-};
diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp
index 7486d4f217..e0f35e81c4 100644
--- a/drivers/register_driver_types.cpp
+++ b/drivers/register_driver_types.cpp
@@ -40,8 +40,6 @@
#include "platform/windows/export/export.h"
#endif
-#include "drivers/nrex/regex.h"
-
static ImageLoaderPNG *image_loader_png=NULL;
static ResourceSaverPNG *resource_saver_png=NULL;
@@ -53,8 +51,6 @@ void register_core_driver_types() {
resource_saver_png = memnew( ResourceSaverPNG );
ResourceSaver::add_resource_format_saver(resource_saver_png);
-
- ObjectTypeDB::register_type<RegEx>();
}
void unregister_core_driver_types() {
diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp
index 08a8c19583..d6d1be3395 100644
--- a/drivers/unix/ip_unix.cpp
+++ b/drivers/unix/ip_unix.cpp
@@ -30,6 +30,7 @@
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
+#include <string.h>
#ifdef WINDOWS_ENABLED
#ifdef WINRT_ENABLED
@@ -62,16 +63,51 @@
#endif
#endif
-IP_Address IP_Unix::_resolve_hostname(const String& p_hostname) {
+static IP_Address _sockaddr2ip(struct sockaddr* p_addr) {
- struct hostent *he;
- if ((he=gethostbyname(p_hostname.utf8().get_data())) == NULL) { // get the host info
- ERR_PRINT("gethostbyname failed!");
- return IP_Address();
- }
IP_Address ip;
+ if (p_addr->sa_family == AF_INET) {
+ struct sockaddr_in* addr = (struct sockaddr_in*)p_addr;
+ ip.field32[0] = *((unsigned long*)&addr->sin_addr);
+ ip.type = IP_Address::TYPE_IPV4;
+ } else {
+ struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+ for (int i=0; i<16; i++)
+ ip.field8[i] = addr6->sin6_addr.s6_addr[i];
+ ip.type = IP_Address::TYPE_IPV6;
+ };
+
+ return ip;
+};
+
+IP_Address IP_Unix::_resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type) {
- ip.host= *((unsigned long*)he->h_addr);
+ struct addrinfo hints;
+ struct addrinfo* result;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ if (p_type == IP_Address::TYPE_IPV4) {
+ hints.ai_family = AF_INET;
+ } else if (p_type == IP_Address::TYPE_IPV6) {
+ hints.ai_family = AF_INET6;
+ } else {
+ hints.ai_family = AF_UNSPEC;
+ };
+
+ int s = getaddrinfo(p_hostname.utf8().get_data(), NULL, &hints, &result);
+ if (s != 0) {
+ ERR_PRINT("getaddrinfo failed!");
+ return IP_Address();
+ };
+
+ if (result == NULL || result->ai_addr == NULL) {
+ ERR_PRINT("Invalid response from getaddrinfo");
+ return IP_Address();
+ };
+
+ IP_Address ip = _sockaddr2ip(result->ai_addr);
+
+ freeaddrinfo(result);
return ip;
@@ -90,10 +126,9 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
for (int i = 0; i < hostnames->Size; i++) {
- if (hostnames->GetAt(i)->Type == HostNameType::Ipv4 && hostnames->GetAt(i)->IPInformation != nullptr) {
+ if (hostnames->GetAt(i)->Type == HostNameType::Ipv4 || hostnames->GetAt(i)->Type == HostNameType::Ipv6 && hostnames->GetAt(i)->IPInformation != nullptr) {
r_addresses->push_back(IP_Address(String(hostnames->GetAt(i)->CanonicalName->Data())));
-
}
}
@@ -108,7 +143,7 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
while (true) {
addrs = (IP_ADAPTER_ADDRESSES*)memalloc(buf_size);
- int err = GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_ANYCAST |
+ int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST |
GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER |
GAA_FLAG_SKIP_FRIENDLY_NAME,
@@ -134,14 +169,23 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
while (address != NULL) {
- char addr_chr[INET_ADDRSTRLEN];
- SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
-
IP_Address ip;
- ip.host= *((unsigned long*)&ipv4->sin_addr);
+ if (address->Address.lpSockaddr->sa_family == AF_INET) {
+
+ SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
+
+ ip.field32[0] = *((unsigned long*)&ipv4->sin_addr);
+ ip.type = IP_Address::TYPE_IPV4;
+ } else { // ipv6
+
+ SOCKADDR_IN6* ipv6 = reinterpret_cast<SOCKADDR_IN6*>(address->Address.lpSockaddr);
+ for (int i=0; i<16; i++) {
+ ip.field8[i] = ipv6->sin6_addr.s6_addr[i];
+ };
+ ip.type = IP_Address::TYPE_IPV6;
+ };
- //inet_ntop(AF_INET, &ipv4->sin_addr, addr_chr, INET_ADDRSTRLEN);
r_addresses->push_back(ip);
@@ -167,20 +211,9 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr)
continue;
- if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4
- // is a valid IP4 Address
- IP_Address ip;
- ip.host= *((unsigned long*)&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr);
-
- r_addresses->push_back(ip);
- }/* else if (ifa->ifa_addr->sa_family==AF_INET6) { // check it is IP6
- // is a valid IP6 Address
- tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
- char addressBuffer[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
- printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
- } */
+ IP_Address ip = _sockaddr2ip(ifa->ifa_addr);
+ r_addresses->push_back(ip);
}
if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h
index c0034c9a3a..d198a330e7 100644
--- a/drivers/unix/ip_unix.h
+++ b/drivers/unix/ip_unix.h
@@ -36,7 +36,7 @@
class IP_Unix : public IP {
OBJ_TYPE(IP_Unix, IP);
- virtual IP_Address _resolve_hostname(const String& p_hostname);
+ virtual IP_Address _resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type);
static IP* _create_unix();
public:
diff --git a/drivers/unix/packet_peer_udp_posix.cpp b/drivers/unix/packet_peer_udp_posix.cpp
index 0201a85651..0b172b6a51 100644
--- a/drivers/unix/packet_peer_udp_posix.cpp
+++ b/drivers/unix/packet_peer_udp_posix.cpp
@@ -54,6 +54,7 @@
#include <arpa/inet.h>
#endif
+#include "drivers/unix/socket_helpers.h"
int PacketPeerUDPPosix::get_available_packet_count() const {
@@ -73,7 +74,15 @@ Error PacketPeerUDPPosix::get_packet(const uint8_t **r_buffer,int &r_buffer_size
return ERR_UNAVAILABLE;
uint32_t size;
- rb.read((uint8_t*)&packet_ip.host,4,true);
+ uint8_t type;
+ rb.read(&type, 1, true);
+ if (type == IP_Address::TYPE_IPV4) {
+ rb.read((uint8_t*)&packet_ip.field8,4,true);
+ packet_ip.type = IP_Address::TYPE_IPV4;
+ } else {
+ rb.read((uint8_t*)&packet_ip.field8,16,true);
+ packet_ip.type = IP_Address::TYPE_IPV6;
+ };
rb.read((uint8_t*)&packet_port,4,true);
rb.read((uint8_t*)&size,4,true);
rb.read(packet_buffer,size,true);
@@ -85,17 +94,17 @@ Error PacketPeerUDPPosix::get_packet(const uint8_t **r_buffer,int &r_buffer_size
}
Error PacketPeerUDPPosix::put_packet(const uint8_t *p_buffer,int p_buffer_size){
- int sock = _get_socket();
+ ERR_FAIL_COND_V(peer_addr.type == IP_Address::TYPE_NONE, ERR_UNCONFIGURED);
+
+ int sock = _get_socket(peer_addr.type);
ERR_FAIL_COND_V( sock == -1, FAILED );
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(peer_port);
- addr.sin_addr = *((struct in_addr*)&peer_addr.host);
+ struct sockaddr_storage addr;
+ size_t addr_size = _set_sockaddr(&addr, peer_addr, peer_port);
errno = 0;
int err;
- while ( (err = sendto(sock, p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, sizeof(addr))) != p_buffer_size) {
+ while ( (err = sendto(sock, p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, addr_size)) != p_buffer_size) {
if (errno != EAGAIN) {
return FAILED;
@@ -110,17 +119,19 @@ int PacketPeerUDPPosix::get_max_packet_size() const{
return 512; // uhm maybe not
}
-Error PacketPeerUDPPosix::listen(int p_port, int p_recv_buffer_size){
+Error PacketPeerUDPPosix::listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size) {
+
+ ERR_FAIL_COND_V(p_address_type != IP_Address::TYPE_IPV4 && p_address_type != IP_Address::TYPE_IPV6, ERR_INVALID_PARAMETER);
close();
- int sock = _get_socket();
+ int sock = _get_socket(p_address_type);
if (sock == -1 )
return ERR_CANT_CREATE;
- sockaddr_in addr = {0};
- addr.sin_family = AF_INET;
- addr.sin_port = htons(p_port);
- addr.sin_addr.s_addr = INADDR_ANY;
- if (bind(sock, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == -1 ) {
+
+ sockaddr_storage addr = {0};
+ size_t addr_size = _set_listen_sockaddr(&addr, p_port, p_address_type, NULL);
+
+ if (bind(sock, (struct sockaddr*)&addr, addr_size) == -1 ) {
close();
return ERR_UNAVAILABLE;
}
@@ -145,16 +156,41 @@ Error PacketPeerUDPPosix::wait() {
Error PacketPeerUDPPosix::_poll(bool p_wait) {
- struct sockaddr_in from = {0};
- socklen_t len = sizeof(struct sockaddr_in);
+ struct sockaddr_storage from = {0};
+ socklen_t len = sizeof(struct sockaddr_storage);
int ret;
while ( (ret = recvfrom(sockfd, recv_buffer, MIN((int)sizeof(recv_buffer),MAX(rb.space_left()-12, 0)), p_wait?0:MSG_DONTWAIT, (struct sockaddr*)&from, &len)) > 0) {
- rb.write((uint8_t*)&from.sin_addr, 4);
- uint32_t port = ntohs(from.sin_port);
+
+ uint32_t port = 0;
+
+ if (from.ss_family == AF_INET) {
+ uint8_t type = (uint8_t)IP_Address::TYPE_IPV4;
+ rb.write(&type, 1);
+ struct sockaddr_in* sin_from = (struct sockaddr_in*)&from;
+ rb.write((uint8_t*)&sin_from->sin_addr, 4);
+ port = ntohs(sin_from->sin_port);
+
+ } else if (from.ss_family == AF_INET6) {
+
+ uint8_t type = (uint8_t)IP_Address::TYPE_IPV6;
+ rb.write(&type, 1);
+
+ struct sockaddr_in6* s6_from = (struct sockaddr_in6*)&from;
+ rb.write((uint8_t*)&s6_from->sin6_addr, 16);
+
+ port = ntohs(s6_from->sin6_port);
+
+ } else {
+ // WARN_PRINT("Ignoring packet with unknown address family");
+ uint8_t type = (uint8_t)IP_Address::TYPE_NONE;
+ rb.write(&type, 1);
+ };
+
rb.write((uint8_t*)&port, 4);
rb.write((uint8_t*)&ret, 4);
rb.write(recv_buffer, ret);
- len = sizeof(struct sockaddr_in);
+
+ len = sizeof(struct sockaddr_storage);
++queue_count;
};
@@ -182,12 +218,14 @@ int PacketPeerUDPPosix::get_packet_port() const{
return packet_port;
}
-int PacketPeerUDPPosix::_get_socket() {
+int PacketPeerUDPPosix::_get_socket(IP_Address::AddrType p_type) {
if (sockfd != -1)
return sockfd;
- sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ int family = p_type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+
+ sockfd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
ERR_FAIL_COND_V( sockfd == -1, -1 );
//fcntl(sockfd, F_SETFL, O_NONBLOCK);
diff --git a/drivers/unix/packet_peer_udp_posix.h b/drivers/unix/packet_peer_udp_posix.h
index a11282e5d6..13b6969e53 100644
--- a/drivers/unix/packet_peer_udp_posix.h
+++ b/drivers/unix/packet_peer_udp_posix.h
@@ -44,15 +44,15 @@ class PacketPeerUDPPosix : public PacketPeerUDP {
mutable RingBuffer<uint8_t> rb;
uint8_t recv_buffer[PACKET_BUFFER_SIZE];
mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- IP_Address packet_ip;
- int packet_port;
+ mutable IP_Address packet_ip;
+ mutable int packet_port;
mutable int queue_count;
int sockfd;
IP_Address peer_addr;
int peer_port;
- _FORCE_INLINE_ int _get_socket();
+ _FORCE_INLINE_ int _get_socket(IP_Address::AddrType p_type);
static PacketPeerUDP* _create();
virtual Error _poll(bool p_block);
@@ -65,7 +65,7 @@ public:
virtual int get_max_packet_size() const;
- virtual Error listen(int p_port,int p_recv_buffer_size=65536);
+ virtual Error listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size=65536);
virtual void close();
virtual Error wait();
virtual bool is_listening() const;
diff --git a/drivers/unix/socket_helpers.h b/drivers/unix/socket_helpers.h
new file mode 100644
index 0000000000..4ee40e3d3d
--- /dev/null
+++ b/drivers/unix/socket_helpers.h
@@ -0,0 +1,70 @@
+#ifndef SOCKET_HELPERS_H
+#define SOCKET_HELPERS_H
+
+#include <string.h>
+
+// helpers for sockaddr -> IP_Address and back, should work for posix and winsock. All implementations should use this
+
+static size_t _set_sockaddr(struct sockaddr_storage* p_addr, const IP_Address& p_ip, int p_port) {
+
+ memset(p_addr, 0, sizeof(struct sockaddr_storage));
+ if (p_ip.type == IP_Address::TYPE_IPV6) {
+
+ struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = htons(p_port);
+ copymem(&addr6->sin6_addr.s6_addr, p_ip.field8, 16);
+ return sizeof(sockaddr_in6);
+
+ } else {
+
+ struct sockaddr_in* addr4 = (struct sockaddr_in*)p_addr;
+ addr4->sin_family = AF_INET; // host byte order
+ addr4->sin_port = htons(p_port); // short, network byte order
+ addr4->sin_addr = *((struct in_addr*)&p_ip.field32[0]);
+ return sizeof(sockaddr_in);
+ };
+};
+
+static size_t _set_listen_sockaddr(struct sockaddr_storage* p_addr, int p_port, IP_Address::AddrType p_address_type, const List<String> *p_accepted_hosts) {
+
+ memset(p_addr, 0, sizeof(struct sockaddr_storage));
+ if (p_address_type == IP_Address::TYPE_IPV4) {
+ struct sockaddr_in* addr4 = (struct sockaddr_in*)p_addr;
+ addr4->sin_family = AF_INET;
+ addr4->sin_port = htons(p_port);
+ addr4->sin_addr.s_addr = INADDR_ANY; // TODO: use accepted hosts list
+ return sizeof(sockaddr_in);
+ } else {
+ struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = htons(p_port);
+ addr6->sin6_addr = in6addr_any; // TODO: use accepted hosts list
+ return sizeof(sockaddr_in6);
+ };
+};
+
+static void _set_ip_addr_port(IP_Address& r_ip, int& r_port, struct sockaddr_storage* p_addr) {
+
+ if (p_addr->ss_family == AF_INET) {
+ r_ip.type = IP_Address::TYPE_IPV4;
+
+ struct sockaddr_in* addr4 = (struct sockaddr_in*)p_addr;
+ r_ip.field32[0] = (uint32_t)addr4->sin_addr.s_addr;
+
+ r_port = ntohs(addr4->sin_port);
+
+ } else if (p_addr->ss_family == AF_INET6) {
+
+ r_ip.type = IP_Address::TYPE_IPV6;
+
+ struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+ copymem(&addr6->sin6_addr.s6_addr, r_ip.field8, 16);
+
+ r_port = ntohs(addr6->sin6_port);
+ };
+};
+
+
+#endif
diff --git a/drivers/unix/stream_peer_tcp_posix.cpp b/drivers/unix/stream_peer_tcp_posix.cpp
index 45a4b934c1..f2915b0a16 100644
--- a/drivers/unix/stream_peer_tcp_posix.cpp
+++ b/drivers/unix/stream_peer_tcp_posix.cpp
@@ -61,13 +61,7 @@
#define MSG_NOSIGNAL SO_NOSIGPIPE
#endif
-static void set_addr_in(struct sockaddr_in& their_addr, const IP_Address& p_host, uint16_t p_port) {
-
- their_addr.sin_family = AF_INET; // host byte order
- their_addr.sin_port = htons(p_port); // short, network byte order
- their_addr.sin_addr = *((struct in_addr*)&p_host.host);
- memset(&(their_addr.sin_zero), '\0', 8);
-};
+#include "drivers/unix/socket_helpers.h"
StreamPeerTCP* StreamPeerTCPPosix::_create() {
@@ -103,9 +97,10 @@ Error StreamPeerTCPPosix::_poll_connection(bool p_block) const {
_block(sockfd, false, true);
};
- struct sockaddr_in their_addr;
- set_addr_in(their_addr, peer_host, peer_port);
- if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1) {
+ struct sockaddr_storage their_addr;
+ size_t addr_size = _set_sockaddr(&their_addr, peer_host, peer_port);
+
+ if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == -1) {
if (errno == EISCONN) {
status = STATUS_CONNECTED;
@@ -140,9 +135,10 @@ void StreamPeerTCPPosix::set_socket(int p_sockfd, IP_Address p_host, int p_port)
Error StreamPeerTCPPosix::connect(const IP_Address& p_host, uint16_t p_port) {
- ERR_FAIL_COND_V( p_host.host == 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V( p_host.type == IP_Address::TYPE_NONE, ERR_INVALID_PARAMETER);
- if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ int family = p_host.type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+ if ((sockfd = socket(family, SOCK_STREAM, 0)) == -1) {
ERR_PRINT("Socket creation failed!");
disconnect();
//perror("socket");
@@ -156,11 +152,11 @@ Error StreamPeerTCPPosix::connect(const IP_Address& p_host, uint16_t p_port) {
ioctl(sockfd, FIONBIO, &bval);
#endif
- struct sockaddr_in their_addr;
- set_addr_in(their_addr, p_host, p_port);
+ struct sockaddr_storage their_addr;
+ size_t addr_size = _set_sockaddr(&their_addr, p_host, p_port);
errno = 0;
- if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1 && errno != EINPROGRESS) {
+ if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == -1 && errno != EINPROGRESS) {
ERR_PRINT("Connection to remote host failed!");
disconnect();
diff --git a/drivers/unix/tcp_server_posix.cpp b/drivers/unix/tcp_server_posix.cpp
index c67bb51334..05b739804c 100644
--- a/drivers/unix/tcp_server_posix.cpp
+++ b/drivers/unix/tcp_server_posix.cpp
@@ -55,6 +55,9 @@
#include <netinet/in.h>
#include <sys/socket.h>
#include <assert.h>
+
+#include "drivers/unix/socket_helpers.h"
+
TCP_Server* TCPServerPosix::_create() {
return memnew(TCPServerPosix);
@@ -65,10 +68,11 @@ void TCPServerPosix::make_default() {
TCP_Server::_create = TCPServerPosix::_create;
};
-Error TCPServerPosix::listen(uint16_t p_port,const List<String> *p_accepted_hosts) {
+Error TCPServerPosix::listen(uint16_t p_port, IP_Address::AddrType p_type, const List<String> *p_accepted_hosts) {
int sockfd;
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ int family = p_type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+ sockfd = socket(family, SOCK_STREAM, 0);
ERR_FAIL_COND_V(sockfd == -1, FAILED);
#ifndef NO_FCNTL
fcntl(sockfd, F_SETFL, O_NONBLOCK);
@@ -82,13 +86,12 @@ Error TCPServerPosix::listen(uint16_t p_port,const List<String> *p_accepted_host
WARN_PRINT("REUSEADDR failed!")
}
- struct sockaddr_in my_addr;
- my_addr.sin_family = AF_INET; // host byte order
- my_addr.sin_port = htons(p_port); // short, network byte order
- my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP TODO: use p_accepted_hosts
- memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
+ struct sockaddr_storage addr;
+ size_t addr_size = _set_listen_sockaddr(&addr, p_port, p_type, p_accepted_hosts);
+
+ // automatically fill with my IP TODO: use p_accepted_hosts
- if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) != -1) {
+ if (bind(sockfd, (struct sockaddr *)&addr, addr_size) != -1) {
if (::listen(sockfd, 1) == -1) {
@@ -136,9 +139,9 @@ Ref<StreamPeerTCP> TCPServerPosix::take_connection() {
return Ref<StreamPeerTCP>();
};
- struct sockaddr_in their_addr;
- socklen_t sin_size = sizeof(their_addr);
- int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &sin_size);
+ struct sockaddr_storage their_addr;
+ socklen_t size = sizeof(their_addr);
+ int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &size);
ERR_FAIL_COND_V(fd == -1, Ref<StreamPeerTCP>());
#ifndef NO_FCNTL
fcntl(fd, F_SETFL, O_NONBLOCK);
@@ -149,8 +152,11 @@ Ref<StreamPeerTCP> TCPServerPosix::take_connection() {
Ref<StreamPeerTCPPosix> conn = memnew(StreamPeerTCPPosix);
IP_Address ip;
- ip.host = (uint32_t)their_addr.sin_addr.s_addr;
- conn->set_socket(fd, ip, ntohs(their_addr.sin_port));
+
+ int port;
+ _set_ip_addr_port(ip, port, &their_addr);
+
+ conn->set_socket(fd, ip, port);
return conn;
};
diff --git a/drivers/unix/tcp_server_posix.h b/drivers/unix/tcp_server_posix.h
index 570bcaab12..134ec494cb 100644
--- a/drivers/unix/tcp_server_posix.h
+++ b/drivers/unix/tcp_server_posix.h
@@ -40,7 +40,7 @@ class TCPServerPosix : public TCP_Server {
public:
- virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL);
+ virtual Error listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4, const List<String> *p_accepted_hosts=NULL);
virtual bool is_connection_available() const;
virtual Ref<StreamPeerTCP> take_connection();
diff --git a/methods.py b/methods.py
index 477fe4f12f..bd5409e3d4 100755
--- a/methods.py
+++ b/methods.py
@@ -1516,11 +1516,6 @@ def detect_visual_c_compiler_version(tools_env):
return vc_chosen_compiler_str
-def msvc_is_detected() :
- # looks for VisualStudio env variable
- # or for Visual C++ Build Tools (which is a standalone MSVC)
- return os.getenv("VSINSTALLDIR") or os.getenv("VS100COMNTOOLS") or os.getenv("VS110COMNTOOLS") or os.getenv("VS120COMNTOOLS") or os.getenv("VS140COMNTOOLS");
-
def precious_program(env, program, sources, **args):
program = env.ProgramOriginal(program, sources, **args)
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index b4dfa9c62e..a82283591d 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -77,6 +77,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port, int p_in_bandwidth, int p_out_bandwidth){
ERR_FAIL_COND_V(active,ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(p_ip.type != IP_Address::TYPE_IPV4, ERR_INVALID_PARAMETER);
host = enet_host_create (NULL /* create a client host */,
1 /* only allow 1 outgoing connection */,
@@ -90,7 +91,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port
_setup_compressor();
ENetAddress address;
- address.host=p_ip.host;
+ address.host=p_ip.field32[0];
address.port=p_port;
//enet_address_set_host (& address, "localhost");
@@ -149,7 +150,8 @@ void NetworkedMultiplayerENet::poll(){
}
IP_Address ip;
- ip.host=event.peer -> address.host;
+ ip.type = IP_Address::TYPE_IPV4;
+ ip.field32[0]=event.peer -> address.host;
int *new_id = memnew( int );
*new_id = event.data;
@@ -683,5 +685,6 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet(){
// sets IP for ENet to bind when using create_server
// if no IP is set, then ENet bind to ENET_HOST_ANY
void NetworkedMultiplayerENet::set_bind_ip(const IP_Address& p_ip){
- bind_ip=p_ip.host;
+ ERR_FAIL_COND(p_ip.type != IP_Address::TYPE_IPV4);
+ bind_ip=p_ip.field32[0];
}
diff --git a/modules/openssl/SCsub b/modules/openssl/SCsub
index 3cc6f21bd2..2327cf483c 100644
--- a/modules/openssl/SCsub
+++ b/modules/openssl/SCsub
@@ -671,7 +671,7 @@ if (env["openssl"] != "system"): # builtin
# Workaround for compilation error with GCC/Clang when -Werror is too greedy (GH-4517)
import os
import methods
- if not (os.name=="nt" and methods.msvc_is_detected()): # not Windows and not MSVC
+ if not (os.name=="nt" and os.getenv("VCINSTALLDIR")): # not Windows and not MSVC
env_openssl.Append(CFLAGS = ["-Wno-error=implicit-function-declaration"])
diff --git a/drivers/nrex/SCsub b/modules/regex/SCsub
index ee39fd2631..0882406761 100644
--- a/drivers/nrex/SCsub
+++ b/modules/regex/SCsub
@@ -2,6 +2,6 @@
Import('env')
-env.add_source_files(env.drivers_sources, "*.cpp")
+env.add_source_files(env.modules_sources, "*.cpp")
Export('env')
diff --git a/modules/regex/config.py b/modules/regex/config.py
new file mode 100644
index 0000000000..667b5d8ba6
--- /dev/null
+++ b/modules/regex/config.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
+
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
new file mode 100644
index 0000000000..a0f4b4934c
--- /dev/null
+++ b/modules/regex/regex.cpp
@@ -0,0 +1,1502 @@
+/*************************************************************************/
+/* regex.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "regex.h"
+#include <wctype.h>
+#include <wchar.h>
+
+static int RegEx_hex2int(const CharType c)
+{
+ if ('0' <= c && c <= '9')
+ return int(c - '0');
+ else if ('a' <= c && c <= 'f')
+ return int(c - 'a') + 10;
+ else if ('A' <= c && c <= 'F')
+ return int(c - 'A') + 10;
+ return -1;
+}
+
+struct RegExSearch {
+
+ Ref<RegExMatch> match;
+ const CharType* str;
+ int end;
+ int eof;
+
+ // For standard quantifier behaviour, test_parent is used to check the
+ // rest of the pattern. If the pattern matches, to prevent the parent
+ // from testing again, the complete flag is used as a shortcut out.
+ bool complete;
+
+ // With lookahead, the position needs to rewind to its starting position
+ // when test_parent is used. Due to functional programming, this state
+ // has to be kept as a parameter.
+ Vector<int> lookahead_pos;
+
+ CharType at(int p_pos) {
+ return str[p_pos];
+ }
+
+ RegExSearch(Ref<RegExMatch>& p_match, int p_end, int p_lookahead) : match(p_match) {
+
+ str = p_match->string.c_str();
+ end = p_end;
+ eof = p_match->string.length();
+ complete = false;
+ lookahead_pos.resize(p_lookahead);
+ }
+
+};
+
+struct RegExNode {
+
+ RegExNode* next;
+ RegExNode* previous;
+ RegExNode* parent;
+ bool quantifiable;
+ int length;
+
+ RegExNode() {
+
+ next = NULL;
+ previous = NULL;
+ parent = NULL;
+ quantifiable = false;
+ length = -1;
+ }
+
+ virtual ~RegExNode() {
+
+ if (next)
+ memdelete(next);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ return next ? next->test(s, pos) : -1;
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ if (next)
+ pos = next->test(s, pos);
+
+ if (pos >= 0) {
+ s.complete = true;
+ if (parent)
+ pos = parent->test_parent(s, pos);
+ }
+
+ if (pos < 0)
+ s.complete = false;
+
+ return pos;
+ }
+
+ void increment_length(int amount, bool subtract = false) {
+
+ if (amount >= 0 && length >= 0) {
+ if (!subtract)
+ length += amount;
+ else
+ length -= amount;
+ } else {
+ length = -1;
+ }
+
+ if (parent)
+ parent->increment_length(amount, subtract);
+
+ }
+
+};
+
+struct RegExNodeChar : public RegExNode {
+
+ CharType ch;
+
+ RegExNodeChar(CharType p_char) {
+
+ length = 1;
+ quantifiable = true;
+ ch = p_char;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos || s.at(pos) != ch)
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+
+ static CharType parse_escape(const CharType*& c) {
+
+ int point = 0;
+ switch (c[1]) {
+ case 'x':
+ for (int i = 2; i <= 3; ++i) {
+ int res = RegEx_hex2int(c[i]);
+ if (res == -1)
+ return '\0';
+ point = (point << 4) + res;
+ }
+ c = &c[3];
+ return CharType(point);
+ case 'u':
+ for (int i = 2; i <= 5; ++i) {
+ int res = RegEx_hex2int(c[i]);
+ if (res == -1)
+ return '\0';
+ point = (point << 4) + res;
+ }
+ c = &c[5];
+ return CharType(point);
+ case '0': ++c; return '\0';
+ case 'a': ++c; return '\a';
+ case 'e': ++c; return '\e';
+ case 'f': ++c; return '\f';
+ case 'n': ++c; return '\n';
+ case 'r': ++c; return '\r';
+ case 't': ++c; return '\t';
+ case 'v': ++c; return '\v';
+ case 'b': ++c; return '\b';
+ default: break;
+ }
+ return (++c)[0];
+ }
+};
+
+struct RegExNodeRange : public RegExNode {
+
+ CharType start;
+ CharType end;
+
+ RegExNodeRange(CharType p_start, CharType p_end) {
+
+ length = 1;
+ quantifiable = true;
+ start = p_start;
+ end = p_end;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos)
+ return -1;
+
+ CharType c = s.at(pos);
+ if (c < start || end < c)
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+};
+
+struct RegExNodeShorthand : public RegExNode {
+
+ CharType repr;
+
+ RegExNodeShorthand(CharType p_repr) {
+
+ length = 1;
+ quantifiable = true;
+ repr = p_repr;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos)
+ return -1;
+
+ bool found = false;
+ bool invert = false;
+ CharType c = s.at(pos);
+ switch (repr) {
+ case '.':
+ found = true;
+ break;
+ case 'W':
+ invert = true;
+ case 'w':
+ found = (c == '_' || iswalnum(c) != 0);
+ break;
+ case 'D':
+ invert = true;
+ case 'd':
+ found = ('0' <= c && c <= '9');
+ break;
+ case 'S':
+ invert = true;
+ case 's':
+ found = (iswspace(c) != 0);
+ break;
+ default:
+ break;
+ }
+
+ if (found == invert)
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+};
+
+struct RegExNodeClass : public RegExNode {
+
+ enum Type {
+ Type_none,
+ Type_alnum,
+ Type_alpha,
+ Type_ascii,
+ Type_blank,
+ Type_cntrl,
+ Type_digit,
+ Type_graph,
+ Type_lower,
+ Type_print,
+ Type_punct,
+ Type_space,
+ Type_upper,
+ Type_xdigit,
+ Type_word
+ };
+
+ Type type;
+
+ bool test_class(CharType c) const {
+
+ static Vector<CharType> REGEX_NODE_SPACE = String(" \t\r\n\f");
+ static Vector<CharType> REGEX_NODE_PUNCT = String("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~");
+
+ switch (type) {
+ case Type_alnum:
+ if ('0' <= c && c <= '9') return true;
+ if ('a' <= c && c <= 'z') return true;
+ if ('A' <= c && c <= 'Z') return true;
+ return false;
+ case Type_alpha:
+ if ('a' <= c && c <= 'z') return true;
+ if ('A' <= c && c <= 'Z') return true;
+ return false;
+ case Type_ascii:
+ return (0x00 <= c && c <= 0x7F);
+ case Type_blank:
+ return (c == ' ' || c == '\t');
+ case Type_cntrl:
+ return ((0x00 <= c && c <= 0x1F) || c == 0x7F);
+ case Type_digit:
+ return ('0' <= c && c <= '9');
+ case Type_graph:
+ return (0x20 < c && c < 0x7F);
+ case Type_lower:
+ return ('a' <= c && c <= 'z');
+ case Type_print:
+ return (0x1F < c && c < 0x1F);
+ case Type_punct:
+ return (REGEX_NODE_PUNCT.find(c) >= 0);
+ case Type_space:
+ return (REGEX_NODE_SPACE.find(c) >= 0);
+ case Type_upper:
+ return ('A' <= c && c <= 'Z');
+ case Type_xdigit:
+ if ('0' <= c && c <= '9') return true;
+ if ('a' <= c && c <= 'f') return true;
+ if ('A' <= c && c <= 'F') return true;
+ return false;
+ case Type_word:
+ if ('0' <= c && c <= '9') return true;
+ if ('a' <= c && c <= 'z') return true;
+ if ('A' <= c && c <= 'Z') return true;
+ return (c == '_');
+ default:
+ return false;
+ }
+ return false;
+ }
+
+ RegExNodeClass(Type p_type) {
+
+ length = 1;
+ quantifiable = true;
+ type = p_type;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos)
+ return -1;
+
+ if (!test_class(s.at(pos)))
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+
+#define REGEX_CMP_CLASS(POS, NAME) if (cmp_class(POS, #NAME)) return Type_ ## NAME
+
+ static Type parse_type(const CharType*& p_pos) {
+
+ REGEX_CMP_CLASS(p_pos, alnum);
+ REGEX_CMP_CLASS(p_pos, alpha);
+ REGEX_CMP_CLASS(p_pos, ascii);
+ REGEX_CMP_CLASS(p_pos, blank);
+ REGEX_CMP_CLASS(p_pos, cntrl);
+ REGEX_CMP_CLASS(p_pos, digit);
+ REGEX_CMP_CLASS(p_pos, graph);
+ REGEX_CMP_CLASS(p_pos, lower);
+ REGEX_CMP_CLASS(p_pos, print);
+ REGEX_CMP_CLASS(p_pos, punct);
+ REGEX_CMP_CLASS(p_pos, space);
+ REGEX_CMP_CLASS(p_pos, upper);
+ REGEX_CMP_CLASS(p_pos, xdigit);
+ REGEX_CMP_CLASS(p_pos, word);
+ return Type_none;
+ }
+
+ static bool cmp_class(const CharType*& p_pos, const char* p_text) {
+
+ unsigned int i = 0;
+ for (i = 0; p_text[i] != '\0'; ++i)
+ if (p_pos[i] != p_text[i])
+ return false;
+
+ if (p_pos[i++] != ':' || p_pos[i] != ']')
+ return false;
+
+ p_pos = &p_pos[i];
+ return true;
+ }
+};
+
+struct RegExNodeAnchorStart : public RegExNode {
+
+ RegExNodeAnchorStart() {
+
+ length = 0;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (pos != 0)
+ return -1;
+
+ return next ? next->test(s, pos) : pos;
+ }
+};
+
+struct RegExNodeAnchorEnd : public RegExNode {
+
+ RegExNodeAnchorEnd() {
+
+ length = 0;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (pos != s.eof)
+ return -1;
+
+ return next ? next->test(s, pos) : pos;
+ }
+};
+
+struct RegExNodeWordBoundary : public RegExNode {
+
+ bool inverse;
+
+ RegExNodeWordBoundary(bool p_inverse) {
+
+ length = 0;
+ inverse = p_inverse;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ bool left = false;
+ bool right = false;
+
+ if (pos != 0) {
+ CharType c = s.at(pos - 1);
+ if (c == '_' || iswalnum(c))
+ left = true;
+ }
+
+ if (pos != s.eof) {
+ CharType c = s.at(pos);
+ if (c == '_' || iswalnum(c))
+ right = true;
+ }
+
+ if ((left == right) != inverse)
+ return -1;
+
+ return next ? next->test(s, pos) : pos;
+ }
+};
+
+struct RegExNodeQuantifier : public RegExNode {
+
+ int min;
+ int max;
+ bool greedy;
+ RegExNode* child;
+
+ RegExNodeQuantifier(int p_min, int p_max) {
+
+ min = p_min;
+ max = p_max;
+ greedy = true;
+ child = NULL;
+ }
+
+ ~RegExNodeQuantifier() {
+
+ if (child)
+ memdelete(child);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ return test_step(s, pos, 0, pos);
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ s.complete = false;
+ return pos;
+ }
+
+ int test_step(RegExSearch& s, int pos, int level, int start) const {
+
+ if (pos > s.end)
+ return -1;
+
+ if (!greedy && level > min) {
+ int res = next ? next->test(s, pos) : pos;
+ if (s.complete)
+ return res;
+
+ if (res >= 0 && parent->test_parent(s, res) >= 0)
+ return res;
+ }
+
+ if (max >= 0 && level > max)
+ return -1;
+
+ int res = pos;
+ if (level >= 1) {
+ if (level > min + 1 && pos == start)
+ return -1;
+
+ res = child->test(s, pos);
+ if (s.complete)
+ return res;
+ }
+
+ if (res >= 0) {
+
+ int res_step = test_step(s, res, level + 1, start);
+ if (res_step >= 0)
+ return res_step;
+
+ if (greedy && level >= min) {
+ if (next)
+ res = next->test(s, res);
+ if (s.complete)
+ return res;
+
+ if (res >= 0 && parent->test_parent(s, res) >= 0)
+ return res;
+ }
+ }
+ return -1;
+ }
+};
+
+struct RegExNodeBackReference : public RegExNode {
+
+ int id;
+
+ RegExNodeBackReference(int p_id) {
+
+ length = -1;
+ quantifiable = true;
+ id = p_id;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ RegExMatch::Group& ref = s.match->captures[id];
+ for (int i = 0; i < ref.length; ++i) {
+
+ if (pos + i >= s.end)
+ return -1;
+
+ if (s.at(ref.start + i) != s.at(pos + i))
+ return -1;
+ }
+ return next ? next->test(s, pos + ref.length) : pos + ref.length;
+ }
+};
+
+
+struct RegExNodeGroup : public RegExNode {
+
+ bool inverse;
+ bool reset_pos;
+ Vector<RegExNode*> childset;
+ RegExNode* back;
+
+ RegExNodeGroup() {
+
+ length = 0;
+ quantifiable = true;
+ inverse = false;
+ reset_pos = false;
+ back = NULL;
+ }
+
+ virtual ~RegExNodeGroup() {
+
+ for (int i = 0; i < childset.size(); ++i)
+ memdelete(childset[i]);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ for (int i = 0; i < childset.size(); ++i) {
+
+ s.complete = false;
+
+ int res = childset[i]->test(s, pos);
+
+ if (s.complete)
+ return res;
+
+ if (inverse) {
+ if (res < 0)
+ res = pos + 1;
+ else
+ return -1;
+
+ if (i + 1 < childset.size())
+ continue;
+ }
+
+ if (res >= 0) {
+ if (reset_pos)
+ res = pos;
+ return next ? next->test(s, res) : res;
+ }
+ }
+ return -1;
+ }
+
+ void add_child(RegExNode* node) {
+
+ node->parent = this;
+ node->previous = back;
+
+ if (back)
+ back->next = node;
+ else
+ childset.push_back(node);
+
+ increment_length(node->length);
+
+ back = node;
+ }
+
+ void add_childset() {
+
+ if (childset.size() > 0)
+ length = -1;
+ back = NULL;
+ }
+
+ RegExNode* swap_back(RegExNode* node) {
+
+ RegExNode* old = back;
+
+ if (old) {
+ if (!old->previous)
+ childset.remove(childset.size() - 1);
+ back = old->previous;
+ increment_length(old->length, true);
+ }
+
+ add_child(node);
+
+ return old;
+ }
+};
+
+struct RegExNodeCapturing : public RegExNodeGroup {
+
+ int id;
+
+ RegExNodeCapturing(int p_id = 0) {
+
+ id = p_id;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ RegExMatch::Group& ref = s.match->captures[id];
+ int old_start = ref.start;
+ ref.start = pos;
+
+ int res = RegExNodeGroup::test(s, pos);
+
+ if (res >= 0) {
+ if (!s.complete)
+ ref.length = res - pos;
+ } else {
+ ref.start = old_start;
+ }
+
+ return res;
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ RegExMatch::Group& ref = s.match->captures[id];
+ ref.length = pos - ref.start;
+ return RegExNode::test_parent(s, pos);
+ }
+
+ static Variant parse_name(const CharType*& c, bool p_allow_numeric) {
+
+ if (c[1] == '0') {
+ return -1;
+ } else if ('1' <= c[1] && c[1] <= '9') {
+ if (!p_allow_numeric)
+ return -1;
+ int res = (++c)[0] - '0';
+ while ('0' <= c[1] && c[1] <= '9')
+ res = res * 10 + int((++c)[0] - '0');
+ if ((++c)[0] != '>')
+ return -1;
+ return res;
+ } else if (iswalnum(c[1])) {
+ String res(++c, 1);
+ while (iswalnum(c[1]))
+ res += String(++c, 1);
+ if ((++c)[0] != '>')
+ return -1;
+ return res;
+ }
+ return -1;
+ }
+};
+
+struct RegExNodeLookAhead : public RegExNodeGroup {
+
+ int id;
+
+ RegExNodeLookAhead(bool p_inverse, int p_id = 0) {
+
+ quantifiable = false;
+ inverse = p_inverse;
+ reset_pos = true;
+ id = p_id;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ s.lookahead_pos[id] = pos;
+ return RegExNodeGroup::test(s, pos);
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ return RegExNode::test_parent(s, s.lookahead_pos[id]);
+ }
+};
+
+struct RegExNodeLookBehind : public RegExNodeGroup {
+
+ RegExNodeLookBehind(bool p_inverse, int p_id = 0) {
+
+ quantifiable = false;
+ inverse = p_inverse;
+ reset_pos = true;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (pos < length)
+ return -1;
+ return RegExNodeGroup::test(s, pos - length);
+ }
+};
+
+struct RegExNodeBracket : public RegExNode {
+
+ bool inverse;
+ Vector<RegExNode*> children;
+
+ RegExNodeBracket() {
+
+ length = 1;
+ quantifiable = true;
+ inverse = false;
+ }
+
+ virtual ~RegExNodeBracket() {
+
+ for (int i = 0; i < children.size(); ++i)
+ memdelete(children[i]);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ for (int i = 0; i < children.size(); ++i) {
+
+ int res = children[i]->test(s, pos);
+
+ if (inverse) {
+ if (res < 0)
+ res = pos + 1;
+ else
+ return -1;
+
+ if (i + 1 < children.size())
+ continue;
+ }
+
+ if (res >= 0)
+ return next ? next->test(s, res) : res;
+ }
+ return -1;
+ }
+
+ void add_child(RegExNode* node) {
+
+ node->parent = this;
+ children.push_back(node);
+ }
+
+ void pop_back() {
+
+ memdelete(children[children.size() - 1]);
+ children.remove(children.size() - 1);
+ }
+};
+
+#define REGEX_EXPAND_FAIL(MSG)\
+{\
+ ERR_PRINT(MSG);\
+ return String();\
+}
+
+String RegExMatch::expand(const String& p_template) const {
+
+ String res;
+ for (const CharType* c = p_template.c_str(); *c != '\0'; ++c) {
+ if (c[0] == '\\') {
+ if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{')) {
+
+ int ref = 0;
+ bool unclosed = false;
+
+ if (c[1] == 'g') {
+ unclosed = true;
+ c = &c[2];
+ }
+
+ while ('0' <= c[1] && c[1] <= '9') {
+ ref = ref * 10 + int(c[1] - '0');
+ ++c;
+ }
+
+ if (unclosed) {
+ if (c[1] != '}')
+ REGEX_EXPAND_FAIL("unclosed backreference '{'");
+ ++c;
+ }
+
+ res += get_string(ref);
+
+ } else if (c[1] =='g' && c[2] == '<') {
+
+ const CharType* d = &c[2];
+
+ Variant name = RegExNodeCapturing::parse_name(d, true);
+ if (name == Variant(-1))
+ REGEX_EXPAND_FAIL("unrecognised character for group name");
+
+ c = d;
+
+ res += get_string(name);
+
+ } else {
+
+ const CharType* d = c;
+ CharType ch = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_EXPAND_FAIL("invalid escape token");
+ res += String(&ch, 1);
+ c = d;
+ }
+ } else {
+ res += String(c, 1);
+ }
+ }
+ return res;
+}
+
+int RegExMatch::get_group_count() const {
+
+ int count = 0;
+ for (int i = 1; i < captures.size(); ++i)
+ if (captures[i].name.get_type() == Variant::INT)
+ ++count;
+ return count;
+}
+
+Array RegExMatch::get_group_array() const {
+
+ Array res;
+ for (int i = 1; i < captures.size(); ++i) {
+ const RegExMatch::Group& capture = captures[i];
+ if (capture.name.get_type() != Variant::INT)
+ continue;
+
+ if (capture.start >= 0)
+ res.push_back(string.substr(capture.start, capture.length));
+ else
+ res.push_back(String());
+ }
+ return res;
+}
+
+Array RegExMatch::get_names() const {
+
+ Array res;
+ for (int i = 1; i < captures.size(); ++i)
+ if (captures[i].name.get_type() == Variant::STRING)
+ res.push_back(captures[i].name);
+ return res;
+}
+
+Dictionary RegExMatch::get_name_dict() const {
+
+ Dictionary res;
+ for (int i = 1; i < captures.size(); ++i) {
+ const RegExMatch::Group& capture = captures[i];
+ if (capture.name.get_type() != Variant::STRING)
+ continue;
+
+ if (capture.start >= 0)
+ res[capture.name] = string.substr(capture.start, capture.length);
+ else
+ res[capture.name] = String();
+ }
+ return res;
+}
+
+String RegExMatch::get_string(const Variant& p_name) const {
+
+ for (int i = 0; i < captures.size(); ++i) {
+
+ const RegExMatch::Group& capture = captures[i];
+
+ if (capture.name != p_name)
+ continue;
+
+ if (capture.start == -1)
+ return String();
+
+ return string.substr(capture.start, capture.length);
+ }
+ return String();
+}
+
+int RegExMatch::get_start(const Variant& p_name) const {
+
+ for (int i = 0; i < captures.size(); ++i)
+ if (captures[i].name == p_name)
+ return captures[i].start;
+ return -1;
+}
+
+int RegExMatch::get_end(const Variant& p_name) const {
+
+ for (int i = 0; i < captures.size(); ++i)
+ if (captures[i].name == p_name)
+ return captures[i].start + captures[i].length;
+ return -1;
+}
+
+RegExMatch::RegExMatch() {
+
+}
+
+static bool RegEx_is_shorthand(CharType ch) {
+
+ switch (ch) {
+ case 'w':
+ case 'W':
+ case 'd':
+ case 'D':
+ case 's':
+ case 'S':
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+#define REGEX_COMPILE_FAIL(MSG)\
+{\
+ ERR_PRINT(MSG);\
+ clear();\
+ return FAILED;\
+}
+
+Error RegEx::compile(const String& p_pattern) {
+
+ ERR_FAIL_COND_V(p_pattern.length() == 0, FAILED);
+
+ if (pattern == p_pattern && root)
+ return OK;
+
+ clear();
+ pattern = p_pattern;
+ group_names.push_back(0);
+ RegExNodeGroup* root_group = memnew(RegExNodeCapturing(0));
+ root = root_group;
+ Vector<RegExNodeGroup*> stack;
+ stack.push_back(root_group);
+ int lookahead_level = 0;
+ int numeric_groups = 0;
+ const int numeric_max = 9;
+
+ for (const CharType* c = p_pattern.c_str(); *c != '\0'; ++c) {
+
+ switch (c[0]) {
+ case '(':
+ if (c[1] == '?') {
+
+ RegExNodeGroup* group = NULL;
+ switch (c[2]) {
+ case ':':
+ c = &c[2];
+ group = memnew(RegExNodeGroup());
+ break;
+ case '!':
+ case '=':
+ group = memnew(RegExNodeLookAhead((c[2] == '!'), lookahead_level++));
+ if (lookahead_depth < lookahead_level)
+ lookahead_depth = lookahead_level;
+ c = &c[2];
+ break;
+ case '<':
+ if (c[3] == '!' || c[3] == '=') {
+ group = memnew(RegExNodeLookBehind((c[3] == '!'), lookahead_level++));
+ c = &c[3];
+ }
+ break;
+ case 'P':
+ if (c[3] == '<') {
+ const CharType* d = &c[3];
+ Variant name = RegExNodeCapturing::parse_name(d, false);
+ if (name == Variant(-1))
+ REGEX_COMPILE_FAIL("unrecognised character for group name");
+ group = memnew(RegExNodeCapturing(group_names.size()));
+ group_names.push_back(name);
+ c = d;
+ }
+ default:
+ break;
+ }
+ if (!group)
+ REGEX_COMPILE_FAIL("unrecognised qualifier for group");
+ stack[0]->add_child(group);
+ stack.insert(0, group);
+
+ } else if (numeric_groups < numeric_max) {
+
+ RegExNodeCapturing* group = memnew(RegExNodeCapturing(group_names.size()));
+ group_names.push_back(++numeric_groups);
+ stack[0]->add_child(group);
+ stack.insert(0, group);
+
+ } else {
+
+ RegExNodeGroup* group = memnew(RegExNodeGroup());
+ stack[0]->add_child(group);
+ stack.insert(0, group);
+ }
+ break;
+ case ')':
+ if (stack.size() == 1)
+ REGEX_COMPILE_FAIL("unexpected ')'");
+ stack.remove(0);
+ break;
+ case '\\':
+ if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{')) {
+
+ int ref = 0;
+ bool unclosed = false;
+
+ if (c[1] == 'g') {
+ unclosed = true;
+ c = &c[2];
+ }
+
+ while ('0' <= c[1] && c[1] <= '9') {
+ ref = ref * 10 + int(c[1] - '0');
+ ++c;
+ }
+
+ if (unclosed) {
+ if (c[1] != '}')
+ REGEX_COMPILE_FAIL("unclosed backreference '{'");
+ ++c;
+ }
+
+ if (ref > numeric_groups || ref <= 0)
+ REGEX_COMPILE_FAIL("backreference not found");
+
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("backreferences inside lookbehind not supported");
+
+ for (int i = 0; i < group_names.size(); ++i) {
+ if (group_names[i].get_type() == Variant::INT && int(group_names[i]) == ref) {
+ ref = group_names[i];
+ break;
+ }
+ }
+
+ stack[0]->add_child(memnew(RegExNodeBackReference(ref)));
+
+ } if (c[1] =='g' && c[2] == '<') {
+
+ const CharType* d = &c[2];
+
+ Variant name = RegExNodeCapturing::parse_name(d, true);
+ if (name == Variant(-1))
+ REGEX_COMPILE_FAIL("unrecognised character for group name");
+
+ c = d;
+
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("backreferences inside lookbehind not supported");
+
+ int ref = -1;
+
+ for (int i = 0; i < group_names.size(); ++i) {
+ if (group_names[i].get_type() == Variant::INT && int(group_names[i]) == ref) {
+ ref = group_names[i];
+ break;
+ }
+ }
+
+ if (ref == -1)
+ REGEX_COMPILE_FAIL("backreference not found");
+
+ stack[0]->add_child(memnew(RegExNodeBackReference(ref)));
+
+ } else if (c[1] == 'b' || c[1] == 'B') {
+
+ stack[0]->add_child(memnew(RegExNodeWordBoundary(*(++c) == 'B')));
+
+ } else if (RegEx_is_shorthand(c[1])) {
+
+ stack[0]->add_child(memnew(RegExNodeShorthand(*(++c))));
+
+ } else {
+
+ const CharType* d = c;
+ CharType ch = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_COMPILE_FAIL("invalid escape token");
+ stack[0]->add_child(memnew(RegExNodeChar(ch)));
+ c = d;
+
+ }
+ break;
+ case '[':
+ {
+ RegExNodeBracket* bracket = memnew(RegExNodeBracket());
+ stack[0]->add_child(bracket);
+ if (c[1] == '^') {
+ bracket->inverse = true;
+ ++c;
+ }
+ bool first_child = true;
+ CharType previous_child;
+ bool previous_child_single = false;
+ while (true) {
+ ++c;
+ if (!first_child && c[0] == ']') {
+
+ break;
+
+ } else if (c[0] == '\0') {
+
+ REGEX_COMPILE_FAIL("unclosed bracket expression '['");
+
+ } else if (c[0] == '\\') {
+
+ if (RegEx_is_shorthand(c[1])) {
+ bracket->add_child(memnew(RegExNodeShorthand(*(++c))));
+ } else {
+ const CharType* d = c;
+ CharType ch = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_COMPILE_FAIL("invalid escape token");
+ bracket->add_child(memnew(RegExNodeChar(ch)));
+ c = d;
+ previous_child = ch;
+ previous_child_single = true;
+ }
+
+ } else if (c[0] == ']' && c[1] == ':') {
+
+ const CharType* d = &c[2];
+ RegExNodeClass::Type type = RegExNodeClass::parse_type(d);
+ if (type != RegExNodeClass::Type_none) {
+
+ c = d;
+ previous_child_single = false;
+
+ } else {
+
+ bracket->add_child(memnew(RegExNodeChar('[')));
+ previous_child = '[';
+ previous_child_single = true;
+ }
+ } else if (previous_child_single && c[0] == '-') {
+
+ if (c[1] != '\0' && c[1] != ']') {
+
+ CharType next;
+
+ if (c[1] == '\\') {
+ const CharType* d = ++c;
+ next = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_COMPILE_FAIL("invalid escape token");
+ } else {
+ next = *(++c);
+ }
+
+ if (next < previous_child)
+ REGEX_COMPILE_FAIL("text range out of order");
+
+ bracket->pop_back();
+ bracket->add_child(memnew(RegExNodeRange(previous_child, next)));
+ previous_child_single = false;
+ } else {
+
+ bracket->add_child(memnew(RegExNodeChar('-')));
+ previous_child = '-';
+ previous_child_single = true;
+ }
+ } else {
+
+ bracket->add_child(memnew(RegExNodeChar(c[0])));
+ previous_child = c[0];
+ previous_child_single = true;
+ }
+ first_child = false;
+ }
+ }
+ break;
+ case '|':
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("alternations inside lookbehind not supported");
+ stack[0]->add_childset();
+ break;
+ case '^':
+ stack[0]->add_child(memnew(RegExNodeAnchorStart()));
+ break;
+ case '$':
+ stack[0]->add_child(memnew(RegExNodeAnchorEnd()));
+ break;
+ case '.':
+ stack[0]->add_child(memnew(RegExNodeShorthand('.')));
+ break;
+ case '?':
+ case '*':
+ case '+':
+ case '{':
+ {
+ int min_val = 0;
+ int max_val = -1;
+ bool valid = true;
+ const CharType* d = c;
+ bool max_set = true;
+ switch (c[0]) {
+ case '?':
+ min_val = 0;
+ max_val = 1;
+ break;
+ case '*':
+ min_val = 0;
+ max_val = -1;
+ break;
+ case '+':
+ min_val = 1;
+ max_val = -1;
+ break;
+ case '{':
+ max_set = false;
+ while (valid) {
+ ++d;
+ if (d[0] == '}') {
+ break;
+ } else if (d[0] == ',') {
+ max_set = true;
+ } else if ('0' <= d[0] && d[0] <= '9') {
+ if (max_set) {
+ if (max_val < 0)
+ max_val = int(d[0] - '0');
+ else
+ max_val = max_val * 10 + int(d[0] - '0');
+ } else {
+ min_val = min_val * 10 + int(d[0] - '0');
+ }
+ } else {
+ valid = false;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!max_set)
+ max_val = min_val;
+
+ if (valid) {
+
+ c = d;
+
+ if (stack[0]->back == NULL || !stack[0]->back->quantifiable)
+ REGEX_COMPILE_FAIL("element not quantifiable");
+
+ if (min_val != max_val)
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("variable length quantifiers inside lookbehind not supported");
+
+ RegExNodeQuantifier* quant = memnew(RegExNodeQuantifier(min_val, max_val));
+ quant->child = stack[0]->swap_back(quant);
+ quant->child->previous = NULL;
+ quant->child->parent = quant;
+
+ if (min_val == max_val && quant->child->length >= 0)
+ quant->length = max_val * quant->child->length;
+
+ if (c[1] == '?') {
+ quant->greedy = false;
+ ++c;
+ }
+ break;
+ }
+ }
+ default:
+ stack[0]->add_child(memnew(RegExNodeChar(c[0])));
+ break;
+ }
+ }
+ if (stack.size() > 1)
+ REGEX_COMPILE_FAIL("unclosed group '('");
+ return OK;
+}
+
+Ref<RegExMatch> RegEx::search(const String& p_text, int p_start, int p_end) const {
+
+ ERR_FAIL_COND_V(!is_valid(), NULL);
+ ERR_FAIL_COND_V(p_start < 0, NULL);
+ ERR_FAIL_COND_V(p_start >= p_text.length(), NULL);
+ ERR_FAIL_COND_V(p_end > p_text.length(), NULL);
+ ERR_FAIL_COND_V(p_end != -1 && p_end < p_start, NULL);
+
+ Ref<RegExMatch> res = memnew(RegExMatch());
+
+ for (int i = 0; i < group_names.size(); ++i) {
+ RegExMatch::Group group;
+ group.name = group_names[i];
+ res->captures.push_back(group);
+ }
+
+ res->string = p_text;
+
+ if (p_end == -1)
+ p_end = p_text.length();
+
+ RegExSearch s(res, p_end, lookahead_depth);
+
+ for (int i = p_start; i <= s.end; ++i) {
+ for (int c = 0; c < group_names.size(); ++c) {
+ res->captures[c].start = -1;
+ res->captures[c].length = 0;
+ }
+ if (root->test(s, i) >= 0)
+ break;
+ }
+
+ if (res->captures[0].start >= 0)
+ return res;
+ return NULL;
+}
+
+String RegEx::sub(const String& p_text, const String& p_replacement, bool p_all, int p_start, int p_end) const {
+
+ ERR_FAIL_COND_V(!is_valid(), p_text);
+ ERR_FAIL_COND_V(p_start < 0, p_text);
+ ERR_FAIL_COND_V(p_start >= p_text.length(), p_text);
+ ERR_FAIL_COND_V(p_end > p_text.length(), p_text);
+ ERR_FAIL_COND_V(p_end != -1 && p_end < p_start, p_text);
+
+ String text = p_text;
+ int start = p_start;
+
+ if (p_end == -1)
+ p_end = p_text.length();
+
+ while (start < text.length() && (p_all || start == p_start)) {
+
+ Ref<RegExMatch> m = search(text, start, p_end);
+
+ RegExMatch::Group& s = m->captures[0];
+
+ if (s.start < 0)
+ break;
+
+ String res = text.substr(0, s.start) + m->expand(p_replacement);
+
+ start = res.length();
+
+ if (s.length == 0)
+ ++start;
+
+ int sub_end = s.start + s.length;
+ if (sub_end < text.length())
+ res += text.substr(sub_end, text.length() - sub_end);
+
+ p_end += res.length() - text.length();
+
+ text = res;
+ }
+ return text;
+}
+
+void RegEx::clear() {
+
+ if (root)
+ memdelete(root);
+
+ root = NULL;
+ group_names.clear();
+ lookahead_depth = 0;
+}
+
+bool RegEx::is_valid() const {
+
+ return (root != NULL);
+}
+
+String RegEx::get_pattern() const {
+
+ return pattern;
+}
+
+int RegEx::get_group_count() const {
+
+ int count = 0;
+ for (int i = 1; i < group_names.size(); ++i)
+ if (group_names[i].get_type() == Variant::INT)
+ ++count;
+ return count;
+}
+
+Array RegEx::get_names() const {
+
+ Array res;
+ for (int i = 1; i < group_names.size(); ++i)
+ if (group_names[i].get_type() == Variant::STRING)
+ res.push_back(group_names[i]);
+ return res;
+}
+
+RegEx::RegEx() {
+
+ root = NULL;
+ lookahead_depth = 0;
+}
+
+RegEx::RegEx(const String& p_pattern) {
+
+ root = NULL;
+ compile(p_pattern);
+}
+
+RegEx::~RegEx() {
+
+ if (root)
+ memdelete(root);
+}
+
+void RegExMatch::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("expand","template"),&RegExMatch::expand);
+ ObjectTypeDB::bind_method(_MD("get_group_count"),&RegExMatch::get_group_count);
+ ObjectTypeDB::bind_method(_MD("get_group_array"),&RegExMatch::get_group_array);
+ ObjectTypeDB::bind_method(_MD("get_names"),&RegExMatch::get_names);
+ ObjectTypeDB::bind_method(_MD("get_name_dict"),&RegExMatch::get_name_dict);
+ ObjectTypeDB::bind_method(_MD("get_string","name"),&RegExMatch::get_string, DEFVAL(0));
+ ObjectTypeDB::bind_method(_MD("get_start","name"),&RegExMatch::get_start, DEFVAL(0));
+ ObjectTypeDB::bind_method(_MD("get_end","name"),&RegExMatch::get_end, DEFVAL(0));
+}
+
+void RegEx::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("clear"),&RegEx::clear);
+ ObjectTypeDB::bind_method(_MD("compile","pattern"),&RegEx::compile);
+ ObjectTypeDB::bind_method(_MD("search","text","start","end"),&RegEx::search, DEFVAL(0), DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("sub","text","replacement","all","start","end"),&RegEx::sub, DEFVAL(false), DEFVAL(0), DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("is_valid"),&RegEx::is_valid);
+ ObjectTypeDB::bind_method(_MD("get_pattern"),&RegEx::get_pattern);
+ ObjectTypeDB::bind_method(_MD("get_group_count"),&RegEx::get_group_count);
+ ObjectTypeDB::bind_method(_MD("get_names"),&RegEx::get_names);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "pattern"), _SCS("compile"), _SCS("get_pattern"));
+}
+
diff --git a/modules/regex/regex.h b/modules/regex/regex.h
new file mode 100644
index 0000000000..803aa72b3f
--- /dev/null
+++ b/modules/regex/regex.h
@@ -0,0 +1,114 @@
+/*************************************************************************/
+/* regex.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef REGEX_H
+#define REGEX_H
+
+#include "core/vector.h"
+#include "core/ustring.h"
+#include "core/dictionary.h"
+#include "core/reference.h"
+#include "core/resource.h"
+
+class RegExNode;
+
+class RegExMatch : public Reference {
+
+ OBJ_TYPE(RegExMatch, Reference);
+
+ struct Group {
+ Variant name;
+ int start;
+ int length;
+ };
+
+ Vector<Group> captures;
+ String string;
+
+ friend class RegEx;
+ friend class RegExSearch;
+ friend class RegExNodeCapturing;
+ friend class RegExNodeBackReference;
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ String expand(const String& p_template) const;
+
+ int get_group_count() const;
+ Array get_group_array() const;
+
+ Array get_names() const;
+ Dictionary get_name_dict() const;
+
+ String get_string(const Variant& p_name) const;
+ int get_start(const Variant& p_name) const;
+ int get_end(const Variant& p_name) const;
+
+ RegExMatch();
+
+};
+
+class RegEx : public Resource {
+
+ OBJ_TYPE(RegEx, Resource);
+
+ RegExNode* root;
+ Vector<Variant> group_names;
+ String pattern;
+ int lookahead_depth;
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ void clear();
+ Error compile(const String& p_pattern);
+
+ Ref<RegExMatch> search(const String& p_text, int p_start = 0, int p_end = -1) const;
+ String sub(const String& p_text, const String& p_replacement, bool p_all = false, int p_start = 0, int p_end = -1) const;
+
+ bool is_valid() const;
+ String get_pattern() const;
+ int get_group_count() const;
+ Array get_names() const;
+
+ RegEx();
+ RegEx(const String& p_pattern);
+ ~RegEx();
+
+};
+
+#endif // REGEX_H
+
diff --git a/drivers/nrex/regex.h b/modules/regex/register_types.cpp
index 74495442c7..050cf3efff 100644
--- a/drivers/nrex/regex.h
+++ b/modules/regex/register_types.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* regex.h */
+/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -26,40 +26,18 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef REGEX_H
-#define REGEX_H
-#include "ustring.h"
-#include "vector.h"
-#include "core/reference.h"
-#include "nrex.hpp"
+#include "register_types.h"
+#include "object_type_db.h"
+#include "regex.h"
-class RegEx : public Reference {
+void register_regex_types() {
- OBJ_TYPE(RegEx, Reference);
+ ObjectTypeDB::register_type<RegExMatch>();
+ ObjectTypeDB::register_type<RegEx>();
+}
- mutable String text;
- mutable Vector<nrex_result> captures;
- nrex exp;
+void unregister_regex_types() {
-protected:
+}
- static void _bind_methods();
- StringArray _bind_get_captures() const;
-
-public:
-
- void clear();
- bool is_valid() const;
- int get_capture_count() const;
- int get_capture_start(int capture) const;
- String get_capture(int capture) const;
- Error compile(const String& p_pattern, int capture = 9);
- int find(const String& p_text, int p_start = 0, int p_end = -1) const;
-
- RegEx();
- RegEx(const String& p_pattern);
- ~RegEx();
-};
-
-#endif // REGEX_H
diff --git a/modules/regex/register_types.h b/modules/regex/register_types.h
new file mode 100644
index 0000000000..df3b508e14
--- /dev/null
+++ b/modules/regex/register_types.h
@@ -0,0 +1,31 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+void register_regex_types();
+void unregister_regex_types();
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index de57d770c4..f934916aa2 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -195,8 +195,8 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t>& p_html, const St
}
CharString cs = strnew.utf8();
- p_html.resize(cs.size());
- for(int i=9;i<cs.size();i++) {
+ p_html.resize(cs.length());
+ for(int i=9;i<cs.length();i++) {
p_html[i]=cs[i];
}
}
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index a5b26930be..7ae0100762 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -1,16 +1,11 @@
#
-# tested on | Windows native | Linux cross-compilation
-# ----------------------------+-------------------+---------------------------
-# MSVS C++ 2010 Express | WORKS | n/a
+# tested on | Windows native | Linux cross-compilation
+# ----------------------------+-------------------+---------------------------
# Visual C++ Build Tools 2015 | WORKS | n/a
-# Mingw-w64 | WORKS | WORKS
-# Mingw-w32 | WORKS | WORKS
-# MinGW | WORKS | untested
-#
-#####
-# Notes about MSVS C++ :
-#
-# - MSVC2010-Express compiles to 32bits only.
+# MSVS C++ 2010 Express | WORKS | n/a
+# Mingw-w64 | WORKS | WORKS
+# Mingw-w32 | WORKS | WORKS
+# MinGW | WORKS | untested
#
#####
# Note about Visual C++ Build Tools :
@@ -19,6 +14,11 @@
# http://landinghub.visualstudio.com/visual-cpp-build-tools
#
#####
+# Notes about MSVS C++ :
+#
+# - MSVC2010-Express compiles to 32bits only.
+#
+#####
# Notes about Mingw-w64 and Mingw-w32 under Windows :
#
# - both can be installed using the official installer :
@@ -109,7 +109,7 @@ def can_build():
if (os.name=="nt"):
#building natively on windows!
- if ( methods.msvc_is_detected() ):
+ if ( os.getenv("VCINSTALLDIR") ):
return True
else:
print("\nMSVC not detected, attempting Mingw.")
@@ -204,7 +204,7 @@ def configure(env):
env.Append(CPPPATH=['#platform/windows'])
env['is_mingw']=False
- if (os.name=="nt" and methods.msvc_is_detected() ):
+ if (os.name=="nt" and os.getenv("VCINSTALLDIR") ):
#build using visual studio
env['ENV']['TMP'] = os.environ['TMP']
env.Append(CPPPATH=['#platform/windows/include'])
diff --git a/platform/windows/packet_peer_udp_winsock.cpp b/platform/windows/packet_peer_udp_winsock.cpp
index 2c79365c08..b4cd60979c 100644
--- a/platform/windows/packet_peer_udp_winsock.cpp
+++ b/platform/windows/packet_peer_udp_winsock.cpp
@@ -29,6 +29,9 @@
#include "packet_peer_udp_winsock.h"
#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include "drivers/unix/socket_helpers.h"
int PacketPeerUDPWinsock::get_available_packet_count() const {
@@ -48,7 +51,15 @@ Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer,int &r_buffer_si
return ERR_UNAVAILABLE;
uint32_t size;
- rb.read((uint8_t*)&packet_ip.host,4,true);
+ uint8_t type;
+ rb.read(&type, 1, true);
+ if (type == IP_Address::TYPE_IPV4) {
+ rb.read((uint8_t*)&packet_ip.field8,4,true);
+ packet_ip.type = IP_Address::TYPE_IPV4;
+ } else {
+ rb.read((uint8_t*)&packet_ip.field8,16,true);
+ packet_ip.type = IP_Address::TYPE_IPV6;
+ };
rb.read((uint8_t*)&packet_port,4,true);
rb.read((uint8_t*)&size,4,true);
rb.read(packet_buffer,size,true);
@@ -60,19 +71,16 @@ Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer,int &r_buffer_si
}
Error PacketPeerUDPWinsock::put_packet(const uint8_t *p_buffer,int p_buffer_size){
- int sock = _get_socket();
+ int sock = _get_socket(peer_addr.type);
ERR_FAIL_COND_V( sock == -1, FAILED );
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(peer_port);
- addr.sin_addr = *((struct in_addr*)&peer_addr.host);
-
+ struct sockaddr_storage addr;
+ size_t addr_size = _set_sockaddr(&addr, peer_addr, peer_port);
_set_blocking(true);
errno = 0;
int err;
- while ( (err = sendto(sock, (const char*)p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, sizeof(addr))) != p_buffer_size) {
+ while ( (err = sendto(sock, (const char*)p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, addr_size)) != p_buffer_size) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FAILED;
@@ -104,17 +112,17 @@ void PacketPeerUDPWinsock::_set_blocking(bool p_blocking) {
};
}
-Error PacketPeerUDPWinsock::listen(int p_port, int p_recv_buffer_size){
+Error PacketPeerUDPWinsock::listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size) {
close();
- int sock = _get_socket();
+ int sock = _get_socket(p_address_type);
if (sock == -1 )
return ERR_CANT_CREATE;
- sockaddr_in addr = {0};
- addr.sin_family = AF_INET;
- addr.sin_port = htons(p_port);
- addr.sin_addr.s_addr = INADDR_ANY;
- if (bind(sock, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == -1 ) {
+
+ struct sockaddr_storage addr = {0};
+ size_t addr_size = _set_listen_sockaddr(&addr, p_port, p_address_type, NULL);
+
+ if (bind(sock, (struct sockaddr*)&addr, addr_size) == -1 ) {
close();
return ERR_UNAVAILABLE;
}
@@ -146,17 +154,41 @@ Error PacketPeerUDPWinsock::_poll(bool p_wait) {
_set_blocking(p_wait);
- struct sockaddr_in from = {0};
- int len = sizeof(struct sockaddr_in);
+ struct sockaddr_storage from = {0};
+ int len = sizeof(struct sockaddr_storage);
int ret;
while ( (ret = recvfrom(sockfd, (char*)recv_buffer, MIN((int)sizeof(recv_buffer),MAX(rb.space_left()-12, 0)), 0, (struct sockaddr*)&from, &len)) > 0) {
- rb.write((uint8_t*)&from.sin_addr, 4);
- uint32_t port = ntohs(from.sin_port);
+
+ uint32_t port = 0;
+
+ if (from.ss_family == AF_INET) {
+ uint8_t type = (uint8_t)IP_Address::TYPE_IPV4;
+ rb.write(&type, 1);
+ struct sockaddr_in* sin_from = (struct sockaddr_in*)&from;
+ rb.write((uint8_t*)&sin_from->sin_addr, 4);
+ port = ntohs(sin_from->sin_port);
+
+ } else if (from.ss_family == AF_INET6) {
+
+ uint8_t type = (uint8_t)IP_Address::TYPE_IPV6;
+ rb.write(&type, 1);
+
+ struct sockaddr_in6* s6_from = (struct sockaddr_in6*)&from;
+ rb.write((uint8_t*)&s6_from->sin6_addr, 16);
+
+ port = ntohs(s6_from->sin6_port);
+
+ } else {
+ // WARN_PRINT("Ignoring packet with unknown address family");
+ uint8_t type = (uint8_t)IP_Address::TYPE_NONE;
+ rb.write(&type, 1);
+ };
+
rb.write((uint8_t*)&port, 4);
rb.write((uint8_t*)&ret, 4);
rb.write(recv_buffer, ret);
- len = sizeof(struct sockaddr_in);
+ len = sizeof(struct sockaddr_storage);
++queue_count;
};
@@ -202,12 +234,14 @@ int PacketPeerUDPWinsock::get_packet_port() const{
return packet_port;
}
-int PacketPeerUDPWinsock::_get_socket() {
+int PacketPeerUDPWinsock::_get_socket(IP_Address::AddrType p_type) {
if (sockfd != -1)
return sockfd;
- sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ int family = p_type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+
+ sockfd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
ERR_FAIL_COND_V( sockfd == -1, -1 );
//fcntl(sockfd, F_SETFL, O_NONBLOCK);
diff --git a/platform/windows/packet_peer_udp_winsock.h b/platform/windows/packet_peer_udp_winsock.h
index b24dbac592..9837ef6621 100644
--- a/platform/windows/packet_peer_udp_winsock.h
+++ b/platform/windows/packet_peer_udp_winsock.h
@@ -42,15 +42,15 @@ class PacketPeerUDPWinsock : public PacketPeerUDP {
mutable RingBuffer<uint8_t> rb;
uint8_t recv_buffer[PACKET_BUFFER_SIZE];
mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- IP_Address packet_ip;
- int packet_port;
+ mutable IP_Address packet_ip;
+ mutable int packet_port;
mutable int queue_count;
int sockfd;
IP_Address peer_addr;
int peer_port;
- _FORCE_INLINE_ int _get_socket();
+ _FORCE_INLINE_ int _get_socket(IP_Address::AddrType p_type);
static PacketPeerUDP* _create();
@@ -67,7 +67,7 @@ public:
virtual int get_max_packet_size() const;
- virtual Error listen(int p_port,int p_recv_buffer_size=65536);
+ virtual Error listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size=65536);
virtual void close();
virtual Error wait();
virtual bool is_listening() const;
diff --git a/platform/windows/stream_peer_winsock.cpp b/platform/windows/stream_peer_winsock.cpp
index fcf0cb1640..b511e38ecb 100644
--- a/platform/windows/stream_peer_winsock.cpp
+++ b/platform/windows/stream_peer_winsock.cpp
@@ -31,16 +31,11 @@
#include "stream_peer_winsock.h"
#include <winsock2.h>
+#include <ws2tcpip.h>
-int winsock_refcount = 0;
-
-static void set_addr_in(struct sockaddr_in& their_addr, const IP_Address& p_host, uint16_t p_port) {
+#include "drivers/unix/socket_helpers.h"
- their_addr.sin_family = AF_INET; // host byte order
- their_addr.sin_port = htons(p_port); // short, network byte order
- their_addr.sin_addr = *((struct in_addr*)&p_host.host);
- memset(&(their_addr.sin_zero), '\0', 8);
-};
+int winsock_refcount = 0;
StreamPeerTCP* StreamPeerWinsock::_create() {
@@ -92,10 +87,10 @@ Error StreamPeerWinsock::_poll_connection(bool p_block) const {
_block(sockfd, false, true);
};
- struct sockaddr_in their_addr;
- set_addr_in(their_addr, peer_host, peer_port);
+ struct sockaddr_storage their_addr;
+ size_t addr_size = _set_sockaddr(&their_addr, peer_host, peer_port);
- if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) {
+ if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == SOCKET_ERROR) {
int err = WSAGetLastError();
if (err == WSAEISCONN) {
@@ -299,9 +294,10 @@ void StreamPeerWinsock::set_socket(int p_sockfd, IP_Address p_host, int p_port)
Error StreamPeerWinsock::connect(const IP_Address& p_host, uint16_t p_port) {
- ERR_FAIL_COND_V( p_host.host == 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V( p_host.type == IP_Address::TYPE_NONE, ERR_INVALID_PARAMETER);
- if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
+ int family = p_host.type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+ if ((sockfd = socket(family, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
ERR_PRINT("Socket creation failed!");
disconnect();
//perror("socket");
@@ -315,10 +311,10 @@ Error StreamPeerWinsock::connect(const IP_Address& p_host, uint16_t p_port) {
return FAILED;
};
- struct sockaddr_in their_addr;
- set_addr_in(their_addr, p_host, p_port);
+ struct sockaddr_storage their_addr;
+ size_t addr_size = _set_sockaddr(&their_addr, p_host, p_port);
- if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) {
+ if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == SOCKET_ERROR) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
ERR_PRINT("Connection to remote host failed!");
diff --git a/platform/windows/tcp_server_winsock.cpp b/platform/windows/tcp_server_winsock.cpp
index dd1cf43f3b..1aedc52bb9 100644
--- a/platform/windows/tcp_server_winsock.cpp
+++ b/platform/windows/tcp_server_winsock.cpp
@@ -31,6 +31,9 @@
#include "stream_peer_winsock.h"
#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include "drivers/unix/socket_helpers.h"
extern int winsock_refcount;
@@ -60,7 +63,7 @@ void TCPServerWinsock::cleanup() {
};
-Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_hosts) {
+Error TCPServerWinsock::listen(uint16_t p_port, IP_Address::AddrType p_type,const List<String> *p_accepted_hosts) {
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
@@ -73,11 +76,8 @@ Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_ho
return FAILED;
};
- struct sockaddr_in my_addr;
- my_addr.sin_family = AF_INET; // host byte order
- my_addr.sin_port = htons(p_port); // short, network byte order
- my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP TODO: use p_accepted_hosts
- memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
+ struct sockaddr_storage my_addr;
+ size_t addr_size = _set_listen_sockaddr(&my_addr, p_port, p_type, p_accepted_hosts);
int reuse=1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) {
@@ -86,7 +86,7 @@ Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_ho
}
- if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) != SOCKET_ERROR) {
+ if (bind(sockfd, (struct sockaddr *)&my_addr, addr_size) != SOCKET_ERROR) {
if (::listen(sockfd, SOMAXCONN) == SOCKET_ERROR) {
@@ -140,16 +140,17 @@ Ref<StreamPeerTCP> TCPServerWinsock::take_connection() {
return NULL;
};
- struct sockaddr_in their_addr;
+ struct sockaddr_storage their_addr;
int sin_size = sizeof(their_addr);
int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &sin_size);
ERR_FAIL_COND_V(fd == INVALID_SOCKET, NULL);
Ref<StreamPeerWinsock> conn = memnew(StreamPeerWinsock);
IP_Address ip;
- ip.host = (uint32_t)their_addr.sin_addr.s_addr;
+ int port;
+ _set_ip_addr_port(ip, port, &their_addr);
- conn->set_socket(fd, ip, ntohs(their_addr.sin_port));
+ conn->set_socket(fd, ip, port);
return conn;
};
diff --git a/platform/windows/tcp_server_winsock.h b/platform/windows/tcp_server_winsock.h
index bd6a05c74d..94601a2e12 100644
--- a/platform/windows/tcp_server_winsock.h
+++ b/platform/windows/tcp_server_winsock.h
@@ -39,7 +39,7 @@ class TCPServerWinsock : public TCP_Server {
public:
- virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL);
+ virtual Error listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4,const List<String> *p_accepted_hosts=NULL);
virtual bool is_connection_available() const;
virtual Ref<StreamPeerTCP> take_connection();
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 6b43425edc..f942f15ed0 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -186,15 +186,17 @@ void FileDialog::_action_pressed() {
hide();
}else if (mode==MODE_OPEN_ANY || mode==MODE_OPEN_DIR) {
-
String path=dir_access->get_current_dir();
- /*if (tree->get_selected()) {
- Dictionary d = tree->get_selected()->get_metadata(0);
+
+ path=path.replace("\\","/");
+
+ if (TreeItem* item = tree->get_selected()) {
+ Dictionary d = item->get_metadata(0);
if (d["dir"]) {
- path=path+"/"+String(d["name"]);
+ path=path.plus_file(d["name"]);
}
- }*/
- path=path.replace("\\","/");
+ }
+
emit_signal("dir_selected",path);
hide();
}
@@ -348,18 +350,18 @@ void FileDialog::update_file_list() {
files.sort_custom<NoCaseComparator>();
while(!dirs.empty()) {
+ String& dir_name = dirs.front()->get();
+ TreeItem *ti=tree->create_item(root);
+ ti->set_text(0,dir_name+"/");
+ ti->set_icon(0,folder);
- if (dirs.front()->get()!=".") {
- TreeItem *ti=tree->create_item(root);
- ti->set_text(0,dirs.front()->get()+"/");
- ti->set_icon(0,folder);
- Dictionary d;
- d["name"]=dirs.front()->get();
- d["dir"]=true;
- ti->set_metadata(0,d);
- }
- dirs.pop_front();
+ Dictionary d;
+ d["name"]=dir_name;
+ d["dir"]=true;
+
+ ti->set_metadata(0,d);
+ dirs.pop_front();
}
dirs.clear();
diff --git a/tools/editor/editor_file_dialog.cpp b/tools/editor/editor_file_dialog.cpp
index b8abd1d32c..90289a275e 100644
--- a/tools/editor/editor_file_dialog.cpp
+++ b/tools/editor/editor_file_dialog.cpp
@@ -336,15 +336,21 @@ void EditorFileDialog::_action_pressed() {
hide();
}else if (mode==MODE_OPEN_ANY || mode==MODE_OPEN_DIR) {
-
String path=dir_access->get_current_dir();
- /*if (tree->get_selected()) {
- Dictionary d = tree->get_selected()->get_metadata(0);
- if (d["dir"]) {
- path=path+"/"+String(d["name"]);
- }
- }*/
+
path=path.replace("\\","/");
+
+ for(int i=0;i<item_list->get_item_count();i++) {
+ if (item_list->is_selected(i)) {
+ Dictionary d=item_list->get_item_metadata(i);
+ if (d["dir"]) {
+ path=path.plus_file(d["name"]);
+
+ break;
+ }
+ }
+ }
+
_save_to_recent();
emit_signal("dir_selected",path);
hide();
@@ -571,25 +577,26 @@ void EditorFileDialog::update_file_list() {
files.sort_custom<NoCaseComparator>();
while(!dirs.empty()) {
+ const String& dir_name=dirs.front()->get();
- if (dirs.front()->get()!=".") {
- item_list->add_item(dirs.front()->get()+"/");
- if (display_mode==DISPLAY_THUMBNAILS) {
+ item_list->add_item(dir_name+"/");
- item_list->set_item_icon(item_list->get_item_count()-1,folder_thumbnail);
- } else {
+ if (display_mode==DISPLAY_THUMBNAILS) {
- item_list->set_item_icon(item_list->get_item_count()-1,folder);
- }
+ item_list->set_item_icon(item_list->get_item_count()-1,folder_thumbnail);
+ } else {
- Dictionary d;
- d["name"]=dirs.front()->get();
- d["path"]=String();
- d["dir"]=true;
- item_list->set_item_metadata( item_list->get_item_count() -1, d);
+ item_list->set_item_icon(item_list->get_item_count()-1,folder);
}
- dirs.pop_front();
+ Dictionary d;
+ d["name"]=dir_name;
+ d["path"]=String();
+ d["dir"]=true;
+
+ item_list->set_item_metadata( item_list->get_item_count() -1, d);
+
+ dirs.pop_front();
}
dirs.clear();
diff --git a/tools/editor/filesystem_dock.cpp b/tools/editor/filesystem_dock.cpp
index 8a94c6e340..5b1e80fc3b 100644
--- a/tools/editor/filesystem_dock.cpp
+++ b/tools/editor/filesystem_dock.cpp
@@ -234,13 +234,6 @@ void FileSystemDock::_dir_selected() {
button_favorite->set_pressed(found);
- if (ti->get_parent() && ti->get_parent()->get_parent()==tree->get_root() && !ti->get_parent()->get_prev()) {
-
- //a favorite!!!
- } else {
-
-
- }
if (!split_mode) {
_open_pressed(); //go directly to dir
diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp
index 56f10ff7f8..7ff2728310 100644
--- a/tools/editor/scene_tree_dock.cpp
+++ b/tools/editor/scene_tree_dock.cpp
@@ -81,7 +81,10 @@ void SceneTreeDock::_unhandled_key_input(InputEvent p_event) {
_tool_selected(TOOL_DUPLICATE);
}
else if (ED_IS_SHORTCUT("scene_tree/add_script", p_event)) {
- _tool_selected(TOOL_SCRIPT);
+ _tool_selected(TOOL_CREATE_SCRIPT);
+ }
+ else if (ED_IS_SHORTCUT("scene_tree/load_script", p_event)) {
+ _tool_selected(TOOL_LOAD_SCRIPT);
}
else if (ED_IS_SHORTCUT("scene_tree/move_up", p_event)) {
_tool_selected(TOOL_MOVE_UP);
@@ -266,6 +269,24 @@ void SceneTreeDock::_replace_with_branch_scene(const String& p_file,Node* base)
scene_tree->set_selected(instanced_scene);
}
+
+void SceneTreeDock::_file_selected(String p_file) {
+ RES p_script = ResourceLoader::load(p_file, "Script");
+ if (p_script.is_null()) {
+ accept->get_ok()->set_text(TTR("Ugh"));
+ accept->set_text(vformat(TTR("Error loading script from %s"), p_file));
+ accept->popup_centered_minsize();
+ return;
+ }
+
+ Node *selected = scene_tree->get_selected();
+ if (!selected)
+ return;
+ selected->set_script(p_script.get_ref_ptr());
+ editor->push_item(p_script.operator->());
+ file_dialog->hide();
+}
+
bool SceneTreeDock::_cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node) {
int childCount = p_desired_node->get_child_count();
@@ -358,7 +379,22 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
//groups_editor->set_current(current);
//groups_editor->popup_centered_ratio();
} break;
- case TOOL_SCRIPT: {
+ case TOOL_LOAD_SCRIPT: {
+ Node *selected = scene_tree->get_selected();
+ if (!selected)
+ break;
+
+ file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+
+ List<String> extensions;
+ ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
+ file_dialog->clear_filters();
+ for (List<String>::Element *E = extensions.front(); E; E = E->next())
+ file_dialog->add_filter("*." + E->get() + " ; " + E->get().to_upper());
+
+ file_dialog->popup_centered_ratio();
+ } break;
+ case TOOL_CREATE_SCRIPT: {
Node *selected = scene_tree->get_selected();
if (!selected)
@@ -672,6 +708,7 @@ void SceneTreeDock::_notification(int p_what) {
button_add->set_icon(get_icon("Add","EditorIcons"));
button_instance->set_icon(get_icon("Instance","EditorIcons"));
button_create_script->set_icon(get_icon("Script","EditorIcons"));
+ button_load_script->set_icon(get_icon("Script", "EditorIcons"));
filter_icon->set_texture(get_icon("Zoom","EditorIcons"));
@@ -1303,8 +1340,10 @@ void SceneTreeDock::_selection_changed() {
if (selection_size==1 && EditorNode::get_singleton()->get_editor_selection()->get_selection().front()->key()->get_script().is_null()) {
button_create_script->show();
+ button_load_script->show();
} else {
button_create_script->hide();
+ button_load_script->hide();
}
//tool_buttons[TOOL_MULTI_EDIT]->set_disabled(EditorNode::get_singleton()->get_editor_selection()->get_selection().size()<2);
@@ -1775,7 +1814,8 @@ void SceneTreeDock::_tree_rmb(const Vector2& p_menu_pos) {
//menu->add_icon_item(get_icon("Groups","EditorIcons"),TTR("Edit Groups"),TOOL_GROUP);
//menu->add_icon_item(get_icon("Connect","EditorIcons"),TTR("Edit Connections"),TOOL_CONNECT);
menu->add_separator();
- menu->add_icon_shortcut(get_icon("Script","EditorIcons"),ED_GET_SHORTCUT("scene_tree/add_script"), TOOL_SCRIPT);
+ menu->add_icon_shortcut(get_icon("Script", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_script"), TOOL_CREATE_SCRIPT);
+ menu->add_icon_shortcut(get_icon("Script", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/load_script"), TOOL_LOAD_SCRIPT);
menu->add_separator();
}
@@ -1834,7 +1874,7 @@ void SceneTreeDock::_focus_node() {
void SceneTreeDock::open_script_dialog(Node* p_for_node) {
scene_tree->set_selected(p_for_node,false);
- _tool_selected(TOOL_SCRIPT);
+ _tool_selected(TOOL_CREATE_SCRIPT);
}
void SceneTreeDock::_bind_methods() {
@@ -1862,6 +1902,7 @@ void SceneTreeDock::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_tree_rmb"),&SceneTreeDock::_tree_rmb);
ObjectTypeDB::bind_method(_MD("_filter_changed"),&SceneTreeDock::_filter_changed);
ObjectTypeDB::bind_method(_MD("_focus_node"),&SceneTreeDock::_focus_node);
+ ObjectTypeDB::bind_method(_MD("_file_selected"), &SceneTreeDock::_file_selected);
ObjectTypeDB::bind_method(_MD("instance"),&SceneTreeDock::instance);
@@ -1886,6 +1927,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
ED_SHORTCUT("scene_tree/instance_scene",TTR("Instance Child Scene"));
ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type"));
ED_SHORTCUT("scene_tree/add_script", TTR("Add Script"));
+ ED_SHORTCUT("scene_tree/load_script", TTR("Load Script"));
ED_SHORTCUT("scene_tree/move_up", TTR("Move Up"), KEY_MASK_CMD | KEY_UP);
ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KEY_MASK_CMD | KEY_DOWN);
ED_SHORTCUT("scene_tree/duplicate", TTR("Duplicate"),KEY_MASK_CMD | KEY_D);
@@ -1922,12 +1964,19 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
tb = memnew( ToolButton );
- tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_SCRIPT, false));
+ tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_CREATE_SCRIPT, false));
tb->set_tooltip(TTR("Create a new script for the selected node."));
tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_script"));
filter_hbc->add_child(tb);
button_create_script=tb;
+ tb = memnew(ToolButton);
+ tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_LOAD_SCRIPT, false));
+ tb->set_tooltip(TTR("Load a script for the selected node."));
+ tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/load_script"));
+ filter_hbc->add_child(tb);
+ button_load_script = tb;
+
scene_tree = memnew( SceneTreeEditor(false,true,true ));
vbc->add_child(scene_tree);
@@ -1955,6 +2004,11 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
add_child(create_dialog);
create_dialog->connect("create",this,"_create");
+ file_dialog = memnew(EditorFileDialog);
+ add_child(file_dialog);
+ file_dialog->hide();
+ file_dialog->connect("file_selected", this, "_file_selected");
+
//groups_editor = memnew( GroupsEditor );
//add_child(groups_editor);
//groups_editor->set_undo_redo(&editor_data->get_undo_redo());
diff --git a/tools/editor/scene_tree_dock.h b/tools/editor/scene_tree_dock.h
index 8933a03883..ddd28cfd2d 100644
--- a/tools/editor/scene_tree_dock.h
+++ b/tools/editor/scene_tree_dock.h
@@ -58,7 +58,8 @@ class SceneTreeDock : public VBoxContainer {
TOOL_REPLACE,
TOOL_CONNECT,
TOOL_GROUP,
- TOOL_SCRIPT,
+ TOOL_CREATE_SCRIPT,
+ TOOL_LOAD_SCRIPT,
TOOL_MOVE_UP,
TOOL_MOVE_DOWN,
TOOL_DUPLICATE,
@@ -75,10 +76,12 @@ class SceneTreeDock : public VBoxContainer {
int current_option;
CreateDialog *create_dialog;
+ EditorFileDialog *file_dialog;
ToolButton *button_add;
ToolButton *button_instance;
ToolButton *button_create_script;
+ ToolButton *button_load_script;
SceneTreeEditor *scene_tree;
@@ -155,6 +158,7 @@ class SceneTreeDock : public VBoxContainer {
void _perform_instance_scenes(const Vector<String>& p_files,Node* parent,int p_pos);
void _replace_with_branch_scene(const String& p_file,Node* base);
+ void _file_selected(String p_file);
protected:
void _notification(int p_what);