package build; // this is plain java, since it is only needed if you want to build CABs for // PocketPCs with the ant build. It will not be used by CacheWolf itself import java.io.*; import java.util.*; import java.util.zip.*; public class CabHelper { private static final Boolean DEBUG = true; private static final String PROJECT_NAME = "CW-Test"; private static final String DOSNL = "\r\n"; private static HashMap<String, HashMap<String, String>> cabMapping = new HashMap<String, HashMap<String, String>>(); private static Integer numFiles = 0; private static Integer numDirectories = 0; public static void main(String[] args) { File zipFile; File outDir; String outDirName; StringBuffer outBuffer = new StringBuffer(); // // check arguments and environment // if (args.length != 2) { throw new IllegalArgumentException( "please specify zipfile and output directory"); } zipFile = new File(args[0]); outDirName = args[1]; outDir = new File(outDirName); if (!zipFile.exists() || !zipFile.canRead() || !zipFile.isFile()) { throw new IllegalArgumentException("zip file not readable"); } if (outDir.exists()) { if (outDir.isDirectory()) { String[] files = outDir.list(); if (files.length > 0) { throw new IllegalArgumentException( "out directory not empty"); } } else { throw new IllegalArgumentException( "output directory exists, but is not a directory"); } } else { boolean success = outDir.mkdir(); if (!success) { throw new IllegalArgumentException( "unable to create output directory"); } } // // input parsing // try { ZipFile zip = new ZipFile(zipFile); Enumeration<? extends ZipEntry> zipList; zipList = zip.entries(); while (zipList.hasMoreElements()) { ZipEntry zipEntry = (ZipEntry) zipList.nextElement(); if (zipEntry.isDirectory()) { String dirName = winifyPath(zipEntry.getName()); pushDirToMap(winifyPath(dirName)); } else { String inCabFileName; String outCabFileName; String inCabDirName; File dummyFile; dummyFile = new File(zipEntry.getName()); inCabFileName = dummyFile.getName(); inCabDirName = pushDirToMap(winifyPath(dummyFile.getParent())); outCabFileName = String.format("%08d.DMY", numFiles); copyInputStream(zip.getInputStream(zipEntry), new BufferedOutputStream(new FileOutputStream( outDirName + File.separator + outCabFileName))); cabMapping.get(inCabDirName).put(outCabFileName, inCabFileName); numFiles++; } } } catch (Exception e) { e.printStackTrace(); System.exit(1); } // create an empty logfile and push it on the stack try { File dummyFile = new File(outDirName + File.separator + String.format("%08d.DMY", numFiles)); dummyFile.createNewFile(); cabMapping.get("").put(String.format("%08d.DMY", numFiles), "log.txt"); numFiles++; } catch (Exception e) { e.printStackTrace(); System.exit(1); } // // output generation // // header of the xml file, mandatory outBuffer.append("<wap-provisioningdoc>").append(DOSNL); // header section with summary outBuffer.append("\t<characteristic type=\"Install\">").append(DOSNL); outBuffer.append("\t\t<parm name=\"InstallPhase\" value=\"install\" />").append(DOSNL); // this should remove the version warning at the end of the cab install // but even with a warning it will work outBuffer.append("\t\t<parm name=\"OSVersionMin\" value=\"4.0\" />").append(DOSNL); outBuffer.append("\t\t<parm name=\"OSVersionMax\" value=\"5.9\" />").append(DOSNL); outBuffer.append(String.format("\t\t<parm name=\"AppName\" value=\"%s\" />",PROJECT_NAME)).append(DOSNL); outBuffer.append(String.format("\t\t<parm name=\"InstallDir\" value=\"%%CE1%%\\%s\" translation=\"install\" />",PROJECT_NAME)).append(DOSNL); outBuffer.append(String.format("\t\t<parm name=\"NumDirs\" value=\"%d\" />",numDirectories)).append(DOSNL); outBuffer.append(String.format("\t\t<parm name=\"NumFiles\" value=\"%d\" />",numFiles)).append(DOSNL); outBuffer.append("\t\t<parm name=\"NumRegKeys\" value=\"0\" />").append(DOSNL); outBuffer.append("\t\t<parm name=\"NumRegVals\" value=\"0\" />").append(DOSNL); outBuffer.append("\t\t<parm name=\"NumShortcuts\" value=\"1\" />").append(DOSNL); outBuffer.append("\t</characteristic>").append(DOSNL); // start of fileoperation section outBuffer.append("\t<characteristic type=\"FileOperation\">").append(DOSNL); for (String outKey : cabMapping.keySet()) { // begin of directory structure outBuffer.append( String.format("\t\t<characteristic type=\"%s\" translation=\"install\">",winifyPath("%InstallDir%\\" + outKey)) ).append(DOSNL); outBuffer.append("\t\t\t<characteristic type=\"MakeDir\" />").append(DOSNL); // iterate through the file entries. file entries must follow the the directory definition for (String inKey : cabMapping.get(outKey).keySet()) { outBuffer.append( generateCabFileEntry( cabMapping.get(outKey).get(inKey), inKey ) ); } // end of directory structur outBuffer.append("\t\t</characteristic>").append(DOSNL); } // add shortcut to start menu outBuffer.append("\t\t<characteristic type=\"%CE11%\" translation=\"install\">").append(DOSNL); outBuffer.append("\t\t\t<characteristic type=\"MakeDir\" />").append(DOSNL); outBuffer.append(String.format("\t\t\t<characteristic type=\"%s.lnk\" translation=\"install\">", PROJECT_NAME)).append(DOSNL); outBuffer.append("\t\t\t\t<characteristic type=\"Shortcut\">").append(DOSNL); outBuffer.append("\t\t\t\t\t<parm name=\"Source\" value=\"%InstallDir%\\CacheWolf.exe\" translation=\"install\" />").append(DOSNL); outBuffer.append("\t\t\t\t</characteristic>").append(DOSNL); outBuffer.append("\t\t\t</characteristic>").append(DOSNL); outBuffer.append("\t\t</characteristic>").append(DOSNL); // end of fileoperation section outBuffer.append("\t</characteristic>").append(DOSNL); // add favorite to internet explorer /* // does not work yet, since %InstallDir% is not expanded correctly :-( outBuffer.append("\t<characteristic type=\"BrowserFavorite\">").append(DOSNL); outBuffer.append(String.format("\t\t<characteristic type=\"%s local\">", PROJECT_NAME)).append(DOSNL); outBuffer.append("\t\t\t<parm name=\"URL\" value=\"file://%InstallDir%\\temp.html\"/>").append(DOSNL); outBuffer.append("\t\t</characteristic>").append(DOSNL); outBuffer.append("\t</characteristic>").append(DOSNL); */ // footer of the xml file, mandatory outBuffer.append("</wap-provisioningdoc>").append(DOSNL); // // store output to file // try { BufferedOutputStream outStream = new BufferedOutputStream( new FileOutputStream( outDirName + File.separator + "_setup.xml" ) ); outStream.write(outBuffer.toString().getBytes()); outStream.flush(); outStream.close(); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } /** * copies content of a given input stream to a given output stream * * @param in input stream * @param out output stream * @throws IOException */ private static final void copyInputStream(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[1024]; int len; while ((len = in.read(buffer)) >= 0) { out.write(buffer, 0, len); } in.close(); out.close(); } /** * generate the XML snippet that represents a file for the CAB * * @param original filename that will later be seen in the PocktPC file system * @param translated safe 8.3 noted filename stored inside the CAB * @return characteristic structure for including a file in an installable PocketPC CAB */ private static final String generateCabFileEntry(String original, String translated) { StringBuffer retBuffer = new StringBuffer(); retBuffer.append(String.format("\t\t\t<characteristic type=\"%s\" translation=\"install\">",original)).append(DOSNL); retBuffer.append("\t\t\t\t<characteristic type=\"Extract\">").append(DOSNL); retBuffer.append(String.format("\t\t\t\t\t<parm name=\"Source\" value=\"%s\"/>",translated)).append(DOSNL); retBuffer.append("\t\t\t\t</characteristic>").append(DOSNL); retBuffer.append("\t\t\t</characteristic>").append(DOSNL); return retBuffer.toString(); } /** * generate a path information from the given paramter suitable for usage in * a cab file this includes converting all separators from the current os (/ * or \)to the format later used by the PocketPC (\) and cutting of any * leading or trainling \ * * @param path path to normalize for use in cab * @return normalized path */ private static final String winifyPath(String path) { if (path == null) { return ""; } path = path.replace(File.separatorChar, '\\'); if (path.indexOf('\\') == 0) { path = path.substring(1); } if (path.lastIndexOf('\\') == path.length() - 1) { path = path.substring(0, path.length() - 1); } return path; } /** * mostly for my convenience so I won't have to search for all the System.out.println * to disable debug output * * @param str string to log */ private static final void log(String str) { if (DEBUG) { System.out.println(str); } } /** * check if a given directory already is a key in cabMapping * if directory is not a key yet, put it on the map and * associate an empty hashmap to it * * @param directory name of directory that was pushed to the map */ private static final String pushDirToMap(String directory) { if ( directory == null ) { directory = ""; } if (cabMapping.get(directory) == null) { cabMapping.put(directory, new HashMap<String, String>()); numDirectories++; } return directory; } }