/* vifm * Copyright (C) 2001 Ken Steen. * Copyright (C) 2007-05-26 Markus Kuhn. * Copyright (C) 2011 xaizek. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "utils.h" #include "utils_int.h" #ifdef _WIN32 #include <windows.h> #include <winioctl.h> #include "utf8.h" #endif #include <sys/types.h> /* pid_t */ #include <unistd.h> #include <assert.h> /* assert() */ #include <ctype.h> /* isalnum() isalpha() iscntrl() */ #include <errno.h> /* EEXIST EINVAL errno */ #include <math.h> /* modf() pow() */ #include <stddef.h> /* size_t */ #include <stdio.h> /* snprintf() */ #include <stdlib.h> /* RAND_MAX free() malloc() qsort() rand() random() srand() srandom() */ #include <string.h> /* memcpy() strdup() strchr() strlen() strpbrk() strtol() */ #include <time.h> /* tm localtime() strftime() */ #include <wchar.h> /* wcwidth() */ #include "../cfg/config.h" #include "../compat/fs_limits.h" #include "../compat/os.h" #include "../compat/reallocarray.h" #include "../engine/keys.h" #include "../engine/variables.h" #include "../int/fuse.h" #include "../modes/dialogs/msg_dialog.h" #include "../ui/cancellation.h" #include "../ui/ui.h" #include "../background.h" #include "../filelist.h" #include "../registers.h" #include "../status.h" #include "env.h" #include "file_streams.h" #include "fs.h" #include "log.h" #include "macros.h" #include "path.h" #include "str.h" #include "string_array.h" #include "utf8.h" /* Prefer random() over rand() because the former is non-linear. */ #if defined(HAVE_RANDOM) && defined(HAVE_SRANDOM) # define USE_POSIX_RANDOM #endif static void show_progress_cb(const void *descr); static const char ** get_size_suffixes(void); static double split_size_double(double d, unsigned long long *ifraction, int *fraction_width); #ifdef _WIN32 static void unquote(char quoted[]); #endif static int is_line_spec(const char str[]); /* Size suffixes of different kinds. */ static const char *iec_i_units[] = { " B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB" }; static const char *iec_units[] = { "B", "K", "M", "G", "T", "P", "E", "Z", "Y" }; static const char *si_units[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; ARRAY_GUARD(iec_units, ARRAY_LEN(iec_i_units)); ARRAY_GUARD(si_units, ARRAY_LEN(iec_units)); int vifm_system(char command[], ShellRequester by) { #ifdef _WIN32 /* The check is primarily for tests, otherwise screen is reset. */ if(curr_stats.load_stage != 0) { system("cls"); } #endif LOG_INFO_MSG("Shell command: %s", command); return run_in_shell_no_cls(command, by); } int vifm_system_input(char command[], FILE *input, ShellRequester by) { #ifdef _WIN32 /* The check is primarily for tests, otherwise screen is reset. */ if(curr_stats.load_stage != 0) { system("cls"); } #endif LOG_INFO_MSG("Shell command with custom input: %s", command); return run_with_input(command, input, by); } int process_cmd_output(const char descr[], const char cmd[], FILE *input, int user_sh, int interactive, cmd_output_handler handler, void *arg) { FILE *file, *err; pid_t pid; int nlines; char **lines; int i; LOG_INFO_MSG("Capturing output of the command: %s", cmd); pid = bg_run_and_capture((char *)cmd, user_sh, input, &file, &err); if(pid == (pid_t)-1) { return 1; } ui_cancellation_push_on(); if(!interactive) { show_progress("", 0); } /* XXX: reading can potentially never end if error pipe gets filled. */ wait_for_data_from(pid, file, 0, &ui_cancellation_info); lines = read_stream_lines(file, &nlines, 1, interactive ? NULL : &show_progress_cb, descr); ui_cancellation_pop(); fclose(file); for(i = 0; i < nlines; ++i) { handler(lines[i], arg); } free_string_array(lines, nlines); show_errors_from_file(err, descr); return 0; } /* Callback implementation for read_stream_lines(). */ static void show_progress_cb(const void *descr) { show_progress(descr, -250); } int vifm_chdir(const char path[]) { char curr_path[PATH_MAX + 1]; if(get_cwd(curr_path, sizeof(curr_path)) == curr_path) { if(stroscmp(curr_path, path) == 0) { return 0; } } return os_chdir(path); } char * expand_path(const char path[]) { char *const expanded_envvars = expand_envvars(path, EEF_NONE); /* replace_tilde() frees memory returned by expand_envvars. */ return replace_tilde(expanded_envvars); } char * expand_envvars(const char str[], int flags) { const int escape_vals = (flags & EEF_ESCAPE_VALS); const int keep_escapes = (flags & EEF_KEEP_ESCAPES); const int double_percents = (flags & EEF_DOUBLE_PERCENTS); char *result = NULL; size_t len = 0; int prev_slash = 0; while(*str != '\0') { if(!prev_slash && *str == '$' && isalpha(str[1])) { char var_name[NAME_MAX + 1]; const char *p = str + 1; char *q = var_name; const char *var_value; while((isalnum(*p) || *p == '_') && (size_t)(q - var_name) < sizeof(var_name) - 1) { *q++ = *p++; } *q = '\0'; var_value = local_getenv(var_name); if(!is_null_or_empty(var_value)) { char *escaped_var_value = NULL; char *doubled_percents_var_value = NULL; if(escape_vals) { escaped_var_value = posix_like_escape(var_value, /*type=*/2); var_value = escaped_var_value; } if(double_percents) { doubled_percents_var_value = double_char(var_value, '%'); var_value = doubled_percents_var_value; } result = extend_string(result, var_value, &len); free(escaped_var_value); free(doubled_percents_var_value); str = p; } else { result = extend_string(result, "$", &len); str++; } } else { prev_slash = (*str == '\\') ? !prev_slash : 0; if(!prev_slash || (keep_escapes && (str[1] != '$' || !isalpha(str[2])))) { const char single_char[] = { *str, '\0' }; result = extend_string(result, single_char, &len); } str++; } } if(result == NULL) result = strdup(""); return result; } int friendly_size_notation(uint64_t num, int str_size, char str[]) { unsigned long long fraction = 0ULL; int fraction_width; const char **const units = get_size_suffixes(); size_t u = 0U; double d = num; while(d >= cfg.sizefmt.base - 0.5 && u < ARRAY_LEN(iec_i_units) - 1U) { d /= cfg.sizefmt.base; ++u; } /* Fractional part is ignored when it's absent (we didn't divide anything) and * when we format size in previously used manner and hide it for values * greater than 9. */ if(u != 0U && !(cfg.sizefmt.precision == 0 && d > 9.0)) { d = split_size_double(d, &fraction, &fraction_width); } if(fraction == 0) { snprintf(str, str_size, "%.0f%s%s", d, cfg.sizefmt.space ? " " : "", units[u]); } else { snprintf(str, str_size, "%.0f.%0*" PRINTF_ULL "%s%s", d, fraction_width, fraction, cfg.sizefmt.space ? " " : "", units[u]); } return u > 0U; } /* Picks size suffixes as per configuration. Returns one of *_units arrays. */ static const char ** get_size_suffixes(void) { return (cfg.sizefmt.base == 1000) ? si_units : (cfg.sizefmt.ieci_prefixes ? iec_i_units : iec_units); } /* Breaks size (floating point, not integer) into integer and fractional parts * rounding and truncating them as necessary. Sets *ifraction and * *fraction_width. Returns new value for the size (truncated and/or * rounded). */ static double split_size_double(double size, unsigned long long *ifraction, int *fraction_width) { double ten_power, dfraction, integer; *fraction_width = (cfg.sizefmt.precision == 0) ? 1 : cfg.sizefmt.precision; ten_power = pow(10, *fraction_width + 1); while(ten_power > (double)ULLONG_MAX) { ten_power /= 10; --*fraction_width; } dfraction = modf(size, &integer); *ifraction = DIV_ROUND_UP(ten_power*dfraction, 10); if(*ifraction >= ten_power/10.0) { /* Overflow into integer part during rounding. */ integer += 1.0; *ifraction -= ten_power/10.0; } else if(*ifraction == 0 && dfraction != 0.0) { /* Fractional part is too small, "round up" to make it visible. */ *ifraction = 1; } /* Skip trailing zeroes. */ for(; *fraction_width != 0 && *ifraction%10 == 0; --*fraction_width) { *ifraction /= 10; } return (*ifraction == 0) ? size : integer; } const char * enclose_in_dquotes(const char str[], ShellType shell_type) { static char buf[1 + PATH_MAX*2 + 1 + 1]; char *p; p = buf; *p++ = '"'; for(; *str != '\0'; ++str) { char c = *str; if(c == '"' || (shell_type == ST_POSIX && (c == '\\' || c == '$' || c == '`'))) { *p++ = '\\'; } *p++ = c; } *p++ = '"'; *p = '\0'; return buf; } const char * make_name_unique(const char filename[]) { static char unique[PATH_MAX + 1]; size_t len; int i; #ifndef _WIN32 snprintf(unique, sizeof(unique), "%s_%u%u_00", filename, getppid(), get_pid()); #else /* TODO: think about better name uniqualization on Windows. */ snprintf(unique, sizeof(unique), "%s_%u_00", filename, get_pid()); #endif len = strlen(unique); i = 0; while(path_exists(unique, DEREF)) { sprintf(unique + len - 2, "%d", ++i); } return unique; } char * extract_cmd_name(const char line[], int raw, size_t buf_len, char buf[]) { const char *result; #ifdef _WIN32 int left_quote, right_quote = 0; #endif line = skip_whitespace(line); #ifdef _WIN32 if((left_quote = (line[0] == '"'))) { result = strchr(line + 1, '"'); } else #endif { /* TODO: this should account for escaping (regular, squotes, dquotes). */ result = strchr(line, ' '); } if(result == NULL) { result = line + strlen(line); } #ifdef _WIN32 if(left_quote && (right_quote = (result[0] == '"'))) { result++; } #endif copy_str(buf, MIN((size_t)(result - line + 1), buf_len), line); #ifdef _WIN32 if(!raw && left_quote && right_quote) { unquote(buf); } #endif if(!raw) { fuse_strip_mount_metadata(buf); } result = skip_whitespace(result); return (char *)result; } #ifdef _WIN32 /* Removes first and the last charater of the string, if they are quotes. */ static void unquote(char quoted[]) { size_t len = strlen(quoted); if(len > 2 && quoted[0] == quoted[len - 1] && strpbrk(quoted, "\"'`") != NULL) { memmove(quoted, quoted + 1, len - 2); quoted[len - 2] = '\0'; } } #endif int vifm_wcwidth(wchar_t wc) { const int width = wcwidth(wc); if(width == -1) { return ((size_t)wc < (size_t)L' ') ? 2 : 1; } return width; } int vifm_wcswidth(const wchar_t str[], size_t n) { int width = 0; while(*str != L'\0' && n--) { width += vifm_wcwidth(*str++); } return width; } char * escape_for_squotes(const char string[], size_t offset) { /* TODO: maybe combine code with escape_for_dquotes(). */ size_t len = strlen(string); char *escaped = malloc(len*2 + 1); char *out = escaped; if(escaped == NULL) { return NULL; } /* Copy prefix not escaping it. */ offset = MIN(len, offset); memcpy(out, string, offset); out += offset; string += offset; while(*string != '\0') { if(*string == '\'') { *out++ = '\''; } *out++ = *string++; } *out = '\0'; return escaped; } char * escape_for_dquotes(const char string[], size_t offset) { /* TODO: maybe combine code with escape_for_squotes(). */ size_t len = strlen(string); char *escaped = malloc(len*2 + 1); char *out = escaped; if(escaped == NULL) { return NULL; } /* Copy prefix not escaping it. */ offset = MIN(len, offset); memcpy(out, string, offset); out += offset; string += offset; while(*string != '\0') { switch(*string) { case '\a': *out++ = '\\'; *out++ = 'a'; break; case '\b': *out++ = '\\'; *out++ = 'b'; break; case '\f': *out++ = '\\'; *out++ = 'f'; break; case '\n': *out++ = '\\'; *out++ = 'n'; break; case '\r': *out++ = '\\'; *out++ = 'r'; break; case '\t': *out++ = '\\'; *out++ = 't'; break; case '\v': *out++ = '\\'; *out++ = 'v'; break; case '"': *out++ = '\\'; *out++ = '"'; break; default: *out++ = *string; break; } ++string; } *out = '\0'; return escaped; } char * escape_unreadable(const char str[]) { int str_len = strlen(str); char *escaped = malloc(str_len*2 + 1); if(escaped == NULL) { return NULL; } char *out = escaped; while(str_len > 0) { int char_len; wchar_t ucs = utf8_first_char(str, &char_len); if(unichar_isprint(ucs)) { memcpy(out, str, char_len); out += char_len; } else { *out++ = '^'; if(char_len == 1 && iscntrl(*str)) { *out++ = *str ^ 64; } else { *out++ = '?'; } } str += char_len; str_len -= char_len; } *out = '\0'; return escaped; } int escape_unreadableo(const char str[], int prefix_len) { int overhead = 0; const char *in = str; while(prefix_len > 0) { int char_len; wchar_t ucs = utf8_first_char(in, &char_len); if(!unichar_isprint(ucs)) { overhead += 2 - char_len; } in += char_len; prefix_len -= char_len; } return overhead; } wchar_t * escape_unreadablew(const wchar_t str[]) { int str_len = wcslen(str); wchar_t *escaped = reallocarray(NULL, str_len*2 + 1, sizeof(*escaped)); wchar_t *out = escaped; while(str_len > 0) { if(unichar_isprint(*str)) { *out++ = *str; } else { *out++ = L'^'; if(*str <= (wchar_t)0xff && iscntrl(*str)) { *out++ = *str ^ 64; } else { *out++ = L'?'; } } ++str; --str_len; } *out = '\0'; return escaped; } int unichar_isprint(wchar_t ucs) { /* Sorted list of non-overlapping intervals of non-spacing characters * generated by "uniset +cat=Cc +cat=Cf c". */ static const interval_t non_printing[] = { { 0x0000, 0x001F }, { 0x007F, 0x009F }, { 0x00AD, 0x00AD }, { 0x0600, 0x0605 }, { 0x061C, 0x061C }, { 0x06DD, 0x06DD }, { 0x070F, 0x070F }, { 0x08E2, 0x08E2 }, { 0x180E, 0x180E }, { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2064 }, { 0x2066, 0x206F }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x110BD, 0x110BD }, { 0x1BCA0, 0x1BCA3 }, { 0x1D173, 0x1D17A }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F } }; return !unichar_bisearch(ucs, non_printing, ARRAY_LEN(non_printing) - 1); } int create_unique_file(char path[], mode_t mode, int auto_delete) { /* Probably way too many, but glibc does this much. */ enum { MAX_ATTEMPTS = 62*62*62 }; if(!ends_with(path, "XXXXXX")) { errno = EINVAL; return -1; } const char *char_set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; char *suffix = &path[strlen(path) - 6]; int attempt; for(attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) { int i; for(i = 0; i < 6; ++i) { suffix[i] = char_set[vifm_rand(0, 61)]; } int fd = create_new_file(path, mode, auto_delete); if(fd != -1 || errno != EEXIST) { return fd; } } /* That's what glibc returns in this case. */ errno = EEXIST; return -1; } void expand_percent_escaping(char s[]) { char *p; p = s; while(s[0] != '\0') { if(s[0] == '%' && s[1] == '%') { ++s; } *p++ = *s++; } *p = '\0'; } void expand_squotes_escaping(char s[]) { char *p; int sq_found; p = s++; sq_found = *p == '\''; while(*p != '\0') { if(*s == '\'' && sq_found) { sq_found = 0; } else { *++p = *s; sq_found = *s == '\''; } s++; } } void expand_dquotes_escaping(char s[]) { static const char table[] = /* 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f */ /* 00 */ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" /* 10 */ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" /* 20 */ "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" /* 30 */ "\x00\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" /* 40 */ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" /* 50 */ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" /* 60 */ "\x60\x07\x0b\x63\x64\x65\x0c\x67\x68\x69\x6a\x6b\x6c\x6d\x0a\x6f" /* 70 */ "\x70\x71\x0d\x73\x09\x75\x0b\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" /* 80 */ "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" /* 90 */ "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" /* a0 */ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" /* b0 */ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" /* c0 */ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" /* d0 */ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" /* e0 */ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" /* f0 */ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; char *str = s; char *p; p = s; while(*s != '\0') { if(*s != '\\') { *p++ = *s++; continue; } s++; if(*s == '\0') { LOG_ERROR_MSG("Escaped eol in \"%s\"", str); break; } *p++ = table[(int)*s++]; } *p = '\0'; } int def_reg(int reg) { return (reg == NO_REG_GIVEN) ? DEFAULT_REG_NAME : reg; } int def_count(int count) { return (count == NO_COUNT_GIVEN) ? 1 : count; } char * parse_line_for_path(const char line[], const char cwd[]) { int line_num; /* Skip empty lines. */ return (skip_whitespace(line)[0] == '\0') ? NULL : parse_file_spec(line, &line_num, cwd); } char * parse_file_spec(const char spec[], int *line_num, const char cwd[]) { #ifdef _WIN32 enum { MIN_COLON_OFFSET = 2 }; #else enum { MIN_COLON_OFFSET = 0 }; #endif char *path_buf; const char *colon; const size_t bufs_len = strlen(cwd) + 1U + strlen(spec) + 1U + 1U; char canonicalized[PATH_MAX + 1]; path_buf = malloc(bufs_len); if(path_buf == NULL) { return NULL; } if(is_path_absolute(spec) || spec[0] == '~') { path_buf[0] = '\0'; } else { snprintf(path_buf, bufs_len, "%s/", cwd); } #ifdef _WIN32 colon = strchr(spec + (is_path_absolute(spec) ? MIN_COLON_OFFSET : 0), ':'); if(colon != NULL && !is_line_spec(colon + 1)) { colon = NULL; } #else colon = strchr(spec, ':'); while(colon != NULL) { if(is_line_spec(colon + 1)) { char path[bufs_len]; strcpy(path, path_buf); strncat(path, spec, colon - spec); if(path_exists(path, NODEREF)) { break; } } colon = strchr(colon + 1, ':'); } #endif if(colon != NULL) { strncat(path_buf, spec, colon - spec); *line_num = atoi(colon + 1); } else { strcat(path_buf, spec); *line_num = 1; while(!path_exists(path_buf, NODEREF) && strchr(path_buf + MIN_COLON_OFFSET, ':') != NULL) { break_atr(path_buf, ':'); } } chomp(path_buf); system_to_internal_slashes(path_buf); canonicalize_path(path_buf, canonicalized, sizeof(canonicalized)); if(!ends_with_slash(path_buf) && !is_root_dir(canonicalized) && strcmp(canonicalized, "./") != 0) { chosp(canonicalized); } free(path_buf); return replace_tilde(strdup(canonicalized)); } /* Checks whether str points to a valid line number. Returns non-zero if so, * otherwise zero is returned. */ static int is_line_spec(const char str[]) { char *endptr; errno = 0; (void)strtol(str, &endptr, 10); return (endptr != str && errno == 0 && *endptr == ':'); } void safe_qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) { if(nmemb != 0U) { /* Even when there are no entries, qsort() shouldn't be called with a NULL * parameter. It isn't allowed by the standard. */ qsort(base, nmemb, size, compar); } } void format_position(char buf[], size_t buf_len, int top, int total, int visible) { if(total <= visible) { copy_str(buf, buf_len, "All"); } else if(top == 0) { copy_str(buf, buf_len, "Top"); } else if(total - top <= visible) { copy_str(buf, buf_len, "Bot"); } else { snprintf(buf, buf_len, "%2d%%", (top*100)/(total - visible)); } } FILE * make_in_file(view_t *view, MacroFlags flags) { if(ma_flags_missing(flags, MF_PIPE_FILE_LIST) && ma_flags_missing(flags, MF_PIPE_FILE_LIST_Z)) { return NULL; } FILE *input_tmp = os_tmpfile(); const int null_sep = ma_flags_present(flags, MF_PIPE_FILE_LIST_Z); write_marked_paths(input_tmp, view, null_sep); return input_tmp; } void write_marked_paths(FILE *file, view_t *view, int null_sep) { const char separator = (null_sep ? '\0' : '\n'); dir_entry_t *entry = NULL; while(iter_marked_entries(view, &entry)) { const char *const sep = (ends_with_slash(entry->origin) ? "" : "/"); fprintf(file, "%s%s%s%c", entry->origin, sep, entry->name, separator); } } void format_iso_time(time_t t, char buf[], size_t buf_size) { struct tm *const tm = localtime(&t); if(tm == NULL) { copy_str(buf, buf_size, ""); return; } strftime(buf, buf_size, "%a, %d %b %Y %H:%M:%S", tm); } int unichar_bisearch(wchar_t ucs, const interval_t table[], int max) { if(ucs < table[0].first || ucs > table[max].last) { return 0; } int min = 0; while(min <= max) { const int mid = min + (max - min)/2; if(ucs > table[mid].last) { min = mid + 1; } else if(ucs < table[mid].first) { max = mid - 1; } else { return 1; } } return 0; } char * posix_like_escape(const char string[], int type) { size_t len; size_t i; char *ret, *dup; len = strlen(string); dup = ret = malloc(len*3 + 2 + 1); if(dup == NULL) { return NULL; } if(*string == '-') { *dup++ = '.'; *dup++ = '/'; } for(i = 0; i < len; i++, string++, dup++) { switch(*string) { case '%': if(type == 1) { *dup++ = '%'; } break; /* Escape the following characters anywhere in the line. */ case '\'': case '\\': case '\r': case '\t': case '"': case ';': case ' ': case '?': case '|': case '[': case ']': case '{': case '}': case '<': case '>': case '`': case '!': case '$': case '&': case '*': case '(': case ')': case '#': *dup++ = '\\'; break; case '\n': if(type != 0) { break; } *dup++ = '"'; *dup++ = '\n'; *dup = '"'; continue; /* Escape the following characters only at the beginning of the line. */ case '~': case '=': /* Command-path expansion in zsh. */ if(dup == ret) *dup++ = '\\'; break; } *dup = *string; } *dup = '\0'; return ret; } void vifm_srand(unsigned int seed) { #ifdef USE_POSIX_RANDOM srandom(seed); #else srand(seed); #endif } int vifm_rand(int min, int max) { assert(min >= 0 && max >= 0 && min <= max && "Invalid vifm_rand() range."); #ifdef USE_POSIX_RANDOM double value = random()/(0x7FFFFFFF + 1.0); #else double value = rand()/(RAND_MAX + 1.0); #endif return min + value*(max - min + 1); } /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set cinoptions+=t0 filetype=c : */