int complete_is_valid_option( const wchar_t *str, const wchar_t *opt, array_list_t *errors ) { complete_entry_t *i; complete_entry_opt_t *o; wchar_t *cmd, *path; int found_match = 0; int authoritative = 1; int opt_found=0; hash_table_t gnu_match_hash; int is_gnu_opt=0; int is_old_opt=0; int is_short_opt=0; int is_gnu_exact=0; int gnu_opt_len=0; char *short_validated; void *context; CHECK( str, 0 ); CHECK( opt, 0 ); /* Check some generic things like -- and - options. */ switch( wcslen(opt ) ) { case 0: case 1: { return 1; } case 2: { if( wcscmp( L"--", opt ) == 0 ) { return 1; } break; } } if( opt[0] != L'-' ) { if( errors ) { al_push( errors, wcsdup(L"Option does not begin with a '-'") ); } return 0; } context = halloc( 0, 0 ); if( !(short_validated = halloc( context, wcslen( opt ) ))) { DIE_MEM(); } memset( short_validated, 0, wcslen( opt ) ); hash_init( &gnu_match_hash, &hash_wcs_func, &hash_wcs_cmp ); is_gnu_opt = opt[1]==L'-'; if( is_gnu_opt ) { wchar_t *opt_end = wcschr(opt, L'=' ); if( opt_end ) { gnu_opt_len = (opt_end-opt)-2; } else { gnu_opt_len = wcslen(opt)-2; } } parse_cmd_string( context, str, &path, &cmd ); /* Make sure completions are loaded for the specified command */ complete_load( cmd, 0 ); for( i=first_entry; i; i=i->next ) { wchar_t *match = i->cmd_type?path:cmd; const wchar_t *a; if( !wildcard_match( match, i->cmd ) ) { continue; } found_match = 1; if( !i->authoritative ) { authoritative = 0; break; } if( is_gnu_opt ) { for( o = i->first_option; o; o=o->next ) { if( o->old_mode ) { continue; } if( wcsncmp( &opt[2], o->long_opt, gnu_opt_len )==0) { hash_put( &gnu_match_hash, o->long_opt, L"" ); if( (wcsncmp( &opt[2], o->long_opt, wcslen( o->long_opt) )==0) ) { is_gnu_exact=1; } } } } else { /* Check for old style options */ for( o = i->first_option; o; o=o->next ) { if( !o->old_mode ) continue; if( wcscmp( &opt[1], o->long_opt )==0) { opt_found = 1; is_old_opt = 1; break; } } if( is_old_opt ) break; for( a = &opt[1]; *a; a++ ) { wchar_t *str_pos = wcschr(i->short_opt_str, *a); if (str_pos ) { if( *(str_pos +1)==L':' ) { /* This is a short option with an embedded argument, call complete_is_valid_argument on the argument. */ wchar_t nopt[3]; nopt[0]=L'-'; nopt[1]=opt[1]; nopt[2]=L'\0'; short_validated[a-opt] = complete_is_valid_argument( str, nopt, &opt[2]); } else { short_validated[a-opt]=1; } } } } } if( authoritative ) { if( !is_gnu_opt && !is_old_opt ) is_short_opt = 1; if( is_short_opt ) { int j; opt_found=1; for( j=1; j<wcslen(opt); j++) { if ( !short_validated[j]) { if( errors ) { wchar_t str[2]; str[0] = opt[j]; str[1]=0; al_push( errors, wcsdupcat(_( L"Unknown option: " ), L"'", str, L"'" ) ); } opt_found = 0; break; } } } if( is_gnu_opt ) { opt_found = is_gnu_exact || (hash_get_count( &gnu_match_hash )==1); if( errors && !opt_found ) { if( hash_get_count( &gnu_match_hash )==0) { al_push( errors, wcsdupcat( _(L"Unknown option: "), L"'", opt, L"\'" ) ); } else { al_push( errors, wcsdupcat( _(L"Multiple matches for option: "), L"'", opt, L"\'" ) ); } } } } hash_destroy( &gnu_match_hash ); halloc_free( context ); return (authoritative && found_match)?opt_found:1; }
int main(int argc, char **argv) { save_argv0(argv[0]); struct cyrbu_cmd_options options = {0}; enum cyrbu_mode mode = CYRBU_MODE_UNSPECIFIED; enum cyrbu_cmd cmd = CYRBU_CMD_UNSPECIFIED; const char *alt_config = NULL; const char *backup_name = NULL; const char *command = NULL; const char *subcommand = NULL; struct backup *backup = NULL; mbname_t *mbname = NULL; int i, opt, r = 0; while ((opt = getopt(argc, argv, "C:fmuv")) != EOF) { switch (opt) { case 'C': alt_config = optarg; break; case 'f': if (mode != CYRBU_MODE_UNSPECIFIED) usage(); mode = CYRBU_MODE_FILENAME; break; case 'm': if (mode != CYRBU_MODE_UNSPECIFIED) usage(); mode = CYRBU_MODE_MBOXNAME; break; case 'u': if (mode != CYRBU_MODE_UNSPECIFIED) usage(); mode = CYRBU_MODE_USERNAME; break; case 'v': options.verbose++; break; default: usage(); break; } } /* default mode is username */ if (mode == CYRBU_MODE_UNSPECIFIED) mode = CYRBU_MODE_USERNAME; /* get the backup name */ if (optind == argc) usage(); backup_name = argv[optind++]; /* get the command */ if (optind == argc) usage(); command = argv[optind++]; /* get the subcommand */ if (optind == argc) usage(); subcommand = argv[optind++]; /* parse the command and subcommand */ cmd = parse_cmd_string(command, subcommand); /* check remaining arguments based on command */ switch (cmd) { case CYRBU_CMD_LIST_ALL: case CYRBU_CMD_LIST_CHUNKS: case CYRBU_CMD_LIST_MAILBOXES: case CYRBU_CMD_LIST_MESSAGES: /* these want no more arguments */ if (optind != argc) usage(); break; case CYRBU_CMD_SHOW_CHUNKS: case CYRBU_CMD_SHOW_MAILBOXES: case CYRBU_CMD_SHOW_MESSAGES: /* these need at least one more argument */ if (optind == argc) usage(); break; case CYRBU_CMD_DUMP_CHUNK: case CYRBU_CMD_DUMP_MAILBOX: case CYRBU_CMD_DUMP_MESSAGE: /* these need exactly one more argument */ if (argc - optind != 1) usage(); break; default: usage(); break; } /* build a nice args list */ options.argv = strarray_new(); for (i = optind; i < argc; i++) { strarray_add(options.argv, argv[i]); } // FIXME finish parsing options cyrus_init(alt_config, "cyr_backup", 0, 0); /* open backup */ switch (mode) { case CYRBU_MODE_FILENAME: r = backup_open_paths(&backup, backup_name, NULL, BACKUP_OPEN_NONBLOCK, BACKUP_OPEN_NOCREATE); break; case CYRBU_MODE_MBOXNAME: mbname = mbname_from_intname(backup_name); if (!mbname) usage(); r = backup_open(&backup, mbname, BACKUP_OPEN_NONBLOCK, BACKUP_OPEN_NOCREATE); break; case CYRBU_MODE_USERNAME: mbname = mbname_from_userid(backup_name); if (!mbname) usage(); r = backup_open(&backup, mbname, BACKUP_OPEN_NONBLOCK, BACKUP_OPEN_NOCREATE); break; default: usage(); break; } /* run command */ if (!r && cmd_func[cmd]) r = cmd_func[cmd](backup, &options); if (r) fprintf(stderr, "%s: %s\n", backup_name, error_message(r)); /* close backup */ if (backup) backup_close(&backup); /* clean up and exit */ backup_cleanup_staging_path(); cyrus_done(); strarray_free(options.argv); exit(r ? EC_TEMPFAIL : EC_OK); }