Skip to content

Commit

Permalink
use a proper commandline interface, complete with a help option that'…
Browse files Browse the repository at this point in the history
…s actually helpful.
  • Loading branch information
ttilley committed Aug 18, 2011
1 parent de04f41 commit 951f4ba
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 37 deletions.
8 changes: 8 additions & 0 deletions fsevent_watch.xcodeproj/project.pbxproj
Expand Up @@ -7,11 +7,15 @@
objects = {

/* Begin PBXBuildFile section */
6A20BF7F13FC9BC000C6C442 /* cli.c in Sources */ = {isa = PBXBuildFile; fileRef = 6A20BF7C13FC9BC000C6C442 /* cli.c */; };
6A57F70413F5E614000BE6A9 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A57F70313F5E614000BE6A9 /* CoreServices.framework */; };
6A57F70713F5E614000BE6A9 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 6A57F70613F5E614000BE6A9 /* main.c */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
6A20BF7C13FC9BC000C6C442 /* cli.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cli.c; sourceTree = "<group>"; };
6A20BF7D13FC9BC000C6C442 /* cli.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cli.h; sourceTree = "<group>"; };
6A20BF7E13FC9BC000C6C442 /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
6A57F6FF13F5E614000BE6A9 /* fsevent_watch */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fsevent_watch; sourceTree = BUILT_PRODUCTS_DIR; };
6A57F70313F5E614000BE6A9 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
6A57F70613F5E614000BE6A9 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
Expand Down Expand Up @@ -59,6 +63,9 @@
6A57F70513F5E614000BE6A9 /* fsevent_watch */ = {
isa = PBXGroup;
children = (
6A20BF7C13FC9BC000C6C442 /* cli.c */,
6A20BF7D13FC9BC000C6C442 /* cli.h */,
6A20BF7E13FC9BC000C6C442 /* common.h */,
6A57F70613F5E614000BE6A9 /* main.c */,
6AD3022F13F8D758007F24E8 /* compat.h */,
6AD3023413F8E825007F24E8 /* fsevent_watch.h */,
Expand Down Expand Up @@ -117,6 +124,7 @@
buildActionMask = 2147483647;
files = (
6A57F70713F5E614000BE6A9 /* main.c in Sources */,
6A20BF7F13FC9BC000C6C442 /* cli.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
153 changes: 153 additions & 0 deletions fsevent_watch/cli.c
@@ -0,0 +1,153 @@
#include <getopt.h>
#include "cli.h"

const char *cli_info_purpose = "A flexible command-line interface for the FSEvents API";
const char *cli_info_usage = "Usage: fsevent_watch [OPTIONS]... [PATHS]...";
const char *cli_info_help[] = {
" -h, --help you're looking at it",
" -V, --version print version number and exit",
" -s, --since-when=EventID fire historical events since ID",
" -l, --latency=seconds latency period (default='0.5')",
" -n, --no-defer enable no-defer latency modifier",
" -r, --watch-root watch for when the root path has changed",
" -i, --ignore-self ignore current process",
" -F, --file-events provide file level event data",
" -f, --format=name output format (classic, niw)",
0
};

static void default_args (struct cli_info *args_info)
{
// args_info->since_when_arg = 0xFFFFFFFFFFFFFFFFULL;
args_info->since_when_arg = kFSEventStreamEventIdSinceNow;
args_info->latency_arg = 0.5;
args_info->no_defer_flag = false;
args_info->watch_root_flag = false;
args_info->ignore_self_flag = false;
args_info->file_events_flag = false;
args_info->format_arg = format_arg_classic;
}

static void cli_parser_release (struct cli_info *args_info)
{
unsigned int i;

for (i=0; i < args_info->inputs_num; ++i)
free(args_info->inputs[i]);

if (args_info->inputs_num)
free(args_info->inputs);

args_info->inputs_num = 0;
}

void cli_parser_init (struct cli_info *args_info)
{
default_args(args_info);

args_info->inputs = 0;
args_info->inputs_num = 0;
}

void cli_parser_free (struct cli_info *args_info)
{
cli_parser_release(args_info);
}

void cli_print_version (void)
{
printf("%s %s\n", CLI_NAME, CLI_VERSION);
}

void cli_print_help (void)
{
cli_print_version();

printf("\n%s\n", cli_info_purpose);
printf("\n%s\n", cli_info_usage);
printf("\n");

int i = 0;
while (cli_info_help[i]) {
printf("%s\n", cli_info_help[i++]);
}
}

int cli_parser (int argc, const char **argv, struct cli_info *args_info)
{
static struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "since-when", required_argument, NULL, 's' },
{ "latency", required_argument, NULL, 'l' },
{ "no-defer", no_argument, NULL, 'n' },
{ "watch-root", no_argument, NULL, 'r' },
{ "ignore-self", no_argument, NULL, 'i' },
{ "file-events", no_argument, NULL, 'F' },
{ "format", required_argument, NULL, 'f' },
{ 0, 0, 0, 0 }
};

const char *shortopts = "hVs:l:nriFf:";

int c = -1;

while ((c = getopt_long(argc, (char * const *)argv, shortopts, longopts, NULL)) != -1)
{
switch(c)
{
case 's': // since-when
args_info->since_when_arg = strtoull(optarg, NULL, 0);
break;
case 'l': // latency
args_info->latency_arg = strtod(optarg, NULL);
break;
case 'n': // no-defer
args_info->no_defer_flag = true;
break;
case 'r': // watch-root
args_info->watch_root_flag = true;
break;
case 'i': // ignore-self
args_info->ignore_self_flag = true;
break;
case 'F': // file-events
args_info->file_events_flag = true;
break;
case 'f': // format
if (strcmp(optarg, "classic") == 0) {
args_info->format_arg = format_arg_classic;
} else if (strcmp(optarg, "niw") == 0) {
args_info->format_arg = format_arg_niw;
} else {
fprintf(stderr, "Unknown output format: %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
case 'V': // version
cli_print_version();
exit(EXIT_SUCCESS);
break;
case 'h': // help
case '?': // invalid option
case ':': // missing argument
cli_print_help();
exit((c == 'h') ? EXIT_SUCCESS : EXIT_FAILURE);
break;
}
}

if (optind < argc)
{
int i = 0;
args_info->inputs_num = argc - optind;
args_info->inputs =
(char **)(malloc ((args_info->inputs_num)*sizeof(char *)));
while (optind < argc)
if (argv[optind++] != argv[0])
args_info->inputs[i++] = strdup(argv[optind-1]);
}

return EXIT_SUCCESS;
}

46 changes: 46 additions & 0 deletions fsevent_watch/cli.h
@@ -0,0 +1,46 @@
//
// cli.h
// fsevent_watch
//
// Copyright (c) 2011 Travis Tilley. All rights reserved.
//

#ifndef CLI_H
#define CLI_H

#define CLI_NAME "fsevent_watch"
#define CLI_VERSION "0.0.1"

#include "common.h"

enum output_format {
format_arg_classic = 0,
format_arg_niw
};

struct cli_info {
UInt64 since_when_arg;
double latency_arg;
bool no_defer_flag;
bool watch_root_flag;
bool ignore_self_flag;
bool file_events_flag;
enum output_format format_arg;

char **inputs;
unsigned inputs_num;
};

extern const char *cli_info_purpose;
extern const char *cli_info_usage;
extern const char *cli_info_help[];

void cli_print_help(void);
void cli_print_version(void);

int cli_parser (int argc, const char **argv, struct cli_info *args_info);
void cli_parser_init (struct cli_info *args_info);
void cli_parser_free (struct cli_info *args_info);


#endif /* CLI_H */
14 changes: 14 additions & 0 deletions fsevent_watch/common.h
@@ -0,0 +1,14 @@
//
// common.h
// fsevent_watch
//
// Copyright (c) 2011 Travis Tilley. All rights reserved.
//

#ifndef fsevent_watch_common_h
#define fsevent_watch_common_h

#include <CoreServices/CoreServices.h>
#include "compat.h"

#endif
2 changes: 2 additions & 0 deletions fsevent_watch/fsevent_watch.h
Expand Up @@ -8,6 +8,8 @@
#ifndef fsevent_watch_h
#define fsevent_watch_h

#include "common.h"

enum FSEventWatchOutputFormat {
kFSEventWatchOutputFormatClassic,
kFSEventWatchOutputFormatNIW
Expand Down
80 changes: 43 additions & 37 deletions fsevent_watch/main.c
Expand Up @@ -4,10 +4,8 @@
// Copyright (c) 2011 Travis Tilley. All rights reserved.
//

#include <CoreServices/CoreServices.h>
#include "compat.h"
#include "fsevent_watch.h"

#include "cli.h"

// Resolve a path and append it to the CLI settings structure
// The FSEvents API will, internally, resolve paths using a similar scheme.
Expand Down Expand Up @@ -74,52 +72,60 @@ static inline void parse_cli_settings(int argc, const char *argv[])
fprintf(stderr, "The FSEvents API is unavailable on this version of macos\n");
exit(EXIT_FAILURE);
}

struct cli_info args_info;
cli_parser_init(&args_info);

if (cli_parser(argc, argv, &args_info) != 0) {
exit(EXIT_FAILURE);
}

config.paths = CFArrayCreateMutable(NULL,
(CFIndex)0,
&kCFTypeArrayCallBacks);

for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--since-when") == 0) {
config.sinceWhen = strtoull(argv[++i], NULL, 0);
} else if (strcmp(argv[i], "--latency") == 0) {
config.latency = strtod(argv[++i], NULL);
} else if (strcmp(argv[i], "--no-defer") == 0) {
config.flags |= kFSEventStreamCreateFlagNoDefer;
} else if (strcmp(argv[i], "--watch-root") == 0) {
config.flags |= kFSEventStreamCreateFlagWatchRoot;
} else if (strcmp(argv[i], "--ignore-self") == 0) {
if ((osMajorVersion == 10) & (osMinorVersion >= 6)) {
config.flags |= kFSEventStreamCreateFlagIgnoreSelf;
} else {
fprintf(stderr, "MacOSX 10.6 or later is required for --ignore-self\n");
exit(EXIT_FAILURE);
}
} else if (strcmp(argv[i], "--file-events") == 0) {
if ((osMajorVersion == 10) & (osMinorVersion >= 7)) {
config.flags |= kFSEventStreamCreateFlagFileEvents;
} else {
fprintf(stderr, "MacOSX 10.7 or later required for --file-events\n");
exit(EXIT_FAILURE);
}
} else if (strcmp(argv[i], "--format") == 0) {
const char *format = argv[++i];
if (strcmp(format, "classic") == 0) {
config.format = kFSEventWatchOutputFormatClassic;
} else if (strcmp(format, "niw") == 0) {
config.format = kFSEventWatchOutputFormatNIW;
} else {
fprintf(stderr, "Unknown format %s, falling back to default\n", format);
}
config.sinceWhen = args_info.since_when_arg;
config.latency = args_info.latency_arg;

if (args_info.no_defer_flag)
config.flags |= kFSEventStreamCreateFlagNoDefer;
if (args_info.watch_root_flag)
config.flags |= kFSEventStreamCreateFlagWatchRoot;

if (args_info.ignore_self_flag) {
if ((osMajorVersion == 10) & (osMinorVersion >= 6)) {
config.flags |= kFSEventStreamCreateFlagIgnoreSelf;
} else {
append_path(argv[i]);
fprintf(stderr, "MacOSX 10.6 or later is required for --ignore-self\n");
exit(EXIT_FAILURE);
}
}

if (CFArrayGetCount(config.paths) == 0) {
if (args_info.file_events_flag) {
if ((osMajorVersion == 10) & (osMinorVersion >= 7)) {
config.flags |= kFSEventStreamCreateFlagFileEvents;
} else {
fprintf(stderr, "MacOSX 10.7 or later required for --file-events\n");
exit(EXIT_FAILURE);
}
}

if (args_info.format_arg == format_arg_classic) {
config.format = kFSEventWatchOutputFormatClassic;
} else if (args_info.format_arg == format_arg_niw) {
config.format = kFSEventWatchOutputFormatNIW;
}

if (args_info.inputs_num == 0) {
append_path(".");
} else {
for (unsigned int i=0; i < args_info.inputs_num; ++i) {
append_path(args_info.inputs[i]);
}
}

cli_parser_free(&args_info);

#ifdef DEBUG
fprintf(stderr, "config.sinceWhen %llu\n", config.sinceWhen);
fprintf(stderr, "config.latency %f\n", config.latency);
Expand Down

0 comments on commit 951f4ba

Please sign in to comment.