diff --git a/fsevent_watch.xcodeproj/project.pbxproj b/fsevent_watch.xcodeproj/project.pbxproj index c085bc3..4703a37 100644 --- a/fsevent_watch.xcodeproj/project.pbxproj +++ b/fsevent_watch.xcodeproj/project.pbxproj @@ -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 = ""; }; + 6A20BF7D13FC9BC000C6C442 /* cli.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cli.h; sourceTree = ""; }; + 6A20BF7E13FC9BC000C6C442 /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; }; 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 = ""; }; @@ -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 */, @@ -117,6 +124,7 @@ buildActionMask = 2147483647; files = ( 6A57F70713F5E614000BE6A9 /* main.c in Sources */, + 6A20BF7F13FC9BC000C6C442 /* cli.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/fsevent_watch/cli.c b/fsevent_watch/cli.c new file mode 100644 index 0000000..5d1de6b --- /dev/null +++ b/fsevent_watch/cli.c @@ -0,0 +1,153 @@ +#include +#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; +} + diff --git a/fsevent_watch/cli.h b/fsevent_watch/cli.h new file mode 100644 index 0000000..25b4dde --- /dev/null +++ b/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 */ diff --git a/fsevent_watch/common.h b/fsevent_watch/common.h new file mode 100644 index 0000000..0cc2366 --- /dev/null +++ b/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 +#include "compat.h" + +#endif diff --git a/fsevent_watch/fsevent_watch.h b/fsevent_watch/fsevent_watch.h index 71bd421..9b8d888 100644 --- a/fsevent_watch/fsevent_watch.h +++ b/fsevent_watch/fsevent_watch.h @@ -8,6 +8,8 @@ #ifndef fsevent_watch_h #define fsevent_watch_h +#include "common.h" + enum FSEventWatchOutputFormat { kFSEventWatchOutputFormatClassic, kFSEventWatchOutputFormatNIW diff --git a/fsevent_watch/main.c b/fsevent_watch/main.c index fe57200..8bdba65 100644 --- a/fsevent_watch/main.c +++ b/fsevent_watch/main.c @@ -4,10 +4,8 @@ // Copyright (c) 2011 Travis Tilley. All rights reserved. // -#include -#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. @@ -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);