24 */ |
24 */ |
25 |
25 |
26 package sun.tools.jar; |
26 package sun.tools.jar; |
27 |
27 |
28 import java.io.*; |
28 import java.io.*; |
|
29 import java.nio.file.Path; |
29 import java.util.*; |
30 import java.util.*; |
30 import java.util.zip.*; |
31 import java.util.zip.*; |
31 import java.util.jar.*; |
32 import java.util.jar.*; |
32 import java.util.jar.Manifest; |
33 import java.util.jar.Manifest; |
33 import java.text.MessageFormat; |
34 import java.text.MessageFormat; |
34 import sun.misc.JarIndex; |
35 import sun.misc.JarIndex; |
|
36 import static sun.misc.JarIndex.INDEX_NAME; |
|
37 import static java.util.jar.JarFile.MANIFEST_NAME; |
|
38 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; |
35 |
39 |
36 /** |
40 /** |
37 * This class implements a simple utility for creating files in the JAR |
41 * This class implements a simple utility for creating files in the JAR |
38 * (Java Archive) file format. The JAR format is based on the ZIP file |
42 * (Java Archive) file format. The JAR format is based on the ZIP file |
39 * format, with optional meta-information stored in a MANIFEST entry. |
43 * format, with optional meta-information stored in a MANIFEST entry. |
69 * Mflag: DO NOT generate a manifest file (just ZIP) |
72 * Mflag: DO NOT generate a manifest file (just ZIP) |
70 * iflag: generate jar index |
73 * iflag: generate jar index |
71 */ |
74 */ |
72 boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag; |
75 boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag; |
73 |
76 |
74 static final String MANIFEST = JarFile.MANIFEST_NAME; |
|
75 static final String MANIFEST_DIR = "META-INF/"; |
77 static final String MANIFEST_DIR = "META-INF/"; |
76 static final String VERSION = "1.0"; |
78 static final String VERSION = "1.0"; |
77 static final String INDEX = JarIndex.INDEX_NAME; |
|
78 |
79 |
79 private static ResourceBundle rsrc; |
80 private static ResourceBundle rsrc; |
80 |
81 |
81 /** |
82 /** |
82 * If true, maintain compatibility with JDK releases prior to 6.0 by |
83 * If true, maintain compatibility with JDK releases prior to 6.0 by |
193 File inputFile = null, tmpFile = null; |
206 File inputFile = null, tmpFile = null; |
194 FileInputStream in; |
207 FileInputStream in; |
195 FileOutputStream out; |
208 FileOutputStream out; |
196 if (fname != null) { |
209 if (fname != null) { |
197 inputFile = new File(fname); |
210 inputFile = new File(fname); |
198 String path = inputFile.getParent(); |
211 tmpFile = createTempFileInSameDirectoryAs(inputFile); |
199 tmpFile = File.createTempFile("tmp", null, |
|
200 new File((path == null) ? "." : path)); |
|
201 in = new FileInputStream(inputFile); |
212 in = new FileInputStream(inputFile); |
202 out = new FileOutputStream(tmpFile); |
213 out = new FileOutputStream(tmpFile); |
203 } else { |
214 } else { |
204 in = new FileInputStream(FileDescriptor.in); |
215 in = new FileInputStream(FileDescriptor.in); |
205 out = new FileOutputStream(FileDescriptor.out); |
216 out = new FileOutputStream(FileDescriptor.out); |
206 vflag = false; |
217 vflag = false; |
207 } |
218 } |
208 InputStream manifest = (!Mflag && (mname != null)) ? |
219 InputStream manifest = (!Mflag && (mname != null)) ? |
209 (new FileInputStream(mname)) : null; |
220 (new FileInputStream(mname)) : null; |
210 expand(null, files, true); |
221 expand(null, files, true); |
211 boolean updateOk = update(in, new BufferedOutputStream(out), manifest, null); |
222 boolean updateOk = update(in, new BufferedOutputStream(out), |
|
223 manifest, null); |
212 if (ok) { |
224 if (ok) { |
213 ok = updateOk; |
225 ok = updateOk; |
214 } |
226 } |
215 in.close(); |
227 in.close(); |
216 out.close(); |
228 out.close(); |
474 addFile(zos, file); |
486 addFile(zos, file); |
475 } |
487 } |
476 zos.close(); |
488 zos.close(); |
477 } |
489 } |
478 |
490 |
479 /* |
491 private char toUpperCaseASCII(char c) { |
480 * update an existing jar file. |
492 return (c < 'a' || c > 'z') ? c : (char) (c + 'A' - 'a'); |
|
493 } |
|
494 |
|
495 /** |
|
496 * Compares two strings for equality, ignoring case. The second |
|
497 * argument must contain only upper-case ASCII characters. |
|
498 * We don't want case comparison to be locale-dependent (else we |
|
499 * have the notorious "turkish i bug"). |
|
500 */ |
|
501 private boolean equalsIgnoreCase(String s, String upper) { |
|
502 assert upper.toUpperCase(java.util.Locale.ENGLISH).equals(upper); |
|
503 int len; |
|
504 if ((len = s.length()) != upper.length()) |
|
505 return false; |
|
506 for (int i = 0; i < len; i++) { |
|
507 char c1 = s.charAt(i); |
|
508 char c2 = upper.charAt(i); |
|
509 if (c1 != c2 && toUpperCaseASCII(c1) != c2) |
|
510 return false; |
|
511 } |
|
512 return true; |
|
513 } |
|
514 |
|
515 /** |
|
516 * Updates an existing jar file. |
481 */ |
517 */ |
482 boolean update(InputStream in, OutputStream out, |
518 boolean update(InputStream in, OutputStream out, |
483 InputStream newManifest, |
519 InputStream newManifest, |
484 JarIndex jarIndex) throws IOException |
520 JarIndex jarIndex) throws IOException |
485 { |
521 { |
486 ZipInputStream zis = new ZipInputStream(in); |
522 ZipInputStream zis = new ZipInputStream(in); |
487 ZipOutputStream zos = new JarOutputStream(out); |
523 ZipOutputStream zos = new JarOutputStream(out); |
488 ZipEntry e = null; |
524 ZipEntry e = null; |
489 boolean foundManifest = false; |
525 boolean foundManifest = false; |
490 byte[] buf = new byte[1024]; |
|
491 int n = 0; |
|
492 boolean updateOk = true; |
526 boolean updateOk = true; |
493 |
527 |
494 if (jarIndex != null) { |
528 if (jarIndex != null) { |
495 addIndex(jarIndex, zos); |
529 addIndex(jarIndex, zos); |
496 } |
530 } |
497 |
531 |
498 // put the old entries first, replace if necessary |
532 // put the old entries first, replace if necessary |
499 while ((e = zis.getNextEntry()) != null) { |
533 while ((e = zis.getNextEntry()) != null) { |
500 String name = e.getName(); |
534 String name = e.getName(); |
501 |
535 |
502 boolean isManifestEntry = name.toUpperCase( |
536 boolean isManifestEntry = equalsIgnoreCase(name, MANIFEST_NAME); |
503 java.util.Locale.ENGLISH). |
537 |
504 equals(MANIFEST); |
538 if ((jarIndex != null && equalsIgnoreCase(name, INDEX_NAME)) |
505 if ((name.toUpperCase().equals(INDEX) && jarIndex != null) |
|
506 || (Mflag && isManifestEntry)) { |
539 || (Mflag && isManifestEntry)) { |
507 continue; |
540 continue; |
508 } else if (isManifestEntry && ((newManifest != null) || |
541 } else if (isManifestEntry && ((newManifest != null) || |
509 (ename != null))) { |
542 (ename != null))) { |
510 foundManifest = true; |
543 foundManifest = true; |
511 if (newManifest != null) { |
544 if (newManifest != null) { |
512 // Don't read from the newManifest InputStream, as we |
545 // Don't read from the newManifest InputStream, as we |
513 // might need it below, and we can't re-read the same data |
546 // might need it below, and we can't re-read the same data |
514 // twice. |
547 // twice. |
515 FileInputStream fis = new FileInputStream(mname); |
548 FileInputStream fis = new FileInputStream(mname); |
516 boolean ambigous = isAmbigousMainClass(new Manifest(fis)); |
549 boolean ambiguous = isAmbiguousMainClass(new Manifest(fis)); |
517 fis.close(); |
550 fis.close(); |
518 if (ambigous) { |
551 if (ambiguous) { |
519 return false; |
552 return false; |
520 } |
553 } |
521 } |
554 } |
522 |
555 |
523 // Update the manifest. |
556 // Update the manifest. |
537 if (e.getMethod() == ZipEntry.STORED) { |
570 if (e.getMethod() == ZipEntry.STORED) { |
538 e2.setSize(e.getSize()); |
571 e2.setSize(e.getSize()); |
539 e2.setCrc(e.getCrc()); |
572 e2.setCrc(e.getCrc()); |
540 } |
573 } |
541 zos.putNextEntry(e2); |
574 zos.putNextEntry(e2); |
542 while ((n = zis.read(buf, 0, buf.length)) != -1) { |
575 copy(zis, zos); |
543 zos.write(buf, 0, n); |
|
544 } |
|
545 } else { // replace with the new files |
576 } else { // replace with the new files |
546 File f = entryMap.get(name); |
577 File f = entryMap.get(name); |
547 addFile(zos, f); |
578 addFile(zos, f); |
548 entryMap.remove(name); |
579 entryMap.remove(name); |
549 entries.remove(f); |
580 entries.remove(f); |
573 |
604 |
574 |
605 |
575 private void addIndex(JarIndex index, ZipOutputStream zos) |
606 private void addIndex(JarIndex index, ZipOutputStream zos) |
576 throws IOException |
607 throws IOException |
577 { |
608 { |
578 ZipEntry e = new ZipEntry(INDEX); |
609 ZipEntry e = new ZipEntry(INDEX_NAME); |
579 e.setTime(System.currentTimeMillis()); |
610 e.setTime(System.currentTimeMillis()); |
580 if (flag0) { |
611 if (flag0) { |
581 e.setMethod(ZipEntry.STORED); |
612 CRC32OutputStream os = new CRC32OutputStream(); |
582 File ifile = File.createTempFile("index", null, new File(".")); |
613 index.write(os); |
583 BufferedOutputStream bos = new BufferedOutputStream |
614 os.updateEntry(e); |
584 (new FileOutputStream(ifile)); |
|
585 index.write(bos); |
|
586 crc32File(e, ifile); |
|
587 bos.close(); |
|
588 ifile.delete(); |
|
589 } |
615 } |
590 zos.putNextEntry(e); |
616 zos.putNextEntry(e); |
591 index.write(zos); |
617 index.write(zos); |
592 if (vflag) { |
618 zos.closeEntry(); |
593 // output(getMsg("out.update.manifest")); |
|
594 } |
|
595 } |
619 } |
596 |
620 |
597 private void updateManifest(Manifest m, ZipOutputStream zos) |
621 private void updateManifest(Manifest m, ZipOutputStream zos) |
598 throws IOException |
622 throws IOException |
599 { |
623 { |
600 addVersion(m); |
624 addVersion(m); |
601 addCreatedBy(m); |
625 addCreatedBy(m); |
602 if (ename != null) { |
626 if (ename != null) { |
603 addMainClass(m, ename); |
627 addMainClass(m, ename); |
604 } |
628 } |
605 ZipEntry e = new ZipEntry(MANIFEST); |
629 ZipEntry e = new ZipEntry(MANIFEST_NAME); |
606 e.setTime(System.currentTimeMillis()); |
630 e.setTime(System.currentTimeMillis()); |
607 if (flag0) { |
631 if (flag0) { |
608 e.setMethod(ZipEntry.STORED); |
|
609 crc32Manifest(e, m); |
632 crc32Manifest(e, m); |
610 } |
633 } |
611 zos.putNextEntry(e); |
634 zos.putNextEntry(e); |
612 m.write(zos); |
635 m.write(zos); |
613 if (vflag) { |
636 if (vflag) { |
735 output(getMsg("out.stored")); |
751 output(getMsg("out.stored")); |
736 } |
752 } |
737 } |
753 } |
738 } |
754 } |
739 |
755 |
740 /* |
756 /** |
741 * compute the crc32 of a file. This is necessary when the ZipOutputStream |
757 * A buffer for use only by copy(InputStream, OutputStream). |
742 * is in STORED mode. |
758 * Not as clean as allocating a new buffer as needed by copy, |
|
759 * but significantly more efficient. |
|
760 */ |
|
761 private byte[] copyBuf = new byte[8192]; |
|
762 |
|
763 /** |
|
764 * Copies all bytes from the input stream to the output stream. |
|
765 * Does not close or flush either stream. |
|
766 * |
|
767 * @param from the input stream to read from |
|
768 * @param to the output stream to write to |
|
769 * @throws IOException if an I/O error occurs |
|
770 */ |
|
771 private void copy(InputStream from, OutputStream to) throws IOException { |
|
772 int n; |
|
773 while ((n = from.read(copyBuf)) != -1) |
|
774 to.write(copyBuf, 0, n); |
|
775 } |
|
776 |
|
777 /** |
|
778 * Copies all bytes from the input file to the output stream. |
|
779 * Does not close or flush the output stream. |
|
780 * |
|
781 * @param from the input file to read from |
|
782 * @param to the output stream to write to |
|
783 * @throws IOException if an I/O error occurs |
|
784 */ |
|
785 private void copy(File from, OutputStream to) throws IOException { |
|
786 InputStream in = new FileInputStream(from); |
|
787 try { |
|
788 copy(in, to); |
|
789 } finally { |
|
790 in.close(); |
|
791 } |
|
792 } |
|
793 |
|
794 /** |
|
795 * Copies all bytes from the input stream to the output file. |
|
796 * Does not close the input stream. |
|
797 * |
|
798 * @param from the input stream to read from |
|
799 * @param to the output file to write to |
|
800 * @throws IOException if an I/O error occurs |
|
801 */ |
|
802 private void copy(InputStream from, File to) throws IOException { |
|
803 OutputStream out = new FileOutputStream(to); |
|
804 try { |
|
805 copy(from, out); |
|
806 } finally { |
|
807 out.close(); |
|
808 } |
|
809 } |
|
810 |
|
811 /** |
|
812 * Computes the crc32 of a Manifest. This is necessary when the |
|
813 * ZipOutputStream is in STORED mode. |
743 */ |
814 */ |
744 private void crc32Manifest(ZipEntry e, Manifest m) throws IOException { |
815 private void crc32Manifest(ZipEntry e, Manifest m) throws IOException { |
745 crc32.reset(); |
816 CRC32OutputStream os = new CRC32OutputStream(); |
746 CRC32OutputStream os = new CRC32OutputStream(crc32); |
|
747 m.write(os); |
817 m.write(os); |
748 e.setSize((long) os.n); |
818 os.updateEntry(e); |
749 e.setCrc(crc32.getValue()); |
819 } |
750 } |
820 |
751 |
821 /** |
752 /* |
822 * Computes the crc32 of a File. This is necessary when the |
753 * compute the crc32 of a file. This is necessary when the ZipOutputStream |
823 * ZipOutputStream is in STORED mode. |
754 * is in STORED mode. |
|
755 */ |
824 */ |
756 private void crc32File(ZipEntry e, File f) throws IOException { |
825 private void crc32File(ZipEntry e, File f) throws IOException { |
757 InputStream is = new BufferedInputStream(new FileInputStream(f)); |
826 CRC32OutputStream os = new CRC32OutputStream(); |
758 byte[] buf = new byte[8192]; |
827 copy(f, os); |
759 crc32.reset(); |
828 if (os.n != f.length()) { |
760 int r = 0; |
|
761 int nread = 0; |
|
762 long len = f.length(); |
|
763 while ((r = is.read(buf)) != -1) { |
|
764 nread += r; |
|
765 crc32.update(buf, 0, r); |
|
766 } |
|
767 is.close(); |
|
768 if (nread != (int) len) { |
|
769 throw new JarException(formatMsg( |
829 throw new JarException(formatMsg( |
770 "error.incorrect.length", f.getPath())); |
830 "error.incorrect.length", f.getPath())); |
771 } |
831 } |
772 e.setCrc(crc32.getValue()); |
832 os.updateEntry(e); |
773 } |
833 } |
774 |
834 |
775 void replaceFSC(String files[]) { |
835 void replaceFSC(String files[]) { |
776 if (files != null) { |
836 if (files != null) { |
777 for (String file : files) { |
837 for (String file : files) { |
778 file = file.replace(File.separatorChar, '/'); |
838 file = file.replace(File.separatorChar, '/'); |
779 } |
839 } |
780 } |
840 } |
781 } |
841 } |
782 |
842 |
|
843 @SuppressWarnings("serial") |
783 Set<ZipEntry> newDirSet() { |
844 Set<ZipEntry> newDirSet() { |
784 return new HashSet<ZipEntry>() { |
845 return new HashSet<ZipEntry>() { |
785 public boolean add(ZipEntry e) { |
846 public boolean add(ZipEntry e) { |
786 return ((e == null || useExtractionTime) ? false : super.add(e)); |
847 return ((e == null || useExtractionTime) ? false : super.add(e)); |
787 }}; |
848 }}; |
825 // instead of during, because creating a file in a directory changes |
886 // instead of during, because creating a file in a directory changes |
826 // that directory's timestamp. |
887 // that directory's timestamp. |
827 updateLastModifiedTime(dirs); |
888 updateLastModifiedTime(dirs); |
828 } |
889 } |
829 |
890 |
830 /* |
891 /** |
831 * Extracts specified entries from JAR file, via ZipFile. |
892 * Extracts specified entries from JAR file, via ZipFile. |
832 */ |
893 */ |
833 void extract(String fname, String files[]) throws IOException { |
894 void extract(String fname, String files[]) throws IOException { |
834 ZipFile zf = new ZipFile(fname); |
895 ZipFile zf = new ZipFile(fname); |
835 Set<ZipEntry> dirs = newDirSet(); |
896 Set<ZipEntry> dirs = newDirSet(); |
886 if (!d.exists() && !d.mkdirs() || !d.isDirectory()) { |
947 if (!d.exists() && !d.mkdirs() || !d.isDirectory()) { |
887 throw new IOException(formatMsg( |
948 throw new IOException(formatMsg( |
888 "error.create.dir", d.getPath())); |
949 "error.create.dir", d.getPath())); |
889 } |
950 } |
890 } |
951 } |
891 OutputStream os = new FileOutputStream(f); |
|
892 byte[] b = new byte[8192]; |
|
893 int len; |
|
894 try { |
952 try { |
895 while ((len = is.read(b, 0, b.length)) != -1) { |
953 copy(is, f); |
896 os.write(b, 0, len); |
|
897 } |
|
898 } finally { |
954 } finally { |
899 if (is instanceof ZipInputStream) |
955 if (is instanceof ZipInputStream) |
900 ((ZipInputStream)is).closeEntry(); |
956 ((ZipInputStream)is).closeEntry(); |
901 else |
957 else |
902 is.close(); |
958 is.close(); |
903 os.close(); |
|
904 } |
959 } |
905 if (vflag) { |
960 if (vflag) { |
906 if (e.getMethod() == ZipEntry.DEFLATED) { |
961 if (e.getMethod() == ZipEntry.DEFLATED) { |
907 output(formatMsg("out.inflated", name)); |
962 output(formatMsg("out.inflated", name)); |
908 } else { |
963 } else { |
948 } |
1003 } |
949 zf.close(); |
1004 zf.close(); |
950 } |
1005 } |
951 |
1006 |
952 /** |
1007 /** |
953 * Output the class index table to the INDEX.LIST file of the |
1008 * Outputs the class index table to the INDEX.LIST file of the |
954 * root jar file. |
1009 * root jar file. |
955 */ |
1010 */ |
956 void dumpIndex(String rootjar, JarIndex index) throws IOException { |
1011 void dumpIndex(String rootjar, JarIndex index) throws IOException { |
957 File scratchFile = File.createTempFile("scratch", null, new File(".")); |
|
958 File jarFile = new File(rootjar); |
1012 File jarFile = new File(rootjar); |
959 boolean updateOk = update(new FileInputStream(jarFile), |
1013 Path jarPath = jarFile.toPath(); |
960 new FileOutputStream(scratchFile), |
1014 Path tmpPath = createTempFileInSameDirectoryAs(jarFile).toPath(); |
961 null, index); |
1015 try { |
962 jarFile.delete(); |
1016 if (update(jarPath.newInputStream(), |
963 if (!scratchFile.renameTo(jarFile)) { |
1017 tmpPath.newOutputStream(), |
964 scratchFile.delete(); |
1018 null, index)) { |
965 throw new IOException(getMsg("error.write.file")); |
1019 try { |
966 } |
1020 tmpPath.moveTo(jarPath, REPLACE_EXISTING); |
967 scratchFile.delete(); |
1021 } catch (IOException e) { |
968 } |
1022 throw new IOException(getMsg("error.write.file"), e); |
969 |
1023 } |
970 private Hashtable jarTable = new Hashtable(); |
1024 } |
971 /* |
1025 } finally { |
972 * Generate the transitive closure of the Class-Path attribute for |
1026 tmpPath.deleteIfExists(); |
|
1027 } |
|
1028 } |
|
1029 |
|
1030 private HashSet<String> jarPaths = new HashSet<String>(); |
|
1031 |
|
1032 /** |
|
1033 * Generates the transitive closure of the Class-Path attribute for |
973 * the specified jar file. |
1034 * the specified jar file. |
974 */ |
1035 */ |
975 Vector getJarPath(String jar) throws IOException { |
1036 List<String> getJarPath(String jar) throws IOException { |
976 Vector files = new Vector(); |
1037 List<String> files = new ArrayList<String>(); |
977 files.add(jar); |
1038 files.add(jar); |
978 jarTable.put(jar, jar); |
1039 jarPaths.add(jar); |
979 |
1040 |
980 // take out the current path |
1041 // take out the current path |
981 String path = jar.substring(0, Math.max(0, jar.lastIndexOf('/') + 1)); |
1042 String path = jar.substring(0, Math.max(0, jar.lastIndexOf('/') + 1)); |
982 |
1043 |
983 // class path attribute will give us jar file name with |
1044 // class path attribute will give us jar file name with |
1025 for (int i = 0; i < files.length; i++) { |
1086 for (int i = 0; i < files.length; i++) { |
1026 jars.addAll(getJarPath(files[i])); |
1087 jars.addAll(getJarPath(files[i])); |
1027 } |
1088 } |
1028 njars = jars.size(); |
1089 njars = jars.size(); |
1029 } |
1090 } |
1030 jarfiles = (String[])jars.toArray(new String[njars]); |
1091 jarfiles = jars.toArray(new String[njars]); |
1031 JarIndex index = new JarIndex(jarfiles); |
1092 JarIndex index = new JarIndex(jarfiles); |
1032 dumpIndex(rootjar, index); |
1093 dumpIndex(rootjar, index); |
1033 } |
1094 } |
1034 |
1095 |
1035 /* |
1096 /** |
1036 * Prints entry information, if requested. |
1097 * Prints entry information, if requested. |
1037 */ |
1098 */ |
1038 void printEntry(ZipEntry e, String[] files) throws IOException { |
1099 void printEntry(ZipEntry e, String[] files) throws IOException { |
1039 if (files == null) { |
1100 if (files == null) { |
1040 printEntry(e); |
1101 printEntry(e); |
1065 } else { |
1126 } else { |
1066 output(e.getName()); |
1127 output(e.getName()); |
1067 } |
1128 } |
1068 } |
1129 } |
1069 |
1130 |
1070 /* |
1131 /** |
1071 * Print usage message and die. |
1132 * Prints usage message. |
1072 */ |
1133 */ |
1073 void usageError() { |
1134 void usageError() { |
1074 error(getMsg("usage")); |
1135 error(getMsg("usage")); |
1075 } |
1136 } |
1076 |
1137 |
1077 /* |
1138 /** |
1078 * A fatal exception has been caught. No recovery possible |
1139 * A fatal exception has been caught. No recovery possible |
1079 */ |
1140 */ |
1080 void fatalError(Exception e) { |
1141 void fatalError(Exception e) { |
1081 e.printStackTrace(); |
1142 e.printStackTrace(); |
1082 } |
1143 } |
1083 |
1144 |
1084 /* |
1145 /** |
1085 * A fatal condition has been detected; message is "s". |
1146 * A fatal condition has been detected; message is "s". |
1086 * No recovery possible |
1147 * No recovery possible |
1087 */ |
1148 */ |
1088 void fatalError(String s) { |
1149 void fatalError(String s) { |
1089 error(program + ": " + s); |
1150 error(program + ": " + s); |
1101 */ |
1162 */ |
1102 protected void error(String s) { |
1163 protected void error(String s) { |
1103 err.println(s); |
1164 err.println(s); |
1104 } |
1165 } |
1105 |
1166 |
1106 /* |
1167 /** |
1107 * Main routine to start program. |
1168 * Main routine to start program. |
1108 */ |
1169 */ |
1109 public static void main(String args[]) { |
1170 public static void main(String args[]) { |
1110 Main jartool = new Main(System.out, System.err, "jar"); |
1171 Main jartool = new Main(System.out, System.err, "jar"); |
1111 System.exit(jartool.run(args) ? 0 : 1); |
1172 System.exit(jartool.run(args) ? 0 : 1); |
1112 } |
1173 } |
|
1174 |
|
1175 /** |
|
1176 * An OutputStream that doesn't send its output anywhere, (but could). |
|
1177 * It's here to find the CRC32 of an input file, necessary for STORED |
|
1178 * mode in ZIP. |
|
1179 */ |
|
1180 private static class CRC32OutputStream extends java.io.OutputStream { |
|
1181 final CRC32 crc = new CRC32(); |
|
1182 long n = 0; |
|
1183 |
|
1184 CRC32OutputStream() {} |
|
1185 |
|
1186 public void write(int r) throws IOException { |
|
1187 crc.update(r); |
|
1188 n++; |
|
1189 } |
|
1190 |
|
1191 public void write(byte[] b, int off, int len) throws IOException { |
|
1192 crc.update(b, off, len); |
|
1193 n += len; |
|
1194 } |
|
1195 |
|
1196 /** |
|
1197 * Updates a ZipEntry which describes the data read by this |
|
1198 * output stream, in STORED mode. |
|
1199 */ |
|
1200 public void updateEntry(ZipEntry e) { |
|
1201 e.setMethod(ZipEntry.STORED); |
|
1202 e.setSize(n); |
|
1203 e.setCrc(crc.getValue()); |
|
1204 } |
|
1205 } |
1113 } |
1206 } |
1114 |
|
1115 /* |
|
1116 * an OutputStream that doesn't send its output anywhere, (but could). |
|
1117 * It's here to find the CRC32 of a manifest, necessary for STORED only |
|
1118 * mode in ZIP. |
|
1119 */ |
|
1120 final class CRC32OutputStream extends java.io.OutputStream { |
|
1121 CRC32 crc; |
|
1122 int n = 0; |
|
1123 CRC32OutputStream(CRC32 crc) { |
|
1124 this.crc = crc; |
|
1125 } |
|
1126 |
|
1127 public void write(int r) throws IOException { |
|
1128 crc.update(r); |
|
1129 n++; |
|
1130 } |
|
1131 |
|
1132 public void write(byte[] b) throws IOException { |
|
1133 crc.update(b, 0, b.length); |
|
1134 n += b.length; |
|
1135 } |
|
1136 |
|
1137 public void write(byte[] b, int off, int len) throws IOException { |
|
1138 crc.update(b, off, len); |
|
1139 n += len - off; |
|
1140 } |
|
1141 } |
|