1 /*
2  * regexpl - Console Registry Explorer
3  *
4  * Copyright (C) 2000-2005 Nedko Arnaudov <nedko@users.sourceforge.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; see the file COPYING.  If not, write to
18  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 // ShellCommandValue.cpp: implementation of the CShellCommandValue class.
23 //
24 //////////////////////////////////////////////////////////////////////
25 
26 #include "ph.h"
27 #include "RegistryExplorer.h"
28 #include "ShellCommandValue.h"
29 #include "RegistryTree.h"
30 #include "RegistryKey.h"
31 
32 #define VALUE_CMD				_T("VV")
33 #define VALUE_CMD_LENGTH		COMMAND_LENGTH(VALUE_CMD)
34 #define VALUE_CMD_SHORT_DESC	VALUE_CMD _T(" command is used to view value.\n")
35 
36 //////////////////////////////////////////////////////////////////////
37 // Construction/Destruction
38 //////////////////////////////////////////////////////////////////////
39 
40 CShellCommandValue::CShellCommandValue(CRegistryTree& rTree):m_rTree(rTree)
41 {
42 }
43 
44 CShellCommandValue::~CShellCommandValue()
45 {
46 }
47 
48 BOOL CShellCommandValue::Match(const TCHAR *pchCommand)
49 {
50 	if (_tcsicmp(pchCommand,VALUE_CMD) == 0)
51 		return TRUE;
52 	if (_tcsnicmp(pchCommand,VALUE_CMD _T(".."),VALUE_CMD_LENGTH+2*sizeof(TCHAR)) == 0)
53 		return TRUE;
54 	if (_tcsnicmp(pchCommand,VALUE_CMD _T("/"),VALUE_CMD_LENGTH+1*sizeof(TCHAR)) == 0)
55 		return TRUE;
56 	if (_tcsnicmp(pchCommand,VALUE_CMD _T("\\"),VALUE_CMD_LENGTH+1*sizeof(TCHAR)) == 0)
57 		return TRUE;
58 	return FALSE;
59 }
60 
61 int CShellCommandValue::Execute(CConsole &rConsole, CArgumentParser& rArguments)
62 {
63 	rArguments.ResetArgumentIteration();
64 	TCHAR *pchCommandItself = rArguments.GetNextArgument();
65 
66 	TCHAR *pchParameter;
67 	TCHAR *pchValueFull = NULL;
68 	BOOL blnUnicodeDump = FALSE;
69 	BOOL blnBadParameter = FALSE;
70 	BOOL blnHelp = FALSE;
71 	LONG nError;
72 	DWORD dwValueSize;
73 	DWORD dwType = REG_NONE;
74 	BYTE *pDataBuffer = NULL;
75 	TCHAR *pchFilename = NULL;
76 
77 	if ((_tcsnicmp(pchCommandItself,VALUE_CMD _T(".."),VALUE_CMD_LENGTH+2*sizeof(TCHAR)) == 0)||
78 		(_tcsnicmp(pchCommandItself,VALUE_CMD _T("\\"),VALUE_CMD_LENGTH+1*sizeof(TCHAR)) == 0))
79 	{
80 		pchValueFull = pchCommandItself + VALUE_CMD_LENGTH;
81 	}
82 	else if (_tcsnicmp(pchCommandItself,VALUE_CMD _T("/"),VALUE_CMD_LENGTH+1*sizeof(TCHAR)) == 0)
83 	{
84 		pchParameter = pchCommandItself + VALUE_CMD_LENGTH;
85 		goto CheckValueArgument;
86 	}
87 
88 	while((pchParameter = rArguments.GetNextArgument()) != NULL)
89 	{
90 CheckValueArgument:
91 		blnBadParameter = FALSE;
92 		if ((_tcsicmp(pchParameter,_T("/?")) == 0)
93 			||(_tcsicmp(pchParameter,_T("-?")) == 0))
94 		{
95 			blnHelp = TRUE;
96 			break;
97 		}
98 		else if (_tcsicmp(pchParameter,_T("/u")) == 0)
99 		{
100 			blnUnicodeDump = TRUE;
101 		}
102 		else if ((*pchParameter == _T('/'))&&(*(pchParameter+1) == _T('f')))
103 		{
104 			pchFilename = pchParameter+2;
105 		}
106 		else if (!pchValueFull)
107 		{
108 			pchValueFull = pchParameter;
109 		}
110 		else
111 		{
112 			blnBadParameter = TRUE;
113 		}
114 		if (blnBadParameter)
115 		{
116 			rConsole.Write(_T("Bad parameter: "));
117 			rConsole.Write(pchParameter);
118 			rConsole.Write(_T("\n"));
119 		}
120 	}
121 
122 	CRegistryKey Key;
123 	TCHAR *pchValueName;
124 	const TCHAR *pszEmpty = _T("");
125 	const TCHAR *pszPath;
126 
127 	if (blnHelp)
128 	{
129 		rConsole.Write(GetHelpString());
130 
131 		if (pDataBuffer)
132 			delete pDataBuffer;
133 
134 		return 0;
135 	}
136 
137 	if (pchValueFull)
138 	{
139 		if (_tcscmp(pchValueFull,_T("\\")) == 0)
140 			goto ValueCommandNAonRoot;
141 
142 		TCHAR *pchSep = _tcsrchr(pchValueFull,_T('\\'));
143 		pchValueName = pchSep?(pchSep+1):(pchValueFull);
144 		pszPath = pchSep?pchValueFull:_T(".");
145 
146 		//if (_tcsrchr(pchValueName,_T('.')))
147 		//{
148 		//	pchValueName = _T("");
149 		//	pchPath = pchValueFull;
150 		//}
151 		//else
152 		if (pchSep)
153 			*pchSep = 0;
154 	}
155 	else
156 	{
157 		pchValueName = (TCHAR*)pszEmpty;
158 		pszPath = _T(".");
159 	}
160 
161   if (!m_rTree.GetKey(pszPath,KEY_READ,Key))
162   {
163     rConsole.Write(m_rTree.GetLastErrorDescription());
164     goto SkipValueCommand;
165   }
166 
167 	if (Key.IsRoot())
168     goto ValueCommandNAonRoot;
169 
170   {
171     rConsole.Write(_T("Value name : \""));
172     rConsole.Write(_T("\\"));
173     rConsole.Write(Key.GetKeyName());
174     size_t l = _tcslen(pchValueName);
175     if (l&&
176         (*pchValueName == _T('\"'))&&
177         (pchValueName[l-1] == _T('\"')))
178     {
179       pchValueName[l-1] = 0;
180       pchValueName++;
181     }
182     rConsole.Write(pchValueName);
183     rConsole.Write(_T("\"\n"));
184 
185     nError = Key.GetValue(pchValueName,NULL,NULL,&dwValueSize);
186     if (nError == ERROR_SUCCESS)
187     {
188       pDataBuffer = new BYTE [dwValueSize];
189       Key.GetValue(pchValueName,&dwType,pDataBuffer,&dwValueSize);
190       rConsole.Write(_T("Value type : "));
191       rConsole.Write(CRegistryKey::GetValueTypeName(dwType));
192       rConsole.Write(_T("\nValue data : "));
193       switch(dwType)
194       {
195       case REG_DWORD_LITTLE_ENDIAN:
196         {
197           TCHAR Buffer[3];
198           rConsole.Write(_T("0x"));
199           for (unsigned int i = 0 ; i < dwValueSize ; i++)
200           {
201             _stprintf(Buffer,_T("%02X"),*(pDataBuffer+((dwValueSize-1)-i)));
202             rConsole.Write(Buffer);
203           }
204         }
205         rConsole.Write(_T("\n"));
206         break;
207       case REG_DWORD_BIG_ENDIAN:
208         {
209           TCHAR Buffer[3];
210           rConsole.Write(_T("0x"));
211           for (unsigned int i = 0 ; i < dwValueSize ; i++)
212           {
213             _stprintf(Buffer,_T("%02X"),*(pDataBuffer+i));
214             rConsole.Write(Buffer);
215           }
216         }
217         rConsole.Write(_T("\n"));
218         break;
219       case REG_LINK:
220         break;
221       case REG_MULTI_SZ:
222         {
223           TCHAR *pchCurrentString = (TCHAR *)pDataBuffer;
224           rConsole.Write(_T("\n"));
225           while(*pchCurrentString)
226           {
227             rConsole.Write(_T("\""));
228             rConsole.Write(pchCurrentString);
229             rConsole.Write(_T("\"\n"));
230             pchCurrentString += _tcslen(pchCurrentString)+1;
231           }
232         }
233         break;
234       case REG_RESOURCE_LIST:
235         break;
236       case REG_SZ:
237       case REG_EXPAND_SZ:
238         rConsole.Write(_T("\""));
239         rConsole.Write((TCHAR *)pDataBuffer);
240         rConsole.Write(_T("\"\n"));
241         break;
242       case REG_BINARY:
243       default:
244         {
245           TCHAR Buffer[256];
246           DWORD i, j;
247           for (i = 0 ; i < dwValueSize ; i++)
248           {
249             if (i%16 == 0)
250             {	// ok this is begining of line
251               rConsole.Write(_T("\n"));
252               // print offset
253               _stprintf(Buffer,_T("0x%08X  "),(unsigned int)i);
254               rConsole.Write(Buffer);
255             }
256             else if (i%8 == 0)
257             {	// this is the additional space between 7th and 8th byte in current line
258               rConsole.Write(_T(" "));
259             }
260 
261             // print current byte
262             unsigned int n = *(pDataBuffer+i);
263             _stprintf(Buffer,_T("%02X "),n);
264             rConsole.Write(Buffer);
265 
266             if (i && (i%16 == 15))
267             {	// if this is the last byte in line
268               // Dump text representation
269               for (j = i-15; j <= i; j += blnUnicodeDump?2:1)\
270                   {
271                     if ((j%8 == 0)&&(j%16 != 0))
272                     {	// this is the additional space between 7th and 8th byte in current line
273                       rConsole.Write(_T(" "));
274                     }
275                     ASSERT(i-j < 16);
276                     // write current char representation
277                     if (blnUnicodeDump)
278                     {
279                       ASSERT(j%2 == 0);
280                       wchar_t ch = *(TCHAR *)(pDataBuffer+j);
281 
282                       _stprintf(Buffer,
283 #ifdef _UNICODE
284                                 _T("%c"),
285 #else
286                                 // g++ may print warnings here (warning: __wchar_t format, different type arg (arg 3))
287                                 // %C in format string is a Microsoft extension.
288                                 _T("%C"),
289 #endif
290                                 iswprint(ch)?ch:L'.');
291                     }
292                     else
293                     {
294                       unsigned char ch = *(pDataBuffer+j);
295 
296                       _stprintf(Buffer,
297 #ifdef _UNICODE
298                                 // g++ may print warnings here (warning: __wchar_t format, different type arg (arg 3))
299                                 // %C in format string is a Microsoft extension.
300                                 _T("%C"),
301 #else
302                                 _T("%c"),
303 #endif
304                                 isprint(ch)?ch:'.');
305                     }
306                     rConsole.Write(Buffer);
307                   }	// for
308             }	// if
309           }	// for
310 
311           // print text representation of last line if it is not full (it have less than 16 bytes)
312           // k is pseudo offset
313           for (DWORD k = i; k%16 != 0; k++)
314           {
315             if (k%8 == 0)
316             {	// this is the additional space between 7th and 8th byte in current line
317               rConsole.Write(_T(" "));
318             }
319             _tcscpy(Buffer,_T("   "));	// the replacement of two digit of current byte + spacing
320             rConsole.Write(Buffer);
321             if (k && (k%16 == 15))
322             {	// if this is the last byte in line
323               ASSERT((k-15)%16 == 0);	// k-15 must point at begin of last line
324               for (j = k-15; j < i; j += blnUnicodeDump?2:1)
325               {
326                 if (blnUnicodeDump&&(j+1 >= i))
327                 {	// ok, buffer size is odd number, so we don't display last byte.
328                   ASSERT(j+1 == i);
329                   break;
330                 }
331                 if ((j%8 == 0)&&(j%16 != 0))
332                 {	// this is the additional space between 7th and 8th byte in current line
333                   rConsole.Write(_T(" "));
334                 }
335 
336                 // write current char representation
337                 if (blnUnicodeDump)
338                 {
339                   ASSERT(j%2 == 0);
340                   wchar_t ch = *(TCHAR *)(pDataBuffer+j);
341 
342                   _stprintf(Buffer,
343 #ifdef _UNICODE
344                             _T("%c"),
345 #else
346                             // g++ may print warnings here (warning: __wchar_t format, different type arg (arg 3))
347                             // %C in format string is a Microsoft extension.
348                             _T("%C"),
349 #endif
350                             iswprint(ch)?ch:L'.');
351                 }
352                 else
353                 {
354                   unsigned char ch = *(pDataBuffer+j);
355 
356                   _stprintf(Buffer,
357 #ifdef _UNICODE
358                             // g++ may print warnings here (warning: __wchar_t format, different type arg (arg 3))
359                             // %C in format string is a Microsoft extension.
360                             _T("%C"),
361 #else
362                             _T("%c"),
363 #endif
364                             isprint(ch)?ch:'.');
365                 }
366                 rConsole.Write(Buffer);
367               } // for
368             } // if
369           } // for
370         } // default:
371         rConsole.Write(_T("\n"));
372       } // switch
373       rConsole.Write(_T("\n"));
374 
375       if (pchFilename)
376       {
377         rConsole.Write(_T("Exporting value data to "));
378         rConsole.Write(pchFilename);
379         rConsole.Write(_T(" ...\n"));
380 
381         HANDLE hFile = CreateFile(pchFilename,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
382         if (hFile == INVALID_HANDLE_VALUE)
383         {
384           rConsole.Write(_T("Cannot create new file "));
385           rConsole.Write(pchFilename);
386           rConsole.Write(_T("\n"));
387           goto SkipValueCommand;
388         }
389 
390         DWORD dwBytesWritten;
391         if (!WriteFile(hFile,pDataBuffer,dwValueSize,&dwBytesWritten,NULL))
392         {
393           rConsole.Write(_T("Error writting file.\n"));
394           VERIFY(CloseHandle(hFile));
395           goto SkipValueCommand;
396         }
397 
398         ASSERT(dwBytesWritten == dwValueSize);
399         VERIFY(CloseHandle(hFile));
400       }
401     }
402     else
403     {
404       rConsole.Write(_T("Error "));
405       TCHAR Buffer[256];
406       rConsole.Write(_itoa(nError,Buffer,10));
407       rConsole.Write(_T("\n"));
408       if (nError == ERROR_FILE_NOT_FOUND)
409       {
410         rConsole.Write(_T("(System cannot find the value specified)\n"));
411       }
412     }
413   }
414 
415 SkipValueCommand:
416 	if (pDataBuffer)
417 		delete[] pDataBuffer;
418 	return 0;
419 ValueCommandNAonRoot:
420 	rConsole.Write(VALUE_CMD COMMAND_NA_ON_ROOT);
421   return 0;
422 }
423 
424 const TCHAR * CShellCommandValue::GetHelpString()
425 {
426 	return VALUE_CMD_SHORT_DESC
427 			_T("Syntax: ") VALUE_CMD _T(" [<PATH>][<VALUE_NAME>] [/u] [/?]\n\n")
428 			_T("    <PATH>       - Optional relative path of key which value will be processed.\n")
429 			_T("    <VALUE_NAME> - Name of key's value. Default is key's default value.\n")
430 			_T("    /u           - On binary dump view as Unicode.\n")
431 			_T("    /fFILE       - Export value data to FILE.\n")
432 			_T("    /?           - This help.\n\n")
433 			_T("Without parameters, command displays default value of current key.\n");
434 }
435 
436 const TCHAR * CShellCommandValue::GetHelpShortDescriptionString()
437 {
438 	return VALUE_CMD_SHORT_DESC;
439 }
440