pvl.dhcp.config: stricter quoting of e.g. include file paths
authorTero Marttila <terom@paivola.fi>
Mon, 02 Mar 2015 00:11:43 +0200
changeset 673 0eda16e29613
parent 672 87dac237b46e
child 674 a5b712cd0273
pvl.dhcp.config: stricter quoting of e.g. include file paths
pvl/dhcp/config.py
--- a/pvl/dhcp/config.py	Sun Mar 01 23:32:57 2015 +0200
+++ b/pvl/dhcp/config.py	Mon Mar 02 00:11:43 2015 +0200
@@ -1,5 +1,10 @@
 import logging; log = logging.getLogger('pvl.dhcp.config')
 import shlex
+import string
+
+# simplified model of lexer chars
+TOKEN_START = string.ascii_letters
+TOKEN = TOKEN_START + string.digits + '-_.'
 
 class DHCPConfigError(Exception):
     def __init__ (self, parser, error, line=None):
@@ -32,6 +37,10 @@
         []
         >>> list(split(''))
         []
+        >>> list(split('next-server foo'))
+        ['next-server', 'foo']
+        >>> list(split('include "foo/bar.conf";'))
+        ['include', 'foo/bar.conf', ';']
     """
 
     if line is None:
@@ -39,7 +48,7 @@
 
     lexer = shlex.shlex(line, posix=True)
     lexer.commenters = '#'
-    lexer.wordchars += '-./'
+    lexer.wordchars = TOKEN
 
     while True:
         item = lexer.get_token()
@@ -49,6 +58,32 @@
 
         yield item
 
+def quote (value):
+    """
+        Build a single field as part of a dhcp.conf line.
+
+        >>> print quote('foo')
+        foo
+        >>> print quote('foo bar')
+        "foo bar"
+        >>> print quote('foo/bar.conf')
+        "foo/bar.conf"
+        >>> print quote(5)
+        5
+        >>> print quote('5')
+        "5"
+    """
+
+    if isinstance(value, int):
+        return str(value)
+    elif not value:
+        return ''
+    elif value[0] in TOKEN_START and all(c in TOKEN for c in value):
+        return str(value)
+    else:
+        # quoted
+        return '"{value}"'.format(value=value)
+
 class Block (object):
     """
         A block in a dhcp conf includes parameters and sub-blocks.
@@ -84,7 +119,7 @@
         """
             Parse an complete file, returning the top-level Block.
 
-            >>> DHCPConfigParser.load(['foo;', 'bar {', '\tasdf "quux";', '}'])
+            >>> DHCPConfigParser.load(['foo;', 'bar {', '\tasdf "quux";', '}'], name='test')
             Block(None, items=[('foo', )], blocks=[Block(('bar', ), items=[('asdf', 'quux')], blocks=[])])
         """
         
@@ -207,22 +242,6 @@
             for block in self.parse_line(line) :
                 yield block
     
-def build_field (value):
-    """
-        Build a single field as part of a dhcp.conf line.
-
-        >>> print build_field('foo')
-        foo
-        >>> print build_field('foo bar')
-        "foo bar"
-    """
-
-    if any(c.isspace() for c in value):
-        # quoted
-        return '"{value}"'.format(value=value)
-    else:
-        return value
-
 def build_line (item, end, indent=0):
     """
         Build a structured line.
@@ -235,7 +254,7 @@
             foo "bar quux";
     """
 
-    return '    '*indent + ' '.join(build_field(field) for field in item) + end
+    return '    '*indent + ' '.join(quote(field) for field in item) + end
 
 def build_item (item, **opts):
     """