mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
Revert "Tighten up empty string checks."
The use of wcstoimax causes certain out-of-range values
to be silently truncated (e.g. when converted to a pid),
and is incompatible with FreeBSD (see #626)
This reverts commit 6faa2f9866
.
This commit is contained in:
parent
b05c09429d
commit
d17f2585fa
8 changed files with 275 additions and 235 deletions
120
src/builtin.cpp
120
src/builtin.cpp
|
@ -20,9 +20,7 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -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);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// Functions for executing the jobs builtin.
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE__PROC_SELF_STAT
|
||||
#include <sys/time.h>
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// IWYU pragma: no_include <cstddef>
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <stdarg.h>
|
||||
|
@ -17,6 +15,7 @@
|
|||
#ifdef HAVE_SYS_SYSCTL_H
|
||||
#include <sys/sysctl.h> // IWYU pragma: keep
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
#ifdef SunOS
|
||||
#include <procfs.h>
|
||||
|
@ -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<long>(j->pgid));
|
||||
|
|
|
@ -5,9 +5,7 @@
|
|||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
|
@ -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
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
|
@ -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"),
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// The library for various signal related issues.
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
288
src/wutil.cpp
288
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<wcstring, wcstring> 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);
|
||||
|
||||
|
|
37
src/wutil.h
37
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
|
||||
|
|
Loading…
Reference in a new issue