<reponame>PJB3005/ByondToolsv3
#!/usr/bin/env python
import sys, re
from byond.basetypes import Proc
from byond.objtree import ObjectTree

class CodeScan:
    def __init__(self, id):
        self.id = id
        self.atom = None
        self.proc = None
        
    def SetContext(self, atom, proc):
        self.atom = atom
        self.proc = proc
        
    def MatchAtom(self, atom):
        return True
    
    def MatchProc(self, atom, proc):
        return True
    
    def ScanLine(self, line):
        return []
    
class RegexScanner(CodeScan):
    def __init__(self, id, regex, warning, in_procnames=()):
        CodeScan.__init__(self, id)
        self.regex = regex
        self.warning = warning
        self.in_procnames = in_procnames
        
    def MatchProc(self, atom, proc):
        # print(proc.path)
        return self.in_procnames == () or proc.name in self.in_procnames
    
    def ScanLine(self, line):
        m = self.regex.search(line)
        if m is not None:
            return [self.warning]
        return []
    
rules = [
    RegexScanner(
        id='sleep-in-process',
        warning='sleep() should not be used in process().',
        regex=re.compile(r'\bsleep\(([0-9]+)\)\b'),
        in_procnames=('process')
    ),
    RegexScanner(
        id='spawn-in-process',
        warning='spawn() should not be used in process().',
        regex=re.compile(r'\bspawn(\(([0-9]*)\))?\b'),
        in_procnames=('process')
    ),
    RegexScanner(
        id='del-in-ex_act',
        warning='del() should not be used in ex_act(). Consider replacing with qdel.',
        regex=re.compile(r'\bdel(\(.*\))?\b'),
        in_procnames=('ex_act')
    ),
]

otr = ObjectTree()
otr.ProcessFilesFromDME(sys.argv[1], '.dm')
procs = 0
alerts = 0
for path in otr.Atoms:
    atom = otr.GetAtom(path)
    if isinstance(atom,Proc): continue
    proceed = False
    skipping = []
    for rule in rules:
        if rule.MatchAtom(atom):
            proceed = True
        else:
            skipping += [rule]
    if not proceed: continue
    for childName in atom.children:
        warnings = {}
        child = atom.children[childName]
        if isinstance(child, Proc):
            proceed = False
            for rule in rules:
                if rule not in skipping and rule.MatchProc(atom, child):
                    proceed = True
                else:
                    skipping += [rule]
            if not proceed: continue
            
            '''
            if 'process' in path + ' - ' + childName:
                print(path + ' - ' + childName)
            '''
            # print(path + ' - ' + childName)
            procs += 1
            min_indent = child.getMinimumIndent()
            # Should be 1, so find the difference.
            indent_delta = 1 - min_indent
            for i in range(len(child.code)):
                line_warnings = []
                indent, code = child.code[i]
                indent = max(1, indent + indent_delta)
                if code.strip() == '':
                    o = '\n'
                else:
                    o = (indent * '\t') + code.strip() + '\n'
                code = o.strip('\n\r')
                for rule in rules:
                    if rule in skipping: continue
                    rule.SetContext(atom, child)
                    line_warnings += rule.ScanLine(code)
                if len(line_warnings):
                    warnings['{0}:{1}: {2}'.format(child.filename, child.line + i, code)] = line_warnings
                    alerts += len(line_warnings)
        if len(warnings) > 0:
            print('IN {0}/{1}:'.format(path, childName))
            for context in warnings:
                print('  {0}'.format(context))
                for warning in warnings[context]:
                    print('    WARNING: {0}'.format(warning))
print('{0} processed procs, {1} alerts raised'.format(procs, alerts))

#!/usr/bin/env python
import sys, re
from byond.basetypes import Proc
from byond.objtree import ObjectTree

class CodeScan:
    def __init__(self, id):
        self.id = id
        self.atom = None
        self.proc = None
        
    def SetContext(self, atom, proc):
        self.atom = atom
        self.proc = proc
        
    def MatchAtom(self, atom):
        return True
    
    def MatchProc(self, atom, proc):
        return True
    
    def ScanLine(self, line):
        return []
    
class RegexScanner(CodeScan):
    def __init__(self, id, regex, warning, in_procnames=()):
        CodeScan.__init__(self, id)
        self.regex = regex
        self.warning = warning
        self.in_procnames = in_procnames
        
    def MatchProc(self, atom, proc):
        # print(proc.path)
        return self.in_procnames == () or proc.name in self.in_procnames
    
    def ScanLine(self, line):
        m = self.regex.search(line)
        if m is not None:
            return [self.warning]
        return []
    
rules = [
    RegexScanner(
        id='sleep-in-process',
        warning='sleep() should not be used in process().',
        regex=re.compile(r'\bsleep\(([0-9]+)\)\b'),
        in_procnames=('process')
    ),
    RegexScanner(
        id='spawn-in-process',
        warning='spawn() should not be used in process().',
        regex=re.compile(r'\bspawn(\(([0-9]*)\))?\b'),
        in_procnames=('process')
    ),
    RegexScanner(
        id='del-in-ex_act',
        warning='del() should not be used in ex_act(). Consider replacing with qdel.',
        regex=re.compile(r'\bdel(\(.*\))?\b'),
        in_procnames=('ex_act')
    ),
]

otr = ObjectTree()
otr.ProcessFilesFromDME(sys.argv[1], '.dm')
procs = 0
alerts = 0
for path in otr.Atoms:
    atom = otr.GetAtom(path)
    if isinstance(atom,Proc): continue
    proceed = False
    skipping = []
    for rule in rules:
        if rule.MatchAtom(atom):
            proceed = True
        else:
            skipping += [rule]
    if not proceed: continue
    for childName in atom.children:
        warnings = {}
        child = atom.children[childName]
        if isinstance(child, Proc):
            proceed = False
            for rule in rules:
                if rule not in skipping and rule.MatchProc(atom, child):
                    proceed = True
                else:
                    skipping += [rule]
            if not proceed: continue
            
            '''
            if 'process' in path + ' - ' + childName:
                print(path + ' - ' + childName)
            '''
            # print(path + ' - ' + childName)
            procs += 1
            min_indent = child.getMinimumIndent()
            # Should be 1, so find the difference.
            indent_delta = 1 - min_indent
            for i in range(len(child.code)):
                line_warnings = []
                indent, code = child.code[i]
                indent = max(1, indent + indent_delta)
                if code.strip() == '':
                    o = '\n'
                else:
                    o = (indent * '\t') + code.strip() + '\n'
                code = o.strip('\n\r')
                for rule in rules:
                    if rule in skipping: continue
                    rule.SetContext(atom, child)
                    line_warnings += rule.ScanLine(code)
                if len(line_warnings):
                    warnings['{0}:{1}: {2}'.format(child.filename, child.line + i, code)] = line_warnings
                    alerts += len(line_warnings)
        if len(warnings) > 0:
            print('IN {0}/{1}:'.format(path, childName))
            for context in warnings:
                print('  {0}'.format(context))
                for warning in warnings[context]:
                    print('    WARNING: {0}'.format(warning))
print('{0} processed procs, {1} alerts raised'.format(procs, alerts))
#!/usr/bin/env python # print(proc.path) if 'process' in path + ' - ' + childName:                 print(path + ' - ' + childName) # print(path + ' - ' + childName) # Should be 1, so find the difference.