This patch prevents the plaintext display of files with binary content
in tree-view by connecting the content to the textContents attribute.

PBGitTree is extended with the method textContents, which returns the
textual representation of a PBGitTree-object. The methods first checks
the output of "git check-attr binary <file>" to see if the user
set/unset the binary attribute manually. Then it checks for common
binary file-extensions. If this method can't determine whether the file
is binary, the file-content is loaded and Unix "file" is run on the
first 100 bytes of the file to make a decision.

Signed-off-by: Johannes Gilger <[email protected]>
---
Hey Pieter,

I implemented your advice. My textContents doesn't return nil though, since I 
think people could mistake that for a file with no content (i.e. only 
whitespaces). I think the view is something we're going to replace anyway, 
since it is very limited in its capacity to display stuff.

What fell out of the patch is the checking for file-size, but this can easily 
be added again later on. I edited the XIB by hand since my IB generated a huge 
diff again. It seems to work fine this way.

I'm also pushing this to jg/showBinary on github. 

I think for 0.7 I still need to rewrite the 02 of the UserManual since it 
includes a passage about auto-refresh.

 PBGitHistoryView.xib |    6 ++--
 PBGitTree.h          |    1 +
 PBGitTree.m          |   66 ++++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 65 insertions(+), 8 deletions(-)

diff --git a/PBGitHistoryView.xib b/PBGitHistoryView.xib
index 10b884e..71d7413 100644
--- a/PBGitHistoryView.xib
+++ b/PBGitHistoryView.xib
@@ -2049,15 +2049,15 @@
                                </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBBindingConnection" 
key="connection">
-                                               <string key="label">value: 
selection.contents</string>
+                                               <string key="label">value: 
selection.textContents</string>
                                                <reference key="source" 
ref="75600241"/>
                                                <reference key="destination" 
ref="69733037"/>
                                                <object 
class="NSNibBindingConnector" key="connector">
                                                        <reference 
key="NSSource" ref="75600241"/>
                                                        <reference 
key="NSDestination" ref="69733037"/>
-                                                       <string 
key="NSLabel">value: selection.contents</string>
+                                                       <string 
key="NSLabel">value: selection.textContents</string>
                                                        <string 
key="NSBinding">value</string>
-                                                       <string 
key="NSKeyPath">selection.contents</string>
+                                                       <string 
key="NSKeyPath">selection.textContents</string>
                                                        <object 
class="NSDictionary" key="NSOptions">
                                                                <bool 
key="EncodedWithXMLCoder">YES</bool>
                                                                <object 
class="NSMutableArray" key="dict.sortedKeys">
diff --git a/PBGitTree.h b/PBGitTree.h
index 9f4334b..612f24b 100644
--- a/PBGitTree.h
+++ b/PBGitTree.h
@@ -36,5 +36,6 @@
 @property(readonly) NSArray* children;
 @property(readonly) NSString* fullPath;
 @property(readonly) NSString* contents;
+...@property(readonly) NSString* textContents;
 
 @end
diff --git a/PBGitTree.m b/PBGitTree.m
index 51fadd2..e49e528 100644
--- a/PBGitTree.m
+++ b/PBGitTree.m
@@ -63,27 +63,83 @@ - (BOOL) isLocallyCached
        return NO;
 }
 
+- (BOOL)hasBinaryHeader:(NSString *)fileHeader
+{
+       if(!fileHeader)
+               return NO;
+
+       NSString* filetype = [PBEasyPipe outputForCommand:@"/usr/bin/file" 
withArgs:[NSArray arrayWithObjects:@"-b", @"-N", @"-", nil] inDir:[repository 
workingDirectory] inputString:fileHeader retValue:nil];
+               if([filetype rangeOfString:@"text"].location == NSNotFound)
+                       return YES;
+               else
+                       return NO;
+}
+
+- (BOOL)hasBinaryAttributes
+{
+       NSFileHandle* handle = [repository handleInWorkDirForArguments:[NSArray 
arrayWithObjects:@"check-attr", @"binary", [self fullPath], nil]];
+       NSData* data = [handle readDataToEndOfFile];
+       NSString* string = [[NSString alloc] initWithData:data 
encoding:NSISOLatin1StringEncoding];
+
+       if(string) {
+               string = [string 
stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
+               if([string hasSuffix:@"binary: set"]) {
+                       return YES;
+               } else if([string hasSuffix:@"binary: unset"]) {
+                       return NO;
+               } else if([string hasSuffix:@"binary: unspecified"]) {
+                       // try common filename-extensions
+                       for (NSString* extension in [NSArray 
arrayWithObjects:@".pdf", @".jpg", @".jpeg", @".png", @".bmp", @".gif", @".o", 
nil]) {
+                               if([[self fullPath] hasSuffix:extension])
+                                       return YES;
+                       }
+               }
+       }
+
+       return NO;
+}
+
 - (NSString*) contents
 {
        if (!leaf)
-               return [NSString stringWithFormat:@"This is a tree with path 
%@", [self fullPath]];
+               return nil;
 
        NSData* data = nil;
-       
+
        if ([self isLocallyCached])
                data = [NSData dataWithContentsOfFile: localFileName];
        else {
                NSFileHandle* handle = [repository handleForArguments:[NSArray 
arrayWithObjects:@"show", [self refSpec], nil]];
                data = [handle readDataToEndOfFile];
        }
-       
+
        NSString* string = [[NSString alloc] initWithData:data 
encoding:NSUTF8StringEncoding];
-       if (!string) {
+       if (!string)
                string = [[NSString alloc] initWithData:data 
encoding:NSISOLatin1StringEncoding];
-       }
+
        return string;
 }
 
+- (NSString*)textContents
+{
+       if (!leaf)
+               return [NSString stringWithFormat:@"This is a tree with path 
%@", [self fullPath]];
+
+       NSFileHandle* handle = [repository handleForArguments:[NSArray 
arrayWithObjects:@"cat-file", @"-s", [self refSpec], nil]];
+       NSString* fileSize = [[NSString alloc] initWithData:[handle 
readDataToEndOfFile] encoding:NSISOLatin1StringEncoding];
+       [handle closeFile];
+
+       if([self hasBinaryAttributes])
+               return [NSString stringWithFormat:@"%@ appears to be a binary 
file of %d bytes", [self fullPath], [fileSize longLongValue]];
+
+       NSString* contents = [self contents];
+
+       if([self hasBinaryHeader:[contents length] >= 100 ? [contents 
substringToIndex:99] : contents])
+               return [NSString stringWithFormat:@"%@ appears to be a binary 
file of %d bytes", [self fullPath], [fileSize longLongValue]];
+
+       return contents;
+}
+
 - (void) saveToFolder: (NSString *) dir
 {
        NSString* newName = [dir stringByAppendingPathComponent:path];
-- 
1.6.4.2.236.gf324c

Reply via email to