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:
Andy McFadden 2015-01-04 21:04:01 -08:00
parent d94b707489
commit b79498da50
24 changed files with 824 additions and 697 deletions

View File

@ -375,7 +375,7 @@ bail:
/*static*/ CString AcuArchive::AppInit(void) /*static*/ CString AcuArchive::AppInit(void)
{ {
return ""; return L"";
} }
GenericArchive::OpenResult AcuArchive::Open(const WCHAR* filename, GenericArchive::OpenResult AcuArchive::Open(const WCHAR* filename,
@ -526,11 +526,11 @@ int AcuArchive::ReadMasterHeader(int* pNumEntries)
header.unknown1 != 1 || header.unknown1 != 1 ||
strcmp((char*) header.fZink, "fZink") != 0) strcmp((char*) header.fZink, "fZink") != 0)
{ {
LOGI("Not an ACU archive"); LOGW("Not an ACU archive");
return -1; 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; *pNumEntries = header.fileCount;
return 0; return 0;

View File

@ -162,7 +162,7 @@ private:
} }
virtual void XferPrepare(const XferFileOptions* pXferOpts) override virtual void XferPrepare(const XferFileOptions* pXferOpts) override
{ ASSERT(false); } { 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 long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override
{ ASSERT(false); return "!"; } { ASSERT(false); return "!"; }
virtual void XferAbort(CWnd* pMsgWnd) override virtual void XferAbort(CWnd* pMsgWnd) override
@ -172,7 +172,7 @@ private:
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveACU; } virtual ArchiveKind GetArchiveKind(void) override { return kArchiveACU; }
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails) override LocalFileDetails* pDetails) override
{ ASSERT(false); return kNuErrGeneric; } { ASSERT(false); return kNuErrGeneric; }
enum { enum {

View File

@ -2138,21 +2138,21 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly()); pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly());
} }
/*static*/ bool MainWindow::SaveToArchive(GenericArchive::FileDetails* pDetails, /*static*/ bool MainWindow::SaveToArchive(GenericArchive::LocalFileDetails* pDetails,
const unsigned char* dataBufIn, long dataLen, const uint8_t* dataBufIn, long dataLen,
const unsigned char* rsrcBufIn, long rsrcLen, const uint8_t* rsrcBufIn, long rsrcLen,
CString& errMsg, CWnd* pDialog) CString* pErrMsg, CWnd* pDialog)
{ {
MainWindow* pMain = GET_MAIN_WINDOW(); MainWindow* pMain = GET_MAIN_WINDOW();
GenericArchive* pArchive = pMain->GetOpenArchive(); GenericArchive* pArchive = pMain->GetOpenArchive();
DiskImgLib::A2File* pTargetSubdir = NULL; DiskImgLib::A2File* pTargetSubdir = NULL;
XferFileOptions xferOpts; XferFileOptions xferOpts;
CString storagePrefix; CString storagePrefix;
unsigned char* dataBuf = NULL; uint8_t* dataBuf = NULL;
unsigned char* rsrcBuf = NULL; uint8_t* rsrcBuf = NULL;
ASSERT(pArchive != NULL); ASSERT(pArchive != NULL);
ASSERT(errMsg.IsEmpty()); ASSERT(pErrMsg->IsEmpty());
/* /*
* Make a copy of the data for XferFile. * Make a copy of the data for XferFile.
@ -2163,7 +2163,7 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
else else
dataBuf = new unsigned char[dataLen]; dataBuf = new unsigned char[dataLen];
if (dataBuf == NULL) { if (dataBuf == NULL) {
errMsg.Format(L"Unable to allocate %ld bytes", dataLen); pErrMsg->Format(L"Unable to allocate %ld bytes", dataLen);
goto bail; goto bail;
} }
memcpy(dataBuf, dataBufIn, dataLen); memcpy(dataBuf, dataBufIn, dataLen);
@ -2187,7 +2187,7 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
// Always use ':' separator for SHK; this is a matter of // Always use ':' separator for SHK; this is a matter of
// convenience, so they can specify a full path. // convenience, so they can specify a full path.
//details.storageName.Replace(':', '_'); //details.storageName.Replace(':', '_');
pDetails->fileSysInfo = ':'; pDetails->SetFssep(':');
} }
if (pTargetSubdir != NULL) { if (pTargetSubdir != NULL) {
storagePrefix = pTargetSubdir->GetPathName(); storagePrefix = pTargetSubdir->GetPathName();
@ -2195,13 +2195,13 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
} }
if (!storagePrefix.IsEmpty()) { if (!storagePrefix.IsEmpty()) {
CString tmpStr, tmpFileName; CString tmpStr, tmpFileName;
tmpFileName = pDetails->storageName; tmpFileName = pDetails->GetStrippedLocalPathName();
tmpFileName.Replace(':', '_'); // strip any ':'s in the name tmpFileName.Replace(':', '_'); // strip any ':'s in the name
pDetails->fileSysInfo = ':'; pDetails->SetFssep(':');
tmpStr = storagePrefix; tmpStr = storagePrefix;
tmpStr += ':'; tmpStr += ':';
tmpStr += tmpFileName; tmpStr += tmpFileName;
pDetails->storageName = tmpStr; pDetails->SetStrippedLocalPathName(tmpStr);
} }
/* /*
@ -2211,18 +2211,18 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
*/ */
pArchive->XferPrepare(&xferOpts); pArchive->XferPrepare(&xferOpts);
errMsg = pArchive->XferFile(pDetails, &dataBuf, dataLen, *pErrMsg = pArchive->XferFile(pDetails, &dataBuf, dataLen,
&rsrcBuf, rsrcLen); &rsrcBuf, rsrcLen);
delete[] dataBuf; delete[] dataBuf;
delete[] rsrcBuf; delete[] rsrcBuf;
if (errMsg.IsEmpty()) if (pErrMsg->IsEmpty())
pArchive->XferFinish(pDialog); pArchive->XferFinish(pDialog);
else else
pArchive->XferAbort(pDialog); pArchive->XferAbort(pDialog);
bail: bail:
return (errMsg.IsEmpty() != 0); return (pErrMsg->IsEmpty() != 0);
} }

View File

@ -149,7 +149,7 @@ private:
} }
virtual void XferPrepare(const XferFileOptions* pXferOpts) override virtual void XferPrepare(const XferFileOptions* pXferOpts) override
{ ASSERT(false); } { 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 long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override
{ ASSERT(false); return "!"; } { ASSERT(false); return "!"; }
virtual void XferAbort(CWnd* pMsgWnd) override virtual void XferAbort(CWnd* pMsgWnd) override
@ -159,7 +159,7 @@ private:
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveBNY; } virtual ArchiveKind GetArchiveKind(void) override { return kArchiveBNY; }
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails) override LocalFileDetails* pDetails) override
{ ASSERT(false); return kNuErrGeneric; } { ASSERT(false); return kNuErrGeneric; }
enum { enum {

View File

@ -616,25 +616,27 @@ void ImportBASDialog::OnOK(void)
/* /*
* Write the file to the currently-open archive. * Write the file to the currently-open archive.
*/ */
GenericArchive::FileDetails details; GenericArchive::LocalFileDetails details;
details.entryKind = GenericArchive::FileDetails::kFileKindDataFork; details.SetEntryKind(GenericArchive::LocalFileDetails::kFileKindDataFork);
details.origName = L"Imported BASIC"; details.SetLocalPathName(L"Imported BASIC");
details.storageName = fileName; details.SetStrippedLocalPathName(fileName);
details.access = 0xe3; // unlocked, backup bit set details.SetAccess(0xe3); // unlocked, backup bit set
details.fileType = kFileTypeBAS; details.SetFileType(kFileTypeBAS);
details.extraType = 0x0801; details.SetExtraType(0x0801);
details.storageType = DiskFS::kStorageSeedling; details.SetStorageType(DiskFS::kStorageSeedling);
time_t now = time(NULL); time_t now = time(NULL);
GenericArchive::UNIXTimeToDateTime(&now, &details.createWhen); NuDateTime ndt;
GenericArchive::UNIXTimeToDateTime(&now, &details.archiveWhen); GenericArchive::UNIXTimeToDateTime(&now, &ndt);
GenericArchive::UNIXTimeToDateTime(&now, &details.modWhen); details.SetCreateWhen(ndt);
details.SetArchiveWhen(ndt);
details.SetModWhen(ndt);
CString errMsg; CString errMsg;
fDirty = true; fDirty = true;
if (!MainWindow::SaveToArchive(&details, (const unsigned char*) fOutput, if (!MainWindow::SaveToArchive(&details, (const unsigned char*) fOutput,
fOutputLen, NULL, -1, /*ref*/errMsg, this)) fOutputLen, NULL, -1, &errMsg, this))
{ {
goto bail; goto bail;
} }

View File

@ -418,29 +418,33 @@ void CassetteDialog::OnImport(void)
/* /*
* Write the file to the currently-open archive. * Write the file to the currently-open archive.
*/ */
GenericArchive::FileDetails details; GenericArchive::LocalFileDetails details;
details.entryKind = GenericArchive::FileDetails::kFileKindDataFork; details.SetEntryKind(GenericArchive::LocalFileDetails::kFileKindDataFork);
details.origName = "Cassette WAV"; details.SetLocalPathName(L"Cassette WAV");
details.storageName = impDialog.fFileName; details.SetStrippedLocalPathName(impDialog.fFileName);
details.access = 0xe3; // unlocked, backup bit set details.SetAccess(0xe3); // unlocked, backup bit set
details.fileType = impDialog.GetFileType(); details.SetFileType(impDialog.GetFileType());
if (details.fileType == kFileTypeBIN) if (details.GetFileType() == kFileTypeBIN) {
details.extraType = impDialog.fStartAddr; details.SetExtraType(impDialog.fStartAddr);
else if (details.fileType == kFileTypeBAS) } else if (details.GetFileType() == kFileTypeBAS) {
details.extraType = 0x0801; details.SetExtraType(0x0801);
else } else {
details.extraType = 0x0000; details.SetExtraType(0x0000);
details.storageType = DiskFS::kStorageSeedling; }
details.SetStorageType(DiskFS::kStorageSeedling);
time_t now = time(NULL); time_t now = time(NULL);
GenericArchive::UNIXTimeToDateTime(&now, &details.createWhen); NuDateTime ndt;
GenericArchive::UNIXTimeToDateTime(&now, &details.archiveWhen); GenericArchive::UNIXTimeToDateTime(&now, &ndt);
details.SetCreateWhen(ndt);
details.SetArchiveWhen(ndt);
details.SetModWhen(ndt);
CString errMsg; CString errMsg;
fDirty = true; fDirty = true;
if (!MainWindow::SaveToArchive(&details, fDataArray[idx].GetDataBuf(), if (!MainWindow::SaveToArchive(&details, fDataArray[idx].GetDataBuf(),
fDataArray[idx].GetDataLen(), NULL, -1, /*ref*/errMsg, this)) fDataArray[idx].GetDataLen(), NULL, -1, &errMsg, this))
{ {
goto bail; goto bail;
} }

View File

@ -80,11 +80,11 @@ typedef struct FileCollectionEntry {
uint32_t cmmtLen; // len of comments uint32_t cmmtLen; // len of comments
uint32_t fileType; uint32_t fileType;
uint32_t auxType; uint32_t auxType;
int64_t createWhen; // time_t int64_t createWhen; // holds time_t
int64_t modWhen; // time_t int64_t modWhen; // holds time_t
uint8_t access; // ProDOS access flags uint8_t access; // ProDOS access flags
uint8_t entryKind; // GenericArchive::FileDetails::FileKind 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. ':' uint8_t fssep; // filesystem separator char, e.g. ':'
/* data comes next: null-terminated WCHAR filename, then data fork, then /* data comes next: null-terminated WCHAR filename, then data fork, then
@ -440,17 +440,17 @@ CString MainWindow::CopyToCollection(GenericEntry* pEntry, void** pBuf,
return errStr; return errStr;
} }
GenericArchive::FileDetails::FileKind entryKind; GenericArchive::LocalFileDetails::FileKind entryKind;
if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory)
entryKind = GenericArchive::FileDetails::kFileKindDirectory; entryKind = GenericArchive::LocalFileDetails::kFileKindDirectory;
else if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork()) else if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork())
entryKind = GenericArchive::FileDetails::kFileKindBothForks; entryKind = GenericArchive::LocalFileDetails::kFileKindBothForks;
else if (pEntry->GetHasDataFork()) else if (pEntry->GetHasDataFork())
entryKind = GenericArchive::FileDetails::kFileKindDataFork; entryKind = GenericArchive::LocalFileDetails::kFileKindDataFork;
else if (pEntry->GetHasRsrcFork()) else if (pEntry->GetHasRsrcFork())
entryKind = GenericArchive::FileDetails::kFileKindRsrcFork; entryKind = GenericArchive::LocalFileDetails::kFileKindRsrcFork;
else if (pEntry->GetHasDiskImage()) else if (pEntry->GetHasDiskImage())
entryKind = GenericArchive::FileDetails::kFileKindDiskImage; entryKind = GenericArchive::LocalFileDetails::kFileKindDiskImage;
else { else {
ASSERT(false); ASSERT(false);
return errStr; return errStr;
@ -912,30 +912,32 @@ bail:
CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt, CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
const WCHAR* pathName, const uint8_t* buf, long remLen) const WCHAR* pathName, const uint8_t* buf, long remLen)
{ {
GenericArchive::FileDetails::FileKind entryKind; GenericArchive::LocalFileDetails::FileKind entryKind;
GenericArchive::FileDetails details; GenericArchive::LocalFileDetails details;
uint8_t* dataBuf = NULL; uint8_t* dataBuf = NULL;
uint8_t* rsrcBuf = NULL; uint8_t* rsrcBuf = NULL;
long dataLen, rsrcLen, cmmtLen; long dataLen, rsrcLen, cmmtLen;
CString errMsg; CString errMsg;
entryKind = (GenericArchive::FileDetails::FileKind) pCollEnt->entryKind; entryKind = (GenericArchive::LocalFileDetails::FileKind) pCollEnt->entryKind;
LOGI(" Processing '%ls' (%d)", pathName, entryKind); LOGD(" Processing '%ls' (%d)", pathName, entryKind);
details.entryKind = entryKind; details.SetEntryKind(entryKind);
details.origName = L"Clipboard"; details.SetLocalPathName(L"Clipboard");
details.storageName = pathName; // TODO MacRoman convert details.SetStrippedLocalPathName(pathName);
details.fileSysFmt = (DiskImg::FSFormat) pCollEnt->sourceFS; details.SetFileSysFmt((DiskImg::FSFormat) pCollEnt->sourceFS);
details.fileSysInfo = pCollEnt->fssep; details.SetFssep(pCollEnt->fssep);
details.access = pCollEnt->access; details.SetAccess(pCollEnt->access);
details.fileType = pCollEnt->fileType; details.SetFileType(pCollEnt->fileType);
details.extraType = pCollEnt->auxType; details.SetExtraType(pCollEnt->auxType);
GenericArchive::UNIXTimeToDateTime(&pCollEnt->createWhen, NuDateTime ndt;
&details.createWhen); GenericArchive::UNIXTimeToDateTime(&pCollEnt->createWhen, &ndt);
GenericArchive::UNIXTimeToDateTime(&pCollEnt->modWhen, details.SetCreateWhen(ndt);
&details.modWhen); GenericArchive::UNIXTimeToDateTime(&pCollEnt->modWhen, &ndt);
details.SetModWhen(ndt);
time_t now = time(NULL); 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 * 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 hasData = false;
bool hasRsrc = false; bool hasRsrc = false;
if (details.entryKind == GenericArchive::FileDetails::kFileKindDataFork) { if (entryKind == GenericArchive::LocalFileDetails::kFileKindDataFork) {
hasData = true; hasData = true;
details.storageType = kNuStorageSeedling; details.SetStorageType(kNuStorageSeedling);
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindRsrcFork) { } else if (entryKind == GenericArchive::LocalFileDetails::kFileKindRsrcFork) {
hasRsrc = true; hasRsrc = true;
details.storageType = kNuStorageExtended; details.SetStorageType(kNuStorageExtended);
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindBothForks) { } else if (entryKind == GenericArchive::LocalFileDetails::kFileKindBothForks) {
hasData = hasRsrc = true; hasData = hasRsrc = true;
details.storageType = kNuStorageExtended; details.SetStorageType(kNuStorageExtended);
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindDiskImage) { } else if (entryKind == GenericArchive::LocalFileDetails::kFileKindDiskImage) {
hasData = true; hasData = true;
details.storageType = kNuStorageSeedling; details.SetStorageType(kNuStorageSeedling);
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindDirectory) { } else if (entryKind == GenericArchive::LocalFileDetails::kFileKindDirectory) {
details.storageType = kNuStorageDirectory; details.SetStorageType(kNuStorageDirectory);
} else { } else {
ASSERT(false); ASSERT(false);
return "Internal error."; return L"Internal error.";
} }
if (hasData) { if (hasData) {
@ -985,7 +987,7 @@ CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
dataLen = pCollEnt->dataLen; dataLen = pCollEnt->dataLen;
dataBuf = new uint8_t[dataLen]; dataBuf = new uint8_t[dataLen];
if (dataBuf == NULL) if (dataBuf == NULL)
return "memory allocation failed."; return L"memory allocation failed.";
memcpy(dataBuf, buf, dataLen); memcpy(dataBuf, buf, dataLen);
buf += dataLen; buf += dataLen;
remLen -= dataLen; remLen -= dataLen;
@ -1003,7 +1005,7 @@ CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
rsrcLen = pCollEnt->rsrcLen; rsrcLen = pCollEnt->rsrcLen;
rsrcBuf = new uint8_t[rsrcLen]; rsrcBuf = new uint8_t[rsrcLen];
if (rsrcBuf == NULL) if (rsrcBuf == NULL)
return "Memory allocation failed."; return L"Memory allocation failed.";
memcpy(rsrcBuf, buf, rsrcLen); memcpy(rsrcBuf, buf, rsrcLen);
buf += rsrcLen; buf += rsrcLen;
remLen -= rsrcLen; remLen -= rsrcLen;

View File

@ -466,21 +466,26 @@ GenericArchive::OpenResult DiskArchive::Open(const WCHAR* filename,
{ {
CWaitCursor waitc; CWaitCursor waitc;
// TODO(Unicode): modify DiskImg lib to accept wide paths
CStringA fileNameA(filename); CStringA fileNameA(filename);
if (!PathName::TestNarrowConversion(filename, fileNameA, &errMsg)) {
result = kResultFailure;
goto bail;
}
dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep, dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep,
readOnly); readOnly);
if (dierr == kDIErrAccessDenied && !readOnly && !isVolume) { if (dierr == kDIErrAccessDenied && !readOnly && !isVolume) {
// retry file open with read-only set // retry file open with read-only set
// don't do that for volumes -- assume they know what they want // 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; fIsReadOnly = readOnly = true;
dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep, dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep,
readOnly); readOnly);
} }
if (dierr != kDIErrNone) { if (dierr != kDIErrNone) {
if (dierr == kDIErrFileArchive) if (dierr == kDIErrFileArchive) {
result = kResultFileArchive; result = kResultFileArchive;
else { } else {
result = kResultFailure; result = kResultFailure;
errMsg.Format(L"Unable to open '%ls': %hs.", filename, errMsg.Format(L"Unable to open '%ls': %hs.", filename,
DiskImgLib::DIStrError(dierr)); DiskImgLib::DIStrError(dierr));
@ -507,7 +512,7 @@ GenericArchive::OpenResult DiskArchive::Open(const WCHAR* filename,
imf.SetAllowGenericFormats(false); imf.SetAllowGenericFormats(false);
if (imf.DoModal() != IDOK) { if (imf.DoModal() != IDOK) {
LOGI("User bailed on IMF dialog"); LOGD("User bailed on IMF dialog");
result = kResultCancel; result = kResultCancel;
goto bail; goto bail;
} }
@ -1246,7 +1251,7 @@ bail:
} }
NuError DiskArchive::DoAddFile(const AddFilesDialog* pAddOpts, 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 * 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 int neededLen = 64; // reasonable guess
char* fsNormalBuf = NULL; // name as it will appear on disk image char* fsNormalBuf = NULL; // name as it will appear on disk image
LOGI(" +++ ADD file: orig='%ls' stor='%ls'", LOGI(" +++ ADD file: orig='%ls' strip='%ls'",
(LPCWSTR) pDetails->origName, (LPCWSTR) pDetails->storageName); (LPCWSTR) pDetails->GetLocalPathName(),
(LPCWSTR) pDetails->GetStrippedLocalPathName());
retry: retry:
/* /*
@ -1295,14 +1301,13 @@ retry:
*/ */
delete[] fsNormalBuf; delete[] fsNormalBuf;
fsNormalBuf = new char[neededLen]; fsNormalBuf = new char[neededLen];
CStringA storageNameA(pDetails->storageName); dierr = pDiskFS->NormalizePath(pDetails->GetStoragePathNameMOR(),
dierr = pDiskFS->NormalizePath(storageNameA,
PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen); PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen);
if (dierr == kDIErrDataOverrun) { if (dierr == kDIErrDataOverrun) {
/* not long enough, try again *once* */ /* not long enough, try again *once* */
delete[] fsNormalBuf; delete[] fsNormalBuf;
fsNormalBuf = new char[neededLen]; fsNormalBuf = new char[neededLen];
dierr = pDiskFS->NormalizePath(storageNameA, dierr = pDiskFS->NormalizePath(pDetails->GetStoragePathNameMOR(),
PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen); PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen);
} }
if (dierr != kDIErrNone) { if (dierr != kDIErrNone) {
@ -1337,16 +1342,16 @@ retry:
goto retry; goto retry;
} else if (result == kNuOverwrite) { } else if (result == kNuOverwrite) {
/* delete the existing file immediately */ /* delete the existing file immediately */
LOGI(" Deleting existing file '%hs'", fsNormalBuf); LOGD(" Deleting existing file '%hs'", fsNormalBuf);
dierr = pDiskFS->DeleteFile(pExisting); dierr = pDiskFS->DeleteFile(pExisting);
if (dierr != kDIErrNone) { if (dierr != kDIErrNone) {
// Would be nice to show a dialog and explain *why*, but // Would be nice to show a dialog and explain *why*, but
// I'm not sure we have a window here. // I'm not sure we have a window here.
LOGI(" Deletion failed (err=%d)", dierr); LOGE(" Deletion failed (err=%d)", dierr);
goto bail; goto bail;
} }
} else { } else {
LOGI("GLITCH: bad return %d from HandleReplaceExisting",result); LOGE("GLITCH: bad return %d from HandleReplaceExisting",result);
assert(false); assert(false);
nuerr = kNuErrInternal; nuerr = kNuErrInternal;
goto bail; goto bail;
@ -1364,7 +1369,7 @@ retry:
goto bail; goto bail;
} }
LOGI("FSNormalized is '%hs'", pAddData->GetFSNormalPath()); LOGD("FSNormalized is '%hs'", pAddData->GetFSNormalPath());
AddToAddDataList(pAddData); AddToAddDataList(pAddData);
@ -1374,7 +1379,7 @@ bail:
} }
NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting, NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
FileDetails* pDetails) LocalFileDetails* pDetails)
{ {
NuResult result; NuResult result;
@ -1390,8 +1395,8 @@ NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
confOvwr.fExistingFile = pExisting->GetPathName(); confOvwr.fExistingFile = pExisting->GetPathName();
confOvwr.fExistingFileModWhen = pExisting->GetModWhen(); confOvwr.fExistingFileModWhen = pExisting->GetModWhen();
PathName srcPath(pDetails->origName); PathName srcPath(pDetails->GetLocalPathName());
confOvwr.fNewFileSource = pDetails->origName; // or storageName? confOvwr.fNewFileSource = pDetails->GetLocalPathName();
confOvwr.fNewFileModWhen = srcPath.GetModWhen(); confOvwr.fNewFileModWhen = srcPath.GetModWhen();
if (confOvwr.DoModal() == IDCANCEL) { if (confOvwr.DoModal() == IDCANCEL) {
@ -1399,6 +1404,9 @@ NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
return kNuAbort; 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) { if (confOvwr.fResultRename) {
/* /*
* Replace the name in FileDetails. They were asked to modify * 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 * full path and reject "OK" if it's not valid. Instead, we just
* allow the FS normalizer to force the filename to be valid. * allow the FS normalizer to force the filename to be valid.
*/ */
pDetails->storageName = confOvwr.fExistingFile; pDetails->SetStrippedLocalPathName(confOvwr.fExistingFile);
LOGI("Trying rename to '%ls'", (LPCWSTR) pDetails->storageName); LOGI("Trying rename to '%ls'",
(LPCWSTR) pDetails->GetStrippedLocalPathName());
return kNuRename; return kNuRename;
} }
@ -1470,26 +1479,26 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
pData = fpAddDataHead; pData = fpAddDataHead;
while (pData != NULL) { while (pData != NULL) {
const FileDetails* pDataDetails = NULL; const LocalFileDetails* pDataDetails = NULL;
const FileDetails* pRsrcDetails = NULL; const LocalFileDetails* pRsrcDetails = NULL;
const FileDetails* pDetails = pData->GetDetails(); const LocalFileDetails* pDetails = pData->GetDetails();
const char* typeStr = "????"; // for debug msg only const char* typeStr = "????"; // for debug msg only
switch (pDetails->entryKind) { switch (pDetails->GetEntryKind()) {
case FileDetails::kFileKindDataFork: case LocalFileDetails::kFileKindDataFork:
pDataDetails = pDetails; pDataDetails = pDetails;
typeStr = "data"; typeStr = "data";
break; break;
case FileDetails::kFileKindRsrcFork: case LocalFileDetails::kFileKindRsrcFork:
pRsrcDetails = pDetails; pRsrcDetails = pDetails;
typeStr = "rsrc"; typeStr = "rsrc";
break; break;
case FileDetails::kFileKindDiskImage: case LocalFileDetails::kFileKindDiskImage:
pDataDetails = pDetails; pDataDetails = pDetails;
typeStr = "disk"; typeStr = "disk";
break; break;
case FileDetails::kFileKindBothForks: case LocalFileDetails::kFileKindBothForks:
case FileDetails::kFileKindDirectory: case LocalFileDetails::kFileKindDirectory:
default: default:
assert(false); assert(false);
return L"internal error"; return L"internal error";
@ -1499,20 +1508,20 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
pDetails = pData->GetOtherFork()->GetDetails(); pDetails = pData->GetOtherFork()->GetDetails();
typeStr = "both"; typeStr = "both";
switch (pDetails->entryKind) { switch (pDetails->GetEntryKind()) {
case FileDetails::kFileKindDataFork: case LocalFileDetails::kFileKindDataFork:
assert(pDataDetails == NULL); assert(pDataDetails == NULL);
pDataDetails = pDetails; pDataDetails = pDetails;
break; break;
case FileDetails::kFileKindRsrcFork: case LocalFileDetails::kFileKindRsrcFork:
assert(pRsrcDetails == NULL); assert(pRsrcDetails == NULL);
pRsrcDetails = pDetails; pRsrcDetails = pDetails;
break; break;
case FileDetails::kFileKindDiskImage: case LocalFileDetails::kFileKindDiskImage:
assert(false); assert(false);
return L"(internal) add other disk error"; return L"(internal) add other disk error";
case FileDetails::kFileKindBothForks: case LocalFileDetails::kFileKindBothForks:
case FileDetails::kFileKindDirectory: case LocalFileDetails::kFileKindDirectory:
default: default:
assert(false); assert(false);
return L"internal error"; return L"internal error";
@ -1520,7 +1529,7 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
} }
LOGI("Adding file '%ls' (%hs)", LOGI("Adding file '%ls' (%hs)",
(LPCWSTR) pDetails->storageName, typeStr); (LPCWSTR) pDetails->GetStrippedLocalPathName(), typeStr);
ASSERT(pDataDetails != NULL || pRsrcDetails != NULL); 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!). * but it could be awkward for HFS (not to mention HFS Plus!).
*/ */
DiskFS::CreateParms parms; 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) if (pRsrcDetails != NULL)
parms.storageType = kNuStorageExtended; parms.storageType = kNuStorageExtended;
else else
parms.storageType = kNuStorageSeedling; parms.storageType = kNuStorageSeedling;
/* use the FS-normalized path here */ /* copy the rest out of the LocalFileDetails */
/* (do we have to? do we want to?) */ parms.fssep = pDetails->GetFssep();
parms.pathName = pData->GetFSNormalPath(); 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; dataLen = rsrcLen = -1;
if (pDataDetails != NULL) { if (pDataDetails != NULL) {
@ -1545,8 +1560,8 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
/* (HA conversion only happens if text conversion happens) */ /* (HA conversion only happens if text conversion happens) */
GenericEntry::ConvertHighASCII convHA; GenericEntry::ConvertHighASCII convHA;
if (addOptsConvEOL == AddFilesDialog::kConvEOLType) { if (addOptsConvEOL == AddFilesDialog::kConvEOLType) {
if (pDataDetails->fileType == kFileTypeTXT || if (pDataDetails->GetFileType() == kFileTypeTXT ||
pDataDetails->fileType == kFileTypeSRC) pDataDetails->GetFileType() == kFileTypeSRC)
{ {
LOGI("Enabling text conversion by type"); LOGI("Enabling text conversion by type");
convEOL = GenericEntry::kConvertEOLOn; convEOL = GenericEntry::kConvertEOLOn;
@ -1559,14 +1574,14 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
else else
convHA = GenericEntry::kConvertHAOff; convHA = GenericEntry::kConvertHAOff;
errMsg = LoadFile(pDataDetails->origName, &dataBuf, &dataLen, errMsg = LoadFile(pDataDetails->GetLocalPathName(), &dataBuf, &dataLen,
convEOL, convHA); convEOL, convHA);
if (!errMsg.IsEmpty()) if (!errMsg.IsEmpty())
goto bail; goto bail;
} }
if (pRsrcDetails != NULL) { if (pRsrcDetails != NULL) {
/* no text conversion on resource forks */ /* no text conversion on resource forks */
errMsg = LoadFile(pRsrcDetails->origName, &rsrcBuf, &rsrcLen, errMsg = LoadFile(pRsrcDetails->GetLocalPathName(), &rsrcBuf, &rsrcLen,
GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff); GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff);
if (!errMsg.IsEmpty()) if (!errMsg.IsEmpty())
goto bail; goto bail;
@ -1575,7 +1590,7 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
/* really ought to do this separately for each thread */ /* really ought to do this separately for each thread */
SET_PROGRESS_BEGIN(); SET_PROGRESS_BEGIN();
CString pathNameW(parms.pathName); CString pathNameW(parms.pathName);
SET_PROGRESS_UPDATE2(0, pDetails->origName, pathNameW); SET_PROGRESS_UPDATE2(0, pDetails->GetLocalPathName(), pathNameW);
DIError dierr; DIError dierr;
dierr = AddForksToDisk(pDiskFS, &parms, dataBuf, dataLen, dierr = AddForksToDisk(pDiskFS, &parms, dataBuf, dataLen,
@ -1954,25 +1969,6 @@ bail:
return dierr; 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) void DiskArchive::AddToAddDataList(FileAddData* pData)
{ {
ASSERT(pData != NULL); 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 * O(n^2) behavior, but I'm expecting N to be relatively small (under
* 1000 in almost all cases). * 1000 in almost all cases).
*/ */
//if (strcasecmp(pData->GetDetails()->storageName, "system\\finder") == 0)
// LOGI("whee");
FileAddData* pSearch = fpAddDataHead; FileAddData* pSearch = fpAddDataHead;
FileDetails::FileKind dataKind, listKind; LocalFileDetails::FileKind dataKind, listKind;
dataKind = pData->GetDetails()->entryKind; dataKind = pData->GetDetails()->GetEntryKind();
while (pSearch != NULL) { while (pSearch != NULL) {
if (pSearch->GetOtherFork() == NULL && if (pSearch->GetOtherFork() == NULL &&
wcscmp(pSearch->GetDetails()->storageName, wcscmp(pSearch->GetDetails()->GetStrippedLocalPathName(),
pData->GetDetails()->storageName) == 0) pData->GetDetails()->GetStrippedLocalPathName()) == 0)
{ {
//NuThreadID dataID = pData->GetDetails()->threadID; //NuThreadID dataID = pData->GetDetails()->threadID;
//NuThreadID listID = pSearch->GetDetails()->threadID; //NuThreadID listID = pSearch->GetDetails()->threadID;
listKind = pSearch->GetDetails()->entryKind; listKind = pSearch->GetDetails()->GetEntryKind();
/* got a name match */ /* got a name match */
if (dataKind != listKind && if (dataKind != listKind &&
(dataKind == FileDetails::kFileKindDataFork || dataKind == FileDetails::kFileKindRsrcFork) && (dataKind == LocalFileDetails::kFileKindDataFork ||
(listKind == FileDetails::kFileKindDataFork || listKind == FileDetails::kFileKindRsrcFork)) dataKind == LocalFileDetails::kFileKindRsrcFork) &&
(listKind == LocalFileDetails::kFileKindDataFork ||
listKind == LocalFileDetails::kFileKindRsrcFork))
{ {
/* looks good, hook it in here instead of the list */ /* looks good, hook it in here instead of the list */
LOGD("--- connecting forks of '%ls' and '%ls'", LOGD("--- connecting forks of '%ls' and '%ls'",
(LPCWSTR) pData->GetDetails()->origName, (LPCWSTR) pData->GetDetails()->GetLocalPathName(),
(LPCWSTR) pSearch->GetDetails()->origName); (LPCWSTR) pSearch->GetDetails()->GetLocalPathName());
pSearch->SetOtherFork(pData); pSearch->SetOtherFork(pData);
return; return;
} }
@ -2299,7 +2295,7 @@ bool DiskArchive::RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED);
goto bail; goto bail;
} }
LOGI("Rename of '%ls' to '%ls' succeeded", LOGD("Rename of '%ls' to '%ls' succeeded",
pEntry->GetDisplayName(), (LPCWSTR) renameDlg.fNewName); pEntry->GetDisplayName(), (LPCWSTR) renameDlg.fNewName);
} else if (result == IDCANCEL) { } else if (result == IDCANCEL) {
LOGI("Canceling out of remaining renames"); LOGI("Canceling out of remaining renames");
@ -2528,7 +2524,7 @@ bool DiskArchive::SetProps(CWnd* pMsgWnd, GenericEntry* pGenericEntry,
DiskImg::FSFormat fsFormat; DiskImg::FSFormat fsFormat;
fsFormat = pFile->GetDiskFS()->GetDiskImg()->GetFSFormat(); fsFormat = pFile->GetDiskFS()->GetDiskImg()->GetFSFormat();
if (fsFormat == DiskImg::kFormatDOS32 || fsFormat == DiskImg::kFormatDOS33) { 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->SetDataForkLen(pFile->GetDataLength());
pEntry->SetCompressedLen(pFile->GetDataSparseLength()); pEntry->SetCompressedLen(pFile->GetDataSparseLength());
pEntry->SetSuspicious(pFile->GetQuality() == A2File::kQualitySuspicious); pEntry->SetSuspicious(pFile->GetQuality() == A2File::kQualitySuspicious);
@ -2559,9 +2555,9 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
* forked or not. * forked or not.
*/ */
LOGI("DiskArchive XferSelection!"); LOGI("DiskArchive XferSelection!");
unsigned char* dataBuf = NULL; uint8_t* dataBuf = NULL;
unsigned char* rsrcBuf = NULL; uint8_t* rsrcBuf = NULL;
FileDetails fileDetails; LocalFileDetails fileDetails;
CString errMsg, extractErrMsg, cmpStr; CString errMsg, extractErrMsg, cmpStr;
CString fixedPathName; CString fixedPathName;
XferStatus retval = kXferFailed; XferStatus retval = kXferFailed;
@ -2590,7 +2586,7 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
*/ */
fixedPathName = pEntry->GetPathName(); fixedPathName = pEntry->GetPathName();
if (fixedPathName.IsEmpty()) if (fixedPathName.IsEmpty())
fixedPathName = _T("(no filename)"); fixedPathName = L"(no filename)";
if (pEntry->GetFSFormat() != DiskImg::kFormatProDOS) if (pEntry->GetFSFormat() != DiskImg::kFormatProDOS)
fixedPathName.Replace(PathProposal::kDefaultStoredFssep, '.'); fixedPathName.Replace(PathProposal::kDefaultStoredFssep, '.');
if (pEntry->GetSubVolName() != NULL) { if (pEntry->GetSubVolName() != NULL) {
@ -2603,7 +2599,7 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
if (pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) { if (pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) {
/* this is the volume dir */ /* this is the volume dir */
LOGI(" XFER not transferring volume dir '%ls'", LOGD(" XFER not transferring volume dir '%ls'",
(LPCWSTR) fixedPathName); (LPCWSTR) fixedPathName);
continue; continue;
} else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) { } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) {
@ -2613,22 +2609,22 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
cmpStr += (char)PathProposal::kDefaultStoredFssep; cmpStr += (char)PathProposal::kDefaultStoredFssep;
if (pSelSet->CountMatchingPrefix(cmpStr) == 0) { if (pSelSet->CountMatchingPrefix(cmpStr) == 0) {
LOGI("FOUND empty dir '%ls'", (LPCWSTR) fixedPathName); LOGD("FOUND empty dir '%ls'", (LPCWSTR) fixedPathName);
cmpStr += kEmptyFolderMarker; cmpStr += kEmptyFolderMarker;
dataBuf = new unsigned char[1]; dataBuf = new unsigned char[1];
dataLen = 0; dataLen = 0;
fileDetails.entryKind = FileDetails::kFileKindDataFork; fileDetails.SetEntryKind(LocalFileDetails::kFileKindDataFork);
fileDetails.storageName = cmpStr; fileDetails.SetStrippedLocalPathName(cmpStr);
fileDetails.fileType = 0; // NON fileDetails.SetFileType(0); // NON
fileDetails.access = fileDetails.SetAccess(
pEntry->GetAccess() | GenericEntry::kAccessInvisible; pEntry->GetAccess() | GenericEntry::kAccessInvisible);
goto have_stuff2; goto have_stuff2;
} else { } 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); (LPCWSTR) fixedPathName);
continue; continue;
} }
@ -2695,38 +2691,42 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
ASSERT(rsrcBuf == NULL); ASSERT(rsrcBuf == NULL);
} }
if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork()) if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork()) {
fileDetails.entryKind = FileDetails::kFileKindBothForks; fileDetails.SetEntryKind(LocalFileDetails::kFileKindBothForks);
else if (pEntry->GetHasDataFork()) } else if (pEntry->GetHasDataFork()) {
fileDetails.entryKind = FileDetails::kFileKindDataFork; fileDetails.SetEntryKind(LocalFileDetails::kFileKindDataFork);
else if (pEntry->GetHasRsrcFork()) } else if (pEntry->GetHasRsrcFork()) {
fileDetails.entryKind = FileDetails::kFileKindRsrcFork; fileDetails.SetEntryKind(LocalFileDetails::kFileKindRsrcFork);
else { } else {
ASSERT(false); 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.SetStrippedLocalPathName(fixedPathName);
fileDetails.fileType = pEntry->GetFileType(); fileDetails.SetFileType(pEntry->GetFileType());
fileDetails.access = pEntry->GetAccess(); fileDetails.SetAccess(pEntry->GetAccess());
have_stuff2: have_stuff2:
fileDetails.fileSysFmt = pEntry->GetSourceFS(); fileDetails.SetFileSysFmt(pEntry->GetSourceFS());
fileDetails.fileSysInfo = PathProposal::kDefaultStoredFssep; fileDetails.SetFssep(PathProposal::kDefaultStoredFssep);
fileDetails.extraType = pEntry->GetAuxType(); fileDetails.SetExtraType(pEntry->GetAuxType());
fileDetails.storageType = kNuStorageUnknown; /* let NufxLib deal */ fileDetails.SetStorageType(kNuStorageUnknown); // let NufxLib deal
NuDateTime ndt;
time_t when; time_t when;
when = time(NULL); when = time(NULL);
UNIXTimeToDateTime(&when, &fileDetails.archiveWhen); UNIXTimeToDateTime(&when, &ndt);
fileDetails.SetArchiveWhen(ndt);
when = pEntry->GetModWhen(); when = pEntry->GetModWhen();
UNIXTimeToDateTime(&when, &fileDetails.modWhen); UNIXTimeToDateTime(&when, &ndt);
fileDetails.SetModWhen(ndt);
when = pEntry->GetCreateWhen(); 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) { if (pActionProgress->SetProgress(0) == IDCANCEL) {
retval = kXferCancelled; retval = kXferCancelled;
goto bail; goto bail;
@ -2780,23 +2780,20 @@ void DiskArchive::XferPrepare(const XferFileOptions* pXferOpts)
fpXferTargetFS = pXferOpts->fpTargetFS; 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) long dataLen, uint8_t** pRsrcBuf, long rsrcLen)
{ {
//const int kFileTypeTXT = 0x04;
DiskFS::CreateParms createParms;
DiskFS* pDiskFS; DiskFS* pDiskFS;
CString errMsg; CString errMsg;
DIError dierr = kDIErrNone; DIError dierr = kDIErrNone;
LOGI(" XFER: transfer '%ls' (dataLen=%ld rsrcLen=%ld)", LOGI(" XFER: transfer '%ls' (dataLen=%ld rsrcLen=%ld)",
(LPCWSTR) pDetails->storageName, dataLen, rsrcLen); (LPCWSTR) pDetails->GetStrippedLocalPathName(), dataLen, rsrcLen);
ASSERT(pDataBuf != NULL); ASSERT(pDataBuf != NULL);
ASSERT(pRsrcBuf != NULL); ASSERT(pRsrcBuf != NULL);
/* fill out CreateParms from FileDetails */ const DiskFS::CreateParms& createParms = pDetails->GetCreateParms();
ConvertFDToCP(pDetails, &createParms);
if (fpXferTargetFS == NULL) if (fpXferTargetFS == NULL)
pDiskFS = fpPrimaryDiskFS; pDiskFS = fpPrimaryDiskFS;
@ -2813,22 +2810,24 @@ CString DiskArchive::XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
* not worth adding a new interface just for that. * not worth adding a new interface just for that.
*/ */
bool srcIsDOS, dstIsDOS; bool srcIsDOS, dstIsDOS;
srcIsDOS = DiskImg::UsesDOSFileStructure(pDetails->fileSysFmt); srcIsDOS = DiskImg::UsesDOSFileStructure(pDetails->GetFileSysFmt());
dstIsDOS = DiskImg::UsesDOSFileStructure(pDiskFS->GetDiskImg()->GetFSFormat()); dstIsDOS = DiskImg::UsesDOSFileStructure(pDiskFS->GetDiskImg()->GetFSFormat());
if (dataLen > 0 && if (dataLen > 0 &&
(pDetails->fileType == kFileTypeTXT || pDetails->fileType == kFileTypeSRC)) (pDetails->GetFileType() == kFileTypeTXT ||
pDetails->GetFileType() == kFileTypeSRC))
{ {
unsigned char* ucp = *pDataBuf; unsigned char* ucp = *pDataBuf;
long len = dataLen; long len = dataLen;
if (srcIsDOS && !dstIsDOS) { if (srcIsDOS && !dstIsDOS) {
LOGD(" Stripping high ASCII from '%ls'", LOGD(" Stripping high ASCII from '%ls'",
(LPCWSTR) pDetails->storageName); (LPCWSTR) pDetails->GetStrippedLocalPathName());
while (len--) while (len--)
*ucp++ &= 0x7f; *ucp++ &= 0x7f;
} else if (!srcIsDOS && dstIsDOS) { } else if (!srcIsDOS && dstIsDOS) {
LOGD(" Adding high ASCII to '%ls'", (LPCWSTR) pDetails->storageName); LOGD(" Adding high ASCII to '%ls'",
(LPCWSTR) pDetails->GetStrippedLocalPathName());
while (len--) { while (len--) {
if (*ucp != '\0') if (*ucp != '\0')
@ -2837,9 +2836,10 @@ CString DiskArchive::XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
} }
} else if (srcIsDOS && dstIsDOS) { } else if (srcIsDOS && dstIsDOS) {
LOGD(" --- not altering DOS-to-DOS text '%ls'", LOGD(" --- not altering DOS-to-DOS text '%ls'",
(LPCWSTR) pDetails->storageName); (LPCWSTR) pDetails->GetStrippedLocalPathName());
} else { } else {
LOGD(" --- non-DOS transfer '%ls'", (LPCWSTR) pDetails->storageName); LOGD(" --- non-DOS transfer '%ls'",
(LPCWSTR) pDetails->GetStrippedLocalPathName());
} }
} }

View File

@ -231,7 +231,7 @@ private:
virtual CString Close(void); virtual CString Close(void);
virtual void XferPrepare(const XferFileOptions* pXferOpts) override; 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; long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override;
virtual void XferAbort(CWnd* pMsgWnd) override; virtual void XferAbort(CWnd* pMsgWnd) override;
virtual void XferFinish(CWnd* pMsgWnd) override; virtual void XferFinish(CWnd* pMsgWnd) override;
@ -253,10 +253,10 @@ private:
*/ */
class FileAddData { class FileAddData {
public: public:
FileAddData(const FileDetails* pDetails, char* fsNormalPath) { FileAddData(const LocalFileDetails* pDetails, char* fsNormalPathMOR) {
fDetails = *pDetails; fDetails = *pDetails;
fFSNormalPath = fsNormalPath; fFSNormalPathMOR = fsNormalPathMOR;
fpOtherFork = NULL; fpOtherFork = NULL;
fpNext = NULL; fpNext = NULL;
} }
@ -267,25 +267,21 @@ private:
FileAddData* GetOtherFork(void) const { return fpOtherFork; } FileAddData* GetOtherFork(void) const { return fpOtherFork; }
void SetOtherFork(FileAddData* pData) { fpOtherFork = pData; } 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 * 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 * 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: private:
// Three filenames stored inside FileDetails: LocalFileDetails fDetails;
// 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
FileDetails fDetails; // The DiskFS-normalized version of the storage name. This is the
CStringA fFSNormalPath; // name as it will appear on the Apple II disk image.
CStringA fFSNormalPathMOR;
FileAddData* fpOtherFork; FileAddData* fpOtherFork;
FileAddData* fpNext; FileAddData* fpNext;
@ -293,7 +289,7 @@ private:
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveDiskImage; } virtual ArchiveKind GetArchiveKind(void) override { return kArchiveDiskImage; }
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails) override; LocalFileDetails* pDetails) override;
/* /*
* Reload the contents of the archive, showing an error message if the * 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 * Replaces pDetails->storageName if the user elects to rename
*/ */
NuResult HandleReplaceExisting(const A2File* pExisting, NuResult HandleReplaceExisting(const A2File* pExisting,
FileDetails* pDetails); LocalFileDetails* pDetails);
/* /*
* Process the list of pending file adds. * Process the list of pending file adds.
@ -394,15 +390,6 @@ private:
*/ */
void FreeAddDataList(void); 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". * Set up a RenameEntryDialog for the entry in "*pEntry".
* *

View File

@ -1287,9 +1287,11 @@ void PathProposal::DenormalizePath(WCHAR* pathBuf)
ch = HexDigit(*srcp) << 4; ch = HexDigit(*srcp) << 4;
srcp++; srcp++;
if (isxdigit((int)*srcp)) { if (isxdigit((int)*srcp)) {
/* valid, output char */ /* valid, output char (unless it's a %00 place-holder) */
ch += HexDigit(*srcp); ch += HexDigit(*srcp);
*dstp++ = ch; if (ch != '\0') {
*dstp++ = ch;
}
srcp++; srcp++;
} else { } else {
/* bogus '%' with trailing hex digit found! */ /* bogus '%' with trailing hex digit found! */

View File

@ -10,6 +10,7 @@
*/ */
#include "stdafx.h" #include "stdafx.h"
#include "GenericArchive.h" #include "GenericArchive.h"
#include "NufxArchive.h"
#include "FileNameConv.h" #include "FileNameConv.h"
#include "ContentList.h" #include "ContentList.h"
#include "Main.h" #include "Main.h"
@ -46,36 +47,34 @@
*/ */
GenericEntry::GenericEntry(void) 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) GenericEntry::~GenericEntry(void)
@ -177,7 +176,7 @@ const WCHAR* GenericEntry::GetFileTypeString(void) const
} }
/*static*/ bool GenericEntry::CheckHighASCII(const uint8_t* buffer, /*static*/ bool GenericEntry::CheckHighASCII(const uint8_t* buffer,
unsigned long count) size_t count)
{ {
/* /*
* (Pulled from NufxLib Funnel.c.) * (Pulled from NufxLib Funnel.c.)
@ -254,7 +253,7 @@ static const char kCharLF = '\n';
static const char kCharCR = '\r'; static const char kCharCR = '\r';
/*static*/ GenericEntry::ConvertEOL GenericEntry::DetermineConversion( /*static*/ GenericEntry::ConvertEOL GenericEntry::DetermineConversion(
const uint8_t* buffer, long count, const uint8_t* buffer, size_t count,
EOLType* pSourceType, ConvertHighASCII* pConvHA) EOLType* pSourceType, ConvertHighASCII* pConvHA)
{ {
/* /*
@ -277,7 +276,7 @@ static const char kCharCR = '\r';
* it will be stripped *before* the EOL determination is made. * it will be stripped *before* the EOL determination is made.
*/ */
ConvertHighASCII wantConvHA = *pConvHA; ConvertHighASCII wantConvHA = *pConvHA;
long bufCount, numBinary, numLF, numCR; size_t bufCount, numBinary, numLF, numCR;
bool isHighASCII; bool isHighASCII;
uint8_t val; uint8_t val;
@ -367,7 +366,7 @@ static inline void PutEOL(FILE* fp)
{ {
int err = 0; int err = 0;
LOGI("+++ WriteConvert conv=%d convHA=%d", *pConv, *pConvHA); LOGD("+++ WriteConvert conv=%d convHA=%d", *pConv, *pConvHA);
if (len == 0) { if (len == 0) {
LOGI("WriteConvert asked to write 0 bytes; returning"); LOGI("WriteConvert asked to write 0 bytes; returning");
@ -396,7 +395,7 @@ static inline void PutEOL(FILE* fp)
*pConvHA = kConvertHAOff; *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(*pConv == kConvertEOLOn || *pConv == kConvertEOLOff);
ASSERT(*pConvHA == kConvertHAOn || *pConvHA == kConvertHAOff); ASSERT(*pConvHA == kConvertHAOn || *pConvHA == kConvertHAOff);
@ -404,7 +403,7 @@ static inline void PutEOL(FILE* fp)
if (*pConv == kConvertEOLOff) { if (*pConv == kConvertEOLOff) {
if (fwrite(buf, len, 1, fp) != 1) { if (fwrite(buf, len, 1, fp) != 1) {
err = errno; err = errno;
LOGI("WriteConvert failed, err=%d", errno); LOGE("WriteConvert failed, err=%d", errno);
} }
} else { } else {
ASSERT(*pConv == kConvertEOLOn); ASSERT(*pConv == kConvertEOLOn);
@ -546,7 +545,7 @@ GenericArchive::CreateIndex(void)
mangle.Delete(idx+1, len-(idx+1)); /* delete out to the end */ mangle.Delete(idx+1, len-(idx+1)); /* delete out to the end */
mangle += kTmpTemplate; mangle += kTmpTemplate;
} }
LOGI("GenDerived: passed '%ls' returned '%ls'", filename, (LPCWSTR) mangle); LOGD("GenDerived: passed '%ls' returned '%ls'", filename, (LPCWSTR) mangle);
return mangle; return mangle;
} }
@ -594,13 +593,9 @@ GenericArchive::CreateIndex(void)
*/ */
/* /*
* This comes straight out of NuLib2, and uses NufxLib data structures. While * Much of this was adapted from NuLib2.
* 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.
*/ */
typedef bool Boolean;
/*static*/ void GenericArchive::UNIXTimeToDateTime(const time_t* pWhen, /*static*/ void GenericArchive::UNIXTimeToDateTime(const time_t* pWhen,
NuDateTime* pDateTime) NuDateTime* pDateTime)
{ {
@ -625,99 +620,6 @@ typedef bool Boolean;
pDateTime->weekDay = ptm->tm_wday +1; 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. * 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) const WCHAR* pathname, CString* pErrMsg)
{ {
NuError err = kNuErrNone; NuError err = kNuErrNone;
Boolean exists, isDir, isReadable; bool exists, isDir, isReadable;
FileDetails details; LocalFileDetails details;
struct _stat sb; struct _stat sb;
ASSERT(pAddOpts != NULL); 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 * filetype and auxtype it has, and whether or not it's actually the
* resource fork of another file. * resource fork of another file.
*/ */
LOGI("+++ ADD '%ls'", pathname); LOGD("+++ ADD '%ls'", pathname);
/* /*
* Fill out the "details" structure. The class has an automatic * Fill out the "details" structure.
* conversion to NuFileDetails, but it relies on the CString storage
* in the FileDetails, so be careful how you use it.
*/ */
err = GetFileDetails(pAddOpts, pathname, &sb, &details); err = details.SetFields(pAddOpts, pathname, &sb);
if (err != kNuErrNone) if (err != kNuErrNone)
goto bail; goto bail;
assert(wcscmp(pathname, details.origName) == 0); assert(wcscmp(pathname, details.GetLocalPathName()) == 0);
err = DoAddFile(pAddOpts, &details); err = DoAddFile(pAddOpts, &details);
if (err == kNuErrSkipped) // ignore "skipped" result if (err == kNuErrSkipped) // ignore "skipped" result
err = kNuErrNone; err = kNuErrNone;
@ -934,80 +834,144 @@ NuError GenericArchive::AddFile(const AddFilesDialog* pAddOpts,
return Win32AddFile(pAddOpts, pathname, pErrMsg); return Win32AddFile(pAddOpts, pathname, pErrMsg);
} }
/* /*
* =========================================================================== * ===========================================================================
* GenericArchive::FileDetails * 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; memset(&fCreateWhen, 0, sizeof(fCreateWhen));
entryKind = kFileKindUnknown; memset(&fModWhen, 0, sizeof(fModWhen));
fileSysFmt = DiskImg::kFormatUnknown; memset(&fArchiveWhen, 0, sizeof(fArchiveWhen));
fileSysInfo = storageType = 0;
access = fileType = extraType = 0; // set these for debugging
memset(&createWhen, 0, sizeof(createWhen)); memset(&fNuFileDetails, 0xcc, sizeof(fNuFileDetails));
memset(&modWhen, 0, sizeof(modWhen)); memset(&fCreateParms, 0xcc, sizeof(&fCreateParms));
memset(&archiveWhen, 0, sizeof(archiveWhen)); 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;
//details.threadID = threadID; ASSERT(pAddOpts != NULL);
switch (entryKind) { ASSERT(pathname != NULL);
case kFileKindDataFork:
details.threadID = kNuThreadIDDataFork; /* 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; break;
case kFileKindBothForks: // not exactly supported, doesn't really matter case GenericEntry::kRsrcThread:
case kFileKindRsrcFork: //pDetails->threadID = kNuThreadIDRsrcFork;
details.threadID = kNuThreadIDRsrcFork; fEntryKind = LocalFileDetails::kFileKindRsrcFork;
break; break;
case kFileKindDiskImage: case GenericEntry::kDiskImageThread:
details.threadID = kNuThreadIDDiskImage; //pDetails->threadID = kNuThreadIDDiskImage;
fEntryKind = LocalFileDetails::kFileKindDiskImage;
break; break;
case kFileKindDirectory:
default: default:
LOGW("Invalid entryKind (%d) for NuFileDetails conversion",
entryKind);
ASSERT(false); ASSERT(false);
details.threadID = 0; // that makes it an old-style comment?! // was initialized to default earlier
break; break;
} }
// The NuFileDetails origName field was added to NufxLib so that CiderPress /*bail:*/
// could receive the original Windows pathname in the NuErrorStatus return kNuErrNone;
// 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.
// TODO: make origName an opaque (void*) field in NufxLib and pass the const NuFileDetails& GenericArchive::LocalFileDetails::GetNuFileDetails()
// wide char representation through {
fOrigNameA = origName; //details.threadID = threadID;
details.origName = fOrigNameA; switch (fEntryKind) {
fStorageNameA = storageName; case kFileKindDataFork:
details.storageNameMOR = fStorageNameA; fNuFileDetails.threadID = kNuThreadIDDataFork;
//details.fileSysID = fileSysID; break;
details.fileSysInfo = fileSysInfo; case kFileKindBothForks: // not exactly supported, doesn't really matter
details.access = access; case kFileKindRsrcFork:
details.fileType = fileType; fNuFileDetails.threadID = kNuThreadIDRsrcFork;
details.extraType = extraType; break;
details.storageType = storageType; case kFileKindDiskImage:
details.createWhen = createWhen; fNuFileDetails.threadID = kNuThreadIDDiskImage;
details.modWhen = modWhen; break;
details.archiveWhen = archiveWhen; case kFileKindDirectory:
default:
LOGW("Invalid entryKind (%d) for NuFileDetails conversion", fEntryKind);
ASSERT(false);
fNuFileDetails.threadID = 0; // that makes it an old-style comment?!
break;
}
switch (fileSysFmt) { 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;
switch (fFileSysFmt) {
case DiskImg::kFormatProDOS: case DiskImg::kFormatProDOS:
case DiskImg::kFormatDOS33: case DiskImg::kFormatDOS33:
case DiskImg::kFormatDOS32: case DiskImg::kFormatDOS32:
@ -1021,42 +985,65 @@ GenericArchive::FileDetails::operator const NuFileDetails() const
//kFormatHighSierra //kFormatHighSierra
case DiskImg::kFormatISO9660: case DiskImg::kFormatISO9660:
/* these map directly */ /* these map directly */
details.fileSysID = (enum NuFileSysID) fileSysFmt; fNuFileDetails.fileSysID = (enum NuFileSysID) fFileSysFmt;
break; break;
case DiskImg::kFormatRDOS33: case DiskImg::kFormatRDOS33:
case DiskImg::kFormatRDOS32: case DiskImg::kFormatRDOS32:
case DiskImg::kFormatRDOS3: case DiskImg::kFormatRDOS3:
/* these look like DOS33, e.g. text is high-ASCII */ /* these look like DOS33, e.g. text is high-ASCII */
details.fileSysID = kNuFileSysDOS33; fNuFileDetails.fileSysID = kNuFileSysDOS33;
break; break;
default: default:
details.fileSysID = kNuFileSysUnknown; fNuFileDetails.fileSysID = kNuFileSysUnknown;
break; break;
} }
// Return stack copy, which copies into compiler temporary with our return fNuFileDetails;
// copy constructor.
return details;
} }
/*static*/ void GenericArchive::FileDetails::CopyFields(FileDetails* pDst, const DiskFS::CreateParms& GenericArchive::LocalFileDetails::GetCreateParms()
const FileDetails* pSrc)
{ {
//pDst->threadID = pSrc->threadID; fCreateParms.pathName = (LPCSTR) fStoragePathNameMOR;
pDst->entryKind = pSrc->entryKind; fCreateParms.fssep = fFssep;
pDst->origName = pSrc->origName; fCreateParms.storageType = fStorageType;
pDst->storageName = pSrc->storageName; fCreateParms.fileType = fFileType;
pDst->fileSysFmt = pSrc->fileSysFmt; fCreateParms.auxType = fExtraType;
pDst->fileSysInfo = pSrc->fileSysInfo; fCreateParms.access = fAccess;
pDst->access = pSrc->access; fCreateParms.createWhen = NufxArchive::DateTimeToSeconds(&fCreateWhen);
pDst->fileType = pSrc->fileType; fCreateParms.modWhen = NufxArchive::DateTimeToSeconds(&fModWhen);
pDst->extraType = pSrc->extraType; return fCreateParms;
pDst->storageType = pSrc->storageType; }
pDst->createWhen = pSrc->createWhen;
pDst->modWhen = pSrc->modWhen; void GenericArchive::LocalFileDetails::GenerateStoragePathName()
pDst->archiveWhen = pSrc->archiveWhen; {
// 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 * (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. * 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; POSITION posn;
posn = pContentList->GetFirstSelectedItemPosition(); posn = pContentList->GetFirstSelectedItemPosition();
@ -1090,7 +1077,7 @@ void SelectionSet::CreateFromSelection(ContentList* pContentList, int threadMask
void SelectionSet::CreateFromAll(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(); int count = pContentList->GetItemCount();
for (int idx = 0; idx < count; idx++) { for (int idx = 0; idx < count; idx++) {
@ -1104,7 +1091,7 @@ void SelectionSet::AddToSet(GenericEntry* pEntry, int threadMask)
{ {
SelectionEntry* pSelEntry; SelectionEntry* pSelEntry;
//LOGI(" Sel '%ls'", pEntry->GetPathName()); LOGV(" Sel '%ls'", pEntry->GetPathName());
if (!(threadMask & GenericEntry::kAllowVolumeDir) && if (!(threadMask & GenericEntry::kAllowVolumeDir) &&
pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir)
@ -1173,7 +1160,7 @@ void SelectionSet::DeleteEntries(void)
SelectionEntry* pEntry; SelectionEntry* pEntry;
SelectionEntry* pNext; SelectionEntry* pNext;
LOGI("Deleting selection entries"); LOGD("Deleting selection entries");
pEntry = GetEntries(); pEntry = GetEntries();
while (pEntry != NULL) { while (pEntry != NULL) {

View File

@ -221,7 +221,6 @@ public:
LONGLONG GetUncompressedLen(void) const { LONGLONG GetUncompressedLen(void) const {
return fDataForkLen + fRsrcForkLen; return fDataForkLen + fRsrcForkLen;
} }
//void SetUncompressedLen(LONGLONG len) { fUncompressedLen = len; }
LONGLONG GetDataForkLen(void) const { return fDataForkLen; } LONGLONG GetDataForkLen(void) const { return fDataForkLen; }
void SetDataForkLen(LONGLONG len) { fDataForkLen = len; } void SetDataForkLen(LONGLONG len) { fDataForkLen = len; }
LONGLONG GetRsrcForkLen(void) const { return fRsrcForkLen; } LONGLONG GetRsrcForkLen(void) const { return fRsrcForkLen; }
@ -259,8 +258,7 @@ public:
/* /*
* Check to see if this is a high-ASCII file. * Check to see if this is a high-ASCII file.
*/ */
static bool CheckHighASCII(const uint8_t* buffer, static bool CheckHighASCII(const uint8_t* buffer, size_t count);
unsigned long count);
/* /*
* Decide, based on the contents of the buffer, whether we should do an * Decide, based on the contents of the buffer, whether we should do an
@ -269,7 +267,7 @@ public:
* Returns kConvEOLOff or kConvEOLOn. * Returns kConvEOLOff or kConvEOLOn.
*/ */
static ConvertEOL DetermineConversion(const uint8_t* buffer, 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 * Write data to a file, possibly converting EOL markers to Windows CRLF
@ -309,7 +307,6 @@ private:
time_t fModWhen; time_t fModWhen;
RecordKind fRecordKind; // forked file, disk image, ?? RecordKind fRecordKind; // forked file, disk image, ??
const WCHAR* fFormatStr; // static str; compression or fs format const WCHAR* fFormatStr; // static str; compression or fs format
//LONGLONG fUncompressedLen;
LONGLONG fDataForkLen; // also for disk images LONGLONG fDataForkLen; // also for disk images
LONGLONG fRsrcForkLen; // set to 0 when nonexistent LONGLONG fRsrcForkLen; // set to 0 when nonexistent
LONGLONG fCompressedLen; // data/disk + rsrc LONGLONG fCompressedLen; // data/disk + rsrc
@ -515,111 +512,202 @@ public:
void AddEntry(GenericEntry* pEntry); void AddEntry(GenericEntry* pEntry);
/* /*
* This class holds details about a file that we're adding. * This class holds details about a file that we're adding from local disk.
*
* 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
*/ */
class FileDetails { class LocalFileDetails {
public: public:
FileDetails(void); LocalFileDetails(void);
virtual ~FileDetails(void) {} virtual ~LocalFileDetails(void) {}
/* /*
* Automatic cast to NuFileDetails. The NuFileDetails structure will * Set the various fields, based on the pathname and characteristics
* have a pointer to at least one of our strings, so structures * of the file.
* 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.
*/ */
operator const NuFileDetails() const; NuError SetFields(const AddFilesDialog* pAddOpts, const WCHAR* pathname,
struct _stat* psb);
/*
* 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);
}
/* /*
* What kind of file this is. Files being added to NuFX from Windows * 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 * separate files. However, files being transferred from a NuFX
* archive, a disk image, or in from the clipboard can be both. * archive, a disk image, or in from the clipboard can be both.
* *
* (NOTE: this gets embedded into clipboard data. If you change * (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, kFileKindUnknown = 0,
kFileKindDataFork, kFileKindDataFork,
kFileKindRsrcFork, kFileKindRsrcFork,
kFileKindDiskImage, kFileKindDiskImage,
kFileKindBothForks, kFileKindBothForks,
kFileKindDirectory, kFileKindDirectory,
} FileKind; };
/* /*
* Data fields. While transitioning from general use of NuFileDetails * Provide operator= and copy constructor.
* (v1.2.x to v2.0) I'm just going to leave these public.
* TODO: make this not public
*/ */
LocalFileDetails& operator=(const LocalFileDetails& src) {
//NuThreadID threadID; /* data, rsrc, disk img? */ if (&src != this)
FileKind entryKind; CopyFields(this, &src);
return *this;
}
LocalFileDetails(const LocalFileDetails& src) {
CopyFields(this, &src);
}
/* /*
* Original full pathname as found on Windows. * Returns a reference to a NuFileDetails structure with the contents
*/ * of the LocalFileDetails.
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.
* *
* 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; * Returns a reference to a NuFileDetails structure with the contents
uint16_t fileSysInfo; /* fssep lurks here */ * of the LocalFileDetails.
uint32_t access; *
uint32_t fileType; * The returned structure may not be used after the LocalFileDetails
uint32_t extraType; * is modified or released.
uint16_t storageType; /* "Unknown" or disk block size */ */
NuDateTime createWhen; const DiskFS::CreateParms& GetCreateParms();
NuDateTime modWhen;
NuDateTime archiveWhen;
/*
* Returns the "local" pathname, i.e. the name of a Windows file.
*/
const CString& GetLocalPathName() const {
return fLocalPathName;
}
// temporary kluge to get things working void SetLocalPathName(const CString& newName) {
mutable CStringA fOrigNameA; fLocalPathName = newName;
mutable CStringA fStorageNameA; }
/*
* 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: 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. * 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. // Prepare for file transfers.
@ -634,7 +722,7 @@ public:
// On success, *pDataBuf and *pRsrcBuf are freed and set to NULL. (It's // On success, *pDataBuf and *pRsrcBuf are freed and set to NULL. (It's
// necessary for the interface to work this way because the NufxArchive // necessary for the interface to work this way because the NufxArchive
// version just tucks the pointers into NufxLib structures.) // 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; long dataLen, uint8_t** pRsrcBuf, long rsrcLen) = 0;
// Abort progress. Not all subclasses are capable of "undo". // Abort progress. Not all subclasses are capable of "undo".
@ -656,16 +744,6 @@ protected:
void ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst); 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. * Prepare a directory for reading.
* *
@ -725,7 +803,7 @@ protected:
* information in "*pDetails" may be modified. * information in "*pDetails" may be modified.
*/ */
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails) = 0; LocalFileDetails* pDetails) = 0;
void SetPathName(const WCHAR* pathName) { void SetPathName(const WCHAR* pathName) {
free(fPathName); free(fPathName);

View File

@ -1844,7 +1844,7 @@ int MainWindow::LoadArchive(const WCHAR* fileName, const WCHAR* extension,
GenericArchive::OpenResult openResult; GenericArchive::OpenResult openResult;
int result = -1; int result = -1;
GenericArchive* pOpenArchive = NULL; GenericArchive* pOpenArchive = NULL;
int origFilterIndex = filterIndex; const int origFilterIndex = filterIndex;
CString errStr, appName; CString errStr, appName;
CheckedLoadString(&appName, IDS_MB_APP_NAME); CheckedLoadString(&appName, IDS_MB_APP_NAME);
@ -1883,7 +1883,7 @@ try_again:
if (filterIndex == kFilterIndexBinaryII) { if (filterIndex == kFilterIndexBinaryII) {
/* try Binary II and nothing else */ /* try Binary II and nothing else */
ASSERT(!createFile); ASSERT(!createFile);
LOGI(" Trying Binary II"); LOGD(" Trying Binary II");
pOpenArchive = new BnyArchive; pOpenArchive = new BnyArchive;
openResult = pOpenArchive->Open(fileName, readOnly, &errStr); openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
if (openResult != GenericArchive::kResultSuccess) { if (openResult != GenericArchive::kResultSuccess) {
@ -1896,7 +1896,7 @@ try_again:
if (filterIndex == kFilterIndexACU) { if (filterIndex == kFilterIndexACU) {
/* try ACU and nothing else */ /* try ACU and nothing else */
ASSERT(!createFile); ASSERT(!createFile);
LOGI(" Trying ACU"); LOGD(" Trying ACU");
pOpenArchive = new AcuArchive; pOpenArchive = new AcuArchive;
openResult = pOpenArchive->Open(fileName, readOnly, &errStr); openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
if (openResult != GenericArchive::kResultSuccess) { if (openResult != GenericArchive::kResultSuccess) {
@ -1909,7 +1909,7 @@ try_again:
if (filterIndex == kFilterIndexDiskImage) { if (filterIndex == kFilterIndexDiskImage) {
/* try various disk image formats */ /* try various disk image formats */
ASSERT(!createFile); ASSERT(!createFile);
LOGI(" Trying disk images"); LOGD(" Trying disk images");
pOpenArchive = new DiskArchive; pOpenArchive = new DiskArchive;
openResult = pOpenArchive->Open(fileName, readOnly, &errStr); openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
@ -1933,13 +1933,13 @@ try_again:
} }
} else if (openResult != GenericArchive::kResultSuccess) { } else if (openResult != GenericArchive::kResultSuccess) {
if (filterIndex != origFilterIndex) { //if (filterIndex != origFilterIndex) {
/* // /*
* Kluge: assume we guessed disk image and were wrong. // * Kluge: assume we guessed disk image and were wrong.
*/ // */
errStr = L"File doesn't appear to be a valid archive" // errStr = L"File doesn't appear to be a valid archive"
L" or disk image."; // L" or disk image.";
} //}
if (!errStr.IsEmpty()) if (!errStr.IsEmpty())
ShowFailureMsg(this, errStr, IDS_FAILED); ShowFailureMsg(this, errStr, IDS_FAILED);
result = -1; result = -1;
@ -1948,7 +1948,7 @@ try_again:
} else } else
if (filterIndex == kFilterIndexNuFX) { if (filterIndex == kFilterIndexNuFX) {
/* try NuFX (including its embedded-in-BNY form) */ /* try NuFX (including its embedded-in-BNY form) */
LOGI(" Trying NuFX"); LOGD(" Trying NuFX");
pOpenArchive = new NufxArchive; pOpenArchive = new NufxArchive;
openResult = pOpenArchive->Open(fileName, readOnly, &errStr); openResult = pOpenArchive->Open(fileName, readOnly, &errStr);

View File

@ -239,10 +239,10 @@ public:
* *
* On failure, returns with an error message in errMsg. * 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* dataBuf, long dataLen,
const uint8_t* rsrcBuf, long rsrcLen, const uint8_t* rsrcBuf, long rsrcLen,
CString& errMsg, CWnd* pDialog); CString* pErrMsg, CWnd* pDialog);
static const WCHAR kOpenNuFX[]; static const WCHAR kOpenNuFX[];
static const WCHAR kOpenBinaryII[]; static const WCHAR kOpenBinaryII[];

View File

@ -1156,51 +1156,57 @@ bail:
} }
NuError NufxArchive::DoAddFile(const AddFilesDialog* pAddOpts, NuError NufxArchive::DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails) LocalFileDetails* pDetails)
{ {
NuError err; NuError err;
NuRecordIdx recordIdx = 0; 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);
if (err == kNuErrNone) { do {
fNumAdded++; // Must re-get the NuFileDetails, because updating the filename for
} else if (err == kNuErrSkipped) { // rename will invalidate the previous version.
/* "maybe overwrite" UI causes this if user declines */ const NuFileDetails& nuFileDetails = pDetails->GetNuFileDetails();
// fall through with the error
LOGI("DoAddFile: skipped '%ls'", (LPCWSTR) pDetails->origName);
} else if (err == kNuErrRecordExists) {
AddClashDialog dlg;
dlg.fWindowsName = pDetails->origName; // TODO(Unicode): NufxLib should accept wide pathname
dlg.fStorageName = pDetails->storageName; CStringA origNameA(pDetails->GetLocalPathName());
if (dlg.DoModal() != IDOK) { CString dummyMsg;
err = kNuErrAborted; if (!PathName::TestNarrowConversion(pDetails->GetLocalPathName(),
origNameA, &dummyMsg)) {
err = kNuErrInvalidFilename;
goto bail_quiet; goto bail_quiet;
} }
if (dlg.fDoRename) { err = NuAddFile(fpArchive, origNameA, &nuFileDetails, false, &recordIdx);
LOGD("add clash: rename to '%ls'", (LPCWSTR) dlg.fNewName);
pDetails->storageName = dlg.fNewName; if (err == kNuErrNone) {
goto retry; fNumAdded++;
} else { } else if (err == kNuErrSkipped) {
LOGD("add clash: skip"); /* "maybe overwrite" UI causes this if user declines */
err = kNuErrSkipped; // fall through with the error
// fall through with error LOGI("DoAddFile: skipped '%ls'", (LPCWSTR) pDetails->GetLocalPathName());
} } else if (err == kNuErrRecordExists) {
} AddClashDialog dlg;
//if (err != kNuErrNone)
// goto bail; 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->SetStrippedLocalPathName(dlg.fNewName);
continue;
} else {
LOGD("add clash: skip");
err = kNuErrSkipped;
// fall through with error
}
}
} while (false);
//bail:
if (err != kNuErrNone && err != kNuErrAborted && err != kNuErrSkipped) { if (err != kNuErrNone && err != kNuErrAborted && err != kNuErrSkipped) {
CString msg; CString msg;
msg.Format(L"Unable to add file '%ls': %hs.", msg.Format(L"Unable to add file '%ls': %hs.",
(LPCWSTR) pDetails->origName, NuStrError(err)); (LPCWSTR) pDetails->GetLocalPathName(), NuStrError(err));
ShowFailureMsg(fpMsgWnd, msg, IDS_FAILED); ShowFailureMsg(fpMsgWnd, msg, IDS_FAILED);
} }
bail_quiet: bail_quiet:
@ -1303,7 +1309,7 @@ NuResult NufxArchive::HandleReplaceExisting(const NuErrorStatus* pErrorStatus)
ASSERT(pErrorStatus->canOverwrite); ASSERT(pErrorStatus->canOverwrite);
ASSERT(pErrorStatus->canSkip); ASSERT(pErrorStatus->canSkip);
ASSERT(pErrorStatus->canAbort); ASSERT(pErrorStatus->canAbort);
ASSERT(!pErrorStatus->canRename); ASSERT(!pErrorStatus->canRename); // TODO: remember why we can't rename
/* no firm policy, ask the user */ /* no firm policy, ask the user */
ConfirmOverwriteDialog confOvwr; ConfirmOverwriteDialog confOvwr;
@ -1313,8 +1319,7 @@ NuResult NufxArchive::HandleReplaceExisting(const NuErrorStatus* pErrorStatus)
confOvwr.fExistingFileModWhen = confOvwr.fExistingFileModWhen =
DateTimeToSeconds(&pErrorStatus->pRecord->recModWhen); DateTimeToSeconds(&pErrorStatus->pRecord->recModWhen);
if (pErrorStatus->origPathname != NULL) { if (pErrorStatus->origPathname != NULL) {
// TODO: use wchar_t instead for origPathname confOvwr.fNewFileSource = (WCHAR*) pErrorStatus->origPathname;
confOvwr.fNewFileSource = (char*) pErrorStatus->origPathname;
PathName checkPath(confOvwr.fNewFileSource); PathName checkPath(confOvwr.fNewFileSource);
confOvwr.fNewFileModWhen = checkPath.GetModWhen(); confOvwr.fNewFileModWhen = checkPath.GetModWhen();
} else { } else {
@ -1858,7 +1863,7 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
* I think this now throws kXferCancelled whenever it's supposed to. Not * I think this now throws kXferCancelled whenever it's supposed to. Not
* 100% sure, but it looks good. * 100% sure, but it looks good.
*/ */
LOGI("NufxArchive XferSelection!"); LOGD("NufxArchive XferSelection!");
XferStatus retval = kXferFailed; XferStatus retval = kXferFailed;
unsigned char* dataBuf = NULL; unsigned char* dataBuf = NULL;
unsigned char* rsrcBuf = NULL; unsigned char* rsrcBuf = NULL;
@ -1870,7 +1875,7 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
for ( ; pSelEntry != NULL; pSelEntry = pSelSet->IterNext()) { for ( ; pSelEntry != NULL; pSelEntry = pSelSet->IterNext()) {
long dataLen=-1, rsrcLen=-1; long dataLen=-1, rsrcLen=-1;
NufxEntry* pEntry = (NufxEntry*) pSelEntry->GetEntry(); NufxEntry* pEntry = (NufxEntry*) pSelEntry->GetEntry();
FileDetails fileDetails; LocalFileDetails fileDetails;
CString errMsg; CString errMsg;
ASSERT(dataBuf == NULL); ASSERT(dataBuf == NULL);
@ -1883,25 +1888,29 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
continue; continue;
} }
LOGI(" XFER converting '%ls'", pEntry->GetDisplayName()); LOGD(" XFER converting '%ls'", pEntry->GetDisplayName());
fileDetails.storageName = pEntry->GetDisplayName(); fileDetails.SetStrippedLocalPathName(pEntry->GetDisplayName());
fileDetails.fileType = pEntry->GetFileType(); fileDetails.SetFssep(PathProposal::kDefaultStoredFssep);
fileDetails.fileSysFmt = DiskImg::kFormatUnknown; fileDetails.SetFileSysFmt(DiskImg::kFormatUnknown);
fileDetails.fileSysInfo = PathProposal::kDefaultStoredFssep; fileDetails.SetFileType(pEntry->GetFileType());
fileDetails.access = pEntry->GetAccess(); fileDetails.SetExtraType(pEntry->GetAuxType());
fileDetails.extraType = pEntry->GetAuxType(); fileDetails.SetAccess(pEntry->GetAccess());
fileDetails.storageType = kNuStorageSeedling; fileDetails.SetStorageType(kNuStorageSeedling);
time_t when; time_t when;
NuDateTime nuwhen;
when = time(NULL); when = time(NULL);
UNIXTimeToDateTime(&when, &fileDetails.archiveWhen); UNIXTimeToDateTime(&when, &nuwhen);
fileDetails.SetArchiveWhen(nuwhen);
when = pEntry->GetModWhen(); when = pEntry->GetModWhen();
UNIXTimeToDateTime(&when, &fileDetails.modWhen); UNIXTimeToDateTime(&when, &nuwhen);
fileDetails.SetModWhen(nuwhen);
when = pEntry->GetCreateWhen(); 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) { if (pActionProgress->SetProgress(0) == IDCANCEL) {
retval = kXferCancelled; retval = kXferCancelled;
goto bail; goto bail;
@ -1975,7 +1984,7 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
goto bail; goto bail;
} }
fileDetails.storageType = kNuStorageExtended; fileDetails.SetStorageType(kNuStorageExtended);
} else { } else {
ASSERT(rsrcBuf == NULL); ASSERT(rsrcBuf == NULL);
} }
@ -2026,22 +2035,23 @@ void NufxArchive::XferPrepare(const XferFileOptions* pXferOpts)
(void) NuSetValue(fpArchive, kNuValueAllowDuplicates, true); (void) NuSetValue(fpArchive, kNuValueAllowDuplicates, true);
} }
CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, CString NufxArchive::XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
long dataLen, unsigned char** pRsrcBuf, long rsrcLen) long dataLen, uint8_t** pRsrcBuf, long rsrcLen)
{ {
NuError nerr; NuError nerr;
const int kFileTypeTXT = 0x04; const int kFileTypeTXT = 0x04;
NuDataSource* pSource = NULL; NuDataSource* pSource = NULL;
CString errMsg; 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", LOGI(" dataBuf=0x%p dataLen=%ld rsrcBuf=0x%p rsrcLen=%ld",
*pDataBuf, dataLen, *pRsrcBuf, rsrcLen); *pDataBuf, dataLen, *pRsrcBuf, rsrcLen);
ASSERT(pDataBuf != NULL); ASSERT(pDataBuf != NULL);
ASSERT(pRsrcBuf != NULL); ASSERT(pRsrcBuf != NULL);
/* NuFX doesn't explicitly store directories */ /* NuFX doesn't explicitly store directories */
if (pDetails->entryKind == FileDetails::kFileKindDirectory) { if (pDetails->GetEntryKind() == LocalFileDetails::kFileKindDirectory) {
delete[] *pDataBuf; delete[] *pDataBuf;
delete[] *pRsrcBuf; delete[] *pRsrcBuf;
*pDataBuf = *pRsrcBuf = NULL; *pDataBuf = *pRsrcBuf = NULL;
@ -2051,11 +2061,6 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
ASSERT(dataLen >= 0 || rsrcLen >= 0); ASSERT(dataLen >= 0 || rsrcLen >= 0);
ASSERT(*pDataBuf != NULL || *pRsrcBuf != NULL); 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 * Odd bit of trivia: NufxLib refuses to accept an fssep of '\0'. It
* really wants to have one. Which is annoying, since files coming * 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 * 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 * 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. * file into a subdirectory once it has been pasted in this way.
*
* TODO: fix this in NufxLib
*/ */
if (NuGetSepFromSysInfo(nuFileDetails.fileSysInfo) == 0) { if (pDetails->GetFssep() == '\0') {
nuFileDetails.fileSysInfo = pDetails->SetFssep(kNufxNoFssep);
NuSetSepInSysInfo(nuFileDetails.fileSysInfo, kNufxNoFssep);
} }
/* add the record; we have "allow duplicates" enabled for clashes */
NuRecordIdx recordIdx;
const NuFileDetails& nuFileDetails = pDetails->GetNuFileDetails();
nerr = NuAddRecord(fpArchive, &nuFileDetails, &recordIdx); nerr = NuAddRecord(fpArchive, &nuFileDetails, &recordIdx);
if (nerr != kNuErrNone) { if (nerr != kNuErrNone) {
if (nerr != kNuErrAborted) { if (nerr != kNuErrAborted) {
@ -2092,13 +2102,13 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
ASSERT(*pDataBuf != NULL); ASSERT(*pDataBuf != NULL);
/* strip the high ASCII from DOS and RDOS text files */ /* strip the high ASCII from DOS and RDOS text files */
if (pDetails->entryKind != FileDetails::kFileKindDiskImage && if (pDetails->GetEntryKind() != LocalFileDetails::kFileKindDiskImage &&
pDetails->fileType == kFileTypeTXT && pDetails->GetFileType() == kFileTypeTXT &&
DiskImg::UsesDOSFileStructure(pDetails->fileSysFmt)) DiskImg::UsesDOSFileStructure(pDetails->GetFileSysFmt()))
{ {
LOGI(" Stripping high ASCII from '%ls'", LOGI(" Stripping high ASCII from '%ls'",
(LPCWSTR) pDetails->storageName); (LPCWSTR) pDetails->GetStrippedLocalPathName());
unsigned char* ucp = *pDataBuf; uint8_t* ucp = *pDataBuf;
long len = dataLen; long len = dataLen;
while (len--) while (len--)
@ -2117,7 +2127,7 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
/* add the data fork, as a disk image if appropriate */ /* add the data fork, as a disk image if appropriate */
NuThreadID targetID; NuThreadID targetID;
if (pDetails->entryKind == FileDetails::kFileKindDiskImage) if (pDetails->GetEntryKind() == LocalFileDetails::kFileKindDiskImage)
targetID = kNuThreadIDDiskImage; targetID = kNuThreadIDDiskImage;
else else
targetID = kNuThreadIDDataFork; targetID = kNuThreadIDDataFork;

View File

@ -171,7 +171,7 @@ private:
CString* pErrMsg); CString* pErrMsg);
virtual void XferPrepare(const XferFileOptions* pXferOpts) override; 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; long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override;
virtual void XferAbort(CWnd* pMsgWnd) override; virtual void XferAbort(CWnd* pMsgWnd) override;
virtual void XferFinish(CWnd* pMsgWnd) override; virtual void XferFinish(CWnd* pMsgWnd) override;
@ -188,7 +188,7 @@ private:
void AddFinish(void); void AddFinish(void);
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails) override; LocalFileDetails* pDetails) override;
/* /*
* Error handler callback for "bulk" adds. * Error handler callback for "bulk" adds.

View File

@ -2468,7 +2468,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
*/ */
dierr = ValidateCreateFormat(); dierr = ValidateCreateFormat();
if (dierr != kDIErrNone) { if (dierr != kDIErrNone) {
LOGI("ERROR: CIC arg validation failed, bailing"); LOGE("ERROR: CIC arg validation failed, bailing");
goto bail; goto bail;
} }
@ -2486,7 +2486,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
fd = open(pathName, O_CREAT | O_EXCL, 0644); fd = open(pathName, O_CREAT | O_EXCL, 0644);
if (fd < 0) { if (fd < 0) {
dierr = (DIError) errno; dierr = (DIError) errno;
LOGI("ERROR: unable to create file '%s' (errno=%d)", LOGE("ERROR: unable to create file '%s' (errno=%d)",
pathName, dierr); pathName, dierr);
goto bail; goto bail;
} }
@ -2627,7 +2627,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
} }
if (fpImageWrapper == NULL) { if (fpImageWrapper == NULL) {
LOGI(" DI couldn't figure out the file format"); LOGW(" DI couldn't figure out the file format");
dierr = kDIErrUnrecognizedFileFmt; dierr = kDIErrUnrecognizedFileFmt;
goto bail; goto bail;
} }
@ -2637,7 +2637,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
dierr = fpImageWrapper->Create(fLength, fPhysical, fOrder, dierr = fpImageWrapper->Create(fLength, fPhysical, fOrder,
fDOSVolumeNum, fpWrapperGFD, &fWrappedLength, &fpDataGFD); fDOSVolumeNum, fpWrapperGFD, &fWrappedLength, &fpDataGFD);
if (dierr != kDIErrNone) { if (dierr != kDIErrNone) {
LOGI("ImageWrapper Create failed, err=%d", dierr); LOGE("ImageWrapper Create failed, err=%d", dierr);
goto bail; goto bail;
} }
assert(fpDataGFD != NULL); 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! assert(!skipFormat); // don't skip low-level nibble formatting!
if (fDOSVolumeNum == kVolumeNumNotSet) { if (fDOSVolumeNum == kVolumeNumNotSet) {
fDOSVolumeNum = kDefaultNibbleVolumeNum; fDOSVolumeNum = kDefaultNibbleVolumeNum;
LOGI(" Using default nibble volume num"); LOGD(" Using default nibble volume num");
} }
dierr = FormatNibbles(fpDataGFD); // write basic nibble stuff dierr = FormatNibbles(fpDataGFD); // write basic nibble stuff
@ -2697,7 +2697,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
*/ */
if (fHasBlocks && fNumBlocks >= 4194304) { // 2GB or larger? if (fHasBlocks && fNumBlocks >= 4194304) { // 2GB or larger?
if (fFileFormat != kFileFormatUnadorned) { if (fFileFormat != kFileFormatUnadorned) {
LOGI("CreateImage: images >= 2GB can only be unadorned"); LOGW("CreateImage: images >= 2GB can only be unadorned");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
} }
@ -2707,14 +2707,14 @@ DIError DiskImg::ValidateCreateFormat(void) const
fOrder == kSectorOrderUnknown || fOrder == kSectorOrderUnknown ||
fFormat == kFormatUnknown) fFormat == kFormatUnknown)
{ {
LOGI("CreateImage: ambiguous format"); LOGW("CreateImage: ambiguous format");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (fOuterFormat != kOuterFormatNone && if (fOuterFormat != kOuterFormatNone &&
fOuterFormat != kOuterFormatGzip && fOuterFormat != kOuterFormatGzip &&
fOuterFormat != kOuterFormatZip) fOuterFormat != kOuterFormatZip)
{ {
LOGI("CreateImage: unsupported outer format %d", fOuterFormat); LOGW("CreateImage: unsupported outer format %d", fOuterFormat);
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (fFileFormat != kFileFormatUnadorned && if (fFileFormat != kFileFormatUnadorned &&
@ -2726,7 +2726,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
fFileFormat != kFileFormatNuFX && fFileFormat != kFileFormatNuFX &&
fFileFormat != kFileFormatDDD) fFileFormat != kFileFormatDDD)
{ {
LOGI("CreateImage: unsupported file format %d", fFileFormat); LOGW("CreateImage: unsupported file format %d", fFileFormat);
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (fFormat != kFormatGenericPhysicalOrd && if (fFormat != kFormatGenericPhysicalOrd &&
@ -2734,7 +2734,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
fFormat != kFormatGenericDOSOrd && fFormat != kFormatGenericDOSOrd &&
fFormat != kFormatGenericCPMOrd) fFormat != kFormatGenericCPMOrd)
{ {
LOGI("CreateImage: may only use 'generic' formats"); LOGW("CreateImage: may only use 'generic' formats");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
@ -2743,25 +2743,25 @@ DIError DiskImg::ValidateCreateFormat(void) const
*/ */
if (fPhysical != kPhysicalFormatSectors) { if (fPhysical != kPhysicalFormatSectors) {
if (fOrder != kSectorOrderPhysical) { if (fOrder != kSectorOrderPhysical) {
LOGI("CreateImage: nibble images are always 'physical' order"); LOGW("CreateImage: nibble images are always 'physical' order");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (GetHasSectors() == false && GetHasNibbles() == false) { 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()); GetHasSectors(), GetHasNibbles());
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (fpNibbleDescr == NULL && GetNumSectPerTrack() > 0) { if (fpNibbleDescr == NULL && GetNumSectPerTrack() > 0) {
LOGI("CreateImage: must provide NibbleDescr for non-sector"); LOGW("CreateImage: must provide NibbleDescr for non-sector");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (fpNibbleDescr != NULL && if (fpNibbleDescr != NULL &&
fpNibbleDescr->numSectors != GetNumSectPerTrack()) fpNibbleDescr->numSectors != GetNumSectPerTrack())
{ {
LOGI("CreateImage: ?? nd->numSectors=%d, GetNumSectPerTrack=%d", LOGW("CreateImage: ?? nd->numSectors=%d, GetNumSectPerTrack=%d",
fpNibbleDescr->numSectors, GetNumSectPerTrack()); fpNibbleDescr->numSectors, GetNumSectPerTrack());
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
@ -2773,14 +2773,14 @@ DIError DiskImg::ValidateCreateFormat(void) const
fpNibbleDescr->encoding != kNibbleEnc62)) fpNibbleDescr->encoding != kNibbleEnc62))
) )
{ {
LOGI("CreateImage: sector count/encoding mismatch"); LOGW("CreateImage: sector count/encoding mismatch");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (GetNumTracks() != kTrackCount525 && if (GetNumTracks() != kTrackCount525 &&
!(GetNumTracks() == 40 && fFileFormat == kFileFormatTrackStar)) !(GetNumTracks() == 40 && fFileFormat == kFileFormatTrackStar))
{ {
LOGI("CreateImage: unexpected track count %ld", GetNumTracks()); LOGW("CreateImage: unexpected track count %ld", GetNumTracks());
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
} }
@ -2788,7 +2788,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
if (fPhysical != kPhysicalFormatSectors && if (fPhysical != kPhysicalFormatSectors &&
fPhysical != kPhysicalFormatNib525_6656) fPhysical != kPhysicalFormatNib525_6656)
{ {
LOGI("CreateImage: 2MG can't handle physical %d", fPhysical); LOGW("CreateImage: 2MG can't handle physical %d", fPhysical);
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
@ -2796,78 +2796,78 @@ DIError DiskImg::ValidateCreateFormat(void) const
(fOrder != kSectorOrderProDOS && (fOrder != kSectorOrderProDOS &&
fOrder != kSectorOrderDOS)) fOrder != kSectorOrderDOS))
{ {
LOGI("CreateImage: 2MG requires DOS or ProDOS ordering"); LOGW("CreateImage: 2MG requires DOS or ProDOS ordering");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
} }
if (fFileFormat == kFileFormatNuFX) { if (fFileFormat == kFileFormatNuFX) {
if (fOuterFormat != kOuterFormatNone) { if (fOuterFormat != kOuterFormatNone) {
LOGI("CreateImage: can't mix NuFX and outer wrapper"); LOGW("CreateImage: can't mix NuFX and outer wrapper");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (fPhysical != kPhysicalFormatSectors) { if (fPhysical != kPhysicalFormatSectors) {
LOGI("CreateImage: NuFX physical must be sectors"); LOGW("CreateImage: NuFX physical must be sectors");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (fOrder != kSectorOrderProDOS) { if (fOrder != kSectorOrderProDOS) {
LOGI("CreateImage: NuFX is always ProDOS-order"); LOGW("CreateImage: NuFX is always ProDOS-order");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
} }
if (fFileFormat == kFileFormatDiskCopy42) { if (fFileFormat == kFileFormatDiskCopy42) {
if (fPhysical != kPhysicalFormatSectors) { if (fPhysical != kPhysicalFormatSectors) {
LOGI("CreateImage: DC42 physical must be sectors"); LOGW("CreateImage: DC42 physical must be sectors");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if ((GetHasBlocks() && GetNumBlocks() != 1600) || if ((GetHasBlocks() && GetNumBlocks() != 1600) ||
(GetHasSectors() && (GetHasSectors() &&
(GetNumTracks() != 200 || GetNumSectPerTrack() != 16))) (GetNumTracks() != 200 || GetNumSectPerTrack() != 16)))
{ {
LOGI("CreateImage: DC42 only for 800K disks"); LOGW("CreateImage: DC42 only for 800K disks");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (fOrder != kSectorOrderProDOS && if (fOrder != kSectorOrderProDOS &&
fOrder != kSectorOrderDOS) // used for UNIDOS disks?? fOrder != kSectorOrderDOS) // used for UNIDOS disks??
{ {
LOGI("CreateImage: DC42 is always ProDOS or DOS"); LOGW("CreateImage: DC42 is always ProDOS or DOS");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
} }
if (fFileFormat == kFileFormatSim2eHDV) { if (fFileFormat == kFileFormatSim2eHDV) {
if (fPhysical != kPhysicalFormatSectors) { if (fPhysical != kPhysicalFormatSectors) {
LOGI("CreateImage: Sim2eHDV physical must be sectors"); LOGW("CreateImage: Sim2eHDV physical must be sectors");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (fOrder != kSectorOrderProDOS) { if (fOrder != kSectorOrderProDOS) {
LOGI("CreateImage: Sim2eHDV is always ProDOS-order"); LOGW("CreateImage: Sim2eHDV is always ProDOS-order");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
} }
if (fFileFormat == kFileFormatTrackStar) { if (fFileFormat == kFileFormatTrackStar) {
if (fPhysical != kPhysicalFormatNib525_Var) { if (fPhysical != kPhysicalFormatNib525_Var) {
LOGI("CreateImage: TrackStar physical must be var-nibbles"); LOGW("CreateImage: TrackStar physical must be var-nibbles");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
} }
if (fFileFormat == kFileFormatFDI) { if (fFileFormat == kFileFormatFDI) {
if (fPhysical != kPhysicalFormatNib525_Var) { if (fPhysical != kPhysicalFormatNib525_Var) {
LOGI("CreateImage: FDI physical must be var-nibbles"); LOGW("CreateImage: FDI physical must be var-nibbles");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
} }
if (fFileFormat == kFileFormatDDD) { if (fFileFormat == kFileFormatDDD) {
if (fPhysical != kPhysicalFormatSectors) { if (fPhysical != kPhysicalFormatSectors) {
LOGI("CreateImage: DDD physical must be sectors"); LOGW("CreateImage: DDD physical must be sectors");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (fOrder != kSectorOrderDOS) { if (fOrder != kSectorOrderDOS) {
LOGI("CreateImage: DDD is always DOS-order"); LOGW("CreateImage: DDD is always DOS-order");
return kDIErrInvalidCreateReq; return kDIErrInvalidCreateReq;
} }
if (!GetHasSectors() || GetNumTracks() != 35 || if (!GetHasSectors() || GetNumTracks() != 35 ||
GetNumSectPerTrack() != 16) 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; return kDIErrInvalidCreateReq;
} }
} }

View File

@ -1159,6 +1159,13 @@ public:
* normalized string. If the buffer is NULL or isn't big enough, no part * 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 * of the normalized path will be copied into the buffer, and a specific
* error (kDIErrDataOverrun) will be returned. * 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, virtual DIError NormalizePath(const char* path, char fssep,
char* normalizedBuf, int* pNormalizedBufLen) char* normalizedBuf, int* pNormalizedBufLen)
@ -1184,9 +1191,9 @@ public:
const char* pathName; // full pathname for file on disk image const char* pathName; // full pathname for file on disk image
char fssep; char fssep;
int storageType; // determines normal, subdir, or forked int storageType; // determines normal, subdir, or forked
long fileType; uint32_t fileType;
long auxType; uint32_t auxType;
int access; uint32_t access;
time_t createWhen; time_t createWhen;
time_t modWhen; time_t modWhen;
} CreateParms; } CreateParms;
@ -1459,10 +1466,13 @@ public:
* NOTE: there is no guarantee that GetPathName will return unique values * NOTE: there is no guarantee that GetPathName will return unique values
* (duplicates are possible). We don't guarantee that you won't get an * (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 * 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 * DOS 3.3, and it's possible for other filesystems to be damaged).
* pathname may receive some minor sanitizing, e.g. removal or conversion *
* of high ASCII and control characters, but some filesystems (like HFS) * The filename returned is defined to be null-terminated Mac OS Roman.
* make use of them. * 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 * We do guarantee that the contents of subdirectories are grouped
* together. This makes it much easier to construct a hierarchy out of * together. This makes it much easier to construct a hierarchy out of
@ -1474,7 +1484,7 @@ public:
virtual char GetFssep(void) const = 0; // '\0' if none virtual char GetFssep(void) const = 0; // '\0' if none
virtual uint32_t GetFileType(void) const = 0; virtual uint32_t GetFileType(void) const = 0;
virtual uint32_t GetAuxType(void) const = 0; virtual uint32_t GetAuxType(void) const = 0;
virtual uint32_t GetAccess(void) const = 0; // ProDOS-style perms virtual uint32_t GetAccess(void) const = 0; // ProDOS-style perms
virtual time_t GetCreateWhen(void) const = 0; virtual time_t GetCreateWhen(void) const = 0;
virtual time_t GetModWhen(void) const = 0; virtual time_t GetModWhen(void) const = 0;
virtual di_off_t GetDataLength(void) const = 0; // len of data fork virtual di_off_t GetDataLength(void) const = 0; // len of data fork

View File

@ -1407,10 +1407,6 @@ private:
/* /*
* Holds DOS files. Works for DOS33, DOS32, and "wide" DOS implementations. * 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 * The embedded address and length fields found in Applesoft, Integer, and
* Binary files are quietly skipped over with the fDataOffset field when * Binary files are quietly skipped over with the fDataOffset field when
* files are read. * files are read.
@ -1470,6 +1466,10 @@ public:
void Dump(void) const; void Dump(void) const;
friend class DiskFSDOS33;
friend class A2FDDOS;
private:
typedef DiskFSDOS33::TrackSector TrackSector; typedef DiskFSDOS33::TrackSector TrackSector;
/* /*
@ -1490,7 +1490,7 @@ public:
// these are computed or determined from the file contents // these are computed or determined from the file contents
uint16_t fAuxType; // addr for bin, etc. 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 fLength; // file length, in bytes
di_off_t fSparseLength; // file length, factoring sparse out di_off_t fSparseLength; // file length, factoring sparse out
@ -1503,7 +1503,6 @@ public:
static void MakeDOSName(char* buf, const char* name); static void MakeDOSName(char* buf, const char* name);
static void TrimTrailingSpaces(char* filename); static void TrimTrailingSpaces(char* filename);
private:
DIError ExtractTSPairs(const uint8_t* sctBuf, TrackSector* tsList, DIError ExtractTSPairs(const uint8_t* sctBuf, TrackSector* tsList,
int* pLastNonZero); int* pLastNonZero);

View File

@ -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); fFd = open(filename, readOnly ? O_RDONLY|O_BINARY : O_RDWR|O_BINARY, 0);
if (fFd < 0) { if (fFd < 0) {
if (errno == EACCES) if (errno == EACCES) {
dierr = kDIErrAccessDenied; 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(); 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); filename, readOnly, dierr);
return dierr; return dierr;
} }
@ -278,7 +283,7 @@ DIError GFDFile::Read(void* buf, size_t length, size_t* pActual)
return kDIErrEOF; return kDIErrEOF;
if (actual < 0) { if (actual < 0) {
dierr = ErrnoOrGeneric(); 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); length, actual, dierr);
return dierr; return dierr;
} }

View File

@ -1097,15 +1097,16 @@ NuError Nu_CloseOutputFile(NuArchive* pArchive, const NuRecord* pRecord,
err = Nu_SetFileDates(pArchive, pRecord, pathnameUNI); err = Nu_SetFileDates(pArchive, pRecord, pathnameUNI);
BailError(err); BailError(err);
err = Nu_SetFileAccess(pArchive, pRecord, pathnameUNI);
BailError(err);
#if defined(MAC_LIKE) #if defined(MAC_LIKE)
/* could also do this earlier and pass the fd for fsetxattr */ /* could also do this earlier and pass the fd for fsetxattr */
/* NOTE: must do this before Nu_SetFileAccess */
err = Nu_SetFinderInfo(pArchive, pRecord, pathnameUNI); err = Nu_SetFinderInfo(pArchive, pRecord, pathnameUNI);
BailError(err); BailError(err);
#endif #endif
err = Nu_SetFileAccess(pArchive, pRecord, pathnameUNI);
BailError(err);
bail: bail:
return kNuErrNone; return kNuErrNone;
} }

View File

@ -429,25 +429,25 @@ union NuDataSink {
* the first arguments to Nu_ReportError, so we don't have to type them * 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 * 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. * 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__
#ifdef HAS__FUNCTION__ # define _FUNCTION_ __FUNCTION__
#define _FUNCTION_ __FUNCTION__ #else
#else # define _FUNCTION_ ""
#define _FUNCTION_ "" #endif
#endif
#define NU_BLOB pArchive, __FILE__, __LINE__, _FUNCTION_, false #define NU_BLOB pArchive, __FILE__, __LINE__, _FUNCTION_, false
#define NU_BLOB_DEBUG pArchive, __FILE__, __LINE__, _FUNCTION_, true #define NU_BLOB_DEBUG pArchive, __FILE__, __LINE__, _FUNCTION_, true
#define NU_NILBLOB NULL, __FILE__, __LINE__, _FUNCTION_, false #define NU_NILBLOB NULL, __FILE__, __LINE__, _FUNCTION_, false
#define DebugShowError(err) \
#ifdef DEBUG_MSGS
# define DebugShowError(err) \
Nu_ReportError(pArchive, __FILE__, __LINE__, _FUNCTION_, \ Nu_ReportError(pArchive, __FILE__, __LINE__, _FUNCTION_, \
true, err, "(DEBUG)"); true, err, "(DEBUG)");
#else #else
#define NU_BLOB pArchive, "", 0, "", false # define DebugShowError(err) ((void)0)
#define NU_BLOB_DEBUG pArchive, "", 0, "", true
#define NU_NILBLOB NULL, "", 0, "", false
#define DebugShowError(err) ((void)0)
#endif #endif
/* /*

View File

@ -76,8 +76,24 @@ void DebugLog::Log(LogSeverity severity, const char* file, int line,
va_list argptr; va_list argptr;
char textBuf[4096]; 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); va_start(argptr, format);
_vsnprintf(textBuf, NELEM(textBuf) - 1, format, argptr); (void) _vsnprintf(textBuf, NELEM(textBuf) - 1, format, argptr);
va_end(argptr); va_end(argptr);
textBuf[NELEM(textBuf) - 1] = '\0'; textBuf[NELEM(textBuf) - 1] = '\0';

View File

@ -127,6 +127,30 @@ public:
*/ */
static const WCHAR* FilenameOnly(const WCHAR* pathname, WCHAR fssep); 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: private:
DECLARE_COPY_AND_OPEQ(PathName) DECLARE_COPY_AND_OPEQ(PathName)