diff options
author | 2015-07-24 00:50:58 -0700 | |
---|---|---|
committer | 2015-07-24 00:59:27 -0700 | |
commit | b4f53143b0e05fd3061cdf2e65e17a6a2904090b (patch) | |
tree | 4785bf31f7b89fc2420aa740d9a6967dc6c6f9b1 /common.cpp | |
parent | 9c2fdc6da57032c4448b59de5872086eea626b74 (diff) |
Migrate source files into src/ directory
This change moves source files into a src/ directory,
and puts object files into an obj/ directory. The Makefile
and xcode project are updated accordingly.
Fixes #1866
Diffstat (limited to 'common.cpp')
-rw-r--r-- | common.cpp | 2427 |
1 files changed, 0 insertions, 2427 deletions
diff --git a/common.cpp b/common.cpp deleted file mode 100644 index 473f3502..00000000 --- a/common.cpp +++ /dev/null @@ -1,2427 +0,0 @@ -/** \file common.c - -Various functions, mostly string utilities, that are used by most -parts of fish. -*/ - -#include "config.h" - - -#include <unistd.h> - -#ifdef HAVE_STROPTS_H -#include <stropts.h> -#endif - -#ifdef HAVE_SIGINFO_H -#include <siginfo.h> -#endif - -#include <stdlib.h> -#include <termios.h> -#include <wchar.h> -#include <string.h> -#include <stdio.h> -#include <dirent.h> -#include <sys/types.h> - -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif - -#include <sys/stat.h> -#include <unistd.h> -#include <wctype.h> -#include <errno.h> -#include <limits.h> -#include <stdarg.h> -#include <locale.h> -#include <time.h> -#include <sys/time.h> -#include <fcntl.h> -#include <algorithm> - -#ifdef HAVE_EXECINFO_H -#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 - -#if HAVE_TERM_H -#include <term.h> -#elif HAVE_NCURSES_TERM_H -#include <ncurses/term.h> -#endif - -#include "fallback.h" -#include "util.h" - -#include "wutil.h" -#include "common.h" -#include "expand.h" -#include "proc.h" -#include "wildcard.h" -#include "parser.h" -#include "complete.h" - -#include "util.cpp" -#include "fallback.cpp" - -#define NOT_A_WCHAR (static_cast<wint_t>(WEOF)) - -struct termios shell_modes; - -// Note we foolishly assume that pthread_t is just a primitive. But it might be a struct. -static pthread_t main_thread_id = 0; -static bool thread_assertions_configured_for_testing = false; - -wchar_t ellipsis_char; -wchar_t omitted_newline_char; - -bool g_profiling_active = false; - -const wchar_t *program_name; - -int debug_level=1; - -/** - 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); - -void show_stackframe() -{ - ASSERT_IS_NOT_FORKED_CHILD(); - - /* Hack to avoid showing backtraces in the tester */ - if (program_name && ! wcscmp(program_name, L"(ignore)")) - return; - - void *trace[32]; - int trace_size = 0; - - trace_size = backtrace(trace, 32); - char **messages = backtrace_symbols(trace, trace_size); - - if (messages) - { - debug(0, L"Backtrace:"); - for (int i=0; i<trace_size; i++) - { - fwprintf(stderr, L"%s\n", messages[i]); - } - free(messages); - } -} - -int fgetws2(wcstring *s, FILE *f) -{ - int i=0; - wint_t c; - - while (1) - { - errno=0; - - c = getwc(f); - - if (errno == EILSEQ || errno == EINTR) - { - continue; - } - - switch (c) - { - /* End of line */ - case WEOF: - case L'\n': - case L'\0': - return i; - /* Ignore carriage returns */ - case L'\r': - break; - - default: - i++; - s->push_back((wchar_t)c); - break; - } - } -} - -/** - Converts the narrow character string \c in into its wide - equivalent, and return it - - The string may contain embedded nulls. - - This function encodes illegal character sequences in a reversible - way using the private use area. -*/ - -static wcstring str2wcs_internal(const char *in, const size_t in_len) -{ - if (in_len == 0) - return wcstring(); - - assert(in != NULL); - - wcstring result; - result.reserve(in_len); - mbstate_t state = {}; - size_t in_pos = 0; - while (in_pos < in_len) - { - wchar_t wc = 0; - size_t ret = mbrtowc(&wc, &in[in_pos], in_len-in_pos, &state); - - /* Determine whether to encode this characters with our crazy scheme */ - bool use_encode_direct = false; - if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE+256) - { - use_encode_direct = true; - } - else if (wc == INTERNAL_SEPARATOR) - { - use_encode_direct = true; - } - else if (ret == (size_t)(-2)) - { - /* Incomplete sequence */ - use_encode_direct = true; - } - else if (ret == (size_t)(-1)) - { - /* Invalid data */ - use_encode_direct = true; - } - else if (ret > in_len - in_pos) - { - /* Other error codes? Terrifying, should never happen */ - use_encode_direct = true; - } - - if (use_encode_direct) - { - wc = ENCODE_DIRECT_BASE + (unsigned char)in[in_pos]; - result.push_back(wc); - in_pos++; - memset(&state, 0, sizeof state); - } - else if (ret == 0) - { - /* Embedded null byte! */ - result.push_back(L'\0'); - in_pos++; - memset(&state, 0, sizeof state); - } - else - { - /* Normal case */ - result.push_back(wc); - in_pos += ret; - } - } - return result; -} - -wcstring str2wcstring(const char *in, size_t len) -{ - return str2wcs_internal(in, len); -} - -wcstring str2wcstring(const char *in) -{ - return str2wcs_internal(in, strlen(in)); -} - -wcstring str2wcstring(const std::string &in) -{ - /* Handles embedded nulls! */ - return str2wcs_internal(in.data(), in.size()); -} - -char *wcs2str(const wchar_t *in) -{ - if (! in) - return NULL; - size_t desired_size = MAX_UTF8_BYTES*wcslen(in)+1; - char local_buff[512]; - if (desired_size <= sizeof local_buff / sizeof *local_buff) - { - // convert into local buff, then use strdup() so we don't waste malloc'd space - char *result = wcs2str_internal(in, local_buff); - if (result) - { - // It converted into the local buffer, so copy it - result = strdup(result); - if (! result) - { - DIE_MEM(); - } - } - return result; - - } - else - { - // here we fall into the bad case of allocating a buffer probably much larger than necessary - char *out = (char *)malloc(MAX_UTF8_BYTES*wcslen(in)+1); - if (!out) - { - DIE_MEM(); - } - return wcs2str_internal(in, out); - } -} - -char *wcs2str(const wcstring &in) -{ - return wcs2str(in.c_str()); -} - -/* This function is distinguished from wcs2str_internal in that it allows embedded null bytes */ -std::string wcs2string(const wcstring &input) -{ - std::string result; - result.reserve(input.size()); - - mbstate_t state; - memset(&state, 0, sizeof(state)); - - char converted[MB_LEN_MAX + 1]; - - for (size_t i=0; i < input.size(); i++) - { - wchar_t wc = input[i]; - if (wc == INTERNAL_SEPARATOR) - { - } - else if ((wc >= ENCODE_DIRECT_BASE) && - (wc < ENCODE_DIRECT_BASE+256)) - { - result.push_back(wc - ENCODE_DIRECT_BASE); - } - else - { - memset(converted, 0, sizeof converted); - size_t len = wcrtomb(converted, wc, &state); - if (len == (size_t)(-1)) - { - debug(1, L"Wide character %d has no narrow representation", wc); - memset(&state, 0, sizeof(state)); - } - else - { - result.append(converted, len); - } - } - } - - return result; -} - -/** - Converts the wide character string \c in into it's narrow - equivalent, stored in \c out. \c out must have enough space to fit - the entire string. - - This function decodes illegal character sequences in a reversible - way using the private use area. -*/ -static char *wcs2str_internal(const wchar_t *in, char *out) -{ - size_t res=0; - size_t in_pos=0; - size_t out_pos = 0; - mbstate_t state; - - CHECK(in, 0); - CHECK(out, 0); - - memset(&state, 0, sizeof(state)); - - while (in[in_pos]) - { - if (in[in_pos] == INTERNAL_SEPARATOR) - { - } - else if ((in[in_pos] >= ENCODE_DIRECT_BASE) && - (in[in_pos] < ENCODE_DIRECT_BASE+256)) - { - out[out_pos++] = in[in_pos]- ENCODE_DIRECT_BASE; - } - else - { - res = wcrtomb(&out[out_pos], in[in_pos], &state); - - if (res == (size_t)(-1)) - { - debug(1, L"Wide character %d has no narrow representation", in[in_pos]); - memset(&state, 0, sizeof(state)); - } - else - { - out_pos += res; - } - } - in_pos++; - } - out[out_pos] = 0; - - return out; -} - -wcstring format_string(const wchar_t *format, ...) -{ - va_list va; - va_start(va, format); - wcstring result = vformat_string(format, va); - va_end(va); - return result; -} - -void append_formatv(wcstring &target, const wchar_t *format, va_list va_orig) -{ - const int saved_err = errno; - /* - As far as I know, there is no way to check if a - vswprintf-call failed because of a badly formated string - option or because the supplied destination string was to - small. In GLIBC, errno seems to be set to EINVAL either way. - - Because of this, on failiure we try to - increase the buffer size until the free space is - larger than max_size, at which point it will - conclude that the error was probably due to a badly - formated string option, and return an error. Make - sure to null terminate string before that, though. - */ - const size_t max_size = (128*1024*1024); - wchar_t static_buff[256]; - size_t size = 0; - wchar_t *buff = NULL; - int status = -1; - while (status < 0) - { - /* Reallocate if necessary */ - if (size == 0) - { - buff = static_buff; - size = sizeof static_buff; - } - else - { - size *= 2; - if (size >= max_size) - { - buff[0] = '\0'; - break; - } - buff = (wchar_t *)realloc((buff == static_buff ? NULL : buff), size); - if (buff == NULL) - { - DIE_MEM(); - } - } - - /* Try printing */ - va_list va; - va_copy(va, va_orig); - status = vswprintf(buff, size / sizeof(wchar_t), format, va); - va_end(va); - } - - target.append(buff); - - if (buff != static_buff) - { - free(buff); - } - - errno = saved_err; -} - -wcstring vformat_string(const wchar_t *format, va_list va_orig) -{ - wcstring result; - append_formatv(result, format, va_orig); - return result; -} - -void append_format(wcstring &str, const wchar_t *format, ...) -{ - va_list va; - va_start(va, format); - append_formatv(str, format, va); - va_end(va); -} - -const wchar_t *wcsvarname(const wchar_t *str) -{ - while (*str) - { - if ((!iswalnum(*str)) && (*str != L'_')) - { - return str; - } - str++; - } - return NULL; -} - -const wchar_t *wcsvarname(const wcstring &str) -{ - return wcsvarname(str.c_str()); -} - -const wchar_t *wcsfuncname(const wcstring &str) -{ - return wcschr(str.c_str(), L'/'); -} - - -bool wcsvarchr(wchar_t chr) -{ - return iswalnum(chr) || chr == L'_'; -} - -int fish_wcswidth(const wchar_t *str) -{ - return fish_wcswidth(str, wcslen(str)); -} - -int fish_wcswidth(const wcstring& str) -{ - return fish_wcswidth(str.c_str(), str.size()); -} - -wchar_t *quote_end(const wchar_t *pos) -{ - wchar_t c = *pos; - - while (1) - { - pos++; - - if (!*pos) - return 0; - - if (*pos == L'\\') - { - pos++; - if (!*pos) - return 0; - } - else - { - if (*pos == c) - { - return (wchar_t *)pos; - } - } - } - return 0; - -} - - -wcstring wsetlocale(int category, const wchar_t *locale) -{ - - char *lang = locale ? wcs2str(locale) : NULL; - char *res = setlocale(category, lang); - free(lang); - - /* - Use ellipsis if on known unicode system, otherwise use $ - */ - char *ctype = setlocale(LC_CTYPE, NULL); - bool unicode = (strstr(ctype, ".UTF") || strstr(ctype, ".utf")); - - ellipsis_char = unicode ? L'\x2026' : L'$'; - - // U+23CE is the "return" character - omitted_newline_char = unicode ? L'\x23CE' : L'~'; - - if (!res) - return wcstring(); - else - return format_string(L"%s", res); -} - -bool contains_internal(const wchar_t *a, int vararg_handle, ...) -{ - const wchar_t *arg; - va_list va; - bool res = false; - - CHECK(a, 0); - - va_start(va, vararg_handle); - while ((arg=va_arg(va, const wchar_t *))!= 0) - { - if (wcscmp(a,arg) == 0) - { - res = true; - break; - } - - } - va_end(va); - return res; -} - -/* 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; - - const wchar_t *needle_cstr = needle.c_str(); - va_start(va, vararg_handle); - while ((arg=va_arg(va, const wchar_t *))!= 0) - { - /* 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; - } - - } - va_end(va); - return res; -} - -long read_blocked(int fd, void *buf, size_t count) -{ - ssize_t res; - sigset_t chldset, oldset; - - sigemptyset(&chldset); - sigaddset(&chldset, SIGCHLD); - VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &chldset, &oldset)); - res = read(fd, buf, count); - VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &oldset, NULL)); - return res; -} - -ssize_t write_loop(int fd, const char *buff, size_t count) -{ - size_t out_cum=0; - while (out_cum < count) - { - ssize_t out = write(fd, &buff[out_cum], count - out_cum); - if (out < 0) - { - if (errno != EAGAIN && errno != EINTR) - { - return -1; - } - } - else - { - out_cum += (size_t)out; - } - } - return (ssize_t)out_cum; -} - -ssize_t read_loop(int fd, void *buff, size_t count) -{ - ssize_t result; - do - { - result = read(fd, buff, count); - } - while (result < 0 && (errno == EAGAIN || errno == EINTR)); - return result; -} - -static bool should_debug(int level) -{ - if (level > debug_level) - return false; - - /* Hack to not print error messages in the tests */ - if (program_name && ! wcscmp(program_name, L"(ignore)")) - return false; - - return true; -} - -static void debug_shared(const wcstring &msg) -{ - const wcstring sb = wcstring(program_name) + L": " + msg; - wcstring sb2; - write_screen(sb, sb2); - fwprintf(stderr, L"%ls", sb2.c_str()); -} - -void debug(int level, const wchar_t *msg, ...) -{ - if (! should_debug(level)) - return; - int errno_old = errno; - va_list va; - va_start(va, msg); - wcstring local_msg = vformat_string(msg, va); - va_end(va); - debug_shared(local_msg); - errno = errno_old; -} - -void debug(int level, const char *msg, ...) -{ - if (! should_debug(level)) - return; - int errno_old = errno; - char local_msg[512]; - va_list va; - va_start(va, msg); - vsnprintf(local_msg, sizeof local_msg, msg, va); - va_end(va); - debug_shared(str2wcstring(local_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) -{ - const char * const params[] = {param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12}; - if (! msg) - return; - - /* Can't call printf, that may allocate memory Just call write() over and over. */ - if (level > debug_level) - return; - int errno_old = errno; - - size_t param_idx = 0; - const char *cursor = msg; - while (*cursor != '\0') - { - const char *end = strchr(cursor, '%'); - if (end == NULL) - end = cursor + strlen(cursor); - - write_ignore(STDERR_FILENO, cursor, end - cursor); - - if (end[0] == '%' && end[1] == 's') - { - /* Handle a format string */ - assert(param_idx < sizeof params / sizeof *params); - const char *format = params[param_idx++]; - if (! format) - format = "(null)"; - write_ignore(STDERR_FILENO, format, strlen(format)); - cursor = end + 2; - } - else if (end[0] == '\0') - { - /* Must be at the end of the string */ - cursor = end; - } - else - { - /* Some other format specifier, just skip it */ - cursor = end + 1; - } - } - - // We always append a newline - write_ignore(STDERR_FILENO, "\n", 1); - - errno = errno_old; -} - -void format_long_safe(char buff[64], long val) -{ - if (val == 0) - { - strcpy(buff, "0"); - } - else - { - /* Generate the string in reverse */ - size_t idx = 0; - bool negative = (val < 0); - - /* Note that we can't just negate val if it's negative, because it may be the most negative value. We do rely on round-towards-zero division though. */ - - while (val != 0) - { - long rem = val % 10; - buff[idx++] = '0' + (rem < 0 ? -rem : rem); - val /= 10; - } - if (negative) - buff[idx++] = '-'; - buff[idx] = 0; - - size_t left = 0, right = idx - 1; - while (left < right) - { - char tmp = buff[left]; - buff[left++] = buff[right]; - buff[right--] = tmp; - } - } -} - -void format_long_safe(wchar_t buff[64], long val) -{ - if (val == 0) - { - wcscpy(buff, L"0"); - } - else - { - /* Generate the string in reverse */ - size_t idx = 0; - bool negative = (val < 0); - - while (val != 0) - { - long rem = val % 10; - buff[idx++] = L'0' + (wchar_t)(rem < 0 ? -rem : rem); - val /= 10; - } - if (negative) - buff[idx++] = L'-'; - buff[idx] = 0; - - size_t left = 0, right = idx - 1; - while (left < right) - { - wchar_t tmp = buff[left]; - buff[left++] = buff[right]; - buff[right--] = tmp; - } - } -} - -void write_screen(const wcstring &msg, wcstring &buff) -{ - int line_width = 0; - int screen_width = common_get_width(); - - if (screen_width) - { - const wchar_t *start = msg.c_str(); - const wchar_t *pos = start; - while (1) - { - int overflow = 0; - - int tok_width=0; - - /* - Tokenize on whitespace, and also calculate the width of the token - */ - while (*pos && (!wcschr(L" \n\r\t", *pos))) - { - - /* - Check is token is wider than one line. - If so we mark it as an overflow and break the token. - */ - if ((tok_width + fish_wcwidth(*pos)) > (screen_width-1)) - { - overflow = 1; - break; - } - - tok_width += fish_wcwidth(*pos); - pos++; - } - - /* - If token is zero character long, we don't do anything - */ - if (pos == start) - { - start = pos = pos+1; - } - else if (overflow) - { - /* - In case of overflow, we print a newline, except if we already are at position 0 - */ - wchar_t *token = wcsndup(start, pos-start); - if (line_width != 0) - buff.push_back(L'\n'); - buff.append(format_string(L"%ls-\n", token)); - free(token); - line_width=0; - } - else - { - /* - Print the token - */ - wchar_t *token = wcsndup(start, pos-start); - if ((line_width + (line_width!=0?1:0) + tok_width) > screen_width) - { - buff.push_back(L'\n'); - line_width=0; - } - buff.append(format_string(L"%ls%ls", line_width?L" ":L"", token)); - free(token); - line_width += (line_width!=0?1:0) + tok_width; - } - - /* - Break on end of string - */ - if (!*pos) - { - break; - } - - start=pos; - } - } - else - { - buff.append(msg); - } - buff.push_back(L'\n'); -} - -/* 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) -{ - 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); - - int need_escape=0; - int need_complex_escape=0; - - /* Avoid dereferencing all over the place */ - wcstring &out = *out_str; - - if (!no_quoted && in_len == 0) - { - out.assign(L"''"); - return; - } - - while (*in != 0) - { - - if ((*in >= ENCODE_DIRECT_BASE) && - (*in < ENCODE_DIRECT_BASE+256)) - { - int val = *in - ENCODE_DIRECT_BASE; - int tmp; - - out += L'\\'; - out += L'X'; - - tmp = val/16; - out += tmp > 9? L'a'+(tmp-10):L'0'+tmp; - - tmp = val%16; - out += tmp > 9? L'a'+(tmp-10):L'0'+tmp; - need_escape=need_complex_escape=1; - - } - else - { - wchar_t c = *in; - switch (c) - { - case L'\t': - out += L'\\'; - out += L't'; - need_escape=need_complex_escape=1; - break; - - case L'\n': - out += L'\\'; - out += L'n'; - need_escape=need_complex_escape=1; - break; - - case L'\b': - out += L'\\'; - out += L'b'; - need_escape=need_complex_escape=1; - break; - - case L'\r': - out += L'\\'; - out += L'r'; - need_escape=need_complex_escape=1; - break; - - case L'\x1b': - out += L'\\'; - out += L'e'; - need_escape=need_complex_escape=1; - break; - - - case L'\\': - case L'\'': - { - need_escape=need_complex_escape=1; - if (escape_all) - 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' ': - case L'#': - case L'^': - case L'<': - case L'>': - case L'(': - case L')': - case L'[': - case L']': - case L'{': - case L'}': - case L'?': - case L'*': - case L'|': - case L';': - case L'"': - case L'%': - case L'~': - { - if (! no_tilde || c != L'~') - { - need_escape=1; - if (escape_all) - out += L'\\'; - } - out += *in; - break; - } - - default: - { - if (*in < 32) - { - if (*in <27 && *in > 0) - { - out += L'\\'; - out += L'c'; - out += L'a' + *in -1; - - need_escape=need_complex_escape=1; - break; - - } - - - int tmp = (*in)%16; - 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 - { - out += *in; - } - break; - } - } - } - - in++; - } - - /* - Use quoted escaping if possible, since most people find it - easier to read. - */ - if (!no_quoted && need_escape && !need_complex_escape && escape_all) - { - 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); - } -} - -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) -{ - wcstring result; - escape_string_internal(in.c_str(), in.size(), &result, flags); - return result; -} - -/* Helper to return the last character in a string, or NOT_A_WCHAR */ -static wint_t string_last_char(const wcstring &str) -{ - size_t len = str.size(); - return len == 0 ? NOT_A_WCHAR : str.at(len - 1); -} - -/* 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; - } - - /* 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; - - 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) - { - - /* 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--; - - /* 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) - { - case L'u': - { - chars=4; - max_val = UCS2_MAX; - break; - } - - case L'U': - { - chars=8; - max_val = WCHAR_MAX; - - // Don't exceed the largest Unicode code point - see #1107 - if (0x10FFFF < max_val) - max_val = (wchar_t)0x10FFFF; - - break; - } - - case L'x': - { - chars = 2; - max_val = ASCII_MAX; - break; - } - - case L'X': - { - byte_literal = true; - max_val = BYTE_MAX; - break; - } - - 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; - } - } - - for (size_t i=0; i<chars; i++) - { - long d = convert_digit(input[in_pos],base); - if (d < 0) - { - break; - } - - res=(res*base)+d; - in_pos++; - } - - if (res <= max_val) - { - result_char_or_none = (wchar_t)((byte_literal ? ENCODE_DIRECT_BASE : 0)+res); - } - else - { - errored = true; - } - - 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; - } - - /* \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; - } - - /* \x1b means escape */ - case L'e': - { - result_char_or_none = L'\x1b'; - break; - } - - /* - \f means form feed - */ - case L'f': - { - result_char_or_none = L'\f'; - break; - } - - /* - \n means newline - */ - case L'n': - { - result_char_or_none = L'\n'; - break; - } - - /* - \r means carriage return - */ - case L'r': - { - result_char_or_none = L'\r'; - break; - } - - /* - \t means tab - */ - case L't': - { - result_char_or_none = L'\t'; - break; - } - - /* - \v means vertical tab - */ - case L'v': - { - result_char_or_none = L'\v'; - break; - } - - /* If a backslash is followed by an actual newline, swallow them both */ - case L'\n': - { - result_char_or_none = NOT_A_WCHAR; - break; - } - - default: - { - if (unescape_special) - result->push_back(INTERNAL_SEPARATOR); - result_char_or_none = c; - 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; -} - -/* 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); - - const bool unescape_special = !!(flags & UNESCAPE_SPECIAL); - const bool allow_incomplete = !!(flags & UNESCAPE_INCOMPLETE); - - int bracket_count = 0; - - bool errored = false; - enum - { - mode_unquoted, - mode_single_quotes, - mode_double_quotes - } mode = mode_unquoted; - - 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) - { - - 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'~': - { - if (unescape_special && (input_position == 0)) - { - to_append_or_none = HOME_DIRECTORY; - } - break; - } - - case L'%': - { - if (unescape_special && (input_position == 0)) - { - to_append_or_none = PROCESS_EXPAND; - } - break; - } - - case L'*': - { - if (unescape_special) - { - /* 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) - { - assert(result.size() > 0); - result.resize(result.size() - 1); - to_append_or_none = ANY_STRING_RECURSIVE; - } - else - { - to_append_or_none = ANY_STRING; - } - } - break; - } - - case L'?': - { - if (unescape_special) - { - to_append_or_none = ANY_CHAR; - } - break; - } - - case L'$': - { - if (unescape_special) - { - to_append_or_none = VARIABLE_EXPAND; - } - break; - } - - case L'{': - { - if (unescape_special) - { - bracket_count++; - to_append_or_none = BRACKET_BEGIN; - } - break; - } - - case L'}': - { - if (unescape_special) - { - bracket_count--; - to_append_or_none = BRACKET_END; - } - 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; - } - - 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'\\') - { - /* A backslash may or may not escape something in single quotes */ - switch (input[input_position + 1]) - { - case '\\': - case L'\'': - { - to_append_or_none = input[input_position + 1]; - input_position += 1; /* Skip over the backslash */ - break; - } - - case L'\0': - { - if (!allow_incomplete) - { - 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 '\\': - { - switch (input[input_position + 1]) - { - case L'\0': - { - if (!allow_incomplete) - { - errored = true; - } - else - { - to_append_or_none = L'\0'; - } - } - break; - - case '\\': - case L'$': - case '"': - { - to_append_or_none = input[input_position + 1]; - input_position += 1; /* Skip over the backslash */ - break; - } - - case '\n': - { - /* 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; - } - - case '$': - { - if (unescape_special) - { - to_append_or_none = VARIABLE_EXPAND_SINGLE; - } - 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); - } - } - - /* Return the string by reference, and then success */ - if (! errored) - { - output_str->swap(result); - } - return ! errored; -} - -bool unescape_string_in_place(wcstring *str, unescape_flags_t escape_special) -{ - assert(str != NULL); - wcstring output; - bool success = unescape_string_internal(str->c_str(), str->size(), &output, escape_special); - if (success) - { - 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) -{ - /* 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) - { - struct winsize size; - if (ioctl(1,TIOCGWINSZ,&size) == 0) - { - retval = size; - guard.upgrade(); - termsize = retval; - } - termsize_valid = true; - } - return retval; -} - -int common_get_width() -{ - return get_current_winsize().ws_col; -} - - -int common_get_height() -{ - 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) - { - size_t next_pos = val.find(ARRAY_SEP, pos); - 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 - } -} - -bool string_prefixes_string(const wchar_t *proposed_prefix, const wcstring &value) -{ - size_t prefix_size = wcslen(proposed_prefix); - return prefix_size <= value.size() && value.compare(0, prefix_size, proposed_prefix) == 0; -} - -bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value) -{ - size_t prefix_size = proposed_prefix.size(); - return prefix_size <= value.size() && value.compare(0, prefix_size, proposed_prefix) == 0; -} - -bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, const wcstring &value) -{ - size_t prefix_size = proposed_prefix.size(); - return prefix_size <= value.size() && wcsncasecmp(proposed_prefix.c_str(), value.c_str(), prefix_size) == 0; -} - -bool string_suffixes_string(const wcstring &proposed_suffix, const wcstring &value) -{ - size_t suffix_size = proposed_suffix.size(); - return suffix_size <= value.size() && value.compare(value.size() - suffix_size, suffix_size, proposed_suffix) == 0; -} - -bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &value) -{ - size_t suffix_size = wcslen(proposed_suffix); - return suffix_size <= value.size() && value.compare(value.size() - suffix_size, suffix_size, proposed_suffix) == 0; -} - -// Returns true if seq, represented as a subsequence, is contained within string -static bool subsequence_in_string(const wcstring &seq, const wcstring &str) -{ - /* Impossible if seq is larger than string */ - if (seq.size() > str.size()) - { - return false; - } - - /* Empty strings are considered to be subsequences of everything */ - if (seq.empty()) - { - return true; - } - - size_t str_idx, seq_idx; - for (seq_idx = str_idx = 0; seq_idx < seq.size() && str_idx < str.size(); seq_idx++) - { - wchar_t c = seq.at(seq_idx); - size_t char_loc = str.find(c, str_idx); - if (char_loc == wcstring::npos) - { - /* Didn't find this character */ - break; - } - else - { - /* We found it. Continue the search just after it. */ - str_idx = char_loc + 1; - } - } - - /* We succeeded if we exhausted our sequence */ - assert(seq_idx <= seq.size()); - return seq_idx == seq.size(); -} - -string_fuzzy_match_t::string_fuzzy_match_t(enum fuzzy_match_type_t t, size_t distance_first, size_t distance_second) : - type(t), - match_distance_first(distance_first), - match_distance_second(distance_second) -{ -} - - -string_fuzzy_match_t string_fuzzy_match_string(const wcstring &string, const wcstring &match_against, fuzzy_match_type_t limit_type) -{ - // Distances are generally the amount of text not matched - string_fuzzy_match_t result(fuzzy_match_none, 0, 0); - size_t location; - if (limit_type >= fuzzy_match_exact && string == match_against) - { - result.type = fuzzy_match_exact; - } - else if (limit_type >= fuzzy_match_prefix && string_prefixes_string(string, match_against)) - { - result.type = fuzzy_match_prefix; - assert(match_against.size() >= string.size()); - result.match_distance_first = match_against.size() - string.size(); - } - else if (limit_type >= fuzzy_match_case_insensitive && wcscasecmp(string.c_str(), match_against.c_str()) == 0) - { - result.type = fuzzy_match_case_insensitive; - } - else if (limit_type >= fuzzy_match_prefix_case_insensitive && string_prefixes_string_case_insensitive(string, match_against)) - { - result.type = fuzzy_match_prefix_case_insensitive; - assert(match_against.size() >= string.size()); - result.match_distance_first = match_against.size() - string.size(); - } - else if (limit_type >= fuzzy_match_substring && (location = match_against.find(string)) != wcstring::npos) - { - // string is contained within match against - result.type = fuzzy_match_substring; - assert(match_against.size() >= string.size()); - result.match_distance_first = match_against.size() - string.size(); - result.match_distance_second = location; //prefer earlier matches - } - else if (limit_type >= fuzzy_match_subsequence_insertions_only && subsequence_in_string(string, match_against)) - { - result.type = fuzzy_match_subsequence_insertions_only; - assert(match_against.size() >= string.size()); - result.match_distance_first = match_against.size() - string.size(); - // it would be nice to prefer matches with greater matching runs here - } - return result; -} - -template<typename T> -static inline int compare_ints(T a, T b) -{ - if (a < b) return -1; - if (a == b) return 0; - return 1; -} - -// Compare types; if the types match, compare distances -int string_fuzzy_match_t::compare(const string_fuzzy_match_t &rhs) const -{ - if (this->type != rhs.type) - { - return compare_ints(this->type, rhs.type); - } - else if (this->match_distance_first != rhs.match_distance_first) - { - return compare_ints(this->match_distance_first, rhs.match_distance_first); - } - else if (this->match_distance_second != rhs.match_distance_second) - { - return compare_ints(this->match_distance_second, rhs.match_distance_second); - } - return 0; //equal -} - -bool list_contains_string(const wcstring_list_t &list, const wcstring &str) -{ - return std::find(list.begin(), list.end(), str) != list.end(); -} - -int create_directory(const wcstring &d) -{ - int ok = 0; - struct stat buf; - int stat_res = 0; - - while ((stat_res = wstat(d, &buf)) != 0) - { - if (errno != EAGAIN) - break; - } - - if (stat_res == 0) - { - if (S_ISDIR(buf.st_mode)) - { - ok = 1; - } - } - else - { - if (errno == ENOENT) - { - wcstring dir = wdirname(d); - if (!create_directory(dir)) - { - if (!wmkdir(d, 0700)) - { - ok = 1; - } - } - } - } - - return ok?0:-1; -} - -__attribute__((noinline)) -void bugreport() -{ - debug(1, - _(L"This is a bug. Break on bugreport to debug." - L"If you can reproduce it, please send a bug report to %s."), - PACKAGE_BUGREPORT); -} - -wcstring format_size(long long sz) -{ - wcstring result; - const wchar_t *sz_name[]= - { - L"kB", L"MB", L"GB", L"TB", L"PB", L"EB", L"ZB", L"YB", 0 - }; - - if (sz < 0) - { - result.append(L"unknown"); - } - else if (sz < 1) - { - result.append(_(L"empty")); - } - else if (sz < 1024) - { - result.append(format_string(L"%lldB", sz)); - } - else - { - int i; - - for (i=0; sz_name[i]; i++) - { - if (sz < (1024*1024) || !sz_name[i+1]) - { - long isz = ((long)sz)/1024; - if (isz > 9) - result.append(format_string(L"%d%ls", isz, sz_name[i])); - else - result.append(format_string(L"%.1f%ls", (double)sz/1024, sz_name[i])); - break; - } - sz /= 1024; - - } - } - return result; -} - -/* Crappy function to extract the most significant digit of an unsigned long long value */ -static char extract_most_significant_digit(unsigned long long *xp) -{ - unsigned long long place_value = 1; - unsigned long long x = *xp; - while (x >= 10) - { - x /= 10; - place_value *= 10; - } - *xp -= (place_value * x); - return x + '0'; -} - -void append_ull(char *buff, unsigned long long val, size_t *inout_idx, size_t max_len) -{ - size_t idx = *inout_idx; - while (val > 0 && idx < max_len) - buff[idx++] = extract_most_significant_digit(&val); - *inout_idx = idx; -} - -void append_str(char *buff, const char *str, size_t *inout_idx, size_t max_len) -{ - size_t idx = *inout_idx; - while (*str && idx < max_len) - buff[idx++] = *str++; - *inout_idx = idx; -} - -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 - memset(buff, 0, buff_size); - size_t idx = 0; - const char * const sz_name[]= - { - "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL - }; - if (sz < 1) - { - strncpy(buff, "empty", buff_size); - } - else if (sz < 1024) - { - append_ull(buff, sz, &idx, max_len); - append_str(buff, "B", &idx, max_len); - } - else - { - for (size_t i=0; sz_name[i]; i++) - { - if (sz < (1024*1024) || !sz_name[i+1]) - { - unsigned long long isz = sz/1024; - if (isz > 9) - { - append_ull(buff, isz, &idx, max_len); - } - else - { - if (isz == 0) - { - append_str(buff, "0", &idx, max_len); - } - else - { - append_ull(buff, isz, &idx, max_len); - } - - // Maybe append a single fraction digit - unsigned long long remainder = sz % 1024; - if (remainder > 0) - { - char tmp[3] = {'.', extract_most_significant_digit(&remainder), 0}; - append_str(buff, tmp, &idx, max_len); - } - } - append_str(buff, sz_name[i], &idx, max_len); - break; - } - sz /= 1024; - } - } -} - -double timef() -{ - int time_res; - struct timeval tv; - - time_res = gettimeofday(&tv, 0); - - if (time_res) - { - /* - Fixme: What on earth is the correct parameter value for NaN? - The man pages and the standard helpfully state that this - parameter is implementation defined. Gcc gives a warning if - a null pointer is used. But not even all mighty Google gives - a hint to what value should actually be returned. - */ - return nan(""); - } - - return (double)tv.tv_sec + 0.000001*tv.tv_usec; -} - -void exit_without_destructors(int code) -{ - _exit(code); -} - -/* Helper function to convert from a null_terminated_array_t<wchar_t> to a null_terminated_array_t<char_t> */ -void convert_wide_array_to_narrow(const null_terminated_array_t<wchar_t> &wide_arr, null_terminated_array_t<char> *output) -{ - const wchar_t *const *arr = wide_arr.get(); - if (! arr) - { - output->clear(); - return; - } - - std::vector<std::string> list; - for (size_t i=0; arr[i]; i++) - { - list.push_back(wcs2string(arr[i])); - } - output->set(list); -} - -void append_path_component(wcstring &path, const wcstring &component) -{ - if (path.empty() || component.empty()) - { - path.append(component); - } - else - { - size_t path_len = path.size(); - bool path_slash = path.at(path_len-1) == L'/'; - bool comp_slash = component.at(0) == L'/'; - if (! path_slash && ! comp_slash) - { - // Need a slash - path.push_back(L'/'); - } - else if (path_slash && comp_slash) - { - // Too many slashes - path.erase(path_len - 1, 1); - } - path.append(component); - } -} - -extern "C" { - __attribute__((noinline)) void debug_thread_error(void) - { - while (1) sleep(9999999); - } -} - - -void set_main_thread() -{ - main_thread_id = pthread_self(); -} - -void configure_thread_assertions_for_testing(void) -{ - thread_assertions_configured_for_testing = true; -} - -/* Notice when we've forked */ -static pid_t initial_pid = 0; - -/* Be able to restore the term's foreground process group */ -static pid_t initial_foreground_process_group = -1; - -bool is_forked_child(void) -{ - /* 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); - if (is_child_of_fork) - { - printf("Uh-oh: %d\n", getpid()); - while (1) sleep(10000); - } - return is_child_of_fork; -} - -void setup_fork_guards(void) -{ - /* Notice when we fork by stashing our pid. This seems simpler than pthread_atfork(). */ - initial_pid = getpid(); -} - -void save_term_foreground_process_group(void) -{ - initial_foreground_process_group = tcgetpgrp(STDIN_FILENO); -} - -void restore_term_foreground_process_group(void) -{ - if (initial_foreground_process_group != -1) - { - /* This is called during shutdown and from a signal handler. We don't bother to complain on failure. */ - if (0 > tcsetpgrp(STDIN_FILENO, initial_foreground_process_group)) - { - /* Ignore failure */ - } - } -} - -bool is_main_thread() -{ - assert(main_thread_id != 0); - return main_thread_id == pthread_self(); -} - -void assert_is_main_thread(const char *who) -{ - if (! is_main_thread() && ! thread_assertions_configured_for_testing) - { - fprintf(stderr, "Warning: %s called off of main thread. Break on debug_thread_error to debug.\n", who); - debug_thread_error(); - } -} - -void assert_is_not_forked_child(const char *who) -{ - if (is_forked_child()) - { - fprintf(stderr, "Warning: %s called in a forked child. Break on debug_thread_error to debug.\n", who); - debug_thread_error(); - } -} - -void assert_is_background_thread(const char *who) -{ - if (is_main_thread() && ! thread_assertions_configured_for_testing) - { - fprintf(stderr, "Warning: %s called on the main thread (may block!). Break on debug_thread_error to debug.\n", who); - debug_thread_error(); - } -} - -void assert_is_locked(void *vmutex, const char *who, const char *caller) -{ - pthread_mutex_t *mutex = static_cast<pthread_mutex_t*>(vmutex); - if (0 == pthread_mutex_trylock(mutex)) - { - fprintf(stderr, "Warning: %s is not locked when it should be in '%s'. Break on debug_thread_error to debug.\n", who, caller); - debug_thread_error(); - pthread_mutex_unlock(mutex); - } -} - -void scoped_lock::lock(void) -{ - assert(! locked); - assert(! is_forked_child()); - VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_lock(lock_obj)); - locked = true; -} - -void scoped_lock::unlock(void) -{ - assert(locked); - assert(! is_forked_child()); - VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_unlock(lock_obj)); - locked = false; -} - -scoped_lock::scoped_lock(pthread_mutex_t &mutex) : lock_obj(&mutex), locked(false) -{ - 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(), - state(), - sep(separator) -{ - buffer = wcsdup(s.c_str()); - str = buffer; - state = NULL; -} - -bool wcstokenizer::next(wcstring &result) -{ - wchar_t *tmp = wcstok(str, sep.c_str(), &state); - str = NULL; - if (tmp) result = tmp; - return tmp != NULL; -} - -wcstokenizer::~wcstokenizer() -{ - free(buffer); -} - - -template <typename CharType_t> -static CharType_t **make_null_terminated_array_helper(const std::vector<std::basic_string<CharType_t> > &argv) -{ - size_t count = argv.size(); - - /* We allocate everything in one giant block. First compute how much space we need. */ - - /* N + 1 pointers */ - size_t pointers_allocation_len = (count + 1) * sizeof(CharType_t *); - - /* In the very unlikely event that CharType_t has stricter alignment requirements than does a pointer, round us up to the size of a CharType_t */ - pointers_allocation_len += sizeof(CharType_t) - 1; - pointers_allocation_len -= pointers_allocation_len % sizeof(CharType_t); - - /* N null terminated strings */ - size_t strings_allocation_len = 0; - for (size_t i=0; i < count; i++) - { - /* The size of the string, plus a null terminator */ - strings_allocation_len += (argv.at(i).size() + 1) * sizeof(CharType_t); - } - - /* Now allocate their sum */ - unsigned char *base = static_cast<unsigned char *>(malloc(pointers_allocation_len + strings_allocation_len)); - if (! base) return NULL; - - /* Divvy it up into the pointers and strings */ - CharType_t **pointers = reinterpret_cast<CharType_t **>(base); - CharType_t *strings = reinterpret_cast<CharType_t *>(base + pointers_allocation_len); - - /* Start copying */ - for (size_t i=0; i < count; i++) - { - const std::basic_string<CharType_t> &str = argv.at(i); - // store the current string pointer into self - *pointers++ = strings; - - // copy the string into strings - strings = std::copy(str.begin(), str.end(), strings); - // each string needs a null terminator - *strings++ = (CharType_t)(0); - } - // array of pointers needs a null terminator - *pointers++ = NULL; - - // Make sure we know what we're doing - assert((unsigned char *)pointers - base == (std::ptrdiff_t)pointers_allocation_len); - assert((unsigned char *)strings - (unsigned char *)pointers == (std::ptrdiff_t)strings_allocation_len); - assert((unsigned char *)strings - base == (std::ptrdiff_t)(pointers_allocation_len + strings_allocation_len)); - - // Return what we did - return reinterpret_cast<CharType_t**>(base); -} - -wchar_t **make_null_terminated_array(const wcstring_list_t &lst) -{ - return make_null_terminated_array_helper(lst); -} - -char **make_null_terminated_array(const std::vector<std::string> &lst) -{ - return make_null_terminated_array_helper(lst); -} |