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)
{
return "";
return L"";
}
GenericArchive::OpenResult AcuArchive::Open(const WCHAR* filename,
@ -526,11 +526,11 @@ int AcuArchive::ReadMasterHeader(int* pNumEntries)
header.unknown1 != 1 ||
strcmp((char*) header.fZink, "fZink") != 0)
{
LOGI("Not an ACU archive");
LOGW("Not an ACU archive");
return -1;
}
LOGI("Looks like an ACU archive with %d entries", header.fileCount);
LOGD("Looks like an ACU archive with %d entries", header.fileCount);
*pNumEntries = header.fileCount;
return 0;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -231,7 +231,7 @@ private:
virtual CString Close(void);
virtual void XferPrepare(const XferFileOptions* pXferOpts) override;
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override;
virtual void XferAbort(CWnd* pMsgWnd) override;
virtual void XferFinish(CWnd* pMsgWnd) override;
@ -253,10 +253,10 @@ private:
*/
class FileAddData {
public:
FileAddData(const FileDetails* pDetails, char* fsNormalPath) {
FileAddData(const LocalFileDetails* pDetails, char* fsNormalPathMOR) {
fDetails = *pDetails;
fFSNormalPath = fsNormalPath;
fFSNormalPathMOR = fsNormalPathMOR;
fpOtherFork = NULL;
fpNext = NULL;
}
@ -267,25 +267,21 @@ private:
FileAddData* GetOtherFork(void) const { return fpOtherFork; }
void SetOtherFork(FileAddData* pData) { fpOtherFork = pData; }
const FileDetails* GetDetails(void) const { return &fDetails; }
const LocalFileDetails* GetDetails(void) const { return &fDetails; }
/*
* Get the "FS-normal" path, i.e. exactly what we want to appear
* on the disk image. This has the result of any conversions, so
* we need to store it as a narrow string.
* we need to store it as a narrow Mac OS Roman string.
*/
const char* GetFSNormalPath(void) const { return fFSNormalPath; }
const char* GetFSNormalPath(void) const { return fFSNormalPathMOR; }
private:
// Three filenames stored inside FileDetails:
// fDetails.origName -- the name of the Windows file
// fDetails.storageName -- origName with type-preservation goodies
// stripped out
// fFSNormalPath -- the FS-normalized version of "storageName", i.e.
// the name as it will appear on the Apple II disk image
LocalFileDetails fDetails;
FileDetails fDetails;
CStringA fFSNormalPath;
// The DiskFS-normalized version of the storage name. This is the
// name as it will appear on the Apple II disk image.
CStringA fFSNormalPathMOR;
FileAddData* fpOtherFork;
FileAddData* fpNext;
@ -293,7 +289,7 @@ private:
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveDiskImage; }
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails) override;
LocalFileDetails* pDetails) override;
/*
* Reload the contents of the archive, showing an error message if the
@ -344,7 +340,7 @@ private:
* Replaces pDetails->storageName if the user elects to rename
*/
NuResult HandleReplaceExisting(const A2File* pExisting,
FileDetails* pDetails);
LocalFileDetails* pDetails);
/*
* Process the list of pending file adds.
@ -394,15 +390,6 @@ private:
*/
void FreeAddDataList(void);
/*
* Fill out a CreateParms structure from a FileDetails structure.
*
* The NuStorageType values correspond exactly to ProDOS storage types, so
* there's no need to convert them.
*/
void ConvertFDToCP(const FileDetails* pDetails,
DiskFS::CreateParms* pCreateParms);
/*
* Set up a RenameEntryDialog for the entry in "*pEntry".
*

View File

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

View File

@ -10,6 +10,7 @@
*/
#include "stdafx.h"
#include "GenericArchive.h"
#include "NufxArchive.h"
#include "FileNameConv.h"
#include "ContentList.h"
#include "Main.h"
@ -46,36 +47,34 @@
*/
GenericEntry::GenericEntry(void)
: fPathName(NULL),
fFileName(NULL),
fFileNameExtension(NULL),
fFssep('\0'),
fSubVolName(NULL),
fDisplayName(NULL),
fFileType(0),
fAuxType(0),
fAccess(0),
fCreateWhen(kDateNone),
fModWhen(kDateNone),
fRecordKind(kRecordKindUnknown),
fFormatStr(L"Unknown"),
fDataForkLen(0),
fRsrcForkLen(0),
fCompressedLen(0),
fSourceFS(DiskImg::kFormatUnknown),
fHasDataFork(false),
fHasRsrcFork(false),
fHasDiskImage(false),
fHasComment(false),
fHasNonEmptyComment(false),
fDamaged(false),
fSuspicious(false),
fIndex(-1),
fpPrev(NULL),
fpNext(NULL)
{
fPathName = NULL;
fFileName = NULL;
fFssep = '\0';
fSubVolName = NULL;
fDisplayName = NULL;
fFileType = 0;
fAuxType = 0;
fAccess = 0;
fModWhen = kDateNone;
fCreateWhen = kDateNone;
fRecordKind = kRecordKindUnknown;
fFormatStr = L"Unknown";
fCompressedLen = 0;
//fUncompressedLen = 0;
fDataForkLen = fRsrcForkLen = 0;
fSourceFS = DiskImg::kFormatUnknown;
fHasDataFork = false;
fHasRsrcFork = false;
fHasDiskImage = false;
fHasComment = false;
fHasNonEmptyComment = false;
fIndex = -1;
fpPrev = NULL;
fpNext = NULL;
fDamaged = fSuspicious = false;
}
GenericEntry::~GenericEntry(void)
@ -177,7 +176,7 @@ const WCHAR* GenericEntry::GetFileTypeString(void) const
}
/*static*/ bool GenericEntry::CheckHighASCII(const uint8_t* buffer,
unsigned long count)
size_t count)
{
/*
* (Pulled from NufxLib Funnel.c.)
@ -254,7 +253,7 @@ static const char kCharLF = '\n';
static const char kCharCR = '\r';
/*static*/ GenericEntry::ConvertEOL GenericEntry::DetermineConversion(
const uint8_t* buffer, long count,
const uint8_t* buffer, size_t count,
EOLType* pSourceType, ConvertHighASCII* pConvHA)
{
/*
@ -277,7 +276,7 @@ static const char kCharCR = '\r';
* it will be stripped *before* the EOL determination is made.
*/
ConvertHighASCII wantConvHA = *pConvHA;
long bufCount, numBinary, numLF, numCR;
size_t bufCount, numBinary, numLF, numCR;
bool isHighASCII;
uint8_t val;
@ -367,7 +366,7 @@ static inline void PutEOL(FILE* fp)
{
int err = 0;
LOGI("+++ WriteConvert conv=%d convHA=%d", *pConv, *pConvHA);
LOGD("+++ WriteConvert conv=%d convHA=%d", *pConv, *pConvHA);
if (len == 0) {
LOGI("WriteConvert asked to write 0 bytes; returning");
@ -396,7 +395,7 @@ static inline void PutEOL(FILE* fp)
*pConvHA = kConvertHAOff;
}
}
LOGI("+++ After auto, conv=%d convHA=%d", *pConv, *pConvHA);
LOGD("+++ After auto, conv=%d convHA=%d", *pConv, *pConvHA);
ASSERT(*pConv == kConvertEOLOn || *pConv == kConvertEOLOff);
ASSERT(*pConvHA == kConvertHAOn || *pConvHA == kConvertHAOff);
@ -404,7 +403,7 @@ static inline void PutEOL(FILE* fp)
if (*pConv == kConvertEOLOff) {
if (fwrite(buf, len, 1, fp) != 1) {
err = errno;
LOGI("WriteConvert failed, err=%d", errno);
LOGE("WriteConvert failed, err=%d", errno);
}
} else {
ASSERT(*pConv == kConvertEOLOn);
@ -546,7 +545,7 @@ GenericArchive::CreateIndex(void)
mangle.Delete(idx+1, len-(idx+1)); /* delete out to the end */
mangle += kTmpTemplate;
}
LOGI("GenDerived: passed '%ls' returned '%ls'", filename, (LPCWSTR) mangle);
LOGD("GenDerived: passed '%ls' returned '%ls'", filename, (LPCWSTR) mangle);
return mangle;
}
@ -594,13 +593,9 @@ GenericArchive::CreateIndex(void)
*/
/*
* This comes straight out of NuLib2, and uses NufxLib data structures. While
* it may seem strange to use these structures for non-NuFX archives, they are
* convenient and hold at least as much information as any other format needs.
* Much of this was adapted from NuLib2.
*/
typedef bool Boolean;
/*static*/ void GenericArchive::UNIXTimeToDateTime(const time_t* pWhen,
NuDateTime* pDateTime)
{
@ -625,99 +620,6 @@ typedef bool Boolean;
pDateTime->weekDay = ptm->tm_wday +1;
}
NuError GenericArchive::GetFileDetails(const AddFilesDialog* pAddOpts,
const WCHAR* pathname, struct _stat* psb, FileDetails* pDetails)
{
//char* livePathStr;
time_t now;
ASSERT(pAddOpts != NULL);
ASSERT(pathname != NULL);
ASSERT(pDetails != NULL);
/* init to defaults */
//pDetails->threadID = kNuThreadIDDataFork;
pDetails->entryKind = FileDetails::kFileKindDataFork;
//pDetails->fileSysID = kNuFileSysUnknown;
pDetails->fileSysFmt = DiskImg::kFormatUnknown;
pDetails->fileSysInfo = PathProposal::kDefaultStoredFssep;
pDetails->fileType = 0;
pDetails->extraType = 0;
pDetails->storageType = kNuStorageUnknown; /* let NufxLib worry about it */
if (psb->st_mode & S_IWUSR)
pDetails->access = kNuAccessUnlocked;
else
pDetails->access = kNuAccessLocked;
#if 0
/* if this is a disk image, fill in disk-specific fields */
if (NState_GetModAddAsDisk(pState)) {
if ((psb->st_size & 0x1ff) != 0) {
/* reject anything whose size isn't a multiple of 512 bytes */
printf("NOT storing odd-sized (%ld) file as disk image: %ls\n",
(long)psb->st_size, livePathStr);
} else {
/* set fields; note the "preserve" stuff can override this */
pDetails->threadID = kNuThreadIDDiskImage;
pDetails->storageType = 512;
pDetails->extraType = psb->st_size / 512;
}
}
#endif
now = time(NULL);
UNIXTimeToDateTime(&now, &pDetails->archiveWhen);
UNIXTimeToDateTime(&psb->st_mtime, &pDetails->modWhen);
UNIXTimeToDateTime(&psb->st_ctime, &pDetails->createWhen);
/* get adjusted filename, along with any preserved type info */
PathProposal pathProp;
pathProp.Init(pathname);
pathProp.LocalToArchive(pAddOpts);
/* set up the local and archived pathnames */
pDetails->storageName = "";
if (!pAddOpts->fStoragePrefix.IsEmpty()) {
pDetails->storageName += pAddOpts->fStoragePrefix;
pDetails->storageName += pathProp.fStoredFssep;
}
pDetails->storageName += pathProp.fStoredPathName;
/*
* Fill in the NuFileDetails struct.
*
* We use GetBuffer to get the string to ensure that the CString object
* doesn't do anything while we're not looking. The string won't be
* modified, and it won't be around for very long, so it's not strictly
* necessary to do this. It is, however, the correct approach.
*/
pDetails->origName = pathname;
pDetails->fileSysInfo = pathProp.fStoredFssep;
pDetails->fileType = pathProp.fFileType;
pDetails->extraType = pathProp.fAuxType;
switch (pathProp.fThreadKind) {
case GenericEntry::kDataThread:
//pDetails->threadID = kNuThreadIDDataFork;
pDetails->entryKind = FileDetails::kFileKindDataFork;
break;
case GenericEntry::kRsrcThread:
//pDetails->threadID = kNuThreadIDRsrcFork;
pDetails->entryKind = FileDetails::kFileKindRsrcFork;
break;
case GenericEntry::kDiskImageThread:
//pDetails->threadID = kNuThreadIDDiskImage;
pDetails->entryKind = FileDetails::kFileKindDiskImage;
break;
default:
ASSERT(false);
// was initialized to default earlier
break;
}
/*bail:*/
return kNuErrNone;
}
/*
* Directory structure and functions, based on zDIR in Info-Zip sources.
*/
@ -864,8 +766,8 @@ NuError GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts,
const WCHAR* pathname, CString* pErrMsg)
{
NuError err = kNuErrNone;
Boolean exists, isDir, isReadable;
FileDetails details;
bool exists, isDir, isReadable;
LocalFileDetails details;
struct _stat sb;
ASSERT(pAddOpts != NULL);
@ -901,18 +803,16 @@ NuError GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts,
* filetype and auxtype it has, and whether or not it's actually the
* resource fork of another file.
*/
LOGI("+++ ADD '%ls'", pathname);
LOGD("+++ ADD '%ls'", pathname);
/*
* Fill out the "details" structure. The class has an automatic
* conversion to NuFileDetails, but it relies on the CString storage
* in the FileDetails, so be careful how you use it.
* Fill out the "details" structure.
*/
err = GetFileDetails(pAddOpts, pathname, &sb, &details);
err = details.SetFields(pAddOpts, pathname, &sb);
if (err != kNuErrNone)
goto bail;
assert(wcscmp(pathname, details.origName) == 0);
assert(wcscmp(pathname, details.GetLocalPathName()) == 0);
err = DoAddFile(pAddOpts, &details);
if (err == kNuErrSkipped) // ignore "skipped" result
err = kNuErrNone;
@ -934,80 +834,144 @@ NuError GenericArchive::AddFile(const AddFilesDialog* pAddOpts,
return Win32AddFile(pAddOpts, pathname, pErrMsg);
}
/*
* ===========================================================================
* GenericArchive::FileDetails
* ===========================================================================
*/
GenericArchive::FileDetails::FileDetails(void)
GenericArchive::LocalFileDetails::LocalFileDetails(void)
: fEntryKind(kFileKindUnknown),
fFileSysFmt(DiskImg::kFormatUnknown),
fFssep('\0'),
fFileType(0),
fExtraType(0),
fAccess(0),
fStorageType(0)
{
//threadID = 0;
entryKind = kFileKindUnknown;
fileSysFmt = DiskImg::kFormatUnknown;
fileSysInfo = storageType = 0;
access = fileType = extraType = 0;
memset(&createWhen, 0, sizeof(createWhen));
memset(&modWhen, 0, sizeof(modWhen));
memset(&archiveWhen, 0, sizeof(archiveWhen));
memset(&fCreateWhen, 0, sizeof(fCreateWhen));
memset(&fModWhen, 0, sizeof(fModWhen));
memset(&fArchiveWhen, 0, sizeof(fArchiveWhen));
// set these for debugging
memset(&fNuFileDetails, 0xcc, sizeof(fNuFileDetails));
memset(&fCreateParms, 0xcc, sizeof(&fCreateParms));
fStoragePathNameMOR = "!INIT!";
}
GenericArchive::FileDetails::operator const NuFileDetails() const
NuError GenericArchive::LocalFileDetails::SetFields(const AddFilesDialog* pAddOpts,
const WCHAR* pathname, struct _stat* psb)
{
NuFileDetails details;
time_t now;
//details.threadID = threadID;
switch (entryKind) {
case kFileKindDataFork:
details.threadID = kNuThreadIDDataFork;
ASSERT(pAddOpts != NULL);
ASSERT(pathname != NULL);
/* get adjusted filename, along with any preserved type info */
PathProposal pathProp;
pathProp.Init(pathname);
pathProp.LocalToArchive(pAddOpts);
/* set up the local and archived pathnames */
fLocalPathName = pathname;
fStrippedLocalPathName = L"";
if (!pAddOpts->fStoragePrefix.IsEmpty()) {
fStrippedLocalPathName += pAddOpts->fStoragePrefix;
fStrippedLocalPathName += pathProp.fStoredFssep;
}
fStrippedLocalPathName += pathProp.fStoredPathName;
GenerateStoragePathName();
fFileSysFmt = DiskImg::kFormatUnknown;
fStorageType = kNuStorageUnknown; /* let NufxLib et.al. worry about it */
if (psb->st_mode & S_IWUSR)
fAccess = kNuAccessUnlocked;
else
fAccess = kNuAccessLocked;
fEntryKind = LocalFileDetails::kFileKindDataFork;
fFssep = pathProp.fStoredFssep;
fFileType = pathProp.fFileType;
fExtraType = pathProp.fAuxType;
#if 0
/* if this is a disk image, fill in disk-specific fields */
if (NState_GetModAddAsDisk(pState)) {
if ((psb->st_size & 0x1ff) != 0) {
/* reject anything whose size isn't a multiple of 512 bytes */
printf("NOT storing odd-sized (%ld) file as disk image: %ls\n",
(long)psb->st_size, livePathStr);
} else {
/* set fields; note the "preserve" stuff can override this */
pDetails->threadID = kNuThreadIDDiskImage;
pDetails->storageType = 512;
pDetails->extraType = psb->st_size / 512;
}
}
#endif
now = time(NULL);
UNIXTimeToDateTime(&now, &fArchiveWhen);
UNIXTimeToDateTime(&psb->st_mtime, &fModWhen);
UNIXTimeToDateTime(&psb->st_ctime, &fCreateWhen);
switch (pathProp.fThreadKind) {
case GenericEntry::kDataThread:
//pDetails->threadID = kNuThreadIDDataFork;
fEntryKind = LocalFileDetails::kFileKindDataFork;
break;
case kFileKindBothForks: // not exactly supported, doesn't really matter
case kFileKindRsrcFork:
details.threadID = kNuThreadIDRsrcFork;
case GenericEntry::kRsrcThread:
//pDetails->threadID = kNuThreadIDRsrcFork;
fEntryKind = LocalFileDetails::kFileKindRsrcFork;
break;
case kFileKindDiskImage:
details.threadID = kNuThreadIDDiskImage;
case GenericEntry::kDiskImageThread:
//pDetails->threadID = kNuThreadIDDiskImage;
fEntryKind = LocalFileDetails::kFileKindDiskImage;
break;
case kFileKindDirectory:
default:
LOGW("Invalid entryKind (%d) for NuFileDetails conversion",
entryKind);
ASSERT(false);
details.threadID = 0; // that makes it an old-style comment?!
// was initialized to default earlier
break;
}
// The NuFileDetails origName field was added to NufxLib so that CiderPress
// could receive the original Windows pathname in the NuErrorStatus
// callback. This is a fairly compelling argument for making origName
// an opaque field.
//
// Whatever the case, we need to pass narrow-string versions of the
// names into NufxLib via the NuFileDetails struct. Since we're returning
// the NuFileDetails struct, the strings will out-live the scope of this
// function, so we have to store them somewhere else. There's nowhere
// in NuFileDetails, so we have to keep them in FileDetails, which currently
// exposes all of its fields publicly. I'm going to kluge it for now and
// just "snapshot" the wide strings into narrow-string fields. This is
// all pretty rickety and needs to be redone.
/*bail:*/
return kNuErrNone;
}
// TODO: make origName an opaque (void*) field in NufxLib and pass the
// wide char representation through
fOrigNameA = origName;
details.origName = fOrigNameA;
fStorageNameA = storageName;
details.storageNameMOR = fStorageNameA;
//details.fileSysID = fileSysID;
details.fileSysInfo = fileSysInfo;
details.access = access;
details.fileType = fileType;
details.extraType = extraType;
details.storageType = storageType;
details.createWhen = createWhen;
details.modWhen = modWhen;
details.archiveWhen = archiveWhen;
const NuFileDetails& GenericArchive::LocalFileDetails::GetNuFileDetails()
{
//details.threadID = threadID;
switch (fEntryKind) {
case kFileKindDataFork:
fNuFileDetails.threadID = kNuThreadIDDataFork;
break;
case kFileKindBothForks: // not exactly supported, doesn't really matter
case kFileKindRsrcFork:
fNuFileDetails.threadID = kNuThreadIDRsrcFork;
break;
case kFileKindDiskImage:
fNuFileDetails.threadID = kNuThreadIDDiskImage;
break;
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::kFormatDOS33:
case DiskImg::kFormatDOS32:
@ -1021,42 +985,65 @@ GenericArchive::FileDetails::operator const NuFileDetails() const
//kFormatHighSierra
case DiskImg::kFormatISO9660:
/* these map directly */
details.fileSysID = (enum NuFileSysID) fileSysFmt;
fNuFileDetails.fileSysID = (enum NuFileSysID) fFileSysFmt;
break;
case DiskImg::kFormatRDOS33:
case DiskImg::kFormatRDOS32:
case DiskImg::kFormatRDOS3:
/* these look like DOS33, e.g. text is high-ASCII */
details.fileSysID = kNuFileSysDOS33;
fNuFileDetails.fileSysID = kNuFileSysDOS33;
break;
default:
details.fileSysID = kNuFileSysUnknown;
fNuFileDetails.fileSysID = kNuFileSysUnknown;
break;
}
// Return stack copy, which copies into compiler temporary with our
// copy constructor.
return details;
return fNuFileDetails;
}
/*static*/ void GenericArchive::FileDetails::CopyFields(FileDetails* pDst,
const FileDetails* pSrc)
const DiskFS::CreateParms& GenericArchive::LocalFileDetails::GetCreateParms()
{
//pDst->threadID = pSrc->threadID;
pDst->entryKind = pSrc->entryKind;
pDst->origName = pSrc->origName;
pDst->storageName = pSrc->storageName;
pDst->fileSysFmt = pSrc->fileSysFmt;
pDst->fileSysInfo = pSrc->fileSysInfo;
pDst->access = pSrc->access;
pDst->fileType = pSrc->fileType;
pDst->extraType = pSrc->extraType;
pDst->storageType = pSrc->storageType;
pDst->createWhen = pSrc->createWhen;
pDst->modWhen = pSrc->modWhen;
pDst->archiveWhen = pSrc->archiveWhen;
fCreateParms.pathName = (LPCSTR) fStoragePathNameMOR;
fCreateParms.fssep = fFssep;
fCreateParms.storageType = fStorageType;
fCreateParms.fileType = fFileType;
fCreateParms.auxType = fExtraType;
fCreateParms.access = fAccess;
fCreateParms.createWhen = NufxArchive::DateTimeToSeconds(&fCreateWhen);
fCreateParms.modWhen = NufxArchive::DateTimeToSeconds(&fModWhen);
return fCreateParms;
}
void GenericArchive::LocalFileDetails::GenerateStoragePathName()
{
// TODO(Unicode): generate MOR name from Unicode, instead of just
// doing a generic CP-1252 conversion. We need to do this on both
// sides though, so until we can extract MOR->Unicode we don't
// want to add Unicode->MOR. And it all depends on NufxLib and
// DiskImg being able to handle UTF-16 filenames.
fStoragePathNameMOR = fStrippedLocalPathName;
}
/*static*/ void GenericArchive::LocalFileDetails::CopyFields(LocalFileDetails* pDst,
const LocalFileDetails* pSrc)
{
// don't copy fNuFileDetails, fCreateParms
pDst->fEntryKind = pSrc->fEntryKind;
pDst->fLocalPathName = pSrc->fLocalPathName;
pDst->fStrippedLocalPathName = pSrc->fStrippedLocalPathName;
pDst->fStoragePathNameMOR = pSrc->fStoragePathNameMOR;
pDst->fFileSysFmt = pSrc->fFileSysFmt;
pDst->fFssep = pSrc->fFssep;
pDst->fAccess = pSrc->fAccess;
pDst->fFileType = pSrc->fFileType;
pDst->fExtraType = pSrc->fExtraType;
pDst->fStorageType = pSrc->fStorageType;
pDst->fCreateWhen = pSrc->fCreateWhen;
pDst->fModWhen = pSrc->fModWhen;
pDst->fArchiveWhen = pSrc->fArchiveWhen;
}
@ -1073,7 +1060,7 @@ void SelectionSet::CreateFromSelection(ContentList* pContentList, int threadMask
* (at least under Win2K), which is a good thing. It appears that, if you
* just grab indices 0..N, you will get them in order.
*/
LOGI("CreateFromSelection (threadMask=0x%02x)", threadMask);
LOGD("CreateFromSelection (threadMask=0x%02x)", threadMask);
POSITION posn;
posn = pContentList->GetFirstSelectedItemPosition();
@ -1090,7 +1077,7 @@ void SelectionSet::CreateFromSelection(ContentList* pContentList, int threadMask
void SelectionSet::CreateFromAll(ContentList* pContentList, int threadMask)
{
LOGI("CreateFromAll (threadMask=0x%02x)", threadMask);
LOGD("CreateFromAll (threadMask=0x%02x)", threadMask);
int count = pContentList->GetItemCount();
for (int idx = 0; idx < count; idx++) {
@ -1104,7 +1091,7 @@ void SelectionSet::AddToSet(GenericEntry* pEntry, int threadMask)
{
SelectionEntry* pSelEntry;
//LOGI(" Sel '%ls'", pEntry->GetPathName());
LOGV(" Sel '%ls'", pEntry->GetPathName());
if (!(threadMask & GenericEntry::kAllowVolumeDir) &&
pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir)
@ -1173,7 +1160,7 @@ void SelectionSet::DeleteEntries(void)
SelectionEntry* pEntry;
SelectionEntry* pNext;
LOGI("Deleting selection entries");
LOGD("Deleting selection entries");
pEntry = GetEntries();
while (pEntry != NULL) {

View File

@ -221,7 +221,6 @@ public:
LONGLONG GetUncompressedLen(void) const {
return fDataForkLen + fRsrcForkLen;
}
//void SetUncompressedLen(LONGLONG len) { fUncompressedLen = len; }
LONGLONG GetDataForkLen(void) const { return fDataForkLen; }
void SetDataForkLen(LONGLONG len) { fDataForkLen = len; }
LONGLONG GetRsrcForkLen(void) const { return fRsrcForkLen; }
@ -259,8 +258,7 @@ public:
/*
* Check to see if this is a high-ASCII file.
*/
static bool CheckHighASCII(const uint8_t* buffer,
unsigned long count);
static bool CheckHighASCII(const uint8_t* buffer, size_t count);
/*
* Decide, based on the contents of the buffer, whether we should do an
@ -269,7 +267,7 @@ public:
* Returns kConvEOLOff or kConvEOLOn.
*/
static ConvertEOL DetermineConversion(const uint8_t* buffer,
long count, EOLType* pSourceType, ConvertHighASCII* pConvHA);
size_t count, EOLType* pSourceType, ConvertHighASCII* pConvHA);
/*
* Write data to a file, possibly converting EOL markers to Windows CRLF
@ -309,7 +307,6 @@ private:
time_t fModWhen;
RecordKind fRecordKind; // forked file, disk image, ??
const WCHAR* fFormatStr; // static str; compression or fs format
//LONGLONG fUncompressedLen;
LONGLONG fDataForkLen; // also for disk images
LONGLONG fRsrcForkLen; // set to 0 when nonexistent
LONGLONG fCompressedLen; // data/disk + rsrc
@ -515,111 +512,202 @@ public:
void AddEntry(GenericEntry* pEntry);
/*
* This class holds details about a file that we're adding.
*
* It's based on the NuFileDetails class from NufxLib (which used to be
* used everywhere).
*
* TODO: fix this
* TODO: may want to hold a raw 8-bit pathname for copy & paste, which
* doesn't need to convert in and out of MacRoman
* This class holds details about a file that we're adding from local disk.
*/
class FileDetails {
class LocalFileDetails {
public:
FileDetails(void);
virtual ~FileDetails(void) {}
LocalFileDetails(void);
virtual ~LocalFileDetails(void) {}
/*
* Automatic cast to NuFileDetails. The NuFileDetails structure will
* have a pointer to at least one of our strings, so structures
* filled out this way need to be short-lived. (Yes, this is
* annoying, but it's how NufxLib works.)
*
* TODO: make this a "GenNuFileDetails" method that returns a
* NuFileDetails struct owned by this object. It fills out the
* fields and references internal strings. Because we own the
* object, we can control the lifespan of the NuFileDetails, and
* ensure it doesn't live longer than our strings.
* Set the various fields, based on the pathname and characteristics
* of the file.
*/
operator const NuFileDetails() const;
/*
* Provide operator= and copy constructor. This'd be easier without
* the strings.
*/
FileDetails& operator=(const FileDetails& src) {
if (&src != this)
CopyFields(this, &src);
return *this;
}
FileDetails(const FileDetails& src) {
CopyFields(this, &src);
}
NuError SetFields(const AddFilesDialog* pAddOpts, const WCHAR* pathname,
struct _stat* psb);
/*
* What kind of file this is. Files being added to NuFX from Windows
* can't be "BothForks" because each the forks are stored in
* can't be "BothForks" because the forks are stored in
* separate files. However, files being transferred from a NuFX
* archive, a disk image, or in from the clipboard can be both.
*
* (NOTE: this gets embedded into clipboard data. If you change
* these values, update the version info in Clipboard.cpp.)
* these values, update the version number in Clipboard.cpp.)
*/
typedef enum FileKind {
enum FileKind {
kFileKindUnknown = 0,
kFileKindDataFork,
kFileKindRsrcFork,
kFileKindDiskImage,
kFileKindBothForks,
kFileKindDirectory,
} FileKind;
};
/*
* Data fields. While transitioning from general use of NuFileDetails
* (v1.2.x to v2.0) I'm just going to leave these public.
* TODO: make this not public
* Provide operator= and copy constructor.
*/
//NuThreadID threadID; /* data, rsrc, disk img? */
FileKind entryKind;
LocalFileDetails& operator=(const LocalFileDetails& src) {
if (&src != this)
CopyFields(this, &src);
return *this;
}
LocalFileDetails(const LocalFileDetails& src) {
CopyFields(this, &src);
}
/*
* Original full pathname as found on Windows.
*/
CString origName;
/*
* "Normalized" pathname. This is the full path with any of our
* added bits removed (e.g. file type & fork identifiers). It has
* not been sanitized for any specific target filesystem.
* Returns a reference to a NuFileDetails structure with the contents
* of the LocalFileDetails.
*
* This is generated by PathProposal::LocalToArchive().
* The returned structure may not be used after the LocalFileDetails
* is modified or released.
*/
CString storageName;
const NuFileDetails& GetNuFileDetails();
//NuFileSysID fileSysID;
DiskImg::FSFormat fileSysFmt;
uint16_t fileSysInfo; /* fssep lurks here */
uint32_t access;
uint32_t fileType;
uint32_t extraType;
uint16_t storageType; /* "Unknown" or disk block size */
NuDateTime createWhen;
NuDateTime modWhen;
NuDateTime archiveWhen;
/*
* Returns a reference to a NuFileDetails structure with the contents
* of the LocalFileDetails.
*
* The returned structure may not be used after the LocalFileDetails
* is modified or released.
*/
const DiskFS::CreateParms& GetCreateParms();
/*
* Returns the "local" pathname, i.e. the name of a Windows file.
*/
const CString& GetLocalPathName() const {
return fLocalPathName;
}
// temporary kluge to get things working
mutable CStringA fOrigNameA;
mutable CStringA fStorageNameA;
void SetLocalPathName(const CString& newName) {
fLocalPathName = newName;
}
/*
* This is the "local" pathname with any file type preservation
* strings removed.
*/
const CString& GetStrippedLocalPathName() const {
return fStrippedLocalPathName;
}
/*
* Sets the "stripped" local path name, and updates the MOR version.
* Does not alter the full local path name.
*/
void SetStrippedLocalPathName(const CString& newName) {
fStrippedLocalPathName = newName;
GenerateStoragePathName();
}
/*
* Returns a copy of the stripped local pathname that has been
* converted to Mac OS Roman.
*
* The returned string will be invalid if SetStrippedLocalPathName
* is subsequently called.
*/
const CStringA& GetStoragePathNameMOR() const {
return fStoragePathNameMOR;
}
FileKind GetEntryKind() const { return fEntryKind; }
void SetEntryKind(FileKind kind) { fEntryKind = kind; }
DiskImg::FSFormat GetFileSysFmt() const { return fFileSysFmt; }
void SetFileSysFmt(DiskImg::FSFormat fmt) { fFileSysFmt = fmt; }
char GetFssep() const { return fFssep; }
void SetFssep(char fssep) { fFssep = fssep; }
uint32_t GetFileType() const { return fFileType; }
void SetFileType(uint32_t type) { fFileType = type; }
uint32_t GetExtraType() const { return fExtraType; }
void SetExtraType(uint32_t type) { fExtraType = type; }
uint32_t GetAccess() const { return fAccess; }
void SetAccess(uint32_t access) { fAccess = access; }
uint16_t GetStorageType() const { return fStorageType; }
void SetStorageType(uint16_t type) { fStorageType = type; }
const NuDateTime& GetArchiveWhen() const { return fArchiveWhen; }
void SetArchiveWhen(const NuDateTime& when) { fArchiveWhen = when; }
const NuDateTime& GetModWhen() const { return fModWhen; }
void SetModWhen(const NuDateTime& when) { fModWhen = when; }
const NuDateTime& GetCreateWhen() const { return fCreateWhen; }
void SetCreateWhen(const NuDateTime& when) { fCreateWhen = when; }
private:
/*
* Copy the contents of our object to a new object.
* Copy the contents of our object to a new object, field by field,
* so the CStrings copy correctly.
*
* Useful for operator= and copy construction.
*/
static void CopyFields(FileDetails* pDst, const FileDetails* pSrc);
static void CopyFields(LocalFileDetails* pDst,
const LocalFileDetails* pSrc);
/*
* Generates fStoragePathNameMOR from fStrippedLocalPathName.
*/
void GenerateStoragePathName();
/*
* These are the structs that the libraries want, so we provide calls
* that populate them and return a reference. These are "hairy"
* structures, so we have to place limitations on their lifetime.
*
* Ideally these would either be proper objects with destructors,
* so we could create them and not worry about who will free up the
* hairy bits, or we would omit the hairy bits from the structure and
* pass them separately.
*/
NuFileDetails fNuFileDetails;
DiskFS::CreateParms fCreateParms;
/*
* What does this entry represent?
*/
FileKind fEntryKind;
/*
* Full pathname of the Windows file.
*/
CString fLocalPathName; // was origName
/*
* "Stripped" pathname. This is the full path with any of our
* added bits removed (e.g. file type & fork identifiers).
*
* This is generated by PathProposal::LocalToArchive().
*/
CString fStrippedLocalPathName; // was storageName
/*
* The storage name, generated by converting strippedLocalPathName
* from Unicode to Mac OS Roman. This is what will be passed to
* DiskImg and NufxLib as the name of the file to create in the
* archive. For DiskImg there's an additional "normalization" pass
* make the filename suitable for use on the filesystem; that name
* is stored in a FileAddData object, not here.
*/
CStringA fStoragePathNameMOR;
DiskImg::FSFormat fFileSysFmt; // only set for xfers?
uint8_t fFssep;
uint32_t fFileType;
uint32_t fExtraType;
uint32_t fAccess;
uint16_t fStorageType; // "Unknown" or disk block size
NuDateTime fCreateWhen;
NuDateTime fModWhen;
NuDateTime fArchiveWhen;
};
// Prepare for file transfers.
@ -634,7 +722,7 @@ public:
// On success, *pDataBuf and *pRsrcBuf are freed and set to NULL. (It's
// necessary for the interface to work this way because the NufxArchive
// version just tucks the pointers into NufxLib structures.)
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) = 0;
// Abort progress. Not all subclasses are capable of "undo".
@ -656,16 +744,6 @@ protected:
void ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst);
/*
* Set the contents of a NuFileDetails structure, based on the pathname
* and characteristics of the file.
*
* For efficiency and simplicity, the pathname fields are set to CStrings in
* the GenericArchive object instead of newly-allocated storage.
*/
NuError GetFileDetails(const AddFilesDialog* pAddOpts, const WCHAR* pathname,
struct _stat* psb, FileDetails* pDetails);
/*
* Prepare a directory for reading.
*
@ -725,7 +803,7 @@ protected:
* information in "*pDetails" may be modified.
*/
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails) = 0;
LocalFileDetails* pDetails) = 0;
void SetPathName(const WCHAR* pathName) {
free(fPathName);

View File

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

View File

@ -239,10 +239,10 @@ public:
*
* On failure, returns with an error message in errMsg.
*/
static bool SaveToArchive(GenericArchive::FileDetails* pDetails,
static bool SaveToArchive(GenericArchive::LocalFileDetails* pDetails,
const uint8_t* dataBuf, long dataLen,
const uint8_t* rsrcBuf, long rsrcLen,
CString& errMsg, CWnd* pDialog);
CString* pErrMsg, CWnd* pDialog);
static const WCHAR kOpenNuFX[];
static const WCHAR kOpenBinaryII[];

View File

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

View File

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

View File

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

View File

@ -1159,6 +1159,13 @@ public:
* normalized string. If the buffer is NULL or isn't big enough, no part
* of the normalized path will be copied into the buffer, and a specific
* error (kDIErrDataOverrun) will be returned.
*
* Generally speaking, the normalization process involves separating
* the pathname into its components, modifying each filename as needed
* to make it legal on the current filesystem, and then reassembling
* the components.
*
* The input and output strings are Mac OS Roman.
*/
virtual DIError NormalizePath(const char* path, char fssep,
char* normalizedBuf, int* pNormalizedBufLen)
@ -1184,9 +1191,9 @@ public:
const char* pathName; // full pathname for file on disk image
char fssep;
int storageType; // determines normal, subdir, or forked
long fileType;
long auxType;
int access;
uint32_t fileType;
uint32_t auxType;
uint32_t access;
time_t createWhen;
time_t modWhen;
} CreateParms;
@ -1459,10 +1466,13 @@ public:
* NOTE: there is no guarantee that GetPathName will return unique values
* (duplicates are possible). We don't guarantee that you won't get an
* empty string back (it's valid to have an empty filename in the dir in
* DOS 3.3, and it's possible for other filesystems to be damaged). The
* pathname may receive some minor sanitizing, e.g. removal or conversion
* of high ASCII and control characters, but some filesystems (like HFS)
* make use of them.
* DOS 3.3, and it's possible for other filesystems to be damaged).
*
* The filename returned is defined to be null-terminated Mac OS Roman.
* For most filesystems this is automatic, as filenames are restricted
* to ASCII, but for DOS 3.3 it requires stripping high bits. It also
* means that embedded nulls in HFS filenames (which are discouraged but
* allowed) will be lost.
*
* We do guarantee that the contents of subdirectories are grouped
* together. This makes it much easier to construct a hierarchy out of
@ -1474,7 +1484,7 @@ public:
virtual char GetFssep(void) const = 0; // '\0' if none
virtual uint32_t GetFileType(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 GetModWhen(void) const = 0;
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.
*
* Catalog contents are public so anyone who wants gory details of DOS 3.3
* stuff can poke at whatever they want. Anybody else can use the virtual
* interfaces to get standardized answers for things like file type.
*
* The embedded address and length fields found in Applesoft, Integer, and
* Binary files are quietly skipped over with the fDataOffset field when
* files are read.
@ -1470,6 +1466,10 @@ public:
void Dump(void) const;
friend class DiskFSDOS33;
friend class A2FDDOS;
private:
typedef DiskFSDOS33::TrackSector TrackSector;
/*
@ -1490,7 +1490,7 @@ public:
// these are computed or determined from the file contents
uint16_t fAuxType; // addr for bin, etc.
short fDataOffset; // for 'A'/'B'/'I' with embedded len
uint16_t fDataOffset; // 0/2/4, for 'A'/'B'/'I' with embedded len
di_off_t fLength; // file length, in bytes
di_off_t fSparseLength; // file length, factoring sparse out
@ -1503,7 +1503,6 @@ public:
static void MakeDOSName(char* buf, const char* name);
static void TrimTrailingSpaces(char* filename);
private:
DIError ExtractTSPairs(const uint8_t* sctBuf, TrackSector* tsList,
int* pLastNonZero);

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);
if (fFd < 0) {
if (errno == EACCES)
if (errno == EACCES) {
dierr = kDIErrAccessDenied;
else
} else if (errno == EINVAL) {
// Happens on Win32 if a Unicode filename conversion failed,
// because non-converting chars turn into '?', which is illegal.
dierr = kDIErrInvalidArg;
} else {
dierr = ErrnoOrGeneric();
LOGI(" GDFile Open failed opening '%s', ro=%d (err=%d)",
}
LOGW(" GDFile Open failed opening '%s', ro=%d (err=%d)",
filename, readOnly, dierr);
return dierr;
}
@ -278,7 +283,7 @@ DIError GFDFile::Read(void* buf, size_t length, size_t* pActual)
return kDIErrEOF;
if (actual < 0) {
dierr = ErrnoOrGeneric();
LOGI(" GDFile Read failed on %d bytes (actual=%d, err=%d)",
LOGW(" GDFile Read failed on %d bytes (actual=%d, err=%d)",
length, actual, dierr);
return dierr;
}

View File

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

View File

@ -429,25 +429,25 @@ union NuDataSink {
* the first arguments to Nu_ReportError, so we don't have to type them
* in every time we use the function. It would've been much easier to
* use a gcc-style varargs macro, but not everybody uses gcc.
*
* TODO: Visual C++ has vararg macros now; time to replace this.
*/
#ifdef DEBUG_MSGS
#ifdef HAS__FUNCTION__
#define _FUNCTION_ __FUNCTION__
#else
#define _FUNCTION_ ""
#endif
#ifdef HAS__FUNCTION__
# define _FUNCTION_ __FUNCTION__
#else
# define _FUNCTION_ ""
#endif
#define NU_BLOB pArchive, __FILE__, __LINE__, _FUNCTION_, false
#define NU_BLOB_DEBUG pArchive, __FILE__, __LINE__, _FUNCTION_, true
#define NU_NILBLOB NULL, __FILE__, __LINE__, _FUNCTION_, false
#define DebugShowError(err) \
#define NU_BLOB pArchive, __FILE__, __LINE__, _FUNCTION_, false
#define NU_BLOB_DEBUG pArchive, __FILE__, __LINE__, _FUNCTION_, true
#define NU_NILBLOB NULL, __FILE__, __LINE__, _FUNCTION_, false
#ifdef DEBUG_MSGS
# define DebugShowError(err) \
Nu_ReportError(pArchive, __FILE__, __LINE__, _FUNCTION_, \
true, err, "(DEBUG)");
#else
#define NU_BLOB pArchive, "", 0, "", false
#define NU_BLOB_DEBUG pArchive, "", 0, "", true
#define NU_NILBLOB NULL, "", 0, "", false
#define DebugShowError(err) ((void)0)
# define DebugShowError(err) ((void)0)
#endif
/*

View File

@ -76,8 +76,24 @@ void DebugLog::Log(LogSeverity severity, const char* file, int line,
va_list argptr;
char textBuf[4096];
// TODO: _vsnprintf() doesn't deal with "%ls" well. If it encounters a
// character it can't translate, it stops and returns -1. This is
// very annoying if you're trying to print a wide-char filename that
// includes characters that aren't in CP-1252.
//
// We can probably fix this by converting "format" to a wide string,
// printing with _vsnwprintf(), and then converting the output back
// to narrow manually (selecting options that prevent it from stopping
// mid-string on failure). As an optimization, we only need to do this
// if the format string includes a "%ls" specifier.
//
// The interpretation of plain "%s" will change if we use a wide printf
// function (it's a Microsoft extension, not ANSI behavior), so we'd also
// want to check for "%s" and complain if we see it. All callers must
// use explicit "%hs" or "%ls".
va_start(argptr, format);
_vsnprintf(textBuf, NELEM(textBuf) - 1, format, argptr);
(void) _vsnprintf(textBuf, NELEM(textBuf) - 1, format, argptr);
va_end(argptr);
textBuf[NELEM(textBuf) - 1] = '\0';

View File

@ -127,6 +127,30 @@ public:
*/
static const WCHAR* FilenameOnly(const WCHAR* pathname, WCHAR fssep);
/*
* Test to see if a wide-to-narrow filename conversion failed.
*
* Returns true if all is well, false with *pErrMsg set if something
* went wrong.
*/
static bool TestNarrowConversion(const CString& original,
const CStringA& converted, CString* pErrMsg) {
int index = converted.ReverseFind('?');
if (index < 0) {
// no '?' is good
} else if (index == 2 && converted.Left(4) == "\\\\?\\") {
// Win32 file namespace path strings start with "\\?\". If that's
// the first occurrence of '?', we're still good.
} else {
// This is most likely the result of a failed wide-to-narrow
// string conversion.
pErrMsg->Format(L"Unable to open '%ls' -- Unicode filename "
L"conversion is invalid ('%hs')",
(LPCWSTR) original, (LPCSTR) converted);
return false;
}
return true;
}
private:
DECLARE_COPY_AND_OPEQ(PathName)