///////////////////////////////////////////////////////////////////////////////
// LookupArgInfo
///////////////////////////////////////////////////////////////////////////////
bool cCmdLineParser::LookupArgInfo(int argId, TSTRING& arg, TSTRING& alias) const
{
    arg     = _T("");
    alias   = _T("");

    cHashTableIter<TSTRING, cArgInfo> iter(mArgTable);
    for(iter.SeekBegin(); ! iter.Done(); iter.Next())
    {
        if(iter.Val().mId == argId)
        {
            TSTRING str = iter.Key();
            if((str.length() > 0) && (str[0] == _T('-')))
            {
                // this is the alias!
                alias = (str.c_str() + 1);
            }
            else
            {
                // this is the arg...
                arg = str;
            }
        }
    }
    return ((! arg.empty()) || (! alias.empty()));
}
Example #2
0
///////////////////////////////////////////////////////////////////////////////
// IsObjectEncrypted
///////////////////////////////////////////////////////////////////////////////
bool cTWUtil::IsObjectEncrypted(const TCHAR* objFileName, const cFileHeaderID& fhid, const TSTRING& errorMsg)
{
    bool   fEncrypted = false;
    cDebug d("IsObjectEncrypted");
    d.TraceDebug(_T("Reading from file %s\n"), objFileName);

    try
    {
        cFileArchive arch;
        arch.OpenRead(objFileName);

        cFileHeader     fileHeader;
        cSerializerImpl fhSer(arch, cSerializerImpl::S_READ, objFileName);
        fileHeader.Read(&fhSer);

        // check for a mismatched header
        if (fileHeader.GetID() != fhid)
            ThrowAndAssert(eSerializerInputStreamFmt(_T(""), objFileName, eSerializer::TY_FILE));

        // switch on the type of encoding...
        if (fileHeader.GetEncoding() == cFileHeader::ASYM_ENCRYPTION)
        {
            fEncrypted = true;
        }
        else if (fileHeader.GetEncoding() == cFileHeader::COMPRESSED)
        {
            fEncrypted = false;
        }
        else
            // unknown encoding...
            ThrowAndAssert(eSerializerInputStreamFmt(_T(""), objFileName, eSerializer::TY_FILE));
    }
    catch (eArchive& e)
    {
        // Note: Output to TCERR is O.K. here, it is documented that this is what this function does
        TSTRING msg = e.GetMsg();
        if (!msg.empty())
            msg += _T("\n");
        msg += errorMsg;

        cTWUtil::PrintErrorMsg(ePoly(e.GetID(), msg, e.GetFlags()));
        ThrowAndAssert(ePoly());
    }
    catch (eSerializer& e)
    {
        // Note: Output to TCERR is O.K. here, it is documented that this is what this function does
        TSTRING msg = e.GetMsg();
        if (!msg.empty())
            msg += _T("\n");
        msg += errorMsg;

        cTWUtil::PrintErrorMsg(ePoly(e.GetID(), msg, e.GetFlags()));
        ThrowAndAssert(ePoly());
    }

    return (fEncrypted);
}
bool cUnixFSServices::GetExecutableFilename(TSTRING& strFullPath, const TSTRING& strFilename) const
{
    bool fGotName = false;

    if (strFilename.empty())
        return false;

    // if there is a slash in the filename, it's absolute or relative to cwd
    if (TSTRING::npos != strFilename.find(_T('/')))
    {
        // if absolute path
        if (strFilename[0] == _T('/'))
        {
            strFullPath = strFilename;
            fGotName    = true;
        }
        else // is relative path; find path from cwd
        {
            fGotName = FullPath(strFullPath, strFilename);
        }
    }
    else // it's just a filename: should be found in path
    {
        fGotName = util_PathFind(strFullPath, strFilename);

        TSTRING strFP;
        if (fGotName && FullPath(strFP, strFullPath))
            strFullPath = strFP;
    }

    return (fGotName);
}
///////////////////////////////////////////////////////////////////////////////
// Function name    : util_FileIsExecutable
// Description      : file ( or file a link points to ) must be a regular
//                    file and executable by someone
//
// Return type      : bool
// Argument         : const TSTRING& strFile
///////////////////////////////////////////////////////////////////////////////
bool util_FileIsExecutable(const TSTRING& strFile)
{
    if (strFile.empty())
        return false;

    struct stat s;
    if (stat(strFile.c_str(), &s) < 0) // this call handles links
        return false;

    return (S_ISREG(s.st_mode) && (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))); // can someone execute it?
}
static inline void util_ThrowIfNull( const cHierAddr& addr, const TSTRING& context )
{
    ASSERT( ! addr.IsNull() );
    if( addr.IsNull() )
    {
        TSTRING msg(_T("Attempt to access null address"));
        if( ! context.empty() )
        {
            msg += _T(" in ") + context;
        }
        throw eHierDatabase( msg );
    }   
}
///////////////////////////////////////////////////////////////////////////////
// AddArg
///////////////////////////////////////////////////////////////////////////////
void cCmdLineParser::AddArg(int argId, const TSTRING& arg, const TSTRING& alias, ParamCount numParams, bool multipleAllowed)
{
    if(arg.empty() && alias.empty())
    {
        // this refers to the list of parameters that comes after all the cmd line switches
        mLastArgInfo.mId        = argId;
        mLastArgInfo.mNumParams = numParams;
        return ;
    }

    if(! arg.empty())
        mArgTable.Insert(arg,   cArgInfo(argId, numParams));
    if(! alias.empty())
    {
        // put the alias in the table with a '-' prepended to it so it matches '--'
        TSTRING str(_T("-"));
        str += alias;
        mArgTable.Insert(str, cArgInfo(argId, numParams));
    }
    // This argument can appear more than once on the command line.
    if( multipleAllowed )
        mMultipleAllowed.insert( argId );
}
////////////////////////////////////////////////////////////////////////////////
// Function name    : IsRoot
// Description      : A root path is all '/'s
//
// Return type      : bool
// Argument         : const TSTRING& strPath
///////////////////////////////////////////////////////////////////////////////
bool cUnixFSServices::IsRoot(const TSTRING& strPath) const
{
    // and empty path is NOT the root path
    if (strPath.empty())
        return false;

    // check to see if all characters are a slash
    for (TSTRING::const_iterator iter = strPath.begin(); iter != strPath.end(); iter++)
    {
        // if we've found a char that's not '/', then it's not the root path
        if (*iter != TW_SLASH)
            return false;
    }

    return true;
}
/////////////////////////////////////////////////////////////////////////
// Function name    : util_TrailingSep
// Description      : ensure that a path ( fLeaveSep ? "has" : "does not have" ) a trailing slash
//
// Return type      : bool : was there a trailing slash?
// Argument         : TSTRING& str
// Argument         : bool fLeaveSep
/////////////////////////////////////////////////////////////////////////////////
bool util_TrailingSep(TSTRING& str, bool fLeaveSep)
{
    bool fWasSep = false;

    // if there's a trailing sep
    if (!str.empty() && str[str.size() - 1] == TW_SLASH)
    {
        if (!fLeaveSep)
            str.resize(str.size() - 1);
        fWasSep = true;
    }
    else // else no trailing sep
    {
        if (fLeaveSep)
            str += TW_SLASH;
        fWasSep = false;
    }

    return (fWasSep);
}
////////////////////////////////////////////////////////////////////////////////////
// Function name    : util_GetNextPathElement
// Description      :
//      starting from the left side of the path string, returns the index'th path element
//      returns true if the element exists, false if there aren't <index + 1> many elements
//
//      index is ZERO BASED
//
//      2rd element of   ABC/DEF/GH -> GH
//      1st element of //ABC/DEF/GH -> DEF
//
// Return type      : bool : got path element? ( i.e. was there index path elements? )
// Argument         : const TSTRING& strPathC
// Argument         : TSTRING& strElem
// Argument         : int index
/////////////////////////////////////////////////////////////////////////////////
bool util_GetNextPathElement(const TSTRING& strPathC, TSTRING& strElem, int index)
{

    // don't do anything if root or empty
    if (strPathC.empty() || iFSServices::GetInstance()->IsRoot(strPathC))
        return false;

    TSTRING strPath = strPathC; // writable local version

    bool               fMoreSeps = true;
    TSTRING::size_type nextSep, nextNonSep;
    nextSep = nextNonSep = (TSTRING::size_type)-1;
    for (int i = 0; i <= index && fMoreSeps; i++)
    {
        // go past leading separators
        nextNonSep = strPath.find_first_not_of(TW_SLASH, nextSep + 1);

        if (nextNonSep != TSTRING::npos)
        {
            // find index'th slash (start of index'th element)
            nextSep = strPath.find(TW_SLASH, nextNonSep);

            // if we're at the end and we haven't found the index'th element
            // left, then tell the caller that there aren't that many elemnts
            if (nextSep == TSTRING::npos && i < index)
                fMoreSeps = false;
        }
        else
            fMoreSeps = false;
    }

    // get the element and remove it from the path
    if (fMoreSeps)
        strElem = strPath.substr(nextNonSep, nextSep - nextNonSep);

    return (fMoreSeps);
}
Example #10
0
	HRESULT SLog::splitFullPath(TSTRING szFullPath)
	{
		HRESULT retValue = S_OK;

		TSTRING szSlash = TSTRING(_T("/\\"));
		TSTRING szDot = TSTRING(_T("."));
		int iLastSlash, iLastDot;
		bool bBadPath = false;

		// Quick sanity check...
		if (szFullPath.empty())
		{
			retValue = ERROR_BAD_ARGUMENTS;
			goto EXIT;
		}

		// First, make sure we actually have strings...
		if (NULL == m_szLogFilePath)
			m_szLogFilePath = new TSTRING();

		if (NULL == m_szLogFileName)
			m_szLogFileName = new TSTRING();

		if (NULL == m_szLogFileExt)
			m_szLogFileExt = new TSTRING();

		// Make sure they're clear (remember, we may not have created them)
		m_szLogFilePath->clear();
		m_szLogFileName->clear();
		m_szLogFileExt->clear();

		// To peel apart the string, we need to find the last slash character
		iLastSlash = szFullPath.find_last_of(szSlash.c_str(), TNPOS);

		// Now, this could go either way; either we have no slash, in which case
		// this DOESN'T have a path, or we do and there's a path...
		if (iLastSlash == TNPOS)
		{
			// We didn't get a path...
			bBadPath = true;
		}
		else
		{
			// Get the path, if there is one...
			m_szLogFilePath->append(szFullPath.substr(0, (iLastSlash + 1)));

			// Does that path actually exist?
			WIN32_FILE_ATTRIBUTE_DATA dirData;
			if (!GetFileAttributesEx(m_szLogFilePath->c_str(),
				GetFileExInfoStandard,
				&dirData))
			{
				// We hit an error!
				DWORD dw;
				dw = GetLastError();
				retValue = HRESULT_FROM_WIN32(dw);
				bBadPath = true;
			}
			else if ((dirData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
			{
				// The path isn't to a directory!
				retValue = ERROR_BAD_PATHNAME;
				bBadPath = true;
			}
		}

		if (bBadPath)
		{
			// We either got no path or a bad path, so let's
			// set it to the Current Directory...
			// First, get the size of buffer we're going to need
			int iDirSize = GetCurrentDirectory(0, NULL);

			// Next, declare the buffer and use it to get the current directory
			m_szLogFilePath->clear();
			TCHAR* szDirBuffer = new TCHAR[iDirSize];
			GetCurrentDirectory(iDirSize, szDirBuffer);
			m_szLogFilePath->append(szDirBuffer);
			m_szLogFilePath->append(_T("\\"));		// Have to add the trailing slash
		}

		// To peel apart the extension, we need to find the last dot character
		iLastDot = szFullPath.find_last_of(szDot.c_str(), TNPOS);

		// We may or may not have a dot; no dot, no extension
		if (iLastDot == TNPOS)
		{
			iLastDot = szFullPath.length();
			m_szLogFileExt->append(DEFAULT_EXT);
		}
		else
		{
			m_szLogFileExt->append(szFullPath.substr(iLastDot));
		}

		// With all that out of the way, we can get our file name
		m_szLogFileName->append(szFullPath.substr((iLastSlash + 1), ( iLastDot - iLastSlash - 1 )));

	EXIT:
		return retValue;
	}
Example #11
0
// Initialize the application
bool InitInstance()
{
    // Parse INI file
    Settings_ParseIniFile(g_ApplicationIniPath);

    // Retrieve general settings
    g_ScreenWidth = Settings_GetValueInt(SETTINGS_SECTION_VIDEO, SETTINGS_KEY_WIDTH, 800);
    if (g_ScreenWidth < 320) g_ScreenWidth = 320;
    g_ScreenHeight = Settings_GetValueInt(SETTINGS_SECTION_VIDEO, SETTINGS_KEY_HEIGHT, 600);
    if (g_ScreenHeight < 240) g_ScreenHeight = 240;
    g_ScreenBitsPerPixel = Settings_GetValueInt(SETTINGS_SECTION_VIDEO, SETTINGS_KEY_BITSPERPIXEL, 0);
    if (g_ScreenBitsPerPixel < 0) g_ScreenBitsPerPixel = 0;
    g_FullScreen = Settings_GetValueBool(SETTINGS_SECTION_VIDEO, SETTINGS_KEY_FULLSCREEN, false);

    g_ScreenMode = Settings_GetValueInt(SETTINGS_SECTION_VIDEO, SETTINGS_KEY_SCREENMODE, 0);
    Screen_GetScreenSize(g_ScreenMode, &g_UkncScreenWid, &g_UkncScreenHei);
    if (g_UkncScreenWid == 0 || g_UkncScreenHei == 0)  // Unallowed mode, reset to default
    {
        g_ScreenMode = 0;
        Screen_GetScreenSize(g_ScreenMode, &g_UkncScreenWid, &g_UkncScreenHei);
    }

    // Init SDL video
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
        return false;  // Unable to initialize SDL

#ifdef _WIN32
    SDL_WM_SetCaption("UKNCBTL SDL", "UKNCBTL SDL");
#else
    SDL_ShowCursor(SDL_DISABLE);
#endif

    // Apply SDL settings from the "SDL" section of the INI file
    // See http://www.libsdl.org/docs/html/sdlenvvars.html for the list of options
    const TSETTINGSKEYVALUEMAP* pMapSdl = Settings_GetKeyValues(SETTINGS_SECTION_SDL);
    if (pMapSdl != 0)
    {
        for (TSETTINGSKEYVALUEMAP::const_iterator itsdl = pMapSdl->begin(); itsdl != pMapSdl->end(); ++itsdl)
        {
            TCHAR buffer[256];
            _sntprintf(buffer, 256, _T("%s=%s"), itsdl->first.c_str(), itsdl->second.c_str());
            SDL_putenv(buffer);
        }
    }

    // Prepare screen surface
    int flags = SDL_HWSURFACE | SDL_DOUBLEBUF;
    if (g_FullScreen) flags |= SDL_FULLSCREEN;
    g_Screen = SDL_SetVideoMode(g_ScreenWidth, g_ScreenHeight, g_ScreenBitsPerPixel, flags);
    if (g_Screen == NULL)
        return false;  // Unable to set video mode
    g_UkncScreen = SDL_CreateRGBSurface(0, g_UkncScreenWid, g_UkncScreenHei, 32, 0,0,0,0);
    if (g_Screen == NULL)
        return false;

    if (!Emulator_Init())
        return false;

    // Restore settings: cartridges
    for (int slot = 1; slot < 2; slot++)
    {
        TCHAR buffer[11];
        _tcscpy(buffer, SETTINGS_KEY_CARTRIDGE1);
        buffer[_tcslen(buffer) - 1] = _T('0') + slot;
        TSTRING sFileName = Settings_GetValue(SETTINGS_SECTION_UKNCBTL, buffer);
        if (! sFileName.empty())
            Emulator_LoadROMCartridge(slot, sFileName.c_str());
    }
    // Restore settings: floppies
    for (int slot = 0; slot < 4; slot++)
    {
        TCHAR buffer[11];
        _tcscpy(buffer, SETTINGS_KEY_FLOPPY0);
        buffer[_tcslen(buffer) - 1] = _T('0') + slot;
        TSTRING sFileName = Settings_GetValue(SETTINGS_SECTION_UKNCBTL, buffer);
        if (! sFileName.empty())
            Emulator_AttachFloppy(slot, sFileName.c_str());
    }
    // Restore settings: hard drives
    for (int slot = 1; slot < 2; slot++)
    {
        TCHAR buffer[11];
        _tcscpy(buffer, SETTINGS_KEY_HARD1);
        buffer[_tcslen(buffer) - 1] = _T('0') + slot;
        TSTRING sFileName = Settings_GetValue(SETTINGS_SECTION_UKNCBTL, buffer);
        if (! sFileName.empty())
            Emulator_AttachHardDrive(slot, sFileName.c_str());
    }

    ////DEBUG: SDL key names
    //std::map<const char*, int> keys;
    //for (int code = 0; code < 512; code++)
    //{
    //    const char * keyname = SDL_GetKeyName((SDLKey)code);
    //    if (_stricmp(keyname, "unknown key") != 0)
    //        keys[keyname] = code;
    //}
    //for (std::map<const char*, int>::iterator it = keys.begin(); it != keys.end(); ++it)
    //{
    //    printf("%04x\t%s\n", it->second, it->first);
    //}
    //printf("TOTAL: %d\n", keys.size());

    return true;
}
/// <summary>
/// <para name='Name'>ResolveDisplayName</para>
/// <para name='Purpose'>Get an email address from a Display Name</para>
/// </summary>
/// <param name='szName'>Display Name to resolve</param>
/// <param name='szEmailAddress'>[out] Returned email address</param>
/// <returns>HRESULT</returns>
/// <remarks>
/// <para name='Notes'></para>
/// <para name='Author'>Kenn Guilstorf</para>
/// <para name='LastModified'>28Jan2016</para>
/// </remarks>
STDMETHODIMP ZybraxiSoft::CMailbox::ResolveDisplayName(TSTRING szName, TSTRING &szEmailAddress)
{
	HRESULT hr = S_OK;
	LPADRBOOK lpAdrBook = NULL;
	LPADRLIST lpAdrList = NULL;
	TSTRING szEX = TSTRING(_T("EX"));
	bool isExchange = false;

	enum
	{
		prDISPLAY_NAME,
		NUM_PROPS
	};

	// Check to make sure we have something in szName
	if (szName.empty() || szName.size() <= 0)
	{
		hr = MAPI_E_INVALID_PARAMETER;
		goto EXIT;
	}

	// Log what we're trying to resolve
	m_log << output::both << level::Informational <<
		"Attempting to resolve '" << szName.c_str() << "'" << endl;

	// Open the address book
	// See: https://msdn.microsoft.com/en-us/library/office/cc815381.aspx
	if (SUCCEEDED(hr = m_lpSession->OpenAddressBook(
		NULL,				// ulUIParam [may be NULL]
		NULL,				// lpInterface [may be NULL; returns IAddrBook:IMAPIProp]
		NULL,				// ulFlags [NULL means no flags]
		&lpAdrBook)))		// lppAdrBook [out]
	{
		// Allocate the address list
		// Note: I like to use 'this' so I know which functions I'm responsible
		//       for if I ever have to work on this...
		if (SUCCEEDED(hr = this->AllocAdrList(
			NUM_PROPS,
			&lpAdrList)))
		{
			// Even though we succeeded, do a check to make sure we have
			//  an address list
			if (lpAdrList)
			{
				lpAdrList->cEntries = 1;	// only looking for 1 entry
				lpAdrList->aEntries[0].cValues = NUM_PROPS;		// Number of props

				// Set the SPropValue to whom we're looking for
				lpAdrList->aEntries[0].rgPropVals[prDISPLAY_NAME].ulPropTag =
					PR_DISPLAY_NAME;
				lpAdrList->aEntries[0].rgPropVals[prDISPLAY_NAME].Value.LPSZ =
					(LPTSTR)szName.c_str();

				// Let's try to resolve the name now
				if (SUCCEEDED(hr = lpAdrBook->ResolveName(
					0L,
					MAPI_UNICODE,
					NULL,
					lpAdrList)))
				{
					m_log << output::both << level::Informational <<
						"Resolve name yielded " <<
						lpAdrList->aEntries[0].cValues <<
						" properties." << endl;

					for (UINT i = 0; i < lpAdrList->aEntries[0].cValues; i++)
					{
						// Store the property so I don't have to type so much...
						SPropValue spvCurrent = lpAdrList->aEntries[0].rgPropVals[i];

						m_log << output::both << level::Informational <<
							"Found Property '0x" <<
							setfill(_T('0')) << setw(8) << setbase(16) <<
							spvCurrent.ulPropTag << "'";

						switch (spvCurrent.ulPropTag)
						{
						case PR_ADDRTYPE:
							if (szEX.compare(spvCurrent.Value.LPSZ) == 0)
								isExchange = true;
							break;
						case PR_DISPLAY_NAME:
							m_log << ": " << spvCurrent.Value.LPSZ;
							break;
						case PR_EMAIL_ADDRESS:
							m_log << ": " << spvCurrent.Value.LPSZ;
							szEmailAddress = TSTRING(spvCurrent.Value.LPSZ);
							break;
						default:
							if (((spvCurrent.ulPropTag & 0x1f) == 0x1f) ||
								((spvCurrent.ulPropTag & 0x1e) == 0x1e))
								m_log << ": " << spvCurrent.Value.LPSZ;
							break;
						}

						m_log << endl;
					}

					if (!isExchange || (szEmailAddress.empty()))
					{
						hr = MAPI_E_NOT_FOUND;
						goto CLEANUP;
					}
				}
				else
				{
					FATAL(_T("lpAdrBook->ResolveName()"), hr);
					goto CLEANUP;
				}
			}
		}
		else
		{
			FATAL(_T("AllocAdrList()"), hr);
			goto CLEANUP;
		}
	}
	else
	{
		FATAL(_T("OpenAddressBook()"), hr);
	}

CLEANUP:
	if (lpAdrList)
	{
		FreePadrlist(lpAdrList);
		lpAdrList = NULL;
	}

	if (lpAdrBook)
	{
		lpAdrBook->Release();
		lpAdrBook = NULL;
	}

EXIT:
	return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Function name    : util_PathFind
// Description      :
//      takes single-element executible filename and looks in path env var for it
//      assumes path is colon-delimited string of directories.
//
// Return type      : bool
// Argument         :  TSTRING& strFullPath
// Argument         : const TSTRING& strFilename
///////////////////////////////////////////////////////////////////////////////
bool util_PathFind(TSTRING& strFullPath, const TSTRING& strFilename)
{
    bool fFoundFile = false;

    if (strFilename.empty())
        return false;

    //
    // get the path environment variable
    //
    TCHAR* pszPathVar = getenv("PATH");
    if (pszPathVar != NULL)
    {
        //
        // cycle over characters in path looking for the ':'
        //
        TSTRING strCurPath;
        TCHAR*  pchTemp    = pszPathVar;
        bool    fMorePaths = true;
        do // while still more paths and haven't found file
        {
            //
            // are we at the ':'?
            //
            if (*pchTemp && *pchTemp != _T(':')) // if we're not at the end of the path
            {
                strCurPath += *pchTemp;
            }
            else // we have found the ':'
            {
                //
                // expand current path into a fully qualified path
                // if it's empty, use current directory
                //
                TSTRING strFP;
                if (strCurPath.empty())
                    strCurPath = _T(".");
                if (iFSServices::GetInstance()->FullPath(strFP, strCurPath))
                    strCurPath = strFP;

                //
                // put the file together with the path dir
                //
                TSTRING strFullName = strCurPath;
                util_TrailingSep(strFullName, true);
                strFullName += strFilename;

                //
                // the file must exist and be executable
                //
                if (util_FileIsExecutable(strFullName))
                {
                    strFullPath = strFullName;
                    fFoundFile  = true;
                }
                else
                    strCurPath.erase(); // start over
            }

            //
            // keep searching if we're not at the end of the path string
            //

            if (*pchTemp)
                pchTemp++;
            else
                fMorePaths = false;
        } while (!fFoundFile && fMorePaths);
    }

    return (fFoundFile);
}
///////////////////////////////////////////////////////////////////////////////
// Function name    : cUnixFSServices::FullPath
// Description      :
//
// Return type      : bool
// Argument         :  TSTRING& strFullPath
// Argument         : const TSTRING& strRelPathC
// Argument         : const TSTRING& pathRelFromC
//
// TODO -- is throwing an exception the more appropriate alternative to returning
//      a bool? I think it is ... mdb
///////////////////////////////////////////////////////////////////////////////
bool cUnixFSServices::FullPath(TSTRING& strFullPath, const TSTRING& strRelPathC, const TSTRING& pathRelFromC) const
{
    cDebug d("cUnixFSServices::FullPath");
    d.TraceDebug("strRelPathC = %s, pathRelFromC = %s\n", strRelPathC.c_str(), pathRelFromC.c_str());

    // don't do anything with an empty path
    if (strRelPathC.empty())
        return false;

#if USES_DEVICE_PATH
    TSTRING strRelPath = cDevicePath::AsPosix(strRelPathC); // make non-const temp var
#else
    TSTRING strRelPath = strRelPathC; // make non-const temp var
#endif
    //
    // get base name (where strRelPath will be relative to), which will either be;
    //  1. the root directory if strRelPath is an absolute path
    //  2. pathRelFrom if it's not empty
    //  3. otherwise ( not abs path AND no rel path ) the current working directory
    //

    if (strRelPath[0] == TW_SLASH) // if is absolute path
    {
        if (IsRoot(strRelPath)) // if it's root, don't monkey with it, just return it.
        {
            strFullPath = strRelPath;
            d.TraceDebug("Is root; returning %s\n", strFullPath.c_str());
            return true;
        }
        else
        {
            strFullPath = _T(""); // push root, then add path elements from strRelPathC
                                  // one by one (in while loop below)
        }
    }
    else // is a relative path, so check pathRelFromC
    {
        if (pathRelFromC.empty()) // if we're relative to CWD...
        {
            //
            // get the current working directory
            //
            try
            {
                GetCurrentDir(strFullPath);
#if USES_DEVICE_PATH
                strFullPath = cDevicePath::AsPosix(strFullPath);
#endif
                util_TrailingSep(strFullPath, false);
            }
            catch (eFSServices&)
            {
                return false;
            }

            d.TraceDebug("Creating prefix relative to CWD: %s\n", strFullPath.c_str());
        }
        else // we're relative to a given dir
        {

#if USES_DEVICE_PATH
            strFullPath = cDevicePath::AsPosix(pathRelFromC);
#else
            strFullPath = pathRelFromC;
#endif
            util_RemoveDuplicateSeps(strFullPath);
            util_TrailingSep(strFullPath, false);

            d.TraceDebug("Creating prefix from supplied path: %s\n", strFullPath.c_str());
        }
    }

    //
    // start adding path elements from strRelPath to the base name
    // ( which already has an absolute starting point.  see above. )
    //

    TSTRING strElem;
    int     index = 0;
    while (util_GetNextPathElement(strRelPath, strElem, index++))
    {
        d.TraceDebug("Path element = %s\n", strElem.c_str());
        if (0 == strElem.compare(_T(".")))
        {
            // ignore it
        }
        else if (0 == strElem.compare(_T("..")))
        {
            // go up a dir ( the function takes care of root dir case )
            TSTRING strDummy;
            util_RemoveLastPathElement(strFullPath, strDummy);
        }
        else // just a regular old path element
        {
            strFullPath += TW_SLASH;
            strFullPath += strElem;
        }

        d.TraceDebug("FullPath is now %s\n", strFullPath.c_str());
    }

#if IS_AROS || IS_REDOX
    strFullPath = cDevicePath::AsNative(strFullPath);
#endif

    d.TraceDebug("Done, returning %s\n", strFullPath.c_str());
    return true;
}