win32-symlink 0.1.1 → 0.1.2
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/Gemfile +6 -5
- data/LICENSE +21 -21
- data/README.md +40 -28
- data/Rakefile +21 -21
- data/ext/win32/extconf.rb +12 -12
- data/ext/win32/win32-symlink.cpp +297 -297
- data/lib/win32/filesymlink.rb +19 -0
- data/lib/win32/symlink/version.rb +12 -12
- data/lib/win32/symlink/win32_symlink.so +0 -0
- data/lib/win32/symlink.rb +2 -2
- data/tasks/ext.rake +7 -7
- data/test/test_win32_symlink.rb +60 -60
- data/win32-symlink.gemspec +36 -35
- metadata +10 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cff5a6b053e50f47c45bdde6a29ba72088182522
|
4
|
+
data.tar.gz: 88a4b8561aba22278fdfedfd94a3938cdbcf24ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2dc15b209921a53f8f0024e275b2c7331a913be813699382b7ede0e16bbfe04f455ac7d3038e71914c5580b001bfdde591983ef9efbbb9a76e468c68813be724
|
7
|
+
data.tar.gz: 6b3bcec0e9283052bf4af873e52faf60f3deda9d1ab72ee58082401a9a92b42fb322754e174c47a05df1f42942b9f92aaa86751bdeb7c7d69258549b9d26d34d
|
data/Gemfile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
# -*- ruby -*-
|
2
|
-
source 'https://rubygems.org'
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
# -*- ruby -*-
|
2
|
+
source 'https://rubygems.org'
|
3
|
+
|
4
|
+
group :development do
|
5
|
+
gem "rake-compiler"
|
6
|
+
end
|
data/LICENSE
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
Copyright (c) 2013 boris
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
1
|
+
Copyright (c) 2013 boris
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
22
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,28 +1,40 @@
|
|
1
|
-
# Win32::Symlink
|
2
|
-
|
3
|
-
Since the standard ruby library lacks symlink functions on Windows, this
|
4
|
-
little library attempts to fill the gap.
|
5
|
-
|
6
|
-
## Installation
|
7
|
-
|
8
|
-
Add this line to your application's Gemfile:
|
9
|
-
|
10
|
-
gem 'win32-symlink'
|
11
|
-
|
12
|
-
And then execute:
|
13
|
-
|
14
|
-
$ bundle
|
15
|
-
|
16
|
-
Or install it yourself as:
|
17
|
-
|
18
|
-
$ gem install win32-symlink
|
19
|
-
|
20
|
-
## Usage
|
21
|
-
|
22
|
-
This library implements the following functons that mimic their ruby
|
23
|
-
counterparts:
|
24
|
-
|
25
|
-
readlink(link_name) -> file_name
|
26
|
-
symlink?(link_name) -> true or false
|
27
|
-
symlink(old_name, new_name) -> 0
|
28
|
-
|
1
|
+
# Win32::Symlink
|
2
|
+
|
3
|
+
Since the standard ruby library lacks symlink functions on Windows, this
|
4
|
+
little library attempts to fill the gap.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'win32-symlink'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install win32-symlink
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
This library implements the following functons that mimic their ruby
|
23
|
+
counterparts:
|
24
|
+
|
25
|
+
Win32::Symlink.readlink(link_name) -> file_name
|
26
|
+
Win32::Symlink.symlink?(link_name) -> true or false
|
27
|
+
Win32::Symlink.symlink(old_name, new_name) -> 0
|
28
|
+
|
29
|
+
### 'Portable' Usage
|
30
|
+
|
31
|
+
Instead of referencing symlink functions in the Win32::Symlink
|
32
|
+
module it is possible to extend the Ruby File class and use the
|
33
|
+
functions as on other systems, which support symlinks directly.
|
34
|
+
|
35
|
+
|
36
|
+
require 'win32/filesymlink' if RUBY_PLATFORM.include?('mingw')
|
37
|
+
|
38
|
+
File.symlink('path to source', 'symlink')
|
39
|
+
File.symlink?('symlink')
|
40
|
+
File.readlink('symlink')
|
data/Rakefile
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
# -*- ruby -*-
|
2
|
-
require 'bundler/gem_tasks'
|
3
|
-
require 'rake/testtask'
|
4
|
-
|
5
|
-
begin
|
6
|
-
Bundler.setup(:default, :development)
|
7
|
-
rescue Bundler::BundlerError => e
|
8
|
-
$stderr.puts e.message
|
9
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
-
exit e.status_code
|
11
|
-
end
|
12
|
-
|
13
|
-
$gemspec = Bundler.load_gemspec('win32-symlink.gemspec')
|
14
|
-
|
15
|
-
Rake::TestTask.new do |t|
|
16
|
-
t.warning = true
|
17
|
-
end
|
18
|
-
|
19
|
-
load 'tasks/ext.rake'
|
20
|
-
|
21
|
-
task :default => [:compile, :test]
|
1
|
+
# -*- ruby -*-
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
|
13
|
+
$gemspec = Bundler.load_gemspec('win32-symlink.gemspec')
|
14
|
+
|
15
|
+
Rake::TestTask.new do |t|
|
16
|
+
t.warning = true
|
17
|
+
end
|
18
|
+
|
19
|
+
load 'tasks/ext.rake'
|
20
|
+
|
21
|
+
task :default => [:compile, :test]
|
data/ext/win32/extconf.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
require 'mkmf'
|
2
|
-
|
3
|
-
if CONFIG['CXXFLAGS'] == '$(cxxflags)'
|
4
|
-
CONFIG['CXXFLAGS'] = CONFIG['cxxflags'].clone
|
5
|
-
end
|
6
|
-
|
7
|
-
# these are not relevant for C++
|
8
|
-
CONFIG['warnflags'].sub! '-Wdeclaration-after-statement', ''
|
9
|
-
CONFIG['warnflags'].sub! '-Wimplicit-function-declaration', ''
|
10
|
-
|
11
|
-
try_cppflags $CPPFLAGS.sub(/_WIN32_WINNT=\S+/, '_WIN32_WINNT=0x600')
|
12
|
-
create_makefile 'win32_symlink'
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
if CONFIG['CXXFLAGS'] == '$(cxxflags)'
|
4
|
+
CONFIG['CXXFLAGS'] = CONFIG['cxxflags'].clone
|
5
|
+
end
|
6
|
+
|
7
|
+
# these are not relevant for C++
|
8
|
+
CONFIG['warnflags'].sub! '-Wdeclaration-after-statement', ''
|
9
|
+
CONFIG['warnflags'].sub! '-Wimplicit-function-declaration', ''
|
10
|
+
|
11
|
+
try_cppflags $CPPFLAGS.sub(/_WIN32_WINNT=\S+/, '_WIN32_WINNT=0x600')
|
12
|
+
create_makefile 'win32_symlink'
|
data/ext/win32/win32-symlink.cpp
CHANGED
@@ -1,297 +1,297 @@
|
|
1
|
-
#include "ruby.h"
|
2
|
-
|
3
|
-
#define filecp_to_wstr(str, plen) cstr_to_wstr(filecp(), str, -1, plen)
|
4
|
-
|
5
|
-
typedef BOOL (WINAPI* create_symbolic_linkW_t)(LPWSTR src, LPWSTR dest, DWORD flags);
|
6
|
-
create_symbolic_linkW_t create_symbolic_linkW = NULL;
|
7
|
-
|
8
|
-
|
9
|
-
typedef struct _REPARSE_DATA_BUFFER
|
10
|
-
{
|
11
|
-
ULONG ReparseTag;
|
12
|
-
USHORT ReparseDataLength;
|
13
|
-
USHORT Reserved;
|
14
|
-
union
|
15
|
-
{
|
16
|
-
struct
|
17
|
-
{
|
18
|
-
USHORT SubstituteNameOffset;
|
19
|
-
USHORT SubstituteNameLength;
|
20
|
-
USHORT PrintNameOffset;
|
21
|
-
USHORT PrintNameLength;
|
22
|
-
ULONG Flags;
|
23
|
-
WCHAR PathBuffer[1];
|
24
|
-
} SymbolicLinkReparseBuffer;
|
25
|
-
struct
|
26
|
-
{
|
27
|
-
USHORT SubstituteNameOffset;
|
28
|
-
USHORT SubstituteNameLength;
|
29
|
-
USHORT PrintNameOffset;
|
30
|
-
USHORT PrintNameLength;
|
31
|
-
WCHAR PathBuffer[1];
|
32
|
-
} MountPointReparseBuffer;
|
33
|
-
struct
|
34
|
-
{
|
35
|
-
UCHAR DataBuffer[1];
|
36
|
-
} GenericReparseBuffer;
|
37
|
-
} DUMMYUNIONNAME;
|
38
|
-
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
39
|
-
|
40
|
-
|
41
|
-
static inline UINT
|
42
|
-
filecp(void)
|
43
|
-
{
|
44
|
-
UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
|
45
|
-
return cp;
|
46
|
-
}
|
47
|
-
|
48
|
-
static wchar_t *
|
49
|
-
cstr_to_wstr(UINT code_page, const char *str, int clen, int *wstr_len)
|
50
|
-
{
|
51
|
-
int len;
|
52
|
-
wchar_t* wstr;
|
53
|
-
|
54
|
-
len = MultiByteToWideChar(code_page, 0, str, clen, NULL, 0);
|
55
|
-
wstr = (wchar_t *)xmalloc(len * sizeof(wchar_t));
|
56
|
-
|
57
|
-
MultiByteToWideChar(code_page, 0, str, clen, wstr, len);
|
58
|
-
if( wstr_len )
|
59
|
-
*wstr_len = len;
|
60
|
-
return wstr;
|
61
|
-
}
|
62
|
-
|
63
|
-
static VALUE
|
64
|
-
wstr_to_str(UINT code_page, const wchar_t* wstr, int clen)
|
65
|
-
{
|
66
|
-
int len, term_len;
|
67
|
-
VALUE v;
|
68
|
-
term_len = clen < 0 ? 1 : 0;
|
69
|
-
len = WideCharToMultiByte(code_page, 0, wstr, clen, NULL, 0, NULL, NULL) - term_len;
|
70
|
-
v = rb_str_new(NULL, len);
|
71
|
-
WideCharToMultiByte(code_page, 0, wstr, clen, RSTRING_PTR(v), len + term_len, NULL, NULL);
|
72
|
-
return v;
|
73
|
-
}
|
74
|
-
|
75
|
-
static VALUE
|
76
|
-
make_error_str(VALUE klass, const char* fmt, ...)
|
77
|
-
{
|
78
|
-
VALUE exc;
|
79
|
-
VALUE str;
|
80
|
-
|
81
|
-
va_list va;
|
82
|
-
va_start(va, fmt);
|
83
|
-
str = rb_vsprintf(fmt, va);
|
84
|
-
va_end(va);
|
85
|
-
exc = rb_exc_new3(klass, str);
|
86
|
-
rb_str_free(str);
|
87
|
-
return exc;
|
88
|
-
}
|
89
|
-
|
90
|
-
static VALUE
|
91
|
-
make_api_error(const char* msg)
|
92
|
-
{
|
93
|
-
DWORD w32_err;
|
94
|
-
int myerrno;
|
95
|
-
w32_err = GetLastError();
|
96
|
-
myerrno = rb_w32_map_errno(w32_err);
|
97
|
-
return rb_syserr_new( myerrno == EINVAL ? w32_err : myerrno, msg);
|
98
|
-
}
|
99
|
-
|
100
|
-
static wchar_t*
|
101
|
-
resolve_api_path(wchar_t* path)
|
102
|
-
{
|
103
|
-
if( 0 == wcsncmp(L"\\??\\UNC\\", path, 8) )
|
104
|
-
{
|
105
|
-
path += 6;
|
106
|
-
*path = L'\\';
|
107
|
-
}
|
108
|
-
else if( 0 == wcsncmp(L"\\??\\", path, 4) )
|
109
|
-
{
|
110
|
-
path += 4;
|
111
|
-
}
|
112
|
-
return path;
|
113
|
-
}
|
114
|
-
|
115
|
-
static VALUE
|
116
|
-
win32_readlink(const char* file)
|
117
|
-
{
|
118
|
-
VALUE error = Qnil;
|
119
|
-
VALUE target = Qnil;
|
120
|
-
HANDLE h = INVALID_HANDLE_VALUE;
|
121
|
-
bool ok = true;
|
122
|
-
REPARSE_DATA_BUFFER* buf = NULL;
|
123
|
-
const size_t buf_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
|
124
|
-
|
125
|
-
buf = (REPARSE_DATA_BUFFER*)xmalloc(buf_size);
|
126
|
-
if(ok)
|
127
|
-
{
|
128
|
-
const DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
129
|
-
const DWORD access_mode = FILE_READ_EA;
|
130
|
-
const DWORD attributes =
|
131
|
-
FILE_FLAG_BACKUP_SEMANTICS
|
132
|
-
| FILE_ATTRIBUTE_REPARSE_POINT
|
133
|
-
| FILE_FLAG_OPEN_REPARSE_POINT;
|
134
|
-
|
135
|
-
h = CreateFile( file, access_mode, share_mode,
|
136
|
-
0, OPEN_EXISTING, attributes, NULL);
|
137
|
-
|
138
|
-
if(!(ok = (h != INVALID_HANDLE_VALUE)))
|
139
|
-
{
|
140
|
-
error = make_api_error(file);
|
141
|
-
}
|
142
|
-
}
|
143
|
-
|
144
|
-
if(ok)
|
145
|
-
{
|
146
|
-
DWORD returned_size;
|
147
|
-
ok = 0 != DeviceIoControl (h, FSCTL_GET_REPARSE_POINT,
|
148
|
-
NULL, 0, buf, buf_size, &returned_size, NULL);
|
149
|
-
if(!ok)
|
150
|
-
{
|
151
|
-
error = make_api_error(file);
|
152
|
-
}
|
153
|
-
}
|
154
|
-
if(ok)
|
155
|
-
{
|
156
|
-
wchar_t* raw_target = NULL;
|
157
|
-
long target_len = 0;
|
158
|
-
|
159
|
-
switch (buf->ReparseTag)
|
160
|
-
{
|
161
|
-
case IO_REPARSE_TAG_MOUNT_POINT:
|
162
|
-
{
|
163
|
-
raw_target = &buf->MountPointReparseBuffer.PathBuffer[
|
164
|
-
buf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)];
|
165
|
-
target_len = buf->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
|
166
|
-
break;
|
167
|
-
}
|
168
|
-
case IO_REPARSE_TAG_SYMLINK:
|
169
|
-
{
|
170
|
-
raw_target = &buf->SymbolicLinkReparseBuffer.PathBuffer[
|
171
|
-
buf->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)];
|
172
|
-
target_len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
|
173
|
-
break;
|
174
|
-
}
|
175
|
-
default:
|
176
|
-
ok = false;
|
177
|
-
error = make_error_str(rb_eIOError, "file %s is not a symlink", file);
|
178
|
-
}
|
179
|
-
if(ok)
|
180
|
-
{
|
181
|
-
wchar_t* resolved;
|
182
|
-
resolved = resolve_api_path(raw_target);
|
183
|
-
target_len -= resolved - raw_target;
|
184
|
-
target = wstr_to_str(filecp(), resolved, target_len);
|
185
|
-
}
|
186
|
-
}
|
187
|
-
|
188
|
-
|
189
|
-
if( h != INVALID_HANDLE_VALUE )
|
190
|
-
{
|
191
|
-
CloseHandle(h);
|
192
|
-
}
|
193
|
-
xfree(buf);
|
194
|
-
|
195
|
-
if( !NIL_P(error) )
|
196
|
-
{
|
197
|
-
rb_exc_raise(error);
|
198
|
-
}
|
199
|
-
return target;
|
200
|
-
}
|
201
|
-
|
202
|
-
static VALUE
|
203
|
-
rb_readlink(VALUE mod, VALUE file)
|
204
|
-
{
|
205
|
-
(void)mod;
|
206
|
-
FilePathValue(file);
|
207
|
-
file = rb_str_encode_ospath(file);
|
208
|
-
return win32_readlink(RSTRING_PTR(file));
|
209
|
-
}
|
210
|
-
|
211
|
-
static VALUE
|
212
|
-
rb_symlink_p(VALUE mod, VALUE file)
|
213
|
-
{
|
214
|
-
(void)mod;
|
215
|
-
DWORD attrs;
|
216
|
-
FilePathValue(file);
|
217
|
-
file = rb_str_encode_ospath(file);
|
218
|
-
attrs = GetFileAttributes(RSTRING_PTR(file));
|
219
|
-
if( attrs == INVALID_FILE_ATTRIBUTES )
|
220
|
-
{
|
221
|
-
rb_exc_raise(make_api_error(RSTRING_PTR(file)));
|
222
|
-
}
|
223
|
-
return (attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
|
224
|
-
}
|
225
|
-
|
226
|
-
static bool
|
227
|
-
is_directory(wchar_t* path)
|
228
|
-
{
|
229
|
-
HANDLE h = INVALID_HANDLE_VALUE;
|
230
|
-
WIN32_FIND_DATAW fd;
|
231
|
-
h = FindFirstFileW(path, &fd);
|
232
|
-
if( h != INVALID_HANDLE_VALUE )
|
233
|
-
{
|
234
|
-
FindClose(h);
|
235
|
-
return (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ;
|
236
|
-
}
|
237
|
-
return false;
|
238
|
-
}
|
239
|
-
|
240
|
-
static VALUE
|
241
|
-
rb_symlink(VALUE mod, VALUE target, VALUE symlink)
|
242
|
-
{
|
243
|
-
(void)mod;
|
244
|
-
|
245
|
-
VALUE error = Qnil;
|
246
|
-
wchar_t* wtarget = NULL;
|
247
|
-
wchar_t* wsymlink = NULL;
|
248
|
-
BOOL res;
|
249
|
-
DWORD flags = 0;
|
250
|
-
|
251
|
-
target = FilePathValue(target);
|
252
|
-
symlink = FilePathValue(symlink);
|
253
|
-
target = rb_str_encode_ospath(target);
|
254
|
-
symlink = rb_str_encode_ospath(symlink);
|
255
|
-
|
256
|
-
wtarget = filecp_to_wstr(RSTRING_PTR(target), NULL);
|
257
|
-
wsymlink = filecp_to_wstr(RSTRING_PTR(symlink), NULL);
|
258
|
-
|
259
|
-
if( is_directory(wsymlink) )
|
260
|
-
{
|
261
|
-
flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
|
262
|
-
}
|
263
|
-
res = create_symbolic_linkW(wsymlink, wtarget, flags);
|
264
|
-
if( !res )
|
265
|
-
{
|
266
|
-
error = make_api_error(RSTRING_PTR(target));
|
267
|
-
}
|
268
|
-
|
269
|
-
xfree(wtarget);
|
270
|
-
xfree(wsymlink);
|
271
|
-
|
272
|
-
if( !NIL_P(error) )
|
273
|
-
{
|
274
|
-
rb_exc_raise(error);
|
275
|
-
}
|
276
|
-
return INT2FIX(0);
|
277
|
-
}
|
278
|
-
|
279
|
-
extern "C" void
|
280
|
-
Init_win32_symlink()
|
281
|
-
{
|
282
|
-
VALUE mod_win32;
|
283
|
-
VALUE mod;
|
284
|
-
|
285
|
-
mod_win32 = rb_define_module("Win32");
|
286
|
-
mod = rb_define_module_under(mod_win32, "Symlink");
|
287
|
-
|
288
|
-
rb_define_module_function(mod, "readlink", RUBY_METHOD_FUNC(rb_readlink), 1);
|
289
|
-
rb_define_module_function(mod, "symlink?", RUBY_METHOD_FUNC(rb_symlink_p), 1);
|
290
|
-
|
291
|
-
create_symbolic_linkW = (create_symbolic_linkW_t)GetProcAddress(
|
292
|
-
GetModuleHandle("kernel32.dll"), "CreateSymbolicLinkW");
|
293
|
-
if( create_symbolic_linkW )
|
294
|
-
{
|
295
|
-
rb_define_module_function(mod, "symlink", RUBY_METHOD_FUNC(rb_symlink), 2);
|
296
|
-
}
|
297
|
-
}
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
#define filecp_to_wstr(str, plen) cstr_to_wstr(filecp(), str, -1, plen)
|
4
|
+
|
5
|
+
typedef BOOL (WINAPI* create_symbolic_linkW_t)(LPWSTR src, LPWSTR dest, DWORD flags);
|
6
|
+
create_symbolic_linkW_t create_symbolic_linkW = NULL;
|
7
|
+
|
8
|
+
|
9
|
+
typedef struct _REPARSE_DATA_BUFFER
|
10
|
+
{
|
11
|
+
ULONG ReparseTag;
|
12
|
+
USHORT ReparseDataLength;
|
13
|
+
USHORT Reserved;
|
14
|
+
union
|
15
|
+
{
|
16
|
+
struct
|
17
|
+
{
|
18
|
+
USHORT SubstituteNameOffset;
|
19
|
+
USHORT SubstituteNameLength;
|
20
|
+
USHORT PrintNameOffset;
|
21
|
+
USHORT PrintNameLength;
|
22
|
+
ULONG Flags;
|
23
|
+
WCHAR PathBuffer[1];
|
24
|
+
} SymbolicLinkReparseBuffer;
|
25
|
+
struct
|
26
|
+
{
|
27
|
+
USHORT SubstituteNameOffset;
|
28
|
+
USHORT SubstituteNameLength;
|
29
|
+
USHORT PrintNameOffset;
|
30
|
+
USHORT PrintNameLength;
|
31
|
+
WCHAR PathBuffer[1];
|
32
|
+
} MountPointReparseBuffer;
|
33
|
+
struct
|
34
|
+
{
|
35
|
+
UCHAR DataBuffer[1];
|
36
|
+
} GenericReparseBuffer;
|
37
|
+
} DUMMYUNIONNAME;
|
38
|
+
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
39
|
+
|
40
|
+
|
41
|
+
static inline UINT
|
42
|
+
filecp(void)
|
43
|
+
{
|
44
|
+
UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
|
45
|
+
return cp;
|
46
|
+
}
|
47
|
+
|
48
|
+
static wchar_t *
|
49
|
+
cstr_to_wstr(UINT code_page, const char *str, int clen, int *wstr_len)
|
50
|
+
{
|
51
|
+
int len;
|
52
|
+
wchar_t* wstr;
|
53
|
+
|
54
|
+
len = MultiByteToWideChar(code_page, 0, str, clen, NULL, 0);
|
55
|
+
wstr = (wchar_t *)xmalloc(len * sizeof(wchar_t));
|
56
|
+
|
57
|
+
MultiByteToWideChar(code_page, 0, str, clen, wstr, len);
|
58
|
+
if( wstr_len )
|
59
|
+
*wstr_len = len;
|
60
|
+
return wstr;
|
61
|
+
}
|
62
|
+
|
63
|
+
static VALUE
|
64
|
+
wstr_to_str(UINT code_page, const wchar_t* wstr, int clen)
|
65
|
+
{
|
66
|
+
int len, term_len;
|
67
|
+
VALUE v;
|
68
|
+
term_len = clen < 0 ? 1 : 0;
|
69
|
+
len = WideCharToMultiByte(code_page, 0, wstr, clen, NULL, 0, NULL, NULL) - term_len;
|
70
|
+
v = rb_str_new(NULL, len);
|
71
|
+
WideCharToMultiByte(code_page, 0, wstr, clen, RSTRING_PTR(v), len + term_len, NULL, NULL);
|
72
|
+
return v;
|
73
|
+
}
|
74
|
+
|
75
|
+
static VALUE
|
76
|
+
make_error_str(VALUE klass, const char* fmt, ...)
|
77
|
+
{
|
78
|
+
VALUE exc;
|
79
|
+
VALUE str;
|
80
|
+
|
81
|
+
va_list va;
|
82
|
+
va_start(va, fmt);
|
83
|
+
str = rb_vsprintf(fmt, va);
|
84
|
+
va_end(va);
|
85
|
+
exc = rb_exc_new3(klass, str);
|
86
|
+
rb_str_free(str);
|
87
|
+
return exc;
|
88
|
+
}
|
89
|
+
|
90
|
+
static VALUE
|
91
|
+
make_api_error(const char* msg)
|
92
|
+
{
|
93
|
+
DWORD w32_err;
|
94
|
+
int myerrno;
|
95
|
+
w32_err = GetLastError();
|
96
|
+
myerrno = rb_w32_map_errno(w32_err);
|
97
|
+
return rb_syserr_new( myerrno == EINVAL ? w32_err : myerrno, msg);
|
98
|
+
}
|
99
|
+
|
100
|
+
static wchar_t*
|
101
|
+
resolve_api_path(wchar_t* path)
|
102
|
+
{
|
103
|
+
if( 0 == wcsncmp(L"\\??\\UNC\\", path, 8) )
|
104
|
+
{
|
105
|
+
path += 6;
|
106
|
+
*path = L'\\';
|
107
|
+
}
|
108
|
+
else if( 0 == wcsncmp(L"\\??\\", path, 4) )
|
109
|
+
{
|
110
|
+
path += 4;
|
111
|
+
}
|
112
|
+
return path;
|
113
|
+
}
|
114
|
+
|
115
|
+
static VALUE
|
116
|
+
win32_readlink(const char* file)
|
117
|
+
{
|
118
|
+
VALUE error = Qnil;
|
119
|
+
VALUE target = Qnil;
|
120
|
+
HANDLE h = INVALID_HANDLE_VALUE;
|
121
|
+
bool ok = true;
|
122
|
+
REPARSE_DATA_BUFFER* buf = NULL;
|
123
|
+
const size_t buf_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
|
124
|
+
|
125
|
+
buf = (REPARSE_DATA_BUFFER*)xmalloc(buf_size);
|
126
|
+
if(ok)
|
127
|
+
{
|
128
|
+
const DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
129
|
+
const DWORD access_mode = FILE_READ_EA;
|
130
|
+
const DWORD attributes =
|
131
|
+
FILE_FLAG_BACKUP_SEMANTICS
|
132
|
+
| FILE_ATTRIBUTE_REPARSE_POINT
|
133
|
+
| FILE_FLAG_OPEN_REPARSE_POINT;
|
134
|
+
|
135
|
+
h = CreateFile( file, access_mode, share_mode,
|
136
|
+
0, OPEN_EXISTING, attributes, NULL);
|
137
|
+
|
138
|
+
if(!(ok = (h != INVALID_HANDLE_VALUE)))
|
139
|
+
{
|
140
|
+
error = make_api_error(file);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
if(ok)
|
145
|
+
{
|
146
|
+
DWORD returned_size;
|
147
|
+
ok = 0 != DeviceIoControl (h, FSCTL_GET_REPARSE_POINT,
|
148
|
+
NULL, 0, buf, buf_size, &returned_size, NULL);
|
149
|
+
if(!ok)
|
150
|
+
{
|
151
|
+
error = make_api_error(file);
|
152
|
+
}
|
153
|
+
}
|
154
|
+
if(ok)
|
155
|
+
{
|
156
|
+
wchar_t* raw_target = NULL;
|
157
|
+
long target_len = 0;
|
158
|
+
|
159
|
+
switch (buf->ReparseTag)
|
160
|
+
{
|
161
|
+
case IO_REPARSE_TAG_MOUNT_POINT:
|
162
|
+
{
|
163
|
+
raw_target = &buf->MountPointReparseBuffer.PathBuffer[
|
164
|
+
buf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)];
|
165
|
+
target_len = buf->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
|
166
|
+
break;
|
167
|
+
}
|
168
|
+
case IO_REPARSE_TAG_SYMLINK:
|
169
|
+
{
|
170
|
+
raw_target = &buf->SymbolicLinkReparseBuffer.PathBuffer[
|
171
|
+
buf->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)];
|
172
|
+
target_len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
|
173
|
+
break;
|
174
|
+
}
|
175
|
+
default:
|
176
|
+
ok = false;
|
177
|
+
error = make_error_str(rb_eIOError, "file %s is not a symlink", file);
|
178
|
+
}
|
179
|
+
if(ok)
|
180
|
+
{
|
181
|
+
wchar_t* resolved;
|
182
|
+
resolved = resolve_api_path(raw_target);
|
183
|
+
target_len -= resolved - raw_target;
|
184
|
+
target = wstr_to_str(filecp(), resolved, target_len);
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
|
189
|
+
if( h != INVALID_HANDLE_VALUE )
|
190
|
+
{
|
191
|
+
CloseHandle(h);
|
192
|
+
}
|
193
|
+
xfree(buf);
|
194
|
+
|
195
|
+
if( !NIL_P(error) )
|
196
|
+
{
|
197
|
+
rb_exc_raise(error);
|
198
|
+
}
|
199
|
+
return target;
|
200
|
+
}
|
201
|
+
|
202
|
+
static VALUE
|
203
|
+
rb_readlink(VALUE mod, VALUE file)
|
204
|
+
{
|
205
|
+
(void)mod;
|
206
|
+
FilePathValue(file);
|
207
|
+
file = rb_str_encode_ospath(file);
|
208
|
+
return win32_readlink(RSTRING_PTR(file));
|
209
|
+
}
|
210
|
+
|
211
|
+
static VALUE
|
212
|
+
rb_symlink_p(VALUE mod, VALUE file)
|
213
|
+
{
|
214
|
+
(void)mod;
|
215
|
+
DWORD attrs;
|
216
|
+
FilePathValue(file);
|
217
|
+
file = rb_str_encode_ospath(file);
|
218
|
+
attrs = GetFileAttributes(RSTRING_PTR(file));
|
219
|
+
if( attrs == INVALID_FILE_ATTRIBUTES )
|
220
|
+
{
|
221
|
+
rb_exc_raise(make_api_error(RSTRING_PTR(file)));
|
222
|
+
}
|
223
|
+
return (attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
|
224
|
+
}
|
225
|
+
|
226
|
+
static bool
|
227
|
+
is_directory(wchar_t* path)
|
228
|
+
{
|
229
|
+
HANDLE h = INVALID_HANDLE_VALUE;
|
230
|
+
WIN32_FIND_DATAW fd;
|
231
|
+
h = FindFirstFileW(path, &fd);
|
232
|
+
if( h != INVALID_HANDLE_VALUE )
|
233
|
+
{
|
234
|
+
FindClose(h);
|
235
|
+
return (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ;
|
236
|
+
}
|
237
|
+
return false;
|
238
|
+
}
|
239
|
+
|
240
|
+
static VALUE
|
241
|
+
rb_symlink(VALUE mod, VALUE target, VALUE symlink)
|
242
|
+
{
|
243
|
+
(void)mod;
|
244
|
+
|
245
|
+
VALUE error = Qnil;
|
246
|
+
wchar_t* wtarget = NULL;
|
247
|
+
wchar_t* wsymlink = NULL;
|
248
|
+
BOOL res;
|
249
|
+
DWORD flags = 0;
|
250
|
+
|
251
|
+
target = FilePathValue(target);
|
252
|
+
symlink = FilePathValue(symlink);
|
253
|
+
target = rb_str_encode_ospath(target);
|
254
|
+
symlink = rb_str_encode_ospath(symlink);
|
255
|
+
|
256
|
+
wtarget = filecp_to_wstr(RSTRING_PTR(target), NULL);
|
257
|
+
wsymlink = filecp_to_wstr(RSTRING_PTR(symlink), NULL);
|
258
|
+
|
259
|
+
if( is_directory(wsymlink) )
|
260
|
+
{
|
261
|
+
flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
|
262
|
+
}
|
263
|
+
res = create_symbolic_linkW(wsymlink, wtarget, flags);
|
264
|
+
if( !res )
|
265
|
+
{
|
266
|
+
error = make_api_error(RSTRING_PTR(target));
|
267
|
+
}
|
268
|
+
|
269
|
+
xfree(wtarget);
|
270
|
+
xfree(wsymlink);
|
271
|
+
|
272
|
+
if( !NIL_P(error) )
|
273
|
+
{
|
274
|
+
rb_exc_raise(error);
|
275
|
+
}
|
276
|
+
return INT2FIX(0);
|
277
|
+
}
|
278
|
+
|
279
|
+
extern "C" void
|
280
|
+
Init_win32_symlink()
|
281
|
+
{
|
282
|
+
VALUE mod_win32;
|
283
|
+
VALUE mod;
|
284
|
+
|
285
|
+
mod_win32 = rb_define_module("Win32");
|
286
|
+
mod = rb_define_module_under(mod_win32, "Symlink");
|
287
|
+
|
288
|
+
rb_define_module_function(mod, "readlink", RUBY_METHOD_FUNC(rb_readlink), 1);
|
289
|
+
rb_define_module_function(mod, "symlink?", RUBY_METHOD_FUNC(rb_symlink_p), 1);
|
290
|
+
|
291
|
+
create_symbolic_linkW = (create_symbolic_linkW_t)GetProcAddress(
|
292
|
+
GetModuleHandle("kernel32.dll"), "CreateSymbolicLinkW");
|
293
|
+
if( create_symbolic_linkW )
|
294
|
+
{
|
295
|
+
rb_define_module_function(mod, "symlink", RUBY_METHOD_FUNC(rb_symlink), 2);
|
296
|
+
}
|
297
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'symlink'
|
2
|
+
|
3
|
+
|
4
|
+
# Extending the File class on windows system, so that its symlink function work as on other sytems.
|
5
|
+
class File
|
6
|
+
|
7
|
+
def File.symlink(file, symlink)
|
8
|
+
Win32::Symlink::symlink(file, symlink)
|
9
|
+
end
|
10
|
+
|
11
|
+
def File.symlink?(file)
|
12
|
+
Win32::Symlink::symlink?(file)
|
13
|
+
end
|
14
|
+
|
15
|
+
def File.readlink(file)
|
16
|
+
Win32::Symlink::readlink(file)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
|
-
module Win32
|
2
|
-
module Symlink
|
3
|
-
module Version
|
4
|
-
MAJOR = 0
|
5
|
-
MINOR = 1
|
6
|
-
PATCH =
|
7
|
-
BUILD = nil
|
8
|
-
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join '.'
|
9
|
-
end
|
10
|
-
VERSION = Version::STRING
|
11
|
-
end
|
12
|
-
end
|
1
|
+
module Win32
|
2
|
+
module Symlink
|
3
|
+
module Version
|
4
|
+
MAJOR = 0
|
5
|
+
MINOR = 1
|
6
|
+
PATCH = 2
|
7
|
+
BUILD = nil
|
8
|
+
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join '.'
|
9
|
+
end
|
10
|
+
VERSION = Version::STRING
|
11
|
+
end
|
12
|
+
end
|
Binary file
|
data/lib/win32/symlink.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require_relative 'symlink/win32_symlink'
|
2
|
-
require_relative 'symlink/version'
|
1
|
+
require_relative 'symlink/win32_symlink'
|
2
|
+
require_relative 'symlink/version'
|
data/tasks/ext.rake
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
# -*- ruby -*-
|
2
|
-
require 'rake/extensiontask'
|
3
|
-
Rake::ExtensionTask.new('win32_symlink', $gemspec) do |ext|
|
4
|
-
ext.ext_dir = 'ext/win32'
|
5
|
-
ext.lib_dir = 'lib/win32/symlink'
|
6
|
-
end
|
7
|
-
|
1
|
+
# -*- ruby -*-
|
2
|
+
require 'rake/extensiontask'
|
3
|
+
Rake::ExtensionTask.new('win32_symlink', $gemspec) do |ext|
|
4
|
+
ext.ext_dir = 'ext/win32'
|
5
|
+
ext.lib_dir = 'lib/win32/symlink'
|
6
|
+
end
|
7
|
+
|
data/test/test_win32_symlink.rb
CHANGED
@@ -1,60 +1,60 @@
|
|
1
|
-
$LOAD_PATH.unshift File.join(File.dirname(File.dirname(__FILE__)), 'ext')
|
2
|
-
|
3
|
-
# puts $LOAD_PATH
|
4
|
-
|
5
|
-
require 'test/unit'
|
6
|
-
require 'win32/symlink'
|
7
|
-
include Win32
|
8
|
-
|
9
|
-
class Errno::E4390 < SystemCallError
|
10
|
-
# The file is not a reparse point
|
11
|
-
end
|
12
|
-
class Errno::E1314 < SystemCallError
|
13
|
-
# A required privilege is not set
|
14
|
-
end
|
15
|
-
|
16
|
-
class TC_Win32Symlink < Test::Unit::TestCase
|
17
|
-
|
18
|
-
def setup
|
19
|
-
@users_dir = File.join(ENV['SYSTEMDRIVE'], '/Users')
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_bad_args
|
23
|
-
assert_raises(Errno::ENOENT) {Symlink.symlink? ""}
|
24
|
-
assert_raises(TypeError) { Symlink.symlink? nil }
|
25
|
-
assert_raises(TypeError) { Symlink.symlink?(Object.new) }
|
26
|
-
assert_raises(TypeError) { Symlink.symlink "", nil }
|
27
|
-
assert_raises(TypeError) { Symlink.symlink nil, "" }
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_system_links
|
31
|
-
assert(Symlink.symlink?(File.join(@users_dir, "All Users")))
|
32
|
-
assert(!Symlink.symlink?(ENV['WINDIR']))
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_readlink
|
36
|
-
assert_equal(Symlink.readlink(File.join(@users_dir, "All Users")), ENV['ALLUSERSPROFILE'])
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_nonlink
|
40
|
-
assert_raises(Errno::E4390) { Symlink.readlink(ENV['WINDIR']) }
|
41
|
-
end
|
42
|
-
|
43
|
-
def test_makelink
|
44
|
-
Dir.chdir(ENV["TMP"]) do
|
45
|
-
IO::write("symlink-test-file", "test")
|
46
|
-
begin
|
47
|
-
Symlink.symlink("symlink-test-file", "symlink-test-symlink")
|
48
|
-
assert(Symlink.symlink? "symlink-test-symlink")
|
49
|
-
rescue Errno::E1314 => e
|
50
|
-
skip e.message
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def teardown
|
56
|
-
Dir.glob(File.join(ENV['TMP'], 'symlink-test*')).each {|f| File::unlink f}
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
1
|
+
$LOAD_PATH.unshift File.join(File.dirname(File.dirname(__FILE__)), 'ext')
|
2
|
+
|
3
|
+
# puts $LOAD_PATH
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
require 'win32/symlink'
|
7
|
+
include Win32
|
8
|
+
|
9
|
+
class Errno::E4390 < SystemCallError
|
10
|
+
# The file is not a reparse point
|
11
|
+
end
|
12
|
+
class Errno::E1314 < SystemCallError
|
13
|
+
# A required privilege is not set
|
14
|
+
end
|
15
|
+
|
16
|
+
class TC_Win32Symlink < Test::Unit::TestCase
|
17
|
+
|
18
|
+
def setup
|
19
|
+
@users_dir = File.join(ENV['SYSTEMDRIVE'], '/Users')
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_bad_args
|
23
|
+
assert_raises(Errno::ENOENT) {Symlink.symlink? ""}
|
24
|
+
assert_raises(TypeError) { Symlink.symlink? nil }
|
25
|
+
assert_raises(TypeError) { Symlink.symlink?(Object.new) }
|
26
|
+
assert_raises(TypeError) { Symlink.symlink "", nil }
|
27
|
+
assert_raises(TypeError) { Symlink.symlink nil, "" }
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_system_links
|
31
|
+
assert(Symlink.symlink?(File.join(@users_dir, "All Users")))
|
32
|
+
assert(!Symlink.symlink?(ENV['WINDIR']))
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_readlink
|
36
|
+
assert_equal(Symlink.readlink(File.join(@users_dir, "All Users")), ENV['ALLUSERSPROFILE'])
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_nonlink
|
40
|
+
assert_raises(Errno::E4390) { Symlink.readlink(ENV['WINDIR']) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_makelink
|
44
|
+
Dir.chdir(ENV["TMP"]) do
|
45
|
+
IO::write("symlink-test-file", "test")
|
46
|
+
begin
|
47
|
+
Symlink.symlink("symlink-test-file", "symlink-test-symlink")
|
48
|
+
assert(Symlink.symlink? "symlink-test-symlink")
|
49
|
+
rescue Errno::E1314 => e
|
50
|
+
skip e.message
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def teardown
|
56
|
+
Dir.glob(File.join(ENV['TMP'], 'symlink-test*')).each {|f| File::unlink f}
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
data/win32-symlink.gemspec
CHANGED
@@ -1,35 +1,36 @@
|
|
1
|
-
# -*- ruby -*-
|
2
|
-
require File.expand_path('../lib/win32/symlink/version', __FILE__)
|
3
|
-
|
4
|
-
Gem::Specification.new do |gem|
|
5
|
-
gem.authors = ['Boris Kropivnitsky']
|
6
|
-
gem.email = ['boris.kro@gmail.com']
|
7
|
-
gem.description = <<DESC
|
8
|
-
Symlink functions for Windows (Vista and above)
|
9
|
-
DESC
|
10
|
-
gem.summary = 'symlink functions for Windows'
|
11
|
-
gem.homepage = 'https://github.com/boriskro/win32-symlink'
|
12
|
-
|
13
|
-
gem.files = %w{
|
14
|
-
Gemfile
|
15
|
-
LICENSE
|
16
|
-
README.md
|
17
|
-
Rakefile
|
18
|
-
ext/win32/extconf.rb
|
19
|
-
ext/win32/win32-symlink.cpp
|
20
|
-
lib/win32/symlink.rb
|
21
|
-
lib/win32/
|
22
|
-
lib/win32/symlink/
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
gem.
|
29
|
-
gem.
|
30
|
-
gem.
|
31
|
-
gem.
|
32
|
-
gem.
|
33
|
-
gem.
|
34
|
-
gem.add_development_dependency '
|
35
|
-
|
1
|
+
# -*- ruby -*-
|
2
|
+
require File.expand_path('../lib/win32/symlink/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ['Boris Kropivnitsky']
|
6
|
+
gem.email = ['boris.kro@gmail.com']
|
7
|
+
gem.description = <<DESC
|
8
|
+
Symlink functions for Windows (Vista and above)
|
9
|
+
DESC
|
10
|
+
gem.summary = 'symlink functions for Windows'
|
11
|
+
gem.homepage = 'https://github.com/boriskro/win32-symlink'
|
12
|
+
|
13
|
+
gem.files = %w{
|
14
|
+
Gemfile
|
15
|
+
LICENSE
|
16
|
+
README.md
|
17
|
+
Rakefile
|
18
|
+
ext/win32/extconf.rb
|
19
|
+
ext/win32/win32-symlink.cpp
|
20
|
+
lib/win32/symlink.rb
|
21
|
+
lib/win32/filesymlink.rb
|
22
|
+
lib/win32/symlink/version.rb
|
23
|
+
lib/win32/symlink/win32_symlink.so
|
24
|
+
tasks/ext.rake
|
25
|
+
test/test_win32_symlink.rb
|
26
|
+
win32-symlink.gemspec
|
27
|
+
}
|
28
|
+
gem.extensions = ['ext/win32/extconf.rb']
|
29
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
30
|
+
gem.name = 'win32-symlink'
|
31
|
+
gem.require_paths = ['lib']
|
32
|
+
gem.version = Win32::Symlink::VERSION
|
33
|
+
gem.licenses = ['MIT']
|
34
|
+
gem.add_development_dependency 'bundler'
|
35
|
+
gem.add_development_dependency 'rake-compiler'
|
36
|
+
end
|
metadata
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: win32-symlink
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boris Kropivnitsky
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake-compiler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
description: |
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- Rakefile
|
54
54
|
- ext/win32/extconf.rb
|
55
55
|
- ext/win32/win32-symlink.cpp
|
56
|
+
- lib/win32/filesymlink.rb
|
56
57
|
- lib/win32/symlink.rb
|
57
58
|
- lib/win32/symlink/version.rb
|
58
59
|
- lib/win32/symlink/win32_symlink.so
|
@@ -69,17 +70,17 @@ require_paths:
|
|
69
70
|
- lib
|
70
71
|
required_ruby_version: !ruby/object:Gem::Requirement
|
71
72
|
requirements:
|
72
|
-
- -
|
73
|
+
- - ">="
|
73
74
|
- !ruby/object:Gem::Version
|
74
75
|
version: '0'
|
75
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
77
|
requirements:
|
77
|
-
- -
|
78
|
+
- - ">="
|
78
79
|
- !ruby/object:Gem::Version
|
79
80
|
version: '0'
|
80
81
|
requirements: []
|
81
82
|
rubyforge_project:
|
82
|
-
rubygems_version: 2.
|
83
|
+
rubygems_version: 2.4.4
|
83
84
|
signing_key:
|
84
85
|
specification_version: 4
|
85
86
|
summary: symlink functions for Windows
|