Created
December 22, 2017 22:32
-
-
Save holzschu/b573233d8a0b824d255c7175babf3773 to your computer and use it in GitHub Desktop.
Argument parsing for command lines, includes conversion from scp to curl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- (char **) makeargs:(NSMutableArray*) listArgv argc:(int*) argc | |
{ | |
// Assumes the command line has been separated into arguments, parse the arguments if needed | |
// does some conversions (~ --> home directory, for example, plus environment variables) | |
// Most of the heavy parsing is done in ios_system.m (check if command is a file, etc) | |
// If the command is "scp" or "sftp", do not replace "~" on remote file locations, but | |
// edit the arguments (we simulate scp and sftp by calling "curl scp://remotefile") | |
if ([listArgv count] == 0) { *argc = 0; return NULL; } | |
NSString* cmd = [listArgv objectAtIndex:0]; | |
// Re-concatenate arguments with quotes (' and ") | |
for (unsigned i = 0; i < [listArgv count]; i++) { | |
NSString *argument = [listArgv objectAtIndex:i]; | |
if ([argument hasPrefix:@"'"] && !([argument hasSuffix:@"'"])) { | |
do { | |
// add a space | |
[listArgv replaceObjectAtIndex:i withObject:[[listArgv objectAtIndex:i] stringByAppendingString:@" "]]; | |
// add all arguments that are part of the argument: | |
[listArgv replaceObjectAtIndex:i withObject:[[listArgv objectAtIndex:i] stringByAppendingString:[listArgv objectAtIndex:(i+1)]]]; | |
[listArgv removeObjectAtIndex:(i+1)]; | |
} while (![[listArgv objectAtIndex:(i+1)] hasSuffix:@"'"]); | |
// including the last one | |
[listArgv replaceObjectAtIndex:i withObject:[[listArgv objectAtIndex:i] stringByAppendingString:@" "]]; | |
[listArgv replaceObjectAtIndex:i withObject:[[listArgv objectAtIndex:i] stringByAppendingString:[listArgv objectAtIndex:(i+1)]]]; | |
[listArgv removeObjectAtIndex:(i+1)]; | |
argument = [listArgv objectAtIndex:i]; | |
argument = [argument stringByReplacingOccurrencesOfString:@"'" withString:@""]; | |
[listArgv replaceObjectAtIndex:i withObject:argument]; | |
} | |
// TODO: " | |
} | |
*argc = [listArgv count]; | |
char** argv = (char **)malloc((*argc + 1) * sizeof(char*)); | |
NSString *fileName = NULL; | |
int mustAddMinusTPosition = -1; | |
// 1) convert command line to argc / argv | |
// 1a) split into elements | |
for (unsigned i = 0; i < [listArgv count]; i++) | |
{ | |
// Operations on individual arguments | |
NSString *argument = [listArgv objectAtIndex:i]; | |
// 1b) expand environment variables, + "~" (not wildcards ? and *) | |
while ([argument containsString:@"$"]) { | |
// It has environment variables inside. Work on them one by one. | |
// position of first "$" sign: | |
NSRange r1 = [argument rangeOfString:@"$"]; | |
// position of first "/" after this $ sign: | |
NSRange r2 = [argument rangeOfString:@"/" options:NULL range:NSMakeRange(r1.location + r1.length, [argument length] - r1.location - r1.length)]; | |
// position of first ":" after this $ sign: | |
NSRange r3 = [argument rangeOfString:@":" options:NULL range:NSMakeRange(r1.location + r1.length, [argument length] - r1.location - r1.length)]; | |
if ((r2.location == NSNotFound) && (r3.location == NSNotFound)) r2.location = [argument length]; | |
else if ((r2.location == NSNotFound) || (r3.location < r2.location)) r2.location = r3.location; | |
NSRange rSub = NSMakeRange(r1.location + r1.length, r2.location - r1.location - r1.length); | |
NSString *variable_string = [argument substringWithRange:rSub]; | |
const char* variable = getenv([variable_string UTF8String]); | |
if (variable) { | |
// Okay, so this one exists. | |
NSString* replacement_string = [NSString stringWithCString:variable encoding:NSASCIIStringEncoding]; | |
variable_string = [[NSString stringWithCString:"$" encoding:NSASCIIStringEncoding] stringByAppendingString:variable_string]; | |
argument = [argument stringByReplacingOccurrencesOfString:variable_string withString:replacement_string]; | |
} | |
} | |
// Bash spec: only convert "~" if: at the beginning of argument, after a ":" or the first "=" | |
// ("=" scenario for export, but we use setenv, so no "="). | |
// Only 1 possibility: "~" (same as $HOME) | |
// If the command is scp or sftp, do not apply this on remote directories | |
if (([cmd isEqualToString:@"scp"] || [cmd isEqualToString:@"sftp"]) && (i >= 1)) { | |
if ([argument containsString:@":"]) { | |
// remote host: [user@]host:[/][~]filepath --> scp://[user@]host/ | |
// if filepath relative, add ~ | |
NSRange r1 = [argument rangeOfString:@":"]; | |
NSRange rSub = NSMakeRange(0, r1.location); | |
NSString* userAndHost = [argument substringWithRange:rSub]; | |
rSub = NSMakeRange(r1.location + 1, [argument length] - r1.location - 1); | |
NSString* fileLocation = [argument substringWithRange:rSub]; | |
if(![fileLocation hasPrefix:@"/"]) { | |
// relative path | |
if([fileLocation hasPrefix:@"~"]) { | |
fileLocation = [[NSString stringWithCString:"/" encoding:NSASCIIStringEncoding] stringByAppendingString:fileLocation]; | |
} else { | |
fileLocation = [[NSString stringWithCString:"/~/" encoding:NSASCIIStringEncoding] stringByAppendingString:fileLocation]; | |
} | |
if (![fileLocation hasSuffix:@"/"]) fileName = fileLocation.lastPathComponent; | |
else fileName = @"result.txt"; | |
} | |
NSString *prefix = [cmd stringByAppendingString:[NSString stringWithCString:"://" encoding:NSASCIIStringEncoding]]; | |
argument = [[prefix stringByAppendingString:userAndHost] stringByAppendingString:fileLocation]; | |
// avoid ~ conversion: | |
argv[i] = [argument UTF8String]; | |
continue; | |
} | |
if (![argument hasPrefix:@"-"]) { | |
// Not beginning with "-", not containing ":", must be a local filename | |
// if it's ".", replace with -O | |
// if it's a directory, add name of file from previous argument at the end. | |
if (!fileName) { | |
// local file before remote file: upload | |
mustAddMinusTPosition = i; | |
} else if ([argument isEqualToString:@"."]) argument = @"-O"; | |
else if ([argument hasSuffix:@"/"]) argument = [argument stringByAppendingString:fileName]; | |
else { | |
BOOL isDir; | |
if ([[NSFileManager defaultManager] fileExistsAtPath:argument isDirectory:&isDir]) { | |
if (isDir) | |
argument = [argument stringByAppendingString:fileName]; | |
} | |
} | |
} | |
} | |
// Tilde conversion: | |
if([argument hasPrefix:@"~"]) { | |
// So it begins with "~" | |
argument = [argument stringByExpandingTildeInPath]; | |
if ([argument hasPrefix:@"~:"]) { | |
NSString* test_string = @"~"; | |
NSString* replacement_string = [NSString stringWithCString:(getenv("HOME")) encoding:NSASCIIStringEncoding]; | |
argument = [argument stringByReplacingOccurrencesOfString:test_string withString:replacement_string options:NULL range:NSMakeRange(0, 1)]; | |
} | |
} | |
// Also convert ":~something" in PATH style variables | |
// We don't use these yet, but we could. | |
if ([argument containsString:@":~"]) { | |
// Only 1 possibility: ":~" (same as $HOME) | |
if (getenv("HOME")) { | |
if ([argument containsString:@":~/"]) { | |
NSString* test_string = @":~/"; | |
NSString* replacement_string = [[NSString stringWithCString:":" encoding:NSASCIIStringEncoding] stringByAppendingString:[NSString stringWithCString:(getenv("HOME")) encoding:NSASCIIStringEncoding]]; | |
replacement_string = [replacement_string stringByAppendingString:[NSString stringWithCString:"/" encoding:NSASCIIStringEncoding]]; | |
argument = [argument stringByReplacingOccurrencesOfString:test_string withString:replacement_string]; | |
} else if ([argument hasSuffix:@":~"]) { | |
NSString* test_string = @":~"; | |
NSString* replacement_string = [[NSString stringWithCString:":" encoding:NSASCIIStringEncoding] stringByAppendingString:[NSString stringWithCString:(getenv("HOME")) encoding:NSASCIIStringEncoding]]; | |
argument = [argument stringByReplacingOccurrencesOfString:test_string withString:replacement_string options:NULL range:NSMakeRange([argument length] - 2, 2)]; | |
} else if ([argument hasSuffix:@":"]) { | |
NSString* test_string = @":"; | |
NSString* replacement_string = [[NSString stringWithCString:":" encoding:NSASCIIStringEncoding] stringByAppendingString:[NSString stringWithCString:(getenv("HOME")) encoding:NSASCIIStringEncoding]]; | |
argument = [argument stringByReplacingOccurrencesOfString:test_string withString:replacement_string options:NULL range:NSMakeRange([argument length] - 2, 2)]; | |
} | |
} | |
} | |
if (([cmd isEqualToString:@"scp"] || [cmd isEqualToString:@"sftp"]) && (i == 0)) | |
argv[i] = [@"curl" UTF8String]; | |
else | |
argv[i] = [argument UTF8String]; | |
} | |
if (mustAddMinusTPosition > 0) { | |
// For scp uploads | |
// Need to add parameter "-T" before parameter number i. | |
*argc += 1; | |
argv = (char **)realloc(argv, (*argc + 1) * sizeof(char*)); | |
for (int i = *argc; i > mustAddMinusTPosition; i--) | |
argv[i - 1] = strdup(argv[i - 2]); | |
argv[mustAddMinusTPosition] = [@"-T" UTF8String]; | |
} | |
argv[*argc] = NULL; | |
return argv; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment