clutil 2010.127.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ $LOAD_PATH << '..'
2
+
3
+ require 'test/unit'
4
+ require 'string'
5
+
6
+ class TestUtilString < Test::Unit::TestCase
7
+ def test_here_ltrim
8
+ test = <<-TEST
9
+ This is a test of the here_ltrim function.
10
+ It's purpose is to shift this whole paragraph to the left, removing
11
+ the indention.
12
+ TEST
13
+
14
+ expected = <<-EXP
15
+ This is a test of the here_ltrim function.
16
+ It's purpose is to shift this whole paragraph to the left, removing
17
+ the indention.
18
+ EXP
19
+
20
+ assert_equal(expected, here_ltrim(test))
21
+ end
22
+
23
+ def test_here_ltrim_indent
24
+ test = <<-TEST
25
+ This is a test of the here_ltrim function.
26
+ It's purpose is to shift this whole paragraph to the left, removing
27
+ the indention.
28
+ TEST
29
+
30
+ expected = <<-EXP
31
+ This is a test of the here_ltrim function.
32
+ It's purpose is to shift this whole paragraph to the left, removing
33
+ the indention.
34
+ EXP
35
+
36
+ assert_equal(expected, here_ltrim(test, 2))
37
+ end
38
+
39
+ def test_indent
40
+ # NOTE: empty lines are not indented - they are ignored
41
+
42
+ s = "hey\nhey\n\n"
43
+ s = indent(s, 2)
44
+ assert_equal(" hey\n hey\n\n", s)
45
+ s = indent(s, -1)
46
+ assert_equal(" hey\n hey\n\n", s)
47
+ s = indent(s, -3)
48
+ assert_equal("hey\nhey\n\n", s)
49
+ end
50
+ end
@@ -0,0 +1,72 @@
1
+ $LOAD_PATH << '..'
2
+
3
+ require 'test/unit'
4
+ require 'version'
5
+ require 'test'
6
+ require 'cl/util/test'
7
+
8
+ class TestVersion < TempDirTest
9
+ def setup
10
+ super
11
+ @lineBefore = 'test line before'
12
+ @lineAfter = 'test line after'
13
+ end
14
+
15
+ def make_sample_file(v, indent)
16
+ fn = File.join(@tempDir, 'test.rb')
17
+ File.open(fn, 'w+') do |f|
18
+ f.puts ' ' * indent << @lineBefore
19
+ f.puts ' ' * indent << CLabs::Version.header_start
20
+ f.puts ' ' * indent << CLabs::Version.header_end
21
+ f.puts ' ' * indent << @lineAfter
22
+ end
23
+ v.write_version_header(fn)
24
+ fn
25
+ end
26
+
27
+ def do_test(indent=0)
28
+ v = CLabs::Version.new(1,2,'3')
29
+ fn = make_sample_file(v, indent)
30
+ readlines = nil
31
+ File.open(fn, 'r') do |f| readlines = f.readlines end
32
+ readlines.collect! { |ln| ln.rstrip.chomp }
33
+ #puts
34
+ #puts readlines
35
+ assert_equal(' ' * indent << @lineBefore, readlines[0])
36
+ assert_equal(' ' * indent << CLabs::Version.header_start, readlines[1])
37
+ assert_equal(' ' * indent << 'begin', readlines[2])
38
+ assert_equal(' ' * indent << ' ' << "require 'cl/util/version'", readlines[3])
39
+ assert_equal(' ' * indent << ' ' << "VERSION = CLabs::Version.new('1', '2', '3')", readlines[4])
40
+ assert_equal(' ' * indent << 'rescue LoadError', readlines[5])
41
+ assert_equal(' ' * indent << ' ' << "VERSION = '1.2.3'", readlines[6])
42
+ assert_equal(' ' * indent << 'end', readlines[7])
43
+ assert_equal(' ' * indent << CLabs::Version.header_end, readlines[8])
44
+ assert_equal(' ' * indent << @lineAfter, readlines[9])
45
+ end
46
+
47
+ def test_write_version_header_no_indent
48
+ do_test(0)
49
+ end
50
+
51
+ def test_write_version_header_indent
52
+ do_test(2)
53
+ end
54
+
55
+ def test_to_s
56
+ v = CLabs::Version.new(1,2,3)
57
+ assert_equal('1.2.3', v.to_s)
58
+
59
+ v = CLabs::Version.new(1,2,'3')
60
+ assert_equal('1.2.3', v.to_s)
61
+ end
62
+
63
+ def test_comparison
64
+ assert_equal(0, CLabs::Version.new(0,0,0) <=> CLabs::Version.new(0,0,0))
65
+ assert_equal(1, CLabs::Version.new(1,0,0) <=> CLabs::Version.new(0,0,0))
66
+ assert_equal(1, CLabs::Version.new(0,1,0) <=> CLabs::Version.new(0,0,0))
67
+ assert_equal(1, CLabs::Version.new(0,0,1) <=> CLabs::Version.new(0,0,0))
68
+ assert_equal(-1, CLabs::Version.new(0,0,0) <=> CLabs::Version.new(1,0,0))
69
+ assert_equal(-1, CLabs::Version.new(0,0,0) <=> CLabs::Version.new(0,1,0))
70
+ assert_equal(-1, CLabs::Version.new(0,0,0) <=> CLabs::Version.new(0,0,1))
71
+ end
72
+ end
@@ -0,0 +1,106 @@
1
+ # $Id: wintest.rb,v 1.6 2004/10/21 15:57:57 chrismo Exp $
2
+ =begin
3
+ --------------------------------------------------------------------------
4
+ Copyright (c) 2002, Chris Morris
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without modification,
8
+ are permitted provided that the following conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice, this
11
+ list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ this list of conditions and the following disclaimer in the documentation and/or
15
+ other materials provided with the distribution.
16
+
17
+ 3. Neither the names Chris Morris, cLabs nor the names of contributors to this
18
+ software may be used to endorse or promote products derived from this software
19
+ without specific prior written permission.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
22
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
25
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
29
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ --------------------------------------------------------------------------------
32
+ =end
33
+ $LOAD_PATH << '..'
34
+ require 'win'
35
+ require 'test/unit'
36
+
37
+ class TestUtilWin < Test::Unit::TestCase
38
+ def testDeleteFile
39
+ fileName = "clutilwintest.test.safe.to.delete.txt"
40
+ f = File.new(fileName, File::CREAT)
41
+ f.close
42
+ File.chmod(0644, fileName)
43
+ File.delete(fileName)
44
+ assert(Dir[fileName] == [])
45
+
46
+ f = File.new(fileName, File::CREAT)
47
+ f.close
48
+ File.chmod(0444, fileName)
49
+ begin
50
+ File.delete(fileName)
51
+ fail('should have raised exception')
52
+ rescue ClUtilWinErr => e
53
+ # nada, expected exception
54
+ end
55
+ assert(Dir[fileName] != [])
56
+ File.delete_all(fileName)
57
+ assert(Dir[fileName] == [])
58
+ end
59
+
60
+ def test_win_to_rb_path
61
+ assert_equal('c:/temp', File.win_to_rb_path("c:\\temp"))
62
+ assert_equal('c:/temp', File.rbpath("c:\\temp"))
63
+ end
64
+
65
+ def test_rb_to_win_path
66
+ assert_equal("c:\\temp", File.rb_to_win_path("c:/temp"))
67
+ assert_equal("c:\\temp", File.winpath("c:/temp"))
68
+ end
69
+
70
+ def test_special_folders
71
+ # quirky test, cuz the test code is the production code, but can't
72
+ # think of an easy way to have a hardcode result that would work on
73
+ # any machine. I guess I could go into the registry myself and look
74
+ # one of these up - but hey...
75
+
76
+ shell = WIN32OLE.new("WScript.Shell")
77
+ desktop = shell.SpecialFolders(File::DESKTOP)
78
+ assert_equal(desktop, File.special_folders('Desktop'))
79
+ end
80
+
81
+ def test_drives
82
+ # does not fully evaluate output
83
+ # just runs to make sure it doesn't blow up
84
+ Windows.drives.each do |drv| puts drv.name + ' ' + drv.typedesc end
85
+ Windows.drives(Windows::Drives::DRIVE_FIXED).each do |drv|
86
+ puts drv.name + ' ' + drv.typedesc
87
+ end
88
+
89
+ Windows::Drives.drives.each do |drv| puts drv.name + ' ' + drv.typedesc end
90
+ Windows::Drives.drives(Windows::Drives::DRIVE_FIXED).each do |drv|
91
+ puts drv.name + ' ' + drv.typedesc
92
+ end
93
+ end
94
+ end
95
+
96
+ class TestSystemReturnExitCode < Test::Unit::TestCase
97
+ def test_system_return_exit_code
98
+ tmpfn = File.join(File.dirname(__FILE__), '_test.child.rb')
99
+ File.delete tmpfn if File.exists?(tmpfn)
100
+ File.open(tmpfn, 'w+') do |f| f.puts 'exit(2)' end
101
+
102
+ cmd = "ruby.exe -C #{File.dirname(tmpfn)} #{File.basename(tmpfn)}"
103
+ assert_equal(2, system_return_exitcode(cmd))
104
+ File.delete tmpfn if File.exists?(tmpfn)
105
+ end
106
+ end
data/cl/util/time.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'date'
2
+
3
+ class Time
4
+ DAY = (60 * 60 * 24)
5
+
6
+ def days_ago(days)
7
+ self - (DAY * days)
8
+ end
9
+
10
+ def days_ahead(days)
11
+ days_ago(-days)
12
+ end
13
+ end
14
+
15
+ class Date
16
+ def days_ago(days)
17
+ self - days
18
+ end
19
+
20
+ def days_ahead(days)
21
+ days_ago(-days)
22
+ end
23
+
24
+ def years_ago(years)
25
+ self << (12 * years)
26
+ end
27
+
28
+ def years_ahead(years)
29
+ years_ago(-years)
30
+ end
31
+
32
+ def mdy
33
+ "#{month}/#{day}/#{year}"
34
+ end
35
+ end
data/cl/util/util.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'cl/util/version'
2
+
3
+ # VERSION auto generated section
4
+ begin
5
+ require 'cl/util/version'
6
+ VERSION = CLabs::Version.new('2010', '127', '0')
7
+ rescue LoadError
8
+ VERSION = '2010.127.0'
9
+ end
10
+ # END_VERSION
@@ -0,0 +1,115 @@
1
+ # $Id: version.rb,v 1.6 2005/06/02 03:39:31 chrismo Exp $
2
+ =begin
3
+ --------------------------------------------------------------------------
4
+ Copyright (c) 2002-2005, Chris Morris
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without modification,
8
+ are permitted provided that the following conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice, this
11
+ list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ this list of conditions and the following disclaimer in the documentation and/or
15
+ other materials provided with the distribution.
16
+
17
+ 3. Neither the names Chris Morris, cLabs nor the names of contributors to this
18
+ software may be used to endorse or promote products derived from this software
19
+ without specific prior written permission.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
22
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
25
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
29
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ --------------------------------------------------------------------------------
32
+ Contributors: Dan Butler
33
+ =end
34
+ begin
35
+ require 'cl/xmlserial'
36
+ rescue LoadError
37
+ $NO_XMLS = true
38
+ end
39
+
40
+ module CLabs
41
+ class Version
42
+ include Comparable
43
+ include XmlSerialization if !$NO_XMLS
44
+
45
+ attr_accessor :major, :minor, :release
46
+
47
+ def initialize(maj=0, min=0, rel=0)
48
+ @major = maj
49
+ @minor = min
50
+ @release = rel
51
+ end
52
+
53
+ def <=>(other_ver)
54
+ res = (@major <=> other_ver.major)
55
+ res = (@minor <=> other_ver.minor) if res == 0
56
+ res = (@release <=> other_ver.release) if res == 0
57
+ res
58
+ end
59
+
60
+ def Version.header_start
61
+ '# VERSION auto generated section'
62
+ end
63
+
64
+ def Version.header_end
65
+ '# END_VERSION'
66
+ end
67
+
68
+ def version_header_code(indent=0)
69
+ ' ' * indent + Version.header_start + "\n" +
70
+ ' ' * indent + "begin \n" +
71
+ ' ' * indent + " require 'cl/util/version' \n" +
72
+ ' ' * indent + " VERSION = CLabs::Version.new('#{@major}', '#{@minor}', '#{@release}')\n" +
73
+ ' ' * indent + "rescue LoadError \n" +
74
+ ' ' * indent + " VERSION = '#{to_s}' \n" +
75
+ ' ' * indent + "end \n" +
76
+ ' ' * indent + Version.header_end + "\n"
77
+ end
78
+
79
+ def write_version_header(fn)
80
+ newfn = fn + '.new'
81
+ File.open(fn, 'r') do |fin|
82
+ File.open(newfn, 'w+') do |fout|
83
+ lines = fin.readlines
84
+ begindex = nil; endindex = nil; indent = 0
85
+ lines.each do |ln|
86
+ if ln =~ /^\s*#{Version.header_start}/
87
+ begindex = lines.index(ln)
88
+ indent = ln.scan(/^\s*/).to_s.length
89
+ end
90
+ endindex = lines.index(ln) if ln =~ /^\s*#{Version.header_end}/
91
+ end
92
+ if begindex && endindex
93
+ lines = lines[0..begindex-1] <<
94
+ version_header_code(indent) <<
95
+ lines[endindex+1..-1]
96
+ else
97
+ raise 'VERSION section not found in ' + fn
98
+ end
99
+ fout.puts lines
100
+ end
101
+ end
102
+ File.delete(fn)
103
+ File.rename(newfn, fn)
104
+ end
105
+
106
+ def to_s
107
+ @major.to_s + '.' + @minor.to_s + '.' + @release.to_s
108
+ end
109
+ end
110
+ end
111
+
112
+ if __FILE__ == $0
113
+ v = CLabs::Version.new
114
+ puts v.version_header_code
115
+ end
data/cl/util/win.rb ADDED
@@ -0,0 +1,351 @@
1
+ # $Id: win.rb,v 1.17 2004/10/21 17:00:57 chrismo Exp $
2
+ =begin
3
+ --------------------------------------------------------------------------
4
+ Copyright (c) 2001-2002, Chris Morris
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without modification,
8
+ are permitted provided that the following conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice, this
11
+ list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ this list of conditions and the following disclaimer in the documentation and/or
15
+ other materials provided with the distribution.
16
+
17
+ 3. Neither the names Chris Morris, cLabs nor the names of contributors to this
18
+ software may be used to endorse or promote products derived from this software
19
+ without specific prior written permission.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
22
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
25
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
29
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ --------------------------------------------------------------------------------
32
+ =end
33
+ require 'Win32API'
34
+ require 'win32ole'
35
+ require 'cl/util/file'
36
+
37
+ # MENON Jean-Francois [Jean-Francois.MENON@meteo.fr]
38
+ # http://ruby-talk.com/41583
39
+ # But I see two potential problems that make not consistent with the
40
+ # original ruby command:
41
+ # 1) it takes only one argument
42
+
43
+ # code by Hee-Sob Park - posted here:
44
+ # http://ruby-talk.com/10006
45
+ def system(command)
46
+ # 2) it always set "$?" variable to false - should be working now...
47
+
48
+ # not allowed to set $?
49
+ # $? = Win32API.new("crtdll", "system", ['P'], 'L').Call(command)
50
+ # $? == 0
51
+
52
+ # http://msdn.microsoft.com/library/en-us/vccore98/html/_crt_system.2c_._wsystem.asp
53
+ Win32API.new("crtdll", "system", ['P'], 'L').Call(command)
54
+ end
55
+
56
+ # the system cmd in MSVC built ruby does not set $?, so no access to
57
+ # the exit code can be had. The following works, but is obtuse enough
58
+ # for me to encapsulate in this method.
59
+ # Bit slightly modified from - nobu http://ruby-talk.com/76086
60
+ def system_return_exitcode(cmd)
61
+ exitcode = nil
62
+ IO.popen(cmd) { |f|
63
+ # must bit shift by 8 to get the error code
64
+ exitcode = Process.waitpid2(f.pid)[1] >> 8
65
+ }
66
+ return exitcode
67
+ end
68
+
69
+ # MENON Jean-Francois [Jean-Francois.MENON@meteo.fr]
70
+ # http://ruby-talk.com/41583
71
+ # But I see two potential problems that make not consistent with the
72
+ # original ruby command:
73
+ # 1) it always set "$?" variable to false
74
+
75
+ # code by Hee-Sob Park - posted here:
76
+ # http://ruby-talk.com/10006
77
+ def `(command)
78
+ # http://msdn.microsoft.com/library/en-us/vccore98/HTML/_crt__popen.2c_._wpopen.asp
79
+ popen = Win32API.new("crtdll", "_popen", ['P','P'], 'L')
80
+ pclose = Win32API.new("crtdll", "_pclose", ['L'], 'L')
81
+ fread = Win32API.new("crtdll", "fread", ['P','L','L','L'], 'L')
82
+ feof = Win32API.new("crtdll", "feof", ['L'], 'L')
83
+ saved_stdout = $stdout.clone
84
+ psBuffer = " " * 128
85
+ rBuffer = ""
86
+ f = popen.Call(command,"r")
87
+ while feof.Call( f )==0
88
+ l = fread.Call( psBuffer,1,128,f )
89
+ # fix emailed direct to me from Park from 10006 code
90
+ # was:
91
+ # rBuffer += psBuffer[0..l]
92
+ rBuffer += psBuffer[0...l]
93
+ end
94
+ pclose.Call f
95
+ $stdout.reopen(saved_stdout)
96
+ rBuffer
97
+ end
98
+
99
+ P_WAIT = 0
100
+ P_NOWAIT = 1
101
+ OLD_P_OVERLAY = 2
102
+ P_NOWAITO = 3
103
+ P_DETACH = 4
104
+
105
+ def async_system(command)
106
+ # http://msdn.microsoft.com/library/en-us/vccore98/html/_crt__spawnv.2c_._wspawnv.asp
107
+ # this is working -- but frequently Segfaults ruby 1.6.6 mswin32
108
+
109
+ spawn = Win32API.new("crtdll", "_spawnvp", ['I', 'P', 'P'] ,'L')
110
+ res = spawn.Call(P_NOWAIT, command, '')
111
+ if res == -1
112
+ Win32API.new("crtdll", "perror", ['P'], 'V').call('async_system')
113
+ end
114
+ res
115
+ end
116
+
117
+ # from WinBase.h in SDK
118
+ # dwCreationFlag values
119
+ NORMAL_PRIORITY_CLASS = 0x00000020
120
+
121
+ STARTUP_INFO_SIZE = 68
122
+ PROCESS_INFO_SIZE = 16
123
+
124
+ def create_process(command)
125
+ # from WinBase.h in SDK
126
+ # Passing nil for a pointer -- I've seen it work with a P type param and
127
+ # an empty string ... here L with a 0 is the only way that works with
128
+ # CreateProcess. (see FormatMessage in raise_last_win32_error for an
129
+ # example of 'P' and '' that works)
130
+ params = [
131
+ 'L', # IN LPCSTR lpApplicationName
132
+ 'P', # IN LPSTR lpCommandLine
133
+ 'L', # IN LPSECURITY_ATTRIBUTES lpProcessAttributes
134
+ 'L', # IN LPSECURITY_ATTRIBUTES lpThreadAttributes
135
+ 'L', # IN BOOL bInheritHandles
136
+ 'L', # IN DWORD dwCreationFlags
137
+ 'L', # IN LPVOID lpEnvironment
138
+ 'L', # IN LPCSTR lpCurrentDirectory
139
+ 'P', # IN LPSTARTUPINFOA lpStartupInfo
140
+ 'P' # OUT LPPROCESS_INFORMATION lpProcessInformation
141
+ ]
142
+ returnValue = 'I' # BOOL
143
+
144
+ startupInfo = [STARTUP_INFO_SIZE].pack('I') + ([0].pack('I') * (STARTUP_INFO_SIZE - 4))
145
+ processInfo = [0].pack('I') * PROCESS_INFO_SIZE
146
+ createProcess = Win32API.new("kernel32", "CreateProcess", params, returnValue)
147
+ if createProcess.call(0, command, 0, 0, 0, NORMAL_PRIORITY_CLASS, 0, 0,
148
+ startupInfo, processInfo) == 0
149
+ raise_last_win_32_error
150
+ end
151
+ processInfo
152
+ end
153
+
154
+ ERROR_SUCCESS = 0x00
155
+ FORMAT_MESSAGE_FROM_SYSTEM = 0x1000
156
+ FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x2000
157
+
158
+ def raise_last_win_32_error
159
+ errorCode = Win32API.new("kernel32", "GetLastError", [], 'L').call
160
+ if errorCode != ERROR_SUCCESS
161
+ params = [
162
+ 'L', # IN DWORD dwFlags,
163
+ 'P', # IN LPCVOID lpSource,
164
+ 'L', # IN DWORD dwMessageId,
165
+ 'L', # IN DWORD dwLanguageId,
166
+ 'P', # OUT LPSTR lpBuffer,
167
+ 'L', # IN DWORD nSize,
168
+ 'P', # IN va_list *Arguments
169
+ ]
170
+
171
+ formatMessage = Win32API.new("kernel32", "FormatMessage", params, 'L')
172
+ msg = ' ' * 255
173
+ msgLength = formatMessage.call(FORMAT_MESSAGE_FROM_SYSTEM +
174
+ FORMAT_MESSAGE_ARGUMENT_ARRAY, '', errorCode, 0, msg, 255, '')
175
+ msg.gsub!("\\000", '')
176
+ msg.strip!
177
+ raise msg
178
+ else
179
+ raise 'GetLastError returned ERROR_SUCCESS'
180
+ end
181
+ end
182
+
183
+ class ClUtilWinErr < Exception
184
+ end
185
+
186
+ class << File
187
+ alias o_delete delete
188
+
189
+ def win_api_delete(filename)
190
+ # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/filesio_5n8l.asp
191
+ # BOOL DeleteFile(
192
+ # LPCTSTR lpFileName // file name
193
+ # );
194
+
195
+ # delete file if not read only
196
+ fdelete = Win32API.new("kernel32", "DeleteFile", ["P"], "I")
197
+ fdelete.Call(filename) != 0
198
+ end
199
+
200
+ # def recycle
201
+ # function SHFileOperation(const lpFileOp: TSHFileOpStruct): Integer; stdcall;
202
+ # TSHFileOpStruct = packed record
203
+ # Wnd: HWND; // HWND = type LongWord; Longword 0..4294967295 unsigned 32-bit
204
+ # wFunc: UINT; // UINT = LongWord;
205
+ # pFrom: PWideChar; // pointer to unicode string
206
+ # pTo: PWideChar;
207
+ # fFlags: FILEOP_FLAGS; // FILEOP_FLAGS = Word; Word 0..65535 unsigned 16-bit
208
+ # fAnyOperationsAborted: BOOL; // BOOL = LongBool; a LongBool variable occupies four bytes (two words).
209
+ # hNameMappings: Pointer;
210
+ # lpszProgressTitle: PWideChar; { only used if FOF_SIMPLEPROGRESS }
211
+ # end;
212
+
213
+ # http://msdn.microsoft.com/en-us/shellcc/platform/shell/reference/structures/shfileopstruct.asp
214
+ # see FOF_ALLOWUNDO, specifically
215
+ # end
216
+
217
+ def win_file_ro?(filename)
218
+ fFILE_ATTRIBUTE_READONLY = 0x1
219
+ # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/filesio_9pgz.asp
220
+ # DWORD GetFileAttributes(
221
+ # LPCTSTR lpFileName // name of file or directory
222
+ # );
223
+ fgetattr = Win32API.new("kernel32", "GetFileAttributes", ["P"], "N")
224
+ fattr = fgetattr.Call(filename)
225
+ (fattr & fFILE_ATTRIBUTE_READONLY) != 0
226
+ end
227
+
228
+ def delete(*files)
229
+ files.flatten!
230
+ files.each do |file|
231
+ if !win_file_ro?(file)
232
+ win_api_delete(file)
233
+ else
234
+ raise ClUtilWinErr.new(file + ' is read only, cannot delete. Use File.delete_all')
235
+ end
236
+ end
237
+ end
238
+
239
+ # I wanted to add a second param to delete, called deleteReadOnly with
240
+ # a default value of false but (a) I can't have a default value param
241
+ # after the *files param because (b) I can't have any non *param after
242
+ # the *files param. So, my compromise was a separate method
243
+
244
+ # I have no idea why I'm doing an api specific call here.
245
+ # I think when I started this journey, I thought the API
246
+ # call would delete read-only files, then when I learned
247
+ # it wouldn't, I forgot my original purpose and just
248
+ # worked around it. It's silly now -- I've moved delete_all
249
+ # to cl/util/file.rb which is a pure Ruby implementation
250
+ # of this one.
251
+
252
+ #def delete_all(*files)
253
+ # files.flatten!
254
+ # files.each do |file|
255
+ # # make writable to allow deletion
256
+ # File.chmod(0644, file)
257
+ # win_api_delete(file)
258
+ # end
259
+ #end
260
+
261
+ def create_shortcut(targetFileName, linkName)
262
+ shell = WIN32OLE.new("WScript.Shell")
263
+ scut = shell.CreateShortcut(linkName + '.lnk')
264
+ scut.TargetPath = File.expand_path(targetFileName)
265
+ scut.Save
266
+ scut
267
+ end
268
+
269
+ def win_to_rb_path(winpath)
270
+ winpath.gsub(/\\/, '/')
271
+ end
272
+
273
+ def rb_to_win_path(rbpath)
274
+ rbpath.gsub('/', "\\")
275
+ end
276
+
277
+ alias rbpath win_to_rb_path
278
+ alias winpath rb_to_win_path
279
+
280
+ def special_folders(folderName)
281
+ shell = WIN32OLE.new("WScript.Shell")
282
+ shell.SpecialFolders(folderName)
283
+ end
284
+ end
285
+
286
+ module Windows
287
+ def Windows.drives(typeFilter=nil)
288
+ Drives::drives(typeFilter)
289
+ end
290
+
291
+ module Drives
292
+ GetDriveType = Win32API.new("kernel32", "GetDriveTypeA", ['P'], 'L')
293
+ GetLogicalDriveStrings = Win32API.new("kernel32", "GetLogicalDriveStrings", ['L', 'P'], 'L')
294
+
295
+ DRIVE_UNKNOWN = 0 # The drive type cannot be determined.
296
+ DRIVE_NO_ROOT_DIR = 1 # The root path is invalid. For example, no volume is mounted at the path.
297
+ DRIVE_REMOVABLE = 2 # The disk can be removed from the drive.
298
+ DRIVE_FIXED = 3 # The disk cannot be removed from the drive.
299
+ DRIVE_REMOTE = 4 # The drive is a remote (network) drive.
300
+ DRIVE_CDROM = 5 # The drive is a CD-ROM drive.
301
+ DRIVE_RAMDISK = 6 # The drive is a RAM disk.
302
+ DriveTypes = {
303
+ DRIVE_UNKNOWN => 'Unknown',
304
+ DRIVE_NO_ROOT_DIR => 'Invalid',
305
+ DRIVE_REMOVABLE => 'Removable/Floppy',
306
+ DRIVE_FIXED => 'Fixed',
307
+ DRIVE_REMOTE => 'Network',
308
+ DRIVE_CDROM => 'CD',
309
+ DRIVE_RAMDISK => 'RAM'
310
+ }
311
+
312
+ Drive = Struct.new('Drive', :name, :type, :typedesc)
313
+
314
+ def Drives.drives(typeFilter=nil)
315
+ driveNames = ' ' * 255
316
+ GetLogicalDriveStrings.Call(255, driveNames)
317
+ driveNames.strip!
318
+ driveNames = driveNames.split("\000")
319
+ drivesAry = []
320
+ driveNames.each do |drv|
321
+ type = GetDriveType.Call(drv)
322
+ if (!typeFilter) || (type == typeFilter)
323
+ drive = Drive.new(drv, type, DriveTypes[type])
324
+ drivesAry << drive
325
+ end
326
+ end
327
+ drivesAry
328
+ end
329
+ end
330
+ end
331
+
332
+ class File
333
+ # from WSH 5.6 docs
334
+ ALLUSERSDESKTOP = "AllUsersDesktop"
335
+ ALLUSERSSTARTMENU = "AllUsersStartMenu"
336
+ ALLUSERSPROGRAMS = "AllUsersPrograms"
337
+ ALLUSERSSTARTUP = "AllUsersStartup"
338
+ DESKTOP = "Desktop"
339
+ FAVORITES = "Favorites"
340
+ FONTS = "Fonts"
341
+ MYDOCUMENTS = "MyDocuments"
342
+ NETHOOD = "NetHood"
343
+ PRINTHOOD = "PrintHood"
344
+ PROGRAMS = "Programs"
345
+ RECENT = "Recent"
346
+ SENDTO = "SendTo"
347
+ STARTMENU = "StartMenu"
348
+ STARTUP = "Startup"
349
+ TEMPLATES = "Templates"
350
+ end
351
+