svn commit: r241910 - user/crees/rclint

Chris Rees crees at FreeBSD.org
Mon Oct 22 20:02:37 UTC 2012


Author: crees (ports committer)
Date: Mon Oct 22 20:02:36 2012
New Revision: 241910
URL: http://svn.freebsd.org/changeset/base/241910

Log:
  Almost complete rewrite-- now revolves around objects.
  
  Less stupid and easier to extend.

Modified:
  user/crees/rclint/errors.en
  user/crees/rclint/rclint.py

Modified: user/crees/rclint/errors.en
==============================================================================
--- user/crees/rclint/errors.en	Mon Oct 22 19:44:17 2012	(r241909)
+++ user/crees/rclint/errors.en	Mon Oct 22 20:02:36 2012	(r241910)
@@ -5,20 +5,31 @@
 # format as follows:
 error_id	message	explanation
 
-defaults_invalid	Invalid lines in defaults block.	Allowed are variable assignments (including eval statements) and comments.  A blank line ends the block.
-defaults_mandatory_colon	Override blanks in mandatory values (:=/:- vs =/-)	Values that must not be blank (such as _enable) should be set by default as ${var:=value}; thus disallowing blank values (man sh)
-defaults_non_mandatory_colon	Do not clobber blank values for non-mandatory variables	Syntax for variables that are not mandatory is ${var=value}; including := will override var="" set in rc.conf (man sh)
-defaults_old_style	Prefer condensed version for setting default values of variables	When setting the default value for a variable, it is much less verbose and clearer to use the : ${variable=var} notation, as well as it being obvious that the source and destination variable are the same
-defaults_value_quoted	Do not quote values set by default	With the syntax ${variable=value}, value can even contain spaces and does not need quoting
-
-definitions_missing	Definitions block missing	Following the default definitions must be other definitions (such as command, command_args)
-definitions_quoted	Do not quote values of variables	Unless the value contains spaces/tabs, do not quote variable assignments
+file_order	Order of rc file incorrect	Order of the rc file should be shebang/header/$FreeBSD$/sourcing rc_subr/name/rcvar/load_rc_config/setting defaults/setting other definitions/defining functions.  Do not include unassociated shell commands, and blocks must be separated by single blank lines.  Single blank lines may appear inside the defaults, definitions and functions blocks
 
-functions_brace_inline	Braces should have their own lines	Do not put a brace on the same line as the function definition; it should have its own line
-functions_brace_missing	Function definition does not start with a brace	The function definition must begin with an opening brace on its own line, and end with a closing brace on its own line
 functions_neverending	Unclosed function block	Functions must end with a closing brace on its own line
+functions_problem	Function incorrectly defined	Functions should not have spaces in the definition
 functions_short	One-line functions discouraged; put command directly in variable	It is wasteful to write a function just for one command.  It is possible to put commands directly inside declarations; name_prestart="install -d -o $name_user /var/run/$name" for example
-functions_spaces	No spaces allowed in function definitions	Do not separate the function name from the parentheses with spaces; func() not func ()
+
+orphaned_line	Orphaned line	Do not put unassociated shell commands inside rc scripts; put them inside functions
+
+rcorder_order	rcorder block in the wrong order; should be PROVIDE/REQUIRE/BEFORE/KEYWORD	See the article on RC scripting
+
+shebang	Incorrect shebang used	All rc scripts must start with the correct shebang; #!/bin/sh
+
+variables_defaults_mandatory_colon	Override blanks in mandatory values (:=/:- vs =/-)	Values that must not be blank (such as _enable) should be set by default as ${var:=value}; thus disallowing blank values (man sh)
+variables_defaults_non_mandatory_colon	Do not clobber blank values for non-mandatory variables	Syntax for variables that are not mandatory is ${var=value}; including := will override var="" set in rc.conf (man sh)
+variables_defaults_old_style	Prefer condensed version for setting default values of variables	When setting the default value for a variable, it is much less verbose and clearer to use the : ${variable=var} notation, as well as it being obvious that the source and destination variable are the same
+variables_order	Order of variables incorrect	Order of the variables should be setting defaults then setting other variables
+
+value_quoted	Do not quote values unless necessary	Unless there are spaces in the value, quotes are unnecessary.  With the syntax ${variable=value}, value can even contain spaces and does not need quoting
+
+
+
+
+defaults_invalid	Invalid lines in defaults block.	Allowed are variable assignments (including eval statements) and comments.  A blank line ends the block.
+
+definitions_missing	Definitions block missing	Following the default definitions must be other definitions (such as command, command_args)
 
 load_rc_config_extra	load_rc_config not followed by blank line	The load_rc_config line must form its own block
 load_rc_config_missing	load_rc_config is late, incorrect or missing	Directly following the name/rcvar block must be load_rc_config $name, unquoted
@@ -30,7 +41,6 @@ rc_subr_late	rc.subr sourcing late or no
 
 rcorder_keyword_freebsd	Do not include FreeBSD in the KEYWORD rcorder line	Historically FreeBSD scripts were marked in the KEYWORD section.  This is no longer necessary
 rcorder_missing	Missing rcorder block	Following the FreeBSD RCSId keyword must be nothing but empty comment lines and an rcorder block
-rcorder_order	rcorder block in the wrong order; should be PROVIDE/REQUIRE/BEFORE/KEYWORD	See the article on RC scripting
 
 rcvar_extra	extra lines in name/rcvar block	Order should be [blank line]/name=/rcvar=/[blank line]
 rcvar_incorrect	rcvar is not set correctly	rcvar must be directly set to name_enable.  Do not quote, and do not use indirection; ${name}_enable is slower than example_enable
@@ -40,8 +50,6 @@ rcvar_quoted	rcvar is quoted	Do not quot
 rcsid	Missing FreeBSD RCSId keyword	All rc scripts must contain a line beginning # $FreeBSD$.  Do not include blank lines without comment markers at the beginning (#) until the script begins
 
 run_rc_argument	Incorrect argument to run_rc_command	The last line of the file should be run_rc_command $1
-run_rc_cruft	Order of rc file incorrect	Order of the rc file should be shebang/header/$FreeBSD$/setting defaults/setting other definitions/defining functions.  Do not include unassociated shell commands, and blocks must be separated by single blank lines.  Single blank lines may appear inside the defaults, definitions and functions blocks.
 run_rc_followed	run_rc_command line is not the last line in the file	Do not write anything after the run_rc_command line
 run_rc_quoted	Quoted argument to run_rc_command	No need to quote the argument to run_rc_command
 
-shebang	Incorrect shebang used	All rc scripts must start with the correct shebang; #!/bin/sh

Modified: user/crees/rclint/rclint.py
==============================================================================
--- user/crees/rclint/rclint.py	Mon Oct 22 19:44:17 2012	(r241909)
+++ user/crees/rclint/rclint.py	Mon Oct 22 20:02:36 2012	(r241910)
@@ -22,331 +22,339 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #
-# $FreeBSD$
 #
 
-major = 0
-minor = 0
-micro = 0
+__version__ = '$FreeBSD$'
+
+MAJOR = 0
+MINOR = 0
+MICRO = 0
 
 import argparse
 import logging
 import re
 import textwrap
 
-def read_db(dbname, language):
-    try:
+class Db:
+    def __init__(self, dbname, language):
+        self._contents = []
         with open('%s.%s' % (dbname, language)) as f:
             logging.debug('Sucking in %s database' % dbname)
-            contents = { }
             for e in f.readlines():
+                e = e.rstrip('\n')
                 if not e or e[0] == '#':
                     continue
-                e = e.split('	')
-                contents[e[0]] = e[1:]
-    except:
-        logging.error('Cannot open %s database for language %s' % (dbname, language))
-        exit()
-    return contents
-
-def explain(error):
-    if verbosity > 0:
-        print textwrap.fill(error[1],
-                            initial_indent='==> ', subsequent_indent='    ')
-
-def error(type, line_number, filename):
-    logging.error("[%d]%s: %s " % (line_number + 1, filename, errors[type][0]))
-    explain(errors[type])
-
-def check_quoted(string):
-    return True if string[0] == '"' or string[0] == "'" else False
-
-def mandatory(var):
-    mand = ['enable']
-    return True if var.split('_')[-1] in mand else False
-
-def get_value(var, line):
-    try:
-        return re.match('%s=(\S+)$' % var, line).group(1)
-    except:
-        return False
+                self._contents.append(e.split('\t'))
 
-def get_assignment(line):
-    try:
-        return re.match('(\S+)=(.+)$', line).groups()
-    except:
+    def _get(self, key, index):
+        for c in self._contents:
+            if c[0] == key:
+                return c[index]
         return False
 
-def check_header(lines, num, filename):
-    # Basic order; shebang, copyright, RCSId, gap, rcorder
-
-    logging.debug('Check shebang')
-    if lines[num] != '#!/bin/sh':
-        error('shebang', num, filename)
-
-    logging.debug('Skipping license')
-    num += 1
-    while (not re.match('# \$FreeBSD[:$]', lines[num]) and
-          (lines[num] == '' or lines[num][0] == '#')):
-        num += 1
-
-    logging.debug('Checking for RCSId')
-    if not re.match('# \$FreeBSD[:$]', lines[num]):
-        error('rcsid', num, filename)
-
-    num += 1
-    while lines[num] == '#' or lines[num] == '': num += 1
-
-    logging.debug('Checking rcorder order and sucking in names')
-    if not re.match('# [PRBK]', lines[num]):
-        error('rcorder_missing', num, filename)
-    orders = ['provide', 'require', 'before', 'keyword']
-    index = 0
-    rcorder = {o: [] for o in orders}
-    while index < 4:
-        order = orders[index]
-        try:
-            for result in re.match('# %s: (.*)' % order.upper(),
-                                   lines[num]).group(1).split(' '):
-                rcorder[order].append(result)
-            num += 1
-        except:
-            index += 1
-
-    if 'FreeBSD' in rcorder['keyword']:
-        error('rcorder_keyword_freebsd', num, filename)
-
-    if re.match('# [PRBK]', lines[num]):
-        error('rcorder_order', num, filename)
-
-    return num
-
-def check_intro(lines, num, filename):
-    logging.debug('Checking sourcing lines')
-    while lines[num] == '' or lines[num][0] == '#':
-        num += 1
-
-    if lines[num] != '. /etc/rc.subr':
-        error('rc_subr_late', num, filename)
-
-    logging.debug('Checking name assignment')
-    while lines[num] == '' or lines[num][0] == '#' or lines[num][0] == '.':
-        num += 1
-
-    name = get_value('name', lines[num])
-    if not name:
-        error('name_missing', num, filename)
-    elif check_quoted(name):
-        error('name_quoted', num, filename)
-    else:
-        logging.debug('name discovered as %s' % name)
-        num += 1
-
-    logging.debug('Checking rcvar')
-    rcvar = get_value('rcvar', lines[num])
-    logging.debug('rcvar discovered as %s' % rcvar if rcvar else 'rcvar not discovered')
-
-    if not rcvar:
-        error('rcvar_missing', num, filename)
-    elif check_quoted(rcvar):
-        error('rcvar_quoted', num, filename)
-    elif rcvar != '%s_enable' % name:
-        error('rcvar_incorrect', num, filename)
-    else:
-        num += 1
-
-    logging.debug('Checking load_rc_config')
-    if lines[num] == '':
-        num += 1
-    else:
-        error('rcvar_extra', num, filename)
-
-    if re.match('load_rc_config (?:\$name|%s)' % name, lines[num]):
-        num += 1
-    else:
-        error('load_rc_config_missing', num, filename)
-
-    if lines[num] == '':
-        num += 1
-    else:
-        error('load_rc_config_extra', num, filename)
-
-    return num
-
-def check_defaults(lines, num, filename):
-    logging.debug('Checking defaults set')
+    def error(self, key):
+        return self._get(key, 1)
 
-    default = { }
-    try:
-        while lines[num]:
-            while lines[num][0] == '#' or lines[num][:4] == 'eval':
-                num += 1
-            if lines[num][0] == ':':
-                # Shorthand set self-default assignment
-                (target, operator, value) = re.match(': \${([^:=]+)(:?=)([^}]+)}', lines[num]).groups()
-                if operator == ':=' and not mandatory(target):
-                    error('defaults_non_mandatory_colon', num, filename)
-                elif operator == '=' and mandatory(target):
-                    error('defaults_mandatory_colon', num, filename)
-
-            else:
-                # Longhand set default assignment
-                (target, source, operator, value) = re.match('([^=]+)=\${([^:-]+)(:?-)([^}]+)}', lines[num]).groups()
-                if target == source:
-                    error('defaults_old_style', num, filename)
-
-                if operator == ':-' and not mandatory(target):
-                    error('defaults_non_mandatory_colon', num, filename)
-                elif operator == '-' and mandatory(target):
-                    error('defaults_mandatory_colon', num, filename)
+    def explanation(self, key):
+        return self._get(key, 2)
 
-            if check_quoted(value):
-                error('defaults_value_quoted', num, filename)
-
-            num += 1
-    except:
-        error('defaults_invalid', num, filename)
+    def give(self, key, num):
+        err = self.error(key)
+        if err:
+            logging.error('[%d]: %s ' % (num+1, err))
+            if verbosity > 0:
+                print textwrap.fill(self.explanation(key),
+                                    initial_indent='==> ',
+                                    subsequent_indent='    ')
+        else:
+            logging.error('No such error: %s' % key)
+
+class Statement:
+    def __init__(self, line, number):
+        types = {'.': 'source', 'load_rc_config': 'load_rc_config',
+                 'run_rc_command': 'run_rc_command'}
+        spl = line.split(' ')
+        if spl[0] in types:
+            self.name = spl[0]
+            self.type = types[spl[0]]
+            self.value = ' '.join(spl[1:])
+            self.line = number
+        else:
+            self.value = False
+
+    def quoted(self):
+        if self.value and (self.value[0] == '"' or self.value[0] == '\''):
+            return True
+        else:
+            return False
+
+    def pointless_quoted(self):
+        if self.quoted():
+            if self.type == 'shorthand':
+                return True
+            elif ' ' not in self.value and '\t' not in self.value:
+                return True
+        return False
 
-    # Allow line breaks in the middle; if we put
-    # gaps in that's usually good style.  Lookahead!
-    if lines[num+1] and (':' in lines[num+1] or '-' in lines[num+1]):
-        return check_defaults(lines, num, filename)
-    else:
-        return num
-
-def check_definitions(lines, num, filename):
-    logging.debug('Checking the basic definitions')
-
-    num += 1
-
-    while lines[num]:
-        while lines[num][0] == '#' or lines[num][:4] == 'eval': num += 1
-        try:
-            (var, value) = get_assignment(lines[num])
-        except:
-            error('definitions_missing', num, filename)
-            return num
-
-        if check_quoted(value):
-            if ' ' not in lines[num] and '	' not in lines[num]:
-                error('definitions_quoted', num, filename)
-        num += 1
-
-    # As in check_defaults, allow line breaks in the middle; if we put
-    # gaps in that's usually good style.  Lookahead!
-    if lines[num+1] and '=' in lines[num+1]:
-        return check_definitions(lines, num, filename)
-    else:
-        return num
-
-def check_functions(lines, num, filename):
-    logging.debug('Now checking functions')
-
-    num += 1
-    func = { }
-
-    while lines[num]:
-        while not lines[num] or lines[num][0] == '#':
-            num += 1
-
-        if lines[num][-2:] != '()':
-            if lines[num][-1] == '{':
-                error('functions_brace_inline', num, filename)
+class Variable(Statement):
+    def __init__(self, line, number):
+        basic = re.compile(r'([^\s=]+)=(.*)')
+        result = basic.match(line)
+        if result:
+            is_longhand = self.is_longhand_default(line)
+            if is_longhand:
+                (self.name, self.source, colon, self.value) = is_longhand
+                self.clobber = True if colon[0] == ':' else False
+                self.type = 'longhand'
             else:
-                logging.debug('No functions left!')
-                return num
-        if ' ' in lines[num]:
-            error('functions_spaces', num, filename)
-        func['name'] = lines[num][:-2]
-        func['length'] = 0
-
-        num += 1
-        if lines[num] != '{':
-            error('functions_brace_missing', num, filename)
-
-        num += 1
-
-        try:
-            while lines[num] != '}':
-                print lines[num]
-                num += 1
-                if lines[num] and lines[num][0] != '#':
-                    func['length'] += 1 
-        except:
-            error('functions_neverending', num, filename)
-            return num
-
-        if func['length'] == 1:
-            error('functions_short', num, filename)
-
-    print lines[num]
-    print lines[num+1]
-    print lines[num+2]
-
-    if lines[num+2] and '{' in lines[num+2]:
-        return check_functions(lines, num, filename)
-    else:
-        return num
+                (self.name, self.value) = result.groups()
+                self.type = (
+                    'init' if self.name in ('name', 'rcvar') else 'basic')
+
+        elif line[:4] == 'eval':
+            self.value = line
+            self.name = line
+            self.type = 'eval'
+        else:
+            is_shorthand = self.is_shorthand_default(line)
+            if is_shorthand:
+                (self.name, assignment, self.value) = is_shorthand
+                self.clobber = True if assignment[0] == ':' else False
+                self.type = 'shorthand'
+        
+        if not hasattr(self, 'value'):
+            self.value = False
+        self.line = number
+
+    def is_longhand_default(self, line):
+        match = re.match(r'([^=\s]+)=\${(^[:-]+)(:?-)([^}]+)', line)
+        return match.groups() if match else False
+
+    def is_shorthand_default(self, line):
+        match = re.match(r': \${([^\s:=]+)(:?=)([^}]+)}', line)
+        return match.groups() if match else False
+
+class Comment:
+    def __init__(self, line, number):
+        self.value = line if line and line[0] == '#' else False
+        self.line = number
+
+    def match(self, regex):
+        result = re.match(regex, self.value)
+        if result:
+            return result.groups()
+        else:
+            return False
+
+class Shebang:
+    def __init__(self, comment):
+        self.line = comment.line
+        result = comment.match(r'#!(\S+)\s*(.*)')
+        if result:
+            self.value = result[0]
+            self.args = result[1]
+        else:
+            self.value = False
+
+class Rcorder:
+    def __init__(self, comment):
+        self.line = comment.line
+        result = comment.match('# ([A-Z]+): (.+)')
+        if result:
+            (self.type, self.value) = (result[0], result[1:])
+        else:
+            self.value = False
+
+class RcsId:
+    def __init__(self, comment):
+        self.line = comment.line
+        result = comment.match(r'# \$Free' + r'BSD([:$].*)')
+        if result:
+            self.value = result[0]
+        else:
+            self.value = False
+
+class Function:
+    def __init__(self, lines, num):
+        if lines[1] and lines[1][0] == '{':
+            try:
+                self.name = re.match(r'([\S_]+)\(\)$', lines[0]).group(1)
+            except:
+                error.give('functions_problem', num)
+            self.length = 0
+            self.line = num
+            self.value = []
+            while lines[self.length] != '}':
+                self.length += 1
+                if self.length >= len(lines):
+                    error.give('functions_neverending', num)
+                    break
+                self.value.append(lines[self.length])
+            # Remove { and } lines from length
+            self.length -= 2
+            logging.debug('Found function %s' % self.name)
+        else:
+            self.value = False
 
-def check_run_rc(lines, num, filename):
-    logging.debug('Checking the last line of the file contains run_rc_command')
+    def short(self):
+        return True if self.length <= 1 else False
 
-    while not lines[num] or lines[num][0] == '#':
-        num += 1
+    def linenumbers(self):
+        return range(self.line, self.line+self.length+3)
 
-    try:
-        arg = re.match('run_rc_command (.*)$', lines[num]).group(1)
-        if check_quoted(arg):
-            error('run_rc_quoted', num, filename)
-        elif arg != r'$1':
-            error('run_rc_argument', num, filename)
-
-        if num < len(lines) - 1:
-            error('run_rc_followed', num, filename)
-    except:
-        error('run_rc_cruft', num, filename)
-
-def general_checks(lines, filename):
-    logging.debug('Checking for unrecommended sequences and orphan commands')
-    # Don't use regex on last line, it must contain run_rc_command, which fails
-    # unassociated shell command test.  We already hacked for load_rc_config
-    for num in range(0, len(lines) - 1):
-        for regex in problems.keys():
-            if lines[num] and re.search(regex, lines[num]):
-                logging.warn("[%d]%s: %s " % (num + 1, filename,
-                             problems[regex][0]))
-                explain(problems[regex])
+    def contains_line(self, line):
+        return True if line in self.linenumbers() else False
 
 def do_rclint(filename):
     logging.debug('Suck in file %s' % filename)
     try:
-        lines=[line.rstrip('\n') for line in open(filename)]
-    except: 
+        lines = [line.rstrip('\n') for line in open(filename)]
+    except:
         logging.error('Cannot open %s for testing' % filename)
         return
-    lineno = {'begin': 0}
-
-    lineno['header'] = check_header(lines, lineno['begin'], filename)
-    lineno['intro'] = check_intro(lines, lineno['header'], filename)
-    lineno['defaults'] = check_defaults(lines, lineno['intro'], filename)
-    lineno['definitions'] = check_definitions(lines, lineno['defaults'], filename)
-    lineno['functions'] = check_functions(lines, lineno['definitions'], filename)
-    check_run_rc(lines, lineno['functions'], filename)
 
-    general_checks(lines, filename)
+    lineobj = {'Variable': [],
+               'Comment': [],
+               'Statement': []}
+
+    for num in range(0, len(lines)):
+        for obj in lineobj.keys():
+            tmp = eval(obj)(lines[num], num)
+            if tmp.value != False:
+                lineobj[obj].append(tmp)
+                break
+
+    lineobj['Shebang'] = [Shebang(lineobj['Comment'][0])]
+    lineobj['Function'] = []
+
+    for num in range(0, len(lines)-1):
+        tmp = Function(lines[num:], num)
+        if tmp.value:
+            lineobj['Function'].append(tmp)
+
+    lineobj.update({'Rcorder': [], 'RcsId': []})
+
+    for comment in lineobj['Comment']:
+        tmp = Rcorder(comment)
+        if tmp.value:
+            lineobj['Rcorder'].append(tmp)
+        tmp = RcsId(comment)
+        if tmp.value:
+            lineobj['RcsId'] = [tmp]
+
+    logging.debug('OK, done collecting variables.  Time to check!')
+
+    logging.debug('Checking shebang')
+    if len(lineobj['Shebang']) < 1:
+        error.give('shebang', num)
+
+    logging.debug('Checking order of file')
+    linenumbers = []
+    for obj in ('Shebang', 'RcsId', 'Rcorder'):
+        for o in lineobj[obj]:
+            linenumbers.append(o.line)
+
+    for s in lineobj['Statement']:
+        if s.type == 'source':
+            linenumbers.append(s.line)
+
+    for v in lineobj['Variable']:
+        if v.type == 'init':
+            linenumbers.append(v.line)
+
+    for s in lineobj['Statement']:
+        if s.type == 'load_rc_config':
+            linenumbers.append(s.line)
+
+    for v in lineobj['Variable']:
+        if v.type != 'init':
+            linenumbers.append(v.line)
+
+    [linenumbers.append(f.line) for f in lineobj['Function']]
+
+    for s in lineobj['Statement']:
+        if s.type == 'run_rc_command':
+            linenumbers.append(s.line)
+
+    if sorted(linenumbers) != linenumbers:
+        error.give('file_order', 0)
+
+    logging.debug('Checking all lines are accounted for')
+    for obj in lineobj.keys():
+        for o in lineobj[obj]:
+            linenumbers.append(o.line)
+    for r in range(0, len(lines)):
+        if r not in linenumbers and lines[r] != '':
+            if True not in [f.contains_line(r) for f in lineobj['Function']]:
+                error.give('orphaned_line', r)
+
+    logging.debug('Checking rcorder')
+    linenumbers = []
+    for typ in ('PROVIDE', 'REQUIRE', 'BEFORE', 'KEYWORD'):
+        for o in lineobj['Rcorder']:
+            if o.type == typ:
+                linenumbers.append(o.line)
+    if sorted(linenumbers) != linenumbers:
+        error.give('rcorder_order')
+
+    for o in lineobj['Rcorder']:
+        if o.type == 'KEYWORD' and 'freebsd' in [v.lower() for v in o.value]:
+            error.give('rcorder_keyword_freebsd', o.line)
+
+    logging.debug('Checking order of variables')
+    linenumbers = []
+    for typ in (('init'), ('longhand', 'shorthand'), ('basic')):
+        for var in lineobj['Variable']:
+            if var.type in typ:
+                linenumbers.append(var.line)
+    if sorted(linenumbers) != linenumbers:
+        error.give('variables_order', 0)
+
+    logging.debug('Checking for pointless quoting')
+    for obj in lineobj['Variable']+lineobj['Statement']:
+        if obj.pointless_quoted():
+            error.give('value_quoted', obj.line)
+
+    logging.debug('Checking for rcvar set correctly')
+    for var in lineobj['Variable']:
+        if var.name == 'name':
+            progname = var.value
+        elif var.name == 'rcvar':
+            try:
+                if progname + '_enable' not in var.value:
+                    error.give('rcvar_incorrect', var.line)
+            except:
+                error.give('file_order', var.line)
+    
+    logging.debug('Checking for short functions')
+    for function in lineobj['Function']:
+        if function.short():
+            error.give('functions_short', function.line)
+
+    logging.debug('Checking for defaults clobbering blank values')
+    for var in lineobj['Variable']:
+        if var.type in ('longhand', 'shorthand'):
+            if var.name.split('_')[-1] not in ('enable') and var.clobber:
+                error.give('variables_defaults_non_mandatory_colon', var.line)
+            elif not var.clobber and var.name.split('_')[-1] in ('enable'):
+                error.give('variables_defaults_mandatory_colon', var.line)
+            if var.type == 'longhand' and var.name == var.source:
+                error.give('variables_defaults_old_style', var.line)
 
 parser = argparse.ArgumentParser()
 parser.add_argument('filenames', nargs = '+')
 parser.add_argument('--language', nargs = 1, type=str, default = ['en'], help = 'sets the language that errors are reported in')
 parser.add_argument('-v', action='count', help='raises debug level; provides detailed explanations of errors')
+parser.add_argument('--version', action='version', version='%s.%s.%s-%s'%(MAJOR, MINOR, MICRO, __version__))
 
 args = parser.parse_args()
 
 verbosity = args.v
 logging.basicConfig(level=logging.DEBUG if verbosity > 1 else logging.WARN)
 
-errors = read_db('errors', args.language[0])
-problems = read_db('problems', args.language[0])
+error = Db('errors', args.language[0])
+# problem = Db('problems', args.language[0])
 
-for file in args.filenames:
-    do_rclint(file)
+for f in args.filenames:
+    print('Checking %s' % f)
+    do_rclint(f)


More information about the svn-src-user mailing list