1- # $Id: rdb.py,v 1.2 2000-08-14 10:51:36 petli Exp $
1+ # $Id: rdb.py,v 1.3 2000-08-15 10:09:48 petli Exp $
22#
33# Xlib.rdb -- X resource database implementation
44#
2323# data structures used.
2424
2525
26+ # Standard modules
2627import string
2728import types
2829import re
2930
31+ # Xlib modules
32+ from support import lock
3033
3134# Set up a few regexpes for parsing string representation of resources
3235
3336comment_re = re .compile (r'^\s*!' )
34- resource_spec_re = re .compile (r'^\s*([-_a-zA-Z0-9?.*]+)\s*:\s*(.*?)\s* $' )
35- value_escape_re = re .compile ('\\ \\ ([ \t n\\ ]|[0-7]{3,3})' )
37+ resource_spec_re = re .compile (r'^\s*([-_a-zA-Z0-9?.*]+)\s*:\s*(.*) $' )
38+ value_escape_re = re .compile ('\\ \\ ([ \t n\\ \\ ]|[0-7]{3,3})' )
3639resource_parts_re = re .compile (r'([.*]+)' )
3740
3841# Constants used for determining which match is best
4245WILD_MATCH = 4
4346MATCH_SKIP = 6
4447
48+ # Option error class
49+ class OptionError (Exception ):
50+ pass
51+
4552
4653class ResourceDB :
4754 def __init__ (self , file = None , string = None , resources = None ):
4855 self .db = {}
56+ self .lock = lock .allocate_lock ()
57+
4958 if file is not None :
5059 self .insert_file (file )
5160 if string is not None :
@@ -110,14 +119,18 @@ def insert_string(self, data):
110119
111120 # Convert all escape sequences in value
112121 splits = value_escape_re .split (value )
113-
122+
114123 for i in range (1 , len (splits ), 2 ):
115124 s = splits [i ]
116125 if len (s ) == 3 :
117126 splits [i ] = chr (string .atoi (s , 8 ))
118127 elif s == 'n' :
119128 splits [i ] = '\n '
120129
130+ # strip the last value part to get rid of any
131+ # unescaped blanks
132+ splits [- 1 ] = string .rstrip (splits [- 1 ])
133+
121134 value = string .join (splits , '' )
122135
123136 self .insert (res , value )
@@ -153,6 +166,8 @@ def insert(self, resource, value):
153166 # which we simply ignore
154167 if parts [- 1 ] == '' :
155168 return
169+
170+ self .lock .acquire ()
156171
157172 db = self .db
158173 for i in range (1 , len (parts ), 2 ):
@@ -173,6 +188,7 @@ def insert(self, resource, value):
173188 else :
174189 db [parts [- 1 ]] = ({}, {}, value )
175190
191+ self .lock .release ()
176192
177193 def __getitem__ (self , (name , cls )):
178194 """db[name, class]
@@ -193,91 +209,99 @@ def __getitem__(self, (name, cls)):
193209 raise ValueError ('Different number of parts in resource name/class: %s/%s' % (name , cls ))
194210
195211 complen = len (namep )
196-
197212 matches = []
198213
199- # Precedence order: name -> class -> ?
214+ # Lock database and wrap the lookup code in a try-finally
215+ # block to make sure that it is unlocked.
200216
201- if self .db .has_key (namep [0 ]):
202- bin_insert (matches , _Match ((NAME_MATCH , ), self .db [namep [0 ]]))
217+ self .lock .acquire ()
218+ try :
219+
220+ # Precedence order: name -> class -> ?
203221
204- elif self .db .has_key (clsp [0 ]):
205- bin_insert (matches , _Match ((CLASS_MATCH , ), self .db [clsp [0 ]]))
222+ if self .db .has_key (namep [0 ]):
223+ bin_insert (matches , _Match ((NAME_MATCH , ), self .db [namep [0 ]]))
206224
207- elif self .db .has_key ('?' ):
208- bin_insert (matches , _Match ((WILD_MATCH , ), self .db ['?' ]))
225+ if self .db .has_key (clsp [ 0 ] ):
226+ bin_insert (matches , _Match ((CLASS_MATCH , ), self .db [clsp [ 0 ] ]))
209227
228+ if self .db .has_key ('?' ):
229+ bin_insert (matches , _Match ((WILD_MATCH , ), self .db ['?' ]))
210230
211- # Special case for the unlikely event that the resource
212- # only has one component
213- if complen == 1 and matches :
214- x = matches [0 ]
215- if x .final (complen ):
216- return x .value ()
217- else :
218- raise KeyError ((name , cls ))
219231
220-
221- # Special case for resources which begins with a loose
222- # binding, e.g. '*foo.bar'
223- if self . db . has_key ( '' ):
224- bin_insert ( matches , _Match ((), self . db [ '' ][ 1 ]))
225-
226-
227- # Now iterate over all components until we find the best match.
232+ # Special case for the unlikely event that the resource
233+ # only has one component
234+ if complen == 1 and matches :
235+ x = matches [ 0 ]
236+ if x . final ( complen ):
237+ return x . value ()
238+ else :
239+ raise KeyError (( name , cls ))
228240
229- # For each component, we choose the best partial match among
230- # the mappings by applying these rules in order:
231-
232- # Rule 1: If the current group contains a match for the
233- # name, class or '?', we drop all previously found loose
234- # binding mappings.
235241
236- # Rule 2: A matching name has precedence over a matching
237- # class, which in turn has precedence over '?'.
242+ # Special case for resources which begins with a loose
243+ # binding, e.g. '*foo.bar'
244+ if self .db .has_key ('' ):
245+ bin_insert (matches , _Match ((), self .db ['' ][1 ]))
238246
239- # Rule 3: Tight bindings have precedence over loose
240- # bindings.
241-
242- while matches :
243247
244- # Work on the first element == the best current match
245-
246- x = matches [0 ]
247- del matches [0 ]
248-
249- # print 'path: ', x.path
250- # if x.skip:
251- # print 'skip: ', x.db
252- # else:
253- # print 'group: ', x.group
254- # print
255-
256- i = x .match_length ()
248+ # Now iterate over all components until we find the best match.
249+
250+ # For each component, we choose the best partial match among
251+ # the mappings by applying these rules in order:
252+
253+ # Rule 1: If the current group contains a match for the
254+ # name, class or '?', we drop all previously found loose
255+ # binding mappings.
256+
257+ # Rule 2: A matching name has precedence over a matching
258+ # class, which in turn has precedence over '?'.
259+
260+ # Rule 3: Tight bindings have precedence over loose
261+ # bindings.
262+
263+ while matches :
264+
265+ # Work on the first element == the best current match
266+
267+ x = matches [0 ]
268+ del matches [0 ]
257269
258- for part , score in ((namep [i ], NAME_MATCH ),
259- (clsp [i ], CLASS_MATCH ),
260- ('?' , WILD_MATCH )):
270+ # print 'path: ', x.path
271+ # if x.skip:
272+ # print 'skip: ', x.db
273+ # else:
274+ # print 'group: ', x.group
275+ # print
261276
262- # Attempt to find a match in x
263- match = x .match (part , score )
264- if match :
265- # Hey, we actually found a value!
266- if match .final (complen ):
267- return match .value ()
277+ i = x .match_length ()
268278
269- # Else just insert the new match
270- else :
279+ for part , score in ((namep [i ], NAME_MATCH ),
280+ (clsp [i ], CLASS_MATCH ),
281+ ('?' , WILD_MATCH )):
282+
283+ # Attempt to find a match in x
284+ match = x .match (part , score )
285+ if match :
286+ # Hey, we actually found a value!
287+ if match .final (complen ):
288+ return match .value ()
289+
290+ # Else just insert the new match
291+ else :
292+ bin_insert (matches , match )
293+
294+ # Generate a new loose match
295+ match = x .skip_match (complen )
296+ if match :
271297 bin_insert (matches , match )
272-
273- # Generate a new loose match
274- match = x .skip_match (complen )
275- if match :
276- bin_insert (matches , match )
277298
278- # Oh well, nothing matched
279- raise KeyError ((name , cls ))
299+ # Oh well, nothing matched
300+ raise KeyError ((name , cls ))
280301
302+ finally :
303+ self .lock .release ()
304+
281305 def get (self , res , cls , default = None ):
282306 """get(name, class [, default])
283307
@@ -300,8 +324,21 @@ def update(self, db):
300324
301325 """
302326
327+ self .lock .acquire ()
303328 update_db (self .db , db .db )
329+ self .lock .release ()
330+
331+ def output (self ):
332+ """output()
333+
334+ Return the resource database in text representation.
335+ """
304336
337+ self .lock .acquire ()
338+ text = output_db ('' , self .db )
339+ self .lock .release ()
340+ return text
341+
305342 def getopt (name , argv , opts ):
306343 """getopt(name, argv, opts)
307344
@@ -326,15 +363,16 @@ def getopt(name, argv, opts):
326363
327364 The remaining, non-option, oparguments is returned.
328365
329- getxopt.error is raised if there is an error in the argument list.
366+ rdb.OptionError is raised if there is an error in the argument list.
330367 """
331- try :
332- while len (argv ):
333- argv = opts [args [0 ]].parse (name , self , argv )
334- except KeyError :
335- return argv
336- except IndexError :
337- raise error , 'Missing argument to option %s' % argv [0 ]
368+
369+ while len (argv ) and argv [0 ] and argv [0 ][0 ] == '-' :
370+ try :
371+ argv = opts [argv [0 ]].parse (name , self , argv )
372+ except KeyError :
373+ raise OptionError ('unknown option: %s' % argv [0 ])
374+ except IndexError :
375+ raise OptionError ('missing argument to option: %s' % argv [0 ])
338376
339377class _Match :
340378 def __init__ (self , path , dbs ):
@@ -469,6 +507,44 @@ def copy_db(db):
469507 return newdb
470508
471509
510+ #
511+ # Helper functions for output
512+ #
513+
514+ def output_db (prefix , db ):
515+ res = ''
516+ for comp , group in db .items ():
517+
518+ # There's a value for this component
519+ if len (group ) > 2 :
520+ res = res + '%s%s: %s\n ' % (prefix , comp , output_escape (group [2 ]))
521+
522+ # Output tight and loose bindings
523+ res = res + output_db (prefix + comp + '.' , group [0 ])
524+ res = res + output_db (prefix + comp + '*' , group [1 ])
525+
526+ return res
527+
528+ def output_escape (value ):
529+ value = str (value )
530+ if not value :
531+ return value
532+
533+ for char , esc in (('\\ ' , '\\ \\ ' ),
534+ ('\000 ' , '\\ 000' ),
535+ ('\n ' , '\\ n' )):
536+
537+ value = string .replace (value , char , esc )
538+
539+ # If first or last character is space or tab, escape them.
540+ if value [0 ] in ' \t ' :
541+ value = '\\ ' + value
542+ if value [- 1 ] in ' \t ' and value [- 2 :- 1 ] != '\\ ' :
543+ value = value [:- 1 ] + '\\ ' + value [- 1 ]
544+
545+ return value
546+
547+
472548#
473549# Option type definitions
474550#
0 commit comments