cpython/Mac/PythonLauncher/FileSettings.m

//
//  FileSettings.m
//  PythonLauncher
//
//  Created by Jack Jansen on Sun Jul 21 2002.
//  Copyright (c) 2002 __MyCompanyName__. All rights reserved.
//

#import "FileSettings.h"

@implementation FileSettings

+ (id)getFactorySettingsForFileType: (NSString *)filetype
{
    static FileSettings *fsdefault_py, *fsdefault_pyw, *fsdefault_pyc;
    FileSettings **curdefault;

    if ([filetype isEqualToString: @"Python Script"]) {
        curdefault = &fsdefault_py;
    } else if ([filetype isEqualToString: @"Python GUI Script"]) {
        curdefault = &fsdefault_pyw;
    } else if ([filetype isEqualToString: @"Python Bytecode Document"]) {
        curdefault = &fsdefault_pyc;
    } else {
        NSLog(@"Funny File Type: %@\n", filetype);
        curdefault = &fsdefault_py;
        filetype = @"Python Script";
    }
    if (!*curdefault) {
        *curdefault = [[FileSettings new] initForFSDefaultFileType: filetype];
    }
    return *curdefault;
}

+ (id)getDefaultsForFileType: (NSString *)filetype
{
    static FileSettings *default_py, *default_pyw, *default_pyc;
    FileSettings **curdefault;

    if ([filetype isEqualToString: @"Python Script"]) {
        curdefault = &default_py;
    } else if ([filetype isEqualToString: @"Python GUI Script"]) {
        curdefault = &default_pyw;
    } else if ([filetype isEqualToString: @"Python Bytecode Document"]) {
        curdefault = &default_pyc;
    } else {
        NSLog(@"Funny File Type: %@\n", filetype);
        curdefault = &default_py;
        filetype = @"Python Script";
    }
    if (!*curdefault) {
        *curdefault = [[FileSettings new] initForDefaultFileType: filetype];
    }
    return *curdefault;
}

+ (id)newSettingsForFileType: (NSString *)filetype
{
    FileSettings *cur;

    cur = [FileSettings new];
    [cur initForFileType: filetype];
    return [cur retain];
}

- (id)initWithFileSettings: (FileSettings *)source
{
    self = [super init];
    if (!self) return self;

    interpreter = [source->interpreter retain];
    honourhashbang = source->honourhashbang;
    debug = source->debug;
    verbose = source->verbose;
    inspect = source->inspect;
    optimize = source->optimize;
    nosite = source->nosite;
    tabs = source->tabs;
    others = [source->others retain];
    scriptargs = [source->scriptargs retain];
    with_terminal = source->with_terminal;
    prefskey = source->prefskey;
    if (prefskey) [prefskey retain];

    return self;
}

- (id)initForFileType: (NSString *)filetype
{
    FileSettings *defaults;

    defaults = [FileSettings getDefaultsForFileType: filetype];
    self = [self initWithFileSettings: defaults];
    origsource = [defaults retain];
    return self;
}

- (id)initForFSDefaultFileType: (NSString *)filetype
{
    int i;
    NSString *filename;
    NSDictionary *dict;
    static NSDictionary *factorySettings;

    self = [super init];
    if (!self) return self;

    if (factorySettings == NULL) {
        NSBundle *bdl = [NSBundle mainBundle];
        NSString *path = [ bdl pathForResource: @"factorySettings"
                ofType: @"plist"];
        factorySettings = [[NSDictionary dictionaryWithContentsOfFile:
            path] retain];
        if (factorySettings == NULL) {
            NSLog(@"Missing %@", path);
            return NULL;
        }
    }
    dict = [factorySettings objectForKey: filetype];
    if (dict == NULL) {
        NSLog(@"factorySettings.plist misses file type \"%@\"", filetype);
        interpreter = [@"no default found" retain];
        return NULL;
    }
    [self applyValuesFromDict: dict];
    interpreters = [dict objectForKey: @"interpreter_list"];
    interpreter = NULL;
    for (i=0; i < [interpreters count]; i++) {
        filename = [interpreters objectAtIndex: i];
        filename = [filename stringByExpandingTildeInPath];
        if ([[NSFileManager defaultManager] fileExistsAtPath: filename]) {
            interpreter = [filename retain];
            break;
        }
    }
    if (interpreter == NULL)
        interpreter = [@"no default found" retain];
    origsource = NULL;
    return self;
}

- (void)applyUserDefaults: (NSString *)filetype
{
    NSUserDefaults *defaults;
    NSDictionary *dict;

    defaults = [NSUserDefaults standardUserDefaults];
    dict = [defaults dictionaryForKey: filetype];
    if (!dict)
        return;
    [self applyValuesFromDict: dict];
}

- (id)initForDefaultFileType: (NSString *)filetype
{
    FileSettings *fsdefaults;

    fsdefaults = [FileSettings getFactorySettingsForFileType: filetype];
    self = [self initWithFileSettings: fsdefaults];
    if (!self) return self;
    interpreters = [fsdefaults->interpreters retain];
    scriptargs = [@"" retain];
    [self applyUserDefaults: filetype];
    prefskey = [filetype retain];
    return self;
}

- (void)reset
{
    if (origsource) {
        [self updateFromSource: origsource];
    } else {
        FileSettings *fsdefaults;
        fsdefaults = [FileSettings getFactorySettingsForFileType: prefskey];
        [self updateFromSource: fsdefaults];
    }
}

- (void)updateFromSource: (id <FileSettingsSource>)source
{
    interpreter = [[source interpreter] retain];
    honourhashbang = [source honourhashbang];
    debug = [source debug];
    verbose = [source verbose];
    inspect = [source inspect];
    optimize = [source optimize];
    nosite = [source nosite];
    tabs = [source tabs];
    others = [[source others] retain];
    scriptargs = [[source scriptargs] retain];
    with_terminal = [source with_terminal];
    // And if this is a user defaults object we also save the
    // values
    if (!origsource) {
        NSUserDefaults *defaults;
        NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
            interpreter, @"interpreter",
            [NSNumber numberWithBool: honourhashbang], @"honourhashbang",
            [NSNumber numberWithBool: debug], @"debug",
            [NSNumber numberWithBool: verbose], @"verbose",
            [NSNumber numberWithBool: inspect], @"inspect",
            [NSNumber numberWithBool: optimize], @"optimize",
            [NSNumber numberWithBool: nosite], @"nosite",
            [NSNumber numberWithBool: tabs], @"tabs",
            others, @"others",
            scriptargs, @"scriptargs",
            [NSNumber numberWithBool: with_terminal], @"with_terminal",
            nil];
        defaults = [NSUserDefaults standardUserDefaults];
        [defaults setObject: dict forKey: prefskey];
    }
}

- (void)applyValuesFromDict: (NSDictionary *)dict
{
    id value;

    value = [dict objectForKey: @"interpreter"];
    if (value) interpreter = [value retain];
    value = [dict objectForKey: @"honourhashbang"];
    if (value) honourhashbang = [value boolValue];
    value = [dict objectForKey: @"debug"];
    if (value) debug = [value boolValue];
    value = [dict objectForKey: @"verbose"];
    if (value) verbose = [value boolValue];
    value = [dict objectForKey: @"inspect"];
    if (value) inspect = [value boolValue];
    value = [dict objectForKey: @"optimize"];
    if (value) optimize = [value boolValue];
    value = [dict objectForKey: @"nosite"];
    if (value) nosite = [value boolValue];
    value = [dict objectForKey: @"tabs"];
    if (value) tabs = [value boolValue];
    value = [dict objectForKey: @"others"];
    if (value) others = [value retain];
    value = [dict objectForKey: @"scriptargs"];
    if (value) scriptargs = [value retain];
    value = [dict objectForKey: @"with_terminal"];
    if (value) with_terminal = [value boolValue];
}

- (NSString*)_replaceSingleQuotes: (NSString*)string
{
    /* Replace all single-quotes by '"'"', that way shellquoting will
     * be correct when the result value is delimited  using single quotes.
     */
    NSArray* components = [string componentsSeparatedByString:@"'"];

    return [components componentsJoinedByString:@"'\"'\"'"];
}

- (NSString *)commandLineForScript: (NSString *)script
{
    NSString *cur_interp = NULL;
    NSString* script_dir = NULL;
    char hashbangbuf[1024];
    FILE *fp;
    char *p;

    script_dir = [script substringToIndex:
	    [script length]-[[script lastPathComponent] length]];

    if (honourhashbang &&
       (fp=fopen([script fileSystemRepresentation], "r")) &&
       fgets(hashbangbuf, sizeof(hashbangbuf), fp) &&
       strncmp(hashbangbuf, "#!", 2) == 0 &&
       (p=strchr(hashbangbuf, '\n'))) {
            *p = '\0';
            p = hashbangbuf + 2;
            while (*p == ' ') p++;
            cur_interp = [NSString stringWithUTF8String: p];
    }
    if (!cur_interp)
        cur_interp = interpreter;

    return [NSString stringWithFormat:
        @"cd '%@' && '%@'%s%s%s%s%s%s %@ '%@' %@ %s",
    	[self _replaceSingleQuotes:script_dir],
        [self _replaceSingleQuotes:cur_interp],
        debug?" -d":"",
        verbose?" -v":"",
        inspect?" -i":"",
        optimize?" -O":"",
        nosite?" -S":"",
        tabs?" -t":"",
        others,
        [self _replaceSingleQuotes:script],
        scriptargs ? scriptargs : @"",
        with_terminal? "&& echo Exit status: $? && exit 1" : " &"];
}

- (NSArray *) interpreters { return interpreters;};

// FileSettingsSource protocol
- (NSString *) interpreter { return interpreter;};
- (BOOL) honourhashbang { return honourhashbang; };
- (BOOL) debug { return debug;};
- (BOOL) verbose { return verbose;};
- (BOOL) inspect { return inspect;};
- (BOOL) optimize { return optimize;};
- (BOOL) nosite { return nosite;};
- (BOOL) tabs { return tabs;};
- (NSString *) others { return others;};
- (NSString *) scriptargs { return scriptargs;};
- (BOOL) with_terminal { return with_terminal;};

@end