mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-20 06:51:08 +00:00
685 lines
18 KiB
Objective-C
685 lines
18 KiB
Objective-C
/** Property list utility.
|
|
Copyright (C) 2020 Free Software Foundation, Inc.
|
|
|
|
Written by: Mingye Wang
|
|
Created: feb 2020
|
|
|
|
This file is part of the GNUstep Project
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either
|
|
version 3 of the License, or (at your option) any later version.
|
|
|
|
You should have received a copy of the GNU General Public
|
|
License along with this program; see the file COPYINGv3.
|
|
If not, write to the Free Software Foundation,
|
|
31 Milk Street #960789 Boston, MA 02196 USA.
|
|
|
|
*/
|
|
|
|
// #import "common.h"
|
|
#include <string.h>
|
|
|
|
#import "Foundation/NSArray.h"
|
|
#import "Foundation/NSAutoreleasePool.h"
|
|
#import "Foundation/NSData.h"
|
|
#import "Foundation/NSDictionary.h"
|
|
#import "Foundation/NSException.h"
|
|
#import "Foundation/NSFileHandle.h"
|
|
#import "Foundation/NSProcessInfo.h"
|
|
#import "Foundation/NSPropertyList.h"
|
|
#import "Foundation/NSString.h"
|
|
#import "Foundation/NSUserDefaults.h"
|
|
#import "Foundation/NSValue.h"
|
|
#import "NSPropertyList+PLUtil.h"
|
|
|
|
// From NSPropertyList.m
|
|
GS_EXPORT void
|
|
GSPropertyListMake(id, NSDictionary *, BOOL, BOOL, unsigned, id *);
|
|
|
|
// We don't have @[] on gcc
|
|
#define NARRAY(...) [NSArray arrayWithObjects: __VA_ARGS__, nil]
|
|
// And no @() or @123
|
|
#define NINT(Num) [NSNumber numberWithInt: Num]
|
|
// Unfortunately we have to define @() for @"" too because a macro later
|
|
#define NSTR(Str) [NSString stringWithCString: Str]
|
|
|
|
/* Bitmap of 'quotable' characters ... those characters which must be
|
|
* inside a quoted string if written to an old style property list.
|
|
* Taken from NSPropertyList.m.
|
|
*/
|
|
static const unsigned char quotables[32] = {
|
|
'\xff', '\xff', '\xff', '\xff', '\x85', '\x13', '\x00', '\x78',
|
|
'\x00', '\x00', '\x00', '\x38', '\x01', '\x00', '\x00', '\xa8',
|
|
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
|
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
|
};
|
|
#define IS_BIT_SET(a, i) ((((a) & (1 << (i)))) > 0)
|
|
#define GS_IS_QUOTABLE(X) IS_BIT_SET(quotables[(X) / 8], (X) % 8)
|
|
|
|
/**
|
|
* Indexes a NSArray or a NSDictionary.
|
|
*/
|
|
id
|
|
plIndex(id obj, NSString *key)
|
|
{
|
|
const char *ckey;
|
|
char *endptr = NULL;
|
|
NSInteger res;
|
|
|
|
if ([obj isKindOfClass: [NSDictionary class]])
|
|
{
|
|
return [(NSDictionary *) obj objectForKey: key];
|
|
}
|
|
else if ([obj isKindOfClass: [NSArray class]])
|
|
{
|
|
ckey = [key cStringUsingEncoding: [NSString defaultCStringEncoding]];
|
|
res = strtoll(ckey, &endptr, 10);
|
|
if (endptr && *endptr != '\0')
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"%@ is not a valid integer", key];
|
|
}
|
|
return [(NSArray *) obj objectAtIndex: res];
|
|
}
|
|
else
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"%@ is not indexible", obj];
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mutate obj[key] to leaf.
|
|
* If leaf is nil, remove.
|
|
* Else if replace is NO, insert.
|
|
* Otherwise replace.
|
|
*
|
|
* Inserting and replacing are the same for NSMutableDictionary.
|
|
*/
|
|
void
|
|
mutate(id obj, NSString *key, id leaf, BOOL replace)
|
|
{
|
|
const char *ckey;
|
|
char *endptr = NULL;
|
|
NSInteger res;
|
|
|
|
if ([obj isKindOfClass: [NSMutableDictionary class]])
|
|
{
|
|
if (!leaf)
|
|
[(NSMutableDictionary *) obj removeObjectForKey: key];
|
|
else
|
|
[(NSMutableDictionary *) obj setObject: leaf forKey: key];
|
|
}
|
|
else if ([obj isKindOfClass: [NSMutableArray class]])
|
|
{
|
|
ckey = [key cStringUsingEncoding: [NSString defaultCStringEncoding]];
|
|
res = strtoll(ckey, &endptr, 10);
|
|
if (endptr && *endptr != '\0')
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"%@ is not a valid integer", key];
|
|
}
|
|
if (!leaf)
|
|
[(NSMutableArray *) obj removeObjectAtIndex: res];
|
|
else if (replace)
|
|
[(NSMutableArray *) obj replaceObjectAtIndex: res withObject: leaf];
|
|
else
|
|
[(NSMutableArray *) obj insertObject: leaf atIndex: res];
|
|
}
|
|
else
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"%@ is not indexible", obj];
|
|
}
|
|
}
|
|
|
|
#define KEYPATH_SEP '.'
|
|
NSString *
|
|
parseQuotedString(const char *, size_t *);
|
|
/**
|
|
* Parses a keypath to its component keys. ISO/IEC 14977 EBNF:
|
|
* <pre>
|
|
* keypath = '' | keypath '.' key;
|
|
* key = quotedString | unquotedStringNotAllowingPeriod;
|
|
* </pre>
|
|
* The definitions of strings follow plist conventions.
|
|
* The use of quoted strings is a GNUstep extension (I think).
|
|
*/
|
|
NSArray *
|
|
parseKeyPath(const char *keypath)
|
|
{
|
|
NSMutableArray *res = [NSMutableArray array];
|
|
NSString *key = nil;
|
|
size_t i;
|
|
size_t j;
|
|
|
|
for (i = 0; keypath[i]; i++)
|
|
{
|
|
switch (keypath[i])
|
|
{
|
|
case KEYPATH_SEP:
|
|
if (key != nil)
|
|
{
|
|
[res addObject: key];
|
|
key = nil;
|
|
}
|
|
break;
|
|
|
|
case '"':
|
|
key = parseQuotedString(keypath, &i);
|
|
break;
|
|
|
|
default:
|
|
j = i;
|
|
while (keypath[j]
|
|
&& !GS_IS_QUOTABLE(keypath[j])
|
|
&& keypath[j] != KEYPATH_SEP)
|
|
{
|
|
j++;
|
|
}
|
|
key = [NSString stringWithCString: &keypath[i] length: (j - i)];
|
|
i = j - 1;
|
|
break;
|
|
}
|
|
}
|
|
return AUTORELEASE([res copy]);
|
|
}
|
|
|
|
/**
|
|
* Parse a quoted string by pretending it is a plist.
|
|
*/
|
|
NSString *
|
|
parseQuotedString(const char *keypath, size_t *i)
|
|
{
|
|
const char *begin = &keypath[*i];
|
|
const char *end;
|
|
id parsed;
|
|
|
|
// Select the part of the quoted string that looks like a plist string
|
|
for (end = begin + 1; *end && *end != '"'; end++)
|
|
{
|
|
if (*end == '\\')
|
|
{
|
|
if (end[1])
|
|
{
|
|
end += 1;
|
|
}
|
|
else
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"Premature EOF in keypath"];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!*end)
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"Premature EOF in keypath"];
|
|
}
|
|
*i = end - keypath + 1;
|
|
parsed = [[NSString stringWithCString: begin length: end-begin] propertyList];
|
|
return (NSString *)parsed;
|
|
}
|
|
|
|
/**
|
|
* Index by keypath.
|
|
*/
|
|
id
|
|
plIndexKeypath(id obj, NSString *keypath, int depthOffset)
|
|
{
|
|
NSArray *parsedPath = parseKeyPath([keypath cString]);
|
|
int count = [parsedPath count];
|
|
int i;
|
|
|
|
for (i = 0; i < count - depthOffset; i++)
|
|
obj = plIndex(obj, [parsedPath objectAtIndex: i]);
|
|
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* Interpret -type value options.
|
|
*
|
|
* The -plist type is a GNUStep extension.
|
|
*/
|
|
id
|
|
parseValue(NSString *type, NSString *value)
|
|
{
|
|
id result;
|
|
|
|
if ([type isEqual: @"-plist"])
|
|
{
|
|
result = [value propertyList];
|
|
}
|
|
else if ([type isEqual: @"-xml"] || [type isEqual: @"-date"])
|
|
{
|
|
NSData *mydata =
|
|
[value dataUsingEncoding: [NSString defaultCStringEncoding]];
|
|
NSPropertyListFormat aFormat;
|
|
NSError *anError;
|
|
|
|
result = [NSPropertyListSerialization
|
|
propertyListWithData: mydata
|
|
options: NSPropertyListMutableContainersAndLeaves
|
|
format: &aFormat
|
|
error: &anError];
|
|
if (result == nil)
|
|
{
|
|
GSPrintf(stderr, @"Parsing plist %@: %@\n", value, anError);
|
|
}
|
|
else if ([type isEqual: @"-xml"]
|
|
&& aFormat != NSPropertyListXMLFormat_v1_0)
|
|
{
|
|
GSPrintf(stderr,
|
|
@"Warning: parsing XML plist %@: Not an XML (fmt %d)\n", value,
|
|
aFormat);
|
|
}
|
|
else if ([type isEqual: @"-date"]
|
|
&& ![result isKindOfClass: [NSDate class]])
|
|
{
|
|
GSPrintf(stderr, @"Warning: parsing date %@: Not a date (got %@)\n",
|
|
value, result);
|
|
}
|
|
}
|
|
else if ([type isEqual: @"-bool"])
|
|
{
|
|
result = [NSNumber numberWithBool:
|
|
([value isEqual: @"YES"] || [value isEqual: @"true"])];
|
|
}
|
|
else if ([type isEqual: @"-integer"])
|
|
{
|
|
result = [NSNumber numberWithLongLong: [value longLongValue]];
|
|
}
|
|
else if ([type isEqual: @"-float"])
|
|
{
|
|
/*
|
|
* We do a step further than NSPropertyList.m and probably Apple,
|
|
* since notsupporting inf and nan is a bad look
|
|
* (No hex literals for now unless someone really wants it)
|
|
*/
|
|
result = [NSNumber numberWithDouble: strtod([value cString], 0)];
|
|
}
|
|
else if ([type isEqual: @"-data"])
|
|
{
|
|
result = AUTORELEASE([[NSData alloc] initWithBase64EncodedData:
|
|
[value dataUsingEncoding: [NSString defaultCStringEncoding]]
|
|
options: NSDataBase64DecodingIgnoreUnknownCharacters]);
|
|
}
|
|
else
|
|
{
|
|
GSPrintf(stderr, @"Unrecognized type %@\n", type);
|
|
result = nil;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#define SELFMAP(Name) NINT(Name), NSTR(#Name)
|
|
/**
|
|
* Translates a string fmt to NSPropertyListFormat.
|
|
*/
|
|
NSPropertyListFormat
|
|
plFormatFromName(NSString *name)
|
|
{
|
|
// clang-format off
|
|
NSDictionary *nameMap = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
NINT(NSPropertyListXMLFormat_v1_0), @"xml1",
|
|
NINT(NSPropertyListBinaryFormat_v1_0), @"binary1",
|
|
NINT(NSPropertyListOpenStepFormat), @"openstep",
|
|
NINT(NSPropertyListGNUstepFormat), @"gnustep",
|
|
NINT(NSPropertyListGNUstepBinaryFormat), @"gsbinary",
|
|
NINT(NSPropertyListJSONFormat), @"json",
|
|
SELFMAP(NSPropertyListOpenStepFormat),
|
|
SELFMAP(NSPropertyListXMLFormat_v1_0),
|
|
SELFMAP(NSPropertyListBinaryFormat_v1_0),
|
|
SELFMAP(NSPropertyListGNUstepFormat),
|
|
SELFMAP(NSPropertyListGNUstepBinaryFormat),
|
|
nil];
|
|
id res = [nameMap objectForKey: name];
|
|
// clang-format on
|
|
if (!res)
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"Invalid fmt %@", name];
|
|
return [res intValue];
|
|
}
|
|
|
|
/**
|
|
* Dumps obj to outfile.
|
|
*/
|
|
int
|
|
dumpToFile(id obj, NSPropertyListFormat fmt, NSString *outfile)
|
|
{
|
|
NSString *errorString = nil;
|
|
NSFileHandle *fh;
|
|
NSData *outdata =
|
|
[NSPropertyListSerialization dataFromPropertyList: obj
|
|
format: fmt
|
|
errorDescription: &errorString];
|
|
if (errorString)
|
|
{
|
|
GSPrintf(stderr, @"Dumping %@ as format %@ - %@\n", obj, fmt,
|
|
errorString);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if ([outfile isEqual: @"-"])
|
|
fh = [NSFileHandle fileHandleWithStandardOutput];
|
|
else
|
|
fh = [NSFileHandle fileHandleForWritingAtPath: outfile];
|
|
[fh writeData: outdata];
|
|
[fh synchronizeFile];
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int
|
|
plCmdConvert(id obj, NSArray *cmdargs, NSString *outfile)
|
|
{
|
|
NSString *fmt = [cmdargs objectAtIndex: 0];
|
|
return dumpToFile(obj, plFormatFromName(fmt), outfile);
|
|
}
|
|
|
|
int
|
|
plCmdExtract(id obj, NSArray *cmdargs, NSString *outfile)
|
|
{
|
|
NSString *keypath = [cmdargs objectAtIndex: 0];
|
|
NSString *fmt = [cmdargs objectAtIndex: 1];
|
|
obj = plIndexKeypath(obj, keypath, 0);
|
|
return dumpToFile(obj, plFormatFromName(fmt), outfile);
|
|
}
|
|
|
|
int
|
|
plCmdRemove(id obj, NSPropertyListFormat fmt, NSArray *cmdargs,
|
|
NSString *outfile)
|
|
{
|
|
NSString *keypath = [cmdargs objectAtIndex: 0];
|
|
|
|
NSArray *parsedPath = parseKeyPath([keypath cString]);
|
|
id leaf = plIndexKeypath(obj, keypath, 1);
|
|
|
|
mutate(leaf, [parsedPath lastObject], nil, false);
|
|
return dumpToFile(obj, fmt, outfile);
|
|
}
|
|
|
|
int
|
|
plCmdInsert(id obj, NSPropertyListFormat fmt, NSArray *cmdargs,
|
|
NSString *outfile)
|
|
{
|
|
NSString *keypath = [cmdargs objectAtIndex: 0];
|
|
id newleaf = parseValue([cmdargs objectAtIndex: 1], [cmdargs objectAtIndex: 2]);
|
|
|
|
NSArray *parsedPath = parseKeyPath([keypath cString]);
|
|
id leaf = plIndexKeypath(obj, keypath, 1);
|
|
|
|
if (!newleaf)
|
|
return EXIT_FAILURE;
|
|
|
|
mutate(leaf, [parsedPath lastObject], newleaf, false);
|
|
return dumpToFile(obj, fmt, outfile);
|
|
}
|
|
|
|
int
|
|
plCmdReplace(id obj, NSPropertyListFormat fmt, NSArray *cmdargs,
|
|
NSString *outfile)
|
|
{
|
|
NSString *keypath = [cmdargs objectAtIndex: 0];
|
|
id newleaf = parseValue([cmdargs objectAtIndex: 1], [cmdargs objectAtIndex: 2]);
|
|
|
|
NSArray *parsedPath = parseKeyPath([keypath cString]);
|
|
id leaf = plIndexKeypath(obj, keypath, 1);
|
|
|
|
if (!newleaf)
|
|
return EXIT_FAILURE;
|
|
|
|
mutate(leaf, [parsedPath lastObject], newleaf, true);
|
|
return dumpToFile(obj, fmt, outfile);
|
|
}
|
|
|
|
static void
|
|
print_help(FILE *f)
|
|
{
|
|
GSPrintf(f, @"Property list utility\n");
|
|
GSPrintf(f, @"Usage: plutil [command] [options] file\n\n");
|
|
GSPrintf(f, @"Accepted commands:\n");
|
|
GSPrintf(
|
|
f, @" -p\tPrints the plists in a human-readable form (GNUstep ASCII).\n");
|
|
GSPrintf(
|
|
f, @" -lint\tVerifies the plist can be parsed.\n");
|
|
GSPrintf(
|
|
f, @" -convert FMT\tConverts the plist to another format.\n");
|
|
GSPrintf(
|
|
f, @" -insert PATH KEY VALUE\tInsert KEY=VALUE to the object at PATH.\n");
|
|
GSPrintf(
|
|
f, @" -replace PATH KEY VALUE\tReplace KEY=VALUE for the object at PATH.\n");
|
|
GSPrintf(
|
|
f, @" -remove PATH KEY\tRemove KEY from the object at PATH.\n");
|
|
GSPrintf(
|
|
f, @" -extract PATH KEY\tExtract the KEY from the object at PATH.\n");
|
|
GSPrintf(f, @"Accepted options:\n");
|
|
GSPrintf(
|
|
f, @" -s\t(No effect.)\n");
|
|
GSPrintf(
|
|
f, @" -o OUTFILE\tOutput to the file given.\n");
|
|
GSPrintf(
|
|
f, @" -e OUTEXT\tOutput to a file with the given extension.\n");
|
|
}
|
|
|
|
typedef enum _Action
|
|
{
|
|
ACTION_LINT,
|
|
ACTION_PRINT,
|
|
ACTION_CONVERT,
|
|
ACTION_REPLACE,
|
|
ACTION_INSERT,
|
|
ACTION_REMOVE,
|
|
ACTION_EXTRACT,
|
|
} Action;
|
|
|
|
/** <p> Property list utility. Should act like macOS catalina plutil(1).
|
|
* </p>
|
|
*/
|
|
int
|
|
main(int argc, char **argv, char **env)
|
|
{
|
|
int status = EXIT_SUCCESS;
|
|
|
|
do
|
|
{
|
|
ENTER_POOL
|
|
NSProcessInfo *proc;
|
|
NSArray *args;
|
|
NSString *arg;
|
|
NSString *inpath;
|
|
NSArray *cmdargs = nil;
|
|
NSDictionary *commands = nil;
|
|
NSArray *command_rhs;
|
|
NSString *outpath = nil;
|
|
NSString *outext = nil;
|
|
Action action = ACTION_LINT;
|
|
int count = 0;
|
|
int i = 1;
|
|
|
|
#ifdef GS_PASS_ARGUMENTS
|
|
GSInitializeProcess(argc, argv, env);
|
|
#endif
|
|
proc = [NSProcessInfo processInfo];
|
|
if (proc == nil)
|
|
{
|
|
NSLog(@"plutil: unable to get process information.");
|
|
status = EXIT_FAILURE;
|
|
break;
|
|
}
|
|
|
|
args = [proc arguments];
|
|
if ((count = [args count]) <= 1)
|
|
{
|
|
NSLog(@"plutil: no files given.");
|
|
status = EXIT_FAILURE;
|
|
break;
|
|
}
|
|
|
|
// Parse the COMMAND.
|
|
// Maps number of args to commands.
|
|
// clang-format off
|
|
commands = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
NARRAY(NINT(ACTION_PRINT), NINT(0)), @"-p",
|
|
NARRAY(NINT(ACTION_LINT), NINT(0)), @"-lint",
|
|
NARRAY(NINT(ACTION_CONVERT), NINT(1)), @"-convert",
|
|
NARRAY(NINT(ACTION_INSERT), NINT(3)), @"-insert",
|
|
NARRAY(NINT(ACTION_REPLACE), NINT(3)), @"-replace",
|
|
NARRAY(NINT(ACTION_REMOVE), NINT(1)), @"-remove",
|
|
NARRAY(NINT(ACTION_EXTRACT), NINT(2)), @"-extract",
|
|
nil];
|
|
// clang-format on
|
|
NS_DURING
|
|
{
|
|
NSData *fileData;
|
|
NSPropertyListFormat aFormat;
|
|
NSError *anError;
|
|
id result;
|
|
NSMutableString *outStr = nil;
|
|
NSDictionary *locale;
|
|
|
|
arg = [args objectAtIndex: i];
|
|
if (![arg hasPrefix: @"-"])
|
|
goto parse_file;
|
|
|
|
command_rhs = [commands objectForKey: arg];
|
|
if (command_rhs)
|
|
{
|
|
int iwant;
|
|
NSRange argrange;
|
|
|
|
action = [[command_rhs objectAtIndex: 0] intValue];
|
|
iwant = [[command_rhs objectAtIndex: 1] intValue];
|
|
|
|
argrange.location = i + 1;
|
|
argrange.length = iwant;
|
|
cmdargs = [args subarrayWithRange: argrange];
|
|
|
|
i += 1 + iwant;
|
|
}
|
|
|
|
// Parse options
|
|
for (; i < count; i++)
|
|
{
|
|
arg = [args objectAtIndex: i];
|
|
if (![arg hasPrefix: @"-"]
|
|
|| [arg isEqual: @"-"]
|
|
|| [arg isEqual: @"--"])
|
|
{
|
|
goto parse_file;
|
|
}
|
|
else if ([arg caseInsensitiveCompare: @"-help"] == NSOrderedSame
|
|
|| [arg caseInsensitiveCompare: @"--help"] == NSOrderedSame
|
|
|| [arg caseInsensitiveCompare: @"-h"] == NSOrderedSame)
|
|
{
|
|
print_help(stdout);
|
|
break;
|
|
}
|
|
else if ([arg isEqual: @"-s"])
|
|
{
|
|
/* NOOP: What the heck is being quiet? */;
|
|
}
|
|
else if ([arg isEqual: @"-o"])
|
|
{
|
|
outpath = [args objectAtIndex: ++i];
|
|
}
|
|
else if ([arg isEqual: @"-e"])
|
|
{
|
|
outext = [args objectAtIndex: ++i];
|
|
}
|
|
else
|
|
{
|
|
GSPrintf(stderr, @"unrecognized option: %@\n", arg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
parse_file:
|
|
inpath = [args objectAtIndex: i];
|
|
if (!outpath && !outext && action != ACTION_EXTRACT)
|
|
{
|
|
outpath = inpath;
|
|
}
|
|
else if (outext)
|
|
{
|
|
NSRange dot;
|
|
|
|
dot = [inpath rangeOfString: @"." options: NSBackwardsSearch];
|
|
if (dot.length == 0)
|
|
{
|
|
dot.location = [inpath length];
|
|
}
|
|
outpath = [NSString stringWithFormat: @"%@.%@",
|
|
[inpath substringToIndex: dot.location], outext];
|
|
}
|
|
|
|
// Open, read, do things.
|
|
if ([inpath isEqual: @"-"])
|
|
{
|
|
NSFileHandle *fh = [NSFileHandle fileHandleWithStandardInput];
|
|
fileData = [fh readDataToEndOfFile];
|
|
}
|
|
else
|
|
fileData = [NSData dataWithContentsOfFile: inpath];
|
|
|
|
result = [NSPropertyListSerialization
|
|
propertyListWithData: fileData
|
|
options: NSPropertyListMutableContainersAndLeaves
|
|
format: &aFormat
|
|
error: &anError];
|
|
if (result == nil)
|
|
{
|
|
GSPrintf(stderr, @"Loading '%@' - %@\n", inpath, anError);
|
|
status = EXIT_FAILURE;
|
|
break;
|
|
}
|
|
|
|
switch (action)
|
|
{
|
|
case ACTION_LINT:
|
|
break;
|
|
case ACTION_PRINT:
|
|
// Not using description because we can GS it
|
|
locale =
|
|
[[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
|
|
GSPropertyListMake(result, locale, NO, NO, 2, &outStr);
|
|
GSPrintf(stdout, @"%@\n", outStr);
|
|
break;
|
|
case ACTION_CONVERT:
|
|
status = plCmdConvert(result, cmdargs, outpath);
|
|
break;
|
|
case ACTION_REPLACE:
|
|
status = plCmdReplace(result, aFormat, cmdargs, outpath);
|
|
break;
|
|
case ACTION_INSERT:
|
|
status = plCmdInsert(result, aFormat, cmdargs, outpath);
|
|
break;
|
|
case ACTION_REMOVE:
|
|
status = plCmdRemove(result, aFormat, cmdargs, outpath);
|
|
break;
|
|
case ACTION_EXTRACT:
|
|
if (!outpath)
|
|
outpath = @"-";
|
|
status = plCmdExtract(result, cmdargs, outpath);
|
|
break;
|
|
}
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
NSLog(@"Problem: %@", localException);
|
|
if ([[localException name] isEqual: NSInvalidArgumentException])
|
|
print_help(stderr);
|
|
status = EXIT_FAILURE;
|
|
break;
|
|
}
|
|
NS_ENDHANDLER
|
|
|
|
LEAVE_POOL
|
|
} while (0);
|
|
return status;
|
|
}
|