File: main.py

package info (click to toggle)
naev 0.8.2-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 386,084 kB
  • sloc: ansic: 93,149; xml: 87,292; python: 2,347; sh: 904; makefile: 654; lisp: 162; awk: 4
file content (283 lines) | stat: -rwxr-xr-x 10,630 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=80:

__doc__ = """
A validity check tool to know where the crap is.

For usage information, run ``main.py --usage``

For licensing information, see the LICENSE file in this directory.
"""

import os, sys
from argparse import ArgumentParser
import re
from types import *

__version__="0.2"

def lineNumber(string, start):
    """
    Return the line number and offset from a regex match
    """
    lineno = string.count('\n', 0, start) + 1
    offset = start - string.rfind('\n', 0, start)
    return (lineno, offset)

class sanitizer:

    _errorstring = "Can not find element ``%(content)s'' for function "    \
                   "%(func)s at line %(lineno)d offset %(offset)d of file " \
                   "%(file)s"

    def __init__(self, **args):
        """
        Initialize the whole parser : create a list of lua files.
        """
        self.config = args
        self.luaScripts = list()

        if 'datpath' not in self.config.keys():
            self.config['datpath'] = os.path.join(self.config['basepath'],
                                                  'dat/')

        if self.config['use'] == 'xml':
            self.dirtyfiles_from_xml()
        elif self.config['use'] == 'rawfiles':
            self.dirtyfiles_from_directory()

        print('Compiling script files...',end='      ')
        for root, dir, files in os.walk(self.config['basepath']+'/scripts/'):
            self.addLuaFile(files, root)
        print('DONE')

        print('Compiling AI Spawn files...', end='      ')
        for root, dir, files in os.walk(self.config['datpath']+'factions/spawn/'):
            self.addLuaFile(files, root)
        print('DONE')

    def addLuaFile(self, files, root):
        """
        Add a lua file in the bucket. Do nothing if the given file do not end
        with 'lua'
        """
        for filename in files:
            if filename[-3:] == "lua":
                realname = os.path.join(root, filename)
                self.luaScripts.append(realname)

    def dirtyfiles_from_directory(self):
        """
        retrieve a list of files from the directory (the wild viking way)
        """
        for name in "mission", "event":
            print('Compiling ' + name + ' file list like a wild viking ...', end='       ')
            for root, dir, files in os.walk("{0}/{1}s".format(luapath, name)):
                self.addLuaFile(files, root)
            print('DONE')
        return True

    def dirtyfiles_from_xml(self):
        """
        retrieve a list of files from the mission and event XML (the quiet british way)
        """
        import xml.etree.ElementTree as ET
        for name in "mission", "event":
            print('Compiling {0} file list ...'.format(name), end='      ')
            file = os.path.join(self.config['basepath'], "{0}/{1}.xml".format(luapath, name))
            naevxml = ET.parse(file)
            for mission in naevxml.findall(name):
                for lf in mission.findall('lua'):
                    lf = os.path.join("{0}/{1}s".format(luapath, name), lf.text + '.lua')
                    self.luaScripts.append(lf)
            print('DONE')
        return True

    def dah_doctor(self):
        """
        That's the doctor, it detect wrong stuff but can never heal you.

        Pretty big function indeed :
        1/ Will do a call to undiff, then initialize the preprocessing stuff.
        2/ Then, initialize each reader with the appropriate config
        3/ Then, parse each lua file searching for function and content
        """
        from readers import fleet
        from readers import ship
        from readers import outfit
        from readers import unidiff
        from readers.preprocessing import tech

        udata = unidiff(datpath=self.config['datpath'],
                        verbose=self.config['verbose'])

        # This will do some preprocessing check in the xml files
        print('Preprocess valitation :')
        otech = tech(unidiffobj=udata,**self.config)

        # TODO: must be called when needed
        fleetdata = fleet(datpath=self.config['datpath'],
                        verbose=self.config['verbose'])
        shipdata = ship(datpath=self.config['datpath'],
                        verbose=self.config['verbose'],
                        tech=otech, fleetobj=fleetdata)
        outfitdata = outfit(datpath=self.config['datpath'],
                        verbose=self.config['verbose'], tech=otech)

        rawstr = r"""
        (?P<func>
            pilot\.add\(|
            pilot\.addRaw\(|
            player\.addShip\(|
            addOutfit\(|
            diff\.apply\(|
            scom\.addPilot\()\s* (?P<hackery>pilots,|
        )\s*"(?P<content>[^"]+)"""

        search_cobj = re.compile(rawstr, re.VERBOSE| re.UNICODE)

        entry = dict()
        errors = list()

        # XXX This variable will stock all the lua files. This could lead to a
        # massive memory consumption.
        line = dict()

        print("Basic check now ...")
        for file in self.luaScripts:
            if self.config['verbose']:
                print("Processing file {0}...".format(file), end='       ')
            if len(errors) > 0:
                for error in errors:
                    print(error, file=sys.stderr)
                errors = list()

            try:
                line[file] = open(file, 'rU').read()
                haserror=False
                for match in search_cobj.finditer(line[file]):
                    lineno, offset = lineNumber(line[file], match.start())
                    info = dict(
                            lineno=lineno,
                            offset=offset,
                            func=match.group('func')[:-1],
                            content= match.group('content'),
                            file=file
                    )

                    if info['func'] == 'pilot.add':
                        if not fleetdata.find(info['content']):
                            haserror=True
                    if info['func'] == 'scom.addPilot':
                        if not fleetdata.find(info['content']):
                            haserror=True
                    if info['func'] == 'pilot.addRaw':
                        if not shipdata.find(info['content']):
                            haserror=True
                    if info['func'] == 'addOutfit':
                        if not outfitdata.find(info['content']):
                            haserror=True
                    if info['func'] == 'player.addShip':
                        if not shipdata.find(info['content']):
                            haserror=True
                    if info['func'] == 'diff.apply':
                        if not udata.find(info['content']):
                            haserror=True

                    if haserror:
                        errors.append(self._errorstring % info)
                        haserror=False

            except IOError as error:
                print("I/O error: {0}".format(error), file=sys.stderr)
            except:
                raise

            if self.config['verbose']:
                print("DONE")

        print('Verifying ...')
        unused_data = dict()

        # makes sure that only category with unused stuff get listed
        tocheck = ((fleetdata, 'fleet'), (udata,'unidiff'),
                   (shipdata, 'ship'), (outfitdata, 'outfit'))
        for obj, key in tocheck:
            tmp = obj.get_unused()
            if len(tmp) > 0:
                unused_data.update({key: tmp})
        del(tmp)

        if len(unused_data) > 0:
           # Create the regex
            mcobj = dict()
            for (category, data) in unused_data.items():
                regex = r"""
                (?P<{0}>%s)
                """.format(category)
                rname = ''
                uniqList = list()
                for name in data:
                    name = name.replace(' ', "\s")
                    rname = rname + name + '|'
                    if name not in uniqList:
                        uniqList.append(name)
                regex = regex % rname[:-1]
                mcobj.update({category: re.compile(regex, re.VERBOSE| re.UNICODE)})

            # For each file, run each regex
            # If an item is found, that item is set to the unknown status
            for (file, content) in line.items():
                for (category, cobj) in mcobj.items():
                    for match in cobj.finditer(content):
                        groups = match.groupdict()
                        for (obj, key) in tocheck:
                            if key == category:
                                obj.set_unknown(groups[category])

        outfitdata.showMissingTech()
        shipdata.showMissingTech()

        if self.config['show_unused']:
            # XXX there is no real needs to use these two because of
            # ''showMissingTech`` called earlier, unless something is tagged as
            # 'UNKNOWN'
            outfitdata.show_unused()
            shipdata.show_unused()
            fleetdata.show_unused()
            udata.show_unused()

if __name__ == "__main__":

    parser = ArgumentParser(description="""
                Naev validity check v%s.
                You are valid.
             """ %  __version__)
    parser.add_argument('--use', '-u',
                        choices=['xml','rawfiles'], default='xml',
                        help="""
                        Use the rawfiles option to run into the directory like
                        a wild viking. Otherwise, the script will use the active
                        mission list to load the file list.
                        """)

    gfleet = parser.add_argument_group('Fleets')
    gfleet.add_argument('--show-unused', action='store_true', default=False,
                        help='Show unused fleets from the xml files')

    parser.add_argument('--version', action='version',
                        version='%(prog)s '+__version__)
    parser.add_argument('--verbose', action='store_true', default=False)
    parser.add_argument('basepath',
                        help='Path to naev/ directory')

    args = parser.parse_args()

    basepath = os.path.abspath(args.basepath)
    luapath = os.path.abspath(basepath + '/dat/')
    invalid = sanitizer(basepath=basepath, luapath=luapath,
                        verbose=args.verbose,use=args.use,
                        show_unused=args.show_unused)

    invalid.dah_doctor()