/* static */ RTCString RTCString::joinEx(const RTCList<RTCString, RTCString *> &a_rList, const RTCString &a_rstrPrefix /* = "" */, const RTCString &a_rstrSep /* = "" */) { RTCString strRet; if (a_rList.size() > 1) { /* calc the required size */ size_t cbNeeded = a_rstrSep.length() * (a_rList.size() - 1) + 1; cbNeeded += a_rstrPrefix.length() * (a_rList.size() - 1) + 1; for (size_t i = 0; i < a_rList.size(); ++i) cbNeeded += a_rList.at(i).length(); strRet.reserve(cbNeeded); /* do the appending. */ for (size_t i = 0; i < a_rList.size() - 1; ++i) { if (a_rstrPrefix.isNotEmpty()) strRet.append(a_rstrPrefix); strRet.append(a_rList.at(i)); strRet.append(a_rstrSep); } strRet.append(a_rList.last()); } /* special case: one list item. */ else if (a_rList.size() > 0) { if (a_rstrPrefix.isNotEmpty()) strRet.append(a_rstrPrefix); strRet.append(a_rList.last()); } return strRet; }
DnDHGSendDirPrivate(DnDURIObject URIObject, PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser) : m_URIObject(URIObject) , m_pfnProgressCallback(pfnProgressCallback) , m_pvProgressUser(pvProgressUser) { RTCString strPath = m_URIObject.GetDestPath(); LogFlowFunc(("strPath=%s (%zu)\n", strPath.c_str(), strPath.length())); VBOXHGCMSVCPARM paTmpParms[3]; paTmpParms[0].setString(strPath.c_str()); paTmpParms[1].setUInt32((uint32_t)(strPath.length() + 1)); paTmpParms[2].setUInt32(m_URIObject.GetMode()); m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_DIR, 3, paTmpParms); }
RTCList<RTCString, RTCString *> RTCString::split(const RTCString &a_rstrSep, SplitMode mode /* = RemoveEmptyParts */) const { RTCList<RTCString> strRet; if (!m_psz) return strRet; if (a_rstrSep.isEmpty()) { strRet.append(RTCString(m_psz)); return strRet; } size_t cch = m_cch; char const *pszTmp = m_psz; while (cch > 0) { char const *pszNext = strstr(pszTmp, a_rstrSep.c_str()); if (!pszNext) { strRet.append(RTCString(pszTmp, cch)); break; } size_t cchNext = pszNext - pszTmp; if ( cchNext > 0 || mode == KeepEmptyParts) strRet.append(RTCString(pszTmp, cchNext)); pszTmp += cchNext + a_rstrSep.length(); cch -= cchNext + a_rstrSep.length(); } return strRet; }
bool RTCString::startsWith(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const { size_t l1 = length(); size_t l2 = that.length(); if (l1 == 0 || l2 == 0) /** @todo r=bird: this differs from endsWith, and I think other IPRT code. If l2 == 0, it matches anything. */ return false; if (l1 < l2) return false; if (cs == CaseSensitive) return ::RTStrNCmp(m_psz, that.m_psz, l2) == 0; return ::RTStrNICmp(m_psz, that.m_psz, l2) == 0; }
bool RTCString::endsWith(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const { size_t l1 = length(); if (l1 == 0) return false; size_t l2 = that.length(); if (l1 < l2) return false; /** @todo r=bird: If l2 is 0, then m_psz can be NULL and we will crash. See * also handling of l2 == in startsWith. */ size_t l = l1 - l2; if (cs == CaseSensitive) return ::RTStrCmp(&m_psz[l], that.m_psz) == 0; return ::RTStrICmp(&m_psz[l], that.m_psz) == 0; }
static int vbglR3DnDHGProcessURIMessages(uint32_t uClientId, uint32_t *puScreenId, char *pszFormat, uint32_t cbFormat, uint32_t *pcbFormatRecv, void **ppvData, uint32_t cbData, size_t *pcbDataRecv) { /* Make a string list out of the uri data. */ RTCList<RTCString> uriList = RTCString(static_cast<char*>(*ppvData), *pcbDataRecv - 1).split("\r\n"); if (uriList.isEmpty()) return VINF_SUCCESS; uint32_t cbTmpData = _1M * 10; void *pvTmpData = RTMemAlloc(cbTmpData); if (!pvTmpData) return VERR_NO_MEMORY; /* Create and query the drop target directory. */ char pszDropDir[RTPATH_MAX]; int rc = vbglR3DnDCreateDropDir(pszDropDir, sizeof(pszDropDir)); if (RT_FAILURE(rc)) { RTMemFree(pvTmpData); return rc; } /* Patch the old drop data with the new drop directory, so the drop target * can find the files. */ RTCList<RTCString> guestUriList; for (size_t i = 0; i < uriList.size(); ++i) { const RTCString &strUri = uriList.at(i); /* Query the path component of a file URI. If this hasn't a * file scheme, null is returned. */ if (char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO)) { RTCString strFullPath = RTCString().printf("%s%c%s", pszDropDir, RTPATH_SLASH, pszFilePath); char *pszNewUri = RTUriFileCreate(strFullPath.c_str()); if (pszNewUri) { guestUriList.append(pszNewUri); RTStrFree(pszNewUri); } } else guestUriList.append(strUri); } /* Cleanup the old data and write the new data back to the event. */ RTMemFree(*ppvData); RTCString newData = RTCString::join(guestUriList, "\r\n") + "\r\n"; *ppvData = RTStrDupN(newData.c_str(), newData.length()); *pcbDataRecv = newData.length() + 1; /* Lists for holding created files & directories in the case of a * rollback. */ RTCList<RTCString> guestDirList; RTCList<RTCString> guestFileList; char pszPathname[RTPATH_MAX]; uint32_t cbPathname = 0; bool fLoop = true; do { uint32_t uNextMsg; uint32_t cNextParms; rc = vbglR3DnDQueryNextHostMessageType(uClientId, &uNextMsg, &cNextParms, false); DO(("%Rrc - %d\n", rc , uNextMsg)); if (RT_SUCCESS(rc)) { switch(uNextMsg) { case DragAndDropSvc::HOST_DND_HG_SND_DIR: { uint32_t fMode = 0; rc = vbglR3DnDHGProcessSendDirMessage(uClientId, pszPathname, sizeof(pszPathname), &cbPathname, &fMode); if (RT_SUCCESS(rc)) { DO(("Got drop dir: %s - %o - %Rrc\n", pszPathname, fMode, rc)); char *pszNewDir = RTPathJoinA(pszDropDir, pszPathname); rc = RTDirCreate(pszNewDir, (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU, 0); if (!guestDirList.contains(pszNewDir)) guestDirList.append(pszNewDir); } break; } case DragAndDropSvc::HOST_DND_HG_SND_FILE: { uint32_t cbDataRecv; uint32_t fMode = 0; rc = vbglR3DnDHGProcessSendFileMessage(uClientId, pszPathname, sizeof(pszPathname), &cbPathname, pvTmpData, cbTmpData, &cbDataRecv, &fMode); if (RT_SUCCESS(rc)) { char *pszNewFile = RTPathJoinA(pszDropDir, pszPathname); DO(("Got drop file: %s - %d - %o - %Rrc\n", pszPathname, cbDataRecv, fMode, rc)); RTFILE hFile; rc = RTFileOpen(&hFile, pszNewFile, RTFILE_O_WRITE | RTFILE_O_APPEND | RTFILE_O_DENY_ALL | RTFILE_O_OPEN_CREATE); if (RT_SUCCESS(rc)) { rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, NULL); if (RT_SUCCESS(rc)) { rc = RTFileWrite(hFile, pvTmpData, cbDataRecv, 0); /* Valid UNIX mode? */ if ( RT_SUCCESS(rc) && (fMode & RTFS_UNIX_MASK)) rc = RTFileSetMode(hFile, (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR); } RTFileClose(hFile); if (!guestFileList.contains(pszNewFile)) guestFileList.append(pszNewFile); } } break; } case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL: { rc = vbglR3DnDHGProcessCancelMessage(uClientId); if (RT_SUCCESS(rc)) rc = VERR_CANCELLED; /* Break out of the loop. */ } default: fLoop = false; break; } } else { if (rc == VERR_NO_DATA) rc = VINF_SUCCESS; break; } }while(fLoop); RTMemFree(pvTmpData); /* Cleanup on failure or if the user has canceled. */ if (RT_FAILURE(rc)) { /* Remove any stuff created. */ for (size_t i = 0; i < guestFileList.size(); ++i) RTFileDelete(guestFileList.at(i).c_str()); for (size_t i = 0; i < guestDirList.size(); ++i) RTDirRemove(guestDirList.at(i).c_str()); RTDirRemove(pszDropDir); } return rc; }
STDMETHODIMP VBoxDnDDropTarget::Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { RT_NOREF(pt); AssertPtrReturn(pDataObject, E_INVALIDARG); AssertPtrReturn(pdwEffect, E_INVALIDARG); LogFlowFunc(("mFormatEtc.cfFormat=%RI16 (%s), pDataObject=0x%p, grfKeyState=0x%x, x=%ld, y=%ld\n", mFormatEtc.cfFormat, VBoxDnDDataObject::ClipboardFormatToString(mFormatEtc.cfFormat), pDataObject, grfKeyState, pt.x, pt.y)); HRESULT hr = S_OK; if (mFormatEtc.cfFormat) /* Did we get a supported format yet? */ { /* Make sure the data object's data format is still valid. */ hr = pDataObject->QueryGetData(&mFormatEtc); AssertMsg(SUCCEEDED(hr), ("Data format changed to invalid between DragEnter() and Drop(), cfFormat=%RI16 (%s), hr=%Rhrc\n", mFormatEtc.cfFormat, VBoxDnDDataObject::ClipboardFormatToString(mFormatEtc.cfFormat), hr)); } int rc = VINF_SUCCESS; if (SUCCEEDED(hr)) { STGMEDIUM stgMed; hr = pDataObject->GetData(&mFormatEtc, &stgMed); if (SUCCEEDED(hr)) { /* * First stage: Prepare the access to the storage medium. * For now we only support HGLOBAL stuff. */ PVOID pvData = NULL; /** @todo Put this in an own union? */ switch (mFormatEtc.tymed) { case TYMED_HGLOBAL: pvData = GlobalLock(stgMed.hGlobal); if (!pvData) { LogFlowFunc(("Locking HGLOBAL storage failed with %Rrc\n", RTErrConvertFromWin32(GetLastError()))); rc = VERR_INVALID_HANDLE; hr = E_INVALIDARG; /* Set special hr for OLE. */ } break; default: AssertMsgFailed(("Storage medium type %RI32 supported\n", mFormatEtc.tymed)); rc = VERR_NOT_SUPPORTED; hr = DV_E_TYMED; /* Set special hr for OLE. */ break; } if (RT_SUCCESS(rc)) { /* * Second stage: Do the actual copying of the data object's data, * based on the storage medium type. */ switch (mFormatEtc.cfFormat) { case CF_TEXT: /* Fall through is intentional. */ case CF_UNICODETEXT: { AssertPtr(pvData); size_t cbSize = GlobalSize(pvData); LogFlowFunc(("CF_TEXT/CF_UNICODETEXT 0x%p got %zu bytes\n", pvData, cbSize)); if (cbSize) { char *pszText = NULL; rc = mFormatEtc.cfFormat == CF_TEXT /* ANSI codepage -> UTF-8 */ ? RTStrCurrentCPToUtf8(&pszText, (char *)pvData) /* Unicode -> UTF-8 */ : RTUtf16ToUtf8((PCRTUTF16)pvData, &pszText); if (RT_SUCCESS(rc)) { AssertPtr(pszText); size_t cbText = strlen(pszText) + 1; /* Include termination. */ mpvData = RTMemDup((void *)pszText, cbText); mcbData = cbText; RTStrFree(pszText); pszText = NULL; } } break; } case CF_HDROP: { AssertPtr(pvData); /* Convert to a string list, separated by \r\n. */ DROPFILES *pDropFiles = (DROPFILES *)pvData; AssertPtr(pDropFiles); bool fUnicode = RT_BOOL(pDropFiles->fWide); /* Get the offset of the file list. */ Assert(pDropFiles->pFiles >= sizeof(DROPFILES)); /* Note: This is *not* pDropFiles->pFiles! DragQueryFile only * will work with the plain storage medium pointer! */ HDROP hDrop = (HDROP)(pvData); /* First, get the file count. */ /** @todo Does this work on Windows 2000 / NT4? */ char *pszFiles = NULL; uint32_t cchFiles = 0; UINT cFiles = DragQueryFile(hDrop, UINT32_MAX /* iFile */, NULL /* lpszFile */, 0 /* cchFile */); LogFlowFunc(("CF_HDROP got %RU16 file(s)\n", cFiles)); for (UINT i = 0; i < cFiles; i++) { UINT cch = DragQueryFile(hDrop, i /* File index */, NULL /* Query size first */, 0 /* cchFile */); Assert(cch); if (RT_FAILURE(rc)) break; char *pszFile = NULL; /* UTF-8 version. */ UINT cchFile = 0; if (fUnicode) { /* Allocate enough space (including terminator). */ WCHAR *pwszFile = (WCHAR *)RTMemAlloc((cch + 1) * sizeof(WCHAR)); if (pwszFile) { cchFile = DragQueryFileW(hDrop, i /* File index */, pwszFile, cch + 1 /* Include terminator */); AssertMsg(cchFile == cch, ("cchCopied (%RU16) does not match cchFile (%RU16)\n", cchFile, cch)); rc = RTUtf16ToUtf8(pwszFile, &pszFile); AssertRC(rc); RTMemFree(pwszFile); } else rc = VERR_NO_MEMORY; } else /* ANSI */ { /* Allocate enough space (including terminator). */ pszFile = (char *)RTMemAlloc((cch + 1) * sizeof(char)); if (pszFile) { cchFile = DragQueryFileA(hDrop, i /* File index */, pszFile, cchFile + 1 /* Include terminator */); AssertMsg(cchFile == cch, ("cchCopied (%RU16) does not match cchFile (%RU16)\n", cchFile, cch)); } else rc = VERR_NO_MEMORY; } if (RT_SUCCESS(rc)) { LogFlowFunc(("\tFile: %s (cchFile=%RU32)\n", pszFile, cchFile)); rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, pszFile, cchFile); if (RT_SUCCESS(rc)) cchFiles += cchFile; } if (pszFile) RTStrFree(pszFile); if (RT_FAILURE(rc)) break; /* Add separation between filenames. * Note: Also do this for the last element of the list. */ rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, "\r\n", 2 /* Bytes */); if (RT_SUCCESS(rc)) cchFiles += 2; /* Include \r\n */ } if (RT_SUCCESS(rc)) { cchFiles += 1; /* Add string termination. */ uint32_t cbFiles = cchFiles * sizeof(char); LogFlowFunc(("cFiles=%u, cchFiles=%RU32, cbFiles=%RU32, pszFiles=0x%p\n", cFiles, cchFiles, cbFiles, pszFiles)); /* Translate the list into URI elements. */ DnDURIList lstURI; rc = lstURI.AppendNativePathsFromList(pszFiles, cbFiles, DNDURILIST_FLAGS_ABSOLUTE_PATHS); if (RT_SUCCESS(rc)) { RTCString strRoot = lstURI.RootToString(); size_t cbRoot = strRoot.length() + 1; /* Include termination */ mpvData = RTMemAlloc(cbRoot); if (mpvData) { memcpy(mpvData, strRoot.c_str(), cbRoot); mcbData = cbRoot; } else rc = VERR_NO_MEMORY; } } LogFlowFunc(("Building CF_HDROP list rc=%Rrc, pszFiles=0x%p, cFiles=%RU16, cchFiles=%RU32\n", rc, pszFiles, cFiles, cchFiles)); if (pszFiles) RTStrFree(pszFiles); break; } default: /* Note: Should not happen due to the checks done in DragEnter(). */ AssertMsgFailed(("Format of type %RI16 (%s) not supported\n", mFormatEtc.cfFormat, VBoxDnDDataObject::ClipboardFormatToString(mFormatEtc.cfFormat))); hr = DV_E_CLIPFORMAT; /* Set special hr for OLE. */ break; } /* * Third stage: Unlock + release access to the storage medium again. */ switch (mFormatEtc.tymed) { case TYMED_HGLOBAL: GlobalUnlock(stgMed.hGlobal); break; default: AssertMsgFailed(("Really should not happen -- see init stage!\n")); break; } } /* Release storage medium again. */ ReleaseStgMedium(&stgMed); /* Signal waiters. */ mDroppedRc = rc; RTSemEventSignal(hEventDrop); } } if (RT_SUCCESS(rc)) { /* Note: pt is not used since we don't need to differentiate within our * proxy window. */ *pdwEffect = VBoxDnDDropTarget::GetDropEffect(grfKeyState, *pdwEffect); } else *pdwEffect = DROPEFFECT_NONE; if (mpWndParent) mpWndParent->hide(); LogFlowFunc(("Returning with hr=%Rhrc (%Rrc), mFormatEtc.cfFormat=%RI16 (%s), *pdwEffect=%RI32\n", hr, rc, mFormatEtc.cfFormat, VBoxDnDDataObject::ClipboardFormatToString(mFormatEtc.cfFormat), *pdwEffect)); return hr; }
/* * This class is a meta message class. It doesn't consist of any own message * data, but handle the meta info, the data itself as well as any files or * directories which have to be transfered to the guest. */ DnDHGSendDataMessage::DnDHGSendDataMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], PFNDNDPROGRESS pfnProgressCallback, void *pvProgressUser) : m_cbTotal(0) , m_cbTransfered(0) , m_pfnProgressCallback(pfnProgressCallback) , m_pvProgressUser(pvProgressUser) { if (cParms < 5) /* Paranoia. */ return; const char *pszFormat = static_cast<const char*>(paParms[1].u.pointer.addr); uint32_t cbFormat = paParms[1].u.pointer.size; int rc = VINF_SUCCESS; RTCString strNewURIs; /* Do we need to build up a file tree? */ if (DnDMIMEHasFileURLs(pszFormat, cbFormat)) { const char *pszList = static_cast<const char*>(paParms[3].u.pointer.addr); AssertPtr(pszList); uint32_t cbList = paParms[3].u.pointer.size; Assert(cbList); LogFlowFunc(("Old data (%RU32 bytes): '%s'\n", cbList, pszList)); /* The list is separated by newline (even if only one file is listed). */ RTCList<RTCString> lstURIOrg = RTCString(pszList, cbList).split("\r\n"); if (!lstURIOrg.isEmpty()) { rc = m_lstURI.AppendURIPathsFromList(lstURIOrg, 0 /* fFlags */); if (RT_SUCCESS(rc)) { /* Add the total size of all meta data + files transferred to * the message's total byte count. */ m_cbTotal += m_lstURI.TotalBytes(); /* We have to change the actual DnD data. Remove any host paths and * just decode the filename into the new data. The Guest Additions will * add the correct path again before sending the DnD drop event to * some window. */ strNewURIs = m_lstURI.RootToString(); /* Note: We don't delete the old pointer here, cause this is done * by the caller. We just use the RTString data, which has the * scope of this ctor. This is enough cause the data is copied in * the DnDHGSendDataMessagePrivate anyway. */ paParms[3].u.pointer.addr = (void *)strNewURIs.c_str(); paParms[3].u.pointer.size = (uint32_t)(strNewURIs.length() + 1); paParms[4].u.uint32 = (uint32_t)(strNewURIs.length() + 1); LogFlowFunc(("Set new data (%RU32 bytes): '%s'\n", paParms[3].u.pointer.size, (const char*)paParms[3].u.pointer.addr)); } } } /* Add the size of the data to the todo list. */ m_cbTotal += paParms[4].u.uint32; LogFlowFunc(("cbTotal=%zu\n", m_cbTotal)); /* The first message is the meta info for the data and the data itself. */ m_pNextPathMsg = new DnDHGSendDataMessagePrivate(uMsg, cParms, paParms, &DnDHGSendDataMessage::progressCallback, this); }
static void test1(RTTEST hTest) { RTTestSub(hTest, "Basics"); #define CHECK(expr) RTTESTI_CHECK(expr) #define CHECK_DUMP(expr, value) \ do { \ if (!(expr)) \ RTTestFailed(hTest, "%d: FAILED %s, got \"%s\"", __LINE__, #expr, value); \ } while (0) #define CHECK_DUMP_I(expr) \ do { \ if (!(expr)) \ RTTestFailed(hTest, "%d: FAILED %s, got \"%d\"", __LINE__, #expr, expr); \ } while (0) #define CHECK_EQUAL(Str, szExpect) \ do { \ if (!(Str).equals(szExpect)) \ RTTestIFailed("line %u: expected \"%s\" got \"%s\"", __LINE__, szExpect, (Str).c_str()); \ } while (0) #define CHECK_EQUAL_I(iRes, iExpect) \ do { \ if (iRes != iExpect) \ RTTestIFailed("line %u: expected \"%zd\" got \"%zd\"", __LINE__, iExpect, iRes); \ } while (0) RTCString empty; CHECK(empty.length() == 0); CHECK(empty.capacity() == 0); RTCString sixbytes("12345"); CHECK(sixbytes.length() == 5); CHECK(sixbytes.capacity() == 6); sixbytes.append(RTCString("678")); CHECK(sixbytes.length() == 8); CHECK(sixbytes.capacity() >= 9); sixbytes.append("9a"); CHECK(sixbytes.length() == 10); CHECK(sixbytes.capacity() >= 11); char *psz = sixbytes.mutableRaw(); // 123456789a // ^ // 0123456 psz[6] = '\0'; sixbytes.jolt(); CHECK(sixbytes.length() == 6); CHECK(sixbytes.capacity() == 7); RTCString morebytes("tobereplaced"); morebytes = "newstring "; morebytes.append(sixbytes); CHECK_DUMP(morebytes == "newstring 123456", morebytes.c_str()); RTCString third(morebytes); third.reserve(100 * 1024); // 100 KB CHECK_DUMP(third == "newstring 123456", morebytes.c_str() ); CHECK(third.capacity() == 100 * 1024); CHECK(third.length() == morebytes.length()); // must not have changed RTCString copy1(morebytes); RTCString copy2 = morebytes; CHECK(copy1 == copy2); copy1 = NULL; CHECK(copy1.length() == 0); copy1 = ""; CHECK(copy1.length() == 0); CHECK(RTCString("abc") < RTCString("def")); CHECK(RTCString("") < RTCString("def")); CHECK(RTCString("abc") > RTCString("")); CHECK(RTCString("abc") != RTCString("def")); CHECK_DUMP_I(RTCString("def") > RTCString("abc")); CHECK(RTCString("abc") == RTCString("abc")); CHECK(RTCString("").compare("") == 0); CHECK(RTCString("").compare(NULL) == 0); CHECK(RTCString("").compare("a") < 0); CHECK(RTCString("a").compare("") > 0); CHECK(RTCString("a").compare(NULL) > 0); CHECK(RTCString("abc") < "def"); CHECK(RTCString("abc") != "def"); CHECK_DUMP_I(RTCString("def") > "abc"); CHECK(RTCString("abc") == "abc"); CHECK(RTCString("abc").equals("abc")); CHECK(!RTCString("abc").equals("def")); CHECK(RTCString("abc").equalsIgnoreCase("Abc")); CHECK(RTCString("abc").equalsIgnoreCase("ABc")); CHECK(RTCString("abc").equalsIgnoreCase("ABC")); CHECK(!RTCString("abc").equalsIgnoreCase("dBC")); CHECK(RTCString("").equals("")); CHECK(RTCString("").equals(NULL)); CHECK(!RTCString("").equals("a")); CHECK(!RTCString("a").equals("")); CHECK(!RTCString("a").equals(NULL)); CHECK(RTCString("").equalsIgnoreCase("")); CHECK(RTCString("").equalsIgnoreCase(NULL)); CHECK(!RTCString("").equalsIgnoreCase("a")); CHECK(!RTCString("a").equalsIgnoreCase("")); copy2.setNull(); for (int i = 0; i < 100; ++i) { copy2.reserve(50); // should be ignored after 50 loops copy2.append("1"); } CHECK(copy2.length() == 100); copy2.setNull(); for (int i = 0; i < 100; ++i) { copy2.reserve(50); // should be ignored after 50 loops copy2.append('1'); } CHECK(copy2.length() == 100); /* printf */ RTCString StrFmt; CHECK(StrFmt.printf("%s-%s-%d", "abc", "def", 42).equals("abc-def-42")); test1Hlp1("abc-42-def", "%s-%d-%s", "abc", 42, "def"); test1Hlp1("", ""); test1Hlp1("1", "1"); test1Hlp1("foobar", "%s", "foobar"); /* substring constructors */ RTCString SubStr1("", (size_t)0); CHECK_EQUAL(SubStr1, ""); RTCString SubStr2("abcdef", 2); CHECK_EQUAL(SubStr2, "ab"); RTCString SubStr3("abcdef", 1); CHECK_EQUAL(SubStr3, "a"); RTCString SubStr4("abcdef", 6); CHECK_EQUAL(SubStr4, "abcdef"); RTCString SubStr5("abcdef", 7); CHECK_EQUAL(SubStr5, "abcdef"); RTCString SubStrBase("abcdef"); RTCString SubStr10(SubStrBase, 0); CHECK_EQUAL(SubStr10, "abcdef"); RTCString SubStr11(SubStrBase, 1); CHECK_EQUAL(SubStr11, "bcdef"); RTCString SubStr12(SubStrBase, 1, 1); CHECK_EQUAL(SubStr12, "b"); RTCString SubStr13(SubStrBase, 2, 3); CHECK_EQUAL(SubStr13, "cde"); RTCString SubStr14(SubStrBase, 2, 4); CHECK_EQUAL(SubStr14, "cdef"); RTCString SubStr15(SubStrBase, 2, 5); CHECK_EQUAL(SubStr15, "cdef"); /* substr() and substrCP() functions */ RTCString strTest(""); CHECK_EQUAL(strTest.substr(0), ""); CHECK_EQUAL(strTest.substrCP(0), ""); CHECK_EQUAL(strTest.substr(1), ""); CHECK_EQUAL(strTest.substrCP(1), ""); /* now let's have some non-ASCII to chew on */ strTest = "abcdefßäbcdef"; // 13 codepoints, but 15 bytes (excluding null terminator); // "ß" and "ä" consume two bytes each CHECK_EQUAL(strTest.substr(0), strTest.c_str()); CHECK_EQUAL(strTest.substrCP(0), strTest.c_str()); CHECK_EQUAL(strTest.substr(2), "cdefßäbcdef"); CHECK_EQUAL(strTest.substrCP(2), "cdefßäbcdef"); CHECK_EQUAL(strTest.substr(2, 2), "cd"); CHECK_EQUAL(strTest.substrCP(2, 2), "cd"); CHECK_EQUAL(strTest.substr(6), "ßäbcdef"); CHECK_EQUAL(strTest.substrCP(6), "ßäbcdef"); CHECK_EQUAL(strTest.substr(6, 2), "ß"); // UTF-8 "ß" consumes two bytes CHECK_EQUAL(strTest.substrCP(6, 1), "ß"); CHECK_EQUAL(strTest.substr(8), "äbcdef"); // UTF-8 "ß" consumes two bytes CHECK_EQUAL(strTest.substrCP(7), "äbcdef"); CHECK_EQUAL(strTest.substr(8, 3), "äb"); // UTF-8 "ä" consumes two bytes CHECK_EQUAL(strTest.substrCP(7, 2), "äb"); CHECK_EQUAL(strTest.substr(14, 1), "f"); CHECK_EQUAL(strTest.substrCP(12, 1), "f"); CHECK_EQUAL(strTest.substr(15, 1), ""); CHECK_EQUAL(strTest.substrCP(13, 1), ""); CHECK_EQUAL(strTest.substr(16, 1), ""); CHECK_EQUAL(strTest.substrCP(15, 1), ""); /* and check cooperation with find() */ size_t pos = strTest.find("ß"); CHECK_EQUAL(strTest.substr(pos), "ßäbcdef"); /* check find() */ CHECK_EQUAL_I(strTest.find("f"), 5); CHECK_EQUAL_I(strTest.find("f", 0), 5); CHECK_EQUAL_I(strTest.find("f", 3), 5); CHECK_EQUAL_I(strTest.find("f", 6), 14); CHECK_EQUAL_I(strTest.find("f", 9), 14); CHECK_EQUAL_I(strTest.substr(pos).find("d"), 6); /* split */ RTCList<RTCString> spList1 = RTCString("##abcdef##abcdef####abcdef##").split("##", RTCString::RemoveEmptyParts); RTTESTI_CHECK(spList1.size() == 3); for (size_t i = 0; i < spList1.size(); ++i) RTTESTI_CHECK(spList1.at(i) == "abcdef"); RTCList<RTCString> spList2 = RTCString("##abcdef##abcdef####abcdef##").split("##", RTCString::KeepEmptyParts); RTTESTI_CHECK_RETV(spList2.size() == 5); RTTESTI_CHECK(spList2.at(0) == ""); RTTESTI_CHECK(spList2.at(1) == "abcdef"); RTTESTI_CHECK(spList2.at(2) == "abcdef"); RTTESTI_CHECK(spList2.at(3) == ""); RTTESTI_CHECK(spList2.at(4) == "abcdef"); RTCList<RTCString> spList3 = RTCString().split("##", RTCString::KeepEmptyParts); RTTESTI_CHECK(spList3.size() == 0); RTCList<RTCString> spList4 = RTCString().split(""); RTTESTI_CHECK(spList4.size() == 0); RTCList<RTCString> spList5 = RTCString("abcdef").split(""); RTTESTI_CHECK_RETV(spList5.size() == 1); RTTESTI_CHECK(spList5.at(0) == "abcdef"); /* join */ RTCList<RTCString> jnList; strTest = RTCString::join(jnList); RTTESTI_CHECK(strTest == ""); strTest = RTCString::join(jnList, "##"); RTTESTI_CHECK(strTest == ""); jnList.append("abcdef"); strTest = RTCString::join(jnList, "##"); RTTESTI_CHECK(strTest == "abcdef"); jnList.append("abcdef"); strTest = RTCString::join(jnList, ";"); RTTESTI_CHECK(strTest == "abcdef;abcdef"); for (size_t i = 0; i < 3; ++i) jnList.append("abcdef"); strTest = RTCString::join(jnList); RTTESTI_CHECK(strTest == "abcdefabcdefabcdefabcdefabcdef"); strTest = RTCString::join(jnList, "##"); RTTESTI_CHECK(strTest == "abcdef##abcdef##abcdef##abcdef##abcdef"); /* special constructor and assignment arguments */ RTCString StrCtor1(""); RTTESTI_CHECK(StrCtor1.isEmpty()); RTTESTI_CHECK(StrCtor1.length() == 0); RTCString StrCtor2(NULL); RTTESTI_CHECK(StrCtor2.isEmpty()); RTTESTI_CHECK(StrCtor2.length() == 0); RTCString StrCtor1d(StrCtor1); RTTESTI_CHECK(StrCtor1d.isEmpty()); RTTESTI_CHECK(StrCtor1d.length() == 0); RTCString StrCtor2d(StrCtor2); RTTESTI_CHECK(StrCtor2d.isEmpty()); RTTESTI_CHECK(StrCtor2d.length() == 0); for (unsigned i = 0; i < 2; i++) { RTCString StrAssign; if (i) StrAssign = "abcdef"; StrAssign = (char *)NULL; RTTESTI_CHECK(StrAssign.isEmpty()); RTTESTI_CHECK(StrAssign.length() == 0); if (i) StrAssign = "abcdef"; StrAssign = ""; RTTESTI_CHECK(StrAssign.isEmpty()); RTTESTI_CHECK(StrAssign.length() == 0); if (i) StrAssign = "abcdef"; StrAssign = StrCtor1; RTTESTI_CHECK(StrAssign.isEmpty()); RTTESTI_CHECK(StrAssign.length() == 0); if (i) StrAssign = "abcdef"; StrAssign = StrCtor2; RTTESTI_CHECK(StrAssign.isEmpty()); RTTESTI_CHECK(StrAssign.length() == 0); } #undef CHECK #undef CHECK_DUMP #undef CHECK_DUMP_I #undef CHECK_EQUAL }
/* * This class is a meta message class. It doesn't consist of any own message * data, but handle the meta info, the data itself as well any files or * directories which have to be transfered to the guest. */ DnDHGSendDataMessage::DnDHGSendDataMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], PFNDNDPROGRESS pfnProgressCallback, void *pvProgressUser) : m_cbAll(0) , m_cbTransfered(0) , m_pfnProgressCallback(pfnProgressCallback) , m_pvProgressUser(pvProgressUser) { RTCString strNewUris; /* Check the format for any uri type. */ if (hasFileUrls(static_cast<const char*>(paParms[1].u.pointer.addr), paParms[1].u.pointer.size)) { DO(("old data '%s'\n", (char*)paParms[3].u.pointer.addr)); /* The list is separated by newline (Even if only one file is * listed). */ RTCList<RTCString> oldUriList = RTCString(static_cast<const char*>(paParms[3].u.pointer.addr), paParms[3].u.pointer.size).split("\r\n"); if (!oldUriList.isEmpty()) { RTCList<RTCString> newUriList; for (size_t i = 0; i < oldUriList.size(); ++i) { const RTCString &strUri = oldUriList.at(i); /* Query the path component of a file URI. If this hasn't a * file scheme null is returned. */ if (char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO)) { /* Add the path to our internal file list (recursive in * the case of a directory). */ if (char *pszFilename = RTPathFilename(pszFilePath)) { char *pszNewUri = RTUriFileCreate(pszFilename); if (pszNewUri) { newUriList.append(pszNewUri); RTStrFree(pszNewUri); buildFileTree(pszFilePath, pszFilename - pszFilePath); } } RTStrFree(pszFilePath); } else newUriList.append(strUri); } /* We have to change the actual DnD data. Remove any host paths and * just decode the filename into the new data. The guest tools will * add the correct path again, before sending the DnD drop event to * some window. */ strNewUris = RTCString::join(newUriList, "\r\n") + "\r\n"; /* Remark: We don't delete the old pointer here, cause this is done * by the caller. We just use the RTString data, which has the * scope of this ctor. This is enough cause the data is copied in * the DnDHGSendDataMessagePrivate anyway. */ paParms[3].u.pointer.addr = (void*)strNewUris.c_str(); paParms[3].u.pointer.size = strNewUris.length() + 1; paParms[4].u.uint32 = strNewUris.length() + 1; } } /* Add the size of the data to the todo list. */ m_cbAll += paParms[4].u.uint32; /* The first message is the meta info for the data and the data itself. */ m_pNextPathMsg = new DnDHGSendDataMessagePrivate(uMsg, cParms, paParms, &DnDHGSendDataMessage::progressCallback, this); DO(("new data '%s'\n", (char*)paParms[3].u.pointer.addr)); DO(("cbAll: %u\n", m_cbAll)); DO(("cbData: %u\n", paParms[4].u.uint32)); for (size_t i = 0; i < m_uriList.size(); ++i) DO(("file: %s : %s - %o - %ld\n", m_uriList.at(i).m_strHostPath.c_str(), m_uriList.at(i).m_strGuestPath.c_str(), m_uriList.at(i).m_fMode, m_uriList.at(i).m_cbSize)); }