/** * Search for config file. * * @return the relative path to config file */ static const char* find_conf_file(void) { # define CONF_FILE_NAME "rhashrc" struct rsh_stat_struct st; char *dir1, *path; #ifndef _WIN32 /* Linux/Unix part */ /* first check for $HOME/.rhashrc file */ if( (dir1 = getenv("HOME")) ) { path = make_path(dir1, ".rhashrc"); if(rsh_stat(path, &st) >= 0) { rsh_vector_add_ptr(opt.mem, path); return (conf_opt.config_file = path); } free(path); } /* then check for global config */ if(rsh_stat( (path = "/etc/" CONF_FILE_NAME), &st) >= 0) { return (conf_opt.config_file = path); } #else /* _WIN32 */ /* first check for the %APPDATA%\RHash\rhashrc config */ if( (dir1 = getenv("APPDATA")) ) { dir1 = make_path(dir1, "RHash"); rsh_vector_add_ptr(opt.mem, path = make_path(dir1, CONF_FILE_NAME)); free(dir1); if(rsh_stat(path, &st) >= 0) { return (conf_opt.config_file = path); } } /* then check for %HOMEDRIVE%%HOMEPATH%\rhashrc */ /* note that %USERPROFILE% is generally not a user home dir */ if( (dir1 = getenv("HOMEDRIVE")) && (path = getenv("HOMEPATH"))) { dir1 = make_path(dir1, path); rsh_vector_add_ptr(opt.mem, path = make_path(dir1, CONF_FILE_NAME)); free(dir1); if(rsh_stat(path, &st) >= 0) { return (conf_opt.config_file = path); } } #endif /* _WIN32 */ return (conf_opt.config_file = NULL); /* config file not found */ }
/** * Expand wildcards in the given filepath and store results into vector. * If no wildcards are found then just the filepath is stored. * Note: only wildcards in the last filename of the path are expanded. * * @param vect the vector to receive wide-strings with file paths * @param filepath the filepath to process */ void expand_wildcards(vector_t* vect, wchar_t* filepath) { int added = 0; size_t len = wcslen(filepath); size_t index = wcscspn(filepath, L"*?"); /* if a wildcard has been found without a directory separator after it */ if(index < len && wcscspn(filepath + index, L"/\\") >= (len - index)) { wchar_t* parent; WIN32_FIND_DATAW d; HANDLE h; /* find directory separator */ for(; index > 0 && !IS_PATH_SEPARATOR(filepath[index]); index--); parent = (IS_PATH_SEPARATOR(filepath[index]) ? filepath : 0); h = FindFirstFileW(filepath, &d); if(INVALID_HANDLE_VALUE != h) { do { wchar_t* wpath; char* cstr; int failed; if((0 == wcscmp(d.cFileName, L".")) || (0 == wcscmp(d.cFileName, L".."))) continue; if(NULL == (wpath = make_pathw(parent, index + 1, d.cFileName))) continue; cstr = wchar_to_cstr(wpath, WIN_DEFAULT_ENCODING, &failed); /* note: just quietly skip unconvertible file names */ if(!cstr || failed) { free(cstr); free(wpath); continue; } rsh_vector_add_ptr(vect, cstr); added++; } while(FindNextFileW(h, &d)); FindClose(h); } } if(added == 0) { wchar_t* wpath = make_pathw(0, 0, filepath); char* cstr = w2c(wpath); if(cstr) rsh_vector_add_ptr(vect, cstr); } }
/** * Find long option info, by it's name and retrieve its parameter if required. * Error is reported for unknown options. * * @param option structure to receive the parsed option info * @param parg pointer to a command line argument */ static void parse_long_option(parsed_option_t* option, rsh_tchar ***parg) { size_t length; rsh_tchar* eq_sign; cmdline_opt_t *t; char* name; #ifdef _WIN32 rsh_tchar* wname = **parg; /* "--<option name>" */ int fail = 0; assert((**parg)[0] == L'-' && (**parg)[1] == L'-'); /* search for the '=' sign */ length = ((eq_sign = wcschr(wname, L'=')) ? (size_t)(eq_sign - wname) : wcslen(wname)); option->name = name = (char*)rsh_malloc(length + 1); rsh_vector_add_ptr(opt.mem, name); if(length < 30) { size_t i = 0; for(; i < length; i++) { if(((unsigned)wname[i]) <= 128) name[i] = (char)wname[i]; else { fail = 1; break; } } name[i] = '\0'; name += 2; /* skip "--" */ length -= 2; } else fail = 1; if(fail) fail_on_unknow_option(w2c(**parg)); #else option->name = **parg; name = **parg + 2; /* skip "--" */ length = ((eq_sign = strchr(name, '=')) ? (size_t)(eq_sign - name) : strlen(name)); name[length] = '\0'; #endif /* search for the option by its name */ for(t = cmdline_opt; t->type && (strncmp(name, t->long_name, length) != 0 || strlen(t->long_name) != length); t++) { } if(!t->type) { fail_on_unknow_option(option->name); /* report error and exit */ } option->o = t; /* store the option found */ if(is_param_required(t->type)) { /* store parameter without a code page conversion */ option->parameter = (eq_sign ? eq_sign + 1 : *(++(*parg))); } }
/** * Process given command line option * * @param opts the structure to store results of option processing * @param option option to process */ static void apply_option(options_t *opts, parsed_option_t* option) { cmdline_opt_t* o = option->o; unsigned short option_type = o->type; char* value = NULL; /* check if option requires a parameter */ if(is_param_required(option_type)) { if(!option->parameter) { log_error(_("argument is required for option %s\n"), option->name); rsh_exit(2); } #ifdef _WIN32 /* convert from UTF-16 if not a filepath */ if(option_type != F_OPTH) { value = w2c((wchar_t*)option->parameter); rsh_vector_add_ptr(opt.mem, value); } else #endif { value = (char*)option->parameter; } } /* process option, choosing the method by type */ switch(option_type) { case F_UFLG: case F_UENC: *(unsigned*)((char*)opts + ((char*)o->ptr - (char*)&opt)) |= o->param; break; case F_CSTR: case F_OPTH: /* save the option parameter */ *(char**)((char*)opts + ((char*)o->ptr - (char*)&opt)) = value; break; case F_PFNC: /* call option parameter handler */ ( ( void(*)(options_t *, char*, unsigned) )o->ptr )(opts, value, o->param); break; case F_VFNC: ( ( void(*)(options_t *) )o->ptr )(opts); /* call option handler */ break; case F_PRNT: log_msg("%s", (char*)o->ptr); rsh_exit(0); break; default: assert(0); /* impossible option type */ } }
/** * Convert the given string to lower-case then put it into * the specified array of 'file masks'. * * @param arr array of file masks * @param mask a string to add */ static void file_mask_add(file_mask_array* vect, const char* mask) { rsh_vector_add_ptr(vect, str_tolower(mask)); }
/** * Parse command line options. * * @param argv program arguments */ void read_options(int argc, char *argv[]) { struct parsed_cmd_line_t cmd_line; #ifdef _WIN32 int i; vector_t *expanded_cnames; #endif memset(&opt, 0, sizeof(opt)); opt.mem = rsh_vector_new_simple(); opt.find_max_depth = -1; /* initialize cmd_line */ memset(&cmd_line, 0, sizeof(cmd_line)); rsh_blocks_vector_init(&cmd_line.options); cmd_line.argv = argv; cmd_line.argc = argc; /* parse command line and apply encoding options */ parse_cmdline_options(&cmd_line); read_config(); #ifdef _WIN32 /* set default encoding if no encoding options were specified, */ /* this should be done here, even if config file was not found. */ if( (opt.flags & OPT_ENCODING) == 0 ) opt.flags |= OPT_UTF8; #endif /* note: encoding and -o/-l options are already applied */ IF_WINDOWS(setup_console()); setup_output(); /* setup program output */ apply_cmdline_options(&cmd_line); /* process the rest of command options */ /* options were processed, so we don't need them anymore */ rsh_blocks_vector_destroy(&cmd_line.options); #ifdef _WIN32 expanded_cnames = rsh_vector_new_simple(); /* convert paths to internal encoding and expand wildcards. */ for(i = 0; i < cmd_line.n_files; i++) { wchar_t* path = cmd_line.files[i]; wchar_t* p = wcschr(path, L'\0') - 1; /* strip trailing '\','/' symbols (if not preceded by ':') */ for(; p > path && IS_PATH_SEPARATOR_W(*p) && p[-1] != L':'; p--) *p = 0; expand_wildcards(expanded_cnames, path); } opt.cmd_vec = expanded_cnames; opt.files = (char**)expanded_cnames->array; opt.n_files = (int)expanded_cnames->size; free(cmd_line.files); LocalFree(cmd_line.warg); #else opt.files = cmd_line.files; opt.n_files = cmd_line.n_files; rsh_vector_add_ptr(opt.mem, opt.files); #endif make_final_options_checks(); set_default_sums_flags(argv[0]); /* detect default hashes from program name */ }
/** * Parse config file of the program. * * @return 0 on success, -1 on fail */ static int read_config(void) { #define LINE_BUF_SIZE 2048 char buf[LINE_BUF_SIZE]; FILE* fd; parsed_option_t option; int res; /* initialize conf_opt and opt structures */ memset(&conf_opt, 0, sizeof(opt)); conf_opt.find_max_depth = -1; if(!find_conf_file()) return 0; fd = fopen(conf_opt.config_file, "r"); if(!fd) return -1; while(fgets(buf, LINE_BUF_SIZE, fd)) { size_t index; cmdline_opt_t* t; char* line = str_trim(buf); char *name, *value; if(*line == 0 || IS_COMMENT(*line)) continue; /* search for '=' */ index = strcspn(line, "="); if(line[index] == 0) { log_warning(_("%s: can't parse line \"%s\"\n"), conf_opt.config_file, line); continue; } line[index] = 0; name = str_trim(line); for(t = cmdline_opt; t->type; t++) { if(strcmp(name, t->long_name) == 0) { break; } } if(!t->type) { log_warning(_("%s: unknown option \"%s\"\n"), conf_opt.config_file, line); continue; } value = str_trim(line + index + 1); /* process a long option */ if(is_param_required(t->type)) { rsh_vector_add_ptr(opt.mem, (value = rsh_strdup(value)));; } else { /* possible boolean values for a config file variable */ static const char* strings[] = {"1", "on", "yes", 0}; const char** cmp; for(cmp = strings; *cmp && strcmp(value, *cmp); cmp++); if(*cmp == 0) continue; } option.name = name; option.parameter = value; option.o = t; apply_option(&conf_opt, &option); } res = fclose(fd); #ifdef _WIN32 if( (opt.flags & OPT_ENCODING) == 0 ) opt.flags |= (conf_opt.flags & OPT_ENCODING); #endif return (res == 0 ? 0 : -1); }