win32-xpath 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES +11 -8
- data/MANIFEST +9 -9
- data/README +65 -54
- data/Rakefile +60 -60
- data/bench/bench_win32_xpath.rb +79 -79
- data/ext/extconf.rb +13 -4
- data/ext/win32/xpath.c +399 -369
- data/futzing/futz.rb +71 -71
- data/futzing/test.rb +90 -90
- data/test/test_win32_xpath.rb +206 -185
- data/win32-xpath.gemspec +25 -25
- metadata +3 -3
data/ext/extconf.rb
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
require 'mkmf'
|
2
|
-
|
3
|
-
have_library('
|
4
|
-
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
have_library('shlwapi')
|
4
|
+
have_library('advapi32')
|
5
|
+
|
6
|
+
# Windows 8 or later
|
7
|
+
if have_library('pathcch')
|
8
|
+
if have_header('pathcch.h')
|
9
|
+
have_func('PathCchAppendEx', 'pathcch.h')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
create_makefile('win32/xpath', 'win32')
|
data/ext/win32/xpath.c
CHANGED
@@ -1,370 +1,400 @@
|
|
1
|
-
#include <ruby.h>
|
2
|
-
#include <ruby/encoding.h>
|
3
|
-
#include <windows.h>
|
4
|
-
#include <shlwapi.h>
|
5
|
-
#include <sddl.h>
|
6
|
-
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <ruby/encoding.h>
|
3
|
+
#include <windows.h>
|
4
|
+
#include <shlwapi.h>
|
5
|
+
#include <sddl.h>
|
6
|
+
|
7
7
|
#ifdef __MINGW32__
|
8
|
-
#define swprintf _snwprintf
|
9
|
-
#endif
|
10
|
-
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
//
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
rb_raise_syserr("
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
}
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
}
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
if (
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
8
|
+
#define swprintf _snwprintf
|
9
|
+
#endif
|
10
|
+
|
11
|
+
#ifdef HAVE_PATHCCH_H
|
12
|
+
#include <pathcch.h>
|
13
|
+
#endif
|
14
|
+
|
15
|
+
#define MAX_WPATH MAX_PATH * sizeof(wchar_t)
|
16
|
+
|
17
|
+
// Equivalent to raise SystemCallError.new(string, errnum)
|
18
|
+
void rb_raise_syserr(const char* msg, DWORD errnum){
|
19
|
+
VALUE v_sys = rb_funcall(rb_eSystemCallError, rb_intern("new"), 2, rb_str_new2(msg), LONG2FIX(errnum));
|
20
|
+
rb_funcall(rb_mKernel, rb_intern("raise"), 1, v_sys);
|
21
|
+
}
|
22
|
+
|
23
|
+
// Helper function to find user's home directory
|
24
|
+
wchar_t* find_user(wchar_t* str){
|
25
|
+
SID* sid;
|
26
|
+
DWORD cbSid, cbDom, cbData, lpType;
|
27
|
+
SID_NAME_USE peUse;
|
28
|
+
LPWSTR str_sid;
|
29
|
+
LONG rv;
|
30
|
+
HKEY phkResult;
|
31
|
+
wchar_t subkey[MAX_WPATH];
|
32
|
+
wchar_t* lpData;
|
33
|
+
wchar_t* dom;
|
34
|
+
wchar_t* ptr;
|
35
|
+
const wchar_t* key_base = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\";
|
36
|
+
|
37
|
+
// Read up until first backslash, and preserve the rest for later
|
38
|
+
if (ptr = wcschr(str, L'\\')){
|
39
|
+
ptr++;
|
40
|
+
str[wcscspn(str, L"\\")] = 0;
|
41
|
+
}
|
42
|
+
|
43
|
+
sid = (SID*)ruby_xmalloc(MAX_WPATH);
|
44
|
+
dom = (wchar_t*)ruby_xmalloc(MAX_WPATH);
|
45
|
+
|
46
|
+
cbSid = MAX_PATH;
|
47
|
+
cbDom = MAX_PATH;
|
48
|
+
|
49
|
+
// Get the user's SID
|
50
|
+
if (!LookupAccountNameW(NULL, str, sid, &cbSid, dom, &cbDom, &peUse)){
|
51
|
+
ruby_xfree(sid);
|
52
|
+
ruby_xfree(dom);
|
53
|
+
rb_raise(rb_eArgError, "can't find user %ls", str);
|
54
|
+
}
|
55
|
+
|
56
|
+
ruby_xfree(dom); // Don't need this any more
|
57
|
+
|
58
|
+
// Get the stringy version of the SID
|
59
|
+
if (!ConvertSidToStringSidW(sid, &str_sid)){
|
60
|
+
ruby_xfree(sid);
|
61
|
+
rb_raise_syserr("ConvertSidToStringSid", GetLastError());
|
62
|
+
}
|
63
|
+
|
64
|
+
ruby_xfree(sid); // Don't need this any more
|
65
|
+
|
66
|
+
// Mash the stringified SID onto our base key
|
67
|
+
if(swprintf(subkey, MAX_WPATH, L"%s%s", key_base, str_sid) < 0)
|
68
|
+
rb_raise_syserr("swprintf", GetLastError());
|
69
|
+
|
70
|
+
// Get the key handle we need
|
71
|
+
rv = RegOpenKeyExW(HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &phkResult);
|
72
|
+
|
73
|
+
if (rv != ERROR_SUCCESS)
|
74
|
+
rb_raise_syserr("RegOpenKeyEx", GetLastError());
|
75
|
+
|
76
|
+
lpData = (wchar_t*)malloc(MAX_WPATH);
|
77
|
+
cbData = MAX_WPATH;
|
78
|
+
lpType = REG_EXPAND_SZ;
|
79
|
+
|
80
|
+
// Finally, get the user's home directory
|
81
|
+
rv = RegQueryValueExW(phkResult, L"ProfileImagePath", NULL, &lpType, (LPBYTE)lpData, &cbData);
|
82
|
+
|
83
|
+
if (rv != ERROR_SUCCESS){
|
84
|
+
ruby_xfree(lpData);
|
85
|
+
rb_raise(rb_eArgError, "can't find home directory for user %ls", str);
|
86
|
+
}
|
87
|
+
|
88
|
+
// Append any remaining path data that was originally present
|
89
|
+
if (ptr){
|
90
|
+
if (swprintf(lpData, MAX_WPATH, L"%s/%s", lpData, ptr) < 0)
|
91
|
+
rb_raise_syserr("swprintf", GetLastError());
|
92
|
+
}
|
93
|
+
|
94
|
+
return lpData;
|
95
|
+
}
|
96
|
+
|
97
|
+
/* Helper function to expand tilde into full path. Note that I don't use the
|
98
|
+
* PathCchXXX functions here because it's extremely unlikely that a person's
|
99
|
+
* home directory exceeds MAX_PATH. In the unlikely even that it does exceed
|
100
|
+
* MAX_PATH, an error will be raised.
|
101
|
+
*/
|
102
|
+
wchar_t* expand_tilde(){
|
103
|
+
DWORD size = 0;
|
104
|
+
wchar_t* home = NULL;
|
105
|
+
const wchar_t* env = L"HOME";
|
106
|
+
|
107
|
+
// First, try to get HOME environment variable
|
108
|
+
size = GetEnvironmentVariableW(env, NULL, 0);
|
109
|
+
|
110
|
+
// If that isn't found then try USERPROFILE
|
111
|
+
if(!size){
|
112
|
+
env = L"USERPROFILE";
|
113
|
+
size = GetEnvironmentVariableW(env, home, size);
|
114
|
+
}
|
115
|
+
|
116
|
+
// If that isn't found the try HOMEDRIVE + HOMEPATH
|
117
|
+
if(!size){
|
118
|
+
DWORD size2;
|
119
|
+
wchar_t* temp;
|
120
|
+
const wchar_t* env2 = L"HOMEPATH";
|
121
|
+
env = L"HOMEDRIVE";
|
122
|
+
|
123
|
+
// If neither are found then raise an error
|
124
|
+
size = GetEnvironmentVariableW(env, NULL, 0);
|
125
|
+
size2 = GetEnvironmentVariableW(env2, NULL, 0);
|
126
|
+
|
127
|
+
if(!size || !size2){
|
128
|
+
if (GetLastError() != ERROR_ENVVAR_NOT_FOUND)
|
129
|
+
rb_raise_syserr("GetEnvironmentVariable", GetLastError());
|
130
|
+
else
|
131
|
+
rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding '~'");
|
132
|
+
}
|
133
|
+
|
134
|
+
home = (wchar_t*)ruby_xmalloc(MAX_WPATH);
|
135
|
+
temp = (wchar_t*)ruby_xmalloc(MAX_WPATH);
|
136
|
+
|
137
|
+
if(!GetEnvironmentVariableW(env, home, size) || !GetEnvironmentVariableW(env2, temp, size2)){
|
138
|
+
ruby_xfree(home);
|
139
|
+
ruby_xfree(temp);
|
140
|
+
rb_raise_syserr("GetEnvironmentVariable", GetLastError());
|
141
|
+
}
|
142
|
+
|
143
|
+
if(!PathAppendW(home, temp))
|
144
|
+
rb_raise_syserr("PathAppend", GetLastError());
|
145
|
+
}
|
146
|
+
else{
|
147
|
+
home = (wchar_t*)ruby_xmalloc(MAX_WPATH);
|
148
|
+
size = GetEnvironmentVariableW(env, home, size);
|
149
|
+
|
150
|
+
if(!size){
|
151
|
+
ruby_xfree(home);
|
152
|
+
rb_raise_syserr("GetEnvironmentVariable", GetLastError());
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
while(wcsstr(home, L"/"))
|
157
|
+
home[wcscspn(home, L"/")] = L'\\';
|
158
|
+
|
159
|
+
if (PathIsRelativeW(home))
|
160
|
+
rb_raise(rb_eArgError, "non-absolute home");
|
161
|
+
|
162
|
+
return home;
|
163
|
+
}
|
164
|
+
|
165
|
+
/*
|
166
|
+
* File.expand_path(path, dir = nil)
|
167
|
+
*
|
168
|
+
* Converts +path+ to an absolute path.
|
169
|
+
*
|
170
|
+
* This is a custom version of the File.expand_path method for Windows
|
171
|
+
* that is much faster than the MRI core method. It also supports "~user"
|
172
|
+
* expansion on Windows while the MRI core method does not.
|
173
|
+
*/
|
174
|
+
static VALUE rb_xpath(int argc, VALUE* argv, VALUE self){
|
175
|
+
VALUE v_path, v_path_orig, v_dir_orig;
|
176
|
+
wchar_t* buffer = NULL;
|
177
|
+
wchar_t* ptr = NULL;
|
178
|
+
wchar_t* path = NULL;
|
179
|
+
char* final_path;
|
180
|
+
int length;
|
181
|
+
rb_encoding* path_encoding;
|
182
|
+
rb_econv_t* ec;
|
183
|
+
const int replaceflags = ECONV_UNDEF_REPLACE|ECONV_INVALID_REPLACE;
|
184
|
+
|
185
|
+
rb_scan_args(argc, argv, "11", &v_path_orig, &v_dir_orig);
|
186
|
+
|
187
|
+
if (rb_respond_to(v_path_orig, rb_intern("to_path")))
|
188
|
+
v_path_orig = rb_funcall(v_path_orig, rb_intern("to_path"), 0, NULL);
|
189
|
+
|
190
|
+
SafeStringValue(v_path_orig);
|
191
|
+
|
192
|
+
if (!NIL_P(v_dir_orig))
|
193
|
+
SafeStringValue(v_dir_orig);
|
194
|
+
|
195
|
+
// Dup and prep string for modification
|
196
|
+
path_encoding = rb_enc_get(v_path_orig);
|
197
|
+
|
198
|
+
if (rb_enc_to_index(path_encoding) == rb_utf8_encindex()){
|
199
|
+
v_path = rb_str_dup(v_path_orig);
|
200
|
+
}
|
201
|
+
else{
|
202
|
+
ec = rb_econv_open(rb_enc_name(path_encoding), "UTF-8", replaceflags);
|
203
|
+
v_path = rb_econv_str_convert(ec, v_path_orig, ECONV_PARTIAL_INPUT);
|
204
|
+
rb_econv_close(ec);
|
205
|
+
}
|
206
|
+
|
207
|
+
rb_str_modify_expand(v_path, MAX_PATH);
|
208
|
+
|
209
|
+
// Make our path a wide string for later functions
|
210
|
+
length = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_path), -1, NULL, 0);
|
211
|
+
path = (wchar_t*)ruby_xmalloc(length * sizeof(wchar_t));
|
212
|
+
|
213
|
+
if(!MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_path), -1, path, length)){
|
214
|
+
ruby_xfree(path);
|
215
|
+
rb_raise_syserr("MultibyteToWideChar", GetLastError());
|
216
|
+
}
|
217
|
+
|
218
|
+
// Convert all forward slashes to backslashes to Windows API functions work properly
|
219
|
+
while(wcsstr(path, L"/"))
|
220
|
+
path[wcscspn(path, L"/")] = L'\\';
|
221
|
+
|
222
|
+
// Handle ~ expansion.
|
223
|
+
if (ptr = wcschr(path, L'~')){
|
224
|
+
wchar_t* home;
|
225
|
+
|
226
|
+
// Handle both ~/user and ~user syntax
|
227
|
+
if (ptr[1] && ptr[1] != L'\\'){
|
228
|
+
home = find_user(++ptr);
|
229
|
+
}
|
230
|
+
else{
|
231
|
+
#ifdef HAVE_PATHCCHAPPENDEX
|
232
|
+
HRESULT result;
|
233
|
+
wchar_t buffer[PATHCCH_MAX_CCH];
|
234
|
+
|
235
|
+
wcscpy_s(buffer, PATHCCH_MAX_CCH, expand_tilde(path));
|
236
|
+
|
237
|
+
result = PathCchAppendEx(buffer, MAX_PATH, ++ptr, PATHCCH_ALLOW_LONG_PATHS);
|
238
|
+
|
239
|
+
if (result != S_OK)
|
240
|
+
rb_raise_syserr("PathCchAppend", result);
|
241
|
+
|
242
|
+
home = &buffer[0];
|
243
|
+
#else
|
244
|
+
home = expand_tilde(path);
|
245
|
+
|
246
|
+
if (!PathAppendW(home, ++ptr)){
|
247
|
+
ruby_xfree(home);
|
248
|
+
rb_raise_syserr("PathAppend", GetLastError());
|
249
|
+
}
|
250
|
+
#endif
|
251
|
+
}
|
252
|
+
|
253
|
+
path = home;
|
254
|
+
}
|
255
|
+
|
256
|
+
// Directory argument is present
|
257
|
+
if (!NIL_P(v_dir_orig)){
|
258
|
+
wchar_t* dir;
|
259
|
+
VALUE v_dir;
|
260
|
+
rb_encoding* dir_encoding;
|
261
|
+
|
262
|
+
dir_encoding = rb_enc_get(v_dir_orig);
|
263
|
+
|
264
|
+
// Hooray for encodings
|
265
|
+
if (rb_enc_to_index(dir_encoding) == rb_utf8_encindex()){
|
266
|
+
v_dir = rb_str_dup(v_dir_orig);
|
267
|
+
}
|
268
|
+
else{
|
269
|
+
ec = rb_econv_open(rb_enc_name(dir_encoding), "UTF-8", replaceflags);
|
270
|
+
v_dir = rb_econv_str_convert(ec, v_dir_orig, ECONV_PARTIAL_INPUT);
|
271
|
+
rb_econv_close(ec);
|
272
|
+
}
|
273
|
+
|
274
|
+
// Prep string for modification
|
275
|
+
rb_str_modify_expand(v_dir, MAX_PATH);
|
276
|
+
|
277
|
+
length = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_dir), -1, NULL, 0);
|
278
|
+
dir = (wchar_t*)ruby_xmalloc(MAX_PATH * sizeof(wchar_t));
|
279
|
+
|
280
|
+
if (!MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_dir), -1, dir, length)){
|
281
|
+
ruby_xfree(dir);
|
282
|
+
rb_raise_syserr("MultibyteToWideChar", GetLastError());
|
283
|
+
}
|
284
|
+
|
285
|
+
while (wcsstr(dir, L"/"))
|
286
|
+
dir[wcscspn(dir, L"/")] = L'\\';
|
287
|
+
|
288
|
+
if (ptr = wcschr(dir, L'~')){
|
289
|
+
if (ptr[1] && ptr[1] != L'\\'){
|
290
|
+
dir = find_user(++ptr);
|
291
|
+
}
|
292
|
+
else{
|
293
|
+
dir = expand_tilde();
|
294
|
+
|
295
|
+
if (!PathAppendW(dir, ++ptr)){
|
296
|
+
ruby_xfree(dir);
|
297
|
+
rb_raise_syserr("PathAppend", GetLastError());
|
298
|
+
}
|
299
|
+
}
|
300
|
+
}
|
301
|
+
|
302
|
+
if (!wcslen(path))
|
303
|
+
path = dir;
|
304
|
+
|
305
|
+
if (PathIsRelativeW(path)){
|
306
|
+
if(!PathAppendW(dir, path)){
|
307
|
+
ruby_xfree(dir);
|
308
|
+
rb_raise_syserr("PathAppend", GetLastError());
|
309
|
+
}
|
310
|
+
|
311
|
+
// Remove leading slashes from relative paths
|
312
|
+
if (dir[0] == L'\\')
|
313
|
+
++dir;
|
314
|
+
|
315
|
+
path = dir;
|
316
|
+
}
|
317
|
+
}
|
318
|
+
else{
|
319
|
+
if (!wcslen(path)){
|
320
|
+
char* pwd;
|
321
|
+
wchar_t* wpwd;
|
322
|
+
|
323
|
+
// First call, get the length
|
324
|
+
length = GetCurrentDirectoryW(0, NULL);
|
325
|
+
wpwd = (wchar_t*)ruby_xmalloc(length * sizeof(wchar_t));
|
326
|
+
|
327
|
+
length = GetCurrentDirectoryW(length, wpwd);
|
328
|
+
|
329
|
+
if(!length){
|
330
|
+
ruby_xfree(wpwd);
|
331
|
+
rb_raise_syserr("GetCurrentDirectory", GetLastError());
|
332
|
+
}
|
333
|
+
|
334
|
+
// Convert backslashes into forward slashes
|
335
|
+
while(wcsstr(wpwd, L"\\"))
|
336
|
+
wpwd[wcscspn(wpwd, L"\\")] = L'/';
|
337
|
+
|
338
|
+
// Convert string back to multibyte string before returning Ruby object
|
339
|
+
length = WideCharToMultiByte(CP_UTF8, 0, wpwd, -1, NULL, 0, NULL, NULL);
|
340
|
+
pwd = (char*)ruby_xmalloc(length);
|
341
|
+
length = WideCharToMultiByte(CP_UTF8, 0, wpwd, -1, pwd, length, NULL, NULL);
|
342
|
+
|
343
|
+
if (!length){
|
344
|
+
ruby_xfree(pwd);
|
345
|
+
rb_raise_syserr("WideCharToMultiByte", GetLastError());
|
346
|
+
}
|
347
|
+
|
348
|
+
return rb_str_new2(pwd);
|
349
|
+
}
|
350
|
+
}
|
351
|
+
|
352
|
+
// Strip all trailing backslashes
|
353
|
+
while (!*PathRemoveBackslashW(path));
|
354
|
+
|
355
|
+
// First call, get the length
|
356
|
+
length = GetFullPathNameW(path, 0, buffer, NULL);
|
357
|
+
buffer = (wchar_t*)ruby_xmalloc(length * sizeof(wchar_t));
|
358
|
+
|
359
|
+
// Now get the path
|
360
|
+
length = GetFullPathNameW(path, length, buffer, NULL);
|
361
|
+
|
362
|
+
if (!length){
|
363
|
+
ruby_xfree(buffer);
|
364
|
+
rb_raise_syserr("GetFullPathName", GetLastError());
|
365
|
+
}
|
366
|
+
|
367
|
+
// Convert backslashes into forward slashes
|
368
|
+
while(wcsstr(buffer, L"\\"))
|
369
|
+
buffer[wcscspn(buffer, L"\\")] = L'/';
|
370
|
+
|
371
|
+
length = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL);
|
372
|
+
final_path = (char*)ruby_xmalloc(length);
|
373
|
+
length = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, final_path, length, NULL, NULL);
|
374
|
+
|
375
|
+
ruby_xfree(buffer);
|
376
|
+
|
377
|
+
if (!length){
|
378
|
+
ruby_xfree(final_path);
|
379
|
+
rb_raise_syserr("WideCharToMultiByte", GetLastError());
|
380
|
+
}
|
381
|
+
|
382
|
+
v_path = rb_str_new(final_path, length - 1); // Don't count null terminator
|
383
|
+
|
384
|
+
if (rb_enc_to_index(path_encoding) != rb_utf8_encindex()){
|
385
|
+
ec = rb_econv_open("UTF-8", rb_enc_name(path_encoding), replaceflags);
|
386
|
+
v_path = rb_econv_str_convert(ec, v_path, ECONV_PARTIAL_INPUT);
|
387
|
+
rb_econv_close(ec);
|
388
|
+
}
|
389
|
+
|
390
|
+
rb_enc_associate(v_path, path_encoding);
|
391
|
+
|
392
|
+
if (OBJ_TAINTED(v_path_orig) || rb_equal(v_path, v_path_orig) == Qfalse)
|
393
|
+
OBJ_TAINT(v_path);
|
394
|
+
|
395
|
+
return v_path;
|
396
|
+
}
|
397
|
+
|
398
|
+
void Init_xpath(){
|
399
|
+
rb_define_singleton_method(rb_cFile, "expand_path", rb_xpath, -1);
|
400
|
+
}
|