Browse Source

updating to make it smarter and fancier, parses the status file to check for duplicate packages, needs to update the status file accordingly

Kevin Bradley 5 years ago
parent
commit
876c6d5648

BIN
bootstrapTool.xcodeproj/project.xcworkspace/xcuserdata/kevinbradley.xcuserdatad/UserInterfaceState.xcuserstate


+ 325 - 4
bootstrapTool/main.m

@@ -34,7 +34,7 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
 
 
 #define DLog(format, ...) CFShow((__bridge CFStringRef)[NSString stringWithFormat:format, ## __VA_ARGS__]);
-
+#define FM [NSFileManager defaultManager]
 /*
  
  fileType, @"fileType",octalPermissions, @"octalPermissions", octalUG, @"octalUG", size, @"size", date, @"date", time, @"time", linkDest, @"linkDest", fullPath, @"fullPath",  nil];
@@ -85,6 +85,233 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
 @end
 
 
+@interface DebPackageModel : NSObject
+
+@property (nonatomic, copy) NSString *name;
+@property (nonatomic, copy) NSString *package;
+@property (nonatomic, copy) NSString *source;
+@property (nonatomic, copy) NSString *version;
+@property (nonatomic, copy) NSString *priority;
+@property (nonatomic, copy) NSString *essential;
+@property (nonatomic, copy) NSArray *depends;
+@property (nonatomic, copy) NSString *maintainer;
+@property (nonatomic, copy) NSString *packageDescription; //cant be description, that is reservered
+@property (nonatomic, copy) NSString *homepage;
+@property (nonatomic, copy) NSString *author;
+@property (nonatomic, copy) NSString *icon;
+@property (nonatomic, copy) NSString *depiction;
+@property (nonatomic, copy) NSString *preDepends;
+@property (nonatomic, copy) NSString *breaks;
+@property (nonatomic, copy) NSString *status;
+@property (nonatomic, copy) NSString *tag;
+@property (nonatomic, copy) NSString *architecture;
+@property (nonatomic, copy) NSString *section;
+
+
+
+- (instancetype)initWithRawControlString:(NSString *)controlString;
+
+
+@end
+@implementation DebPackageModel
+
+- (NSString *)description {
+    
+    NSString *orig = [super description];
+    return [NSString stringWithFormat:@"%@ = %@ (%@)", orig, self.package, self.version];
+}
+
+- (NSString*) fullDescription {
+    
+    NSString *orig = [super description];
+    NSMutableDictionary *details = [NSMutableDictionary new];
+    NSArray *props = [self properties];
+    [props enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+        
+        NSString *cv = [self valueForKey:obj];
+        if (cv){
+            details[obj] = cv;
+        }
+        
+    }];
+    return [NSString stringWithFormat:@"%@ = %@", orig, details];
+    
+}
+
++ (NSDictionary *)dependencyDictionaryFromString:(NSString *)depend
+{
+    NSMutableCharacterSet *whitespaceAndPunctuationSet = [NSMutableCharacterSet characterSetWithCharactersInString:@"()"];
+    [whitespaceAndPunctuationSet formUnionWithCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+    
+    NSScanner *stringScanner = [[NSScanner alloc] initWithString:depend];
+    stringScanner.charactersToBeSkipped = whitespaceAndPunctuationSet;
+    
+    NSString *name = nil;
+    NSInteger i = 0;
+    NSMutableDictionary *predicate = [NSMutableDictionary new];
+    while ([stringScanner scanUpToCharactersFromSet:whitespaceAndPunctuationSet intoString:&name]) {
+        // NSLog(@"%@ pass %li", name, (long)i);
+        switch (i) {
+            case 0 :
+                predicate[@"package"] = name;
+                break;
+            case 1:
+                predicate[@"predicate"] = name;
+                break;
+            case 2:
+                predicate[@"requirement"] = name;
+                break;
+            default:
+                break;
+        }
+        i++;
+    }
+    return predicate;
+}
+
+
++ (NSArray *)dependencyArrayFromString:(NSString *)depends
+{
+    NSMutableArray *cleanArray = [[NSMutableArray alloc] init];
+    NSArray *dependsArray = [depends componentsSeparatedByString:@","];
+    for (id depend in dependsArray)
+    {
+        NSArray *spaceDelimitedArray = [depend componentsSeparatedByString:@" "];
+        NSString *isolatedDependency = [[spaceDelimitedArray objectAtIndex:0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+        if ([isolatedDependency length] == 0)
+            isolatedDependency = [[spaceDelimitedArray objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+        
+        NSDictionary *dependDict = [self dependencyDictionaryFromString:depend];
+        //DLog(@"depend dict: %@", dependDict);
+        [cleanArray addObject:dependDict];
+    }
+    
+    return cleanArray;
+}
+
++ (NSDictionary *)JSONKeyPathsByPropertyKey {
+    return @{
+             @"name": @"Name",
+             @"package": @"Package",
+             @"source": @"Source",
+             @"version": @"Version",
+             @"priority": @"Priority",
+             @"essential": @"Essential",
+             @"depends": @"Depends",
+             @"maintainer": @"Maintainer",
+             @"packageDescription": @"Description",
+             @"homepage": @"Homepage",
+             @"icon": @"Icon",
+             @"author": @"Author",
+             @"preDepends": @"Pre-Depends",
+             @"breaks": @"Breaks",
+             @"depiction": @"Depiction",
+             @"tag": @"Tag",
+             @"architecture": @"Architecture",
+             @"section": @"Section",
+             @"osMax": @"osMax",
+             @"osMin": @"osMin",
+             @"banner": @"Banner",
+             @"topShelfImage": @"TopShelfImage"
+             };
+}
+
+- (instancetype)initWithRawControlString:(NSString *)controlString
+{
+    NSArray *packageArray = [controlString componentsSeparatedByString:@"\n"];
+    NSMutableDictionary *currentPackage = [[NSMutableDictionary alloc] init];
+    for (id currentLine in packageArray)
+    {
+        NSArray *itemArray = [currentLine componentsSeparatedByString:@": "];
+        if ([itemArray count] >= 2)
+        {
+            NSString *key = [itemArray objectAtIndex:0];
+            NSString *object = [itemArray objectAtIndex:1];
+            
+            if ([key isEqualToString:@"Depends"]) //process the array
+            {
+                NSArray *dependsObject = [DebPackageModel dependencyArrayFromString:object];
+                
+                [currentPackage setObject:dependsObject forKey:key];
+                
+            } else { //every other key, even if it has an array is treated as a string
+                
+                [currentPackage setObject:object forKey:key];
+            }
+        }
+    }
+
+    if ([[currentPackage allKeys] count] > 4)
+    {
+        self = [super init];
+        [self mapDictionaryToProperties:currentPackage];
+        return self;
+    }
+    return nil;
+    
+    
+}
+
+- (void)mapDictionaryToProperties:(NSDictionary *)theProps {
+    
+    NSArray *ourProps = [self properties];
+    NSArray *allKeys = [theProps allKeys];
+    NSDictionary *mappedKeys = [DebPackageModel JSONKeyPathsByPropertyKey];
+    [ourProps enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+       
+        NSString *mappedProp = mappedKeys[obj];
+        
+        //DLog(@"allkeys: %@", allKeys);
+        
+        if ([allKeys containsObject:mappedProp]){
+            if ([self respondsToSelector:NSSelectorFromString(obj)]){ //redudant
+                
+                id value = theProps[mappedProp];
+                //DLog(@"setting value: %@ for key: %@ from mapped key: %@", value, obj, mappedProp);
+                [self setValue:value forKey:obj];
+                
+            }
+        } else {
+            
+            //DLog(@"%@ doesnt respond to %@", self, mappedProp);
+            
+        }
+       
+        
+    }];
+    
+}
+
+
+@end
+
+@interface NSString (Additions)
+
+- (void)writeToFileWithoutAttributes:(NSString *)theFile;
+
+
+@end
+
+@implementation NSString (Additions)
+
+- (void)writeToFileWithoutAttributes:(NSString *)theFile {
+    
+    if ([FM fileExistsAtPath:theFile]){
+        
+        DLog(@"overwriting file: %@", theFile);
+    }
+        FILE *fd = fopen([theFile UTF8String], "w+");
+        const char *text = self.UTF8String;
+        fwrite(text, strlen(text) + 1, 1, fd);
+        fclose(fd);
+    
+}
+
+@end
+
+
+
+
 
 @interface PackageFile: NSObject
 
@@ -205,6 +432,7 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
 @property (nonatomic, strong) NSArray <PackageFile *> *files;
 @property (nonatomic, strong) NSArray  *controlFiles;
 @property (nonatomic, strong) NSString *packageName;
+@property (nonatomic, strong) NSString *version;
 
 - (ErrorReturn *)errorReturnForBootstrap:(NSString *)bootstrapPath;
 - (NSString *)listfile;
@@ -304,7 +532,37 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
 
 @implementation HelperClass
 
-
++ (NSArray <DebPackageModel*>*)statusInstalledPackagesFromFile:(NSString *)statusFile
+{
+    
+    if (![FM fileExistsAtPath:statusFile]) {
+        return nil;
+    }
+    NSString *packageString = [NSString stringWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];
+    NSArray *lineArray = [packageString componentsSeparatedByString:@"\n\n"];
+    //DDLogInfo(@"lineArray: %@", lineArray);
+    NSMutableArray *mutableList = [[NSMutableArray alloc] init];
+    //NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc] init];
+    for (id currentItem in lineArray)
+    {
+        
+        DebPackageModel *debModel = [[DebPackageModel alloc] initWithRawControlString:currentItem];
+        if (debModel != nil)
+            [mutableList addObject:debModel];
+    }
+    
+    NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES
+                                                                    selector:@selector(localizedCaseInsensitiveCompare:)];
+    NSSortDescriptor *packageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"package" ascending:YES
+                                                                       selector:@selector(localizedCaseInsensitiveCompare:)];
+    NSArray *descriptors = [NSArray arrayWithObjects:nameDescriptor, packageDescriptor, nil];
+    NSArray *sortedArray = [mutableList sortedArrayUsingDescriptors:descriptors];
+    
+    
+    mutableList = nil;
+    
+    return sortedArray;
+}
 
 + (BOOL)shouldContinueWithError:(NSString *)errorMessage {
     
@@ -513,9 +771,14 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
     
 }
 
+
+
+
+
 + (Package *)packageForDeb:(NSString *)debFile {
     
     NSString *packageName = [[self returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -f %@ Package", debFile]] componentsJoinedByString:@"\n"];
+    NSString *packageVersion = [[self returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -f %@ Version", debFile]] componentsJoinedByString:@"\n"];
     NSArray <PackageFile *> *fileList = [self returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -c %@", debFile]];
     
     __block NSMutableArray *finalArray = [NSMutableArray new];
@@ -533,6 +796,7 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
     Package *pkg = [Package new];
     pkg.files = finalArray;
     pkg.packageName = packageName;
+    pkg.version = packageVersion;
     return pkg;
     
 }
@@ -687,10 +951,47 @@ int main(int argc, const char * argv[]) {
         }
         
         if (debFile && bootstrapPath) {
+        
+            NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
+            NSArray *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
+            
+            //DLog(@"installedPackages: %@", installedPackages);
             
             DLog(@"\n\nProcessing file: %@\n", debFile);
             Package *output = [HelperClass packageForDeb:debFile];
             
+            DLog(@"\nFound package: '%@' at version: '%@'...\n", output.packageName, output.version );
+            
+            
+            
+            DebPackageModel *model = [[installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", output.packageName]] lastObject];
+            
+            if (model) {
+                
+                DLog(@"\n [WARNING] This package has already been installed! %@", model);
+                NSComparisonResult result = [output.version compare:model.version options:NSNumericSearch];
+                DLog(@"\n [WARNING] Comparing version numbers...");
+                switch (result) {
+                    case NSOrderedSame:
+                        DLog(@"\n [WARNING] %@ and %@ match!", output.version, model.version);
+                        break;
+                        
+                    case NSOrderedAscending:
+                        DLog(@"\n [WARNING] Package version %@ is less than installed version %@!", output.version, model.version);
+                        break;
+                        
+                    case NSOrderedDescending:
+                        DLog(@"\n [WARNING] Package version %@ is greater than installed version %@!", output.version, model.version);
+                        break;
+                        
+                    default:
+                        break;
+                }
+                
+                
+                
+            }
+            
             ErrorReturn *safePackage = [output errorReturnForBootstrap:bootstrapPath];
             
             if (safePackage.returnStatus != 0) { //zero is success
@@ -718,17 +1019,35 @@ int main(int argc, const char * argv[]) {
             //DLog(@"%@", output);
             //DLog(@"list: %@", output.listfile);
             NSFileManager *man = [NSFileManager defaultManager];
+            
+         
+            
+            
             NSString *tmpPath = [pwd stringByAppendingPathComponent:output.packageName];
             NSString *debian = [tmpPath stringByAppendingPathComponent:@"DEBIAN"];
             [man createDirectoryAtPath:tmpPath withIntermediateDirectories:TRUE attributes:nil error:nil];
+            DLog(@"\nExtracting deb for processing...\n");
             [HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -x %@ %@", debFile, tmpPath]];
-            NSString *listFile = [pwd stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.list", output.packageName]];
-            NSString *md5s = [pwd stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.md5sums", output.packageName]];
+            
+            NSString *bootstrapInfoPath = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/info"];
+            
+            NSString *listFile = [bootstrapInfoPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.list", output.packageName]];
+            NSString *md5s = [bootstrapInfoPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.md5sums", output.packageName]];
+            
+            DLog(@"\nCreating list file '%@'...\n", listFile);
+            
             [output.listfile writeToFile:listFile atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
+            
+            DLog(@"\nGenerating md5sums...\n");
             NSString *runString = [NSString stringWithFormat:@"/usr/bin/find %@ -type f -not -path \"*.DS_Store*\" -exec /sbin/md5 -r {} \\; |  /usr/bin/awk '{ print $1 \"  \" $2 }' | /usr/bin/sed \"s|%@||g\"", tmpPath, tmpPath];
             NSString *outputs = [[HelperClass returnForProcess:runString] componentsJoinedByString:@"\n"];
+            
+            DLog(@"\nCreating md5sum file '%@'...\n", listFile);
+            
             [outputs writeToFile:md5s atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
+            
             [man createDirectoryAtPath:debian withIntermediateDirectories:TRUE attributes:nil error:nil];
+            
             [HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -e %@ %@", debFile, debian]];
             //NSString *nextPath = [tmpPath stringByAppendingPathComponent:@"DEBIAN"];
             
@@ -750,8 +1069,10 @@ int main(int argc, const char * argv[]) {
                     
                     
                     [controlFile replaceOccurrencesOfString:@"iphoneos-arm" withString:@"appletvos-arm64" options:NSLiteralSearch range:NSMakeRange(0, [controlFile length])];
+                    
                     DLog(@"control file: %@", controlFile);
                     
+                    
                     NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
                     
                     NSMutableString *statusContents = [[NSMutableString alloc] initWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];