reactos/base/shell/cmd/assoc.c

314 lines
7.2 KiB
C
Raw Normal View History

/*
* ASSOC.C - assoc internal command.
*
*
* History:
*
* 14-Mar-2009 Lee C. Baker
* - initial implementation.
*
* 15-Mar-2009 Lee C. Baker
* - Don't write to (or use) HKEY_CLASSES_ROOT directly.
* - Externalize strings.
*
* TODO:
* - PrintAllAssociations could be optimized to not fetch all registry subkeys under 'Classes', just the ones that start with '.'
*/
#include "precomp.h"
#ifdef INCLUDE_CMD_ASSOC
static LONG
PrintAssociationEx(
IN HKEY hKeyClasses,
IN PCTSTR pszExtension)
{
LONG lRet;
HKEY hKey;
DWORD dwFileTypeLen = 0;
PTSTR pszFileType;
lRet = RegOpenKeyEx(hKeyClasses, pszExtension, 0, KEY_QUERY_VALUE, &hKey);
if (lRet != ERROR_SUCCESS)
{
if (lRet != ERROR_FILE_NOT_FOUND)
ErrorMessage(lRet, NULL);
return lRet;
}
/* Obtain the string length */
lRet = RegQueryValueEx(hKey, NULL, NULL, NULL, NULL, &dwFileTypeLen);
/* If there is no default value, don't display it */
if (lRet == ERROR_FILE_NOT_FOUND)
{
RegCloseKey(hKey);
return lRet;
}
if (lRet != ERROR_SUCCESS)
{
ErrorMessage(lRet, NULL);
RegCloseKey(hKey);
return lRet;
}
++dwFileTypeLen;
pszFileType = cmd_alloc(dwFileTypeLen * sizeof(TCHAR));
if (!pszFileType)
{
WARN("Cannot allocate memory for pszFileType!\n");
RegCloseKey(hKey);
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Obtain the actual file type */
lRet = RegQueryValueEx(hKey, NULL, NULL, NULL, (LPBYTE)pszFileType, &dwFileTypeLen);
RegCloseKey(hKey);
if (lRet != ERROR_SUCCESS)
{
ErrorMessage(lRet, NULL);
cmd_free(pszFileType);
return lRet;
}
/* If there is a default key, display the relevant information */
if (dwFileTypeLen != 0)
{
ConOutPrintf(_T("%s=%s\n"), pszExtension, pszFileType);
}
cmd_free(pszFileType);
return ERROR_SUCCESS;
}
static LONG
PrintAssociation(
IN PCTSTR pszExtension)
{
LONG lRet;
HKEY hKeyClasses;
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Classes"), 0,
KEY_ENUMERATE_SUB_KEYS, &hKeyClasses);
if (lRet != ERROR_SUCCESS)
{
ErrorMessage(lRet, NULL);
return lRet;
}
lRet = PrintAssociationEx(hKeyClasses, pszExtension);
RegCloseKey(hKeyClasses);
return lRet;
}
static LONG
PrintAllAssociations(VOID)
{
LONG lRet;
HKEY hKeyClasses;
DWORD dwKeyCtr;
DWORD dwNumKeys = 0;
DWORD dwExtLen = 0;
PTSTR pszExtName;
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Classes"), 0,
KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKeyClasses);
if (lRet != ERROR_SUCCESS)
{
ErrorMessage(lRet, NULL);
return lRet;
}
lRet = RegQueryInfoKey(hKeyClasses, NULL, NULL, NULL, &dwNumKeys, &dwExtLen,
NULL, NULL, NULL, NULL, NULL, NULL);
if (lRet != ERROR_SUCCESS)
{
ErrorMessage(lRet, NULL);
RegCloseKey(hKeyClasses);
return lRet;
}
++dwExtLen;
pszExtName = cmd_alloc(dwExtLen * sizeof(TCHAR));
if (!pszExtName)
{
WARN("Cannot allocate memory for pszExtName!\n");
RegCloseKey(hKeyClasses);
return ERROR_NOT_ENOUGH_MEMORY;
}
for (dwKeyCtr = 0; dwKeyCtr < dwNumKeys; ++dwKeyCtr)
{
DWORD dwBufSize = dwExtLen;
lRet = RegEnumKeyEx(hKeyClasses, dwKeyCtr, pszExtName, &dwBufSize,
NULL, NULL, NULL, NULL);
if (lRet == ERROR_SUCCESS || lRet == ERROR_MORE_DATA)
{
/* Name starts with '.': this is an extension */
if (*pszExtName == _T('.'))
PrintAssociationEx(hKeyClasses, pszExtName);
}
else
{
ErrorMessage(lRet, NULL);
cmd_free(pszExtName);
RegCloseKey(hKeyClasses);
return lRet;
}
}
RegCloseKey(hKeyClasses);
cmd_free(pszExtName);
return ERROR_SUCCESS;
}
static LONG
AddAssociation(
IN PCTSTR pszExtension,
IN PCTSTR pszType)
{
LONG lRet;
HKEY hKeyClasses, hKey;
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Classes"), 0,
KEY_CREATE_SUB_KEY, &hKeyClasses);
if (lRet != ERROR_SUCCESS)
{
ErrorMessage(lRet, NULL);
return lRet;
}
lRet = RegCreateKeyEx(hKeyClasses, pszExtension, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE, NULL, &hKey, NULL);
RegCloseKey(hKeyClasses);
if (lRet != ERROR_SUCCESS)
{
ErrorMessage(lRet, NULL);
return lRet;
}
lRet = RegSetValueEx(hKey, NULL, 0, REG_SZ,
(LPBYTE)pszType, (DWORD)(_tcslen(pszType) + 1) * sizeof(TCHAR));
RegCloseKey(hKey);
if (lRet != ERROR_SUCCESS)
{
ErrorMessage(lRet, NULL);
return lRet;
}
return ERROR_SUCCESS;
}
static LONG
RemoveAssociation(
IN PCTSTR pszExtension)
{
LONG lRet;
HKEY hKeyClasses;
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Classes"), 0,
KEY_QUERY_VALUE, &hKeyClasses);
if (lRet != ERROR_SUCCESS)
{
ErrorMessage(lRet, NULL);
return lRet;
}
lRet = RegDeleteKey(hKeyClasses, pszExtension);
RegCloseKey(hKeyClasses);
if (lRet != ERROR_SUCCESS)
{
if (lRet != ERROR_FILE_NOT_FOUND)
ErrorMessage(lRet, NULL);
return lRet;
}
return ERROR_SUCCESS;
}
INT CommandAssoc(LPTSTR param)
{
[CMD] Change ERRORLEVEL behaviour for commands ASSOC, PATH, PROMPT and SET. Commands APPEND/DPATH and FTYPE are also concerned by this; however we do not implement them in our CMD.EXE yet. These commands set the ERRORLEVEL differently, whether or not they are run manually from the command-line/from a .BAT file, or from a .CMD file: - From command-line/.BAT file, these commands set the ERRORLEVEL only if an error occurs. So, if two commands are run consecutively and the first one fails, the ERRORLEVEL will remain set even if the second command succeeds. - However, when being run from a .CMD file, these command will always set the ERRORLEVEL. In the example case described above, the second command that succeeds will reset the ERRORLEVEL to 0. This behaviour is determined from the top-level batch/script file being run. This means that, if a .BAT file is first started, then starts a .CMD file, the commands will still behave the .BAT way; on the opposite, if a .CMD file is first started, then starts a .BAT file, these commands will still behave the .CMD way. To implement this we introduce one global BATCH_TYPE enum variable that is initialized to the corresponding batch/script file type when the top-level script is loaded. It is reset to "none" when that script terminates. See https://ss64.com/nt/errorlevel.html for more details, section "Old style .bat Batch files vs .cmd Batch scripts", and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J (comment by Mark Zbikowski).
2020-07-01 00:15:52 +00:00
INT retval = 0;
PTCHAR pEqualSign;
/* Print help */
if (!_tcsncmp(param, _T("/?"), 2))
{
ConOutResPaging(TRUE, STRING_ASSOC_HELP);
return 0;
}
/* Print all associations if no parameter has been specified */
if (!*param)
{
PrintAllAssociations();
[CMD] Change ERRORLEVEL behaviour for commands ASSOC, PATH, PROMPT and SET. Commands APPEND/DPATH and FTYPE are also concerned by this; however we do not implement them in our CMD.EXE yet. These commands set the ERRORLEVEL differently, whether or not they are run manually from the command-line/from a .BAT file, or from a .CMD file: - From command-line/.BAT file, these commands set the ERRORLEVEL only if an error occurs. So, if two commands are run consecutively and the first one fails, the ERRORLEVEL will remain set even if the second command succeeds. - However, when being run from a .CMD file, these command will always set the ERRORLEVEL. In the example case described above, the second command that succeeds will reset the ERRORLEVEL to 0. This behaviour is determined from the top-level batch/script file being run. This means that, if a .BAT file is first started, then starts a .CMD file, the commands will still behave the .BAT way; on the opposite, if a .CMD file is first started, then starts a .BAT file, these commands will still behave the .CMD way. To implement this we introduce one global BATCH_TYPE enum variable that is initialized to the corresponding batch/script file type when the top-level script is loaded. It is reset to "none" when that script terminates. See https://ss64.com/nt/errorlevel.html for more details, section "Old style .bat Batch files vs .cmd Batch scripts", and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J (comment by Mark Zbikowski).
2020-07-01 00:15:52 +00:00
goto Quit;
}
pEqualSign = _tcschr(param, _T('='));
if (pEqualSign != NULL)
{
PTSTR pszFileType = pEqualSign + 1;
/* NULL-terminate at the equals sign */
*pEqualSign = 0;
/* If the equals sign is the last character
* in the string, delete the association. */
if (*pszFileType == 0)
{
retval = RemoveAssociation(param);
}
else
/* Otherwise, add the association and print it out */
{
retval = AddAssociation(param, pszFileType);
PrintAssociation(param);
}
if (retval != ERROR_SUCCESS)
{
if (retval != ERROR_FILE_NOT_FOUND)
{
// FIXME: Localize
ConErrPrintf(_T("Error occurred while processing: %s.\n"), param);
}
// retval = 1; /* Fixup the error value */
}
}
else
{
/* No equals sign, print the association */
[CMD] Change ERRORLEVEL behaviour for commands ASSOC, PATH, PROMPT and SET. Commands APPEND/DPATH and FTYPE are also concerned by this; however we do not implement them in our CMD.EXE yet. These commands set the ERRORLEVEL differently, whether or not they are run manually from the command-line/from a .BAT file, or from a .CMD file: - From command-line/.BAT file, these commands set the ERRORLEVEL only if an error occurs. So, if two commands are run consecutively and the first one fails, the ERRORLEVEL will remain set even if the second command succeeds. - However, when being run from a .CMD file, these command will always set the ERRORLEVEL. In the example case described above, the second command that succeeds will reset the ERRORLEVEL to 0. This behaviour is determined from the top-level batch/script file being run. This means that, if a .BAT file is first started, then starts a .CMD file, the commands will still behave the .BAT way; on the opposite, if a .CMD file is first started, then starts a .BAT file, these commands will still behave the .CMD way. To implement this we introduce one global BATCH_TYPE enum variable that is initialized to the corresponding batch/script file type when the top-level script is loaded. It is reset to "none" when that script terminates. See https://ss64.com/nt/errorlevel.html for more details, section "Old style .bat Batch files vs .cmd Batch scripts", and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J (comment by Mark Zbikowski).
2020-07-01 00:15:52 +00:00
retval = PrintAssociation(param);
if (retval != ERROR_SUCCESS)
[CMD] Change ERRORLEVEL behaviour for commands ASSOC, PATH, PROMPT and SET. Commands APPEND/DPATH and FTYPE are also concerned by this; however we do not implement them in our CMD.EXE yet. These commands set the ERRORLEVEL differently, whether or not they are run manually from the command-line/from a .BAT file, or from a .CMD file: - From command-line/.BAT file, these commands set the ERRORLEVEL only if an error occurs. So, if two commands are run consecutively and the first one fails, the ERRORLEVEL will remain set even if the second command succeeds. - However, when being run from a .CMD file, these command will always set the ERRORLEVEL. In the example case described above, the second command that succeeds will reset the ERRORLEVEL to 0. This behaviour is determined from the top-level batch/script file being run. This means that, if a .BAT file is first started, then starts a .CMD file, the commands will still behave the .BAT way; on the opposite, if a .CMD file is first started, then starts a .BAT file, these commands will still behave the .CMD way. To implement this we introduce one global BATCH_TYPE enum variable that is initialized to the corresponding batch/script file type when the top-level script is loaded. It is reset to "none" when that script terminates. See https://ss64.com/nt/errorlevel.html for more details, section "Old style .bat Batch files vs .cmd Batch scripts", and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J (comment by Mark Zbikowski).
2020-07-01 00:15:52 +00:00
{
ConErrResPrintf(STRING_ASSOC_ERROR, param);
[CMD] Change ERRORLEVEL behaviour for commands ASSOC, PATH, PROMPT and SET. Commands APPEND/DPATH and FTYPE are also concerned by this; however we do not implement them in our CMD.EXE yet. These commands set the ERRORLEVEL differently, whether or not they are run manually from the command-line/from a .BAT file, or from a .CMD file: - From command-line/.BAT file, these commands set the ERRORLEVEL only if an error occurs. So, if two commands are run consecutively and the first one fails, the ERRORLEVEL will remain set even if the second command succeeds. - However, when being run from a .CMD file, these command will always set the ERRORLEVEL. In the example case described above, the second command that succeeds will reset the ERRORLEVEL to 0. This behaviour is determined from the top-level batch/script file being run. This means that, if a .BAT file is first started, then starts a .CMD file, the commands will still behave the .BAT way; on the opposite, if a .CMD file is first started, then starts a .BAT file, these commands will still behave the .CMD way. To implement this we introduce one global BATCH_TYPE enum variable that is initialized to the corresponding batch/script file type when the top-level script is loaded. It is reset to "none" when that script terminates. See https://ss64.com/nt/errorlevel.html for more details, section "Old style .bat Batch files vs .cmd Batch scripts", and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J (comment by Mark Zbikowski).
2020-07-01 00:15:52 +00:00
retval = 1; /* Fixup the error value */
}
}
[CMD] Change ERRORLEVEL behaviour for commands ASSOC, PATH, PROMPT and SET. Commands APPEND/DPATH and FTYPE are also concerned by this; however we do not implement them in our CMD.EXE yet. These commands set the ERRORLEVEL differently, whether or not they are run manually from the command-line/from a .BAT file, or from a .CMD file: - From command-line/.BAT file, these commands set the ERRORLEVEL only if an error occurs. So, if two commands are run consecutively and the first one fails, the ERRORLEVEL will remain set even if the second command succeeds. - However, when being run from a .CMD file, these command will always set the ERRORLEVEL. In the example case described above, the second command that succeeds will reset the ERRORLEVEL to 0. This behaviour is determined from the top-level batch/script file being run. This means that, if a .BAT file is first started, then starts a .CMD file, the commands will still behave the .BAT way; on the opposite, if a .CMD file is first started, then starts a .BAT file, these commands will still behave the .CMD way. To implement this we introduce one global BATCH_TYPE enum variable that is initialized to the corresponding batch/script file type when the top-level script is loaded. It is reset to "none" when that script terminates. See https://ss64.com/nt/errorlevel.html for more details, section "Old style .bat Batch files vs .cmd Batch scripts", and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J (comment by Mark Zbikowski).
2020-07-01 00:15:52 +00:00
Quit:
if (BatType != CMD_TYPE)
{
if (retval != 0)
nErrorLevel = retval;
}
else
{
nErrorLevel = retval;
}
return retval;
}
#endif /* INCLUDE_CMD_ASSOC */