001: /*
002: * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
003: *
004: * http://izpack.org/
005: * http://izpack.codehaus.org/
006: *
007: * Copyright 2004 Elmar Klaus Bartz
008: *
009: * Licensed under the Apache License, Version 2.0 (the "License");
010: * you may not use this file except in compliance with the License.
011: * You may obtain a copy of the License at
012: *
013: * http://www.apache.org/licenses/LICENSE-2.0
014: *
015: * Unless required by applicable law or agreed to in writing, software
016: * distributed under the License is distributed on an "AS IS" BASIS,
017: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018: * See the License for the specific language governing permissions and
019: * limitations under the License.
020: */
021:
022: package com.izforge.izpack.util;
023:
024: import java.io.BufferedInputStream;
025: import java.io.BufferedOutputStream;
026: import java.io.File;
027: import java.io.FileInputStream;
028: import java.io.FileOutputStream;
029: import java.io.IOException;
030: import java.util.Properties;
031: import java.util.StringTokenizer;
032:
033: /**
034: * <p>
035: * Class with some IO related helper.
036: * </p>
037: *
038: */
039: public class IoHelper {
040:
041: // This class uses the same values for family and flavor as
042: // TargetFactory. But this class should not depends on TargetFactory,
043: // because it is possible that TargetFactory is not bound. Therefore
044: // the definition here again.
045:
046: // ------------------------------------------------------------------------
047: // Constant Definitions
048: // ------------------------------------------------------------------------
049:
050: /** Placeholder during translatePath computing */
051: private static final String MASKED_SLASH_PLACEHOLDER = "~&_&~";
052:
053: private static Properties envVars = null;
054:
055: /**
056: * Default constructor
057: */
058: private IoHelper() {
059: }
060:
061: /**
062: * Copies the contents of inFile into outFile.
063: *
064: * @param inFile path of file which should be copied
065: * @param outFile path of file to create and copy the contents of inFile into
066: */
067: public static void copyFile(String inFile, String outFile)
068: throws IOException {
069: copyFile(new File(inFile), new File(outFile));
070: }
071:
072: /**
073: * Creates an in- and output stream for the given File objects and copies all the data from the
074: * specified input to the specified output.
075: *
076: * @param inFile File object for input
077: * @param outFile File object for output
078: * @exception IOException if an I/O error occurs
079: */
080: public static void copyFile(File inFile, File outFile)
081: throws IOException {
082: copyFile(inFile, outFile, null, null);
083: }
084:
085: /**
086: * Creates an in- and output stream for the given File objects and copies all the data from the
087: * specified input to the specified output. If permissions is not null, a chmod will be done on
088: * the output file.
089: *
090: * @param inFile File object for input
091: * @param outFile File object for output
092: * @param permissions permissions for the output file
093: * @exception IOException if an I/O error occurs
094: */
095: public static void copyFile(File inFile, File outFile,
096: String permissions) throws IOException {
097: copyFile(inFile, outFile, permissions, null);
098: }
099:
100: /**
101: * Creates an in- and output stream for the given File objects and copies all the data from the
102: * specified input to the specified output. If the VariableSubstitutor is not null, a substition
103: * will be done during copy.
104: *
105: * @param inFile File object for input
106: * @param outFile File object for output
107: * @param vss substitutor which is used during copying
108: * @exception IOException if an I/O error occurs
109: */
110: public static void copyFile(File inFile, File outFile,
111: VariableSubstitutor vss) throws IOException {
112: copyFile(inFile, outFile, null, vss);
113: }
114:
115: /**
116: * Creates an in- and output stream for the given File objects and copies all the data from the
117: * specified input to the specified output. If the VariableSubstitutor is not null, a substition
118: * will be done during copy. If permissions is not null, a chmod will be done on the output
119: * file.
120: *
121: * @param inFile File object for input
122: * @param outFile File object for output
123: * @param permissions permissions for the output file
124: * @param vs substitutor which is used during copying
125: * @exception IOException if an I/O error occurs
126: */
127: public static void copyFile(File inFile, File outFile,
128: String permissions, VariableSubstitutor vs)
129: throws IOException {
130: copyFile(inFile, outFile, permissions, vs, null);
131: }
132:
133: /**
134: * Creates an in- and output stream for the given File objects and copies all the data from the
135: * specified input to the specified output. If the VariableSubstitutor is not null, a substition
136: * will be done during copy. If permissions is not null, a chmod will be done on the output
137: * file. If type is not null, that type is used as file type at substitution.
138: *
139: * @param inFile File object for input
140: * @param outFile File object for output
141: * @param permissions permissions for the output file
142: * @param vs substitutor which is used during copying
143: * @param type file type for the substitutor
144: * @exception IOException if an I/O error occurs
145: */
146: public static void copyFile(File inFile, File outFile,
147: String permissions, VariableSubstitutor vs, String type)
148: throws IOException {
149: FileOutputStream out = new FileOutputStream(outFile);
150: FileInputStream in = new FileInputStream(inFile);
151: if (vs == null) {
152: byte[] buffer = new byte[5120];
153: long bytesCopied = 0;
154: int bytesInBuffer;
155: while ((bytesInBuffer = in.read(buffer)) != -1) {
156: out.write(buffer, 0, bytesInBuffer);
157: bytesCopied += bytesInBuffer;
158: }
159: in.close();
160: out.close();
161: } else {
162: BufferedInputStream bin = new BufferedInputStream(in, 5120);
163: BufferedOutputStream bout = new BufferedOutputStream(out,
164: 5120);
165: vs.substitute(bin, bout, type, null);
166: bin.close();
167: bout.close();
168: }
169: if (permissions != null && IoHelper.supported("chmod")) {
170: chmod(outFile.getAbsolutePath(), permissions);
171: }
172: }
173:
174: /**
175: * Creates a temp file with delete on exit rule. The extension is extracted from the template if
176: * possible, else the default extension is used. The contents of template will be copied into
177: * the temporary file.
178: *
179: * @param template file to copy from and define file extension
180: * @param defaultExtension file extension if no is contained in template
181: * @return newly created and filled temporary file
182: * @throws IOException
183: */
184: public static File copyToTempFile(File template,
185: String defaultExtension) throws IOException {
186: return copyToTempFile(template, defaultExtension, null);
187: }
188:
189: /**
190: * Creates a temp file with delete on exit rule. The extension is extracted from the template if
191: * possible, else the default extension is used. The contents of template will be copied into
192: * the temporary file. If the variable substitutor is not null, variables will be replaced
193: * during copying.
194: *
195: * @param template file to copy from and define file extension
196: * @param defaultExtension file extension if no is contained in template
197: * @param vss substitutor which is used during copying
198: * @return newly created and filled temporary file
199: * @throws IOException
200: */
201: public static File copyToTempFile(File template,
202: String defaultExtension, VariableSubstitutor vss)
203: throws IOException {
204: String path = template.getCanonicalPath();
205: int pos = path.lastIndexOf('.');
206: String ext = path.substring(pos);
207: if (ext == null)
208: ext = defaultExtension;
209: File tmpFile = File.createTempFile("izpack_io", ext);
210: tmpFile.deleteOnExit();
211: IoHelper.copyFile(template, tmpFile, vss);
212: return tmpFile;
213: }
214:
215: /**
216: * Creates a temp file with delete on exit rule. The extension is extracted from the template if
217: * possible, else the default extension is used. The contents of template will be copied into
218: * the temporary file.
219: *
220: * @param template file to copy from and define file extension
221: * @param defaultExtension file extension if no is contained in template
222: * @return newly created and filled temporary file
223: * @throws IOException
224: */
225: public static File copyToTempFile(String template,
226: String defaultExtension) throws IOException {
227: return copyToTempFile(new File(template), defaultExtension);
228: }
229:
230: /**
231: * Changes the permissions of the given file to the given POSIX permissions.
232: *
233: * @param file the file for which the permissions should be changed
234: * @param permissions POSIX permissions to be set
235: * @throws IOException if an I/O error occurs
236: */
237: public static void chmod(File file, String permissions)
238: throws IOException {
239: chmod(file.getAbsolutePath(), permissions);
240: }
241:
242: /**
243: * Changes the permissions of the given file to the given POSIX permissions. This method will be
244: * raised an exception, if the OS is not UNIX.
245: *
246: * @param path the absolute path of the file for which the permissions should be changed
247: * @param permissions POSIX permissions to be set
248: * @throws IOException if an I/O error occurs
249: */
250: public static void chmod(String path, String permissions)
251: throws IOException {
252: // Perform UNIX
253: if (OsVersion.IS_UNIX) {
254: String[] params = { "chmod", permissions, path };
255: String[] output = new String[2];
256: FileExecutor fe = new FileExecutor();
257: fe.executeCommand(params, output);
258: } else {
259: throw new IOException("Sorry, chmod not supported yet on "
260: + OsVersion.OS_NAME + ".");
261: }
262: }
263:
264: /**
265: * Returns the free (disk) space for the given path. If it is not ascertainable -1 returns.
266: *
267: * @param path path for which the free space should be detected
268: * @return the free space for the given path
269: */
270: public static long getFreeSpace(String path) {
271: long retval = -1;
272: if (OsVersion.IS_WINDOWS) {
273: String command = "cmd.exe";
274: if (System.getProperty("os.name").toLowerCase().indexOf(
275: "windows 9") > -1)
276: return (-1);
277: String[] params = { command, "/C",
278: "\"dir /D /-C \"" + path + "\"\"" };
279: String[] output = new String[2];
280: FileExecutor fe = new FileExecutor();
281: fe.executeCommand(params, output);
282: retval = extractLong(output[0], -3, 3, "%");
283: } else if (OsVersion.IS_SUNOS) {
284: String[] params = { "df", "-k", path };
285: String[] output = new String[2];
286: FileExecutor fe = new FileExecutor();
287: fe.executeCommand(params, output);
288: retval = extractLong(output[0], -3, 3, "%") * 1024;
289: } else if (OsVersion.IS_HPUX) {
290: String[] params = { "bdf", path };
291: String[] output = new String[2];
292: FileExecutor fe = new FileExecutor();
293: fe.executeCommand(params, output);
294: retval = extractLong(output[0], -3, 3, "%") * 1024;
295: } else if (OsVersion.IS_UNIX) {
296: String[] params = { "df", "-Pk", path };
297: String[] output = new String[2];
298: FileExecutor fe = new FileExecutor();
299: fe.executeCommand(params, output);
300: retval = extractLong(output[0], -3, 3, "%") * 1024;
301: }
302: return retval;
303: }
304:
305: /**
306: * Returns whether the given method will be supported with the given environment. Some methods
307: * of this class are not supported on all operation systems.
308: *
309: * @param method name of the method
310: * @return true if the method will be supported with the current enivronment else false
311: * @throws RuntimeException if the given method name does not exist
312: */
313: public static boolean supported(String method) {
314: if ("getFreeSpace".equals(method)) {
315: if (OsVersion.IS_UNIX)
316: return true;
317: if (OsVersion.IS_WINDOWS) { // getFreeSpace do not work on Windows 98.
318: if (System.getProperty("os.name").toLowerCase()
319: .indexOf("windows 9") > -1)
320: return (false);
321: return (true);
322: }
323: } else if ("chmod".equals(method)) {
324: if (OsVersion.IS_UNIX)
325: return true;
326: } else if ("copyFile".equals(method)) {
327: return true;
328: } else if ("getPrimaryGroup".equals(method)) {
329: if (OsVersion.IS_UNIX)
330: return true;
331: } else if ("getenv".equals(method)) {
332: return true;
333: } else {
334: throw new RuntimeException("method name " + method
335: + "not supported by this method");
336: }
337: return false;
338:
339: }
340:
341: /**
342: * Returns the first existing parent directory in a path
343: *
344: * @param path path which should be scanned
345: * @return the first existing parent directory in a path
346: */
347: public static File existingParent(File path) {
348: File result = path;
349: while (!result.exists()) {
350: if (result.getParent() == null)
351: return result;
352: result = result.getParentFile();
353: }
354: return result;
355: }
356:
357: /**
358: * Extracts a long value from a string in a special manner. The string will be broken into
359: * tokens with a standard StringTokenizer. Arround the assumed place (with the given half range)
360: * the tokens are scaned reverse for a token which represents a long. if useNotIdentifier is not
361: * null, tokens which are contains this string will be ignored. The first founded long returns.
362: *
363: * @param in the string which should be parsed
364: * @param assumedPlace token number which should contain the value
365: * @param halfRange half range for detection range
366: * @param useNotIdentifier string which determines tokens which should be ignored
367: * @return founded long
368: */
369: private static long extractLong(String in, int assumedPlace,
370: int halfRange, String useNotIdentifier) {
371: long retval = -1;
372: StringTokenizer st = new StringTokenizer(in);
373: int length = st.countTokens();
374: int i;
375: int currentRange = 0;
376: String[] interestedEntries = new String[halfRange + halfRange];
377: for (i = 0; i < length - halfRange + assumedPlace; ++i)
378: st.nextToken(); // Forget this entries.
379:
380: for (i = 0; i < halfRange + halfRange; ++i) { // Put the interesting Strings into an intermediaer array.
381: if (st.hasMoreTokens()) {
382: interestedEntries[i] = st.nextToken();
383: currentRange++;
384: }
385: }
386:
387: for (i = currentRange - 1; i >= 0; --i) {
388: if (useNotIdentifier != null
389: && interestedEntries[i].indexOf(useNotIdentifier) > -1)
390: continue;
391: try {
392: retval = Long.parseLong(interestedEntries[i]);
393: } catch (NumberFormatException nfe) {
394: continue;
395: }
396: break;
397: }
398: return retval;
399: }
400:
401: /**
402: * Returns the primary group of the current user. This feature will be supported only on Unix.
403: * On other systems null returns.
404: *
405: * @return the primary group of the current user
406: */
407: public static String getPrimaryGroup() {
408: if (supported("getPrimaryGroup")) {
409: if (OsVersion.IS_SUNOS) { // Standard id of SOLARIS do not support -gn.
410: String[] params = { "id" };
411: String[] output = new String[2];
412: FileExecutor fe = new FileExecutor();
413: fe.executeCommand(params, output);
414: // No we have "uid=%u(%s) gid=%u(%s)"
415: if (output[0] != null) {
416: StringTokenizer st = new StringTokenizer(output[0],
417: "()");
418: int length = st.countTokens();
419: if (length >= 4) {
420: for (int i = 0; i < 3; ++i)
421: st.nextToken();
422: return (st.nextToken());
423: }
424: }
425: return (null);
426: } else {
427: String[] params = { "id", "-gn" };
428: String[] output = new String[2];
429: FileExecutor fe = new FileExecutor();
430: fe.executeCommand(params, output);
431: return output[0];
432: }
433: } else
434: return null;
435: }
436:
437: /**
438: * Returns a string resulting from replacing all occurrences of what in this string with with.
439: * In opposite to the String.replaceAll method this method do not use regular expression or
440: * other methods which are only available in JRE 1.4 and later. This method was special made to
441: * mask masked slashes to avert a conversion during path translation.
442: *
443: * @param destination string for which the replacing should be performed
444: * @param what what string should be replaced
445: * @param with with what string what should be replaced
446: * @return a new String object if what was found in the given string, else the given string self
447: */
448: public static String replaceString(String destination, String what,
449: String with) {
450: if (destination.indexOf(what) >= 0) { // what found, with (placeholder) not included in destination ->
451: // perform changing.
452: StringBuffer buf = new StringBuffer();
453: int last = 0;
454: int current = destination.indexOf(what);
455: int whatLength = what.length();
456: while (current >= 0) { // Do not use Methods from JRE 1.4 and higher ...
457: if (current > 0)
458: buf.append(destination.substring(last, current));
459: buf.append(with);
460: last = current + whatLength;
461: current = destination.indexOf(what, last);
462: }
463: if (destination.length() > last)
464: buf.append(destination.substring(last));
465: return buf.toString();
466: }
467: return destination;
468: }
469:
470: /**
471: * Translates a relative path to a local system path.
472: *
473: * @param destination The path to translate.
474: * @return The translated path.
475: */
476: public static String translatePath(String destination,
477: VariableSubstitutor vs) {
478: // Parse for variables
479: destination = vs.substitute(destination, null);
480:
481: // Convert the file separator characters
482:
483: // destination = destination.replace('/', File.separatorChar);
484: // Undo the conversion if the slashes was masked with
485: // a backslash
486:
487: // Not all occurencies of slashes are path separators. To differ
488: // between it we allow to mask a slash with a backslash infront.
489: // Unfortunately we cannot use String.replaceAll because it
490: // handles backslashes in the replacement string in a special way
491: // and the method exist only beginning with JRE 1.4.
492: // Therefore the little bit crude way following ...
493: if (destination.indexOf("\\/") >= 0
494: && destination.indexOf(MASKED_SLASH_PLACEHOLDER) < 0) { // Masked slash found, placeholder not included in destination ->
495: // perform masking.
496: destination = replaceString(destination, "\\/",
497: MASKED_SLASH_PLACEHOLDER);
498: // Masked slashes changed to MASKED_SLASH_PLACEHOLDER.
499: // Replace unmasked slashes.
500: destination = destination.replace('/', File.separatorChar);
501: // Replace the MASKED_SLASH_PLACEHOLDER to slashes; masking
502: // backslashes will
503: // be removed.
504: destination = replaceString(destination,
505: MASKED_SLASH_PLACEHOLDER, "/");
506: } else
507: destination = destination.replace('/', File.separatorChar);
508: return destination;
509: }
510:
511: /**
512: * Returns the value of the environment variable given by key. This method is a work around for
513: * VM versions which do not support getenv in an other way. At the first call all environment
514: * variables will be loaded via an exec. On Windows keys are not case sensitive.
515: *
516: * @param key variable name for which the value should be resolved
517: * @return the value of the environment variable given by key
518: */
519: public static String getenv(String key) {
520: if (envVars == null)
521: loadEnv();
522: if (envVars == null)
523: return (null);
524: if (OsVersion.IS_WINDOWS)
525: key = key.toUpperCase();
526: return (String) (envVars.get(key));
527: }
528:
529: /**
530: * Loads all environment variables via an exec.
531: */
532: private static void loadEnv() {
533: String[] output = new String[2];
534: String[] params;
535: if (OsVersion.IS_WINDOWS) {
536: String command = "cmd.exe";
537: if (System.getProperty("os.name").toLowerCase().indexOf(
538: "windows 9") > -1)
539: command = "command.com";
540: String[] paramst = { command, "/C", "set" };
541: params = paramst;
542: } else {
543: String[] paramst = { "env" };
544: params = paramst;
545: }
546: FileExecutor fe = new FileExecutor();
547: fe.executeCommand(params, output);
548: if (output[0].length() <= 0)
549: return;
550: String lineSep = System.getProperty("line.separator");
551: StringTokenizer st = new StringTokenizer(output[0], lineSep);
552: envVars = new Properties();
553: String var = null;
554: while (st.hasMoreTokens()) {
555: String line = st.nextToken();
556: if (line.indexOf('=') == -1) { // May be a env var with a new line in it.
557: if (var == null) {
558: var = lineSep + line;
559: } else {
560: var += lineSep + line;
561: }
562: } else { // New var, perform the previous one.
563: setEnvVar(var);
564: var = line;
565: }
566: }
567: setEnvVar(var);
568: }
569:
570: /**
571: * Extracts key and value from the given string var. The key should be separated from the value
572: * by a sign. On Windows all chars of the key are translated to upper case.
573: *
574: * @param var
575: */
576: private static void setEnvVar(String var) {
577: if (var == null)
578: return;
579: int index = var.indexOf('=');
580: if (index < 0)
581: return;
582: String key = var.substring(0, index);
583: // On windows change all key chars to upper.
584: if (OsVersion.IS_WINDOWS)
585: key = key.toUpperCase();
586: envVars.setProperty(key, var.substring(index + 1));
587:
588: }
589: }
|