/*
 *  ATTRIB.C - attrib internal command.
 *
 *
 *  History:
 *
 *    04-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
 *        started
 *
 *    09-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
 *        implementation works, except recursion ("attrib /s").
 *
 *    05-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
 *        major rewrite.
 *        fixed recursion ("attrib /s").
 *        started directory support ("attrib /s /d").
 *        updated help text.
 *
 *    14-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
 *        Unicode ready!
 *
 *    19-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
 *        Redirection ready!
 *
 *    21-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
 *        Added check for invalid filenames.
 *
 *    23-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
 *        Added handling of multiple filenames.
 *
 *    02-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>)
 *        Remove all hardcode string to En.rc
 */

#include <precomp.h>
#include "resource.h"

#ifdef INCLUDE_CMD_ATTRIB


static VOID
PrintAttribute (LPTSTR pszPath, LPTSTR pszFile, BOOL bRecurse)
{
	WIN32_FIND_DATA findData;
	HANDLE hFind;
	TCHAR  szFullName[MAX_PATH];
	LPTSTR pszFileName;

	/* prepare full file name buffer */
	_tcscpy (szFullName, pszPath);
	pszFileName = szFullName + _tcslen (szFullName);

	/* display all subdirectories */
	if (bRecurse)
	{
		/* append file name */
		_tcscpy (pszFileName, pszFile);

		hFind = FindFirstFile (szFullName, &findData);
		if (hFind == INVALID_HANDLE_VALUE)
		{
			ErrorMessage (GetLastError (), pszFile);
			return;
		}

		do
		{
			if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
				continue;

			if (!_tcscmp (findData.cFileName, _T(".")) ||
				!_tcscmp (findData.cFileName, _T("..")))
				continue;

			_tcscpy (pszFileName, findData.cFileName);
			_tcscat (pszFileName, _T("\\"));
			PrintAttribute (szFullName, pszFile, bRecurse);
		}
		while (FindNextFile (hFind, &findData));
		FindClose (hFind);
	}

	/* append file name */
	_tcscpy (pszFileName, pszFile);

	/* display current directory */
	hFind = FindFirstFile (szFullName, &findData);
	if (hFind == INVALID_HANDLE_VALUE)
	{
		ErrorMessage (GetLastError (), pszFile);
		return;
	}

	do
	{
		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			continue;

		_tcscpy (pszFileName, findData.cFileName);

		ConOutPrintf (_T("%c  %c%c%c     %s\n"),
		              (findData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) ? _T('A') : _T(' '),
		              (findData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? _T('S') : _T(' '),
		              (findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? _T('H') : _T(' '),
		              (findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? _T('R') : _T(' '),
		              szFullName);
	}
	while (FindNextFile (hFind, &findData));
	FindClose (hFind);
}


static VOID
ChangeAttribute (LPTSTR pszPath, LPTSTR pszFile, DWORD dwMask,
		 DWORD dwAttrib, BOOL bRecurse, BOOL bDirectories)
{
	WIN32_FIND_DATA findData;
	HANDLE hFind;
	DWORD  dwAttribute;
	TCHAR  szFullName[MAX_PATH];
	LPTSTR pszFileName;


	/* prepare full file name buffer */
	_tcscpy (szFullName, pszPath);
	pszFileName = szFullName + _tcslen (szFullName);

	/* change all subdirectories */
	if (bRecurse)
	{
		/* append file name */
		_tcscpy (pszFileName, _T("*.*"));

		hFind = FindFirstFile (szFullName, &findData);
		if (hFind == INVALID_HANDLE_VALUE)
		{
			ErrorMessage (GetLastError (), pszFile);
      nErrorLevel = 1;
			return;
		}

		do
		{
			if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				if (!_tcscmp (findData.cFileName, _T(".")) ||
				    !_tcscmp (findData.cFileName, _T("..")))
					continue;

				_tcscpy (pszFileName, findData.cFileName);
				_tcscat (pszFileName, _T("\\"));

				ChangeAttribute (szFullName, pszFile, dwMask,
								 dwAttrib, bRecurse, bDirectories);
			}
		}
		while (FindNextFile (hFind, &findData));
		FindClose (hFind);
	}

	/* append file name */
	_tcscpy (pszFileName, pszFile);

	hFind = FindFirstFile (szFullName, &findData);
	if (hFind == INVALID_HANDLE_VALUE)
	{
		ErrorMessage (GetLastError (), pszFile);
    nErrorLevel = 1;
		return;
	}

	do
	{
		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			continue;

		_tcscpy (pszFileName, findData.cFileName);

		dwAttribute = GetFileAttributes (szFullName);

		if (dwAttribute != 0xFFFFFFFF)
		{
			dwAttribute = (dwAttribute & ~dwMask) | dwAttrib;
			SetFileAttributes (szFullName, dwAttribute);
		}
	}
	while (FindNextFile (hFind, &findData));
	FindClose (hFind);
}


INT CommandAttrib (LPTSTR cmd, LPTSTR param)
{
	LPTSTR *arg;
	INT    argc, i;
	TCHAR  szPath[MAX_PATH];
	TCHAR  szFileName [MAX_PATH];
	BOOL   bRecurse = FALSE;
	BOOL   bDirectories = FALSE;
	DWORD  dwAttrib = 0;
	DWORD  dwMask = 0;

	/* initialize strings */
	szPath[0] = _T('\0');
	szFileName[0] = _T('\0');

	/* print help */
	if (!_tcsncmp (param, _T("/?"), 2))
	{
		ConOutResPaging(TRUE,STRING_ATTRIB_HELP);
		return 0;
	}

  nErrorLevel = 0;

	/* build parameter array */
	arg = split (param, &argc, FALSE);

	/* check for options */
	for (i = 0; i < argc; i++)
	{
		if (_tcsicmp (arg[i], _T("/s")) == 0)
			bRecurse = TRUE;
		else if (_tcsicmp (arg[i], _T("/d")) == 0)
			bDirectories = TRUE;
	}

	/* create attributes and mask */
	for (i = 0; i < argc; i++)
	{
		if (*arg[i] == _T('+'))
		{
			if (_tcslen (arg[i]) != 2)
			{
				error_invalid_parameter_format (arg[i]);
				freep (arg);
				return -1;
			}

			switch ((TCHAR)_totupper (arg[i][1]))
			{
				case _T('A'):
					dwMask   |= FILE_ATTRIBUTE_ARCHIVE;
					dwAttrib |= FILE_ATTRIBUTE_ARCHIVE;
					break;

				case _T('H'):
					dwMask   |= FILE_ATTRIBUTE_HIDDEN;
					dwAttrib |= FILE_ATTRIBUTE_HIDDEN;
					break;

				case _T('R'):
					dwMask   |= FILE_ATTRIBUTE_READONLY;
					dwAttrib |= FILE_ATTRIBUTE_READONLY;
					break;

				case _T('S'):
					dwMask   |= FILE_ATTRIBUTE_SYSTEM;
					dwAttrib |= FILE_ATTRIBUTE_SYSTEM;
					break;

				default:
					error_invalid_parameter_format (arg[i]);
					freep (arg);
					return -1;
			}
		}
		else if (*arg[i] == _T('-'))
		{
			if (_tcslen (arg[i]) != 2)
			{
				error_invalid_parameter_format (arg[i]);
				freep (arg);
				return -1;
			}

			switch ((TCHAR)_totupper (arg[i][1]))
			{
				case _T('A'):
					dwMask   |= FILE_ATTRIBUTE_ARCHIVE;
					dwAttrib &= ~FILE_ATTRIBUTE_ARCHIVE;
					break;

				case _T('H'):
					dwMask   |= FILE_ATTRIBUTE_HIDDEN;
					dwAttrib &= ~FILE_ATTRIBUTE_HIDDEN;
					break;

				case _T('R'):
					dwMask   |= FILE_ATTRIBUTE_READONLY;
					dwAttrib &= ~FILE_ATTRIBUTE_READONLY;
					break;

				case _T('S'):
					dwMask   |= FILE_ATTRIBUTE_SYSTEM;
					dwAttrib &= ~FILE_ATTRIBUTE_SYSTEM;
					break;

				default:
					error_invalid_parameter_format (arg[i]);
					freep (arg);
					return -1;
			}
		}
	}

	if (argc == 0)
	{
		DWORD len;

		len = GetCurrentDirectory (MAX_PATH, szPath);
		if (szPath[len-1] != _T('\\'))
		{
			szPath[len] = _T('\\');
			szPath[len + 1] = 0;
		}
		_tcscpy (szFileName, _T("*.*"));
		PrintAttribute (szPath, szFileName, bRecurse);
		freep (arg);
		return 0;
	}

	/* get full file name */
	for (i = 0; i < argc; i++)
	{
		if ((*arg[i] != _T('+')) && (*arg[i] != _T('-')) && (*arg[i] != _T('/')))
		{
			LPTSTR p;
			GetFullPathName (arg[i], MAX_PATH, szPath, NULL);
			p = _tcsrchr (szPath, _T('\\')) + 1;
			_tcscpy (szFileName, p);
			*p = _T('\0');

			if (dwMask == 0)
				PrintAttribute (szPath, szFileName, bRecurse);
			else
				ChangeAttribute (szPath, szFileName, dwMask,
						 dwAttrib, bRecurse, bDirectories);
		}
	}

	freep (arg);
	return 0;
}

#endif /* INCLUDE_CMD_ATTRIB */
