| #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", |
| " -p, --show-plist display the embedded Info.plist values", |
| " -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, \n" |
| " tnetstring, otnetstring)", |
| 0 |
| }; |
| |
| static void default_args (struct cli_info* args_info) |
| { |
| 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->mark_self_flag = false; |
| args_info->format_arg = kFSEventWatchOutputFormatOTNetstring; |
| } |
| |
| 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); |
| } |
| |
| static void cli_print_info_dict (const void *key, |
| const void *value, |
| void *context) |
| { |
| CFStringRef entry = CFStringCreateWithFormat(NULL, NULL, |
| CFSTR("%@:\n %@"), key, value); |
| if (entry) { |
| CFShow(entry); |
| CFRelease(entry); |
| } |
| } |
| |
| void cli_show_plist (void) |
| { |
| CFBundleRef mainBundle = CFBundleGetMainBundle(); |
| CFRetain(mainBundle); |
| CFDictionaryRef mainBundleDict = CFBundleGetInfoDictionary(mainBundle); |
| if (mainBundleDict) { |
| CFRetain(mainBundleDict); |
| printf("Embedded Info.plist metadata:\n\n"); |
| CFDictionaryApplyFunction(mainBundleDict, cli_print_info_dict, NULL); |
| CFRelease(mainBundleDict); |
| } |
| CFRelease(mainBundle); |
| printf("\n"); |
| } |
| |
| void cli_print_version (void) |
| { |
| printf("%s %s\n\n", CLI_NAME, CLI_VERSION); |
| #ifdef COMPILED_AT |
| printf("Compiled at: %s\n", COMPILED_AT); |
| #endif |
| #ifdef COMPILER |
| printf("Compiled with: %s\n", COMPILER); |
| #endif |
| #ifdef TARGET_CPU |
| printf("Compiled for: %s\n", TARGET_CPU); |
| #endif |
| printf("\n"); |
| } |
| |
| 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' }, |
| { "show-plist", no_argument, NULL, 'p' }, |
| { "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' }, |
| { "mark-self", no_argument, NULL, 'm' }, |
| { "format", required_argument, NULL, 'f' }, |
| { 0, 0, 0, 0 } |
| }; |
| |
| const char* shortopts = "hVps: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 'm': // mark-self |
| args_info->mark_self_flag = true; |
| break; |
| case 'f': // format |
| if (strcmp(optarg, "classic") == 0) { |
| args_info->format_arg = kFSEventWatchOutputFormatClassic; |
| } else if (strcmp(optarg, "niw") == 0) { |
| args_info->format_arg = kFSEventWatchOutputFormatNIW; |
| } else if (strcmp(optarg, "tnetstring") == 0) { |
| args_info->format_arg = kFSEventWatchOutputFormatTNetstring; |
| } else if (strcmp(optarg, "otnetstring") == 0) { |
| args_info->format_arg = kFSEventWatchOutputFormatOTNetstring; |
| } else { |
| fprintf(stderr, "Unknown output format: %s\n", optarg); |
| exit(EXIT_FAILURE); |
| } |
| break; |
| case 'V': // version |
| cli_print_version(); |
| exit(EXIT_SUCCESS); |
| case 'p': // show-plist |
| cli_show_plist(); |
| exit(EXIT_SUCCESS); |
| case 'h': // help |
| case '?': // invalid option |
| case ':': // missing argument |
| cli_print_help(); |
| exit((c == 'h') ? EXIT_SUCCESS : EXIT_FAILURE); |
| } |
| } |
| |
| if (optind < argc) { |
| int i = 0; |
| args_info->inputs_num = (unsigned int)(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; |
| } |