Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

# Copyright (C) 2009  Internet Systems Consortium. 

# 

# Permission to use, copy, modify, and distribute this software for any 

# purpose with or without fee is hereby granted, provided that the above 

# copyright notice and this permission notice appear in all copies. 

# 

# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM 

# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 

# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 

# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, 

# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 

# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 

# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 

# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 

 

 

"""This module holds the command parser class for bindctl""" 

 

import re 

from bindctl.exception import * 

try: 

    from collections import OrderedDict 

except ImportError: 

    from bindctl.mycollections import OrderedDict 

 

param_name_str = "^\s*(?P<param_name>[\w]+)\s*=\s*" 

 

# The value string can be a sequence without space or comma  

# characters, or a string surroundedby quotation marks(such marks 

# can be part of string in an escaped form) 

#param_value_str  = "(?P<param_value>[\"\'].+?(?<!\\\)[\"\']|[^\'\"][^, ]+)" 

param_value_str  = "(?P<param_value>[^\'\" ][^, ]+)" 

param_value_with_quota_str  = "[\"\'](?P<param_value>.+?)(?<!\\\)[\"\']" 

next_params_str = "(?P<blank>\s*)(?P<comma>,?)(?P<next_params>.*)$" 

 

 

PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str + 

                                      param_value_with_quota_str + 

                                      next_params_str) 

PARAM_PATTERN = re.compile(param_name_str + param_value_str + next_params_str) 

# Used for module and command name 

NAME_PATTERN = re.compile("^\s*(?P<name>[\w]+)(?P<blank>\s*)(?P<others>.*)$") 

 

# this removes all whitespace in the given string, except when 

# between " quotes 

_remove_unquoted_whitespace = \ 

    lambda text:'"'.join( it if i%2 else ''.join(it.split()) 

        for i,it in enumerate(text.split('"'))  ) 

 

 

def _remove_list_and_map_whitespace(text): 

    """Returns a string where the whitespace between matching [ and ] 

       is removed, unless quoted""" 

    # regular expression aren't really the right tool, since we may have 

    # nested structures 

    result = [] 

    start_pos = 0 

    pos = 0 

    list_count = 0 

    map_count = 0 

    cur_start_list_pos = None 

    cur_start_map_pos = None 

    for i in text: 

        if i == '[' and map_count == 0: 

            if list_count == 0: 

                result.append(text[start_pos:pos + 1]) 

                cur_start_list_pos = pos + 1 

            list_count = list_count + 1 

        elif i == ']' and map_count == 0: 

            if list_count > 0: 

                list_count = list_count - 1 

                if list_count == 0: 

                    result.append(_remove_unquoted_whitespace(text[cur_start_list_pos:pos + 1])) 

                    start_pos = pos + 1 

        if i == '{' and list_count == 0: 

            if map_count == 0: 

                result.append(text[start_pos:pos + 1]) 

                cur_start_map_pos = pos + 1 

            map_count = map_count + 1 

        elif i == '}' and list_count == 0: 

            if map_count > 0: 

                map_count = map_count - 1 

                if map_count == 0: 

                    result.append(_remove_unquoted_whitespace(text[cur_start_map_pos:pos + 1])) 

                    start_pos = pos + 1 

 

 

        pos = pos + 1 

91    if start_pos <= len(text): 

        result.append(text[start_pos:len(text)]) 

    return "".join(result) 

 

 

class BindCmdParse: 

    """ This class will parse the command line user input into three parts: 

    module name, command, parameters 

    the first two parts are strings and parameter is one hash,  

    parameters part is optional 

     

    Example: zone reload, zone_name=example.com  

    module == zone 

    command == reload 

    params == [zone_name = 'example.com'] 

    """ 

 

    def __init__(self, cmd): 

        self.params = OrderedDict() 

        self.module = '' 

        self.command = '' 

        self._parse_cmd(cmd) 

 

    def _parse_cmd(self, text_str): 

        '''Parse command line. ''' 

        # Get module name 

        groups = NAME_PATTERN.match(text_str) 

        if not groups: 

            raise CmdModuleNameFormatError 

 

        self.module = groups.group('name') 

        cmd_str = groups.group('others') 

        if cmd_str: 

            if not groups.group('blank'): 

                raise CmdModuleNameFormatError 

        else: 

            raise CmdMissCommandNameFormatError(self.module) 

 

        # Get command name 

        groups = NAME_PATTERN.match(cmd_str) 

        if (not groups): 

            raise CmdCommandNameFormatError(self.module) 

 

        self.command = groups.group('name') 

        param_str = groups.group('others') 

        if param_str: 

            if not groups.group('blank'): 

                raise CmdCommandNameFormatError(self.module) 

 

            self._parse_params(param_str) 

 

    def _remove_list_whitespace(self, text): 

        return "" 

 

    def _parse_params(self, param_text): 

        """convert a=b,c=d into one hash """ 

        param_text = _remove_list_and_map_whitespace(param_text) 

 

        # Check parameter name "help" 

        param = NAME_PATTERN.match(param_text) 

        if param and param.group('name') == "help": 

            self.params["help"] = "help" 

            return 

 

        while True: 

155            if not param_text.strip(): 

                break 

 

            groups = PARAM_PATTERN.match(param_text) or \ 

                     PARAM_WITH_QUOTA_PATTERN.match(param_text) 

            if not groups: 

                # ok, fill in the params in the order entered 

                params = re.findall("([^\" ]+|\".*\")", param_text) 

                i = 0 

                for p in params: 

                    self.params[i] = p 

                    i += 1 

                break 

            else: 

                self.params[groups.group('param_name')] = groups.group('param_value') 

                param_text = groups.group('next_params') 

                if not param_text or (not param_text.strip()): 

                    break 

 

175                if not groups.group('blank') and \ 

                   not groups.group('comma'): 

                    raise CmdParamFormatError(self.module, self.command)