file_mode 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e7111de3e1b1269c364ec2b68c7337f75686cc17
4
+ data.tar.gz: 2287af1a240cce77f7b370a4987271259f72e057
5
+ SHA512:
6
+ metadata.gz: 6516cac2615f49fb57edd9964a5b9cc1cf128e3a1bebcfc98dafb76feb14343b7351e1bb786302d92cb41b25de39ad4a9ba60f871810c6cdd77f5d7b537f7a15
7
+ data.tar.gz: f593a5c60f92342ed114aacd8edda386f9d60231bf237cb5cc946b80a9b4b71d6e93df91e3b2db8112af59e17d80a3dfd581eedebaf70d16caf27f2fe1dc8b76
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright 2014 Martin Carpenter. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are
4
+ permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of
7
+ conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
10
+ of conditions and the following disclaimer in the documentation and/or other materials
11
+ provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY Martin Carpenter ``AS IS'' AND ANY EXPRESS OR IMPLIED
14
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
15
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Martin Carpenter OR
16
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
20
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
21
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
+
23
+ The views and conclusions contained in the software and documentation are those of the
24
+ authors and should not be interpreted as representing official policies, either expressed
25
+ or implied, of Martin Carpenter.
26
+
data/README.rdoc ADDED
@@ -0,0 +1,87 @@
1
+
2
+ = file_mode
3
+
4
+ Author:: Martin Carpenter
5
+ Email:: mcarpenter@free.fr
6
+ Copyright:: Copyright (c) Martin Carpenter 2014
7
+
8
+ == About
9
+
10
+ The file_mode gem assists with manipulation of four-octal-digit UNIX
11
+ file modes by providing readable method names rather than error-prone
12
+ bit twiddles. It also provides methods to convert to and from standard
13
+ ls(1)-style long listing formats. The class methods can be used standalone
14
+ or this module can be mixed in to a class that has an integer :mode
15
+ attribute.
16
+
17
+ == Examples
18
+
19
+ === Class method use
20
+
21
+ require 'file_mode'
22
+
23
+ FileMode.user_readable?(0o644)
24
+ => true
25
+ FileMode.setuid?(0o4755)
26
+ => true
27
+ FileMode.int_to_listing(0o644)
28
+ => "rw-r--r--"
29
+ FileMode.listing_to_str('rwsr-xr-x')
30
+ => "4755"
31
+
32
+ === Mixin use
33
+
34
+ require 'file_mode'
35
+
36
+ class FileLikeThing
37
+ include FileMode
38
+ attr_accessor :mode
39
+ end
40
+
41
+ flt = FileLikeThing.new
42
+ flt.mode = 0o0644
43
+ flt.user_readable?
44
+ => true
45
+ flt.setuid?
46
+ => false
47
+
48
+ == Mode formats
49
+
50
+ The module offers three forms of mode representation:
51
+
52
+ * integer (eg 0o0644 or 420 decimal)
53
+ * string ('0644')
54
+ * listing ('rw-r--r--')
55
+
56
+ Listing formats are not necessarily identical across UNIX-like operating
57
+ systems, or even across applications on the same operating system. A
58
+ case in point is Solaris which shows mandatory file lock permission as
59
+ 'l' with standard ls(1) and 'L' with XPG-compatible ls(1).
60
+
61
+ This module aims to take any standard format as input
62
+ (listing_to_... methods) and targets Linux/GNU coreutils for
63
+ output(..._to_listing methods).
64
+
65
+ === Some conversion gotchas
66
+
67
+ Beware the following standard ruby gotchas when making conversions using
68
+ #to_i and a solitary leading zero to specify octal mode:
69
+
70
+ 644.to_i
71
+ => 644 # ok
72
+ '644'.to_i
73
+ => 644 # ok
74
+ 0644.to_i
75
+ => 420 # ok
76
+ '0644'.to_i
77
+ => 644 # ! leading zero is ignored
78
+ '0o644'.to_i
79
+ => 0 # ! parsing stops at 'o'
80
+
81
+ == Unit tests
82
+
83
+ Unit tests are provided particularly for the conversion between the
84
+ ls(1)-style long-listing format and integer representations. Since
85
+ the long-listing format can vary between systems a script
86
+ bin/create_listing_test.rb is provided to generate unit test files.
87
+
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'rdoc/task'
5
+ require 'rubygems/package_task'
6
+
7
+ desc 'Default task (package)'
8
+ task :default => [:package]
9
+
10
+ desc 'Create listing unit test'
11
+ task :create_listing_test do
12
+ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
13
+ load File.join('bin', 'create_listing_test.rb')
14
+ end
15
+
16
+ Rake::TestTask.new( 'test' )
17
+
18
+ SPECFILE = 'file_mode.gemspec'
19
+ if File.exist?(SPECFILE)
20
+ spec = eval(File.read(SPECFILE))
21
+ Gem::PackageTask.new(spec).define
22
+ end
23
+
24
+ RDoc::Task.new do |rdoc|
25
+ rdoc.rdoc_dir = 'rdoc'
26
+ rdoc.title = 'file_mode'
27
+ rdoc.options << '--charset' << 'utf-8'
28
+ rdoc.options << '--main' << 'README.rdoc'
29
+ rdoc.options << '--all'
30
+ rdoc.rdoc_files.include('README.rdoc' )
31
+ rdoc.rdoc_files.include(FileList['lib/**/*.rb'])
32
+ rdoc.rdoc_files.include(FileList['test/*.rb' ])
33
+ end
34
+
data/lib/file_mode.rb ADDED
@@ -0,0 +1,150 @@
1
+
2
+ # Module for UNIX octal file modes. Classes that mixin this module
3
+ # must have a mode attribute that is the integer representation
4
+ # of the mode.
5
+ module FileMode
6
+
7
+ # Class methods. These are both extended into the FileMode
8
+ # class and mixed-in as instance methods on classes that
9
+ # "include FileMode" when the class Fixnum attribute :mode
10
+ # is used in place of the argument of the same name.
11
+ module ClassMethods
12
+
13
+ # Return true if the mode is readable by user, false otherwise.
14
+ def user_readable?(mode)
15
+ mode & 00400 == 00400
16
+ end
17
+
18
+ # Return true if the mode is readable by group, false otherwise.
19
+ def group_readable?(mode)
20
+ mode & 00040 == 00040
21
+ end
22
+
23
+ # Return true if the mode is readable by other, false otherwise.
24
+ def other_readable?(mode)
25
+ mode & 00004 == 00004
26
+ end
27
+
28
+ # Return true if the mode is writable by user, false otherwise.
29
+ def user_writable?(mode)
30
+ mode & 00200 == 00200
31
+ end
32
+
33
+ # Return true if the mode is writable by group, false otherwise.
34
+ def group_writable?(mode)
35
+ mode & 00020 == 00020
36
+ end
37
+
38
+ # Return true if the mode is writable by other, false otherwise.
39
+ def other_writable?(mode)
40
+ mode & 00002 == 00002
41
+ end
42
+
43
+ # Return true if the mode is executable by user, false otherwise.
44
+ def user_executable?(mode)
45
+ mode & 00100 == 00100
46
+ end
47
+
48
+ # Return true if the mode is executable by group, false otherwise.
49
+ def group_executable?(mode)
50
+ mode & 00010 == 00010
51
+ end
52
+
53
+ # Return true if the mode is executable by other, false otherwise.
54
+ def other_executable?(mode)
55
+ mode & 00001 == 00001
56
+ end
57
+
58
+ # Return true if mode has the setuid bit set, false otherwise.
59
+ def setuid?(mode)
60
+ mode & 04000 == 04000
61
+ end
62
+
63
+ # Return true if mode has the setgid bit set, false otherwise.
64
+ def setgid?(mode)
65
+ mode & 02000 == 02000
66
+ end
67
+
68
+ # Return true if mode has the sticky bit set, false otherwise.
69
+ def sticky?(mode)
70
+ mode & 01000 == 01000
71
+ end
72
+
73
+ end # ClassMethods
74
+
75
+ extend ClassMethods
76
+
77
+ # Convert an integer mode to a 4 digit octal string.
78
+ def self.int_to_str(int)
79
+ int.to_s(8).rjust(4, '0')
80
+ end
81
+
82
+ # Convert an octal string to an integer.
83
+ def self.str_to_int(str)
84
+ str.to_i(8)
85
+ end
86
+
87
+ # Convert a permissions listing string to its integer equivalent.
88
+ def self.listing_to_int(listing)
89
+ int = listing.chars.map do |c|
90
+ # l/L are for mandatory file locking on Solaris.
91
+ %w{ - S T l L }.include?(c) ? 0 : 1
92
+ end.join.to_i(2)
93
+ int += 04000 if %w{ s S }.include?(listing[2]) # setuid
94
+ int += 02000 if %w{ s S l L }.include?(listing[5]) # setgid
95
+ int += 01000 if %w{ t T }.include?(listing[8]) # sticky
96
+ int
97
+ end
98
+
99
+ # Convert permissions listing string to a string of its octal representation.
100
+ def self.listing_to_str(listing)
101
+ int_to_str(listing_to_int(listing))
102
+ end
103
+
104
+ # Mapping between a single octal digit and its permissions listing.
105
+ OCTAL_DIGIT_TO_LISTING = {
106
+ 0 => '---',
107
+ 1 => '--x',
108
+ 2 => '-w-',
109
+ 3 => '-wx',
110
+ 4 => 'r--',
111
+ 5 => 'r-x',
112
+ 6 => 'rw-',
113
+ 7 => 'rwx'
114
+ }
115
+
116
+ # Convert an integer into its permissions listing string. Listing strings
117
+ # may vary between operating systems (and even between applications on the
118
+ # same operating system). The listing output here matches the ouput from
119
+ # Linux/GNU coreutils ls(1).
120
+ def self.int_to_listing(int)
121
+ perms = [ [6, 00700], [3, 00070], [0, 00007] ].map do |bitshift, mask|
122
+ OCTAL_DIGIT_TO_LISTING[(int & mask) >> bitshift]
123
+ end.join
124
+ if 0 != int & 04000 # setuid
125
+ perms[2] = perms[2] == 'x' ? 's' : 'S'
126
+ end
127
+ if 0 != int & 02000 # setgid
128
+ perms[5] = perms[5] == 'x' ? 's' : 'S'
129
+ end
130
+ if 0 != int & 01000 # sticky
131
+ perms[8] = perms[8] == 'x' ? 't' : 'T'
132
+ end
133
+ perms
134
+ end
135
+
136
+ # Convert an octal string into its permissions listing string.
137
+ def self.str_to_listing(str)
138
+ int_to_listing(str.to_i(8))
139
+ end
140
+
141
+ # Create instance methods on FileMode that forward to the
142
+ # corresponding class method with an implicit self.mode argument.
143
+ ClassMethods.instance_methods(false).each do |mixin_method|
144
+ define_method(mixin_method) do |*args|
145
+ FileMode.send(mixin_method, *(args.dup.unshift(self.mode)))
146
+ end
147
+ end
148
+
149
+ end # FileMode
150
+