Skip to content

Commit 224b6fb

Browse files
committed
Added thread locks, and implmented output function
1 parent d5456bd commit 224b6fb

1 file changed

Lines changed: 155 additions & 79 deletions

File tree

Xlib/rdb.py

Lines changed: 155 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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
#
@@ -23,16 +23,19 @@
2323
# data structures used.
2424

2525

26+
# Standard modules
2627
import string
2728
import types
2829
import re
2930

31+
# Xlib modules
32+
from support import lock
3033

3134
# Set up a few regexpes for parsing string representation of resources
3235

3336
comment_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('\\\\([ \tn\\]|[0-7]{3,3})')
37+
resource_spec_re = re.compile(r'^\s*([-_a-zA-Z0-9?.*]+)\s*:\s*(.*)$')
38+
value_escape_re = re.compile('\\\\([ \tn\\\\]|[0-7]{3,3})')
3639
resource_parts_re = re.compile(r'([.*]+)')
3740

3841
# Constants used for determining which match is best
@@ -42,10 +45,16 @@
4245
WILD_MATCH = 4
4346
MATCH_SKIP = 6
4447

48+
# Option error class
49+
class OptionError(Exception):
50+
pass
51+
4552

4653
class 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

339377
class _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

Comments
 (0)