/*++ Copyright (c) 1998 Microsoft Corporation Module Name: sfspace.c Abstract: sfspace calculates the amount of space required for shell folders in a clean install of Windows 2000 and outputs the results in a form which can be copied into win95upg.inf. Author: Marc R. Whitten (marcw) 24-Mar-1999 Revision History: <full name> (<alias>) <date> <comments> --*/ #include "pch.h" #include "shlobj.h" HANDLE g_hHeap; HINSTANCE g_hInst; BOOL WINAPI MigUtil_Entry (HINSTANCE, DWORD, PVOID); //BOOL WINAPI MemDb_Entry (HINSTANCE, DWORD, PVOID); #define SFLIST \ DEFMAC(AppData, CSIDL_APPDATA) \ DEFMAC(Cache, CSIDL_INTERNET_CACHE) \ DEFMAC(Cookies, CSIDL_COOKIES) \ DEFMAC(Desktop, CSIDL_DESKTOPDIRECTORY) \ DEFMAC(Favorites, CSIDL_FAVORITES) \ DEFMAC(History, CSIDL_HISTORY) \ DEFMAC(Local AppData, CSIDL_LOCAL_APPDATA) \ DEFMAC(Local Settings, CSIDL_LOCAL_APPDATA) \ DEFMAC(My Pictures, CSIDL_MYPICTURES) \ DEFMAC(NetHood, CSIDL_NETHOOD) \ DEFMAC(Personal, CSIDL_PERSONAL) \ DEFMAC(PrintHood, CSIDL_PRINTHOOD) \ DEFMAC(Programs, CSIDL_PROGRAMS) \ DEFMAC(Recent, CSIDL_RECENT) \ DEFMAC(SendTo, CSIDL_SENDTO) \ DEFMAC(Start Menu, CSIDL_STARTMENU) \ DEFMAC(StartUp, CSIDL_STARTUP) \ DEFMAC(Templates, CSIDL_TEMPLATES) \ DEFMAC(Common AppData, CSIDL_COMMON_APPDATA) \ DEFMAC(Common Desktop, CSIDL_COMMON_DESKTOPDIRECTORY) \ DEFMAC(Common Personal, CSIDL_COMMON_DOCUMENTS) \ DEFMAC(Common Favorites, CSIDL_COMMON_FAVORITES) \ DEFMAC(Common Programs, CSIDL_COMMON_PROGRAMS) \ DEFMAC(Common Start Menu, CSIDL_COMMON_STARTMENU) \ DEFMAC(Common StartUp, CSIDL_COMMON_STARTUP) \ DEFMAC(Common Templates, CSIDL_COMMON_TEMPLATES) \ enum { CS_512 = 0, CS_1024, CS_2048, CS_4096, CS_8192, CS_16384, CS_32768, CS_65536, CS_131072, CS_262144, LAST_CLUSTER_SIZE }; typedef struct { PCTSTR RegKey; UINT Csidl; PCTSTR Path; LONG TableOffset; LONG SpaceNeeded[LAST_CLUSTER_SIZE]; LONG RawSize; UINT FileCount; UINT DirectoryCount; } SFDATA, *PSFDATA; #define DEFMAC(regName, csidl) {TEXT(#regName),(csidl)}, SFDATA g_Data[] = {SFLIST /*, */ {NULL,0}}; HASHTABLE g_Table; POOLHANDLE g_Pool; BOOL g_Verbose = FALSE; UINT g_ClusterTable[LAST_CLUSTER_SIZE] = {512,1024,2048,4096,8192,16384,32768,65536,131072,262144}; #define DIRECTORY_SIZE 512 BOOL pCallEntryPoints ( DWORD Reason ) { HINSTANCE Instance; // // Simulate DllMain // Instance = g_hInst; // // Initialize the common libs // if (!MigUtil_Entry (Instance, Reason, NULL)) { return FALSE; } /* if (!MemDb_Entry (Instance, Reason, NULL)) { return FALSE; } */ return TRUE; } BOOL Init ( VOID ) { g_hHeap = GetProcessHeap(); g_hInst = GetModuleHandle (NULL); return pCallEntryPoints (DLL_PROCESS_ATTACH); } VOID Terminate ( VOID ) { pCallEntryPoints (DLL_PROCESS_DETACH); } BOOL pInitShellFolderData ( VOID ) { PSFDATA sf; TCHAR buffer[MAX_TCHAR_PATH]; PTSTR p; sf = g_Data; while (sf->RegKey) { if (SHGetFolderPath (NULL, sf->Csidl, NULL, 0, buffer) != S_OK) { _ftprintf (stderr, TEXT("sfspace: Unable to retrieve folder path for %s.\n"), sf->RegKey); return FALSE; } sf->Path = PoolMemDuplicateString (g_Pool, buffer); // // We don't have a CSIDL for the local settings directory. We need to hack it. // if (StringIMatch (sf->RegKey, TEXT("Local Settings"))) { p = _tcsrchr (sf->Path, TEXT('\\')); MYASSERT (p); *p = 0; if (g_Verbose) { _tprintf (TEXT("sfspace: Hacked path of local settings to %s.\n"), sf->Path); } } sf->TableOffset = HtAddString (g_Table, sf->Path); if (g_Verbose) { _tprintf (TEXT("sfspace: Shell folder %s has path %s.\n"), sf->RegKey, sf->Path); } sf++; } return TRUE; } BOOL pGatherSpaceRequirements ( VOID ) { PSFDATA sf; UINT i; TREE_ENUM e; LONG offset; sf = g_Data; while (sf->RegKey) { if (EnumFirstFileInTree (&e, sf->Path, NULL, FALSE)) { do { if (e.Directory) { // // Check to see if this is a different shell folder. // offset = HtFindString (g_Table, e.FullPath); if (offset && offset != sf->TableOffset) { // // This is actually another shell folder. Don't enumerate // it. // if (g_Verbose) { _tprintf (TEXT("sfspace: %s is handled by another shell folder.\n"), e.FullPath); } AbortEnumCurrentDir (&e); } else { // // Increment directory count for this shell folder. // sf->DirectoryCount++; } } else { // // this is a file. Add its data to our structure. // sf->FileCount++; sf->RawSize += e.FindData->nFileSizeLow; for (i=0; i<LAST_CLUSTER_SIZE; i++) { // // We assume NT doesn't install any massively large files by default. // MYASSERT (!e.FindData->nFileSizeHigh); sf->SpaceNeeded[i] += ((e.FindData->nFileSizeLow / g_ClusterTable[i]) * g_ClusterTable[i]) + g_ClusterTable[i]; } } } while (EnumNextFileInTree (&e)); } // // Add the space for all of the directories we found in this shell folder. // for (i=0; i<LAST_CLUSTER_SIZE; i++) { sf->SpaceNeeded[i] += (((sf->DirectoryCount * DIRECTORY_SIZE) / g_ClusterTable[i]) * g_ClusterTable[i]) + g_ClusterTable[i]; } if (g_Verbose) { _tprintf ( TEXT("sfspace: %u files and %u directories enumerated for shell folder %s. Space needed (512k cluster size): %u Raw Space: %u\n"), sf->FileCount, sf->DirectoryCount, sf->RegKey, sf->SpaceNeeded[CS_512], sf->RawSize ); } sf++; } return TRUE; } PCTSTR pLeftJustify ( IN PCTSTR String, IN UINT FieldWidth ) { static TCHAR rBuffer[MAX_TCHAR_PATH]; UINT length; UINT i; MYASSERT(String); length = CharCount (String); MYASSERT(FieldWidth < MAX_TCHAR_PATH && length < FieldWidth); StringCopy (rBuffer,String); for (i=length; i<FieldWidth; i++) { rBuffer[i] = TEXT(' '); } rBuffer[i] = 0; return rBuffer; } VOID pOutputSpaceTable ( VOID ) { PSFDATA sf; UINT i; TCHAR buffer[20]; _tprintf (TEXT("[%s]\n"), S_SHELL_FOLDERS_DISK_SPACE); _tprintf ( TEXT("@*: \n") TEXT("@*: Disk space requirements for each shell folder. The key name is a registry \n") TEXT("@*: value name. \n") TEXT("@*: \n") ); sf = g_Data; while (sf->RegKey) { _tprintf (TEXT("%s"),pLeftJustify (sf->RegKey,20)); for (i=0; i<LAST_CLUSTER_SIZE; i++) { _tprintf (TEXT("%s %u"),i ? TEXT(",") : TEXT("="), sf->SpaceNeeded[i]); } _tprintf (TEXT("\n")); sf++; } } VOID HelpAndExit ( VOID ) { // // This routine is called whenever command line args are wrong // _ftprintf ( stderr, TEXT("Command Line Syntax:\n\n") TEXT(" sfspace [/V]\n") TEXT("\nDescription:\n\n") TEXT(" sfspace gathers the space requirements for the default\n") TEXT(" shell folders installed by Windows 2000. It should be\n") TEXT(" run against a clean install of Windows 2000.\n") TEXT("\nArguments:\n\n") TEXT(" /V Instructs sfspace to generate verbose output.\n") ); exit (1); } INT __cdecl _tmain ( INT argc, PCTSTR argv[] ) { INT i; for (i = 1 ; i < argc ; i++) { if (argv[i][0] == TEXT('/') || argv[i][0] == TEXT('-')) { switch (_totlower (_tcsnextc (&argv[i][1]))) { case TEXT('v'): // // Verbose output wanted. // g_Verbose = TRUE; break; default: HelpAndExit(); } } else { // // Parse other args that don't require / or - // // // None // HelpAndExit(); } } // // Begin processing // if (!Init()) { return 0; } // // Initialize data structures. // g_Table = HtAlloc (); g_Pool = PoolMemInitPool (); _try { if (!pInitShellFolderData ()) { _ftprintf (stderr, TEXT("sfspace: Unable to initialize shell folder data. Exiting.\n")); __leave; } if (!pGatherSpaceRequirements ()) { _ftprintf (stderr, TEXT("sfspace: Unable to gather space requirements for shell folders. Exiting.\n")); __leave; } pOutputSpaceTable (); } __finally { HtFree (g_Table); PoolMemDestroyPool (g_Pool); } // // End of processing // Terminate(); return 0; }