#!/usr/bin/env python3
# cp866 1.0: texts of DOS and of the Russian ZX Spectrum (cp866, "alt" coding) to UTF-8 and back, for Midnight
# Commander. See README.md.
# Copyright (c) 2026 Spectre (Optical Brothers), https://www.zxby.org. MIT License (see LICENSE).
#
#   cp866 [-f] INPUT [OUTPUT]      cp866 -> UTF-8; line ends CR LF and CR (the Spectrum's) -> LF
#   cp866 -r [-f] INPUT [OUTPUT]   UTF-8 -> cp866, line ends CR LF
#
# INPUT "-" is stdin, OUTPUT "-" stdout; without OUTPUT the file goes next to INPUT: name.txt -> name.utf8.txt
# (-r: name.cp866.txt).
# An output that exists is kept unless -f.

import os
import re
import sys


class Error(Exception):
    pass


def to_utf8(data):
    text = data.decode('cp866')
    return re.sub('\r\n?', '\n', text).encode('utf-8')


def to_cp866(data):
    try:
        text = data.decode('utf-8')
    except UnicodeDecodeError as e:
        raise Error('not UTF-8: byte #%02X at %d' % (data[e.start], e.start))
    if text.startswith('\ufeff'):
        text = text[1:]
    lines = re.split('\r\n|\r|\n', text)
    out = bytearray()
    for n, line in enumerate(lines):
        try:
            out += line.encode('cp866')
        except UnicodeEncodeError as e:
            raise Error('line %d, column %d: %r is not in cp866' % (n + 1, e.start + 1, line[e.start]))
        if n < len(lines) - 1:
            out += b'\r\n'
    return bytes(out)


def output_name(path, suffix):
    """name.txt -> name.utf8.txt; name -> name.utf8."""
    head, base = os.path.split(path)
    stem, ext = os.path.splitext(base)
    if not stem:  # .profile
        stem, ext = base, ''
    return os.path.join(head, stem + '.' + suffix + ext)


def main(argv):
    args = argv[1:]
    reverse = force = False
    while args and args[0].startswith('-') and args[0] != '-':
        opt = args.pop(0)
        if opt == '-r':
            reverse = True
        elif opt == '-f':
            force = True
        elif opt == '--':
            break
        else:
            args = []
            break
    if len(args) not in (1, 2):
        sys.stderr.write('usage: cp866 [-r] [-f] INPUT [OUTPUT|-]\n')
        return 2
    src = args[0]
    dest = args[1] if len(args) == 2 else '-' if src == '-' else output_name(src, 'cp866' if reverse else 'utf8')
    try:
        if src == '-':
            data = sys.stdin.buffer.read()
        else:
            with open(src, 'rb') as f:
                data = f.read()
        out = to_cp866(data) if reverse else to_utf8(data)
        if dest == '-':
            sys.stdout.buffer.write(out)
            return 0
        if os.path.exists(dest) and not force:
            raise Error('%s exists (-f writes over it)' % dest)
        with open(dest, 'wb') as f:
            f.write(out)
    except (Error, OSError) as e:
        sys.stderr.write('cp866: %s\n' % e)
        return 1
    return 0


if __name__ == '__main__':
    sys.exit(main(sys.argv))
