Update Files

This commit is contained in:
2025-01-22 16:18:30 +01:00
parent ed4603cf95
commit a36294b518
16718 changed files with 2960346 additions and 0 deletions

View File

@ -0,0 +1,24 @@
<xml>
<files id="hxcpp_mysql" dir="${this_dir}" >
<depend files="hxcpp-depends"/>
<depend name="${this_dir}/Build.xml" dateOnly="true" />
<depend name="${this_dir}/sha1.h" />
<depend name="${this_dir}/socket.h" />
<cache value="true" asLibrary="true" />
<file name="Mysql.cpp"/>
<file name="my_api.cpp"/>
<file name="my_proto.cpp"/>
<file name="socket.cpp"/>
<file name="sha1.cpp"/>
</files>
<target id="haxe">
<files id="hxcpp_mysql"/>
<lib name="ws2_32.lib" if="windows" unless="static_link" />
</target>
</xml>

View File

@ -0,0 +1,555 @@
/*
* Copyright (C)2005-2012 Haxe Foundation
*
* 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 <hxcpp.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "mysql.h"
#include <string.h>
#ifdef HX_ANDROID
#define atof(x) strtod((x),0)
#endif
/**
<doc>
<h1>MySQL</h1>
<p>
API to connect and use MySQL database
</p>
</doc>
**/
#define HXTHROW(x) hx::Throw(HX_CSTRING(x))
namespace
{
struct Connection : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdMysql };
MYSQL *m;
void create(MYSQL *inM)
{
m = inM;
_hx_set_finalizer(this, finalize);
}
void destroy()
{
if (m)
{
mysql_close(m);
m = 0;
}
}
static void finalize(Dynamic obj)
{
((Connection *)(obj.mPtr))->destroy();
}
};
Connection *getConnection(Dynamic o)
{
Connection *connection = dynamic_cast<Connection *>(o.mPtr);
if (!connection || !connection->m)
hx::Throw( HX_CSTRING("Invalid Connection") );
return connection;
}
static void error( MYSQL *m, const char *msg )
{
hx::Throw( String(msg) + HX_CSTRING(" ") + String(mysql_error(m)) );
}
// ---------------------------------------------------------------
// Result
/**
<doc><h2>Result</h2></doc>
**/
#undef CONV_FLOAT
typedef enum {
CONV_INT,
CONV_STRING,
CONV_FLOAT,
CONV_BINARY,
CONV_DATE,
CONV_DATETIME,
CONV_BOOL
} CONV;
struct Result : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdMysqlResult };
MYSQL_RES *r;
int nfields;
CONV *fields_convs;
String *field_names;
MYSQL_ROW current;
void create(MYSQL_RES *inR)
{
r = inR;
fields_convs = 0;
field_names = 0;
nfields = 0;
_hx_set_finalizer(this, finalize);
}
void destroy()
{
if (r)
{
if (fields_convs)
free(fields_convs);
if (field_names)
free(field_names);
fields_convs = 0;
field_names = 0;
mysql_free_result(r);
r = 0;
}
}
int numRows() { return mysql_num_rows(r); }
static void finalize(Dynamic obj)
{
((Result *)(obj.mPtr))->destroy();
}
};
Result *getResult(Dynamic o)
{
Result *result = dynamic_cast<Result *>(o.mPtr);
if (!result)
HXTHROW("Invalid result");
return result;
}
cpp::Function< Dynamic(Dynamic) > gDataToBytes;
cpp::Function< Dynamic(Float) > gDateFromSeconds;
}
void _hx_mysql_set_conversion(
cpp::Function< Dynamic(Dynamic) > inDataToBytes,
cpp::Function< Dynamic(Float) > inDateFromSeconds )
{
gDataToBytes = inDataToBytes;
gDateFromSeconds = inDateFromSeconds;
}
/**
result_get_length : 'result -> int
<doc>Return the number of rows returned or affected</doc>
**/
int _hx_mysql_result_get_length(Dynamic handle)
{
if( handle->__GetType() == vtInt )
return handle;
return getResult(handle)->numRows();
}
/**
result_get_nfields : 'result -> int
<doc>Return the number of fields in a result row</doc>
**/
int _hx_mysql_result_get_nfields(Dynamic handle)
{
if( handle->__GetType() == vtInt )
return 0;
return getResult(handle)->nfields;
}
/**
result_get_fields_names : 'result -> string array
<doc>Return the fields names corresponding results columns</doc>
**/
Array<String> _hx_mysql_result_get_fields_names(Dynamic handle)
{
Result *r = getResult(handle);
MYSQL_FIELD *fields = mysql_fetch_fields(r->r);
int count = r->nfields;
Array<String> output = Array_obj<String>::__new(count);
for(int k=0;k<count;k++)
output[k] = String(fields[k].name);
return output;
}
/**
result_next : 'result -> object?
<doc>
Return the next row if available. A row is represented
as an object, which fields have been converted to the
corresponding Neko value (int, float or string). For
Date and DateTime you can specify your own conversion
function using [result_set_conv_date]. By default they're
returned as plain strings. Additionally, the TINYINT(1) will
be converted to either true or false if equal to 0.
</doc>
**/
Dynamic _hx_mysql_result_next(Dynamic handle)
{
Result *r = getResult(handle);
MYSQL_ROW row = mysql_fetch_row(r->r);
if( !row )
return null();
int count = r->nfields;
hx::Anon cur = hx::Anon_obj::Create(0);
r->current = row;
unsigned long *lengths = 0;
for(int i=0;i<r->nfields;i++)
{
if( row[i] )
{
Dynamic v;
switch( r->fields_convs[i] )
{
case CONV_INT:
v = atoi(row[i]);
break;
case CONV_STRING:
v = String(row[i]);
break;
case CONV_BOOL:
v = *row[i] != '0';
break;
case CONV_FLOAT:
v = atof(row[i]);
break;
case CONV_BINARY:
{
if( lengths == NULL )
{
lengths = mysql_fetch_lengths(r->r);
if( lengths == NULL )
HXTHROW("mysql_fetch_lengths");
}
Array<unsigned char> buf = Array_obj<unsigned char>::__new(lengths[i],lengths[i]);
memcpy(&buf[0],row[i],lengths[i]);
v = gDataToBytes.call(buf);
}
break;
case CONV_DATE:
{
struct tm t;
sscanf(row[i],"%4d-%2d-%2d",&t.tm_year,&t.tm_mon,&t.tm_mday);
t.tm_hour = 0;
t.tm_min = 0;
t.tm_sec = 0;
t.tm_isdst = -1;
t.tm_year -= 1900;
t.tm_mon--;
v = gDateFromSeconds.call((int)mktime(&t));
}
break;
case CONV_DATETIME:
{
struct tm t;
sscanf(row[i],"%4d-%2d-%2d %2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec);
t.tm_isdst = -1;
t.tm_year -= 1900;
t.tm_mon--;
v = gDateFromSeconds.call(mktime(&t));
}
break;
default:
break;
}
cur->__SetField(r->field_names[i],v, hx::paccDynamic );
}
}
return cur;
}
/**
result_get : 'result -> n:int -> string
<doc>Return the [n]th field of the current row</doc>
**/
String _hx_mysql_result_get(Dynamic handle,int n)
{
Result *r = getResult(handle);
if( n < 0 || n >= r->nfields )
HXTHROW("Invalid index");
if( !r->current )
{
_hx_mysql_result_next(handle);
if( !r->current )
HXTHROW("No more results");
}
return String(r->current[n]);
}
/**
result_get_int : 'result -> n:int -> int
<doc>Return the [n]th field of the current row as an integer (or 0)</doc>
**/
int _hx_mysql_result_get_int(Dynamic handle,int n)
{
Result *r = getResult(handle);
if( n < 0 || n >= r->nfields )
HXTHROW("Invalid index");
if( !r->current )
{
_hx_mysql_result_next(handle);
if( !r->current )
HXTHROW("No more results");
}
const char *s = r->current[n];
return s?atoi(s):0;
}
/**
result_get_float : 'result -> n:int -> float
<doc>Return the [n]th field of the current row as a float (or 0)</doc>
**/
Float _hx_mysql_result_get_float(Dynamic handle,int n)
{
Result *r = getResult(handle);
if( n < 0 || n >= r->nfields )
HXTHROW("Invalid index");
if( !r->current )
{
_hx_mysql_result_next(handle);
if( !r->current )
HXTHROW("No more results");
}
const char *s = r->current[n];
return s?atof(s):0;
}
static CONV convert_type( enum enum_field_types t, int flags, unsigned int length ) {
// FIELD_TYPE_TIME
// FIELD_TYPE_YEAR
// FIELD_TYPE_NEWDATE
// FIELD_TYPE_NEWDATE + 2: // 5.0 MYSQL_TYPE_BIT
switch( t ) {
case FIELD_TYPE_TINY:
if( length == 1 )
return CONV_BOOL;
case FIELD_TYPE_SHORT:
case FIELD_TYPE_LONG:
case FIELD_TYPE_INT24:
return CONV_INT;
case FIELD_TYPE_LONGLONG:
case FIELD_TYPE_DECIMAL:
case FIELD_TYPE_FLOAT:
case FIELD_TYPE_DOUBLE:
case 246: // 5.0 MYSQL_NEW_DECIMAL
return CONV_FLOAT;
case FIELD_TYPE_BLOB:
case FIELD_TYPE_TINY_BLOB:
case FIELD_TYPE_MEDIUM_BLOB:
case FIELD_TYPE_LONG_BLOB:
if( (flags & BINARY_FLAG) != 0 )
return CONV_BINARY;
return CONV_STRING;
case FIELD_TYPE_DATETIME:
case FIELD_TYPE_TIMESTAMP:
return CONV_DATETIME;
case FIELD_TYPE_DATE:
return CONV_DATE;
case FIELD_TYPE_NULL:
case FIELD_TYPE_ENUM:
case FIELD_TYPE_SET:
//case FIELD_TYPE_VAR_STRING:
//case FIELD_TYPE_GEOMETRY:
// 5.0 MYSQL_TYPE_VARCHAR
default:
if( (flags & BINARY_FLAG) != 0 )
return CONV_BINARY;
return CONV_STRING;
}
}
static Result *alloc_result( Connection *c, MYSQL_RES *r )
{
Result *res = new Result();
res->create(r);
int num_fields = mysql_num_fields(r);
int i,j;
MYSQL_FIELD *fields = mysql_fetch_fields(r);
res->current = 0;
res->nfields = num_fields;
res->field_names = (String *)malloc(sizeof(String)*num_fields);
res->fields_convs = (CONV*)malloc(sizeof(CONV)*num_fields);
for(i=0;i<num_fields;i++)
{
String name;
if( strchr(fields[i].name,'(') )
name = String::createPermanent("???",3); // looks like an inner request : prevent hashing + cashing it
else
name = String::createPermanent(fields[i].name, -1);
res->field_names[i] = name;
res->fields_convs[i] = convert_type(fields[i].type,fields[i].flags,fields[i].length);
}
return res;
}
// ---------------------------------------------------------------
// Connection
/** <doc><h2>Connection</h2></doc> **/
/**
close : 'connection -> void
<doc>Close the connection. Any subsequent operation will fail on it</doc>
**/
Dynamic _hx_mysql_close(Dynamic handle)
{
Connection *connection = getConnection(handle);
connection->destroy();
return true;
}
/**
select_db : 'connection -> string -> void
<doc>Select the database</doc>
**/
void _hx_mysql_select_db(Dynamic handle,String db)
{
Connection *connection = getConnection(handle);
if( mysql_select_db(connection->m,db.utf8_str()) != 0 )
error(connection->m,"Failed to select database :");
}
/**
request : 'connection -> string -> 'result
<doc>Execute an SQL request. Exception on error</doc>
**/
Dynamic _hx_mysql_request(Dynamic handle,String req)
{
Connection *connection = getConnection(handle);
if( mysql_real_query(connection->m,req.utf8_str(),req.length) != 0 )
error(connection->m,req);
MYSQL_RES *res = mysql_store_result(connection->m);
if( !res )
{
if( mysql_field_count(connection->m) == 0 )
return mysql_affected_rows(connection->m);
else
error(connection->m,req);
}
return alloc_result(connection,res);
}
/**
escape : 'connection -> string -> string
<doc>Escape the string for inserting into a SQL request</doc>
**/
struct AutoBuf
{
AutoBuf(int inLen) { buffer = new char[inLen]; }
~AutoBuf() { delete [] buffer; }
char *buffer;
};
String _hx_mysql_escape(Dynamic handle,String str)
{
Connection *connection = getConnection(handle);
int len = str.length * 2 + 1;
AutoBuf sout(len);
int finalLen = mysql_real_escape_string(connection->m,sout.buffer,str.utf8_str(),str.length);
if( finalLen < 0 )
hx::Throw( HX_CSTRING("Unsupported charset : ") + String(mysql_character_set_name(connection->m)) );
return String::create(sout.buffer,finalLen);
}
// ---------------------------------------------------------------
// Sql
/**
connect : { host => string, port => int, user => string, pass => string, socket => string? } -> 'connection
<doc>Connect to a database using the connection informations</doc>
**/
Dynamic _hx_mysql_connect(Dynamic params)
{
String host = params->__Field(HX_CSTRING("host"), hx::paccDynamic );
int port = params->__Field(HX_CSTRING("port"), hx::paccDynamic);
String user = params->__Field(HX_CSTRING("user"), hx::paccDynamic);
String pass = params->__Field(HX_CSTRING("pass"), hx::paccDynamic);
String socket = params->__Field(HX_CSTRING("socket"), hx::paccDynamic );
MYSQL *cnx = mysql_init(NULL);
if( mysql_real_connect(cnx,host.utf8_str(),user.utf8_str(),pass.utf8_str(),NULL,port,socket.utf8_str(),0) == NULL )
{
String error = HX_CSTRING("Failed to connect to mysql server : ") + String(mysql_error(cnx));
mysql_close(cnx);
hx::Throw(error);
}
Connection *connection = new Connection();
connection->create(cnx);
return connection;
}

View File

@ -0,0 +1,511 @@
/* ************************************************************************ */
/* */
/* MYSQL 5.0 Protocol Implementation */
/* Copyright (c)2008 Nicolas Cannasse */
/*
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 <stdlib.h>
#include <memory.h>
#include <string.h>
#include <stdio.h>
#include "my_proto.h"
static void error( MYSQL *m, const char *err, const char *param ) {
if( param ) {
unsigned int max = MAX_ERR_SIZE - (strlen(err) + 3);
if( strlen(param) > max ) {
char *p2 = (char*)malloc(max + 1);
memcpy(p2,param,max-3);
p2[max - 3] = '.';
p2[max - 2] = '.';
p2[max - 1] = '.';
p2[max] = 0;
sprintf(m->last_error,err,param);
free(p2);
return;
}
}
sprintf(m->last_error,err,param);
m->errcode = -1;
}
static void save_error( MYSQL *m, MYSQL_PACKET *p ) {
int ecode;
p->pos = 0;
// seems like we sometimes get some FFFFFF sequences before
// the actual error...
do {
if( myp_read_byte(p) != 0xFF ) {
m->errcode = -1;
error(m,"Failed to decode error",NULL);
return;
}
ecode = myp_read_ui16(p);
} while( ecode == 0xFFFF );
if( m->is41 && p->buf[p->pos] == '#' )
p->pos += 6; // skip sqlstate marker
error(m,"%s",myp_read_string(p));
m->errcode = ecode;
}
static int myp_ok( MYSQL *m, int allow_others ) {
int code;
MYSQL_PACKET *p = &m->packet;
if( !myp_read_packet(m,p) ) {
error(m,"Failed to read packet",NULL);
return 0;
}
code = myp_read_byte(p);
if( code == 0x00 )
return 1;
if( code == 0xFF )
save_error(m,p);
else if( allow_others )
return 1;
else
error(m,"Invalid packet error",NULL);
return 0;
}
static void myp_close( MYSQL *m ) {
psock_close(m->s);
m->s = INVALID_SOCKET;
}
MYSQL *mysql_init( void *unused ) {
MYSQL *m = (MYSQL*)malloc(sizeof(struct _MYSQL));
psock_init();
memset(m,0,sizeof(struct _MYSQL));
m->s = INVALID_SOCKET;
error(m,"NO ERROR",NULL);
m->errcode = 0;
m->last_field_count = -1;
m->last_insert_id = -1;
m->affected_rows = -1;
return m;
}
MYSQL *mysql_real_connect( MYSQL *m, const char *host, const char *user, const char *pass, void *unused, int port, const char *socket, int options ) {
PHOST h;
char scramble_buf[21];
MYSQL_PACKET *p = &m->packet;
int pcount = 1;
if( socket && *socket ) {
error(m,"Unix Socket connections are not supported",NULL);
return NULL;
}
h = phost_resolve(host);
if( h == UNRESOLVED_HOST ) {
error(m,"Failed to resolve host '%s'",host);
return NULL;
}
m->s = psock_create();
if( m->s == INVALID_SOCKET ) {
error(m,"Failed to create socket",NULL);
return NULL;
}
psock_set_fastsend(m->s,1);
psock_set_timeout(m->s,50); // 50 seconds
if( psock_connect(m->s,h,port) != PS_OK ) {
myp_close(m);
error(m,"Failed to connect on host '%s'",host);
return NULL;
}
if( !myp_read_packet(m,p) ) {
myp_close(m);
error(m,"Failed to read handshake packet",NULL);
return NULL;
}
// process handshake packet
{
char filler[13];
unsigned int len;
m->infos.proto_version = myp_read_byte(p);
// this seems like an error packet
if( m->infos.proto_version == 0xFF ) {
myp_close(m);
save_error(m,p);
return NULL;
}
m->infos.server_version = strdup(myp_read_string(p));
m->infos.thread_id = myp_read_int(p);
myp_read(p,scramble_buf,8);
myp_read_byte(p); // should be 0
m->infos.server_flags = myp_read_ui16(p);
m->infos.server_charset = myp_read_byte(p);
m->infos.server_status = myp_read_ui16(p);
m->infos.server_flags |= myp_read_ui16(p) << 16;
len = myp_read_byte(p);
myp_read(p,filler,10);
// try to disable 41
m->is41 = (m->infos.server_flags & FL_PROTOCOL_41) != 0;
if( !p->error && m->is41 )
myp_read(p,scramble_buf + 8,13);
if( p->pos != p->size )
myp_read_string(p); // 5.5+
if( p->error ) {
myp_close(m);
error(m,"Failed to decode server handshake",NULL);
return NULL;
}
// fill answer packet
{
unsigned int flags = m->infos.server_flags;
int max_packet_size = 0x01000000;
SHA1_DIGEST hpass;
char filler[23];
flags &= (FL_PROTOCOL_41 | FL_TRANSACTIONS | FL_SECURE_CONNECTION);
myp_begin_packet(p,128);
if( m->is41 ) {
myp_write_int(p,flags);
myp_write_int(p,max_packet_size);
myp_write_byte(p,m->infos.server_charset);
memset(filler,0,23);
myp_write(p,filler,23);
myp_write_string(p,user);
if( *pass ) {
myp_encrypt_password(pass,scramble_buf,hpass);
myp_write_bin(p,SHA1_SIZE);
myp_write(p,hpass,SHA1_SIZE);
myp_write_byte(p,0);
} else
myp_write_bin(p,0);
} else {
myp_write_ui16(p,flags);
// max_packet_size
myp_write_byte(p,0xFF);
myp_write_byte(p,0xFF);
myp_write_byte(p,0xFF);
myp_write_string(p,user);
if( *pass ) {
char hpass[SEED_LENGTH_323 + 1];
myp_encrypt_pass_323(pass,scramble_buf,hpass);
hpass[SEED_LENGTH_323] = 0;
myp_write(p,hpass,SEED_LENGTH_323 + 1);
} else
myp_write_bin(p,0);
}
}
}
// send connection packet
send_cnx_packet:
if( !myp_send_packet(m,p,&pcount) ) {
myp_close(m);
error(m,"Failed to send connection packet",NULL);
return NULL;
}
// read answer packet
if( !myp_read_packet(m,p) ) {
myp_close(m);
error(m,"Failed to read packet",NULL);
return NULL;
}
// increase packet counter (because we read one packet)
pcount++;
// process answer
{
int code = myp_read_byte(p);
switch( code ) {
case 0: // OK packet
break;
case 0xFF: // ERROR
myp_close(m);
save_error(m,p);
return NULL;
case 0xFE: // EOF
// we are asked to send old password authentification
if( p->size == 1 ) {
char hpass[SEED_LENGTH_323 + 1];
myp_encrypt_pass_323(pass,scramble_buf,hpass);
hpass[SEED_LENGTH_323] = 0;
myp_begin_packet(p,0);
myp_write(p,hpass,SEED_LENGTH_323 + 1);
goto send_cnx_packet;
}
// fallthrough
default:
myp_close(m);
error(m,"Invalid packet error",NULL);
return NULL;
}
}
// we are connected, setup a longer timeout
psock_set_timeout(m->s,18000);
return m;
}
int mysql_select_db( MYSQL *m, const char *dbname ) {
MYSQL_PACKET *p = &m->packet;
int pcount = 0;
myp_begin_packet(p,0);
myp_write_byte(p,COM_INIT_DB);
// send dbname without trailing 0x00
myp_write(p,dbname,strlen(dbname));
if( !myp_send_packet(m,p,&pcount) ) {
error(m,"Failed to send packet",NULL);
return -1;
}
return myp_ok(m,0) ? 0 : -1;
}
int mysql_real_query( MYSQL *m, const char *query, int qlength ) {
MYSQL_PACKET *p = &m->packet;
int pcount = 0;
myp_begin_packet(p,0);
myp_write_byte(p,COM_QUERY);
myp_write(p,query,qlength);
m->last_field_count = -1;
m->affected_rows = -1;
m->last_insert_id = -1;
if( !myp_send_packet(m,p,&pcount) ) {
error(m,"Failed to send packet",NULL);
return -1;
}
if( !myp_ok(m,1) )
return -1;
p->id = IS_QUERY;
return 0;
}
static int do_store( MYSQL *m, MYSQL_RES *r ) {
int i;
MYSQL_PACKET *p = &m->packet;
p->pos = 0;
r->nfields = myp_read_bin(p);
if( p->error ) return 0;
r->fields = (MYSQL_FIELD*)malloc(sizeof(MYSQL_FIELD) * r->nfields);
memset(r->fields,0,sizeof(MYSQL_FIELD) * r->nfields);
for(i=0;i<r->nfields;i++) {
if( !myp_read_packet(m,p) )
return 0;
{
MYSQL_FIELD *f = r->fields + i;
f->catalog = m->is41 ? myp_read_bin_str(p) : NULL;
f->db = m->is41 ? myp_read_bin_str(p) : NULL;
f->table = myp_read_bin_str(p);
f->org_table = m->is41 ? myp_read_bin_str(p) : NULL;
f->name = myp_read_bin_str(p);
f->org_name = m->is41 ? myp_read_bin_str(p) : NULL;
if( m->is41 ) myp_read_byte(p);
f->charset = m->is41 ? myp_read_ui16(p) : 0x08;
f->length = m->is41 ? myp_read_int(p) : myp_read_bin(p);
f->type = (FIELD_TYPE)(m->is41 ? myp_read_byte(p) : myp_read_bin(p));
f->flags = m->is41 ? myp_read_ui16(p) : myp_read_bin(p);
f->decimals = myp_read_byte(p);
if( m->is41 ) myp_read_byte(p); // should be 0
if( m->is41 ) myp_read_byte(p); // should be 0
if( p->error )
return 0;
}
}
// first EOF packet
if( !myp_read_packet(m,p) )
return 0;
if( myp_read_byte(p) != 0xFE || p->size >= 9 )
return 0;
// reset packet buffer (to prevent to store large buffer in row data)
free(p->buf);
p->buf = NULL;
p->mem = 0;
// datas
while( 1 ) {
if( !myp_read_packet(m,p) )
return 0;
// EOF : end of datas
if( (unsigned char)p->buf[0] == 0xFE && p->size < 9 )
break;
// ERROR ?
if( (unsigned char)p->buf[0] == 0xFF ) {
save_error(m,p);
return 0;
}
// allocate one more row
if( r->row_count == r->memory_rows ) {
MYSQL_ROW_DATA *rows;
r->memory_rows = r->memory_rows ? (r->memory_rows << 1) : 1;
rows = (MYSQL_ROW_DATA*)malloc(r->memory_rows * sizeof(MYSQL_ROW_DATA));
memcpy(rows,r->rows,r->row_count * sizeof(MYSQL_ROW_DATA));
free(r->rows);
r->rows = rows;
}
// read row fields
{
MYSQL_ROW_DATA *current = r->rows + r->row_count++;
int prev = 0;
current->raw = p->buf;
current->lengths = (unsigned long*)malloc(sizeof(unsigned long) * r->nfields);
current->datas = (char**)malloc(sizeof(char*) * r->nfields);
for(i=0;i<r->nfields;i++) {
int l = myp_read_bin(p);
if( !p->error )
p->buf[prev] = 0;
if( l == -1 ) {
current->lengths[i] = 0;
current->datas[i] = NULL;
} else {
current->lengths[i] = l;
current->datas[i] = p->buf + p->pos;
p->pos += l;
}
prev = p->pos;
}
if( !p->error )
p->buf[prev] = 0;
}
// the packet buffer as been stored, don't reuse it
p->buf = NULL;
p->mem = 0;
if( p->error )
return 0;
}
return 1;
}
MYSQL_RES *mysql_store_result( MYSQL *m ) {
MYSQL_RES *r;
MYSQL_PACKET *p = &m->packet;
if( p->id != IS_QUERY )
return NULL;
// OK without result
if( p->buf[0] == 0 ) {
p->pos = 0;
m->last_field_count = myp_read_byte(p); // 0
m->affected_rows = myp_read_bin(p);
m->last_insert_id = myp_read_bin(p);
return NULL;
}
r = (MYSQL_RES*)malloc(sizeof(struct _MYSQL_RES));
memset(r,0,sizeof(struct _MYSQL_RES));
m->errcode = 0;
if( !do_store(m,r) ) {
mysql_free_result(r);
if( !m->errcode )
error(m,"Failure while storing result",NULL);
return NULL;
}
m->last_field_count = r->nfields;
return r;
}
int mysql_field_count( MYSQL *m ) {
return m->last_field_count;
}
int mysql_affected_rows( MYSQL *m ) {
return m->affected_rows;
}
int mysql_escape_string( MYSQL *m, char *sout, const char *sin, int length ) {
return myp_escape_string(m->infos.server_charset,sout,sin,length);
}
const char *mysql_character_set_name( MYSQL *m ) {
const char *name = myp_charset_name(m->infos.server_charset);
if( name == NULL ) {
static char tmp[512];
sprintf(tmp,"#%d",m->infos.server_charset);
return tmp;
}
return name;
}
int mysql_real_escape_string( MYSQL *m, char *sout, const char *sin, int length ) {
if( !myp_supported_charset(m->infos.server_charset) )
return -1;
if( m->infos.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES )
return myp_escape_quotes(m->infos.server_charset,sout,sin,length);
return myp_escape_string(m->infos.server_charset,sout,sin,length);
}
void mysql_close( MYSQL *m ) {
MYSQL_PACKET *p = &m->packet;
int pcount = 0;
myp_begin_packet(p,0);
myp_write_byte(p,COM_QUIT);
myp_send_packet(m,p,&pcount);
myp_close(m);
free(m->packet.buf);
free(m->infos.server_version);
free(m);
}
const char *mysql_error( MYSQL *m ) {
return m->last_error;
}
// RESULTS API
unsigned int mysql_num_rows( MYSQL_RES *r ) {
return r->row_count;
}
int mysql_num_fields( MYSQL_RES *r ) {
return r->nfields;
}
MYSQL_FIELD *mysql_fetch_fields( MYSQL_RES *r ) {
return r->fields;
}
unsigned long *mysql_fetch_lengths( MYSQL_RES *r ) {
return r->current ? r->current->lengths : NULL;
}
MYSQL_ROW mysql_fetch_row( MYSQL_RES * r ) {
MYSQL_ROW_DATA *cur = r->current;
if( cur == NULL )
cur = r->rows;
else {
// free the previous result, since we're done with it
free(cur->datas);
free(cur->lengths);
free(cur->raw);
cur->datas = NULL;
cur->lengths = NULL;
cur->raw = NULL;
// next
cur++;
}
if( cur >= r->rows + r->row_count ) {
free(r->rows);
r->rows = NULL;
r->memory_rows = 0;
cur = NULL;
}
r->current = cur;
return cur ? cur->datas : NULL;
}
void mysql_free_result( MYSQL_RES *r ) {
if( r->fields ) {
int i;
for(i=0;i<r->nfields;i++) {
MYSQL_FIELD *f = r->fields + i;
free(f->catalog);
free(f->db);
free(f->table);
free(f->org_table);
free(f->name);
free(f->org_name);
}
free(r->fields);
}
if( r->rows ) {
int i;
for(i=0;i<r->row_count;i++) {
MYSQL_ROW_DATA *row = r->rows + i;
free(row->datas);
free(row->lengths);
free(row->raw);
}
free(r->rows);
}
free(r);
}
/* ************************************************************************ */

View File

@ -0,0 +1,414 @@
/* ************************************************************************ */
/* */
/* MYSQL 5.0 Protocol Implementation */
/* Copyright (c)2008 Nicolas Cannasse */
/*
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 <hxcpp.h>
#include <hx/OS.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "my_proto.h"
#define MAX_PACKET_LENGTH 0xFFFFFF
int myp_recv( MYSQL *m, void *buf, int size ) {
hx::AutoGCFreeZone blocking;
return myp_recv_no_gc(m,buf,size);
}
int myp_recv_no_gc( MYSQL *m, void *buf, int size ) {
while( size ) {
int len = psock_recv_no_gc(m->s,(char*)buf,size);
if( len <= 0 ) return size == 0 ? 1 : 0;
buf = ((char*)buf) + len;
size -= len;
}
return 1;
}
int myp_send( MYSQL *m, void *buf, int size ) {
hx::AutoGCFreeZone blocking;
return myp_send_no_gc(m,buf,size);
}
int myp_send_no_gc( MYSQL *m, void *buf, int size ) {
while( size ) {
int len = psock_send_no_gc(m->s,(char*)buf,size);
if( len <= 0 ) return size == 0 ? 1 : 0;
buf = ((char*)buf) + len;
size -= len;
}
return 1;
}
int myp_read( MYSQL_PACKET *p, void *buf, int size ) {
if( p->size - p->pos < size ) {
p->error = 1;
return 0;
}
memcpy(buf,p->buf + p->pos,size);
p->pos += size;
return 1;
}
unsigned char myp_read_byte( MYSQL_PACKET *p ) {
unsigned char c;
if( !myp_read(p,&c,1) )
return 0;
return c;
}
unsigned short myp_read_ui16( MYSQL_PACKET *p ) {
unsigned short i;
if( !myp_read(p,&i,2) )
return 0;
return i;
}
int myp_read_int( MYSQL_PACKET *p ) {
int i;
if( !myp_read(p,&i,4) )
return 0;
return i;
}
int myp_read_bin( MYSQL_PACKET *p ) {
int c = myp_read_byte(p);
if( c <= 250 )
return c;
if( c == 251 )
return -1; // NULL
if( c == 252 )
return myp_read_ui16(p);
if( c == 253 ) {
c = 0;
myp_read(p,&c,3);
return c;
}
if( c == 254 )
return myp_read_int(p);
p->error = 1;
return 0;
}
const char *myp_read_string( MYSQL_PACKET *p ) {
char *str;
if( p->pos >= p->size ) {
p->error = 1;
return "";
}
str = p->buf + p->pos;
p->pos += strlen(str) + 1;
return str;
}
char *myp_read_bin_str( MYSQL_PACKET *p ) {
int size = myp_read_bin(p);
char *str;
if( size == -1 )
return NULL;
if( p->error || p->pos + size > p->size ) {
p->error = 1;
return NULL;
}
str = (char*)malloc(size + 1);
memcpy(str,p->buf + p->pos, size);
str[size] = 0;
p->pos += size;
return str;
}
int myp_read_packet( MYSQL *m, MYSQL_PACKET *p ) {
hx::AutoGCFreeZone blocking;
unsigned int psize;
p->pos = 0;
p->error = 0;
if( !myp_recv_no_gc(m,&psize,4) ) {
p->error = 1;
p->size = 0;
return 0;
}
//p->id = (psize >> 24);
psize &= 0xFFFFFF;
p->size = psize;
if( p->mem < (int)psize ) {
free(p->buf);
p->buf = (char*)malloc(psize + 1);
p->mem = psize;
}
p->buf[psize] = 0;
if( psize == 0 || !myp_recv_no_gc(m,p->buf,psize) ) {
p->error = 1;
p->size = 0;
p->buf[0] = 0;
return 0;
}
return 1;
}
int myp_send_packet( MYSQL *m, MYSQL_PACKET *p, int *packet_counter ) {
hx::AutoGCFreeZone blocking;
unsigned int header;
char *buf = p->buf;
int size = p->size;
int next = 1;
while( next ) {
int psize;
if( size >= MAX_PACKET_LENGTH )
psize = MAX_PACKET_LENGTH;
else {
psize = size;
next = 0;
}
header = psize | (((*packet_counter)++) << 24);
if( !myp_send_no_gc(m,&header,4) || !myp_send_no_gc(m,buf,psize) ) {
p->error = 1;
return 0;
}
buf += psize;
size -= psize;
}
return 1;
}
void myp_begin_packet( MYSQL_PACKET *p, int minsize ) {
if( p->mem < minsize ) {
free(p->buf);
p->buf = (char*)malloc(minsize + 1);
p->mem = minsize;
}
p->error = 0;
p->size = 0;
}
void myp_write( MYSQL_PACKET *p, const void *data, int size ) {
if( p->size + size > p->mem ) {
char *buf2;
if( p->mem == 0 ) p->mem = 32;
do {
p->mem <<= 1;
} while( p->size + size > p->mem );
buf2 = (char*)malloc(p->mem + 1);
memcpy(buf2,p->buf,p->size);
free(p->buf);
p->buf = buf2;
}
memcpy( p->buf + p->size , data, size );
p->size += size;
}
void myp_write_byte( MYSQL_PACKET *p, int i ) {
unsigned char c = (unsigned char)i;
myp_write(p,&c,1);
}
void myp_write_ui16( MYSQL_PACKET *p, int i ) {
unsigned short c = (unsigned char)i;
myp_write(p,&c,2);
}
void myp_write_int( MYSQL_PACKET *p, int i ) {
myp_write(p,&i,4);
}
void myp_write_string( MYSQL_PACKET *p, const char *str ) {
myp_write(p,str,strlen(str) + 1);
}
void myp_write_bin( MYSQL_PACKET *p, int size ) {
if( size <= 250 ) {
unsigned char l = (unsigned char)size;
myp_write(p,&l,1);
} else if( size < 0x10000 ) {
unsigned char c = 252;
unsigned short l = (unsigned short)size;
myp_write(p,&c,1);
myp_write(p,&l,2);
} else if( size < 0x1000000 ) {
unsigned char c = 253;
unsigned int l = (unsigned short)size;
myp_write(p,&c,1);
myp_write(p,&l,3);
} else {
unsigned char c = 254;
myp_write(p,&c,1);
myp_write(p,&size,4);
}
}
void myp_crypt( unsigned char *out, const unsigned char *s1, const unsigned char *s2, unsigned int len ) {
unsigned int i;
for(i=0;i<len;i++)
out[i] = s1[i] ^ s2[i];
}
void myp_encrypt_password( const char *pass, const char *seed, SHA1_DIGEST out ) {
SHA1_CTX ctx;
SHA1_DIGEST hash_stage1, hash_stage2;
// stage 1: hash password
sha1_init(&ctx);
sha1_update(&ctx,(const unsigned char *)pass,strlen(pass));;
sha1_final(&ctx,hash_stage1);
// stage 2: hash stage 1; note that hash_stage2 is stored in the database
sha1_init(&ctx);
sha1_update(&ctx, hash_stage1, SHA1_SIZE);
sha1_final(&ctx, hash_stage2);
// create crypt string as sha1(message, hash_stage2)
sha1_init(&ctx);
sha1_update(&ctx, (const unsigned char *)seed, SHA1_SIZE);
sha1_update(&ctx, hash_stage2, SHA1_SIZE);
sha1_final( &ctx, out );
// xor the result
myp_crypt(out,out,hash_stage1,SHA1_SIZE);
}
typedef struct {
unsigned long seed1;
unsigned long seed2;
unsigned long max_value;
double max_value_dbl;
} rand_ctx;
static void random_init( rand_ctx *r, unsigned long seed1, unsigned long seed2 ) {
r->max_value = 0x3FFFFFFFL;
r->max_value_dbl = (double)r->max_value;
r->seed1 = seed1 % r->max_value ;
r->seed2 = seed2 % r->max_value;
}
static double myp_rnd( rand_ctx *r ) {
r->seed1 = (r->seed1 * 3 + r->seed2) % r->max_value;
r->seed2 = (r->seed1 + r->seed2 + 33) % r->max_value;
return (((double) r->seed1)/r->max_value_dbl);
}
static void hash_password( unsigned long *result, const char *password, int password_len ) {
unsigned long nr = 1345345333L, add = 7, nr2 = 0x12345671L;
unsigned long tmp;
const char *password_end = password + password_len;
for(; password < password_end; password++) {
if( *password == ' ' || *password == '\t' )
continue;
tmp = (unsigned long)(unsigned char)*password;
nr ^= (((nr & 63)+add)*tmp)+(nr << 8);
nr2 += (nr2 << 8) ^ nr;
add += tmp;
}
result[0] = nr & (((unsigned long) 1L << 31) -1L);
result[1] = nr2 & (((unsigned long) 1L << 31) -1L);
}
void myp_encrypt_pass_323( const char *password, const char seed[SEED_LENGTH_323], char to[SEED_LENGTH_323] ) {
rand_ctx r;
unsigned long hash_pass[2], hash_seed[2];
char extra, *to_start = to;
const char *seed_end = seed + SEED_LENGTH_323;
hash_password(hash_pass,password,(unsigned int)strlen(password));
hash_password(hash_seed,seed,SEED_LENGTH_323);
random_init(&r,hash_pass[0] ^ hash_seed[0],hash_pass[1] ^ hash_seed[1]);
while( seed < seed_end ) {
*to++ = (char)(floor(myp_rnd(&r)*31)+64);
seed++;
}
extra= (char)(floor(myp_rnd(&r)*31));
while( to_start != to )
*(to_start++) ^= extra;
}
// defined in mysql/strings/ctype-*.c
const char *myp_charset_name( int charset ) {
switch( charset ) {
case 8:
case 31:
case 47:
return "latin1";
case 63:
return "binary";
// 101+ : utf16
// 160+ : utf32
case 33:
case 83:
case 223:
case 254:
return "utf8";
case 45:
case 46:
return "utf8mb4"; // superset of utf8 with up to 4 bytes per-char
default:
if( charset >= 192 && charset <= 211 )
return "utf8";
if( charset >= 224 && charset <= 243 )
return "utf8mb4";
}
return NULL;
}
int myp_supported_charset( int charset ) {
return myp_charset_name(charset) != NULL;
}
int myp_escape_string( int charset, char *sout, const char *sin, int length ) {
// this is safe for UTF8 as well since mysql protects against invalid UTF8 char injection
const char *send = sin + length;
char *sbegin = sout;
while( sin != send ) {
char c = *sin++;
switch( c ) {
case 0:
*sout++ = '\\';
*sout++ = '0';
break;
case '\n':
*sout++ = '\\';
*sout++ = 'n';
break;
case '\r':
*sout++ = '\\';
*sout++ = 'r';
break;
case '\\':
*sout++ = '\\';
*sout++ = '\\';
break;
case '\'':
*sout++ = '\\';
*sout++ = '\'';
break;
case '"':
*sout++ = '\\';
*sout++ = '"';
break;
case '\032':
*sout++ = '\\';
*sout++ = 'Z';
break;
default:
*sout++ = c;
}
}
*sout = 0;
return sout - sbegin;
}
int myp_escape_quotes( int charset, char *sout, const char *sin, int length ) {
const char *send = sin + length;
char *sbegin = sout;
while( sin != send ) {
char c = *sin++;
*sout++ = c;
if( c == '\'' )
*sout++ = c;
}
*sout = 0;
return sout - sbegin;
}
/* ************************************************************************ */

View File

@ -0,0 +1,174 @@
/* ************************************************************************ */
/* */
/* MYSQL 5.0 Protocol Implementation */
/* Copyright (c)2008 Nicolas Cannasse */
/*
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 MY_PROTO_H
#define MY_PROTO_H
#include "mysql.h"
#include "socket.h"
#include "sha1.h"
typedef enum {
FL_LONG_PASSWORD = 1,
FL_FOUND_ROWS = 2,
FL_LONG_FLAG = 4,
FL_CONNECT_WITH_DB = 8,
FL_NO_SCHEMA = 16,
FL_COMPRESS = 32,
FL_ODBC = 64,
FL_LOCAL_FILES = 128,
FL_IGNORE_SPACE = 256,
FL_PROTOCOL_41 = 512,
FL_INTERACTIVE = 1024,
FL_SSL = 2048,
FL_IGNORE_SIGPIPE = 4096,
FL_TRANSACTIONS = 8192,
FL_RESERVED = 16384,
FL_SECURE_CONNECTION = 32768,
FL_MULTI_STATEMENTS = 65536,
FL_MULTI_RESULTS = 131072,
} MYSQL_FLAG;
typedef enum {
COM_SLEEP = 0x00,
COM_QUIT = 0x01,
COM_INIT_DB = 0x02,
COM_QUERY = 0x03,
COM_FIELD_LIST = 0x04,
//COM_CREATE_DB = 0x05,
//COM_DROP_DB = 0x06
COM_REFRESH = 0x07,
COM_SHUTDOWN = 0x08,
COM_STATISTICS = 0x09,
COM_PROCESS_INFO = 0x0A,
//COM_CONNECT = 0x0B,
COM_PROCESS_KILL = 0x0C,
COM_DEBUG = 0x0D,
COM_PING = 0x0E,
//COM_TIME = 0x0F,
//COM_DELAYED_INSERT = 0x10,
COM_CHANGE_USER = 0x11,
COM_BINLOG_DUMP = 0x12,
COM_TABLE_DUMP = 0x13,
COM_CONNECT_OUT = 0x14,
COM_REGISTER_SLAVE = 0x15,
COM_STMT_PREPARE = 0x16,
COM_STMT_EXECUTE = 0x17,
COM_STMT_SEND_LONG_DATA = 0x18,
COM_STMT_CLOSE = 0x19,
COM_STMT_RESET = 0x1A,
COM_SET_OPTION = 0x1B,
COM_STMT_FETCH = 0x1C,
} MYSQL_COMMAND;
typedef enum {
SERVER_STATUS_IN_TRANS = 1,
SERVER_STATUS_AUTOCOMMIT = 2,
SERVER_MORE_RESULTS_EXISTS = 8,
SERVER_QUERY_NO_GOOD_INDEX_USED = 16,
SERVER_QUERY_NO_INDEX_USED = 32,
SERVER_STATUS_CURSOR_EXISTS = 64,
SERVER_STATUS_LAST_ROW_SENT = 128,
SERVER_STATUS_DB_DROPPED = 256,
SERVER_STATUS_NO_BACKSLASH_ESCAPES = 512,
} MYSQL_SERVER_STATUS;
typedef struct {
unsigned char proto_version;
char *server_version;
unsigned int thread_id;
unsigned int server_flags;
unsigned char server_charset;
unsigned short server_status;
} MYSQL_INFOS;
typedef struct {
int id;
int error;
int size;
int pos;
int mem;
char *buf;
} MYSQL_PACKET;
#define MAX_ERR_SIZE 1024
#define IS_QUERY -123456
struct _MYSQL {
PSOCK s;
MYSQL_INFOS infos;
MYSQL_PACKET packet;
int is41;
int errcode;
int last_field_count;
int affected_rows;
int last_insert_id;
char last_error[MAX_ERR_SIZE];
};
typedef struct {
char *raw;
unsigned long *lengths;
char **datas;
} MYSQL_ROW_DATA;
struct _MYSQL_RES {
int nfields;
MYSQL_FIELD *fields;
MYSQL_ROW_DATA *rows;
MYSQL_ROW_DATA *current;
int row_count;
int memory_rows;
};
// network
int myp_recv_no_gc( MYSQL *m, void *buf, int size );
int myp_send_no_gc( MYSQL *m, void *buf, int size );
int myp_recv( MYSQL *m, void *buf, int size );
int myp_send( MYSQL *m, void *buf, int size );
int myp_read_packet( MYSQL *m, MYSQL_PACKET *p );
int myp_send_packet( MYSQL *m, MYSQL_PACKET *p, int *packet_counter );
// packet read
int myp_read( MYSQL_PACKET *p, void *buf, int size );
unsigned char myp_read_byte( MYSQL_PACKET *p );
unsigned short myp_read_ui16( MYSQL_PACKET *p );
int myp_read_int( MYSQL_PACKET *p );
const char *myp_read_string( MYSQL_PACKET *p );
int myp_read_bin( MYSQL_PACKET *p );
char *myp_read_bin_str( MYSQL_PACKET *p );
// packet write
void myp_begin_packet( MYSQL_PACKET *p, int minsize );
void myp_write( MYSQL_PACKET *p, const void *data, int size );
void myp_write_byte( MYSQL_PACKET *p, int b );
void myp_write_ui16( MYSQL_PACKET *p, int b );
void myp_write_int( MYSQL_PACKET *p, int b );
void myp_write_string( MYSQL_PACKET *p, const char *str );
void myp_write_bin( MYSQL_PACKET *p, int size );
// passwords
#define SEED_LENGTH_323 8
void myp_crypt( unsigned char *out, const unsigned char *s1, const unsigned char *s2, unsigned int len );
void myp_encrypt_password( const char *pass, const char *seed, SHA1_DIGEST out );
void myp_encrypt_pass_323( const char *pass, const char seed[SEED_LENGTH_323], char out[SEED_LENGTH_323] );
// escaping
int myp_supported_charset( int charset );
const char *myp_charset_name( int charset );
int myp_escape_string( int charset, char *sout, const char *sin, int length );
int myp_escape_quotes( int charset, char *sout, const char *sin, int length );
#endif
/* ************************************************************************ */

View File

@ -0,0 +1,120 @@
/* ************************************************************************ */
/* */
/* MYSQL 5.0 Protocol Implementation */
/* Copyright (c)2008 Nicolas Cannasse */
/*
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 MYSQL_H
#define MYSQL_H
struct _MYSQL;
struct _MYSQL_RES;
typedef struct _MYSQL MYSQL;
typedef struct _MYSQL_RES MYSQL_RES;
typedef char **MYSQL_ROW;
typedef enum enum_field_types {
FIELD_TYPE_DECIMAL = 0x00,
FIELD_TYPE_TINY = 0x01,
FIELD_TYPE_SHORT = 0x02,
FIELD_TYPE_LONG = 0x03,
FIELD_TYPE_FLOAT = 0x04,
FIELD_TYPE_DOUBLE = 0x05,
FIELD_TYPE_NULL = 0x06,
FIELD_TYPE_TIMESTAMP = 0x07,
FIELD_TYPE_LONGLONG = 0x08,
FIELD_TYPE_INT24 = 0x09,
FIELD_TYPE_DATE = 0x0A,
FIELD_TYPE_TIME = 0x0B,
FIELD_TYPE_DATETIME = 0x0C,
FIELD_TYPE_YEAR = 0x0D,
FIELD_TYPE_NEWDATE = 0x0E,
FIELD_TYPE_VARCHAR = 0x0F,
FIELD_TYPE_BIT = 0x10,
FIELD_TYPE_NEWDECIMAL = 0xF6,
FIELD_TYPE_ENUM = 0xF7,
FIELD_TYPE_SET = 0xF8,
FIELD_TYPE_TINY_BLOB = 0xF9,
FIELD_TYPE_MEDIUM_BLOB = 0xFA,
FIELD_TYPE_LONG_BLOB = 0xFB,
FIELD_TYPE_BLOB = 0xFC,
FIELD_TYPE_VAR_STRING = 0xFD,
FIELD_TYPE_STRING = 0xFE,
FIELD_TYPE_GEOMETRY = 0xFF
} FIELD_TYPE;
typedef enum {
NOT_NULL_FLAG = 1,
PRI_KEY_FLAG = 2,
UNIQUE_KEY_FLAG = 4,
MULTIPLE_KEY_FLAG = 8,
BLOB_FLAG = 16,
UNSIGNED_FLAG = 32,
ZEROFILL_FLAG = 64,
BINARY_FLAG = 128,
ENUM_FLAG = 256,
AUTO_INCREMENT_FLAG = 512,
TIMESTAMP_FLAG = 1024,
SET_FLAG = 2048,
NUM_FLAG = 32768,
} __FIELD_FLAG;
typedef struct {
char *catalog;
char *db;
char *table;
char *org_table;
char *name;
char *org_name;
int charset;
int length;
int flags;
int decimals;
FIELD_TYPE type;
} MYSQL_FIELD;
#define mysql_init mp_init
#define mysql_real_connect mp_real_connect
#define mysql_select_db mp_select_db
#define mysql_real_query mp_real_query
#define mysql_store_result mp_store_result
#define mysql_field_count mp_field_count
#define mysql_affected_rows mp_affected_rows
#define mysql_escape_string mp_escape_string
#define mysql_real_escape_string mp_real_escape_string
#define mysql_close mp_close
#define mysql_error mp_error
#define mysql_num_rows mp_num_rows
#define mysql_num_fields mp_num_fields
#define mysql_fetch_fields mp_fetch_fields
#define mysql_fetch_lengths mp_fetch_lengths
#define mysql_fetch_row mp_fetch_row
#define mysql_free_result mp_free_result
MYSQL *mysql_init( void * );
MYSQL *mysql_real_connect( MYSQL *m, const char *host, const char *user, const char *pass, void *unused, int port, const char *socket, int options );
int mysql_select_db( MYSQL *m, const char *dbname );
int mysql_real_query( MYSQL *m, const char *query, int qlength );
MYSQL_RES *mysql_store_result( MYSQL *m );
int mysql_field_count( MYSQL *m );
int mysql_affected_rows( MYSQL *m );
int mysql_escape_string( MYSQL *m, char *sout, const char *sin, int length );
int mysql_real_escape_string( MYSQL *m, char *sout, const char *sin, int length );
void mysql_close( MYSQL *m );
const char *mysql_error( MYSQL *m );
const char *mysql_character_set_name( MYSQL *m );
unsigned int mysql_num_rows( MYSQL_RES *r );
int mysql_num_fields( MYSQL_RES *r );
MYSQL_FIELD *mysql_fetch_fields( MYSQL_RES *r );
unsigned long *mysql_fetch_lengths( MYSQL_RES *r );
MYSQL_ROW mysql_fetch_row( MYSQL_RES * r );
void mysql_free_result( MYSQL_RES *r );
#endif
/* ************************************************************************ */

View File

@ -0,0 +1,128 @@
/*
* Copyright (C)2005-2012 Haxe Foundation
*
* 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 <hxcpp.h>
#include "sha1.h"
#include <stdio.h>
#include <string.h>
// original code by Steve Reid
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
#ifdef HXCPP_BIG_ENDIAN
# define blk0(i) block[i]
#else
# define blk0(i) (block[i] = (rol(block[i],24)&0xFF00FF00) \
|(rol(block[i],8)&0x00FF00FF))
#endif
#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
^block[(i+2)&15]^block[i&15],1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
static void sha1_transform( unsigned int state[5], unsigned char buffer[64] ) {
unsigned int a, b, c, d, e;
unsigned int block[16];
memcpy(block, buffer, 64);
/* Copy context->state[] to working vars */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
}
void sha1_init( SHA1_CTX *context ) {
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
void sha1_update( SHA1_CTX *context, const unsigned char *data, unsigned int len ) {
unsigned int i, j;
j = (context->count[0] >> 3) & 63;
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
context->count[1] += (len >> 29);
if ((j + len) > 63) {
memcpy(&context->buffer[j], data, (i = 64-j));
sha1_transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64 )
sha1_transform(context->state, (unsigned char *)&data[i]);
j = 0;
} else
i = 0;
memcpy(&context->buffer[j], &data[i], len - i);
}
void sha1_final( SHA1_CTX *context, unsigned char digest[SHA1_SIZE] ) {
unsigned int i;
unsigned char finalcount[8];
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
}
sha1_update(context, (unsigned char *)"\200", 1);
while ((context->count[0] & 504) != 448) {
sha1_update(context, (unsigned char *)"\0", 1);
}
sha1_update(context, finalcount, 8);
for (i = 0; i < SHA1_SIZE; i++) {
digest[i] = (unsigned char)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
sha1_transform(context->state, context->buffer);
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (C)2005-2012 Haxe Foundation
*
* 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 SHA1_H
#define SHA1_H
#define SHA1_SIZE 20
typedef unsigned char SHA1_DIGEST[SHA1_SIZE];
typedef struct {
unsigned int state[5];
unsigned int count[2];
unsigned char buffer[64];
} SHA1_CTX;
void sha1_init( SHA1_CTX *c );
void sha1_update( SHA1_CTX *c, const unsigned char *data, unsigned int len );
void sha1_final( SHA1_CTX *c, SHA1_DIGEST digest );
#endif
/* ************************************************************************ */

View File

@ -0,0 +1,224 @@
/*
* Copyright (C)2005-2012 Haxe Foundation
*
* 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 <hxcpp.h>
#include "socket.h"
#include <string.h>
#ifdef NEKO_WINDOWS
static int init_done = 0;
static WSADATA init_data;
#else
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/time.h>
# include <netinet/in.h>
# include <netinet/tcp.h>
# include <arpa/inet.h>
# include <unistd.h>
# include <netdb.h>
# include <fcntl.h>
# include <errno.h>
# include <stdio.h>
# include <poll.h>
# define closesocket close
# define SOCKET_ERROR (-1)
#endif
#if (defined(NEKO_WINDOWS) || defined(NEKO_MAC)) && !defined(MSG_NOSIGNAL)
# define MSG_NOSIGNAL 0
#endif
static SERR block_error() {
#ifdef NEKO_WINDOWS
int err = WSAGetLastError();
if( err == WSAEWOULDBLOCK || err == WSAEALREADY )
#else
if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == EALREADY )
#endif
return PS_BLOCK;
return PS_ERROR;
}
void psock_init() {
#ifdef NEKO_WINDOWS
if( !init_done ) {
WSAStartup(MAKEWORD(2,0),&init_data);
init_done = 1;
}
#endif
}
PSOCK psock_create() {
hx::AutoGCFreeZone block;
PSOCK s = socket(AF_INET,SOCK_STREAM,0);
# if defined(NEKO_MAC) || defined(NEKO_BSD)
if( s != INVALID_SOCKET )
setsockopt(s,SOL_SOCKET,SO_NOSIGPIPE,NULL,0);
# endif
# ifdef NEKO_POSIX
// we don't want sockets to be inherited in case of exec
{
int old = fcntl(s,F_GETFD,0);
if( old >= 0 ) fcntl(s,F_SETFD,old|FD_CLOEXEC);
}
# endif
return s;
}
void psock_close( PSOCK s ) {
hx::AutoGCFreeZone block;
POSIX_LABEL(close_again);
if( closesocket(s) ) {
HANDLE_EINTR(close_again);
}
}
int psock_send( PSOCK s, const char *buf, int size ) {
hx::AutoGCFreeZone block;
return psock_send_no_gc(s,buf,size);
}
int psock_send_no_gc( PSOCK s, const char *buf, int size ) {
int ret;
POSIX_LABEL(send_again);
ret = send(s,buf,size,MSG_NOSIGNAL);
if( ret == SOCKET_ERROR ) {
HANDLE_EINTR(send_again);
return block_error();
}
return ret;
}
int psock_recv( PSOCK s, char *buf, int size ) {
hx::AutoGCFreeZone block;
return psock_recv_no_gc(s,buf,size);
}
int psock_recv_no_gc( PSOCK s, char *buf, int size ) {
int ret;
POSIX_LABEL(recv_again);
ret = recv(s,buf,size,MSG_NOSIGNAL);
if( ret == SOCKET_ERROR ) {
HANDLE_EINTR(recv_again);
return block_error();
}
return ret;
}
PHOST phost_resolve( const char *host ) {
hx::AutoGCFreeZone block;
PHOST ip = inet_addr(host);
if( ip == INADDR_NONE ) {
struct hostent *h;
# if defined(NEKO_WINDOWS) || defined(NEKO_MAC) || defined(BLACKBERRY)
h = gethostbyname(host);
# else
struct hostent hbase;
char buf[1024];
int errcode;
gethostbyname_r(host,&hbase,buf,1024,&h,&errcode);
# endif
if( h == NULL )
return UNRESOLVED_HOST;
ip = *((unsigned int*)h->h_addr);
}
return ip;
}
SERR psock_connect( PSOCK s, PHOST host, int port ) {
hx::AutoGCFreeZone block;
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
*(int*)&addr.sin_addr.s_addr = host;
if( connect(s,(struct sockaddr*)&addr,sizeof(addr)) != 0 )
return block_error();
return PS_OK;
}
SERR psock_set_timeout( PSOCK s, double t ) {
#ifdef NEKO_WINDOWS
int time = (int)(t * 1000);
#else
struct timeval time;
time.tv_usec = (int)((t - (int)t)*1000000);
time.tv_sec = (int)t;
#endif
if( setsockopt(s,SOL_SOCKET,SO_SNDTIMEO,(char*)&time,sizeof(time)) != 0 )
return PS_ERROR;
if( setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,(char*)&time,sizeof(time)) != 0 )
return PS_ERROR;
return PS_OK;
}
SERR psock_set_blocking( PSOCK s, int block ) {
#ifdef NEKO_WINDOWS
{
unsigned long arg = !block;
if( ioctlsocket(s,FIONBIO,&arg) != 0 )
return PS_ERROR;
}
#else
{
int rights = fcntl(s,F_GETFL);
if( rights == -1 )
return PS_ERROR;
if( block )
rights &= ~O_NONBLOCK;
else
rights |= O_NONBLOCK;
if( fcntl(s,F_SETFL,rights) == -1 )
return PS_ERROR;
}
#endif
return PS_OK;
}
SERR psock_set_fastsend( PSOCK s, int fast ) {
if( setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char*)&fast,sizeof(fast)) )
return block_error();
return PS_OK;
}
void psock_wait( PSOCK s ) {
hx::AutoGCFreeZone block;
# ifdef NEKO_WINDOWS
fd_set set;
FD_ZERO(&set);
FD_SET(s,&set);
select((int)s+1,&set,NULL,NULL,NULL);
# else
struct pollfd fds;
POSIX_LABEL(poll_again);
fds.fd = s;
fds.events = POLLIN;
fds.revents = 0;
if( poll(&fds,1,-1) < 0 ) {
HANDLE_EINTR(poll_again);
}
# endif
}
/* ************************************************************************ */

View File

@ -0,0 +1,62 @@
/*
* Copyright (C)2005-2012 Haxe Foundation
*
* 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 SOCKET_H
#define SOCKET_H
#include <hx/OS.h>
#ifdef NEKO_WINDOWS
# include <winsock2.h>
typedef SOCKET PSOCK;
#else
typedef int PSOCK;
# define INVALID_SOCKET (-1)
#endif
typedef unsigned int PHOST;
#define UNRESOLVED_HOST ((PHOST)-1)
typedef enum {
PS_OK = 0,
PS_ERROR = -1,
PS_BLOCK = -2,
} SERR;
void psock_init();
PSOCK psock_create();
void psock_close( PSOCK s );
SERR psock_connect( PSOCK s, PHOST h, int port );
SERR psock_set_timeout( PSOCK s, double timeout );
SERR psock_set_blocking( PSOCK s, int block );
SERR psock_set_fastsend( PSOCK s, int fast );
int psock_send( PSOCK s, const char *buf, int size );
int psock_recv( PSOCK s, char *buf, int size );
int psock_send_no_gc( PSOCK s, const char *buf, int size );
int psock_recv_no_gc( PSOCK s, char *buf, int size );
PHOST phost_resolve( const char *hostname );
#endif
/* ************************************************************************ */

View File

@ -0,0 +1,45 @@
<xml>
<pragma once="true" />
<set name="PCRE_DIR" value="${HXCPP}/project/thirdparty/pcre2-10.42/src" />
<files id="pcre2-8" >
<depend files="hxcpp-depends"/>
<depend name="${this_dir}/Build.xml" dateOnly="true" />
<cache value="true" asLibrary="true" />
<compilerflag value="-DPCRE2_CODE_UNIT_WIDTH=8" />
<compilerflag value="-DSUPPORT_PCRE2_8" />
<include name="${this_dir}/pcre2_sources.xml" />
</files>
<files id="pcre2-16" >
<depend files="hxcpp-depends"/>
<depend name="${this_dir}/Build.xml" dateOnly="true" />
<cache value="true" asLibrary="true" />
<compilerflag value="-DPCRE2_CODE_UNIT_WIDTH=16" />
<compilerflag value="-DSUPPORT_PCRE2_16" />
<include name="${this_dir}/pcre2_sources.xml" />
</files>
<files id="hxcpp_regexp">
<depend files="hxcpp-depends"/>
<depend name="${this_dir}/Build.xml" dateOnly="true" />
<cache value="true" asLibrary="true" />
<compilerflag value="-I${PCRE_DIR}"/>
<file name="${this_dir}/RegExp.cpp"/>
</files>
<target id="haxe">
<files id="hxcpp_regexp" />
<files id="pcre2-8" />
<files id="pcre2-16" if="hxcpp_smart_strings" />
</target>
</xml>

View File

@ -0,0 +1,294 @@
#include <hxcpp.h>
#include <string.h>
//#define PCRE2_STATIC
#define PCRE2_CODE_UNIT_WIDTH 0
//#include <pcre2.h>
#include "../../../../project/thirdparty/pcre2-10.42-8/src/pcre2.h"
#define PCRE(o) ((pcredata*)o.mPtr)
static void regexp_compilation_error(String pattern, int error_code, size_t error_offset) {
PCRE2_UCHAR8 error_buffer[128];
pcre2_get_error_message_8(error_code, error_buffer, sizeof(error_buffer));
hx::Throw(HX_CSTRING("Regexp compilation error : ") + String((const char*)error_buffer) + HX_CSTRING(" in ") + pattern);
}
struct pcredata : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdPcreData };
pcre2_code_8 *rUtf8;
#ifdef HX_SMART_STRINGS
pcre2_code_16 *rUtf16;
#endif
int n_groups;
pcre2_match_data_8* match_data8;
#ifdef HX_SMART_STRINGS
pcre2_match_data_16* match_data16;
#endif
unsigned int flags;
String string;
String expr;
void create8(pcre2_code_8 *inR, String inExpr, int inFlags)
{
rUtf8 = inR;
#ifdef HX_SMART_STRINGS
rUtf16 = 0;
#endif
expr = inExpr;
HX_OBJ_WB_GET(this, expr.raw_ref());
flags = inFlags;
n_groups = 0;
pcre2_pattern_info_8(rUtf8,PCRE2_INFO_CAPTURECOUNT,&n_groups);
n_groups++;
match_data8 = pcre2_match_data_create_from_pattern_8(rUtf8, NULL);
#ifdef HX_SMART_STRINGS
match_data16 = 0;
#endif
_hx_set_finalizer(this, finalize);
}
#ifdef HX_SMART_STRINGS
void create16(pcre2_code_16 *inR, String inExpr, int inFlags)
{
rUtf8 = 0;
rUtf16 = inR;
expr = inExpr;
HX_OBJ_WB_GET(this, expr.raw_ref());
flags = inFlags;
n_groups = 0;
pcre2_pattern_info_16(rUtf16,PCRE2_INFO_CAPTURECOUNT,&n_groups);
n_groups++;
match_data8 = 0;
match_data16 = pcre2_match_data_create_from_pattern_16(rUtf16, NULL);
_hx_set_finalizer(this, finalize);
}
#endif
bool run(String string,int pos,int len)
{
#ifdef HX_SMART_STRINGS
if (string.isUTF16Encoded())
{
if (!rUtf16)
{
int error_code;
size_t error_offset;
hx::strbuf buf;
int utf16Length = 0;
PCRE2_SPTR16 utf16 = (PCRE2_SPTR16)expr.wc_str(&buf, &utf16Length);
rUtf16 = pcre2_compile_16((PCRE2_SPTR16)expr.wc_str(&buf),utf16Length,flags,&error_code,&error_offset,NULL);
if (!rUtf16) {
regexp_compilation_error(expr,error_code,error_offset);
}
match_data16 = pcre2_match_data_create_from_pattern_16(rUtf16, NULL);
}
int n = pcre2_match_16(rUtf16,(PCRE2_SPTR16)string.raw_wptr(),pos+len,pos,PCRE2_NO_UTF_CHECK,match_data16,NULL);
return n>=0;
}
if (!rUtf8)
{
int error_code;
size_t error_offset;
int utf8Length = 0;
PCRE2_SPTR8 utf8 = (PCRE2_SPTR8)expr.utf8_str(NULL, true, &utf8Length);
rUtf8 = pcre2_compile_8(utf8, utf8Length, flags, &error_code, &error_offset, NULL);
if (!rUtf8) {
regexp_compilation_error(expr,error_code,error_offset);
}
match_data8 = pcre2_match_data_create_from_pattern_8(rUtf8, NULL);
}
#endif
return pcre2_match_8(rUtf8,(PCRE2_SPTR8)string.utf8_str(),pos+len,pos,PCRE2_NO_UTF_CHECK,match_data8,NULL) >= 0;
}
size_t* get_matches() {
#ifdef HX_SMART_STRINGS
if (string.isUTF16Encoded()) {
return pcre2_get_ovector_pointer_16(match_data16);
}
#endif
return pcre2_get_ovector_pointer_8(match_data8);
}
void destroy()
{
pcre2_code_free_8( rUtf8 );
pcre2_match_data_free_8( match_data8 );
#ifdef HX_SMART_STRINGS
pcre2_code_free_16( rUtf16 );
pcre2_match_data_free_16( match_data16 );
#endif
}
void __Mark(hx::MarkContext *__inCtx) { HX_MARK_MEMBER(string); HX_MARK_MEMBER(expr); }
#ifdef HXCPP_VISIT_ALLOCS
void __Visit(hx::VisitContext *__inCtx) { HX_VISIT_MEMBER(string); HX_VISIT_MEMBER(expr); }
#endif
static void finalize(Dynamic obj)
{
((pcredata *)(obj.mPtr))->destroy();
}
String toString() { return expr; }
};
/**
regexp_new_options : reg:string -> options:string -> 'regexp
<doc>Build a new regexpr with the following options :
<ul>
<li>i : case insensitive matching</li>
<li>s : . match anything including newlines</li>
<li>m : treat the input as a multiline string</li>
<li>u : run in utf8 mode</li>
<li>g : turn off greedy behavior</li>
</ul>
</doc>
**/
Dynamic _hx_regexp_new_options(String s, String opt)
{
hx::strbuf buf;
const char *o = opt.utf8_str(&buf);
int options = PCRE2_UCP | PCRE2_UTF;
while( *o )
{
switch( *o++ )
{
case 'i':
options |= PCRE2_CASELESS;
break;
case 's':
options |= PCRE2_DOTALL;
break;
case 'm':
options |= PCRE2_MULTILINE;
break;
case 'g':
options |= PCRE2_UNGREEDY;
break;
case 'u':
break;
default:
hx::Throw( HX_CSTRING("Regexp unknown modifier : ") + String::fromCharCode(o[-1]) );
break;
}
}
#ifdef HX_SMART_STRINGS
if (s.isUTF16Encoded())
{
int error_code;
size_t error_offset;
pcre2_code_16 *p = pcre2_compile_16((PCRE2_SPTR16)s.raw_wptr(),s.length,options,&error_code,&error_offset,NULL);
if( !p ) {
regexp_compilation_error(s,error_code,error_offset);
}
pcredata *pdata = new pcredata;
pdata->create16(p,s,options);
return pdata;
}
else
#endif
{
int error_code = 0;
size_t error_offset;
pcre2_code_8 *p = pcre2_compile_8((PCRE2_SPTR8)s.utf8_str(),s.length,options,&error_code,&error_offset,NULL);
if( !p ) {
regexp_compilation_error(s,error_code,error_offset);
}
pcredata *pdata = new pcredata;
pdata->create8(p,s,options);
return pdata;
}
}
bool _hx_regexp_match(Dynamic handle, String string, int pos, int len)
{
if( pos < 0 || len < 0 || pos > string.length || pos + len > string.length )
return false;
pcredata *d = PCRE(handle);
if( d->run(string,pos,len) )
{
d->string = string;
HX_OBJ_WB_GET(d, d->string.raw_ref());
return true;
}
else
{
d->string = String();
return false;
}
}
String _hx_regexp_matched(Dynamic handle, int m)
{
pcredata *d = PCRE(handle);
if( m < 0 || m >= d->n_groups || !d->string.raw_ptr() )
return null();
//hx::Throw( HX_CSTRING("regexp_matched - no valid match"));
size_t* matches = d->get_matches();
int start = matches[m*2];
int len = matches[m*2+1] - start;
if( start == -1 )
return String();
return d->string.substr(start, len);
}
/**
regexp_matched_pos : 'regexp -> n:int -> { pos => int, len => int }
<doc>Return the [n]th matched block position by the regexp. If [n] is 0 then
return the whole matched substring position</doc>
**/
Dynamic _hx_regexp_matched_pos(Dynamic handle, int m)
{
pcredata *d = PCRE(handle);
if (m < 0 || m >= d->n_groups || !d->string.raw_ptr())
return null();
size_t* matches = d->get_matches();
int start = matches[m*2];
int len = matches[m*2+1] - start;
return hx::Anon_obj::Create(2)
->setFixed(0,HX_("len",d5,4b,52,00),len)
->setFixed(1,HX_("pos",94,5d,55,00),start);
}
/**
regexp_matched_num : 'regexp -> int
<doc>Return the total number of matched groups, or -1 if the regexp has not
been matched yet</doc>
**/
int _hx_regexp_matched_num(Dynamic handle)
{
pcredata *d = PCRE(handle);
if( !d->string.raw_ptr() )
return -1;
else
return d->n_groups;
}

View File

@ -0,0 +1,35 @@
<xml>
<compilerflag value="-DHAVE_CONFIG_H" />
<compilerflag value="-DPCRE2_STATIC" />
<compilerflag value="-DSUPPORT_UNICODE" />
<compilerflag value="-I${PCRE_DIR}" />
<compilerflag value="-std=c99" unless="MSVC_VER" />
<file name="${PCRE_DIR}/pcre2_auto_possess.c" />
<file name="${PCRE_DIR}/pcre2_chartables.c" />
<file name="${PCRE_DIR}/pcre2_compile.c" />
<file name="${PCRE_DIR}/pcre2_config.c" />
<file name="${PCRE_DIR}/pcre2_context.c" />
<file name="${PCRE_DIR}/pcre2_convert.c" />
<file name="${PCRE_DIR}/pcre2_dfa_match.c" />
<file name="${PCRE_DIR}/pcre2_error.c" />
<file name="${PCRE_DIR}/pcre2_extuni.c" />
<file name="${PCRE_DIR}/pcre2_find_bracket.c" />
<file name="${PCRE_DIR}/pcre2_jit_compile.c" />
<file name="${PCRE_DIR}/pcre2_maketables.c" />
<file name="${PCRE_DIR}/pcre2_match.c" />
<file name="${PCRE_DIR}/pcre2_match_data.c" />
<file name="${PCRE_DIR}/pcre2_newline.c" />
<file name="${PCRE_DIR}/pcre2_ord2utf.c" />
<file name="${PCRE_DIR}/pcre2_pattern_info.c" />
<file name="${PCRE_DIR}/pcre2_script_run.c" />
<file name="${PCRE_DIR}/pcre2_serialize.c" />
<file name="${PCRE_DIR}/pcre2_string_utils.c" />
<file name="${PCRE_DIR}/pcre2_study.c" />
<file name="${PCRE_DIR}/pcre2_substitute.c" />
<file name="${PCRE_DIR}/pcre2_substring.c" />
<file name="${PCRE_DIR}/pcre2_tables.c" />
<file name="${PCRE_DIR}/pcre2_ucd.c" />
<file name="${PCRE_DIR}/pcre2_valid_utf.c" />
<file name="${PCRE_DIR}/pcre2_xclass.c" />
</xml>

View File

@ -0,0 +1,25 @@
<xml>
<pragma once="true" />
<set name="SQLITE_DIR" value="${HXCPP}/project/thirdparty/sqlite-3.40.1"/>
<files id="hxcpp_sqlite" dir="${this_dir}" >
<depend files="hxcpp-depends"/>
<depend name="${this_dir}/Build.xml" dateOnly="true" />
<cache value="true" asLibrary="true" />
<compilerflag value="-I${SQLITE_DIR}"/>
<file name="Sqlite.cpp"/>
<depend name="${SQLITE_DIR}/sqlite3.h" />
<file name="${SQLITE_DIR}/sqlite3.c"/>
</files>
<target id="haxe">
<files id="hxcpp_sqlite"/>
<lib name="-lpthread" if="linux" unless="static_link" />
</target>
</xml>

View File

@ -0,0 +1,404 @@
/*
* Copyright (C)2005-2012 Haxe Foundation
*
* 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 <hxcpp.h>
#include "sqlite3.h"
#include <stdlib.h>
// Put in anon-namespace to avoid conflicts if static-linked
namespace {
struct result : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSqlite };
sqlite3 *db;
sqlite3_stmt *r;
int ncols;
int count;
String *names;
int *bools;
int done;
int first;
void create(sqlite3 *inDb, sqlite3_stmt *inR, String sql)
{
_hx_set_finalizer(this, finalize);
db = inDb;
r = inR;
ncols = sqlite3_column_count(r);
names = (String *)malloc(sizeof(String)*ncols);
bools = (int*)malloc(sizeof(int)*ncols);
first = 1;
done = 0;
for(int i=0;i<ncols;i++)
{
names[i] = String::createPermanent(sqlite3_column_name(r,i),-1);
for(int j=0;j<i;j++)
if( names[j] == names[i] )
hx::Throw(HX_CSTRING("Error, same field is two times in the request ") + sql);
const char *dtype = sqlite3_column_decltype(r,i);
bools[i] = dtype?(strcmp(dtype,"BOOL") == 0):0;
}
}
static void finalize(Dynamic obj) { ((result *)(obj.mPtr))->destroy(false); }
void destroy(bool inThrowError)
{
if (bools)
{
free(bools);
bools = 0;
}
if (names)
{
free(names);
names = 0;
}
if (r)
{
first = 0;
done = 1;
if( ncols == 0 )
count = sqlite3_changes(db);
bool err = sqlite3_finalize(r) != SQLITE_OK;
db = 0;
r = 0;
if( err && inThrowError)
hx::Throw(HX_CSTRING("Could not finalize request"));
}
}
String toString() { return HX_CSTRING("Sqlite Result"); }
//static void finalize_result( result *r, int exc, bool throwError = true )
};
/**
<doc>
<h1>SQLite</h1>
<p>
Sqlite is a small embeddable SQL database that store all its data into
a single file. See http://sqlite.org for more details.
</p>
</doc>
**/
struct database : public hx::Object
{
sqlite3 *db;
hx::ObjectPtr<result> last;
void create(sqlite3 *inDb)
{
db = inDb;
_hx_set_finalizer(this, finalize);
}
static void finalize(Dynamic obj) { ((database *)(obj.mPtr))->destroy(false); }
void destroy(bool inThrowError)
{
if (db)
{
if (last.mPtr)
{
last->destroy(inThrowError);
last = null();
}
if( sqlite3_close(db) != SQLITE_OK )
{
if (inThrowError)
hx::Throw(HX_CSTRING("Sqlite: could not close"));
}
db = 0;
}
}
void setResult(result *inResult)
{
if (last.mPtr)
last->destroy(true);
last = inResult;
HX_OBJ_WB_GET(this, last.mPtr);
}
void __Mark(hx::MarkContext *__inCtx) { HX_MARK_MEMBER(last); }
#ifdef HXCPP_VISIT_ALLOCS
void __Visit(hx::VisitContext *__inCtx) { HX_VISIT_MEMBER(last); }
#endif
String toString() { return HX_CSTRING("Sqlite Databse"); }
};
static void sqlite_error( sqlite3 *db ) {
hx::Throw( HX_CSTRING("Sqlite error : ") + String(sqlite3_errmsg(db)) );
}
database *getDatabase(Dynamic handle)
{
database *db = dynamic_cast<database *>(handle.mPtr);
if (!db || !db->db)
hx::Throw( HX_CSTRING("Invalid sqlite database") );
return db;
}
result *getResult(Dynamic handle, bool inRequireStatement)
{
result *r = dynamic_cast<result *>(handle.mPtr);
if (!r || (inRequireStatement && !r->r))
hx::Throw( HX_CSTRING("Invalid sqlite result") );
return r;
}
} // End anon-namespace
/**
connect : filename:string -> 'db
<doc>Open or create the database stored in the specified file.</doc>
**/
Dynamic _hx_sqlite_connect(String filename)
{
int err;
sqlite3 *sqlDb = 0;
if( (err = sqlite3_open(filename.utf8_str(),&sqlDb)) != SQLITE_OK )
sqlite_error(sqlDb);
database *db = new database();
db->create(sqlDb);
return db;
}
/**
close : 'db -> void
<doc>Closes the database.</doc>
**/
void _hx_sqlite_close(Dynamic handle)
{
database *db = getDatabase(handle);
db->destroy(true);
}
/**
last_insert_id : 'db -> int
<doc>Returns the last inserted auto_increment id.</doc>
**/
int _hx_sqlite_last_insert_id(Dynamic handle)
{
database *db = getDatabase(handle);
return sqlite3_last_insert_rowid(db->db);
}
/**
request : 'db -> sql:string -> 'result
<doc>Executes the SQL request and returns its result</doc>
**/
Dynamic _hx_sqlite_request(Dynamic handle,String sql)
{
database *db = getDatabase(handle);
int byteLength = 0;
const char * sqlStr = sql.utf8_str(0, true, &byteLength);
sqlite3_stmt *statement = 0;
const char *tl = 0;
if( sqlite3_prepare(db->db,sqlStr,byteLength,&statement,&tl) != SQLITE_OK )
{
hx::Throw( HX_CSTRING("Sqlite error in ") + sql + HX_CSTRING(" : ") +
String(sqlite3_errmsg(db->db) ) );
}
if( *tl )
{
sqlite3_finalize(statement);
hx::Throw(HX_CSTRING("Cannot execute several SQL requests at the same time"));
}
int i,j;
result *r = new result();
r->create(db->db, statement,sql);
db->setResult(r);
return r;
}
/**
result_get_length : 'result -> int
<doc>Returns the number of rows in the result or the number of rows changed by the request.</doc>
**/
int _hx_sqlite_result_get_length(Dynamic handle)
{
result *r = getResult(handle,false);
if( r->ncols != 0 )
hx::Throw(HX_CSTRING("Getting change count from non-change request")); // ???
return r->count;
}
/**
result_get_nfields : 'result -> int
<doc>Returns the number of fields in the result.</doc>
**/
int _hx_sqlite_result_get_nfields(Dynamic handle)
{
return getResult(handle,false)->ncols;
}
/**
result_next : 'result -> object?
<doc>Returns the next row in the result or [null] if no more result.</doc>
**/
Dynamic _hx_sqlite_result_next(Dynamic handle)
{
result *r = getResult(handle,false);
if( r->done )
return null();
switch( sqlite3_step(r->r) )
{
case SQLITE_ROW:
{
hx::Anon v = hx::Anon_obj::Create();
r->first = 0;
for(int i=0;i<r->ncols;i++)
{
Dynamic f;
switch( sqlite3_column_type(r->r,i) )
{
case SQLITE_NULL:
break;
case SQLITE_INTEGER:
if( r->bools[i] )
f = bool(sqlite3_column_int(r->r,i));
else
f = int(sqlite3_column_int(r->r,i));
break;
case SQLITE_FLOAT:
f = Float(sqlite3_column_double(r->r,i));
break;
case SQLITE_TEXT:
f = String((char*)sqlite3_column_text(r->r,i));
break;
case SQLITE_BLOB:
{
int size = sqlite3_column_bytes(r->r,i);
f = Array_obj<unsigned char>::fromData((const unsigned char *)sqlite3_column_blob(r->r,i),size);
break;
}
default:
{
hx::Throw( HX_CSTRING("Unknown Sqlite type #") +
String((int)sqlite3_column_type(r->r,i)));
}
}
v->__SetField(r->names[i],f,hx::paccDynamic);
}
return v;
}
case SQLITE_DONE:
r->destroy(true);
return null();
case SQLITE_BUSY:
hx::Throw(HX_CSTRING("Database is busy"));
case SQLITE_ERROR:
sqlite_error(r->db);
default:
hx::Throw(HX_CSTRING("Unkown sqlite result"));
}
return null();
}
static sqlite3_stmt *prepStatement(Dynamic handle,int n)
{
result *r = getResult(handle,true);
if( n < 0 || n >= r->ncols )
hx::Throw( HX_CSTRING("Sqlite: Invalid index") );
if( r->first )
_hx_sqlite_result_next(handle);
if( r->done )
hx::Throw( HX_CSTRING("Sqlite: no more results") );
return r->r;
}
/**
result_get : 'result -> n:int -> string
<doc>Return the [n]th field of the current result row.</doc>
**/
String _hx_sqlite_result_get(Dynamic handle,int n)
{
sqlite3_stmt *r = prepStatement(handle,n);
return String((char*)sqlite3_column_text(r,n));
}
/**
result_get_int : 'result -> n:int -> int
<doc>Return the [n]th field of the current result row as an integer.</doc>
**/
int _hx_sqlite_result_get_int(Dynamic handle,int n)
{
sqlite3_stmt *r = prepStatement(handle,n);
return sqlite3_column_int(r,n);
}
/**
result_get_float : 'result -> n:int -> float
<doc>Return the [n]th field of the current result row as a float.</doc>
**/
Float _hx_sqlite_result_get_float(Dynamic handle,int n)
{
sqlite3_stmt *r = prepStatement(handle,n);
return sqlite3_column_double(r,n);
}

View File

@ -0,0 +1,129 @@
<xml>
<pragma once="true" />
<set name="MBEDTLS_DIR" value="${HXCPP}/project/thirdparty/mbedtls-2.28.2" />
<files id="hxcpp_ssl" >
<depend files="hxcpp-depends"/>
<depend name="${this_dir}/Build.xml" dateOnly="true" />
<cache value="true" asLibrary="true" />
<compilerflag value="-I${MBEDTLS_DIR}/include"/>
<compilerflag value="-I${this_dir}"/>
<file name="${this_dir}/SSL.cpp"/>
<compilerflag value="-DMBEDTLS_USER_CONFIG_FILE=&lt;mbedtls_config.h&gt;"/>
<file name="${MBEDTLS_DIR}/library/aes.c"/>
<file name="${MBEDTLS_DIR}/library/aesni.c"/>
<file name="${MBEDTLS_DIR}/library/arc4.c"/>
<file name="${MBEDTLS_DIR}/library/aria.c"/>
<file name="${MBEDTLS_DIR}/library/asn1parse.c"/>
<file name="${MBEDTLS_DIR}/library/asn1write.c"/>
<file name="${MBEDTLS_DIR}/library/base64.c"/>
<file name="${MBEDTLS_DIR}/library/bignum.c"/>
<file name="${MBEDTLS_DIR}/library/blowfish.c"/>
<file name="${MBEDTLS_DIR}/library/camellia.c"/>
<file name="${MBEDTLS_DIR}/library/ccm.c"/>
<file name="${MBEDTLS_DIR}/library/chacha20.c"/>
<file name="${MBEDTLS_DIR}/library/chachapoly.c"/>
<file name="${MBEDTLS_DIR}/library/cipher.c"/>
<file name="${MBEDTLS_DIR}/library/cipher_wrap.c"/>
<file name="${MBEDTLS_DIR}/library/constant_time.c"/>
<file name="${MBEDTLS_DIR}/library/cmac.c"/>
<file name="${MBEDTLS_DIR}/library/ctr_drbg.c"/>
<file name="${MBEDTLS_DIR}/library/des.c"/>
<file name="${MBEDTLS_DIR}/library/dhm.c"/>
<file name="${MBEDTLS_DIR}/library/ecdh.c"/>
<file name="${MBEDTLS_DIR}/library/ecdsa.c"/>
<file name="${MBEDTLS_DIR}/library/ecjpake.c"/>
<file name="${MBEDTLS_DIR}/library/ecp.c"/>
<file name="${MBEDTLS_DIR}/library/ecp_curves.c"/>
<file name="${MBEDTLS_DIR}/library/entropy.c"/>
<file name="${MBEDTLS_DIR}/library/entropy_poll.c"/>
<file name="${MBEDTLS_DIR}/library/error.c"/>
<file name="${MBEDTLS_DIR}/library/gcm.c"/>
<file name="${MBEDTLS_DIR}/library/havege.c"/>
<file name="${MBEDTLS_DIR}/library/hkdf.c"/>
<file name="${MBEDTLS_DIR}/library/hmac_drbg.c"/>
<file name="${MBEDTLS_DIR}/library/md.c"/>
<file name="${MBEDTLS_DIR}/library/md2.c"/>
<file name="${MBEDTLS_DIR}/library/md4.c"/>
<file name="${MBEDTLS_DIR}/library/md5.c"/>
<file name="${MBEDTLS_DIR}/library/memory_buffer_alloc.c"/>
<file name="${MBEDTLS_DIR}/library/mps_reader.c"/>
<file name="${MBEDTLS_DIR}/library/mps_trace.c"/>
<file name="${MBEDTLS_DIR}/library/nist_kw.c"/>
<file name="${MBEDTLS_DIR}/library/oid.c"/>
<file name="${MBEDTLS_DIR}/library/padlock.c"/>
<file name="${MBEDTLS_DIR}/library/pem.c"/>
<file name="${MBEDTLS_DIR}/library/pk.c"/>
<file name="${MBEDTLS_DIR}/library/pk_wrap.c"/>
<file name="${MBEDTLS_DIR}/library/pkcs12.c"/>
<file name="${MBEDTLS_DIR}/library/pkcs5.c"/>
<file name="${MBEDTLS_DIR}/library/pkparse.c"/>
<file name="${MBEDTLS_DIR}/library/pkwrite.c"/>
<file name="${MBEDTLS_DIR}/library/platform.c"/>
<file name="${MBEDTLS_DIR}/library/platform_util.c"/>
<file name="${MBEDTLS_DIR}/library/poly1305.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_aead.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_cipher.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_client.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_driver_wrappers.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_ecp.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_hash.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_mac.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_rsa.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_se.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_slot_management.c"/>
<file name="${MBEDTLS_DIR}/library/psa_crypto_storage.c"/>
<file name="${MBEDTLS_DIR}/library/psa_its_file.c"/>
<file name="${MBEDTLS_DIR}/library/ripemd160.c"/>
<file name="${MBEDTLS_DIR}/library/rsa.c"/>
<file name="${MBEDTLS_DIR}/library/rsa_internal.c"/>
<file name="${MBEDTLS_DIR}/library/sha1.c"/>
<file name="${MBEDTLS_DIR}/library/sha256.c"/>
<file name="${MBEDTLS_DIR}/library/sha512.c"/>
<file name="${MBEDTLS_DIR}/library/threading.c"/>
<file name="${MBEDTLS_DIR}/library/timing.c"/>
<file name="${MBEDTLS_DIR}/library/version.c"/>
<file name="${MBEDTLS_DIR}/library/version_features.c"/>
<file name="${MBEDTLS_DIR}/library/xtea.c"/>
<file name="${MBEDTLS_DIR}/library/certs.c"/>
<file name="${MBEDTLS_DIR}/library/pkcs11.c"/>
<file name="${MBEDTLS_DIR}/library/x509.c"/>
<file name="${MBEDTLS_DIR}/library/x509_create.c"/>
<file name="${MBEDTLS_DIR}/library/x509_crl.c"/>
<file name="${MBEDTLS_DIR}/library/x509_crt.c"/>
<file name="${MBEDTLS_DIR}/library/x509_csr.c"/>
<file name="${MBEDTLS_DIR}/library/x509write_crt.c"/>
<file name="${MBEDTLS_DIR}/library/x509write_csr.c"/>
<file name="${MBEDTLS_DIR}/library/debug.c"/>
<file name="${MBEDTLS_DIR}/library/net_sockets.c"/>
<file name="${MBEDTLS_DIR}/library/ssl_cache.c"/>
<file name="${MBEDTLS_DIR}/library/ssl_ciphersuites.c"/>
<file name="${MBEDTLS_DIR}/library/ssl_cli.c"/>
<file name="${MBEDTLS_DIR}/library/ssl_cookie.c"/>
<file name="${MBEDTLS_DIR}/library/ssl_msg.c"/>
<file name="${MBEDTLS_DIR}/library/ssl_srv.c"/>
<file name="${MBEDTLS_DIR}/library/ssl_ticket.c"/>
<file name="${MBEDTLS_DIR}/library/ssl_tls.c"/>
<file name="${MBEDTLS_DIR}/library/ssl_tls13_keys.c"/>
</files>
<target id="haxe">
<files id="hxcpp_ssl" />
<lib name="advapi32.lib" if="windows" unless="static_link" />
<lib name="crypt32.lib" if="windows" unless="static_link" />
<lib name="ws2_32.lib" if="windows" unless="static_link" />
<flag value="-framework" if="macos"/>
<flag value="Security" if="macos"/>
</target>
</xml>

View File

@ -0,0 +1,861 @@
#include <string.h>
#ifndef KORE_CONSOLE
#ifdef __clang__
#pragma clang diagnostic ignored "-Wparentheses"
#endif
#ifdef HX_WINDOWS
# include <winsock2.h>
# include <wincrypt.h>
#else
# include <sys/socket.h>
# include <strings.h>
# include <errno.h>
typedef int SOCKET;
#endif
#include <hxcpp.h>
#include <hx/OS.h>
#if defined(NEKO_MAC) && !defined(IPHONE) && !defined(APPLETV)
#include <Security/Security.h>
#endif
typedef size_t socket_int;
#define SOCKET_ERROR (-1)
#define NRETRYS 20
#include "mbedtls/error.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/md.h"
#include "mbedtls/pk.h"
#include "mbedtls/oid.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/ssl.h"
#include "mbedtls/net.h"
#include "mbedtls/debug.h"
#define val_ssl(o) ((sslctx*)o.mPtr)
#define val_conf(o) ((sslconf*)o.mPtr)
#define val_cert(o) ((sslcert*)o.mPtr)
#define val_pkey(o) ((sslpkey*)o.mPtr)
struct SocketWrapper : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSocket };
SOCKET socket;
};
struct sslctx : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSsl };
mbedtls_ssl_context *s;
void create()
{
s = (mbedtls_ssl_context *)malloc(sizeof(mbedtls_ssl_context));
mbedtls_ssl_init(s);
_hx_set_finalizer(this, finalize);
}
void destroy()
{
if( s )
{
mbedtls_ssl_free( s );
free(s);
s = 0;
}
}
static void finalize(Dynamic obj)
{
((sslctx *)(obj.mPtr))->destroy();
}
String toString() { return HX_CSTRING("sslctx"); }
};
struct sslconf : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSslConf };
mbedtls_ssl_config *c;
void create()
{
c = (mbedtls_ssl_config *)malloc(sizeof(mbedtls_ssl_config));
mbedtls_ssl_config_init(c);
_hx_set_finalizer(this, finalize);
}
void destroy()
{
if( c )
{
mbedtls_ssl_config_free( c );
free(c);
c = 0;
}
}
static void finalize(Dynamic obj)
{
((sslconf *)(obj.mPtr))->destroy();
}
String toString() { return HX_CSTRING("sslconfig"); }
};
struct sslcert : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSslCert };
mbedtls_x509_crt *c;
bool head;
void create(const mbedtls_x509_crt *inC)
{
if( inC ){
c = (mbedtls_x509_crt *)inC;
head = false;
}else{
c = (mbedtls_x509_crt *)malloc(sizeof(mbedtls_x509_crt));
mbedtls_x509_crt_init(c);
head = true;
}
_hx_set_finalizer(this, finalize);
}
void destroy()
{
if( c && head )
{
mbedtls_x509_crt_free( c );
free(c);
head = 0;
}
c = 0;
}
static void finalize(Dynamic obj)
{
((sslcert *)(obj.mPtr))->destroy();
}
String toString() { return HX_CSTRING("sslcert"); }
};
struct sslpkey : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSslKey };
mbedtls_pk_context *k;
void create()
{
k = (mbedtls_pk_context *)malloc(sizeof(mbedtls_pk_context));
mbedtls_pk_init(k);
_hx_set_finalizer(this, finalize);
}
void destroy()
{
if( k )
{
mbedtls_pk_free(k);
free(k);
k = 0;
}
}
static void finalize(Dynamic obj)
{
((sslpkey *)(obj.mPtr))->destroy();
}
String toString() { return HX_CSTRING("sslpkey"); }
};
static mbedtls_entropy_context entropy;
static mbedtls_ctr_drbg_context ctr_drbg;
static bool is_ssl_blocking( int r ) {
return r == MBEDTLS_ERR_SSL_WANT_READ || r == MBEDTLS_ERR_SSL_WANT_WRITE;
}
static bool is_block_error() {
#ifdef NEKO_WINDOWS
int err = WSAGetLastError();
if( err == WSAEWOULDBLOCK || err == WSAEALREADY || err == WSAETIMEDOUT )
#else
if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == EALREADY )
#endif
return true;
return false;
}
static void ssl_error( int ret ){
char buf[256];
mbedtls_strerror(ret, buf, sizeof(buf));
hx::Throw( String(buf) );
}
Dynamic _hx_ssl_new( Dynamic hconf ) {
int ret;
sslctx *ssl = new sslctx();
ssl->create();
sslconf *conf = val_conf(hconf);
if( ret = mbedtls_ssl_setup(ssl->s, conf->c) != 0 ){
ssl->destroy();
ssl_error(ret);
}
return ssl;
}
void _hx_ssl_close( Dynamic hssl ) {
sslctx *ssl = val_ssl(hssl);
ssl->destroy();
}
void _hx_ssl_debug_set (int i) {
mbedtls_debug_set_threshold(i);
}
void _hx_ssl_handshake( Dynamic hssl ) {
int r;
sslctx *ssl = val_ssl(hssl);
POSIX_LABEL(handshake_again);
r = mbedtls_ssl_handshake( ssl->s );
if ( is_ssl_blocking(r) ) {
HANDLE_EINTR(handshake_again);
hx::Throw(HX_CSTRING("Blocking"));
}else if( r == SOCKET_ERROR ) {
HANDLE_EINTR(handshake_again);
hx::Throw(HX_CSTRING("ssl network error"));
}else if( r != 0 )
ssl_error(r);
}
int net_read( void *fd, unsigned char *buf, size_t len ){
hx::EnterGCFreeZone();
int r = recv((SOCKET)(socket_int)fd, (char *)buf, len, 0);
if( r == SOCKET_ERROR && is_block_error() )
r = MBEDTLS_ERR_SSL_WANT_READ;
hx::ExitGCFreeZone();
return r;
}
int net_write( void *fd, const unsigned char *buf, size_t len ){
hx::EnterGCFreeZone();
int r = send((SOCKET)(socket_int)fd, (char *)buf, len, 0);
if( r == SOCKET_ERROR && is_block_error() )
r = MBEDTLS_ERR_SSL_WANT_WRITE;
hx::ExitGCFreeZone();
return r;
}
void _hx_ssl_set_socket( Dynamic hssl, Dynamic hsocket ) {
sslctx *ssl = val_ssl(hssl);
SocketWrapper *socket = (SocketWrapper *)hsocket.mPtr;
mbedtls_ssl_set_bio( ssl->s, (void *)(socket_int)socket->socket, net_write, net_read, NULL );
}
void _hx_ssl_set_hostname( Dynamic hssl, String hostname ){
int ret;
sslctx *ssl = val_ssl(hssl);
hx::strbuf buf;
if( ret = mbedtls_ssl_set_hostname(ssl->s, hostname.utf8_str(&buf)) != 0 )
ssl_error(ret);
}
Dynamic _hx_ssl_get_peer_certificate( Dynamic hssl ){
sslctx *ssl = val_ssl(hssl);
const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(ssl->s);
if( crt == NULL )
return null();
sslcert *cert = new sslcert();
cert->create( crt );
return cert;
}
bool _hx_ssl_get_verify_result( Dynamic hssl ){
sslctx *ssl = val_ssl(hssl);
int r = mbedtls_ssl_get_verify_result( ssl->s );
if( r == 0 )
return true;
else if( r != -1 )
return false;
hx::Throw( HX_CSTRING("not available") );
return false;
}
void _hx_ssl_send_char( Dynamic hssl, int c ) {
if( c < 0 || c > 255 )
hx::Throw( HX_CSTRING("invalid char") );
sslctx *ssl = val_ssl(hssl);
const unsigned char cc = c;
mbedtls_ssl_write( ssl->s, &cc, 1 );
}
int _hx_ssl_send( Dynamic hssl, Array<unsigned char> buf, int p, int l ) {
sslctx *ssl = val_ssl(hssl);
int dlen = buf->length;
if( p < 0 || l < 0 || p > dlen || p + l > dlen )
hx::Throw( HX_CSTRING("ssl_send") );
POSIX_LABEL(send_again);
const unsigned char *base = (const unsigned char *)&buf[0];
dlen = mbedtls_ssl_write( ssl->s, base + p, l );
if ( is_ssl_blocking(dlen) ) {
HANDLE_EINTR(send_again);
hx::Throw(HX_CSTRING("Blocking"));
}else if( dlen == SOCKET_ERROR ) {
HANDLE_EINTR(send_again);
hx::Throw(HX_CSTRING("ssl network error"));
}
return dlen;
}
void _hx_ssl_write( Dynamic hssl, Array<unsigned char> buf ) {
sslctx *ssl = val_ssl(hssl);
int len = buf->length;
unsigned char *cdata = &buf[0];
while( len > 0 ) {
POSIX_LABEL( write_again );
int slen = mbedtls_ssl_write( ssl->s, cdata, len );
if ( is_ssl_blocking(slen) ) {
HANDLE_EINTR( write_again );
hx::Throw(HX_CSTRING("Blocking"));
}else if( slen == SOCKET_ERROR ) {
HANDLE_EINTR( write_again );
hx::Throw(HX_CSTRING("ssl network error"));
}
cdata += slen;
len -= slen;
}
}
int _hx_ssl_recv_char( Dynamic hssl ) {
sslctx *ssl = val_ssl(hssl);
unsigned char cc;
int r = mbedtls_ssl_read( ssl->s, &cc, 1 );
if( r <= 0 )
hx::Throw( HX_CSTRING("ssl_recv_char") );
return (int)cc;
}
int _hx_ssl_recv( Dynamic hssl, Array<unsigned char> buf, int p, int l ) {
sslctx *ssl = val_ssl(hssl);
int dlen = buf->length;
if( p < 0 || l < 0 || p > dlen || p + l > dlen )
hx::Throw( HX_CSTRING("ssl_recv") );
unsigned char *base = &buf[0];
POSIX_LABEL(recv_again);
dlen = mbedtls_ssl_read( ssl->s, base + p, l );
if ( is_ssl_blocking(dlen) ) {
HANDLE_EINTR(recv_again);
hx::Throw(HX_CSTRING("Blocking"));
}else if( dlen == SOCKET_ERROR ) {
HANDLE_EINTR(recv_again);
hx::Throw(HX_CSTRING("ssl network error"));
}
if( dlen < 0 ) {
if( dlen == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY ) {
mbedtls_ssl_close_notify( ssl->s );
return 0;
}
hx::Throw( HX_CSTRING("ssl_recv") );
}
return dlen;
}
Array<unsigned char> _hx_ssl_read( Dynamic hssl ) {
sslctx *ssl = val_ssl(hssl);
Array<unsigned char> result = Array_obj<unsigned char>::__new();
unsigned char buf[256];
while( true ) {
POSIX_LABEL(read_again);
int len = mbedtls_ssl_read( ssl->s, buf, 256 );
if ( is_ssl_blocking(len) ) {
HANDLE_EINTR(read_again);
hx::Throw(HX_CSTRING("Blocking"));
}else if( len == SOCKET_ERROR ) {
HANDLE_EINTR(read_again);
hx::Throw(HX_CSTRING("ssl network error"));
}
if( len == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY ) {
mbedtls_ssl_close_notify( ssl->s );
len = 0;
}
if( len == 0 )
break;
result->memcpy(result->length, buf, len);
}
return result;
}
Dynamic _hx_ssl_conf_new( bool server ) {
int ret;
sslconf *conf = new sslconf();
conf->create();
if( ret = mbedtls_ssl_config_defaults( conf->c,
server ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM, 0 ) != 0 ){
conf->destroy();
ssl_error( ret );
}
mbedtls_ssl_conf_rng( conf->c, mbedtls_ctr_drbg_random, &ctr_drbg );
return conf;
}
void _hx_ssl_conf_close( Dynamic hconf ) {
sslconf *conf = val_conf(hconf);
conf->destroy();
}
void _hx_ssl_conf_set_ca( Dynamic hconf, Dynamic hcert ) {
sslconf *conf = val_conf(hconf);
if( hconf.mPtr ){
sslcert *cert = val_cert(hcert);
mbedtls_ssl_conf_ca_chain( conf->c, cert->c, NULL );
}else{
mbedtls_ssl_conf_ca_chain( conf->c, NULL, NULL );
}
}
void _hx_ssl_conf_set_verify( Dynamic hconf, int mode ) {
sslconf *conf = val_conf(hconf);
if( mode == 2 )
mbedtls_ssl_conf_authmode(conf->c, MBEDTLS_SSL_VERIFY_OPTIONAL);
else if( mode == 1 )
mbedtls_ssl_conf_authmode(conf->c, MBEDTLS_SSL_VERIFY_REQUIRED);
else
mbedtls_ssl_conf_authmode(conf->c, MBEDTLS_SSL_VERIFY_NONE);
}
void _hx_ssl_conf_set_cert( Dynamic hconf, Dynamic hcert, Dynamic hpkey ) {
int r;
sslconf *conf = val_conf(hconf);
sslcert *cert = val_cert(hcert);
sslpkey *pkey = val_pkey(hpkey);
if( r = mbedtls_ssl_conf_own_cert(conf->c, cert->c, pkey->k) != 0 )
ssl_error(r);
}
static int sni_callback( void *arg, mbedtls_ssl_context *ctx, const unsigned char *name, size_t len ){
if( name && arg ){
Dynamic cb = new Dynamic();
cb.mPtr = (hx::Object*)arg;
const char *n = (const char *)name;
Dynamic ret = cb->__run( String(n,strlen(n)) );
if( ret != null() ){
// TODO authmode and ca
Dynamic hcert = ret->__Field(HX_CSTRING("cert"), hx::paccDynamic);
Dynamic hpkey = ret->__Field(HX_CSTRING("key"), hx::paccDynamic);
sslcert *cert = val_cert(hcert);
sslpkey *pk = val_pkey(hpkey);
return mbedtls_ssl_set_hs_own_cert( ctx, cert->c, pk->k );
}
}
return -1;
}
void _hx_ssl_conf_set_servername_callback( Dynamic hconf, Dynamic cb ){
sslconf *conf = val_conf(hconf);
mbedtls_ssl_conf_sni( conf->c, sni_callback, (void *)cb.mPtr );
}
Dynamic _hx_ssl_cert_load_defaults(){
#if defined(NEKO_WINDOWS) && !defined(KORE_WINDOWSAPP)
HCERTSTORE store;
PCCERT_CONTEXT cert;
sslcert *chain = NULL;
if( store = CertOpenSystemStoreA(0, (LPCSTR)"Root") ){
cert = NULL;
while( cert = CertEnumCertificatesInStore(store, cert) ){
if( chain == NULL ){
chain = new sslcert();
chain->create( NULL );
}
mbedtls_x509_crt_parse_der( chain->c, (unsigned char *)cert->pbCertEncoded, cert->cbCertEncoded );
}
CertCloseStore(store, 0);
}
if( chain != NULL )
return chain;
#elif defined(NEKO_MAC) && !defined(IPHONE) && !defined(APPLETV)
CFMutableDictionaryRef search;
CFArrayRef result;
SecKeychainRef keychain;
SecCertificateRef item;
CFDataRef dat;
sslcert *chain = NULL;
// Load keychain
if( SecKeychainOpen("/System/Library/Keychains/SystemRootCertificates.keychain",&keychain) != errSecSuccess )
return null();
// Search for certificates
search = CFDictionaryCreateMutable( NULL, 0, NULL, NULL );
CFDictionarySetValue( search, kSecClass, kSecClassCertificate );
CFDictionarySetValue( search, kSecMatchLimit, kSecMatchLimitAll );
CFDictionarySetValue( search, kSecReturnRef, kCFBooleanTrue );
CFDictionarySetValue( search, kSecMatchSearchList, CFArrayCreate(NULL, (const void **)&keychain, 1, NULL) );
if( SecItemCopyMatching( search, (CFTypeRef *)&result ) == errSecSuccess ){
CFIndex n = CFArrayGetCount( result );
for( CFIndex i = 0; i < n; i++ ){
item = (SecCertificateRef)CFArrayGetValueAtIndex( result, i );
// Get certificate in DER format
dat = SecCertificateCopyData( item );
if( dat ){
if( chain == NULL ){
chain = new sslcert();
chain->create( NULL );
}
mbedtls_x509_crt_parse_der( chain->c, (unsigned char *)CFDataGetBytePtr(dat), CFDataGetLength(dat) );
CFRelease( dat );
}
}
}
CFRelease(keychain);
if( chain != NULL )
return chain;
#endif
return null();
}
Dynamic _hx_ssl_cert_load_file( String file ){
int r;
sslcert *cert = new sslcert();
cert->create( NULL );
hx::strbuf buf;
if( r = mbedtls_x509_crt_parse_file(cert->c, file.utf8_str(&buf)) != 0 ){
cert->destroy();
ssl_error(r);
}
return cert;
}
Dynamic _hx_ssl_cert_load_path( String path ){
int r;
sslcert *cert = new sslcert();
cert->create( NULL );
hx::strbuf buf;
if( r = mbedtls_x509_crt_parse_path(cert->c, path.utf8_str(&buf)) != 0 ){
cert->destroy();
ssl_error(r);
}
return cert;
}
static String asn1_buf_to_string( mbedtls_asn1_buf *dat ){
unsigned int i, c;
HX_CHAR *result = hx::NewString( dat->len );
for( i = 0; i < dat->len; i++ ) {
c = dat->p[i];
if( c < 32 || c == 127 || ( c > 128 && c < 160 ) )
result[i] = '?';
else
result[i] = c;
}
result[i] = '\0';
return String(result,dat->len);
}
String _hx_ssl_cert_get_subject( Dynamic hcert, String objname ){
mbedtls_x509_name *obj;
int r;
const char *oname;
sslcert *cert = val_cert(hcert);
obj = &cert->c->subject;
if( obj == NULL )
hx::Throw( HX_CSTRING("cert_get_subject") );
hx::strbuf buf;
while( obj != NULL ){
r = mbedtls_oid_get_attr_short_name( &obj->oid, &oname );
if( r == 0 && strcmp( oname, objname.utf8_str(&buf) ) == 0 )
return asn1_buf_to_string( &obj->val );
obj = obj->next;
}
return String();
}
String _hx_ssl_cert_get_issuer( Dynamic hcert, String objname ){
mbedtls_x509_name *obj;
int r;
const char *oname;
sslcert *cert = val_cert(hcert);
obj = &cert->c->issuer;
if( obj == NULL )
hx::Throw( HX_CSTRING("cert_get_issuer") );
hx::strbuf buf;
while( obj != NULL ){
r = mbedtls_oid_get_attr_short_name( &obj->oid, &oname );
if( r == 0 && strcmp( oname, objname.utf8_str(&buf) ) == 0 )
return asn1_buf_to_string( &obj->val );
obj = obj->next;
}
return String();
}
Array<String> _hx_ssl_cert_get_altnames( Dynamic hcert ){
sslcert *cert = val_cert(hcert);
mbedtls_asn1_sequence *cur;
Array<String> result(0,1);
if( cert->c->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME ){
cur = &cert->c->subject_alt_names;
while( cur != NULL ){
result.Add( asn1_buf_to_string(&cur->buf) );
cur = cur->next;
}
}
return result;
}
static Array<int> x509_time_to_array( mbedtls_x509_time *t ){
if( !t )
hx::Throw( HX_CSTRING("x509_time_to_array") );
Array<int> result(6,6);
result[0] = t->year;
result[1] = t->mon;
result[2] = t->day;
result[3] = t->hour;
result[4] = t->min;
result[5] = t->sec;
return result;
}
Array<int> _hx_ssl_cert_get_notbefore( Dynamic hcert ){
sslcert *cert = val_cert(hcert);
if( !cert->c )
hx::Throw( HX_CSTRING("cert_get_notbefore") );
return x509_time_to_array( &cert->c->valid_from );
}
Array<int> _hx_ssl_cert_get_notafter( Dynamic hcert ){
sslcert *cert = val_cert(hcert);
if( !cert->c )
hx::Throw( HX_CSTRING("cert_get_notafter") );
return x509_time_to_array( &cert->c->valid_to );
}
Dynamic _hx_ssl_cert_get_next( Dynamic hcert ){
sslcert *cert = val_cert(hcert);
mbedtls_x509_crt *crt = cert->c->next;
if( crt == NULL )
return null();
cert = new sslcert();
cert->create(crt);
return cert;
}
Dynamic _hx_ssl_cert_add_pem( Dynamic hcert, String data ){
#ifdef HX_SMART_STRINGS
if (data.isUTF16Encoded())
hx::Throw( HX_CSTRING("Invalid data encoding") );
#endif
sslcert *cert = val_cert(hcert);
int r;
bool isnew = 0;
if( !cert ){
cert = new sslcert();
cert->create( NULL );
isnew = 1;
}
unsigned char *b = (unsigned char *)malloc((data.length+1) * sizeof(unsigned char));
memcpy(b,data.raw_ptr(),data.length);
b[data.length] = '\0';
r = mbedtls_x509_crt_parse( cert->c, b, data.length+1 );
free(b);
if( r < 0 ){
if( isnew )
cert->destroy();
ssl_error(r);
}
return cert;
}
Dynamic _hx_ssl_cert_add_der( Dynamic hcert, Array<unsigned char> buf ){
sslcert *cert = val_cert(hcert);
int r;
bool isnew = 0;
if( !cert ){
cert = new sslcert();
cert->create( NULL );
isnew = 1;
}
if( (r = mbedtls_x509_crt_parse_der( cert->c, &buf[0], buf->length)) < 0 ){
if( isnew )
cert->destroy();
ssl_error(r);
}
return cert;
}
Dynamic _hx_ssl_key_from_der( Array<unsigned char> buf, bool pub ){
sslpkey *pk = new sslpkey();
pk->create();
int r;
if( pub )
r = mbedtls_pk_parse_public_key( pk->k, &buf[0], buf->length );
else
r = mbedtls_pk_parse_key( pk->k, &buf[0], buf->length, NULL, 0 );
if( r != 0 ){
pk->destroy();
ssl_error(r);
}
return pk;
}
Dynamic _hx_ssl_key_from_pem( String data, bool pub, String pass ){
#ifdef HX_SMART_STRINGS
if (data.isUTF16Encoded())
hx::Throw( HX_CSTRING("Invalid data encoding") );
#endif
sslpkey *pk = new sslpkey();
pk->create();
int r;
unsigned char *b = (unsigned char *)malloc((data.length+1) * sizeof(unsigned char));
memcpy(b,data.raw_ptr(),data.length);
b[data.length] = '\0';
if( pub ){
r = mbedtls_pk_parse_public_key( pk->k, b, data.length+1 );
}else if( pass == null() ){
r = mbedtls_pk_parse_key( pk->k, b, data.length+1, NULL, 0 );
}else{
Array<unsigned char> pbytes(0,0);
__hxcpp_bytes_of_string(pbytes,pass);
r = mbedtls_pk_parse_key( pk->k, b, data.length+1, (const unsigned char *)pbytes->GetBase(), pbytes->length );
}
free(b);
if( r != 0 ){
pk->destroy();
ssl_error(r);
}
return pk;
}
Array<unsigned char> _hx_ssl_dgst_make( Array<unsigned char> buf, String alg ){
hx::strbuf ubuf;
const mbedtls_md_info_t *md = mbedtls_md_info_from_string(alg.utf8_str(&ubuf));
if( md == NULL )
hx::Throw( HX_CSTRING("Invalid hash algorithm") );
int size = mbedtls_md_get_size(md);
Array<unsigned char> out = Array_obj<int>::__new(size,size);
int r = -1;
if( r = mbedtls_md( md, &buf[0], buf->length, &out[0] ) != 0 )
ssl_error(r);
return out;
}
Array<unsigned char> _hx_ssl_dgst_sign( Array<unsigned char> buf, Dynamic hpkey, String alg ){
int r = -1;
size_t olen = 0;
unsigned char hash[32];
sslpkey *pk = val_pkey(hpkey);
hx::strbuf ubuf;
const mbedtls_md_info_t *md = mbedtls_md_info_from_string( alg.utf8_str(&ubuf) );
if( md == NULL )
hx::Throw( HX_CSTRING("Invalid hash algorithm") );
if( r = mbedtls_md( md, &buf[0], buf->length, hash ) != 0 )
ssl_error(r);
Array<unsigned char> result = Array_obj<unsigned char>::__new(MBEDTLS_MPI_MAX_SIZE,MBEDTLS_MPI_MAX_SIZE);
if( r = mbedtls_pk_sign( pk->k, mbedtls_md_get_type(md), hash, 0, &result[0], &olen, mbedtls_ctr_drbg_random, &ctr_drbg ) != 0 )
ssl_error(r);
result[olen] = 0;
result->__SetSize(olen);
return result;
}
bool _hx_ssl_dgst_verify( Array<unsigned char> buf, Array<unsigned char> sign, Dynamic hpkey, String alg ){
const mbedtls_md_info_t *md;
int r = -1;
unsigned char hash[32];
sslpkey *pk = val_pkey(hpkey);
hx::strbuf ubuf;
md = mbedtls_md_info_from_string( alg.utf8_str(&ubuf) );
if( md == NULL )
hx::Throw( HX_CSTRING("Invalid hash algorithm") );
if( r = mbedtls_md( md, &buf[0], buf->length, hash ) != 0 )
ssl_error(r);
if( r = mbedtls_pk_verify( pk->k, mbedtls_md_get_type(md), hash, 0, &sign[0], sign->length ) != 0 )
return false;
return true;
}
#if (_MSC_VER || defined(WIN32))
static void threading_mutex_init_alt( mbedtls_threading_mutex_t *mutex ){
if( mutex == NULL )
return;
InitializeCriticalSection( &mutex->cs );
mutex->is_valid = 1;
}
static void threading_mutex_free_alt( mbedtls_threading_mutex_t *mutex ){
if( mutex == NULL || !mutex->is_valid )
return;
DeleteCriticalSection( &mutex->cs );
mutex->is_valid = 0;
}
static int threading_mutex_lock_alt( mbedtls_threading_mutex_t *mutex ){
if( mutex == NULL || !mutex->is_valid )
return( MBEDTLS_ERR_THREADING_BAD_INPUT_DATA );
EnterCriticalSection( &mutex->cs );
return( 0 );
}
static int threading_mutex_unlock_alt( mbedtls_threading_mutex_t *mutex ){
if( mutex == NULL || !mutex->is_valid )
return( MBEDTLS_ERR_THREADING_BAD_INPUT_DATA );
LeaveCriticalSection( &mutex->cs );
return( 0 );
}
#endif
static bool _hx_ssl_inited = false;
void _hx_ssl_init() {
if (_hx_ssl_inited) return;
_hx_ssl_inited = true;
#if (_MSC_VER || defined(WIN32))
mbedtls_threading_set_alt( threading_mutex_init_alt, threading_mutex_free_alt,
threading_mutex_lock_alt, threading_mutex_unlock_alt );
#endif
// Init RNG
mbedtls_entropy_init( &entropy );
mbedtls_ctr_drbg_init( &ctr_drbg );
mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0 );
}
#else
#endif

View File

@ -0,0 +1,10 @@
#ifdef HX_WINDOWS
#define MBEDTLS_THREADING_ALT
#endif
#ifndef HX_WINDOWS
#define MBEDTLS_THREADING_PTHREAD
#endif
#undef MBEDTLS_NET_C
#define MBEDTLS_THREADING_C

View File

@ -0,0 +1,6 @@
#include <windows.h>
typedef struct {
CRITICAL_SECTION cs;
char is_valid;
} mbedtls_threading_mutex_t;

View File

@ -0,0 +1,26 @@
<xml>
<pragma once="true" />
<files id="hxcpp_std" dir="${this_dir}" >
<depend files="hxcpp-depends"/>
<depend name="${this_dir}/Build.xml" dateOnly="true" />
<cache value="1" asLibrary="true" />
<file name="File.cpp"/>
<file name="Sys.cpp"/>
<file name="Process.cpp"/>
<file name="Random.cpp"/>
<file name="Socket.cpp" />
</files>
<target id="haxe">
<files id="hxcpp_std"/>
<lib name="ws2_32.lib" if="windows" unless="static_link" />
<lib name="-lsocket" if="blackberry" unless="static_link" />
</target>
</xml>

View File

@ -0,0 +1,404 @@
#include <hxcpp.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <hx/OS.h>
#ifdef NEKO_WINDOWS
# include <windows.h>
#endif
/**
<doc>
<h1>File</h1>
<p>
The file api can be used for different kind of file I/O.
</p>
</doc>
**/
namespace
{
struct fio : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdFio };
String name;
FILE *io;
bool closeIo;
void create(FILE *inFile, String inName, bool inClose)
{
name = inName;
HX_OBJ_WB_GET(this,name.raw_ref());
io = inFile;
closeIo = inClose;
_hx_set_finalizer(this, finalize);
}
void destroy(bool inForceClose = false)
{
if (io && (inForceClose || closeIo))
fclose(io);
io = 0;
name = String();
}
void __Mark(hx::MarkContext *__inCtx) { HX_MARK_MEMBER(name); }
#ifdef HXCPP_VISIT_ALLOCS
void __Visit(hx::VisitContext *__inCtx) { HX_VISIT_MEMBER(name); }
#endif
static void finalize(Dynamic inObj)
{
((fio *)(inObj.mPtr))->destroy();
}
String toString() { return HX_CSTRING("fio:") + name; }
};
fio *getFio(Dynamic handle, bool inRequireFile=true)
{
fio *result = dynamic_cast<fio *>(handle.mPtr);
if (!result || (!result->io && inRequireFile))
hx::Throw( HX_CSTRING("Bad file handle") );
return result;
}
static void file_error(const char *msg, String inName)
{
hx::ExitGCFreeZone();
Array<String> err = Array_obj<String>::__new(2,2);
err[0] = String(msg);
err[1] = inName;
hx::Throw(err);
}
}
/**
file_open : f:string -> r:string -> 'file
<doc>
Call the C function [fopen] with the file path and access rights.
Return the opened file or throw an exception if the file couldn't be open.
</doc>
**/
Dynamic _hx_std_file_open( String fname, String r )
{
FILE *file = 0;
hx::strbuf buf0;
hx::strbuf buf1;
#ifdef NEKO_WINDOWS
hx::EnterGCFreeZone();
file = _wfopen(fname.wchar_str(&buf0),r.wchar_str(&buf1));
#else
hx::EnterGCFreeZone();
file = fopen(fname.utf8_str(&buf0),r.utf8_str(&buf1));
#endif
if (!file)
file_error("file_open",fname);
hx::ExitGCFreeZone();
fio *f = new fio;
f->create(file,fname,true);
return f;
}
/**
file_close : 'file -> void
<doc>Close an file. Any other operations on this file will fail</doc>
**/
void _hx_std_file_close( Dynamic handle )
{
fio *fio = getFio(handle);
fio->destroy(true);
}
/**
file_name : 'file -> string
<doc>Return the name of the file which was opened</doc>
**/
String hx_std_file_name( Dynamic handle )
{
fio *fio = getFio(handle);
return fio->name;
}
/**
file_write : 'file -> s:string -> p:int -> l:int -> int
<doc>
Write up to [l] chars of string [s] starting at position [p].
Returns the number of chars written which is >= 0.
</doc>
**/
int _hx_std_file_write( Dynamic handle, Array<unsigned char> s, int p, int n )
{
fio *f = getFio(handle);
int buflen = s->length;
int len = n;
if( p < 0 || len < 0 || p > buflen || p + len > buflen )
return 0;
hx::EnterGCFreeZone();
while( len > 0 )
{
POSIX_LABEL(file_write_again);
int d = (int)fwrite(&s[p],1,len,f->io);
if( d <= 0 )
{
HANDLE_FINTR(f->io,file_write_again);
file_error("file_write",f->name);
}
p += d;
len -= d;
}
hx::ExitGCFreeZone();
return n;
}
/**
file_read : 'file -> s:string -> p:int -> l:int -> int
<doc>
Read up to [l] chars into the string [s] starting at position [p].
Returns the number of chars readed which is > 0 (or 0 if l == 0).
</doc>
**/
int _hx_std_file_read( Dynamic handle, Array<unsigned char> buf, int p, int n )
{
fio *f = getFio(handle);
int buf_len = buf->length;
int len = n;
if( p < 0 || len < 0 || p > buf_len || p + len > buf_len )
return 0;
hx::EnterGCFreeZone();
// Attempt to increase the chances of pinning on the stack...
unsigned char *bufPtr = &buf[0];
while( len > 0 )
{
POSIX_LABEL(file_read_again);
int d = (int)fread(bufPtr + p,1,len,f->io);
if( d <= 0 )
{
int size = n - len;
HANDLE_FINTR(f->io,file_read_again);
if( size == 0 )
file_error("file_read",f->name);
hx::ExitGCFreeZone();
return size;
}
p += d;
len -= d;
}
hx::ExitGCFreeZone();
return n;
}
/**
file_write_char : 'file -> c:int -> void
<doc>Write the char [c]. Error if [c] outside of the range 0..255</doc>
**/
void _hx_std_file_write_char( Dynamic handle, int c )
{
fio *f = getFio(handle);
if( c < 0 || c > 255 )
return;
char cc = (char)c;
hx::EnterGCFreeZone();
POSIX_LABEL(write_char_again);
if( fwrite(&cc,1,1,f->io) != 1 )
{
HANDLE_FINTR(f->io,write_char_again);
file_error("file_write_char",f->name);
}
hx::ExitGCFreeZone();
}
/**
file_read_char : 'file -> int
<doc>Read a char from the file. Exception on error</doc>
**/
int _hx_std_file_read_char( Dynamic handle )
{
fio *f = getFio(handle);
unsigned char cc = 0;
hx::EnterGCFreeZone();
POSIX_LABEL(read_char_again);
if( fread(&cc,1,1,f->io) != 1 )
{
HANDLE_FINTR(f->io,read_char_again);
file_error("file_read_char",f->name);
}
hx::ExitGCFreeZone();
return cc;
}
/**
file_seek : 'file -> pos:int -> mode:int -> void
<doc>Use [fseek] to move the file pointer.</doc>
**/
void _hx_std_file_seek( Dynamic handle, int pos, int kind )
{
fio *f = getFio(handle);
hx::EnterGCFreeZone();
if( fseek(f->io,pos,kind) != 0 )
file_error("file_seek",f->name);
hx::ExitGCFreeZone();
}
/**
file_tell : 'file -> int
<doc>Return the current position in the file</doc>
**/
int _hx_std_file_tell( Dynamic handle )
{
fio *f = getFio(handle);
hx::EnterGCFreeZone();
int p = ftell(f->io);
if( p == -1 )
file_error("file_tell",f->name);
hx::ExitGCFreeZone();
return p;
}
/**
file_eof : 'file -> bool
<doc>Tell if we have reached the end of the file</doc>
**/
bool _hx_std_file_eof( Dynamic handle )
{
fio *f = getFio(handle);
return feof(f->io);
}
/**
file_flush : 'file -> void
<doc>Flush the file buffer</doc>
**/
void _hx_std_file_flush( Dynamic handle )
{
fio *f = getFio(handle);
hx::EnterGCFreeZone();
if( fflush( f->io ) != 0 )
file_error("file_flush",f->name);
hx::ExitGCFreeZone();
}
/**
file_contents : f:string -> string
<doc>Read the content of the file [f] and return it.</doc>
**/
String _hx_std_file_contents_string( String name )
{
std::vector<char> buffer;
hx::strbuf buf;
#ifdef NEKO_WINDOWS
hx::EnterGCFreeZone();
FILE *file = _wfopen(name.wchar_str(&buf), L"rb");
#else
hx::EnterGCFreeZone();
FILE *file = fopen(name.utf8_str(&buf), "rb");
#endif
if(!file)
file_error("file_contents",name);
fseek(file,0,SEEK_END);
int len = ftell(file);
if (len<0)
file_error("file_ftell",name);
if (len==0)
{
fclose(file);
hx::ExitGCFreeZone();
return String::emptyString;
}
fseek(file,0,SEEK_SET);
buffer.resize(len);
int p = 0;
while( len > 0 )
{
POSIX_LABEL(file_contents);
int d = (int)fread(&buffer[p],1,len,file);
if( d <= 0 )
{
HANDLE_FINTR(file,file_contents);
fclose(file);
file_error("file_contents",name);
}
p += d;
len -= d;
}
fclose(file);
hx::ExitGCFreeZone();
return String::create(&buffer[0], buffer.size());
}
#include <kinc/io/filereader.h>
/**
file_contents : f:string -> string
<doc>Read the content of the file [f] and return it.</doc>
**/
Array<unsigned char> _hx_std_file_contents_bytes( String name )
{
hx::strbuf buf;
hx::EnterGCFreeZone();
kinc_file_reader_t file;
if(!kinc_file_reader_open(&file, name.utf8_str(&buf), KINC_FILE_TYPE_ASSET))
file_error("file_contents",name);
hx::ExitGCFreeZone();
Array<unsigned char> buffer = Array_obj<unsigned char>::__new(kinc_file_reader_size(&file),kinc_file_reader_size(&file));
hx::EnterGCFreeZone();
if (kinc_file_reader_size(&file))
{
char *dest = (char *)&buffer[0];
kinc_file_reader_read(&file, dest, kinc_file_reader_size(&file));
}
kinc_file_reader_close(&file);
hx::ExitGCFreeZone();
return buffer;
}
Dynamic _hx_std_file_stdin()
{
fio *f = new fio();
f->create(stdin, HX_CSTRING("stdin"), false);
return f;
}
Dynamic _hx_std_file_stdout()
{
fio *f = new fio();
f->create(stdout, HX_CSTRING("stdout"), false);
return f;
}
Dynamic _hx_std_file_stderr()
{
fio *f = new fio();
f->create(stderr, HX_CSTRING("stderr"), false);
return f;
}

View File

@ -0,0 +1,621 @@
#include <hxcpp.h>
#include <hx/OS.h>
#if !defined(HX_WINRT) && !defined(EPPC)
#ifdef NEKO_WINDOWS
# include <windows.h>
#else
# include <sys/types.h>
# include <unistd.h>
# include <memory.h>
# include <errno.h>
# include <signal.h>
# if (defined(ANDROID) || defined(BLACKBERRY) || defined(EMSCRIPTEN) || defined(__FreeBSD__)) && !defined(KORE_CONSOLE)
# include <sys/wait.h>
# elif !defined(NEKO_MAC) && !defined(KORE_CONSOLE)
# include <wait.h>
# endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <string>
namespace
{
#ifndef NEKO_WINDOWS
static int do_close( int fd )
{
#ifndef KORE_CONSOLE
POSIX_LABEL(close_again);
if( close(fd) != 0 ) {
HANDLE_EINTR(close_again);
return 1;
}
#endif
return 0;
}
#endif
struct vprocess : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdProcess };
bool open;
#ifdef NEKO_WINDOWS
HANDLE oread;
HANDLE eread;
HANDLE iwrite;
PROCESS_INFORMATION pinf;
#define HANDLE_INIT 0
#else
int oread;
int eread;
int iwrite;
int pid;
#define HANDLE_INIT -1
#endif
void create()
{
open = true;
oread = HANDLE_INIT;
eread = HANDLE_INIT;
iwrite = HANDLE_INIT;
_hx_set_finalizer(this, finalize);
}
void destroy()
{
if (open)
{
#ifdef NEKO_WINDOWS
if (eread)
CloseHandle(eread);
if (oread)
CloseHandle(oread);
if (iwrite)
CloseHandle(iwrite);
CloseHandle(pinf.hProcess);
CloseHandle(pinf.hThread);
#else
if (eread!=-1)
do_close(eread);
if (oread!=-1)
do_close(oread);
if (iwrite!=-1)
do_close(iwrite);
#endif
open = false;
}
}
static void finalize(Dynamic obj)
{
((vprocess *)(obj.mPtr))->destroy();
}
String toString() { return HX_CSTRING("vprocess"); }
};
vprocess *getProcess(Dynamic handle)
{
vprocess *p = dynamic_cast<vprocess *>(handle.mPtr);
if (!p)
hx::Throw(HX_CSTRING("Invalid process"));
return p;
}
/**
<doc>
<h1>Process</h1>
<p>
An API for starting and communication with sub processes.
</p>
</doc>
**/
} // end anon namespace
template<typename T>
static String TQuoted(const T *ptr, int len)
{
std::vector<T> quoted;
quoted.reserve(len*2);
unsigned int bs_count = 0;
for(int j=0;j<len;j++)
{
T c = ptr[j];
switch( c )
{
case '"':
// Double backslashes.
for (int k=0;k<bs_count*2;k++)
quoted.push_back('\\');
bs_count = 0;
quoted.push_back('\\');
quoted.push_back('"');
break;
case '\\':
// Don't know if we need to double yet.
bs_count++;
break;
default:
// Normal char
for (int k=0;k<bs_count;k++)
quoted.push_back('\\');
bs_count = 0;
quoted.push_back(c);
break;
}
}
// Add remaining backslashes, if any.
for (int k=0;k<bs_count*2;k++)
quoted.push_back('\\');
int qlen = (int)quoted.size();
quoted.push_back('\0');
return String::create( &quoted[0], qlen );
}
#if defined(NEKO_WINDOWS)
static String quoteString(String v)
{
#ifdef HX_SMART_STRINGS
if (v.isUTF16Encoded())
return TQuoted(v.raw_wptr(),v.length);
#endif
return TQuoted(v.raw_ptr(),v.length);
}
#endif
/**
process_run : cmd:string -> args:string array -> 'process
<doc>
Start a process using a command.
When args is not null, cmd and args will be auto-quoted/escaped.
If no auto-quoting/escaping is desired, you should append necessary
arguments to cmd as if it is inputted to the shell directly, and pass
null to args.
inShowParam = only for windows, SHOW_* from "ShowWindow" function
default = 1 = SHOW_WINDOW
</doc>
**/
Dynamic _hx_std_process_run( String cmd, Array<String> vargs, int inShowParam )
{
#if defined(APPLETV) || defined(HX_APPLEWATCH) || defined(KORE_CONSOLE)
return null();
#else
vprocess *p = 0;
bool isRaw = !vargs.mPtr;
#ifdef NEKO_WINDOWS
{
SECURITY_ATTRIBUTES sattr;
STARTUPINFOW sinf;
HANDLE proc = GetCurrentProcess();
HANDLE oread,eread,iwrite;
// creates commandline
String b;
if (isRaw)
{
b = HX_CSTRING("\"");
const char* cmdexe = getenv("COMSPEC");
if (!cmdexe) cmdexe = "cmd.exe";
b += String(cmdexe) + HX_CSTRING("\" /C \"") + cmd + HX_CSTRING("\"");
}
else
{
b = HX_CSTRING("\"") + cmd + HX_CSTRING("\"");
for(int i=0;i<vargs->length;i++)
{
b += HX_CSTRING(" \"");
if (vargs[i].length)
b += quoteString(vargs[i]);
b += HX_CSTRING("\"");
}
}
const wchar_t *name = b.__WCStr();
hx::EnterGCFreeZone();
// startup process
sattr.nLength = sizeof(sattr);
sattr.bInheritHandle = TRUE;
sattr.lpSecurityDescriptor = NULL;
memset(&sinf,0,sizeof(sinf));
sinf.cb = sizeof(sinf);
sinf.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
sinf.wShowWindow = inShowParam;
CreatePipe(&oread,&sinf.hStdOutput,&sattr,0);
CreatePipe(&eread,&sinf.hStdError,&sattr,0);
CreatePipe(&sinf.hStdInput,&iwrite,&sattr,0);
HANDLE procOread,procEread,procIwrite;
DuplicateHandle(proc,oread,proc,&procOread,0,FALSE,DUPLICATE_SAME_ACCESS);
DuplicateHandle(proc,eread,proc,&procEread,0,FALSE,DUPLICATE_SAME_ACCESS);
DuplicateHandle(proc,iwrite,proc,&procIwrite,0,FALSE,DUPLICATE_SAME_ACCESS);
CloseHandle(oread);
CloseHandle(eread);
CloseHandle(iwrite);
//printf("Cmd %s\n",val_string(cmd));
PROCESS_INFORMATION pinf;
memset(&pinf,0,sizeof(pinf));
if( !CreateProcessW(NULL,(wchar_t *)name,NULL,NULL,TRUE,CREATE_NO_WINDOW,NULL,NULL,&sinf,&pinf) )
{
hx::ExitGCFreeZone();
hx::Throw(HX_CSTRING("Could not start process"));
}
// close unused pipes
CloseHandle(sinf.hStdOutput);
CloseHandle(sinf.hStdError);
CloseHandle(sinf.hStdInput);
hx::ExitGCFreeZone();
p = new vprocess;
p->create();
p->oread = procOread;
p->eread = procEread;
p->iwrite = procIwrite;
p->pinf = pinf;
}
#else // not windows ...
{
int input[2], output[2], error[2];
if( pipe(input) || pipe(output) || pipe(error) )
return null();
hx::strbuf buf;
std::vector< std::string > values;
if (isRaw)
{
values.resize(3);
values[0] = "/bin/sh";
values[1] = "-c";
values[2] = cmd.utf8_str(&buf);
}
else
{
values.resize(vargs->length+1);
values[0] = cmd.utf8_str(&buf);
for(int i=0;i<vargs->length;i++)
values[i+1] = vargs[i].utf8_str(&buf);
}
std::vector<const char *> argv(values.size()+1);
for(int i=0;i<values.size();i++)
argv[i] = values[i].c_str();
int pid = fork();
if( pid == -1 )
return null();
// child
if( pid == 0 )
{
close(input[1]);
close(output[0]);
close(error[0]);
dup2(input[0],0);
dup2(output[1],1);
dup2(error[1],2);
execvp(argv[0],(char* const*)&argv[0]);
fprintf(stderr,"Command not found : %S\n",cmd.wchar_str());
exit(1);
}
// parent
do_close(input[0]);
do_close(output[1]);
do_close(error[1]);
p = new vprocess;
p->create();
p->iwrite = input[1];
p->oread = output[0];
p->eread = error[0];
p->pid = pid;
}
#endif
return p;
#endif // not APPLETV/HX_APPLEWATCH
}
/**
process_stdout_read : 'process -> buf:string -> pos:int -> len:int -> int
<doc>
Read up to [len] bytes in [buf] starting at [pos] from the process stdout.
Returns the number of bytes readed this way. Raise an exception if this
process stdout is closed and no more data is available for reading.
For hxcpp, the input buffer is in bytes, not characters
</doc>
**/
int _hx_std_process_stdout_read( Dynamic handle, Array<unsigned char> buf, int pos, int len )
{
if( pos < 0 || len < 0 || pos + len > buf->length )
return 0;
vprocess *p = getProcess(handle);
unsigned char *dest = &buf[0];
hx::EnterGCFreeZone();
#ifdef NEKO_WINDOWS
DWORD nbytes = 0;
if( !ReadFile(p->oread,dest+pos,len,&nbytes,0) )
nbytes = 0;
#elif !defined(KORE_CONSOLE)
int nbytes = read(p->oread,dest + pos,len);
if( nbytes <= 0 )
nbytes = 0;
#endif
hx::ExitGCFreeZone();
#ifdef KORE_CONSOLE
return 0;
#else
return nbytes;
#endif
}
/**
process_stderr_read : 'process -> buf:string -> pos:int -> len:int -> int
<doc>
Read up to [len] bytes in [buf] starting at [pos] from the process stderr.
Returns the number of bytes readed this way. Raise an exception if this
process stderr is closed and no more data is available for reading.
</doc>
**/
int _hx_std_process_stderr_read( Dynamic handle, Array<unsigned char> buf, int pos, int len )
{
if( pos < 0 || len < 0 || pos + len > buf->length )
return 0;
vprocess *p = getProcess(handle);
unsigned char *dest = &buf[0];
hx::EnterGCFreeZone();
#ifdef NEKO_WINDOWS
DWORD nbytes = 0;
if( !ReadFile(p->eread,dest+pos,len,&nbytes,0) )
nbytes = 0;
#elif !defined(KORE_CONSOLE)
int nbytes = read(p->eread,dest + pos,len);
if( nbytes <= 0 )
nbytes = 0;
#endif
hx::ExitGCFreeZone();
#ifdef KORE_CONSOLE
return 0;
#else
return nbytes;
#endif
}
/**
process_stdin_write : 'process -> buf:string -> pos:int -> len:int -> int
<doc>
Write up to [len] bytes from [buf] starting at [pos] to the process stdin.
Returns the number of bytes writen this way. Raise an exception if this
process stdin is closed.
</doc>
**/
int _hx_std_process_stdin_write( Dynamic handle, Array<unsigned char> buf, int pos, int len )
{
if( pos < 0 || len < 0 || pos + len > buf->length )
return 0;
vprocess *p = getProcess(handle);
unsigned char *src = &buf[0];
hx::EnterGCFreeZone();
#ifdef NEKO_WINDOWS
DWORD nbytes =0;
if( !WriteFile(p->iwrite,src+pos,len,&nbytes,0) )
nbytes = 0;
#elif !defined(KORE_CONSOLE)
int nbytes = write(p->iwrite,src+pos,len);
if( nbytes == -1 )
nbytes = 0;
#endif
hx::ExitGCFreeZone();
#ifdef KORE_CONSOLE
return 0;
#else
return nbytes;
#endif
}
/**
process_stdin_close : 'process -> void
<doc>
Close the process standard input.
</doc>
**/
void _hx_std_process_stdin_close( Dynamic handle )
{
vprocess *p = getProcess(handle);
#ifdef NEKO_WINDOWS
if ( p->iwrite )
CloseHandle(p->iwrite);
#else
if( p->iwrite!=-1 )
do_close(p->iwrite);
#endif
p->iwrite = HANDLE_INIT;
}
/**
process_exit : 'process -> int
<doc>
Wait until the process terminate, then returns its exit code.
</doc>
**/
#if (HXCPP_API_LEVEL > 420)
Dynamic _hx_std_process_exit( Dynamic handle, bool block )
{
vprocess *p = getProcess(handle);
hx::EnterGCFreeZone();
#ifdef NEKO_WINDOWS
{
DWORD rval;
DWORD wait = INFINITE;
if (!block)
wait = 0;
WaitForSingleObject(p->pinf.hProcess,wait);
hx::ExitGCFreeZone();
if( !GetExitCodeProcess(p->pinf.hProcess,&rval) && block)
return 0;
else if (!block && rval == STILL_ACTIVE)
return null();
else
return rval;
}
#else
int options=0;
if (!block)
options = WNOHANG;
int rval=0;
pid_t ret=-1;
while( (ret = waitpid(p->pid,&rval,options)) != p->pid )
{
if( errno == EINTR )
continue;
if (!block && ret == 0)
{
hx::ExitGCFreeZone();
return null();
}
hx::ExitGCFreeZone();
return 0;
}
hx::ExitGCFreeZone();
if( !WIFEXITED(rval) )
return 0;
return WEXITSTATUS(rval);
#endif
}
#else
int _hx_std_process_exit( Dynamic handle )
{
vprocess *p = getProcess(handle);
hx::EnterGCFreeZone();
#ifdef NEKO_WINDOWS
{
DWORD rval;
WaitForSingleObject(p->pinf.hProcess,INFINITE);
hx::ExitGCFreeZone();
if( !GetExitCodeProcess(p->pinf.hProcess,&rval) )
return 0;
return rval;
}
#elif !defined(KORE_CONSOLE)
int rval=0;
while( waitpid(p->pid,&rval,0) != p->pid )
{
if( errno == EINTR )
continue;
hx::ExitGCFreeZone();
return 0;
}
hx::ExitGCFreeZone();
if( !WIFEXITED(rval) )
return 0;
return WEXITSTATUS(rval);
#else
hx::ExitGCFreeZone();
return 0;
#endif
}
#endif
/**
process_pid : 'process -> int
<doc>
Returns the process id.
</doc>
**/
int _hx_std_process_pid( Dynamic handle )
{
vprocess *p = getProcess(handle);
#ifdef NEKO_WINDOWS
return p->pinf.dwProcessId;
#else
return p->pid;
#endif
}
void _hx_std_process_kill( Dynamic handle )
{
vprocess *p = getProcess(handle);
#ifdef NEKO_WINDOWS
TerminateProcess(p->pinf.hProcess, -1);
#else
kill(p->pid, SIGTERM);
#endif
}
/**
process_close : 'process -> void
<doc>
Close the process I/O.
</doc>
**/
void _hx_std_process_close( Dynamic handle )
{
vprocess *p = getProcess(handle);
p->destroy();
}
#else // !HX_WINRT
Dynamic _hx_std_process_run( String cmd, Array<String> vargs, int inShowParam ){ return null(); }
int _hx_std_process_stdout_read( Dynamic handle, Array<unsigned char> buf, int pos, int len ) { return 0; }
int _hx_std_process_stderr_read( Dynamic handle, Array<unsigned char> buf, int pos, int len ) { return 0; }
int _hx_std_process_stdin_write( Dynamic handle, Array<unsigned char> buf, int pos, int len ) { return 0; }
void _hx_std_process_stdin_close( Dynamic handle ) { }
#if (HXCPP_API_LEVEL > 420)
Dynamic _hx_std_process_exit( Dynamic handle, bool block ) { return 0; }
#else
int _hx_std_process_exit( Dynamic handle ) { return 0; }
#endif
int _hx_std_process_pid( Dynamic handle ) { return 0; }
void _hx_std_process_close( Dynamic handle ) { }
void _hx_std_process_kill( Dynamic handle ) { }
#endif // HX_WINRT

View File

@ -0,0 +1,181 @@
#include <hxcpp.h>
#include <hx/OS.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#ifdef HX_WINDOWS
# include <windows.h>
# include <process.h>
#elif defined(EPPC) || defined(KORE_CONSOLE)
# include <time.h>
#else
# include <sys/time.h>
# include <sys/types.h>
# include <unistd.h>
#endif
/**
<doc>
<h1>Random</h1>
<p>A seeded pseudo-random generator</p>
</doc>
**/
#define NSEEDS 25
#ifdef MAX
#undef MAX
#endif
#define MAX 7
namespace
{
struct rnd : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdRandom };
unsigned long seeds[NSEEDS];
unsigned long cur;
String toString() { return HX_CSTRING("rand"); }
};
static unsigned long mag01[2]={
0x0, 0x8ebfd028 // magic, don't change
};
static const unsigned long init_seeds[] = {
0x95f24dab, 0x0b685215, 0xe76ccae7, 0xaf3ec239, 0x715fad23,
0x24a590ad, 0x69e4b5ef, 0xbf456141, 0x96bc1b7b, 0xa7bdf825,
0xc1de75b7, 0x8858a9c9, 0x2da87693, 0xb657f9dd, 0xffdc8a9f,
0x8121da71, 0x8b823ecb, 0x885d05f5, 0x4e20cd47, 0x5a9ad5d9,
0x512c0c03, 0xea857ccd, 0x4cc1d30f, 0x8891a8a1, 0xa6b7aadb
};
static void rnd_set_seed( rnd *r, int s )
{
int i;
r->cur = 0;
memcpy(r->seeds,init_seeds,sizeof(init_seeds));
for(i=0;i<NSEEDS;i++)
r->seeds[i] ^= s;
}
rnd *getRnd(Dynamic handle)
{
rnd *r = dynamic_cast<rnd *>(handle.mPtr);
if (!r)
hx::Throw(HX_CSTRING("Invalid random handle"));
return r;
}
} // end anon namespace
Dynamic _hx_std_random_new()
{
rnd *r = new rnd();
#if defined(NEKO_WINDOWS) && !defined(KORE_CONSOLE)
#if defined(HX_WINRT) && defined(__cplusplus_winrt)
int pid = Windows::Security::Cryptography::CryptographicBuffer::GenerateRandomNumber();
#else
int pid = GetCurrentProcessId();
#endif
#elif defined(EPPC) || defined(KORE_CONSOLE)
int pid = 1;
#else
int pid = getpid();
#endif
unsigned int t;
#ifdef HX_WINRT
t = (unsigned int)GetTickCount64();
#elif defined(NEKO_WINDOWS) && !defined(KORE_CONSOLE)
t = GetTickCount();
#elif defined(EPPC) || defined(KORE_CONSOLE)
time_t tod;
time(&tod);
t = (double)tod;
#else
struct timeval tv;
gettimeofday(&tv,NULL);
t = tv.tv_sec * 1000000 + tv.tv_usec;
#endif
rnd_set_seed(r,t ^ (pid | (pid << 16)));
return r;
}
static unsigned int rnd_int( rnd *r )
{
unsigned int y;
int pos = r->cur++;
if( pos >= NSEEDS ) {
int kk;
for(kk=0;kk<NSEEDS-MAX;kk++)
r->seeds[kk] = r->seeds[kk+MAX] ^ (r->seeds[kk] >> 1) ^ mag01[r->seeds[kk] % 2];
for(;kk<NSEEDS;kk++)
r->seeds[kk] = r->seeds[kk+(MAX-NSEEDS)] ^ (r->seeds[kk] >> 1) ^ mag01[r->seeds[kk] % 2];
r->cur = 1;
pos = 0;
}
y = r->seeds[pos];
y ^= (y << 7) & 0x2b5b2500;
y ^= (y << 15) & 0xdb8b0000;
y ^= (y >> 16);
return y;
}
static double rnd_float( rnd *r )
{
double big = 4294967296.0;
return ((rnd_int(r) / big + rnd_int(r)) / big + rnd_int(r)) / big;
}
/**
random_new : void -> 'random
<doc>Create a new random with random seed</doc>
**/
#include<stdlib.h>
/**
random_set_seed : 'random -> int -> void
<doc>Set the generator seed</doc>
**/
void _hx_std_random_set_seed( Dynamic handle, int v )
{
rnd_set_seed( getRnd(handle) ,v);
}
/**
random_int : 'random -> max:int -> int
<doc>Return a random integer modulo [max]</doc>
**/
int _hx_std_random_int( Dynamic handle, int max )
{
if( max <= 0 )
return 0;
return (rnd_int( getRnd(handle)) & 0x3FFFFFFF) % max;
}
/**
random_float : 'random -> float
<doc>Return a random float</doc>
**/
double _hx_std_random_float( Dynamic handle )
{
return rnd_float(getRnd(handle));
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,915 @@
#include <hxcpp.h>
#include <hx/OS.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifndef EPPC
#include <sys/types.h>
#include <sys/stat.h>
#endif
#ifndef __clang__
#pragma warning(disable : 4996)
#endif
#ifdef NEKO_WINDOWS
#include <windows.h>
#include <direct.h>
#include <conio.h>
#include <locale.h>
#else
#include <errno.h>
#if !defined(EPPC) && !defined(KORE_CONSOLE)
#include <unistd.h>
#include <dirent.h>
#include <termios.h>
#include <sys/time.h>
#include <sys/times.h>
#endif
#include <limits.h>
#ifndef ANDROID
#include <locale.h>
#if !defined(BLACKBERRY) && !defined(EPPC) && !defined(GCW0) && !defined(__GLIBC__) && !defined(KORE_CONSOLE)
#include <xlocale.h>
#endif
#endif
#endif
#ifdef EMSCRIPTEN
#include <sys/wait.h>
#endif
#if !defined(IPHONE) && !defined(APPLETV) && !defined(HX_APPLEWATCH)
#ifdef NEKO_MAC
#include <sys/syslimits.h>
#include <limits.h>
#include <mach-o/dyld.h>
#endif
#endif
#if defined(HX_WINRT) && !defined(_XBOX_ONE)
#include <string>
#endif
#ifdef HX_ANDROID
#include <sys/wait.h>
#endif
#ifndef CLK_TCK
#define CLK_TCK 100
#endif
/**
<doc>
<h1>System</h1>
<p>
Interactions with the operating system.
</p>
</doc>
**/
/**
get_env : string -> string?
<doc>Get some environment variable if exists</doc>
**/
String _hx_std_get_env( String v )
{
#if defined(HX_WINRT) || defined(KORE_CONSOLE)
return String();
#else
#if defined(NEKO_WINDOWS) && defined(HX_SMART_STRINGS)
hx::strbuf wbuf;
return String::create( _wgetenv( v.wchar_str(&wbuf) ) );
#else
hx::strbuf buf;
return String::create( getenv(v.utf8_str(&buf)) );
#endif
#endif
}
/**
put_env : var:string -> val:string -> void
<doc>Set some environment variable value</doc>
**/
void _hx_std_put_env( String e, String v )
{
#if defined(HX_WINRT) || defined(KORE_CONSOLE)
// Do nothing
#elif defined(NEKO_WINDOWS)
String set = e + HX_CSTRING("=") + (v != null()?v:"");
#ifdef HX_SMART_STRINGS
if (set.isUTF16Encoded())
_wputenv(set.wchar_str());
else
#endif
putenv(set.utf8_str());
#else
if (v == null())
unsetenv(e.utf8_str());
else
setenv(e.utf8_str(),v.utf8_str(),1);
#endif
}
/**
sys_sleep : number -> void
<doc>Sleep a given number of seconds</doc>
**/
void _hx_std_sys_sleep( double f )
{
hx::EnterGCFreeZone();
#if defined(NEKO_WINDOWS)
Sleep((DWORD)(f * 1000));
#elif defined(EPPC) || defined(KORE_CONSOLE)
//TODO: Implement sys_sleep for EPPC
#else
{
struct timespec t;
struct timespec tmp;
t.tv_sec = (int)(f);
t.tv_nsec = (int)(((f) - t.tv_sec) * 1e9);
while( nanosleep(&t,&tmp) == -1 )
{
if( errno != EINTR )
{
hx::ExitGCFreeZone();
return;
}
t = tmp;
}
}
#endif
hx::ExitGCFreeZone();
}
/**
set_time_locale : string -> bool
<doc>Set the locale for LC_TIME, returns true on success</doc>
**/
bool _hx_std_set_time_locale( String l )
{
#if defined(ANDROID) || defined(GCW0) || defined(KORE_CONSOLE)
return false;
#else
#if defined(NEKO_POSIX)
locale_t lc, old;
lc = newlocale(LC_TIME_MASK,l.utf8_str(),NULL);
if( !lc )
return false;
old = uselocale(lc);
if( !old )
{
freelocale(lc);
return false;
}
if( old != LC_GLOBAL_LOCALE )
freelocale(old);
return true;
#else
#if defined(NEKO_WINDOWS) && defined(HX_SMART_STRINGS)
if (l.isUTF16Encoded())
return _wsetlocale(LC_TIME,l.wchar_str());
#endif
return setlocale(LC_TIME,l.utf8_str());
#endif
#endif // !Android
}
/**
get_cwd : void -> string
<doc>Return current working directory</doc>
**/
String _hx_std_get_cwd()
{
#if defined(HX_WINRT) || defined(KORE_CONSOLE)
return HX_CSTRING("ms-appdata:///local/");
#elif defined(EPPC)
return String();
#else
#ifdef NEKO_WINDOWS
wchar_t buf[261];
int l;
if( !GetCurrentDirectoryW(260,buf) )
return String();
l = (int)wcslen(buf);
if( buf[l-1] != '/' && buf[l-1] != '\\' ) {
buf[l] = '/';
buf[l+1] = 0;
}
#else
char buf[1025];
int l;
if( getcwd(buf,1024) == NULL )
return String();
l = (int)strlen(buf);
if( buf[l-1] != '/' && buf[l-1] != '\\' ) {
buf[l] = '/';
buf[l+1] = 0;
}
#endif
return String::create(buf);
#endif
}
/**
set_cwd : string -> void
<doc>Set current working directory</doc>
**/
bool _hx_std_set_cwd( String d )
{
#if !defined(HX_WINRT) && !defined(EPPC) && !defined(KORE_CONSOLE)
#ifdef NEKO_WINDOWS
return SetCurrentDirectoryW(d.wchar_str()) == 0;
#else
return chdir(d.utf8_str()) == 0;
#endif
#else
return false;
#endif
}
/**
sys_string : void -> string
<doc>
Return the local system string. The current value are possible :
<ul>
<li>[Windows]</li>
<li>[Linux]</li>
<li>[BSD]</li>
<li>[Mac]</li>
</ul>
</doc>
**/
String _hx_std_sys_string()
{
#if defined(KORE_CONSOLE)
return HX_CSTRING("Kore Console");
#elif defined(HX_WINRT)
return HX_CSTRING("WinRT");
#elif defined(NEKO_WINDOWS)
return HX_CSTRING("Windows");
#elif defined(NEKO_GNUKBSD)
return HX_CSTRING("GNU/kFreeBSD");
#elif defined(NEKO_LINUX)
return HX_CSTRING("Linux");
#elif defined(NEKO_BSD)
return HX_CSTRING("BSD");
#elif defined(NEKO_MAC)
return HX_CSTRING("Mac");
#elif defined(ANDROID)
return HX_CSTRING("Android");
#elif defined(BLACKBERRY)
return HX_CSTRING("BlackBerry");
#elif defined(EMSCRIPTEN)
return HX_CSTRING("Emscripten");
#elif defined(EPPC)
return HX_CSTRING("EPPC");
#else
#error Unknow system string
#endif
}
/**
sys_is64 : void -> bool
<doc>
Returns true if we are on a 64-bit system
</doc>
**/
bool _hx_std_sys_is64()
{
#ifdef NEKO_64BITS
return true;
#else
return false;
#endif
}
/**
sys_command : string -> int
<doc>Run the shell command and return exit code</doc>
**/
int _hx_std_sys_command( String cmd )
{
#if defined(HX_WINRT) || defined(EMSCRIPTEN) || defined(EPPC) || defined(IPHONE) || defined(APPLETV) || defined(HX_APPLEWATCH) || defined(KORE_CONSOLE)
return -1;
#else
if( !cmd.raw_ptr() || !cmd.length )
return -1;
#ifdef NEKO_WINDOWS
int result = 0;
hx::EnterGCFreeZone();
#ifdef HX_SMART_STRINGS
if (!cmd.isUTF16Encoded())
result = system(cmd.raw_ptr());
else
#endif
{
hx::strbuf wbuf;
result = _wsystem(cmd.wchar_str(&wbuf));
}
#else
hx::strbuf buf;
hx::EnterGCFreeZone();
int result = system(cmd.utf8_str(&buf));
#endif
hx::ExitGCFreeZone();
#if !defined(NEKO_WINDOWS) && !defined(__FreeBSD__)
result = WEXITSTATUS(result) | (WTERMSIG(result) << 8);
#endif
return result;
#endif
}
/**
sys_exit : int -> void
<doc>Exit with the given errorcode. Never returns.</doc>
**/
void _hx_std_sys_exit( int code )
{
exit(code);
}
/**
sys_exists : string -> bool
<doc>Returns true if the file or directory exists.</doc>
**/
bool _hx_std_sys_exists( String path )
{
#if defined(EPPC) || defined(KORE_CONSOLE)
return true;
#else
#ifdef NEKO_WINDOWS
const wchar_t * wpath = path.wchar_str();
hx::EnterGCFreeZone();
bool result = GetFileAttributesW(wpath) != INVALID_FILE_ATTRIBUTES;
#else
struct stat st;
hx::EnterGCFreeZone();
hx::strbuf buf;
bool result = stat(path.utf8_str(&buf),&st) == 0;
#endif
hx::ExitGCFreeZone();
return result;
#endif
}
/**
file_delete : string -> void
<doc>Delete the file. Exception on error.</doc>
**/
void _hx_std_file_delete( String path )
{
#if !defined(EPPC) && !defined(KORE_CONSOLE)
hx::EnterGCFreeZone();
bool err = false;
#if defined(NEKO_WINDOWS) && defined(HX_SMART_STRINGS)
if (path.isUTF16Encoded())
err = _wunlink(path.wchar_str());
else
#endif
{
hx::strbuf buf;
err = unlink(path.utf8_str(&buf));
}
hx::ExitGCFreeZone();
if (err)
hx::Throw( HX_CSTRING("Could not delete ") + path );
#endif
}
/**
sys_rename : from:string -> to:string -> void
<doc>Rename the file or directory. Exception on error.</doc>
**/
void _hx_std_sys_rename( String path, String newname )
{
#ifdef KORE_CONSOLE
bool err = true;
#else
hx::EnterGCFreeZone();
hx::strbuf buf0;
hx::strbuf buf1;
#ifdef NEKO_WINDOWS
bool err = _wrename(path.wchar_str(&buf0),newname.wchar_str(&buf1));
#else
bool err = rename(path.utf8_str(&buf0),newname.utf8_str(&buf1));
#endif
hx::ExitGCFreeZone();
#endif
if (err)
hx::Throw(HX_CSTRING("Could not rename"));
}
#define STATF(f) o->Add(HX_CSTRING(#f),(int)(s.st_##f))
/**
sys_stat : string -> {
gid => int,
uid => int,
atime => 'int,
mtime => 'int,
ctime => 'int,
dev => int,
ino => int,
nlink => int,
rdev => int,
mode => int,
size => int
}
<doc>Run the [stat] command on the given file or directory.</doc>
**/
Dynamic _hx_std_sys_stat( String path )
{
#if defined(EPPC) || defined(KORE_CONSOLE)
return null();
#else
hx::EnterGCFreeZone();
bool err = false;
#if defined(NEKO_WINDOWS)
struct _stat s;
#if defined(HX_SMART_STRINGS)
if (path.isUTF16Encoded())
{
hx::strbuf buf;
err = _wstat(path.wchar_str(&buf),&s);
}
else
#endif
{
hx::strbuf buf;
err = _stat(path.utf8_str(&buf),&s);
}
#else
struct stat s;
hx::strbuf buf;
err = stat(path.utf8_str(&buf),&s);
#endif
hx::ExitGCFreeZone();
if (err)
return null();
hx::Anon o = hx::Anon_obj::Create();
STATF(gid);
STATF(uid);
STATF(atime);
STATF(mtime);
STATF(ctime);
STATF(dev);
STATF(ino);
STATF(mode);
STATF(nlink);
STATF(rdev);
STATF(size);
STATF(mode);
return o;
#endif
}
/**
sys_file_type : string -> string
<doc>
Return the type of the file. The current values are possible :
<ul>
<li>[file]</li>
<li>[dir]</li>
<li>[symlink]</li>
<li>[sock]</li>
<li>[char]</li>
<li>[block]</li>
<li>[fifo]</li>
</ul>
</doc>
**/
String _hx_std_sys_file_type( String path )
{
if (path==null())
return String();
#if defined(EPPC) || defined(KORE_CONSOLE)
return String();
#else
hx::EnterGCFreeZone();
bool err = false;
#if defined(NEKO_WINDOWS)
struct _stat s;
#if defined(HX_SMART_STRINGS)
if (path.isUTF16Encoded())
{
hx::strbuf buf;
err = _wstat(path.wchar_str(&buf),&s);
}
else
#endif
{
hx::strbuf buf;
err = _stat(path.utf8_str(&buf),&s);
}
#else
struct stat s;
hx::strbuf buf;
err = stat(path.utf8_str(&buf),&s);
#endif
hx::ExitGCFreeZone();
if (err)
return String();
if( s.st_mode & S_IFREG )
return HX_CSTRING("file");
if( s.st_mode & S_IFDIR )
return HX_CSTRING("dir");
if( s.st_mode & S_IFCHR )
return HX_CSTRING("char");
#ifndef NEKO_WINDOWS
if( s.st_mode & S_IFLNK )
return HX_CSTRING("symlink");
if( s.st_mode & S_IFBLK )
return HX_CSTRING("block");
if( s.st_mode & S_IFIFO )
return HX_CSTRING("fifo");
if( s.st_mode & S_IFSOCK )
return HX_CSTRING("sock");
#endif
return String();
#endif
}
/**
sys_create_dir : string -> mode:int -> void
<doc>Create a directory with the specified rights</doc>
**/
bool _hx_std_sys_create_dir( String path, int mode )
{
#if defined(EPPC) || defined(KORE_CONSOLE)
return true;
#else
#ifdef NEKO_WINDOWS
const wchar_t * wpath = path.wchar_str();
hx::EnterGCFreeZone();
bool err = _wmkdir(wpath);
#else
hx::EnterGCFreeZone();
hx::strbuf buf;
bool err = mkdir(path.utf8_str(&buf), mode);
#endif
hx::ExitGCFreeZone();
return !err;
#endif
}
/**
sys_remove_dir : string -> void
<doc>Remove a directory. Exception on error</doc>
**/
void _hx_std_sys_remove_dir( String path )
{
#if defined(EPPC) || defined(KORE_CONSOLE)
#else
hx::EnterGCFreeZone();
bool ok = false;
#if defined(NEKO_WINDOWS) && defined(HX_SMART_STRINGS)
if (path.isUTF16Encoded())
{
ok = _wrmdir(path.wchar_str()) == 0;
}
else
#endif
{
hx::strbuf buf;
ok = rmdir(path.utf8_str(&buf)) == 0;
}
hx::ExitGCFreeZone();
if (!ok)
hx::Throw(HX_CSTRING("Could not remove directory"));
#endif
}
/**
sys_time : void -> float
<doc>Return an accurate local time stamp in seconds since Jan 1 1970</doc>
**/
double _hx_std_sys_time()
{
#ifdef NEKO_WINDOWS
#define EPOCH_DIFF (134774*24*60*60.0)
SYSTEMTIME t;
FILETIME ft;
ULARGE_INTEGER ui;
GetSystemTime(&t);
if( !SystemTimeToFileTime(&t,&ft) )
return 0;
ui.LowPart = ft.dwLowDateTime;
ui.HighPart = ft.dwHighDateTime;
return ( ((double)ui.QuadPart) / 10000000.0 - EPOCH_DIFF );
#elif defined(EPPC) || defined(KORE_CONSOLE)
time_t tod;
time(&tod);
return ((double)tod);
#else
struct timeval tv;
if( gettimeofday(&tv,NULL) != 0 )
return 0;
return ( tv.tv_sec + ((double)tv.tv_usec) / 1000000.0 );
#endif
}
/**
sys_cpu_time : void -> float
<doc>Return the most accurate CPU time spent since the process started (in seconds)</doc>
**/
double _hx_std_sys_cpu_time()
{
#if defined(HX_WINRT) && !defined(_XBOX_ONE)
return ((double)GetTickCount64()/1000.0);
#elif defined(NEKO_WINDOWS)
FILETIME unused;
FILETIME stime;
FILETIME utime;
if( !GetProcessTimes(GetCurrentProcess(),&unused,&unused,&stime,&utime) )
return 0;
return ( ((double)(utime.dwHighDateTime+stime.dwHighDateTime)) * 65.536 * 6.5536 + (((double)utime.dwLowDateTime + (double)stime.dwLowDateTime) / 10000000) );
#elif defined(EPPC) || defined(KORE_CONSOLE)
return ((double)clock()/(double)CLOCKS_PER_SEC);
#else
struct tms t;
times(&t);
return ( ((double)(t.tms_utime + t.tms_stime)) / CLK_TCK );
#endif
}
/**
sys_read_dir : string -> string list
<doc>Return the content of a directory</doc>
**/
Array<String> _hx_std_sys_read_dir( String p )
{
Array<String> result = Array_obj<String>::__new();
#if defined(NEKO_WINDOWS)
const wchar_t *path = p.wchar_str();
size_t len = wcslen(path);
if (len>MAX_PATH)
return null();
WIN32_FIND_DATAW d;
HANDLE handle;
#if defined(HX_WINRT) && !defined(_XBOX_ONE)
std::wstring tempWStr(path);
std::string searchPath(tempWStr.begin(), tempWStr.end());
#else
wchar_t searchPath[ MAX_PATH + 4 ];
memcpy(searchPath,path, len*sizeof(wchar_t));
#endif
if( len && path[len-1] != '/' && path[len-1] != '\\' )
searchPath[len++] = '/';
searchPath[len++] = '*';
searchPath[len++] = '.';
searchPath[len++] = '*';
searchPath[len] = '\0';
hx::EnterGCFreeZone();
#if defined(HX_WINRT) && !defined(_XBOX_ONE)
handle = FindFirstFileExA(searchPath.c_str(), FindExInfoStandard, &d, FindExSearchNameMatch, NULL, 0);
#else
handle = FindFirstFileW(searchPath,&d);
#endif
if( handle == INVALID_HANDLE_VALUE )
{
hx::ExitGCFreeZone();
return null();
}
while( true )
{
// skip magic dirs
if( d.cFileName[0] != '.' || (d.cFileName[1] != 0 && (d.cFileName[1] != '.' || d.cFileName[2] != 0)) )
{
hx::ExitGCFreeZone();
result->push(String::create(d.cFileName));
hx::EnterGCFreeZone();
}
if( !FindNextFileW(handle,&d) )
break;
}
FindClose(handle);
#elif !defined(EPPC) && !defined(KORE_CONSOLE)
const char *name = p.utf8_str();
hx::EnterGCFreeZone();
DIR *d = opendir(name);
if( d == NULL )
{
hx::ExitGCFreeZone();
hx::Throw(HX_CSTRING("Invalid directory"));
}
while( true )
{
struct dirent *e = readdir(d);
if( e == NULL )
break;
// skip magic dirs
if( e->d_name[0] == '.' && (e->d_name[1] == 0 || (e->d_name[1] == '.' && e->d_name[2] == 0)) )
continue;
hx::ExitGCFreeZone();
result->push( String::create(e->d_name) );
hx::EnterGCFreeZone();
}
closedir(d);
#endif
hx::ExitGCFreeZone();
return result;
}
/**
file_full_path : string -> string
<doc>Return an absolute path from a relative one. The file or directory must exists</doc>
**/
String _hx_std_file_full_path( String path )
{
#if defined(HX_WINRT)
return path;
#elif defined(NEKO_WINDOWS)
wchar_t buf[MAX_PATH+1];
hx::strbuf wbuf;
if( GetFullPathNameW(path.wchar_str(&wbuf),MAX_PATH+1,buf,NULL) == 0 )
return null();
return String::create(buf);
#elif defined(EPPC) || defined(KORE_CONSOLE)
return path;
#else
char buf[PATH_MAX];
hx::strbuf ubuf;
if( realpath(path.utf8_str(&ubuf),buf) == NULL )
return null();
return String::create(buf);
#endif
}
/**
sys_exe_path : void -> string
<doc>Return the path of the executable</doc>
**/
String _hx_std_sys_exe_path()
{
#if defined(HX_WINRT) && defined(__cplusplus_winrt)
Windows::ApplicationModel::Package^ package = Windows::ApplicationModel::Package::Current;
Windows::Storage::StorageFolder^ installedLocation = package->InstalledLocation;
return(String::create(installedLocation->Path->Data()));
#elif defined(NEKO_WINDOWS)
wchar_t path[MAX_PATH];
if( GetModuleFileNameW(NULL,path,MAX_PATH) == 0 )
return null();
return String::create(path);
#elif defined(NEKO_MAC) && !defined(IPHONE) && !defined(APPLETV) && !defined(HX_APPLEWATCH)
char path[PATH_MAX+1];
uint32_t path_len = PATH_MAX;
if( _NSGetExecutablePath(path, &path_len) )
return null();
return String::create(path);
#elif defined(EPPC) || defined(KORE_CONSOLE)
return HX_CSTRING("");
#else
{
char path[PATH_MAX];
int length = readlink("/proc/self/exe", path, sizeof(path));
if( length < 0 )
{
const char *p = getenv("_");
if (p)
return String::create(p);
return null();
}
path[length] = '\0';
return String::create(path);
}
#endif
}
#if !defined(IPHONE) && !defined(APPLETV) && !defined(HX_APPLEWATCH)
#ifdef NEKO_MAC
#include <crt_externs.h>
# define environ (*_NSGetEnviron())
#endif
#endif
#ifndef NEKO_WINDOWS
extern char **environ;
#endif
/**
sys_env : void -> #list
<doc>Return all the (key,value) pairs in the environment as a chained list</doc>
**/
Array<String> _hx_std_sys_env()
{
Array<String> result = Array_obj<String>::__new();
#if !defined(HX_WINRT) && !defined(KORE_CONSOLE)
char **e = environ;
while( *e )
{
char *x = strchr(*e,'=');
if( x == NULL )
{
e++;
continue;
}
result->push(String::create(*e,(int)(x-*e)));
result->push(String::create(x+1));
e++;
}
#endif
return result;
}
#ifdef HX_ANDROID
#define tcsetattr(fd,opt,s) ioctl(fd,opt,s)
#define tcgetattr(fd,s) ioctl(fd,TCGETS,s)
static __inline__ void inline_cfmakeraw(struct termios *s)
{
s->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
s->c_oflag &= ~OPOST;
s->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
s->c_cflag &= ~(CSIZE|PARENB);
s->c_cflag |= CS8;
}
#define cfmakeraw inline_cfmakeraw
#endif
/**
sys_getch : bool -> int
<doc>Read a character from stdin with or without echo</doc>
**/
int _hx_std_sys_getch( bool b )
{
#if defined(HX_WINRT) || defined(EMSCRIPTEN) || defined(EPPC) || defined(KORE_CONSOLE) || defined(__FreeBSD__)
return 0;
#elif defined(NEKO_WINDOWS)
hx::EnterGCFreeZone();
int result = b?getche():getch();
hx::ExitGCFreeZone();
return result;
#else
// took some time to figure out how to do that
// without relying on ncurses, which clear the
// terminal on initscr()
int c;
struct termios term, old;
hx::EnterGCFreeZone();
tcgetattr(fileno(stdin), &old);
term = old;
cfmakeraw(&term);
tcsetattr(fileno(stdin), 0, &term);
c = getchar();
tcsetattr(fileno(stdin), 0, &old);
if( b ) fputc(c,stdout);
hx::ExitGCFreeZone();
return c;
# endif
}
/**
sys_get_pid : void -> int
<doc>Returns the current process identifier</doc>
**/
int _hx_std_sys_get_pid()
{
# ifdef NEKO_WINDOWS
return (int)(GetCurrentProcessId());
#elif defined(EPPC) || defined(KORE_CONSOLE)
return (1);
# else
return (getpid());
# endif
}

View File

@ -0,0 +1,42 @@
<xml>
<pragma once="true" />
<set name="ZLIB_DIR" value="${HXCPP}/project/thirdparty/zlib-1.2.13"/>
<files id="hxcpp_zlib" dir="${this_dir}" >
<depend files="hxcpp-depends"/>
<depend name="${this_dir}/Build.xml" dateOnly="true" />
<cache value="true" asLibrary="1" />
<compilerflag value="-I${ZLIB_DIR}"/>
<compilerflag value="-DSTDC" unless="windows" />
<compilerflag value="-DHAVE_UNISTD_H" unless="windows" />
<file name="ZLib.cpp"/>
<!-- HXCPP_LINK_NO_ZLIB may be set too late, so use filterout as well. -->
<section unless="HXCPP_LINK_NO_ZLIB" >
<file name="${ZLIB_DIR}/adler32.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/compress.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/crc32.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/deflate.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/gzclose.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/gzlib.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/gzread.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/gzwrite.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/infback.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/inffast.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/inflate.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/inftrees.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/trees.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/uncompr.c" filterout="HXCPP_LINK_NO_ZLIB" />
<file name="${ZLIB_DIR}/zutil.c" filterout="HXCPP_LINK_NO_ZLIB" />
</section>
</files>
<target id="haxe">
<files id="hxcpp_zlib" />
</target>
</xml>

View File

@ -0,0 +1,284 @@
#include <hxcpp.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
/**
<doc>
<h1>ZLib</h1>
<p>
Give access to the popular ZLib compression library, used in several file
formats such as ZIP and PNG.
</p>
</doc>
**/
namespace {
struct ZipResult
{
inline ZipResult(bool inOk, bool inDone, int inRead, int inWrite)
: ok(inOk)
, done(inDone)
, read(inRead)
, write(inWrite) { }
bool ok;
bool done;
int read;
int write;
};
struct ZStream : public hx::Object
{
HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdZLib };
z_stream *z;
bool isInflate;
int flush;
void create(bool inIsInflate, int inParam)
{
isInflate = inIsInflate;
flush = Z_NO_FLUSH;
z = (z_stream*)malloc(sizeof(z_stream));
memset(z,0,sizeof(z_stream));
int err = 0;
if (!isInflate)
{
if( (err = deflateInit(z,inParam)) != Z_OK )
{
free(z);
z = 0;
onError(err);
}
}
else
{
if( (err = inflateInit2(z,inParam)) != Z_OK )
{
free(z);
z = 0;
onError(err);
}
}
_hx_set_finalizer(this, finalize);
}
static void finalize(Dynamic obj)
{
((ZStream *)(obj.mPtr))->destroy();
}
void destroy()
{
if (z)
{
if (isInflate)
inflateEnd(z);
else
deflateEnd(z);
free(z);
z = 0;
}
}
ZipResult deflate(Array<unsigned char> src, int srcpos, Array<unsigned char> dest, int dstpos)
{
if( srcpos < 0 || dstpos < 0 )
return ZipResult( 0,0,0,0 );
int slen = src->length - srcpos;
int dlen = dest->length - dstpos;
if( slen < 0 || dlen < 0 )
return ZipResult( 0,0,0,0 );
z->next_in = (Bytef*)(&src[srcpos]);
z->next_out = (Bytef*)(&dest[dstpos]);
z->avail_in = slen;
z->avail_out = dlen;
int err = 0;
if( (err = ::deflate(z,flush)) < 0 )
onError(err);
z->next_in = 0;
z->next_out = 0;
return ZipResult( true, err == Z_STREAM_END, (int)(slen - z->avail_in), (int)(dlen - z->avail_out) );
}
ZipResult inflate(Array<unsigned char> src, int srcpos, Array<unsigned char> dest, int dstpos)
{
int slen = src->length;
int dlen = dest->length;
if( srcpos < 0 || dstpos < 0 )
return ZipResult( 0,0,0,0 );
slen -= srcpos;
dlen -= dstpos;
if( slen < 0 || dlen < 0 )
return ZipResult( 0,0,0,0 );
z->next_in = (Bytef*)&src[srcpos];
z->next_out = (Bytef*)&dest[dstpos];
z->avail_in = slen;
z->avail_out = dlen;
int err = 0;
if( (err = ::inflate(z,flush)) < 0 )
onError(err);
z->next_in = 0;
z->next_out = 0;
return ZipResult( true, err == Z_STREAM_END, (int)(slen - z->avail_in), (int)(dlen - z->avail_out) );
}
int getDeflateBound(int inLength)
{
return deflateBound(z,inLength);
}
void setFlushMode(String inMode)
{
if( inMode == HX_CSTRING("NO") )
flush = Z_NO_FLUSH;
else if( inMode==HX_CSTRING("SYNC"))
flush = Z_SYNC_FLUSH;
else if( inMode==HX_CSTRING("FULL"))
flush = Z_FULL_FLUSH;
else if( inMode==HX_CSTRING("FINISH"))
flush = Z_FINISH;
else if( inMode==HX_CSTRING("BLOCK"))
flush = Z_BLOCK;
}
void onError(int inCode)
{
String message = HX_CSTRING("ZLib Error : ");
if( z && z->msg )
message += String(z->msg) + HX_CSTRING("(") + String(inCode) + HX_CSTRING(")");
else
message += String(inCode);
hx::Throw(message);
}
};
ZStream *GetDeflateStream(Dynamic inHandle)
{
ZStream *z = dynamic_cast<ZStream *>(inHandle.mPtr);
if (!z || !z->z || z->isInflate)
hx::Throw( HX_CSTRING("ZLib: Not a valid deflate stream"));
return z;
}
ZStream *GetInflateStream(Dynamic inHandle)
{
ZStream *z = dynamic_cast<ZStream *>(inHandle.mPtr);
if (!z || !z->z || !z->isInflate)
hx::Throw( HX_CSTRING("ZLib: Not a valid inflate stream"));
return z;
}
} // end namespace
/**
deflate_init : level:int -> 'dstream
<doc>Open a compression stream with the given level of compression</doc>
**/
Dynamic _hx_deflate_init(int level)
{
ZStream *zStream = new ZStream;
zStream->create(false, level);
return zStream;
}
/**
deflate_buffer : 'dstream -> src:string -> srcpos:int -> dst:string -> dstpos:int -> { done => bool, read => int, write => int }
**/
Dynamic _hx_deflate_buffer(Dynamic handle, Array<unsigned char> src, int srcPos, Array<unsigned char> dest, int destPos)
{
ZipResult result = GetDeflateStream(handle)->deflate(src,srcPos,dest,destPos);
if (!result.ok)
return null();
return hx::Anon_obj::Create(3)
->setFixed(0,HX_("write",df,6c,59,d0),result.write)
->setFixed(1,HX_("done",82,f0,6d,42),result.done)
->setFixed(2,HX_("read",56,4b,a7,4b),result.read);
}
/**
deflate_bound : 'dstream -> n:int -> int
<doc>Return the maximum buffer size needed to write [n] bytes</doc>
**/
int _hx_deflate_bound(Dynamic handle,int length)
{
return GetDeflateStream(handle)->getDeflateBound(length);
}
void _hx_deflate_end(Dynamic handle)
{
GetDeflateStream(handle)->destroy();
}
/**
set_flush_mode : 'stream -> string -> void
<doc>Change the flush mode ("NO","SYNC","FULL","FINISH","BLOCK")</doc>
**/
void _hx_zip_set_flush_mode(Dynamic handle, String flushMode)
{
ZStream *zstream = dynamic_cast<ZStream *>(handle.mPtr);
if (!zstream || !zstream->z)
hx::Throw( HX_CSTRING("ZLib flush: not a valid stream") );
zstream->setFlushMode(flushMode);
}
/**
inflate_init : window_size:int? -> 'istream
<doc>Open a decompression stream</doc>
**/
Dynamic _hx_inflate_init(Dynamic windowBits)
{
int bits = windowBits==null() ? MAX_WBITS : (int)windowBits;
ZStream *zStream = new ZStream();
zStream->create(true, bits);
return zStream;
}
/**
inflate_buffer : 'istream -> src:string -> srcpos:int -> dst:string -> dstpos:int -> { done => bool, read => int, write => int }
**/
Dynamic _hx_inflate_buffer(Dynamic handle, Array<unsigned char> src, int srcPos, Array<unsigned char> dest, int destPos)
{
ZipResult result = GetInflateStream(handle)->inflate(src,srcPos,dest,destPos);
if (!result.ok)
return null();
return hx::Anon_obj::Create(3)
->setFixed(0,HX_("write",df,6c,59,d0),result.write)
->setFixed(1,HX_("done",82,f0,6d,42),result.done)
->setFixed(2,HX_("read",56,4b,a7,4b),result.read);
}
/**
inflate_end : 'istream -> void
<doc>Close a decompression stream</doc>
**/
void _hx_inflate_end(Dynamic handle)
{
GetInflateStream(handle)->destroy();
}