/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // /////////////////////////////////////////////////////////////////////////////// using System; using ManifestReader; using System.Collections; using System.Diagnostics; using System.IO; namespace AppReader { class ReaderMain { static void Usage() { Console.WriteLine("AppReader.exe "); return; } static double computeSpread(int[] directoryCounts) { double count = 0; for(int i = 0; i < directoryCounts.Length; i++) { int curLevelCount = directoryCounts[i]; count += Math.Pow(100, -i)*(curLevelCount - 1); } return count; } static int getAppNumber(string name, Hashtable appNameTable, ref int nameCount) { if (!appNameTable.ContainsKey(name)) { appNameTable[name] = nameCount++; } return (int)appNameTable[name]; } static void parseFile(string appName, string file) { ManifestParser mp; // get the suffix string[] components = file.Split(new char[] {'.'}, 50); string suffix = components[components.Length - 1]; if (suffix.Equals("wxs")) { mp = new WiXParser(); } else if (suffix.ToLower().Equals("man") || suffix.ToLower().Equals("manifest")) { mp = new CMIParser(); } else if (suffix.Equals("inf")) { mp = new INFParser(); } else if (suffix.Equals("inft")) { mp = new RegINFParser(); } else { //Console.WriteLine("Unknown manifest suffix " + suffix); return; } // otherwise, try to parse it try { mp.Parse(appName, file, new Hashtable()); } catch (System.Xml.XmlException) { Console.WriteLine("Could not parse " + file); } // catch(System.Exception e) // { // Console.WriteLine("Failed to parse " + file + " : " + e.Message); // } return; } static Hashtable parseFile(string appName, string file, Hashtable wxsvals, Hashtable cmivals) { Hashtable whash = new Hashtable();; ManifestParser mp; Hashtable inHash; // get the suffix string[] components = file.Split(new char[] {'.'}, 50); string suffix = components[components.Length - 1]; if (suffix.Equals("wxs")) { inHash = wxsvals; mp = new WiXParser(); } else if (suffix.ToLower().Equals("man") || suffix.ToLower().Equals("manifest")) { inHash = cmivals; mp = new CMIParser(); } else if (suffix.Equals("inf")) { inHash = null; mp = new INFParser(); } else if (suffix.Equals("inft")) { inHash = null; mp = new RegINFParser(); } else { Console.WriteLine("Unknown manifest suffix " + suffix); return whash; } // otherwise, try to parse it try { whash = mp.Parse(appName, file, inHash); } catch (System.Xml.XmlException) { Console.WriteLine("Could not parse " + file); } return whash; } static bool sameApp(string first, string second) { // we'll compute a heuristic measure of similarity by checking // the number of elements they have in common in // their strings. Any difference of 2 or less and // we will say that they are the same string[] firstArray = first.Split(new char[] {'.', '_'}, 40); string[] secondArray = second.Split(new char[] {'.', '_'}, 40); Hashtable firstHash = new Hashtable(); for(int i = 0; i < firstArray.Length; i++) { if (!firstHash.ContainsKey(firstArray[i])) { firstHash[firstArray[i]] = 0; } firstHash[firstArray[i]] = (int)firstHash[firstArray[i]] + 1; } for(int i = 0; i < secondArray.Length; i++) { if (firstHash.ContainsKey(secondArray[i])) { firstHash[secondArray[i]] = (int)firstHash[secondArray[i]] - 1; } } ICollection vcol = firstHash.Values; IEnumerator ienum = vcol.GetEnumerator(); int sum = 0; while(ienum.MoveNext()) { sum += (int)ienum.Current; } return System.Math.Abs(sum) <= 2; } [STAThread] static void Main(string[] args) { if (args.Length < 1) { Usage(); return; } #region Setup Variables // grab a new app tree //AppTree tempat = AppTree.CreateAppTree(args[0]); //ArrayList tempar = tempat.Flatten(); // ... that we can spit out for clustering //for(int j = 0; j < tempar.Count; j++) //{ // Console.Write(((AppNode)tempar[j]).Name + "\t"); //} //Console.WriteLine(); // get the mapping from WiX elements to app tree paths //Hashtable wxvals = WiXParser.GetManifestHashtable(args[1]); // get the mapping from CMI elements to app tree paths //Hashtable cmivals = CMIParser.GetManifestHashtable(args[2]); #endregion bool doConflictsAndDependencies = true; if (args[0].Equals("-s")) { doConflictsAndDependencies = false; } #region Parse Directory if (args[0].Equals("-r")) { // the next argument must be a directory that // we can parse to get a list of directories that map to apps if (args.Length != 2) { Usage(); return; } string dir = args[1]; DirectoryInfo di = new DirectoryInfo(dir); if (!di.Exists) { Console.WriteLine("Directory " + dir + " does not exist"); return; } DirectoryInfo[] dis = di.GetDirectories(); for(int i = 0; i < dis.Length; i++) { DirectoryInfo tempDi = dis[i]; // if this directory itself contains directories, // step into them to get each app DirectoryInfo[] subDirs = tempDi.GetDirectories(); if (subDirs.Length > 0) { for(int j = 0; j < subDirs.Length; j++) { DirectoryInfo subDir = subDirs[j]; string appName = tempDi.Name + " " + subDir.Name; FileInfo[] fis = subDir.GetFiles(); for(int k = 0; k < fis.Length; k++) { parseFile(appName, subDir.FullName + "\\" + fis[k].Name); } } } else { string appName = tempDi.Name; FileInfo[] fis = tempDi.GetFiles(); for(int j = 0; j < fis.Length; j++) { parseFile(appName, tempDi.FullName + "\\" + fis[j].Name); } } } } else if (args[0].Equals("-d") || args[0].Equals("-s")) { // the next argument must be a directory that // contains a set of .wxs files for us to parse if (args.Length != 2) { Usage(); return; } Console.WriteLine("About to parse " + args[1]); int count = 0; string dir = args[1]; DirectoryInfo di = new DirectoryInfo(dir); if (!di.Exists) { Console.WriteLine("Directory " + dir + " does not exist"); return; } FileInfo[] fis = di.GetFiles(); // Console.WriteLine("Got here with size " + fis.Length); // for each manifest file for(int i = 0; i < fis.Length; i++) { Console.WriteLine(++count); // grab a new app tree //AppTree at = AppTree.CreateAppTree(args[0]); //Hashtable whash = parseFile(fis[i].DirectoryName + "\\" + fis[i].Name, wxvals, cmivals); parseFile(fis[i].DirectoryName + "\\" + fis[i].Name, fis[i].DirectoryName + "\\" + fis[i].Name); // then set up the app tree //at.IncrementWithHashtable(whash); // and flatten it to a point in R^n //ArrayList ar = at.Flatten(); //Console.Write(fis[i].Name + "\t"); // ... that we can spit out for clustering //for(int j = 0; j < ar.Count; j++) //{ // Console.Write(((AppNode)ar[j]).Weight() + "\t"); //} //Console.WriteLine(); } } #endregion #region Parse Single File else { if (args.Length != 1) { Usage(); return; } // grab a new app tree //AppTree at = AppTree.CreateAppTree(args[0]); Console.WriteLine("about to parse file"); //Hashtable whash = parseFile(args[3], wxvals, cmivals); parseFile(args[0], args[0]); //at.IncrementWithHashtable(whash); //at.Dump(); } #endregion #region Enumerate Registry Dependencies Console.WriteLine("---Registry Conflicts---"); // this Hashtable will contain all the names found in the conflict table // and the other apps with which they conflict on registry key/value pairs. Hashtable appConflicts = new Hashtable(); // this table will be used to reduce the size of the generated file Hashtable appNameTable = new Hashtable(); int nameCount = 0; if (doConflictsAndDependencies) { // now walk the Hashtable and dump any conflicts Hashtable conflictTable = ManifestParser.registryConflictTable; IDictionaryEnumerator iDictEnum = conflictTable.GetEnumerator(); while(iDictEnum.MoveNext()) { if (((ArrayList)iDictEnum.Value).Count > 1) { // then we might have a conflict ArrayList confs = ((ArrayList)iDictEnum.Value); string val = (string)((Pair)confs[1]).First; string appname = (string)((Pair)confs[1]).Second; bool founddifference = false; bool foundtwoapps = false; for(int i = 0; i < confs.Count; i++) { Pair outp = (Pair)confs[i]; string newval = (string)((Pair)confs[i]).First; string newappname = (string)((Pair)confs[i]).Second; if ((val == null && newval == null) || (val != null && newval != null && val.ToLower().Equals(newval.ToLower()))) { continue; } // heuristic (may give some false negatives, but then we're just underestimating a bit) // if one of these starts with a '[' in the first two characters, // then it's probably just a substitution that we didn't notice. // In that case, claim that it is not a conflict if ((val != null && val.Length > 1 && val[0] == '[') || (val != null && val.Length > 2 && val[1] == '[') || (newval != null && newval.Length > 1 && newval[0] == '[') || (newval != null && newval.Length > 2 && newval[1] == '[')) { continue; } founddifference = true; if (newappname.Equals(appname)) continue; foundtwoapps = true; } if (!founddifference || !foundtwoapps) continue; Console.WriteLine("C: " + ((string)iDictEnum.Key) + " : " + ((ArrayList)iDictEnum.Value).Count); Hashtable confHash = new Hashtable(); for(int i = 0; i < confs.Count; i++) { Pair outp = (Pair)confs[i]; string confAppName = (string)outp.Second; if (!appConflicts.ContainsKey(confAppName)) { appConflicts[confAppName] = new Hashtable(); } // write the conflicting app into the hash with its value confHash[confAppName] = (string)outp.First; Console.WriteLine("\t " + getAppNumber((string)outp.Second, appNameTable, ref nameCount) + " writes " + (string)outp.First); } IDictionaryEnumerator iconf1 = confHash.GetEnumerator(); while(iconf1.MoveNext()) { string confAppName = (string)iconf1.Key; string confVal1 = (string)iconf1.Value; Hashtable htable = (Hashtable)appConflicts[confAppName]; IDictionaryEnumerator iconf2 = confHash.GetEnumerator(); while(iconf2.MoveNext()) { string curAppName = (string)iconf2.Key; string confVal2 = (string)iconf2.Value; // don't add this conflict if they are the same app if (curAppName.Equals(confAppName)) continue; // don't add this conflict if they have the same value on this key if (confVal2 == null && confVal1 == null) { continue; } else if (confVal2 != null && confVal1 != null && confVal2.ToLower().Equals(confVal1.ToLower())) { continue; } if (!htable.ContainsKey(curAppName)) { htable[curAppName] = 0; } htable[curAppName] = (int)htable[curAppName] + 1; } } } } IDictionaryEnumerator iDE = appConflicts.GetEnumerator(); while(iDE.MoveNext()) { string firstName = (string)iDE.Key; Hashtable confvals = (Hashtable)iDE.Value; IDictionaryEnumerator iDEInner = confvals.GetEnumerator(); while(iDEInner.MoveNext()) { string secondName = (string)iDEInner.Key; int strength = (int)iDEInner.Value; Console.WriteLine(getAppNumber(firstName, appNameTable, ref nameCount) + "\t" + getAppNumber(secondName, appNameTable, ref nameCount) + "\t" + strength); } } #endregion #region Enumerate File Dependencies Console.WriteLine("---File Dependencies---"); // now let's display all the file dependencies. // We'll walk through the ManifestParser.provideFileTable // and ManifestParser.requireFileTable IDictionaryEnumerator ireq = ManifestParser.requiresFileTable.GetEnumerator(); while(ireq.MoveNext()) { string requiredFile = (string)ireq.Key; ArrayList reqArray = (ArrayList)ireq.Value; for(int i = 0; i < reqArray.Count; i++) { string requiringAppName = (string)reqArray[i]; if (!ManifestParser.provideFileTable.ContainsKey(requiredFile)) { // do nothing about it for now //throw new Exception("File " + requiredFile + " is required but not provided!"); } else { // otherwise, we'll walk through all the apps that provide // this file and print a dependency between them ArrayList provArray = (ArrayList)ManifestParser.provideFileTable[requiredFile]; for(int j = 0; j < provArray.Count; j++) { string providingAppName = (string)provArray[j]; Console.WriteLine(getAppNumber(requiringAppName, appNameTable, ref nameCount) + "\t" + getAppNumber(providingAppName, appNameTable, ref nameCount)); } } } } #endregion } #region Evaluate File Spread Console.WriteLine("\n---File Spread---\n"); // we use the appFileTable for each app to compute this IDictionaryEnumerator appFileEnum = ManifestParser.appFileTable.GetEnumerator(); while(appFileEnum.MoveNext()) { string appName = (string)appFileEnum.Key; Hashtable appFiles = (Hashtable)appFileEnum.Value; // walk through the list and figure out how many unique prefixes there are of each length IDictionaryEnumerator lengthEnum = appFiles.GetEnumerator(); int max = 0; while(lengthEnum.MoveNext()) { string path = (string)lengthEnum.Key; string[] pathElts = path.Split(new char[] {'\\'}, 40); // since the paths all start with \ and // we don't want to count the empty first pathElt int length = pathElts.Length - 1; if (length > max) { max = length; } } int[] lengths = new int[max]; for(int i = 0; i < lengths.Length; i++) { lengths[i] = 0; } lengthEnum.Reset(); while(lengthEnum.MoveNext()) { string path = (string)lengthEnum.Key; string[] pathElts = path.Split(new char[] {'\\'}, 40); // since the paths all start with \ and // we don't want to count the empty first pathElt int length = pathElts.Length - 1; // this -1 is because the lengths array is 0-based lengths[length - 1]++; } for(int i = 0; i < lengths.Length; i++) { if (lengths[i] == 0) { lengths[i] = 1; } } double spread = computeSpread(lengths); // look up the real name if (ManifestParser.nameTable.ContainsKey(appName)) { appName = (string)ManifestParser.nameTable[appName]; } string strSpread = ""; for(int i = 0; i < lengths.Length; i++) { if (i != 0) { strSpread += " "; } strSpread += lengths[i]; } Console.WriteLine(appName + " : " + spread + " : " + strSpread); } #endregion #region Evaluate Registry Spread Console.WriteLine("\n---Registry Spread---\n"); // we use the appFileTable for each app to compute this IDictionaryEnumerator appRegistryEnum = ManifestParser.appRegistryTable.GetEnumerator(); while(appRegistryEnum.MoveNext()) { string appName = (string)appRegistryEnum.Key; Hashtable appRegs = (Hashtable)appRegistryEnum.Value; // walk through the list and figure out how many unique prefixes there are of each length IDictionaryEnumerator lengthEnum = appRegs.GetEnumerator(); int max = 0; while(lengthEnum.MoveNext()) { string path = (string)lengthEnum.Key; string[] pathElts = path.Split(new char[] {'\\'}, 40); // since the paths all start with \ and // we don't want to count the empty first pathElt int length = pathElts.Length - 1; if (length > max) { max = length; } } int[] lengths = new int[max]; for(int i = 0; i < lengths.Length; i++) { lengths[i] = 0; } lengthEnum.Reset(); while(lengthEnum.MoveNext()) { string path = (string)lengthEnum.Key; string[] pathElts = path.Split(new char[] {'\\'}, 40); // since the paths all start with \ and // we don't want to count the empty first pathElt int length = pathElts.Length - 1; // this -1 is because the lengths array is 0-based lengths[length - 1]++; } for(int i = 0; i < lengths.Length; i++) { if (lengths[i] == 0) { lengths[i] = 1; } } double spread = computeSpread(lengths); // look up the real name if (ManifestParser.nameTable.ContainsKey(appName)) { appName = (string)ManifestParser.nameTable[appName]; } string strSpread = ""; for(int i = 0; i < lengths.Length; i++) { if (i != 0) { strSpread += " "; } strSpread += lengths[i]; } Console.WriteLine(appName + " : " + spread + " : " + strSpread); } #endregion #region Print Custom Actions Console.WriteLine("---Custom Actions---"); // walk through the ManifestParser.customActionsTable IDictionaryEnumerator customEnum = ManifestParser.customActionsTable.GetEnumerator(); while(customEnum.MoveNext()) { string appName = (string)customEnum.Key; int customCount = (int)customEnum.Value; Console.WriteLine(appName + " : " + customCount); } #endregion #region Print File Extensions and Counts Console.WriteLine("---File Extensions---"); // walk through the ManifestParser.fileTypeTable IDictionaryEnumerator fileEnum = ManifestParser.fileTypeTable.GetEnumerator(); while(fileEnum.MoveNext()) { int uniqueFileTypes = 0; string appName = (string)fileEnum.Key; Hashtable exts = (Hashtable)fileEnum.Value; IDictionaryEnumerator extEnum = exts.GetEnumerator(); while(extEnum.MoveNext()) { uniqueFileTypes++; string extension = (string)extEnum.Key; int extCount = (int)extEnum.Value; Console.WriteLine(appName + " : " + extension + " : " + extCount); } Console.WriteLine(appName + " : " + uniqueFileTypes); } #endregion #region Print Name Table Console.WriteLine("\n---Name Table---\n"); // now write out the name table IDictionaryEnumerator iDENT = appNameTable.GetEnumerator(); while(iDENT.MoveNext()) { string appName = ""; // look up the real name if (ManifestParser.nameTable.ContainsKey((string)iDENT.Key)) { appName = (string)ManifestParser.nameTable[(string)iDENT.Key]; } if (appName.Equals("")) { Console.WriteLine((string)iDENT.Key + " " + (int)iDENT.Value); } else { Console.WriteLine((string)iDENT.Key + " " + appName + " " + (int)iDENT.Value); } } #endregion return; } } }