Click here to Skip to main content
15,897,518 members
Articles / Desktop Programming / MFC

LintProject - Improving the Usability of PC-Lint with Visual C++ Solutions and Projects

,
Rate me:
Please Sign up or sign in to vote.
4.83/5 (36 votes)
29 Jan 2009CPOL13 min read 282.1K   1.7K   52  
Utility to run PC-Lint on Visual C++ solutions and projects, generating XML and HTML reports of the results.
/************************************************************************
 *
 *  Description : File related utility functions.
 *
 *     (c) Copyright 2000-2008 by Anna-Jayne Metcalfe (anna@riverblade.co.uk)
 *     and Beth Mackenzie (beth@riverblade.co.uk) / Riverblade Limited
 *
 *  Licence Terms:
 *
 *     This code may be freely reused, subject to the licence terms below.
 *     Please do let us know of any bugs you find or improvements you make,
 *     so that we can pass them on to the rest of the development community.
 *
 *     This code is free software; you can redistribute it and/or
 *     modify it under the terms of the Code Project Open License (CPOL)
 *     version 1.0 (http://www.codeproject.com/info/cpol10.aspx).
 *
 *     This code is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     Code Project Open Licence for further details.
 *
 ************************************************************************
 *    $Archive: /Projects/AddIns/Visual Lint/Development/Libraries/Utils/src/FileUtils.cpp $
 *   $Revision: 10 $
 *       $Date: 28/03/08 16:27 $
 *     $Author: Anna $
 * 
 * $Nokeywords: $
 ************************************************************************/

/// \file	
/// \brief File utility functions implementation.

#include "StdAfx.h"
#ifndef __ATLTIME_H__
	#pragma message(__FILE__" : For faster compilation, #include <atltime.h> in stdafx.h")
	#include <atltime.h>
#endif

#ifndef _INC_COMDEF
	#pragma message(__FILE__" : For faster compilation, #include <comdef.h> in stdafx.h")
	#include <comdef.h>				// For _com_error
#endif

#include "StringUtils.h"

#include "FileUtils.h"


namespace Riverblade
{
	namespace Libraries
	{


/////////////////////////////////////////////////////////////////////////////
// Helper functions

/// \brief Determine whether the given file exists.
///
/// \param	sPathName			The pathname of the file.
/// \return						\em true if the file exists; \em false otherwise.
///
bool Utils::FileExists(const CString& sPathName)
{
	if (!sPathName.IsEmpty() )
	{
		WIN32_FIND_DATA FileData; 

		// Use FindFirstFile() to see if the file exists. Note that if a valid
		// handle is returned (i.e. the file is found) the handle must be closed
		// using FindClose() or else a handle leak will result [IMS ID 18]
		const HANDLE hFind = ::FindFirstFile(sPathName, &FileData);
 		if (INVALID_HANDLE_VALUE != hFind)
		{
			ATLVERIFY(::FindClose(hFind) );

			return true;
		}
	}
	return false;
}


/// \brief Determine whether the given folder exists.
///
/// \param	sFolderPath			The pathname of the folder.
/// \return						\em true if the folder exists; \em false otherwise.
///
bool Utils::FolderExists(const CString& sFolderPath)
{
	if (!sFolderPath.IsEmpty() )
	{
		const DWORD dwAttribs = ::GetFileAttributes(sFolderPath);

		if ( (INVALID_FILE_ATTRIBUTES != dwAttribs) && (FILE_ATTRIBUTE_DIRECTORY == (dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ) )
		{
			return true;
		}
	}
	return false;
}


/// \brief Return the last modification time of the given file.
///
/// \param	sPathName			The pathname of the file whose modification time should be tested against.
/// \param	pFileTime			A pointer to an LPFILETIME structure which will receive the modification
///								time of the file.
/// \return						\em true if the operation succeeded; \em false otherwise.
///
bool Utils::GetFileLastWriteTime(const CString& sPathName, LPFILETIME pFileTime)
{
	BOOL bResult = false;

	const HANDLE hFile = ::CreateFile(sPathName,
								GENERIC_READ, 				// Desired Access
								FILE_SHARE_READ,			// Share Mode 
								NULL,						// Security Attributes 
								OPEN_EXISTING,				// Creation Disposition 
								FILE_ATTRIBUTE_NORMAL,		// File AndAttributes,
								NULL);						// Template File
	if (INVALID_HANDLE_VALUE != hFile)
	{
		bResult = ::GetFileTime(hFile,
								NULL,						// Creation time
								NULL,						// Last access time 
								pFileTime);					// Last write time

		ATLVERIFY(::CloseHandle(hFile) );
	}

#ifdef _DEBUG
	if (!bResult)
	{
		const DWORD dwError = ::GetLastError();

		//lint -save -e437 (Warning -- Passing struct 'CStringT' to ellipsis)
		ATLTRACE( _T("WARNING: GetFileLastWriteTime(%s) failed with error code %x\n"), sPathName, dwError);
		//lint -restore
	}
#endif

	//lint -save -e731 (Info -- Boolean argument to equal/not equal)
	return (false != bResult);
	//lint -restore
}


/// \brief Determine whether the given file has a modification date which is newer than the given time.
///
/// \param	sPathName			The pathname of the file whose modification time should be tested against.
/// \param	pFileTimeCompare	A pointer to an LPFILETIME structure holding the timestamp to compare against.
/// \return						\em true if the file has been modified since the time given in pFileTimeCompare;
///								\em false otherwise.
///
bool Utils::IsFileWriteTimeNewerThan(const CString& sPathName, const LPFILETIME pFileTimeCompare)
{
	if (FileExists(sPathName) )
	{
		CFileTime timeFileModified;
		ATLVERIFY(GetFileLastWriteTime(sPathName, &timeFileModified) );

		if (CompareFileTime(&timeFileModified, pFileTimeCompare) > 0)
		{
			// The object file is newer than the results file
			return true;
		}
	}
	return false;
}	//lint !e818	(Information -- Pointer parameter 'pFileTimeCompare' could be declared as pointing to const)


/// \brief Generate a pathname for a temporary file in the specified folder.
///
/// \param	pszPrefixString		The prefix string which should be used in the filename.
/// \param	pszFolder			The folder within which the file will be created. 
/// \param	uUnique				Unsigned integer to be used in creating the temporary file name.
///								If uUnique is zero, the function attempts to form a unique file name
///								using the current system time. If the file already exists, the number
///								is increased by one and the functions tests if this file already exists.
///								This continues until a unique filename is found; the function creates
///								a file by that name and closes it.
///
///								Note that the function does not attempt to verify the uniqueness of the
///								file name when uUnique is nonzero.
/// \return						The pathname of the temporary file.
///
CString Utils::GetTempFilePathName(LPCTSTR const pszPrefixString, LPCTSTR const pszFolder /*= NULL*/, UINT uUnique /*= 0*/)
{
	CString sFolder(pszFolder);

	if (sFolder.IsEmpty() )
	{
		TCHAR szTempFolder[_MAX_PATH + 1];
		(void)::GetTempPath(_MAX_PATH, szTempFolder);

		sFolder = szTempFolder;
	}
	ATLASSERT(FolderExists(sFolder) );

	CString sTempPathName;
	LPTSTR const pszTempPathName = sTempPathName.GetBuffer(_MAX_PATH + 1);

	(void)::GetTempFileName(sFolder,
							pszPrefixString,
							uUnique,
							pszTempPathName);

	sTempPathName.ReleaseBuffer();

	return sTempPathName;
}  
  

/// \brief Read the contents of the given text file to a string array.
///
/// \param	sPathName			The pathname of the file.
/// \param	rarrayText			A reference to an array object which will receive a copy of the contents
///								from the file if the operation succeeds.
/// \param	puLength			A pointer to a ULONGLONG which will receive the size of the file on completion.
/// \return						\em S_OK if the file was read successfully.
///
HRESULT Utils::ReadTextFile(const CString& sPathName, CAtlArray<CString>& rarrayText, ULONGLONG* puLength /*= NULL*/)
{
	ATLVERIFY(rarrayText.SetCount(0, 100) );

	CString sText;
	const HRESULT hr = ReadTextFile(sPathName, sText, puLength);

	if (SUCCEEDED(hr) )
	{
		(void)StrToArray(sText, rarrayText);
	}
	return hr;
}


/// \brief Read the contents of the given text file to a string.
///
/// \param	sPathName			The pathname of the file.
/// \param	rsText				A reference to a CString object which will receive a copy of the text
///								from the file if the operation succeeds.
/// \param	puLength			A pointer to a ULONGLONG which will receive the size of the file on completion.
/// \return						\em S_OK if the file was read successfully.
///
HRESULT Utils::ReadTextFile(const CString& sPathName, CString& rsText, ULONGLONG* puLength /*= NULL*/)
{
	rsText = _T("");

	CAtlFile file;
	const HRESULT hr = file.Create(sPathName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
	ATLASSERT(SUCCEEDED(hr) );

	if (SUCCEEDED(hr) )
	{
		ULONGLONG uLen = 0;
		ATLVERIFY(SUCCEEDED(file.GetSize(uLen) ) );

		CStringA sRawDataA;
		LPVOID const pBuffer = static_cast<LPVOID>(sRawDataA.GetBuffer(static_cast<int>(uLen) + 1) );

		DWORD dwRead = 0;
		ATLVERIFY(SUCCEEDED(file.Read(pBuffer, static_cast<DWORD>(uLen), dwRead) ) );

		sRawDataA.ReleaseBuffer(static_cast<int>(dwRead) );

		file.Close();

		rsText = CA2CT(sRawDataA);

		if (NULL != puLength)
		{
			*puLength = uLen;
		}
	}
	return hr;
}


/// \brief Write a text file containing the given text.
///
/// \param	sPathName			The pathname of the file.
/// \param	arrayText			The text to write.
/// \param	sDelimiter			The end of line marker to use (default cr + lf)
/// \param	psErrorMsg			A pointer to a CString object which will receive error information in the event of failure. Optional.
/// \return						\em S_OK if the file was written successfully.
///
HRESULT Utils::WriteTextFile(const CString& sPathName,
							 const CAtlArray<CString>& arrayText,
							 const CString& sDelimiter /*= _T("\r\n")*/,
							 CString* psErrorMsg /*= NULL*/)
{
	const CString sText = ArrayToStr(arrayText, sDelimiter);

	return WriteTextFile(sPathName, sText, psErrorMsg);
}


/// \brief Write a text file containing the given text.
///
/// \param	sPathName			The pathname of the file.
/// \param	sText				The text to write.
/// \param	psErrorMsg			A pointer to a CString object which will receive error information in the event of failure. Optional.
/// \return						\em S_OK if the file was written successfully.
///
HRESULT Utils::WriteTextFile(const CString& sPathName, const CString& sText, CString* psErrorMsg /*= NULL*/)
{
	HRESULT hr = S_FALSE;

	if (!sText.IsEmpty() )
	{
		CAtlFile file;
		hr = file.Create(sPathName, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS);
		
		if (SUCCEEDED(hr) )
		{
			ATLVERIFY(SUCCEEDED(file.Write( CT2CA(sText),
											static_cast<DWORD>(sText.GetLength() ) ) ) );

			file.Close();
		}
		else
		{
			// Find out why the file could not be written
			_com_error e(static_cast<HRESULT>(::GetLastError() ) );

			hr = e.Error();

			ATLTRACE( _T("Unable to create output file. Error code 0x%X (%s)\n"), e.Error(), e.ErrorMessage() );

			if (NULL != psErrorMsg)
			{
				*psErrorMsg = e.ErrorMessage();
			}
		}
	}
	return hr;
}


/// \brief Recursively search for the first instance of the specified file.
///
/// \param	sStartPath					The folder path at which the search should start.
/// \param	sMask						The file mask to search for (wildcards are allowed).
/// \return								The pathname of the first occurance of the specified file.
///
CString Utils::RecursiveFindFile(const CString& sStartPath, const CString& sMask)
{
	CAtlArray<CString> arrayFoundPathNames;
	
	if (RecursiveFindFile(sStartPath, sMask, arrayFoundPathNames, true) )
	{
		return arrayFoundPathNames[0];
	}
	return _T("");
}


/// \brief Recursively search for the specified file.
///
/// \param	sStartPath					The folder path at which the search should start.
/// \param	sMask						The file mask to search for (wildcards are allowed).
/// \param	rarrayFoundPathNames		A reference to an array of CString objects which will receive
///										the pathnames of the found files.
/// \param	bStopWhenFound				\em true if the search will stop when the first file is found;
///										\em false otherwise.
/// \return								\em true if the specified file was found; \em false otherwise.
///
bool Utils::RecursiveFindFile(const CString& sStartPath, const CString& sMask, CAtlArray<CString>& rarrayFoundPathNames, bool bStopWhenFound /*= false*/)
{
	WIN32_FIND_DATA ffd;

	// 1st Part: find the files
	CPath path(sStartPath);
	ATLVERIFY(path.Append(sMask) );

	HANDLE hFind = ::FindFirstFileEx(	static_cast<LPCTSTR>(path),		// Pathname
										FindExInfoStandard,				// information Level
										&ffd,							// information buffer
										FindExSearchNameMatch,			// 
										NULL,							// search Criteria
										0 );							// reserved

	if (INVALID_HANDLE_VALUE == hFind)
	{
		const DWORD le = ::GetLastError();

		if (ERROR_FILE_NOT_FOUND != le)
		{
			ATLTRACE(_T("Utils::RecursiveFindFile(): Invalid File Handle, err code: %d\n"), le);

			return false;
		}
	} 

	if (INVALID_HANDLE_VALUE != hFind)
	{
		do
		{
			if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
				continue;

			rarrayFoundPathNames.Add(sStartPath + _T("\\") + ffd.cFileName);

			if (bStopWhenFound)
			{
				ATLVERIFY(::FindClose(hFind) );

				return true;
			}
		}
		while (::FindNextFile(hFind,&ffd) );

		ATLVERIFY(::FindClose(hFind) );
	}


	// 2nd Part: Find the directories
	CPath pathDirs(sStartPath);
	ATLVERIFY(pathDirs.Append(_T("\\*") ) );

	hFind = ::FindFirstFileEx(	static_cast<LPCTSTR>(pathDirs),	// File Name
								FindExInfoStandard,				// information Level
								&ffd,							// information buffer
								FindExSearchLimitToDirectories, // only Directories
								NULL,							// search Criteria
								0);								// reserved

	if (INVALID_HANDLE_VALUE == hFind)
	{
		ATLTRACE(_T("Utils::RecursiveFindFile(): Invalid File Handle, err code: %d\n"), ::GetLastError() );

		return false;
	} 

	do
	{
		const CString dirName = ffd.cFileName;

		if ( (dirName == _T(".") ) || (dirName == _T("..") ) ) 
			continue;

		if (! (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
			continue;

		CPath pathRecurse(sStartPath);
		ATLVERIFY(pathRecurse.Append(ffd.cFileName) );

		if (RecursiveFindFile(static_cast<LPCTSTR>(pathRecurse), sMask, rarrayFoundPathNames, bStopWhenFound) && bStopWhenFound)
		{
			return true;
		}
	} 
	while (::FindNextFile(hFind,&ffd));

	ATLVERIFY(::FindClose(hFind) );

	return false;
}


	};	// namespace Libraries
};	// namespace Riverblade

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Riverblade Limited
United Kingdom United Kingdom
I haven't always written software for a living. When I graduated from Surrey University in 1989, it was with an Electronic Engineering degree, but unfortunately that never really gave me the opportunity to do anything particularly interesting (with the possible exception of designing Darth Vader's Codpiece * for the UK Army in 1990).
    * Also known as the Standard Army Bootswitch. But that's another story...
Since the opportunity arose to lead a software team developing C++ software for Avionic Test Systems in 1996, I've not looked back. More recently I've been involved in the development of subsea acoustic navigation systems, digital TV broadcast systems, port security/tracking systems, and most recently software development tools with my own company, Riverblade Ltd.

One of my personal specialities is IDE plug-in development. ResOrg was my first attempt at a plug-in, but my day to day work is with Visual Lint, an interactive code analysis tool environment with works within the Visual Studio and Eclipse IDEs or on build servers.

I love lots of things, but particularly music, photography and anything connected with history or engineering. I despise ignorant, intolerant and obstructive people - and it shows...I can be a bolshy cow if you wind me up the wrong way...Laugh | :laugh:

I'm currently based 15 minutes walk from the beach in Bournemouth on the south coast of England. Since I moved here I've grown to love the place - even if it is full of grockles in Summer!

Written By
Software Developer Riverblade Ltd
United Kingdom United Kingdom
I'm a software developer and/or tester with Riverblade Ltd (www.riverblade.co.uk) developing our core product range including our Visual Lint integration product and Lint Project Professional.

I incorporate a number of technologies into a daily basis including Windows API, C++ (VS2008), Managed C++, CLI, Databases, Java, JNI, Eclipse Framework, CDT and of course Visual Studio Extensibility (VSIP VSX).

In my spare time I enjoy cooking (prepping ingredients from scratch!), running, cycling, swimming, reading, interested in experimental electronic music (such as ClockDVA), movies, volunteering my IT skills where I can.

Comments and Discussions