static void export_func(const std::map<wcstring, wcstring> &envs, std::vector<std::string> &out) { std::map<wcstring, wcstring>::const_iterator iter; for (iter = envs.begin(); iter != envs.end(); ++iter) { char* ks = wcs2str(iter->first.c_str()); char* vs = wcs2str(iter->second.c_str()); char *pos = vs; while( *pos ) { if( *pos == ARRAY_SEP ) *pos = ':'; pos++; } /* Put a string on the vector */ out.push_back(std::string()); std::string &str = out.back(); /* Append our environment variable data to it */ str.append(ks); str.append("="); str.append(vs); free(ks); free(vs); } }
static int mf_enumerate_cameras(int (*callback)(IMFSourceReader* reader)) { com_t<IMFAttributes> config; if_failed_return_result(MFCreateAttributes(&config, 1)); if_failed_return_result(config->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID)); UINT32 num = 0; IMFActivate** factory = null; // [count] if_failed_return_result(MFEnumDeviceSources(config, &factory, &num)); if (num == 0) { return 0; } for (int i = 0; i < (int)num; i++) { const char* sl = ""; const char* fn= ""; WCHAR* symbolic_link = null; WCHAR* friendly_name = null; UINT32 size = 0; if (OK(factory[i]->GetAllocatedString(MF_SL, &symbolic_link, &size))) { sl = wcs2str(symbolic_link); } if (OK(factory[i]->GetAllocatedString(MF_FN, &friendly_name, &size))) { fn = wcs2str(friendly_name); } com_t<IMFMediaSource> source; if_failed_return_result(factory[i]->ActivateObject(IID_PPV_ARGS(&source))); com_t<IMFSourceReader> reader; if_failed_return_result(MFCreateSourceReaderFromMediaSource(source, null, &reader)); print("\nMMF camera %d %s %s\n", i, fn, sl); callback(reader); com_release(factory[i]); } CoTaskMemFree(factory); return S_OK; }
std::string wcs2string(const wcstring &input) { char *tmp = wcs2str(input.c_str()); std::string result = tmp; free(tmp); return result; }
wcstring wbasename(const wcstring &path) { char *tmp = wcs2str(path.c_str()); char *narrow_res = basename(tmp); wcstring result = format_string(L"%s", narrow_res); free(tmp); return result; }
char *encode_say (wchar *in) { static char buf[1024]; wchar *p; char *out; for (p = in; *p; p++) if (*p > 256) goto encode; strlcpy (buf, wcs2str(in), sizeof(buf)); return buf; encode: strlcpy (buf, "=`k8:", sizeof (buf)); out = buf + strlen(buf); while (*in && (out - buf < sizeof(buf)/sizeof(buf[0]))) { if (*in <= 255) *out++ = *in; else { *out++ = wc2koi(*in); } in++; } *out++ = '`'; *out++ = '='; *out++ = 0; return buf; }
/** Set up default values for various variables if not defined. */ static void env_set_defaults() { if( env_get_string(L"USER").missing() ) { struct passwd *pw = getpwuid( getuid()); wchar_t *unam = str2wcs( pw->pw_name ); env_set( L"USER", unam, ENV_GLOBAL ); free( unam ); } if( env_get_string(L"HOME").missing() ) { const env_var_t unam = env_get_string( L"USER" ); char *unam_narrow = wcs2str( unam.c_str() ); struct passwd *pw = getpwnam( unam_narrow ); wchar_t *dir = str2wcs( pw->pw_dir ); env_set( L"HOME", dir, ENV_GLOBAL ); free( dir ); free( unam_narrow ); } env_set_pwd(); }
/** Set up default values for various variables if not defined. */ static void env_set_defaults() { if (env_get_string(L"USER").missing()) { struct passwd *pw = getpwuid(getuid()); if (pw->pw_name != NULL) { const wcstring wide_name = str2wcstring(pw->pw_name); env_set(L"USER", wide_name.c_str(), ENV_GLOBAL); } } if (env_get_string(L"HOME").missing()) { const env_var_t unam = env_get_string(L"USER"); char *unam_narrow = wcs2str(unam.c_str()); struct passwd *pw = getpwnam(unam_narrow); if (pw->pw_dir != NULL) { const wcstring dir = str2wcstring(pw->pw_dir); env_set(L"HOME", dir.c_str(), ENV_GLOBAL); } free(unam_narrow); } env_set_pwd(); }
const wchar_t *wsetlocale(int category, const wchar_t *locale) { char *lang = locale?wcs2str( locale ):0; char * res = setlocale(category,lang); free( lang ); /* Use ellipsis if on known unicode system, otherwise use $ */ char *ctype = setlocale( LC_CTYPE, (void *)0 ); ellipsis_char = (strstr( ctype, ".UTF")||strstr( ctype, ".utf") )?L'\u2026':L'$'; if( !res ) return 0; if( !setlocale_buff ) { setlocale_buff = sb_halloc( global_context); } sb_clear( setlocale_buff ); sb_printf( setlocale_buff, L"%s", res ); return (wchar_t *)setlocale_buff->buff; }
static int ds_enumerate_cameras(int (*callback)(IBaseFilter* camera)) { com_t<ICreateDevEnum> enumerator; if_failed_return(CoCreateInstance(CLSID_SystemDeviceEnum, null, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator)), -1); com_t<IEnumMoniker> class_enumerator; if_failed_return(enumerator->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &class_enumerator, 0), -1); int n = 0; while (class_enumerator != null) { com_t<IMoniker> moniker; ULONG fetched = 0; if (class_enumerator->Next(1, &moniker, &fetched) != S_OK || fetched != 1 || moniker == null) { break; } com_t<IPropertyBag> prop_bag; HRESULT hr = moniker->BindToStorage(0, 0, IID_PPV_ARGS(&prop_bag)); if (OK(hr)) { com_t<IBaseFilter> camera; if (OK(moniker->BindToObject(0, 0, IID_PPV_ARGS(&camera)))) { const char* dp = ""; const char* fn = ""; VARIANT friendly_name; VariantInit(&friendly_name); if (OK(prop_bag->Read(L"FriendlyName", &friendly_name, 0))) { fn = wcs2str(friendly_name.bstrVal); } VARIANT device_path; VariantInit(&device_path); if (OK(prop_bag->Read(L"DevicePath", &device_path, 0))) { dp = wcs2str(device_path.bstrVal); } VariantClear(&friendly_name); VariantClear(&device_path); print("\nDirectShow camera %d %s %s\n", n++, fn, dp); callback(camera); } else { print("%s hr=0x%08X\n", strwinerr(hr), hr); } } else { print("%s hr=0x%08X\n", strwinerr(hr), hr); } } return 0; }
/// This function is similar to launch_process, except it is not called after a fork (i.e. it only /// calls exec) and therefore it can allocate memory. static void launch_process_nofork(process_t *p) { ASSERT_IS_MAIN_THREAD(); ASSERT_IS_NOT_FORKED_CHILD(); null_terminated_array_t<char> argv_array; convert_wide_array_to_narrow(p->get_argv_array(), &argv_array); const char *const *envv = env_export_arr(); char *actual_cmd = wcs2str(p->actual_cmd); // Ensure the terminal modes are what they were before we changed them. restore_term_mode(); // Bounce to launch_process. This never returns. safe_launch_process(p, actual_cmd, argv_array.get(), envv); }
static IPin* ds_get_pin(IBaseFilter* filter, const char* pin_name) { com_t<IEnumPins> enum_pins; IPin* pin = null; if_failed_return(filter->EnumPins(&enum_pins), null); while (enum_pins->Next(1, &pin, 0) == S_OK) { PIN_INFO pin_info = {0}; pin->QueryPinInfo(&pin_info); com_release(pin_info.pFilter); if (stricmp(pin_name, wcs2str(pin_info.achName)) == 0) { return pin; } com_release(pin); } return null; }
bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *out_path, int *out_fd) { /* Create and open a temporary file for writing within the given directory */ /* Try to create a temporary file, up to 10 times. We don't use mkstemps because we want to open it CLO_EXEC. This should almost always succeed on the first try. */ assert(! string_suffixes_string(L"/", directory)); bool success = false; const wcstring tmp_name_template = directory + L"/fishd.tmp.XXXXXX"; wcstring tmp_name; for (size_t attempt = 0; attempt < 10 && ! success; attempt++) { int result_fd = -1; char *narrow_str = wcs2str(tmp_name_template.c_str()); #if HAVE_MKOSTEMP result_fd = mkostemp(narrow_str, O_CLOEXEC); if (result_fd >= 0) { tmp_name = str2wcstring(narrow_str); } #else if (mktemp(narrow_str)) { /* It was successfully templated; try opening it atomically */ tmp_name = str2wcstring(narrow_str); result_fd = wopen_cloexec(tmp_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0644); } #endif if (result_fd >= 0) { /* Success */ *out_fd = result_fd; *out_path = str2wcstring(narrow_str); success = true; } free(narrow_str); } if (! success) { int err = errno; report_error(err, L"Unable to open file '%ls'", tmp_name.c_str()); } return success; }
/** Get environment variable value. The resulting string needs to be free'd. */ static wchar_t *fishd_env_get( const wchar_t *key ) { char *nres, *nkey; wchar_t *res; nkey = wcs2str( key ); nres = getenv( nkey ); free( nkey ); if( nres ) { return str2wcs( nres ); } else { res = env_universal_common_get( key ); if( res ) res = wcsdup( res ); return res; } }
wcstring wsetlocale(int category, const wchar_t *locale) { char *lang = NULL; if (locale){ lang = wcs2str( locale ); } char * res = setlocale(category,lang); free( lang ); /* Use ellipsis if on known unicode system, otherwise use $ */ char *ctype = setlocale( LC_CTYPE, NULL ); ellipsis_char = (strstr( ctype, ".UTF")||strstr( ctype, ".utf") )?L'\x2026':L'$'; if( !res ) return wcstring(); else return format_string(L"%s", res); }
/** The real implementation of wildcard expansion is in this function. Other functions are just wrappers around this one. This function traverses the relevant directory tree looking for matches, and recurses when needed to handle wildcrards spanning multiple components and recursive wildcards. */ static int wildcard_expand_internal( const wchar_t *wc, const wchar_t *base_dir, expand_flags_t flags, std::vector<completion_t> &out, std::set<wcstring> &completion_set, std::set<file_id_t> &visited_files ) { /* Points to the end of the current wildcard segment */ const wchar_t *wc_end; /* Variables for traversing a directory */ DIR *dir; /* The result returned */ int res = 0; /* Length of the directory to search in */ size_t base_len; /* Variables for testing for presense of recursive wildcards */ const wchar_t *wc_recursive; int is_recursive; /* Slightly mangled version of base_dir */ const wchar_t *dir_string; // debug( 3, L"WILDCARD_EXPAND %ls in %ls", wc, base_dir ); if( reader_interrupted() ) { return -1; } if( !wc || !base_dir ) { debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); return 0; } if( flags & ACCEPT_INCOMPLETE ) { /* Avoid excessive number of returned matches for wc ending with a * */ size_t len = wcslen(wc); if( len && (wc[len-1]==ANY_STRING) ) { wchar_t * foo = wcsdup( wc ); foo[len-1]=0; int res = wildcard_expand_internal( foo, base_dir, flags, out, completion_set, visited_files ); free( foo ); return res; } } /* Initialize various variables */ dir_string = base_dir[0]==L'\0'?L".":base_dir; if( !(dir = wopendir( dir_string ))) { return 0; } wc_end = wcschr(wc,L'/'); base_len = wcslen( base_dir ); /* Test for recursive match string in current segment */ wc_recursive = wcschr( wc, ANY_STRING_RECURSIVE ); is_recursive = ( wc_recursive && (!wc_end || wc_recursive < wc_end)); /* Is this segment of the wildcard the last? */ if( !wc_end ) { /* Wildcard segment is the last segment, Insert all matching files/directories */ if( wc[0]=='\0' ) { /* The last wildcard segment is empty. Insert everything if completing, the directory itself otherwise. */ if( flags & ACCEPT_INCOMPLETE ) { wcstring next; while(wreaddir(dir, next)) { if( next[0] != L'.' ) { wcstring long_name = make_path( base_dir, next ); if( test_flags( long_name.c_str(), flags ) ) { wildcard_completion_allocate( out, long_name, next, L"", flags); } } } } else { res = 1; insert_completion_if_missing(base_dir, out, completion_set); } } else { /* This is the last wildcard segment, and it is not empty. Match files/directories. */ wcstring next; while (wreaddir(dir, next)) { const wchar_t * const name = next.c_str(); if( flags & ACCEPT_INCOMPLETE ) { const wcstring long_name = make_path( base_dir, next ); /* Test for matches before stating file, so as to minimize the number of calls to the much slower stat function */ std::vector<completion_t> test; if( wildcard_complete( name, wc, L"", 0, test, 0 ) ) { if( test_flags( long_name.c_str(), flags ) ) { wildcard_completion_allocate( out, long_name, name, wc, flags); } } } else { if( wildcard_match2( name, wc, 1 ) ) { const wcstring long_name = make_path(base_dir, next); int skip = 0; if( is_recursive ) { /* In recursive mode, we are only interested in adding files -directories will be added in the next pass. */ struct stat buf; if( !wstat( long_name, &buf ) ) { skip = S_ISDIR(buf.st_mode); } } if (! skip) { insert_completion_if_missing(long_name, out, completion_set); } res = 1; } } } } } if( wc_end || is_recursive ) { /* Wilcard segment is not the last segment. Recursively call wildcard_expand for all matching subdirectories. */ /* wc_str is the part of the wildcarded string from the beginning to the first slash */ wchar_t *wc_str; /* new_dir is a scratch area containing the full path to a file/directory we are iterating over */ wchar_t *new_dir; /* The maximum length of a file element */ long ln=MAX_FILE_LENGTH; char * narrow_dir_string = wcs2str( dir_string ); /* In recursive mode, we look through the directory twice. If so, this rewind is needed. */ rewinddir( dir ); if( narrow_dir_string ) { /* Find out how long the filename can be in a worst case scenario */ ln = pathconf( narrow_dir_string, _PC_NAME_MAX ); /* If not specified, use som large number as fallback */ if( ln < 0 ) ln = MAX_FILE_LENGTH; free( narrow_dir_string ); } new_dir= (wchar_t *)malloc( sizeof(wchar_t)*(base_len+ln+2) ); wc_str = wc_end?wcsndup(wc, wc_end-wc):wcsdup(wc); if( (!new_dir) || (!wc_str) ) { DIE_MEM(); } wcscpy( new_dir, base_dir ); wcstring next; while (wreaddir(dir, next)) { const wchar_t *name = next.c_str(); /* Test if the file/directory name matches the whole wildcard element, i.e. regular matching. */ int whole_match = wildcard_match2( name, wc_str, 1 ); int partial_match = 0; /* If we are doing recursive matching, also check if this directory matches the part up to the recusrive wildcard, if so, then we can search all subdirectories for matches. */ if( is_recursive ) { const wchar_t *end = wcschr( wc, ANY_STRING_RECURSIVE ); wchar_t *wc_sub = wcsndup( wc, end-wc+1); partial_match = wildcard_match2( name, wc_sub, 1 ); free( wc_sub ); } if( whole_match || partial_match ) { struct stat buf; char *dir_str; int stat_res; int new_res; wcscpy(&new_dir[base_len], name ); dir_str = wcs2str( new_dir ); if( dir_str ) { stat_res = stat( dir_str, &buf ); free( dir_str ); if( !stat_res ) { // Insert a "file ID" into visited_files // If the insertion fails, we've already visited this file (i.e. a symlink loop) const file_id_t file_id(buf.st_dev, buf.st_ino); if( S_ISDIR(buf.st_mode) && visited_files.insert(file_id).second) { size_t new_len = wcslen( new_dir ); new_dir[new_len] = L'/'; new_dir[new_len+1] = L'\0'; /* Regular matching */ if( whole_match ) { const wchar_t *new_wc = L""; if( wc_end ) { new_wc=wc_end+1; /* Accept multiple '/' as a single direcotry separator */ while(*new_wc==L'/') { new_wc++; } } new_res = wildcard_expand_internal( new_wc, new_dir, flags, out, completion_set, visited_files ); if( new_res == -1 ) { res = -1; break; } res |= new_res; } /* Recursive matching */ if( partial_match ) { new_res = wildcard_expand_internal( wcschr( wc, ANY_STRING_RECURSIVE ), new_dir, flags | WILDCARD_RECURSIVE, out, completion_set, visited_files); if( new_res == -1 ) { res = -1; break; } res |= new_res; } } } } } } free( wc_str ); free( new_dir ); } closedir( dir ); return res; }
void env_init(const struct config_paths_t *paths /* or NULL */) { /* env_read_only variables can not be altered directly by the user */ const wchar_t * const ro_keys[] = { L"status", L"history", L"version", L"_", L"LINES", L"COLUMNS", L"PWD", //L"SHLVL", // will be inserted a bit lower down L"FISH_VERSION", }; for (size_t i=0; i < sizeof ro_keys / sizeof *ro_keys; i++) { env_read_only.insert(ro_keys[i]); } /* Names of all dynamically calculated variables */ env_electric.insert(L"history"); env_electric.insert(L"status"); env_electric.insert(L"umask"); env_electric.insert(L"COLUMNS"); env_electric.insert(L"LINES"); top = new env_node_t; global_env = top; global = &top->env; /* Now the environemnt variable handling is set up, the next step is to insert valid data */ /* Import environment variables */ for (char **p = (environ ? environ : __environ); p && *p; p++) { const wcstring key_and_val = str2wcstring(*p); //like foo=bar size_t eql = key_and_val.find(L'='); if (eql == wcstring::npos) { // no equals found if (is_read_only(key_and_val) || is_electric(key_and_val)) continue; env_set(key_and_val, L"", ENV_EXPORT | ENV_GLOBAL); } else { wcstring key = key_and_val.substr(0, eql); if (is_read_only(key) || is_electric(key)) continue; wcstring val = key_and_val.substr(eql + 1); if (variable_is_colon_delimited_array(key)) { std::replace(val.begin(), val.end(), L':', ARRAY_SEP); } env_set(key, val.c_str(), ENV_EXPORT | ENV_GLOBAL); } } /* Set the given paths in the environment, if we have any */ if (paths != NULL) { env_set(FISH_DATADIR_VAR, paths->data.c_str(), ENV_GLOBAL); env_set(FISH_SYSCONFDIR_VAR, paths->sysconf.c_str(), ENV_GLOBAL); env_set(FISH_HELPDIR_VAR, paths->doc.c_str(), ENV_GLOBAL); env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL); } /* Set up the PATH variable */ setup_path(); /* Set up the USER variable */ if (env_get_string(L"USER").missing_or_empty()) { const struct passwd *pw = getpwuid(getuid()); if (pw && pw->pw_name) { const wcstring uname = str2wcstring(pw->pw_name); env_set(L"USER", uname.c_str(), ENV_GLOBAL | ENV_EXPORT); } } /* Set up the version variables */ wcstring version = str2wcstring(get_fish_version()); env_set(L"version", version.c_str(), ENV_GLOBAL); env_set(L"FISH_VERSION", version.c_str(), ENV_GLOBAL); /* Set up SHLVL variable */ const env_var_t shlvl_str = env_get_string(L"SHLVL"); wcstring nshlvl_str = L"1"; if (! shlvl_str.missing()) { wchar_t *end; long shlvl_i = wcstol(shlvl_str.c_str(), &end, 10); while (iswspace(*end)) ++end; /* skip trailing whitespace */ if (shlvl_i >= 0 && *end == '\0') { nshlvl_str = to_string<long>(shlvl_i + 1); } } env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT); env_read_only.insert(L"SHLVL"); /* Set up the HOME variable */ if (env_get_string(L"HOME").missing_or_empty()) { const env_var_t unam = env_get_string(L"USER"); char *unam_narrow = wcs2str(unam.c_str()); struct passwd *pw = getpwnam(unam_narrow); if (pw->pw_dir != NULL) { const wcstring dir = str2wcstring(pw->pw_dir); env_set(L"HOME", dir.c_str(), ENV_GLOBAL | ENV_EXPORT); } free(unam_narrow); } /* Set PWD */ env_set_pwd(); /* Set up universal variables. The empty string means to use the deafult path. */ assert(s_universal_variables == NULL); s_universal_variables = new env_universal_t(L""); s_universal_variables->load(); /* Set g_log_forks */ env_var_t log_forks = env_get_string(L"fish_log_forks"); g_log_forks = ! log_forks.missing_or_empty() && from_string<bool>(log_forks); /* Set g_use_posix_spawn. Default to true. */ env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn"); g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string<bool>(use_posix_spawn)); /* Set fish_bind_mode to "default" */ env_set(FISH_BIND_MODE_VAR, DEFAULT_BIND_MODE, ENV_GLOBAL); /* Now that the global scope is fully initialized, add a toplevel local scope. This same local scope will persist throughout the lifetime of the fish process, and it will ensure that `set -l` commands run at the command-line don't affect the global scope. */ env_push(false); }
/** Get a socket for reading from the server */ static int get_socket( int fork_ok ) { int s, len; struct sockaddr_un local; char *name; wchar_t *wdir; wchar_t *wuname; char *dir =0, *uname=0; get_socket_count++; wdir = path; wuname = user; if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { wperror(L"socket"); return -1; } if( wdir ) dir = wcs2str(wdir ); else dir = strdup("/tmp"); if( wuname ) uname = wcs2str(wuname ); else { struct passwd *pw; pw = getpwuid( getuid() ); uname = strdup( pw->pw_name ); } name = (char *)malloc( strlen(dir) + strlen(uname) + strlen(SOCK_FILENAME) + 2 ); strcpy( name, dir ); strcat( name, "/" ); strcat( name, SOCK_FILENAME ); strcat( name, uname ); free( dir ); free( uname ); debug( 3, L"Connect to socket %s at fd %2", name, s ); local.sun_family = AF_UNIX; strcpy(local.sun_path, name ); free( name ); len = sizeof(local); if( connect( s, (struct sockaddr *)&local, len) == -1 ) { close( s ); if( fork_ok && start_fishd ) { debug( 2, L"Could not connect to socket %d, starting fishd", s ); start_fishd(); return get_socket( 0 ); } debug( 1, L"Could not connect to universal variable server, already tried manual restart (or no command supplied). You will not be able to share variable values between fish sessions. Is fish properly installed?" ); return -1; } if( (fcntl( s, F_SETFL, O_NONBLOCK ) != 0) || (fcntl( s, F_SETFD, FD_CLOEXEC ) != 0) ) { wperror( L"fcntl" ); close( s ); return -1; } debug( 3, L"Connected to fd %d", s ); return s; }
//Find and execute sound triggers. A sound trigger must be terminated by either a CR or LF. //Returns true if a sound was found and played qbool TP_CheckSoundTrigger (wchar *wstr) { char *str; int i, j, start, length; char soundname[MAX_OSPATH]; #ifndef WITH_FTE_VFS FILE *f; #else vfsfile_t *v; #endif str = wcs2str (wstr); if (!*str) return false; if (!tp_soundtrigger.string[0]) return false; for (i = strlen (str) - 1; i; i--) { if (str[i] != 0x0A && str[i] != 0x0D) continue; for (j = i - 1; j >= 0; j--) { // quick check for chars that cannot be used // as sound triggers but might be part of a file name if (isalnum((unsigned char)str[j])) continue; // file name or chat if (strchr(tp_soundtrigger.string, str[j])) { // this might be a sound trigger start = j + 1; length = i - start; if (!length) break; if (length >= MAX_QPATH) break; strlcpy (soundname, str + start, length + 1); if (strstr(soundname, "..")) break; // no thank you // clean up the message strcpy (str + j, str + i); qwcscpy (wstr + j, wstr + i); if (!snd_initialized) return false; COM_DefaultExtension (soundname, ".wav"); // make sure we have it on disk (FIXME) #ifndef WITH_FTE_VFS FS_FOpenFile (va("sound/%s", soundname), &f); if (!f) return false; fclose (f); #else if (!(v = FS_OpenVFS(va("sound/%s", soundname), "rb", FS_ANY))) return false; VFS_CLOSE(v); #endif // now play the sound S_LocalSound (soundname); return true; } if (str[j] == '\\') str[j] = '/'; if (str[j] <= ' ' || strchr("\"&'*,:;<>?\\|\x7f", str[j])) break; // we don't allow these in a file name } } return false; }
void exec( job_t *j ) { process_t *p; pid_t pid; int mypipe[2]; sigset_t chldset; int skip_fork; io_data_t pipe_read, pipe_write; io_data_t *tmp; io_data_t *io_buffer =0; /* Set to 1 if something goes wrong while exec:ing the job, in which case the cleanup code will kick in. */ int exec_error=0; int needs_keepalive = 0; process_t keepalive; CHECK( j, ); CHECK_BLOCK(); if( no_exec ) return; sigemptyset( &chldset ); sigaddset( &chldset, SIGCHLD ); debug( 4, L"Exec job '%ls' with id %d", j->command, j->job_id ); if( block_io ) { if( j->io ) { j->io = io_add( io_duplicate( j, block_io), j->io ); } else { j->io=io_duplicate( j, block_io); } } io_data_t *input_redirect; for( input_redirect = j->io; input_redirect; input_redirect = input_redirect->next ) { if( (input_redirect->io_mode == IO_BUFFER) && input_redirect->is_input ) { /* Input redirection - create a new gobetween process to take care of buffering */ process_t *fake = halloc( j, sizeof(process_t) ); fake->type = INTERNAL_BUFFER; fake->pipe_write_fd = 1; j->first_process->pipe_read_fd = input_redirect->fd; fake->next = j->first_process; j->first_process = fake; break; } } if( j->first_process->type==INTERNAL_EXEC ) { /* Do a regular launch - but without forking first... */ signal_block(); /* setup_child_process makes sure signals are properly set up. It will also call signal_unblock */ if( !setup_child_process( j, 0 ) ) { /* launch_process _never_ returns */ launch_process( j->first_process ); } else { job_set_flag( j, JOB_CONSTRUCTED, 1 ); j->first_process->completed=1; return; } } pipe_read.fd=0; pipe_write.fd=1; pipe_read.io_mode=IO_PIPE; pipe_read.param1.pipe_fd[0] = -1; pipe_read.param1.pipe_fd[1] = -1; pipe_read.is_input = 1; pipe_write.io_mode=IO_PIPE; pipe_write.is_input = 0; pipe_read.next=0; pipe_write.next=0; pipe_write.param1.pipe_fd[0]=pipe_write.param1.pipe_fd[1]=-1; j->io = io_add( j->io, &pipe_write ); signal_block(); /* See if we need to create a group keepalive process. This is a process that we create to make sure that the process group doesn't die accidentally, and is often needed when a builtin/block/function is inside a pipeline, since that usually means we have to wait for one program to exit before continuing in the pipeline, causing the group leader to exit. */ if( job_get_flag( j, JOB_CONTROL ) ) { for( p=j->first_process; p; p = p->next ) { if( p->type != EXTERNAL ) { if( p->next ) { needs_keepalive = 1; break; } if( p != j->first_process ) { needs_keepalive = 1; break; } } } } if( needs_keepalive ) { keepalive.pid = exec_fork(); if( keepalive.pid == 0 ) { keepalive.pid = getpid(); set_child_group( j, &keepalive, 1 ); pause(); exit(0); } else { set_child_group( j, &keepalive, 0 ); } } /* This loop loops over every process_t in the job, starting it as appropriate. This turns out to be rather complex, since a process_t can be one of many rather different things. The loop also has to handle pipelining between the jobs. */ for( p=j->first_process; p; p = p->next ) { mypipe[1]=-1; skip_fork=0; pipe_write.fd = p->pipe_write_fd; pipe_read.fd = p->pipe_read_fd; // debug( 0, L"Pipe created from fd %d to fd %d", pipe_write.fd, pipe_read.fd ); /* This call is used so the global environment variable array is regenerated, if needed, before the fork. That way, we avoid a lot of duplicate work where EVERY child would need to generate it, since that result would not get written back to the parent. This call could be safely removed, but it would result in slightly lower performance - at least on uniprocessor systems. */ if( p->type == EXTERNAL ) env_export_arr( 1 ); /* Set up fd:s that will be used in the pipe */ if( p == j->first_process->next ) { j->io = io_add( j->io, &pipe_read ); } if( p->next ) { // debug( 1, L"%ls|%ls" , p->argv[0], p->next->argv[0]); if( exec_pipe( mypipe ) == -1 ) { debug( 1, PIPE_ERROR ); wperror (L"pipe"); exec_error=1; break; } memcpy( pipe_write.param1.pipe_fd, mypipe, sizeof(int)*2); } else { /* This is the last element of the pipeline. Remove the io redirection for pipe output. */ j->io = io_remove( j->io, &pipe_write ); } switch( p->type ) { case INTERNAL_FUNCTION: { const wchar_t * orig_def; wchar_t * def=0; array_list_t *named_arguments; int shadows; /* Calls to function_get_definition might need to source a file as a part of autoloading, hence there must be no blocks. */ signal_unblock(); orig_def = function_get_definition( p->argv[0] ); named_arguments = function_get_named_arguments( p->argv[0] ); shadows = function_get_shadows( p->argv[0] ); signal_block(); if( orig_def ) { def = halloc_register( j, wcsdup(orig_def) ); } if( def == 0 ) { debug( 0, _( L"Unknown function '%ls'" ), p->argv[0] ); break; } parser_push_block( shadows?FUNCTION_CALL:FUNCTION_CALL_NO_SHADOW ); current_block->param2.function_call_process = p; current_block->param1.function_call_name = halloc_register( current_block, wcsdup( p->argv[0] ) ); /* set_argv might trigger an event handler, hence we need to unblock signals. */ signal_unblock(); parse_util_set_argv( p->argv+1, named_arguments ); signal_block(); parser_forbid_function( p->argv[0] ); if( p->next ) { io_buffer = io_buffer_create( 0 ); j->io = io_add( j->io, io_buffer ); } internal_exec_helper( def, TOP, j->io ); parser_allow_function(); parser_pop_block(); break; } case INTERNAL_BLOCK: { if( p->next ) { io_buffer = io_buffer_create( 0 ); j->io = io_add( j->io, io_buffer ); } internal_exec_helper( p->argv[0], TOP, j->io ); break; } case INTERNAL_BUILTIN: { int builtin_stdin=0; int fg; int close_stdin=0; /* If this is the first process, check the io redirections and see where we should be reading from. */ if( p == j->first_process ) { io_data_t *in = io_get( j->io, 0 ); if( in ) { switch( in->io_mode ) { case IO_FD: { builtin_stdin = in->param1.old_fd; break; } case IO_PIPE: { builtin_stdin = in->param1.pipe_fd[0]; break; } case IO_FILE: { builtin_stdin=wopen( in->param1.filename, in->param2.flags, OPEN_MASK ); if( builtin_stdin == -1 ) { debug( 1, FILE_ERROR, in->param1.filename ); wperror( L"open" ); } else { close_stdin = 1; } break; } case IO_CLOSE: { /* FIXME: When requesting that stdin be closed, we really don't do anything. How should this be handled? */ builtin_stdin = -1; break; } default: { builtin_stdin=-1; debug( 1, _( L"Unknown input redirection type %d" ), in->io_mode); break; } } } } else { builtin_stdin = pipe_read.param1.pipe_fd[0]; } if( builtin_stdin == -1 ) { exec_error=1; break; } else { int old_out = builtin_out_redirect; int old_err = builtin_err_redirect; /* Since this may be the foreground job, and since a builtin may execute another foreground job, we need to pretend to suspend this job while running the builtin, in order to avoid a situation where two jobs are running at once. The reason this is done here, and not by the relevant builtins, is that this way, the builtin does not need to know what job it is part of. It could probably figure that out by walking the job list, but it seems more robust to make exec handle things. */ builtin_push_io( builtin_stdin ); builtin_out_redirect = has_fd( j->io, 1 ); builtin_err_redirect = has_fd( j->io, 2 ); fg = job_get_flag( j, JOB_FOREGROUND ); job_set_flag( j, JOB_FOREGROUND, 0 ); signal_unblock(); p->status = builtin_run( p->argv, j->io ); builtin_out_redirect=old_out; builtin_err_redirect=old_err; signal_block(); /* Restore the fg flag, which is temporarily set to false during builtin execution so as not to confuse some job-handling builtins. */ job_set_flag( j, JOB_FOREGROUND, fg ); } /* If stdin has been redirected, close the redirection stream. */ if( close_stdin ) { exec_close( builtin_stdin ); } break; } } if( exec_error ) { break; } switch( p->type ) { case INTERNAL_BLOCK: case INTERNAL_FUNCTION: { int status = proc_get_last_status(); /* Handle output from a block or function. This usually means do nothing, but in the case of pipes, we have to buffer such io, since otherwise the internal pipe buffer might overflow. */ if( !io_buffer ) { /* No buffer, so we exit directly. This means we have to manually set the exit status. */ if( p->next == 0 ) { proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status); } p->completed = 1; break; } j->io = io_remove( j->io, io_buffer ); io_buffer_read( io_buffer ); if( io_buffer->param2.out_buffer->used != 0 ) { pid = exec_fork(); if( pid == 0 ) { /* This is the child process. Write out the contents of the pipeline. */ p->pid = getpid(); setup_child_process( j, p ); exec_write_and_exit(io_buffer->fd, io_buffer->param2.out_buffer->buff, io_buffer->param2.out_buffer->used, status); } else { /* This is the parent process. Store away information on the child, and possibly give it control over the terminal. */ p->pid = pid; set_child_group( j, p, 0 ); } } else { if( p->next == 0 ) { proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status); } p->completed = 1; } io_buffer_destroy( io_buffer ); io_buffer=0; break; } case INTERNAL_BUFFER: { pid = exec_fork(); if( pid == 0 ) { /* This is the child process. Write out the contents of the pipeline. */ p->pid = getpid(); setup_child_process( j, p ); exec_write_and_exit( 1, input_redirect->param2.out_buffer->buff, input_redirect->param2.out_buffer->used, 0); } else { /* This is the parent process. Store away information on the child, and possibly give it control over the terminal. */ p->pid = pid; set_child_group( j, p, 0 ); } break; } case INTERNAL_BUILTIN: { int skip_fork; /* Handle output from builtin commands. In the general case, this means forking of a worker process, that will write out the contents of the stdout and stderr buffers to the correct file descriptor. Since forking is expensive, fish tries to avoid it wehn possible. */ /* If a builtin didn't produce any output, and it is not inside a pipeline, there is no need to fork */ skip_fork = ( !sb_out->used ) && ( !sb_err->used ) && ( !p->next ); /* If the output of a builtin is to be sent to an internal buffer, there is no need to fork. This helps out the performance quite a bit in complex completion code. */ io_data_t *io = io_get( j->io, 1 ); int buffer_stdout = io && io->io_mode == IO_BUFFER; if( ( !sb_err->used ) && ( !p->next ) && ( sb_out->used ) && ( buffer_stdout ) ) { char *res = wcs2str( (wchar_t *)sb_out->buff ); b_append( io->param2.out_buffer, res, strlen( res ) ); skip_fork = 1; free( res ); } for( io = j->io; io; io=io->next ) { if( io->io_mode == IO_FILE && wcscmp(io->param1.filename, L"/dev/null" )) { skip_fork = 0; } } if( skip_fork ) { p->completed=1; if( p->next == 0 ) { debug( 3, L"Set status of %ls to %d using short circut", j->command, p->status ); int status = proc_format_status(p->status); proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status ); } break; } /* Ok, unfortunatly, we have to do a real fork. Bummer. */ pid = exec_fork(); if( pid == 0 ) { /* This is the child process. Setup redirections, print correct output to stdout and stderr, and then exit. */ p->pid = getpid(); setup_child_process( j, p ); do_builtin_io( sb_out->used ? (wchar_t *)sb_out->buff : 0, sb_err->used ? (wchar_t *)sb_err->buff : 0 ); exit( p->status ); } else { /* This is the parent process. Store away information on the child, and possibly give it control over the terminal. */ p->pid = pid; set_child_group( j, p, 0 ); } break; } case EXTERNAL: { pid = exec_fork(); if( pid == 0 ) { /* This is the child process. */ p->pid = getpid(); setup_child_process( j, p ); launch_process( p ); /* launch_process _never_ returns... */ } else { /* This is the parent process. Store away information on the child, and possibly fice it control over the terminal. */ p->pid = pid; set_child_group( j, p, 0 ); } break; } } if( p->type == INTERNAL_BUILTIN ) builtin_pop_io(); /* Close the pipe the current process uses to read from the previous process_t */ if( pipe_read.param1.pipe_fd[0] >= 0 ) exec_close( pipe_read.param1.pipe_fd[0] ); /* Set up the pipe the next process uses to read from the current process_t */ if( p->next ) pipe_read.param1.pipe_fd[0] = mypipe[0]; /* If there is a next process in the pipeline, close the output end of the current pipe (the surrent child subprocess already has a copy of the pipe - this makes sure we don't leak file descriptors either in the shell or in the children). */ if( p->next ) { exec_close(mypipe[1]); } } /* The keepalive process is no longer needed, so we terminate it with extreme prejudice */ if( needs_keepalive ) { kill( keepalive.pid, SIGKILL ); } signal_unblock(); debug( 3, L"Job is constructed" ); j->io = io_remove( j->io, &pipe_read ); for( tmp = block_io; tmp; tmp=tmp->next ) j->io = io_remove( j->io, tmp ); job_set_flag( j, JOB_CONSTRUCTED, 1 ); if( !job_get_flag( j, JOB_FOREGROUND ) ) { proc_last_bg_pid = j->pgid; } if( !exec_error ) { job_continue (j, 0); } }
/** This function is executed by the child process created by a call to fork(). It should be called after \c setup_child_process. It calls execve to replace the fish process image with the command specified in \c p. It never returns. */ static void launch_process( process_t *p ) { FILE* f; int err; // debug( 1, L"exec '%ls'", p->argv[0] ); char **argv = wcsv2strv( (const wchar_t **) p->argv); char **envv = env_export_arr( 0 ); execve ( wcs2str(p->actual_cmd), argv, envv ); err = errno; /* Something went wrong with execve, check for a ":", and run /bin/sh if encountered. This is a weird predecessor to the shebang that is still sometimes used since it is supported on Windows. */ f = wfopen(p->actual_cmd, "r"); if( f ) { char begin[1] = {0}; size_t read; read = fread(begin, 1, 1, f); fclose( f ); if( (read==1) && (begin[0] == ':') ) { int count = 0; int i = 1; wchar_t **res; char **res_real; while( p->argv[count] != 0 ) count++; res = malloc( sizeof(wchar_t*)*(count+2)); res[0] = L"/bin/sh"; res[1] = p->actual_cmd; for( i=1; p->argv[i]; i++ ){ res[i+1] = p->argv[i]; } res[i+1] = 0; p->argv = res; p->actual_cmd = L"/bin/sh"; res_real = wcsv2strv( (const wchar_t **) res); execve ( wcs2str(p->actual_cmd), res_real, envv ); } } errno = err; debug( 0, _( L"Failed to execute process '%ls'. Reason:" ), p->actual_cmd ); switch( errno ) { case E2BIG: { size_t sz = 0; char **p; string_buffer_t sz1; string_buffer_t sz2; long arg_max = -1; sb_init( &sz1 ); sb_init( &sz2 ); for(p=argv; *p; p++) { sz += strlen(*p)+1; } for(p=envv; *p; p++) { sz += strlen(*p)+1; } sb_format_size( &sz1, sz ); arg_max = sysconf( _SC_ARG_MAX ); if( arg_max > 0 ) { sb_format_size( &sz2, arg_max ); debug( 0, L"The total size of the argument and environment lists (%ls) exceeds the operating system limit of %ls.", (wchar_t *)sz1.buff, (wchar_t *)sz2.buff); } else { debug( 0, L"The total size of the argument and environment lists (%ls) exceeds the operating system limit.", (wchar_t *)sz1.buff); } debug( 0, L"Try running the command again with fewer arguments."); sb_destroy( &sz1 ); sb_destroy( &sz2 ); exit(STATUS_EXEC_FAIL); break; } case ENOEXEC: { wperror(L"exec"); debug(0, L"The file '%ls' is marked as an executable but could not be run by the operating system.", p->actual_cmd); exit(STATUS_EXEC_FAIL); } case ENOENT: { wchar_t *interpreter = get_interpreter( p->actual_cmd ); if( interpreter && waccess( interpreter, X_OK ) ) { debug(0, L"The file '%ls' specified the interpreter '%ls', which is not an executable command.", p->actual_cmd, interpreter ); } else { debug(0, L"The file '%ls' or a script or ELF interpreter does not exist, or a shared library needed for file or interpreter cannot be found.", p->actual_cmd); } exit(STATUS_EXEC_FAIL); } case ENOMEM: { debug(0, L"Out of memory"); exit(STATUS_EXEC_FAIL); } default: { wperror(L"exec"); // debug(0, L"The file '%ls' is marked as an executable but could not be run by the operating system.", p->actual_cmd); exit(STATUS_EXEC_FAIL); } } }
int main(int argc, char *argv[]) { program_name = L"fish_indent"; set_main_thread(); setup_fork_guards(); // Using the user's default locale could be a problem if it doesn't use UTF-8 encoding. That's // because the fish project assumes Unicode UTF-8 encoding in all of its scripts. // // TODO: Auto-detect the encoding of the script. We should look for a vim style comment // (e.g., "# vim: set fileencoding=<encoding-name>:") or an emacs style comment // (e.g., "# -*- coding: <encoding-name> -*-"). setlocale(LC_ALL, ""); env_init(); input_init(); // Types of output we support. enum { output_type_plain_text, output_type_file, output_type_ansi, output_type_html } output_type = output_type_plain_text; const char *output_location = ""; bool do_indent = true; const char *short_opts = "+d:hvwiD:"; const struct option long_opts[] = {{"debug-level", required_argument, NULL, 'd'}, {"debug-stack-frames", required_argument, NULL, 'D'}, {"dump-parse-tree", no_argument, NULL, 'P'}, {"no-indent", no_argument, NULL, 'i'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"write", no_argument, NULL, 'w'}, {"html", no_argument, NULL, 1}, {"ansi", no_argument, NULL, 2}, {NULL, 0, NULL, 0} }; int opt; while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch (opt) { case 0: { fwprintf(stderr, _(L"getopt_long() unexpectedly returned zero\n")); exit(127); break; } case 'P': { dump_parse_tree = true; break; } case 'h': { print_help("fish_indent", 1); exit(0); break; } case 'v': { fwprintf(stderr, _(L"%ls, version %s\n"), program_name, get_fish_version()); exit(0); break; } case 'w': { output_type = output_type_file; break; } case 'i': { do_indent = false; break; } case 1: { output_type = output_type_html; break; } case 2: { output_type = output_type_ansi; break; } case 'd': { char *end; long tmp; errno = 0; tmp = strtol(optarg, &end, 10); if (tmp >= 0 && tmp <= 10 && !*end && !errno) { debug_level = (int)tmp; } else { fwprintf(stderr, _(L"Invalid value '%s' for debug-level flag"), optarg); exit(1); } break; } case 'D': { char *end; long tmp; errno = 0; tmp = strtol(optarg, &end, 10); if (tmp > 0 && tmp <= 128 && !*end && !errno) { debug_stack_frames = (int)tmp; } else { fwprintf(stderr, _(L"Invalid value '%s' for debug-stack-frames flag"), optarg); exit(1); } break; } default: { // We assume getopt_long() has already emitted a diagnostic msg. exit(1); break; } } } argc -= optind; argv += optind; wcstring src; if (argc == 0) { if (output_type == output_type_file) { fwprintf(stderr, _(L"Expected file path to read/write for -w:\n\n $ %ls -w foo.fish\n"), program_name); exit(1); } src = read_file(stdin); } else if (argc == 1) { FILE *fh = fopen(*argv, "r"); if (fh) { src = read_file(fh); fclose(fh); output_location = *argv; } else { fwprintf(stderr, _(L"Opening \"%s\" failed: %s\n"), *argv, strerror(errno)); exit(1); } } else { fwprintf(stderr, _(L"Too many arguments\n")); exit(1); } const wcstring output_wtext = prettify(src, do_indent); // Maybe colorize. std::vector<highlight_spec_t> colors; if (output_type != output_type_plain_text) { highlight_shell_no_io(output_wtext, colors, output_wtext.size(), NULL, env_vars_snapshot_t::current()); } std::string colored_output; switch (output_type) { case output_type_plain_text: { colored_output = no_colorize(output_wtext); break; } case output_type_file: { FILE *fh = fopen(output_location, "w"); if (fh) { fputs(wcs2str(output_wtext), fh); fclose(fh); exit(0); } else { fwprintf(stderr, _(L"Opening \"%s\" failed: %s\n"), output_location, strerror(errno)); exit(1); } break; } case output_type_ansi: { colored_output = ansi_colorize(output_wtext, colors); break; } case output_type_html: { colored_output = html_colorize(output_wtext, colors); break; } } fputs(colored_output.c_str(), stdout); return 0; }
static int try_get_socket_once(void) { int s; wchar_t *wdir; wchar_t *wuname; char *dir = 0; wdir = path; wuname = user; if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { wperror(L"socket"); return -1; } if (wdir) dir = wcs2str(wdir); else dir = strdup("/tmp"); std::string uname; if (wuname) { uname = wcs2string(wuname); } else { struct passwd *pw = getpwuid(getuid()); if (pw && pw->pw_name) { uname = pw->pw_name; } } std::string name; name.reserve(strlen(dir) + uname.size() + strlen(SOCK_FILENAME) + 2); name.append(dir); name.append("/"); name.append(SOCK_FILENAME); name.append(uname); free(dir); debug(3, L"Connect to socket %s at fd %d", name.c_str(), s); struct sockaddr_un local = {}; local.sun_family = AF_UNIX; strncpy(local.sun_path, name.c_str(), (sizeof local.sun_path) - 1); if (connect(s, (struct sockaddr *)&local, sizeof local) == -1) { close(s); /* If it fails on first try, it's probably no serious error, but fishd hasn't been launched yet. This happens (at least) on the first concurrent session. */ if (get_socket_count > 1) wperror(L"connect"); return -1; } if ((make_fd_nonblocking(s) != 0) || (fcntl(s, F_SETFD, FD_CLOEXEC) != 0)) { wperror(L"fcntl"); close(s); return -1; } debug(3, L"Connected to fd %d", s); return s; }
void s_write( screen_t *s, const wchar_t *prompt, const wchar_t *commandline, size_t explicit_len, const int *c, const int *indent, size_t cursor_pos ) { screen_data_t::cursor_t cursor_arr; size_t prompt_width; size_t screen_width; int current_line_width = 0, newline_count = 0, explicit_portion_width = 0; size_t max_line_width = 0; CHECK( s, ); CHECK( prompt, ); CHECK( commandline, ); CHECK( c, ); CHECK( indent, ); /* If we are using a dumb terminal, don't try any fancy stuff, just print out the text. */ if( is_dumb() ) { char *prompt_narrow = wcs2str( prompt ); char *buffer_narrow = wcs2str( commandline ); write_loop( 1, "\r", 1 ); write_loop( 1, prompt_narrow, strlen( prompt_narrow ) ); write_loop( 1, buffer_narrow, strlen( buffer_narrow ) ); free( prompt_narrow ); free( buffer_narrow ); return; } prompt_width = calc_prompt_width( prompt ); screen_width = common_get_width(); s_check_status( s ); /* Ignore prompts wider than the screen - only print a two character placeholder... It would be cool to truncate the prompt, but because it can contain escape sequences, this is harder than you'd think. */ if( prompt_width >= screen_width ) { prompt = L"> "; prompt_width = 2; } /* Completely ignore impossibly small screens */ if( screen_width < 4 ) { return; } /* Check if we are overflowing */ size_t last_char_that_fits = 0; for( size_t i=0; commandline[i]; i++ ) { if( commandline[i] == L'\n' ) { if( current_line_width > max_line_width ) max_line_width = current_line_width; current_line_width = indent[i]*INDENT_STEP; newline_count++; } else { int width = fish_wcwidth(commandline[i]); current_line_width += width; if (i < explicit_len) explicit_portion_width += width; if (prompt_width + current_line_width < screen_width) last_char_that_fits = i; } } if( current_line_width > max_line_width ) max_line_width = current_line_width; s->desired.resize(0); s->desired.cursor.x = s->desired.cursor.y = 0; /* If we cannot fit with the autosuggestion, but we can fit without it, truncate the autosuggestion. We limit this check to just one line to avoid confusion; not sure how well this would work with multiple lines */ wcstring truncated_autosuggestion_line; if (newline_count == 0 && prompt_width + max_line_width >= screen_width && prompt_width + explicit_portion_width < screen_width) { assert(screen_width - prompt_width >= 1); max_line_width = screen_width - prompt_width - 1; truncated_autosuggestion_line = wcstring(commandline, 0, last_char_that_fits); commandline = truncated_autosuggestion_line.c_str(); } for( size_t i=0; i<prompt_width; i++ ) { s_desired_append_char( s, L' ', 0, 0, prompt_width ); } /* If overflowing, give the prompt its own line to improve the situation. */ if( max_line_width + prompt_width >= screen_width ) { s_desired_append_char( s, L'\n', 0, 0, 0 ); prompt_width=0; } size_t i; for( i=0; commandline[i]; i++ ) { int col = c[i]; if( i == cursor_pos ) { col = 0; } if( i == cursor_pos ) { cursor_arr = s->desired.cursor; } s_desired_append_char( s, commandline[i], col, indent[i], prompt_width ); } if( i == cursor_pos ) { cursor_arr = s->desired.cursor; } s->desired.cursor = cursor_arr; s_update( s, prompt ); s_save_status( s ); }
/** Test wide/narrow conversion by creating random strings and verifying that the original string comes back thorugh double conversion. */ static void test_convert() { /* char o[] = { -17, -128, -121, -68, 0 } ; wchar_t *w = str2wcs(o); char *n = wcs2str(w); int i; for( i=0; o[i]; i++ ) { bitprint(o[i]);; //wprintf(L"%d ", o[i]); } wprintf(L"\n"); for( i=0; w[i]; i++ ) { wbitprint(w[i]);; //wprintf(L"%d ", w[i]); } wprintf(L"\n"); for( i=0; n[i]; i++ ) { bitprint(n[i]);; //wprintf(L"%d ", n[i]); } wprintf(L"\n"); return; */ int i; buffer_t sb; say( L"Testing wide/narrow string conversion" ); b_init( &sb ); for( i=0; i<ESCAPE_TEST_COUNT; i++ ) { wchar_t *w; char *o, *n; char c; sb.used=0; while( rand() % ESCAPE_TEST_LENGTH ) { c = rand (); b_append( &sb, &c, 1 ); } c = 0; b_append( &sb, &c, 1 ); o = (char *)sb.buff; w = str2wcs(o); n = wcs2str(w); if( !o || !w || !n ) { err( L"Conversion cycle of string %s produced null pointer on %s", o, w?L"str2wcs":L"wcs2str" ); } if( strcmp(o, n) ) { err( L"%d: Conversion cycle of string %s produced different string %s", i, o, n ); } free( w ); free( n ); } }