Revision 5b69bc7c

b/lib/utils.py
1897 1897
  return resu
1898 1898

  
1899 1899

  
1900
def UnescapeAndSplit(text, sep=","):
1901
  """Split and unescape a string based on a given separator.
1902

  
1903
  This function splits a string based on a separator where the
1904
  separator itself can be escape in order to be an element of the
1905
  elements. The escaping rules are (assuming coma being the
1906
  separator):
1907
    - a plain , separates the elements
1908
    - a sequence \\\\, (double backslash plus comma) is handled as a
1909
      backslash plus a separator comma
1910
    - a sequence \, (backslash plus comma) is handled as a
1911
      non-separator comma
1912

  
1913
  @type text: string
1914
  @param text: the string to split
1915
  @type sep: string
1916
  @param text: the separator
1917
  @rtype: string
1918
  @return: a list of strings
1919

  
1920
  """
1921
  # we split the list by sep (with no escaping at this stage)
1922
  slist = text.split(sep)
1923
  # next, we revisit the elements and if any of them ended with an odd
1924
  # number of backslashes, then we join it with the next
1925
  rlist = []
1926
  while slist:
1927
    e1 = slist.pop(0)
1928
    if e1.endswith("\\"):
1929
      num_b = len(e1) - len(e1.rstrip("\\"))
1930
      if num_b % 2 == 1:
1931
        e2 = slist.pop(0)
1932
        # here the backslashes remain (all), and will be reduced in
1933
        # the next step
1934
        rlist.append(e1 + sep + e2)
1935
        continue
1936
    rlist.append(e1)
1937
  # finally, replace backslash-something with something
1938
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
1939
  return rlist
1940

  
1941

  
1900 1942
def CommaJoin(names):
1901 1943
  """Nicely join a set of identifiers.
1902 1944

  
b/test/ganeti.utils_unittest.py
45 45
     ParseUnit, AddAuthorizedKey, RemoveAuthorizedKey, \
46 46
     ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \
47 47
     SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree, OwnIpAddress, \
48
     TailFile, ForceDictType, SafeEncode, IsNormAbsPath, FormatTime
48
     TailFile, ForceDictType, SafeEncode, IsNormAbsPath, FormatTime, \
49
     UnescapeAndSplit
49 50

  
50 51
from ganeti.errors import LockError, UnitParseError, GenericError, \
51 52
     ProgrammerError
......
1053 1054
    FormatTime(int(time.time()))
1054 1055

  
1055 1056

  
1057
class TestUnescapeAndSplit(unittest.TestCase):
1058
  """Testing case for UnescapeAndSplit"""
1059

  
1060
  def setUp(self):
1061
    # testing more that one separator for regexp safety
1062
    self._seps = [",", "+", "."]
1063

  
1064
  def testSimple(self):
1065
    a = ["a", "b", "c", "d"]
1066
    for sep in self._seps:
1067
      self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), a)
1068

  
1069
  def testEscape(self):
1070
    for sep in self._seps:
1071
      a = ["a", "b\\" + sep + "c", "d"]
1072
      b = ["a", "b" + sep + "c", "d"]
1073
      self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), b)
1074

  
1075
  def testDoubleEscape(self):
1076
    for sep in self._seps:
1077
      a = ["a", "b\\\\", "c", "d"]
1078
      b = ["a", "b\\", "c", "d"]
1079
      self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), b)
1080

  
1081
  def testThreeEscape(self):
1082
    for sep in self._seps:
1083
      a = ["a", "b\\\\\\" + sep + "c", "d"]
1084
      b = ["a", "b\\" + sep + "c", "d"]
1085
      self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), b)
1086

  
1087

  
1056 1088
if __name__ == '__main__':
1057 1089
  testutils.GanetiTestProgram()

Also available in: Unified diff