file_mode 0.0.1

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 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
+