pdk 0.1.0 → 0.2.0

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/README.md +3 -9
  4. data/exe/pdk +1 -1
  5. data/lib/pdk.rb +5 -4
  6. data/lib/pdk/cli.rb +62 -59
  7. data/lib/pdk/cli/errors.rb +1 -1
  8. data/lib/pdk/cli/exec.rb +154 -29
  9. data/lib/pdk/cli/input.rb +2 -2
  10. data/lib/pdk/cli/new.rb +12 -27
  11. data/lib/pdk/cli/new/class.rb +28 -41
  12. data/lib/pdk/cli/new/module.rb +30 -41
  13. data/lib/pdk/cli/test.rb +9 -20
  14. data/lib/pdk/cli/test/unit.rb +38 -0
  15. data/lib/pdk/cli/util/option_normalizer.rb +45 -19
  16. data/lib/pdk/cli/util/option_validator.rb +24 -20
  17. data/lib/pdk/cli/validate.rb +65 -65
  18. data/lib/pdk/generate.rb +5 -0
  19. data/lib/pdk/generators/module.rb +37 -33
  20. data/lib/pdk/generators/puppet_class.rb +1 -1
  21. data/lib/pdk/generators/puppet_object.rb +19 -20
  22. data/lib/pdk/logger.rb +1 -1
  23. data/lib/pdk/module/metadata.rb +35 -18
  24. data/lib/pdk/module/templatedir.rb +40 -33
  25. data/lib/pdk/report.rb +76 -19
  26. data/lib/pdk/report/event.rb +276 -0
  27. data/lib/pdk/template_file.rb +8 -6
  28. data/lib/pdk/tests/unit.rb +8 -3
  29. data/lib/pdk/util.rb +65 -0
  30. data/lib/pdk/util/bundler.rb +167 -0
  31. data/lib/pdk/util/version.rb +34 -0
  32. data/lib/pdk/validate.rb +3 -4
  33. data/lib/pdk/validators/base_validator.rb +60 -4
  34. data/lib/pdk/validators/metadata.rb +29 -0
  35. data/lib/pdk/validators/puppet/puppet_lint.rb +47 -0
  36. data/lib/pdk/validators/puppet/puppet_parser.rb +34 -0
  37. data/lib/pdk/validators/puppet_validator.rb +30 -0
  38. data/lib/pdk/validators/ruby/rubocop.rb +59 -0
  39. data/lib/pdk/validators/ruby_validator.rb +29 -0
  40. data/lib/pdk/version.rb +1 -1
  41. data/lib/puppet/util/windows.rb +14 -0
  42. data/lib/puppet/util/windows/api_types.rb +278 -0
  43. data/lib/puppet/util/windows/file.rb +488 -0
  44. data/lib/puppet/util/windows/string.rb +16 -0
  45. data/locales/de/pdk.po +263 -78
  46. data/locales/pdk.pot +224 -65
  47. metadata +60 -8
  48. data/lib/pdk/cli/tests/unit.rb +0 -52
  49. data/lib/pdk/validators/puppet_lint.rb +0 -17
  50. data/lib/pdk/validators/puppet_parser.rb +0 -17
  51. data/lib/pdk/validators/ruby_lint.rb +0 -17
@@ -0,0 +1,488 @@
1
+ require 'puppet/util/windows'
2
+
3
+ module Puppet::Util::Windows::File
4
+ require 'ffi'
5
+ extend FFI::Library
6
+ extend Puppet::Util::Windows::String
7
+
8
+ FILE_ATTRIBUTE_READONLY = 0x00000001
9
+
10
+ SYNCHRONIZE = 0x100000
11
+ STANDARD_RIGHTS_REQUIRED = 0xf0000
12
+ STANDARD_RIGHTS_READ = 0x20000
13
+ STANDARD_RIGHTS_WRITE = 0x20000
14
+ STANDARD_RIGHTS_EXECUTE = 0x20000
15
+ STANDARD_RIGHTS_ALL = 0x1F0000
16
+ SPECIFIC_RIGHTS_ALL = 0xFFFF
17
+
18
+ FILE_READ_DATA = 1
19
+ FILE_WRITE_DATA = 2
20
+ FILE_APPEND_DATA = 4
21
+ FILE_READ_EA = 8
22
+ FILE_WRITE_EA = 16
23
+ FILE_EXECUTE = 32
24
+ FILE_DELETE_CHILD = 64
25
+ FILE_READ_ATTRIBUTES = 128
26
+ FILE_WRITE_ATTRIBUTES = 256
27
+
28
+ FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF
29
+
30
+ FILE_GENERIC_READ =
31
+ STANDARD_RIGHTS_READ |
32
+ FILE_READ_DATA |
33
+ FILE_READ_ATTRIBUTES |
34
+ FILE_READ_EA |
35
+ SYNCHRONIZE
36
+
37
+ FILE_GENERIC_WRITE =
38
+ STANDARD_RIGHTS_WRITE |
39
+ FILE_WRITE_DATA |
40
+ FILE_WRITE_ATTRIBUTES |
41
+ FILE_WRITE_EA |
42
+ FILE_APPEND_DATA |
43
+ SYNCHRONIZE
44
+
45
+ FILE_GENERIC_EXECUTE =
46
+ STANDARD_RIGHTS_EXECUTE |
47
+ FILE_READ_ATTRIBUTES |
48
+ FILE_EXECUTE |
49
+ SYNCHRONIZE
50
+
51
+ REPLACEFILE_WRITE_THROUGH = 0x1
52
+ REPLACEFILE_IGNORE_MERGE_ERRORS = 0x2
53
+ REPLACEFILE_IGNORE_ACL_ERRORS = 0x3
54
+
55
+ def replace_file(target, source)
56
+ target_encoded = wide_string(target.to_s)
57
+ source_encoded = wide_string(source.to_s)
58
+
59
+ flags = REPLACEFILE_IGNORE_MERGE_ERRORS
60
+ backup_file = nil
61
+ result = ReplaceFileW(
62
+ target_encoded,
63
+ source_encoded,
64
+ backup_file,
65
+ flags,
66
+ FFI::Pointer::NULL,
67
+ FFI::Pointer::NULL
68
+ )
69
+
70
+ return true if result != FFI::WIN32_FALSE
71
+ raise Puppet::Util::Windows::Error.new("ReplaceFile(#{target}, #{source})")
72
+ end
73
+ module_function :replace_file
74
+
75
+ def move_file_ex(source, target, flags = 0)
76
+ result = MoveFileExW(wide_string(source.to_s),
77
+ wide_string(target.to_s),
78
+ flags)
79
+
80
+ return true if result != FFI::WIN32_FALSE
81
+ raise Puppet::Util::Windows::Error.
82
+ new("MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})")
83
+ end
84
+ module_function :move_file_ex
85
+
86
+ def symlink(target, symlink)
87
+ flags = File.directory?(target) ? 0x1 : 0x0
88
+ result = CreateSymbolicLinkW(wide_string(symlink.to_s),
89
+ wide_string(target.to_s), flags)
90
+ return true if result != FFI::WIN32_FALSE
91
+ raise Puppet::Util::Windows::Error.new(
92
+ "CreateSymbolicLink(#{symlink}, #{target}, #{flags.to_s(8)})")
93
+ end
94
+ module_function :symlink
95
+
96
+
97
+ def exist?(path)
98
+ path = path.to_str if path.respond_to?(:to_str) # support WatchedFile
99
+ path = path.to_s # support String and Pathname
100
+
101
+ seen_paths = []
102
+ # follow up to 64 symlinks before giving up
103
+ 0.upto(64) do |depth|
104
+ # return false if this path has been seen before. This is protection against circular symlinks
105
+ return false if seen_paths.include?(path.downcase)
106
+
107
+ result = get_attributes(path,false)
108
+
109
+ # return false for path not found
110
+ return false if result == INVALID_FILE_ATTRIBUTES
111
+
112
+ # return true if path exists and it's not a symlink
113
+ # Other file attributes are ignored. https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx
114
+ return true if (result & FILE_ATTRIBUTE_REPARSE_POINT) != FILE_ATTRIBUTE_REPARSE_POINT
115
+
116
+ # walk the symlink and try again...
117
+ seen_paths << path.downcase
118
+ path = readlink(path)
119
+ end
120
+
121
+ false
122
+ end
123
+ module_function :exist?
124
+
125
+
126
+ INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF #define INVALID_FILE_ATTRIBUTES (DWORD (-1))
127
+
128
+ def get_attributes(file_name, raise_on_invalid = true)
129
+ result = GetFileAttributesW(wide_string(file_name.to_s))
130
+ if raise_on_invalid && result == INVALID_FILE_ATTRIBUTES
131
+ raise Puppet::Util::Windows::Error.new("GetFileAttributes(#{file_name})")
132
+ end
133
+
134
+ result
135
+ end
136
+ module_function :get_attributes
137
+
138
+ def add_attributes(path, flags)
139
+ oldattrs = get_attributes(path)
140
+
141
+ if (oldattrs | flags) != oldattrs
142
+ set_attributes(path, oldattrs | flags)
143
+ end
144
+ end
145
+ module_function :add_attributes
146
+
147
+ def remove_attributes(path, flags)
148
+ oldattrs = get_attributes(path)
149
+
150
+ if (oldattrs & ~flags) != oldattrs
151
+ set_attributes(path, oldattrs & ~flags)
152
+ end
153
+ end
154
+ module_function :remove_attributes
155
+
156
+ def set_attributes(path, flags)
157
+ success = SetFileAttributesW(wide_string(path), flags) != FFI::WIN32_FALSE
158
+ raise Puppet::Util::Windows::Error.new(_("Failed to set file attributes")) if !success
159
+
160
+ success
161
+ end
162
+ module_function :set_attributes
163
+
164
+ #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
165
+ INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address
166
+ def self.create_file(file_name, desired_access, share_mode, security_attributes,
167
+ creation_disposition, flags_and_attributes, template_file_handle)
168
+
169
+ result = CreateFileW(wide_string(file_name.to_s),
170
+ desired_access, share_mode, security_attributes, creation_disposition,
171
+ flags_and_attributes, template_file_handle)
172
+
173
+ return result unless result == INVALID_HANDLE_VALUE
174
+ raise Puppet::Util::Windows::Error.new(
175
+ "CreateFile(#{file_name}, #{desired_access.to_s(8)}, #{share_mode.to_s(8)}, " +
176
+ "#{security_attributes}, #{creation_disposition.to_s(8)}, " +
177
+ "#{flags_and_attributes.to_s(8)}, #{template_file_handle})")
178
+ end
179
+
180
+ def self.get_reparse_point_data(handle, &block)
181
+ # must be multiple of 1024, min 10240
182
+ FFI::MemoryPointer.new(REPARSE_DATA_BUFFER.size) do |reparse_data_buffer_ptr|
183
+ device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, reparse_data_buffer_ptr)
184
+ yield REPARSE_DATA_BUFFER.new(reparse_data_buffer_ptr)
185
+ end
186
+
187
+ # underlying struct MemoryPointer has been cleaned up by this point, nothing to return
188
+ nil
189
+ end
190
+
191
+ def self.device_io_control(handle, io_control_code, in_buffer = nil, out_buffer = nil)
192
+ if out_buffer.nil?
193
+ raise Puppet::Util::Windows::Error.new(_("out_buffer is required"))
194
+ end
195
+
196
+ FFI::MemoryPointer.new(:dword, 1) do |bytes_returned_ptr|
197
+ result = DeviceIoControl(
198
+ handle,
199
+ io_control_code,
200
+ in_buffer, in_buffer.nil? ? 0 : in_buffer.size,
201
+ out_buffer, out_buffer.size,
202
+ bytes_returned_ptr,
203
+ nil
204
+ )
205
+
206
+ if result == FFI::WIN32_FALSE
207
+ raise Puppet::Util::Windows::Error.new(
208
+ "DeviceIoControl(#{handle}, #{io_control_code}, " +
209
+ "#{in_buffer}, #{in_buffer ? in_buffer.size : ''}, " +
210
+ "#{out_buffer}, #{out_buffer ? out_buffer.size : ''}")
211
+ end
212
+ end
213
+
214
+ out_buffer
215
+ end
216
+
217
+ FILE_ATTRIBUTE_REPARSE_POINT = 0x400
218
+ def symlink?(file_name)
219
+ attributes = get_attributes(file_name, false)
220
+
221
+ return false if (attributes == INVALID_FILE_ATTRIBUTES)
222
+ (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT
223
+ end
224
+ module_function :symlink?
225
+
226
+ GENERIC_READ = 0x80000000
227
+ GENERIC_WRITE = 0x40000000
228
+ GENERIC_EXECUTE = 0x20000000
229
+ GENERIC_ALL = 0x10000000
230
+ FILE_SHARE_READ = 1
231
+ FILE_SHARE_WRITE = 2
232
+ OPEN_EXISTING = 3
233
+ FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
234
+ FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
235
+
236
+ def self.open_symlink(link_name)
237
+ begin
238
+ yield handle = create_file(
239
+ link_name,
240
+ GENERIC_READ,
241
+ FILE_SHARE_READ,
242
+ nil, # security_attributes
243
+ OPEN_EXISTING,
244
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
245
+ 0) # template_file
246
+ ensure
247
+ FFI::WIN32.CloseHandle(handle) if handle
248
+ end
249
+
250
+ # handle has had CloseHandle called against it, so nothing to return
251
+ nil
252
+ end
253
+
254
+ def readlink(link_name)
255
+ link = nil
256
+ open_symlink(link_name) do |handle|
257
+ link = resolve_symlink(handle)
258
+ end
259
+
260
+ link
261
+ end
262
+ module_function :readlink
263
+
264
+ ERROR_FILE_NOT_FOUND = 2
265
+ ERROR_PATH_NOT_FOUND = 3
266
+
267
+ def get_long_pathname(path)
268
+ converted = ''
269
+ FFI::Pointer.from_string_to_wide_string(path) do |path_ptr|
270
+ # includes terminating NULL
271
+ buffer_size = GetLongPathNameW(path_ptr, FFI::Pointer::NULL, 0)
272
+ FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr|
273
+ if GetLongPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE
274
+ raise Puppet::Util::Windows::Error.new(_("Failed to call GetLongPathName"))
275
+ end
276
+
277
+ converted = converted_ptr.read_wide_string(buffer_size - 1)
278
+ end
279
+ end
280
+
281
+ converted
282
+ end
283
+ module_function :get_long_pathname
284
+
285
+ def get_short_pathname(path)
286
+ converted = ''
287
+ FFI::Pointer.from_string_to_wide_string(path) do |path_ptr|
288
+ # includes terminating NULL
289
+ buffer_size = GetShortPathNameW(path_ptr, FFI::Pointer::NULL, 0)
290
+ FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr|
291
+ if GetShortPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE
292
+ raise Puppet::Util::Windows::Error.new("Failed to call GetShortPathName")
293
+ end
294
+
295
+ converted = converted_ptr.read_wide_string(buffer_size - 1)
296
+ end
297
+ end
298
+
299
+ converted
300
+ end
301
+ module_function :get_short_pathname
302
+
303
+ def stat(file_name)
304
+ file_name = file_name.to_s # accommodate PathName or String
305
+ stat = File.stat(file_name)
306
+ singleton_class = class << stat; self; end
307
+ target_path = file_name
308
+
309
+ if symlink?(file_name)
310
+ target_path = readlink(file_name)
311
+ link_ftype = File.stat(target_path).ftype
312
+
313
+ # sigh, monkey patch instance method for instance, and close over link_ftype
314
+ singleton_class.send(:define_method, :ftype) do
315
+ link_ftype
316
+ end
317
+ end
318
+
319
+ singleton_class.send(:define_method, :mode) do
320
+ Puppet::Util::Windows::Security.get_mode(target_path)
321
+ end
322
+
323
+ stat
324
+ end
325
+ module_function :stat
326
+
327
+ def lstat(file_name)
328
+ file_name = file_name.to_s # accommodate PathName or String
329
+ # monkey'ing around!
330
+ stat = File.lstat(file_name)
331
+
332
+ singleton_class = class << stat; self; end
333
+ singleton_class.send(:define_method, :mode) do
334
+ Puppet::Util::Windows::Security.get_mode(file_name)
335
+ end
336
+
337
+ if symlink?(file_name)
338
+ def stat.ftype
339
+ "link"
340
+ end
341
+ end
342
+ stat
343
+ end
344
+ module_function :lstat
345
+
346
+ private
347
+
348
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx
349
+ FSCTL_GET_REPARSE_POINT = 0x900a8
350
+
351
+ def self.resolve_symlink(handle)
352
+ path = nil
353
+ get_reparse_point_data(handle) do |reparse_data|
354
+ offset = reparse_data[:PrintNameOffset]
355
+ length = reparse_data[:PrintNameLength]
356
+
357
+ ptr = reparse_data.pointer + reparse_data.offset_of(:PathBuffer) + offset
358
+ path = ptr.read_wide_string(length / 2) # length is bytes, need UTF-16 wchars
359
+ end
360
+
361
+ path
362
+ end
363
+
364
+ ffi_convention :stdcall
365
+
366
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365512(v=vs.85).aspx
367
+ # BOOL WINAPI ReplaceFile(
368
+ # _In_ LPCTSTR lpReplacedFileName,
369
+ # _In_ LPCTSTR lpReplacementFileName,
370
+ # _In_opt_ LPCTSTR lpBackupFileName,
371
+ # _In_ DWORD dwReplaceFlags - 0x1 REPLACEFILE_WRITE_THROUGH,
372
+ # 0x2 REPLACEFILE_IGNORE_MERGE_ERRORS,
373
+ # 0x4 REPLACEFILE_IGNORE_ACL_ERRORS
374
+ # _Reserved_ LPVOID lpExclude,
375
+ # _Reserved_ LPVOID lpReserved
376
+ # );
377
+ ffi_lib :kernel32
378
+ attach_function_private :ReplaceFileW,
379
+ [:lpcwstr, :lpcwstr, :lpcwstr, :dword, :lpvoid, :lpvoid], :win32_bool
380
+
381
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx
382
+ # BOOL WINAPI MoveFileEx(
383
+ # _In_ LPCTSTR lpExistingFileName,
384
+ # _In_opt_ LPCTSTR lpNewFileName,
385
+ # _In_ DWORD dwFlags
386
+ # );
387
+ ffi_lib :kernel32
388
+ attach_function_private :MoveFileExW,
389
+ [:lpcwstr, :lpcwstr, :dword], :win32_bool
390
+
391
+ # BOOLEAN WINAPI CreateSymbolicLink(
392
+ # _In_ LPTSTR lpSymlinkFileName, - symbolic link to be created
393
+ # _In_ LPTSTR lpTargetFileName, - name of target for symbolic link
394
+ # _In_ DWORD dwFlags - 0x0 target is a file, 0x1 target is a directory
395
+ # );
396
+ # rescue on Windows < 6.0 so that code doesn't explode
397
+ begin
398
+ ffi_lib :kernel32
399
+ attach_function_private :CreateSymbolicLinkW,
400
+ [:lpwstr, :lpwstr, :dword], :boolean
401
+ rescue LoadError
402
+ end
403
+
404
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364944(v=vs.85).aspx
405
+ # DWORD WINAPI GetFileAttributes(
406
+ # _In_ LPCTSTR lpFileName
407
+ # );
408
+ ffi_lib :kernel32
409
+ attach_function_private :GetFileAttributesW,
410
+ [:lpcwstr], :dword
411
+
412
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365535(v=vs.85).aspx
413
+ # BOOL WINAPI SetFileAttributes(
414
+ # _In_ LPCTSTR lpFileName,
415
+ # _In_ DWORD dwFileAttributes
416
+ # );
417
+ ffi_lib :kernel32
418
+ attach_function_private :SetFileAttributesW,
419
+ [:lpcwstr, :dword], :win32_bool
420
+
421
+ # HANDLE WINAPI CreateFile(
422
+ # _In_ LPCTSTR lpFileName,
423
+ # _In_ DWORD dwDesiredAccess,
424
+ # _In_ DWORD dwShareMode,
425
+ # _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
426
+ # _In_ DWORD dwCreationDisposition,
427
+ # _In_ DWORD dwFlagsAndAttributes,
428
+ # _In_opt_ HANDLE hTemplateFile
429
+ # );
430
+ ffi_lib :kernel32
431
+ attach_function_private :CreateFileW,
432
+ [:lpcwstr, :dword, :dword, :pointer, :dword, :dword, :handle], :handle
433
+
434
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx
435
+ # BOOL WINAPI DeviceIoControl(
436
+ # _In_ HANDLE hDevice,
437
+ # _In_ DWORD dwIoControlCode,
438
+ # _In_opt_ LPVOID lpInBuffer,
439
+ # _In_ DWORD nInBufferSize,
440
+ # _Out_opt_ LPVOID lpOutBuffer,
441
+ # _In_ DWORD nOutBufferSize,
442
+ # _Out_opt_ LPDWORD lpBytesReturned,
443
+ # _Inout_opt_ LPOVERLAPPED lpOverlapped
444
+ # );
445
+ ffi_lib :kernel32
446
+ attach_function_private :DeviceIoControl,
447
+ [:handle, :dword, :lpvoid, :dword, :lpvoid, :dword, :lpdword, :pointer], :win32_bool
448
+
449
+ MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384
450
+
451
+ # REPARSE_DATA_BUFFER
452
+ # https://msdn.microsoft.com/en-us/library/cc232006.aspx
453
+ # https://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
454
+ # struct is always MAXIMUM_REPARSE_DATA_BUFFER_SIZE bytes
455
+ class REPARSE_DATA_BUFFER < FFI::Struct
456
+ layout :ReparseTag, :win32_ulong,
457
+ :ReparseDataLength, :ushort,
458
+ :Reserved, :ushort,
459
+ :SubstituteNameOffset, :ushort,
460
+ :SubstituteNameLength, :ushort,
461
+ :PrintNameOffset, :ushort,
462
+ :PrintNameLength, :ushort,
463
+ :Flags, :win32_ulong,
464
+ # max less above fields dword / uint 4 bytes, ushort 2 bytes
465
+ # technically a WCHAR buffer, but we care about size in bytes here
466
+ :PathBuffer, [:byte, MAXIMUM_REPARSE_DATA_BUFFER_SIZE - 20]
467
+ end
468
+
469
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx
470
+ # DWORD WINAPI GetLongPathName(
471
+ # _In_ LPCTSTR lpszShortPath,
472
+ # _Out_ LPTSTR lpszLongPath,
473
+ # _In_ DWORD cchBuffer
474
+ # );
475
+ ffi_lib :kernel32
476
+ attach_function_private :GetLongPathNameW,
477
+ [:lpcwstr, :lpwstr, :dword], :dword
478
+
479
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
480
+ # DWORD WINAPI GetShortPathName(
481
+ # _In_ LPCTSTR lpszLongPath,
482
+ # _Out_ LPTSTR lpszShortPath,
483
+ # _In_ DWORD cchBuffer
484
+ # );
485
+ ffi_lib :kernel32
486
+ attach_function_private :GetShortPathNameW,
487
+ [:lpcwstr, :lpwstr, :dword], :dword
488
+ end