diff --git a/src/builtin.cpp b/src/builtin.cpp index 2e2d35a1c..9bf331971 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -20,9 +20,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -394,7 +392,7 @@ static int builtin_bind_add(const wchar_t *seq, const wchar_t *const *cmds, size /// if specified, _all_ key bindings will be erased /// @param mode /// if specified, only bindings from that mode will be erased. If not given -/// and all is false, @c DEFAULT_BIND_MODE will be used. +/// and @c all is @c false, @c DEFAULT_BIND_MODE will be used. /// @param use_terminfo /// Whether to look use terminfo -k name /// @@ -408,19 +406,23 @@ static int builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int u input_mapping_erase(it->seq, it->mode); } } - return STATUS_BUILTIN_OK; + + return 0; } + int res = 0; + if (mode == NULL) mode = DEFAULT_BIND_MODE; + while (*seq) { if (use_terminfo) { wcstring seq2; if (get_terminfo_sequence(*seq++, &seq2, streams)) { - input_mapping_erase(seq2, mode != NULL ? mode : DEFAULT_BIND_MODE); + input_mapping_erase(seq2, mode); } else { - res = STATUS_BUILTIN_ERROR; + res = 1; } } else { - input_mapping_erase(*seq++, mode != NULL ? mode : DEFAULT_BIND_MODE); + input_mapping_erase(*seq++, mode); } } @@ -434,12 +436,12 @@ static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) int argc = builtin_count_args(argv); int mode = BIND_INSERT; int res = STATUS_BUILTIN_OK; - bool all = false; + int all = 0; const wchar_t *bind_mode = DEFAULT_BIND_MODE; bool bind_mode_given = false; const wchar_t *sets_bind_mode = DEFAULT_BIND_MODE; bool sets_bind_mode_given = false; - bool use_terminfo = false; + int use_terminfo = 0; w.woptind = 0; @@ -468,7 +470,7 @@ static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) return STATUS_BUILTIN_ERROR; } case 'a': { - all = true; + all = 1; break; } case 'e': { @@ -480,7 +482,7 @@ static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) return STATUS_BUILTIN_OK; } case 'k': { - use_terminfo = true; + use_terminfo = 1; break; } case 'K': { @@ -931,7 +933,7 @@ static wcstring functions_def(const wcstring &name) { case EVENT_EXIT: { if (next->param1.pid > 0) append_format(out, L" --on-process-exit %d", next->param1.pid); - else if (next->param1.pid < 0) + else append_format(out, L" --on-job-exit %d", -next->param1.pid); break; } @@ -1251,7 +1253,7 @@ static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *cons bool success = false; unsigned int start = 0; // the first character of the numeric part of the sequence - unsigned short base = 0, max_digits = 0; + unsigned int base = 0, max_digits = 0; if (builtin_echo_digit(str[0], 8) != UINT_MAX) { // Octal escape base = 8; @@ -1270,7 +1272,7 @@ static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *cons if (base != 0) { unsigned int idx; - unsigned char val = '\0'; // resulting character + unsigned char val = 0; // resulting character for (idx = start; idx < start + max_digits; idx++) { unsigned int digit = builtin_echo_digit(str[idx], base); if (digit == UINT_MAX) break; @@ -1556,6 +1558,7 @@ int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_lis } case 'j': case 'p': { + pid_t pid; wchar_t *end; event_t e(EVENT_ANY); @@ -1582,17 +1585,18 @@ int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_lis append_format(*out_err, _(L"%ls: Cannot find calling job for event handler"), argv[0]); - res = STATUS_BUILTIN_ERROR; + res = 1; } else { e.type = EVENT_JOB_ID; e.param1.job_id = job_id; } } else { - pid_t pid = wcstoimax(w.woptarg, &end, 10); - if (pid < 1 || !(*w.woptarg != L'\0' && *end == L'\0')) { - append_format(*out_err, _(L"%ls: Invalid process id '%ls'"), argv[0], + errno = 0; + pid = fish_wcstoi(w.woptarg, &end, 10); + if (errno || !end || *end) { + append_format(*out_err, _(L"%ls: Invalid process id %ls"), argv[0], w.woptarg); - res = STATUS_BUILTIN_ERROR; + res = 1; break; } @@ -1640,7 +1644,7 @@ int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_lis } case '?': { builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); - res = STATUS_BUILTIN_ERROR; + res = 1; break; } } @@ -1730,7 +1734,7 @@ int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_lis /// The random builtin generates random numbers. static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv) { - static bool seeded = false; + static int seeded = 0; static struct drand48_data seed_buffer; int argc = builtin_count_args(argv); @@ -1768,7 +1772,7 @@ static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **arg case 0: { long res; if (!seeded) { - seeded = true; + seeded = 1; srand48_r(time(0), &seed_buffer); } lrand48_r(&seed_buffer, &res); @@ -1776,16 +1780,19 @@ static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **arg break; } case 1: { + long foo; wchar_t *end = 0; - long seedval = wcstol(argv[w.woptind], &end, 10); - if (!(*argv[w.woptind] != L'\0' && *end == L'\0')) { + + errno = 0; + foo = wcstol(argv[w.woptind], &end, 10); + if (errno || *end) { streams.err.append_format(_(L"%ls: Seed value '%ls' is not a valid number\n"), argv[0], argv[w.woptind]); return STATUS_BUILTIN_ERROR; } - seeded = true; - srand48_r(seedval, &seed_buffer); + seeded = 1; + srand48_r(foo, &seed_buffer); break; } default: { @@ -1811,8 +1818,8 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) const wchar_t *mode_name = READ_MODE_NAME; int nchars = 0; wchar_t *end; - bool shell = false; - bool array = false; + int shell = 0; + int array = 0; bool split_null = false; while (1) { @@ -1882,9 +1889,9 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) break; } case L'n': { - nchars = wcstoimax(w.woptarg, &end, 10); - if (!(*w.woptarg != L'\0' && *end == L'\0')) { - // MUST be an error + errno = 0; + nchars = fish_wcstoi(w.woptarg, &end, 10); + if (errno || *end != 0) { switch (errno) { case ERANGE: { streams.err.append_format(_(L"%ls: Argument '%ls' is out of range\n"), @@ -1903,11 +1910,11 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) break; } case 's': { - shell = true; + shell = 1; break; } case 'a': { - array = true; + array = 1; break; } case L'z': { @@ -2303,8 +2310,9 @@ static int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv) } case 2: { wchar_t *end; + errno = 0; ec = wcstol(argv[1], &end, 10); - if (!(*argv[1] != L'\0' && *end == L'\0')) { + if (errno || *end != 0) { streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"), argv[0], argv[1]); builtin_print_help(parser, streams, argv[0], streams.err); @@ -2369,8 +2377,9 @@ static int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { res = 1; } else if (wchdir(dir) != 0) { struct stat buffer; - int status = wstat(dir, &buffer); + int status; + status = wstat(dir, &buffer); if (!status && S_ISDIR(buffer.st_mode)) { streams.err.append_format(_(L"%ls: Permission denied: '%ls'\n"), argv[0], dir.c_str()); @@ -2551,12 +2560,14 @@ static int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // try to locate the job argv[1], since we want to know if this is an ambigous job // specification or if this is an malformed job id. wchar_t *endptr; - bool found_job = false; + int pid; + int found_job = 0; - pid_t pid = wcstoimax(argv[1], &endptr, 10); - if (pid >= 1 || (*argv[1] != L'\0' && *endptr == L'\0')) { + errno = 0; + pid = fish_wcstoi(argv[1], &endptr, 10); + if (!(*endptr || errno)) { j = job_get_from_pid(pid); - if (j) found_job = true; + if (j) found_job = 1; } if (found_job) { @@ -2567,25 +2578,27 @@ static int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { builtin_print_help(parser, streams, argv[0], streams.err); - return STATUS_BUILTIN_ERROR; + j = 0; } else { wchar_t *end; - pid_t pid = wcstoimax(argv[1], &end, 10); - if (pid < 1 || !(*argv[1] != L'\0' && *end == L'\0')) { + int pid; + errno = 0; + pid = abs(fish_wcstoi(argv[1], &end, 10)); + + if (*end || errno) { streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, argv[0], argv[1]); builtin_print_help(parser, streams, argv[0], streams.err); } else { j = job_get_from_pid(pid); if (!j || !job_get_flag(j, JOB_CONSTRUCTED) || job_is_completed(j)) { streams.err.append_format(_(L"%ls: No suitable job: %d\n"), argv[0], pid); - return STATUS_BUILTIN_ERROR; - } - if (!job_get_flag(j, JOB_CONTROL)) { + j = 0; + } else if (!job_get_flag(j, JOB_CONTROL)) { streams.err.append_format(_(L"%ls: Can't put job %d, '%ls' to foreground because " L"it is not under job control\n"), argv[0], pid, j->command_wcstr()); - return STATUS_BUILTIN_ERROR; + j = 0; } } } @@ -2654,9 +2667,13 @@ static int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } } else { wchar_t *end; - for (int i = 1; argv[i]; i++) { - pid_t pid = wcstoimax(argv[i], &end, 10); - if (pid < 1 || !(*argv[i] != L'\0' && *end == L'\0') || !job_get_from_pid(pid)) { + int i; + int pid; + + for (i = 1; argv[i]; i++) { + errno = 0; + pid = fish_wcstoi(argv[i], &end, 10); + if (errno || pid < 0 || *end || !job_get_from_pid(pid)) { streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[i]); return STATUS_BUILTIN_ERROR; } @@ -2729,8 +2746,9 @@ static int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **arg } case 2: { wchar_t *end; - status = wcstoimax(argv[1], &end, 10); - if (!(*argv[1] != L'\0' && *end == L'\0')) { + errno = 0; + status = fish_wcstoi(argv[1], &end, 10); + if (errno || *end != 0) { streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"), argv[0], argv[1]); builtin_print_help(parser, streams, argv[0], streams.err); diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index 2c46d3ae1..72765cb82 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -1,8 +1,6 @@ // Functions for executing the jobs builtin. #include "config.h" // IWYU pragma: keep -#include -#include #include #ifdef HAVE__PROC_SELF_STAT #include @@ -177,10 +175,14 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } else { if (w.woptind < argc) { - for (int i = w.woptind; i < argc; i++) { + int i; + + for (i = w.woptind; i < argc; i++) { + int pid; wchar_t *end; - pid_t pid = wcstoimax(argv[i], &end, 10); - if (!(*argv[i] != L'\0' && *end == L'\0')) { + errno = 0; + pid = fish_wcstoi(argv[i], &end, 10); + if (errno || *end) { streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[i]); return 1; } diff --git a/src/expand.cpp b/src/expand.cpp index 2dadff21e..1b80fd334 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -2,8 +2,6 @@ // IWYU pragma: no_include #include "config.h" -#include -#include #include #include #include @@ -17,6 +15,7 @@ #ifdef HAVE_SYS_SYSCTL_H #include // IWYU pragma: keep #endif +#include #include #ifdef SunOS #include @@ -401,7 +400,7 @@ bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) { if (buf.st_uid != getuid()) continue; // Remember the pid. - pid = wcstoimax(name.c_str(), NULL, 10); + pid = fish_wcstoi(name.c_str(), NULL, 10); // The 'cmdline' file exists, it should contain the commandline. FILE *cmdfile; @@ -488,8 +487,9 @@ static int find_job(const struct find_job_data_t *info) { int jid; wchar_t *end; - jid = wcstoimax(proc, &end, 10); - if (jid >= 1 && (*proc != L'\0' && *end == L'\0')) { + errno = 0; + jid = fish_wcstoi(proc, &end, 10); + if (jid > 0 && !errno && !*end) { j = job_get(jid); if ((j != 0) && (j->command_wcstr() != 0) && (!j->command_is_empty())) { append_completion(&completions, to_string(j->pgid)); diff --git a/src/highlight.cpp b/src/highlight.cpp index f40e5e7fb..49dd2f065 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -5,9 +5,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -877,12 +875,12 @@ void highlighter_t::color_redirection(const parse_node_t &redirection_node) { switch (redirect_type) { case TOK_REDIRECT_FD: { // Target should be an fd. It must be all digits, and must not overflow. - // wcstoimax returns INT_MAX on overflow; we could instead check errno to + // fish_wcstoi returns INT_MAX on overflow; we could instead check errno to // disambiguiate this from a real INT_MAX fd, but instead we just disallow // that. const wchar_t *target_cstr = target.c_str(); - wchar_t *end; - int fd = wcstoimax(target_cstr, &end, 10); + wchar_t *end = NULL; + int fd = fish_wcstoi(target_cstr, &end, 10); // The iswdigit check ensures there's no leading whitespace, the *end check // ensures the entire string was consumed, and the numeric checks ensure the diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 062908542..5ee3a12d0 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -13,8 +13,6 @@ #include #include #include -#include -#include #include #include #include @@ -1002,8 +1000,8 @@ bool parse_execution_context_t::determine_io_chain(const parse_node_t &statement } else { wchar_t *end = NULL; errno = 0; - int old_fd = wcstoimax(target.c_str(), &end, 10); - if (old_fd < 0 || errno || *end != L'\0') { + int old_fd = fish_wcstoi(target.c_str(), &end, 10); + if (old_fd < 0 || errno || *end) { errored = report_error(redirect_node, _(L"Requested redirection to '%ls', which " L"is not a valid file descriptor"), diff --git a/src/signal.cpp b/src/signal.cpp index 91337a77e..213b2ffb7 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -1,8 +1,6 @@ // The library for various signal related issues. #include "config.h" // IWYU pragma: keep -#include -#include #include #include #include @@ -147,28 +145,31 @@ static const struct lookup_entry lookup[] = { /// Test if \c name is a string describing the signal named \c canonical. static int match_signal_name(const wchar_t *canonical, const wchar_t *name) { - if (wcsncasecmp(name, L"sig", 3) == 0) { - name += 3; - } + if (wcsncasecmp(name, L"sig", 3) == 0) name += 3; return wcscasecmp(canonical + 3, name) == 0; } int wcs2sig(const wchar_t *str) { + int i; wchar_t *end = 0; - for (int i = 0; lookup[i].desc; i++) { + for (i = 0; lookup[i].desc; i++) { if (match_signal_name(lookup[i].name, str)) { return lookup[i].signal; } } + errno = 0; + int res = fish_wcstoi(str, &end, 10); + if (!errno && res >= 0 && !*end) return res; - int res = wcstoimax(str, &end, 10); - return (*str != L'\0' && *end == L'\0') ? res : -1; + return -1; } const wchar_t *sig2wcs(int sig) { - for (int i = 0; lookup[i].desc; i++) { + int i; + + for (i = 0; lookup[i].desc; i++) { if (lookup[i].signal == sig) { return lookup[i].name; } @@ -178,7 +179,9 @@ const wchar_t *sig2wcs(int sig) { } const wchar_t *signal_get_desc(int sig) { - for (int i = 0; lookup[i].desc; i++) { + int i; + + for (i = 0; lookup[i].desc; i++) { if (lookup[i].signal == sig) { return _(lookup[i].desc); } @@ -226,19 +229,21 @@ static void handle_int(int sig, siginfo_t *info, void *context) { default_handler(sig, info, context); } -/// sigchld handler. Does notification and calls the handler in proc.cpp. +/// sigchld handler. Does notification and calls the handler in proc.c. static void handle_chld(int sig, siginfo_t *info, void *context) { job_handle_signal(sig, info, context); default_handler(sig, info, context); } void signal_reset_handlers() { + int i; + struct sigaction act; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_DFL; - for (int i = 0; lookup[i].desc; i++) { + for (i = 0; lookup[i].desc; i++) { sigaction(lookup[i].signal, &act, 0); } } diff --git a/src/wutil.cpp b/src/wutil.cpp index 5a08b6aca..b59de14d3 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -24,23 +24,128 @@ #include "fallback.h" // IWYU pragma: keep #include "wutil.h" // IWYU pragma: keep +typedef std::string cstring; + const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, -1, -1, -1}; #ifndef PATH_MAX #ifdef MAXPATHLEN #define PATH_MAX MAXPATHLEN #else -#define PATH_MAX 4096 // Fallback length of MAXPATHLEN. Hopefully a sane value. +/// Fallback length of MAXPATHLEN. Hopefully a sane value. +#define PATH_MAX 4096 #endif #endif +/// Lock to protect wgettext. +static pthread_mutex_t wgettext_lock; + /// Map used as cache by wgettext. typedef std::map wgettext_map_t; -/// Lock to protect wgettext. -static pthread_mutex_t wgettext_lock; static wgettext_map_t wgettext_map; -/// Wide character version of fopen(). This sets CLO_EXEC. +bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, + bool *out_is_dir) { + struct dirent *d = readdir(dir); + if (!d) return false; + + out_name = str2wcstring(d->d_name); + if (out_is_dir) { + // The caller cares if this is a directory, so check. + bool is_dir = false; + + // We may be able to skip stat, if the readdir can tell us the file type directly. + bool check_with_stat = true; +#ifdef HAVE_STRUCT_DIRENT_D_TYPE + if (d->d_type == DT_DIR) { + // Known directory. + is_dir = true; + check_with_stat = false; + } else if (d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) { + // We want to treat symlinks to directories as directories. Use stat to resolve it. + check_with_stat = true; + } else { + // Regular file. + is_dir = false; + check_with_stat = false; + } +#endif // HAVE_STRUCT_DIRENT_D_TYPE + if (check_with_stat) { + // We couldn't determine the file type from the dirent; check by stat'ing it. + cstring fullpath = wcs2string(dir_path); + fullpath.push_back('/'); + fullpath.append(d->d_name); + struct stat buf; + if (stat(fullpath.c_str(), &buf) != 0) { + is_dir = false; + } else { + is_dir = !!(S_ISDIR(buf.st_mode)); + } + } + *out_is_dir = is_dir; + } + return true; +} + +bool wreaddir(DIR *dir, std::wstring &out_name) { + struct dirent *d = readdir(dir); + if (!d) return false; + + out_name = str2wcstring(d->d_name); + return true; +} + +bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) { + struct dirent *result = NULL; + while (result == NULL) { + struct dirent *d = readdir(dir); + if (!d) break; + +#if HAVE_STRUCT_DIRENT_D_TYPE + switch (d->d_type) { + case DT_DIR: + case DT_LNK: + case DT_UNKNOWN: { + // These may be directories. + result = d; + break; + } + default: { + // Nothing else can. + break; + } + } +#else + // We can't determine if it's a directory or not, so just return it. + result = d; +#endif + } + if (result && out_name) { + *out_name = str2wcstring(result->d_name); + } + return result != NULL; +} + +const wcstring wgetcwd() { + wcstring retval; + + char *res = getcwd(NULL, 0); + if (res) { + retval = str2wcstring(res); + free(res); + } else { + debug(0, _(L"getcwd() failed with errno %d/%s"), errno, strerror(errno)); + retval = wcstring(); + } + + return retval; +} + +int wchdir(const wcstring &dir) { + cstring tmp = wcs2string(dir); + return chdir(tmp.c_str()); +} + FILE *wfopen(const wcstring &path, const char *mode) { int permissions = 0, options = 0; size_t idx = 0; @@ -90,7 +195,7 @@ bool set_cloexec(int fd) { static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec) { ASSERT_IS_NOT_FORKED_CHILD(); - std::string tmp = wcs2string(pathname); + cstring tmp = wcs2string(pathname); int fd; #ifdef O_CLOEXEC @@ -110,152 +215,35 @@ static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool return fd; } -/// Wide character version of open() that also sets the close-on-exec flag (atomically when -/// possible). int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode) { return wopen_internal(pathname, flags, mode, true); } - -bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name, bool *out_is_dir) { - struct dirent *d = readdir(dir); if (!d) return false; - - out_name = str2wcstring(d->d_name); - if (out_is_dir) { - // The caller cares if this is a directory, so check. - bool is_dir = false; - - // We may be able to skip stat, if the readdir can tell us the file type directly. - bool check_with_stat = true; -#ifdef HAVE_STRUCT_DIRENT_D_TYPE - if (d->d_type == DT_DIR) { - // Known directory. - is_dir = true; - check_with_stat = false; - } else if (d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) { - // We want to treat symlinks to directories as directories. Use stat to resolve it. - check_with_stat = true; - } else { - // Regular file. - is_dir = false; - check_with_stat = false; - } -#endif // HAVE_STRUCT_DIRENT_D_TYPE - if (check_with_stat) { - // We couldn't determine the file type from the dirent; check by stat'ing it. - std::string fullpath = wcs2string(dir_path); - fullpath.push_back('/'); - fullpath.append(d->d_name); - struct stat buf; - if (stat(fullpath.c_str(), &buf) != 0) { - is_dir = false; - } else { - is_dir = !!(S_ISDIR(buf.st_mode)); - } - } - *out_is_dir = is_dir; - } - return true; -} - -/// Wide-character version of readdir() -bool wreaddir(DIR *dir, wcstring &out_name) { - struct dirent *d = readdir(dir); - if (!d) return false; - - out_name = str2wcstring(d->d_name); - return true; -} - -/// Like wreaddir, but skip items that are known to not be directories. If this requires a stat -/// (i.e. the file is a symlink), then return it. Note that this does not guarantee that everything -/// returned is a directory, it's just an optimization for cases where we would check for -/// directories anyways. -bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) { - struct dirent *result = NULL; - while (result == NULL) { - struct dirent *d = readdir(dir); - if (!d) break; - -#if HAVE_STRUCT_DIRENT_D_TYPE - switch (d->d_type) { - case DT_DIR: - case DT_LNK: - case DT_UNKNOWN: { - // These may be directories. - result = d; - break; - } - default: { - // Nothing else can. - break; - } - } -#else - // We can't determine if it's a directory or not, so just return it. - result = d; -#endif - } - if (result && out_name) { - *out_name = str2wcstring(result->d_name); - } - return result != NULL; -} - -/// Wide character version of getcwd(). -const wcstring wgetcwd() { - wcstring retval; - - char *res = getcwd(NULL, 0); - if (res) { - retval = str2wcstring(res); - free(res); - } else { - debug(0, _(L"getcwd() failed with errno %d/%s"), errno, strerror(errno)); - retval = wcstring(); - } - - return retval; -} - -/// Wide character version of chdir(). -int wchdir(const wcstring &dir) { - std::string tmp = wcs2string(dir); - return chdir(tmp.c_str()); -} - -/// Wide character version of opendir(). Note that opendir() is guaranteed to set close-on-exec by -/// POSIX (hooray). DIR *wopendir(const wcstring &name) { - const std::string tmp = wcs2string(name); + const cstring tmp = wcs2string(name); return opendir(tmp.c_str()); } -/// Wide character version of stat(). int wstat(const wcstring &file_name, struct stat *buf) { - const std::string tmp = wcs2string(file_name); + const cstring tmp = wcs2string(file_name); return stat(tmp.c_str(), buf); } -/// Wide character version of lstat(). int lwstat(const wcstring &file_name, struct stat *buf) { - const std::string tmp = wcs2string(file_name); + const cstring tmp = wcs2string(file_name); return lstat(tmp.c_str(), buf); } -/// Wide character version of access(). int waccess(const wcstring &file_name, int mode) { - const std::string tmp = wcs2string(file_name); + const cstring tmp = wcs2string(file_name); return access(tmp.c_str(), mode); } -/// Wide character version of unlink(). int wunlink(const wcstring &file_name) { - const std::string tmp = wcs2string(file_name); + const cstring tmp = wcs2string(file_name); return unlink(tmp.c_str()); } -/// Wide character version of perror(). void wperror(const wchar_t *s) { int e = errno; if (s[0] != L'\0') { @@ -264,7 +252,6 @@ void wperror(const wchar_t *s) { fwprintf(stderr, L"%s\n", strerror(e)); } -/// Mark an fd as nonblocking; returns errno or 0 on success. int make_fd_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); int err = 0; @@ -275,7 +262,6 @@ int make_fd_nonblocking(int fd) { return err == -1 ? errno : 0; } -/// Mark an fd as blocking; returns errno or 0 on success. int make_fd_blocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); int err = 0; @@ -290,11 +276,9 @@ static inline void safe_append(char *buffer, const char *s, size_t buffsize) { strncat(buffer, s, buffsize - strlen(buffer) - 1); } -/// Async-safe version of strerror(). -/// In general, strerror is not async-safe, and therefore we cannot use it directly. So instead we -/// have to grub through sys_nerr and sys_errlist directly On GNU toolchain, this will produce a -/// deprecation warning from the linker (!!), which appears impossible to suppress! -/// XXX Use strerror_r instead! +// In general, strerror is not async-safe, and therefore we cannot use it directly. So instead we +// have to grub through sys_nerr and sys_errlist directly On GNU toolchain, this will produce a +// deprecation warning from the linker (!!), which appears impossible to suppress! const char *safe_strerror(int err) { #if defined(__UCLIBC__) // uClibc does not have sys_errlist, however, its strerror is believed to be async-safe. @@ -330,7 +314,6 @@ const char *safe_strerror(int err) { return buff; } -/// Async-safe version of perror(). void safe_perror(const char *message) { // Note we cannot use strerror, because on Linux it uses gettext, which is not safe. int err = errno; @@ -351,11 +334,8 @@ void safe_perror(const char *message) { #ifdef HAVE_REALPATH_NULL -/// Wide character version of realpath function. Just like the GNU version of realpath, wrealpath -/// will accept 0 as the value for the second argument, in which case the result will be allocated -/// using malloc, and must be free'd by the user. wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { - std::string narrow_path = wcs2string(pathname); + cstring narrow_path = wcs2string(pathname); char *narrow_res = realpath(narrow_path.c_str(), NULL); if (!narrow_res) return NULL; @@ -383,7 +363,7 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { #else wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { - std::string tmp = wcs2string(pathname); + cstring tmp = wcs2string(pathname); char narrow_buff[PATH_MAX]; char *narrow_res = realpath(tmp.c_str(), narrow_buff); wchar_t *res; @@ -402,7 +382,6 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { #endif -/// Wide character version of dirname(). wcstring wdirname(const wcstring &path) { char *tmp = wcs2str(path.c_str()); char *narrow_res = dirname(tmp); @@ -411,7 +390,6 @@ wcstring wdirname(const wcstring &path) { return result; } -/// Wide character version of basename(). wcstring wbasename(const wcstring &path) { char *tmp = wcs2str(path.c_str()); char *narrow_res = basename(tmp); @@ -434,10 +412,6 @@ static void wgettext_init_if_necessary() { pthread_once(&once, wgettext_really_init); } -/// Wide character wrapper around the gettext function. For historic reasons, unlike the real -/// gettext function, wgettext takes care of setting the correct domain, etc. using the textdomain -/// and bindtextdomain functions. This should probably be moved out of wgettext, so that wgettext -/// will be nothing more than a wrapper around gettext, like all other functions in this file. const wcstring &wgettext(const wchar_t *in) { // Preserve errno across this since this is often used in printing error messages. int err = errno; @@ -447,7 +421,7 @@ const wcstring &wgettext(const wchar_t *in) { scoped_lock locker(wgettext_lock); wcstring &val = wgettext_map[key]; if (val.empty()) { - std::string mbs_in = wcs2string(key); + cstring mbs_in = wcs2string(key); char *out = fish_gettext(mbs_in.c_str()); val = format_string(L"%s", out); } @@ -458,16 +432,14 @@ const wcstring &wgettext(const wchar_t *in) { return val; } -/// Wide character version of mkdir. int wmkdir(const wcstring &name, int mode) { - std::string name_narrow = wcs2string(name); + cstring name_narrow = wcs2string(name); return mkdir(name_narrow.c_str(), mode); } -/// Wide character version of rename. int wrename(const wcstring &old, const wcstring &newv) { - std::string old_narrow = wcs2string(old); - std::string new_narrow = wcs2string(newv); + cstring old_narrow = wcs2string(old); + cstring new_narrow = wcs2string(newv); return rename(old_narrow.c_str(), new_narrow.c_str()); } @@ -549,6 +521,18 @@ int fish_wcswidth(const wchar_t *str) { return fish_wcswidth(str, wcslen(str)); /// See fallback.h for the normal definitions. int fish_wcswidth(const wcstring &str) { return fish_wcswidth(str.c_str(), str.size()); } +int fish_wcstoi(const wchar_t *str, wchar_t **endptr, int base) { + long ret = wcstol(str, endptr, base); + if (ret > INT_MAX) { + ret = INT_MAX; + errno = ERANGE; + } else if (ret < INT_MIN) { + ret = INT_MIN; + errno = ERANGE; + } + return (int)ret; +} + file_id_t file_id_t::file_id_from_stat(const struct stat *buf) { assert(buf != NULL); diff --git a/src/wutil.h b/src/wutil.h index 304c3b23c..601fac936 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -10,53 +10,85 @@ #include "common.h" +/// Wide character version of fopen(). This sets CLO_EXEC. FILE *wfopen(const wcstring &path, const char *mode); +/// Sets CLO_EXEC on a given fd. bool set_cloexec(int fd); +/// Wide character version of open() that also sets the close-on-exec flag (atomically when +/// possible). int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode = 0); +/// Mark an fd as nonblocking; returns errno or 0 on success. int make_fd_nonblocking(int fd); +/// Mark an fd as blocking; returns errno or 0 on success. int make_fd_blocking(int fd); +/// Wide character version of opendir(). Note that opendir() is guaranteed to set close-on-exec by +/// POSIX (hooray). DIR *wopendir(const wcstring &name); +/// Wide character version of stat(). int wstat(const wcstring &file_name, struct stat *buf); +/// Wide character version of lstat(). int lwstat(const wcstring &file_name, struct stat *buf); +/// Wide character version of access(). int waccess(const wcstring &pathname, int mode); +/// Wide character version of unlink(). int wunlink(const wcstring &pathname); +/// Wide character version of perror(). void wperror(const wchar_t *s); +/// Async-safe version of perror(). void safe_perror(const char *message); +/// Async-safe version of strerror(). const char *safe_strerror(int err); +/// Wide character version of getcwd(). const wcstring wgetcwd(); +/// Wide character version of chdir(). int wchdir(const wcstring &dir); +/// Wide character version of realpath function. Just like the GNU version of realpath, wrealpath +/// will accept 0 as the value for the second argument, in which case the result will be allocated +/// using malloc, and must be free'd by the user. wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path); +/// Wide character version of readdir(). bool wreaddir(DIR *dir, std::wstring &out_name); - bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir); +/// Like wreaddir, but skip items that are known to not be directories. If this requires a stat +/// (i.e. the file is a symlink), then return it. Note that this does not guarantee that everything +/// returned is a directory, it's just an optimization for cases where we would check for +/// directories anyways. bool wreaddir_for_dirs(DIR *dir, wcstring *out_name); +/// Wide character version of dirname(). std::wstring wdirname(const std::wstring &path); +/// Wide character version of basename(). std::wstring wbasename(const std::wstring &path); +/// Wide character wrapper around the gettext function. For historic reasons, unlike the real +/// gettext function, wgettext takes care of setting the correct domain, etc. using the textdomain +/// and bindtextdomain functions. This should probably be moved out of wgettext, so that wgettext +/// will be nothing more than a wrapper around gettext, like all other functions in this file. const wcstring &wgettext(const wchar_t *in); +/// Wide character version of mkdir. int wmkdir(const wcstring &dir, int mode); +/// Wide character version of rename. int wrename(const wcstring &oldName, const wcstring &newName); #define PUA1_START 0xE000 @@ -84,6 +116,9 @@ bool wcsvarchr(wchar_t chr); int fish_wcswidth(const wchar_t *str); int fish_wcswidth(const wcstring &str); +/// Like wcstol(), but fails on a value outside the range of an int. +int fish_wcstoi(const wchar_t *str, wchar_t **endptr, int base); + /// Class for representing a file's inode. We use this to detect and avoid symlink loops, among /// other things. While an inode / dev pair is sufficient to distinguish co-existing files, Linux /// seems to aggressively re-use inodes, so it cannot determine if a file has been deleted (ABA