/// Extracts the nth substring from a delimited string /// This is a wxWidgets port of MFC's AfxExtractSubString /// @param string Holds the returned substring /// @param fullString String to extract the substring from /// @param subString Zero-based index of the substring to extract /// @param separator Character used to separator the substrings /// @return True if the substring was extracted, false if not bool wxExtractSubString(wxString& string, const wxChar* fullString, wxUint32 subString, wxChar separator) { //------Last Verified------// // - Nov 27, 2004 wxCHECK(fullString != (wxChar*)NULL, false); string.Clear(); while (subString--) { fullString = wxStrchr(fullString, separator); if (fullString == NULL) { string.Clear(); // return empty string as well return (false); } fullString++; // point past the separator } const wxChar* end = wxStrchr(fullString, separator); wxInt32 length = (end == NULL) ? wxStrlen_(fullString) : (wxInt32)(end - fullString); wxASSERT(length >= 0); memcpy(string.GetWriteBuf(length), fullString, length * sizeof(wxChar)); string.UngetWriteBuf(); // Need to call ReleaseBuffer // after calling GetBufferSetLength return (true); }
bool cbNetwork::ReadFileContents(const wxString& remote, wxString& buffer) { if (!Connect(remote)) return false; m_Busy = true; wxString name = wxFileName(remote).GetFullName(); FileInfo* info = PrivateGetFileInfo(remote); Notify(cbEVT_CBNET_START_DOWNLOAD, name, info ? info->size : 0); buffer.Clear(); wxTextInputStream tis(*m_pStream); while (!m_Abort && !m_pStream->Eof()) { buffer += tis.ReadLine() + _T('\n'); Notify(cbEVT_CBNET_PROGRESS, name, buffer.Length()); } if (m_Abort) { Notify(cbEVT_CBNET_ABORTED, _("Aborted")); buffer.Clear(); } Notify(cbEVT_CBNET_END_DOWNLOAD, name, m_Abort ? -1 : 0); m_Busy = false; Disconnect(); return true; }
bool SearchTreeNode::UnSerializeString(const wxString& s,wxString& result) { result.Clear(); size_t i; int mode = 0; wxString entity(_T("")); unsigned int u; for (i = 0;mode >=0 && i<s.length();i++) { wxChar ch = s[i]; if (ch==_T('"') || ch==_T('>') || ch==_T('<')) { mode = -1; // Error break; } switch(mode) { case 0: // normal if (ch==_T('&')) { mode = 1; entity.Clear(); } else result << ch; case 1: // escaped if (ch==_T('&')) { mode = -1; // Error break; } else if (ch==_T(';')) { mode = 0; if (entity==_T("quot")) ch = _T('"'); else if (entity==_T("amp")) ch = _T('&'); else if (entity==_T("apos")) ch = _T('\''); else if (entity==_T("lt")) ch = _T('<'); else if (entity==_T("gt")) ch = _T('>'); else if (entity[0]==_T('#') && S2U(entity.substr(1),u)) ch = u; else { mode = -1; // Error: Unrecognized entity break; } result << ch; } break; } } if (mode < 0) result.Clear(); return (mode >= 0); }
bool Verb::ReadFromRegistry(const wxRegKey & base, const wxString & verbName) { // Store descriptive verb name m_name = verbName; if (!base.HasSubKey(verbName)) return false; wxRegKey verbKey(base, verbName); // Read command key const wxString commandKeyName(wxT("command")); if (!verbKey.HasSubKey(commandKeyName)) return false; wxRegKey commandKey(base, commandKeyName); if (commandKey.HasValue(NULL)) commandKey.QueryValue(NULL, m_command); // Attempt to read ddeexec key m_ddeCommand.Clear(); m_ddeTopic.Clear(); m_ddeApplication.Clear(); const wxString ddeCommandKeyName(wxT("ddeexec")); m_usesDde = verbKey.HasSubKey(ddeCommandKeyName); if (m_usesDde) { wxRegKey ddeCommandKey(verbKey, ddeCommandKeyName);; if (ddeCommandKey.HasValue(NULL)) ddeCommandKey.QueryValue(NULL, m_ddeCommand); const wxString ddeTopicName(wxT("Topic")); if (ddeCommandKey.HasSubKey(ddeTopicName)) { wxRegKey ddeTopicKey(ddeCommandKey, ddeTopicName); if (ddeTopicKey.HasValue(NULL)) ddeTopicKey.QueryValue(NULL, m_ddeTopic); } const wxString ddeApplicationName(wxT("Application")); if (ddeCommandKey.HasSubKey(ddeApplicationName)) { wxRegKey ddeApplicationKey(ddeCommandKey, ddeApplicationName); if (ddeApplicationKey.HasValue(NULL)) ddeApplicationKey.QueryValue(NULL, m_ddeApplication); } } return true; }
void GetMonitorProfile(wxString& profileName, cmsHPROFILE& profile) { if (profile != NULL) { cmsCloseProfile(profile); } profileName.Clear(); profile = NULL; detail::GetMonitorProfile(profileName, profile); // check if monitor profile could be successful loaded, if not switch back to default sRGB profile if (profile == NULL) { profile = cmsCreate_sRGBProfile(); profileName.Clear(); }; };
bool WinProcessImpl::Read(wxString& buff) { DWORD le1(-1); DWORD le2(-1); buff.Clear(); if( !DoReadFromPipe(hChildStderrRdDup, buff) ) { le2 = GetLastError(); } if( !DoReadFromPipe(hChildStdoutRdDup, buff) ) { le1 = GetLastError(); } if( le1 == ERROR_NO_DATA && le2 == ERROR_NO_DATA) { if ( IsAlive() ) { wxThread::Sleep(15); return true; } } bool success = !buff.IsEmpty(); if(!success) { DWORD dwExitCode; if (GetExitCodeProcess(piProcInfo.hProcess, &dwExitCode)) { SetProcessExitCode(GetPid(), (int)dwExitCode); } } return success; }
void CSimplePanelBase::EllipseStringIfNeeded(wxString& s, wxWindow *win) { int x, y; int w, h; wxSize sz = GetSize(); win->GetPosition(&x, &y); int maxWidth = sz.GetWidth() - x - SIDEMARGINS; win->GetTextExtent(s, &w, &h); // Adapted from ellipis code in wxRendererGeneric::DrawHeaderButtonContents() if (w > maxWidth) { int ellipsisWidth; win->GetTextExtent( wxT("..."), &ellipsisWidth, NULL); if (ellipsisWidth > maxWidth) { s.Clear(); w = 0; } else { do { s.Truncate( s.length() - 1 ); win->GetTextExtent( s, &w, &h); } while (((w + ellipsisWidth) > maxWidth) && s.length() ); s.append( wxT("...") ); w += ellipsisWidth; } } }
static void __RemoveTerminalColoring(const wxString& buffer, wxString& modbuffer) { modbuffer.Clear(); short state = BUFF_STATE_NORMAL; wxString::const_iterator iter = buffer.begin(); for(; iter != buffer.end(); ++iter) { wxChar ch = *iter; switch(state) { case BUFF_STATE_NORMAL: if(ch == 0x1B) { // found ESC char state = BUFF_STATE_IN_ESC; } else { modbuffer << ch; } break; case BUFF_STATE_IN_ESC: if(ch == 'm') { // end of color sequence state = BUFF_STATE_NORMAL; } break; } } }
bool FormatOptions::GetPhpFixerCommand(const wxFileName& fileName, wxString& command) { command.Clear(); m_optionsPhp.Load(); wxString phar, php, parameters, filePath; php = m_optionsPhp.GetPhpExe(); if(php.IsEmpty()) { clDEBUG() << "CodeForamtter: GetPhpFixerCommand(): empty php command" << clEndl; return false; } ::WrapWithQuotes(php); phar = GetPHPCSFixerPhar(); if(phar.IsEmpty()) { clDEBUG() << "CodeForamtter: GetPhpFixerCommand(): empty php-cs-fixer phar path" << clEndl; return false; } ::WrapWithQuotes(phar); parameters = GetPHPCSFixerPharOptions(); if(parameters.IsEmpty()) { if(m_PHPCSFixerPharSettings & kPcfAllowRisky) { parameters << " --allow-risky=yes"; } parameters << GetPhpFixerRules(fileName); } parameters.Trim().Trim(false); clDEBUG() << parameters << clEndl; filePath = fileName.GetFullPath(); ::WrapWithQuotes(filePath); command << php << " " << phar << " fix " << parameters << " " << filePath; return true; }
bool clSSH::AuthenticateServer(wxString& message) { int state; unsigned char* hash = NULL; char* hexa = NULL; message.Clear(); state = ssh_is_server_known(m_session); #if LIBSSH_VERSION_INT < SSH_VERSION_INT(0, 6, 1) int hlen = 0; hlen = ssh_get_pubkey_hash(m_session, &hash); if(hlen < 0) { throw clException("Unable to obtain server public key!"); } #else size_t hlen = 0; ssh_key key = NULL; ssh_get_publickey(m_session, &key); ssh_get_publickey_hash(key, SSH_PUBLICKEY_HASH_SHA1, &hash, &hlen); if(hlen == 0) { throw clException("Unable to obtain server public key!"); } #endif switch(state) { case SSH_SERVER_KNOWN_OK: free(hash); return true; case SSH_SERVER_KNOWN_CHANGED: hexa = ssh_get_hexa(hash, hlen); message << _("Host key for server changed: it is now:\n") << hexa << "\n" << _("Accept server authentication?"); free(hexa); free(hash); return false; case SSH_SERVER_FOUND_OTHER: message << _("The host key for this server was not found but another type of key exists.\n") << _("An attacker might change the default server key to confuse your client into thinking the key " "does not exist\n") << _("Accept server authentication?"); free(hash); return false; case SSH_SERVER_FILE_NOT_FOUND: message << _("Could not find known host file.\n") << _("If you accept the host key here, the file will be automatically created.\n"); /* fallback to SSH_SERVER_NOT_KNOWN behavior */ case SSH_SERVER_NOT_KNOWN: hexa = ssh_get_hexa(hash, hlen); message << _("The server is unknown. Do you trust the host key?\n") << _("Public key hash: ") << hexa << "\n" << _("Accept server authentication?"); free(hexa); free(hash); return false; default: case SSH_SERVER_ERROR: throw clException(wxString() << "An error occurred: " << ssh_get_error(m_session)); } return false; }
bool enumRegistryValue(HKEY parent, DWORD index, wxString& valueNameOut, wxString& valueOut) { valueNameOut.Clear(); valueOut.Clear(); WCHAR valueNameBuffer[512]; DWORD valueNameBufferSize = static_cast<DWORD>(sizeof(valueNameBuffer)); BYTE valueBuffer[512]; DWORD valueBufferSize = static_cast<DWORD>(sizeof(valueBuffer)); DWORD type; LONG ret = RegEnumValueW(parent, index, valueNameBuffer, &valueNameBufferSize, NULL, &type, valueBuffer, &valueBufferSize); if (ret != ERROR_SUCCESS) { return false; } valueNameOut = wxString(valueNameBuffer, valueNameBufferSize); switch (type) { case REG_BINARY: valueOut = wxString::From8BitData(reinterpret_cast<char*>(valueBuffer), valueBufferSize); break; case REG_DWORD: { ULONG val = *reinterpret_cast<ULONG*>(valueBuffer); // I feel dirty... valueOut << val; break; } case REG_QWORD: { ULONGLONG val = *reinterpret_cast<ULONGLONG*>(valueBuffer); // I feel dirty... valueOut << val; break; } case REG_SZ: valueOut = wxString(reinterpret_cast<wchar_t*>(valueBuffer), (valueBufferSize / sizeof(WCHAR)) - 1); break; default: return false; } return true; }
// Try converting a C-string from different encodings until a possible match is found. // This tries the following encoding converters (in the same order): // utf8, system, default and iso8859-1 to iso8859-15 wxFontEncoding DetectEncodingAndConvert(const char* strIn, wxString& strOut, wxFontEncoding possibleEncoding) { wxFontEncoding encoding = possibleEncoding; strOut.Clear(); if (platform::unicode) { if (possibleEncoding != wxFONTENCODING_UTF16 && possibleEncoding != wxFONTENCODING_UTF16LE && possibleEncoding != wxFONTENCODING_UTF16BE && possibleEncoding != wxFONTENCODING_UTF32 && possibleEncoding != wxFONTENCODING_UTF32LE && possibleEncoding != wxFONTENCODING_UTF32BE) { // crashes deep in the runtime (windows, at least) // if one of the above encodings, hence the guard wxCSConv conv(possibleEncoding); strOut = wxString(strIn, conv); if (strOut.Length() == 0) { // oops! wrong encoding... // try utf8 first, if that was not what was asked for if (possibleEncoding != wxFONTENCODING_UTF8) { encoding = wxFONTENCODING_UTF8; strOut = wxString(strIn, wxConvUTF8); } // check again: if still not right, try system encoding, default encoding and then iso8859-1 to iso8859-15 if (strOut.Length() == 0) { for (int i = wxFONTENCODING_SYSTEM; i < wxFONTENCODING_ISO8859_MAX; ++i) { encoding = (wxFontEncoding)i; if (encoding == possibleEncoding) continue; // skip if same as what was asked wxCSConv csconv(encoding); strOut = wxString(strIn, csconv); if (strOut.Length() != 0) break; // got it! } } } } else { strOut = (const wxChar*) strIn; } } else { strOut = (const wxChar*) strIn; } return encoding; }
void FileUtils::OSXOpenDebuggerTerminalAndGetTTY(const wxString& path, wxString& tty, long& pid) { tty.Clear(); wxString command; wxString tmpfile; wxString escapedPath = path; if(escapedPath.Contains(" ")) { escapedPath.Prepend("\"").Append("\""); } tmpfile << "/tmp/terminal.tty." << ::wxGetProcessId(); command << "osascript -e 'tell app \"Terminal\" to do script \"tty > " << tmpfile << " && clear && sleep 12345\"'"; CL_DEBUG("Executing: %s", command); long res = ::wxExecute(command); if(res == 0) { CL_WARNING("Failed to execute command:\n%s", command); return; } // Read the tty from the file, wait for it up to 10 seconds wxFileName ttyFile(tmpfile); pid = wxNOT_FOUND; for(size_t i = 0; i < 10; ++i) { if(!ttyFile.Exists()) { ::wxSleep(1); continue; } ReadFileContent(ttyFile, tty); tty.Trim().Trim(false); // Remove the file wxLogNull noLog; ::wxRemoveFile(ttyFile.GetFullPath()); // Get the parent process ID (we want the parent PID and not // the sleep command PID) wxString psCommand; psCommand << "ps -A -o ppid,command"; wxString psOutput = ProcUtils::SafeExecuteCommand(psCommand); CL_DEBUG("PS output:\n%s\n", psOutput); wxArrayString lines = ::wxStringTokenize(psOutput, "\n", wxTOKEN_STRTOK); for(size_t u = 0; u < lines.GetCount(); ++u) { wxString l = lines.Item(u); l.Trim().Trim(false); if(l.Contains("sleep") && l.Contains("12345")) { // we got a match CL_DEBUG("Got a match!"); wxString ppidString = l.BeforeFirst(' '); ppidString.ToCLong(&pid); break; } } break; } CL_DEBUG("PID is: %d\n", (int)pid); CL_DEBUG("TTY is: %s\n", tty); }
OCPNListCtrl::~OCPNListCtrl() { g_AisTargetList_column_spec.Clear(); for( int i = 0; i < tlSOG + 1; i++ ) { wxListItem item; GetColumn( i, item ); wxString sitem; sitem.Printf( _T("%d;"), item.m_width ); g_AisTargetList_column_spec += sitem; } }
void DebuggerTree::FixupVarNameForChange(wxString& str) { // remove everything from '=' and after str = str.BeforeFirst(_T('=')); str.Trim(false); str.Trim(true); // if it contains invalid chars, clear it if (str.find_first_of(_T(" \t")) != wxString::npos) str.Clear(); }
const wxString & IniParser::GetValue(const wxString& group, const wxString& key, bool caseSensitive) const { static wxString ret; ret.Clear(); int g = FindGroupByName(group, caseSensitive); int k = FindKeyByName(g, key, caseSensitive); if (g != -1 && k != -1) return m_Array[g].pairs[k].value; return ret; }
wxString ScriptingManager::GetErrorString(SquirrelError* exception, bool clearErrors) { wxString msg; if (exception) msg << cbC2U(exception->desc); msg << s_ScriptErrors; if (clearErrors) s_ScriptErrors.Clear(); return msg; }
void LocalWorkspace::GetSearchInFilesMask(wxString& findInFileMask, const wxString& defaultValue) { findInFileMask.Clear(); findInFileMask = defaultValue; if(!SanityCheck()) return; wxXmlNode* optsNode = XmlUtils::FindFirstByTagName(m_doc.GetRoot(), wxT("FindInFilesMask")); if(optsNode) { findInFileMask = optsNode->GetNodeContent(); findInFileMask.Trim().Trim(false); } }
wxRect CCBoxTipWindow::DoPrintText(wxDC& dc, wxString& text, wxPoint& pt) { if ( text.IsEmpty() == false ) { wxSize sz = dc.GetTextExtent(text); wxRect rect(pt, sz); dc.DrawText(text, pt); pt.x += sz.x; text.Clear(); return rect; } return wxRect(); }
bool stConnection::OnExecute( const wxString& WXUNUSED( topic ), wxChar *data, int WXUNUSED( size ), wxIPCFormat WXUNUSED( format ) ) { wxString dataStr( data ); static wxString tmpCommand; if( dataStr.IsEmpty() ) { if( wxparaverApp::mainWindow ) wxparaverApp::mainWindow->Raise(); } else if( dataStr == wxT( "BEGIN" ) ) { wxparaverApp::mainWindow->SetCanServeSignal( false ); tmpCommand.Clear(); } else if( dataStr == wxT( "END" ) ) { wxparaverApp::mainWindow->SetCanServeSignal( true ); wxCmdLineParser tmpLine( wxparaverApp::argumentsParseSyntax/*, tmpCommand*/ ); tmpLine.SetCmdLine( tmpCommand ); tmpLine.Parse(); wxGetApp().ParseCommandLine( tmpLine ); } else { tmpCommand += dataStr + wxT( " " ); } /* else if( filename[ 0 ] == '-' ) { if( filename != wxT( "-h" ) && filename != wxT( "--help" ) ) { wxString tmpStr = filename.AfterFirst( '=' ); if( tmpStr == wxT( "" ) ) tmpStr = filename.AfterFirst( ' ' ); if( tmpStr != wxT( "" ) ) { unsigned long tmpType; tmpStr.ToULong( &tmpType ); wxGetApp().SetEventTypeForCode( tmpType ); } } } else { wxparaverApp::mainWindow->enqueueFile( string( filename.mb_str() ) ); } */ return true; }
bool UnixProcessImpl::Read(wxString& buff) { fd_set rs; timeval timeout; memset(&rs, 0, sizeof(rs)); FD_SET(GetReadHandle(), &rs); timeout.tv_sec = 0; // 0 seconds timeout.tv_usec = 50000; // 50 ms int errCode(0); errno = 0; buff.Clear(); int rc = select(GetReadHandle() + 1, &rs, NULL, NULL, &timeout); errCode = errno; if(rc == 0) { // timeout return true; } else if(rc > 0) { // there is something to read char buffer[BUFF_SIZE + 1]; // our read buffer memset(buffer, 0, sizeof(buffer)); int bytesRead = read(GetReadHandle(), buffer, sizeof(buffer)); if(bytesRead > 0) { buffer[BUFF_SIZE] = 0; // allways place a terminator // Remove coloring chars from the incomnig buffer // colors are marked with ESC and terminates with lower case 'm' RemoveTerminalColoring(buffer); wxString convBuff = wxString(buffer, wxConvUTF8); if(convBuff.IsEmpty()) { convBuff = wxString::From8BitData(buffer); } buff = convBuff; return true; } return false; } else { if(errCode == EINTR || errCode == EAGAIN) { return true; } // Process terminated // the exit code will be set in the sigchld event handler return false; } }
bool ThreadSearch::GetCursorWord(wxString& sWord) { bool wordFound = false; sWord = wxEmptyString; // Gets active editor cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor(); if ( ed != NULL ) { cbStyledTextCtrl* control = ed->GetControl(); sWord = control->GetSelectedText(); if (sWord != wxEmptyString) { sWord.Trim(true); sWord.Trim(false); wxString::size_type pos = sWord.find(wxT('\n')); if (pos != wxString::npos) { sWord.Remove(pos, sWord.length() - pos); sWord.Trim(true); sWord.Trim(false); } return !sWord.IsEmpty(); } // Gets word under cursor int pos = control->GetCurrentPos(); int ws = control->WordStartPosition(pos, true); int we = control->WordEndPosition(pos, true); const wxString word = control->GetTextRange(ws, we); if (!word.IsEmpty()) // Avoid empty strings { sWord.Clear(); while (--ws > 0) { const wxChar ch = control->GetCharAt(ws); if (ch <= _T(' ')) continue; else if (ch == _T('~')) sWord << _T("~"); break; } // m_SearchedWord will be used if 'Find occurrences' ctx menu is clicked sWord << word; wordFound = true; } } return wordFound; }
void LocalWorkspace::GetParserMacros(wxString& macros) { if(!SanityCheck()) return; macros.Clear(); if(!SanityCheck()) return; wxXmlNode* optsNode = XmlUtils::FindFirstByTagName(m_doc.GetRoot(), wxT("WorkspaceParserMacros")); if(optsNode) { macros = optsNode->GetNodeContent(); macros.Trim().Trim(false); } }
wxString ScriptingManager::LoadBufferRedirectOutput(const wxString& buffer) { // wxCriticalSectionLocker c(cs); s_ScriptErrors.Clear(); ::capture.Clear(); sq_setprintfunc(SquirrelVM::GetVMPtr(), CaptureScriptOutput); bool res = LoadBuffer(buffer); sq_setprintfunc(SquirrelVM::GetVMPtr(), ScriptsPrintFunc); return res ? ::capture : (wxString) wxEmptyString; }
void mxOutputView::GetProcessOutput() { if (!process) return; wxTextInputStream input(*(process->GetInputStream())); wxTextInputStream input2(*(process->GetErrorStream())); static wxString line; static char c; line.Clear(); while (process->IsInputAvailable()) { c=input.GetChar(); if (c!='\r') line<<c; } ctrl_std->AppendText(line); ctrl_std->ShowPosition(ctrl_std->GetLastPosition()); line.Clear(); while (process->IsErrorAvailable()) { c=input2.GetChar(); if (c!='\r') line<<c; } if (textfile) textfile->Write(line); ctrl_err->AppendText(line); ctrl_err->ShowPosition(ctrl_err->GetLastPosition()); if (working) timer->Start(500,true); }
bool DbgGdb::DoGetNextLine( wxString& line ) { line.Clear(); if ( m_gdbOutputArr.IsEmpty() ) { return false; } line = m_gdbOutputArr.Item( 0 ); m_gdbOutputArr.RemoveAt( 0 ); line.Replace( wxT( "(gdb)" ), wxT( "" ) ); line.Trim().Trim( false ); if( line.IsEmpty() ) { return false; } return true; }
wxString DisassembledItem::GetCodeStr() { static wxString gcstr; uint i; byte b; gcstr.Clear(); for (i = 0; i < opcode_size_; i++) { b = data_file_->GetData(offset_in_file_ + i); gcstr << gcstr.Format("%.2X ", b); } gcstr.Trim(true); return (gcstr); }
bool nsHeaderFixUp::IsInsideMultilineComment(wxString& Line) { int EndCommentPos = Line.Find(_T("*/")); bool OutsideMultilineComment = false; if ( EndCommentPos == wxNOT_FOUND ) Line.Clear(); // skip line else { Line.Remove(0,EndCommentPos+2); OutsideMultilineComment = true; // END Multiline comment } return !OutsideMultilineComment; }
void OnSignaturePINCode(wxCommandEvent& WXUNUSED(event)) { pinCode.Clear(); wxWindow* parent = wxGetActiveWindow(); SignPinCodeDlg signPINCodeDlg(parent, wxID_ANY, wxT("Signature PIN code"), nbrRetries, hash); if (signPINCodeDlg.ShowModal() == wxID_OK) { pinCode = signPINCodeDlg.GetPassword(); } if (parent) { parent->SetFocus(); } ExitMainLoop(); }
void OnAuthenticationPINCode(wxCommandEvent& WXUNUSED(event)) { pinCode.Clear(); wxWindow* parent = wxGetActiveWindow(); PinCodeDlg pinCodeDlg(parent, wxID_ANY, wxT("Authentication PIN code"), nbrRetries); if (pinCodeDlg.ShowModal() == wxID_OK) { pinCode = pinCodeDlg.GetPassword(); } if (parent) { parent->SetFocus(); } ExitMainLoop(); }