453 lines
9.9 KiB
C
453 lines
9.9 KiB
C
/*++
|
|
|
|
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;
|
|
}
|
|
|
|
|