1/*
2 * Licensed under a two-clause BSD-style license.
3 * See LICENSE for details.
4 */
5
6#include "git-compat-util.h"
7#include "sliding_window.h"
8#include "line_buffer.h"
9#include "svndiff.h"
10
11/*
12 * svndiff0 applier
13 *
14 * See http://svn.apache.org/repos/asf/subversion/trunk/notes/svndiff.
15 *
16 * svndiff0 ::= 'SVN\0' window*
17 * window ::= int int int int int instructions inline_data;
18 * int ::= highdigit* lowdigit;
19 * highdigit ::= # binary 1000 0000 OR-ed with 7 bit value;
20 * lowdigit ::= # 7 bit value;
21 */
22
23#define VLI_CONTINUE 0x80
24#define VLI_DIGIT_MASK 0x7f
25#define VLI_BITS_PER_DIGIT 7
26
27static int error_short_read(struct line_buffer *input)
28{
29 if (buffer_ferror(input))
30 return error("error reading delta: %s", strerror(errno));
31 return error("invalid delta: unexpected end of file");
32}
33
34static int read_magic(struct line_buffer *in, off_t *len)
35{
36 static const char magic[] = {'S', 'V', 'N', '\0'};
37 struct strbuf sb = STRBUF_INIT;
38
39 if (*len < sizeof(magic) ||
40 buffer_read_binary(in, &sb, sizeof(magic)) != sizeof(magic)) {
41 error_short_read(in);
42 strbuf_release(&sb);
43 return -1;
44 }
45
46 if (memcmp(sb.buf, magic, sizeof(magic))) {
47 strbuf_release(&sb);
48 return error("invalid delta: unrecognized file type");
49 }
50
51 *len -= sizeof(magic);
52 strbuf_release(&sb);
53 return 0;
54}
55
56static int read_int(struct line_buffer *in, uintmax_t *result, off_t *len)
57{
58 uintmax_t rv = 0;
59 off_t sz;
60 for (sz = *len; sz; sz--) {
61 const int ch = buffer_read_char(in);
62 if (ch == EOF)
63 break;
64
65 rv <<= VLI_BITS_PER_DIGIT;
66 rv += (ch & VLI_DIGIT_MASK);
67 if (ch & VLI_CONTINUE)
68 continue;
69
70 *result = rv;
71 *len = sz - 1;
72 return 0;
73 }
74 return error_short_read(in);
75}
76
77static int read_offset(struct line_buffer *in, off_t *result, off_t *len)
78{
79 uintmax_t val;
80 if (read_int(in, &val, len))
81 return -1;
82 if (val > maximum_signed_value_of_type(off_t))
83 return error("unrepresentable offset in delta: %"PRIuMAX"", val);
84 *result = val;
85 return 0;
86}
87
88static int read_length(struct line_buffer *in, size_t *result, off_t *len)
89{
90 uintmax_t val;
91 if (read_int(in, &val, len))
92 return -1;
93 if (val > SIZE_MAX)
94 return error("unrepresentable length in delta: %"PRIuMAX"", val);
95 *result = val;
96 return 0;
97}
98
99static int apply_one_window(struct line_buffer *delta, off_t *delta_len)
100{
101 size_t out_len;
102 size_t instructions_len;
103 size_t data_len;
104 assert(delta_len);
105
106 /* "source view" offset and length already handled; */
107 if (read_length(delta, &out_len, delta_len) ||
108 read_length(delta, &instructions_len, delta_len) ||
109 read_length(delta, &data_len, delta_len))
110 return -1;
111 if (instructions_len)
112 return error("What do you think I am? A delta applier?");
113 if (data_len)
114 return error("No support for inline data yet");
115 return 0;
116}
117
118int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
119 struct sliding_view *preimage, FILE *postimage)
120{
121 assert(delta && preimage && postimage);
122
123 if (read_magic(delta, &delta_len))
124 return -1;
125 while (delta_len) { /* For each window: */
126 off_t pre_off;
127 size_t pre_len;
128
129 if (read_offset(delta, &pre_off, &delta_len) ||
130 read_length(delta, &pre_len, &delta_len) ||
131 move_window(preimage, pre_off, pre_len) ||
132 apply_one_window(delta, &delta_len))
133 return -1;
134 }
135 return 0;
136}