/* * Copyright Likewise Software 2004-2009 * All rights reserved. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the license, or (at * your option) any later version. * * This library 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 GNU Lesser * General Public License for more details. You should have received a copy * of the GNU Lesser General Public License along with this program. If * not, see . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright (C) Likewise Software. All rights reserved. * * Module Name: * * regshell.c * * Abstract: * * Registry * * Registry Shell application * * Authors: Adam Bernstein (abernstein@likewise.com) */ #include "regshell.h" #include #include #include "histedit.h" #define REGSHELL_ESC_CHAR '|' static int gCaughtSignal; typedef struct _EDITLINE_CLIENT_DATA { int continuation; PREGSHELL_PARSE_STATE pParseState; /* File name completion Data */ PSTR pszCompletePrevCmd; PSTR pszCompletePrevArg; PSTR *ppszCompleteMatches; DWORD dwCompleteMatchesLen; DWORD dwEnteredTextLen; } EDITLINE_CLIENT_DATA, *PEDITLINE_CLIENT_DATA; void pfnRegShellSignal(int signal) { gCaughtSignal = signal; } PSTR RegShellGetRootKey( PREGSHELL_PARSE_STATE pParseState) { return pParseState->pszFullRootKeyName ? pParseState->pszFullRootKeyName : pParseState->pszDefaultRootKeyName; } PSTR RegShellGetDefaultKey( PREGSHELL_PARSE_STATE pParseState) { return pParseState->pszFullKeyPath ? pParseState->pszFullKeyPath : pParseState->pszDefaultKey; } DWORD RegShellListKeys( PREGSHELL_PARSE_STATE pParseState, PREGSHELL_CMD_ITEM rsItem) { DWORD dwError = 0; DWORD dwSubKeyLen = 0; DWORD i = 0; PSTR pszSubKey = NULL; LW_WCHAR **ppSubKeys = NULL; BAIL_ON_INVALID_HANDLE(pParseState); BAIL_ON_INVALID_HANDLE(pParseState->hReg); dwError = RegShellUtilGetKeys( pParseState->hReg, RegShellGetRootKey(pParseState), RegShellGetDefaultKey(pParseState), rsItem->keyName, &ppSubKeys, &dwSubKeyLen); BAIL_ON_REG_ERROR(dwError); for (i=0; ihReg); dwError = RegShellUtilAddKey( pParseState->hReg, RegShellGetRootKey(pParseState), RegShellGetDefaultKey(pParseState), rsItem->keyName, TRUE); cleanup: return dwError; error: goto cleanup; } DWORD RegShellDeleteKey( PREGSHELL_PARSE_STATE pParseState, PREGSHELL_CMD_ITEM rsItem) { DWORD dwError = 0; BAIL_ON_INVALID_HANDLE(pParseState); BAIL_ON_INVALID_HANDLE(pParseState->hReg); dwError = RegShellUtilDeleteKey( pParseState->hReg, RegShellGetRootKey(pParseState), RegShellGetDefaultKey(pParseState), rsItem->keyName); cleanup: return dwError; error: goto cleanup; } DWORD RegShellDeleteTree( PREGSHELL_PARSE_STATE pParseState, PREGSHELL_CMD_ITEM rsItem) { DWORD dwError = 0; PSTR pszKeyName = NULL; BAIL_ON_INVALID_HANDLE(pParseState); BAIL_ON_INVALID_HANDLE(pParseState->hReg); pszKeyName = rsItem->keyName; if (pszKeyName && pszKeyName[0] == '\\') { pszKeyName++; } dwError = RegShellUtilDeleteTree( pParseState->hReg, RegShellGetRootKey(pParseState), RegShellGetDefaultKey(pParseState), pszKeyName); cleanup: return dwError; error: goto cleanup; } DWORD RegShellDeleteValue( PREGSHELL_PARSE_STATE pParseState, PREGSHELL_CMD_ITEM rsItem) { DWORD dwError = 0; BAIL_ON_INVALID_HANDLE(pParseState); BAIL_ON_INVALID_HANDLE(pParseState->hReg); dwError = RegShellUtilDeleteValue( pParseState->hReg, RegShellGetRootKey(pParseState), RegShellGetDefaultKey(pParseState), rsItem->keyName, rsItem->valueName); cleanup: return dwError; error: goto cleanup; } DWORD RegShellSetValue( PREGSHELL_PARSE_STATE pParseState, PREGSHELL_CMD_ITEM rsItem) { LW_PVOID data = NULL; DWORD dataLen = 0; DWORD dwError = 0; DWORD type = 0; PSTR pszKeyName = NULL; BOOLEAN bFullPath = FALSE; pszKeyName = rsItem->keyName; if (pszKeyName && pszKeyName[0] == '\\') { pszKeyName++; bFullPath = TRUE; } dwError = RegShellUtilGetValue( pParseState->hReg, RegShellGetRootKey(pParseState), !bFullPath ? RegShellGetDefaultKey(pParseState) : NULL, pszKeyName, rsItem->valueName, &type, NULL, NULL); if (rsItem->command == REGSHELL_CMD_SET_VALUE) { BAIL_ON_REG_ERROR(dwError); } else { /* Don't allow addition of existing value */ if (dwError == 0) { dwError = LWREG_ERROR_DUPLICATE_KEYVALUENAME; BAIL_ON_REG_ERROR(dwError); } type = rsItem->type; } switch (type) { case REG_MULTI_SZ: data = rsItem->args; break; case REG_SZ: data = rsItem->args[0]; break; case REG_DWORD: case REG_BINARY: data = rsItem->binaryValue; dataLen = rsItem->binaryValueLen; break; default: break; } dwError = RegShellUtilSetValue( pParseState->hReg, RegShellGetRootKey(pParseState), RegShellGetDefaultKey(pParseState), rsItem->keyName, rsItem->valueName, type, data, dataLen); cleanup: return dwError; error: goto cleanup; } DWORD RegShellDumpByteArray( PBYTE pByteArray, DWORD dwByteArrayLen) { DWORD i = 0; DWORD dwError = 0; BAIL_ON_INVALID_POINTER(pByteArray); for (i=0; iargs[0], NULL, NULL, &parseH); BAIL_ON_REG_ERROR(dwError); importCtx.hReg = hReg; importCtx.eImportMode = eMode; dwError = RegParseInstallCallback( parseH, RegShellUtilImportCallback, &importCtx, NULL); BAIL_ON_REG_ERROR(dwError); dwError = RegParseRegistry(parseH); BAIL_ON_REG_ERROR(dwError); RegParseClose(parseH); cleanup: return dwError; error: goto cleanup; } DWORD RegShellExportFile( HANDLE hReg, PREGSHELL_CMD_ITEM rsItem ) { DWORD dwError = 0; DWORD dwSubKeysCount = 0; DWORD dwMaxSubKeyLen = 0; char szRegFileName[PATH_MAX + 1]; FILE* fp = NULL; HKEY hSubKey = NULL; HKEY hRootKey = NULL; PSTR pszFullPath = NULL; PSTR pszRootFullPath = NULL; PSTR pszDefaultKey = NULL; PSTR pszRootKey = NULL; PSTR pszTemp = NULL; strcpy(szRegFileName, rsItem->args[0]); RegStripWhitespace(szRegFileName, TRUE, TRUE); if (!strcmp(szRegFileName, "-")) { fp = stdout; } else { fp = fopen(szRegFileName, "w"); } if (fp == NULL) { dwError = errno; goto error; } if (rsItem->keyName && *rsItem->keyName) { dwError = LwRtlCStringDuplicate( &pszRootKey, rsItem->keyName); BAIL_ON_REG_ERROR(dwError); pszTemp = strchr(pszRootKey, '\\'); if (pszTemp) { *pszTemp = '\0'; } dwError = RegOpenKeyExA(hReg, NULL, pszRootKey, 0, KEY_READ, &hRootKey); if (dwError) { dwError = RegOpenKeyExA(hReg, NULL, HKEY_THIS_MACHINE, 0, KEY_READ, &hRootKey); BAIL_ON_REG_ERROR(dwError); LWREG_SAFE_FREE_STRING(pszRootKey); dwError = LwRtlCStringDuplicate( &pszRootKey, HKEY_THIS_MACHINE); BAIL_ON_REG_ERROR(dwError); pszDefaultKey = strchr(rsItem->keyName, '\\'); if (pszDefaultKey) { pszDefaultKey++; } else { pszDefaultKey = rsItem->keyName; } } else { pszDefaultKey = pszTemp + 1; } BAIL_ON_REG_ERROR(dwError); dwError = RegShellCanonicalizePath(pszDefaultKey, NULL, &pszFullPath, NULL, NULL); BAIL_ON_REG_ERROR(dwError); if (pszFullPath && strcmp(pszFullPath, "\\") != 0) { dwError = RegOpenKeyExA( hReg, hRootKey, pszFullPath+1, 0, KEY_READ, &hSubKey); BAIL_ON_REG_ERROR(dwError); } else { hSubKey = hRootKey; hRootKey = NULL; } } dwError = LwRtlCStringAllocatePrintf( &pszRootFullPath, "%s%s", pszRootKey, pszFullPath); BAIL_ON_REG_ERROR(dwError); dwError = RegQueryInfoKeyA( hReg, hSubKey, NULL, NULL, NULL, &dwSubKeysCount, &dwMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL); dwError = RegShellUtilExport(hReg, fp, hSubKey, pszRootFullPath, dwSubKeysCount, dwMaxSubKeyLen); BAIL_ON_REG_ERROR(dwError); cleanup: if (hSubKey) { RegCloseKey(hReg, hSubKey); } if (hRootKey) { RegCloseKey(hReg, hRootKey); } LWREG_SAFE_FREE_STRING(pszRootKey); LWREG_SAFE_FREE_STRING(pszFullPath); LWREG_SAFE_FREE_STRING(pszRootFullPath); if (fp && fp != stdout) { fclose(fp); } return dwError; error: goto cleanup; } DWORD RegShellListValues( PREGSHELL_PARSE_STATE pParseState, PREGSHELL_CMD_ITEM rsItem, PDWORD pdwValuesListed) { DWORD dwError = 0; DWORD dwValuesLen = 0; DWORD i = 0; DWORD dwMultiIndex = 0; DWORD dwValueNameLenMax = 0; DWORD dwValueNameLen = 0; DWORD dwValue = 0; DWORD dwValuesListed = 0; DWORD dwEscapedValueLen = 0; PSTR pszValueName = NULL; PSTR *ppszMultiStrArray = NULL; PSTR pszEscapedValue = NULL; PBYTE pData = NULL; PREGSHELL_UTIL_VALUE pValues = NULL; dwError = RegShellUtilGetValues( pParseState->hReg, RegShellGetRootKey(pParseState), RegShellGetDefaultKey(pParseState), rsItem->keyName, &pValues, &dwValuesLen); BAIL_ON_REG_ERROR(dwError); for (i=0; idwValueNameLenMax) { dwValueNameLenMax = dwValueNameLen; } } dwValueNameLenMax++; for (i=0; icommand) { case REGSHELL_CMD_LIST_KEYS: pszErrorPrefix = "list_keys: failed "; dwError = RegShellListKeys(pParseState, rsItem); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_LIST: case REGSHELL_CMD_DIRECTORY: pszErrorPrefix = "list: failed "; pszRootKeyName = RegShellGetRootKey(pParseState); pszFullKeyName = RegShellGetDefaultKey(pParseState); if (pszRootKeyName) { printf("\n[%s%s%s%s%s]\n", pszRootKeyName ? pszRootKeyName : "", pszFullKeyName ? "\\" : "", pszFullKeyName ? pszFullKeyName : "", rsItem->keyName ? "\\" : "", rsItem->keyName ? rsItem->keyName : "\\"); } if (pszRootKeyName) { dwError = RegShellListValues( pParseState, rsItem, &dwValuesListed); BAIL_ON_REG_ERROR(dwError); } if (dwValuesListed > 0) { printf("\n"); } dwError = RegShellListKeys(pParseState, rsItem); BAIL_ON_REG_ERROR(dwError); printf("\n"); break; case REGSHELL_CMD_ADD_KEY: pszErrorPrefix = "add_key: failed"; dwError = RegShellAddKey(pParseState, rsItem); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_DELETE_KEY: pszErrorPrefix = "delete_key: failed "; dwError = RegShellDeleteKey(pParseState, rsItem); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_DELETE_VALUE: pszErrorPrefix = "delete_value: failed "; dwError = RegShellDeleteValue(pParseState, rsItem); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_DELETE_TREE: pszErrorPrefix = "delete_tree: failed "; dwError = RegShellDeleteTree(pParseState, rsItem); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_SET_VALUE: pszErrorPrefix = "set_value: failed "; dwError = RegShellSetValue(pParseState, rsItem); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_ADD_VALUE: pszErrorPrefix = "add_value: failed "; dwError = RegShellSetValue(pParseState, rsItem); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_HELP: RegShellUsage(argv[0]); break; case REGSHELL_CMD_CHDIR: dwError = LwRtlCStringDuplicate(&pszNewKeyName, &argv[2][1]); BAIL_ON_REG_ERROR(dwError); pszNewKeyName [strlen(pszNewKeyName) - 1] = '\0'; pszKeyName = pszNewKeyName; pszToken = strtok_r(pszKeyName, "\\/", &pszStrtokState); if (!pszToken) { /* * Handle special case where the only thing provided in * the path is one or more \ characters (e.g [\\\]). In * this case, strtok_r() return NULL when parsing * a non-zero length pszKeyName string. This is essentially * the cd \ case */ if (pParseState->pszDefaultKey) { LWREG_SAFE_FREE_MEMORY(pParseState->pszDefaultKey); dwError = 0; goto cleanup; } LWREG_SAFE_FREE_MEMORY(pParseState->pszDefaultRootKeyName); } if (pParseState->pszDefaultKey) { /* * Another special case when the path begins with a / or \. * Force the current working directory to root. */ if (pszNewKeyName[0] != '/' && pszNewKeyName[0] != '\\') { dwError = LwRtlCStringDuplicate( &pszNewDefaultKey, pParseState->pszDefaultKey); BAIL_ON_REG_ERROR(dwError); } } while (pszToken) { pszKeyName = NULL; dwOpenRootKeyError = RegOpenKeyExA( pParseState->hReg, NULL, pszToken, 0, KEY_READ, &hRootKey); if (dwOpenRootKeyError == 0) { RegCloseKey(pParseState->hReg, hRootKey); LWREG_SAFE_FREE_MEMORY(pParseState->pszDefaultRootKeyName); LWREG_SAFE_FREE_STRING(pParseState->pszDefaultKey); LWREG_SAFE_FREE_STRING(pszNewDefaultKey); dwError = LwRtlCStringDuplicate( &pParseState->pszDefaultRootKeyName, pszToken); BAIL_ON_REG_ERROR(dwError); } else if (strcmp(pszToken, "..") == 0) { if (pszNewDefaultKey) { pszPwd = strrchr(pszNewDefaultKey, '\\'); if (pszPwd) { pszPwd[0] = '\0'; } else { pszPwd = strrchr(pszNewDefaultKey, '/'); if (pszPwd) { pszPwd[0] = '\0'; } else { LWREG_SAFE_FREE_MEMORY(pszNewDefaultKey); } } } } else if (strcmp(pszToken, ".") == 0) { /* This is a no-op */ } else if (strcmp(pszToken, "...") == 0) { /* This is a broken path! */ dwError = LWREG_ERROR_INVALID_CONTEXT; BAIL_ON_REG_ERROR(dwError); } else if (pszNewDefaultKey) { /* Append this token to current relative path */ dwError = RegAllocateMemory( sizeof(*pszPwd) * (strlen(pszToken) + strlen(pszNewDefaultKey)+3), (PVOID*)&pszPwd); BAIL_ON_REG_ERROR(dwError); strcpy(pszPwd, pszNewDefaultKey); strcat(pszPwd, "\\"); strcat(pszPwd, pszToken); dwError = RegShellIsValidKey( pParseState->hReg, pParseState->pszDefaultRootKeyName, pszPwd); if (dwError) { LwRegGetErrorString(dwError, szError, sizeof(szError)-1); printf("cd: key not valid '%s' [%s]\n", pszToken, szError); LWREG_SAFE_FREE_MEMORY(pszPwd); pszPwd = NULL; bChdirOk = FALSE; dwError = 0; break; } else { LWREG_SAFE_FREE_MEMORY(pszNewDefaultKey); pszNewDefaultKey = pszPwd; pszPwd = NULL; } } else { dwError = RegShellIsValidKey( pParseState->hReg, pParseState->pszDefaultRootKeyName, pszToken); if (dwError) { LwRegGetErrorString(dwError, szError, sizeof(szError)-1); printf("cd: key not valid '%s' [%s]\n", pszToken, szError); bChdirOk = FALSE; dwError = 0; break; } else { LWREG_SAFE_FREE_MEMORY(pszNewDefaultKey); dwError = LwRtlCStringDuplicate( &pszNewDefaultKey, pszToken); BAIL_ON_REG_ERROR(dwError); } } pszToken = strtok_r(pszKeyName, "\\/", &pszStrtokState); } if (bChdirOk) { if (pParseState->pszDefaultKey) { LWREG_SAFE_FREE_MEMORY(pParseState->pszDefaultKey); } pParseState->pszDefaultKey = pszNewDefaultKey; pszNewDefaultKey = NULL; } else { LWREG_SAFE_FREE_MEMORY(pszNewDefaultKey); } break; case REGSHELL_CMD_PWD: if (RegShellGetRootKey(pParseState)) { printf("[%s\\%s]\n\n", RegShellGetRootKey(pParseState), RegShellGetDefaultKey(pParseState) ? RegShellGetDefaultKey(pParseState) : ""); } else { printf("'%s'\n\n", "\\"); } break; case REGSHELL_CMD_QUIT: exit(0); break; case REGSHELL_CMD_LIST_VALUES: pszErrorPrefix = "list_values: failed "; dwError = RegShellListValues(pParseState, rsItem, NULL); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_IMPORT: dwError = RegShellImportFile( pParseState->hReg, rsItem, REGSHELL_UTIL_IMPORT_OVERWRITE); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_UPGRADE: dwError = RegShellImportFile( pParseState->hReg, rsItem, REGSHELL_UTIL_IMPORT_UPGRADE); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_EXPORT: dwError = RegShellExportFile(pParseState->hReg, rsItem); BAIL_ON_REG_ERROR(dwError); break; case REGSHELL_CMD_SET_HIVE: if (argc < 3) { dwError = LWREG_ERROR_INVALID_CONTEXT; goto error; } LWREG_SAFE_FREE_STRING(pParseState->pszDefaultRootKeyName); dwError = LwRtlCStringDuplicate(&pParseState->pszDefaultRootKeyName, argv[2]); BAIL_ON_REG_ERROR(dwError); LWREG_SAFE_FREE_STRING(pParseState->pszDefaultKey); break; default: break; } } else { printf("%s: error parsing command, invalid syntax\n", argv[0]); RegShellUsage(argv[0]); } cleanup: RegShellCmdParseFree(rsItem); LWREG_SAFE_FREE_STRING(pszNewKeyName); LWREG_SAFE_FREE_STRING(pszNewDefaultKey); return dwError; error: goto cleanup; } DWORD RegShellInitParseState( PREGSHELL_PARSE_STATE *ppParseState) { DWORD dwError = 0; PREGSHELL_PARSE_STATE pParseState = NULL; BAIL_ON_INVALID_POINTER(ppParseState); dwError = RegAllocateMemory(sizeof(*pParseState), (PVOID*)&pParseState); BAIL_ON_REG_ERROR(dwError); dwError = RegOpenServer(&pParseState->hReg); BAIL_ON_REG_ERROR(dwError); dwError = RegLexOpen(&pParseState->lexHandle); BAIL_ON_REG_ERROR(dwError); dwError = RegIOBufferOpen(&pParseState->ioHandle); BAIL_ON_REG_ERROR(dwError); *ppParseState = pParseState; cleanup: return dwError; error: LWREG_SAFE_FREE_STRING(pParseState->pszDefaultRootKeyName); RegCloseServer(pParseState->hReg); RegLexClose(pParseState->lexHandle); RegIOClose(pParseState->ioHandle); LWREG_SAFE_FREE_MEMORY(pParseState); goto cleanup; } DWORD RegShellCloseParseState( PREGSHELL_PARSE_STATE pParseState) { DWORD dwError = 0; BAIL_ON_INVALID_POINTER(pParseState); LWREG_SAFE_FREE_STRING(pParseState->pszDefaultRootKeyName); LWREG_SAFE_FREE_STRING(pParseState->pszDefaultKey); LWREG_SAFE_FREE_STRING(pParseState->pszDefaultKeyCompletion); LWREG_SAFE_FREE_STRING(pParseState->pszFullRootKeyName); LWREG_SAFE_FREE_STRING(pParseState->pszFullKeyPath); RegIOClose(pParseState->ioHandle); RegLexClose(pParseState->lexHandle); RegCloseServer(pParseState->hReg); LwRtlMemoryFree(pParseState); cleanup: return dwError; error: goto cleanup; } DWORD RegShellStrcmpLen( PSTR pszMatchStr, PSTR pszHaystackStr, DWORD dwMatchMaxLen, PDWORD pdwExtentLen) { DWORD index = 0; DWORD dwMaxMatch = 0; DWORD dwError = 0; BAIL_ON_INVALID_POINTER(pszMatchStr); BAIL_ON_INVALID_POINTER(pszHaystackStr); BAIL_ON_INVALID_POINTER(pdwExtentLen); dwMaxMatch = *pdwExtentLen; for (index = 0; pszMatchStr[index] && pszHaystackStr[index] && pszMatchStr[index] == pszHaystackStr[index] && index < dwMaxMatch; index++) { ; } if (dwMatchMaxLen) { if (pszHaystackStr[dwMatchMaxLen] == '\0') { *pdwExtentLen = index; } else { dwError = LWREG_ERROR_INVALID_CONTEXT; } } else { *pdwExtentLen = index; } cleanup: return dwError; error: goto cleanup; } DWORD RegShellCompletionMatch( PSTR pszMatchStr, PWSTR *ppSubKeys, DWORD dwSubKeyLen, PSTR pszDefaultRootKeyName, PSTR pszDefaultKey, PSTR **pppMatchArgs, PDWORD pdwMatchArgsLen, PDWORD pdwMatchCommonIndex, PDWORD pdwMatchCommonLen) { DWORD dwError = 0; DWORD i = 0; DWORD dwMatchArgsLen = 0; DWORD dwMinCommonLen = INT32_MAX; DWORD dwMaxCommonLen = 0; DWORD dwPrevMaxCommonLen = 0; DWORD dwMinCommonLenIndex = 0; DWORD dwMaxCommonLenIndex = 0; DWORD dwMatchMaxLen = 0; DWORD dwStrLen = 0; BOOLEAN bBackslashEnd = FALSE; PSTR pszSubKey = NULL; PSTR *ppMatchArgs = NULL; PSTR pszPtr = NULL; if (dwSubKeyLen > 0) { dwError = RegAllocateMemory(sizeof(*ppMatchArgs) * dwSubKeyLen, (PVOID*)&ppMatchArgs); BAIL_ON_REG_ERROR(dwError); } dwStrLen = strlen(pszMatchStr); if (dwStrLen>0 && pszMatchStr[dwStrLen-1] == '\\') { dwMatchMaxLen = dwStrLen-1; pszMatchStr[dwMatchMaxLen] = '\0'; } for (i=0; ipParseState); BAIL_ON_INVALID_HANDLE(cldata->pParseState->hReg); if (cldata->pParseState->pszDefaultKey && !cldata->pParseState->pszDefaultKeyCompletion) { dwError = LwRtlCStringDuplicate( &cldata->pParseState->pszDefaultKeyCompletion, cldata->pParseState->pszDefaultKey); BAIL_ON_REG_ERROR(dwError); } dwLineLen = lineInfoCtx->cursor - lineInfoCtx->buffer; dwError = RegAllocateMemory(sizeof(*pszCurrentCmd) * (dwLineLen+1), (PVOID*)&pszCurrentCmd); BAIL_ON_REG_ERROR(dwError); strncat(pszCurrentCmd, lineInfoCtx->buffer, dwLineLen); /* Find end of current command */ for (pszPtr = pszCurrentCmd; *pszPtr && !isspace((int) *pszPtr); pszPtr++) ; /* Find the start of the current key */ for (;*pszPtr && isspace((int) *pszPtr); pszPtr++) { *pszPtr = '\0'; } if (pszPtr && *pszPtr) { dwArgPtrLen = strlen(pszPtr); dwLineLen = pszPtr - pszCurrentCmd; pszArgPtr = pszCurrentCmd + dwLineLen; if (pszArgPtr[strlen(pszArgPtr)-1] != '\\') { pszPtr = strrchr(pszArgPtr, '\\'); if (pszPtr) { pszArgPtr = pszPtr + 1; } } else { /* * This is not quite right here. * When user enters "cd a\b\c" then tries tab * completion, this code breaks. */ pszPtr = strchr(pszArgPtr, '\\'); if (pszPtr && pszPtr != &pszArgPtr[strlen(pszArgPtr)-1]) { pszArgPtr = pszPtr + 1; } bBackslashEnd = TRUE; } dwStrLen = strlen(pszArgPtr) + 3; if (cldata->pParseState->pszDefaultRootKeyName) { dwStrLen += strlen(cldata->pParseState->pszDefaultRootKeyName); } if (cldata->pParseState->pszDefaultKeyCompletion) { dwStrLen += strlen(cldata->pParseState->pszDefaultKeyCompletion); } dwError = RegAllocateMemory(sizeof(*pszFullMatchStr) * dwStrLen, (PVOID*)&pszFullMatchStr); BAIL_ON_REG_ERROR(dwError); if (cldata->pParseState->pszDefaultRootKeyName) { strcat(pszFullMatchStr, cldata->pParseState->pszDefaultRootKeyName); strcat(pszFullMatchStr, "\\"); } if (cldata->pParseState->pszDefaultKeyCompletion) { strcat(pszFullMatchStr, cldata->pParseState->pszDefaultKeyCompletion); strcat(pszFullMatchStr, "\\"); } if (cldata->pParseState->pszDefaultKeyCompletion) { pszPtr = strstr(pszArgPtr, cldata->pParseState->pszDefaultKeyCompletion); } else { pszPtr = NULL; } if (!pszPtr || (pszPtr && pszPtr != pszArgPtr)) { strcat(pszFullMatchStr, pszArgPtr); } pszArgPtr = pszFullMatchStr; } else { pszArgPtr = ""; } /* * Something was looked up already by tab completion. Look to see if * any input has changed since the last list was presented. If not, * just display the existing list again. */ if (cldata->pszCompletePrevCmd && cldata->pszCompletePrevArg && strcmp(cldata->pszCompletePrevCmd, pszCurrentCmd) == 0 && strcmp(cldata->pszCompletePrevArg, pszArgPtr) == 0) { ppMatchArgs = cldata->ppszCompleteMatches; dwMatchArgsLen = cldata->dwCompleteMatchesLen; printf("\n"); for (i=0; ipParseState->hReg, RegShellGetRootKey(cldata->pParseState), cldata->pParseState->pszDefaultKeyCompletion, NULL, &ppSubKeys, &dwSubKeyLen); BAIL_ON_REG_ERROR(dwError); bAllocArgs = TRUE; dwError = RegShellCompletionMatch( pszArgPtr, ppSubKeys, dwSubKeyLen, cldata->pParseState->pszDefaultRootKeyName, cldata->pParseState->pszDefaultKeyCompletion, &ppMatchArgs, &dwMatchArgsLen, &dwMatchBestIndex, &dwMatchBestLen); /* * Match is ambiguous. Display the longest matches that are all common. */ if (dwMatchArgsLen > 1) { if (pszArgPtr && *pszArgPtr) { dwStrLen = strlen(pszArgPtr); dwError = LwRtlCStringDuplicate( &pszBestMatchValue, ppMatchArgs[dwMatchBestIndex]); BAIL_ON_REG_ERROR(dwError); pszBestMatchValue[dwMatchBestLen] = '\0'; if (pszBestMatchValue[dwStrLen] && el_insertstr(el, &pszBestMatchValue[dwStrLen]) == -1) { printf("Oops: 1 el_insertstr failed\n"); } else { putchar('\a'); } } else { /* * No input provided, display all keys at this level */ putchar('\a'); for (i=0; idwEnteredTextLen) == 0)) { if (dwMatchArgsLen == 1) { dwStrLen = strlen(pszArgPtr); /* * Completion is unique. add to command line. */ if (ppMatchArgs[i][dwStrLen] && el_insertstr(el, &ppMatchArgs[i][dwStrLen]) == -1) { printf("Oops: 2 el_insertstr failed\n"); } else if (!bBackslashEnd) { /* No string to append */ if (el_insertstr(el, "\\") == -1) { printf("Oops: 3 el_insertstr failed\n"); } } } else { dwError = LwRtlCStringAllocateFromWC16String(&pszSubKey, ppSubKeys[0]); BAIL_ON_REG_ERROR(dwError); /* * Completion is unique. add to command line. */ pszPtr = strrchr(pszSubKey, '\\'); if (pszPtr) { pszPtr++; } else { pszPtr = pszSubKey; } if (el_insertstr(el, pszPtr) == -1) { printf("Oops: 2 el_insertstr failed\n"); } /* No string to append */ if (el_insertstr(el, "\\") == -1) { printf("Oops: 3 el_insertstr failed\n"); } } if (ppMatchArgs[i]) { pszPtr = strchr(ppMatchArgs[i], '\\'); if (pszPtr) { pszPtr++; } else { pszPtr = ppMatchArgs[i]; } } else { pszPtr = strchr(pszSubKey, '\\'); if (pszPtr) { pszPtr++; } else { pszPtr = pszSubKey; } } LWREG_SAFE_FREE_STRING(cldata->pParseState->pszDefaultKeyCompletion); dwError = LwRtlCStringDuplicate( &cldata->pParseState->pszDefaultKeyCompletion, pszPtr); BAIL_ON_REG_ERROR(dwError); LWREG_SAFE_FREE_STRING(cldata->pszCompletePrevCmd); LWREG_SAFE_FREE_STRING(cldata->pszCompletePrevArg); for (i=0; cldata->ppszCompleteMatches && idwCompleteMatchesLen; i++) { LWREG_SAFE_FREE_STRING(cldata->ppszCompleteMatches[i]); } LWREG_SAFE_FREE_MEMORY(cldata->ppszCompleteMatches); cldata->dwCompleteMatchesLen = 0; cldata-> dwEnteredTextLen = strlen(pszPtr) + 1; bExactMatch = TRUE; dwError = CC_REFRESH; } else { printf("\a\n"); for (i=0; ipszCompletePrevCmd); cldata->pszCompletePrevCmd = pszCurrentCmd; pszCurrentCmd = NULL; LWREG_SAFE_FREE_STRING(cldata->pszCompletePrevArg); cldata->pszCompletePrevArg = pszBestMatchValue; pszBestMatchValue = NULL; /* Free previous command line entered */ if (bAllocArgs) { RegShellCmdlineParseFree(cldata->dwCompleteMatchesLen, cldata->ppszCompleteMatches); } cldata->ppszCompleteMatches = ppMatchArgs; cldata->dwCompleteMatchesLen = dwMatchArgsLen; } else { RegShellCmdlineParseFree(dwMatchArgsLen, ppMatchArgs); } LWREG_SAFE_FREE_MEMORY(pszFullMatchStr); LWREG_SAFE_FREE_STRING(pszSubKey); LWREG_SAFE_FREE_STRING(pszCurrentCmd); LWREG_SAFE_FREE_STRING(pszBestMatchValue); return dwError; error: goto cleanup; } static char * pfnRegShellPromptCallback(EditLine *el) { static char promptBuf[1024] = ""; EDITLINE_CLIENT_DATA *cldata = NULL; el_get(el, EL_CLIENTDATA, (void *) &cldata); snprintf(promptBuf, sizeof(promptBuf), "%s%s%s%s ", cldata->pParseState->pszDefaultRootKeyName ? cldata->pParseState->pszDefaultRootKeyName : "", cldata->pParseState->pszDefaultKey ? "\\" : "", cldata->pParseState->pszDefaultKey ? cldata->pParseState->pszDefaultKey : "\\", cldata->continuation ? ">>>" : ">"); return promptBuf; } DWORD RegShellExecuteCmdLine( PREGSHELL_PARSE_STATE pParseState, PSTR pszCmdLine, DWORD dwCmdLineLen) { DWORD dwError = 0; DWORD dwNewArgc = 0; PSTR *pszNewArgv = NULL; dwError = RegIOBufferSetData( pParseState->ioHandle, pszCmdLine, dwCmdLineLen); BAIL_ON_REG_ERROR(dwError); dwError = RegShellCmdlineParseToArgv( pParseState, &dwNewArgc, &pszNewArgv); if (dwError == 0) { dwError = RegShellProcessCmd(pParseState, dwNewArgc, pszNewArgv); } if (dwError) { RegPrintError("regshell", dwError); dwError = 0; } RegShellCmdlineParseFree(dwNewArgc, pszNewArgv); LWREG_SAFE_FREE_STRING(pParseState->pszFullRootKeyName); pParseState->pszFullKeyPath = NULL; cleanup: return dwError; error: goto cleanup; } void RegShellHandleSignalEditLine(int *signal, void *ctx) { #ifdef SIGWINCH if (*signal == SIGWINCH) { el_set((EditLine *) ctx, EL_REFRESH); } #endif *signal = 0; } DWORD RegShellProcessInteractiveEditLine( FILE *readFP, PREGSHELL_PARSE_STATE pParseState, PSTR pszProgramName) { History *hist = NULL; HistEvent ev; EDITLINE_CLIENT_DATA el_cdata = {0}; int num = 0; int ncontinuation = 0; int rv = 0; const char *buf = NULL; EditLine *el = NULL; BOOLEAN bHistFirst = FALSE; DWORD dwError = 0; DWORD i = 0; DWORD dwCmdLineLen = 0; DWORD dwEventNum = 0; PSTR pszCmdLine = NULL; PSTR pszNewCmdLine = NULL; PSTR pszNumEnd = NULL; PSTR pszHistoryFileDir = NULL; PSTR pszHistoryFileName = NULL; const char *hist_str = NULL; struct passwd *userPwdEntry = NULL; hist = history_init(); history(hist, &ev, H_SETSIZE, 100); el = el_init(pszProgramName, stdin, stdout, stderr); /* Make configurable in regshellrc file */ el_set(el, EL_EDITOR, "emacs"); /* Signal handling in editline seems not to function... */ el_set(el, EL_SIGNAL, 0); /* Set escape character from \ to | */ el_set(el, EL_ESC_CHAR, (int) REGSHELL_ESC_CHAR); /* Editline prompt function; display info from pParseState */ el_set(el, EL_PROMPT, pfnRegShellPromptCallback); /* Set regshell context */ el_cdata.pParseState = pParseState; el_set(el, EL_CLIENTDATA, (void *) &el_cdata); /* Setup history context, and load previous history file */ el_set(el, EL_HIST, history, hist); /* Build fully qualified path for history file */ userPwdEntry = getpwuid(getuid()); if (userPwdEntry) { pszHistoryFileDir = userPwdEntry->pw_dir; } if (!pszHistoryFileDir) { pszHistoryFileDir = "/tmp"; } dwError = RegAllocateMemory( strlen(pszHistoryFileDir) + sizeof("/.regshell_history"), (PVOID) &pszHistoryFileName); BAIL_ON_REG_ERROR(dwError); strcpy(pszHistoryFileName, pszHistoryFileDir); strcat(pszHistoryFileName, "/.regshell_history"); /* Retrieve this from user's home directory */ history(hist, &ev, H_LOAD, pszHistoryFileName); /* * Bind j, k in vi command mode to previous and next line, instead * of previous and next history. */ el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL); el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL); /* * Register complete function callback */ el_set(el, EL_ADDFN, "ed-complete", "Complete argument", pfnRegShellCompleteCallback); el_set(el, EL_BIND, "^I", "ed-complete", NULL); /* * Source the user's defaults file. */ el_source(el, NULL); while ((buf = el_gets(el, &num))!=NULL && num!=0) { if (gCaughtSignal > 0) { RegShellHandleSignalEditLine(&gCaughtSignal, (LW_PVOID) el); } if (num>1 && buf[num-2] == REGSHELL_ESC_CHAR) { ncontinuation = 1; } else { ncontinuation = 0; } #if 1 /* This mess needs to be put into a completion cleanup function */ LWREG_SAFE_FREE_STRING(el_cdata.pParseState->pszDefaultKeyCompletion); for (i=0; el_cdata.ppszCompleteMatches && i ", parseState->pszDefaultRootKeyName ? parseState->pszDefaultRootKeyName : "\\", parseState->pszDefaultKey ? "\\" : "", parseState->pszDefaultKey ? parseState->pszDefaultKey : "\\"); fflush(stdout); } pszCmdLine = fgets(cmdLine, sizeof(cmdLine)-1, readFP); if (pszCmdLine) { if (strlen(cmdLine) == 1) { if (readFP != stdin) { bDoPrompt = FALSE; } continue; } /* Ignore leading white space or # comment on lines */ for (pszTmpStr=cmdLine; (int)*pszTmpStr && isspace((int)*pszTmpStr); pszTmpStr++) { ; } if (pszTmpStr && *pszTmpStr == '#') { printf("%s%s", bFoundComment ? "" : "\n", cmdLine); bDoPrompt = FALSE; bFoundComment = TRUE; continue; } if (cmdLine[strlen(cmdLine)-1] == '\n') { cmdLine[strlen(cmdLine)-1] = '\0'; } if (readFP != stdin) { printf("%s\n", cmdLine); } bDoPrompt = TRUE; bFoundComment = FALSE; dwError = RegShellExecuteCmdLine( parseState, cmdLine, strlen(cmdLine)); BAIL_ON_REG_ERROR(dwError); } } while (!feof(readFP)); cleanup: return dwError; error: if (dwError) { RegPrintError("regshell", dwError); } goto cleanup; } int main(int argc, char *argv[]) { DWORD dwError = 0; PSTR pszInFile = NULL; PREGSHELL_PARSE_STATE parseState = NULL; FILE *readFP = stdin; DWORD indx = 0; struct sigaction action; setlocale(LC_ALL, ""); dwError = RegShellInitParseState(&parseState); BAIL_ON_REG_ERROR(dwError); memset(&action, 0, sizeof(action)); action.sa_handler = pfnRegShellSignal; action.sa_flags = SA_RESTART; if (sigaction(SIGINT, &action, NULL) < 0) { dwError = RegMapErrnoToLwRegError(errno); BAIL_ON_REG_ERROR(dwError); } #ifdef SIGWINCH if (sigaction(SIGWINCH, &action, NULL) < 0) { dwError = RegMapErrnoToLwRegError(errno); BAIL_ON_REG_ERROR(dwError); } #endif indx = 1; while (argc>1 && argv[indx][0] == '-') { if (strcmp(argv[indx], "--file") == 0 || strcmp(argv[indx], "-f") == 0) { argc--; indx++; if (argc>1) { pszInFile = argv[indx++]; argc--; } if (pszInFile) { readFP = fopen(pszInFile, "r"); if (!readFP) { fprintf(stderr, "Error opening file '%s'\n", pszInFile); dwError = RegMapErrnoToLwRegError(errno); BAIL_ON_REG_ERROR(dwError); } } dwError = RegShellProcessInteractive(readFP, parseState); BAIL_ON_REG_ERROR(dwError); if (readFP != stdin) { fclose(readFP); } dwError = 0; goto cleanup; } else if (strcmp(argv[indx], "--help") == 0 || strcmp(argv[indx], "-h") == 0) { RegShellUsage(argv[0]); dwError = 0; goto cleanup; } else if (argv[indx][0] == '-') { printf("ERROR: unknown option %s\n", argv[indx]); RegShellUsage(argv[0]); dwError = 0; goto cleanup; } } if (argc == 1) { dwError = RegShellProcessInteractiveEditLine( readFP, parseState, argv[0]); BAIL_ON_REG_ERROR(dwError); } else { dwError = RegShellProcessCmd(parseState, argc, argv); BAIL_ON_REG_ERROR(dwError); } cleanup: RegShellCloseParseState(parseState); return dwError; error: if (dwError) { RegPrintError("regshell", dwError); } goto cleanup; }