xdg 0.5.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,65 +1,81 @@
1
1
  = RELEASE HISTORY
2
2
 
3
+ == 1.0.0 / 2009-12-01
4
+
5
+ This is major reimplementation of the XDG API to be more flexiable
6
+ and object-oriented. Instead of a single module with every
7
+ needed method, the system is devided up into sub-modules, one for
8
+ each set of XDG locations. So, for example, instead of "XDG.data_dirs"
9
+ you use "XDG::Data.dirs" or "XDG.data.dirs".
10
+
11
+ Changes:
12
+
13
+ * Reworked API and underlying implementation to be more OOP-style.
14
+ * Began work on xdg/extended.rb, exploring future proposals.
15
+ * Provides xdg/compat.rb, for backward compatabilty (temporary).
16
+
17
+
3
18
  == 0.5.2 / 2009-05-30
4
19
 
5
20
  This release requires rbconfig.rb and uses system entries in place of
6
- hardcoded FHS locations.
21
+ some hardcoded FHS locations.
7
22
 
8
23
  Changes:
9
24
 
10
- * 1 Enhancement
11
-
12
- * replaced hardcoded system directories with rbconfig entries.
25
+ * Replaced hardcoded system directories with rbconfig entries.
13
26
 
14
27
 
15
28
  == 0.5.1 / 2008-11-17
16
29
 
17
30
  Changes:
18
31
 
19
- * 4 Enhancements
20
-
21
- * data work directory is '.local', not '.share'
22
- * data_work is deprecated
23
- * updated website
32
+ * Fixed data work directory is '.local', not '.share'
33
+ * Deprecated #data_work
24
34
 
25
35
 
26
36
  == 0.5.0 / 2008-10-28
27
37
 
28
38
  Changes:
29
39
 
30
- * 1 Enhancement
31
-
32
- * changed _glob to _select
40
+ * Changed _glob to _select
33
41
 
34
42
 
35
43
  == 0.4.0 / 2008-10-26
36
44
 
37
- Changes:
45
+ This release removes the xdg_ prefix from the instance-level
46
+ method names. Now module and instance levels are the same.
38
47
 
39
- * 9 Enhancements
48
+ Also, data_file, config_file and cache_file have been replaced with
49
+ data_find, config_find, cache_find, data_glob, config_glob and
50
+ cache_glob.
40
51
 
41
- * removed xdg_ prefixes and replaced _file methods with _find and _glob methods
42
- * prepare for v0.4 release
43
- * remove some old commented-out code
44
- * fixed data_find and data_glob
45
- * Update RELEASE file
46
- * updated documentation for 0.4 release
47
- * added MANIFEST to .gitignore
48
- * correction or RELEASE
49
- * Fixed plural in RELEASE file
52
+ What's next? Well, I can't say I'm fond of the term 'glob', so I
53
+ may rename that to 'map' or 'collect' (or 'select'?) and then
54
+ add the ability to use blocks for matching too.
55
+
56
+ Changes:
57
+
58
+ * Renamed instance level methods without 'xdg_' prefix.
59
+ * Replace _file methods with _find and _glob methods.
60
+ * Prepare for v0.4 release
61
+ * Remove some old commented-out code
62
+ * Fixed data_find and data_glob
63
+ * Update RELEASE file
64
+ * Updated documentation for 0.4 release
65
+ * Added MANIFEST to .gitignore
66
+ * Correction or RELEASE
67
+ * Fixed plural in RELEASE file
50
68
 
51
69
 
52
70
  == 0.3.0 / 2008-10-11
53
71
 
54
72
  Changes:
55
73
 
56
- * 5 Enhancements
57
-
58
- * removed xdg_ prefix from module methods
59
- * moved web/index.html to doc directory
60
- * updated reap serives
61
- * prepare for next release
62
- * fixed issue of xdg_ prefix still being used internally
74
+ * Removed xdg_ prefix from module methods
75
+ * Moved web/index.html to doc directory
76
+ * Updated reap serives
77
+ * Prepare for next release
78
+ * Fixed issue of xdg_ prefix still being used internally
63
79
 
64
80
 
65
81
  == 0.1.0 / 2008-09-27
data/MANIFEST CHANGED
@@ -1,33 +1,22 @@
1
- #!mast bin lib meta test [A-Z]*
2
- lib
1
+ #!mast bin lib meta script test [A-Z]*
2
+ lib/xdg/compat.rb
3
+ lib/xdg/extended.rb
3
4
  lib/xdg.rb
4
- meta
5
- meta/abstract
6
5
  meta/authors
6
+ meta/collection
7
7
  meta/contact
8
+ meta/description
8
9
  meta/homepage
9
- meta/package
10
+ meta/name
11
+ meta/repository
10
12
  meta/summary
11
13
  meta/title
12
14
  meta/version
13
- test
14
- test/fakeroot
15
- test/fakeroot/.cache
16
- test/fakeroot/.config
17
- test/fakeroot/.share
18
- test/fakeroot/etc
19
- test/fakeroot/etc/xdg
15
+ script/test
20
16
  test/fakeroot/etc/xdg/bar.config
21
- test/fakeroot/home
22
- test/fakeroot/home/.cache
23
17
  test/fakeroot/home/.cache/foo.cache
24
- test/fakeroot/home/.config
25
18
  test/fakeroot/home/.config/foo.config
26
- test/fakeroot/home/.local
27
- test/fakeroot/home/.local/share
28
19
  test/fakeroot/home/.local/share/foo.dat
29
- test/fakeroot/usr
30
- test/fakeroot/usr/share
31
20
  test/fakeroot/usr/share/bar.dat
32
21
  test/test_xdg.rb
33
22
  README
data/README CHANGED
@@ -1,6 +1,7 @@
1
1
  = XDG Base Directory Standard for Ruby
2
2
 
3
- http://xdg.rubyforge.org
3
+ * http://rubyworks.github.com/xdg
4
+ * http://github.com/rubyworks/xdg
4
5
 
5
6
  == Introduction
6
7
 
@@ -11,62 +12,86 @@ If your program utilizes user or system-wide support files
11
12
  (eg. configuration files), you owe it to yourself to checkout
12
13
  the XDG standard.
13
14
 
14
- What's next? The API should be stable now. I do not forsee
15
- and reasons for it to change. In the future, I may add some
16
- additional ruby-esque features, perhaps for other parts of
17
- the XDG utilities, but that should be purely in addition to
18
- what is already here. If all this holds true for a while a
19
- 1.0 release will be forth-coming.
20
-
21
15
  [1]http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
22
16
 
23
- == Release Notes
24
17
 
25
- Please see the RELEASE file.
18
+ == HOW TO USE
19
+
20
+ XDG consists of a small set of modules, one for each XDG directory. Each of
21
+ these has a small base set of easy to use methods.
22
+
23
+ XDG::Config.home
24
+ XDG::Config.dirs
25
+ XDG::Config.find(pattern){ |path| ... }
26
+ XDG::Config.search(pattern){ |path| ... }
26
27
 
27
- == Usage
28
+ XDG::Data.home
29
+ XDG::Data.dirs
30
+ XDG::Data.find(pattern){ |path| ... }
31
+ XDG::Data.search(pattern){ |path| ... }
28
32
 
29
- XDG is a module with a small set of easy to use methods:
33
+ XDG::Cache.home
34
+ XDG::Cache.find(pattern){ |path| ... }
35
+ XDG::Cache.search(pattern){ |path| ... }
30
36
 
31
- XDG.config_home
32
- XDG.config_dirs
33
- XDG.config_find(pattern){ |path| ... }
34
- XDG.config_select(pattern){ |path| ... }
37
+ XDG can also access these modules via methods.
35
38
 
36
- XDG.data_home
37
- XDG.data_dirs
38
- XDG.data_find(pattern){ |path| ... }
39
- XDG.data_select(pattern){ |path| ... }
39
+ XDG.config.home
40
+ XDG.config.dirs
41
+ XDG.config.find(pattern){ |path| ... }
42
+ XDG.config.search(pattern){ |path| ... }
40
43
 
41
- XDG.cache_home
42
- XDG.cache_find(pattern){ |path| ... }
43
- XDG.cache_select(pattern){ |path| ... }
44
+ XDG.data.home
45
+ XDG.data.dirs
46
+ XDG.data.find(pattern){ |path| ... }
47
+ XDG.data.search(pattern){ |path| ... }
44
48
 
45
- XDG.config_work
46
- XDG.cache_work
49
+ XDG.cache.home
50
+ XDG.cache.find(pattern){ |path| ... }
51
+ XDG.cache.search(pattern){ |path| ... }
47
52
 
48
53
  If you know XDG these are pretty much self-explanitory.
49
54
  But see the RDocs for specifics.
50
55
 
51
- == Installation
56
+ === Extended Functionality
57
+
58
+ The Ruby XDG module also provides extended functionality
59
+ not part of the standard specification. These extensions are
60
+ simply add-on funcitonality deemed useful, or implementations
61
+ of proposals being discussed for a possible future version of
62
+ the standard.
63
+
64
+ require 'xdg/extended'
65
+
66
+ XDG.config.work
67
+ XDG.cache.work
68
+
69
+ See the API for the extended.rb, to learn more. Note that
70
+ the extended modules are subject to great change of change
71
+ as they are still being designed.
72
+
73
+
74
+ == HOW TO INSTALL
52
75
 
53
76
  Using RubyGems:
54
77
 
55
78
  $ sudo gem install xdg
56
79
 
57
- Installing the tarball requires Ruby Setup
58
- (see http://setup.rubyforge.org).
80
+ Installing the tarball requires Ruby Setup (see http://proutils.github.com/setup).
59
81
 
60
82
  $ tar -xvzf xdg-0.5.2
61
83
  $ cd xdg-0.5.2
62
84
  $ sudo setup.rb all
63
85
 
64
- == Development
65
86
 
66
- Visit http://rubyforge.org/projects/xdg
87
+ == DEVELOPMENT
88
+
89
+ Visit http://github.com/proutils/xdg
90
+
67
91
 
68
- == Copyright
92
+ == COPYRIGHT
69
93
 
70
- Copyright (c) 2008 Tiger Ops / 7rans
94
+ Copyright (c) 2008, 2009 Thomas Sawyer
71
95
  Distributed under the terms of the LGPL v3 license.
96
+ See COPYING file for details.
72
97
 
data/lib/xdg.rb CHANGED
@@ -7,12 +7,11 @@ require 'rbconfig'
7
7
  #
8
8
  # http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
9
9
  #
10
- # Some important clarifications, not made clear by the
11
- # above specification.
10
+ # Some important clarifications, not made clear by the above specification.
12
11
  #
13
12
  # The data directories are for "read-only" files. In other words once
14
13
  # something is put there, it should only be read, and never written to
15
- # by a program. (Generally speaking only users or package mangers should
14
+ # by a program. (Generally speaking only users or package managers should
16
15
  # be adding, changing or removing files from the data locations.)
17
16
  #
18
17
  # The config locations are where you store files that may change,
@@ -21,217 +20,239 @@ require 'rbconfig'
21
20
  # not just root and sudo admin scripts.
22
21
  #
23
22
  # The cache locations stores files that could just as well be deleted
24
- # and everyihtng would still work fine. This is for variable and
25
- # temporary files. much like var/ in FHS.
23
+ # and everything would still work fine. This is for variable and
24
+ # temporary files. Much like var/ and tmp/ in FHS.
26
25
  #
27
26
  # The module returns all paths as String.
28
27
  #
29
28
  module XDG
30
29
 
31
- extend self #module_function
30
+ #module_function
31
+ extend self
32
32
 
33
33
  # Returns user's home directory.
34
+ #
34
35
  def home
35
36
  File.expand_path('~') # ENV['HOME']
36
37
  end
37
38
 
38
- # Location of user's personal config directory.
39
- def config_home
40
- File.expand_path(
41
- ENV['XDG_CONFIG_HOME'] || File.join(home, '.config')
42
- )
43
- end
44
-
45
- # List of user's shared config directories.
46
- def config_dirs
47
- dirs = ENV['XDG_CONFIG_DIRS'].to_s.split(/[:;]/)
48
- if dirs.empty?
49
- dirs = File.join(Config::CONFIG['sysconfdir'], 'xdg') #%w{/etc/xdg}
39
+ # Access to data resource locations.
40
+ #
41
+ # XDG.data.each{ |dir| ... }
42
+ #
43
+ def data(*glob_and_flags, &block)
44
+ if !glob_and_flags.empty? or block_given?
45
+ Data.select(*glob_and_flags, &block)
46
+ else
47
+ Data
50
48
  end
51
- dirs.collect{ |d| File.expand_path(d) }
52
- end
53
-
54
- # Location of user's personal data directory.
55
- def data_home
56
- File.expand_path(
57
- ENV['XDG_DATA_HOME'] || File.join(home, '.local', 'share')
58
- )
59
49
  end
60
50
 
61
- # List of user's shared data directores.
62
- def data_dirs
63
- dirs = ENV['XDG_DATA_DIRS'].split(/[:;]/)
64
- if dirs.empty?
65
- dirs = [ Config::CONFIG['localdatadir'], Config::CONFIG['datadir'] ] #%w{/usr/local/share/ /usr/share/}
51
+ # Access to configuration locations.
52
+ #
53
+ # XDG.config.each{ |dir| ... }
54
+ #
55
+ def config(*glob_and_flags, &block)
56
+ if !glob_and_flags.empty? or block_given?
57
+ Config.select(*glob_and_flags, &block)
58
+ else
59
+ Config
66
60
  end
67
- dirs.collect{ |d| File.expand_path(d) }
68
61
  end
69
62
 
70
- # Location of user's personal cache directory.
71
- def cache_home
72
- File.expand_path(
73
- ENV['XDG_CACHE_HOME'] || File.join(home, '.cache')
74
- )
75
- end
76
-
77
- # Find a file or directory in data dirs.
63
+ # Access to cache locations.
78
64
  #
79
- # See: +data_select+.
65
+ # XDG.cache.each{ |dir| ... }
80
66
  #
81
- def data_find(*glob_and_flags, &block)
82
- data_select(*glob_and_flags, &block).first
67
+ def cache(*glob_and_flags, &block)
68
+ if !glob_and_flags.empty? or block_given?
69
+ Cache.select(*glob_and_flags, &block)
70
+ else
71
+ Cache
72
+ end
83
73
  end
84
74
 
85
- # Return array of matching files or directories
86
- # in any of the data locations.
75
+ # Each directory set shares these common methods.
87
76
  #
88
- # This starts with the user's home directory
89
- # and then searches system directories.
90
- #
91
- # String parameters are joined into a pathname
92
- # while Integers and Symbols treated as flags.
93
- #
94
- # For example, the following are equivalent:
95
- #
96
- # XDG.data_select('stick/units', File::FNM_CASEFOLD)
97
- #
98
- # XDG.data_select('stick', 'uits', :casefold)
99
- #
100
- def data_select(*glob_and_flags, &block)
101
- glob, flags = *glob_and_flags.partition{ |e| String===e }
102
- glob = ['**/*'] if glob.empty?
103
- flag = flags.inject(0) do |m, f|
104
- if Symbol === f
105
- m + File::const_get("FNM_#{f.to_s.upcase}")
106
- else
107
- m + f.to_i
108
- end
77
+ module Common
78
+
79
+ # Returns a complete list of directories, starting
80
+ # with the home location and moving outward.
81
+ def list
82
+ [home, *dirs]
109
83
  end
110
- find = []
111
- [data_home, *data_dirs].each do |dir|
112
- path = File.join(dir, *glob)
113
- if block_given?
114
- find.concat(Dir.glob(path, flag).select(&block))
115
- else
116
- find.concat(Dir.glob(path, flag))
84
+
85
+ # Return array of matching files or directories
86
+ # in any of the resource locations, starting with
87
+ # the home directory and searching outward into
88
+ # system directories.
89
+ #
90
+ # Unlike #select, this doesn't take a block and each
91
+ # additional glob argument is treated as a logical-or.
92
+ #
93
+ # XDG::Data.glob("stick/*.rb", "stick/*.yaml")
94
+ #
95
+ def glob(*glob_and_flags)
96
+ glob, flags = *parse_arguments(*glob_and_flags)
97
+ find = []
98
+ list.each do |dir|
99
+ glob.each do |pattern|
100
+ find.concat(Dir.glob(File.join(dir, pattern), flags))
101
+ end
117
102
  end
103
+ find.uniq
118
104
  end
119
- find
120
- end
121
105
 
122
- # Return the fist matching file or directory
123
- # from the config locations.
124
- #
125
- # See: +config_select+.
126
- #
127
- def config_find(*glob_and_flags, &block)
128
- config_select(*glob_and_flags, &block).first
129
- end
106
+ # Return array of matching files or directories
107
+ # in any of the resource locations, starting with
108
+ # the home directory and searching outward into
109
+ # system directories.
110
+ #
111
+ # String parameters are joined into a pathname
112
+ # while Integers and Symbols treated as flags.
113
+ #
114
+ # For example, the following are equivalent:
115
+ #
116
+ # XDG.data.select('stick/units', File::FNM_CASEFOLD)
117
+ #
118
+ # XDG.data.select('stick', 'units', :casefold)
119
+ #
120
+ def select(*glob_and_flags, &block)
121
+ glob, flag = *parse_arguments(*glob_and_flags)
122
+ find = []
123
+ list.each do |dir|
124
+ path = File.join(dir, *glob)
125
+ hits = Dir.glob(path, flag)
126
+ hits = hits.select(&block) if block_given?
127
+ find.concat(hits)
128
+ end
129
+ find.uniq
130
+ end
130
131
 
131
- # Return array of matching files or directories
132
- # in any of the config locations.
133
- #
134
- # This starts with the user's home directory
135
- # and then searches system directories.
136
- #
137
- # String parameters are joined into a pathname
138
- # while Integers and Symbols treated as flags.
139
- #
140
- # For example, the following are equivalent:
141
- #
142
- # XDG.config_select('sow/plugins', File::FNM_CASEFOLD)
143
- #
144
- # XDG.config_select('sow', 'plugins', :casefold)
145
- #
146
- def config_select(*glob_and_flags)
147
- glob, flags = *glob_and_flags.partition{ |e| String===e }
148
- glob = ['**/*'] if glob.empty?
149
- flag = flags.inject(0) do |m, f|
150
- if Symbol === f
151
- m + File::const_get("FNM_#{f.to_s.upcase}")
152
- else
153
- m + f.to_i
132
+ # Find a file or directory. This works just like #select
133
+ # except that it returns the first match found.
134
+ #
135
+ # TODO: It would be more efficient to traverse the dirs and use #fnmatch.
136
+ #
137
+ def find(*glob_and_flags, &block)
138
+ glob, flag = *parse_arguments(*glob_and_flags)
139
+ find = nil
140
+ list.each do |dir|
141
+ path = File.join(dir, *glob)
142
+ hits = Dir.glob(path, flag)
143
+ hits = hits.select(&block) if block_given?
144
+ find = hits.first
145
+ break if find
154
146
  end
147
+ find
155
148
  end
156
- find = []
157
- [config_home, *config_dirs].each do |dir|
158
- path = File.join(dir, *glob)
159
- if block_given?
160
- find.concat(Dir.glob(path, flag).select(&block))
161
- else
162
- find.concat(Dir.glob(path, flag))
149
+
150
+ private
151
+
152
+ def parse_arguments(*glob_and_flags)
153
+ glob, flags = *glob_and_flags.partition{ |e| String===e }
154
+ glob = ['**/*'] if glob.empty?
155
+ flag = flags.inject(0) do |m, f|
156
+ if Symbol === f
157
+ m + File::const_get("FNM_#{f.to_s.upcase}")
158
+ else
159
+ m + f.to_i
160
+ end
163
161
  end
162
+ return glob, flag
164
163
  end
165
- find
164
+
166
165
  end
167
166
 
168
- # Return the fist matching file or directory
169
- # in the cache directory.
170
- #
171
- # See: +cache_select+.
167
+ # = DATA LOCATIONS
172
168
  #
173
- def cache_find(*glob_and_flags, &block)
174
- cache_select(*glob_and_flags, &block).first
169
+ module Data
170
+ include Common
171
+
172
+ # Location of personal data directory.
173
+ def home
174
+ @home ||= (
175
+ File.expand_path(
176
+ ENV['XDG_DATA_HOME'] || File.join(XDG.home, '.local', 'share')
177
+ )
178
+ )
179
+ end
180
+
181
+ # List of shared data directores.
182
+ def dirs
183
+ @dirs ||= (
184
+ dirs = ENV['XDG_DATA_DIRS'].split(/[:;]/)
185
+ if dirs.empty?
186
+ #dirs = [ Config::CONFIG['localdatadir'], Config::CONFIG['datadir'] ]
187
+ dirs = Resource.dirs.map{ |d| File.join(d, 'share') }
188
+ end
189
+ dirs = dirs.map{ |d| File.expand_path(d) }.uniq
190
+ dirs = dirs.select{ |d| File.directory?(d) }
191
+ dirs
192
+ )
193
+ end
194
+
195
+ extend self
175
196
  end
176
197
 
177
- # Return array of matching files or directories
178
- # from the cache directory.
198
+ # = CONFIGUTATION LOCATIONS
179
199
  #
180
- # String parameters are joined into a pathname
181
- # while Integers and Symbols treated as flags.
182
- #
183
- # For example, the following are equivalent:
184
- #
185
- # XDG.cache_select('sow/tmp', File::FNM_CASEFOLD)
186
- #
187
- # XDG.cache_select('sow', 'tmp', :casefold)
188
- #
189
- def cache_select(*glob_and_flags)
190
- glob, flags = *glob_and_flags.partition{ |e| String===e }
191
- glob = ['**/*'] if glob.empty?
192
- flag = flags.inject(0) do |m, f|
193
- if Symbol === f
194
- m + File::const_get("FNM_#{f.to_s.upcase}")
195
- else
196
- m + f.to_i
197
- end
200
+ module Config
201
+ include Common
202
+ extend self
203
+
204
+ # Location of personal config directory.
205
+ def home
206
+ @home ||= (
207
+ File.expand_path(
208
+ ENV['XDG_CONFIG_HOME'] || File.join(XDG.home, '.config')
209
+ )
210
+ )
198
211
  end
199
- path = File.join(cache_home,*glob)
200
- if block_given?
201
- Dir.glob(path, flag).select(&block)
202
- else
203
- Dir.glob(path, flag)
212
+
213
+ # List of shared config directories.
214
+ def dirs
215
+ @dirs ||= (
216
+ dirs = ENV['XDG_CONFIG_DIRS'].to_s.split(/[:;]/)
217
+ if dirs.empty?
218
+ #dirs = ['etc/xdg', 'etc']
219
+ sysconfdir = ::Config::CONFIG['sysconfdir']
220
+ dirs = [ File.join(sysconfdir, 'xdg'), sysconfdir ]
221
+ end
222
+ dirs = dirs.map{ |d| File.expand_path(d) }.uniq
223
+ dirs = dirs.select{ |d| File.directory?(d) }
224
+ dirs
225
+ )
204
226
  end
227
+
228
+ extend self
205
229
  end
206
230
 
207
- #--
208
- # The following are not strictly XDG spec,
209
- # but are useful in an analogous respect.
210
- #++
231
+ # = CACHE LOCATIONS
232
+ #
233
+ module Cache
234
+ include Common
211
235
 
212
- # Location of working config directory.
213
- def config_work
214
- File.expand_path(
215
- File.join(Dir.pwd, '.config')
216
- )
217
- end
236
+ # Location of user's personal cache directory.
237
+ def home
238
+ @home ||= (
239
+ File.expand_path(
240
+ ENV['XDG_CACHE_HOME'] || File.join(XDG.home, '.cache')
241
+ )
242
+ )
243
+ end
244
+
245
+ # Serves as a no-op, since there are no common cache directories
246
+ # defined by the XDG standard. (Though one might argue that
247
+ # <tt>tmp/</tt> is one.)
248
+ def dirs
249
+ @dirs ||= []
250
+ end
218
251
 
219
- # DEPRECATED -- Does not make sense to have this.
220
- # Location of working data directory.
221
- #def data_work
222
- # File.expand_path(
223
- # File.join(Dir.pwd, '.local')
224
- # )
225
- #end
226
-
227
- # Location of working cache directory.
228
- def cache_work
229
- File.expand_path(
230
- File.join(Dir.pwd, '.cache')
231
- )
252
+ extend self
232
253
  end
233
254
 
234
255
  end # module XDG
235
256
 
236
- # Copyright (c)2008 Tiger Ops / Trans
237
- # Distributed under the terms of the GPL v3.
257
+ # Copyright (c) 2008,2009 Thomas Sawyer
258
+ # Distributed under the terms of the LGPL v3.