~ubuntu-branches/ubuntu/saucy/dhelp/saucy-proposed

« back to all changes in this revision

Viewing changes to lib/dhelp.rb

  • Committer: Bazaar Package Importer
  • Author(s): Esteban Manchado Velázquez
  • Date: 2007-10-20 17:35:26 UTC
  • Revision ID: james.westby@ubuntu.com-20071020173526-q1ekqfek5tlctsn1
Tags: 0.5.25
* Maintainer change.
* Rewrite dhelp_parse in Ruby. This fixes some bugs and avoids many problems
  (Closes: #21678, #268487, #62454, #312950, #442943, #444429, #193428).
* This release should be a drop-in replacement for the C version, and still
  uses the same database and internal format.
* It also adds a Ruby library, to allow other developers to write other
  programs that read and/or update the dhelp databases.
* Use "http://localhost" for CGI script URLs (Closes: #114588).
* Removed misleading dot from example (Closes: #381804).
* Uses sensible-browser instead of having its own configuration system
  (Closes: #146002, #162518, #381805, #217162, #430590).
* Clean up the HTML a bit (Closes: #438973, #134567, #115306).
* Remove references to obsolete script dh_dhelp (Closes: #369459).
* Raise title limit from 49 to 100 characters (Closes: #102393).
* Depend on doc-base, to make sure packages have their documentation
  available for dhelp (Closes: #314733, #368035).
* Wait a couple of seconds before exiting, after a fatal error, to make sure
  the user can read the error message (Closes: #35097).
* Make documentation index files world-readable, regardless of current umask
  (Closes: #158792, #430474, #430505).
* Remove obsolete script dhelp2dwww.pl, and references to it
  (Closes: #364245).
* Remove "dangerous" environment variables from dsearch, to avoid taint
  problems (Closes: #389944).
* Strip blanks from .dhelp field values (Closes: #133218).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/ruby -w
 
2
 
 
3
=begin
 
4
    Ruby support library for dhelp database access
 
5
 
 
6
    Copyright (C) 2005  Esteban Manchado Vel�zquez
 
7
 
 
8
    This program is free software; you can redistribute it and/or modify
 
9
    it under the terms of the GNU General Public License as published by
 
10
    the Free Software Foundation; either version 2 of the License, or
 
11
    (at your option) any later version.
 
12
 
 
13
    This program is distributed in the hope that it will be useful,
 
14
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
    GNU General Public License for more details.
 
17
 
 
18
    You should have received a copy of the GNU General Public License
 
19
    along with this program; if not, write to the Free Software
 
20
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
21
=end
 
22
 
 
23
require 'bdb'
 
24
 
 
25
# Dhelp-related classes
 
26
module Dhelp
 
27
    # C struct wrapper
 
28
    class CStructWrapper
 
29
        # Class methods
 
30
        class <<self
 
31
            # Get/Set the pack format
 
32
            def pack_fmt(fmt = nil)
 
33
                @fmt = fmt if fmt
 
34
                @fmt
 
35
            end
 
36
            # Get/Set the field list. It can be set as an array, or as several
 
37
            # parameters
 
38
            def field_list(*args)
 
39
                case args.size
 
40
                when 0
 
41
                    # Just get the value, do nothing
 
42
                when 1
 
43
                    first = args.first
 
44
                    @fieldList = first.kind_of?(Array) ? first : [first]
 
45
                else
 
46
                    @fieldList = args
 
47
                end
 
48
                @fieldList.map {|f| f.to_sym}
 
49
            end
 
50
        end
 
51
 
 
52
        def initialize(data)
 
53
            case data
 
54
            when String
 
55
                @data = data
 
56
            when Hash
 
57
                @data = keys.map {|f| data[f]}.pack(pack_fmt)
 
58
            else
 
59
                raise ArgumentError, "Argument must be either String or Hash"
 
60
            end
 
61
        end
 
62
 
 
63
        # Handy shortcut methods
 
64
        def keys;     self.class.field_list; end
 
65
        def pack_fmt; self.class.pack_fmt;   end
 
66
 
 
67
        def get_field(f)
 
68
            i = keys.index(f.to_sym)
 
69
            if i
 
70
                @data.unpack(pack_fmt)[i]
 
71
            else
 
72
                raise ArgumentError, "Unknown field '#{f}'"
 
73
            end
 
74
        end
 
75
 
 
76
        # Returns a Hash object with all the C struct fields
 
77
        def to_hash
 
78
            h = {}
 
79
            keys.each do |f|
 
80
                h[f] = get_field(f)
 
81
            end
 
82
            h
 
83
        end
 
84
 
 
85
        # Returns the data in C format
 
86
        def to_raw_data
 
87
            @data
 
88
        end
 
89
 
 
90
        # Catches missing methods, to get the field values
 
91
        def method_missing(meth, *args)
 
92
            if keys.include? meth.to_sym
 
93
                get_field(meth)
 
94
            else
 
95
                super
 
96
            end
 
97
        end
 
98
    end
 
99
 
 
100
 
 
101
    # Key data entry
 
102
    class KeyData < CStructWrapper
 
103
        pack_fmt    'Z100 Z100 Z100'
 
104
        field_list  %w(file dir name)
 
105
    end
 
106
 
 
107
 
 
108
    # Value data entry
 
109
    class ValueData < CStructWrapper
 
110
        pack_fmt    'Z1000'
 
111
        field_list  %w(descrip)
 
112
    end
 
113
 
 
114
 
 
115
    # Title database key data entry
 
116
    class TitleKeyData < CStructWrapper
 
117
        pack_fmt    'Z1000'
 
118
        field_list  %w(dir)
 
119
    end
 
120
 
 
121
 
 
122
    # Title database value data entry
 
123
    class TitleValueData < CStructWrapper
 
124
        pack_fmt    'Z1000'
 
125
        field_list  %w(dtitle)
 
126
    end
 
127
 
 
128
 
 
129
    # Item data entry
 
130
    class ItemData < CStructWrapper
 
131
        pack_fmt    'Z100 Z100 Z100 Z100 Z1000'
 
132
        field_list  %w(dir dtitle name file descrip)
 
133
    end
 
134
 
 
135
 
 
136
    # Dhelp database
 
137
    class Database < BDB::Btree
 
138
        STD_COMPARISON = lambda {|a,b|
 
139
                                    dataA, dataB = KeyData.new(a), KeyData.new(b)
 
140
                                    r = dataA.dir <=> dataB.dir
 
141
                                    r == 0 ? (dataA.name <=> dataB.name) : r
 
142
                                }
 
143
 
 
144
        def Database.open(flags   = BDB::RDONLY,
 
145
                          options = {},
 
146
                          mode    = 0644,
 
147
                          # name    = 'dhelpdbase',
 
148
                          name    = '/var/lib/dhelp/dbase',
 
149
                          subname = nil)
 
150
            defaultOptions = {"flags"      => 0,
 
151
                              "cachesize"  => 10000,
 
152
                              "minkeypage" => 0,
 
153
                              "psize"      => 0,
 
154
                              "compare"    => STD_COMPARISON,
 
155
                              "prefix"     => nil,
 
156
                              "lorder"     => 0}
 
157
            super(name, subname, flags, mode, defaultOptions.merge(options))
 
158
        end
 
159
 
 
160
        # Writes an ItemData object to the database
 
161
        def write(data)
 
162
            key = KeyData.new(:file => data.file, :dir => data.dir,
 
163
                              :name => data.name)
 
164
            value = ValueData.new(:descrip => data.descrip)
 
165
            put(key.to_raw_data, value.to_raw_data)
 
166
        end
 
167
 
 
168
        def del(data)
 
169
            key = KeyData.new(:file => data.file, :dir => data.dir,
 
170
                              :name => data.name)
 
171
            delete(key.to_raw_data)
 
172
        end
 
173
 
 
174
        # Hide delete method, always use the high-level one
 
175
        protected :delete
 
176
 
 
177
        # Traverse entire BD, passing each item to the block
 
178
        def each
 
179
            super do |k,v|
 
180
                key, value = KeyData.new(k), ValueData.new(v)
 
181
                # Note: missing dtitle field
 
182
                yield ItemData.new(:file    => key.file,
 
183
                                   :dir     => key.dir,
 
184
                                   :name    => key.name,
 
185
                                   :descrip => value.descrip)
 
186
            end
 
187
        end
 
188
 
 
189
        # Traverse each _item_, collecting their categories, and pass the
 
190
        # category and item list to the given block
 
191
        def each_category
 
192
            itemList        = {}
 
193
            each do |item|
 
194
                itemList[item.dir] ||= []
 
195
                itemList[item.dir] << item
 
196
            end
 
197
 
 
198
            orderedCategories = itemList.keys.sort {|a,b|
 
199
                # Order subcategories first
 
200
                case tmp = a.scan('/').size <=> b.scan('/').size
 
201
                when 0
 
202
                    a <=> b
 
203
                else
 
204
                    tmp * -1
 
205
                end
 
206
            }
 
207
            orderedCategories.each do |cat|
 
208
                yield cat, itemList[cat].sort {|a,b| a.name <=> b.name}
 
209
            end
 
210
        end
 
211
    end
 
212
 
 
213
 
 
214
    # Dhelp titles database
 
215
    class TitleDatabase < BDB::Hash
 
216
        def TitleDatabase.open(flags   = BDB::RDONLY,
 
217
                                options = {},
 
218
                                mode    = 0644,
 
219
                                name    = '/var/lib/dhelp/titles',
 
220
                                # name    = 'dhelptitles',
 
221
                                subname = nil)
 
222
            defaultOptions = {"ffactor"   => 8,
 
223
                              "nelem"     => 1,
 
224
                              "cachesize" => 5000,
 
225
                              "hash"      => nil,
 
226
                              "lorder"    => 0}
 
227
            super(name, subname, flags, mode, defaultOptions.merge(options))
 
228
        end
 
229
 
 
230
        # Traverse entire BD, passing each item to the block
 
231
        def each
 
232
            super do |k,v|
 
233
                key, value = KeyData.new(k), ValueData.new(v)
 
234
                # Note: missing dtitle field
 
235
                yield ItemData.new(:file    => key.file,
 
236
                                   :dir     => key.dir,
 
237
                                   :name    => key.name,
 
238
                                   :descrip => value.descrip)
 
239
            end
 
240
        end
 
241
 
 
242
        def title_for(dir)
 
243
            valueData = get(TitleKeyData.new(:dir => dir).to_raw_data)
 
244
            valueData ? TitleValueData.new(valueData).dtitle : nil
 
245
        end
 
246
 
 
247
        # Writes an ItemData object to the database
 
248
        def write(data)
 
249
            key = TitleKeyData.new(:dir => data.dir)
 
250
            value = TitleValueData.new(:dtitle => data.dtitle)
 
251
            put(key.to_raw_data, value.to_raw_data)
 
252
        end
 
253
    end
 
254
end