mirror of
https://github.com/fadden/ciderpress.git
synced 2024-12-20 23:29:17 +00:00
Improve filename handling when adding files
Most of this change is a conversion of the old FileDetails struct into a new LocalFileDetails class. The new class keeps the members private, and keeps the Unicode and MOR representations of the string separate. The NuFX and DiskImg libraries don't support UTF-16 filenames, so we stil can't add files with non-CP-1252 filenames, but we're a step closer. Also, update NufxLib with a couple of fixes from the main project. Also, fix handling of "%00" when adding files. Also, mark most of the A2FileDOS fields private. Not sure why they weren't.
This commit is contained in:
parent
d94b707489
commit
b79498da50
@ -375,7 +375,7 @@ bail:
|
||||
|
||||
/*static*/ CString AcuArchive::AppInit(void)
|
||||
{
|
||||
return "";
|
||||
return L"";
|
||||
}
|
||||
|
||||
GenericArchive::OpenResult AcuArchive::Open(const WCHAR* filename,
|
||||
@ -526,11 +526,11 @@ int AcuArchive::ReadMasterHeader(int* pNumEntries)
|
||||
header.unknown1 != 1 ||
|
||||
strcmp((char*) header.fZink, "fZink") != 0)
|
||||
{
|
||||
LOGI("Not an ACU archive");
|
||||
LOGW("Not an ACU archive");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGI("Looks like an ACU archive with %d entries", header.fileCount);
|
||||
LOGD("Looks like an ACU archive with %d entries", header.fileCount);
|
||||
|
||||
*pNumEntries = header.fileCount;
|
||||
return 0;
|
||||
|
@ -162,7 +162,7 @@ private:
|
||||
}
|
||||
virtual void XferPrepare(const XferFileOptions* pXferOpts) override
|
||||
{ ASSERT(false); }
|
||||
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
||||
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override
|
||||
{ ASSERT(false); return "!"; }
|
||||
virtual void XferAbort(CWnd* pMsgWnd) override
|
||||
@ -172,7 +172,7 @@ private:
|
||||
|
||||
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveACU; }
|
||||
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
||||
FileDetails* pDetails) override
|
||||
LocalFileDetails* pDetails) override
|
||||
{ ASSERT(false); return kNuErrGeneric; }
|
||||
|
||||
enum {
|
||||
|
@ -2138,21 +2138,21 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
|
||||
pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly());
|
||||
}
|
||||
|
||||
/*static*/ bool MainWindow::SaveToArchive(GenericArchive::FileDetails* pDetails,
|
||||
const unsigned char* dataBufIn, long dataLen,
|
||||
const unsigned char* rsrcBufIn, long rsrcLen,
|
||||
CString& errMsg, CWnd* pDialog)
|
||||
/*static*/ bool MainWindow::SaveToArchive(GenericArchive::LocalFileDetails* pDetails,
|
||||
const uint8_t* dataBufIn, long dataLen,
|
||||
const uint8_t* rsrcBufIn, long rsrcLen,
|
||||
CString* pErrMsg, CWnd* pDialog)
|
||||
{
|
||||
MainWindow* pMain = GET_MAIN_WINDOW();
|
||||
GenericArchive* pArchive = pMain->GetOpenArchive();
|
||||
DiskImgLib::A2File* pTargetSubdir = NULL;
|
||||
XferFileOptions xferOpts;
|
||||
CString storagePrefix;
|
||||
unsigned char* dataBuf = NULL;
|
||||
unsigned char* rsrcBuf = NULL;
|
||||
uint8_t* dataBuf = NULL;
|
||||
uint8_t* rsrcBuf = NULL;
|
||||
|
||||
ASSERT(pArchive != NULL);
|
||||
ASSERT(errMsg.IsEmpty());
|
||||
ASSERT(pErrMsg->IsEmpty());
|
||||
|
||||
/*
|
||||
* Make a copy of the data for XferFile.
|
||||
@ -2163,7 +2163,7 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
|
||||
else
|
||||
dataBuf = new unsigned char[dataLen];
|
||||
if (dataBuf == NULL) {
|
||||
errMsg.Format(L"Unable to allocate %ld bytes", dataLen);
|
||||
pErrMsg->Format(L"Unable to allocate %ld bytes", dataLen);
|
||||
goto bail;
|
||||
}
|
||||
memcpy(dataBuf, dataBufIn, dataLen);
|
||||
@ -2187,7 +2187,7 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
|
||||
// Always use ':' separator for SHK; this is a matter of
|
||||
// convenience, so they can specify a full path.
|
||||
//details.storageName.Replace(':', '_');
|
||||
pDetails->fileSysInfo = ':';
|
||||
pDetails->SetFssep(':');
|
||||
}
|
||||
if (pTargetSubdir != NULL) {
|
||||
storagePrefix = pTargetSubdir->GetPathName();
|
||||
@ -2195,13 +2195,13 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
|
||||
}
|
||||
if (!storagePrefix.IsEmpty()) {
|
||||
CString tmpStr, tmpFileName;
|
||||
tmpFileName = pDetails->storageName;
|
||||
tmpFileName = pDetails->GetStrippedLocalPathName();
|
||||
tmpFileName.Replace(':', '_'); // strip any ':'s in the name
|
||||
pDetails->fileSysInfo = ':';
|
||||
pDetails->SetFssep(':');
|
||||
tmpStr = storagePrefix;
|
||||
tmpStr += ':';
|
||||
tmpStr += tmpFileName;
|
||||
pDetails->storageName = tmpStr;
|
||||
pDetails->SetStrippedLocalPathName(tmpStr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2211,18 +2211,18 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
|
||||
*/
|
||||
pArchive->XferPrepare(&xferOpts);
|
||||
|
||||
errMsg = pArchive->XferFile(pDetails, &dataBuf, dataLen,
|
||||
*pErrMsg = pArchive->XferFile(pDetails, &dataBuf, dataLen,
|
||||
&rsrcBuf, rsrcLen);
|
||||
delete[] dataBuf;
|
||||
delete[] rsrcBuf;
|
||||
|
||||
if (errMsg.IsEmpty())
|
||||
if (pErrMsg->IsEmpty())
|
||||
pArchive->XferFinish(pDialog);
|
||||
else
|
||||
pArchive->XferAbort(pDialog);
|
||||
|
||||
bail:
|
||||
return (errMsg.IsEmpty() != 0);
|
||||
return (pErrMsg->IsEmpty() != 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -149,7 +149,7 @@ private:
|
||||
}
|
||||
virtual void XferPrepare(const XferFileOptions* pXferOpts) override
|
||||
{ ASSERT(false); }
|
||||
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
||||
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override
|
||||
{ ASSERT(false); return "!"; }
|
||||
virtual void XferAbort(CWnd* pMsgWnd) override
|
||||
@ -159,7 +159,7 @@ private:
|
||||
|
||||
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveBNY; }
|
||||
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
||||
FileDetails* pDetails) override
|
||||
LocalFileDetails* pDetails) override
|
||||
{ ASSERT(false); return kNuErrGeneric; }
|
||||
|
||||
enum {
|
||||
|
@ -616,25 +616,27 @@ void ImportBASDialog::OnOK(void)
|
||||
/*
|
||||
* Write the file to the currently-open archive.
|
||||
*/
|
||||
GenericArchive::FileDetails details;
|
||||
GenericArchive::LocalFileDetails details;
|
||||
|
||||
details.entryKind = GenericArchive::FileDetails::kFileKindDataFork;
|
||||
details.origName = L"Imported BASIC";
|
||||
details.storageName = fileName;
|
||||
details.access = 0xe3; // unlocked, backup bit set
|
||||
details.fileType = kFileTypeBAS;
|
||||
details.extraType = 0x0801;
|
||||
details.storageType = DiskFS::kStorageSeedling;
|
||||
details.SetEntryKind(GenericArchive::LocalFileDetails::kFileKindDataFork);
|
||||
details.SetLocalPathName(L"Imported BASIC");
|
||||
details.SetStrippedLocalPathName(fileName);
|
||||
details.SetAccess(0xe3); // unlocked, backup bit set
|
||||
details.SetFileType(kFileTypeBAS);
|
||||
details.SetExtraType(0x0801);
|
||||
details.SetStorageType(DiskFS::kStorageSeedling);
|
||||
time_t now = time(NULL);
|
||||
GenericArchive::UNIXTimeToDateTime(&now, &details.createWhen);
|
||||
GenericArchive::UNIXTimeToDateTime(&now, &details.archiveWhen);
|
||||
GenericArchive::UNIXTimeToDateTime(&now, &details.modWhen);
|
||||
NuDateTime ndt;
|
||||
GenericArchive::UNIXTimeToDateTime(&now, &ndt);
|
||||
details.SetCreateWhen(ndt);
|
||||
details.SetArchiveWhen(ndt);
|
||||
details.SetModWhen(ndt);
|
||||
|
||||
CString errMsg;
|
||||
|
||||
fDirty = true;
|
||||
if (!MainWindow::SaveToArchive(&details, (const unsigned char*) fOutput,
|
||||
fOutputLen, NULL, -1, /*ref*/errMsg, this))
|
||||
fOutputLen, NULL, -1, &errMsg, this))
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
@ -418,29 +418,33 @@ void CassetteDialog::OnImport(void)
|
||||
/*
|
||||
* Write the file to the currently-open archive.
|
||||
*/
|
||||
GenericArchive::FileDetails details;
|
||||
GenericArchive::LocalFileDetails details;
|
||||
|
||||
details.entryKind = GenericArchive::FileDetails::kFileKindDataFork;
|
||||
details.origName = "Cassette WAV";
|
||||
details.storageName = impDialog.fFileName;
|
||||
details.access = 0xe3; // unlocked, backup bit set
|
||||
details.fileType = impDialog.GetFileType();
|
||||
if (details.fileType == kFileTypeBIN)
|
||||
details.extraType = impDialog.fStartAddr;
|
||||
else if (details.fileType == kFileTypeBAS)
|
||||
details.extraType = 0x0801;
|
||||
else
|
||||
details.extraType = 0x0000;
|
||||
details.storageType = DiskFS::kStorageSeedling;
|
||||
details.SetEntryKind(GenericArchive::LocalFileDetails::kFileKindDataFork);
|
||||
details.SetLocalPathName(L"Cassette WAV");
|
||||
details.SetStrippedLocalPathName(impDialog.fFileName);
|
||||
details.SetAccess(0xe3); // unlocked, backup bit set
|
||||
details.SetFileType(impDialog.GetFileType());
|
||||
if (details.GetFileType() == kFileTypeBIN) {
|
||||
details.SetExtraType(impDialog.fStartAddr);
|
||||
} else if (details.GetFileType() == kFileTypeBAS) {
|
||||
details.SetExtraType(0x0801);
|
||||
} else {
|
||||
details.SetExtraType(0x0000);
|
||||
}
|
||||
details.SetStorageType(DiskFS::kStorageSeedling);
|
||||
time_t now = time(NULL);
|
||||
GenericArchive::UNIXTimeToDateTime(&now, &details.createWhen);
|
||||
GenericArchive::UNIXTimeToDateTime(&now, &details.archiveWhen);
|
||||
NuDateTime ndt;
|
||||
GenericArchive::UNIXTimeToDateTime(&now, &ndt);
|
||||
details.SetCreateWhen(ndt);
|
||||
details.SetArchiveWhen(ndt);
|
||||
details.SetModWhen(ndt);
|
||||
|
||||
CString errMsg;
|
||||
|
||||
fDirty = true;
|
||||
if (!MainWindow::SaveToArchive(&details, fDataArray[idx].GetDataBuf(),
|
||||
fDataArray[idx].GetDataLen(), NULL, -1, /*ref*/errMsg, this))
|
||||
fDataArray[idx].GetDataLen(), NULL, -1, &errMsg, this))
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
@ -80,11 +80,11 @@ typedef struct FileCollectionEntry {
|
||||
uint32_t cmmtLen; // len of comments
|
||||
uint32_t fileType;
|
||||
uint32_t auxType;
|
||||
int64_t createWhen; // time_t
|
||||
int64_t modWhen; // time_t
|
||||
int64_t createWhen; // holds time_t
|
||||
int64_t modWhen; // holds time_t
|
||||
uint8_t access; // ProDOS access flags
|
||||
uint8_t entryKind; // GenericArchive::FileDetails::FileKind
|
||||
uint8_t sourceFS; // DiskImgLib::DiskImg::FSFormat
|
||||
uint8_t sourceFS; // holds DiskImgLib::DiskImg::FSFormat
|
||||
uint8_t fssep; // filesystem separator char, e.g. ':'
|
||||
|
||||
/* data comes next: null-terminated WCHAR filename, then data fork, then
|
||||
@ -440,17 +440,17 @@ CString MainWindow::CopyToCollection(GenericEntry* pEntry, void** pBuf,
|
||||
return errStr;
|
||||
}
|
||||
|
||||
GenericArchive::FileDetails::FileKind entryKind;
|
||||
GenericArchive::LocalFileDetails::FileKind entryKind;
|
||||
if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory)
|
||||
entryKind = GenericArchive::FileDetails::kFileKindDirectory;
|
||||
entryKind = GenericArchive::LocalFileDetails::kFileKindDirectory;
|
||||
else if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork())
|
||||
entryKind = GenericArchive::FileDetails::kFileKindBothForks;
|
||||
entryKind = GenericArchive::LocalFileDetails::kFileKindBothForks;
|
||||
else if (pEntry->GetHasDataFork())
|
||||
entryKind = GenericArchive::FileDetails::kFileKindDataFork;
|
||||
entryKind = GenericArchive::LocalFileDetails::kFileKindDataFork;
|
||||
else if (pEntry->GetHasRsrcFork())
|
||||
entryKind = GenericArchive::FileDetails::kFileKindRsrcFork;
|
||||
entryKind = GenericArchive::LocalFileDetails::kFileKindRsrcFork;
|
||||
else if (pEntry->GetHasDiskImage())
|
||||
entryKind = GenericArchive::FileDetails::kFileKindDiskImage;
|
||||
entryKind = GenericArchive::LocalFileDetails::kFileKindDiskImage;
|
||||
else {
|
||||
ASSERT(false);
|
||||
return errStr;
|
||||
@ -912,30 +912,32 @@ bail:
|
||||
CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
|
||||
const WCHAR* pathName, const uint8_t* buf, long remLen)
|
||||
{
|
||||
GenericArchive::FileDetails::FileKind entryKind;
|
||||
GenericArchive::FileDetails details;
|
||||
GenericArchive::LocalFileDetails::FileKind entryKind;
|
||||
GenericArchive::LocalFileDetails details;
|
||||
uint8_t* dataBuf = NULL;
|
||||
uint8_t* rsrcBuf = NULL;
|
||||
long dataLen, rsrcLen, cmmtLen;
|
||||
CString errMsg;
|
||||
|
||||
entryKind = (GenericArchive::FileDetails::FileKind) pCollEnt->entryKind;
|
||||
LOGI(" Processing '%ls' (%d)", pathName, entryKind);
|
||||
entryKind = (GenericArchive::LocalFileDetails::FileKind) pCollEnt->entryKind;
|
||||
LOGD(" Processing '%ls' (%d)", pathName, entryKind);
|
||||
|
||||
details.entryKind = entryKind;
|
||||
details.origName = L"Clipboard";
|
||||
details.storageName = pathName; // TODO MacRoman convert
|
||||
details.fileSysFmt = (DiskImg::FSFormat) pCollEnt->sourceFS;
|
||||
details.fileSysInfo = pCollEnt->fssep;
|
||||
details.access = pCollEnt->access;
|
||||
details.fileType = pCollEnt->fileType;
|
||||
details.extraType = pCollEnt->auxType;
|
||||
GenericArchive::UNIXTimeToDateTime(&pCollEnt->createWhen,
|
||||
&details.createWhen);
|
||||
GenericArchive::UNIXTimeToDateTime(&pCollEnt->modWhen,
|
||||
&details.modWhen);
|
||||
details.SetEntryKind(entryKind);
|
||||
details.SetLocalPathName(L"Clipboard");
|
||||
details.SetStrippedLocalPathName(pathName);
|
||||
details.SetFileSysFmt((DiskImg::FSFormat) pCollEnt->sourceFS);
|
||||
details.SetFssep(pCollEnt->fssep);
|
||||
details.SetAccess(pCollEnt->access);
|
||||
details.SetFileType(pCollEnt->fileType);
|
||||
details.SetExtraType(pCollEnt->auxType);
|
||||
NuDateTime ndt;
|
||||
GenericArchive::UNIXTimeToDateTime(&pCollEnt->createWhen, &ndt);
|
||||
details.SetCreateWhen(ndt);
|
||||
GenericArchive::UNIXTimeToDateTime(&pCollEnt->modWhen, &ndt);
|
||||
details.SetModWhen(ndt);
|
||||
time_t now = time(NULL);
|
||||
GenericArchive::UNIXTimeToDateTime(&now, &details.archiveWhen);
|
||||
GenericArchive::UNIXTimeToDateTime(&now, &ndt);
|
||||
details.SetArchiveWhen(ndt);
|
||||
|
||||
/*
|
||||
* Because of the way XferFile works, we need to make a copy of
|
||||
@ -958,23 +960,23 @@ CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
|
||||
*/
|
||||
bool hasData = false;
|
||||
bool hasRsrc = false;
|
||||
if (details.entryKind == GenericArchive::FileDetails::kFileKindDataFork) {
|
||||
if (entryKind == GenericArchive::LocalFileDetails::kFileKindDataFork) {
|
||||
hasData = true;
|
||||
details.storageType = kNuStorageSeedling;
|
||||
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindRsrcFork) {
|
||||
details.SetStorageType(kNuStorageSeedling);
|
||||
} else if (entryKind == GenericArchive::LocalFileDetails::kFileKindRsrcFork) {
|
||||
hasRsrc = true;
|
||||
details.storageType = kNuStorageExtended;
|
||||
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindBothForks) {
|
||||
details.SetStorageType(kNuStorageExtended);
|
||||
} else if (entryKind == GenericArchive::LocalFileDetails::kFileKindBothForks) {
|
||||
hasData = hasRsrc = true;
|
||||
details.storageType = kNuStorageExtended;
|
||||
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindDiskImage) {
|
||||
details.SetStorageType(kNuStorageExtended);
|
||||
} else if (entryKind == GenericArchive::LocalFileDetails::kFileKindDiskImage) {
|
||||
hasData = true;
|
||||
details.storageType = kNuStorageSeedling;
|
||||
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindDirectory) {
|
||||
details.storageType = kNuStorageDirectory;
|
||||
details.SetStorageType(kNuStorageSeedling);
|
||||
} else if (entryKind == GenericArchive::LocalFileDetails::kFileKindDirectory) {
|
||||
details.SetStorageType(kNuStorageDirectory);
|
||||
} else {
|
||||
ASSERT(false);
|
||||
return "Internal error.";
|
||||
return L"Internal error.";
|
||||
}
|
||||
|
||||
if (hasData) {
|
||||
@ -985,7 +987,7 @@ CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
|
||||
dataLen = pCollEnt->dataLen;
|
||||
dataBuf = new uint8_t[dataLen];
|
||||
if (dataBuf == NULL)
|
||||
return "memory allocation failed.";
|
||||
return L"memory allocation failed.";
|
||||
memcpy(dataBuf, buf, dataLen);
|
||||
buf += dataLen;
|
||||
remLen -= dataLen;
|
||||
@ -1003,7 +1005,7 @@ CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
|
||||
rsrcLen = pCollEnt->rsrcLen;
|
||||
rsrcBuf = new uint8_t[rsrcLen];
|
||||
if (rsrcBuf == NULL)
|
||||
return "Memory allocation failed.";
|
||||
return L"Memory allocation failed.";
|
||||
memcpy(rsrcBuf, buf, rsrcLen);
|
||||
buf += rsrcLen;
|
||||
remLen -= rsrcLen;
|
||||
|
@ -466,21 +466,26 @@ GenericArchive::OpenResult DiskArchive::Open(const WCHAR* filename,
|
||||
{
|
||||
CWaitCursor waitc;
|
||||
|
||||
// TODO(Unicode): modify DiskImg lib to accept wide paths
|
||||
CStringA fileNameA(filename);
|
||||
if (!PathName::TestNarrowConversion(filename, fileNameA, &errMsg)) {
|
||||
result = kResultFailure;
|
||||
goto bail;
|
||||
}
|
||||
dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep,
|
||||
readOnly);
|
||||
if (dierr == kDIErrAccessDenied && !readOnly && !isVolume) {
|
||||
// retry file open with read-only set
|
||||
// don't do that for volumes -- assume they know what they want
|
||||
LOGI(" Retrying open with read-only set");
|
||||
LOGD(" Retrying open with read-only set");
|
||||
fIsReadOnly = readOnly = true;
|
||||
dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep,
|
||||
readOnly);
|
||||
}
|
||||
if (dierr != kDIErrNone) {
|
||||
if (dierr == kDIErrFileArchive)
|
||||
if (dierr == kDIErrFileArchive) {
|
||||
result = kResultFileArchive;
|
||||
else {
|
||||
} else {
|
||||
result = kResultFailure;
|
||||
errMsg.Format(L"Unable to open '%ls': %hs.", filename,
|
||||
DiskImgLib::DIStrError(dierr));
|
||||
@ -507,7 +512,7 @@ GenericArchive::OpenResult DiskArchive::Open(const WCHAR* filename,
|
||||
imf.SetAllowGenericFormats(false);
|
||||
|
||||
if (imf.DoModal() != IDOK) {
|
||||
LOGI("User bailed on IMF dialog");
|
||||
LOGD("User bailed on IMF dialog");
|
||||
result = kResultCancel;
|
||||
goto bail;
|
||||
}
|
||||
@ -1246,7 +1251,7 @@ bail:
|
||||
}
|
||||
|
||||
NuError DiskArchive::DoAddFile(const AddFilesDialog* pAddOpts,
|
||||
FileDetails* pDetails)
|
||||
LocalFileDetails* pDetails)
|
||||
{
|
||||
/*
|
||||
* Add a file to a disk image Unfortunately we can't just add the files
|
||||
@ -1286,8 +1291,9 @@ NuError DiskArchive::DoAddFile(const AddFilesDialog* pAddOpts,
|
||||
int neededLen = 64; // reasonable guess
|
||||
char* fsNormalBuf = NULL; // name as it will appear on disk image
|
||||
|
||||
LOGI(" +++ ADD file: orig='%ls' stor='%ls'",
|
||||
(LPCWSTR) pDetails->origName, (LPCWSTR) pDetails->storageName);
|
||||
LOGI(" +++ ADD file: orig='%ls' strip='%ls'",
|
||||
(LPCWSTR) pDetails->GetLocalPathName(),
|
||||
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||
|
||||
retry:
|
||||
/*
|
||||
@ -1295,14 +1301,13 @@ retry:
|
||||
*/
|
||||
delete[] fsNormalBuf;
|
||||
fsNormalBuf = new char[neededLen];
|
||||
CStringA storageNameA(pDetails->storageName);
|
||||
dierr = pDiskFS->NormalizePath(storageNameA,
|
||||
dierr = pDiskFS->NormalizePath(pDetails->GetStoragePathNameMOR(),
|
||||
PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen);
|
||||
if (dierr == kDIErrDataOverrun) {
|
||||
/* not long enough, try again *once* */
|
||||
delete[] fsNormalBuf;
|
||||
fsNormalBuf = new char[neededLen];
|
||||
dierr = pDiskFS->NormalizePath(storageNameA,
|
||||
dierr = pDiskFS->NormalizePath(pDetails->GetStoragePathNameMOR(),
|
||||
PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen);
|
||||
}
|
||||
if (dierr != kDIErrNone) {
|
||||
@ -1337,16 +1342,16 @@ retry:
|
||||
goto retry;
|
||||
} else if (result == kNuOverwrite) {
|
||||
/* delete the existing file immediately */
|
||||
LOGI(" Deleting existing file '%hs'", fsNormalBuf);
|
||||
LOGD(" Deleting existing file '%hs'", fsNormalBuf);
|
||||
dierr = pDiskFS->DeleteFile(pExisting);
|
||||
if (dierr != kDIErrNone) {
|
||||
// Would be nice to show a dialog and explain *why*, but
|
||||
// I'm not sure we have a window here.
|
||||
LOGI(" Deletion failed (err=%d)", dierr);
|
||||
LOGE(" Deletion failed (err=%d)", dierr);
|
||||
goto bail;
|
||||
}
|
||||
} else {
|
||||
LOGI("GLITCH: bad return %d from HandleReplaceExisting",result);
|
||||
LOGE("GLITCH: bad return %d from HandleReplaceExisting",result);
|
||||
assert(false);
|
||||
nuerr = kNuErrInternal;
|
||||
goto bail;
|
||||
@ -1364,7 +1369,7 @@ retry:
|
||||
goto bail;
|
||||
}
|
||||
|
||||
LOGI("FSNormalized is '%hs'", pAddData->GetFSNormalPath());
|
||||
LOGD("FSNormalized is '%hs'", pAddData->GetFSNormalPath());
|
||||
|
||||
AddToAddDataList(pAddData);
|
||||
|
||||
@ -1374,7 +1379,7 @@ bail:
|
||||
}
|
||||
|
||||
NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
|
||||
FileDetails* pDetails)
|
||||
LocalFileDetails* pDetails)
|
||||
{
|
||||
NuResult result;
|
||||
|
||||
@ -1390,8 +1395,8 @@ NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
|
||||
confOvwr.fExistingFile = pExisting->GetPathName();
|
||||
confOvwr.fExistingFileModWhen = pExisting->GetModWhen();
|
||||
|
||||
PathName srcPath(pDetails->origName);
|
||||
confOvwr.fNewFileSource = pDetails->origName; // or storageName?
|
||||
PathName srcPath(pDetails->GetLocalPathName());
|
||||
confOvwr.fNewFileSource = pDetails->GetLocalPathName();
|
||||
confOvwr.fNewFileModWhen = srcPath.GetModWhen();
|
||||
|
||||
if (confOvwr.DoModal() == IDCANCEL) {
|
||||
@ -1399,6 +1404,9 @@ NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
|
||||
return kNuAbort;
|
||||
}
|
||||
|
||||
// TODO: if they rename one fork, we need to track that fact and
|
||||
// carry it over to the other fork -- otherwise they can rename
|
||||
// the data and resource forks into separate files.
|
||||
if (confOvwr.fResultRename) {
|
||||
/*
|
||||
* Replace the name in FileDetails. They were asked to modify
|
||||
@ -1413,8 +1421,9 @@ NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
|
||||
* full path and reject "OK" if it's not valid. Instead, we just
|
||||
* allow the FS normalizer to force the filename to be valid.
|
||||
*/
|
||||
pDetails->storageName = confOvwr.fExistingFile;
|
||||
LOGI("Trying rename to '%ls'", (LPCWSTR) pDetails->storageName);
|
||||
pDetails->SetStrippedLocalPathName(confOvwr.fExistingFile);
|
||||
LOGI("Trying rename to '%ls'",
|
||||
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||
return kNuRename;
|
||||
}
|
||||
|
||||
@ -1470,26 +1479,26 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||
|
||||
pData = fpAddDataHead;
|
||||
while (pData != NULL) {
|
||||
const FileDetails* pDataDetails = NULL;
|
||||
const FileDetails* pRsrcDetails = NULL;
|
||||
const FileDetails* pDetails = pData->GetDetails();
|
||||
const LocalFileDetails* pDataDetails = NULL;
|
||||
const LocalFileDetails* pRsrcDetails = NULL;
|
||||
const LocalFileDetails* pDetails = pData->GetDetails();
|
||||
const char* typeStr = "????"; // for debug msg only
|
||||
|
||||
switch (pDetails->entryKind) {
|
||||
case FileDetails::kFileKindDataFork:
|
||||
switch (pDetails->GetEntryKind()) {
|
||||
case LocalFileDetails::kFileKindDataFork:
|
||||
pDataDetails = pDetails;
|
||||
typeStr = "data";
|
||||
break;
|
||||
case FileDetails::kFileKindRsrcFork:
|
||||
case LocalFileDetails::kFileKindRsrcFork:
|
||||
pRsrcDetails = pDetails;
|
||||
typeStr = "rsrc";
|
||||
break;
|
||||
case FileDetails::kFileKindDiskImage:
|
||||
case LocalFileDetails::kFileKindDiskImage:
|
||||
pDataDetails = pDetails;
|
||||
typeStr = "disk";
|
||||
break;
|
||||
case FileDetails::kFileKindBothForks:
|
||||
case FileDetails::kFileKindDirectory:
|
||||
case LocalFileDetails::kFileKindBothForks:
|
||||
case LocalFileDetails::kFileKindDirectory:
|
||||
default:
|
||||
assert(false);
|
||||
return L"internal error";
|
||||
@ -1499,20 +1508,20 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||
pDetails = pData->GetOtherFork()->GetDetails();
|
||||
typeStr = "both";
|
||||
|
||||
switch (pDetails->entryKind) {
|
||||
case FileDetails::kFileKindDataFork:
|
||||
switch (pDetails->GetEntryKind()) {
|
||||
case LocalFileDetails::kFileKindDataFork:
|
||||
assert(pDataDetails == NULL);
|
||||
pDataDetails = pDetails;
|
||||
break;
|
||||
case FileDetails::kFileKindRsrcFork:
|
||||
case LocalFileDetails::kFileKindRsrcFork:
|
||||
assert(pRsrcDetails == NULL);
|
||||
pRsrcDetails = pDetails;
|
||||
break;
|
||||
case FileDetails::kFileKindDiskImage:
|
||||
case LocalFileDetails::kFileKindDiskImage:
|
||||
assert(false);
|
||||
return L"(internal) add other disk error";
|
||||
case FileDetails::kFileKindBothForks:
|
||||
case FileDetails::kFileKindDirectory:
|
||||
case LocalFileDetails::kFileKindBothForks:
|
||||
case LocalFileDetails::kFileKindDirectory:
|
||||
default:
|
||||
assert(false);
|
||||
return L"internal error";
|
||||
@ -1520,7 +1529,7 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||
}
|
||||
|
||||
LOGI("Adding file '%ls' (%hs)",
|
||||
(LPCWSTR) pDetails->storageName, typeStr);
|
||||
(LPCWSTR) pDetails->GetStrippedLocalPathName(), typeStr);
|
||||
ASSERT(pDataDetails != NULL || pRsrcDetails != NULL);
|
||||
|
||||
/*
|
||||
@ -1530,14 +1539,20 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||
* but it could be awkward for HFS (not to mention HFS Plus!).
|
||||
*/
|
||||
DiskFS::CreateParms parms;
|
||||
ConvertFDToCP(pData->GetDetails(), &parms);
|
||||
/* use the FS-normalized path here */
|
||||
/* (do we have to? do we want to?) */
|
||||
parms.pathName = pData->GetFSNormalPath();
|
||||
if (pRsrcDetails != NULL)
|
||||
parms.storageType = kNuStorageExtended;
|
||||
else
|
||||
parms.storageType = kNuStorageSeedling;
|
||||
/* use the FS-normalized path here */
|
||||
/* (do we have to? do we want to?) */
|
||||
parms.pathName = pData->GetFSNormalPath();
|
||||
/* copy the rest out of the LocalFileDetails */
|
||||
parms.fssep = pDetails->GetFssep();
|
||||
parms.fileType = pDetails->GetFileType();
|
||||
parms.auxType = pDetails->GetExtraType();
|
||||
parms.access = pDetails->GetAccess();
|
||||
parms.createWhen = NufxArchive::DateTimeToSeconds(&pDetails->GetCreateWhen());
|
||||
parms.modWhen = NufxArchive::DateTimeToSeconds(&pDetails->GetModWhen());
|
||||
|
||||
dataLen = rsrcLen = -1;
|
||||
if (pDataDetails != NULL) {
|
||||
@ -1545,8 +1560,8 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||
/* (HA conversion only happens if text conversion happens) */
|
||||
GenericEntry::ConvertHighASCII convHA;
|
||||
if (addOptsConvEOL == AddFilesDialog::kConvEOLType) {
|
||||
if (pDataDetails->fileType == kFileTypeTXT ||
|
||||
pDataDetails->fileType == kFileTypeSRC)
|
||||
if (pDataDetails->GetFileType() == kFileTypeTXT ||
|
||||
pDataDetails->GetFileType() == kFileTypeSRC)
|
||||
{
|
||||
LOGI("Enabling text conversion by type");
|
||||
convEOL = GenericEntry::kConvertEOLOn;
|
||||
@ -1559,14 +1574,14 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||
else
|
||||
convHA = GenericEntry::kConvertHAOff;
|
||||
|
||||
errMsg = LoadFile(pDataDetails->origName, &dataBuf, &dataLen,
|
||||
errMsg = LoadFile(pDataDetails->GetLocalPathName(), &dataBuf, &dataLen,
|
||||
convEOL, convHA);
|
||||
if (!errMsg.IsEmpty())
|
||||
goto bail;
|
||||
}
|
||||
if (pRsrcDetails != NULL) {
|
||||
/* no text conversion on resource forks */
|
||||
errMsg = LoadFile(pRsrcDetails->origName, &rsrcBuf, &rsrcLen,
|
||||
errMsg = LoadFile(pRsrcDetails->GetLocalPathName(), &rsrcBuf, &rsrcLen,
|
||||
GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff);
|
||||
if (!errMsg.IsEmpty())
|
||||
goto bail;
|
||||
@ -1575,7 +1590,7 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||
/* really ought to do this separately for each thread */
|
||||
SET_PROGRESS_BEGIN();
|
||||
CString pathNameW(parms.pathName);
|
||||
SET_PROGRESS_UPDATE2(0, pDetails->origName, pathNameW);
|
||||
SET_PROGRESS_UPDATE2(0, pDetails->GetLocalPathName(), pathNameW);
|
||||
|
||||
DIError dierr;
|
||||
dierr = AddForksToDisk(pDiskFS, &parms, dataBuf, dataLen,
|
||||
@ -1954,25 +1969,6 @@ bail:
|
||||
return dierr;
|
||||
}
|
||||
|
||||
// TODO: make this a member of FileDetails, and return a struct owned by
|
||||
// FileDetails. This is necessary because we put const strings into
|
||||
// pCreateParms that are owned by FileDetails, and need to coordinate the
|
||||
// object lifetime.
|
||||
void DiskArchive::ConvertFDToCP(const FileDetails* pDetails,
|
||||
DiskFS::CreateParms* pCreateParms)
|
||||
{
|
||||
// ugly hack to get storage for narrow string
|
||||
pDetails->fStorageNameA = pDetails->storageName;
|
||||
pCreateParms->pathName = pDetails->fStorageNameA;
|
||||
pCreateParms->fssep = (char) pDetails->fileSysInfo;
|
||||
pCreateParms->storageType = pDetails->storageType;
|
||||
pCreateParms->fileType = pDetails->fileType;
|
||||
pCreateParms->auxType = pDetails->extraType;
|
||||
pCreateParms->access = pDetails->access;
|
||||
pCreateParms->createWhen = NufxArchive::DateTimeToSeconds(&pDetails->createWhen);
|
||||
pCreateParms->modWhen = NufxArchive::DateTimeToSeconds(&pDetails->modWhen);
|
||||
}
|
||||
|
||||
void DiskArchive::AddToAddDataList(FileAddData* pData)
|
||||
{
|
||||
ASSERT(pData != NULL);
|
||||
@ -1983,31 +1979,31 @@ void DiskArchive::AddToAddDataList(FileAddData* pData)
|
||||
* O(n^2) behavior, but I'm expecting N to be relatively small (under
|
||||
* 1000 in almost all cases).
|
||||
*/
|
||||
//if (strcasecmp(pData->GetDetails()->storageName, "system\\finder") == 0)
|
||||
// LOGI("whee");
|
||||
FileAddData* pSearch = fpAddDataHead;
|
||||
FileDetails::FileKind dataKind, listKind;
|
||||
LocalFileDetails::FileKind dataKind, listKind;
|
||||
|
||||
dataKind = pData->GetDetails()->entryKind;
|
||||
dataKind = pData->GetDetails()->GetEntryKind();
|
||||
while (pSearch != NULL) {
|
||||
if (pSearch->GetOtherFork() == NULL &&
|
||||
wcscmp(pSearch->GetDetails()->storageName,
|
||||
pData->GetDetails()->storageName) == 0)
|
||||
wcscmp(pSearch->GetDetails()->GetStrippedLocalPathName(),
|
||||
pData->GetDetails()->GetStrippedLocalPathName()) == 0)
|
||||
{
|
||||
//NuThreadID dataID = pData->GetDetails()->threadID;
|
||||
//NuThreadID listID = pSearch->GetDetails()->threadID;
|
||||
|
||||
listKind = pSearch->GetDetails()->entryKind;
|
||||
listKind = pSearch->GetDetails()->GetEntryKind();
|
||||
|
||||
/* got a name match */
|
||||
if (dataKind != listKind &&
|
||||
(dataKind == FileDetails::kFileKindDataFork || dataKind == FileDetails::kFileKindRsrcFork) &&
|
||||
(listKind == FileDetails::kFileKindDataFork || listKind == FileDetails::kFileKindRsrcFork))
|
||||
(dataKind == LocalFileDetails::kFileKindDataFork ||
|
||||
dataKind == LocalFileDetails::kFileKindRsrcFork) &&
|
||||
(listKind == LocalFileDetails::kFileKindDataFork ||
|
||||
listKind == LocalFileDetails::kFileKindRsrcFork))
|
||||
{
|
||||
/* looks good, hook it in here instead of the list */
|
||||
LOGD("--- connecting forks of '%ls' and '%ls'",
|
||||
(LPCWSTR) pData->GetDetails()->origName,
|
||||
(LPCWSTR) pSearch->GetDetails()->origName);
|
||||
(LPCWSTR) pData->GetDetails()->GetLocalPathName(),
|
||||
(LPCWSTR) pSearch->GetDetails()->GetLocalPathName());
|
||||
pSearch->SetOtherFork(pData);
|
||||
return;
|
||||
}
|
||||
@ -2299,7 +2295,7 @@ bool DiskArchive::RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
|
||||
ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED);
|
||||
goto bail;
|
||||
}
|
||||
LOGI("Rename of '%ls' to '%ls' succeeded",
|
||||
LOGD("Rename of '%ls' to '%ls' succeeded",
|
||||
pEntry->GetDisplayName(), (LPCWSTR) renameDlg.fNewName);
|
||||
} else if (result == IDCANCEL) {
|
||||
LOGI("Canceling out of remaining renames");
|
||||
@ -2528,7 +2524,7 @@ bool DiskArchive::SetProps(CWnd* pMsgWnd, GenericEntry* pGenericEntry,
|
||||
DiskImg::FSFormat fsFormat;
|
||||
fsFormat = pFile->GetDiskFS()->GetDiskImg()->GetFSFormat();
|
||||
if (fsFormat == DiskImg::kFormatDOS32 || fsFormat == DiskImg::kFormatDOS33) {
|
||||
LOGI(" (reloading additional fields after DOS SFI)");
|
||||
LOGD(" (reloading additional fields after DOS SFI)");
|
||||
pEntry->SetDataForkLen(pFile->GetDataLength());
|
||||
pEntry->SetCompressedLen(pFile->GetDataSparseLength());
|
||||
pEntry->SetSuspicious(pFile->GetQuality() == A2File::kQualitySuspicious);
|
||||
@ -2559,9 +2555,9 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
|
||||
* forked or not.
|
||||
*/
|
||||
LOGI("DiskArchive XferSelection!");
|
||||
unsigned char* dataBuf = NULL;
|
||||
unsigned char* rsrcBuf = NULL;
|
||||
FileDetails fileDetails;
|
||||
uint8_t* dataBuf = NULL;
|
||||
uint8_t* rsrcBuf = NULL;
|
||||
LocalFileDetails fileDetails;
|
||||
CString errMsg, extractErrMsg, cmpStr;
|
||||
CString fixedPathName;
|
||||
XferStatus retval = kXferFailed;
|
||||
@ -2590,7 +2586,7 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
|
||||
*/
|
||||
fixedPathName = pEntry->GetPathName();
|
||||
if (fixedPathName.IsEmpty())
|
||||
fixedPathName = _T("(no filename)");
|
||||
fixedPathName = L"(no filename)";
|
||||
if (pEntry->GetFSFormat() != DiskImg::kFormatProDOS)
|
||||
fixedPathName.Replace(PathProposal::kDefaultStoredFssep, '.');
|
||||
if (pEntry->GetSubVolName() != NULL) {
|
||||
@ -2603,7 +2599,7 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
|
||||
|
||||
if (pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) {
|
||||
/* this is the volume dir */
|
||||
LOGI(" XFER not transferring volume dir '%ls'",
|
||||
LOGD(" XFER not transferring volume dir '%ls'",
|
||||
(LPCWSTR) fixedPathName);
|
||||
continue;
|
||||
} else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) {
|
||||
@ -2613,22 +2609,22 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
|
||||
cmpStr += (char)PathProposal::kDefaultStoredFssep;
|
||||
|
||||
if (pSelSet->CountMatchingPrefix(cmpStr) == 0) {
|
||||
LOGI("FOUND empty dir '%ls'", (LPCWSTR) fixedPathName);
|
||||
LOGD("FOUND empty dir '%ls'", (LPCWSTR) fixedPathName);
|
||||
cmpStr += kEmptyFolderMarker;
|
||||
dataBuf = new unsigned char[1];
|
||||
dataLen = 0;
|
||||
fileDetails.entryKind = FileDetails::kFileKindDataFork;
|
||||
fileDetails.storageName = cmpStr;
|
||||
fileDetails.fileType = 0; // NON
|
||||
fileDetails.access =
|
||||
pEntry->GetAccess() | GenericEntry::kAccessInvisible;
|
||||
fileDetails.SetEntryKind(LocalFileDetails::kFileKindDataFork);
|
||||
fileDetails.SetStrippedLocalPathName(cmpStr);
|
||||
fileDetails.SetFileType(0); // NON
|
||||
fileDetails.SetAccess(
|
||||
pEntry->GetAccess() | GenericEntry::kAccessInvisible);
|
||||
goto have_stuff2;
|
||||
} else {
|
||||
LOGI("NOT empty dir '%ls'", (LPCWSTR) fixedPathName);
|
||||
LOGD("NOT empty dir '%ls'", (LPCWSTR) fixedPathName);
|
||||
}
|
||||
}
|
||||
|
||||
LOGI(" XFER not transferring directory '%ls'",
|
||||
LOGD(" XFER not transferring directory '%ls'",
|
||||
(LPCWSTR) fixedPathName);
|
||||
continue;
|
||||
}
|
||||
@ -2695,38 +2691,42 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
|
||||
ASSERT(rsrcBuf == NULL);
|
||||
}
|
||||
|
||||
if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork())
|
||||
fileDetails.entryKind = FileDetails::kFileKindBothForks;
|
||||
else if (pEntry->GetHasDataFork())
|
||||
fileDetails.entryKind = FileDetails::kFileKindDataFork;
|
||||
else if (pEntry->GetHasRsrcFork())
|
||||
fileDetails.entryKind = FileDetails::kFileKindRsrcFork;
|
||||
else {
|
||||
if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork()) {
|
||||
fileDetails.SetEntryKind(LocalFileDetails::kFileKindBothForks);
|
||||
} else if (pEntry->GetHasDataFork()) {
|
||||
fileDetails.SetEntryKind(LocalFileDetails::kFileKindDataFork);
|
||||
} else if (pEntry->GetHasRsrcFork()) {
|
||||
fileDetails.SetEntryKind(LocalFileDetails::kFileKindRsrcFork);
|
||||
} else {
|
||||
ASSERT(false);
|
||||
fileDetails.entryKind = FileDetails::kFileKindUnknown;
|
||||
fileDetails.SetEntryKind(LocalFileDetails::kFileKindUnknown);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the FileDetails.
|
||||
* Set up the rest of the LocalFileDetails fields.
|
||||
*/
|
||||
fileDetails.storageName = fixedPathName;
|
||||
fileDetails.fileType = pEntry->GetFileType();
|
||||
fileDetails.access = pEntry->GetAccess();
|
||||
fileDetails.SetStrippedLocalPathName(fixedPathName);
|
||||
fileDetails.SetFileType(pEntry->GetFileType());
|
||||
fileDetails.SetAccess(pEntry->GetAccess());
|
||||
have_stuff2:
|
||||
fileDetails.fileSysFmt = pEntry->GetSourceFS();
|
||||
fileDetails.fileSysInfo = PathProposal::kDefaultStoredFssep;
|
||||
fileDetails.extraType = pEntry->GetAuxType();
|
||||
fileDetails.storageType = kNuStorageUnknown; /* let NufxLib deal */
|
||||
fileDetails.SetFileSysFmt(pEntry->GetSourceFS());
|
||||
fileDetails.SetFssep(PathProposal::kDefaultStoredFssep);
|
||||
fileDetails.SetExtraType(pEntry->GetAuxType());
|
||||
fileDetails.SetStorageType(kNuStorageUnknown); // let NufxLib deal
|
||||
|
||||
NuDateTime ndt;
|
||||
time_t when;
|
||||
when = time(NULL);
|
||||
UNIXTimeToDateTime(&when, &fileDetails.archiveWhen);
|
||||
UNIXTimeToDateTime(&when, &ndt);
|
||||
fileDetails.SetArchiveWhen(ndt);
|
||||
when = pEntry->GetModWhen();
|
||||
UNIXTimeToDateTime(&when, &fileDetails.modWhen);
|
||||
UNIXTimeToDateTime(&when, &ndt);
|
||||
fileDetails.SetModWhen(ndt);
|
||||
when = pEntry->GetCreateWhen();
|
||||
UNIXTimeToDateTime(&when, &fileDetails.createWhen);
|
||||
UNIXTimeToDateTime(&when, &ndt);
|
||||
fileDetails.SetCreateWhen(ndt);
|
||||
|
||||
pActionProgress->SetArcName(fileDetails.storageName);
|
||||
pActionProgress->SetArcName(fileDetails.GetStrippedLocalPathName());
|
||||
if (pActionProgress->SetProgress(0) == IDCANCEL) {
|
||||
retval = kXferCancelled;
|
||||
goto bail;
|
||||
@ -2780,23 +2780,20 @@ void DiskArchive::XferPrepare(const XferFileOptions* pXferOpts)
|
||||
fpXferTargetFS = pXferOpts->fpTargetFS;
|
||||
}
|
||||
|
||||
CString DiskArchive::XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
||||
CString DiskArchive::XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen)
|
||||
{
|
||||
//const int kFileTypeTXT = 0x04;
|
||||
DiskFS::CreateParms createParms;
|
||||
DiskFS* pDiskFS;
|
||||
CString errMsg;
|
||||
DIError dierr = kDIErrNone;
|
||||
|
||||
LOGI(" XFER: transfer '%ls' (dataLen=%ld rsrcLen=%ld)",
|
||||
(LPCWSTR) pDetails->storageName, dataLen, rsrcLen);
|
||||
(LPCWSTR) pDetails->GetStrippedLocalPathName(), dataLen, rsrcLen);
|
||||
|
||||
ASSERT(pDataBuf != NULL);
|
||||
ASSERT(pRsrcBuf != NULL);
|
||||
|
||||
/* fill out CreateParms from FileDetails */
|
||||
ConvertFDToCP(pDetails, &createParms);
|
||||
const DiskFS::CreateParms& createParms = pDetails->GetCreateParms();
|
||||
|
||||
if (fpXferTargetFS == NULL)
|
||||
pDiskFS = fpPrimaryDiskFS;
|
||||
@ -2813,22 +2810,24 @@ CString DiskArchive::XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
||||
* not worth adding a new interface just for that.
|
||||
*/
|
||||
bool srcIsDOS, dstIsDOS;
|
||||
srcIsDOS = DiskImg::UsesDOSFileStructure(pDetails->fileSysFmt);
|
||||
srcIsDOS = DiskImg::UsesDOSFileStructure(pDetails->GetFileSysFmt());
|
||||
dstIsDOS = DiskImg::UsesDOSFileStructure(pDiskFS->GetDiskImg()->GetFSFormat());
|
||||
if (dataLen > 0 &&
|
||||
(pDetails->fileType == kFileTypeTXT || pDetails->fileType == kFileTypeSRC))
|
||||
(pDetails->GetFileType() == kFileTypeTXT ||
|
||||
pDetails->GetFileType() == kFileTypeSRC))
|
||||
{
|
||||
unsigned char* ucp = *pDataBuf;
|
||||
long len = dataLen;
|
||||
|
||||
if (srcIsDOS && !dstIsDOS) {
|
||||
LOGD(" Stripping high ASCII from '%ls'",
|
||||
(LPCWSTR) pDetails->storageName);
|
||||
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||
|
||||
while (len--)
|
||||
*ucp++ &= 0x7f;
|
||||
} else if (!srcIsDOS && dstIsDOS) {
|
||||
LOGD(" Adding high ASCII to '%ls'", (LPCWSTR) pDetails->storageName);
|
||||
LOGD(" Adding high ASCII to '%ls'",
|
||||
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||
|
||||
while (len--) {
|
||||
if (*ucp != '\0')
|
||||
@ -2837,9 +2836,10 @@ CString DiskArchive::XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
||||
}
|
||||
} else if (srcIsDOS && dstIsDOS) {
|
||||
LOGD(" --- not altering DOS-to-DOS text '%ls'",
|
||||
(LPCWSTR) pDetails->storageName);
|
||||
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||
} else {
|
||||
LOGD(" --- non-DOS transfer '%ls'", (LPCWSTR) pDetails->storageName);
|
||||
LOGD(" --- non-DOS transfer '%ls'",
|
||||
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,7 +231,7 @@ private:
|
||||
virtual CString Close(void);
|
||||
|
||||
virtual void XferPrepare(const XferFileOptions* pXferOpts) override;
|
||||
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
||||
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override;
|
||||
virtual void XferAbort(CWnd* pMsgWnd) override;
|
||||
virtual void XferFinish(CWnd* pMsgWnd) override;
|
||||
@ -253,10 +253,10 @@ private:
|
||||
*/
|
||||
class FileAddData {
|
||||
public:
|
||||
FileAddData(const FileDetails* pDetails, char* fsNormalPath) {
|
||||
FileAddData(const LocalFileDetails* pDetails, char* fsNormalPathMOR) {
|
||||
fDetails = *pDetails;
|
||||
|
||||
fFSNormalPath = fsNormalPath;
|
||||
fFSNormalPathMOR = fsNormalPathMOR;
|
||||
fpOtherFork = NULL;
|
||||
fpNext = NULL;
|
||||
}
|
||||
@ -267,25 +267,21 @@ private:
|
||||
FileAddData* GetOtherFork(void) const { return fpOtherFork; }
|
||||
void SetOtherFork(FileAddData* pData) { fpOtherFork = pData; }
|
||||
|
||||
const FileDetails* GetDetails(void) const { return &fDetails; }
|
||||
const LocalFileDetails* GetDetails(void) const { return &fDetails; }
|
||||
|
||||
/*
|
||||
* Get the "FS-normal" path, i.e. exactly what we want to appear
|
||||
* on the disk image. This has the result of any conversions, so
|
||||
* we need to store it as a narrow string.
|
||||
* we need to store it as a narrow Mac OS Roman string.
|
||||
*/
|
||||
const char* GetFSNormalPath(void) const { return fFSNormalPath; }
|
||||
const char* GetFSNormalPath(void) const { return fFSNormalPathMOR; }
|
||||
|
||||
private:
|
||||
// Three filenames stored inside FileDetails:
|
||||
// fDetails.origName -- the name of the Windows file
|
||||
// fDetails.storageName -- origName with type-preservation goodies
|
||||
// stripped out
|
||||
// fFSNormalPath -- the FS-normalized version of "storageName", i.e.
|
||||
// the name as it will appear on the Apple II disk image
|
||||
LocalFileDetails fDetails;
|
||||
|
||||
FileDetails fDetails;
|
||||
CStringA fFSNormalPath;
|
||||
// The DiskFS-normalized version of the storage name. This is the
|
||||
// name as it will appear on the Apple II disk image.
|
||||
CStringA fFSNormalPathMOR;
|
||||
|
||||
FileAddData* fpOtherFork;
|
||||
FileAddData* fpNext;
|
||||
@ -293,7 +289,7 @@ private:
|
||||
|
||||
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveDiskImage; }
|
||||
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
||||
FileDetails* pDetails) override;
|
||||
LocalFileDetails* pDetails) override;
|
||||
|
||||
/*
|
||||
* Reload the contents of the archive, showing an error message if the
|
||||
@ -344,7 +340,7 @@ private:
|
||||
* Replaces pDetails->storageName if the user elects to rename
|
||||
*/
|
||||
NuResult HandleReplaceExisting(const A2File* pExisting,
|
||||
FileDetails* pDetails);
|
||||
LocalFileDetails* pDetails);
|
||||
|
||||
/*
|
||||
* Process the list of pending file adds.
|
||||
@ -394,15 +390,6 @@ private:
|
||||
*/
|
||||
void FreeAddDataList(void);
|
||||
|
||||
/*
|
||||
* Fill out a CreateParms structure from a FileDetails structure.
|
||||
*
|
||||
* The NuStorageType values correspond exactly to ProDOS storage types, so
|
||||
* there's no need to convert them.
|
||||
*/
|
||||
void ConvertFDToCP(const FileDetails* pDetails,
|
||||
DiskFS::CreateParms* pCreateParms);
|
||||
|
||||
/*
|
||||
* Set up a RenameEntryDialog for the entry in "*pEntry".
|
||||
*
|
||||
|
@ -1287,9 +1287,11 @@ void PathProposal::DenormalizePath(WCHAR* pathBuf)
|
||||
ch = HexDigit(*srcp) << 4;
|
||||
srcp++;
|
||||
if (isxdigit((int)*srcp)) {
|
||||
/* valid, output char */
|
||||
/* valid, output char (unless it's a %00 place-holder) */
|
||||
ch += HexDigit(*srcp);
|
||||
if (ch != '\0') {
|
||||
*dstp++ = ch;
|
||||
}
|
||||
srcp++;
|
||||
} else {
|
||||
/* bogus '%' with trailing hex digit found! */
|
||||
|
@ -10,6 +10,7 @@
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "GenericArchive.h"
|
||||
#include "NufxArchive.h"
|
||||
#include "FileNameConv.h"
|
||||
#include "ContentList.h"
|
||||
#include "Main.h"
|
||||
@ -46,36 +47,34 @@
|
||||
*/
|
||||
|
||||
GenericEntry::GenericEntry(void)
|
||||
: fPathName(NULL),
|
||||
fFileName(NULL),
|
||||
fFileNameExtension(NULL),
|
||||
fFssep('\0'),
|
||||
fSubVolName(NULL),
|
||||
fDisplayName(NULL),
|
||||
fFileType(0),
|
||||
fAuxType(0),
|
||||
fAccess(0),
|
||||
fCreateWhen(kDateNone),
|
||||
fModWhen(kDateNone),
|
||||
fRecordKind(kRecordKindUnknown),
|
||||
fFormatStr(L"Unknown"),
|
||||
fDataForkLen(0),
|
||||
fRsrcForkLen(0),
|
||||
fCompressedLen(0),
|
||||
fSourceFS(DiskImg::kFormatUnknown),
|
||||
fHasDataFork(false),
|
||||
fHasRsrcFork(false),
|
||||
fHasDiskImage(false),
|
||||
fHasComment(false),
|
||||
fHasNonEmptyComment(false),
|
||||
fDamaged(false),
|
||||
fSuspicious(false),
|
||||
fIndex(-1),
|
||||
fpPrev(NULL),
|
||||
fpNext(NULL)
|
||||
{
|
||||
fPathName = NULL;
|
||||
fFileName = NULL;
|
||||
fFssep = '\0';
|
||||
fSubVolName = NULL;
|
||||
fDisplayName = NULL;
|
||||
fFileType = 0;
|
||||
fAuxType = 0;
|
||||
fAccess = 0;
|
||||
fModWhen = kDateNone;
|
||||
fCreateWhen = kDateNone;
|
||||
fRecordKind = kRecordKindUnknown;
|
||||
fFormatStr = L"Unknown";
|
||||
fCompressedLen = 0;
|
||||
//fUncompressedLen = 0;
|
||||
fDataForkLen = fRsrcForkLen = 0;
|
||||
|
||||
fSourceFS = DiskImg::kFormatUnknown;
|
||||
|
||||
fHasDataFork = false;
|
||||
fHasRsrcFork = false;
|
||||
fHasDiskImage = false;
|
||||
fHasComment = false;
|
||||
fHasNonEmptyComment = false;
|
||||
|
||||
fIndex = -1;
|
||||
fpPrev = NULL;
|
||||
fpNext = NULL;
|
||||
|
||||
fDamaged = fSuspicious = false;
|
||||
}
|
||||
|
||||
GenericEntry::~GenericEntry(void)
|
||||
@ -177,7 +176,7 @@ const WCHAR* GenericEntry::GetFileTypeString(void) const
|
||||
}
|
||||
|
||||
/*static*/ bool GenericEntry::CheckHighASCII(const uint8_t* buffer,
|
||||
unsigned long count)
|
||||
size_t count)
|
||||
{
|
||||
/*
|
||||
* (Pulled from NufxLib Funnel.c.)
|
||||
@ -254,7 +253,7 @@ static const char kCharLF = '\n';
|
||||
static const char kCharCR = '\r';
|
||||
|
||||
/*static*/ GenericEntry::ConvertEOL GenericEntry::DetermineConversion(
|
||||
const uint8_t* buffer, long count,
|
||||
const uint8_t* buffer, size_t count,
|
||||
EOLType* pSourceType, ConvertHighASCII* pConvHA)
|
||||
{
|
||||
/*
|
||||
@ -277,7 +276,7 @@ static const char kCharCR = '\r';
|
||||
* it will be stripped *before* the EOL determination is made.
|
||||
*/
|
||||
ConvertHighASCII wantConvHA = *pConvHA;
|
||||
long bufCount, numBinary, numLF, numCR;
|
||||
size_t bufCount, numBinary, numLF, numCR;
|
||||
bool isHighASCII;
|
||||
uint8_t val;
|
||||
|
||||
@ -367,7 +366,7 @@ static inline void PutEOL(FILE* fp)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
LOGI("+++ WriteConvert conv=%d convHA=%d", *pConv, *pConvHA);
|
||||
LOGD("+++ WriteConvert conv=%d convHA=%d", *pConv, *pConvHA);
|
||||
|
||||
if (len == 0) {
|
||||
LOGI("WriteConvert asked to write 0 bytes; returning");
|
||||
@ -396,7 +395,7 @@ static inline void PutEOL(FILE* fp)
|
||||
*pConvHA = kConvertHAOff;
|
||||
}
|
||||
}
|
||||
LOGI("+++ After auto, conv=%d convHA=%d", *pConv, *pConvHA);
|
||||
LOGD("+++ After auto, conv=%d convHA=%d", *pConv, *pConvHA);
|
||||
ASSERT(*pConv == kConvertEOLOn || *pConv == kConvertEOLOff);
|
||||
ASSERT(*pConvHA == kConvertHAOn || *pConvHA == kConvertHAOff);
|
||||
|
||||
@ -404,7 +403,7 @@ static inline void PutEOL(FILE* fp)
|
||||
if (*pConv == kConvertEOLOff) {
|
||||
if (fwrite(buf, len, 1, fp) != 1) {
|
||||
err = errno;
|
||||
LOGI("WriteConvert failed, err=%d", errno);
|
||||
LOGE("WriteConvert failed, err=%d", errno);
|
||||
}
|
||||
} else {
|
||||
ASSERT(*pConv == kConvertEOLOn);
|
||||
@ -546,7 +545,7 @@ GenericArchive::CreateIndex(void)
|
||||
mangle.Delete(idx+1, len-(idx+1)); /* delete out to the end */
|
||||
mangle += kTmpTemplate;
|
||||
}
|
||||
LOGI("GenDerived: passed '%ls' returned '%ls'", filename, (LPCWSTR) mangle);
|
||||
LOGD("GenDerived: passed '%ls' returned '%ls'", filename, (LPCWSTR) mangle);
|
||||
|
||||
return mangle;
|
||||
}
|
||||
@ -594,13 +593,9 @@ GenericArchive::CreateIndex(void)
|
||||
*/
|
||||
|
||||
/*
|
||||
* This comes straight out of NuLib2, and uses NufxLib data structures. While
|
||||
* it may seem strange to use these structures for non-NuFX archives, they are
|
||||
* convenient and hold at least as much information as any other format needs.
|
||||
* Much of this was adapted from NuLib2.
|
||||
*/
|
||||
|
||||
typedef bool Boolean;
|
||||
|
||||
/*static*/ void GenericArchive::UNIXTimeToDateTime(const time_t* pWhen,
|
||||
NuDateTime* pDateTime)
|
||||
{
|
||||
@ -625,99 +620,6 @@ typedef bool Boolean;
|
||||
pDateTime->weekDay = ptm->tm_wday +1;
|
||||
}
|
||||
|
||||
NuError GenericArchive::GetFileDetails(const AddFilesDialog* pAddOpts,
|
||||
const WCHAR* pathname, struct _stat* psb, FileDetails* pDetails)
|
||||
{
|
||||
//char* livePathStr;
|
||||
time_t now;
|
||||
|
||||
ASSERT(pAddOpts != NULL);
|
||||
ASSERT(pathname != NULL);
|
||||
ASSERT(pDetails != NULL);
|
||||
|
||||
/* init to defaults */
|
||||
//pDetails->threadID = kNuThreadIDDataFork;
|
||||
pDetails->entryKind = FileDetails::kFileKindDataFork;
|
||||
//pDetails->fileSysID = kNuFileSysUnknown;
|
||||
pDetails->fileSysFmt = DiskImg::kFormatUnknown;
|
||||
pDetails->fileSysInfo = PathProposal::kDefaultStoredFssep;
|
||||
pDetails->fileType = 0;
|
||||
pDetails->extraType = 0;
|
||||
pDetails->storageType = kNuStorageUnknown; /* let NufxLib worry about it */
|
||||
if (psb->st_mode & S_IWUSR)
|
||||
pDetails->access = kNuAccessUnlocked;
|
||||
else
|
||||
pDetails->access = kNuAccessLocked;
|
||||
|
||||
#if 0
|
||||
/* if this is a disk image, fill in disk-specific fields */
|
||||
if (NState_GetModAddAsDisk(pState)) {
|
||||
if ((psb->st_size & 0x1ff) != 0) {
|
||||
/* reject anything whose size isn't a multiple of 512 bytes */
|
||||
printf("NOT storing odd-sized (%ld) file as disk image: %ls\n",
|
||||
(long)psb->st_size, livePathStr);
|
||||
} else {
|
||||
/* set fields; note the "preserve" stuff can override this */
|
||||
pDetails->threadID = kNuThreadIDDiskImage;
|
||||
pDetails->storageType = 512;
|
||||
pDetails->extraType = psb->st_size / 512;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
now = time(NULL);
|
||||
UNIXTimeToDateTime(&now, &pDetails->archiveWhen);
|
||||
UNIXTimeToDateTime(&psb->st_mtime, &pDetails->modWhen);
|
||||
UNIXTimeToDateTime(&psb->st_ctime, &pDetails->createWhen);
|
||||
|
||||
/* get adjusted filename, along with any preserved type info */
|
||||
PathProposal pathProp;
|
||||
pathProp.Init(pathname);
|
||||
pathProp.LocalToArchive(pAddOpts);
|
||||
|
||||
/* set up the local and archived pathnames */
|
||||
pDetails->storageName = "";
|
||||
if (!pAddOpts->fStoragePrefix.IsEmpty()) {
|
||||
pDetails->storageName += pAddOpts->fStoragePrefix;
|
||||
pDetails->storageName += pathProp.fStoredFssep;
|
||||
}
|
||||
pDetails->storageName += pathProp.fStoredPathName;
|
||||
|
||||
/*
|
||||
* Fill in the NuFileDetails struct.
|
||||
*
|
||||
* We use GetBuffer to get the string to ensure that the CString object
|
||||
* doesn't do anything while we're not looking. The string won't be
|
||||
* modified, and it won't be around for very long, so it's not strictly
|
||||
* necessary to do this. It is, however, the correct approach.
|
||||
*/
|
||||
pDetails->origName = pathname;
|
||||
pDetails->fileSysInfo = pathProp.fStoredFssep;
|
||||
pDetails->fileType = pathProp.fFileType;
|
||||
pDetails->extraType = pathProp.fAuxType;
|
||||
switch (pathProp.fThreadKind) {
|
||||
case GenericEntry::kDataThread:
|
||||
//pDetails->threadID = kNuThreadIDDataFork;
|
||||
pDetails->entryKind = FileDetails::kFileKindDataFork;
|
||||
break;
|
||||
case GenericEntry::kRsrcThread:
|
||||
//pDetails->threadID = kNuThreadIDRsrcFork;
|
||||
pDetails->entryKind = FileDetails::kFileKindRsrcFork;
|
||||
break;
|
||||
case GenericEntry::kDiskImageThread:
|
||||
//pDetails->threadID = kNuThreadIDDiskImage;
|
||||
pDetails->entryKind = FileDetails::kFileKindDiskImage;
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
// was initialized to default earlier
|
||||
break;
|
||||
}
|
||||
|
||||
/*bail:*/
|
||||
return kNuErrNone;
|
||||
}
|
||||
|
||||
/*
|
||||
* Directory structure and functions, based on zDIR in Info-Zip sources.
|
||||
*/
|
||||
@ -864,8 +766,8 @@ NuError GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts,
|
||||
const WCHAR* pathname, CString* pErrMsg)
|
||||
{
|
||||
NuError err = kNuErrNone;
|
||||
Boolean exists, isDir, isReadable;
|
||||
FileDetails details;
|
||||
bool exists, isDir, isReadable;
|
||||
LocalFileDetails details;
|
||||
struct _stat sb;
|
||||
|
||||
ASSERT(pAddOpts != NULL);
|
||||
@ -901,18 +803,16 @@ NuError GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts,
|
||||
* filetype and auxtype it has, and whether or not it's actually the
|
||||
* resource fork of another file.
|
||||
*/
|
||||
LOGI("+++ ADD '%ls'", pathname);
|
||||
LOGD("+++ ADD '%ls'", pathname);
|
||||
|
||||
/*
|
||||
* Fill out the "details" structure. The class has an automatic
|
||||
* conversion to NuFileDetails, but it relies on the CString storage
|
||||
* in the FileDetails, so be careful how you use it.
|
||||
* Fill out the "details" structure.
|
||||
*/
|
||||
err = GetFileDetails(pAddOpts, pathname, &sb, &details);
|
||||
err = details.SetFields(pAddOpts, pathname, &sb);
|
||||
if (err != kNuErrNone)
|
||||
goto bail;
|
||||
|
||||
assert(wcscmp(pathname, details.origName) == 0);
|
||||
assert(wcscmp(pathname, details.GetLocalPathName()) == 0);
|
||||
err = DoAddFile(pAddOpts, &details);
|
||||
if (err == kNuErrSkipped) // ignore "skipped" result
|
||||
err = kNuErrNone;
|
||||
@ -934,80 +834,144 @@ NuError GenericArchive::AddFile(const AddFilesDialog* pAddOpts,
|
||||
return Win32AddFile(pAddOpts, pathname, pErrMsg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* GenericArchive::FileDetails
|
||||
* ===========================================================================
|
||||
*/
|
||||
|
||||
GenericArchive::FileDetails::FileDetails(void)
|
||||
GenericArchive::LocalFileDetails::LocalFileDetails(void)
|
||||
: fEntryKind(kFileKindUnknown),
|
||||
fFileSysFmt(DiskImg::kFormatUnknown),
|
||||
fFssep('\0'),
|
||||
fFileType(0),
|
||||
fExtraType(0),
|
||||
fAccess(0),
|
||||
fStorageType(0)
|
||||
{
|
||||
//threadID = 0;
|
||||
entryKind = kFileKindUnknown;
|
||||
fileSysFmt = DiskImg::kFormatUnknown;
|
||||
fileSysInfo = storageType = 0;
|
||||
access = fileType = extraType = 0;
|
||||
memset(&createWhen, 0, sizeof(createWhen));
|
||||
memset(&modWhen, 0, sizeof(modWhen));
|
||||
memset(&archiveWhen, 0, sizeof(archiveWhen));
|
||||
memset(&fCreateWhen, 0, sizeof(fCreateWhen));
|
||||
memset(&fModWhen, 0, sizeof(fModWhen));
|
||||
memset(&fArchiveWhen, 0, sizeof(fArchiveWhen));
|
||||
|
||||
// set these for debugging
|
||||
memset(&fNuFileDetails, 0xcc, sizeof(fNuFileDetails));
|
||||
memset(&fCreateParms, 0xcc, sizeof(&fCreateParms));
|
||||
fStoragePathNameMOR = "!INIT!";
|
||||
}
|
||||
|
||||
GenericArchive::FileDetails::operator const NuFileDetails() const
|
||||
NuError GenericArchive::LocalFileDetails::SetFields(const AddFilesDialog* pAddOpts,
|
||||
const WCHAR* pathname, struct _stat* psb)
|
||||
{
|
||||
NuFileDetails details;
|
||||
time_t now;
|
||||
|
||||
ASSERT(pAddOpts != NULL);
|
||||
ASSERT(pathname != NULL);
|
||||
|
||||
/* get adjusted filename, along with any preserved type info */
|
||||
PathProposal pathProp;
|
||||
pathProp.Init(pathname);
|
||||
pathProp.LocalToArchive(pAddOpts);
|
||||
|
||||
/* set up the local and archived pathnames */
|
||||
fLocalPathName = pathname;
|
||||
fStrippedLocalPathName = L"";
|
||||
if (!pAddOpts->fStoragePrefix.IsEmpty()) {
|
||||
fStrippedLocalPathName += pAddOpts->fStoragePrefix;
|
||||
fStrippedLocalPathName += pathProp.fStoredFssep;
|
||||
}
|
||||
fStrippedLocalPathName += pathProp.fStoredPathName;
|
||||
GenerateStoragePathName();
|
||||
|
||||
fFileSysFmt = DiskImg::kFormatUnknown;
|
||||
fStorageType = kNuStorageUnknown; /* let NufxLib et.al. worry about it */
|
||||
if (psb->st_mode & S_IWUSR)
|
||||
fAccess = kNuAccessUnlocked;
|
||||
else
|
||||
fAccess = kNuAccessLocked;
|
||||
fEntryKind = LocalFileDetails::kFileKindDataFork;
|
||||
fFssep = pathProp.fStoredFssep;
|
||||
fFileType = pathProp.fFileType;
|
||||
fExtraType = pathProp.fAuxType;
|
||||
|
||||
#if 0
|
||||
/* if this is a disk image, fill in disk-specific fields */
|
||||
if (NState_GetModAddAsDisk(pState)) {
|
||||
if ((psb->st_size & 0x1ff) != 0) {
|
||||
/* reject anything whose size isn't a multiple of 512 bytes */
|
||||
printf("NOT storing odd-sized (%ld) file as disk image: %ls\n",
|
||||
(long)psb->st_size, livePathStr);
|
||||
} else {
|
||||
/* set fields; note the "preserve" stuff can override this */
|
||||
pDetails->threadID = kNuThreadIDDiskImage;
|
||||
pDetails->storageType = 512;
|
||||
pDetails->extraType = psb->st_size / 512;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
now = time(NULL);
|
||||
UNIXTimeToDateTime(&now, &fArchiveWhen);
|
||||
UNIXTimeToDateTime(&psb->st_mtime, &fModWhen);
|
||||
UNIXTimeToDateTime(&psb->st_ctime, &fCreateWhen);
|
||||
|
||||
switch (pathProp.fThreadKind) {
|
||||
case GenericEntry::kDataThread:
|
||||
//pDetails->threadID = kNuThreadIDDataFork;
|
||||
fEntryKind = LocalFileDetails::kFileKindDataFork;
|
||||
break;
|
||||
case GenericEntry::kRsrcThread:
|
||||
//pDetails->threadID = kNuThreadIDRsrcFork;
|
||||
fEntryKind = LocalFileDetails::kFileKindRsrcFork;
|
||||
break;
|
||||
case GenericEntry::kDiskImageThread:
|
||||
//pDetails->threadID = kNuThreadIDDiskImage;
|
||||
fEntryKind = LocalFileDetails::kFileKindDiskImage;
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
// was initialized to default earlier
|
||||
break;
|
||||
}
|
||||
|
||||
/*bail:*/
|
||||
return kNuErrNone;
|
||||
}
|
||||
|
||||
const NuFileDetails& GenericArchive::LocalFileDetails::GetNuFileDetails()
|
||||
{
|
||||
//details.threadID = threadID;
|
||||
switch (entryKind) {
|
||||
switch (fEntryKind) {
|
||||
case kFileKindDataFork:
|
||||
details.threadID = kNuThreadIDDataFork;
|
||||
fNuFileDetails.threadID = kNuThreadIDDataFork;
|
||||
break;
|
||||
case kFileKindBothForks: // not exactly supported, doesn't really matter
|
||||
case kFileKindRsrcFork:
|
||||
details.threadID = kNuThreadIDRsrcFork;
|
||||
fNuFileDetails.threadID = kNuThreadIDRsrcFork;
|
||||
break;
|
||||
case kFileKindDiskImage:
|
||||
details.threadID = kNuThreadIDDiskImage;
|
||||
fNuFileDetails.threadID = kNuThreadIDDiskImage;
|
||||
break;
|
||||
case kFileKindDirectory:
|
||||
default:
|
||||
LOGW("Invalid entryKind (%d) for NuFileDetails conversion",
|
||||
entryKind);
|
||||
LOGW("Invalid entryKind (%d) for NuFileDetails conversion", fEntryKind);
|
||||
ASSERT(false);
|
||||
details.threadID = 0; // that makes it an old-style comment?!
|
||||
fNuFileDetails.threadID = 0; // that makes it an old-style comment?!
|
||||
break;
|
||||
}
|
||||
|
||||
// The NuFileDetails origName field was added to NufxLib so that CiderPress
|
||||
// could receive the original Windows pathname in the NuErrorStatus
|
||||
// callback. This is a fairly compelling argument for making origName
|
||||
// an opaque field.
|
||||
//
|
||||
// Whatever the case, we need to pass narrow-string versions of the
|
||||
// names into NufxLib via the NuFileDetails struct. Since we're returning
|
||||
// the NuFileDetails struct, the strings will out-live the scope of this
|
||||
// function, so we have to store them somewhere else. There's nowhere
|
||||
// in NuFileDetails, so we have to keep them in FileDetails, which currently
|
||||
// exposes all of its fields publicly. I'm going to kluge it for now and
|
||||
// just "snapshot" the wide strings into narrow-string fields. This is
|
||||
// all pretty rickety and needs to be redone.
|
||||
fNuFileDetails.origName = (LPCWSTR) fLocalPathName;
|
||||
fNuFileDetails.storageNameMOR = (LPCSTR) fStoragePathNameMOR;
|
||||
fNuFileDetails.fileSysInfo = fFssep;
|
||||
fNuFileDetails.access = fAccess;
|
||||
fNuFileDetails.fileType = fFileType;
|
||||
fNuFileDetails.extraType = fExtraType;
|
||||
fNuFileDetails.storageType = fStorageType;
|
||||
fNuFileDetails.createWhen = fCreateWhen;
|
||||
fNuFileDetails.modWhen = fModWhen;
|
||||
fNuFileDetails.archiveWhen = fArchiveWhen;
|
||||
|
||||
// TODO: make origName an opaque (void*) field in NufxLib and pass the
|
||||
// wide char representation through
|
||||
fOrigNameA = origName;
|
||||
details.origName = fOrigNameA;
|
||||
fStorageNameA = storageName;
|
||||
details.storageNameMOR = fStorageNameA;
|
||||
//details.fileSysID = fileSysID;
|
||||
details.fileSysInfo = fileSysInfo;
|
||||
details.access = access;
|
||||
details.fileType = fileType;
|
||||
details.extraType = extraType;
|
||||
details.storageType = storageType;
|
||||
details.createWhen = createWhen;
|
||||
details.modWhen = modWhen;
|
||||
details.archiveWhen = archiveWhen;
|
||||
|
||||
switch (fileSysFmt) {
|
||||
switch (fFileSysFmt) {
|
||||
case DiskImg::kFormatProDOS:
|
||||
case DiskImg::kFormatDOS33:
|
||||
case DiskImg::kFormatDOS32:
|
||||
@ -1021,42 +985,65 @@ GenericArchive::FileDetails::operator const NuFileDetails() const
|
||||
//kFormatHighSierra
|
||||
case DiskImg::kFormatISO9660:
|
||||
/* these map directly */
|
||||
details.fileSysID = (enum NuFileSysID) fileSysFmt;
|
||||
fNuFileDetails.fileSysID = (enum NuFileSysID) fFileSysFmt;
|
||||
break;
|
||||
|
||||
case DiskImg::kFormatRDOS33:
|
||||
case DiskImg::kFormatRDOS32:
|
||||
case DiskImg::kFormatRDOS3:
|
||||
/* these look like DOS33, e.g. text is high-ASCII */
|
||||
details.fileSysID = kNuFileSysDOS33;
|
||||
fNuFileDetails.fileSysID = kNuFileSysDOS33;
|
||||
break;
|
||||
|
||||
default:
|
||||
details.fileSysID = kNuFileSysUnknown;
|
||||
fNuFileDetails.fileSysID = kNuFileSysUnknown;
|
||||
break;
|
||||
}
|
||||
|
||||
// Return stack copy, which copies into compiler temporary with our
|
||||
// copy constructor.
|
||||
return details;
|
||||
return fNuFileDetails;
|
||||
}
|
||||
|
||||
/*static*/ void GenericArchive::FileDetails::CopyFields(FileDetails* pDst,
|
||||
const FileDetails* pSrc)
|
||||
const DiskFS::CreateParms& GenericArchive::LocalFileDetails::GetCreateParms()
|
||||
{
|
||||
//pDst->threadID = pSrc->threadID;
|
||||
pDst->entryKind = pSrc->entryKind;
|
||||
pDst->origName = pSrc->origName;
|
||||
pDst->storageName = pSrc->storageName;
|
||||
pDst->fileSysFmt = pSrc->fileSysFmt;
|
||||
pDst->fileSysInfo = pSrc->fileSysInfo;
|
||||
pDst->access = pSrc->access;
|
||||
pDst->fileType = pSrc->fileType;
|
||||
pDst->extraType = pSrc->extraType;
|
||||
pDst->storageType = pSrc->storageType;
|
||||
pDst->createWhen = pSrc->createWhen;
|
||||
pDst->modWhen = pSrc->modWhen;
|
||||
pDst->archiveWhen = pSrc->archiveWhen;
|
||||
fCreateParms.pathName = (LPCSTR) fStoragePathNameMOR;
|
||||
fCreateParms.fssep = fFssep;
|
||||
fCreateParms.storageType = fStorageType;
|
||||
fCreateParms.fileType = fFileType;
|
||||
fCreateParms.auxType = fExtraType;
|
||||
fCreateParms.access = fAccess;
|
||||
fCreateParms.createWhen = NufxArchive::DateTimeToSeconds(&fCreateWhen);
|
||||
fCreateParms.modWhen = NufxArchive::DateTimeToSeconds(&fModWhen);
|
||||
return fCreateParms;
|
||||
}
|
||||
|
||||
void GenericArchive::LocalFileDetails::GenerateStoragePathName()
|
||||
{
|
||||
// TODO(Unicode): generate MOR name from Unicode, instead of just
|
||||
// doing a generic CP-1252 conversion. We need to do this on both
|
||||
// sides though, so until we can extract MOR->Unicode we don't
|
||||
// want to add Unicode->MOR. And it all depends on NufxLib and
|
||||
// DiskImg being able to handle UTF-16 filenames.
|
||||
fStoragePathNameMOR = fStrippedLocalPathName;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void GenericArchive::LocalFileDetails::CopyFields(LocalFileDetails* pDst,
|
||||
const LocalFileDetails* pSrc)
|
||||
{
|
||||
// don't copy fNuFileDetails, fCreateParms
|
||||
pDst->fEntryKind = pSrc->fEntryKind;
|
||||
pDst->fLocalPathName = pSrc->fLocalPathName;
|
||||
pDst->fStrippedLocalPathName = pSrc->fStrippedLocalPathName;
|
||||
pDst->fStoragePathNameMOR = pSrc->fStoragePathNameMOR;
|
||||
pDst->fFileSysFmt = pSrc->fFileSysFmt;
|
||||
pDst->fFssep = pSrc->fFssep;
|
||||
pDst->fAccess = pSrc->fAccess;
|
||||
pDst->fFileType = pSrc->fFileType;
|
||||
pDst->fExtraType = pSrc->fExtraType;
|
||||
pDst->fStorageType = pSrc->fStorageType;
|
||||
pDst->fCreateWhen = pSrc->fCreateWhen;
|
||||
pDst->fModWhen = pSrc->fModWhen;
|
||||
pDst->fArchiveWhen = pSrc->fArchiveWhen;
|
||||
}
|
||||
|
||||
|
||||
@ -1073,7 +1060,7 @@ void SelectionSet::CreateFromSelection(ContentList* pContentList, int threadMask
|
||||
* (at least under Win2K), which is a good thing. It appears that, if you
|
||||
* just grab indices 0..N, you will get them in order.
|
||||
*/
|
||||
LOGI("CreateFromSelection (threadMask=0x%02x)", threadMask);
|
||||
LOGD("CreateFromSelection (threadMask=0x%02x)", threadMask);
|
||||
|
||||
POSITION posn;
|
||||
posn = pContentList->GetFirstSelectedItemPosition();
|
||||
@ -1090,7 +1077,7 @@ void SelectionSet::CreateFromSelection(ContentList* pContentList, int threadMask
|
||||
|
||||
void SelectionSet::CreateFromAll(ContentList* pContentList, int threadMask)
|
||||
{
|
||||
LOGI("CreateFromAll (threadMask=0x%02x)", threadMask);
|
||||
LOGD("CreateFromAll (threadMask=0x%02x)", threadMask);
|
||||
|
||||
int count = pContentList->GetItemCount();
|
||||
for (int idx = 0; idx < count; idx++) {
|
||||
@ -1104,7 +1091,7 @@ void SelectionSet::AddToSet(GenericEntry* pEntry, int threadMask)
|
||||
{
|
||||
SelectionEntry* pSelEntry;
|
||||
|
||||
//LOGI(" Sel '%ls'", pEntry->GetPathName());
|
||||
LOGV(" Sel '%ls'", pEntry->GetPathName());
|
||||
|
||||
if (!(threadMask & GenericEntry::kAllowVolumeDir) &&
|
||||
pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir)
|
||||
@ -1173,7 +1160,7 @@ void SelectionSet::DeleteEntries(void)
|
||||
SelectionEntry* pEntry;
|
||||
SelectionEntry* pNext;
|
||||
|
||||
LOGI("Deleting selection entries");
|
||||
LOGD("Deleting selection entries");
|
||||
|
||||
pEntry = GetEntries();
|
||||
while (pEntry != NULL) {
|
||||
|
@ -221,7 +221,6 @@ public:
|
||||
LONGLONG GetUncompressedLen(void) const {
|
||||
return fDataForkLen + fRsrcForkLen;
|
||||
}
|
||||
//void SetUncompressedLen(LONGLONG len) { fUncompressedLen = len; }
|
||||
LONGLONG GetDataForkLen(void) const { return fDataForkLen; }
|
||||
void SetDataForkLen(LONGLONG len) { fDataForkLen = len; }
|
||||
LONGLONG GetRsrcForkLen(void) const { return fRsrcForkLen; }
|
||||
@ -259,8 +258,7 @@ public:
|
||||
/*
|
||||
* Check to see if this is a high-ASCII file.
|
||||
*/
|
||||
static bool CheckHighASCII(const uint8_t* buffer,
|
||||
unsigned long count);
|
||||
static bool CheckHighASCII(const uint8_t* buffer, size_t count);
|
||||
|
||||
/*
|
||||
* Decide, based on the contents of the buffer, whether we should do an
|
||||
@ -269,7 +267,7 @@ public:
|
||||
* Returns kConvEOLOff or kConvEOLOn.
|
||||
*/
|
||||
static ConvertEOL DetermineConversion(const uint8_t* buffer,
|
||||
long count, EOLType* pSourceType, ConvertHighASCII* pConvHA);
|
||||
size_t count, EOLType* pSourceType, ConvertHighASCII* pConvHA);
|
||||
|
||||
/*
|
||||
* Write data to a file, possibly converting EOL markers to Windows CRLF
|
||||
@ -309,7 +307,6 @@ private:
|
||||
time_t fModWhen;
|
||||
RecordKind fRecordKind; // forked file, disk image, ??
|
||||
const WCHAR* fFormatStr; // static str; compression or fs format
|
||||
//LONGLONG fUncompressedLen;
|
||||
LONGLONG fDataForkLen; // also for disk images
|
||||
LONGLONG fRsrcForkLen; // set to 0 when nonexistent
|
||||
LONGLONG fCompressedLen; // data/disk + rsrc
|
||||
@ -515,111 +512,202 @@ public:
|
||||
void AddEntry(GenericEntry* pEntry);
|
||||
|
||||
/*
|
||||
* This class holds details about a file that we're adding.
|
||||
*
|
||||
* It's based on the NuFileDetails class from NufxLib (which used to be
|
||||
* used everywhere).
|
||||
*
|
||||
* TODO: fix this
|
||||
* TODO: may want to hold a raw 8-bit pathname for copy & paste, which
|
||||
* doesn't need to convert in and out of MacRoman
|
||||
* This class holds details about a file that we're adding from local disk.
|
||||
*/
|
||||
class FileDetails {
|
||||
class LocalFileDetails {
|
||||
public:
|
||||
FileDetails(void);
|
||||
virtual ~FileDetails(void) {}
|
||||
LocalFileDetails(void);
|
||||
virtual ~LocalFileDetails(void) {}
|
||||
|
||||
/*
|
||||
* Automatic cast to NuFileDetails. The NuFileDetails structure will
|
||||
* have a pointer to at least one of our strings, so structures
|
||||
* filled out this way need to be short-lived. (Yes, this is
|
||||
* annoying, but it's how NufxLib works.)
|
||||
*
|
||||
* TODO: make this a "GenNuFileDetails" method that returns a
|
||||
* NuFileDetails struct owned by this object. It fills out the
|
||||
* fields and references internal strings. Because we own the
|
||||
* object, we can control the lifespan of the NuFileDetails, and
|
||||
* ensure it doesn't live longer than our strings.
|
||||
* Set the various fields, based on the pathname and characteristics
|
||||
* of the file.
|
||||
*/
|
||||
operator const NuFileDetails() const;
|
||||
|
||||
/*
|
||||
* Provide operator= and copy constructor. This'd be easier without
|
||||
* the strings.
|
||||
*/
|
||||
FileDetails& operator=(const FileDetails& src) {
|
||||
if (&src != this)
|
||||
CopyFields(this, &src);
|
||||
return *this;
|
||||
}
|
||||
FileDetails(const FileDetails& src) {
|
||||
CopyFields(this, &src);
|
||||
}
|
||||
NuError SetFields(const AddFilesDialog* pAddOpts, const WCHAR* pathname,
|
||||
struct _stat* psb);
|
||||
|
||||
/*
|
||||
* What kind of file this is. Files being added to NuFX from Windows
|
||||
* can't be "BothForks" because each the forks are stored in
|
||||
* can't be "BothForks" because the forks are stored in
|
||||
* separate files. However, files being transferred from a NuFX
|
||||
* archive, a disk image, or in from the clipboard can be both.
|
||||
*
|
||||
* (NOTE: this gets embedded into clipboard data. If you change
|
||||
* these values, update the version info in Clipboard.cpp.)
|
||||
* these values, update the version number in Clipboard.cpp.)
|
||||
*/
|
||||
typedef enum FileKind {
|
||||
enum FileKind {
|
||||
kFileKindUnknown = 0,
|
||||
kFileKindDataFork,
|
||||
kFileKindRsrcFork,
|
||||
kFileKindDiskImage,
|
||||
kFileKindBothForks,
|
||||
kFileKindDirectory,
|
||||
} FileKind;
|
||||
};
|
||||
|
||||
/*
|
||||
* Data fields. While transitioning from general use of NuFileDetails
|
||||
* (v1.2.x to v2.0) I'm just going to leave these public.
|
||||
* TODO: make this not public
|
||||
* Provide operator= and copy constructor.
|
||||
*/
|
||||
|
||||
//NuThreadID threadID; /* data, rsrc, disk img? */
|
||||
FileKind entryKind;
|
||||
LocalFileDetails& operator=(const LocalFileDetails& src) {
|
||||
if (&src != this)
|
||||
CopyFields(this, &src);
|
||||
return *this;
|
||||
}
|
||||
LocalFileDetails(const LocalFileDetails& src) {
|
||||
CopyFields(this, &src);
|
||||
}
|
||||
|
||||
/*
|
||||
* Original full pathname as found on Windows.
|
||||
*/
|
||||
CString origName;
|
||||
|
||||
/*
|
||||
* "Normalized" pathname. This is the full path with any of our
|
||||
* added bits removed (e.g. file type & fork identifiers). It has
|
||||
* not been sanitized for any specific target filesystem.
|
||||
* Returns a reference to a NuFileDetails structure with the contents
|
||||
* of the LocalFileDetails.
|
||||
*
|
||||
* This is generated by PathProposal::LocalToArchive().
|
||||
* The returned structure may not be used after the LocalFileDetails
|
||||
* is modified or released.
|
||||
*/
|
||||
CString storageName;
|
||||
const NuFileDetails& GetNuFileDetails();
|
||||
|
||||
//NuFileSysID fileSysID;
|
||||
DiskImg::FSFormat fileSysFmt;
|
||||
uint16_t fileSysInfo; /* fssep lurks here */
|
||||
uint32_t access;
|
||||
uint32_t fileType;
|
||||
uint32_t extraType;
|
||||
uint16_t storageType; /* "Unknown" or disk block size */
|
||||
NuDateTime createWhen;
|
||||
NuDateTime modWhen;
|
||||
NuDateTime archiveWhen;
|
||||
/*
|
||||
* Returns a reference to a NuFileDetails structure with the contents
|
||||
* of the LocalFileDetails.
|
||||
*
|
||||
* The returned structure may not be used after the LocalFileDetails
|
||||
* is modified or released.
|
||||
*/
|
||||
const DiskFS::CreateParms& GetCreateParms();
|
||||
|
||||
/*
|
||||
* Returns the "local" pathname, i.e. the name of a Windows file.
|
||||
*/
|
||||
const CString& GetLocalPathName() const {
|
||||
return fLocalPathName;
|
||||
}
|
||||
|
||||
// temporary kluge to get things working
|
||||
mutable CStringA fOrigNameA;
|
||||
mutable CStringA fStorageNameA;
|
||||
void SetLocalPathName(const CString& newName) {
|
||||
fLocalPathName = newName;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the "local" pathname with any file type preservation
|
||||
* strings removed.
|
||||
*/
|
||||
const CString& GetStrippedLocalPathName() const {
|
||||
return fStrippedLocalPathName;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the "stripped" local path name, and updates the MOR version.
|
||||
* Does not alter the full local path name.
|
||||
*/
|
||||
void SetStrippedLocalPathName(const CString& newName) {
|
||||
fStrippedLocalPathName = newName;
|
||||
GenerateStoragePathName();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a copy of the stripped local pathname that has been
|
||||
* converted to Mac OS Roman.
|
||||
*
|
||||
* The returned string will be invalid if SetStrippedLocalPathName
|
||||
* is subsequently called.
|
||||
*/
|
||||
const CStringA& GetStoragePathNameMOR() const {
|
||||
return fStoragePathNameMOR;
|
||||
}
|
||||
|
||||
FileKind GetEntryKind() const { return fEntryKind; }
|
||||
void SetEntryKind(FileKind kind) { fEntryKind = kind; }
|
||||
|
||||
DiskImg::FSFormat GetFileSysFmt() const { return fFileSysFmt; }
|
||||
void SetFileSysFmt(DiskImg::FSFormat fmt) { fFileSysFmt = fmt; }
|
||||
|
||||
char GetFssep() const { return fFssep; }
|
||||
void SetFssep(char fssep) { fFssep = fssep; }
|
||||
|
||||
uint32_t GetFileType() const { return fFileType; }
|
||||
void SetFileType(uint32_t type) { fFileType = type; }
|
||||
|
||||
uint32_t GetExtraType() const { return fExtraType; }
|
||||
void SetExtraType(uint32_t type) { fExtraType = type; }
|
||||
|
||||
uint32_t GetAccess() const { return fAccess; }
|
||||
void SetAccess(uint32_t access) { fAccess = access; }
|
||||
|
||||
uint16_t GetStorageType() const { return fStorageType; }
|
||||
void SetStorageType(uint16_t type) { fStorageType = type; }
|
||||
|
||||
const NuDateTime& GetArchiveWhen() const { return fArchiveWhen; }
|
||||
void SetArchiveWhen(const NuDateTime& when) { fArchiveWhen = when; }
|
||||
|
||||
const NuDateTime& GetModWhen() const { return fModWhen; }
|
||||
void SetModWhen(const NuDateTime& when) { fModWhen = when; }
|
||||
|
||||
const NuDateTime& GetCreateWhen() const { return fCreateWhen; }
|
||||
void SetCreateWhen(const NuDateTime& when) { fCreateWhen = when; }
|
||||
|
||||
private:
|
||||
/*
|
||||
* Copy the contents of our object to a new object.
|
||||
* Copy the contents of our object to a new object, field by field,
|
||||
* so the CStrings copy correctly.
|
||||
*
|
||||
* Useful for operator= and copy construction.
|
||||
*/
|
||||
static void CopyFields(FileDetails* pDst, const FileDetails* pSrc);
|
||||
static void CopyFields(LocalFileDetails* pDst,
|
||||
const LocalFileDetails* pSrc);
|
||||
|
||||
/*
|
||||
* Generates fStoragePathNameMOR from fStrippedLocalPathName.
|
||||
*/
|
||||
void GenerateStoragePathName();
|
||||
|
||||
/*
|
||||
* These are the structs that the libraries want, so we provide calls
|
||||
* that populate them and return a reference. These are "hairy"
|
||||
* structures, so we have to place limitations on their lifetime.
|
||||
*
|
||||
* Ideally these would either be proper objects with destructors,
|
||||
* so we could create them and not worry about who will free up the
|
||||
* hairy bits, or we would omit the hairy bits from the structure and
|
||||
* pass them separately.
|
||||
*/
|
||||
NuFileDetails fNuFileDetails;
|
||||
DiskFS::CreateParms fCreateParms;
|
||||
|
||||
/*
|
||||
* What does this entry represent?
|
||||
*/
|
||||
FileKind fEntryKind;
|
||||
|
||||
/*
|
||||
* Full pathname of the Windows file.
|
||||
*/
|
||||
CString fLocalPathName; // was origName
|
||||
|
||||
/*
|
||||
* "Stripped" pathname. This is the full path with any of our
|
||||
* added bits removed (e.g. file type & fork identifiers).
|
||||
*
|
||||
* This is generated by PathProposal::LocalToArchive().
|
||||
*/
|
||||
CString fStrippedLocalPathName; // was storageName
|
||||
|
||||
/*
|
||||
* The storage name, generated by converting strippedLocalPathName
|
||||
* from Unicode to Mac OS Roman. This is what will be passed to
|
||||
* DiskImg and NufxLib as the name of the file to create in the
|
||||
* archive. For DiskImg there's an additional "normalization" pass
|
||||
* make the filename suitable for use on the filesystem; that name
|
||||
* is stored in a FileAddData object, not here.
|
||||
*/
|
||||
CStringA fStoragePathNameMOR;
|
||||
|
||||
DiskImg::FSFormat fFileSysFmt; // only set for xfers?
|
||||
uint8_t fFssep;
|
||||
uint32_t fFileType;
|
||||
uint32_t fExtraType;
|
||||
uint32_t fAccess;
|
||||
uint16_t fStorageType; // "Unknown" or disk block size
|
||||
NuDateTime fCreateWhen;
|
||||
NuDateTime fModWhen;
|
||||
NuDateTime fArchiveWhen;
|
||||
};
|
||||
|
||||
// Prepare for file transfers.
|
||||
@ -634,7 +722,7 @@ public:
|
||||
// On success, *pDataBuf and *pRsrcBuf are freed and set to NULL. (It's
|
||||
// necessary for the interface to work this way because the NufxArchive
|
||||
// version just tucks the pointers into NufxLib structures.)
|
||||
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
||||
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) = 0;
|
||||
|
||||
// Abort progress. Not all subclasses are capable of "undo".
|
||||
@ -656,16 +744,6 @@ protected:
|
||||
|
||||
void ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst);
|
||||
|
||||
/*
|
||||
* Set the contents of a NuFileDetails structure, based on the pathname
|
||||
* and characteristics of the file.
|
||||
*
|
||||
* For efficiency and simplicity, the pathname fields are set to CStrings in
|
||||
* the GenericArchive object instead of newly-allocated storage.
|
||||
*/
|
||||
NuError GetFileDetails(const AddFilesDialog* pAddOpts, const WCHAR* pathname,
|
||||
struct _stat* psb, FileDetails* pDetails);
|
||||
|
||||
/*
|
||||
* Prepare a directory for reading.
|
||||
*
|
||||
@ -725,7 +803,7 @@ protected:
|
||||
* information in "*pDetails" may be modified.
|
||||
*/
|
||||
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
||||
FileDetails* pDetails) = 0;
|
||||
LocalFileDetails* pDetails) = 0;
|
||||
|
||||
void SetPathName(const WCHAR* pathName) {
|
||||
free(fPathName);
|
||||
|
24
app/Main.cpp
24
app/Main.cpp
@ -1844,7 +1844,7 @@ int MainWindow::LoadArchive(const WCHAR* fileName, const WCHAR* extension,
|
||||
GenericArchive::OpenResult openResult;
|
||||
int result = -1;
|
||||
GenericArchive* pOpenArchive = NULL;
|
||||
int origFilterIndex = filterIndex;
|
||||
const int origFilterIndex = filterIndex;
|
||||
CString errStr, appName;
|
||||
|
||||
CheckedLoadString(&appName, IDS_MB_APP_NAME);
|
||||
@ -1883,7 +1883,7 @@ try_again:
|
||||
if (filterIndex == kFilterIndexBinaryII) {
|
||||
/* try Binary II and nothing else */
|
||||
ASSERT(!createFile);
|
||||
LOGI(" Trying Binary II");
|
||||
LOGD(" Trying Binary II");
|
||||
pOpenArchive = new BnyArchive;
|
||||
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
||||
if (openResult != GenericArchive::kResultSuccess) {
|
||||
@ -1896,7 +1896,7 @@ try_again:
|
||||
if (filterIndex == kFilterIndexACU) {
|
||||
/* try ACU and nothing else */
|
||||
ASSERT(!createFile);
|
||||
LOGI(" Trying ACU");
|
||||
LOGD(" Trying ACU");
|
||||
pOpenArchive = new AcuArchive;
|
||||
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
||||
if (openResult != GenericArchive::kResultSuccess) {
|
||||
@ -1909,7 +1909,7 @@ try_again:
|
||||
if (filterIndex == kFilterIndexDiskImage) {
|
||||
/* try various disk image formats */
|
||||
ASSERT(!createFile);
|
||||
LOGI(" Trying disk images");
|
||||
LOGD(" Trying disk images");
|
||||
|
||||
pOpenArchive = new DiskArchive;
|
||||
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
||||
@ -1933,13 +1933,13 @@ try_again:
|
||||
}
|
||||
|
||||
} else if (openResult != GenericArchive::kResultSuccess) {
|
||||
if (filterIndex != origFilterIndex) {
|
||||
/*
|
||||
* Kluge: assume we guessed disk image and were wrong.
|
||||
*/
|
||||
errStr = L"File doesn't appear to be a valid archive"
|
||||
L" or disk image.";
|
||||
}
|
||||
//if (filterIndex != origFilterIndex) {
|
||||
// /*
|
||||
// * Kluge: assume we guessed disk image and were wrong.
|
||||
// */
|
||||
// errStr = L"File doesn't appear to be a valid archive"
|
||||
// L" or disk image.";
|
||||
//}
|
||||
if (!errStr.IsEmpty())
|
||||
ShowFailureMsg(this, errStr, IDS_FAILED);
|
||||
result = -1;
|
||||
@ -1948,7 +1948,7 @@ try_again:
|
||||
} else
|
||||
if (filterIndex == kFilterIndexNuFX) {
|
||||
/* try NuFX (including its embedded-in-BNY form) */
|
||||
LOGI(" Trying NuFX");
|
||||
LOGD(" Trying NuFX");
|
||||
|
||||
pOpenArchive = new NufxArchive;
|
||||
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
||||
|
@ -239,10 +239,10 @@ public:
|
||||
*
|
||||
* On failure, returns with an error message in errMsg.
|
||||
*/
|
||||
static bool SaveToArchive(GenericArchive::FileDetails* pDetails,
|
||||
static bool SaveToArchive(GenericArchive::LocalFileDetails* pDetails,
|
||||
const uint8_t* dataBuf, long dataLen,
|
||||
const uint8_t* rsrcBuf, long rsrcLen,
|
||||
CString& errMsg, CWnd* pDialog);
|
||||
CString* pErrMsg, CWnd* pDialog);
|
||||
|
||||
static const WCHAR kOpenNuFX[];
|
||||
static const WCHAR kOpenBinaryII[];
|
||||
|
@ -1156,51 +1156,57 @@ bail:
|
||||
}
|
||||
|
||||
NuError NufxArchive::DoAddFile(const AddFilesDialog* pAddOpts,
|
||||
FileDetails* pDetails)
|
||||
LocalFileDetails* pDetails)
|
||||
{
|
||||
NuError err;
|
||||
NuRecordIdx recordIdx = 0;
|
||||
NuFileDetails nuFileDetails;
|
||||
|
||||
retry:
|
||||
nuFileDetails = *pDetails; // stuff class contents into struct
|
||||
CStringA origNameA(pDetails->origName);
|
||||
err = NuAddFile(fpArchive, origNameA /*pathname*/,
|
||||
&nuFileDetails, false, &recordIdx);
|
||||
do {
|
||||
// Must re-get the NuFileDetails, because updating the filename for
|
||||
// rename will invalidate the previous version.
|
||||
const NuFileDetails& nuFileDetails = pDetails->GetNuFileDetails();
|
||||
|
||||
// TODO(Unicode): NufxLib should accept wide pathname
|
||||
CStringA origNameA(pDetails->GetLocalPathName());
|
||||
CString dummyMsg;
|
||||
if (!PathName::TestNarrowConversion(pDetails->GetLocalPathName(),
|
||||
origNameA, &dummyMsg)) {
|
||||
err = kNuErrInvalidFilename;
|
||||
goto bail_quiet;
|
||||
}
|
||||
err = NuAddFile(fpArchive, origNameA, &nuFileDetails, false, &recordIdx);
|
||||
|
||||
if (err == kNuErrNone) {
|
||||
fNumAdded++;
|
||||
} else if (err == kNuErrSkipped) {
|
||||
/* "maybe overwrite" UI causes this if user declines */
|
||||
// fall through with the error
|
||||
LOGI("DoAddFile: skipped '%ls'", (LPCWSTR) pDetails->origName);
|
||||
LOGI("DoAddFile: skipped '%ls'", (LPCWSTR) pDetails->GetLocalPathName());
|
||||
} else if (err == kNuErrRecordExists) {
|
||||
AddClashDialog dlg;
|
||||
|
||||
dlg.fWindowsName = pDetails->origName;
|
||||
dlg.fStorageName = pDetails->storageName;
|
||||
dlg.fWindowsName = pDetails->GetLocalPathName();
|
||||
dlg.fStorageName = pDetails->GetStrippedLocalPathName();
|
||||
if (dlg.DoModal() != IDOK) {
|
||||
err = kNuErrAborted;
|
||||
goto bail_quiet;
|
||||
}
|
||||
if (dlg.fDoRename) {
|
||||
LOGD("add clash: rename to '%ls'", (LPCWSTR) dlg.fNewName);
|
||||
pDetails->storageName = dlg.fNewName;
|
||||
goto retry;
|
||||
pDetails->SetStrippedLocalPathName(dlg.fNewName);
|
||||
continue;
|
||||
} else {
|
||||
LOGD("add clash: skip");
|
||||
err = kNuErrSkipped;
|
||||
// fall through with error
|
||||
}
|
||||
}
|
||||
//if (err != kNuErrNone)
|
||||
// goto bail;
|
||||
} while (false);
|
||||
|
||||
//bail:
|
||||
if (err != kNuErrNone && err != kNuErrAborted && err != kNuErrSkipped) {
|
||||
CString msg;
|
||||
msg.Format(L"Unable to add file '%ls': %hs.",
|
||||
(LPCWSTR) pDetails->origName, NuStrError(err));
|
||||
(LPCWSTR) pDetails->GetLocalPathName(), NuStrError(err));
|
||||
ShowFailureMsg(fpMsgWnd, msg, IDS_FAILED);
|
||||
}
|
||||
bail_quiet:
|
||||
@ -1303,7 +1309,7 @@ NuResult NufxArchive::HandleReplaceExisting(const NuErrorStatus* pErrorStatus)
|
||||
ASSERT(pErrorStatus->canOverwrite);
|
||||
ASSERT(pErrorStatus->canSkip);
|
||||
ASSERT(pErrorStatus->canAbort);
|
||||
ASSERT(!pErrorStatus->canRename);
|
||||
ASSERT(!pErrorStatus->canRename); // TODO: remember why we can't rename
|
||||
|
||||
/* no firm policy, ask the user */
|
||||
ConfirmOverwriteDialog confOvwr;
|
||||
@ -1313,8 +1319,7 @@ NuResult NufxArchive::HandleReplaceExisting(const NuErrorStatus* pErrorStatus)
|
||||
confOvwr.fExistingFileModWhen =
|
||||
DateTimeToSeconds(&pErrorStatus->pRecord->recModWhen);
|
||||
if (pErrorStatus->origPathname != NULL) {
|
||||
// TODO: use wchar_t instead for origPathname
|
||||
confOvwr.fNewFileSource = (char*) pErrorStatus->origPathname;
|
||||
confOvwr.fNewFileSource = (WCHAR*) pErrorStatus->origPathname;
|
||||
PathName checkPath(confOvwr.fNewFileSource);
|
||||
confOvwr.fNewFileModWhen = checkPath.GetModWhen();
|
||||
} else {
|
||||
@ -1858,7 +1863,7 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
|
||||
* I think this now throws kXferCancelled whenever it's supposed to. Not
|
||||
* 100% sure, but it looks good.
|
||||
*/
|
||||
LOGI("NufxArchive XferSelection!");
|
||||
LOGD("NufxArchive XferSelection!");
|
||||
XferStatus retval = kXferFailed;
|
||||
unsigned char* dataBuf = NULL;
|
||||
unsigned char* rsrcBuf = NULL;
|
||||
@ -1870,7 +1875,7 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
|
||||
for ( ; pSelEntry != NULL; pSelEntry = pSelSet->IterNext()) {
|
||||
long dataLen=-1, rsrcLen=-1;
|
||||
NufxEntry* pEntry = (NufxEntry*) pSelEntry->GetEntry();
|
||||
FileDetails fileDetails;
|
||||
LocalFileDetails fileDetails;
|
||||
CString errMsg;
|
||||
|
||||
ASSERT(dataBuf == NULL);
|
||||
@ -1883,25 +1888,29 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGI(" XFER converting '%ls'", pEntry->GetDisplayName());
|
||||
LOGD(" XFER converting '%ls'", pEntry->GetDisplayName());
|
||||
|
||||
fileDetails.storageName = pEntry->GetDisplayName();
|
||||
fileDetails.fileType = pEntry->GetFileType();
|
||||
fileDetails.fileSysFmt = DiskImg::kFormatUnknown;
|
||||
fileDetails.fileSysInfo = PathProposal::kDefaultStoredFssep;
|
||||
fileDetails.access = pEntry->GetAccess();
|
||||
fileDetails.extraType = pEntry->GetAuxType();
|
||||
fileDetails.storageType = kNuStorageSeedling;
|
||||
fileDetails.SetStrippedLocalPathName(pEntry->GetDisplayName());
|
||||
fileDetails.SetFssep(PathProposal::kDefaultStoredFssep);
|
||||
fileDetails.SetFileSysFmt(DiskImg::kFormatUnknown);
|
||||
fileDetails.SetFileType(pEntry->GetFileType());
|
||||
fileDetails.SetExtraType(pEntry->GetAuxType());
|
||||
fileDetails.SetAccess(pEntry->GetAccess());
|
||||
fileDetails.SetStorageType(kNuStorageSeedling);
|
||||
|
||||
time_t when;
|
||||
NuDateTime nuwhen;
|
||||
when = time(NULL);
|
||||
UNIXTimeToDateTime(&when, &fileDetails.archiveWhen);
|
||||
UNIXTimeToDateTime(&when, &nuwhen);
|
||||
fileDetails.SetArchiveWhen(nuwhen);
|
||||
when = pEntry->GetModWhen();
|
||||
UNIXTimeToDateTime(&when, &fileDetails.modWhen);
|
||||
UNIXTimeToDateTime(&when, &nuwhen);
|
||||
fileDetails.SetModWhen(nuwhen);
|
||||
when = pEntry->GetCreateWhen();
|
||||
UNIXTimeToDateTime(&when, &fileDetails.createWhen);
|
||||
UNIXTimeToDateTime(&when, &nuwhen);
|
||||
fileDetails.SetCreateWhen(nuwhen);
|
||||
|
||||
pActionProgress->SetArcName(fileDetails.storageName);
|
||||
pActionProgress->SetArcName(fileDetails.GetStrippedLocalPathName());
|
||||
if (pActionProgress->SetProgress(0) == IDCANCEL) {
|
||||
retval = kXferCancelled;
|
||||
goto bail;
|
||||
@ -1975,7 +1984,7 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
|
||||
goto bail;
|
||||
}
|
||||
|
||||
fileDetails.storageType = kNuStorageExtended;
|
||||
fileDetails.SetStorageType(kNuStorageExtended);
|
||||
} else {
|
||||
ASSERT(rsrcBuf == NULL);
|
||||
}
|
||||
@ -2026,22 +2035,23 @@ void NufxArchive::XferPrepare(const XferFileOptions* pXferOpts)
|
||||
(void) NuSetValue(fpArchive, kNuValueAllowDuplicates, true);
|
||||
}
|
||||
|
||||
CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
|
||||
long dataLen, unsigned char** pRsrcBuf, long rsrcLen)
|
||||
CString NufxArchive::XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen)
|
||||
{
|
||||
NuError nerr;
|
||||
const int kFileTypeTXT = 0x04;
|
||||
NuDataSource* pSource = NULL;
|
||||
CString errMsg;
|
||||
|
||||
LOGI(" NufxArchive::XferFile '%ls'", (LPCWSTR) pDetails->storageName);
|
||||
LOGI(" NufxArchive::XferFile '%ls'",
|
||||
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||
LOGI(" dataBuf=0x%p dataLen=%ld rsrcBuf=0x%p rsrcLen=%ld",
|
||||
*pDataBuf, dataLen, *pRsrcBuf, rsrcLen);
|
||||
ASSERT(pDataBuf != NULL);
|
||||
ASSERT(pRsrcBuf != NULL);
|
||||
|
||||
/* NuFX doesn't explicitly store directories */
|
||||
if (pDetails->entryKind == FileDetails::kFileKindDirectory) {
|
||||
if (pDetails->GetEntryKind() == LocalFileDetails::kFileKindDirectory) {
|
||||
delete[] *pDataBuf;
|
||||
delete[] *pRsrcBuf;
|
||||
*pDataBuf = *pRsrcBuf = NULL;
|
||||
@ -2051,11 +2061,6 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
|
||||
ASSERT(dataLen >= 0 || rsrcLen >= 0);
|
||||
ASSERT(*pDataBuf != NULL || *pRsrcBuf != NULL);
|
||||
|
||||
/* add the record; we have "allow duplicates" enabled for clashes */
|
||||
NuRecordIdx recordIdx;
|
||||
NuFileDetails nuFileDetails;
|
||||
nuFileDetails = *pDetails;
|
||||
|
||||
/*
|
||||
* Odd bit of trivia: NufxLib refuses to accept an fssep of '\0'. It
|
||||
* really wants to have one. Which is annoying, since files coming
|
||||
@ -2072,12 +2077,17 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
|
||||
* One issue: we don't currently allow changing the fssep when renaming
|
||||
* a file. We need to fix this, or else there's no way to rename a
|
||||
* file into a subdirectory once it has been pasted in this way.
|
||||
*
|
||||
* TODO: fix this in NufxLib
|
||||
*/
|
||||
if (NuGetSepFromSysInfo(nuFileDetails.fileSysInfo) == 0) {
|
||||
nuFileDetails.fileSysInfo =
|
||||
NuSetSepInSysInfo(nuFileDetails.fileSysInfo, kNufxNoFssep);
|
||||
if (pDetails->GetFssep() == '\0') {
|
||||
pDetails->SetFssep(kNufxNoFssep);
|
||||
}
|
||||
|
||||
/* add the record; we have "allow duplicates" enabled for clashes */
|
||||
NuRecordIdx recordIdx;
|
||||
const NuFileDetails& nuFileDetails = pDetails->GetNuFileDetails();
|
||||
|
||||
nerr = NuAddRecord(fpArchive, &nuFileDetails, &recordIdx);
|
||||
if (nerr != kNuErrNone) {
|
||||
if (nerr != kNuErrAborted) {
|
||||
@ -2092,13 +2102,13 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
|
||||
ASSERT(*pDataBuf != NULL);
|
||||
|
||||
/* strip the high ASCII from DOS and RDOS text files */
|
||||
if (pDetails->entryKind != FileDetails::kFileKindDiskImage &&
|
||||
pDetails->fileType == kFileTypeTXT &&
|
||||
DiskImg::UsesDOSFileStructure(pDetails->fileSysFmt))
|
||||
if (pDetails->GetEntryKind() != LocalFileDetails::kFileKindDiskImage &&
|
||||
pDetails->GetFileType() == kFileTypeTXT &&
|
||||
DiskImg::UsesDOSFileStructure(pDetails->GetFileSysFmt()))
|
||||
{
|
||||
LOGI(" Stripping high ASCII from '%ls'",
|
||||
(LPCWSTR) pDetails->storageName);
|
||||
unsigned char* ucp = *pDataBuf;
|
||||
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||
uint8_t* ucp = *pDataBuf;
|
||||
long len = dataLen;
|
||||
|
||||
while (len--)
|
||||
@ -2117,7 +2127,7 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
|
||||
|
||||
/* add the data fork, as a disk image if appropriate */
|
||||
NuThreadID targetID;
|
||||
if (pDetails->entryKind == FileDetails::kFileKindDiskImage)
|
||||
if (pDetails->GetEntryKind() == LocalFileDetails::kFileKindDiskImage)
|
||||
targetID = kNuThreadIDDiskImage;
|
||||
else
|
||||
targetID = kNuThreadIDDataFork;
|
||||
|
@ -171,7 +171,7 @@ private:
|
||||
CString* pErrMsg);
|
||||
|
||||
virtual void XferPrepare(const XferFileOptions* pXferOpts) override;
|
||||
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
||||
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override;
|
||||
virtual void XferAbort(CWnd* pMsgWnd) override;
|
||||
virtual void XferFinish(CWnd* pMsgWnd) override;
|
||||
@ -188,7 +188,7 @@ private:
|
||||
void AddFinish(void);
|
||||
|
||||
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
||||
FileDetails* pDetails) override;
|
||||
LocalFileDetails* pDetails) override;
|
||||
|
||||
/*
|
||||
* Error handler callback for "bulk" adds.
|
||||
|
@ -2468,7 +2468,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
|
||||
*/
|
||||
dierr = ValidateCreateFormat();
|
||||
if (dierr != kDIErrNone) {
|
||||
LOGI("ERROR: CIC arg validation failed, bailing");
|
||||
LOGE("ERROR: CIC arg validation failed, bailing");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
@ -2486,7 +2486,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
|
||||
fd = open(pathName, O_CREAT | O_EXCL, 0644);
|
||||
if (fd < 0) {
|
||||
dierr = (DIError) errno;
|
||||
LOGI("ERROR: unable to create file '%s' (errno=%d)",
|
||||
LOGE("ERROR: unable to create file '%s' (errno=%d)",
|
||||
pathName, dierr);
|
||||
goto bail;
|
||||
}
|
||||
@ -2627,7 +2627,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
|
||||
}
|
||||
|
||||
if (fpImageWrapper == NULL) {
|
||||
LOGI(" DI couldn't figure out the file format");
|
||||
LOGW(" DI couldn't figure out the file format");
|
||||
dierr = kDIErrUnrecognizedFileFmt;
|
||||
goto bail;
|
||||
}
|
||||
@ -2637,7 +2637,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
|
||||
dierr = fpImageWrapper->Create(fLength, fPhysical, fOrder,
|
||||
fDOSVolumeNum, fpWrapperGFD, &fWrappedLength, &fpDataGFD);
|
||||
if (dierr != kDIErrNone) {
|
||||
LOGI("ImageWrapper Create failed, err=%d", dierr);
|
||||
LOGE("ImageWrapper Create failed, err=%d", dierr);
|
||||
goto bail;
|
||||
}
|
||||
assert(fpDataGFD != NULL);
|
||||
@ -2658,7 +2658,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
|
||||
assert(!skipFormat); // don't skip low-level nibble formatting!
|
||||
if (fDOSVolumeNum == kVolumeNumNotSet) {
|
||||
fDOSVolumeNum = kDefaultNibbleVolumeNum;
|
||||
LOGI(" Using default nibble volume num");
|
||||
LOGD(" Using default nibble volume num");
|
||||
}
|
||||
|
||||
dierr = FormatNibbles(fpDataGFD); // write basic nibble stuff
|
||||
@ -2697,7 +2697,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||
*/
|
||||
if (fHasBlocks && fNumBlocks >= 4194304) { // 2GB or larger?
|
||||
if (fFileFormat != kFileFormatUnadorned) {
|
||||
LOGI("CreateImage: images >= 2GB can only be unadorned");
|
||||
LOGW("CreateImage: images >= 2GB can only be unadorned");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
}
|
||||
@ -2707,14 +2707,14 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||
fOrder == kSectorOrderUnknown ||
|
||||
fFormat == kFormatUnknown)
|
||||
{
|
||||
LOGI("CreateImage: ambiguous format");
|
||||
LOGW("CreateImage: ambiguous format");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
if (fOuterFormat != kOuterFormatNone &&
|
||||
fOuterFormat != kOuterFormatGzip &&
|
||||
fOuterFormat != kOuterFormatZip)
|
||||
{
|
||||
LOGI("CreateImage: unsupported outer format %d", fOuterFormat);
|
||||
LOGW("CreateImage: unsupported outer format %d", fOuterFormat);
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
if (fFileFormat != kFileFormatUnadorned &&
|
||||
@ -2726,7 +2726,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||
fFileFormat != kFileFormatNuFX &&
|
||||
fFileFormat != kFileFormatDDD)
|
||||
{
|
||||
LOGI("CreateImage: unsupported file format %d", fFileFormat);
|
||||
LOGW("CreateImage: unsupported file format %d", fFileFormat);
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
if (fFormat != kFormatGenericPhysicalOrd &&
|
||||
@ -2734,7 +2734,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||
fFormat != kFormatGenericDOSOrd &&
|
||||
fFormat != kFormatGenericCPMOrd)
|
||||
{
|
||||
LOGI("CreateImage: may only use 'generic' formats");
|
||||
LOGW("CreateImage: may only use 'generic' formats");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
|
||||
@ -2743,25 +2743,25 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||
*/
|
||||
if (fPhysical != kPhysicalFormatSectors) {
|
||||
if (fOrder != kSectorOrderPhysical) {
|
||||
LOGI("CreateImage: nibble images are always 'physical' order");
|
||||
LOGW("CreateImage: nibble images are always 'physical' order");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
|
||||
if (GetHasSectors() == false && GetHasNibbles() == false) {
|
||||
LOGI("CreateImage: must set hasSectors(%d) or hasNibbles(%d)",
|
||||
LOGW("CreateImage: must set hasSectors(%d) or hasNibbles(%d)",
|
||||
GetHasSectors(), GetHasNibbles());
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
|
||||
if (fpNibbleDescr == NULL && GetNumSectPerTrack() > 0) {
|
||||
LOGI("CreateImage: must provide NibbleDescr for non-sector");
|
||||
LOGW("CreateImage: must provide NibbleDescr for non-sector");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
|
||||
if (fpNibbleDescr != NULL &&
|
||||
fpNibbleDescr->numSectors != GetNumSectPerTrack())
|
||||
{
|
||||
LOGI("CreateImage: ?? nd->numSectors=%d, GetNumSectPerTrack=%d",
|
||||
LOGW("CreateImage: ?? nd->numSectors=%d, GetNumSectPerTrack=%d",
|
||||
fpNibbleDescr->numSectors, GetNumSectPerTrack());
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
@ -2773,14 +2773,14 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||
fpNibbleDescr->encoding != kNibbleEnc62))
|
||||
)
|
||||
{
|
||||
LOGI("CreateImage: sector count/encoding mismatch");
|
||||
LOGW("CreateImage: sector count/encoding mismatch");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
|
||||
if (GetNumTracks() != kTrackCount525 &&
|
||||
!(GetNumTracks() == 40 && fFileFormat == kFileFormatTrackStar))
|
||||
{
|
||||
LOGI("CreateImage: unexpected track count %ld", GetNumTracks());
|
||||
LOGW("CreateImage: unexpected track count %ld", GetNumTracks());
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
}
|
||||
@ -2788,7 +2788,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||
if (fPhysical != kPhysicalFormatSectors &&
|
||||
fPhysical != kPhysicalFormatNib525_6656)
|
||||
{
|
||||
LOGI("CreateImage: 2MG can't handle physical %d", fPhysical);
|
||||
LOGW("CreateImage: 2MG can't handle physical %d", fPhysical);
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
|
||||
@ -2796,78 +2796,78 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||
(fOrder != kSectorOrderProDOS &&
|
||||
fOrder != kSectorOrderDOS))
|
||||
{
|
||||
LOGI("CreateImage: 2MG requires DOS or ProDOS ordering");
|
||||
LOGW("CreateImage: 2MG requires DOS or ProDOS ordering");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
}
|
||||
if (fFileFormat == kFileFormatNuFX) {
|
||||
if (fOuterFormat != kOuterFormatNone) {
|
||||
LOGI("CreateImage: can't mix NuFX and outer wrapper");
|
||||
LOGW("CreateImage: can't mix NuFX and outer wrapper");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
if (fPhysical != kPhysicalFormatSectors) {
|
||||
LOGI("CreateImage: NuFX physical must be sectors");
|
||||
LOGW("CreateImage: NuFX physical must be sectors");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
if (fOrder != kSectorOrderProDOS) {
|
||||
LOGI("CreateImage: NuFX is always ProDOS-order");
|
||||
LOGW("CreateImage: NuFX is always ProDOS-order");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
}
|
||||
if (fFileFormat == kFileFormatDiskCopy42) {
|
||||
if (fPhysical != kPhysicalFormatSectors) {
|
||||
LOGI("CreateImage: DC42 physical must be sectors");
|
||||
LOGW("CreateImage: DC42 physical must be sectors");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
if ((GetHasBlocks() && GetNumBlocks() != 1600) ||
|
||||
(GetHasSectors() &&
|
||||
(GetNumTracks() != 200 || GetNumSectPerTrack() != 16)))
|
||||
{
|
||||
LOGI("CreateImage: DC42 only for 800K disks");
|
||||
LOGW("CreateImage: DC42 only for 800K disks");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
if (fOrder != kSectorOrderProDOS &&
|
||||
fOrder != kSectorOrderDOS) // used for UNIDOS disks??
|
||||
{
|
||||
LOGI("CreateImage: DC42 is always ProDOS or DOS");
|
||||
LOGW("CreateImage: DC42 is always ProDOS or DOS");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
}
|
||||
if (fFileFormat == kFileFormatSim2eHDV) {
|
||||
if (fPhysical != kPhysicalFormatSectors) {
|
||||
LOGI("CreateImage: Sim2eHDV physical must be sectors");
|
||||
LOGW("CreateImage: Sim2eHDV physical must be sectors");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
if (fOrder != kSectorOrderProDOS) {
|
||||
LOGI("CreateImage: Sim2eHDV is always ProDOS-order");
|
||||
LOGW("CreateImage: Sim2eHDV is always ProDOS-order");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
}
|
||||
if (fFileFormat == kFileFormatTrackStar) {
|
||||
if (fPhysical != kPhysicalFormatNib525_Var) {
|
||||
LOGI("CreateImage: TrackStar physical must be var-nibbles");
|
||||
LOGW("CreateImage: TrackStar physical must be var-nibbles");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
}
|
||||
if (fFileFormat == kFileFormatFDI) {
|
||||
if (fPhysical != kPhysicalFormatNib525_Var) {
|
||||
LOGI("CreateImage: FDI physical must be var-nibbles");
|
||||
LOGW("CreateImage: FDI physical must be var-nibbles");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
}
|
||||
if (fFileFormat == kFileFormatDDD) {
|
||||
if (fPhysical != kPhysicalFormatSectors) {
|
||||
LOGI("CreateImage: DDD physical must be sectors");
|
||||
LOGW("CreateImage: DDD physical must be sectors");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
if (fOrder != kSectorOrderDOS) {
|
||||
LOGI("CreateImage: DDD is always DOS-order");
|
||||
LOGW("CreateImage: DDD is always DOS-order");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
if (!GetHasSectors() || GetNumTracks() != 35 ||
|
||||
GetNumSectPerTrack() != 16)
|
||||
{
|
||||
LOGI("CreateImage: DDD is only for 16-sector 35-track disks");
|
||||
LOGW("CreateImage: DDD is only for 16-sector 35-track disks");
|
||||
return kDIErrInvalidCreateReq;
|
||||
}
|
||||
}
|
||||
|
@ -1159,6 +1159,13 @@ public:
|
||||
* normalized string. If the buffer is NULL or isn't big enough, no part
|
||||
* of the normalized path will be copied into the buffer, and a specific
|
||||
* error (kDIErrDataOverrun) will be returned.
|
||||
*
|
||||
* Generally speaking, the normalization process involves separating
|
||||
* the pathname into its components, modifying each filename as needed
|
||||
* to make it legal on the current filesystem, and then reassembling
|
||||
* the components.
|
||||
*
|
||||
* The input and output strings are Mac OS Roman.
|
||||
*/
|
||||
virtual DIError NormalizePath(const char* path, char fssep,
|
||||
char* normalizedBuf, int* pNormalizedBufLen)
|
||||
@ -1184,9 +1191,9 @@ public:
|
||||
const char* pathName; // full pathname for file on disk image
|
||||
char fssep;
|
||||
int storageType; // determines normal, subdir, or forked
|
||||
long fileType;
|
||||
long auxType;
|
||||
int access;
|
||||
uint32_t fileType;
|
||||
uint32_t auxType;
|
||||
uint32_t access;
|
||||
time_t createWhen;
|
||||
time_t modWhen;
|
||||
} CreateParms;
|
||||
@ -1459,10 +1466,13 @@ public:
|
||||
* NOTE: there is no guarantee that GetPathName will return unique values
|
||||
* (duplicates are possible). We don't guarantee that you won't get an
|
||||
* empty string back (it's valid to have an empty filename in the dir in
|
||||
* DOS 3.3, and it's possible for other filesystems to be damaged). The
|
||||
* pathname may receive some minor sanitizing, e.g. removal or conversion
|
||||
* of high ASCII and control characters, but some filesystems (like HFS)
|
||||
* make use of them.
|
||||
* DOS 3.3, and it's possible for other filesystems to be damaged).
|
||||
*
|
||||
* The filename returned is defined to be null-terminated Mac OS Roman.
|
||||
* For most filesystems this is automatic, as filenames are restricted
|
||||
* to ASCII, but for DOS 3.3 it requires stripping high bits. It also
|
||||
* means that embedded nulls in HFS filenames (which are discouraged but
|
||||
* allowed) will be lost.
|
||||
*
|
||||
* We do guarantee that the contents of subdirectories are grouped
|
||||
* together. This makes it much easier to construct a hierarchy out of
|
||||
|
@ -1407,10 +1407,6 @@ private:
|
||||
/*
|
||||
* Holds DOS files. Works for DOS33, DOS32, and "wide" DOS implementations.
|
||||
*
|
||||
* Catalog contents are public so anyone who wants gory details of DOS 3.3
|
||||
* stuff can poke at whatever they want. Anybody else can use the virtual
|
||||
* interfaces to get standardized answers for things like file type.
|
||||
*
|
||||
* The embedded address and length fields found in Applesoft, Integer, and
|
||||
* Binary files are quietly skipped over with the fDataOffset field when
|
||||
* files are read.
|
||||
@ -1470,6 +1466,10 @@ public:
|
||||
|
||||
void Dump(void) const;
|
||||
|
||||
friend class DiskFSDOS33;
|
||||
friend class A2FDDOS;
|
||||
|
||||
private:
|
||||
typedef DiskFSDOS33::TrackSector TrackSector;
|
||||
|
||||
/*
|
||||
@ -1490,7 +1490,7 @@ public:
|
||||
|
||||
// these are computed or determined from the file contents
|
||||
uint16_t fAuxType; // addr for bin, etc.
|
||||
short fDataOffset; // for 'A'/'B'/'I' with embedded len
|
||||
uint16_t fDataOffset; // 0/2/4, for 'A'/'B'/'I' with embedded len
|
||||
di_off_t fLength; // file length, in bytes
|
||||
di_off_t fSparseLength; // file length, factoring sparse out
|
||||
|
||||
@ -1503,7 +1503,6 @@ public:
|
||||
static void MakeDOSName(char* buf, const char* name);
|
||||
static void TrimTrailingSpaces(char* filename);
|
||||
|
||||
private:
|
||||
DIError ExtractTSPairs(const uint8_t* sctBuf, TrackSector* tsList,
|
||||
int* pLastNonZero);
|
||||
|
||||
|
@ -254,11 +254,16 @@ DIError GFDFile::Open(const char* filename, bool readOnly)
|
||||
|
||||
fFd = open(filename, readOnly ? O_RDONLY|O_BINARY : O_RDWR|O_BINARY, 0);
|
||||
if (fFd < 0) {
|
||||
if (errno == EACCES)
|
||||
if (errno == EACCES) {
|
||||
dierr = kDIErrAccessDenied;
|
||||
else
|
||||
} else if (errno == EINVAL) {
|
||||
// Happens on Win32 if a Unicode filename conversion failed,
|
||||
// because non-converting chars turn into '?', which is illegal.
|
||||
dierr = kDIErrInvalidArg;
|
||||
} else {
|
||||
dierr = ErrnoOrGeneric();
|
||||
LOGI(" GDFile Open failed opening '%s', ro=%d (err=%d)",
|
||||
}
|
||||
LOGW(" GDFile Open failed opening '%s', ro=%d (err=%d)",
|
||||
filename, readOnly, dierr);
|
||||
return dierr;
|
||||
}
|
||||
@ -278,7 +283,7 @@ DIError GFDFile::Read(void* buf, size_t length, size_t* pActual)
|
||||
return kDIErrEOF;
|
||||
if (actual < 0) {
|
||||
dierr = ErrnoOrGeneric();
|
||||
LOGI(" GDFile Read failed on %d bytes (actual=%d, err=%d)",
|
||||
LOGW(" GDFile Read failed on %d bytes (actual=%d, err=%d)",
|
||||
length, actual, dierr);
|
||||
return dierr;
|
||||
}
|
||||
|
@ -1097,15 +1097,16 @@ NuError Nu_CloseOutputFile(NuArchive* pArchive, const NuRecord* pRecord,
|
||||
err = Nu_SetFileDates(pArchive, pRecord, pathnameUNI);
|
||||
BailError(err);
|
||||
|
||||
err = Nu_SetFileAccess(pArchive, pRecord, pathnameUNI);
|
||||
BailError(err);
|
||||
|
||||
#if defined(MAC_LIKE)
|
||||
/* could also do this earlier and pass the fd for fsetxattr */
|
||||
/* NOTE: must do this before Nu_SetFileAccess */
|
||||
err = Nu_SetFinderInfo(pArchive, pRecord, pathnameUNI);
|
||||
BailError(err);
|
||||
#endif
|
||||
|
||||
err = Nu_SetFileAccess(pArchive, pRecord, pathnameUNI);
|
||||
BailError(err);
|
||||
|
||||
bail:
|
||||
return kNuErrNone;
|
||||
}
|
||||
|
@ -429,8 +429,9 @@ union NuDataSink {
|
||||
* the first arguments to Nu_ReportError, so we don't have to type them
|
||||
* in every time we use the function. It would've been much easier to
|
||||
* use a gcc-style varargs macro, but not everybody uses gcc.
|
||||
*
|
||||
* TODO: Visual C++ has vararg macros now; time to replace this.
|
||||
*/
|
||||
#ifdef DEBUG_MSGS
|
||||
#ifdef HAS__FUNCTION__
|
||||
# define _FUNCTION_ __FUNCTION__
|
||||
#else
|
||||
@ -440,13 +441,12 @@ union NuDataSink {
|
||||
#define NU_BLOB pArchive, __FILE__, __LINE__, _FUNCTION_, false
|
||||
#define NU_BLOB_DEBUG pArchive, __FILE__, __LINE__, _FUNCTION_, true
|
||||
#define NU_NILBLOB NULL, __FILE__, __LINE__, _FUNCTION_, false
|
||||
|
||||
#ifdef DEBUG_MSGS
|
||||
# define DebugShowError(err) \
|
||||
Nu_ReportError(pArchive, __FILE__, __LINE__, _FUNCTION_, \
|
||||
true, err, "(DEBUG)");
|
||||
#else
|
||||
#define NU_BLOB pArchive, "", 0, "", false
|
||||
#define NU_BLOB_DEBUG pArchive, "", 0, "", true
|
||||
#define NU_NILBLOB NULL, "", 0, "", false
|
||||
# define DebugShowError(err) ((void)0)
|
||||
#endif
|
||||
|
||||
|
@ -76,8 +76,24 @@ void DebugLog::Log(LogSeverity severity, const char* file, int line,
|
||||
va_list argptr;
|
||||
char textBuf[4096];
|
||||
|
||||
// TODO: _vsnprintf() doesn't deal with "%ls" well. If it encounters a
|
||||
// character it can't translate, it stops and returns -1. This is
|
||||
// very annoying if you're trying to print a wide-char filename that
|
||||
// includes characters that aren't in CP-1252.
|
||||
//
|
||||
// We can probably fix this by converting "format" to a wide string,
|
||||
// printing with _vsnwprintf(), and then converting the output back
|
||||
// to narrow manually (selecting options that prevent it from stopping
|
||||
// mid-string on failure). As an optimization, we only need to do this
|
||||
// if the format string includes a "%ls" specifier.
|
||||
//
|
||||
// The interpretation of plain "%s" will change if we use a wide printf
|
||||
// function (it's a Microsoft extension, not ANSI behavior), so we'd also
|
||||
// want to check for "%s" and complain if we see it. All callers must
|
||||
// use explicit "%hs" or "%ls".
|
||||
|
||||
va_start(argptr, format);
|
||||
_vsnprintf(textBuf, NELEM(textBuf) - 1, format, argptr);
|
||||
(void) _vsnprintf(textBuf, NELEM(textBuf) - 1, format, argptr);
|
||||
va_end(argptr);
|
||||
textBuf[NELEM(textBuf) - 1] = '\0';
|
||||
|
||||
|
@ -127,6 +127,30 @@ public:
|
||||
*/
|
||||
static const WCHAR* FilenameOnly(const WCHAR* pathname, WCHAR fssep);
|
||||
|
||||
/*
|
||||
* Test to see if a wide-to-narrow filename conversion failed.
|
||||
*
|
||||
* Returns true if all is well, false with *pErrMsg set if something
|
||||
* went wrong.
|
||||
*/
|
||||
static bool TestNarrowConversion(const CString& original,
|
||||
const CStringA& converted, CString* pErrMsg) {
|
||||
int index = converted.ReverseFind('?');
|
||||
if (index < 0) {
|
||||
// no '?' is good
|
||||
} else if (index == 2 && converted.Left(4) == "\\\\?\\") {
|
||||
// Win32 file namespace path strings start with "\\?\". If that's
|
||||
// the first occurrence of '?', we're still good.
|
||||
} else {
|
||||
// This is most likely the result of a failed wide-to-narrow
|
||||
// string conversion.
|
||||
pErrMsg->Format(L"Unable to open '%ls' -- Unicode filename "
|
||||
L"conversion is invalid ('%hs')",
|
||||
(LPCWSTR) original, (LPCSTR) converted);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
DECLARE_COPY_AND_OPEQ(PathName)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user