mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 21:33:09 +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 <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <inttypes.h>
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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
|
/// if specified, _all_ key bindings will be erased
|
||||||
/// @param mode
|
/// @param mode
|
||||||
/// if specified, only bindings from that mode will be erased. If not given
|
/// 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
|
/// @param use_terminfo
|
||||||
/// Whether to look use terminfo -k name
|
/// 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);
|
input_mapping_erase(it->seq, it->mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return STATUS_BUILTIN_OK;
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
if (mode == NULL) mode = DEFAULT_BIND_MODE;
|
||||||
|
|
||||||
while (*seq) {
|
while (*seq) {
|
||||||
if (use_terminfo) {
|
if (use_terminfo) {
|
||||||
wcstring seq2;
|
wcstring seq2;
|
||||||
if (get_terminfo_sequence(*seq++, &seq2, streams)) {
|
if (get_terminfo_sequence(*seq++, &seq2, streams)) {
|
||||||
input_mapping_erase(seq2, mode != NULL ? mode : DEFAULT_BIND_MODE);
|
input_mapping_erase(seq2, mode);
|
||||||
} else {
|
} else {
|
||||||
res = STATUS_BUILTIN_ERROR;
|
res = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} 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 argc = builtin_count_args(argv);
|
||||||
int mode = BIND_INSERT;
|
int mode = BIND_INSERT;
|
||||||
int res = STATUS_BUILTIN_OK;
|
int res = STATUS_BUILTIN_OK;
|
||||||
bool all = false;
|
int all = 0;
|
||||||
const wchar_t *bind_mode = DEFAULT_BIND_MODE;
|
const wchar_t *bind_mode = DEFAULT_BIND_MODE;
|
||||||
bool bind_mode_given = false;
|
bool bind_mode_given = false;
|
||||||
const wchar_t *sets_bind_mode = DEFAULT_BIND_MODE;
|
const wchar_t *sets_bind_mode = DEFAULT_BIND_MODE;
|
||||||
bool sets_bind_mode_given = false;
|
bool sets_bind_mode_given = false;
|
||||||
bool use_terminfo = false;
|
int use_terminfo = 0;
|
||||||
|
|
||||||
w.woptind = 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;
|
return STATUS_BUILTIN_ERROR;
|
||||||
}
|
}
|
||||||
case 'a': {
|
case 'a': {
|
||||||
all = true;
|
all = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'e': {
|
case 'e': {
|
||||||
|
@ -480,7 +482,7 @@ static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv)
|
||||||
return STATUS_BUILTIN_OK;
|
return STATUS_BUILTIN_OK;
|
||||||
}
|
}
|
||||||
case 'k': {
|
case 'k': {
|
||||||
use_terminfo = true;
|
use_terminfo = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'K': {
|
case 'K': {
|
||||||
|
@ -931,7 +933,7 @@ static wcstring functions_def(const wcstring &name) {
|
||||||
case EVENT_EXIT: {
|
case EVENT_EXIT: {
|
||||||
if (next->param1.pid > 0)
|
if (next->param1.pid > 0)
|
||||||
append_format(out, L" --on-process-exit %d", next->param1.pid);
|
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);
|
append_format(out, L" --on-job-exit %d", -next->param1.pid);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1251,7 +1253,7 @@ static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *cons
|
||||||
bool success = false;
|
bool success = false;
|
||||||
unsigned int start = 0; // the first character of the numeric part of the sequence
|
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) {
|
if (builtin_echo_digit(str[0], 8) != UINT_MAX) {
|
||||||
// Octal escape
|
// Octal escape
|
||||||
base = 8;
|
base = 8;
|
||||||
|
@ -1270,7 +1272,7 @@ static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *cons
|
||||||
|
|
||||||
if (base != 0) {
|
if (base != 0) {
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
unsigned char val = '\0'; // resulting character
|
unsigned char val = 0; // resulting character
|
||||||
for (idx = start; idx < start + max_digits; idx++) {
|
for (idx = start; idx < start + max_digits; idx++) {
|
||||||
unsigned int digit = builtin_echo_digit(str[idx], base);
|
unsigned int digit = builtin_echo_digit(str[idx], base);
|
||||||
if (digit == UINT_MAX) break;
|
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 'j':
|
||||||
case 'p': {
|
case 'p': {
|
||||||
|
pid_t pid;
|
||||||
wchar_t *end;
|
wchar_t *end;
|
||||||
event_t e(EVENT_ANY);
|
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,
|
append_format(*out_err,
|
||||||
_(L"%ls: Cannot find calling job for event handler"),
|
_(L"%ls: Cannot find calling job for event handler"),
|
||||||
argv[0]);
|
argv[0]);
|
||||||
res = STATUS_BUILTIN_ERROR;
|
res = 1;
|
||||||
} else {
|
} else {
|
||||||
e.type = EVENT_JOB_ID;
|
e.type = EVENT_JOB_ID;
|
||||||
e.param1.job_id = job_id;
|
e.param1.job_id = job_id;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pid_t pid = wcstoimax(w.woptarg, &end, 10);
|
errno = 0;
|
||||||
if (pid < 1 || !(*w.woptarg != L'\0' && *end == L'\0')) {
|
pid = fish_wcstoi(w.woptarg, &end, 10);
|
||||||
append_format(*out_err, _(L"%ls: Invalid process id '%ls'"), argv[0],
|
if (errno || !end || *end) {
|
||||||
|
append_format(*out_err, _(L"%ls: Invalid process id %ls"), argv[0],
|
||||||
w.woptarg);
|
w.woptarg);
|
||||||
res = STATUS_BUILTIN_ERROR;
|
res = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1640,7 +1644,7 @@ int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_lis
|
||||||
}
|
}
|
||||||
case '?': {
|
case '?': {
|
||||||
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||||
res = STATUS_BUILTIN_ERROR;
|
res = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1730,7 +1734,7 @@ int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_lis
|
||||||
|
|
||||||
/// The random builtin generates random numbers.
|
/// The random builtin generates random numbers.
|
||||||
static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
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;
|
static struct drand48_data seed_buffer;
|
||||||
|
|
||||||
int argc = builtin_count_args(argv);
|
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: {
|
case 0: {
|
||||||
long res;
|
long res;
|
||||||
if (!seeded) {
|
if (!seeded) {
|
||||||
seeded = true;
|
seeded = 1;
|
||||||
srand48_r(time(0), &seed_buffer);
|
srand48_r(time(0), &seed_buffer);
|
||||||
}
|
}
|
||||||
lrand48_r(&seed_buffer, &res);
|
lrand48_r(&seed_buffer, &res);
|
||||||
|
@ -1776,16 +1780,19 @@ static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **arg
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1: {
|
case 1: {
|
||||||
|
long foo;
|
||||||
wchar_t *end = 0;
|
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"),
|
streams.err.append_format(_(L"%ls: Seed value '%ls' is not a valid number\n"),
|
||||||
argv[0], argv[w.woptind]);
|
argv[0], argv[w.woptind]);
|
||||||
|
|
||||||
return STATUS_BUILTIN_ERROR;
|
return STATUS_BUILTIN_ERROR;
|
||||||
}
|
}
|
||||||
seeded = true;
|
seeded = 1;
|
||||||
srand48_r(seedval, &seed_buffer);
|
srand48_r(foo, &seed_buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
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;
|
const wchar_t *mode_name = READ_MODE_NAME;
|
||||||
int nchars = 0;
|
int nchars = 0;
|
||||||
wchar_t *end;
|
wchar_t *end;
|
||||||
bool shell = false;
|
int shell = 0;
|
||||||
bool array = false;
|
int array = 0;
|
||||||
bool split_null = false;
|
bool split_null = false;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
@ -1882,9 +1889,9 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case L'n': {
|
case L'n': {
|
||||||
nchars = wcstoimax(w.woptarg, &end, 10);
|
errno = 0;
|
||||||
if (!(*w.woptarg != L'\0' && *end == L'\0')) {
|
nchars = fish_wcstoi(w.woptarg, &end, 10);
|
||||||
// MUST be an error
|
if (errno || *end != 0) {
|
||||||
switch (errno) {
|
switch (errno) {
|
||||||
case ERANGE: {
|
case ERANGE: {
|
||||||
streams.err.append_format(_(L"%ls: Argument '%ls' is out of range\n"),
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case 's': {
|
case 's': {
|
||||||
shell = true;
|
shell = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'a': {
|
case 'a': {
|
||||||
array = true;
|
array = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case L'z': {
|
case L'z': {
|
||||||
|
@ -2303,8 +2310,9 @@ static int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv)
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
wchar_t *end;
|
wchar_t *end;
|
||||||
|
errno = 0;
|
||||||
ec = wcstol(argv[1], &end, 10);
|
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],
|
streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"), argv[0],
|
||||||
argv[1]);
|
argv[1]);
|
||||||
builtin_print_help(parser, streams, argv[0], streams.err);
|
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;
|
res = 1;
|
||||||
} else if (wchdir(dir) != 0) {
|
} else if (wchdir(dir) != 0) {
|
||||||
struct stat buffer;
|
struct stat buffer;
|
||||||
int status = wstat(dir, &buffer);
|
int status;
|
||||||
|
|
||||||
|
status = wstat(dir, &buffer);
|
||||||
if (!status && S_ISDIR(buffer.st_mode)) {
|
if (!status && S_ISDIR(buffer.st_mode)) {
|
||||||
streams.err.append_format(_(L"%ls: Permission denied: '%ls'\n"), argv[0], dir.c_str());
|
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
|
// 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.
|
// specification or if this is an malformed job id.
|
||||||
wchar_t *endptr;
|
wchar_t *endptr;
|
||||||
bool found_job = false;
|
int pid;
|
||||||
|
int found_job = 0;
|
||||||
|
|
||||||
pid_t pid = wcstoimax(argv[1], &endptr, 10);
|
errno = 0;
|
||||||
if (pid >= 1 || (*argv[1] != L'\0' && *endptr == L'\0')) {
|
pid = fish_wcstoi(argv[1], &endptr, 10);
|
||||||
|
if (!(*endptr || errno)) {
|
||||||
j = job_get_from_pid(pid);
|
j = job_get_from_pid(pid);
|
||||||
if (j) found_job = true;
|
if (j) found_job = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found_job) {
|
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);
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
||||||
|
|
||||||
return STATUS_BUILTIN_ERROR;
|
j = 0;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
wchar_t *end;
|
wchar_t *end;
|
||||||
pid_t pid = wcstoimax(argv[1], &end, 10);
|
int pid;
|
||||||
if (pid < 1 || !(*argv[1] != L'\0' && *end == L'\0')) {
|
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]);
|
streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, argv[0], argv[1]);
|
||||||
builtin_print_help(parser, streams, argv[0], streams.err);
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
||||||
} else {
|
} else {
|
||||||
j = job_get_from_pid(pid);
|
j = job_get_from_pid(pid);
|
||||||
if (!j || !job_get_flag(j, JOB_CONSTRUCTED) || job_is_completed(j)) {
|
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);
|
streams.err.append_format(_(L"%ls: No suitable job: %d\n"), argv[0], pid);
|
||||||
return STATUS_BUILTIN_ERROR;
|
j = 0;
|
||||||
}
|
} else if (!job_get_flag(j, JOB_CONTROL)) {
|
||||||
if (!job_get_flag(j, JOB_CONTROL)) {
|
|
||||||
streams.err.append_format(_(L"%ls: Can't put job %d, '%ls' to foreground because "
|
streams.err.append_format(_(L"%ls: Can't put job %d, '%ls' to foreground because "
|
||||||
L"it is not under job control\n"),
|
L"it is not under job control\n"),
|
||||||
argv[0], pid, j->command_wcstr());
|
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 {
|
} else {
|
||||||
wchar_t *end;
|
wchar_t *end;
|
||||||
for (int i = 1; argv[i]; i++) {
|
int i;
|
||||||
pid_t pid = wcstoimax(argv[i], &end, 10);
|
int pid;
|
||||||
if (pid < 1 || !(*argv[i] != L'\0' && *end == L'\0') || !job_get_from_pid(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]);
|
streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[i]);
|
||||||
return STATUS_BUILTIN_ERROR;
|
return STATUS_BUILTIN_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -2729,8 +2746,9 @@ static int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **arg
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
wchar_t *end;
|
wchar_t *end;
|
||||||
status = wcstoimax(argv[1], &end, 10);
|
errno = 0;
|
||||||
if (!(*argv[1] != L'\0' && *end == L'\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],
|
streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"), argv[0],
|
||||||
argv[1]);
|
argv[1]);
|
||||||
builtin_print_help(parser, streams, argv[0], streams.err);
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// Functions for executing the jobs builtin.
|
// Functions for executing the jobs builtin.
|
||||||
#include "config.h" // IWYU pragma: keep
|
#include "config.h" // IWYU pragma: keep
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#ifdef HAVE__PROC_SELF_STAT
|
#ifdef HAVE__PROC_SELF_STAT
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
@ -177,10 +175,14 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (w.woptind < argc) {
|
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;
|
wchar_t *end;
|
||||||
pid_t pid = wcstoimax(argv[i], &end, 10);
|
errno = 0;
|
||||||
if (!(*argv[i] != L'\0' && *end == L'\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]);
|
streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[i]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
// IWYU pragma: no_include <cstddef>
|
// IWYU pragma: no_include <cstddef>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
@ -17,6 +15,7 @@
|
||||||
#ifdef HAVE_SYS_SYSCTL_H
|
#ifdef HAVE_SYS_SYSCTL_H
|
||||||
#include <sys/sysctl.h> // IWYU pragma: keep
|
#include <sys/sysctl.h> // IWYU pragma: keep
|
||||||
#endif
|
#endif
|
||||||
|
#include <assert.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#ifdef SunOS
|
#ifdef SunOS
|
||||||
#include <procfs.h>
|
#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;
|
if (buf.st_uid != getuid()) continue;
|
||||||
|
|
||||||
// Remember the pid.
|
// 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.
|
// The 'cmdline' file exists, it should contain the commandline.
|
||||||
FILE *cmdfile;
|
FILE *cmdfile;
|
||||||
|
@ -488,8 +487,9 @@ static int find_job(const struct find_job_data_t *info) {
|
||||||
int jid;
|
int jid;
|
||||||
wchar_t *end;
|
wchar_t *end;
|
||||||
|
|
||||||
jid = wcstoimax(proc, &end, 10);
|
errno = 0;
|
||||||
if (jid >= 1 && (*proc != L'\0' && *end == L'\0')) {
|
jid = fish_wcstoi(proc, &end, 10);
|
||||||
|
if (jid > 0 && !errno && !*end) {
|
||||||
j = job_get(jid);
|
j = job_get(jid);
|
||||||
if ((j != 0) && (j->command_wcstr() != 0) && (!j->command_is_empty())) {
|
if ((j != 0) && (j->command_wcstr() != 0) && (!j->command_is_empty())) {
|
||||||
append_completion(&completions, to_string<long>(j->pgid));
|
append_completion(&completions, to_string<long>(j->pgid));
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <inttypes.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <stddef.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include <wctype.h>
|
#include <wctype.h>
|
||||||
|
@ -877,12 +875,12 @@ void highlighter_t::color_redirection(const parse_node_t &redirection_node) {
|
||||||
switch (redirect_type) {
|
switch (redirect_type) {
|
||||||
case TOK_REDIRECT_FD: {
|
case TOK_REDIRECT_FD: {
|
||||||
// Target should be an fd. It must be all digits, and must not overflow.
|
// 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
|
// disambiguiate this from a real INT_MAX fd, but instead we just disallow
|
||||||
// that.
|
// that.
|
||||||
const wchar_t *target_cstr = target.c_str();
|
const wchar_t *target_cstr = target.c_str();
|
||||||
wchar_t *end;
|
wchar_t *end = NULL;
|
||||||
int fd = wcstoimax(target_cstr, &end, 10);
|
int fd = fish_wcstoi(target_cstr, &end, 10);
|
||||||
|
|
||||||
// The iswdigit check ensures there's no leading whitespace, the *end check
|
// The iswdigit check ensures there's no leading whitespace, the *end check
|
||||||
// ensures the entire string was consumed, and the numeric checks ensure the
|
// ensures the entire string was consumed, and the numeric checks ensure the
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stddef.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
@ -1002,8 +1000,8 @@ bool parse_execution_context_t::determine_io_chain(const parse_node_t &statement
|
||||||
} else {
|
} else {
|
||||||
wchar_t *end = NULL;
|
wchar_t *end = NULL;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
int old_fd = wcstoimax(target.c_str(), &end, 10);
|
int old_fd = fish_wcstoi(target.c_str(), &end, 10);
|
||||||
if (old_fd < 0 || errno || *end != L'\0') {
|
if (old_fd < 0 || errno || *end) {
|
||||||
errored =
|
errored =
|
||||||
report_error(redirect_node, _(L"Requested redirection to '%ls', which "
|
report_error(redirect_node, _(L"Requested redirection to '%ls', which "
|
||||||
L"is not a valid file descriptor"),
|
L"is not a valid file descriptor"),
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// The library for various signal related issues.
|
// The library for various signal related issues.
|
||||||
#include "config.h" // IWYU pragma: keep
|
#include "config.h" // IWYU pragma: keep
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.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.
|
/// 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) {
|
static int match_signal_name(const wchar_t *canonical, const wchar_t *name) {
|
||||||
if (wcsncasecmp(name, L"sig", 3) == 0) {
|
if (wcsncasecmp(name, L"sig", 3) == 0) name += 3;
|
||||||
name += 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wcscasecmp(canonical + 3, name) == 0;
|
return wcscasecmp(canonical + 3, name) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wcs2sig(const wchar_t *str) {
|
int wcs2sig(const wchar_t *str) {
|
||||||
|
int i;
|
||||||
wchar_t *end = 0;
|
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)) {
|
if (match_signal_name(lookup[i].name, str)) {
|
||||||
return lookup[i].signal;
|
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 -1;
|
||||||
return (*str != L'\0' && *end == L'\0') ? res : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *sig2wcs(int sig) {
|
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) {
|
if (lookup[i].signal == sig) {
|
||||||
return lookup[i].name;
|
return lookup[i].name;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +179,9 @@ const wchar_t *sig2wcs(int sig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *signal_get_desc(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) {
|
if (lookup[i].signal == sig) {
|
||||||
return _(lookup[i].desc);
|
return _(lookup[i].desc);
|
||||||
}
|
}
|
||||||
|
@ -226,19 +229,21 @@ static void handle_int(int sig, siginfo_t *info, void *context) {
|
||||||
default_handler(sig, info, 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) {
|
static void handle_chld(int sig, siginfo_t *info, void *context) {
|
||||||
job_handle_signal(sig, info, context);
|
job_handle_signal(sig, info, context);
|
||||||
default_handler(sig, info, context);
|
default_handler(sig, info, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_reset_handlers() {
|
void signal_reset_handlers() {
|
||||||
|
int i;
|
||||||
|
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
sigemptyset(&act.sa_mask);
|
sigemptyset(&act.sa_mask);
|
||||||
act.sa_flags = 0;
|
act.sa_flags = 0;
|
||||||
act.sa_handler = SIG_DFL;
|
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);
|
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 "fallback.h" // IWYU pragma: keep
|
||||||
#include "wutil.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};
|
const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, -1, -1, -1};
|
||||||
|
|
||||||
#ifndef PATH_MAX
|
#ifndef PATH_MAX
|
||||||
#ifdef MAXPATHLEN
|
#ifdef MAXPATHLEN
|
||||||
#define PATH_MAX MAXPATHLEN
|
#define PATH_MAX MAXPATHLEN
|
||||||
#else
|
#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
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Lock to protect wgettext.
|
||||||
|
static pthread_mutex_t wgettext_lock;
|
||||||
|
|
||||||
/// Map used as cache by wgettext.
|
/// Map used as cache by wgettext.
|
||||||
typedef std::map<wcstring, wcstring> wgettext_map_t;
|
typedef std::map<wcstring, wcstring> wgettext_map_t;
|
||||||
/// Lock to protect wgettext.
|
|
||||||
static pthread_mutex_t wgettext_lock;
|
|
||||||
static wgettext_map_t wgettext_map;
|
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) {
|
FILE *wfopen(const wcstring &path, const char *mode) {
|
||||||
int permissions = 0, options = 0;
|
int permissions = 0, options = 0;
|
||||||
size_t idx = 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) {
|
static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec) {
|
||||||
ASSERT_IS_NOT_FORKED_CHILD();
|
ASSERT_IS_NOT_FORKED_CHILD();
|
||||||
std::string tmp = wcs2string(pathname);
|
cstring tmp = wcs2string(pathname);
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
#ifdef O_CLOEXEC
|
#ifdef O_CLOEXEC
|
||||||
|
@ -110,152 +215,35 @@ static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool
|
||||||
return fd;
|
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) {
|
int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode) {
|
||||||
return wopen_internal(pathname, flags, mode, true);
|
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) {
|
DIR *wopendir(const wcstring &name) {
|
||||||
const std::string tmp = wcs2string(name);
|
const cstring tmp = wcs2string(name);
|
||||||
return opendir(tmp.c_str());
|
return opendir(tmp.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wide character version of stat().
|
|
||||||
int wstat(const wcstring &file_name, struct stat *buf) {
|
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);
|
return stat(tmp.c_str(), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wide character version of lstat().
|
|
||||||
int lwstat(const wcstring &file_name, struct stat *buf) {
|
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);
|
return lstat(tmp.c_str(), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wide character version of access().
|
|
||||||
int waccess(const wcstring &file_name, int mode) {
|
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);
|
return access(tmp.c_str(), mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wide character version of unlink().
|
|
||||||
int wunlink(const wcstring &file_name) {
|
int wunlink(const wcstring &file_name) {
|
||||||
const std::string tmp = wcs2string(file_name);
|
const cstring tmp = wcs2string(file_name);
|
||||||
return unlink(tmp.c_str());
|
return unlink(tmp.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wide character version of perror().
|
|
||||||
void wperror(const wchar_t *s) {
|
void wperror(const wchar_t *s) {
|
||||||
int e = errno;
|
int e = errno;
|
||||||
if (s[0] != L'\0') {
|
if (s[0] != L'\0') {
|
||||||
|
@ -264,7 +252,6 @@ void wperror(const wchar_t *s) {
|
||||||
fwprintf(stderr, L"%s\n", strerror(e));
|
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 make_fd_nonblocking(int fd) {
|
||||||
int flags = fcntl(fd, F_GETFL, 0);
|
int flags = fcntl(fd, F_GETFL, 0);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
@ -275,7 +262,6 @@ int make_fd_nonblocking(int fd) {
|
||||||
return err == -1 ? errno : 0;
|
return err == -1 ? errno : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark an fd as blocking; returns errno or 0 on success.
|
|
||||||
int make_fd_blocking(int fd) {
|
int make_fd_blocking(int fd) {
|
||||||
int flags = fcntl(fd, F_GETFL, 0);
|
int flags = fcntl(fd, F_GETFL, 0);
|
||||||
int err = 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);
|
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
|
||||||
/// 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
|
||||||
/// 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!
|
||||||
/// deprecation warning from the linker (!!), which appears impossible to suppress!
|
|
||||||
/// XXX Use strerror_r instead!
|
|
||||||
const char *safe_strerror(int err) {
|
const char *safe_strerror(int err) {
|
||||||
#if defined(__UCLIBC__)
|
#if defined(__UCLIBC__)
|
||||||
// uClibc does not have sys_errlist, however, its strerror is believed to be async-safe.
|
// 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;
|
return buff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Async-safe version of perror().
|
|
||||||
void safe_perror(const char *message) {
|
void safe_perror(const char *message) {
|
||||||
// Note we cannot use strerror, because on Linux it uses gettext, which is not safe.
|
// Note we cannot use strerror, because on Linux it uses gettext, which is not safe.
|
||||||
int err = errno;
|
int err = errno;
|
||||||
|
@ -351,11 +334,8 @@ void safe_perror(const char *message) {
|
||||||
|
|
||||||
#ifdef HAVE_REALPATH_NULL
|
#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) {
|
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);
|
char *narrow_res = realpath(narrow_path.c_str(), NULL);
|
||||||
|
|
||||||
if (!narrow_res) return NULL;
|
if (!narrow_res) return NULL;
|
||||||
|
@ -383,7 +363,7 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) {
|
||||||
#else
|
#else
|
||||||
|
|
||||||
wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) {
|
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_buff[PATH_MAX];
|
||||||
char *narrow_res = realpath(tmp.c_str(), narrow_buff);
|
char *narrow_res = realpath(tmp.c_str(), narrow_buff);
|
||||||
wchar_t *res;
|
wchar_t *res;
|
||||||
|
@ -402,7 +382,6 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Wide character version of dirname().
|
|
||||||
wcstring wdirname(const wcstring &path) {
|
wcstring wdirname(const wcstring &path) {
|
||||||
char *tmp = wcs2str(path.c_str());
|
char *tmp = wcs2str(path.c_str());
|
||||||
char *narrow_res = dirname(tmp);
|
char *narrow_res = dirname(tmp);
|
||||||
|
@ -411,7 +390,6 @@ wcstring wdirname(const wcstring &path) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wide character version of basename().
|
|
||||||
wcstring wbasename(const wcstring &path) {
|
wcstring wbasename(const wcstring &path) {
|
||||||
char *tmp = wcs2str(path.c_str());
|
char *tmp = wcs2str(path.c_str());
|
||||||
char *narrow_res = basename(tmp);
|
char *narrow_res = basename(tmp);
|
||||||
|
@ -434,10 +412,6 @@ static void wgettext_init_if_necessary() {
|
||||||
pthread_once(&once, wgettext_really_init);
|
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) {
|
const wcstring &wgettext(const wchar_t *in) {
|
||||||
// Preserve errno across this since this is often used in printing error messages.
|
// Preserve errno across this since this is often used in printing error messages.
|
||||||
int err = errno;
|
int err = errno;
|
||||||
|
@ -447,7 +421,7 @@ const wcstring &wgettext(const wchar_t *in) {
|
||||||
scoped_lock locker(wgettext_lock);
|
scoped_lock locker(wgettext_lock);
|
||||||
wcstring &val = wgettext_map[key];
|
wcstring &val = wgettext_map[key];
|
||||||
if (val.empty()) {
|
if (val.empty()) {
|
||||||
std::string mbs_in = wcs2string(key);
|
cstring mbs_in = wcs2string(key);
|
||||||
char *out = fish_gettext(mbs_in.c_str());
|
char *out = fish_gettext(mbs_in.c_str());
|
||||||
val = format_string(L"%s", out);
|
val = format_string(L"%s", out);
|
||||||
}
|
}
|
||||||
|
@ -458,16 +432,14 @@ const wcstring &wgettext(const wchar_t *in) {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wide character version of mkdir.
|
|
||||||
int wmkdir(const wcstring &name, int mode) {
|
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);
|
return mkdir(name_narrow.c_str(), mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wide character version of rename.
|
|
||||||
int wrename(const wcstring &old, const wcstring &newv) {
|
int wrename(const wcstring &old, const wcstring &newv) {
|
||||||
std::string old_narrow = wcs2string(old);
|
cstring old_narrow = wcs2string(old);
|
||||||
std::string new_narrow = wcs2string(newv);
|
cstring new_narrow = wcs2string(newv);
|
||||||
return rename(old_narrow.c_str(), new_narrow.c_str());
|
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.
|
/// See fallback.h for the normal definitions.
|
||||||
int fish_wcswidth(const wcstring &str) { return fish_wcswidth(str.c_str(), str.size()); }
|
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) {
|
file_id_t file_id_t::file_id_from_stat(const struct stat *buf) {
|
||||||
assert(buf != NULL);
|
assert(buf != NULL);
|
||||||
|
|
||||||
|
|
37
src/wutil.h
37
src/wutil.h
|
@ -10,53 +10,85 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
/// Wide character version of fopen(). This sets CLO_EXEC.
|
||||||
FILE *wfopen(const wcstring &path, const char *mode);
|
FILE *wfopen(const wcstring &path, const char *mode);
|
||||||
|
|
||||||
|
/// Sets CLO_EXEC on a given fd.
|
||||||
bool set_cloexec(int 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);
|
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);
|
int make_fd_nonblocking(int fd);
|
||||||
|
|
||||||
|
/// Mark an fd as blocking; returns errno or 0 on success.
|
||||||
int make_fd_blocking(int fd);
|
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);
|
DIR *wopendir(const wcstring &name);
|
||||||
|
|
||||||
|
/// Wide character version of stat().
|
||||||
int wstat(const wcstring &file_name, struct stat *buf);
|
int wstat(const wcstring &file_name, struct stat *buf);
|
||||||
|
|
||||||
|
/// Wide character version of lstat().
|
||||||
int lwstat(const wcstring &file_name, struct stat *buf);
|
int lwstat(const wcstring &file_name, struct stat *buf);
|
||||||
|
|
||||||
|
/// Wide character version of access().
|
||||||
int waccess(const wcstring &pathname, int mode);
|
int waccess(const wcstring &pathname, int mode);
|
||||||
|
|
||||||
|
/// Wide character version of unlink().
|
||||||
int wunlink(const wcstring &pathname);
|
int wunlink(const wcstring &pathname);
|
||||||
|
|
||||||
|
/// Wide character version of perror().
|
||||||
void wperror(const wchar_t *s);
|
void wperror(const wchar_t *s);
|
||||||
|
|
||||||
|
/// Async-safe version of perror().
|
||||||
void safe_perror(const char *message);
|
void safe_perror(const char *message);
|
||||||
|
|
||||||
|
/// Async-safe version of strerror().
|
||||||
const char *safe_strerror(int err);
|
const char *safe_strerror(int err);
|
||||||
|
|
||||||
|
/// Wide character version of getcwd().
|
||||||
const wcstring wgetcwd();
|
const wcstring wgetcwd();
|
||||||
|
|
||||||
|
/// Wide character version of chdir().
|
||||||
int wchdir(const wcstring &dir);
|
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);
|
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(DIR *dir, std::wstring &out_name);
|
||||||
|
|
||||||
bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name,
|
bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name,
|
||||||
bool *out_is_dir);
|
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);
|
bool wreaddir_for_dirs(DIR *dir, wcstring *out_name);
|
||||||
|
|
||||||
|
/// Wide character version of dirname().
|
||||||
std::wstring wdirname(const std::wstring &path);
|
std::wstring wdirname(const std::wstring &path);
|
||||||
|
|
||||||
|
/// Wide character version of basename().
|
||||||
std::wstring wbasename(const std::wstring &path);
|
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);
|
const wcstring &wgettext(const wchar_t *in);
|
||||||
|
|
||||||
|
/// Wide character version of mkdir.
|
||||||
int wmkdir(const wcstring &dir, int mode);
|
int wmkdir(const wcstring &dir, int mode);
|
||||||
|
|
||||||
|
/// Wide character version of rename.
|
||||||
int wrename(const wcstring &oldName, const wcstring &newName);
|
int wrename(const wcstring &oldName, const wcstring &newName);
|
||||||
|
|
||||||
#define PUA1_START 0xE000
|
#define PUA1_START 0xE000
|
||||||
|
@ -84,6 +116,9 @@ bool wcsvarchr(wchar_t chr);
|
||||||
int fish_wcswidth(const wchar_t *str);
|
int fish_wcswidth(const wchar_t *str);
|
||||||
int fish_wcswidth(const wcstring &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
|
/// 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
|
/// 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
|
/// seems to aggressively re-use inodes, so it cannot determine if a file has been deleted (ABA
|
||||||
|
|
Loading…
Reference in a new issue