Look like working now.

This commit is contained in:
Charlie Qiu
2016-06-10 03:59:27 +08:00
parent 250dfc7b38
commit ccd666bc2a
34 changed files with 1774 additions and 231 deletions

View File

@ -0,0 +1,62 @@
// BRLOptionParser.h
//
// Copyright © 20132015 Stephen Celis (<stephen@stephencelis.com>)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
@import Foundation;
typedef void (^BRLOptionParserOptionBlock)();
typedef void (^BRLOptionParserOptionBlockWithArgument)(NSString *value);
static NSString *const BRLOptionParserErrorDomain = @"BRLOptionParserErrorDomain";
typedef NS_ENUM(NSUInteger, BRLOptionParserErrorCode) {
BRLOptionParserErrorCodeUnrecognized = 1,
BRLOptionParserErrorCodeRequired
};
@interface BRLOptionParser : NSObject
+ (instancetype)parser;
+ (instancetype)longOnlyParser;
@property (nonatomic, getter = isLongOnly) BOOL longOnly;
@property (nonatomic, copy) NSString *banner;
- (void)setBanner:(NSString *)banner, ...;
- (void)addOption:(char *)option flag:(unichar)flag description:(NSString *)description block:(BRLOptionParserOptionBlock)block;
- (void)addOption:(char *)option flag:(unichar)flag description:(NSString *)description blockWithArgument:(BRLOptionParserOptionBlockWithArgument)blockWithArgument;
- (void)addOption:(char *)option flag:(unichar)flag description:(NSString *)description value:(BOOL *)value;
- (void)addOption:(char *)option flag:(unichar)flag description:(NSString *)description argument:(NSString *__strong *)argument;
- (void)addSeparator;
- (void)addSeparator:(NSString *)separator;
- (BOOL)parseArgc:(int)argc argv:(const char **)argv error:(NSError **)error;
@end

View File

@ -0,0 +1,306 @@
// BRLOptionParser.m
//
// Copyright © 20132015 Stephen Celis (<stephen@stephencelis.com>)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "BRLOptionParser.h"
#import <getopt.h>
typedef NS_ENUM(NSUInteger, BRLOptionArgument) {
BRLOptionArgumentNone = no_argument,
BRLOptionArgumentRequired = required_argument
};
@interface BRLOption : NSObject
@property (assign) BRLOptionArgument argument;
@property (assign) char * name;
@property (assign) unichar flag;
@property (strong) NSString *description;
@property (copy) id block;
+ (instancetype)optionWithName:(char *)name flag:(unichar)flag description:(NSString *)description block:(BRLOptionParserOptionBlock)block;
+ (instancetype)optionWithName:(char *)name flag:(unichar)flag description:(NSString *)description blockWithArgument:(BRLOptionParserOptionBlockWithArgument)blockWithArgument;
@end
@implementation BRLOption
@synthesize description = _description;
+ (instancetype)optionWithName:(char *)name flag:(unichar)flag description:(NSString *)description block:(BRLOptionParserOptionBlock)block
{
BRLOption *option = [[self alloc] initWithName:name flag:flag description:description argument:BRLOptionArgumentNone block:block];
return option;
}
+ (instancetype)optionWithName:(char *)name flag:(unichar)flag description:(NSString *)description blockWithArgument:(BRLOptionParserOptionBlockWithArgument)blockWithArgument
{
BRLOption *option = [[self alloc] initWithName:name flag:flag description:description argument:BRLOptionArgumentRequired block:blockWithArgument];
return option;
}
- (instancetype)initWithName:(char *)name flag:(unichar)flag description:(NSString *)description argument:(BRLOptionArgument)argument block:(id)block
{
if (self = [super init]) {
_argument = argument;
_name = name;
_flag = flag;
_block = block;
_description = description;
}
return self;
}
@end
@interface BRLOptionParser ()
@property NSMutableArray *options;
@end
@implementation BRLOptionParser
+ (instancetype)parser
{
return [self new];
}
+ (instancetype)longOnlyParser
{
BRLOptionParser *parser = [self parser];
if (parser) {
parser.longOnly = YES;
}
return parser;
}
- (id)init
{
if (self = [super init]) {
[self setBanner:@"usage: %@ [options]", [NSProcessInfo processInfo].processName];
_options = [NSMutableArray new];
}
return self;
}
- (void)setBanner:(NSString *)banner, ...
{
va_list args;
va_start(args, banner);
_banner = [[NSString alloc] initWithFormat:banner arguments:args];
va_end(args);
return;
}
- (void)addOption:(char *)option flag:(unichar)flag description:(NSString *)description block:(BRLOptionParserOptionBlock)block
{
[self.options addObject:[BRLOption optionWithName:option flag:flag description:description block:block]];
}
- (void)addOption:(char *)option flag:(unichar)flag description:(NSString *)description blockWithArgument:(BRLOptionParserOptionBlockWithArgument)blockWithArgument
{
[self.options addObject:[BRLOption optionWithName:option flag:flag description:description blockWithArgument:blockWithArgument]];
}
- (void)addOption:(char *)option flag:(unichar)flag description:(NSString *)description value:(BOOL *)value
{
[self addOption:option flag:flag description:description block:^{
*value = YES;
}];
}
- (void)addOption:(char *)option flag:(unichar)flag description:(NSString *)description argument:(NSString *__strong *)argument
{
[self addOption:option flag:flag description:description blockWithArgument:^(NSString *value) {
*argument = value;
}];
}
- (void)addSeparator
{
[self addSeparator:nil];
}
- (void)addSeparator:(NSString *)separator
{
if (separator == nil) {
separator = @"";
}
[self.options addObject:separator];
}
- (BOOL)parseArgc:(int)argc argv:(const char **)argv error:(NSError *__autoreleasing *)error
{
NSMapTable *mapTable = NSCreateMapTable(NSIntegerMapKeyCallBacks, NSNonRetainedObjectMapValueCallBacks, self.options.count);
NSUInteger i = 0;
NSUInteger c = 0;
struct option * long_options = malloc((self.options.count + 1) * sizeof(struct option));
char * short_options = malloc(((self.options.count * 2) + 1) * sizeof(char));
for (id each in self.options) {
if (![each isKindOfClass:[BRLOption class]]) {
continue;
}
BRLOption *option = each;
if (option.name) {
NSMapInsert(mapTable, (const void *)option.name, (__bridge void *)option);
long_options[i++] = (struct option){option.name, option.argument, NULL, option.flag};
}
if (option.flag) {
NSMapInsert(mapTable, (const void *)(NSUInteger)option.flag, (__bridge void *)option);
short_options[c++] = option.flag;
if (option.argument == BRLOptionArgumentRequired) {
short_options[c++] = ':';
};
}
}
long_options[i] = (struct option){0, 0, 0, 0};
short_options[c] = '\0';
int ch = 0;
int long_options_index = 0;
opterr = 0;
int (* getopt_long_method)(int, char * const *, const char *, const struct option *, int *);
getopt_long_method = self.isLongOnly ? &getopt_long_only : &getopt_long;
int cached_optind = optind;
while ((ch = getopt_long_method(argc, (char **)argv, short_options, long_options, &long_options_index)) != -1) {
@try {
BRLOption *option = nil;
switch (ch) {
case '?': {
if (error) {
// I wish this could be done more cleanly, but getopt doesn't appear to expose the current failing option as originally input.
NSString *arg = [NSString stringWithUTF8String:argv[cached_optind]];
if ([arg hasPrefix:[self longPrefix]]) {
arg = [arg componentsSeparatedByString:@"="].firstObject;
} else if (optopt) {
arg = [NSString stringWithFormat:@"-%c", optopt];
}
if (optopt) {
option = (__bridge BRLOption *)NSMapGet(mapTable, (const void *)(NSUInteger)optopt);
} else {
NSString *longOption = [NSString stringWithFormat:@"%@%s", [self longPrefix], long_options[long_options_index].name];
if ([arg isEqualToString:longOption]) {
option = (__bridge BRLOption *)NSMapGet(mapTable, (const void *)long_options[long_options_index].name);
}
}
if (option && option.argument == BRLOptionArgumentRequired) {
*error = [NSError errorWithDomain:BRLOptionParserErrorDomain code:BRLOptionParserErrorCodeRequired userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"option `%@' requires an argument", arg]}];
} else {
*error = [NSError errorWithDomain:BRLOptionParserErrorDomain code:BRLOptionParserErrorCodeUnrecognized userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"unrecognized option `%@'", arg]}];
}
}
return NO;
break;
}
case ':':
break;
case 0:
option = (__bridge BRLOption *)NSMapGet(mapTable, (const void *)long_options[long_options_index].name);
break;
default: {
option = (__bridge BRLOption *)NSMapGet(mapTable, (const void *)(NSUInteger)ch);
break;
}
}
if (option.argument == BRLOptionArgumentRequired) {
BRLOptionParserOptionBlockWithArgument block = option.block;
block([NSString stringWithUTF8String:optarg]);
} else {
BRLOptionParserOptionBlock block = option.block;
block();
}
} @finally {
cached_optind = optind;
}
}
return YES;
}
- (NSString *)description
{
NSMutableString *(^trimLine)(NSMutableString *) = ^NSMutableString *(NSMutableString *line) {
NSRange range = [line rangeOfCharacterFromSet:[[NSCharacterSet whitespaceCharacterSet] invertedSet] options:NSBackwardsSearch];
if (range.location != NSNotFound) {
line = [[line substringToIndex:range.location + 1] mutableCopy];
}
return line;
};
NSMutableArray *description = [NSMutableArray arrayWithObject:self.banner];
for (id each in self.options) {
NSMutableString *line = [NSMutableString new];
if ([each isKindOfClass:[BRLOption class]]) {
BRLOption *option = each;
[line appendString:@" "];
if (option.flag) {
[line appendFormat:@"-%c", option.flag];
[line appendString:option.name ? @", " : @" "];
} else {
[line appendString:@" "];
}
if (option.name) {
[line appendFormat:@"%@%-24s ", [self longPrefix], option.name];
} else {
[line appendString:@" "];
}
if (line.length > 37) {
line = trimLine(line);
[line appendString:@"\n "];
}
if (option.description) {
[line appendString:option.description];
}
line = trimLine(line);
} else {
[line appendFormat:@"%@", each];
}
[description addObject:line];
}
return [[description componentsJoinedByString:@"\n"] stringByAppendingString:@"\n"];
}
#pragma mark -
- (NSString *)longPrefix
{
return self.isLongOnly ? @"-" : @"--";
}
@end

22
Pods/BRLOptionParser/LICENSE generated Normal file
View File

@ -0,0 +1,22 @@
(The MIT License)
Copyright © 2013-2015 Stephen Celis (<stephen@stephencelis.com>)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

88
Pods/BRLOptionParser/README.markdown generated Normal file
View File

@ -0,0 +1,88 @@
# BRLOptionParser [![Build Status][1]][2]
A short wrapper for [getopt_long(3)][3] (and getopt_long_only(3)).
[1]: https://img.shields.io/travis/stephencelis/BRLOptionParser.svg?style=flat
[2]: https://travis-ci.org/stephencelis/BRLOptionParser
[3]: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/getopt_long.3.html
## Install
With [CocoaPods][4]:
``` rb
# Podfile
pod 'BRLOptionParser', '~> 0.3.1'
```
[4]: http://cocoapods.org
## Example
``` objc
// main.m
#import <BRLOptionParser/BRLOptionParser.h>
int main(int argc, const char * argv[])
{
@autoreleasepool {
NSString *name = @"world";
BOOL verbose = NO;
BRLOptionParser *options = [BRLOptionParser new];
[options setBanner:@"usage: %s [-n <name>] [-vh]", argv[0]];
[options addOption:"name" flag:'n' description:@"Your name" argument:&name];
[options addSeparator];
[options addOption:"verbose" flag:'v' description:nil value:&verbose];
__weak typeof(options) weakOptions = options;
[options addOption:"help" flag:'h' description:@"Show this message" block:^{
printf("%s", [[weakOptions description] UTF8String]);
exit(EXIT_SUCCESS);
}];
NSError *error = nil;
if (![options parseArgc:argc argv:argv error:&error]) {
const char * message = error.localizedDescription.UTF8String;
fprintf(stderr, "%s: %s\n", argv[0], message);
exit(EXIT_FAILURE);
}
if (verbose) {
fprintf(stderr, "(Preparing to say hello...)\n");
}
printf("Hello, %s!\n", name.UTF8String);
}
return EXIT_SUCCESS;
}
```
In practice:
```
$ hello
Hello, world!
$ hello -h
usage: hello [-n <name>] [-vh]
-n, --name Your name
-v, --verbose
-h, --help Show this message
$ hello -n
hello: option `-n' requires an argument
$ hello --name Stephen
Hello, Stephen!
$ hello -vngoodbye
(Preparing to say hello...)
Hello, goodbye!
$ hello --goodbye
hello: unrecognized option `--goodbye'
```
## License
BRLOptionParser is available under the MIT license. See the LICENSE file
for more information.