##// END OF EJS Templates
move shortuser into util module.
Vadim Gelfer -
r1903:e4abeafd default
parent child Browse files
Show More
@@ -1,173 +1,167 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, ConfigParser
8 import os, ConfigParser
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "re socket sys util")
11 demandload(globals(), "re socket sys util")
12
12
13 class ui(object):
13 class ui(object):
14 def __init__(self, verbose=False, debug=False, quiet=False,
14 def __init__(self, verbose=False, debug=False, quiet=False,
15 interactive=True):
15 interactive=True):
16 self.overlay = {}
16 self.overlay = {}
17 self.cdata = ConfigParser.SafeConfigParser()
17 self.cdata = ConfigParser.SafeConfigParser()
18 self.readconfig(util.rcpath)
18 self.readconfig(util.rcpath)
19
19
20 self.quiet = self.configbool("ui", "quiet")
20 self.quiet = self.configbool("ui", "quiet")
21 self.verbose = self.configbool("ui", "verbose")
21 self.verbose = self.configbool("ui", "verbose")
22 self.debugflag = self.configbool("ui", "debug")
22 self.debugflag = self.configbool("ui", "debug")
23 self.interactive = self.configbool("ui", "interactive", True)
23 self.interactive = self.configbool("ui", "interactive", True)
24
24
25 self.updateopts(verbose, debug, quiet, interactive)
25 self.updateopts(verbose, debug, quiet, interactive)
26 self.diffcache = None
26 self.diffcache = None
27
27
28 def updateopts(self, verbose=False, debug=False, quiet=False,
28 def updateopts(self, verbose=False, debug=False, quiet=False,
29 interactive=True):
29 interactive=True):
30 self.quiet = (self.quiet or quiet) and not verbose and not debug
30 self.quiet = (self.quiet or quiet) and not verbose and not debug
31 self.verbose = (self.verbose or verbose) or debug
31 self.verbose = (self.verbose or verbose) or debug
32 self.debugflag = (self.debugflag or debug)
32 self.debugflag = (self.debugflag or debug)
33 self.interactive = (self.interactive and interactive)
33 self.interactive = (self.interactive and interactive)
34
34
35 def readconfig(self, fn):
35 def readconfig(self, fn):
36 if isinstance(fn, basestring):
36 if isinstance(fn, basestring):
37 fn = [fn]
37 fn = [fn]
38 for f in fn:
38 for f in fn:
39 try:
39 try:
40 self.cdata.read(f)
40 self.cdata.read(f)
41 except ConfigParser.ParsingError, inst:
41 except ConfigParser.ParsingError, inst:
42 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
42 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
43
43
44 def setconfig(self, section, name, val):
44 def setconfig(self, section, name, val):
45 self.overlay[(section, name)] = val
45 self.overlay[(section, name)] = val
46
46
47 def config(self, section, name, default=None):
47 def config(self, section, name, default=None):
48 if self.overlay.has_key((section, name)):
48 if self.overlay.has_key((section, name)):
49 return self.overlay[(section, name)]
49 return self.overlay[(section, name)]
50 if self.cdata.has_option(section, name):
50 if self.cdata.has_option(section, name):
51 return self.cdata.get(section, name)
51 return self.cdata.get(section, name)
52 return default
52 return default
53
53
54 def configbool(self, section, name, default=False):
54 def configbool(self, section, name, default=False):
55 if self.overlay.has_key((section, name)):
55 if self.overlay.has_key((section, name)):
56 return self.overlay[(section, name)]
56 return self.overlay[(section, name)]
57 if self.cdata.has_option(section, name):
57 if self.cdata.has_option(section, name):
58 return self.cdata.getboolean(section, name)
58 return self.cdata.getboolean(section, name)
59 return default
59 return default
60
60
61 def configitems(self, section):
61 def configitems(self, section):
62 if self.cdata.has_section(section):
62 if self.cdata.has_section(section):
63 return self.cdata.items(section)
63 return self.cdata.items(section)
64 return []
64 return []
65
65
66 def walkconfig(self):
66 def walkconfig(self):
67 seen = {}
67 seen = {}
68 for (section, name), value in self.overlay.iteritems():
68 for (section, name), value in self.overlay.iteritems():
69 yield section, name, value
69 yield section, name, value
70 seen[section, name] = 1
70 seen[section, name] = 1
71 for section in self.cdata.sections():
71 for section in self.cdata.sections():
72 for name, value in self.cdata.items(section):
72 for name, value in self.cdata.items(section):
73 if (section, name) in seen: continue
73 if (section, name) in seen: continue
74 yield section, name, value.replace('\n', '\\n')
74 yield section, name, value.replace('\n', '\\n')
75 seen[section, name] = 1
75 seen[section, name] = 1
76
76
77 def extensions(self):
77 def extensions(self):
78 return self.configitems("extensions")
78 return self.configitems("extensions")
79
79
80 def diffopts(self):
80 def diffopts(self):
81 if self.diffcache:
81 if self.diffcache:
82 return self.diffcache
82 return self.diffcache
83 ret = { 'showfunc' : True, 'ignorews' : False}
83 ret = { 'showfunc' : True, 'ignorews' : False}
84 for x in self.configitems("diff"):
84 for x in self.configitems("diff"):
85 k = x[0].lower()
85 k = x[0].lower()
86 v = x[1]
86 v = x[1]
87 if v:
87 if v:
88 v = v.lower()
88 v = v.lower()
89 if v == 'true':
89 if v == 'true':
90 value = True
90 value = True
91 else:
91 else:
92 value = False
92 value = False
93 ret[k] = value
93 ret[k] = value
94 self.diffcache = ret
94 self.diffcache = ret
95 return ret
95 return ret
96
96
97 def username(self):
97 def username(self):
98 return (os.environ.get("HGUSER") or
98 return (os.environ.get("HGUSER") or
99 self.config("ui", "username") or
99 self.config("ui", "username") or
100 os.environ.get("EMAIL") or
100 os.environ.get("EMAIL") or
101 (os.environ.get("LOGNAME",
101 (os.environ.get("LOGNAME",
102 os.environ.get("USERNAME", "unknown"))
102 os.environ.get("USERNAME", "unknown"))
103 + '@' + socket.getfqdn()))
103 + '@' + socket.getfqdn()))
104
104
105 def shortuser(self, user):
105 def shortuser(self, user):
106 """Return a short representation of a user name or email address."""
106 """Return a short representation of a user name or email address."""
107 if not self.verbose:
107 if not self.verbose: user = util.shortuser(user)
108 f = user.find('@')
109 if f >= 0:
110 user = user[:f]
111 f = user.find('<')
112 if f >= 0:
113 user = user[f+1:]
114 return user
108 return user
115
109
116 def expandpath(self, loc, root=""):
110 def expandpath(self, loc, root=""):
117 paths = {}
111 paths = {}
118 for name, path in self.configitems("paths"):
112 for name, path in self.configitems("paths"):
119 m = path.find("://")
113 m = path.find("://")
120 if m == -1:
114 if m == -1:
121 path = os.path.join(root, path)
115 path = os.path.join(root, path)
122 paths[name] = path
116 paths[name] = path
123
117
124 return paths.get(loc, loc)
118 return paths.get(loc, loc)
125
119
126 def write(self, *args):
120 def write(self, *args):
127 for a in args:
121 for a in args:
128 sys.stdout.write(str(a))
122 sys.stdout.write(str(a))
129
123
130 def write_err(self, *args):
124 def write_err(self, *args):
131 if not sys.stdout.closed: sys.stdout.flush()
125 if not sys.stdout.closed: sys.stdout.flush()
132 for a in args:
126 for a in args:
133 sys.stderr.write(str(a))
127 sys.stderr.write(str(a))
134
128
135 def readline(self):
129 def readline(self):
136 return sys.stdin.readline()[:-1]
130 return sys.stdin.readline()[:-1]
137 def prompt(self, msg, pat, default="y"):
131 def prompt(self, msg, pat, default="y"):
138 if not self.interactive: return default
132 if not self.interactive: return default
139 while 1:
133 while 1:
140 self.write(msg, " ")
134 self.write(msg, " ")
141 r = self.readline()
135 r = self.readline()
142 if re.match(pat, r):
136 if re.match(pat, r):
143 return r
137 return r
144 else:
138 else:
145 self.write(_("unrecognized response\n"))
139 self.write(_("unrecognized response\n"))
146 def status(self, *msg):
140 def status(self, *msg):
147 if not self.quiet: self.write(*msg)
141 if not self.quiet: self.write(*msg)
148 def warn(self, *msg):
142 def warn(self, *msg):
149 self.write_err(*msg)
143 self.write_err(*msg)
150 def note(self, *msg):
144 def note(self, *msg):
151 if self.verbose: self.write(*msg)
145 if self.verbose: self.write(*msg)
152 def debug(self, *msg):
146 def debug(self, *msg):
153 if self.debugflag: self.write(*msg)
147 if self.debugflag: self.write(*msg)
154 def edit(self, text):
148 def edit(self, text):
155 import tempfile
149 import tempfile
156 (fd, name) = tempfile.mkstemp("hg")
150 (fd, name) = tempfile.mkstemp("hg")
157 f = os.fdopen(fd, "w")
151 f = os.fdopen(fd, "w")
158 f.write(text)
152 f.write(text)
159 f.close()
153 f.close()
160
154
161 editor = (os.environ.get("HGEDITOR") or
155 editor = (os.environ.get("HGEDITOR") or
162 self.config("ui", "editor") or
156 self.config("ui", "editor") or
163 os.environ.get("EDITOR", "vi"))
157 os.environ.get("EDITOR", "vi"))
164
158
165 os.environ["HGUSER"] = self.username()
159 os.environ["HGUSER"] = self.username()
166 util.system("%s \"%s\"" % (editor, name), errprefix=_("edit failed"))
160 util.system("%s \"%s\"" % (editor, name), errprefix=_("edit failed"))
167
161
168 t = open(name).read()
162 t = open(name).read()
169 t = re.sub("(?m)^HG:.*\n", "", t)
163 t = re.sub("(?m)^HG:.*\n", "", t)
170
164
171 os.unlink(name)
165 os.unlink(name)
172
166
173 return t
167 return t
@@ -1,692 +1,702 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8
8
9 This contains helper routines that are independent of the SCM core and hide
9 This contains helper routines that are independent of the SCM core and hide
10 platform-specific details from the core.
10 platform-specific details from the core.
11 """
11 """
12
12
13 import os, errno
13 import os, errno
14 from i18n import gettext as _
14 from i18n import gettext as _
15 from demandload import *
15 from demandload import *
16 demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile")
16 demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile")
17 demandload(globals(), "threading time")
17 demandload(globals(), "threading time")
18
18
19 def pipefilter(s, cmd):
19 def pipefilter(s, cmd):
20 '''filter string S through command CMD, returning its output'''
20 '''filter string S through command CMD, returning its output'''
21 (pout, pin) = popen2.popen2(cmd, -1, 'b')
21 (pout, pin) = popen2.popen2(cmd, -1, 'b')
22 def writer():
22 def writer():
23 pin.write(s)
23 pin.write(s)
24 pin.close()
24 pin.close()
25
25
26 # we should use select instead on UNIX, but this will work on most
26 # we should use select instead on UNIX, but this will work on most
27 # systems, including Windows
27 # systems, including Windows
28 w = threading.Thread(target=writer)
28 w = threading.Thread(target=writer)
29 w.start()
29 w.start()
30 f = pout.read()
30 f = pout.read()
31 pout.close()
31 pout.close()
32 w.join()
32 w.join()
33 return f
33 return f
34
34
35 def tempfilter(s, cmd):
35 def tempfilter(s, cmd):
36 '''filter string S through a pair of temporary files with CMD.
36 '''filter string S through a pair of temporary files with CMD.
37 CMD is used as a template to create the real command to be run,
37 CMD is used as a template to create the real command to be run,
38 with the strings INFILE and OUTFILE replaced by the real names of
38 with the strings INFILE and OUTFILE replaced by the real names of
39 the temporary files generated.'''
39 the temporary files generated.'''
40 inname, outname = None, None
40 inname, outname = None, None
41 try:
41 try:
42 infd, inname = tempfile.mkstemp(prefix='hgfin')
42 infd, inname = tempfile.mkstemp(prefix='hgfin')
43 fp = os.fdopen(infd, 'wb')
43 fp = os.fdopen(infd, 'wb')
44 fp.write(s)
44 fp.write(s)
45 fp.close()
45 fp.close()
46 outfd, outname = tempfile.mkstemp(prefix='hgfout')
46 outfd, outname = tempfile.mkstemp(prefix='hgfout')
47 os.close(outfd)
47 os.close(outfd)
48 cmd = cmd.replace('INFILE', inname)
48 cmd = cmd.replace('INFILE', inname)
49 cmd = cmd.replace('OUTFILE', outname)
49 cmd = cmd.replace('OUTFILE', outname)
50 code = os.system(cmd)
50 code = os.system(cmd)
51 if code: raise Abort(_("command '%s' failed: %s") %
51 if code: raise Abort(_("command '%s' failed: %s") %
52 (cmd, explain_exit(code)))
52 (cmd, explain_exit(code)))
53 return open(outname, 'rb').read()
53 return open(outname, 'rb').read()
54 finally:
54 finally:
55 try:
55 try:
56 if inname: os.unlink(inname)
56 if inname: os.unlink(inname)
57 except: pass
57 except: pass
58 try:
58 try:
59 if outname: os.unlink(outname)
59 if outname: os.unlink(outname)
60 except: pass
60 except: pass
61
61
62 filtertable = {
62 filtertable = {
63 'tempfile:': tempfilter,
63 'tempfile:': tempfilter,
64 'pipe:': pipefilter,
64 'pipe:': pipefilter,
65 }
65 }
66
66
67 def filter(s, cmd):
67 def filter(s, cmd):
68 "filter a string through a command that transforms its input to its output"
68 "filter a string through a command that transforms its input to its output"
69 for name, fn in filtertable.iteritems():
69 for name, fn in filtertable.iteritems():
70 if cmd.startswith(name):
70 if cmd.startswith(name):
71 return fn(s, cmd[len(name):].lstrip())
71 return fn(s, cmd[len(name):].lstrip())
72 return pipefilter(s, cmd)
72 return pipefilter(s, cmd)
73
73
74 def patch(strip, patchname, ui):
74 def patch(strip, patchname, ui):
75 """apply the patch <patchname> to the working directory.
75 """apply the patch <patchname> to the working directory.
76 a list of patched files is returned"""
76 a list of patched files is returned"""
77 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
77 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
78 files = {}
78 files = {}
79 for line in fp:
79 for line in fp:
80 line = line.rstrip()
80 line = line.rstrip()
81 ui.status("%s\n" % line)
81 ui.status("%s\n" % line)
82 if line.startswith('patching file '):
82 if line.startswith('patching file '):
83 pf = parse_patch_output(line)
83 pf = parse_patch_output(line)
84 files.setdefault(pf, 1)
84 files.setdefault(pf, 1)
85 code = fp.close()
85 code = fp.close()
86 if code:
86 if code:
87 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
87 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
88 return files.keys()
88 return files.keys()
89
89
90 def binary(s):
90 def binary(s):
91 """return true if a string is binary data using diff's heuristic"""
91 """return true if a string is binary data using diff's heuristic"""
92 if s and '\0' in s[:4096]:
92 if s and '\0' in s[:4096]:
93 return True
93 return True
94 return False
94 return False
95
95
96 def unique(g):
96 def unique(g):
97 """return the uniq elements of iterable g"""
97 """return the uniq elements of iterable g"""
98 seen = {}
98 seen = {}
99 for f in g:
99 for f in g:
100 if f not in seen:
100 if f not in seen:
101 seen[f] = 1
101 seen[f] = 1
102 yield f
102 yield f
103
103
104 class Abort(Exception):
104 class Abort(Exception):
105 """Raised if a command needs to print an error and exit."""
105 """Raised if a command needs to print an error and exit."""
106
106
107 def always(fn): return True
107 def always(fn): return True
108 def never(fn): return False
108 def never(fn): return False
109
109
110 def patkind(name, dflt_pat='glob'):
110 def patkind(name, dflt_pat='glob'):
111 """Split a string into an optional pattern kind prefix and the
111 """Split a string into an optional pattern kind prefix and the
112 actual pattern."""
112 actual pattern."""
113 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
113 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
114 if name.startswith(prefix + ':'): return name.split(':', 1)
114 if name.startswith(prefix + ':'): return name.split(':', 1)
115 return dflt_pat, name
115 return dflt_pat, name
116
116
117 def globre(pat, head='^', tail='$'):
117 def globre(pat, head='^', tail='$'):
118 "convert a glob pattern into a regexp"
118 "convert a glob pattern into a regexp"
119 i, n = 0, len(pat)
119 i, n = 0, len(pat)
120 res = ''
120 res = ''
121 group = False
121 group = False
122 def peek(): return i < n and pat[i]
122 def peek(): return i < n and pat[i]
123 while i < n:
123 while i < n:
124 c = pat[i]
124 c = pat[i]
125 i = i+1
125 i = i+1
126 if c == '*':
126 if c == '*':
127 if peek() == '*':
127 if peek() == '*':
128 i += 1
128 i += 1
129 res += '.*'
129 res += '.*'
130 else:
130 else:
131 res += '[^/]*'
131 res += '[^/]*'
132 elif c == '?':
132 elif c == '?':
133 res += '.'
133 res += '.'
134 elif c == '[':
134 elif c == '[':
135 j = i
135 j = i
136 if j < n and pat[j] in '!]':
136 if j < n and pat[j] in '!]':
137 j += 1
137 j += 1
138 while j < n and pat[j] != ']':
138 while j < n and pat[j] != ']':
139 j += 1
139 j += 1
140 if j >= n:
140 if j >= n:
141 res += '\\['
141 res += '\\['
142 else:
142 else:
143 stuff = pat[i:j].replace('\\','\\\\')
143 stuff = pat[i:j].replace('\\','\\\\')
144 i = j + 1
144 i = j + 1
145 if stuff[0] == '!':
145 if stuff[0] == '!':
146 stuff = '^' + stuff[1:]
146 stuff = '^' + stuff[1:]
147 elif stuff[0] == '^':
147 elif stuff[0] == '^':
148 stuff = '\\' + stuff
148 stuff = '\\' + stuff
149 res = '%s[%s]' % (res, stuff)
149 res = '%s[%s]' % (res, stuff)
150 elif c == '{':
150 elif c == '{':
151 group = True
151 group = True
152 res += '(?:'
152 res += '(?:'
153 elif c == '}' and group:
153 elif c == '}' and group:
154 res += ')'
154 res += ')'
155 group = False
155 group = False
156 elif c == ',' and group:
156 elif c == ',' and group:
157 res += '|'
157 res += '|'
158 else:
158 else:
159 res += re.escape(c)
159 res += re.escape(c)
160 return head + res + tail
160 return head + res + tail
161
161
162 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
162 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
163
163
164 def pathto(n1, n2):
164 def pathto(n1, n2):
165 '''return the relative path from one place to another.
165 '''return the relative path from one place to another.
166 this returns a path in the form used by the local filesystem, not hg.'''
166 this returns a path in the form used by the local filesystem, not hg.'''
167 if not n1: return localpath(n2)
167 if not n1: return localpath(n2)
168 a, b = n1.split('/'), n2.split('/')
168 a, b = n1.split('/'), n2.split('/')
169 a.reverse()
169 a.reverse()
170 b.reverse()
170 b.reverse()
171 while a and b and a[-1] == b[-1]:
171 while a and b and a[-1] == b[-1]:
172 a.pop()
172 a.pop()
173 b.pop()
173 b.pop()
174 b.reverse()
174 b.reverse()
175 return os.sep.join((['..'] * len(a)) + b)
175 return os.sep.join((['..'] * len(a)) + b)
176
176
177 def canonpath(root, cwd, myname):
177 def canonpath(root, cwd, myname):
178 """return the canonical path of myname, given cwd and root"""
178 """return the canonical path of myname, given cwd and root"""
179 if root == os.sep:
179 if root == os.sep:
180 rootsep = os.sep
180 rootsep = os.sep
181 else:
181 else:
182 rootsep = root + os.sep
182 rootsep = root + os.sep
183 name = myname
183 name = myname
184 if not name.startswith(os.sep):
184 if not name.startswith(os.sep):
185 name = os.path.join(root, cwd, name)
185 name = os.path.join(root, cwd, name)
186 name = os.path.normpath(name)
186 name = os.path.normpath(name)
187 if name.startswith(rootsep):
187 if name.startswith(rootsep):
188 return pconvert(name[len(rootsep):])
188 return pconvert(name[len(rootsep):])
189 elif name == root:
189 elif name == root:
190 return ''
190 return ''
191 else:
191 else:
192 raise Abort('%s not under root' % myname)
192 raise Abort('%s not under root' % myname)
193
193
194 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
194 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
195 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
195 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
196
196
197 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
197 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
198 if os.name == 'nt':
198 if os.name == 'nt':
199 dflt_pat = 'glob'
199 dflt_pat = 'glob'
200 else:
200 else:
201 dflt_pat = 'relpath'
201 dflt_pat = 'relpath'
202 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
202 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
203
203
204 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
204 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
205 """build a function to match a set of file patterns
205 """build a function to match a set of file patterns
206
206
207 arguments:
207 arguments:
208 canonroot - the canonical root of the tree you're matching against
208 canonroot - the canonical root of the tree you're matching against
209 cwd - the current working directory, if relevant
209 cwd - the current working directory, if relevant
210 names - patterns to find
210 names - patterns to find
211 inc - patterns to include
211 inc - patterns to include
212 exc - patterns to exclude
212 exc - patterns to exclude
213 head - a regex to prepend to patterns to control whether a match is rooted
213 head - a regex to prepend to patterns to control whether a match is rooted
214
214
215 a pattern is one of:
215 a pattern is one of:
216 'glob:<rooted glob>'
216 'glob:<rooted glob>'
217 're:<rooted regexp>'
217 're:<rooted regexp>'
218 'path:<rooted path>'
218 'path:<rooted path>'
219 'relglob:<relative glob>'
219 'relglob:<relative glob>'
220 'relpath:<relative path>'
220 'relpath:<relative path>'
221 'relre:<relative regexp>'
221 'relre:<relative regexp>'
222 '<rooted path or regexp>'
222 '<rooted path or regexp>'
223
223
224 returns:
224 returns:
225 a 3-tuple containing
225 a 3-tuple containing
226 - list of explicit non-pattern names passed in
226 - list of explicit non-pattern names passed in
227 - a bool match(filename) function
227 - a bool match(filename) function
228 - a bool indicating if any patterns were passed in
228 - a bool indicating if any patterns were passed in
229
229
230 todo:
230 todo:
231 make head regex a rooted bool
231 make head regex a rooted bool
232 """
232 """
233
233
234 def contains_glob(name):
234 def contains_glob(name):
235 for c in name:
235 for c in name:
236 if c in _globchars: return True
236 if c in _globchars: return True
237 return False
237 return False
238
238
239 def regex(kind, name, tail):
239 def regex(kind, name, tail):
240 '''convert a pattern into a regular expression'''
240 '''convert a pattern into a regular expression'''
241 if kind == 're':
241 if kind == 're':
242 return name
242 return name
243 elif kind == 'path':
243 elif kind == 'path':
244 return '^' + re.escape(name) + '(?:/|$)'
244 return '^' + re.escape(name) + '(?:/|$)'
245 elif kind == 'relglob':
245 elif kind == 'relglob':
246 return head + globre(name, '(?:|.*/)', tail)
246 return head + globre(name, '(?:|.*/)', tail)
247 elif kind == 'relpath':
247 elif kind == 'relpath':
248 return head + re.escape(name) + tail
248 return head + re.escape(name) + tail
249 elif kind == 'relre':
249 elif kind == 'relre':
250 if name.startswith('^'):
250 if name.startswith('^'):
251 return name
251 return name
252 return '.*' + name
252 return '.*' + name
253 return head + globre(name, '', tail)
253 return head + globre(name, '', tail)
254
254
255 def matchfn(pats, tail):
255 def matchfn(pats, tail):
256 """build a matching function from a set of patterns"""
256 """build a matching function from a set of patterns"""
257 if not pats:
257 if not pats:
258 return
258 return
259 matches = []
259 matches = []
260 for k, p in pats:
260 for k, p in pats:
261 try:
261 try:
262 pat = '(?:%s)' % regex(k, p, tail)
262 pat = '(?:%s)' % regex(k, p, tail)
263 matches.append(re.compile(pat).match)
263 matches.append(re.compile(pat).match)
264 except re.error:
264 except re.error:
265 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
265 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
266 else: raise Abort("invalid pattern (%s): %s" % (k, p))
266 else: raise Abort("invalid pattern (%s): %s" % (k, p))
267
267
268 def buildfn(text):
268 def buildfn(text):
269 for m in matches:
269 for m in matches:
270 r = m(text)
270 r = m(text)
271 if r:
271 if r:
272 return r
272 return r
273
273
274 return buildfn
274 return buildfn
275
275
276 def globprefix(pat):
276 def globprefix(pat):
277 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
277 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
278 root = []
278 root = []
279 for p in pat.split(os.sep):
279 for p in pat.split(os.sep):
280 if contains_glob(p): break
280 if contains_glob(p): break
281 root.append(p)
281 root.append(p)
282 return '/'.join(root)
282 return '/'.join(root)
283
283
284 pats = []
284 pats = []
285 files = []
285 files = []
286 roots = []
286 roots = []
287 for kind, name in [patkind(p, dflt_pat) for p in names]:
287 for kind, name in [patkind(p, dflt_pat) for p in names]:
288 if kind in ('glob', 'relpath'):
288 if kind in ('glob', 'relpath'):
289 name = canonpath(canonroot, cwd, name)
289 name = canonpath(canonroot, cwd, name)
290 if name == '':
290 if name == '':
291 kind, name = 'glob', '**'
291 kind, name = 'glob', '**'
292 if kind in ('glob', 'path', 're'):
292 if kind in ('glob', 'path', 're'):
293 pats.append((kind, name))
293 pats.append((kind, name))
294 if kind == 'glob':
294 if kind == 'glob':
295 root = globprefix(name)
295 root = globprefix(name)
296 if root: roots.append(root)
296 if root: roots.append(root)
297 elif kind == 'relpath':
297 elif kind == 'relpath':
298 files.append((kind, name))
298 files.append((kind, name))
299 roots.append(name)
299 roots.append(name)
300
300
301 patmatch = matchfn(pats, '$') or always
301 patmatch = matchfn(pats, '$') or always
302 filematch = matchfn(files, '(?:/|$)') or always
302 filematch = matchfn(files, '(?:/|$)') or always
303 incmatch = always
303 incmatch = always
304 if inc:
304 if inc:
305 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
305 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
306 excmatch = lambda fn: False
306 excmatch = lambda fn: False
307 if exc:
307 if exc:
308 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
308 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
309
309
310 return (roots,
310 return (roots,
311 lambda fn: (incmatch(fn) and not excmatch(fn) and
311 lambda fn: (incmatch(fn) and not excmatch(fn) and
312 (fn.endswith('/') or
312 (fn.endswith('/') or
313 (not pats and not files) or
313 (not pats and not files) or
314 (pats and patmatch(fn)) or
314 (pats and patmatch(fn)) or
315 (files and filematch(fn)))),
315 (files and filematch(fn)))),
316 (inc or exc or (pats and pats != [('glob', '**')])) and True)
316 (inc or exc or (pats and pats != [('glob', '**')])) and True)
317
317
318 def system(cmd, errprefix=None):
318 def system(cmd, errprefix=None):
319 """execute a shell command that must succeed"""
319 """execute a shell command that must succeed"""
320 rc = os.system(cmd)
320 rc = os.system(cmd)
321 if rc:
321 if rc:
322 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
322 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
323 explain_exit(rc)[0])
323 explain_exit(rc)[0])
324 if errprefix:
324 if errprefix:
325 errmsg = "%s: %s" % (errprefix, errmsg)
325 errmsg = "%s: %s" % (errprefix, errmsg)
326 raise Abort(errmsg)
326 raise Abort(errmsg)
327
327
328 def rename(src, dst):
328 def rename(src, dst):
329 """forcibly rename a file"""
329 """forcibly rename a file"""
330 try:
330 try:
331 os.rename(src, dst)
331 os.rename(src, dst)
332 except:
332 except:
333 os.unlink(dst)
333 os.unlink(dst)
334 os.rename(src, dst)
334 os.rename(src, dst)
335
335
336 def unlink(f):
336 def unlink(f):
337 """unlink and remove the directory if it is empty"""
337 """unlink and remove the directory if it is empty"""
338 os.unlink(f)
338 os.unlink(f)
339 # try removing directories that might now be empty
339 # try removing directories that might now be empty
340 try: os.removedirs(os.path.dirname(f))
340 try: os.removedirs(os.path.dirname(f))
341 except: pass
341 except: pass
342
342
343 def copyfiles(src, dst, hardlink=None):
343 def copyfiles(src, dst, hardlink=None):
344 """Copy a directory tree using hardlinks if possible"""
344 """Copy a directory tree using hardlinks if possible"""
345
345
346 if hardlink is None:
346 if hardlink is None:
347 hardlink = (os.stat(src).st_dev ==
347 hardlink = (os.stat(src).st_dev ==
348 os.stat(os.path.dirname(dst)).st_dev)
348 os.stat(os.path.dirname(dst)).st_dev)
349
349
350 if os.path.isdir(src):
350 if os.path.isdir(src):
351 os.mkdir(dst)
351 os.mkdir(dst)
352 for name in os.listdir(src):
352 for name in os.listdir(src):
353 srcname = os.path.join(src, name)
353 srcname = os.path.join(src, name)
354 dstname = os.path.join(dst, name)
354 dstname = os.path.join(dst, name)
355 copyfiles(srcname, dstname, hardlink)
355 copyfiles(srcname, dstname, hardlink)
356 else:
356 else:
357 if hardlink:
357 if hardlink:
358 try:
358 try:
359 os_link(src, dst)
359 os_link(src, dst)
360 except:
360 except:
361 hardlink = False
361 hardlink = False
362 shutil.copy(src, dst)
362 shutil.copy(src, dst)
363 else:
363 else:
364 shutil.copy(src, dst)
364 shutil.copy(src, dst)
365
365
366 def opener(base):
366 def opener(base):
367 """
367 """
368 return a function that opens files relative to base
368 return a function that opens files relative to base
369
369
370 this function is used to hide the details of COW semantics and
370 this function is used to hide the details of COW semantics and
371 remote file access from higher level code.
371 remote file access from higher level code.
372 """
372 """
373 p = base
373 p = base
374
374
375 def mktempcopy(name):
375 def mktempcopy(name):
376 d, fn = os.path.split(name)
376 d, fn = os.path.split(name)
377 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
377 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
378 fp = os.fdopen(fd, "wb")
378 fp = os.fdopen(fd, "wb")
379 try:
379 try:
380 fp.write(file(name, "rb").read())
380 fp.write(file(name, "rb").read())
381 except:
381 except:
382 try: os.unlink(temp)
382 try: os.unlink(temp)
383 except: pass
383 except: pass
384 raise
384 raise
385 fp.close()
385 fp.close()
386 st = os.lstat(name)
386 st = os.lstat(name)
387 os.chmod(temp, st.st_mode)
387 os.chmod(temp, st.st_mode)
388 return temp
388 return temp
389
389
390 class atomicfile(file):
390 class atomicfile(file):
391 """the file will only be copied on close"""
391 """the file will only be copied on close"""
392 def __init__(self, name, mode, atomic=False):
392 def __init__(self, name, mode, atomic=False):
393 self.__name = name
393 self.__name = name
394 self.temp = mktempcopy(name)
394 self.temp = mktempcopy(name)
395 file.__init__(self, self.temp, mode)
395 file.__init__(self, self.temp, mode)
396 def close(self):
396 def close(self):
397 if not self.closed:
397 if not self.closed:
398 file.close(self)
398 file.close(self)
399 rename(self.temp, self.__name)
399 rename(self.temp, self.__name)
400 def __del__(self):
400 def __del__(self):
401 self.close()
401 self.close()
402
402
403 def o(path, mode="r", text=False, atomic=False):
403 def o(path, mode="r", text=False, atomic=False):
404 f = os.path.join(p, path)
404 f = os.path.join(p, path)
405
405
406 if not text:
406 if not text:
407 mode += "b" # for that other OS
407 mode += "b" # for that other OS
408
408
409 if mode[0] != "r":
409 if mode[0] != "r":
410 try:
410 try:
411 nlink = nlinks(f)
411 nlink = nlinks(f)
412 except OSError:
412 except OSError:
413 d = os.path.dirname(f)
413 d = os.path.dirname(f)
414 if not os.path.isdir(d):
414 if not os.path.isdir(d):
415 os.makedirs(d)
415 os.makedirs(d)
416 else:
416 else:
417 if atomic:
417 if atomic:
418 return atomicfile(f, mode)
418 return atomicfile(f, mode)
419 if nlink > 1:
419 if nlink > 1:
420 rename(mktempcopy(f), f)
420 rename(mktempcopy(f), f)
421 return file(f, mode)
421 return file(f, mode)
422
422
423 return o
423 return o
424
424
425 def _makelock_file(info, pathname):
425 def _makelock_file(info, pathname):
426 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
426 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
427 os.write(ld, info)
427 os.write(ld, info)
428 os.close(ld)
428 os.close(ld)
429
429
430 def _readlock_file(pathname):
430 def _readlock_file(pathname):
431 return file(pathname).read()
431 return file(pathname).read()
432
432
433 def nlinks(pathname):
433 def nlinks(pathname):
434 """Return number of hardlinks for the given file."""
434 """Return number of hardlinks for the given file."""
435 return os.stat(pathname).st_nlink
435 return os.stat(pathname).st_nlink
436
436
437 if hasattr(os, 'link'):
437 if hasattr(os, 'link'):
438 os_link = os.link
438 os_link = os.link
439 else:
439 else:
440 def os_link(src, dst):
440 def os_link(src, dst):
441 raise OSError(0, _("Hardlinks not supported"))
441 raise OSError(0, _("Hardlinks not supported"))
442
442
443 # Platform specific variants
443 # Platform specific variants
444 if os.name == 'nt':
444 if os.name == 'nt':
445 demandload(globals(), "msvcrt")
445 demandload(globals(), "msvcrt")
446 nulldev = 'NUL:'
446 nulldev = 'NUL:'
447
447
448 class winstdout:
448 class winstdout:
449 '''stdout on windows misbehaves if sent through a pipe'''
449 '''stdout on windows misbehaves if sent through a pipe'''
450
450
451 def __init__(self, fp):
451 def __init__(self, fp):
452 self.fp = fp
452 self.fp = fp
453
453
454 def __getattr__(self, key):
454 def __getattr__(self, key):
455 return getattr(self.fp, key)
455 return getattr(self.fp, key)
456
456
457 def close(self):
457 def close(self):
458 try:
458 try:
459 self.fp.close()
459 self.fp.close()
460 except: pass
460 except: pass
461
461
462 def write(self, s):
462 def write(self, s):
463 try:
463 try:
464 return self.fp.write(s)
464 return self.fp.write(s)
465 except IOError, inst:
465 except IOError, inst:
466 if inst.errno != 0: raise
466 if inst.errno != 0: raise
467 self.close()
467 self.close()
468 raise IOError(errno.EPIPE, 'Broken pipe')
468 raise IOError(errno.EPIPE, 'Broken pipe')
469
469
470 sys.stdout = winstdout(sys.stdout)
470 sys.stdout = winstdout(sys.stdout)
471
471
472 try:
472 try:
473 import win32api, win32process
473 import win32api, win32process
474 filename = win32process.GetModuleFileNameEx(win32api.GetCurrentProcess(), 0)
474 filename = win32process.GetModuleFileNameEx(win32api.GetCurrentProcess(), 0)
475 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
475 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
476
476
477 except ImportError:
477 except ImportError:
478 systemrc = r'c:\mercurial\mercurial.ini'
478 systemrc = r'c:\mercurial\mercurial.ini'
479 pass
479 pass
480
480
481 rcpath = (systemrc,
481 rcpath = (systemrc,
482 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
482 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
483
483
484 def parse_patch_output(output_line):
484 def parse_patch_output(output_line):
485 """parses the output produced by patch and returns the file name"""
485 """parses the output produced by patch and returns the file name"""
486 pf = output_line[14:]
486 pf = output_line[14:]
487 if pf[0] == '`':
487 if pf[0] == '`':
488 pf = pf[1:-1] # Remove the quotes
488 pf = pf[1:-1] # Remove the quotes
489 return pf
489 return pf
490
490
491 try: # ActivePython can create hard links using win32file module
491 try: # ActivePython can create hard links using win32file module
492 import win32file
492 import win32file
493
493
494 def os_link(src, dst): # NB will only succeed on NTFS
494 def os_link(src, dst): # NB will only succeed on NTFS
495 win32file.CreateHardLink(dst, src)
495 win32file.CreateHardLink(dst, src)
496
496
497 def nlinks(pathname):
497 def nlinks(pathname):
498 """Return number of hardlinks for the given file."""
498 """Return number of hardlinks for the given file."""
499 try:
499 try:
500 fh = win32file.CreateFile(pathname,
500 fh = win32file.CreateFile(pathname,
501 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
501 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
502 None, win32file.OPEN_EXISTING, 0, None)
502 None, win32file.OPEN_EXISTING, 0, None)
503 res = win32file.GetFileInformationByHandle(fh)
503 res = win32file.GetFileInformationByHandle(fh)
504 fh.Close()
504 fh.Close()
505 return res[7]
505 return res[7]
506 except:
506 except:
507 return os.stat(pathname).st_nlink
507 return os.stat(pathname).st_nlink
508
508
509 except ImportError:
509 except ImportError:
510 pass
510 pass
511
511
512 def is_exec(f, last):
512 def is_exec(f, last):
513 return last
513 return last
514
514
515 def set_exec(f, mode):
515 def set_exec(f, mode):
516 pass
516 pass
517
517
518 def set_binary(fd):
518 def set_binary(fd):
519 msvcrt.setmode(fd.fileno(), os.O_BINARY)
519 msvcrt.setmode(fd.fileno(), os.O_BINARY)
520
520
521 def pconvert(path):
521 def pconvert(path):
522 return path.replace("\\", "/")
522 return path.replace("\\", "/")
523
523
524 def localpath(path):
524 def localpath(path):
525 return path.replace('/', '\\')
525 return path.replace('/', '\\')
526
526
527 def normpath(path):
527 def normpath(path):
528 return pconvert(os.path.normpath(path))
528 return pconvert(os.path.normpath(path))
529
529
530 makelock = _makelock_file
530 makelock = _makelock_file
531 readlock = _readlock_file
531 readlock = _readlock_file
532
532
533 def explain_exit(code):
533 def explain_exit(code):
534 return _("exited with status %d") % code, code
534 return _("exited with status %d") % code, code
535
535
536 else:
536 else:
537 nulldev = '/dev/null'
537 nulldev = '/dev/null'
538
538
539 def rcfiles(path):
539 def rcfiles(path):
540 rcs = [os.path.join(path, 'hgrc')]
540 rcs = [os.path.join(path, 'hgrc')]
541 rcdir = os.path.join(path, 'hgrc.d')
541 rcdir = os.path.join(path, 'hgrc.d')
542 try:
542 try:
543 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
543 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
544 if f.endswith(".rc")])
544 if f.endswith(".rc")])
545 except OSError, inst: pass
545 except OSError, inst: pass
546 return rcs
546 return rcs
547 rcpath = []
547 rcpath = []
548 if len(sys.argv) > 0:
548 if len(sys.argv) > 0:
549 rcpath.extend(rcfiles(os.path.dirname(sys.argv[0]) + '/../etc/mercurial'))
549 rcpath.extend(rcfiles(os.path.dirname(sys.argv[0]) + '/../etc/mercurial'))
550 rcpath.extend(rcfiles('/etc/mercurial'))
550 rcpath.extend(rcfiles('/etc/mercurial'))
551 rcpath.append(os.path.expanduser('~/.hgrc'))
551 rcpath.append(os.path.expanduser('~/.hgrc'))
552 rcpath = [os.path.normpath(f) for f in rcpath]
552 rcpath = [os.path.normpath(f) for f in rcpath]
553
553
554 def parse_patch_output(output_line):
554 def parse_patch_output(output_line):
555 """parses the output produced by patch and returns the file name"""
555 """parses the output produced by patch and returns the file name"""
556 pf = output_line[14:]
556 pf = output_line[14:]
557 if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0:
557 if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0:
558 pf = pf[1:-1] # Remove the quotes
558 pf = pf[1:-1] # Remove the quotes
559 return pf
559 return pf
560
560
561 def is_exec(f, last):
561 def is_exec(f, last):
562 """check whether a file is executable"""
562 """check whether a file is executable"""
563 return (os.stat(f).st_mode & 0100 != 0)
563 return (os.stat(f).st_mode & 0100 != 0)
564
564
565 def set_exec(f, mode):
565 def set_exec(f, mode):
566 s = os.stat(f).st_mode
566 s = os.stat(f).st_mode
567 if (s & 0100 != 0) == mode:
567 if (s & 0100 != 0) == mode:
568 return
568 return
569 if mode:
569 if mode:
570 # Turn on +x for every +r bit when making a file executable
570 # Turn on +x for every +r bit when making a file executable
571 # and obey umask.
571 # and obey umask.
572 umask = os.umask(0)
572 umask = os.umask(0)
573 os.umask(umask)
573 os.umask(umask)
574 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
574 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
575 else:
575 else:
576 os.chmod(f, s & 0666)
576 os.chmod(f, s & 0666)
577
577
578 def set_binary(fd):
578 def set_binary(fd):
579 pass
579 pass
580
580
581 def pconvert(path):
581 def pconvert(path):
582 return path
582 return path
583
583
584 def localpath(path):
584 def localpath(path):
585 return path
585 return path
586
586
587 normpath = os.path.normpath
587 normpath = os.path.normpath
588
588
589 def makelock(info, pathname):
589 def makelock(info, pathname):
590 try:
590 try:
591 os.symlink(info, pathname)
591 os.symlink(info, pathname)
592 except OSError, why:
592 except OSError, why:
593 if why.errno == errno.EEXIST:
593 if why.errno == errno.EEXIST:
594 raise
594 raise
595 else:
595 else:
596 _makelock_file(info, pathname)
596 _makelock_file(info, pathname)
597
597
598 def readlock(pathname):
598 def readlock(pathname):
599 try:
599 try:
600 return os.readlink(pathname)
600 return os.readlink(pathname)
601 except OSError, why:
601 except OSError, why:
602 if why.errno == errno.EINVAL:
602 if why.errno == errno.EINVAL:
603 return _readlock_file(pathname)
603 return _readlock_file(pathname)
604 else:
604 else:
605 raise
605 raise
606
606
607 def explain_exit(code):
607 def explain_exit(code):
608 """return a 2-tuple (desc, code) describing a process's status"""
608 """return a 2-tuple (desc, code) describing a process's status"""
609 if os.WIFEXITED(code):
609 if os.WIFEXITED(code):
610 val = os.WEXITSTATUS(code)
610 val = os.WEXITSTATUS(code)
611 return _("exited with status %d") % val, val
611 return _("exited with status %d") % val, val
612 elif os.WIFSIGNALED(code):
612 elif os.WIFSIGNALED(code):
613 val = os.WTERMSIG(code)
613 val = os.WTERMSIG(code)
614 return _("killed by signal %d") % val, val
614 return _("killed by signal %d") % val, val
615 elif os.WIFSTOPPED(code):
615 elif os.WIFSTOPPED(code):
616 val = os.WSTOPSIG(code)
616 val = os.WSTOPSIG(code)
617 return _("stopped by signal %d") % val, val
617 return _("stopped by signal %d") % val, val
618 raise ValueError(_("invalid exit code"))
618 raise ValueError(_("invalid exit code"))
619
619
620 class chunkbuffer(object):
620 class chunkbuffer(object):
621 """Allow arbitrary sized chunks of data to be efficiently read from an
621 """Allow arbitrary sized chunks of data to be efficiently read from an
622 iterator over chunks of arbitrary size."""
622 iterator over chunks of arbitrary size."""
623
623
624 def __init__(self, in_iter, targetsize = 2**16):
624 def __init__(self, in_iter, targetsize = 2**16):
625 """in_iter is the iterator that's iterating over the input chunks.
625 """in_iter is the iterator that's iterating over the input chunks.
626 targetsize is how big a buffer to try to maintain."""
626 targetsize is how big a buffer to try to maintain."""
627 self.in_iter = iter(in_iter)
627 self.in_iter = iter(in_iter)
628 self.buf = ''
628 self.buf = ''
629 self.targetsize = int(targetsize)
629 self.targetsize = int(targetsize)
630 if self.targetsize <= 0:
630 if self.targetsize <= 0:
631 raise ValueError(_("targetsize must be greater than 0, was %d") %
631 raise ValueError(_("targetsize must be greater than 0, was %d") %
632 targetsize)
632 targetsize)
633 self.iterempty = False
633 self.iterempty = False
634
634
635 def fillbuf(self):
635 def fillbuf(self):
636 """Ignore target size; read every chunk from iterator until empty."""
636 """Ignore target size; read every chunk from iterator until empty."""
637 if not self.iterempty:
637 if not self.iterempty:
638 collector = cStringIO.StringIO()
638 collector = cStringIO.StringIO()
639 collector.write(self.buf)
639 collector.write(self.buf)
640 for ch in self.in_iter:
640 for ch in self.in_iter:
641 collector.write(ch)
641 collector.write(ch)
642 self.buf = collector.getvalue()
642 self.buf = collector.getvalue()
643 self.iterempty = True
643 self.iterempty = True
644
644
645 def read(self, l):
645 def read(self, l):
646 """Read L bytes of data from the iterator of chunks of data.
646 """Read L bytes of data from the iterator of chunks of data.
647 Returns less than L bytes if the iterator runs dry."""
647 Returns less than L bytes if the iterator runs dry."""
648 if l > len(self.buf) and not self.iterempty:
648 if l > len(self.buf) and not self.iterempty:
649 # Clamp to a multiple of self.targetsize
649 # Clamp to a multiple of self.targetsize
650 targetsize = self.targetsize * ((l // self.targetsize) + 1)
650 targetsize = self.targetsize * ((l // self.targetsize) + 1)
651 collector = cStringIO.StringIO()
651 collector = cStringIO.StringIO()
652 collector.write(self.buf)
652 collector.write(self.buf)
653 collected = len(self.buf)
653 collected = len(self.buf)
654 for chunk in self.in_iter:
654 for chunk in self.in_iter:
655 collector.write(chunk)
655 collector.write(chunk)
656 collected += len(chunk)
656 collected += len(chunk)
657 if collected >= targetsize:
657 if collected >= targetsize:
658 break
658 break
659 if collected < targetsize:
659 if collected < targetsize:
660 self.iterempty = True
660 self.iterempty = True
661 self.buf = collector.getvalue()
661 self.buf = collector.getvalue()
662 s, self.buf = self.buf[:l], buffer(self.buf, l)
662 s, self.buf = self.buf[:l], buffer(self.buf, l)
663 return s
663 return s
664
664
665 def filechunkiter(f, size = 65536):
665 def filechunkiter(f, size = 65536):
666 """Create a generator that produces all the data in the file size
666 """Create a generator that produces all the data in the file size
667 (default 65536) bytes at a time. Chunks may be less than size
667 (default 65536) bytes at a time. Chunks may be less than size
668 bytes if the chunk is the last chunk in the file, or the file is a
668 bytes if the chunk is the last chunk in the file, or the file is a
669 socket or some other type of file that sometimes reads less data
669 socket or some other type of file that sometimes reads less data
670 than is requested."""
670 than is requested."""
671 s = f.read(size)
671 s = f.read(size)
672 while len(s) > 0:
672 while len(s) > 0:
673 yield s
673 yield s
674 s = f.read(size)
674 s = f.read(size)
675
675
676 def makedate():
676 def makedate():
677 lt = time.localtime()
677 lt = time.localtime()
678 if lt[8] == 1 and time.daylight:
678 if lt[8] == 1 and time.daylight:
679 tz = time.altzone
679 tz = time.altzone
680 else:
680 else:
681 tz = time.timezone
681 tz = time.timezone
682 return time.mktime(lt), tz
682 return time.mktime(lt), tz
683
683
684 def datestr(date=None, format='%c'):
684 def datestr(date=None, format='%c'):
685 """represent a (unixtime, offset) tuple as a localized time.
685 """represent a (unixtime, offset) tuple as a localized time.
686 unixtime is seconds since the epoch, and offset is the time zone's
686 unixtime is seconds since the epoch, and offset is the time zone's
687 number of seconds away from UTC."""
687 number of seconds away from UTC."""
688 t, tz = date or makedate()
688 t, tz = date or makedate()
689 return ("%s %+03d%02d" %
689 return ("%s %+03d%02d" %
690 (time.strftime(format, time.gmtime(float(t) - tz)),
690 (time.strftime(format, time.gmtime(float(t) - tz)),
691 -tz / 3600,
691 -tz / 3600,
692 ((-tz % 3600) / 60)))
692 ((-tz % 3600) / 60)))
693
694 def shortuser(user):
695 """Return a short representation of a user name or email address."""
696 f = user.find('@')
697 if f >= 0:
698 user = user[:f]
699 f = user.find('<')
700 if f >= 0:
701 user = user[f+1:]
702 return user
General Comments 0
You need to be logged in to leave comments. Login now