summaryrefslogtreecommitdiffstats
path: root/tools/check_tfs.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xtools/check_tfs.py179
1 files changed, 96 insertions, 83 deletions
diff --git a/tools/check_tfs.py b/tools/check_tfs.py
index cecf8d9d..f7c59377 100755
--- a/tools/check_tfs.py
+++ b/tools/check_tfs.py
@@ -12,12 +12,10 @@ import argparse
import signal
# This utility scans for tfs items, and works out if standard ones
-# could have been used intead (from epan/tfs.c)
+# could have been used instead (from epan/tfs.c)
# Can also check for value_string where common tfs could be used instead.
# TODO:
-# - check how many of the definitions in epan/tfs.c are used in other dissectors
-# - although even if unused, might be in external dissectors?
# - consider merging Item class with check_typed_item_calls.py ?
@@ -39,7 +37,7 @@ def isGeneratedFile(filename):
return False
# Open file
- f_read = open(os.path.join(filename), 'r')
+ f_read = open(os.path.join(filename), 'r', encoding="utf8", errors="ignore")
lines_tested = 0
for line in f_read:
# The comment to say that its generated is near the top, so give up once
@@ -70,60 +68,61 @@ def isGeneratedFile(filename):
# Keep track of custom entries that might appear in multiple dissectors,
# so we can consider adding them to tfs.c
custom_tfs_entries = {}
-def AddCustomEntry(val1, val2, file):
+def AddCustomEntry(true_val, false_val, file):
global custom_tfs_entries
- if (val1, val2) in custom_tfs_entries:
- custom_tfs_entries[(val1, val2)].append(file)
+ if (true_val, false_val) in custom_tfs_entries:
+ custom_tfs_entries[(true_val, false_val)].append(file)
else:
- custom_tfs_entries[(val1, val2)] = [file]
-
+ custom_tfs_entries[(true_val, false_val)] = [file]
+# Individual parsed TFS entry
class TFS:
- def __init__(self, file, name, val1, val2):
+ def __init__(self, file, name, true_val, false_val):
self.file = file
self.name = name
- self.val1 = val1
- self.val2 = val2
+ self.true_val = true_val
+ self.false_val = false_val
global warnings_found
# Should not be empty
- if not len(val1) or not len(val2):
+ if not len(true_val) or not len(false_val):
print('Warning:', file, name, 'has an empty field', self)
warnings_found += 1
#else:
# Strange if one begins with capital but other doesn't?
- #if val1[0].isalpha() and val2[0].isalpha():
- # if val1[0].isupper() != val2[0].isupper():
+ #if true_val[0].isalpha() and false_val[0].isalpha():
+ # if true_val[0].isupper() != false_val[0].isupper():
# print(file, name, 'one starts lowercase and the other upper', self)
# Leading or trailing space should not be needed.
- if val1.startswith(' ') or val1.endswith(' '):
- print('Note: ' + self.file + ' ' + self.name + ' - false val begins or ends with space \"' + self.val1 + '\"')
- if val2.startswith(' ') or val2.endswith(' '):
- print('Note: ' + self.file + ' ' + self.name + ' - true val begins or ends with space \"' + self.val2 + '\"')
+ if true_val.startswith(' ') or true_val.endswith(' '):
+ print('Note: ' + self.file + ' ' + self.name + ' - true val begins or ends with space \"' + self.true_val + '\"')
+ if false_val.startswith(' ') or false_val.endswith(' '):
+ print('Note: ' + self.file + ' ' + self.name + ' - false val begins or ends with space \"' + self.false_val + '\"')
# Should really not be identical...
- if val1.lower() == val2.lower():
+ if true_val.lower() == false_val.lower():
print('Warning:', file, name, 'true and false strings are the same', self)
warnings_found += 1
# Shouldn't both be negation (with exception..)
- if (file != os.path.join('epan', 'dissectors', 'packet-smb.c') and (val1.lower().find('not ') != -1) and (val2.lower().find('not ') != -1)):
+ if (file != os.path.join('epan', 'dissectors', 'packet-smb.c') and (true_val.lower().find('not ') != -1) and (false_val.lower().find('not ') != -1)):
print('Warning:', file, name, self, 'both strings contain not')
warnings_found += 1
# Not expecting full-stops inside strings..
- if val1.find('.') != -1 or val2.find('.') != -1:
+ if true_val.find('.') != -1 or false_val.find('.') != -1:
print('Warning:', file, name, 'Period found in string', self)
warnings_found += 1
def __str__(self):
- return '{' + '"' + self.val1 + '", "' + self.val2 + '"}'
+ return '{' + '"' + self.true_val + '", "' + self.false_val + '"}'
+# Only looking at in terms of could/should it be TFS instead.
class ValueString:
def __init__(self, file, name, vals):
self.file = file
@@ -198,7 +197,7 @@ class Item:
self.strings = strings
self.mask = mask
- # N.B. Not sestting mask by looking up macros.
+ # N.B. Not setting mask by looking up macros.
self.item_type = item_type
self.type_modifier = type_modifier
@@ -210,16 +209,10 @@ class Item:
if self.check_bit(self.mask_value, n):
self.bits_set += 1
- def check_bit(self, value, n):
- return (value & (0x1 << n)) != 0
-
-
def __str__(self):
return 'Item ({0} "{1}" {2} type={3}:{4} strings={5} mask={6})'.format(self.filename, self.label, self.filter,
self.item_type, self.type_modifier, self.strings, self.mask)
-
-
def set_mask_value(self, macros):
try:
self.mask_read = True
@@ -227,12 +220,11 @@ class Item:
# Substitute mask if found as a macro..
if self.mask in macros:
self.mask = macros[self.mask]
- elif any(not c in '0123456789abcdefABCDEFxX' for c in self.mask):
+ elif any(c not in '0123456789abcdefABCDEFxX' for c in self.mask):
self.mask_read = False
self.mask_value = 0
return
-
# Read according to the appropriate base.
if self.mask.startswith('0x'):
self.mask_value = int(self.mask, 16)
@@ -240,7 +232,7 @@ class Item:
self.mask_value = int(self.mask, 8)
else:
self.mask_value = int(self.mask, 10)
- except:
+ except Exception:
self.mask_read = False
self.mask_value = 0
@@ -262,8 +254,7 @@ class Item:
try:
# For FT_BOOLEAN, modifier is just numerical number of bits. Round up to next nibble.
return int((int(self.type_modifier) + 3)/4)*4
- except:
- #print('oops', self)
+ except Exception:
return 0
else:
if self.item_type in field_widths:
@@ -289,7 +280,7 @@ def removeComments(code_string):
def findTFS(filename):
tfs_found = {}
- with open(filename, 'r', encoding="utf8") as f:
+ with open(filename, 'r', encoding="utf8", errors="ignore") as f:
contents = f.read()
# Example: const true_false_string tfs_yes_no = { "Yes", "No" };
@@ -299,10 +290,10 @@ def findTFS(filename):
matches = re.finditer(r'\sconst\s*true_false_string\s*([a-zA-Z0-9_]*)\s*=\s*{\s*\"([a-zA-Z_0-9/:! ]*)\"\s*,\s*\"([a-zA-Z_0-9/:! ]*)\"', contents)
for m in matches:
name = m.group(1)
- val1 = m.group(2)
- val2 = m.group(3)
+ true_val = m.group(2)
+ false_val = m.group(3)
# Store this entry.
- tfs_found[name] = TFS(filename, name, val1, val2)
+ tfs_found[name] = TFS(filename, name, true_val, false_val)
return tfs_found
@@ -317,7 +308,7 @@ def findValueStrings(filename):
# { 0, NULL }
#};
- with open(filename, 'r', encoding="utf8") as f:
+ with open(filename, 'r', encoding="utf8", errors="ignore") as f:
contents = f.read()
# Remove comments so as not to trip up RE.
@@ -333,9 +324,8 @@ def findValueStrings(filename):
# Look for hf items (i.e. full item to be registered) in a dissector file.
def find_items(filename, macros, check_mask=False, mask_exact_width=False, check_label=False, check_consecutive=False):
- is_generated = isGeneratedFile(filename)
items = {}
- with open(filename, 'r', encoding="utf8") as f:
+ with open(filename, 'r', encoding="utf8", errors="ignore") as f:
contents = f.read()
# Remove comments so as not to trip up RE.
contents = removeComments(contents)
@@ -354,7 +344,7 @@ def find_items(filename, macros, check_mask=False, mask_exact_width=False, check
def find_macros(filename):
macros = {}
- with open(filename, 'r', encoding="utf8") as f:
+ with open(filename, 'r', encoding="utf8", errors="ignore") as f:
contents = f.read()
# Remove comments so as not to trip up RE.
contents = removeComments(contents)
@@ -368,31 +358,32 @@ def find_macros(filename):
def is_dissector_file(filename):
- p = re.compile(r'.*packet-.*\.c')
+ p = re.compile(r'.*(packet|file)-.*\.c')
return p.match(filename)
def findDissectorFilesInFolder(folder):
- # Look at files in sorted order, to give some idea of how far through is.
- files = []
+ files = set()
- for f in sorted(os.listdir(folder)):
- if should_exit:
- return
- if is_dissector_file(f):
- filename = os.path.join(folder, f)
- files.append(filename)
- return files
+ for path, tmp_unused, names in os.walk(folder):
+ for f in names:
+ if should_exit:
+ return
+ if is_dissector_file(f):
+ files.add(os.path.join(path, f))
+ return files
+# Global counts
warnings_found = 0
errors_found = 0
+# name -> count
+common_usage = {}
-tfs_found = 0
# Check the given dissector file.
-def checkFile(filename, common_tfs, look_for_common=False, check_value_strings=False):
+def checkFile(filename, common_tfs, look_for_common=False, check_value_strings=False, count_common_usage=False):
global warnings_found
global errors_found
@@ -422,14 +413,15 @@ def checkFile(filename, common_tfs, look_for_common=False, check_value_strings=F
#
if os.path.commonprefix([filename, 'plugin/epan/']) == '':
exact_case = False
- if file_tfs[f].val1 == common_tfs[c].val1 and file_tfs[f].val2 == common_tfs[c].val2:
+ if file_tfs[f].true_val == common_tfs[c].true_val and file_tfs[f].false_val == common_tfs[c].false_val:
found = True
exact_case = True
- elif file_tfs[f].val1.upper() == common_tfs[c].val1.upper() and file_tfs[f].val2.upper() == common_tfs[c].val2.upper():
+ elif file_tfs[f].true_val.upper() == common_tfs[c].true_val.upper() and file_tfs[f].false_val.upper() == common_tfs[c].false_val.upper():
found = True
if found:
- print("Error:" if exact_case else "Warn: ", filename, f, "- could have used", c, 'from tfs.c instead: ', common_tfs[c],
+ print("Error:" if exact_case else "Warning: ", filename, f,
+ "- could have used", c, 'from tfs.c instead: ', common_tfs[c],
'' if exact_case else ' (capitalisation differs)')
if exact_case:
errors_found += 1
@@ -438,7 +430,7 @@ def checkFile(filename, common_tfs, look_for_common=False, check_value_strings=F
break
if not found:
if look_for_common:
- AddCustomEntry(file_tfs[f].val1, file_tfs[f].val2, filename)
+ AddCustomEntry(file_tfs[f].true_val, file_tfs[f].false_val, filename)
if check_value_strings:
# Get macros
@@ -456,7 +448,6 @@ def checkFile(filename, common_tfs, look_for_common=False, check_value_strings=F
found = False
exact_case = False
- #print('Candidate', v, vs[v])
for c in common_tfs:
found = False
@@ -473,10 +464,10 @@ def checkFile(filename, common_tfs, look_for_common=False, check_value_strings=F
#
if os.path.commonprefix([filename, 'plugin/epan/']) == '':
exact_case = False
- if common_tfs[c].val1 == vs[v].parsed_vals[True] and common_tfs[c].val2 == vs[v].parsed_vals[False]:
+ if common_tfs[c].true_val == vs[v].parsed_vals[True] and common_tfs[c].false_val == vs[v].parsed_vals[False]:
found = True
exact_case = True
- elif common_tfs[c].val1.upper() == vs[v].parsed_vals[True].upper() and common_tfs[c].val2.upper() == vs[v].parsed_vals[False].upper():
+ elif common_tfs[c].true_val.upper() == vs[v].parsed_vals[True].upper() and common_tfs[c].false_val.upper() == vs[v].parsed_vals[False].upper():
found = True
# Do values match?
@@ -488,11 +479,24 @@ def checkFile(filename, common_tfs, look_for_common=False, check_value_strings=F
if re.match(r'VALS\(\s*'+v+r'\s*\)', items[i].strings):
if items[i].bits_set == 1:
print("Warn:" if exact_case else "Note:", filename, 'value_string', "'"+v+"'",
- "- could have used", c, 'from tfs.c instead: ', common_tfs[c], 'for', i,
- '' if exact_case else ' (capitalisation differs)')
+ '- could have used tfs.c entry instead: for', i,
+ ' - "FT_BOOLEAN,', str(items[i].get_field_width_in_bits()) + ', TFS(&' + c + '),"',
+ '' if exact_case else ' (capitalisation differs)')
if exact_case:
warnings_found += 1
+ if count_common_usage:
+ # Look for TFS(&<name>) in dissector
+ with open(filename, 'r') as f:
+ contents = f.read()
+ for c in common_tfs:
+ m = re.search(r'TFS\(\s*\&' + c + r'\s*\)', contents)
+ if m:
+ if c not in common_usage:
+ common_usage[c] = 1
+ else:
+ common_usage[c] += 1
+
#################################################################
@@ -512,46 +516,46 @@ parser.add_argument('--check-value-strings', action='store_true',
parser.add_argument('--common', action='store_true',
help='check for potential new entries for tfs.c')
-
+parser.add_argument('--common-usage', action='store_true',
+ help='count how many dissectors are using common tfs entries')
args = parser.parse_args()
# Get files from wherever command-line args indicate.
-files = []
+files = set()
if args.file:
# Add specified file(s)
for f in args.file:
- if not f.startswith('epan'):
+ if not os.path.isfile(f) and not f.startswith('epan'):
f = os.path.join('epan', 'dissectors', f)
if not os.path.isfile(f):
print('Chosen file', f, 'does not exist.')
exit(1)
else:
- files.append(f)
+ files.add(f)
elif args.commits:
# Get files affected by specified number of commits.
command = ['git', 'diff', '--name-only', 'HEAD~' + args.commits]
- files = [f.decode('utf-8')
- for f in subprocess.check_output(command).splitlines()]
+ files = {f.decode('utf-8')
+ for f in subprocess.check_output(command).splitlines()}
# Will examine dissector files only
- files = list(filter(lambda f : is_dissector_file(f), files))
+ files = set(filter(is_dissector_file, files))
elif args.open:
# Unstaged changes.
command = ['git', 'diff', '--name-only']
- files = [f.decode('utf-8')
- for f in subprocess.check_output(command).splitlines()]
+ files = {f.decode('utf-8')
+ for f in subprocess.check_output(command).splitlines()}
# Only interested in dissector files.
- files = list(filter(lambda f : is_dissector_file(f), files))
+ files = list(filter(is_dissector_file, files))
# Staged changes.
command = ['git', 'diff', '--staged', '--name-only']
- files_staged = [f.decode('utf-8')
- for f in subprocess.check_output(command).splitlines()]
+ files_staged = {f.decode('utf-8')
+ for f in subprocess.check_output(command).splitlines()}
# Only interested in dissector files.
- files_staged = list(filter(lambda f : is_dissector_file(f), files_staged))
+ files = set(filter(is_dissector_file, files_staged))
for f in files_staged:
- if not f in files:
- files.append(f)
+ files.add(f)
else:
# Find all dissector files from folder.
files = findDissectorFilesInFolder(os.path.join('epan', 'dissectors'))
@@ -561,7 +565,7 @@ else:
print('Examining:')
if args.file or args.commits or args.open:
if files:
- print(' '.join(files), '\n')
+ print(' '.join(sorted(files)), '\n')
else:
print('No files to check.\n')
else:
@@ -569,14 +573,17 @@ else:
# Get standard/ shared ones.
-tfs_entries = findTFS(os.path.join('epan', 'tfs.c'))
+common_tfs_entries = findTFS(os.path.join('epan', 'tfs.c'))
# Now check the files to see if they could have used shared ones instead.
-for f in files:
+# Look at files in sorted order, to give some idea of how far through we are.
+for f in sorted(files):
if should_exit:
exit(1)
if not isGeneratedFile(f):
- checkFile(f, tfs_entries, look_for_common=args.common, check_value_strings=args.check_value_strings)
+ checkFile(f, common_tfs_entries, look_for_common=args.common,
+ check_value_strings=args.check_value_strings,
+ count_common_usage=args.common_usage)
# Report on commonly-defined values.
if args.common:
@@ -587,6 +594,12 @@ if args.common:
if len(custom_tfs_entries[c]) > 2:
print(c, 'appears', len(custom_tfs_entries[c]), 'times, in: ', custom_tfs_entries[c])
+if args.common_usage:
+ for c in common_tfs_entries:
+ if c in common_usage:
+ print(c, 'used in', common_usage[c], 'dissectors')
+ else:
+ print('***', c, 'IS NOT USED! ***')
# Show summary.
print(warnings_found, 'warnings found')