summaryrefslogtreecommitdiff
path: root/morphlib/xfer-hole
blob: 08222322212f56a8e3bc6f0fb49b4df032d70b6a (plain)
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
#!/usr/bin/env python


import errno
import os
import sys


SEEK_DATA = 3
SEEK_HOLE = 4


filename = sys.argv[1]
fd = os.open(filename, os.O_RDONLY)
pos = 0


DATA = 'data'
HOLE = 'hole'
EOF = 'eof'


def safe_lseek(fd, pos, whence):
    try:
        return os.lseek(fd, pos, whence)
    except OSError as e:
        if e.errno == errno.ENXIO:
            return -1
        raise


def current_data_or_pos(fd, pos):
    length = safe_lseek(fd, 0, os.SEEK_END)
    next_data = safe_lseek(fd, pos, SEEK_DATA)
    next_hole = safe_lseek(fd, pos, SEEK_HOLE)

    if pos == length:
        return EOF, pos
    elif pos == next_data:
        return DATA, pos
    elif pos == next_hole:
        return HOLE, pos
    else:
        assert False, \
            ("Do not understand: pos=%d next_data=%d next_hole=%d" %
             (pos, next_data, next_hole))


def next_data_or_hole(fd, pos):
    length = safe_lseek(fd, 0, os.SEEK_END)
    next_data = safe_lseek(fd, pos, SEEK_DATA)
    next_hole = safe_lseek(fd, pos, SEEK_HOLE)

    if pos == length:
        return EOF, pos
    elif pos == next_data:
        # We are at data.
        if next_hole == -1 or next_hole == length:
            return EOF, length
        else:
            return HOLE, next_hole
    elif pos == next_hole:
        # We are at a hole.
        if next_data == -1 or next_data == length:
            return EOF, length
        else:
            return DATA, next_data
    else:
        assert False, \
            ("Do not understand: pos=%d next_data=%d next_hole=%d" %
             (pos, next_data, next_hole))


def find_data_and_holes(fd):
    pos = safe_lseek(fd, 0, os.SEEK_CUR)

    kind, pos = current_data_or_pos(fd, pos)
    while kind != EOF:
        yield kind, pos
        kind, pos = next_data_or_hole(fd, pos)
    yield kind, pos


def make_xfer_instructions(fd):
    prev_kind = None
    prev_pos = None
    for kind, pos in find_data_and_holes(fd):
        if prev_kind == DATA:
            yield (DATA, prev_pos, pos)
        elif prev_kind == HOLE:
            yield (HOLE, prev_pos, pos)
        prev_kind = kind
        prev_pos = pos


def copy_slice_from_file(to, fd, start, end):
    safe_lseek(fd, start, os.SEEK_SET)
    data = os.read(fd, end - start)
    to.write(data)
    

for kind, start, end in make_xfer_instructions(fd):
    if kind == HOLE:
        sys.stdout.write('HOLE\n%d\n' % (end - start))
    elif kind == DATA:
        sys.stdout.write('DATA\n%d\n' % (end - start))
        copy_slice_from_file(sys.stdout, fd, start, end)