Statistics
| Branch: | Tag: | Revision:

root / lib / utils / text.py @ d728ac75

History | View | Annotate | Download (14 kB)

1 7fcffe27 Michael Hanselmann
#
2 7fcffe27 Michael Hanselmann
#
3 7fcffe27 Michael Hanselmann
4 7fcffe27 Michael Hanselmann
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5 7fcffe27 Michael Hanselmann
#
6 7fcffe27 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 7fcffe27 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 7fcffe27 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 7fcffe27 Michael Hanselmann
# (at your option) any later version.
10 7fcffe27 Michael Hanselmann
#
11 7fcffe27 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 7fcffe27 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 7fcffe27 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 7fcffe27 Michael Hanselmann
# General Public License for more details.
15 7fcffe27 Michael Hanselmann
#
16 7fcffe27 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 7fcffe27 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 7fcffe27 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 7fcffe27 Michael Hanselmann
# 02110-1301, USA.
20 7fcffe27 Michael Hanselmann
21 7fcffe27 Michael Hanselmann
"""Utility functions for manipulating or working with text.
22 7fcffe27 Michael Hanselmann

23 7fcffe27 Michael Hanselmann
"""
24 7fcffe27 Michael Hanselmann
25 7fcffe27 Michael Hanselmann
26 7fcffe27 Michael Hanselmann
import re
27 7fcffe27 Michael Hanselmann
import os
28 7fcffe27 Michael Hanselmann
import time
29 7fcffe27 Michael Hanselmann
import collections
30 7fcffe27 Michael Hanselmann
31 7fcffe27 Michael Hanselmann
from ganeti import errors
32 7fcffe27 Michael Hanselmann
33 7fcffe27 Michael Hanselmann
34 7fcffe27 Michael Hanselmann
#: Unit checker regexp
35 7fcffe27 Michael Hanselmann
_PARSEUNIT_REGEX = re.compile(r"^([.\d]+)\s*([a-zA-Z]+)?$")
36 7fcffe27 Michael Hanselmann
37 7fcffe27 Michael Hanselmann
#: Characters which don't need to be quoted for shell commands
38 d0c8c01d Iustin Pop
_SHELL_UNQUOTED_RE = re.compile("^[-.,=:/_+@A-Za-z0-9]+$")
39 7fcffe27 Michael Hanselmann
40 7fcffe27 Michael Hanselmann
#: MAC checker regexp
41 7fcffe27 Michael Hanselmann
_MAC_CHECK_RE = re.compile("^([0-9a-f]{2}:){5}[0-9a-f]{2}$", re.I)
42 7fcffe27 Michael Hanselmann
43 7ebd876f Michael Hanselmann
#: Shell param checker regexp
44 7ebd876f Michael Hanselmann
_SHELLPARAM_REGEX = re.compile(r"^[-a-zA-Z0-9._+/:%@]+$")
45 7ebd876f Michael Hanselmann
46 7fcffe27 Michael Hanselmann
47 7fcffe27 Michael Hanselmann
def MatchNameComponent(key, name_list, case_sensitive=True):
48 7fcffe27 Michael Hanselmann
  """Try to match a name against a list.
49 7fcffe27 Michael Hanselmann

50 7fcffe27 Michael Hanselmann
  This function will try to match a name like test1 against a list
51 7fcffe27 Michael Hanselmann
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
52 7fcffe27 Michael Hanselmann
  this list, I{'test1'} as well as I{'test1.example'} will match, but
53 7fcffe27 Michael Hanselmann
  not I{'test1.ex'}. A multiple match will be considered as no match
54 7fcffe27 Michael Hanselmann
  at all (e.g. I{'test1'} against C{['test1.example.com',
55 7fcffe27 Michael Hanselmann
  'test1.example.org']}), except when the key fully matches an entry
56 7fcffe27 Michael Hanselmann
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
57 7fcffe27 Michael Hanselmann

58 7fcffe27 Michael Hanselmann
  @type key: str
59 7fcffe27 Michael Hanselmann
  @param key: the name to be searched
60 7fcffe27 Michael Hanselmann
  @type name_list: list
61 7fcffe27 Michael Hanselmann
  @param name_list: the list of strings against which to search the key
62 7fcffe27 Michael Hanselmann
  @type case_sensitive: boolean
63 7fcffe27 Michael Hanselmann
  @param case_sensitive: whether to provide a case-sensitive match
64 7fcffe27 Michael Hanselmann

65 7fcffe27 Michael Hanselmann
  @rtype: None or str
66 7fcffe27 Michael Hanselmann
  @return: None if there is no match I{or} if there are multiple matches,
67 7fcffe27 Michael Hanselmann
      otherwise the element from the list which matches
68 7fcffe27 Michael Hanselmann

69 7fcffe27 Michael Hanselmann
  """
70 7fcffe27 Michael Hanselmann
  if key in name_list:
71 7fcffe27 Michael Hanselmann
    return key
72 7fcffe27 Michael Hanselmann
73 7fcffe27 Michael Hanselmann
  re_flags = 0
74 7fcffe27 Michael Hanselmann
  if not case_sensitive:
75 7fcffe27 Michael Hanselmann
    re_flags |= re.IGNORECASE
76 7fcffe27 Michael Hanselmann
    key = key.upper()
77 bbfed756 Michael Hanselmann
78 bbfed756 Michael Hanselmann
  name_re = re.compile(r"^%s(\..*)?$" % re.escape(key), re_flags)
79 bbfed756 Michael Hanselmann
80 7fcffe27 Michael Hanselmann
  names_filtered = []
81 7fcffe27 Michael Hanselmann
  string_matches = []
82 7fcffe27 Michael Hanselmann
  for name in name_list:
83 bbfed756 Michael Hanselmann
    if name_re.match(name) is not None:
84 7fcffe27 Michael Hanselmann
      names_filtered.append(name)
85 7fcffe27 Michael Hanselmann
      if not case_sensitive and key == name.upper():
86 7fcffe27 Michael Hanselmann
        string_matches.append(name)
87 7fcffe27 Michael Hanselmann
88 7fcffe27 Michael Hanselmann
  if len(string_matches) == 1:
89 7fcffe27 Michael Hanselmann
    return string_matches[0]
90 7fcffe27 Michael Hanselmann
  if len(names_filtered) == 1:
91 7fcffe27 Michael Hanselmann
    return names_filtered[0]
92 bbfed756 Michael Hanselmann
93 7fcffe27 Michael Hanselmann
  return None
94 7fcffe27 Michael Hanselmann
95 7fcffe27 Michael Hanselmann
96 bbfed756 Michael Hanselmann
def _DnsNameGlobHelper(match):
97 bbfed756 Michael Hanselmann
  """Helper function for L{DnsNameGlobPattern}.
98 bbfed756 Michael Hanselmann

99 bbfed756 Michael Hanselmann
  Returns regular expression pattern for parts of the pattern.
100 bbfed756 Michael Hanselmann

101 bbfed756 Michael Hanselmann
  """
102 bbfed756 Michael Hanselmann
  text = match.group(0)
103 bbfed756 Michael Hanselmann
104 bbfed756 Michael Hanselmann
  if text == "*":
105 bbfed756 Michael Hanselmann
    return "[^.]*"
106 bbfed756 Michael Hanselmann
  elif text == "?":
107 bbfed756 Michael Hanselmann
    return "[^.]"
108 bbfed756 Michael Hanselmann
  else:
109 bbfed756 Michael Hanselmann
    return re.escape(text)
110 bbfed756 Michael Hanselmann
111 bbfed756 Michael Hanselmann
112 bbfed756 Michael Hanselmann
def DnsNameGlobPattern(pattern):
113 bbfed756 Michael Hanselmann
  """Generates regular expression from DNS name globbing pattern.
114 bbfed756 Michael Hanselmann

115 bbfed756 Michael Hanselmann
  A DNS name globbing pattern (e.g. C{*.site}) is converted to a regular
116 bbfed756 Michael Hanselmann
  expression. Escape sequences or ranges (e.g. [a-z]) are not supported.
117 bbfed756 Michael Hanselmann

118 bbfed756 Michael Hanselmann
  Matching always starts at the leftmost part. An asterisk (*) matches all
119 bbfed756 Michael Hanselmann
  characters except the dot (.) separating DNS name parts. A question mark (?)
120 bbfed756 Michael Hanselmann
  matches a single character except the dot (.).
121 bbfed756 Michael Hanselmann

122 bbfed756 Michael Hanselmann
  @type pattern: string
123 bbfed756 Michael Hanselmann
  @param pattern: DNS name globbing pattern
124 bbfed756 Michael Hanselmann
  @rtype: string
125 bbfed756 Michael Hanselmann
  @return: Regular expression
126 bbfed756 Michael Hanselmann

127 bbfed756 Michael Hanselmann
  """
128 bbfed756 Michael Hanselmann
  return r"^%s(\..*)?$" % re.sub(r"\*|\?|[^*?]*", _DnsNameGlobHelper, pattern)
129 bbfed756 Michael Hanselmann
130 bbfed756 Michael Hanselmann
131 7fcffe27 Michael Hanselmann
def FormatUnit(value, units):
132 7fcffe27 Michael Hanselmann
  """Formats an incoming number of MiB with the appropriate unit.
133 7fcffe27 Michael Hanselmann

134 7fcffe27 Michael Hanselmann
  @type value: int
135 7fcffe27 Michael Hanselmann
  @param value: integer representing the value in MiB (1048576)
136 7fcffe27 Michael Hanselmann
  @type units: char
137 7fcffe27 Michael Hanselmann
  @param units: the type of formatting we should do:
138 7fcffe27 Michael Hanselmann
      - 'h' for automatic scaling
139 7fcffe27 Michael Hanselmann
      - 'm' for MiBs
140 7fcffe27 Michael Hanselmann
      - 'g' for GiBs
141 7fcffe27 Michael Hanselmann
      - 't' for TiBs
142 7fcffe27 Michael Hanselmann
  @rtype: str
143 7fcffe27 Michael Hanselmann
  @return: the formatted value (with suffix)
144 7fcffe27 Michael Hanselmann

145 7fcffe27 Michael Hanselmann
  """
146 d0c8c01d Iustin Pop
  if units not in ("m", "g", "t", "h"):
147 7fcffe27 Michael Hanselmann
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
148 7fcffe27 Michael Hanselmann
149 d0c8c01d Iustin Pop
  suffix = ""
150 7fcffe27 Michael Hanselmann
151 d0c8c01d Iustin Pop
  if units == "m" or (units == "h" and value < 1024):
152 d0c8c01d Iustin Pop
    if units == "h":
153 d0c8c01d Iustin Pop
      suffix = "M"
154 7fcffe27 Michael Hanselmann
    return "%d%s" % (round(value, 0), suffix)
155 7fcffe27 Michael Hanselmann
156 d0c8c01d Iustin Pop
  elif units == "g" or (units == "h" and value < (1024 * 1024)):
157 d0c8c01d Iustin Pop
    if units == "h":
158 d0c8c01d Iustin Pop
      suffix = "G"
159 7fcffe27 Michael Hanselmann
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
160 7fcffe27 Michael Hanselmann
161 7fcffe27 Michael Hanselmann
  else:
162 d0c8c01d Iustin Pop
    if units == "h":
163 d0c8c01d Iustin Pop
      suffix = "T"
164 7fcffe27 Michael Hanselmann
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
165 7fcffe27 Michael Hanselmann
166 7fcffe27 Michael Hanselmann
167 7fcffe27 Michael Hanselmann
def ParseUnit(input_string):
168 7fcffe27 Michael Hanselmann
  """Tries to extract number and scale from the given string.
169 7fcffe27 Michael Hanselmann

170 7fcffe27 Michael Hanselmann
  Input must be in the format C{NUMBER+ [DOT NUMBER+] SPACE*
171 7fcffe27 Michael Hanselmann
  [UNIT]}. If no unit is specified, it defaults to MiB. Return value
172 7fcffe27 Michael Hanselmann
  is always an int in MiB.
173 7fcffe27 Michael Hanselmann

174 7fcffe27 Michael Hanselmann
  """
175 7fcffe27 Michael Hanselmann
  m = _PARSEUNIT_REGEX.match(str(input_string))
176 7fcffe27 Michael Hanselmann
  if not m:
177 7fcffe27 Michael Hanselmann
    raise errors.UnitParseError("Invalid format")
178 7fcffe27 Michael Hanselmann
179 7fcffe27 Michael Hanselmann
  value = float(m.groups()[0])
180 7fcffe27 Michael Hanselmann
181 7fcffe27 Michael Hanselmann
  unit = m.groups()[1]
182 7fcffe27 Michael Hanselmann
  if unit:
183 7fcffe27 Michael Hanselmann
    lcunit = unit.lower()
184 7fcffe27 Michael Hanselmann
  else:
185 d0c8c01d Iustin Pop
    lcunit = "m"
186 7fcffe27 Michael Hanselmann
187 d0c8c01d Iustin Pop
  if lcunit in ("m", "mb", "mib"):
188 7fcffe27 Michael Hanselmann
    # Value already in MiB
189 7fcffe27 Michael Hanselmann
    pass
190 7fcffe27 Michael Hanselmann
191 d0c8c01d Iustin Pop
  elif lcunit in ("g", "gb", "gib"):
192 7fcffe27 Michael Hanselmann
    value *= 1024
193 7fcffe27 Michael Hanselmann
194 d0c8c01d Iustin Pop
  elif lcunit in ("t", "tb", "tib"):
195 7fcffe27 Michael Hanselmann
    value *= 1024 * 1024
196 7fcffe27 Michael Hanselmann
197 7fcffe27 Michael Hanselmann
  else:
198 7fcffe27 Michael Hanselmann
    raise errors.UnitParseError("Unknown unit: %s" % unit)
199 7fcffe27 Michael Hanselmann
200 7fcffe27 Michael Hanselmann
  # Make sure we round up
201 7fcffe27 Michael Hanselmann
  if int(value) < value:
202 7fcffe27 Michael Hanselmann
    value += 1
203 7fcffe27 Michael Hanselmann
204 7fcffe27 Michael Hanselmann
  # Round up to the next multiple of 4
205 7fcffe27 Michael Hanselmann
  value = int(value)
206 7fcffe27 Michael Hanselmann
  if value % 4:
207 7fcffe27 Michael Hanselmann
    value += 4 - value % 4
208 7fcffe27 Michael Hanselmann
209 7fcffe27 Michael Hanselmann
  return value
210 7fcffe27 Michael Hanselmann
211 7fcffe27 Michael Hanselmann
212 7fcffe27 Michael Hanselmann
def ShellQuote(value):
213 7fcffe27 Michael Hanselmann
  """Quotes shell argument according to POSIX.
214 7fcffe27 Michael Hanselmann

215 7fcffe27 Michael Hanselmann
  @type value: str
216 7fcffe27 Michael Hanselmann
  @param value: the argument to be quoted
217 7fcffe27 Michael Hanselmann
  @rtype: str
218 7fcffe27 Michael Hanselmann
  @return: the quoted value
219 7fcffe27 Michael Hanselmann

220 7fcffe27 Michael Hanselmann
  """
221 7fcffe27 Michael Hanselmann
  if _SHELL_UNQUOTED_RE.match(value):
222 7fcffe27 Michael Hanselmann
    return value
223 7fcffe27 Michael Hanselmann
  else:
224 7fcffe27 Michael Hanselmann
    return "'%s'" % value.replace("'", "'\\''")
225 7fcffe27 Michael Hanselmann
226 7fcffe27 Michael Hanselmann
227 7fcffe27 Michael Hanselmann
def ShellQuoteArgs(args):
228 7fcffe27 Michael Hanselmann
  """Quotes a list of shell arguments.
229 7fcffe27 Michael Hanselmann

230 7fcffe27 Michael Hanselmann
  @type args: list
231 7fcffe27 Michael Hanselmann
  @param args: list of arguments to be quoted
232 7fcffe27 Michael Hanselmann
  @rtype: str
233 7fcffe27 Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
234 7fcffe27 Michael Hanselmann

235 7fcffe27 Michael Hanselmann
  """
236 7fcffe27 Michael Hanselmann
  return " ".join([ShellQuote(i) for i in args])
237 7fcffe27 Michael Hanselmann
238 7fcffe27 Michael Hanselmann
239 7fcffe27 Michael Hanselmann
class ShellWriter:
240 7fcffe27 Michael Hanselmann
  """Helper class to write scripts with indentation.
241 7fcffe27 Michael Hanselmann

242 7fcffe27 Michael Hanselmann
  """
243 7fcffe27 Michael Hanselmann
  INDENT_STR = "  "
244 7fcffe27 Michael Hanselmann
245 7fcffe27 Michael Hanselmann
  def __init__(self, fh):
246 7fcffe27 Michael Hanselmann
    """Initializes this class.
247 7fcffe27 Michael Hanselmann

248 7fcffe27 Michael Hanselmann
    """
249 7fcffe27 Michael Hanselmann
    self._fh = fh
250 7fcffe27 Michael Hanselmann
    self._indent = 0
251 7fcffe27 Michael Hanselmann
252 7fcffe27 Michael Hanselmann
  def IncIndent(self):
253 7fcffe27 Michael Hanselmann
    """Increase indentation level by 1.
254 7fcffe27 Michael Hanselmann

255 7fcffe27 Michael Hanselmann
    """
256 7fcffe27 Michael Hanselmann
    self._indent += 1
257 7fcffe27 Michael Hanselmann
258 7fcffe27 Michael Hanselmann
  def DecIndent(self):
259 7fcffe27 Michael Hanselmann
    """Decrease indentation level by 1.
260 7fcffe27 Michael Hanselmann

261 7fcffe27 Michael Hanselmann
    """
262 7fcffe27 Michael Hanselmann
    assert self._indent > 0
263 7fcffe27 Michael Hanselmann
    self._indent -= 1
264 7fcffe27 Michael Hanselmann
265 7fcffe27 Michael Hanselmann
  def Write(self, txt, *args):
266 7fcffe27 Michael Hanselmann
    """Write line to output file.
267 7fcffe27 Michael Hanselmann

268 7fcffe27 Michael Hanselmann
    """
269 7fcffe27 Michael Hanselmann
    assert self._indent >= 0
270 7fcffe27 Michael Hanselmann
271 7fcffe27 Michael Hanselmann
    self._fh.write(self._indent * self.INDENT_STR)
272 7fcffe27 Michael Hanselmann
273 7fcffe27 Michael Hanselmann
    if args:
274 7fcffe27 Michael Hanselmann
      self._fh.write(txt % args)
275 7fcffe27 Michael Hanselmann
    else:
276 7fcffe27 Michael Hanselmann
      self._fh.write(txt)
277 7fcffe27 Michael Hanselmann
278 7fcffe27 Michael Hanselmann
    self._fh.write("\n")
279 7fcffe27 Michael Hanselmann
280 7fcffe27 Michael Hanselmann
281 7fcffe27 Michael Hanselmann
def GenerateSecret(numbytes=20):
282 7fcffe27 Michael Hanselmann
  """Generates a random secret.
283 7fcffe27 Michael Hanselmann

284 7fcffe27 Michael Hanselmann
  This will generate a pseudo-random secret returning an hex string
285 7fcffe27 Michael Hanselmann
  (so that it can be used where an ASCII string is needed).
286 7fcffe27 Michael Hanselmann

287 7fcffe27 Michael Hanselmann
  @param numbytes: the number of bytes which will be represented by the returned
288 7fcffe27 Michael Hanselmann
      string (defaulting to 20, the length of a SHA1 hash)
289 7fcffe27 Michael Hanselmann
  @rtype: str
290 7fcffe27 Michael Hanselmann
  @return: an hex representation of the pseudo-random sequence
291 7fcffe27 Michael Hanselmann

292 7fcffe27 Michael Hanselmann
  """
293 7fcffe27 Michael Hanselmann
  return os.urandom(numbytes).encode("hex")
294 7fcffe27 Michael Hanselmann
295 7fcffe27 Michael Hanselmann
296 7fcffe27 Michael Hanselmann
def NormalizeAndValidateMac(mac):
297 7fcffe27 Michael Hanselmann
  """Normalizes and check if a MAC address is valid.
298 7fcffe27 Michael Hanselmann

299 7fcffe27 Michael Hanselmann
  Checks whether the supplied MAC address is formally correct, only
300 7fcffe27 Michael Hanselmann
  accepts colon separated format. Normalize it to all lower.
301 7fcffe27 Michael Hanselmann

302 7fcffe27 Michael Hanselmann
  @type mac: str
303 7fcffe27 Michael Hanselmann
  @param mac: the MAC to be validated
304 7fcffe27 Michael Hanselmann
  @rtype: str
305 7fcffe27 Michael Hanselmann
  @return: returns the normalized and validated MAC.
306 7fcffe27 Michael Hanselmann

307 7fcffe27 Michael Hanselmann
  @raise errors.OpPrereqError: If the MAC isn't valid
308 7fcffe27 Michael Hanselmann

309 7fcffe27 Michael Hanselmann
  """
310 7fcffe27 Michael Hanselmann
  if not _MAC_CHECK_RE.match(mac):
311 7fcffe27 Michael Hanselmann
    raise errors.OpPrereqError("Invalid MAC address '%s'" % mac,
312 7fcffe27 Michael Hanselmann
                               errors.ECODE_INVAL)
313 7fcffe27 Michael Hanselmann
314 7fcffe27 Michael Hanselmann
  return mac.lower()
315 7fcffe27 Michael Hanselmann
316 7fcffe27 Michael Hanselmann
317 7fcffe27 Michael Hanselmann
def SafeEncode(text):
318 7fcffe27 Michael Hanselmann
  """Return a 'safe' version of a source string.
319 7fcffe27 Michael Hanselmann

320 7fcffe27 Michael Hanselmann
  This function mangles the input string and returns a version that
321 7fcffe27 Michael Hanselmann
  should be safe to display/encode as ASCII. To this end, we first
322 7fcffe27 Michael Hanselmann
  convert it to ASCII using the 'backslashreplace' encoding which
323 7fcffe27 Michael Hanselmann
  should get rid of any non-ASCII chars, and then we process it
324 7fcffe27 Michael Hanselmann
  through a loop copied from the string repr sources in the python; we
325 7fcffe27 Michael Hanselmann
  don't use string_escape anymore since that escape single quotes and
326 7fcffe27 Michael Hanselmann
  backslashes too, and that is too much; and that escaping is not
327 7fcffe27 Michael Hanselmann
  stable, i.e. string_escape(string_escape(x)) != string_escape(x).
328 7fcffe27 Michael Hanselmann

329 7fcffe27 Michael Hanselmann
  @type text: str or unicode
330 7fcffe27 Michael Hanselmann
  @param text: input data
331 7fcffe27 Michael Hanselmann
  @rtype: str
332 7fcffe27 Michael Hanselmann
  @return: a safe version of text
333 7fcffe27 Michael Hanselmann

334 7fcffe27 Michael Hanselmann
  """
335 7fcffe27 Michael Hanselmann
  if isinstance(text, unicode):
336 7fcffe27 Michael Hanselmann
    # only if unicode; if str already, we handle it below
337 d0c8c01d Iustin Pop
    text = text.encode("ascii", "backslashreplace")
338 7fcffe27 Michael Hanselmann
  resu = ""
339 7fcffe27 Michael Hanselmann
  for char in text:
340 7fcffe27 Michael Hanselmann
    c = ord(char)
341 e687ec01 Michael Hanselmann
    if char == "\t":
342 d0c8c01d Iustin Pop
      resu += r"\t"
343 d0c8c01d Iustin Pop
    elif char == "\n":
344 d0c8c01d Iustin Pop
      resu += r"\n"
345 d0c8c01d Iustin Pop
    elif char == "\r":
346 7fcffe27 Michael Hanselmann
      resu += r'\'r'
347 7fcffe27 Michael Hanselmann
    elif c < 32 or c >= 127: # non-printable
348 7fcffe27 Michael Hanselmann
      resu += "\\x%02x" % (c & 0xff)
349 7fcffe27 Michael Hanselmann
    else:
350 7fcffe27 Michael Hanselmann
      resu += char
351 7fcffe27 Michael Hanselmann
  return resu
352 7fcffe27 Michael Hanselmann
353 7fcffe27 Michael Hanselmann
354 7fcffe27 Michael Hanselmann
def UnescapeAndSplit(text, sep=","):
355 7fcffe27 Michael Hanselmann
  """Split and unescape a string based on a given separator.
356 7fcffe27 Michael Hanselmann

357 7fcffe27 Michael Hanselmann
  This function splits a string based on a separator where the
358 7fcffe27 Michael Hanselmann
  separator itself can be escape in order to be an element of the
359 7fcffe27 Michael Hanselmann
  elements. The escaping rules are (assuming coma being the
360 7fcffe27 Michael Hanselmann
  separator):
361 7fcffe27 Michael Hanselmann
    - a plain , separates the elements
362 7fcffe27 Michael Hanselmann
    - a sequence \\\\, (double backslash plus comma) is handled as a
363 7fcffe27 Michael Hanselmann
      backslash plus a separator comma
364 7fcffe27 Michael Hanselmann
    - a sequence \, (backslash plus comma) is handled as a
365 7fcffe27 Michael Hanselmann
      non-separator comma
366 7fcffe27 Michael Hanselmann

367 7fcffe27 Michael Hanselmann
  @type text: string
368 7fcffe27 Michael Hanselmann
  @param text: the string to split
369 7fcffe27 Michael Hanselmann
  @type sep: string
370 7fcffe27 Michael Hanselmann
  @param text: the separator
371 7fcffe27 Michael Hanselmann
  @rtype: string
372 7fcffe27 Michael Hanselmann
  @return: a list of strings
373 7fcffe27 Michael Hanselmann

374 7fcffe27 Michael Hanselmann
  """
375 7fcffe27 Michael Hanselmann
  # we split the list by sep (with no escaping at this stage)
376 7fcffe27 Michael Hanselmann
  slist = text.split(sep)
377 7fcffe27 Michael Hanselmann
  # next, we revisit the elements and if any of them ended with an odd
378 7fcffe27 Michael Hanselmann
  # number of backslashes, then we join it with the next
379 7fcffe27 Michael Hanselmann
  rlist = []
380 7fcffe27 Michael Hanselmann
  while slist:
381 7fcffe27 Michael Hanselmann
    e1 = slist.pop(0)
382 7fcffe27 Michael Hanselmann
    if e1.endswith("\\"):
383 7fcffe27 Michael Hanselmann
      num_b = len(e1) - len(e1.rstrip("\\"))
384 e4a48c7b Michael Hanselmann
      if num_b % 2 == 1 and slist:
385 7fcffe27 Michael Hanselmann
        e2 = slist.pop(0)
386 7fcffe27 Michael Hanselmann
        # here the backslashes remain (all), and will be reduced in
387 7fcffe27 Michael Hanselmann
        # the next step
388 7fcffe27 Michael Hanselmann
        rlist.append(e1 + sep + e2)
389 7fcffe27 Michael Hanselmann
        continue
390 7fcffe27 Michael Hanselmann
    rlist.append(e1)
391 7fcffe27 Michael Hanselmann
  # finally, replace backslash-something with something
392 7fcffe27 Michael Hanselmann
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
393 7fcffe27 Michael Hanselmann
  return rlist
394 7fcffe27 Michael Hanselmann
395 7fcffe27 Michael Hanselmann
396 7fcffe27 Michael Hanselmann
def CommaJoin(names):
397 7fcffe27 Michael Hanselmann
  """Nicely join a set of identifiers.
398 7fcffe27 Michael Hanselmann

399 7fcffe27 Michael Hanselmann
  @param names: set, list or tuple
400 7fcffe27 Michael Hanselmann
  @return: a string with the formatted results
401 7fcffe27 Michael Hanselmann

402 7fcffe27 Michael Hanselmann
  """
403 7fcffe27 Michael Hanselmann
  return ", ".join([str(val) for val in names])
404 7fcffe27 Michael Hanselmann
405 7fcffe27 Michael Hanselmann
406 7fcffe27 Michael Hanselmann
def FormatTime(val):
407 7fcffe27 Michael Hanselmann
  """Formats a time value.
408 7fcffe27 Michael Hanselmann

409 7fcffe27 Michael Hanselmann
  @type val: float or None
410 7fcffe27 Michael Hanselmann
  @param val: Timestamp as returned by time.time() (seconds since Epoch,
411 7fcffe27 Michael Hanselmann
    1970-01-01 00:00:00 UTC)
412 7fcffe27 Michael Hanselmann
  @return: a string value or N/A if we don't have a valid timestamp
413 7fcffe27 Michael Hanselmann

414 7fcffe27 Michael Hanselmann
  """
415 7fcffe27 Michael Hanselmann
  if val is None or not isinstance(val, (int, float)):
416 7fcffe27 Michael Hanselmann
    return "N/A"
417 7fcffe27 Michael Hanselmann
  # these two codes works on Linux, but they are not guaranteed on all
418 7fcffe27 Michael Hanselmann
  # platforms
419 7fcffe27 Michael Hanselmann
  return time.strftime("%F %T", time.localtime(val))
420 7fcffe27 Michael Hanselmann
421 7fcffe27 Michael Hanselmann
422 7fcffe27 Michael Hanselmann
def FormatSeconds(secs):
423 7fcffe27 Michael Hanselmann
  """Formats seconds for easier reading.
424 7fcffe27 Michael Hanselmann

425 7fcffe27 Michael Hanselmann
  @type secs: number
426 7fcffe27 Michael Hanselmann
  @param secs: Number of seconds
427 7fcffe27 Michael Hanselmann
  @rtype: string
428 7fcffe27 Michael Hanselmann
  @return: Formatted seconds (e.g. "2d 9h 19m 49s")
429 7fcffe27 Michael Hanselmann

430 7fcffe27 Michael Hanselmann
  """
431 7fcffe27 Michael Hanselmann
  parts = []
432 7fcffe27 Michael Hanselmann
433 7fcffe27 Michael Hanselmann
  secs = round(secs, 0)
434 7fcffe27 Michael Hanselmann
435 7fcffe27 Michael Hanselmann
  if secs > 0:
436 7fcffe27 Michael Hanselmann
    # Negative values would be a bit tricky
437 7fcffe27 Michael Hanselmann
    for unit, one in [("d", 24 * 60 * 60), ("h", 60 * 60), ("m", 60)]:
438 7fcffe27 Michael Hanselmann
      (complete, secs) = divmod(secs, one)
439 7fcffe27 Michael Hanselmann
      if complete or parts:
440 7fcffe27 Michael Hanselmann
        parts.append("%d%s" % (complete, unit))
441 7fcffe27 Michael Hanselmann
442 7fcffe27 Michael Hanselmann
  parts.append("%ds" % secs)
443 7fcffe27 Michael Hanselmann
444 7fcffe27 Michael Hanselmann
  return " ".join(parts)
445 7fcffe27 Michael Hanselmann
446 7fcffe27 Michael Hanselmann
447 7fcffe27 Michael Hanselmann
class LineSplitter:
448 7fcffe27 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
449 7fcffe27 Michael Hanselmann

450 7fcffe27 Michael Hanselmann
  Instances provide a file-like interface.
451 7fcffe27 Michael Hanselmann

452 7fcffe27 Michael Hanselmann
  """
453 7fcffe27 Michael Hanselmann
  def __init__(self, line_fn, *args):
454 7fcffe27 Michael Hanselmann
    """Initializes this class.
455 7fcffe27 Michael Hanselmann

456 7fcffe27 Michael Hanselmann
    @type line_fn: callable
457 7fcffe27 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
458 7fcffe27 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
459 7fcffe27 Michael Hanselmann

460 7fcffe27 Michael Hanselmann
    """
461 7fcffe27 Michael Hanselmann
    assert callable(line_fn)
462 7fcffe27 Michael Hanselmann
463 7fcffe27 Michael Hanselmann
    if args:
464 7fcffe27 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
465 7fcffe27 Michael Hanselmann
      self._line_fn = \
466 b459a848 Andrea Spadaccini
        lambda line: line_fn(line, *args) # pylint: disable=W0142
467 7fcffe27 Michael Hanselmann
    else:
468 7fcffe27 Michael Hanselmann
      self._line_fn = line_fn
469 7fcffe27 Michael Hanselmann
470 7fcffe27 Michael Hanselmann
    self._lines = collections.deque()
471 7fcffe27 Michael Hanselmann
    self._buffer = ""
472 7fcffe27 Michael Hanselmann
473 7fcffe27 Michael Hanselmann
  def write(self, data):
474 7fcffe27 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
475 7fcffe27 Michael Hanselmann
    self._buffer = parts.pop()
476 7fcffe27 Michael Hanselmann
    self._lines.extend(parts)
477 7fcffe27 Michael Hanselmann
478 7fcffe27 Michael Hanselmann
  def flush(self):
479 7fcffe27 Michael Hanselmann
    while self._lines:
480 7fcffe27 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
481 7fcffe27 Michael Hanselmann
482 7fcffe27 Michael Hanselmann
  def close(self):
483 7fcffe27 Michael Hanselmann
    self.flush()
484 7fcffe27 Michael Hanselmann
    if self._buffer:
485 7fcffe27 Michael Hanselmann
      self._line_fn(self._buffer)
486 7ebd876f Michael Hanselmann
487 7ebd876f Michael Hanselmann
488 7ebd876f Michael Hanselmann
def IsValidShellParam(word):
489 7ebd876f Michael Hanselmann
  """Verifies is the given word is safe from the shell's p.o.v.
490 7ebd876f Michael Hanselmann

491 7ebd876f Michael Hanselmann
  This means that we can pass this to a command via the shell and be
492 7ebd876f Michael Hanselmann
  sure that it doesn't alter the command line and is passed as such to
493 7ebd876f Michael Hanselmann
  the actual command.
494 7ebd876f Michael Hanselmann

495 7ebd876f Michael Hanselmann
  Note that we are overly restrictive here, in order to be on the safe
496 7ebd876f Michael Hanselmann
  side.
497 7ebd876f Michael Hanselmann

498 7ebd876f Michael Hanselmann
  @type word: str
499 7ebd876f Michael Hanselmann
  @param word: the word to check
500 7ebd876f Michael Hanselmann
  @rtype: boolean
501 7ebd876f Michael Hanselmann
  @return: True if the word is 'safe'
502 7ebd876f Michael Hanselmann

503 7ebd876f Michael Hanselmann
  """
504 7ebd876f Michael Hanselmann
  return bool(_SHELLPARAM_REGEX.match(word))
505 7ebd876f Michael Hanselmann
506 7ebd876f Michael Hanselmann
507 7ebd876f Michael Hanselmann
def BuildShellCmd(template, *args):
508 7ebd876f Michael Hanselmann
  """Build a safe shell command line from the given arguments.
509 7ebd876f Michael Hanselmann

510 7ebd876f Michael Hanselmann
  This function will check all arguments in the args list so that they
511 7ebd876f Michael Hanselmann
  are valid shell parameters (i.e. they don't contain shell
512 7ebd876f Michael Hanselmann
  metacharacters). If everything is ok, it will return the result of
513 7ebd876f Michael Hanselmann
  template % args.
514 7ebd876f Michael Hanselmann

515 7ebd876f Michael Hanselmann
  @type template: str
516 7ebd876f Michael Hanselmann
  @param template: the string holding the template for the
517 7ebd876f Michael Hanselmann
      string formatting
518 7ebd876f Michael Hanselmann
  @rtype: str
519 7ebd876f Michael Hanselmann
  @return: the expanded command line
520 7ebd876f Michael Hanselmann

521 7ebd876f Michael Hanselmann
  """
522 7ebd876f Michael Hanselmann
  for word in args:
523 7ebd876f Michael Hanselmann
    if not IsValidShellParam(word):
524 7ebd876f Michael Hanselmann
      raise errors.ProgrammerError("Shell argument '%s' contains"
525 7ebd876f Michael Hanselmann
                                   " invalid characters" % word)
526 7ebd876f Michael Hanselmann
  return template % args
527 92ea69bf Michael Hanselmann
528 92ea69bf Michael Hanselmann
529 92ea69bf Michael Hanselmann
def FormatOrdinal(value):
530 92ea69bf Michael Hanselmann
  """Formats a number as an ordinal in the English language.
531 92ea69bf Michael Hanselmann

532 92ea69bf Michael Hanselmann
  E.g. the number 1 becomes "1st", 22 becomes "22nd".
533 92ea69bf Michael Hanselmann

534 92ea69bf Michael Hanselmann
  @type value: integer
535 92ea69bf Michael Hanselmann
  @param value: Number
536 92ea69bf Michael Hanselmann
  @rtype: string
537 92ea69bf Michael Hanselmann

538 92ea69bf Michael Hanselmann
  """
539 92ea69bf Michael Hanselmann
  tens = value % 10
540 92ea69bf Michael Hanselmann
541 92ea69bf Michael Hanselmann
  if value > 10 and value < 20:
542 92ea69bf Michael Hanselmann
    suffix = "th"
543 92ea69bf Michael Hanselmann
  elif tens == 1:
544 92ea69bf Michael Hanselmann
    suffix = "st"
545 92ea69bf Michael Hanselmann
  elif tens == 2:
546 92ea69bf Michael Hanselmann
    suffix = "nd"
547 92ea69bf Michael Hanselmann
  elif tens == 3:
548 92ea69bf Michael Hanselmann
    suffix = "rd"
549 92ea69bf Michael Hanselmann
  else:
550 92ea69bf Michael Hanselmann
    suffix = "th"
551 92ea69bf Michael Hanselmann
552 92ea69bf Michael Hanselmann
  return "%s%s" % (value, suffix)