aboutsummaryrefslogtreecommitdiffhomepage
path: root/common.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common.cpp')
-rw-r--r--common.cpp1407
1 files changed, 705 insertions, 702 deletions
diff --git a/common.cpp b/common.cpp
index 203eda52..3440f075 100644
--- a/common.cpp
+++ b/common.cpp
@@ -24,7 +24,6 @@ parts of fish.
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
-#include <pwd.h>
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
@@ -46,9 +45,10 @@ parts of fish.
#include <execinfo.h>
#endif
-
#if HAVE_NCURSES_H
#include <ncurses.h>
+#elif HAVE_NCURSES_CURSES_H
+#include <ncurses/curses.h>
#else
#include <curses.h>
#endif
@@ -73,6 +73,7 @@ parts of fish.
#include "util.cpp"
#include "fallback.cpp"
+#define NOT_A_WCHAR (static_cast<wint_t>(WEOF))
struct termios shell_modes;
@@ -83,16 +84,19 @@ static bool thread_assertions_configured_for_testing = false;
wchar_t ellipsis_char;
wchar_t omitted_newline_char;
-char *profile=0;
+bool g_profiling_active = false;
const wchar_t *program_name;
int debug_level=1;
/**
- This struct should be continually updated by signals as the term resizes, and as such always contain the correct current size.
+ This struct maintains the current state of the terminal size. It is updated on demand after receiving a SIGWINCH.
+ Do not touch this struct directly, it's managed with a rwlock. Use common_get_width()/common_get_height().
*/
static struct winsize termsize;
+static volatile bool termsize_valid;
+static rwlock_t termsize_rwlock;
static char *wcs2str_internal(const wchar_t *in, char *out);
@@ -105,7 +109,7 @@ void show_stackframe()
return;
void *trace[32];
- int i, trace_size = 0;
+ int trace_size = 0;
trace_size = backtrace(trace, 32);
char **messages = backtrace_symbols(trace, trace_size);
@@ -113,7 +117,7 @@ void show_stackframe()
if (messages)
{
debug(0, L"Backtrace:");
- for (i=0; i<trace_size; i++)
+ for (int i=0; i<trace_size; i++)
{
fwprintf(stderr, L"%s\n", messages[i]);
}
@@ -213,14 +217,14 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len)
wc = ENCODE_DIRECT_BASE + (unsigned char)in[in_pos];
result.push_back(wc);
in_pos++;
- bzero(&state, sizeof state);
+ memset(&state, 0, sizeof state);
}
else if (ret == 0)
{
/* Embedded null byte! */
result.push_back(L'\0');
in_pos++;
- bzero(&state, sizeof state);
+ memset(&state, 0, sizeof state);
}
else
{
@@ -252,7 +256,6 @@ char *wcs2str(const wchar_t *in)
{
if (! in)
return NULL;
- char *out;
size_t desired_size = MAX_UTF8_BYTES*wcslen(in)+1;
char local_buff[512];
if (desired_size <= sizeof local_buff / sizeof *local_buff)
@@ -274,15 +277,13 @@ char *wcs2str(const wchar_t *in)
else
{
// here we fall into the bad case of allocating a buffer probably much larger than necessary
- out = (char *)malloc(MAX_UTF8_BYTES*wcslen(in)+1);
+ char *out = (char *)malloc(MAX_UTF8_BYTES*wcslen(in)+1);
if (!out)
{
DIE_MEM();
}
return wcs2str_internal(in, out);
}
-
- return wcs2str_internal(in, out);
}
char *wcs2str(const wcstring &in)
@@ -314,7 +315,7 @@ std::string wcs2string(const wcstring &input)
}
else
{
- bzero(converted, sizeof converted);
+ memset(converted, 0, sizeof converted);
size_t len = wcrtomb(converted, wc, &state);
if (len == (size_t)(-1))
{
@@ -382,27 +383,6 @@ static char *wcs2str_internal(const wchar_t *in, char *out)
return out;
}
-char **wcsv2strv(const wchar_t * const *in)
-{
- size_t i, count = 0;
-
- while (in[count] != 0)
- count++;
- char **res = (char **)malloc(sizeof(char *)*(count+1));
- if (res == 0)
- {
- DIE_MEM();
- }
-
- for (i=0; i<count; i++)
- {
- res[i]=wcs2str(in[i]);
- }
- res[count]=0;
- return res;
-
-}
-
wcstring format_string(const wchar_t *format, ...)
{
va_list va;
@@ -412,7 +392,7 @@ wcstring format_string(const wchar_t *format, ...)
return result;
}
-wcstring vformat_string(const wchar_t *format, va_list va_orig)
+void append_formatv(wcstring &target, const wchar_t *format, va_list va_orig)
{
const int saved_err = errno;
/*
@@ -463,22 +443,21 @@ wcstring vformat_string(const wchar_t *format, va_list va_orig)
va_end(va);
}
- wcstring result = wcstring(buff);
+ target.append(buff);
if (buff != static_buff)
+ {
free(buff);
+ }
errno = saved_err;
- return result;
}
-void append_formatv(wcstring &str, const wchar_t *format, va_list ap)
+wcstring vformat_string(const wchar_t *format, va_list va_orig)
{
- /* Preserve errno across this call since it likes to stomp on it */
- int err = errno;
- str.append(vformat_string(format, ap));
- errno = err;
-
+ wcstring result;
+ append_formatv(result, format, va_orig);
+ return result;
}
void append_format(wcstring &str, const wchar_t *format, ...)
@@ -489,17 +468,17 @@ void append_format(wcstring &str, const wchar_t *format, ...)
va_end(va);
}
-wchar_t *wcsvarname(const wchar_t *str)
+const wchar_t *wcsvarname(const wchar_t *str)
{
while (*str)
{
if ((!iswalnum(*str)) && (*str != L'_'))
{
- return (wchar_t *)str;
+ return str;
}
str++;
}
- return 0;
+ return NULL;
}
const wchar_t *wcsfuncname(const wchar_t *str)
@@ -508,18 +487,19 @@ const wchar_t *wcsfuncname(const wchar_t *str)
}
-int wcsvarchr(wchar_t chr)
+bool wcsvarchr(wchar_t chr)
{
return iswalnum(chr) || chr == L'_';
}
+int fish_wcswidth(const wchar_t *str)
+{
+ return fish_wcswidth(str, wcslen(str));
+}
-/**
- The glibc version of wcswidth seems to hang on some strings. fish uses this replacement.
-*/
-int my_wcswidth(const wchar_t *c)
+int fish_wcswidth(const wcstring& str)
{
- return fish_wcswidth(c, wcslen(c));
+ return fish_wcswidth(str.c_str(), str.size());
}
wchar_t *quote_end(const wchar_t *pos)
@@ -576,7 +556,7 @@ wcstring wsetlocale(int category, const wchar_t *locale)
return format_string(L"%s", res);
}
-bool contains_internal(const wchar_t *a, ...)
+bool contains_internal(const wchar_t *a, int vararg_handle, ...)
{
const wchar_t *arg;
va_list va;
@@ -584,7 +564,7 @@ bool contains_internal(const wchar_t *a, ...)
CHECK(a, 0);
- va_start(va, a);
+ va_start(va, vararg_handle);
while ((arg=va_arg(va, const wchar_t *))!= 0)
{
if (wcscmp(a,arg) == 0)
@@ -598,17 +578,19 @@ bool contains_internal(const wchar_t *a, ...)
return res;
}
-/* wcstring variant of contains_internal. The first parameter is a wcstring, the rest are const wchar_t* */
-__sentinel bool contains_internal(const wcstring &needle, ...)
+/* wcstring variant of contains_internal. The first parameter is a wcstring, the rest are const wchar_t *. vararg_handle exists only to give us a POD-value to apss to va_start */
+__sentinel bool contains_internal(const wcstring &needle, int vararg_handle, ...)
{
const wchar_t *arg;
va_list va;
int res = 0;
- va_start(va, needle);
+ const wchar_t *needle_cstr = needle.c_str();
+ va_start(va, vararg_handle);
while ((arg=va_arg(va, const wchar_t *))!= 0)
{
- if (needle == arg)
+ /* libc++ has an unfortunate implementation of operator== that unconditonally wcslen's the wchar_t* parameter, so prefer wcscmp directly */
+ if (! wcscmp(needle_cstr, arg))
{
res=1;
break;
@@ -711,6 +693,23 @@ void debug(int level, const char *msg, ...)
errno = errno_old;
}
+void print_stderr(const wcstring &str)
+{
+ fprintf(stderr, "%ls\n", str.c_str());
+}
+
+void read_ignore(int fd, void *buff, size_t count)
+{
+ size_t ignore __attribute__((unused));
+ ignore = read(fd, buff, count);
+}
+
+void write_ignore(int fd, const void *buff, size_t count)
+{
+ size_t ignore __attribute__((unused));
+ ignore = write(fd, buff, count);
+}
+
void debug_safe(int level, const char *msg, const char *param1, const char *param2, const char *param3, const char *param4, const char *param5, const char *param6, const char *param7, const char *param8, const char *param9, const char *param10, const char *param11, const char *param12)
{
@@ -731,7 +730,7 @@ void debug_safe(int level, const char *msg, const char *param1, const char *para
if (end == NULL)
end = cursor + strlen(cursor);
- write(STDERR_FILENO, cursor, end - cursor);
+ write_ignore(STDERR_FILENO, cursor, end - cursor);
if (end[0] == '%' && end[1] == 's')
{
@@ -740,7 +739,7 @@ void debug_safe(int level, const char *msg, const char *param1, const char *para
const char *format = params[param_idx++];
if (! format)
format = "(null)";
- write(STDERR_FILENO, format, strlen(format));
+ write_ignore(STDERR_FILENO, format, strlen(format));
cursor = end + 2;
}
else if (end[0] == '\0')
@@ -756,12 +755,12 @@ void debug_safe(int level, const char *msg, const char *param1, const char *para
}
// We always append a newline
- write(STDERR_FILENO, "\n", 1);
+ write_ignore(STDERR_FILENO, "\n", 1);
errno = errno_old;
}
-void format_long_safe(char buff[128], long val)
+void format_long_safe(char buff[64], long val)
{
if (val == 0)
{
@@ -795,7 +794,7 @@ void format_long_safe(char buff[128], long val)
}
}
-void format_long_safe(wchar_t buff[128], long val)
+void format_long_safe(wchar_t buff[64], long val)
{
if (val == 0)
{
@@ -807,10 +806,9 @@ void format_long_safe(wchar_t buff[128], long val)
size_t idx = 0;
bool negative = (val < 0);
- while (val > 0)
+ while (val != 0)
{
long rem = val % 10;
- /* Here we're assuming that wide character digits are contiguous - is that a correct assumption? */
buff[idx++] = L'0' + (wchar_t)(rem < 0 ? -rem : rem);
val /= 10;
}
@@ -830,19 +828,18 @@ void format_long_safe(wchar_t buff[128], long val)
void write_screen(const wcstring &msg, wcstring &buff)
{
- const wchar_t *start, *pos;
int line_width = 0;
- int tok_width = 0;
int screen_width = common_get_width();
if (screen_width)
{
- start = pos = msg.c_str();
+ const wchar_t *start = msg.c_str();
+ const wchar_t *pos = start;
while (1)
{
int overflow = 0;
- tok_width=0;
+ int tok_width=0;
/*
Tokenize on whitespace, and also calculate the width of the token
@@ -917,61 +914,28 @@ void write_screen(const wcstring &msg, wcstring &buff)
buff.push_back(L'\n');
}
-/**
- Perform string escaping of a strinng by only quoting it. Assumes
- the string has already been checked for characters that can not be
- escaped this way.
- */
-static wchar_t *escape_simple(const wchar_t *in)
-{
- wchar_t *out;
- size_t len = wcslen(in);
- out = (wchar_t *)malloc(sizeof(wchar_t)*(len+3));
- if (!out)
- DIE_MEM();
-
- out[0] = L'\'';
- wcscpy(&out[1], in);
- out[len+1]=L'\'';
- out[len+2]=0;
- return out;
-}
-
-wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
+/* Escape a string, storing the result in out_str */
+static void escape_string_internal(const wchar_t *orig_in, size_t in_len, wcstring *out_str, escape_flags_t flags)
{
- const wchar_t *in = in_orig;
+ assert(orig_in != NULL);
+ const wchar_t *in = orig_in;
bool escape_all = !!(flags & ESCAPE_ALL);
bool no_quoted = !!(flags & ESCAPE_NO_QUOTED);
bool no_tilde = !!(flags & ESCAPE_NO_TILDE);
- wchar_t *out;
- wchar_t *pos;
-
int need_escape=0;
int need_complex_escape=0;
- if (!in)
- {
- debug(0, L"%s called with null input", __func__);
- FATAL_EXIT();
- }
+ /* Avoid dereferencing all over the place */
+ wcstring &out = *out_str;
- if (!no_quoted && (wcslen(in) == 0))
+ if (!no_quoted && in_len == 0)
{
- out = wcsdup(L"''");
- if (!out)
- DIE_MEM();
- return out;
+ out.assign(L"''");
+ return;
}
-
- out = (wchar_t *)malloc(sizeof(wchar_t)*(wcslen(in)*4 + 1));
- pos = out;
-
- if (!out)
- DIE_MEM();
-
while (*in != 0)
{
@@ -981,14 +945,14 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
int val = *in - ENCODE_DIRECT_BASE;
int tmp;
- *(pos++) = L'\\';
- *(pos++) = L'X';
+ out += L'\\';
+ out += L'X';
tmp = val/16;
- *pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp;
+ out += tmp > 9? L'a'+(tmp-10):L'0'+tmp;
tmp = val%16;
- *pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp;
+ out += tmp > 9? L'a'+(tmp-10):L'0'+tmp;
need_escape=need_complex_escape=1;
}
@@ -998,32 +962,32 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
switch (c)
{
case L'\t':
- *(pos++) = L'\\';
- *(pos++) = L't';
+ out += L'\\';
+ out += L't';
need_escape=need_complex_escape=1;
break;
case L'\n':
- *(pos++) = L'\\';
- *(pos++) = L'n';
+ out += L'\\';
+ out += L'n';
need_escape=need_complex_escape=1;
break;
case L'\b':
- *(pos++) = L'\\';
- *(pos++) = L'b';
+ out += L'\\';
+ out += L'b';
need_escape=need_complex_escape=1;
break;
case L'\r':
- *(pos++) = L'\\';
- *(pos++) = L'r';
+ out += L'\\';
+ out += L'r';
need_escape=need_complex_escape=1;
break;
case L'\x1b':
- *(pos++) = L'\\';
- *(pos++) = L'e';
+ out += L'\\';
+ out += L'e';
need_escape=need_complex_escape=1;
break;
@@ -1033,11 +997,26 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
{
need_escape=need_complex_escape=1;
if (escape_all)
- *pos++ = L'\\';
- *pos++ = *in;
+ out += L'\\';
+ out += *in;
break;
}
+
+
+ // Experimental fix for #1614
+ // The hope is that any time these appear in a string, they came from wildcard expansion
+ case ANY_CHAR:
+ out += L'?';
+ break;
+ case ANY_STRING:
+ out += L'*';
+ break;
+
+ case ANY_STRING_RECURSIVE:
+ out += L"**";
+ break;
+
case L'&':
case L'$':
case L' ':
@@ -1063,9 +1042,9 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
{
need_escape=1;
if (escape_all)
- *pos++ = L'\\';
+ out += L'\\';
}
- *pos++ = *in;
+ out += *in;
break;
}
@@ -1075,9 +1054,9 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
{
if (*in <27 && *in > 0)
{
- *(pos++) = L'\\';
- *(pos++) = L'c';
- *(pos++) = L'a' + *in -1;
+ out += L'\\';
+ out += L'c';
+ out += L'a' + *in -1;
need_escape=need_complex_escape=1;
break;
@@ -1086,15 +1065,15 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
int tmp = (*in)%16;
- *pos++ = L'\\';
- *pos++ = L'x';
- *pos++ = ((*in>15)? L'1' : L'0');
- *pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp;
+ out += L'\\';
+ out += L'x';
+ out += ((*in>15)? L'1' : L'0');
+ out += tmp > 9? L'a'+(tmp-10):L'0'+tmp;
need_escape=need_complex_escape=1;
}
else
{
- *pos++ = *in;
+ out += *in;
}
break;
}
@@ -1103,7 +1082,6 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
in++;
}
- *pos = 0;
/*
Use quoted escaping if possible, since most people find it
@@ -1111,627 +1089,639 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
*/
if (!no_quoted && need_escape && !need_complex_escape && escape_all)
{
- free(out);
- out = escape_simple(in_orig);
+ wchar_t single_quote = L'\'';
+ out.clear();
+ out.reserve(2 + in_len);
+ out.push_back(single_quote);
+ out.append(orig_in, in_len);
+ out.push_back(single_quote);
}
+}
- return out;
+wcstring escape(const wchar_t *in, escape_flags_t flags)
+{
+ if (!in)
+ {
+ debug(0, L"%s called with null input", __func__);
+ FATAL_EXIT();
+ }
+
+ wcstring result;
+ escape_string_internal(in, wcslen(in), &result, flags);
+ return result;
}
wcstring escape_string(const wcstring &in, escape_flags_t flags)
{
- wchar_t *tmp = escape(in.c_str(), flags);
- wcstring result(tmp);
- free(tmp);
+ wcstring result;
+ escape_string_internal(in.c_str(), in.size(), &result, flags);
return result;
}
-wchar_t *unescape(const wchar_t * orig, int flags)
+/* Helper to return the last character in a string, or NOT_A_WCHAR */
+static wint_t string_last_char(const wcstring &str)
{
- int out_pos;
- size_t in_pos;
- size_t len;
- int c;
- int bracket_count=0;
- wchar_t prev=0;
- wchar_t *in;
- bool unescape_special = !!(flags & UNESCAPE_SPECIAL);
- bool allow_incomplete = !!(flags & UNESCAPE_INCOMPLETE);
-
- CHECK(orig, 0);
+ size_t len = str.size();
+ return len == 0 ? NOT_A_WCHAR : str.at(len - 1);
+}
- len = wcslen(orig);
- in = wcsdup(orig);
+/* Given a null terminated string starting with a backslash, read the escape as if it is unquoted, appending to result. Return the number of characters consumed, or 0 on error */
+static size_t read_unquoted_escape(const wchar_t *input, wcstring *result, bool allow_incomplete, bool unescape_special)
+{
+ if (input[0] != L'\\')
+ {
+ // not an escape
+ return 0;
+ }
- if (!in)
- DIE_MEM();
+ /* Here's the character we'll ultimately append, or NOT_A_WCHAR for none. Note that L'\0' is a valid thing to append. */
+ wint_t result_char_or_none = NOT_A_WCHAR;
- enum
+ bool errored = false;
+ size_t in_pos = 1; //in_pos always tracks the next character to read (and therefore the number of characters read so far)
+ const wchar_t c = input[in_pos++];
+ switch (c)
{
- mode_unquoted,
- mode_single_quotes,
- mode_double_quotes
- } mode = mode_unquoted;
- for (in_pos=0, out_pos=0;
- in_pos<len;
- (prev=(out_pos>=0)?in[out_pos]:0), out_pos++, in_pos++)
- {
- c = in[in_pos];
- switch (mode)
+ /* A null character after a backslash is an error */
+ case L'\0':
{
+ /* Adjust in_pos to only include the backslash */
+ assert(in_pos > 0);
+ in_pos--;
- /*
- Mode 0 means unquoted string
- */
- case mode_unquoted:
+ /* It's an error, unless we're allowing incomplete escapes */
+ if (! allow_incomplete)
+ errored = true;
+ break;
+ }
+
+ /* Numeric escape sequences. No prefix means octal escape, otherwise hexadecimal. */
+ case L'0':
+ case L'1':
+ case L'2':
+ case L'3':
+ case L'4':
+ case L'5':
+ case L'6':
+ case L'7':
+ case L'u':
+ case L'U':
+ case L'x':
+ case L'X':
+ {
+ long long res=0;
+ size_t chars=2;
+ int base=16;
+
+ bool byte_literal = false;
+ wchar_t max_val = ASCII_MAX;
+
+ switch (c)
{
- if (c == L'\\')
+ case L'u':
{
- switch (in[++in_pos])
- {
-
- /*
- A null character after a backslash is an
- error, return null
- */
- case L'\0':
- {
- if (!allow_incomplete)
- {
- free(in);
- return 0;
- }
- }
+ chars=4;
+ max_val = UCS2_MAX;
+ break;
+ }
- /*
- Numeric escape sequences. No prefix means
- octal escape, otherwise hexadecimal.
- */
-
- case L'0':
- case L'1':
- case L'2':
- case L'3':
- case L'4':
- case L'5':
- case L'6':
- case L'7':
- case L'u':
- case L'U':
- case L'x':
- case L'X':
- {
- int i;
- long long res=0;
- int chars=2;
- int base=16;
+ case L'U':
+ {
+ chars=8;
+ max_val = WCHAR_MAX;
- int byte = 0;
- wchar_t max_val = ASCII_MAX;
+ // Don't exceed the largest Unicode code point - see #1107
+ if (0x10FFFF < max_val)
+ max_val = (wchar_t)0x10FFFF;
- switch (in[in_pos])
- {
- case L'u':
- {
- chars=4;
- max_val = UCS2_MAX;
- break;
- }
-
- case L'U':
- {
- chars=8;
- max_val = WCHAR_MAX;
- break;
- }
-
- case L'x':
- {
- break;
- }
-
- case L'X':
- {
- byte=1;
- max_val = BYTE_MAX;
- break;
- }
-
- default:
- {
- base=8;
- chars=3;
- // note in_pod must be larger than 0 since we incremented it above
- assert(in_pos > 0);
- in_pos--;
- break;
- }
- }
+ break;
+ }
- for (i=0; i<chars; i++)
- {
- long d = convert_digit(in[++in_pos],base);
+ case L'x':
+ {
+ chars = 2;
+ max_val = ASCII_MAX;
+ break;
+ }
- if (d < 0)
- {
- in_pos--;
- break;
- }
+ case L'X':
+ {
+ byte_literal = true;
+ max_val = BYTE_MAX;
+ break;
+ }
- res=(res*base)+d;
- }
+ default:
+ {
+ base=8;
+ chars=3;
+ // note that in_pos currently is just after the first post-backslash character; we want to start our escape from there
+ assert(in_pos > 0);
+ in_pos--;
+ break;
+ }
+ }
- if ((res <= max_val))
- {
- in[out_pos] = (wchar_t)((byte?ENCODE_DIRECT_BASE:0)+res);
- }
- else
- {
- free(in);
- return 0;
- }
+ for (size_t i=0; i<chars; i++)
+ {
+ long d = convert_digit(input[in_pos],base);
+ if (d < 0)
+ {
+ break;
+ }
- break;
- }
+ res=(res*base)+d;
+ in_pos++;
+ }
- /*
- \a means bell (alert)
- */
- case L'a':
- {
- in[out_pos]=L'\a';
- break;
- }
+ if (res <= max_val)
+ {
+ result_char_or_none = (wchar_t)((byte_literal ? ENCODE_DIRECT_BASE : 0)+res);
+ }
+ else
+ {
+ errored = true;
+ }
- /*
- \b means backspace
- */
- case L'b':
- {
- in[out_pos]=L'\b';
- break;
- }
+ break;
+ }
- /*
- \cX means control sequence X
- */
- case L'c':
- {
- in_pos++;
- if (in[in_pos] >= L'a' &&
- in[in_pos] <= (L'a'+32))
- {
- in[out_pos]=in[in_pos]-L'a'+1;
- }
- else if (in[in_pos] >= L'A' &&
- in[in_pos] <= (L'A'+32))
- {
- in[out_pos]=in[in_pos]-L'A'+1;
- }
- else
- {
- free(in);
- return 0;
- }
- break;
+ /* \a means bell (alert) */
+ case L'a':
+ {
+ result_char_or_none = L'\a';
+ break;
+ }
- }
+ /* \b means backspace */
+ case L'b':
+ {
+ result_char_or_none = L'\b';
+ break;
+ }
- /*
- \x1b means escape
- */
- case L'e':
- {
- in[out_pos]=L'\x1b';
- break;
- }
+ /* \cX means control sequence X */
+ case L'c':
+ {
+ const wchar_t sequence_char = input[in_pos++];
+ if (sequence_char >= L'a' && sequence_char <= (L'a'+32))
+ {
+ result_char_or_none = sequence_char-L'a'+1;
+ }
+ else if (sequence_char >= L'A' && sequence_char <= (L'A'+32))
+ {
+ result_char_or_none = sequence_char-L'A'+1;
+ }
+ else
+ {
+ errored = true;
+ }
+ break;
+ }
- /*
- \f means form feed
- */
- case L'f':
- {
- in[out_pos]=L'\f';
- break;
- }
+ /* \x1b means escape */
+ case L'e':
+ {
+ result_char_or_none = L'\x1b';
+ break;
+ }
- /*
- \n means newline
- */
- case L'n':
- {
- in[out_pos]=L'\n';
- break;
- }
+ /*
+ \f means form feed
+ */
+ case L'f':
+ {
+ result_char_or_none = L'\f';
+ break;
+ }
- /*
- \r means carriage return
- */
- case L'r':
- {
- in[out_pos]=L'\r';
- break;
- }
+ /*
+ \n means newline
+ */
+ case L'n':
+ {
+ result_char_or_none = L'\n';
+ break;
+ }
- /*
- \t means tab
- */
- case L't':
- {
- in[out_pos]=L'\t';
- break;
- }
+ /*
+ \r means carriage return
+ */
+ case L'r':
+ {
+ result_char_or_none = L'\r';
+ break;
+ }
- /*
- \v means vertical tab
- */
- case L'v':
- {
- in[out_pos]=L'\v';
- break;
- }
+ /*
+ \t means tab
+ */
+ case L't':
+ {
+ result_char_or_none = L'\t';
+ break;
+ }
- /* If a backslash is followed by an actual newline, swallow them both */
- case L'\n':
- out_pos--;
- break;
+ /*
+ \v means vertical tab
+ */
+ case L'v':
+ {
+ result_char_or_none = L'\v';
+ break;
+ }
- default:
- {
- if (unescape_special)
- in[out_pos++] = INTERNAL_SEPARATOR;
- in[out_pos]=in[in_pos];
- break;
- }
- }
- }
- else
- {
- switch (in[in_pos])
- {
- case L'~':
- {
- if (unescape_special && (in_pos == 0))
- {
- in[out_pos]=HOME_DIRECTORY;
- }
- else
- {
- in[out_pos] = L'~';
- }
- break;
- }
+ /* If a backslash is followed by an actual newline, swallow them both */
+ case L'\n':
+ {
+ result_char_or_none = NOT_A_WCHAR;
+ break;
+ }
- case L'%':
- {
- if (unescape_special && (in_pos == 0))
- {
- in[out_pos]=PROCESS_EXPAND;
- }
- else
- {
- in[out_pos]=in[in_pos];
- }
- break;
- }
+ default:
+ {
+ if (unescape_special)
+ result->push_back(INTERNAL_SEPARATOR);
+ result_char_or_none = c;
+ break;
+ }
+ }
- case L'*':
- {
- if (unescape_special)
- {
- if (out_pos > 0 && in[out_pos-1]==ANY_STRING)
- {
- out_pos--;
- in[out_pos] = ANY_STRING_RECURSIVE;
- }
- else
- in[out_pos]=ANY_STRING;
- }
- else
- {
- in[out_pos]=in[in_pos];
- }
- break;
- }
+ if (! errored && result_char_or_none != NOT_A_WCHAR)
+ {
+ wchar_t result_char = static_cast<wchar_t>(result_char_or_none);
+ // if result_char is not NOT_A_WCHAR, it must be a valid wchar
+ assert((wint_t)result_char == result_char_or_none);
+ result->push_back(result_char);
+ }
+ return errored ? 0 : in_pos;
+}
- case L'?':
- {
- if (unescape_special)
- {
- in[out_pos]=ANY_CHAR;
- }
- else
- {
- in[out_pos]=in[in_pos];
- }
- break;
- }
+/* Returns the unescaped version of input_str into output_str (by reference). Returns true if successful. If false, the contents of output_str are undefined (!) */
+static bool unescape_string_internal(const wchar_t * const input, const size_t input_len, wcstring *output_str, unescape_flags_t flags)
+{
+ /* Set up result string, which we'll swap with the output on success */
+ wcstring result;
+ result.reserve(input_len);
- case L'$':
- {
- if (unescape_special)
- {
- in[out_pos]=VARIABLE_EXPAND;
- }
- else
- {
- in[out_pos]=in[in_pos];
- }
- break;
- }
+ const bool unescape_special = !!(flags & UNESCAPE_SPECIAL);
+ const bool allow_incomplete = !!(flags & UNESCAPE_INCOMPLETE);
- case L'{':
- {
- if (unescape_special)
- {
- bracket_count++;
- in[out_pos]=BRACKET_BEGIN;
- }
- else
- {
- in[out_pos]=in[in_pos];
- }
- break;
- }
+ int bracket_count = 0;
- case L'}':
- {
- if (unescape_special)
- {
- bracket_count--;
- in[out_pos]=BRACKET_END;
- }
- else
- {
- in[out_pos]=in[in_pos];
- }
- break;
- }
+ bool errored = false;
+ enum
+ {
+ mode_unquoted,
+ mode_single_quotes,
+ mode_double_quotes
+ } mode = mode_unquoted;
- case L',':
- {
- if (unescape_special && bracket_count && prev!=BRACKET_SEP)
- {
- in[out_pos]=BRACKET_SEP;
- }
- else
- {
- in[out_pos]=in[in_pos];
- }
- break;
- }
+ for (size_t input_position = 0; input_position < input_len && ! errored; input_position++)
+ {
+ const wchar_t c = input[input_position];
+ /* Here's the character we'll append to result, or NOT_A_WCHAR to suppress it */
+ wint_t to_append_or_none = c;
+ if (mode == mode_unquoted)
+ {
- case L'\'':
- {
- mode = mode_single_quotes;
- if (unescape_special)
- in[out_pos] = INTERNAL_SEPARATOR;
- else
- out_pos--;
- break;
- }
+ switch (c)
+ {
+ case L'\\':
+ {
+ /* Backslashes (escapes) are complicated and may result in errors, or appending INTERNAL_SEPARATORs, so we have to handle them specially */
+ size_t escape_chars = read_unquoted_escape(input + input_position, &result, allow_incomplete, unescape_special);
+ if (escape_chars == 0)
+ {
+ /* A 0 return indicates an error */
+ errored = true;
+ }
+ else
+ {
+ /* Skip over the characters we read, minus one because the outer loop will increment it */
+ assert(escape_chars > 0);
+ input_position += escape_chars - 1;
+ }
+ /* We've already appended, don't append anything else */
+ to_append_or_none = NOT_A_WCHAR;
+ break;
+ }
- case L'\"':
- {
- mode = mode_double_quotes;
- if (unescape_special)
- in[out_pos] = INTERNAL_SEPARATOR;
- else
- out_pos--;
- break;
- }
+ case L'~':
+ {
+ if (unescape_special && (input_position == 0))
+ {
+ to_append_or_none = HOME_DIRECTORY;
+ }
+ break;
+ }
- default:
- {
- in[out_pos] = in[in_pos];
- break;
- }
+ case L'%':
+ {
+ if (unescape_special && (input_position == 0))
+ {
+ to_append_or_none = PROCESS_EXPAND;
}
+ break;
}
- break;
- }
- /*
- Mode 1 means single quoted string, i.e 'foo'.
- A backslash at the end of a line in a single quoted string does not swallow the backslash or newline.
- */
- case mode_single_quotes:
- {
- if (c == L'\\')
+ case L'*':
{
- switch (in[++in_pos])
+ if (unescape_special)
{
- case '\\':
- case L'\'':
+ /* In general, this is ANY_STRING. But as a hack, if the last appended char is ANY_STRING, delete the last char and store ANY_STRING_RECURSIVE to reflect the fact that ** is the recursive wildcard. */
+ if (string_last_char(result) == ANY_STRING)
{
- in[out_pos]=in[in_pos];
- break;
+ assert(result.size() > 0);
+ result.resize(result.size() - 1);
+ to_append_or_none = ANY_STRING_RECURSIVE;
}
-
- case L'\0':
+ else
{
- if (!allow_incomplete)
- {
- free(in);
- return 0;
- }
- else
- {
- //We may ever escape a NULL character, but still appending a \ in case I am wrong.
- in[out_pos] = L'\\';
- }
+ to_append_or_none = ANY_STRING;
}
- break;
+ }
+ break;
+ }
- default:
- {
- in[out_pos++] = L'\\';
- in[out_pos]= in[in_pos];
- }
+ case L'?':
+ {
+ if (unescape_special)
+ {
+ to_append_or_none = ANY_CHAR;
}
+ break;
+ }
+ case L'$':
+ {
+ if (unescape_special)
+ {
+ to_append_or_none = VARIABLE_EXPAND;
+ }
+ break;
}
- if (c == L'\'')
+
+ case L'{':
{
if (unescape_special)
- in[out_pos] = INTERNAL_SEPARATOR;
- else
- out_pos--;
- mode = mode_unquoted;
+ {
+ bracket_count++;
+ to_append_or_none = BRACKET_BEGIN;
+ }
+ break;
}
- else
+
+ case L'}':
{
- in[out_pos] = in[in_pos];
+ if (unescape_special)
+ {
+ bracket_count--;
+ to_append_or_none = BRACKET_END;
+ }
+ break;
}
- break;
- }
+ case L',':
+ {
+ /* If the last character was a separator, then treat this as a literal comma */
+ if (unescape_special && bracket_count > 0 && string_last_char(result) != BRACKET_SEP)
+ {
+ to_append_or_none = BRACKET_SEP;
+ }
+ break;
+ }
- /*
- Mode 2 means double quoted string, i.e. "foo"
- */
- case mode_double_quotes:
+ case L'\'':
+ {
+ mode = mode_single_quotes;
+ to_append_or_none = unescape_special ? INTERNAL_SEPARATOR : NOT_A_WCHAR;
+ break;
+ }
+
+ case L'\"':
+ {
+ mode = mode_double_quotes;
+ to_append_or_none = unescape_special ? INTERNAL_SEPARATOR : NOT_A_WCHAR;
+ break;
+ }
+ }
+ }
+ else if (mode == mode_single_quotes)
+ {
+ if (c == L'\\')
{
- switch (c)
+ /* A backslash may or may not escape something in single quotes */
+ switch (input[input_position + 1])
{
- case '"':
+ case '\\':
+ case L'\'':
{
- mode = mode_unquoted;
- if (unescape_special)
- in[out_pos] = INTERNAL_SEPARATOR;
- else
- out_pos--;
+ to_append_or_none = input[input_position + 1];
+ input_position += 1; /* Skip over the backslash */
break;
}
- case '\\':
+ case L'\0':
{
- switch (in[++in_pos])
+ if (!allow_incomplete)
{
- case L'\0':
- {
- if (!allow_incomplete)
- {
- free(in);
- return 0;
- }
- else
- {
- //We probably don't need it since NULL character is always appended before ending this function.
- in[out_pos]=in[in_pos];
- }
- }
- break;
- case '\\':
- case L'$':
- case '"':
- {
- in[out_pos]=in[in_pos];
- break;
- }
+ errored = true;
+ }
+ else
+ {
+ // PCA this line had the following cryptic comment:
+ // 'We may ever escape a NULL character, but still appending a \ in case I am wrong.'
+ // Not sure what it means or the importance of this
+ input_position += 1; /* Skip over the backslash */
+ to_append_or_none = L'\\';
+ }
+ }
+ break;
+
+ default:
+ {
+ /* Literal backslash that doesn't escape anything! Leave things alone; we'll append the backslash itself */
+ break;
+ }
+ }
+ }
+ else if (c == L'\'')
+ {
+ to_append_or_none = unescape_special ? INTERNAL_SEPARATOR : NOT_A_WCHAR;
+ mode = mode_unquoted;
+ }
+ }
+ else if (mode == mode_double_quotes)
+ {
+ switch (c)
+ {
+ case L'"':
+ {
+ mode = mode_unquoted;
+ to_append_or_none = unescape_special ? INTERNAL_SEPARATOR : NOT_A_WCHAR;
+ break;
+ }
- case '\n':
+ case '\\':
+ {
+ switch (input[input_position + 1])
+ {
+ case L'\0':
+ {
+ if (!allow_incomplete)
{
- out_pos--;
- break;
+ errored = true;
}
-
- default:
+ else
{
- in[out_pos++] = L'\\';
- in[out_pos] = in[in_pos];
- break;
+ to_append_or_none = L'\0';
}
}
break;
- }
- case '$':
- {
- if (unescape_special)
+ case '\\':
+ case L'$':
+ case '"':
{
- in[out_pos]=VARIABLE_EXPAND_SINGLE;
+ to_append_or_none = input[input_position + 1];
+ input_position += 1; /* Skip over the backslash */
+ break;
}
- else
+
+ case '\n':
{
- in[out_pos]=in[in_pos];
+ /* Swallow newline */
+ to_append_or_none = NOT_A_WCHAR;
+ input_position += 1; /* Skip over the backslash */
+ break;
+ }
+
+ default:
+ {
+ /* Literal backslash that doesn't escape anything! Leave things alone; we'll append the backslash itself */
+ break;
}
- break;
}
+ break;
+ }
- default:
+ case '$':
+ {
+ if (unescape_special)
{
- in[out_pos] = in[in_pos];
- break;
+ to_append_or_none = VARIABLE_EXPAND_SINGLE;
}
-
+ break;
}
- break;
+
}
}
+
+ /* Now maybe append the char */
+ if (to_append_or_none != NOT_A_WCHAR)
+ {
+ wchar_t to_append_char = static_cast<wchar_t>(to_append_or_none);
+ // if result_char is not NOT_A_WCHAR, it must be a valid wchar
+ assert((wint_t)to_append_char == to_append_or_none);
+ result.push_back(to_append_char);
+ }
}
- if (!allow_incomplete && mode)
+ /* Return the string by reference, and then success */
+ if (! errored)
{
- free(in);
- return 0;
+ output_str->swap(result);
}
-
- in[out_pos]=L'\0';
- return in;
+ return ! errored;
}
-bool unescape_string(wcstring &str, int escape_special)
+bool unescape_string_in_place(wcstring *str, unescape_flags_t escape_special)
{
- bool success = false;
- wchar_t *result = unescape(str.c_str(), escape_special);
- if (result)
+ assert(str != NULL);
+ wcstring output;
+ bool success = unescape_string_internal(str->c_str(), str->size(), &output, escape_special);
+ if (success)
{
- str.replace(str.begin(), str.end(), result);
- free(result);
- success = true;
+ str->swap(output);
}
return success;
}
+bool unescape_string(const wchar_t *input, wcstring *output, unescape_flags_t escape_special)
+{
+ bool success = unescape_string_internal(input, wcslen(input), output, escape_special);
+ if (! success)
+ output->clear();
+ return success;
+}
+
+bool unescape_string(const wcstring &input, wcstring *output, unescape_flags_t escape_special)
+{
+ bool success = unescape_string_internal(input.c_str(), input.size(), output, escape_special);
+ if (! success)
+ output->clear();
+ return success;
+}
void common_handle_winch(int signal)
{
-#ifdef HAVE_WINSIZE
- if (ioctl(1,TIOCGWINSZ,&termsize)!=0)
+ /* don't run ioctl() here, it's not safe to use in signals */
+ termsize_valid = false;
+}
+
+/* updates termsize as needed, and returns a copy of the winsize. */
+static struct winsize get_current_winsize()
+{
+#ifndef HAVE_WINSIZE
+ struct winsize retval = {0};
+ retval.ws_col = 80;
+ retval.ws_row = 24;
+ return retval;
+#endif
+ scoped_rwlock guard(termsize_rwlock, true);
+ struct winsize retval = termsize;
+ if (!termsize_valid)
{
- return;
+ struct winsize size;
+ if (ioctl(1,TIOCGWINSZ,&size) == 0)
+ {
+ retval = size;
+ guard.upgrade();
+ termsize = retval;
+ }
+ termsize_valid = true;
}
-#else
- termsize.ws_col = 80;
- termsize.ws_row = 24;
-#endif
+ return retval;
}
int common_get_width()
{
- return termsize.ws_col;
+ return get_current_winsize().ws_col;
}
int common_get_height()
{
- return termsize.ws_row;
+ return get_current_winsize().ws_row;
}
void tokenize_variable_array(const wcstring &val, std::vector<wcstring> &out)
{
size_t pos = 0, end = val.size();
- while (pos < end)
+ while (pos <= end)
{
size_t next_pos = val.find(ARRAY_SEP, pos);
- if (next_pos == wcstring::npos) break;
- out.push_back(val.substr(pos, next_pos - pos));
- pos = next_pos + 1; //skip the separator
+ if (next_pos == wcstring::npos)
+ {
+ next_pos = end;
+ }
+ out.resize(out.size() + 1);
+ out.back().assign(val, pos, next_pos - pos);
+ pos = next_pos + 1; //skip the separator, or skip past the end
}
- out.push_back(val.substr(pos, end - pos));
}
bool string_prefixes_string(const wchar_t *proposed_prefix, const wcstring &value)
@@ -2005,7 +1995,7 @@ void format_size_safe(char buff[128], unsigned long long sz)
{
const size_t buff_size = 128;
const size_t max_len = buff_size - 1; //need to leave room for a null terminator
- bzero(buff, buff_size);
+ memset(buff, 0, buff_size);
size_t idx = 0;
const char * const sz_name[]=
{
@@ -2154,7 +2144,7 @@ static pid_t initial_foreground_process_group = -1;
bool is_forked_child(void)
{
- /* Just bail if nobody's called setup_fork_guards - e.g. fishd */
+ /* Just bail if nobody's called setup_fork_guards, e.g. some of our tools */
if (! initial_pid) return false;
bool is_child_of_fork = (getpid() != initial_pid);
@@ -2233,7 +2223,7 @@ void scoped_lock::lock(void)
{
assert(! locked);
assert(! is_forked_child());
- VOMIT_ON_FAILURE(pthread_mutex_lock(lock_obj));
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_lock(lock_obj));
locked = true;
}
@@ -2241,7 +2231,7 @@ void scoped_lock::unlock(void)
{
assert(locked);
assert(! is_forked_child());
- VOMIT_ON_FAILURE(pthread_mutex_unlock(lock_obj));
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_unlock(lock_obj));
locked = false;
}
@@ -2250,11 +2240,94 @@ scoped_lock::scoped_lock(pthread_mutex_t &mutex) : lock_obj(&mutex), locked(fals
this->lock();
}
+scoped_lock::scoped_lock(mutex_lock_t &lock) : lock_obj(&lock.mutex), locked(false)
+{
+ this->lock();
+}
+
scoped_lock::~scoped_lock()
{
if (locked) this->unlock();
}
+void scoped_rwlock::lock(void)
+{
+ assert(! (locked || locked_shared));
+ assert(! is_forked_child());
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_rdlock(rwlock_obj));
+ locked = true;
+}
+
+void scoped_rwlock::unlock(void)
+{
+ assert(locked);
+ assert(! is_forked_child());
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj));
+ locked = false;
+}
+
+void scoped_rwlock::lock_shared(void)
+{
+ assert(! (locked || locked_shared));
+ assert(! is_forked_child());
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_wrlock(rwlock_obj));
+ locked_shared = true;
+}
+
+void scoped_rwlock::unlock_shared(void)
+{
+ assert(locked_shared);
+ assert(! is_forked_child());
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj));
+ locked_shared = false;
+}
+
+void scoped_rwlock::upgrade(void)
+{
+ assert(locked_shared);
+ assert(! is_forked_child());
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj));
+ locked = false;
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_wrlock(rwlock_obj));
+ locked_shared = true;
+}
+
+scoped_rwlock::scoped_rwlock(pthread_rwlock_t &rwlock, bool shared) : rwlock_obj(&rwlock), locked(false), locked_shared(false)
+{
+ if (shared)
+ {
+ this->lock_shared();
+ }
+ else
+ {
+ this->lock();
+ }
+}
+
+scoped_rwlock::scoped_rwlock(rwlock_t &rwlock, bool shared) : rwlock_obj(&rwlock.rwlock), locked(false), locked_shared(false)
+{
+ if (shared)
+ {
+ this->lock_shared();
+ }
+ else
+ {
+ this->lock();
+ }
+}
+
+scoped_rwlock::~scoped_rwlock()
+{
+ if (locked)
+ {
+ this->unlock();
+ }
+ else if (locked_shared)
+ {
+ this->unlock_shared();
+ }
+}
+
wcstokenizer::wcstokenizer(const wcstring &s, const wcstring &separator) :
buffer(),
str(),
@@ -2343,73 +2416,3 @@ char **make_null_terminated_array(const std::vector<std::string> &lst)
{
return make_null_terminated_array_helper(lst);
}
-
-/**
- Check, and create if necessary, a secure runtime path
- Derived from tmux.c in tmux (http://tmux.sourceforge.net/)
-*/
-static int check_runtime_path(const char * path)
-{
- /*
- * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
- struct stat statpath;
- u_int uid = geteuid();
-
- if (mkdir(path, S_IRWXU) != 0 && errno != EEXIST)
- return errno;
- if (lstat(path, &statpath) != 0)
- return errno;
- if (!S_ISDIR(statpath.st_mode)
- || statpath.st_uid != uid
- || (statpath.st_mode & (S_IRWXG|S_IRWXO)) != 0)
- return EACCES;
- return 0;
-}
-
-/** Return the path of an appropriate runtime data directory */
-std::string common_get_runtime_path()
-{
- const char *dir = getenv("XDG_RUNTIME_DIR");
- const char *uname = getenv("USER");
- std::string path;
-
- if (uname == NULL)
- {
- const struct passwd *pw = getpwuid(getuid());
- uname = pw->pw_name;
- }
-
- if (dir == NULL)
- {
- // /tmp/fish.user
- dir = "/tmp/fish.";
- path.reserve(strlen(dir) + strlen(uname));
- path.append(dir);
- path.append(uname);
- if (check_runtime_path(path.c_str()) != 0)
- {
- debug(0, L"Runtime path not available. Try deleting the directory %s and restarting fish.", path.c_str());
- path.clear();
- }
- }
- else
- {
- path.reserve(strlen(dir));
- path.append(dir);
- }
- return path;
-}