svn commit: r241617 - user/crees/rclint
Chris Rees
crees at FreeBSD.org
Tue Oct 16 20:24:55 UTC 2012
Author: crees (ports committer)
Date: Tue Oct 16 20:24:54 2012
New Revision: 241617
URL: http://svn.freebsd.org/changeset/base/241617
Log:
Allow line breaks in large blocks
Do basic checking of definitions
Use tab as delimiter; regexes need colons and backslashes
Check style and position of functions
Handle missing rc files more gracefully
Make regex checks for the whole file
Added:
user/crees/rclint/problems.en
Modified:
user/crees/rclint/errors.en
user/crees/rclint/rclint.py
Modified: user/crees/rclint/errors.en
==============================================================================
--- user/crees/rclint/errors.en Tue Oct 16 20:18:15 2012 (r241616)
+++ user/crees/rclint/errors.en Tue Oct 16 20:24:54 2012 (r241617)
@@ -1,31 +1,42 @@
-# Rigid format of this file; backslash-separated tuples to define
+# Rigid format of this file; tab-separated tuples to define
# error messages and explanations.
+# Yes, tab isn't a great separator; alternatives that aren't
+# regex chars would be gratefully heard
# format as follows:
-error_id\message\explanation
+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
+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
-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
+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
-name_missing\name is set late or not at all\Directly following the sourcing of scripts must follow setting of the variable name
-name_quoted\name is quoted\Do not quote the value of name. If it has spaces, use underscores
+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_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 ()
-rc_subr_late\rc.subr sourcing late or nonexistent\The first non-comment non-blank line in any rc file must be sourcing /etc/rc.subr
+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
-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
+name_missing name is set late or not at all Directly following the sourcing of scripts must follow setting of the variable name
+name_quoted name is quoted Do not quote the value of name. If it has spaces, use underscores
-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
-rcvar_missing\rcvar is set late or not at all\Setting rcvar must be done straight after setting name
-rcvar_quoted\rcvar is quoted\Do not quote the value of rcvar
+rc_subr_late rc.subr sourcing late or nonexistent The first non-comment non-blank line in any rc file must be sourcing /etc/rc.subr
-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
+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
-shebang\Incorrect shebang used\All rc scripts must start with the correct shebang; #!/bin/sh
+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
+rcvar_missing rcvar is set late or not at all Setting rcvar must be done straight after setting name
+rcvar_quoted rcvar is quoted Do not quote the value of rcvar
+
+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
+
+shebang Incorrect shebang used All rc scripts must start with the correct shebang; #!/bin/sh
Added: user/crees/rclint/problems.en
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ user/crees/rclint/problems.en Tue Oct 16 20:24:54 2012 (r241617)
@@ -0,0 +1,2 @@
+# Format of this file: regex<TAB>short_explanation<TAB>explanation
+regex explain explanation
Modified: user/crees/rclint/rclint.py
==============================================================================
--- user/crees/rclint/rclint.py Tue Oct 16 20:18:15 2012 (r241616)
+++ user/crees/rclint/rclint.py Tue Oct 16 20:24:54 2012 (r241617)
@@ -34,12 +34,27 @@ import logging
import re
import textwrap
-def error_explain(error):
- if verbosity > 0: print textwrap.fill(error['explanation'], initial_indent='==> ', subsequent_indent=' ')
+def read_db(dbname, language):
+ try:
+ with open('%s.%s' % (dbname, language)) as f:
+ logging.debug('Sucking in %s database' % dbname)
+ contents = { }
+ for e in f.readlines():
+ 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, filename, errors[type]['message']))
- error_explain(errors[type])
+ logging.error("[%d]%s: %s " % (line_number, filename, errors[type][0]))
+ explain(errors[type])
def check_quoted(string):
return True if string[0] == '"' or string[0] == "'" else False
@@ -49,12 +64,12 @@ def mandatory(var):
return True if var.split('_')[-1] in mand else False
def get_value(var, line):
- n = re.match('%s=(\S*)$' % var, line)
- if n:
- value=n.group(1)
- return value
- else:
- return False
+ try: return re.match('%s=(\S+)$' % var, line).group(1)
+ except: return False
+
+def get_assignment(line):
+ try: return re.match('(\S+)=(\S+)$', line).groups()
+ except: return False
def check_header(lines, num, filename):
# Basic order; shebang, copyright, RCSId, gap, rcorder
@@ -156,7 +171,7 @@ def check_defaults(lines, num, filename)
default = { }
try:
- while lines[num] != '':
+ while lines[num]:
while lines[num][0] == '#' or lines[num][:4] == 'eval': num += 1
if lines[num][0] == ':':
# Shorthand set self-default assignment
@@ -184,15 +199,99 @@ def check_defaults(lines, num, filename)
except:
error('defaults_invalid', num, filename)
+ # 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)
+ else:
+ logging.debug('No functions left!')
+ return num
+ if ' ' in lines[num]:
+ error('functions_spaces', num, filename)
+ func['name'] = lines[num][:-2]
+
+ num += 1
+ if lines[num] != '{':
+ error('functions_brace_missing', num, filename)
+
+ num += 1
+ tmp = num
+ try:
+ while lines[num] != '}':
+ tmp += 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)
+
+def general_checks(lines, filename):
+ logging.debug('Checking for unrecommended sequences')
+ for num in range(0, len(lines)):
+ for regex in problems.keys():
+ if lines[num] and re.search(regex, lines[num]):
+ logging.warn("[%d]%s: %s " % (num, filename, problems[key][1]))
+ explain(problem)
+
def do_rclint(filename):
logging.debug('Suck in file %s' % filename)
- lines=[line.rstrip('\n') for line in open(filename)]
- begin_num = 0
-
- endofheader_num = check_header(lines, begin_num, filename)
- endofintro_num = check_intro(lines, endofheader_num, filename)
- endofdefaults_num = check_defaults(lines, endofintro_num, filename)
+ try:
+ 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)
+ general_checks(lines, filename)
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs = '+')
@@ -202,22 +301,10 @@ parser.add_argument('-v', action='count'
args = parser.parse_args()
verbosity = args.v
-errordb = 'errors.%s' % args.language[0]
+logging.basicConfig(level=logging.DEBUG if verbosity > 1 else logging.ERROR)
-logging.basicConfig(level=(logging.DEBUG if verbosity > 1 else logging.ERROR))
-
-try:
- with open(errordb) as f:
- logging.debug('Sucking in error database')
- errors = {}
- for e in f.readlines():
- if e[0] == '#' or len(e) == 1:
- continue
- e = e.split('\\')
- errors[e[0]] = { 'message': e[1], 'explanation': e[2] }
-except:
- logging.error('Cannot open database for language %s' % errordb)
- exit()
+errors = read_db('errors', args.language[0])
+problems = read_db('problems', args.language[0])
for file in args.filenames:
do_rclint(file)
More information about the svn-src-user
mailing list