mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* ext/win32ole/win32ole.c : check String encoding when
converting String to VT_BSTR in OLE. * test/win32ole/test_win32ole.rb : ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@17620 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
d080d174f5
commit
dd02419760
3 changed files with 79 additions and 21 deletions
|
@ -1,3 +1,10 @@
|
||||||
|
Sat Jun 28 08:40:18 2008 Masaki Suketa <masaki.suketa@nifty.ne.jp>
|
||||||
|
|
||||||
|
* ext/win32ole/win32ole.c : check String encoding when
|
||||||
|
converting String to VT_BSTR in OLE.
|
||||||
|
|
||||||
|
* test/win32ole/test_win32ole.rb : ditto.
|
||||||
|
|
||||||
Sat Jun 28 01:08:42 2008 Tanaka Akira <akr@fsij.org>
|
Sat Jun 28 01:08:42 2008 Tanaka Akira <akr@fsij.org>
|
||||||
|
|
||||||
* time.c (time_timespec): fix rounding negative float.
|
* time.c (time_timespec): fix rounding negative float.
|
||||||
|
|
|
@ -116,7 +116,7 @@
|
||||||
|
|
||||||
#define WC2VSTR(x) ole_wc2vstr((x), TRUE)
|
#define WC2VSTR(x) ole_wc2vstr((x), TRUE)
|
||||||
|
|
||||||
#define WIN32OLE_VERSION "1.1.5"
|
#define WIN32OLE_VERSION "1.1.6"
|
||||||
|
|
||||||
typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX)
|
typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX)
|
||||||
(REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*);
|
(REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*);
|
||||||
|
@ -201,6 +201,7 @@ static rb_encoding *cWIN32OLE_enc;
|
||||||
static UINT g_cp_to_check = CP_ACP;
|
static UINT g_cp_to_check = CP_ACP;
|
||||||
static char g_lcid_to_check[8 + 1];
|
static char g_lcid_to_check[8 + 1];
|
||||||
static VARTYPE g_nil_to = VT_ERROR;
|
static VARTYPE g_nil_to = VT_ERROR;
|
||||||
|
static st_table *enc2cp_table;
|
||||||
|
|
||||||
struct oledata {
|
struct oledata {
|
||||||
IDispatch *pDispatch;
|
IDispatch *pDispatch;
|
||||||
|
@ -274,6 +275,7 @@ static void oletype_free(struct oletypedata *poletype);
|
||||||
static void olemethod_free(struct olemethoddata *polemethod);
|
static void olemethod_free(struct olemethoddata *polemethod);
|
||||||
static void olevariable_free(struct olevariabledata *polevar);
|
static void olevariable_free(struct olevariabledata *polevar);
|
||||||
static void oleparam_free(struct oleparamdata *pole);
|
static void oleparam_free(struct oleparamdata *pole);
|
||||||
|
static LPWSTR ole_vstr2wc(VALUE vstr);
|
||||||
static LPWSTR ole_mb2wc(char *pm, int len);
|
static LPWSTR ole_mb2wc(char *pm, int len);
|
||||||
static VALUE ole_wc2vstr(LPWSTR pw, BOOL isfree);
|
static VALUE ole_wc2vstr(LPWSTR pw, BOOL isfree);
|
||||||
static VALUE ole_ary_m_entry(VALUE val, long *pid);
|
static VALUE ole_ary_m_entry(VALUE val, long *pid);
|
||||||
|
@ -518,6 +520,8 @@ static VALUE folevariant_ary_aset(int argc, VALUE *argv, VALUE self);
|
||||||
static VALUE folevariant_value(VALUE self);
|
static VALUE folevariant_value(VALUE self);
|
||||||
static VALUE folevariant_vartype(VALUE self);
|
static VALUE folevariant_vartype(VALUE self);
|
||||||
static VALUE folevariant_set_value(VALUE self, VALUE val);
|
static VALUE folevariant_set_value(VALUE self, VALUE val);
|
||||||
|
static void init_enc2cp();
|
||||||
|
static void free_enc2cp();
|
||||||
|
|
||||||
typedef struct _Win32OLEIDispatch
|
typedef struct _Win32OLEIDispatch
|
||||||
{
|
{
|
||||||
|
@ -1105,6 +1109,38 @@ oleparam_free(struct oleparamdata *pole)
|
||||||
free(pole);
|
free(pole);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LPWSTR
|
||||||
|
ole_vstr2wc(VALUE vstr)
|
||||||
|
{
|
||||||
|
rb_encoding *enc;
|
||||||
|
int cp;
|
||||||
|
int size;
|
||||||
|
LPWSTR pw;
|
||||||
|
st_data_t data;
|
||||||
|
enc = rb_enc_get(vstr);
|
||||||
|
if (st_lookup(enc2cp_table, (st_data_t)enc, &data)) {
|
||||||
|
cp = data;
|
||||||
|
} else {
|
||||||
|
cp = ole_encoding2cp(enc);
|
||||||
|
if (code_page_installed(cp) ||
|
||||||
|
cp == CP_ACP ||
|
||||||
|
cp == CP_OEMCP ||
|
||||||
|
cp == CP_MACCP ||
|
||||||
|
cp == CP_THREAD_ACP ||
|
||||||
|
cp == CP_SYMBOL ||
|
||||||
|
cp == CP_UTF7 ||
|
||||||
|
cp == CP_UTF8 ) {
|
||||||
|
st_insert(enc2cp_table, (st_data_t)enc, (st_data_t)cp);
|
||||||
|
} else {
|
||||||
|
rb_raise(eWIN32OLERuntimeError, "not installed Windows codepage(%d) according to `%s'", cp, rb_enc_name(enc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size = MultiByteToWideChar(cp, 0, StringValuePtr(vstr), -1, NULL, 0);
|
||||||
|
pw = SysAllocStringLen(NULL, size - 1);
|
||||||
|
MultiByteToWideChar(cp, 0, StringValuePtr(vstr), -1, pw, size);
|
||||||
|
return pw;
|
||||||
|
}
|
||||||
|
|
||||||
static LPWSTR
|
static LPWSTR
|
||||||
ole_mb2wc(char *pm, int len)
|
ole_mb2wc(char *pm, int len)
|
||||||
{
|
{
|
||||||
|
@ -1387,7 +1423,7 @@ ole_val2variant(VALUE val, VARIANT *var)
|
||||||
break;
|
break;
|
||||||
case T_STRING:
|
case T_STRING:
|
||||||
V_VT(var) = VT_BSTR;
|
V_VT(var) = VT_BSTR;
|
||||||
V_BSTR(var) = ole_mb2wc(StringValuePtr(val), -1);
|
V_BSTR(var) = ole_vstr2wc(val);
|
||||||
break;
|
break;
|
||||||
case T_FIXNUM:
|
case T_FIXNUM:
|
||||||
V_VT(var) = VT_I4;
|
V_VT(var) = VT_I4;
|
||||||
|
@ -1465,7 +1501,7 @@ ole_val2ptr_variant(VALUE val, VARIANT *var)
|
||||||
switch (TYPE(val)) {
|
switch (TYPE(val)) {
|
||||||
case T_STRING:
|
case T_STRING:
|
||||||
if (V_VT(var) == (VT_BSTR | VT_BYREF)) {
|
if (V_VT(var) == (VT_BSTR | VT_BYREF)) {
|
||||||
*V_BSTRREF(var) = ole_mb2wc(StringValuePtr(val), -1);
|
*V_BSTRREF(var) = ole_vstr2wc(val);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_FIXNUM:
|
case T_FIXNUM:
|
||||||
|
@ -2347,7 +2383,7 @@ ole_create_dcom(int argc, VALUE *argv, VALUE self)
|
||||||
rb_raise(rb_eRuntimeError, "CoCreateInstanceEx is not supported in this environment");
|
rb_raise(rb_eRuntimeError, "CoCreateInstanceEx is not supported in this environment");
|
||||||
rb_scan_args(argc, argv, "2*", &ole, &host, &others);
|
rb_scan_args(argc, argv, "2*", &ole, &host, &others);
|
||||||
|
|
||||||
pbuf = ole_mb2wc(StringValuePtr(ole), -1);
|
pbuf = ole_vstr2wc(ole);
|
||||||
hr = CLSIDFromProgID(pbuf, &clsid);
|
hr = CLSIDFromProgID(pbuf, &clsid);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
hr = clsid_from_remote(host, ole, &clsid);
|
hr = clsid_from_remote(host, ole, &clsid);
|
||||||
|
@ -2359,7 +2395,7 @@ ole_create_dcom(int argc, VALUE *argv, VALUE self)
|
||||||
"unknown OLE server: `%s'",
|
"unknown OLE server: `%s'",
|
||||||
StringValuePtr(ole));
|
StringValuePtr(ole));
|
||||||
memset(&serverinfo, 0, sizeof(COSERVERINFO));
|
memset(&serverinfo, 0, sizeof(COSERVERINFO));
|
||||||
serverinfo.pwszName = ole_mb2wc(StringValuePtr(host), -1);
|
serverinfo.pwszName = ole_vstr2wc(host);
|
||||||
memset(&multi_qi, 0, sizeof(MULTI_QI));
|
memset(&multi_qi, 0, sizeof(MULTI_QI));
|
||||||
multi_qi.pIID = &IID_IDispatch;
|
multi_qi.pIID = &IID_IDispatch;
|
||||||
hr = gCoCreateInstanceEx(&clsid, NULL, clsctx, &serverinfo, 1, &multi_qi);
|
hr = gCoCreateInstanceEx(&clsid, NULL, clsctx, &serverinfo, 1, &multi_qi);
|
||||||
|
@ -2392,7 +2428,7 @@ ole_bind_obj(VALUE moniker, int argc, VALUE *argv, VALUE self)
|
||||||
"failed to create bind context");
|
"failed to create bind context");
|
||||||
}
|
}
|
||||||
|
|
||||||
pbuf = ole_mb2wc(StringValuePtr(moniker), -1);
|
pbuf = ole_vstr2wc(moniker);
|
||||||
hr = MkParseDisplayName(pBindCtx, pbuf, &eaten, &pMoniker);
|
hr = MkParseDisplayName(pBindCtx, pbuf, &eaten, &pMoniker);
|
||||||
SysFreeString(pbuf);
|
SysFreeString(pbuf);
|
||||||
if(FAILED(hr)) {
|
if(FAILED(hr)) {
|
||||||
|
@ -2446,7 +2482,7 @@ fole_s_connect(int argc, VALUE *argv, VALUE self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get CLSID from OLE server name */
|
/* get CLSID from OLE server name */
|
||||||
pBuf = ole_mb2wc(StringValuePtr(svr_name), -1);
|
pBuf = ole_vstr2wc(svr_name);
|
||||||
hr = CLSIDFromProgID(pBuf, &clsid);
|
hr = CLSIDFromProgID(pBuf, &clsid);
|
||||||
if(FAILED(hr)) {
|
if(FAILED(hr)) {
|
||||||
hr = CLSIDFromString(pBuf, &clsid);
|
hr = CLSIDFromString(pBuf, &clsid);
|
||||||
|
@ -2551,7 +2587,7 @@ fole_s_const_load(int argc, VALUE *argv, VALUE self)
|
||||||
if (file == Qnil) {
|
if (file == Qnil) {
|
||||||
file = ole;
|
file = ole;
|
||||||
}
|
}
|
||||||
pBuf = ole_mb2wc(StringValuePtr(file), -1);
|
pBuf = ole_vstr2wc(file);
|
||||||
hr = LoadTypeLibEx(pBuf, REGKIND_NONE, &pTypeLib);
|
hr = LoadTypeLibEx(pBuf, REGKIND_NONE, &pTypeLib);
|
||||||
SysFreeString(pBuf);
|
SysFreeString(pBuf);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
|
@ -2954,7 +2990,7 @@ fole_initialize(int argc, VALUE *argv, VALUE self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get CLSID from OLE server name */
|
/* get CLSID from OLE server name */
|
||||||
pBuf = ole_mb2wc(StringValuePtr(svr_name), -1);
|
pBuf = ole_vstr2wc(svr_name);
|
||||||
hr = CLSIDFromProgID(pBuf, &clsid);
|
hr = CLSIDFromProgID(pBuf, &clsid);
|
||||||
if(FAILED(hr)) {
|
if(FAILED(hr)) {
|
||||||
hr = CLSIDFromString(pBuf, &clsid);
|
hr = CLSIDFromString(pBuf, &clsid);
|
||||||
|
@ -3007,7 +3043,7 @@ hash2named_arg(VALUE pair, struct oleparam* pOp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pNamedArgs[0] is <method name>, so "index + 1" */
|
/* pNamedArgs[0] is <method name>, so "index + 1" */
|
||||||
pOp->pNamedArgs[index + 1] = ole_mb2wc(StringValuePtr(key), -1);
|
pOp->pNamedArgs[index + 1] = ole_vstr2wc(key);
|
||||||
|
|
||||||
value = rb_ary_entry(pair, 1);
|
value = rb_ary_entry(pair, 1);
|
||||||
VariantInit(&(pOp->dp.rgvarg[index]));
|
VariantInit(&(pOp->dp.rgvarg[index]));
|
||||||
|
@ -3075,7 +3111,7 @@ ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket)
|
||||||
argc += 1;
|
argc += 1;
|
||||||
rb_funcall(paramS, rb_intern("unshift"), 1, cmd);
|
rb_funcall(paramS, rb_intern("unshift"), 1, cmd);
|
||||||
} else {
|
} else {
|
||||||
wcmdname = ole_mb2wc(StringValuePtr(cmd), -1);
|
wcmdname = ole_vstr2wc(cmd);
|
||||||
hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL,
|
hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL,
|
||||||
&wcmdname, 1, lcid, &DispID);
|
&wcmdname, 1, lcid, &DispID);
|
||||||
SysFreeString(wcmdname);
|
SysFreeString(wcmdname);
|
||||||
|
@ -3103,7 +3139,7 @@ ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket)
|
||||||
rb_block_call(param, rb_intern("each"), 0, 0, hash2named_arg, (VALUE)&op);
|
rb_block_call(param, rb_intern("each"), 0, 0, hash2named_arg, (VALUE)&op);
|
||||||
|
|
||||||
pDispID = ALLOCA_N(DISPID, cNamedArgs + 1);
|
pDispID = ALLOCA_N(DISPID, cNamedArgs + 1);
|
||||||
op.pNamedArgs[0] = ole_mb2wc(StringValuePtr(cmd), -1);
|
op.pNamedArgs[0] = ole_vstr2wc(cmd);
|
||||||
hr = pole->pDispatch->lpVtbl->GetIDsOfNames(pole->pDispatch,
|
hr = pole->pDispatch->lpVtbl->GetIDsOfNames(pole->pDispatch,
|
||||||
&IID_NULL,
|
&IID_NULL,
|
||||||
op.pNamedArgs,
|
op.pNamedArgs,
|
||||||
|
@ -3614,7 +3650,7 @@ ole_propertyput(VALUE self, VALUE property, VALUE value)
|
||||||
OLEData_Get_Struct(self, pole);
|
OLEData_Get_Struct(self, pole);
|
||||||
|
|
||||||
/* get ID from property name */
|
/* get ID from property name */
|
||||||
pBuf[0] = ole_mb2wc(StringValuePtr(property), -1);
|
pBuf[0] = ole_vstr2wc(property);
|
||||||
hr = pole->pDispatch->lpVtbl->GetIDsOfNames(pole->pDispatch, &IID_NULL,
|
hr = pole->pDispatch->lpVtbl->GetIDsOfNames(pole->pDispatch, &IID_NULL,
|
||||||
pBuf, 1, lcid, &dispID);
|
pBuf, 1, lcid, &dispID);
|
||||||
SysFreeString(pBuf[0]);
|
SysFreeString(pBuf[0]);
|
||||||
|
@ -4210,7 +4246,7 @@ fole_query_interface(VALUE self, VALUE str_iid)
|
||||||
struct oledata *pole;
|
struct oledata *pole;
|
||||||
IDispatch *pDispatch;
|
IDispatch *pDispatch;
|
||||||
|
|
||||||
pBuf = ole_mb2wc(StringValuePtr(str_iid), -1);
|
pBuf = ole_vstr2wc(str_iid);
|
||||||
hr = CLSIDFromString(pBuf, &iid);
|
hr = CLSIDFromString(pBuf, &iid);
|
||||||
SysFreeString(pBuf);
|
SysFreeString(pBuf);
|
||||||
if(FAILED(hr)) {
|
if(FAILED(hr)) {
|
||||||
|
@ -4868,7 +4904,7 @@ foletypelib_initialize(VALUE self, VALUE args)
|
||||||
found = oletypelib_search_registry2(self, args);
|
found = oletypelib_search_registry2(self, args);
|
||||||
}
|
}
|
||||||
if (found == Qfalse) {
|
if (found == Qfalse) {
|
||||||
pbuf = ole_mb2wc(StringValuePtr(typelib), -1);
|
pbuf = ole_vstr2wc(typelib);
|
||||||
hr = LoadTypeLibEx(pbuf, REGKIND_NONE, &pTypeLib);
|
hr = LoadTypeLibEx(pbuf, REGKIND_NONE, &pTypeLib);
|
||||||
SysFreeString(pbuf);
|
SysFreeString(pbuf);
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
|
@ -5029,7 +5065,7 @@ oletypelib2itypelib(VALUE self, ITypeLib **ppTypeLib)
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
path = rb_funcall(self, rb_intern("path"), 0);
|
path = rb_funcall(self, rb_intern("path"), 0);
|
||||||
if (path != Qnil) {
|
if (path != Qnil) {
|
||||||
pbuf = ole_mb2wc(StringValuePtr(path), -1);
|
pbuf = ole_vstr2wc(path);
|
||||||
hr = LoadTypeLibEx(pbuf, REGKIND_NONE, ppTypeLib);
|
hr = LoadTypeLibEx(pbuf, REGKIND_NONE, ppTypeLib);
|
||||||
SysFreeString(pbuf);
|
SysFreeString(pbuf);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
|
@ -5166,7 +5202,7 @@ foletype_initialize(VALUE self, VALUE typelib, VALUE oleclass)
|
||||||
if (file == Qnil) {
|
if (file == Qnil) {
|
||||||
file = typelib;
|
file = typelib;
|
||||||
}
|
}
|
||||||
pbuf = ole_mb2wc(StringValuePtr(file), -1);
|
pbuf = ole_vstr2wc(file);
|
||||||
hr = LoadTypeLibEx(pbuf, REGKIND_NONE, &pTypeLib);
|
hr = LoadTypeLibEx(pbuf, REGKIND_NONE, &pTypeLib);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
ole_raise(hr, eWIN32OLERuntimeError, "failed to LoadTypeLibEx");
|
ole_raise(hr, eWIN32OLERuntimeError, "failed to LoadTypeLibEx");
|
||||||
|
@ -8108,6 +8144,18 @@ folevariant_set_value(VALUE self, VALUE val)
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_enc2cp()
|
||||||
|
{
|
||||||
|
enc2cp_table = st_init_numtable();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_enc2cp()
|
||||||
|
{
|
||||||
|
st_free_table(enc2cp_table);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Init_win32ole()
|
Init_win32ole()
|
||||||
{
|
{
|
||||||
|
@ -8327,6 +8375,8 @@ Init_win32ole()
|
||||||
|
|
||||||
eWIN32OLERuntimeError = rb_define_class("WIN32OLERuntimeError", rb_eRuntimeError);
|
eWIN32OLERuntimeError = rb_define_class("WIN32OLERuntimeError", rb_eRuntimeError);
|
||||||
|
|
||||||
|
init_enc2cp();
|
||||||
|
atexit((void (*)(void))free_enc2cp);
|
||||||
ole_init_cp();
|
ole_init_cp();
|
||||||
cWIN32OLE_enc = ole_cp2encoding(cWIN32OLE_cp);
|
cWIN32OLE_enc = ole_cp2encoding(cWIN32OLE_cp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,8 +282,8 @@ if defined?(WIN32OLE)
|
||||||
fso = WIN32OLE.new("Scripting.FileSystemObject")
|
fso = WIN32OLE.new("Scripting.FileSystemObject")
|
||||||
fname = fso.getTempName
|
fname = fso.getTempName
|
||||||
begin
|
begin
|
||||||
|
obj = WIN32OLE_VARIANT.new([0x3042].pack("U*").force_encoding("UTF-8"))
|
||||||
WIN32OLE.codepage = WIN32OLE::CP_UTF8
|
WIN32OLE.codepage = WIN32OLE::CP_UTF8
|
||||||
obj = WIN32OLE_VARIANT.new([0x3042].pack("U*"))
|
|
||||||
assert_equal("\xE3\x81\x82".force_encoding("CP65001"), obj.value)
|
assert_equal("\xE3\x81\x82".force_encoding("CP65001"), obj.value)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
@ -302,9 +302,9 @@ if defined?(WIN32OLE)
|
||||||
assert_equal("\xA4\xA2".force_encoding("CP20932"), obj.value)
|
assert_equal("\xA4\xA2".force_encoding("CP20932"), obj.value)
|
||||||
end
|
end
|
||||||
|
|
||||||
WIN32OLE.codepage = WIN32OLE::CP_UTF8
|
WIN32OLE.codepage = cp
|
||||||
file = fso.opentextfile(fname, 2, true)
|
file = fso.opentextfile(fname, 2, true)
|
||||||
file.write [0x3042].pack("U*")
|
file.write [0x3042].pack("U*").force_encoding("UTF-8")
|
||||||
file.close
|
file.close
|
||||||
str = ""
|
str = ""
|
||||||
open(fname, "r:ascii-8bit") {|ifs|
|
open(fname, "r:ascii-8bit") {|ifs|
|
||||||
|
@ -318,8 +318,9 @@ if defined?(WIN32OLE)
|
||||||
rescue WIN32OLERuntimeError
|
rescue WIN32OLERuntimeError
|
||||||
end
|
end
|
||||||
if (WIN32OLE.codepage == 20932)
|
if (WIN32OLE.codepage == 20932)
|
||||||
|
WIN32OLE.codepage = cp
|
||||||
file = fso.opentextfile(fname, 2, true)
|
file = fso.opentextfile(fname, 2, true)
|
||||||
file.write [164, 162].pack("c*")
|
file.write [164, 162].pack("c*").force_encoding("EUC-JP")
|
||||||
file.close
|
file.close
|
||||||
open(fname, "r:ascii-8bit") {|ifs|
|
open(fname, "r:ascii-8bit") {|ifs|
|
||||||
str = ifs.read
|
str = ifs.read
|
||||||
|
|
Loading…
Reference in a new issue