KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nzip


1 /*
2  * nzip.java
3  *
4  * Created on 23. Oktober 2004, 00:28
5  */

6 /*
7  * Copyright 2004-2006 Schlichtherle IT Services
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */

21
22 import de.schlichtherle.io.*;
23 import de.schlichtherle.io.File;
24 import de.schlichtherle.io.archive.tar.*;
25 import de.schlichtherle.io.archive.zip.*;
26 import de.schlichtherle.io.swing.tree.*;
27
28 import java.io.*;
29 import java.util.*;
30 import java.text.*;
31
32 /**
33  * This is a fully fledged ZIP utility which allows you to work with entries
34  * in a ZIP, TAR or any derivative archive file using Unix like commands
35  * (<code>cat</code>, <code>cp</code>, <code>rm</code>, <code>mkdir</code>,
36  * <code>rmdir</code>, <code>ls</code>, etc.).
37  * <p>
38  * Please note that TrueZIP is designed for optimum performance.
39  * However, this utility showcases some optional archive drivers which
40  * provide additional safety or otherwise unavailable features.
41  * Hence this utility should not serve as a performance benchmark
42  * (and by the way this code needs some serious clean up).
43  * These drivers are not used in the default configuration -
44  * see {@link de.schlichtherle.io.DefaultArchiveDetector} for more
45  * information.
46  * <p>
47  * For example, the ZIP drivers used in this utility <em>always</em> check
48  * the CRC-32 values provided in the ZIP file.
49  * Furthermore, the SFX driver is used which allows you to browse
50  * <code>.exe</code> files if they happen to be SelF eXtracting archives (SFX).
51  * If they are not however, TrueZIP will unnecessarily spend some considerable
52  * amount of time searching for the Central Directory required to be present
53  * in ZIP (and hence SFX) files.
54  *
55  *
56  * @author Christian Schlichtherle
57  * @version @version@
58  */

59 public class nzip {
60
61     private static final NumberFormat numberFormat = NumberFormat.getNumberInstance();
62     private static final DateFormat dateFormat = DateFormat.getDateTimeInstance();
63     private static final FieldPosition fpos = new FieldPosition(NumberFormat.INTEGER_FIELD);
64     private static final ProgressMonitor progressMonitor = new ProgressMonitor();
65     private static final boolean PROMPT_WITH_SWING = true;
66
67     private static void usage() {
68         System.err.println(
69                 "nzip (@version@)\n" +
70                 "Copyright 2004-2006 Schlichtherle IT Services\n\n" +
71                 "Usage: nzip COMMAND ...\n\n" +
72                 "where COMMAND is one of (case is ignored):\n\n" +
73                 "\tls [PATH] ...\t\tSimilar to GNU \"ls\"\n" +
74                 "\tll [PATH] ...\t\tSimilar to GNU \"ls -l\"\n" +
75                 "\tllR [PATH] ...\t\tSimilar to GNU \"ls -lR\"\n" +
76                 "\tcat FILE ...\t\tLike GNU \"cat\"\n" +
77                 "\tcp [-unzip|-cp437in|-utf8in|-cp437out|-utf8out] SRC ... DST\tSimilar to GNU \"cp -a\"\n" +
78                 "\tmv SRC ... DST\t\tLike GNU \"mv\"\n" +
79                 "\ttouch PATH ...\t\tLike GNU \"touch\"\n" +
80                 "\tmkdir PATH ...\t\tLike GNU \"mkdir\"\n" +
81                 "\tmkdirs PATH ...\t\tSimilar to GNU \"mkdir -p\"\n" +
82                 "\trm PATH ...\t\tLike GNU \"rm\"\n" +
83                 "\trmR PATH ...\t\tLike GNU \"rm -r\"\n" +
84                 "\tisArchive PATH\t\tTests if PATH names an archive file\n" +
85                 "\tisDirectory PATH\tTests if PATH really is a directory or archive file\n" +
86                 "\tisFile PATH\t\tTests if PATH really is a file\n" +
87                 "\texists PATH\t\tTests if PATH really exists\n\n" +
88                 "\tlength PATH\t\tPrints length of PATH\n\n" +
89                 "For more information please refer to the respective man pages (on Linux).\n\n" +
90                 "Options (case is ignored):\n\n"+
91                 "\t-unzip\t\tTreat any ZIP like pathnames in DST as plain\n" +
92                 "\t\t\tdirectories.\n" +
93                 "\t-cp437(in|out)\tForce any ZIP entry names in SRC/DST to be encoded in CP437.\n" +
94                 "\t-utf8(in|out)\tForce any ZIP entry names in SRC/DST to be encoded in UTF-8.\n"
95                 );
96         System.exit(1);
97     }
98
99     /**
100      * Runs the <code>nzip</code> utility. For more information, please refer
101      * to the source code and the examples published on the project's home
102      * page at <a HREF="http://truezip.dev.java.net">http://truezip.dev.java.net</a>.
103      */

104     public static void main(String JavaDoc[] args) throws Exception JavaDoc {
105         if (args.length < 1)
106             usage();
107
108         // Let this utility recognize all archive types for which an archive
109
// driver is installed.
110
// Note that this may be pretty slow if there are SFX EXE files or
111
// TAR(.GZ|.BZ2) archives to ls.
112
// In addition, for access to RAES encrypted ZIP archives (".tzp",
113
// ".zip.rae", ".zip.raes" suffix) you will need to have Bouncy
114
// Castle's Lightweight Crypto API for JDK 1.4 version 1.30 or higher
115
// in your run time class path.
116
// Furthermore, for access to TAR, TAR.GZ and TAR.BZ2 archives,
117
// you need to have "ant.jar" from Ant 1.6.5 or higher in your
118
// run time class path.
119
// Finally, for regular ZIP files we want to check the CRC32 value
120
// when an entry input stream is closed.
121
File.setDefaultArchiveDetector(new DefaultArchiveDetector(
122                 ArchiveDetector.ALL,
123                 new Object JavaDoc[] {
124                     "zip", new CheckedZip32Driver(), // check CRC32
125
"exe", new CheckedReadOnlySfxDriver(), // check CRC32
126
}));
127
128         configKeyManager();
129
130         final String JavaDoc cmd = args[0].toLowerCase();
131         args = lshift(args);
132         final boolean success;
133
134         try {
135             if ("ls".equals(cmd)) {
136                 success = ls(args, false, false);
137             } else if ("ll".equals(cmd)) {
138                 success = ls(args, true, false);
139             } else if ("llr".equals(cmd)) {
140                 success = ls(args, true, true);
141             } else if ("cat".equals(cmd)) {
142                 success = cat(args);
143             } else if ("cp".equals(cmd)) {
144                 success = cpOrMv(args, false);
145             } else if ("mv".equals(cmd)) {
146                 success = cpOrMv(args, true);
147             } else if ("touch".equals(cmd)) {
148                 success = touch(args);
149             } else if ("mkdir".equals(cmd)) {
150                 success = mkdir(args, false);
151             } else if ("mkdirs".equals(cmd)) {
152                 success = mkdir(args, true);
153             } else if ("rm".equals(cmd)) {
154                 success = rm(args, false);
155             } else if ("rmr".equals(cmd)) {
156                 success = rm(args, true);
157             } else if ("isarchive".equals(cmd)) {
158                 success = isArchive(args);
159             } else if ("isdirectory".equals(cmd)) {
160                 success = isDirectory(args);
161             } else if ("isfile".equals(cmd)) {
162                 success = isFile(args);
163             } else if ("exists".equals(cmd)) {
164                 success = exists(args);
165             } else if ("length".equals(cmd)) {
166                 success = length(args);
167             } else {
168                 usage();
169                 success = false; // make compiler happy
170
}
171         } finally {
172             // Always update all modified ZIP files and grant priority
173
// for any exceptions thrown here.
174
try {
175                 File.umount();
176             } finally {
177                 progressMonitor.interrupt();
178             }
179         }
180         
181         if (!success)
182             System.exit(1);
183     } // main(...)
184

185     /**
186      * Configure the key manager to use when prompting the user for keys for
187      * RAES encrypted ZIP files.
188      */

189     static void configKeyManager() {
190         final String JavaDoc manager = "de.schlichtherle.key.KeyManager";
191         if (PROMPT_WITH_SWING) {
192             // Don't need to set default.
193
/*System.setProperty(manager,
194                     System.getProperty(manager,
195                         "de.schlichtherle.key.passwd.swing.PromptingKeyManager"));*/

196
197             String JavaDoc feedback;
198             feedback = "de.schlichtherle.key.passwd.swing.InvalidOpenKeyFeedback";
199             System.setProperty(feedback,
200                     System.getProperty(feedback,
201                         "de.schlichtherle.key.passwd.swing.HurlingWindowFeedback"));
202
203             feedback = "de.schlichtherle.key.passwd.swing.InvalidCreateKeyFeedback";
204             System.setProperty(feedback,
205                     System.getProperty(feedback,
206                         "de.schlichtherle.key.passwd.swing.HurlingWindowFeedback"));
207         } else {
208             System.setProperty(manager,
209                     System.getProperty(manager,
210                         "de.schlichtherle.key.passwd.console.PromptingKeyManager"));
211         }
212     }
213
214     private static ArchiveDetector createArchiveDetector(final String JavaDoc encoding)
215     throws UnsupportedEncodingException, IllegalAccessException JavaDoc, InstantiationException JavaDoc {
216         assert encoding != null;
217         return new DefaultArchiveDetector(ArchiveDetector.ALL,
218                 new Object JavaDoc[] {
219                     "zip", new CheckedZip32Driver(encoding), // check CRC32
220
"exe", new CheckedReadOnlySfxDriver(encoding), // check CRC32
221
"tar", new TarDriver(encoding),
222                     "tgz|tar.gz", new TarGZipDriver(encoding),
223                     "tbz|tar.bz2", new TarBZip2Driver(encoding),
224                 });
225     }
226
227     private static final String JavaDoc[] lshift(final String JavaDoc[] args) {
228         return lshift(args, 1);
229     }
230
231     private static String JavaDoc[] lshift(final String JavaDoc[] args, final int num) {
232         final int rem = args.length - num;
233         if (rem < 0)
234             throw new IllegalArgumentException JavaDoc();
235         final String JavaDoc[] ret = new String JavaDoc[rem];
236         System.arraycopy(args, num, ret, 0, rem);
237         return ret;
238     }
239
240     private static boolean ls(
241             final String JavaDoc[] args,
242             final boolean detailed,
243             final boolean recursive) {
244         boolean success = true;
245         for (int i = 0; i < args.length || i == 0; i++) {
246             final File file = new File(i < args.length ? args[i] : ".");
247             if (args.length > 1)
248                 System.out.println(args[i] + ":");
249             if (file.isDirectory())
250                 success &= ls("", file, detailed, recursive);
251             else
252                 success &= ls(file.getPath(), file, detailed, recursive);
253         }
254         return success;
255     }
256
257     /**
258      * Lists the given file with the given display path.
259      */

260     private static boolean ls(
261             final String JavaDoc path,
262             final java.io.File JavaDoc file,
263             final boolean detailed,
264             final boolean recursive) {
265         if (file.isDirectory()) {
266             final java.io.File JavaDoc[] entries = file.listFiles();
267             if (entries == null) {
268                 System.err.println(path + " (directory is inaccessible)");
269                 return false;
270             }
271             // Sort directories to the start.
272
Arrays.sort(entries, FileTreeModel.FILE_NAME_COMPARATOR);
273             for (int i = 0; i < entries.length; i++) {
274                 final java.io.File JavaDoc entry = entries[i];
275                 final String JavaDoc entryPath = path.length() > 0
276                         ? path + File.separator + entry.getName()
277                         : entry.getName();
278                 ls(entryPath, entry, detailed);
279                 if (recursive && entry.isDirectory())
280                     ls(entryPath, entries[i], detailed, true);
281             }
282             return true;
283         } else if (file.exists()) {
284             ls(path, file, detailed);
285             return true;
286         } else {
287             System.err.println(path + " (no such file or directory)");
288             return false;
289         }
290     }
291
292     private static void ls(
293             final String JavaDoc path,
294             final java.io.File JavaDoc file,
295             final boolean detailed) {
296         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
297         if (detailed) {
298             align(buf, file.length(), 11);
299             buf.append(' ');
300             buf.append(dateFormat.format(new Date(file.lastModified())));
301             buf.append(' ');
302         }
303         buf.append(path);
304         if (detailed)
305             buf.append(file.isDirectory()
306                     ? File.separator
307                     : (file.isFile() ? "" : "?"));
308         System.out.println(buf.toString());
309     }
310
311     private static void align(StringBuffer JavaDoc buf, long number, int spacing) {
312         final int length = buf.length();
313         numberFormat.format(number, buf, fpos);
314         for (int i = spacing - fpos.getEndIndex(); --i >= 0; )
315             buf.insert(length, ' ');
316     }
317
318     private static boolean cat(final String JavaDoc[] args) {
319         if (args.length < 1)
320             usage();
321
322         boolean success = true;
323         for (int i = 0; i < args.length; i++) {
324             final File file = new File(args[i]);
325             boolean ok = file.catTo(System.out);
326             if (!ok) {
327                 System.err.println(file.getPath() + " (cannot read file)");
328                 success = false;
329             }
330         }
331         return success;
332     }
333
334     private static boolean cpOrMv(final String JavaDoc[] args, final boolean mv)
335     throws Exception JavaDoc {
336         if (args.length < 2)
337             usage();
338
339         int srcI = 0;
340         boolean _unzip = false;
341         boolean _cp437out = false;
342         boolean _utf8out = false;
343         boolean _cp437in = false;
344         boolean _utf8in = false;
345         int in = 0, out = 0;
346         for (; srcI < args.length && args[srcI].charAt(0) == '-'; srcI++) {
347             if (mv) // mv
348
usage();
349             if ("-unzip".equals(args[srcI].toLowerCase())) {
350                 _unzip = true;
351                 out++;
352             } else if ("-cp437out".equals(args[srcI].toLowerCase())) {
353                 _cp437out = true;
354                 out++;
355             } else if ("-utf8out".equals(args[srcI].toLowerCase())) {
356                 _utf8out = true;
357                 out++;
358             } else if ("-cp437in".equals(args[srcI].toLowerCase())) {
359                 _cp437in = true;
360                 in++;
361             } else if ("-utf8in".equals(args[srcI].toLowerCase())) {
362                 _utf8in = true;
363                 in++;
364             } else {
365                 usage();
366             }
367         }
368         if (in > 1 || out > 1)
369             usage();
370
371         final boolean unzip = _unzip;
372         final boolean cp437out = _cp437out;
373         final boolean utf8out = _utf8out;
374         final boolean cp437in = _cp437in;
375         final boolean utf8in = _utf8in;
376
377         final ArchiveDetector srcDetector;
378         if (cp437in)
379             srcDetector = createArchiveDetector("CP437");
380         else if (utf8in)
381             srcDetector = createArchiveDetector("UTF-8");
382         else
383             srcDetector = File.getDefaultArchiveDetector();
384
385         final ArchiveDetector dstDetector;
386         if (unzip)
387             dstDetector = ArchiveDetector.NULL;
388         else if (cp437out)
389             dstDetector = createArchiveDetector("CP437");
390         else if (utf8out)
391             dstDetector = createArchiveDetector("UTF-8");
392         else
393             dstDetector = File.getDefaultArchiveDetector();
394
395         final int dstI = args.length - 1;
396         final File dst = new File(args[dstI], dstDetector);
397         if (dstI - srcI < 1 || (dstI - srcI > 1
398                 && !dst.isArchive() && !dst.isDirectory()))
399             usage();
400
401         if (dst.isArchive() || dst.isEntry())
402             progressMonitor.start();
403
404         boolean success = true;
405         for (int i = srcI; i < dstI; i++) {
406             final File src = new File(args[i], srcDetector);
407             final File tmp;
408             if (dstI - srcI > 1 || dst.isDirectory())
409                 tmp = new File(dst, src.getName(), dstDetector);
410             else
411                 tmp = dst;
412             final boolean ok;
413             if (mv) { // mv
414
ok = !src.contains(tmp)
415                         && (!tmp.isFile() || tmp.delete())
416                         && src.renameTo(tmp);
417                 if (!ok)
418                     System.err.println(src + ": Cannot move to " + tmp + "!");
419             } else { // cp
420
if (!(ok = src.archiveCopyAllTo(
421                         tmp, srcDetector, dstDetector)))
422                     System.err.println(src + ": Copying to " + tmp + " has failed. Check output!");
423             }
424             success &= ok;
425         }
426         return success;
427     }
428
429     private static boolean touch(final String JavaDoc[] args) throws IOException {
430         if (args.length < 1)
431             usage();
432
433         boolean success = true;
434         for (int i = 0; i < args.length; i++) {
435             final File file = new File(args[i]);
436             final boolean ok;
437             if (!file.exists())
438                 ok = file.createNewFile();
439             else
440                 ok = file.setLastModified(System.currentTimeMillis());
441             if (!ok) {
442                 final String JavaDoc msg;
443                 if (!file.exists())
444                     msg = "cannot create file";
445                 else if (file.isDirectory())
446                     msg = "cannot update directory last modification time";
447                 else if (file.isFile())
448                     msg = "cannot update file last modification time";
449                 else
450                     msg = "cannot update special file or directory last modification time";
451                 System.err.println(file.getPath() + " (" + msg + ")");
452                 success = false;
453             }
454         }
455         return success;
456     }
457
458     private static boolean mkdir(final String JavaDoc[] args, final boolean recursive) {
459         if (args.length < 1)
460             usage();
461
462         boolean success = true;
463         for (int i = 0; i < args.length; i++) {
464             final File file = new File(args[i]);
465             final boolean ok = recursive ? file.mkdirs() : file.mkdir();
466             if (!ok) {
467                 final String JavaDoc msg;
468                 if (!file.exists())
469                     msg = "cannot create directory";
470                 else if (file.isDirectory())
471                     msg = "directory exists already";
472                 else if (file.isFile())
473                     msg = "file exists already";
474                 else
475                     msg = "special file or directory exists already";
476                 System.err.println(file.getPath() + " (" + msg + ")");
477                 success = false;
478             }
479         }
480         return success;
481     }
482
483     private static boolean rm(final String JavaDoc[] args, final boolean recursive) {
484         if (args.length < 1)
485             usage();
486
487         boolean success = true;
488         for (int i = 0; i < args.length; i++) {
489             final File file = new File(args[i]);
490             final boolean ok = recursive
491                     ? file.deleteAll()
492                     : file.delete();
493             if (!ok) {
494                 final String JavaDoc msg;
495                 if (!file.exists())
496                     msg = "no such file or directory";
497                 else if (file.isDirectory())
498                     if (file.list().length > 0)
499                         msg = "directory not empty";
500                     else
501                         msg = "cannot remove directory";
502                 else if (file.isFile())
503                     msg = "cannot remove file";
504                 else
505                     msg = "cannot remove special file or directory";
506                 System.err.println(file.getPath() + " (" + msg + ")");
507                 success = false;
508             }
509         }
510         return success;
511     }
512
513     private static boolean isArchive(final String JavaDoc[] args) {
514         if (args.length < 1)
515             usage();
516
517         boolean success = new File(args[0]).isArchive();
518         System.out.println(success);
519         return success;
520     }
521
522     private static boolean isDirectory(final String JavaDoc[] args) {
523         if (args.length < 1)
524             usage();
525
526         boolean success = new File(args[0]).isDirectory();
527         System.out.println(success);
528         return success;
529     }
530
531     private static boolean isFile(final String JavaDoc[] args) {
532         if (args.length < 1)
533             usage();
534
535         boolean success = new File(args[0]).isFile();
536         System.out.println(success);
537         return success;
538     }
539
540     private static boolean exists(final String JavaDoc[] args) {
541         if (args.length < 1)
542             usage();
543
544         boolean success = new File(args[0]).exists();
545         System.out.println(success);
546         return success;
547     }
548
549     private static boolean length(final String JavaDoc[] args) {
550         if (args.length < 1)
551             usage();
552
553         final long length = new File(args[0]).length();
554         System.out.println(length);
555         return true;
556     }
557
558     static final class ProgressMonitor extends Thread JavaDoc {
559         private final Long JavaDoc[] args = new Long JavaDoc[2];
560         private final ArchiveStatistics liveStats = File.getLiveArchiveStatistics();
561
562         public ProgressMonitor() {
563             setPriority(Thread.MAX_PRIORITY);
564             setDaemon(true);
565         }
566
567         public void run() {
568             for (long sleep = 200; ; sleep = 200) {
569                 try {
570                     Thread.sleep(sleep);
571                 } catch (InterruptedException JavaDoc interrupted) {
572                     break;
573                 }
574                 showProgress();
575             }
576         }
577
578         /**
579          * Prints statistics about the amount of data read and written by
580          * {@link File#update()} or {@link File#umount()} on standard output.
581          */

582         private void showProgress() {
583             // Round up to kilobytes.
584
args[0] = new Long JavaDoc(
585                     (liveStats.getUpdateTotalByteCountRead() + 1023) / 1024);
586             args[1] = new Long JavaDoc(
587                     (liveStats.getUpdateTotalByteCountWritten() + 1023) / 1024);
588             System.out.print(MessageFormat.format(
589                     "Top level ZIP update: In {0} / Out {1} KB \r", args));
590         }
591     };
592 }
593
Popular Tags