1#!/usr/bin/env python
2
3from __future__ import print_function
4from builtins import str
5import sys, os, os.path as path, struct, errno
6from optparse import OptionParser
7
8def xencov_split(opts):
9    """Split input into multiple gcda files"""
10
11    # Check native byte order and explicitly specify it.  The "native"
12    # byte order in struct module takes into account padding while the
13    # data is always packed.
14    if sys.byteorder == 'little':
15        bo_prefix = '<'
16    else:
17        bo_prefix = '>'
18
19    input_file = opts.args[0]
20
21    f = open(input_file, "rb")
22
23    # Magic number
24    s = f.read(4)
25    magic, = struct.unpack(bo_prefix + "I", s)
26    # See public/sysctl.h for magic number -- "XCOV"
27    if magic != 0x58434f56:
28        raise Exception("Invalid magic number")
29
30    # The rest is zero or more records
31    content = f.read()
32
33    f.close()
34
35    while content:
36        off = content.find(b'\x00')
37        fmt = bo_prefix + str(off) + 's'
38        fn, = struct.unpack_from(fmt, content)
39        fn = fn.decode('utf-8')
40        content = content[off+1:]
41
42        fmt = bo_prefix + 'I'
43        sz, = struct.unpack_from(fmt, content)
44        content = content[struct.calcsize(fmt):]
45
46        fmt = bo_prefix + str(sz) + 's'
47        payload, = struct.unpack_from(fmt, content)
48        content = content[sz:]
49
50        # Create and store files
51        if opts.output_dir == '.':
52            opts.output_dir = os.getcwd()
53
54        dir = opts.output_dir + path.dirname(fn)
55        try:
56            os.makedirs(dir)
57        except OSError as e:
58            if e.errno == errno.EEXIST and os.path.isdir(dir):
59                pass
60            else:
61                raise
62
63        full_path = dir + '/' + path.basename(fn)
64        f = open(full_path, "wb")
65        f.write(payload)
66        f.close()
67
68def main():
69    """ Main entrypoint """
70
71    # Change stdout to be line-buffered.
72    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
73
74    parser = OptionParser(
75        usage = "%prog [OPTIONS] <INPUT>",
76        description = "Utility to split xencov data file",
77        )
78
79    parser.add_option("--output-dir", action = "store",
80                      dest = "output_dir", default = ".",
81                      type = "string",
82                      help = ('Specify the directory to place output files, '
83                              'defaults to current directory'),
84                      )
85
86    opts, args = parser.parse_args()
87    opts.args = args
88
89    xencov_split(opts)
90
91
92if __name__ == "__main__":
93    try:
94        sys.exit(main())
95    except Exception as e:
96        print("Error:", e, file=sys.stderr)
97        sys.exit(1)
98    except KeyboardInterrupt:
99        sys.exit(1)
100