TEST(CClipboardTests, unmarshall_withTextSize285_getTextIsValid) { CClipboard clipboard; // 285 chars CString text; text.append("Synergy is Free and Open Source Software that lets you "); text.append("easily share your mouse and keyboard between multiple "); text.append("computers, where each computer has it's own display. No "); text.append("special hardware is required, all you need is a local area "); text.append("network. Synergy is supported on Windows, Mac OS X and Linux."); CString data; data += (char)0; data += (char)0; data += (char)0; data += (char)1; // 1 format added data += (char)0; data += (char)0; data += (char)0; data += (char)IClipboard::kText; data += (char)0; // 285 >> 24 = 285 / (256^3) = 0 data += (char)0; // 285 >> 16 = 285 / (256^2) = 0 data += (char)1; // 285 >> 8 = 285 / (256^1) = 1(.11328125) data += (char)29; // 285 - 256 = 29 data += text; clipboard.unmarshall(data, 0); clipboard.open(0); CString actual = clipboard.get(IClipboard::kText); EXPECT_EQ(text, actual); }
void CClient::writeToDropDirThread(void*) { LOG((CLOG_DEBUG "starting write to drop dir thread")); while (m_screen->isFakeDraggingStarted()) { ARCH->sleep(.1f); } m_fileTransferDes = m_screen->getDropTarget(); LOG((CLOG_DEBUG "dropping file, files=%i target=%s", m_dragFileList.size(), m_fileTransferDes.c_str())); if (!m_fileTransferDes.empty() && m_dragFileList.size() > 0) { std::fstream file; CString dropTarget = m_fileTransferDes; #ifdef SYSAPI_WIN32 dropTarget.append("\\"); #else dropTarget.append("/"); #endif dropTarget.append(m_dragFileList.at(0).getFilename()); file.open(dropTarget.c_str(), std::ios::out | std::ios::binary); if (!file.is_open()) { // TODO: file open failed } file.write(m_receivedFileData.c_str(), m_receivedFileData.size()); file.close(); } else { LOG((CLOG_ERR "drop file failed: drop target is empty")); } }
// TODO: there's some integer -> char encoding going on here. i find it // hard to believe that the clipboard is the only thing doing this. maybe // we should refactor this stuff out of the clipboard. TEST(CClipboardTests, marshall_withTextSize285_sizeCharsValid) { // 285 chars CString data; data.append("Synergy is Free and Open Source Software that lets you "); data.append("easily share your mouse and keyboard between multiple "); data.append("computers, where each computer has it's own display. No "); data.append("special hardware is required, all you need is a local area "); data.append("network. Synergy is supported on Windows, Mac OS X and Linux."); CClipboard clipboard; clipboard.open(0); clipboard.add(IClipboard::kText, data); clipboard.close(); CString actual = clipboard.marshall(); // 4 asserts here, but that's ok because we're really just asserting 1 // thing. the 32-bit size value is split into 4 chars. if the size is 285 // (29 more than the 8-bit max size), the last char "rolls over" to 29 // (this is caused by a bit-wise & on 0xff and 8-bit truncation). each // char before the last stores a bit-shifted version of the number, each // 1 more power than the last, which is done by bit-shifting [0] by 24, // [1] by 16, [2] by 8 ([3] is not bit-shifted). EXPECT_EQ(0, actual[8]); // 285 >> 24 = 285 / (256^3) = 0 EXPECT_EQ(0, actual[9]); // 285 >> 16 = 285 / (256^2) = 0 EXPECT_EQ(1, actual[10]); // 285 >> 8 = 285 / (256^1) = 1(.11328125) EXPECT_EQ(29, actual[11]); // 285 - 256 = 29 }
CString CArgParser::assembleCommand(std::vector<CString>& argsArray, CString ignoreArg, int parametersRequired) { CString result; for (std::vector<CString>::iterator it = argsArray.begin(); it != argsArray.end(); ++it) { if (it->compare(ignoreArg) == 0) { it = it + parametersRequired; continue; } // if there is a space in this arg, use double quotes surround it if ((*it).find(" ") != CString::npos) { (*it).insert(0, "\""); (*it).push_back('\"'); } result.append(*it); // add space to saperate args result.append(" "); } if (!result.empty()) { // remove the tail space result = result.substr(0, result.size() - 1); } return result; }
void ListCommand(const CString& sLine) { CString output = "The following aliases exist:"; MCString::iterator i = BeginNV(); if (i == EndNV()) output += " [none]"; for (; i != EndNV(); ++i) { output.append(" "); output.append(i->first); } PutModule(output); }
int CDragInformation::setupDragInfo(CDragFileList& fileList, CString& output) { int size = fileList.size(); for (int i = 0; i < size; ++i) { output.append(fileList.at(i).getFilename()); output.append(","); CString filesize = getFileSize(fileList.at(i).getFilename()); output.append(filesize); output.append(","); } return size; }
// read an IRC line and do token substitution // throws an exception if a required parameter is missing, and might also throw if you manage to make it bork CString Imprint(CString line) const { CString output; CString alias_data = GetCommands(); alias_data = parent->ExpandString(alias_data); size_t lastfound = 0, skip = 0; // it would be very inefficient to attempt to blindly replace every possible token // so let's just parse the line and replace when we find them // token syntax: // %[?]n[+]% // adding ? makes the substitution optional (you'll get "" if there are insufficient tokens, otherwise the alias will fail) // adding + makes the substitution contain all tokens from the nth to the end of the line while (true) { // if (found >= (int) alias_data.length()) break; // shouldn't be possible. size_t found = alias_data.find("%", lastfound + skip); if (found == CString::npos) break; // if we found nothing, break output.append(alias_data.substr(lastfound, found - lastfound)); // capture everything between the last stopping point and here ParseToken(alias_data, line, output, found, skip); // attempt to read a token, updates indices based on success/failure lastfound = found; } output += alias_data.substr(lastfound); // append from the final return output; }
// this function helps imprint out. it checks if there is a substitution token at 'caret' in 'alias_data' // and if it finds one, pulls the appropriate token out of 'line' and appends it to 'output', and updates 'caret'. // 'skip' is updated based on the logic that we should skip the % at the caret if we fail to parse the token. static void ParseToken(const CString &alias_data, const CString &line, CString &output, size_t &caret, size_t &skip) { bool optional = false; bool subsequent = false; size_t index = caret + 1; int token = -1; skip = 1; if (alias_data.length() > index && alias_data[index] == '?') { optional = true; ++index; } // try to read optional flag if (alias_data.length() > index && CString(alias_data.substr(index)).Convert(&token)) // try to read integer { while(alias_data.length() > index && alias_data[index] >= '0' && alias_data[index] <= '9') ++index; // skip any numeric digits in string } // (supposed to fail if whitespace precedes integer) else return; // token was malformed. leave caret unchanged, and flag first character for skipping if (alias_data.length() > index && alias_data[index] == '+') { subsequent = true; ++index; } // try to read subsequent flag if (alias_data.length() > index && alias_data[index] == '%') { ++index; } // try to read end-of-substitution marker else return; CString stok = line.Token(token, subsequent, " "); // if we get here, we're definitely dealing with a token, so get the token's value if (stok.empty() && !optional) throw std::invalid_argument(CString("missing required parameter: ") + CString(token)); // blow up if token is required and also empty output.append(stok); // write token value to output skip = 0; // since we're moving the cursor after the end of the token, skip no characters caret = index; // advance the cursor forward by the size of the token }
int RageFile::Read( CString &buffer, int bytes ) { buffer.erase( buffer.begin(), buffer.end() ); buffer.reserve( bytes != -1? bytes: this->GetFileSize() ); int ret = 0; char buf[4096]; while( bytes == -1 || ret < bytes ) { int ToRead = sizeof(buf); if( bytes != -1 ) ToRead = min( ToRead, bytes-ret ); const int got = Read( buf, ToRead ); if( got == 0 ) break; if( got == -1 ) return -1; buffer.append( buf, got ); ret += got; } return ret; }
int RageFileObj::Read( CString &sBuffer, int iBytes ) { sBuffer.erase( sBuffer.begin(), sBuffer.end() ); sBuffer.reserve( iBytes != -1? iBytes: this->GetFileSize() ); int iRet = 0; char buf[4096]; while( iBytes == -1 || iRet < iBytes ) { int ToRead = sizeof(buf); if( iBytes != -1 ) ToRead = min( ToRead, iBytes-iRet ); const int iGot = Read( buf, ToRead ); if( iGot == 0 ) break; if( iGot == -1 ) return -1; sBuffer.append( buf, iGot ); iRet += iGot; } return iRet; }
CString MakeIvec() { CString sRet; time_t t; time(&t); int r = rand(); sRet.append((char*) &t, 4); sRet.append((char*) &r, 4); return sRet; }
void COSXKeyState::fixStickyKeys() { KeyModifierMask synergyMask = getActiveModifiers(); KeyModifierMask hardwareMask = pollActiveModifiers(); if (synergyMask != hardwareMask) { // modifier key stuck // compute changed modifiers KeyModifierMask changed = (hardwareMask ^ synergyMask); if (changed) { KeyButton kb; CString keyFixed; // synthesize changed modifier keys if ((changed & KeyModifierShift) != 0) { kb = mapVirtualKeyToKeyButton(s_shiftVK); fakeKeyUp(kb); keyFixed.append("shift "); } if ((changed & KeyModifierControl) != 0) { kb = mapVirtualKeyToKeyButton(s_controlVK); fakeKeyUp(kb); keyFixed.append("ctrl "); } if ((changed & KeyModifierAlt) != 0) { kb = mapVirtualKeyToKeyButton(s_altVK); fakeKeyUp(kb); keyFixed.append("alt "); } if ((changed & KeyModifierSuper) != 0) { kb = mapVirtualKeyToKeyButton(s_superVK); fakeKeyUp(kb); keyFixed.append("cmd "); } LOG((CLOG_DEBUG "fixed stuck modifier key: %s", keyFixed.c_str())); } } }
void CDCCSock::ReadData(const char* data, size_t len) { if (!m_pFile) { DEBUG("File not open! closing get."); if (m_bSend) { m_pModule->PutModule(t_f("Sending [{1}] to [{2}]: File not open!")( m_sFileName, m_sRemoteNick)); } else { m_pModule->PutModule( t_f("Receiving [{1}] from [{2}]: File not open!")( m_sFileName, m_sRemoteNick)); } Close(); return; } // DCC specs says the receiving end sends the number of bytes it // received so far as a 4 byte integer in network byte order, so we need // uint32_t to do the job portably. This also means that the maximum // file that we can transfer is 4 GiB big (see OpenFile()). if (m_bSend) { m_sSendBuf.append(data, len); while (m_sSendBuf.size() >= 4) { uint32_t iRemoteSoFar; memcpy(&iRemoteSoFar, m_sSendBuf.data(), sizeof(iRemoteSoFar)); iRemoteSoFar = ntohl(iRemoteSoFar); if ((iRemoteSoFar + 65536) >= m_uBytesSoFar) { SendPacket(); } m_sSendBuf.erase(0, 4); } } else { m_pFile->Write(data, len); m_uBytesSoFar += len; uint32_t uSoFar = htonl((uint32_t)m_uBytesSoFar); Write((char*)&uSoFar, sizeof(uSoFar)); if (m_uBytesSoFar >= m_uFileSize) { Close(); } } }
void ShowCommand(const CString& sCommand) { map< CString, vector< CString> > msvOutput; for (u_int a = 0; a < m_vMessages.size(); a++) { CString sTime = m_vMessages[a].Token(0, false, ":"); CString sWhom = m_vMessages[a].Token(1, false, ":"); CString sMessage = m_vMessages[a].Token(2, true, ":"); if ((sTime.empty()) || (sWhom.empty()) || (sMessage.empty())) { // illegal format PutModule("Corrupt message! [" + m_vMessages[a] + "]"); m_vMessages.erase(m_vMessages.begin() + a--); continue; } time_t iTime = sTime.ToULong(); char szFormat[64]; struct tm t; localtime_r(&iTime, &t); size_t iCount = strftime(szFormat, 64, "%F %T", &t); if (iCount <= 0) { PutModule("Corrupt time stamp! [" + m_vMessages[a] + "]"); m_vMessages.erase(m_vMessages.begin() + a--); continue; } CString sTmp = " " + CString(a) + ") ["; sTmp.append(szFormat, iCount); sTmp += "] "; sTmp += sMessage; msvOutput[sWhom].push_back(sTmp); } for (map< CString, vector< CString> >::iterator it = msvOutput.begin(); it != msvOutput.end(); ++it) { PutModule(it->first); for (u_int a = 0; a < it->second.size(); a++) PutModule(it->second[a]); } PutModule("#--- End Messages"); }
bool CFile::ReadFile(CString& sData, size_t iMaxSize) { char buff[4096]; size_t iBytesRead = 0; sData.clear(); while (iBytesRead < iMaxSize) { ssize_t iBytes = Read(buff, sizeof(buff)); if (iBytes < 0) // Error return false; if (iBytes == 0) // EOF return true; sData.append(buff, iBytes); iBytesRead += iBytes; } // Buffer limit reached return false; }
void CXWindowsClipboard::icccmFillCache() { LOG((CLOG_DEBUG "ICCCM fill clipboard %d", m_id)); // see if we can get the list of available formats from the selection. // if not then use a default list of formats. note that some clipboard // owners are broken and report TARGETS as the type of the TARGETS data // instead of the correct type ATOM; allow either. const Atom atomTargets = m_atomTargets; Atom target; CString data; if (!icccmGetSelection(atomTargets, &target, &data) || (target != m_atomAtom && target != m_atomTargets)) { LOG((CLOG_DEBUG1 "selection doesn't support TARGETS")); data = ""; target = XA_STRING; data.append(reinterpret_cast<char*>(&target), sizeof(target)); } const Atom* targets = reinterpret_cast<const Atom*>(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); LOG((CLOG_DEBUG " available targets: %s", CXWindowsUtil::atomsToString(m_display, targets, numTargets).c_str())); // try each converter in order (because they're in order of // preference). for (ConverterList::const_iterator index = m_converters.begin(); index != m_converters.end(); ++index) { IXWindowsClipboardConverter* converter = *index; // skip already handled targets if (m_added[converter->getFormat()]) { continue; } // see if atom is in target list Atom target = None; for (UInt32 i = 0; i < numTargets; ++i) { if (converter->getAtom() == targets[i]) { target = targets[i]; break; } } if (target == None) { continue; } // get the data Atom actualTarget; CString targetData; if (!icccmGetSelection(target, &actualTarget, &targetData)) { LOG((CLOG_DEBUG1 " no data for target %s", CXWindowsUtil::atomToString(m_display, target).c_str())); continue; } // add to clipboard and note we've done it IClipboard::EFormat format = converter->getFormat(); m_data[format] = converter->toIClipboard(targetData); m_added[format] = true; LOG((CLOG_DEBUG " added format %d for target %s (%u %s)", format, CXWindowsUtil::atomToString(m_display, target).c_str(), targetData.size(), targetData.size() == 1 ? "byte" : "bytes")); } }
void CLanguage::LoadFile ( const CString& szLangName, const CString& szEntryName ) { CString szPath ( "../lang/%s/%s.txt", szLangName.c_str (), szEntryName.c_str () ); FILE* fp; #ifdef WIN32 fopen_s ( &fp, szPath.c_str (), "r" ); #else fp = fopen ( szPath.c_str (), "r" ); #endif if ( !fp ) return; CString szCurTopic; CString szCurTopicName; char szBuffer [ 4096 ]; char* p; t_topicMap* map = new t_topicMap ( ); map->set_deleted_key ( (char*)HASH_STRING_DELETED ); map->set_empty_key ( (char*)HASH_STRING_EMPTY ); char* szKey = strdup ( szEntryName.c_str () ); m_entriesMap.insert ( t_entriesMap::value_type ( szKey, map ) ); while ( !feof ( fp ) ) { fgets ( szBuffer, sizeof ( szBuffer ), fp ); p = szBuffer + strlen ( szBuffer ) - 1; while ( p >= szBuffer && ( *p == '\r' || *p == '\n' ) ) { *p = '\0'; --p; } if ( *p == '#' ) { // Es un comentario continue; } if ( *p == '%' && szBuffer [ 0 ] == '%' ) { if ( p > szBuffer + 1 ) { // Inicio de un tema szCurTopic.clear (); *p = '\0'; szCurTopicName = szBuffer + 1; } else { // Fin de un tema if ( szCurTopicName.length () > 0 ) { char* szKey = strdup ( szCurTopicName.c_str () ); map->insert ( t_topicMap::value_type ( szKey, szCurTopic ) ); } szCurTopicName.clear (); } } else { szCurTopic.append ( szBuffer ); szCurTopic.append ( "\n" ); } } fclose ( fp ); }
CString CStringUtil::vformat(const char* fmt, va_list args) { // find highest indexed substitution and the locations of substitutions std::vector<size_t> pos; std::vector<size_t> width; std::vector<int> index; int maxIndex = 0; for (const char* scan = fmt; *scan != '\0'; ++scan) { if (*scan == '%') { ++scan; if (*scan == '\0') { break; } else if (*scan == '%') { // literal index.push_back(0); pos.push_back(static_cast<int>(scan - 1 - fmt)); width.push_back(2); } else if (*scan == '{') { // get argument index char* end; int i = static_cast<int>(strtol(scan + 1, &end, 10)); if (*end != '}') { // invalid index -- ignore scan = end - 1; } else { index.push_back(i); pos.push_back(static_cast<int>(scan - 1 - fmt)); width.push_back(static_cast<int>(end - scan + 2)); if (i > maxIndex) { maxIndex = i; } scan = end; } } else { // improper escape -- ignore } } } // get args std::vector<const char*> value; std::vector<size_t> length; value.push_back("%"); length.push_back(1); for (int i = 0; i < maxIndex; ++i) { const char* arg = va_arg(args, const char*); size_t len = strlen(arg); value.push_back(arg); length.push_back(len); } // compute final length size_t resultLength = strlen(fmt); const int n = static_cast<int>(pos.size()); for (int i = 0; i < n; ++i) { resultLength -= width[i]; resultLength += length[index[i]]; } // substitute CString result; result.reserve(resultLength); size_t src = 0; for (int i = 0; i < n; ++i) { result.append(fmt + src, pos[i] - src); result.append(value[index[i]]); src = pos[i] + width[i]; } result.append(fmt + src); return result; }
void FormatLogMessage(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszDate, LPCTSTR pszTime, LPCTSTR pszThreadId, LPCTSTR pszThreadName, LPCTSTR pszModule, LPCTSTR pszMessage, CString& output) { #if 1 output.Empty(); output.Preallocate(1024); if (type != eLM_DirectOutput) { output += pszDate; output += _T(' '); output += pszTime; #if defined(LOG_THREAD_NAME) output += _T(" ["); output += pszThreadId; output += _T(":"); size_t nThreadNameLen = _tcslen(pszThreadName); if (nThreadNameLen > 12) output.append(pszThreadName, 12); else { output.append(12 - nThreadNameLen, _T(' ')); output += pszThreadName; } output += _T("] "); #else output += _T(" "); #endif switch (type) { case eLM_Info: output += _T('I'); break; case eLM_Debug: output += _T('-'); break; case eLM_Warning: output += _T('W'); break; case eLM_Error: output += _T('E'); break; default: ASSERT(false); } if (nLevel>0) output.AppendFormat(_T("%i"), nLevel); else output += _T(' '); #if 0 output += _T(" : ["); output += pszModule; output += _T("] "); #else output += _T(" : "); #endif } output += pszMessage; output.TrimRight(); #else output.Empty(); output.reserve(1024); output += pszDate; output += _T(' '); output += pszTime; output += _T("\t"); output += pszThreadId; output += _T("\t"); output += pszThreadName; output += _T("\t"); output += pszModule; output += _T("\t"); switch (type) { case eLM_Info: output += _T("Inf"); break; case eLM_Debug: output += _T("Dbg"); break; case eLM_Warning: output += _T("Wrn"); break; case eLM_Error: output += _T("Err"); break; default: ASSERT(false); } if (nLevel>0) output.AppendFormat(_T("%i"), nLevel); output += _T('\t'); output += pszMessage; output.TrimRight(); #endif }
virtual void OnModCommand(const CString& sCommand) { CString sCmdName = sCommand.Token(0); if (sCmdName == "away") { CString sReason; if (sCommand.Token(1) != "-quiet") { sReason = sCommand.Token(1, true); PutModNotice("You have been marked as away", "away"); } else sReason = sCommand.Token(2, true); Away(false, sReason); } else if (sCmdName == "back") { if ((m_vMessages.empty()) && (sCommand.Token(1) != "-quiet")) PutModNotice("Welcome Back!", "away"); Back(); } else if (sCmdName == "messages") { for (u_int a = 0; a < m_vMessages.size(); a++) PutModule(m_vMessages[a], "away"); } else if (sCmdName == "delete") { CString sWhich = sCommand.Token(1); if (sWhich == "all") { PutModNotice("Deleted " + CString(m_vMessages.size()) + " Messages.", "away"); for (u_int a = 0; a < m_vMessages.size(); a++) m_vMessages.erase(m_vMessages.begin() + a--); } else if (sWhich.empty()) { PutModNotice("USAGE: delete <num|all>", "away"); return; } else { u_int iNum = sWhich.ToUInt(); if (iNum >= m_vMessages.size()) { PutModNotice("Illegal Message # Requested", "away"); return; } else { m_vMessages.erase(m_vMessages.begin() + iNum); PutModNotice("Message Erased.", "away"); } SaveBufferToDisk(); } } else if (sCmdName == "save" && m_saveMessages) { SaveBufferToDisk(); PutModNotice("Messages saved to disk.", "away"); } else if (sCmdName == "ping") { Ping(); if (m_bIsAway) Back(); } else if (sCmdName == "pass") { m_sPassword = sCommand.Token(1); PutModNotice("Password Updated to [" + m_sPassword + "]"); } else if (sCmdName == "show") { map< CString, vector< CString> > msvOutput; for (u_int a = 0; a < m_vMessages.size(); a++) { CString sTime = m_vMessages[a].Token(0, false, ":"); CString sWhom = m_vMessages[a].Token(1, false, ":"); CString sMessage = m_vMessages[a].Token(2, true, ":"); if ((sTime.empty()) || (sWhom.empty()) || (sMessage.empty())) { // illegal format PutModule("Corrupt message! [" + m_vMessages[a] + "]", "away"); m_vMessages.erase(m_vMessages.begin() + a--); continue; } time_t iTime = sTime.ToULong(); char szFormat[64]; struct tm t; localtime_r(&iTime, &t); size_t iCount = strftime(szFormat, 64, "%Y-%m-%d %H:%M:%S", &t); if (iCount <= 0) { PutModule("Corrupt time stamp! [" + m_vMessages[a] + "]", "away"); m_vMessages.erase(m_vMessages.begin() + a--); continue; } CString sTmp = " " + CString(a) + ") ["; sTmp.append(szFormat, iCount); sTmp += "] "; sTmp += sMessage; msvOutput[sWhom].push_back(sTmp); } for (map< CString, vector< CString> >::iterator it = msvOutput.begin(); it != msvOutput.end(); ++it) { PutModule(it->first, "away"); for (u_int a = 0; a < it->second.size(); a++) PutModule(it->second[a]); } PutModule("#--- End Messages", "away"); } else if (sCmdName == "enabletimer") { SetAwayTime(300); PutModule("Timer set to 300 seconds"); } else if (sCmdName == "disabletimer") { SetAwayTime(0); PutModule("Timer disabled"); } else if (sCmdName == "settimer") { int iSetting = sCommand.Token(1).ToInt(); SetAwayTime(iSetting); if (iSetting == 0) PutModule("Timer disabled"); else PutModule("Timer set to " + CString(iSetting) + " seconds"); } else if (sCmdName == "timer") { PutModule("Current timer setting: " + CString(GetAwayTime()) + " seconds"); } else { PutModule("Commands: away [-quiet], back [-quiet], delete <num|all>, ping, show, save, enabletimer, disabletimer, settimer <secs>, timer", "away"); } }
/* Read up to the next \n, and return it in out. Strip the \n. If the \n is * preceded by a \r (DOS newline), strip that, too. */ int RageFile::GetLine( CString &out ) { out = ""; if ( !IsOpen() ) RageException::Throw("\"%s\" is not open.", m_Path.c_str()); if( !(m_Mode&READ) ) RageException::Throw("\"%s\" is not open for reading", GetPath().c_str()); if( m_EOF ) return 0; bool GotData = false; while( 1 ) { bool done = false; /* Find the end of the block we'll move to out. */ char *p = (char *) memchr( m_pBuf, '\n', m_BufAvail ); bool ReAddCR = false; if( p == NULL ) { /* Hack: If the last character of the buffer is \r, then it's likely that an * \r\n has been split across buffers. Move everything else, then move the * \r to the beginning of the buffer and handle it the next time around the loop. */ if( m_BufAvail && m_pBuf[m_BufAvail-1] == '\r' ) { ReAddCR = true; --m_BufAvail; } p = m_pBuf+m_BufAvail; /* everything */ } else done = true; if( p >= m_pBuf ) { char *RealEnd = p; if( done && p > m_pBuf && p[-1] == '\r' ) --RealEnd; /* not including \r */ out.append( m_pBuf, RealEnd ); if( done ) ++p; /* skip \n */ const int used = p-m_pBuf; if( used ) { m_BufAvail -= used; m_FilePos += used; GotData = true; m_pBuf = p; } } if( ReAddCR ) { ASSERT( m_BufAvail == 0 ); m_pBuf = m_Buffer; m_Buffer[m_BufAvail] = '\r'; ++m_BufAvail; } if( done ) break; /* We need more data. */ m_pBuf = m_Buffer; const int size = FillBuf(); /* If we've read data already, then don't mark EOF yet. Wait until the * next time we're called. */ if( size == 0 && !GotData ) { m_EOF = true; return 0; } if( size == -1 ) return -1; // error if( size == 0 ) break; // EOF or error } return GotData? 1:0; }