ciderpress/util/PathName.h
Andy McFadden b79498da50 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.
2015-01-08 14:16:20 -08:00

202 lines
6.7 KiB
C++

/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Filename manipulations. Includes some basic file ops (e.g. tests for
* file existence) as well.
*/
#ifndef UTIL_PATHNAME_H
#define UTIL_PATHNAME_H
/*
* Holds a full or partial pathname, manipulating it in various ways.
*
* This creates some hefty buffers for _splitpath() to use, so don't use
* these as storage within other objects.
*
* Functions that return CStrings should have their values assigned into
* CString variables. Attempting to set a "const char*" to them can cause
* problems as the CString being returned is in local storage.
*/
class PathName {
public:
PathName(const WCHAR* pathName = L"", WCHAR fssep = '\\') {
ASSERT(fssep == '\\'); // not handling other cases yet
fPathName = pathName;
fFssep = fssep;
fSplit = false;
}
PathName(const CString& pathName, WCHAR fssep = '\\') {
ASSERT(fssep == '\\'); // not handling other cases yet
fPathName = pathName;
fFssep = fssep;
fSplit = false;
}
~PathName(void) {}
/*
* Name manipulations.
*/
void SetPathName(const WCHAR* pathName, WCHAR fssep = '\\') {
ASSERT(fssep == '\\'); // not handling other cases yet
fPathName = pathName;
fFssep = fssep;
fSplit = false;
}
void SetPathName(const CString& pathName, WCHAR fssep = '\\') {
ASSERT(fssep == '\\'); // not handling other cases yet
fPathName = pathName;
fFssep = fssep;
fSplit = false;
}
// get the full pathname we have stored
CString GetPathName(void) const { return fPathName; }
// create a pathname from a "foreign" OS name
int ConvertFrom(const char* foreignName, char foreignFssep);
// return just the filename: "C:\foo\bar.txt" -> "bar.txt"
CString GetFileName(void);
// return just the extension: C:\foo\bar.txt --> ".txt"
CString GetExtension(void);
// return just the drive component: "C:\foo\bar.txt" --> "C:"
CString GetDriveOnly(void);
// return drive and path component: "C:\foo\bar.txt" -> "C:\foo\"
// (assumes trailing paths end in '\')
CString GetDriveAndPath(void);
CString GetPathOnly(void);
/*
* Expand the short file name of an existing file into its long form.
*
* Returns 0 on success, -1 on failure.
*/
int SFNToLFN(void);
// returns the description of the file type (as seen in explorer)
CString GetDescription(void);
// determine whether or not the file exists
bool Exists(void);
// check the status of a file
int CheckFileStatus(struct _stat* psb, bool* pExists, bool* pIsReadable,
bool* pIsDir);
// get the modification date
time_t GetModWhen(void);
// set the modification date
int SetModWhen(time_t when);
/*
* Create subdirectories, if needed. The paths leading up to the filename
* in "pathname" will be created.
*
* If "pathname" is just a filename, or the set of directories matches
* the last directory we created, we don't do anything.
*
* Returns 0 on success, or a Windows error code on failure.
*/
int CreatePathIFN(void);
/*
* Return the filename extension found in a full pathname.
*
* An extension is the stuff following the last '.' in the filename. If
* there is nothing following the last '.', then there is no extension.
*
* Returns a pointer to the '.' preceding the extension, or NULL if no
* extension was found.
*
* We guarantee that there is at least one character after the '.'.
*/
static const WCHAR* FindExtension(const WCHAR* pathname, WCHAR fssep);
/*
* Find the filename component of a local pathname. Uses the fssep passed
* in. If the fssep is '\0' (as is the case for DOS 3.3), then the entire
* pathname is returned.
*
* Always returns a pointer to a string; never returns NULL.
*/
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)
void SplitIFN(void) {
if (!fSplit) {
_wsplitpath(fPathName, fDrive, fDir, fFileName, fExt);
fSplit = true;
}
}
/*
* Invoke the system-dependent directory creation function.
*/
int Mkdir(const WCHAR* dir);
/*
* Determine if a file exists, and if so whether or not it's a directory.
*
* Set fields you're not interested in to NULL.
*
* On success, returns 0 and fields are set appropriately. On failure,
* returns nonzero and result values are undefined.
*/
int GetFileInfo(const WCHAR* pathname, struct _stat* psb, time_t* pModWhen,
bool* pExists, bool* pIsReadable, bool* pIsDirectory);
/*
* Create a single subdirectory if it doesn't exist. If the next-highest
* subdirectory level doesn't exist either, cut down the pathname and
* recurse.
*
* "pathEnd" points at the last valid character. The length of the valid
* path component is therefore (pathEnd-pathStart+1).
*/
int CreateSubdirIFN(const WCHAR* pathStart, const WCHAR* pathEnd,
WCHAR fssep);
CString fPathName;
WCHAR fFssep;
bool fSplit;
WCHAR fDrive[_MAX_DRIVE]; // 3
WCHAR fDir[_MAX_DIR]; // 256
WCHAR fFileName[_MAX_FNAME]; // 256
WCHAR fExt[_MAX_EXT]; // 256
};
#endif /*UTIL_PATHNAME_H*/