Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1691 lines
51 KiB

#include "headers.hxx"
#include "..\CSVDSReader.hpp"
#include "..\constants.hpp"
#include "..\global.hpp"
#include <winnls.h>
///////////// Basic Functions ///////////////
// included so that sizeof(guids) works properlly
#include "..\guids.inc"
// used for parameter checking in wmain
bool fileExists(const wchar_t *fileName,const wchar_t *mode=L"r")
{
FILE *f=_wfopen(fileName,mode);
if(f==NULL) return false;
fclose(f);
return true;
}
// used for parameter checking in wmain
#define BREAK_IF_MISSING(hr,fileName) \
if(!fileExists(fileName.c_str())) \
{ \
hr=E_FAIL; \
wprintf(L"\n File Missing: %s.\n",fileName.c_str()); \
break; \
} \
// used for parameter checking in wmain
#define BREAK_IF_MISSING_OR_READONLY(hr,fileName) \
BREAK_IF_MISSING(hr,fileName) \
if(!fileExists(fileName.c_str(),L"a+")) \
{ \
hr=E_FAIL; \
wprintf(L"\n Read Only: %s.\n",fileName.c_str()); \
break; \
} \
// converts outStr to AnsiString and writes to fileOut
// Fails if conversion or writing fails
HRESULT writeStringAsAnsi(HANDLE fileOut,const String& outStr)
{
AnsiString ansiStr;
String::ConvertResult res=outStr.convert(ansiStr);
if(res!=String::CONVERT_SUCCESSFUL)
{
ASSERT(res==String::CONVERT_SUCCESSFUL);
error=L"Ansi conversion failed";
return E_FAIL;
}
return FS::Write(fileOut,ansiStr);
}
// converts outStr to AnsiString and writes to fileOut
// Fails if conversion or writing fails
HRESULT printStringAsAnsi(const String& outStr)
{
AnsiString ansiStr;
String::ConvertResult res=outStr.convert(ansiStr);
if(res!=String::CONVERT_SUCCESSFUL)
{
ASSERT(res==String::CONVERT_SUCCESSFUL);
error=L"Ansi conversion failed";
return E_FAIL;
}
return printf(ansiStr.c_str());
}
// performs a-b, keys in a but not in b go to out
template <class T,class Y,class less,class allocator>
void mapKeyDifference
(
const map <T,Y,less,allocator> &a,
const map <T,Y,less,allocator> &b,
map <T,Y,less,allocator> &out
)
{
out.clear();
map <T,Y,less,allocator>::const_iterator cur=a.begin(),end=a.end();
while(cur!=end)
{
if(b.find(cur->first)==b.end())
{
out[cur->first]=cur->second;
}
cur++;
}
}
// true if all keys in a are in b and a.size()=b.size()
template <class T,class Y,class less,class allocator>
bool mapKeyEqual
(
const map <T,Y,less,allocator> &a,
const map <T,Y,less,allocator> &b
)
{
if (a.size()!=b.size()) return false;
map <T,Y,less,allocator>::const_iterator cur=a.begin(),end=a.end();
while(cur!=end)
{
if(b.find(cur->first)==b.end())
{
return false;
}
cur++;
}
return true;
}
// performs a ^ b, keys in both a and in b go to out
template <class T,class Y,class less,class allocator>
void mapKeyIntersection
(
const map <T,Y,less,allocator> &a,
const map <T,Y,less,allocator> &b,
map <T,Y,less,allocator> &out
)
{
out.clear();
map <T,Y,less,allocator>::const_iterator cur=a.begin(),end=a.end();
while(cur!=end)
{
if(b.find(cur->first)!=b.end())
{
out[cur->first]=cur->second;
}
cur++;
}
}
String escape(const String &str)
{
LOG_FUNCTION(escape);
String dest;
wchar_t strNum[7];
const wchar_t *csr=str.c_str();
while(*csr!=0)
{
wsprintf(strNum,L"\\x%x",*csr);
dest+=String(strNum);
csr++;
}
return dest;
}
HRESULT
parseGUID
(
const String& str,
long *ordinal,
GUID *guid
)
{
wchar_t *stop;
HRESULT hr=S_OK;
do
{
if (str.size()==0 || str[str.size()-1]!='}')
{
hr=E_FAIL;
break;
}
String strAux=str.substr(0,str.size()-1);
const wchar_t *strGuid=strAux.c_str();
*ordinal=wcstol(strGuid,&stop,10);
if(*stop!=L',' || *(stop+1)!=L'{' || stop==strGuid)
{
hr=E_FAIL;
break;
}
if(UuidFromString(stop+2,guid)!=RPC_S_OK)
{
hr=E_FAIL;
break;
}
} while(0);
return hr;
}
bool isGuid(const String &str)
{
long ordinal;
GUID guid;
return SUCCEEDED(parseGUID(str,&ordinal,&guid));
}
String makeGuidString(long ordinal,GUID guid)
{
String ret;
wchar_t *wRet;
if(UuidToString(&guid,&wRet)!=RPC_S_OK) throw new bad_alloc;
ret=String::format(L"%1!d!,{%2}",ordinal,wRet);
RpcStringFree(&wRet);
return ret;
}
///////////// Basic Functions End ///////////////
///////////////////////////////////////////////////
// Return the differences and commonalities between the
// properties in oldCsv and newCsv. Uses csvName to specify
// the csv in error messages.
// Failure cases:
// no common properties
// properties in oldCsv not in newCsv
HRESULT getPropertyChanges
(
const CSVDSReader &oldCsv,
const CSVDSReader &newCsv,
mapOfPositions &commonProperties,
mapOfPositions &newProperties,
const wchar_t *csvName
)
{
const mapOfPositions &oldProps=oldCsv.getProperties();
const mapOfPositions &newProps=newCsv.getProperties();
mapKeyIntersection(oldProps,newProps,commonProperties);
if(commonProperties.size()==0)
{
error=String::format(L"No comon %1!s! properties!",csvName);
return E_FAIL;
}
mapOfPositions deletedProps;
mapKeyDifference(oldProps,newProps,deletedProps);
if(deletedProps.size()!=0)
{
error=String::format
(
L"Properties only in the old %1!s! are not supported, since"
L"there is no operation to delete a property. there are %2!d!"
L"properties like this and \"%3!s!\" is the first property.",
csvName,deletedProps.size(),
deletedProps.begin()->first
);
return E_FAIL;
}
mapKeyDifference(newProps,oldProps,newProperties);
if(newProperties.size()==0)
{
wprintf(L"No new %s properties.\n",csvName);
return S_OK;
}
return S_OK;
}
// Adds to commonProperties the commonProperties between oldDcpromo
// and newDcpromo. and to new properties the properties in oldDcPromo
// not in newDcpromo
// Failure cases:
// dcpromo's common properties are not the same as 409's
// dcpromo's new properties are not the same as 409's
HRESULT getAllPropertyChanges
(
const CSVDSReader &oldDcpromo,
const CSVDSReader &newDcpromo,
const CSVDSReader &old409,
const CSVDSReader &new409,
mapOfPositions &commonProperties,
mapOfPositions &newProperties
)
{
HRESULT hr=S_OK;
do
{
hr=getPropertyChanges(
oldDcpromo,
newDcpromo,
commonProperties,
newProperties,
L"dcpromo"
);
BREAK_ON_FAILED_HRESULT(hr);
mapOfPositions prop409New,prop409Common;
hr=getPropertyChanges(
old409,
new409,
prop409Common,
prop409New,
L"409"
);
BREAK_ON_FAILED_HRESULT(hr);
if(!mapKeyEqual(prop409New,newProperties))
{
error=L"409 and dcpromo new properties are not the same.";
hr=E_FAIL;
break;
}
if(!mapKeyEqual(prop409Common,commonProperties))
{
error=L"409 and dcpromo common properties are not the same.";
hr=E_FAIL;
break;
}
} while (0);
return hr;
}
// Writes the very begining of a computer generated
// file header to fileOut
HRESULT writeHeader(const HANDLE fileOut)
{
char* header;
header ="// This file is generated by preBuild.exe\r\n"
"// Copyright (c) 2001 Microsoft Corporation\r\n"
"// Nov 2001 lucios\r\n"
"\r\n"
"#include \"headers.hxx\"\r\n"
"#include \"constants.hpp\"\r\n"
"\r\n";
return FS::Write(fileOut,AnsiString(header));
}
// Writes the nsetLocaleDependentChangesN function declaration to fileOut,
// where N is the guidNumber.
HRESULT writeChangesHeader(const HANDLE fileOut,int guidNumber)
{
String locDepStr=String::format
(
"\r\nvoid setChanges%1!d!()\r\n{\r\n",
guidNumber
);
return writeStringAsAnsi(fileOut,locDepStr);
}
// Add an entry for the object/locale to fileOut.
HRESULT writeChange
(
HANDLE fileOut,
long locale,
const String &object,
const String &property,
const String &arg1,
const String &arg2,
const String &operation,
int guidNumber
)
{
String entry=String::format
(
L"\r\n"
L" addChange\r\n"
L" (\r\n"
L" guids[%1!d!],\r\n"
L" 0x%2!x!,\r\n"
L" L\"%3\",\r\n"
L" L\"%4\",\r\n"
L" //%5\r\n"
L" L\"%6\",\r\n"
L" //%7\n"
L" L\"%8\",\r\n"
L" %9\r\n"
L" );\r\n\r\n",
guidNumber,
locale,
object.c_str(),
property.c_str(),
arg1.c_str(),
escape(arg1).c_str(),
arg2.c_str(),
escape(arg2).c_str(),
operation.c_str()
);
return writeStringAsAnsi(fileOut,entry);
}
HRESULT dealWithSingleValue
(
HANDLE fileOut,
long locale,
const String &object,
const String &property,
const StringList &valuesOld,
const StringList &valuesNew,
int guidNumber
)
{
// both sizes 0 is ok.
if (valuesOld.size()==0 && valuesNew.size()==0) return S_OK;
if (valuesOld.size()!=1 && valuesNew.size()!=1)
{
// In the future we might want to add ADD_VALUE and REMOVE_VALUE
// operations, for now we just want to be flagged.
error = String::format
(
L"Error in locale %1!x!, object %2,"
L"property %3.Number of values should be 1,1 "
L"instead of %4,%5.",
locale,
object.c_str(),
property.c_str(),
valuesOld.size(),
valuesNew.size()
);
return E_FAIL;
}
// Now we know we have a single value in each
if(*valuesOld.begin()!=*valuesNew.begin())
{
return
(
writeChange
(
fileOut,
locale,
object,
property,
*valuesOld.begin(),
*valuesNew.begin(),
L"REPLACE_W2K_SINGLE_VALUE",
guidNumber
)
);
}
return S_OK;
}
// These are values in the form "root,rest"
// if a value has the same root but a different rest we need to add
// a REPLACE_MULTIPLE_VALUE_OPERATION
// rotts in new that are not in old and roots in old that are not in new
// should be printed for manual inclusion since we don't know how to deal
// with them.
typedef map<
String,
String,
less<String>,
Burnslib::Heap::Allocator<String>
> rootToRest;
HRESULT dealWithMultipleValue
(
HANDLE fileOut,
long locale,
const String &object,
const String &property,
const StringList &valuesOld,
const StringList &valuesNew,
int guidNumber
)
{
HRESULT hr=S_OK;
rootToRest newRoots, oldRoots;
do
{
if(valuesOld.size()!=valuesNew.size())
{
error= String::format
(
L"Error in locale %1!x!, object %2,"
L"property %3. Old has %4 values and new has %5. "
L"They should have the same number of values.",
locale,
object.c_str(),
property.c_str(),
valuesOld.size(),
valuesNew.size()
);
hr=E_FAIL;
break;
}
// first lets add all roots and rests in maps
// Starting by the old values...
StringList::const_iterator csr,end;
for(csr=valuesOld.begin(),end=valuesOld.end();csr!=end;csr++)
{
const String& value=*csr;
long pos=value.find(L',');
if(pos==String::npos) continue;
String root=value.substr(0,pos);
String rest=value.substr(pos+1);
oldRoots[root]=rest;
}
BREAK_ON_FAILED_HRESULT(hr);
//...And then the new values
for(csr=valuesNew.begin(),end=valuesNew.end();csr!=end;csr++)
{
const String& value=*csr;
long pos=value.find(L',');
if(pos==String::npos) continue;
String root=value.substr(0,pos);
String rest=value.substr(pos+1);
newRoots[root]=rest;
}
BREAK_ON_FAILED_HRESULT(hr);
// now lets check all the values in one that are not in the other...
rootToRest::iterator csrRoot=oldRoots.begin(),endRoot=oldRoots.end();
rootToRest oldRootsNotInNew;
for(;csrRoot!=endRoot;csrRoot++)
{
if(newRoots.find(csrRoot->first)==newRoots.end())
{
oldRootsNotInNew[csrRoot->first]=csrRoot->second;
}
}
BREAK_ON_FAILED_HRESULT(hr);
// ..and the values in other that are not in one, and...
rootToRest newRootsNotInOld;
csrRoot=newRoots.begin(),endRoot=newRoots.end();
for(;csrRoot!=endRoot;csrRoot++)
{
if(oldRoots.find(csrRoot->first)==oldRoots.end())
{
newRootsNotInOld[csrRoot->first]=csrRoot->second;
}
}
BREAK_ON_FAILED_HRESULT(hr);
// ..if we have such values we need to investigate it further
if(!oldRootsNotInNew.empty() || !newRootsNotInOld.empty())
{
// if we have exactly one "old value" not in "new" and one
// "new value" not in "old" we are going to assume that the
// "old value" should be replaced by the "new value"...
if(oldRootsNotInNew.size()==1 && newRootsNotInOld.size()==1)
{
String arg1=String::format
(
L"%1,%2",
oldRootsNotInNew.begin()->first.c_str(),
oldRootsNotInNew.begin()->second.c_str()
);
String arg2=String::format
(
L"%1,%2",
newRootsNotInOld.begin()->first.c_str(),
newRootsNotInOld.begin()->second.c_str()
);
String outStr=String::format
(
L"\nAssuming change from:\"%1\" to \"%2\" for "
L"locale %3!lx!, object %4 and property %5.\n",
arg1.c_str(),
arg2.c_str(),
locale,
object.c_str(),
property.c_str()
);
// We are ignoring the result returned here.
printStringAsAnsi(outStr);
hr=writeChange
(
fileOut,
locale,
object,
property,
arg1,
arg2,
L"REPLACE_W2K_MULTIPLE_VALUE",
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
}
else // ...otherwise we flag it as an error.
{
error= String::format
(
L"Error in locale %1!x!, object %2,"
L"property %3. There are %4 old values with the pre comma "
L"string not present in the new values and %5 new values "
L"with the pre comma string not present in the old values."
L"Without a common root it is not possible to know what "
L"replacement to make.",
object.c_str(),
property.c_str(),
newRootsNotInOld.size(),
oldRootsNotInNew.size()
);
hr=E_FAIL;
break;
}
}
//Now we detect changes for common root values
csrRoot=newRoots.begin(),endRoot=newRoots.end();
for(;csrRoot!=endRoot;csrRoot++)
{
const String& newRoot=csrRoot->first;
const String& newRest=csrRoot->second;
// if the new root is in old and the value changed
if(
oldRoots.find(newRoot)!=oldRoots.end() &&
newRest!=oldRoots[newRoot]
)
{
hr=writeChange
(
fileOut,
locale,
object,
property,
String::format(L"%1,%2",newRoot.c_str(),
oldRoots[newRoot].c_str()).c_str(),
String::format(L"%1,%2",newRoot.c_str(),
newRest.c_str()).c_str(),
L"REPLACE_W2K_MULTIPLE_VALUE",
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
}
}
BREAK_ON_FAILED_HRESULT(hr);
} while (0);
return hr;
}
// if any value in valuesOld or valuesNew does not ressemble a GUID, fails
// since, in order to call this function, we've already checked that at least
// one in valuesOld or valuesNew ressembles a GUID.
// if x,{xxx} is y,{xxx} fails
// if x,{xxx} is x,{yyy} in the new csv REPLACE_GUID
// all {guids} in old not in new(not replaced) REMOVE_GUID
// all {guids} in new not in old(not replaced) ADD_GUID
typedef map<
GUID,
long,
GUIDLess<GUID>,
Burnslib::Heap::Allocator<long>
> guidToOrd;
typedef map<
long,
GUID,
less<long>,
Burnslib::Heap::Allocator<GUID>
> ordToGuid;
HRESULT dealWithGuids
(
HANDLE fileOut,
long locale,
const String &object,
const String &property,
const StringList &valuesOld,
const StringList &valuesNew,
int guidNumber
)
{
HRESULT hr=S_OK;
do
{
guidToOrd guidToOrdNew;
guidToOrd guidToOrdOld;
ordToGuid ordToGuidOld;
guidToOrd replacements;
GUID oldGuid;long oldOrd;
GUID guid;long ordinal;
// First lets add the guids and ordinals to auxilliary maps
// starting with the old values...
StringList::const_iterator cur,end;
cur=valuesOld.begin();end=valuesOld.end();
for(;cur!=end;cur++)
{
const String &guidValue=*cur;
hr=parseGUID(guidValue,&ordinal,&guid);
if(FAILED(hr))
{
error= String::format
(
L"Error in locale %1!x!, object %2,"
L"property %3. Failed to parse old guid: %4",
locale,
object.c_str(),
property.c_str(),
guidValue.c_str()
);
break;
}
guidToOrdOld[guid]=ordinal;
ordToGuidOld[ordinal]=guid;
}
BREAK_ON_FAILED_HRESULT(hr);
// ...and then the new values.
cur=valuesNew.begin();end=valuesNew.end();
for(;cur!=end;cur++)
{
const String &guidValue=*cur;
hr=parseGUID(guidValue,&ordinal,&guid);
if(FAILED(hr))
{
error= String::format
(
L"Error in locale %1!x!, object %2,"
L"property %3. Failed to parse new guid: %4",
locale,
object.c_str(),
property.c_str(),
guidValue.c_str()
);
break;
}
guidToOrdNew[guid]=ordinal;
}
BREAK_ON_FAILED_HRESULT(hr);
// Lets treat replacements and additions first
guidToOrd::iterator csr,endCsr;
csr=guidToOrdNew.begin();
endCsr=guidToOrdNew.end();
for(;csr!=endCsr;csr++)
{
GUID newGuid=csr->first;
long newOrd=csr->second;
// this flag is used not to add a replacement
bool newGuidWasReplaced=false;
//... if ordinal is in old...
if( ordToGuidOld.find(newOrd)!=ordToGuidOld.end() )
{
GUID oldGuid=ordToGuidOld[newOrd];
// ...with a different GUID, this means a replacement.
if(oldGuid!=newGuid)
{
hr=writeChange
(
fileOut,
locale,
object,
property,
makeGuidString(newOrd,oldGuid),
makeGuidString(newOrd,newGuid),
L"REPLACE_GUID",
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
replacements[oldGuid]=newOrd;
newGuidWasReplaced=true;
}
// we have no else because if both the ordinal and guid
// are the same there is nothing to do.
}
// if new guid is also in old...
if( guidToOrdOld.find(newGuid)!=guidToOrdOld.end() )
{
long oldOrd=guidToOrdOld[newGuid];
//...with a different ordinal we have a situation we are not
// prepared to deal with for now
if(oldOrd!=newOrd)
{
error= String::format
(
L"Error in locale %1!x!, object %2,"
L"property %3. Guid:%4 has different ordinals in "
L"new and old (ordinal=%5!d!) csv files.",
locale,
object.c_str(),
property.c_str(),
makeGuidString(newOrd,newGuid).c_str(),
oldOrd
);
break;
}
// we have no else because if both the ordinal and guid
// are the same there is nothing to do.
}
else
{
if(!newGuidWasReplaced)
{
hr=writeChange
(
fileOut,
locale,
object,
property,
makeGuidString(newOrd,newGuid),
"",
L"ADD_GUID",
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
}
}
}
BREAK_ON_FAILED_HRESULT(hr);
// Now let's check for guids only in the old
csr=guidToOrdOld.begin(),endCsr=guidToOrdOld.end();
for(;csr!=endCsr;csr++)
{
oldGuid=csr->first;
oldOrd=csr->second;
// if oldGuid is not in new and has not already been replaced
if(
guidToOrdNew.find(oldGuid)==guidToOrdNew.end() &&
replacements.find(oldGuid)==replacements.end()
)
{
hr=writeChange
(
fileOut,
locale,
object,
property,
makeGuidString(oldOrd,oldGuid).c_str(),
L"",
L"REMOVE_GUID",
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
}
}
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
return hr;
}
BOOL
MyIsNLSDefinedString
(
const String& str,
wchar_t *badChar
)
{
BOOL ret=IsNLSDefinedString
(
COMPARE_STRING,
0,
NULL,
str.c_str(),
str.length()
);
if(ret==FALSE)
{
wchar_t s[2]={0};
for(long t=0;t<str.length();t++)
{
s[0]=str[t];
ret=IsNLSDefinedString(COMPARE_STRING,0,NULL,s,1);
if(ret==FALSE)
{
*badChar=str[t];
return FALSE;
}
}
// Some character in the for must return before this point
ASSERT(ret!=FALSE);
}
return TRUE;
}
HRESULT
checkValues
(
long locale,
const String &object,
const mapOfProperties &values
)
{
HRESULT hr=S_OK;
do
{
mapOfProperties::const_iterator csr,end;
for(csr=values.begin(),end=values.end();csr!=end;csr++)
{
StringList::const_iterator csrVal=csr->second.begin();
StringList::const_iterator endVal=csr->second.end();
for(;csrVal!=endVal;csrVal++)
{
wchar_t badChar;
if( MyIsNLSDefinedString(*csrVal,&badChar) == FALSE )
{
String outStr=String::format
(
L"\nNon unicode char %1!x! in string:\"%2\" for "
L"locale %3!lx!, object %4 and property %5.\n",
badChar,
csrVal->c_str(),
locale,
object.c_str(),
csr->first.c_str()
);
// We are ignoring the result returned here.
printStringAsAnsi(outStr);
hr=E_FAIL;
break;
}
}
BREAK_ON_FAILED_HRESULT(hr);
}
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
hr=S_OK;
// for now we always return true but we will make this
// a critical error when this checking makes to the AD
return hr;
}
// Reads synchronally csvOld and csvNew adding the necessary changes.
// Objects only in csvOld will cause failure
// For objects only in csvNew call addNewObject to add an ADD_OBJECT entry.
// For each common object betweem csvOld and csvNew:
// For each object property belonging to newProperties and with a non
// empty value add call addAllCsvValues to add an ADD_CSV_VALUES entry.
// for each property in the object belonging to commonProperties
// valuesOld = value of the property in oldCsv
// valuesNew = value of the property in newCsv
// if valuesNew and valuesOld are empty skip this value
// if either first value of valuesNew or valuesOld ressembles a GUID
// call dealWithGuid to add ADD_GUID/REPLACE_GUID/DELETE_GUID as necessary and
// and skip to next.
// if both valuesNew.size and valuesOld.size <= 1 call dealWithSingleValue
// to add REPLACE_SINGLE_VALUE as necessary and skip to next
// dealWithMultipleValue to add REPLACE_MULTIPLE_VALUE as necessary and
// skip to next.
HRESULT addChanges
(
HANDLE fileOut,
const CSVDSReader &csvOld,
const CSVDSReader &csvNew,
const mapOfPositions &commonProperties,
const mapOfPositions &newProperties,
int guidNumber,
const wchar_t *csvName
)
{
HRESULT hr=S_OK;
do
{
// Here we start readding both csv files
hr=csvOld.initializeGetNext();
BREAK_ON_FAILED_HRESULT(hr);
hr=csvNew.initializeGetNext();
BREAK_ON_FAILED_HRESULT(hr);
// The loop bellow will sequentially read objects
// in both csv's making sure the same objects are read.
bool flagEOF=false;
do
{
mapOfProperties oldValues,newValues;
long locOld=0,locNew=0;
String objOld,objNew;
hr=csvOld.getNextObject(locOld,objOld,oldValues);
BREAK_ON_FAILED_HRESULT(hr);
if(hr==S_FALSE) {flagEOF=true;hr=S_OK;}
hr=checkValues(locOld,objOld,oldValues);
BREAK_ON_FAILED_HRESULT(hr);
hr=csvNew.getNextObject(locNew,objNew,newValues);
BREAK_ON_FAILED_HRESULT(hr);
// While we don't find the object from the old csv
// in the new csv we add entries for the new objects found
while(hr!=S_FALSE && (locNew!=locOld || objNew!=objOld) )
{
hr= writeChange
(
fileOut,
locNew,
objNew,
L"",
L"",
L"",
L"ADD_OBJECT",
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
hr=checkValues(locNew,objNew,newValues);
BREAK_ON_FAILED_HRESULT(hr);
hr=csvNew.getNextObject(locNew,objNew,newValues);
BREAK_ON_FAILED_HRESULT(hr);
}
BREAK_ON_FAILED_HRESULT(hr);
if(hr==S_FALSE) {flagEOF=true;hr=S_OK;}
hr=checkValues(locNew,objNew,newValues);
BREAK_ON_FAILED_HRESULT(hr);
// This means that we searched the whole new csv file and didn't
// find the object we've read from the old csv file.
if(locNew!=locOld || objNew!=objOld)
{
error=String::format
(
L"Error:%1!d!,%2 was only in the old %3 csv file.",
locOld,objOld.c_str(),
csvName
);
hr=E_FAIL;
break;
}
// From this point on we know the object is
// the same in old and new csv files
// This happens if we have a blank line at the end of the files
if(locNew==0) break;
// now let's check the differences in the common properties
mapOfPositions::const_iterator cur=newProperties.begin();
mapOfPositions::const_iterator end=newProperties.end();
for(;cur!=end;cur++)
{
const String& property=cur->first;
const StringList &valuesNew=newValues[property];
if(!valuesNew.empty())
{
// We only want to use ADD_ALL_CSV_VALUES if it
// is not a guid. It will probably be the same as
// ADD_ALL_CSV_VALUES for most cases but it is
// better policy to keep all guid additions
// with an ADD_GUID change.
if( isGuid(*valuesNew.begin()) )
{
// We know we don't have old values because this is a
// new property
StringList emptyValues;
hr= dealWithGuids
(
fileOut,
locNew,
objNew,
property,
emptyValues,
valuesNew,
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
}
else
{
hr= writeChange
(
fileOut,
locNew,
objNew,
property,
L"",
L"",
L"ADD_ALL_CSV_VALUES",
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
}
}
}
BREAK_ON_FAILED_HRESULT(hr);
// now let's check the differences in the common properties
cur=commonProperties.begin();
end=commonProperties.end();
for(;cur!=end;cur++)
{
const String& property=cur->first;
const StringList &valuesOld=oldValues[property];
const StringList &valuesNew=newValues[property];
if (valuesOld.empty() && valuesNew.empty()) continue;
// The or bellows means either value being guid we want
// to deal with them in dealWithGuids. Inside it, all non
// guids would trigger an error.
if (
( !valuesOld.empty() && isGuid(*valuesOld.begin()) ) ||
( !valuesNew.empty() && isGuid(*valuesNew.begin()) )
)
{
hr= dealWithGuids
(
fileOut,
locNew,
objNew,
property,
valuesOld,
valuesNew,
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
continue;
}
// Now we know that we don't have a guid change
if(valuesNew.size()<=1 && valuesOld.size()<=1)
{
hr= dealWithSingleValue
(
fileOut,
locNew,
objNew,
property,
valuesOld,
valuesNew,
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
continue;
}
// Now we know that we don't have GUIDS or single values
hr= dealWithMultipleValue
(
fileOut,
locNew,
objNew,
property,
valuesOld,
valuesNew,
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
}
BREAK_ON_FAILED_HRESULT(hr);
} while (flagEOF==false);
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
return hr;
}
// Writes the whole SetChanges function with the locales
// from dcpromo and 409.
// Calls writeChangesHeader and then addChanges twice, one
// for each csv pair. Finally, calls FS::Write(fileOut,L"\n}");.
HRESULT addAllChanges
(
HANDLE fileOut,
const CSVDSReader &oldDcpromo,
const CSVDSReader &newDcpromo,
const CSVDSReader &old409,
const CSVDSReader &new409,
const mapOfPositions &commonProperties,
const mapOfPositions &newProperties,
int guidNumber
)
{
HRESULT hr=S_OK;
do
{
hr=writeChangesHeader(fileOut,guidNumber);
BREAK_ON_FAILED_HRESULT(hr);
hr=addChanges
(
fileOut,
old409,
new409,
commonProperties,
newProperties,
guidNumber,
L"409"
);
BREAK_ON_FAILED_HRESULT(hr);
hr=addChanges
(
fileOut,
oldDcpromo,
newDcpromo,
commonProperties,
newProperties,
guidNumber,
L"dcpromo"
);
BREAK_ON_FAILED_HRESULT(hr);
hr = writeStringAsAnsi(fileOut,L"\r\n}");
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
return hr;
}
// writes setChangesNNN.cpp. Sets up by caling getAllPropertyChanges, calls
// writeHeader and then addAllChanges
// Creates the CSVReader objects corresponding to the 4 first parameters
// to pass to writeGlobalChanges and writeGlobalChanges.
// guidNumber is repassed to addAllChanges
HRESULT writeChanges
(
const String &oldDcpromoName,
const String &newDcpromoName,
const String &old409Name,
const String &new409Name,
const String &changesCpp,
int guidNumber
)
{
HRESULT hr=S_OK;
HANDLE fChanges = INVALID_HANDLE_VALUE;
hr=FS::CreateFile
(
changesCpp.c_str(),
fChanges,
GENERIC_WRITE,
FILE_SHARE_READ,
CREATE_ALWAYS
);
do
{
if (FAILED(hr))
{
wprintf(L"Could not create changes file: %s.",changesCpp.c_str());
break;
}
do
{
CSVDSReader oldDcpromo;
hr=oldDcpromo.read(oldDcpromoName.c_str(),LOCALEIDS);
BREAK_ON_FAILED_HRESULT(hr);
CSVDSReader newDcpromo;
hr=newDcpromo.read(newDcpromoName.c_str(),LOCALEIDS);
BREAK_ON_FAILED_HRESULT(hr);
CSVDSReader old409;
hr=old409.read(old409Name.c_str(),LOCALE409);
BREAK_ON_FAILED_HRESULT(hr);
CSVDSReader new409;
hr=new409.read(new409Name.c_str(),LOCALE409);
BREAK_ON_FAILED_HRESULT(hr);
mapOfPositions commonProperties,newProperties;
hr=getAllPropertyChanges
(
oldDcpromo,
newDcpromo,
old409,
new409,
commonProperties,
newProperties
);
BREAK_ON_FAILED_HRESULT(hr);
hr=writeHeader(fChanges);
BREAK_ON_FAILED_HRESULT(hr);
hr=addAllChanges
(
fChanges,
oldDcpromo,
newDcpromo,
old409,
new409,
commonProperties,
newProperties,
guidNumber
);
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
CloseHandle(fChanges);
} while(0);
return hr;
}
HRESULT writeGuid(HANDLE fOut,const GUID &guid)
{
return
(
writeStringAsAnsi
(
fOut,
String::format
(
" {0x%1!x!,0x%2!x!,0x%3!x!,{0x%4!x!,0x%5!x!,0x%6!x!,"
"0x%7!x!,0x%8!x!,0x%9!x!,0x%10!x!,0x%11!x!}},\r\n",
guid.Data1,guid.Data2,guid.Data3,
guid.Data4[0],guid.Data4[1],guid.Data4[2],guid.Data4[3],
guid.Data4[4],guid.Data4[5],guid.Data4[6],guid.Data4[7]
).c_str()
)
);
}
HRESULT writeGuids(const String& guidsInc,const GUID &newGuid)
{
HRESULT hr=S_OK;
HANDLE fOut= INVALID_HANDLE_VALUE;
hr=FS::CreateFile
(
guidsInc.c_str(),
fOut,
GENERIC_WRITE,
FILE_SHARE_READ,
CREATE_ALWAYS
);
do
{
if (FAILED(hr))
{
wprintf(L"Could not create changes file: %s.",guidsInc.c_str());
break;
}
do
{
int sizeGuids=sizeof(guids)/sizeof(*guids);
hr=FS::Write
(
fOut,
AnsiString
(
"// This file is generated by preBuild.exe\r\n"
"// Copyright (c) 2001 Microsoft Corporation\r\n"
"// Nov 2001 lucios\r\n\r\n\r\n"
"GUID guids[]=\r\n"
"{\r\n"
)
);
BREAK_ON_FAILED_HRESULT(hr);
for(int ix=0;ix<sizeGuids;ix++)
{
hr=writeGuid(fOut,guids[ix]);
BREAK_ON_FAILED_HRESULT(hr);
}
BREAK_ON_FAILED_HRESULT(hr);
hr=writeGuid(fOut,newGuid);
BREAK_ON_FAILED_HRESULT(hr);
hr=FS::Write(fOut,AnsiString("};\r\n"));
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
CloseHandle(fOut);
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
return hr;
}
HRESULT writeSetChanges(const String& setChanges)
{
HRESULT hr=S_OK;
HANDLE fOut= INVALID_HANDLE_VALUE;
hr=FS::CreateFile
(
setChanges.c_str(),
fOut,
GENERIC_WRITE,
FILE_SHARE_READ,
CREATE_ALWAYS
);
do
{
if (FAILED(hr))
{
wprintf(L"Could not create setChanges file: %s.",setChanges.c_str());
break;
}
do
{
int sizeGuids=sizeof(guids)/sizeof(*guids);
hr=FS::Write
(
fOut,
AnsiString
(
"// This file is generated by preBuild.exe\r\n"
"// Copyright (c) 2001 Microsoft Corporation\r\n"
"// Nov 2001 lucios\r\n\r\n\n"
"#include \"headers.hxx\"\r\n\r\n"
)
);
BREAK_ON_FAILED_HRESULT(hr);
for(int ix=0;ix<sizeGuids+1;ix++)
{
hr=writeStringAsAnsi
(
fOut,
String::format("void setChanges%1!d!();\r\n",ix)
);
BREAK_ON_FAILED_HRESULT(hr);
}
BREAK_ON_FAILED_HRESULT(hr);
hr=FS::Write(fOut,AnsiString("\nvoid setChanges()\r\n{\r\n"));
BREAK_ON_FAILED_HRESULT(hr);
for(int ix=0;ix<sizeGuids+1;ix++)
{
hr=writeStringAsAnsi
(
fOut,
String::format(" setChanges%1!d!();\r\n",ix)
);
}
BREAK_ON_FAILED_HRESULT(hr);
hr=FS::Write(fOut,AnsiString("}\r\n"));
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
CloseHandle(fOut);
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
return hr;
}
HRESULT writeSources(const String& sources,const String& changesCpp)
{
HRESULT hr=S_OK;
AnsiString ansiStr;
String::ConvertResult res=changesCpp.convert(ansiStr);
if(res!=String::CONVERT_SUCCESSFUL)
{
ASSERT(res==String::CONVERT_SUCCESSFUL);
error=L"Ansi conversion failed";
return E_FAIL;
}
FILE *fOut=_wfopen(sources.c_str(),L"a+");
do
{
if (fOut==NULL)
{
wprintf(L"Could not create sources file: %s.",sources.c_str());
break;
}
do
{
fprintf(fOut," %s \\\r\n" ,ansiStr.c_str());
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
fclose(fOut);
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
return hr;
}
///////////////////////////////////////////////////
// entry point
void __cdecl wmain(int argc,wchar_t *argv[])
{
HRESULT hr=S_OK;
do
{
if(argc!=7)
{
error=L"\nThis program generates a new set of changes to be "
L"used in dcpromo.lib by comparing the new and previous"
L" csv files. Usage:\n\n\"preBuild.exe GUID oldDcpromo "
L"newDcpromo old409 new409 targetFolder\"\n\n"
L"GUID is the identifier for this set of changes, for example:\n"
L"8B53221B-EA3C-4638-8D00-7C1BE42B2873\n\n"
L"oldDcpromo is the previous dcpromo.csv\n"
L"newDcpromo is the new dcpromo.csv\n"
L"old409 is the previous 409.csv\n"
L"new409 is the new 409.csv\n\n"
L"targetFolder is the sources file for dcpromo.lib,"
L" where guids.inc,setChanges.cpp, and "
L"changes.NNN.cpp will be generated and where the sources "
L"file for the display specifier upgrade library is. "
L"An entry like: \"changes.NNN.cpp \\\" will be added "
L"at the end of targetFolder\\sources.\n\n";
hr=E_FAIL;
break;
}
String guidStr=argv[1],oldDcpromo=argv[2],newDcpromo=argv[3],old409=argv[4];
String new409=argv[5],targetFolder=argv[6];
String sources=targetFolder + L"\\sources";
String guidsInc=targetFolder + L"\\guids.inc";
String setChanges=targetFolder + L"\\setChanges.cpp";
GUID guid={0};
if(UuidFromString((wchar_t*)guidStr.c_str(),&guid)!=RPC_S_OK)
{
error=String::format(L"\n Invalid GUID:%s.\n",guidStr.c_str());
break;
}
BREAK_IF_MISSING(hr,oldDcpromo);
BREAK_IF_MISSING(hr,newDcpromo);
BREAK_IF_MISSING(hr,old409);
BREAK_IF_MISSING(hr,new409);
BREAK_IF_MISSING_OR_READONLY(hr,guidsInc);
BREAK_IF_MISSING_OR_READONLY(hr,sources);
BREAK_IF_MISSING_OR_READONLY(hr,setChanges);
int sizeGuids=sizeof(guids)/sizeof(*guids);
for(int t=0;t<sizeGuids;t++)
{
if (guids[t]==guid)
{
hr=E_FAIL;
error=String::format("The guid you entered (%s) is already present\n");
break;
}
String shouldExist = targetFolder +
String::format(L"\\changes%1!03d!.cpp",t);
BREAK_IF_MISSING(hr,shouldExist);
}
BREAK_ON_FAILED_HRESULT(hr);
String changesCppOnly = String::format(L"changes%1!03d!.cpp",t);
String changesCpp = targetFolder + L"\\" + changesCppOnly;
if( fileExists(changesCpp.c_str()) )
{
hr=E_FAIL;
error=String::format(L"Change file already exists: %1.",changesCpp.c_str());
break;
}
hr=writeChanges
(
oldDcpromo,
newDcpromo,
old409,
new409,
changesCpp,
sizeGuids
);
hr=writeGuids(guidsInc,guid);
BREAK_ON_FAILED_HRESULT(hr);
hr=writeSetChanges(setChanges);
BREAK_ON_FAILED_HRESULT(hr);
hr=writeSources(sources,changesCppOnly);
BREAK_ON_FAILED_HRESULT(hr);
} while(0);
if(FAILED(hr)) wprintf(L"\nFailure code: %lx\n",hr);
else wprintf(L"\nSuccess. Don't forget to bcz this project and targetFolder.\n");
if(!error.empty()) wprintf(String::format("\n%1\n",error.c_str()).c_str());
}
///////////////////////////////////////////////////////////////////
// Function: cchLoadHrMsg
//
// Given an HRESULT error,
// it loads the string for the error. It returns the # of characters returned
int cchLoadHrMsg( HRESULT hr, String &message )
{
if(hr == S_OK) return 0;
wchar_t *msgPtr = NULL;
// Try from the system table
int cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&msgPtr,
0,
NULL);
if (!cch)
{
//try ads errors
static HMODULE g_adsMod = 0;
if (0 == g_adsMod)
{
g_adsMod = GetModuleHandle (L"activeds.dll");
}
cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
g_adsMod,
hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&msgPtr,
0,
NULL);
}
if (!cch)
{
// Try NTSTATUS error codes
hr = HRESULT_FROM_WIN32(RtlNtStatusToDosError(hr));
cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&msgPtr,
0,
NULL);
}
message.erase();
if(cch!=0)
{
if(msgPtr==NULL)
{
cch=0;
}
else
{
message=msgPtr;
::LocalFree(msgPtr);
}
}
return cch;
}