Skip to content

Instantly share code, notes, and snippets.

@holzschu
Created December 22, 2017 22:32
Show Gist options
  • Save holzschu/b573233d8a0b824d255c7175babf3773 to your computer and use it in GitHub Desktop.
Save holzschu/b573233d8a0b824d255c7175babf3773 to your computer and use it in GitHub Desktop.
Argument parsing for command lines, includes conversion from scp to curl
- (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