shithub: aacenc

ref: 4da5aaebe94a8a13ceea2c001b1fd5161ef33e9b
dir: /wingui/FilePathCalc.cpp/

View raw version
// FilePathCalc.cpp: implementation of the CFilePathCalc class.
// Author: Torsten Landmann
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "FilePathCalc.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CFilePathCalc::CFilePathCalc()
{

}

CFilePathCalc::~CFilePathCalc()
{

}

bool CFilePathCalc::GetRelativePosition(const CString &oFrom, const CString &oTo, CString &oRelativePath)
{
	CString oFromPath(oFrom);
	CString oToPath(oTo);

	oFromPath.MakeLower();
	oToPath.MakeLower();

	// extract raw paths
	{
		if (!MakePath(oFromPath) || !MakePath(oToPath))
		{
			return false;
		}
	}

	// compare drive letters
	{
		CString oFromDrive;
		CString oToDrive;
		if (!ExtractDrive(oFromPath, oFromDrive) ||
			!ExtractDrive(oToPath, oToDrive))
		{
			return false;
		}

		if (oFromDrive!=oToDrive)
		{
			// drive letters are different

			oRelativePath=oTo;
			return true;
		}
	}

	// determine relative path
	{
		CString oUpPath;
		CString oDownPath;
		CString oBuf1, oBuf2;

		// make paths to same depth
		while (CountBackSlashes(oFromPath)>CountBackSlashes(oToPath))
		{
			oUpPath+="..\\";
			if (!RemoveOneDirFromEnd(oFromPath, oBuf1))
			{
				return false;
			}
		}
		while (CountBackSlashes(oToPath)>CountBackSlashes(oFromPath))
		{
			if (!RemoveOneDirFromEnd(oToPath, oBuf2))
			{
				return false;
			}

			oDownPath=oBuf2+oDownPath;
		}

		// keep track how deep we must go back to find
		// the directory from which we can go towards
		// the target directory
		while (oFromPath!=oToPath)
		{
			oUpPath+="..\\";

			if (!RemoveOneDirFromEnd(oFromPath, oBuf1))
			{
				return false;
			}
			if (!RemoveOneDirFromEnd(oToPath, oBuf2))
			{
				return false;
			}

			oDownPath=oBuf2+oDownPath;
		}

		CString oToFileName;
		if (!ExtractFileName(oTo, oToFileName))
		{
			return false;
		}

		oRelativePath=oUpPath+oDownPath+oToFileName;
	}

	return true;
}

bool CFilePathCalc::ApplyRelativePath(
	const CString &oRelativePath, CString &oAbsolutePath)
{
	CString oRelativePathCpy(oRelativePath);
	if (oRelativePathCpy.Find(':')==1)
	{
		// relative path contains a drive specificiation

		oAbsolutePath=oRelativePath;

		return true;
	}

	// delete ".\"s
	{
		while (oRelativePathCpy.Find(".\\")==0)
		{
			oRelativePathCpy.Delete(0, 2);
		}
	}

	if (oRelativePathCpy.Find('\\')==0)
	{
		// relative path begins with a back slash

		CString oDrive;
		if (!ExtractDrive(oAbsolutePath, oDrive))
		{
			return false;
		}

		oAbsolutePath=oDrive+oRelativePathCpy;
		
		return true;
	}

	CString oBuf1;
	while (oRelativePathCpy.Find("..\\")!=-1)
	{
		// delete first three characters
		oRelativePathCpy.Delete(0, 3);

		if (!RemoveOneDirFromEnd(oAbsolutePath, oBuf1))
		{
			return false;
		}
	}

	oAbsolutePath+=oRelativePathCpy;

	return true;
}


bool CFilePathCalc::MakePath(CString &oStr, bool bAssumeDirectory)
{
	long lCharPos, lCharPos2;

	if ((lCharPos=oStr.Find(':'))!=-1)
	{
		// there is a colon in the string

		if (oStr.Find(':', lCharPos+1)!=-1)
		{
			// error case: more than one colon in the string
			return false;
		}

		if (oStr.GetAt(lCharPos+1)!='\\')
		{
			// the colon is not followed by a back slash
			if (oStr.GetLength()>lCharPos+2)
			{
				// error case
				// something like "c:windows" was passed on
				return false;
			}
			else
			{
				// something like "c:" was passed on
				oStr+="\\";
				return true;
			}
		}
		else
		{
			// the colon is followed by a back slash

			if ((lCharPos=oStr.ReverseFind('.'))!=-1)
			{
				// the path description contains at least one dot

				if ((lCharPos2=oStr.ReverseFind('\\'))!=-1)
				{
					if (lCharPos2>lCharPos || bAssumeDirectory)
					{
						// there is a back slash behind the last
						// dot or we shall assume there is one

						if (oStr.GetAt(oStr.GetLength()-1)!='\\')
						{
							// last character is not yet a backslash but
							// it must become one
							
							// append a back slash
							oStr+='\\';
							
							return true;
						}

						// path name is ok
						return true;
					}
					else
					{
						// there is no back slash behind the last
						// dot -> the path contains a file name;

						// remove the file name;
						// terminate behind last back slash
						oStr.GetBuffer(0);
						oStr.SetAt(lCharPos2+1, '\0');
						oStr.ReleaseBuffer();
						return true;
					}
				}
				else
				{
					// error case: no backslash in path name
					return false;
				}
			}
			else
			{
				// the path description does not contain a dot

				if (oStr.GetAt(oStr.GetLength()-1)!='\\')
				{
					// last character is not a back slash

					// append a back slash
					oStr+='\\';
					return true;
				}
				else
				{
					// last character is a back slash
					return true;
				}
			}
		}
	}
	else
	{
		// error case: no colon in string
		return false;
	}
}

bool CFilePathCalc::ExtractFileName(const CString &oPath, CString &oFileName)
{
	if (&oPath!=&oFileName)
	{
		oFileName=oPath;
	}

	long lBackSlashPos=oFileName.ReverseFind('\\');
	if (lBackSlashPos==-1)
	{
		// the path did not contain a back slash

		if (oFileName.Find(':')!=-1)
		{
			// the path contains a drive letter

			// delete first two characters
			oFileName.Delete(0, 2);

			return true;
		}
		else
		{
			// the path did not contain a colon

			return true;
		}
	}
	else
	{
		// the path contains at least one backslash

		// delete all characters up to the last back slash
		oFileName.Delete(0, lBackSlashPos+1);

		return true;
	}
}

bool CFilePathCalc::ExtractDrive(const CString &oPath, CString &oDrive)
{
	if (&oPath!=&oDrive)
	{
		oDrive=oPath;
	}

	if (oDrive.Find(':')==-1)
	{
		return false;
	}

	oDrive.GetBuffer(0);
	oDrive.SetAt(2, '\0');
	oDrive.ReleaseBuffer();

	return true;
}

bool CFilePathCalc::SplitFileAndExtension(const CString &oFileName, CString &oSimpleFileName, CString &oExtension)
{
	int iLastBackslashPos=oFileName.ReverseFind('\\');
	int iLastDotPos=oFileName.ReverseFind('.');
	int iStringLength=oFileName.GetLength();
	
	oSimpleFileName=oFileName;
	if (iLastBackslashPos>=0 && iLastDotPos>=0 && iLastBackslashPos>iLastDotPos)
	{
		// the last dot is BEFORE the last backslash
		oExtension.Empty();
		return true;
	}
	
	if (iLastDotPos==-1)
	{
		// there is no dot	
		oExtension.Empty();
		return true;
	}
	else
	{
		// there is a dot
		oSimpleFileName=oFileName;
		oExtension=oSimpleFileName.Mid(iLastDotPos+1, iStringLength-iLastDotPos-1);
		oSimpleFileName.Delete(iLastDotPos, iStringLength-iLastDotPos);
		return true;
	}

	return false;
}

bool CFilePathCalc::IsValidFileMask(const CString &oFileMask, bool bAcceptDirectories)
{
	CString oFileName;
	if (!ExtractFileName(oFileMask, oFileName)) return false;
	if (!bAcceptDirectories && oFileName!=oFileMask) return false;		// contains a directory?

	if (oFileName.IsEmpty()) return false;		// "too short"

	// make sure there's at least one joker
	CString oFileNameTemp(oFileName);
	FilterString(oFileNameTemp, "*?");
	if (oFileNameTemp.IsEmpty()) return false;

	CString oTemp(oFileName);
	FilterStringInverse(oTemp, "/\\:\"<>|");		// these two are invalid for files but not for filters: ?*
	if (oFileName!=oTemp) return false;			// file path contained invalid characters
	
	// make sure no characters are between an asterisk and the next period
	int iCurPos=oFileName.Find('*');
	while (iCurPos>=0 && iCurPos<oFileName.GetLength()-1)
	{
		if (oFileName[iCurPos+1]!='.') return false;
		iCurPos=oFileName.Find('*', iCurPos+1);
	}

	// make sure the file doesn't end with a period
	if (oFileName[oFileName.GetLength()-1]=='.') return false;

	// make sure the extension doesn't contain spaces
	{
		CString oFileNameRaw;
		CString oExtension;
		if (!SplitFileAndExtension(oFileName, oFileNameRaw, oExtension)) return false;
		FilterString(oExtension, " ");
		if (!oExtension.IsEmpty()) return false;		// extension contains spaces
	}

	// passed all tests
	return true;
}

bool CFilePathCalc::RemoveOneDirFromEnd(CString &oPath, CString &oRemoved)
{
	oRemoved=oPath;

	// delete last back slash
	oPath.GetBuffer(0);
	oPath.SetAt(oPath.ReverseFind('\\'), '\0');
	oPath.ReleaseBuffer();
	
	long lLastBackSlashPos=
		oPath.ReverseFind('\\');
	if (lLastBackSlashPos==-1)
	{
		// error: no further back slash
		return false;
	}

	// truncate behind last back slash
	oPath.GetBuffer(0);
	oPath.SetAt(lLastBackSlashPos+1, '\0');
	oPath.ReleaseBuffer();

	oRemoved.Delete(0, lLastBackSlashPos+1);

	return true;
}

long CFilePathCalc::CountBackSlashes(const CString &oPath)
{
	long lCount=0;
	long lCurPos=0;
	CString oDummy(oPath);
	while ((lCurPos=oDummy.Find('\\', lCurPos+1))!=-1)
	{
		lCount++;
	}
	
	return lCount;
}

void CFilePathCalc::FilterString(CString &oString, const CString &oAcceptedChars)
{
	long lCurPos=0;
	while (lCurPos<oString.GetLength())
	{
		if (oAcceptedChars.Find(oString.GetAt(lCurPos))!=-1)
		{
			// character is ok
			lCurPos++;
		}
		else
		{
			// character is not ok
			oString.Delete(lCurPos);
		}
	}
}

void CFilePathCalc::FilterStringInverse(CString &oString, const CString &oCharsToRemove)
{
	long lCurPos=0;
	while (lCurPos<oString.GetLength())
	{
		if (oCharsToRemove.Find(oString.GetAt(lCurPos))==-1)
		{
			// character is ok
			lCurPos++;
		}
		else
		{
			// character is not ok
			oString.Delete(lCurPos);
		}
	}
}