~ubuntu-branches/ubuntu/raring/monodevelop/raring

« back to all changes in this revision

Viewing changes to contrib/NGit/NGit.Patch/FileHeader.cs

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Mitchell
  • Date: 2011-06-29 06:56:25 UTC
  • mfrom: (1.8.1 upstream) (1.3.11 experimental)
  • Revision ID: james.westby@ubuntu.com-20110629065625-7xx19c4vb95j65pl
Tags: 2.5.92+dfsg-1ubuntu1
* Merge from Debian experimental:
 - Dropped patches & changes to debian/control for Moonlight
   + debian/patches/remove_support_for_moonlight.patch,
   + debian/patches/dont_add_moonlight_to_core_addins.patch,
 - Remaining patches:
   + debian/patches/no_appmenu:

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This code is derived from jgit (http://eclipse.org/jgit).
 
3
Copyright owners are documented in jgit's IP log.
 
4
 
 
5
This program and the accompanying materials are made available
 
6
under the terms of the Eclipse Distribution License v1.0 which
 
7
accompanies this distribution, is reproduced below, and is
 
8
available at http://www.eclipse.org/org/documents/edl-v10.php
 
9
 
 
10
All rights reserved.
 
11
 
 
12
Redistribution and use in source and binary forms, with or
 
13
without modification, are permitted provided that the following
 
14
conditions are met:
 
15
 
 
16
- Redistributions of source code must retain the above copyright
 
17
  notice, this list of conditions and the following disclaimer.
 
18
 
 
19
- Redistributions in binary form must reproduce the above
 
20
  copyright notice, this list of conditions and the following
 
21
  disclaimer in the documentation and/or other materials provided
 
22
  with the distribution.
 
23
 
 
24
- Neither the name of the Eclipse Foundation, Inc. nor the
 
25
  names of its contributors may be used to endorse or promote
 
26
  products derived from this software without specific prior
 
27
  written permission.
 
28
 
 
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
42
*/
 
43
 
 
44
using System;
 
45
using System.Collections.Generic;
 
46
using System.IO;
 
47
using System.Text;
 
48
using NGit;
 
49
using NGit.Diff;
 
50
using NGit.Patch;
 
51
using NGit.Util;
 
52
using Sharpen;
 
53
 
 
54
namespace NGit.Patch
 
55
{
 
56
        /// <summary>Patch header describing an action for a single file path.</summary>
 
57
        /// <remarks>Patch header describing an action for a single file path.</remarks>
 
58
        public class FileHeader : DiffEntry
 
59
        {
 
60
                private static readonly byte[] OLD_MODE = Constants.EncodeASCII("old mode ");
 
61
 
 
62
                private static readonly byte[] NEW_MODE = Constants.EncodeASCII("new mode ");
 
63
 
 
64
                internal static readonly byte[] DELETED_FILE_MODE = Constants.EncodeASCII("deleted file mode "
 
65
                        );
 
66
 
 
67
                internal static readonly byte[] NEW_FILE_MODE = Constants.EncodeASCII("new file mode "
 
68
                        );
 
69
 
 
70
                private static readonly byte[] COPY_FROM = Constants.EncodeASCII("copy from ");
 
71
 
 
72
                private static readonly byte[] COPY_TO = Constants.EncodeASCII("copy to ");
 
73
 
 
74
                private static readonly byte[] RENAME_OLD = Constants.EncodeASCII("rename old ");
 
75
 
 
76
                private static readonly byte[] RENAME_NEW = Constants.EncodeASCII("rename new ");
 
77
 
 
78
                private static readonly byte[] RENAME_FROM = Constants.EncodeASCII("rename from "
 
79
                        );
 
80
 
 
81
                private static readonly byte[] RENAME_TO = Constants.EncodeASCII("rename to ");
 
82
 
 
83
                private static readonly byte[] SIMILARITY_INDEX = Constants.EncodeASCII("similarity index "
 
84
                        );
 
85
 
 
86
                private static readonly byte[] DISSIMILARITY_INDEX = Constants.EncodeASCII("dissimilarity index "
 
87
                        );
 
88
 
 
89
                internal static readonly byte[] INDEX = Constants.EncodeASCII("index ");
 
90
 
 
91
                internal static readonly byte[] OLD_NAME = Constants.EncodeASCII("--- ");
 
92
 
 
93
                internal static readonly byte[] NEW_NAME = Constants.EncodeASCII("+++ ");
 
94
 
 
95
                /// <summary>Type of patch used by this file.</summary>
 
96
                /// <remarks>Type of patch used by this file.</remarks>
 
97
                public enum PatchType
 
98
                {
 
99
                        UNIFIED,
 
100
                        BINARY,
 
101
                        GIT_BINARY
 
102
                }
 
103
 
 
104
                /// <summary>Buffer holding the patch data for this file.</summary>
 
105
                /// <remarks>Buffer holding the patch data for this file.</remarks>
 
106
                internal readonly byte[] buf;
 
107
 
 
108
                /// <summary>
 
109
                /// Offset within
 
110
                /// <see cref="buf">buf</see>
 
111
                /// to the "diff ..." line.
 
112
                /// </summary>
 
113
                internal readonly int startOffset;
 
114
 
 
115
                /// <summary>
 
116
                /// Position 1 past the end of this file within
 
117
                /// <see cref="buf">buf</see>
 
118
                /// .
 
119
                /// </summary>
 
120
                internal int endOffset;
 
121
 
 
122
                /// <summary>Type of patch used to modify this file</summary>
 
123
                internal FileHeader.PatchType patchType;
 
124
 
 
125
                /// <summary>The hunks of this file</summary>
 
126
                private IList<HunkHeader> hunks;
 
127
 
 
128
                /// <summary>
 
129
                /// If
 
130
                /// <see cref="patchType">patchType</see>
 
131
                /// is
 
132
                /// <see cref="PatchType.GIT_BINARY">PatchType.GIT_BINARY</see>
 
133
                /// , the new image
 
134
                /// </summary>
 
135
                internal BinaryHunk forwardBinaryHunk;
 
136
 
 
137
                /// <summary>
 
138
                /// If
 
139
                /// <see cref="patchType">patchType</see>
 
140
                /// is
 
141
                /// <see cref="PatchType.GIT_BINARY">PatchType.GIT_BINARY</see>
 
142
                /// , the old image
 
143
                /// </summary>
 
144
                internal BinaryHunk reverseBinaryHunk;
 
145
 
 
146
                /// <summary>Constructs a new FileHeader</summary>
 
147
                /// <param name="headerLines">buffer holding the diff header for this file</param>
 
148
                /// <param name="edits">the edits for this file</param>
 
149
                /// <param name="type">the type of patch used to modify this file</param>
 
150
                public FileHeader(byte[] headerLines, EditList edits, FileHeader.PatchType type) : 
 
151
                        this(headerLines, 0)
 
152
                {
 
153
                        endOffset = headerLines.Length;
 
154
                        int ptr = ParseGitFileName(NGit.Patch.Patch.DIFF_GIT.Length, headerLines.Length);
 
155
                        ParseGitHeaders(ptr, headerLines.Length);
 
156
                        this.patchType = type;
 
157
                        AddHunk(new HunkHeader(this, edits));
 
158
                }
 
159
 
 
160
                internal FileHeader(byte[] b, int offset)
 
161
                {
 
162
                        buf = b;
 
163
                        startOffset = offset;
 
164
                        changeType = DiffEntry.ChangeType.MODIFY;
 
165
                        // unless otherwise designated
 
166
                        patchType = FileHeader.PatchType.UNIFIED;
 
167
                }
 
168
 
 
169
                internal virtual int GetParentCount()
 
170
                {
 
171
                        return 1;
 
172
                }
 
173
 
 
174
                /// <returns>the byte array holding this file's patch script.</returns>
 
175
                public virtual byte[] GetBuffer()
 
176
                {
 
177
                        return buf;
 
178
                }
 
179
 
 
180
                /// <returns>
 
181
                /// offset the start of this file's script in
 
182
                /// <see cref="GetBuffer()">GetBuffer()</see>
 
183
                /// .
 
184
                /// </returns>
 
185
                public virtual int GetStartOffset()
 
186
                {
 
187
                        return startOffset;
 
188
                }
 
189
 
 
190
                /// <returns>offset one past the end of the file script.</returns>
 
191
                public virtual int GetEndOffset()
 
192
                {
 
193
                        return endOffset;
 
194
                }
 
195
 
 
196
                /// <summary>Convert the patch script for this file into a string.</summary>
 
197
                /// <remarks>
 
198
                /// Convert the patch script for this file into a string.
 
199
                /// <p>
 
200
                /// The default character encoding (
 
201
                /// <see cref="NGit.Constants.CHARSET">NGit.Constants.CHARSET</see>
 
202
                /// ) is assumed for
 
203
                /// both the old and new files.
 
204
                /// </remarks>
 
205
                /// <returns>the patch script, as a Unicode string.</returns>
 
206
                public virtual string GetScriptText()
 
207
                {
 
208
                        return GetScriptText(null, null);
 
209
                }
 
210
 
 
211
                /// <summary>Convert the patch script for this file into a string.</summary>
 
212
                /// <remarks>Convert the patch script for this file into a string.</remarks>
 
213
                /// <param name="oldCharset">hint character set to decode the old lines with.</param>
 
214
                /// <param name="newCharset">hint character set to decode the new lines with.</param>
 
215
                /// <returns>the patch script, as a Unicode string.</returns>
 
216
                public virtual string GetScriptText(Encoding oldCharset, Encoding newCharset)
 
217
                {
 
218
                        return GetScriptText(new Encoding[] { oldCharset, newCharset });
 
219
                }
 
220
 
 
221
                internal virtual string GetScriptText(Encoding[] charsetGuess)
 
222
                {
 
223
                        if (GetHunks().IsEmpty())
 
224
                        {
 
225
                                // If we have no hunks then we can safely assume the entire
 
226
                                // patch is a binary style patch, or a meta-data only style
 
227
                                // patch. Either way the encoding of the headers should be
 
228
                                // strictly 7-bit US-ASCII and the body is either 7-bit ASCII
 
229
                                // (due to the base 85 encoding used for a BinaryHunk) or is
 
230
                                // arbitrary noise we have chosen to ignore and not understand
 
231
                                // (e.g. the message "Binary files ... differ").
 
232
                                //
 
233
                                return RawParseUtils.ExtractBinaryString(buf, startOffset, endOffset);
 
234
                        }
 
235
                        if (charsetGuess != null && charsetGuess.Length != GetParentCount() + 1)
 
236
                        {
 
237
                                throw new ArgumentException(MessageFormat.Format(JGitText.Get().expectedCharacterEncodingGuesses
 
238
                                        , (GetParentCount() + 1)));
 
239
                        }
 
240
                        if (TrySimpleConversion(charsetGuess))
 
241
                        {
 
242
                                Encoding cs = charsetGuess != null ? charsetGuess[0] : null;
 
243
                                if (cs == null)
 
244
                                {
 
245
                                        cs = Constants.CHARSET;
 
246
                                }
 
247
                                try
 
248
                                {
 
249
                                        return RawParseUtils.DecodeNoFallback(cs, buf, startOffset, endOffset);
 
250
                                }
 
251
                                catch (CharacterCodingException)
 
252
                                {
 
253
                                }
 
254
                        }
 
255
                        // Try the much slower, more-memory intensive version which
 
256
                        // can handle a character set conversion patch.
 
257
                        StringBuilder r = new StringBuilder(endOffset - startOffset);
 
258
                        // Always treat the headers as US-ASCII; Git file names are encoded
 
259
                        // in a C style escape if any character has the high-bit set.
 
260
                        //
 
261
                        int hdrEnd = GetHunks()[0].GetStartOffset();
 
262
                        for (int ptr = startOffset; ptr < hdrEnd; )
 
263
                        {
 
264
                                int eol = Math.Min(hdrEnd, RawParseUtils.NextLF(buf, ptr));
 
265
                                r.Append(RawParseUtils.ExtractBinaryString(buf, ptr, eol));
 
266
                                ptr = eol;
 
267
                        }
 
268
                        string[] files = ExtractFileLines(charsetGuess);
 
269
                        int[] offsets = new int[files.Length];
 
270
                        foreach (HunkHeader h in GetHunks())
 
271
                        {
 
272
                                h.ExtractFileLines(r, files, offsets);
 
273
                        }
 
274
                        return r.ToString();
 
275
                }
 
276
 
 
277
                private static bool TrySimpleConversion(Encoding[] charsetGuess)
 
278
                {
 
279
                        if (charsetGuess == null)
 
280
                        {
 
281
                                return true;
 
282
                        }
 
283
                        for (int i = 1; i < charsetGuess.Length; i++)
 
284
                        {
 
285
                                if (charsetGuess[i] != charsetGuess[0])
 
286
                                {
 
287
                                        return false;
 
288
                                }
 
289
                        }
 
290
                        return true;
 
291
                }
 
292
 
 
293
                private string[] ExtractFileLines(Encoding[] csGuess)
 
294
                {
 
295
                        TemporaryBuffer[] tmp = new TemporaryBuffer[GetParentCount() + 1];
 
296
                        try
 
297
                        {
 
298
                                for (int i = 0; i < tmp.Length; i++)
 
299
                                {
 
300
                                        tmp[i] = new TemporaryBuffer.LocalFile();
 
301
                                }
 
302
                                foreach (HunkHeader h in GetHunks())
 
303
                                {
 
304
                                        h.ExtractFileLines(tmp);
 
305
                                }
 
306
                                string[] r = new string[tmp.Length];
 
307
                                for (int i_1 = 0; i_1 < tmp.Length; i_1++)
 
308
                                {
 
309
                                        Encoding cs = csGuess != null ? csGuess[i_1] : null;
 
310
                                        if (cs == null)
 
311
                                        {
 
312
                                                cs = Constants.CHARSET;
 
313
                                        }
 
314
                                        r[i_1] = RawParseUtils.Decode(cs, tmp[i_1].ToByteArray());
 
315
                                }
 
316
                                return r;
 
317
                        }
 
318
                        catch (IOException ioe)
 
319
                        {
 
320
                                throw new RuntimeException(JGitText.Get().cannotConvertScriptToText, ioe);
 
321
                        }
 
322
                        finally
 
323
                        {
 
324
                                foreach (TemporaryBuffer b in tmp)
 
325
                                {
 
326
                                        if (b != null)
 
327
                                        {
 
328
                                                b.Destroy();
 
329
                                        }
 
330
                                }
 
331
                        }
 
332
                }
 
333
 
 
334
                /// <returns>style of patch used to modify this file</returns>
 
335
                public virtual FileHeader.PatchType GetPatchType()
 
336
                {
 
337
                        return patchType;
 
338
                }
 
339
 
 
340
                /// <returns>true if this patch modifies metadata about a file</returns>
 
341
                public virtual bool HasMetaDataChanges()
 
342
                {
 
343
                        return changeType != DiffEntry.ChangeType.MODIFY || newMode != oldMode;
 
344
                }
 
345
 
 
346
                /// <returns>hunks altering this file; in order of appearance in patch</returns>
 
347
                public virtual IList<HunkHeader> GetHunks()
 
348
                {
 
349
                        if (hunks == null)
 
350
                        {
 
351
                                return Sharpen.Collections.EmptyList<HunkHeader>();
 
352
                        }
 
353
                        return hunks;
 
354
                }
 
355
 
 
356
                internal virtual void AddHunk(HunkHeader h)
 
357
                {
 
358
                        if (h.GetFileHeader() != this)
 
359
                        {
 
360
                                throw new ArgumentException(JGitText.Get().hunkBelongsToAnotherFile);
 
361
                        }
 
362
                        if (hunks == null)
 
363
                        {
 
364
                                hunks = new AList<HunkHeader>();
 
365
                        }
 
366
                        hunks.AddItem(h);
 
367
                }
 
368
 
 
369
                internal virtual HunkHeader NewHunkHeader(int offset)
 
370
                {
 
371
                        return new HunkHeader(this, offset);
 
372
                }
 
373
 
 
374
                /// <returns>
 
375
                /// if a
 
376
                /// <see cref="PatchType.GIT_BINARY">PatchType.GIT_BINARY</see>
 
377
                /// , the new-image delta/literal
 
378
                /// </returns>
 
379
                public virtual BinaryHunk GetForwardBinaryHunk()
 
380
                {
 
381
                        return forwardBinaryHunk;
 
382
                }
 
383
 
 
384
                /// <returns>
 
385
                /// if a
 
386
                /// <see cref="PatchType.GIT_BINARY">PatchType.GIT_BINARY</see>
 
387
                /// , the old-image delta/literal
 
388
                /// </returns>
 
389
                public virtual BinaryHunk GetReverseBinaryHunk()
 
390
                {
 
391
                        return reverseBinaryHunk;
 
392
                }
 
393
 
 
394
                /// <returns>a list describing the content edits performed on this file.</returns>
 
395
                public virtual EditList ToEditList()
 
396
                {
 
397
                        EditList r = new EditList();
 
398
                        foreach (HunkHeader hunk in hunks)
 
399
                        {
 
400
                                Sharpen.Collections.AddAll(r, hunk.ToEditList());
 
401
                        }
 
402
                        return r;
 
403
                }
 
404
 
 
405
                /// <summary>Parse a "diff --git" or "diff --cc" line.</summary>
 
406
                /// <remarks>Parse a "diff --git" or "diff --cc" line.</remarks>
 
407
                /// <param name="ptr">first character after the "diff --git " or "diff --cc " part.</param>
 
408
                /// <param name="end">one past the last position to parse.</param>
 
409
                /// <returns>first character after the LF at the end of the line; -1 on error.</returns>
 
410
                internal virtual int ParseGitFileName(int ptr, int end)
 
411
                {
 
412
                        int eol = RawParseUtils.NextLF(buf, ptr);
 
413
                        int bol = ptr;
 
414
                        if (eol >= end)
 
415
                        {
 
416
                                return -1;
 
417
                        }
 
418
                        // buffer[ptr..eol] looks like "a/foo b/foo\n". After the first
 
419
                        // A regex to match this is "^[^/]+/(.*?) [^/+]+/\1\n$". There
 
420
                        // is only one way to split the line such that text to the left
 
421
                        // of the space matches the text to the right, excluding the part
 
422
                        // before the first slash.
 
423
                        //
 
424
                        int aStart = RawParseUtils.NextLF(buf, ptr, '/');
 
425
                        if (aStart >= eol)
 
426
                        {
 
427
                                return eol;
 
428
                        }
 
429
                        while (ptr < eol)
 
430
                        {
 
431
                                int sp = RawParseUtils.NextLF(buf, ptr, ' ');
 
432
                                if (sp >= eol)
 
433
                                {
 
434
                                        // We can't split the header, it isn't valid.
 
435
                                        // This may be OK if this is a rename patch.
 
436
                                        //
 
437
                                        return eol;
 
438
                                }
 
439
                                int bStart = RawParseUtils.NextLF(buf, sp, '/');
 
440
                                if (bStart >= eol)
 
441
                                {
 
442
                                        return eol;
 
443
                                }
 
444
                                // If buffer[aStart..sp - 1] = buffer[bStart..eol - 1]
 
445
                                // we have a valid split.
 
446
                                //
 
447
                                if (Eq(aStart, sp - 1, bStart, eol - 1))
 
448
                                {
 
449
                                        if (buf[bol] == '"')
 
450
                                        {
 
451
                                                // We're a double quoted name. The region better end
 
452
                                                // in a double quote too, and we need to decode the
 
453
                                                // characters before reading the name.
 
454
                                                //
 
455
                                                if (buf[sp - 2] != '"')
 
456
                                                {
 
457
                                                        return eol;
 
458
                                                }
 
459
                                                oldPath = QuotedString.GIT_PATH.Dequote(buf, bol, sp - 1);
 
460
                                                oldPath = P1(oldPath);
 
461
                                        }
 
462
                                        else
 
463
                                        {
 
464
                                                oldPath = RawParseUtils.Decode(Constants.CHARSET, buf, aStart, sp - 1);
 
465
                                        }
 
466
                                        newPath = oldPath;
 
467
                                        return eol;
 
468
                                }
 
469
                                // This split wasn't correct. Move past the space and try
 
470
                                // another split as the space must be part of the file name.
 
471
                                //
 
472
                                ptr = sp;
 
473
                        }
 
474
                        return eol;
 
475
                }
 
476
 
 
477
                internal virtual int ParseGitHeaders(int ptr, int end)
 
478
                {
 
479
                        while (ptr < end)
 
480
                        {
 
481
                                int eol = RawParseUtils.NextLF(buf, ptr);
 
482
                                if (IsHunkHdr(buf, ptr, eol) >= 1)
 
483
                                {
 
484
                                        // First hunk header; break out and parse them later.
 
485
                                        break;
 
486
                                }
 
487
                                else
 
488
                                {
 
489
                                        if (RawParseUtils.Match(buf, ptr, OLD_NAME) >= 0)
 
490
                                        {
 
491
                                                ParseOldName(ptr, eol);
 
492
                                        }
 
493
                                        else
 
494
                                        {
 
495
                                                if (RawParseUtils.Match(buf, ptr, NEW_NAME) >= 0)
 
496
                                                {
 
497
                                                        ParseNewName(ptr, eol);
 
498
                                                }
 
499
                                                else
 
500
                                                {
 
501
                                                        if (RawParseUtils.Match(buf, ptr, OLD_MODE) >= 0)
 
502
                                                        {
 
503
                                                                oldMode = ParseFileMode(ptr + OLD_MODE.Length, eol);
 
504
                                                        }
 
505
                                                        else
 
506
                                                        {
 
507
                                                                if (RawParseUtils.Match(buf, ptr, NEW_MODE) >= 0)
 
508
                                                                {
 
509
                                                                        newMode = ParseFileMode(ptr + NEW_MODE.Length, eol);
 
510
                                                                }
 
511
                                                                else
 
512
                                                                {
 
513
                                                                        if (RawParseUtils.Match(buf, ptr, DELETED_FILE_MODE) >= 0)
 
514
                                                                        {
 
515
                                                                                oldMode = ParseFileMode(ptr + DELETED_FILE_MODE.Length, eol);
 
516
                                                                                newMode = FileMode.MISSING;
 
517
                                                                                changeType = DiffEntry.ChangeType.DELETE;
 
518
                                                                        }
 
519
                                                                        else
 
520
                                                                        {
 
521
                                                                                if (RawParseUtils.Match(buf, ptr, NEW_FILE_MODE) >= 0)
 
522
                                                                                {
 
523
                                                                                        ParseNewFileMode(ptr, eol);
 
524
                                                                                }
 
525
                                                                                else
 
526
                                                                                {
 
527
                                                                                        if (RawParseUtils.Match(buf, ptr, COPY_FROM) >= 0)
 
528
                                                                                        {
 
529
                                                                                                oldPath = ParseName(oldPath, ptr + COPY_FROM.Length, eol);
 
530
                                                                                                changeType = DiffEntry.ChangeType.COPY;
 
531
                                                                                        }
 
532
                                                                                        else
 
533
                                                                                        {
 
534
                                                                                                if (RawParseUtils.Match(buf, ptr, COPY_TO) >= 0)
 
535
                                                                                                {
 
536
                                                                                                        newPath = ParseName(newPath, ptr + COPY_TO.Length, eol);
 
537
                                                                                                        changeType = DiffEntry.ChangeType.COPY;
 
538
                                                                                                }
 
539
                                                                                                else
 
540
                                                                                                {
 
541
                                                                                                        if (RawParseUtils.Match(buf, ptr, RENAME_OLD) >= 0)
 
542
                                                                                                        {
 
543
                                                                                                                oldPath = ParseName(oldPath, ptr + RENAME_OLD.Length, eol);
 
544
                                                                                                                changeType = DiffEntry.ChangeType.RENAME;
 
545
                                                                                                        }
 
546
                                                                                                        else
 
547
                                                                                                        {
 
548
                                                                                                                if (RawParseUtils.Match(buf, ptr, RENAME_NEW) >= 0)
 
549
                                                                                                                {
 
550
                                                                                                                        newPath = ParseName(newPath, ptr + RENAME_NEW.Length, eol);
 
551
                                                                                                                        changeType = DiffEntry.ChangeType.RENAME;
 
552
                                                                                                                }
 
553
                                                                                                                else
 
554
                                                                                                                {
 
555
                                                                                                                        if (RawParseUtils.Match(buf, ptr, RENAME_FROM) >= 0)
 
556
                                                                                                                        {
 
557
                                                                                                                                oldPath = ParseName(oldPath, ptr + RENAME_FROM.Length, eol);
 
558
                                                                                                                                changeType = DiffEntry.ChangeType.RENAME;
 
559
                                                                                                                        }
 
560
                                                                                                                        else
 
561
                                                                                                                        {
 
562
                                                                                                                                if (RawParseUtils.Match(buf, ptr, RENAME_TO) >= 0)
 
563
                                                                                                                                {
 
564
                                                                                                                                        newPath = ParseName(newPath, ptr + RENAME_TO.Length, eol);
 
565
                                                                                                                                        changeType = DiffEntry.ChangeType.RENAME;
 
566
                                                                                                                                }
 
567
                                                                                                                                else
 
568
                                                                                                                                {
 
569
                                                                                                                                        if (RawParseUtils.Match(buf, ptr, SIMILARITY_INDEX) >= 0)
 
570
                                                                                                                                        {
 
571
                                                                                                                                                score = RawParseUtils.ParseBase10(buf, ptr + SIMILARITY_INDEX.Length, null);
 
572
                                                                                                                                        }
 
573
                                                                                                                                        else
 
574
                                                                                                                                        {
 
575
                                                                                                                                                if (RawParseUtils.Match(buf, ptr, DISSIMILARITY_INDEX) >= 0)
 
576
                                                                                                                                                {
 
577
                                                                                                                                                        score = RawParseUtils.ParseBase10(buf, ptr + DISSIMILARITY_INDEX.Length, null);
 
578
                                                                                                                                                }
 
579
                                                                                                                                                else
 
580
                                                                                                                                                {
 
581
                                                                                                                                                        if (RawParseUtils.Match(buf, ptr, INDEX) >= 0)
 
582
                                                                                                                                                        {
 
583
                                                                                                                                                                ParseIndexLine(ptr + INDEX.Length, eol);
 
584
                                                                                                                                                        }
 
585
                                                                                                                                                        else
 
586
                                                                                                                                                        {
 
587
                                                                                                                                                                // Probably an empty patch (stat dirty).
 
588
                                                                                                                                                                break;
 
589
                                                                                                                                                        }
 
590
                                                                                                                                                }
 
591
                                                                                                                                        }
 
592
                                                                                                                                }
 
593
                                                                                                                        }
 
594
                                                                                                                }
 
595
                                                                                                        }
 
596
                                                                                                }
 
597
                                                                                        }
 
598
                                                                                }
 
599
                                                                        }
 
600
                                                                }
 
601
                                                        }
 
602
                                                }
 
603
                                        }
 
604
                                }
 
605
                                ptr = eol;
 
606
                        }
 
607
                        return ptr;
 
608
                }
 
609
 
 
610
                internal virtual void ParseOldName(int ptr, int eol)
 
611
                {
 
612
                        oldPath = P1(ParseName(oldPath, ptr + OLD_NAME.Length, eol));
 
613
                        if (oldPath == DEV_NULL)
 
614
                        {
 
615
                                changeType = DiffEntry.ChangeType.ADD;
 
616
                        }
 
617
                }
 
618
 
 
619
                internal virtual void ParseNewName(int ptr, int eol)
 
620
                {
 
621
                        newPath = P1(ParseName(newPath, ptr + NEW_NAME.Length, eol));
 
622
                        if (newPath == DEV_NULL)
 
623
                        {
 
624
                                changeType = DiffEntry.ChangeType.DELETE;
 
625
                        }
 
626
                }
 
627
 
 
628
                internal virtual void ParseNewFileMode(int ptr, int eol)
 
629
                {
 
630
                        oldMode = FileMode.MISSING;
 
631
                        newMode = ParseFileMode(ptr + NEW_FILE_MODE.Length, eol);
 
632
                        changeType = DiffEntry.ChangeType.ADD;
 
633
                }
 
634
 
 
635
                internal virtual int ParseTraditionalHeaders(int ptr, int end)
 
636
                {
 
637
                        while (ptr < end)
 
638
                        {
 
639
                                int eol = RawParseUtils.NextLF(buf, ptr);
 
640
                                if (IsHunkHdr(buf, ptr, eol) >= 1)
 
641
                                {
 
642
                                        // First hunk header; break out and parse them later.
 
643
                                        break;
 
644
                                }
 
645
                                else
 
646
                                {
 
647
                                        if (RawParseUtils.Match(buf, ptr, OLD_NAME) >= 0)
 
648
                                        {
 
649
                                                ParseOldName(ptr, eol);
 
650
                                        }
 
651
                                        else
 
652
                                        {
 
653
                                                if (RawParseUtils.Match(buf, ptr, NEW_NAME) >= 0)
 
654
                                                {
 
655
                                                        ParseNewName(ptr, eol);
 
656
                                                }
 
657
                                                else
 
658
                                                {
 
659
                                                        // Possibly an empty patch.
 
660
                                                        break;
 
661
                                                }
 
662
                                        }
 
663
                                }
 
664
                                ptr = eol;
 
665
                        }
 
666
                        return ptr;
 
667
                }
 
668
 
 
669
                private string ParseName(string expect, int ptr, int end)
 
670
                {
 
671
                        if (ptr == end)
 
672
                        {
 
673
                                return expect;
 
674
                        }
 
675
                        string r;
 
676
                        if (buf[ptr] == '"')
 
677
                        {
 
678
                                // New style GNU diff format
 
679
                                //
 
680
                                r = QuotedString.GIT_PATH.Dequote(buf, ptr, end - 1);
 
681
                        }
 
682
                        else
 
683
                        {
 
684
                                // Older style GNU diff format, an optional tab ends the name.
 
685
                                //
 
686
                                int tab = end;
 
687
                                while (ptr < tab && buf[tab - 1] != '\t')
 
688
                                {
 
689
                                        tab--;
 
690
                                }
 
691
                                if (ptr == tab)
 
692
                                {
 
693
                                        tab = end;
 
694
                                }
 
695
                                r = RawParseUtils.Decode(Constants.CHARSET, buf, ptr, tab - 1);
 
696
                        }
 
697
                        if (r.Equals(DEV_NULL))
 
698
                        {
 
699
                                r = DEV_NULL;
 
700
                        }
 
701
                        return r;
 
702
                }
 
703
 
 
704
                private static string P1(string r)
 
705
                {
 
706
                        int s = r.IndexOf('/');
 
707
                        return s > 0 ? Sharpen.Runtime.Substring(r, s + 1) : r;
 
708
                }
 
709
 
 
710
                internal virtual FileMode ParseFileMode(int ptr, int end)
 
711
                {
 
712
                        int tmp = 0;
 
713
                        while (ptr < end - 1)
 
714
                        {
 
715
                                tmp <<= 3;
 
716
                                tmp += buf[ptr++] - '0';
 
717
                        }
 
718
                        return FileMode.FromBits(tmp);
 
719
                }
 
720
 
 
721
                internal virtual void ParseIndexLine(int ptr, int end)
 
722
                {
 
723
                        // "index $asha1..$bsha1[ $mode]" where $asha1 and $bsha1
 
724
                        // can be unique abbreviations
 
725
                        //
 
726
                        int dot2 = RawParseUtils.NextLF(buf, ptr, '.');
 
727
                        int mode = RawParseUtils.NextLF(buf, dot2, ' ');
 
728
                        oldId = AbbreviatedObjectId.FromString(buf, ptr, dot2 - 1);
 
729
                        newId = AbbreviatedObjectId.FromString(buf, dot2 + 1, mode - 1);
 
730
                        if (mode < end)
 
731
                        {
 
732
                                newMode = oldMode = ParseFileMode(mode, end);
 
733
                        }
 
734
                }
 
735
 
 
736
                private bool Eq(int aPtr, int aEnd, int bPtr, int bEnd)
 
737
                {
 
738
                        if (aEnd - aPtr != bEnd - bPtr)
 
739
                        {
 
740
                                return false;
 
741
                        }
 
742
                        while (aPtr < aEnd)
 
743
                        {
 
744
                                if (buf[aPtr++] != buf[bPtr++])
 
745
                                {
 
746
                                        return false;
 
747
                                }
 
748
                        }
 
749
                        return true;
 
750
                }
 
751
 
 
752
                /// <summary>Determine if this is a patch hunk header.</summary>
 
753
                /// <remarks>Determine if this is a patch hunk header.</remarks>
 
754
                /// <param name="buf">the buffer to scan</param>
 
755
                /// <param name="start">first position in the buffer to evaluate</param>
 
756
                /// <param name="end">
 
757
                /// last position to consider; usually the end of the buffer (
 
758
                /// <code>buf.length</code>) or the first position on the next
 
759
                /// line. This is only used to avoid very long runs of '@' from
 
760
                /// killing the scan loop.
 
761
                /// </param>
 
762
                /// <returns>
 
763
                /// the number of "ancestor revisions" in the hunk header. A
 
764
                /// traditional two-way diff ("@@ -...") returns 1; a combined diff
 
765
                /// for a 3 way-merge returns 3. If this is not a hunk header, 0 is
 
766
                /// returned instead.
 
767
                /// </returns>
 
768
                internal static int IsHunkHdr(byte[] buf, int start, int end)
 
769
                {
 
770
                        int ptr = start;
 
771
                        while (ptr < end && buf[ptr] == '@')
 
772
                        {
 
773
                                ptr++;
 
774
                        }
 
775
                        if (ptr - start < 2)
 
776
                        {
 
777
                                return 0;
 
778
                        }
 
779
                        if (ptr == end || buf[ptr++] != ' ')
 
780
                        {
 
781
                                return 0;
 
782
                        }
 
783
                        if (ptr == end || buf[ptr++] != '-')
 
784
                        {
 
785
                                return 0;
 
786
                        }
 
787
                        return (ptr - 3) - start;
 
788
                }
 
789
        }
 
790
}