#!/usr/pkg/bin/python3.13

from __future__ import print_function
from builtins import str
import sys, os, os.path as path, struct, errno
from optparse import OptionParser

def xencov_split(opts):
    """Split input into multiple gcda files"""

    # Check native byte order and explicitly specify it.  The "native"
    # byte order in struct module takes into account padding while the
    # data is always packed.
    if sys.byteorder == 'little':
        bo_prefix = '<'
    else:
        bo_prefix = '>'

    input_file = opts.args[0]

    f = open(input_file, "rb")

    # Magic number
    s = f.read(4)
    magic, = struct.unpack(bo_prefix + "I", s)
    # See public/sysctl.h for magic number -- "XCOV"
    if magic != 0x58434f56:
        raise Exception("Invalid magic number")

    # The rest is zero or more records
    content = f.read()

    f.close()

    while content:
        off = content.find(b'\x00')
        fmt = bo_prefix + str(off) + 's'
        fn, = struct.unpack_from(fmt, content)
        fn = fn.decode('utf-8')
        content = content[off+1:]

        fmt = bo_prefix + 'I'
        sz, = struct.unpack_from(fmt, content)
        content = content[struct.calcsize(fmt):]

        fmt = bo_prefix + str(sz) + 's'
        payload, = struct.unpack_from(fmt, content)
        content = content[sz:]

        # Create and store files
        if opts.output_dir == '.':
            opts.output_dir = os.getcwd()

        dir = opts.output_dir + path.dirname(fn)
        try:
            os.makedirs(dir)
        except OSError as e:
            if e.errno == errno.EEXIST and os.path.isdir(dir):
                pass
            else:
                raise

        full_path = dir + '/' + path.basename(fn)
        f = open(full_path, "wb")
        f.write(payload)
        f.close()

def main():
    """ Main entrypoint """

    # Change stdout to be line-buffered.
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)

    parser = OptionParser(
        usage = "%prog [OPTIONS] <INPUT>",
        description = "Utility to split xencov data file",
        )

    parser.add_option("--output-dir", action = "store",
                      dest = "output_dir", default = ".",
                      type = "string",
                      help = ('Specify the directory to place output files, '
                              'defaults to current directory'),
                      )

    opts, args = parser.parse_args()
    opts.args = args

    xencov_split(opts)


if __name__ == "__main__":
    try:
        sys.exit(main())
    except Exception as e:
        print("Error:", e, file=sys.stderr)
        sys.exit(1)
    except KeyboardInterrupt:
        sys.exit(1)
