187 Map<Integer,Set<String>> pathsMap = new HashMap<>();
188
189 // There's also a files array per version
190 Map<Integer,String[]> filesMap = new HashMap<>();
191
192 // Do we think this is a multi-release jar? Set to true
193 // if --release option found followed by at least file
194 boolean isMultiRelease;
195
196 /*
197 * cflag: create
198 * uflag: update
199 * xflag: xtract
200 * tflag: table
201 * vflag: verbose
202 * flag0: no zip compression (store only)
203 * Mflag: DO NOT generate a manifest file (just ZIP)
204 * iflag: generate jar index
205 * nflag: Perform jar normalization at the end
206 * pflag: preserve/don't strip leading slash and .. component from file name
207 */
208 boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, nflag, pflag;
209
210 /* To support additional GNU Style informational options */
211 enum Info {
212 HELP(GNUStyleOptions::printHelp),
213 COMPAT_HELP(GNUStyleOptions::printCompatHelp),
214 USAGE_SUMMARY(GNUStyleOptions::printUsageSummary),
215 VERSION(GNUStyleOptions::printVersion);
216
217 private Consumer<PrintWriter> printFunction;
218 Info(Consumer<PrintWriter> f) { this.printFunction = f; }
219 void print(PrintWriter out) { printFunction.accept(out); }
220 };
221 Info info;
222
223 /* Modular jar related options */
224 boolean printModuleDescriptor;
225 Version moduleVersion;
226 Pattern modulesToHash;
227 ModuleFinder moduleFinder = ModuleFinder.of();
228
229 private static final String MODULE_INFO = "module-info.class";
230
231 static final String MANIFEST_DIR = "META-INF/";
232 static final String VERSIONS_DIR = MANIFEST_DIR + "versions/";
233 static final String VERSION = "1.0";
234
235 private static ResourceBundle rsrc;
236
237 /**
238 * If true, maintain compatibility with JDK releases prior to 6.0 by
239 * timestamping extracted files with the time at which they are extracted.
240 * Default is to use the time given in the archive.
241 */
242 private static final boolean useExtractionTime =
243 Boolean.getBoolean("sun.tools.jar.useExtractionTime");
244
492 // latter can handle it.
493
494 String[] files = filesMapToFiles(filesMap);
495 if (fname != null && files != null) {
496 extract(fname, files);
497 } else {
498 InputStream in = (fname == null)
499 ? new FileInputStream(FileDescriptor.in)
500 : new FileInputStream(fname);
501 try {
502 if (!extract(new BufferedInputStream(in), files) && fname != null) {
503 extract(fname, files);
504 }
505 } finally {
506 in.close();
507 }
508 }
509 } else if (iflag) {
510 String[] files = filesMap.get(BASE_VERSION); // base entries only, can be null
511 genIndex(rootjar, files);
512 } else if (printModuleDescriptor) {
513 boolean found;
514 if (fname != null) {
515 try (ZipFile zf = new ZipFile(fname)) {
516 found = printModuleDescriptor(zf);
517 }
518 } else {
519 try (FileInputStream fin = new FileInputStream(FileDescriptor.in)) {
520 found = printModuleDescriptor(fin);
521 }
522 }
523 if (!found)
524 error(getMsg("error.module.descriptor.not.found"));
525 }
526 } catch (IOException e) {
527 fatalError(e);
528 ok = false;
529 } catch (Error ee) {
530 ee.printStackTrace();
531 ok = false;
532 } catch (Throwable t) {
654 } catch (FileNotFoundException e) {
655 fatalError(formatMsg("error.cant.open", e.getMessage()));
656 return false;
657 } catch (IOException e) {
658 fatalError(e);
659 return false;
660 }
661 /* parse flags */
662 int count = 1;
663 try {
664 String flags = args[0];
665
666 // Note: flags.length == 2 can be treated as the short version of
667 // the GNU option since the there cannot be any other options,
668 // excluding -C, as per the old way.
669 if (flags.startsWith("--")
670 || (flags.startsWith("-") && flags.length() == 2)) {
671 try {
672 count = GNUStyleOptions.parseOptions(this, args);
673 } catch (GNUStyleOptions.BadArgs x) {
674 if (info != null) {
675 info.print(out);
676 return true;
677 }
678 error(x.getMessage());
679 if (x.showUsage)
680 Info.USAGE_SUMMARY.print(err);
681 return false;
682 }
683 } else {
684 // Legacy/compatibility options
685 if (flags.startsWith("-")) {
686 flags = flags.substring(1);
687 }
688 for (int i = 0; i < flags.length(); i++) {
689 switch (flags.charAt(i)) {
690 case 'c':
691 if (xflag || tflag || uflag || iflag) {
692 usageError();
693 return false;
694 }
695 cflag = true;
696 break;
697 case 'u':
698 if (cflag || xflag || tflag || iflag) {
699 usageError();
700 return false;
701 }
702 uflag = true;
703 break;
704 case 'x':
705 if (cflag || uflag || tflag || iflag) {
706 usageError();
707 return false;
708 }
709 xflag = true;
710 break;
711 case 't':
712 if (cflag || uflag || xflag || iflag) {
713 usageError();
714 return false;
715 }
716 tflag = true;
717 break;
718 case 'M':
719 Mflag = true;
720 break;
721 case 'v':
722 vflag = true;
723 break;
724 case 'f':
725 fname = args[count++];
726 break;
727 case 'm':
728 mname = args[count++];
729 break;
730 case '0':
731 flag0 = true;
732 break;
733 case 'i':
734 if (cflag || uflag || xflag || tflag) {
735 usageError();
736 return false;
737 }
738 // do not increase the counter, files will contain rootjar
739 rootjar = args[count++];
740 iflag = true;
741 break;
742 case 'n':
743 nflag = true;
744 break;
745 case 'e':
746 ename = args[count++];
747 break;
748 case 'P':
749 pflag = true;
750 break;
751 default:
752 error(formatMsg("error.illegal.option",
753 String.valueOf(flags.charAt(i))));
754 usageError();
755 return false;
756 }
757 }
758 }
759 } catch (ArrayIndexOutOfBoundsException e) {
760 usageError();
761 return false;
762 }
763
764 if (info != null) {
765 info.print(out);
766 return true;
767 }
768
769 if (!cflag && !tflag && !xflag && !uflag && !iflag && !printModuleDescriptor) {
770 error(getMsg("error.bad.option"));
771 usageError();
772 return false;
773 }
774 /* parse file arguments */
775 int n = args.length - count;
776 if (n > 0) {
777 if (printModuleDescriptor) {
778 // "--print-module-descriptor/-d" does not require file argument(s)
779 error(formatMsg("error.bad.dflag", args[count]));
780 usageError();
781 return false;
782 }
783 int version = BASE_VERSION;
784 int k = 0;
785 String[] nameBuf = new String[n];
786 pathsMap.put(version, new HashSet<>());
787 try {
788 for (int i = count; i < args.length; i++) {
789 if (args[i].equals("-C")) {
790 /* change the directory */
791 String dir = args[++i];
792 dir = (dir.endsWith(File.separator) ?
793 dir : (dir + File.separator));
794 dir = dir.replace(File.separatorChar, '/');
795 while (dir.indexOf("//") > -1) {
796 dir = dir.replace("//", "/");
797 }
798 pathsMap.get(version).add(dir.replace(File.separatorChar, '/'));
799 nameBuf[k++] = dir + args[++i];
800 } else if (args[i].startsWith("--release")) {
801 int v = BASE_VERSION;
802 try {
803 v = Integer.valueOf(args[++i]);
804 } catch (NumberFormatException x) {
805 error(formatMsg("error.release.value.notnumber", args[i]));
806 // this will fall into the next error, thus returning false
807 }
808 if (v < 9) {
809 error(formatMsg("error.release.value.toosmall", String.valueOf(v)));
810 usageError();
811 return false;
812 }
813 // associate the files, if any, with the previous version number
814 if (k > 0) {
815 String[] files = new String[k];
816 System.arraycopy(nameBuf, 0, files, 0, k);
817 filesMap.put(version, files);
818 isMultiRelease = version > BASE_VERSION;
819 }
820 // reset the counters and start with the new version number
821 k = 0;
822 nameBuf = new String[n];
823 version = v;
824 pathsMap.put(version, new HashSet<>());
825 } else {
826 nameBuf[k++] = args[i];
827 }
828 }
829 } catch (ArrayIndexOutOfBoundsException e) {
830 usageError();
831 return false;
832 }
833 // associate remaining files, if any, with a version
834 if (k > 0) {
835 String[] files = new String[k];
836 System.arraycopy(nameBuf, 0, files, 0, k);
837 filesMap.put(version, files);
838 isMultiRelease = version > BASE_VERSION;
839 }
840 } else if (cflag && (mname == null)) {
841 error(getMsg("error.bad.cflag"));
842 usageError();
843 return false;
844 } else if (uflag) {
845 if ((mname != null) || (ename != null)) {
846 /* just want to update the manifest */
847 return true;
848 } else {
849 error(getMsg("error.bad.uflag"));
850 usageError();
851 return false;
852 }
853 }
854 return true;
855 }
856
857 /*
858 * Add the package of the given resource name if it's a .class
859 * or a resource in a named package.
860 */
861 boolean addPackageIfNamed(String name) {
862 if (name.startsWith(VERSIONS_DIR)) {
863 throw new InternalError(name);
864 }
865
866 String pn = toPackageName(name);
867 // add if this is a class or resource in a package
868 if (Checks.isJavaIdentifier(pn)) {
869 packages.add(pn);
870 return true;
1337 javaVendor + ")");
1338 }
1339 }
1340
1341 private void addMainClass(Manifest m, String mainApp) {
1342 Attributes global = m.getMainAttributes();
1343
1344 // overrides any existing Main-Class attribute
1345 global.put(Attributes.Name.MAIN_CLASS, mainApp);
1346 }
1347
1348 private void addMultiRelease(Manifest m) {
1349 Attributes global = m.getMainAttributes();
1350 global.put(Attributes.Name.MULTI_RELEASE, "true");
1351 }
1352
1353 private boolean isAmbiguousMainClass(Manifest m) {
1354 if (ename != null) {
1355 Attributes global = m.getMainAttributes();
1356 if ((global.get(Attributes.Name.MAIN_CLASS) != null)) {
1357 error(getMsg("error.bad.eflag"));
1358 usageError();
1359 return true;
1360 }
1361 }
1362 return false;
1363 }
1364
1365 /**
1366 * Adds a new file entry to the ZIP output stream.
1367 */
1368 void addFile(ZipOutputStream zos, Entry entry) throws IOException {
1369 // skip the generation of directory entries for META-INF/versions/*/
1370 if (entry.basename.isEmpty()) return;
1371
1372 File file = entry.file;
1373 String name = entry.entryname;
1374 boolean isDir = entry.isDir;
1375
1376 if (name.equals("") || name.equals(".") || name.equals(zname)) {
1377 return;
1378 } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST_NAME))
1814 * Prints entry information.
1815 */
1816 void printEntry(ZipEntry e) throws IOException {
1817 if (vflag) {
1818 StringBuilder sb = new StringBuilder();
1819 String s = Long.toString(e.getSize());
1820 for (int i = 6 - s.length(); i > 0; --i) {
1821 sb.append(' ');
1822 }
1823 sb.append(s).append(' ').append(new Date(e.getTime()).toString());
1824 sb.append(' ').append(e.getName());
1825 output(sb.toString());
1826 } else {
1827 output(e.getName());
1828 }
1829 }
1830
1831 /**
1832 * Prints usage message.
1833 */
1834 void usageError() {
1835 Info.USAGE_SUMMARY.print(err);
1836 }
1837
1838 /**
1839 * A fatal exception has been caught. No recovery possible
1840 */
1841 void fatalError(Exception e) {
1842 e.printStackTrace();
1843 }
1844
1845 /**
1846 * A fatal condition has been detected; message is "s".
1847 * No recovery possible
1848 */
1849 void fatalError(String s) {
1850 error(program + ": " + s);
1851 }
1852
1853 /**
1854 * Print an output message; like verbose output and the like
1855 */
|
187 Map<Integer,Set<String>> pathsMap = new HashMap<>();
188
189 // There's also a files array per version
190 Map<Integer,String[]> filesMap = new HashMap<>();
191
192 // Do we think this is a multi-release jar? Set to true
193 // if --release option found followed by at least file
194 boolean isMultiRelease;
195
196 /*
197 * cflag: create
198 * uflag: update
199 * xflag: xtract
200 * tflag: table
201 * vflag: verbose
202 * flag0: no zip compression (store only)
203 * Mflag: DO NOT generate a manifest file (just ZIP)
204 * iflag: generate jar index
205 * nflag: Perform jar normalization at the end
206 * pflag: preserve/don't strip leading slash and .. component from file name
207 * dflag: print module descriptor
208 */
209 boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, nflag, pflag, dflag;
210
211 /* To support additional GNU Style informational options */
212 enum Info {
213 HELP(GNUStyleOptions::printHelp),
214 COMPAT_HELP(GNUStyleOptions::printCompatHelp),
215 USAGE_TRYHELP(GNUStyleOptions::printUsageTryHelp),
216 VERSION(GNUStyleOptions::printVersion);
217
218 private Consumer<PrintWriter> printFunction;
219 Info(Consumer<PrintWriter> f) { this.printFunction = f; }
220 void print(PrintWriter out) { printFunction.accept(out); }
221 };
222 Info info;
223
224
225 /* Modular jar related options */
226 Version moduleVersion;
227 Pattern modulesToHash;
228 ModuleFinder moduleFinder = ModuleFinder.of();
229
230 private static final String MODULE_INFO = "module-info.class";
231
232 static final String MANIFEST_DIR = "META-INF/";
233 static final String VERSIONS_DIR = MANIFEST_DIR + "versions/";
234 static final String VERSION = "1.0";
235
236 private static ResourceBundle rsrc;
237
238 /**
239 * If true, maintain compatibility with JDK releases prior to 6.0 by
240 * timestamping extracted files with the time at which they are extracted.
241 * Default is to use the time given in the archive.
242 */
243 private static final boolean useExtractionTime =
244 Boolean.getBoolean("sun.tools.jar.useExtractionTime");
245
493 // latter can handle it.
494
495 String[] files = filesMapToFiles(filesMap);
496 if (fname != null && files != null) {
497 extract(fname, files);
498 } else {
499 InputStream in = (fname == null)
500 ? new FileInputStream(FileDescriptor.in)
501 : new FileInputStream(fname);
502 try {
503 if (!extract(new BufferedInputStream(in), files) && fname != null) {
504 extract(fname, files);
505 }
506 } finally {
507 in.close();
508 }
509 }
510 } else if (iflag) {
511 String[] files = filesMap.get(BASE_VERSION); // base entries only, can be null
512 genIndex(rootjar, files);
513 } else if (dflag) {
514 boolean found;
515 if (fname != null) {
516 try (ZipFile zf = new ZipFile(fname)) {
517 found = printModuleDescriptor(zf);
518 }
519 } else {
520 try (FileInputStream fin = new FileInputStream(FileDescriptor.in)) {
521 found = printModuleDescriptor(fin);
522 }
523 }
524 if (!found)
525 error(getMsg("error.module.descriptor.not.found"));
526 }
527 } catch (IOException e) {
528 fatalError(e);
529 ok = false;
530 } catch (Error ee) {
531 ee.printStackTrace();
532 ok = false;
533 } catch (Throwable t) {
655 } catch (FileNotFoundException e) {
656 fatalError(formatMsg("error.cant.open", e.getMessage()));
657 return false;
658 } catch (IOException e) {
659 fatalError(e);
660 return false;
661 }
662 /* parse flags */
663 int count = 1;
664 try {
665 String flags = args[0];
666
667 // Note: flags.length == 2 can be treated as the short version of
668 // the GNU option since the there cannot be any other options,
669 // excluding -C, as per the old way.
670 if (flags.startsWith("--")
671 || (flags.startsWith("-") && flags.length() == 2)) {
672 try {
673 count = GNUStyleOptions.parseOptions(this, args);
674 } catch (GNUStyleOptions.BadArgs x) {
675 if (info == null) {
676 error(x.getMessage());
677 if (x.showUsage)
678 Info.USAGE_TRYHELP.print(err);
679 return false;
680 }
681 }
682 if (info != null) {
683 info.print(out);
684 return true;
685 }
686 } else {
687 // Legacy/compatibility options
688 if (flags.startsWith("-")) {
689 flags = flags.substring(1);
690 }
691 for (int i = 0; i < flags.length(); i++) {
692 switch (flags.charAt(i)) {
693 case 'c':
694 if (xflag || tflag || uflag || iflag) {
695 usageError(getMsg("error.multiple.main.operations"));
696 return false;
697 }
698 cflag = true;
699 break;
700 case 'u':
701 if (cflag || xflag || tflag || iflag) {
702 usageError(getMsg("error.multiple.main.operations"));
703 return false;
704 }
705 uflag = true;
706 break;
707 case 'x':
708 if (cflag || uflag || tflag || iflag) {
709 usageError(getMsg("error.multiple.main.operations"));
710 return false;
711 }
712 xflag = true;
713 break;
714 case 't':
715 if (cflag || uflag || xflag || iflag) {
716 usageError(getMsg("error.multiple.main.operations"));
717 return false;
718 }
719 tflag = true;
720 break;
721 case 'M':
722 Mflag = true;
723 break;
724 case 'v':
725 vflag = true;
726 break;
727 case 'f':
728 fname = args[count++];
729 break;
730 case 'm':
731 mname = args[count++];
732 break;
733 case '0':
734 flag0 = true;
735 break;
736 case 'i':
737 if (cflag || uflag || xflag || tflag) {
738 usageError(getMsg("error.multiple.main.operations"));
739 return false;
740 }
741 // do not increase the counter, files will contain rootjar
742 rootjar = args[count++];
743 iflag = true;
744 break;
745 case 'n':
746 nflag = true;
747 break;
748 case 'e':
749 ename = args[count++];
750 break;
751 case 'P':
752 pflag = true;
753 break;
754 default:
755 usageError(formatMsg("error.illegal.option",
756 String.valueOf(flags.charAt(i))));
757 return false;
758 }
759 }
760 }
761 } catch (ArrayIndexOutOfBoundsException e) {
762 usageError(getMsg("main.usage.summary"));
763 return false;
764 }
765 if (!cflag && !tflag && !xflag && !uflag && !iflag && !dflag) {
766 usageError(getMsg("error.bad.option"));
767 return false;
768 }
769
770 /* parse file arguments */
771 int n = args.length - count;
772 if (n > 0) {
773 if (dflag) {
774 // "--print-module-descriptor/-d" does not require file argument(s)
775 usageError(formatMsg("error.bad.dflag", args[count]));
776 return false;
777 }
778 int version = BASE_VERSION;
779 int k = 0;
780 String[] nameBuf = new String[n];
781 pathsMap.put(version, new HashSet<>());
782 try {
783 for (int i = count; i < args.length; i++) {
784 if (args[i].equals("-C")) {
785 /* change the directory */
786 String dir = args[++i];
787 dir = (dir.endsWith(File.separator) ?
788 dir : (dir + File.separator));
789 dir = dir.replace(File.separatorChar, '/');
790 while (dir.indexOf("//") > -1) {
791 dir = dir.replace("//", "/");
792 }
793 pathsMap.get(version).add(dir.replace(File.separatorChar, '/'));
794 nameBuf[k++] = dir + args[++i];
795 } else if (args[i].startsWith("--release")) {
796 int v = BASE_VERSION;
797 try {
798 v = Integer.valueOf(args[++i]);
799 } catch (NumberFormatException x) {
800 error(formatMsg("error.release.value.notnumber", args[i]));
801 // this will fall into the next error, thus returning false
802 }
803 if (v < 9) {
804 usageError(formatMsg("error.release.value.toosmall", String.valueOf(v)));
805 return false;
806 }
807 // associate the files, if any, with the previous version number
808 if (k > 0) {
809 String[] files = new String[k];
810 System.arraycopy(nameBuf, 0, files, 0, k);
811 filesMap.put(version, files);
812 isMultiRelease = version > BASE_VERSION;
813 }
814 // reset the counters and start with the new version number
815 k = 0;
816 nameBuf = new String[n];
817 version = v;
818 pathsMap.put(version, new HashSet<>());
819 } else {
820 nameBuf[k++] = args[i];
821 }
822 }
823 } catch (ArrayIndexOutOfBoundsException e) {
824 usageError(getMsg("error.bad.file.arg"));
825 return false;
826 }
827 // associate remaining files, if any, with a version
828 if (k > 0) {
829 String[] files = new String[k];
830 System.arraycopy(nameBuf, 0, files, 0, k);
831 filesMap.put(version, files);
832 isMultiRelease = version > BASE_VERSION;
833 }
834 } else if (cflag && (mname == null)) {
835 usageError(getMsg("error.bad.cflag"));
836 return false;
837 } else if (uflag) {
838 if ((mname != null) || (ename != null)) {
839 /* just want to update the manifest */
840 return true;
841 } else {
842 usageError(getMsg("error.bad.uflag"));
843 return false;
844 }
845 }
846 return true;
847 }
848
849 /*
850 * Add the package of the given resource name if it's a .class
851 * or a resource in a named package.
852 */
853 boolean addPackageIfNamed(String name) {
854 if (name.startsWith(VERSIONS_DIR)) {
855 throw new InternalError(name);
856 }
857
858 String pn = toPackageName(name);
859 // add if this is a class or resource in a package
860 if (Checks.isJavaIdentifier(pn)) {
861 packages.add(pn);
862 return true;
1329 javaVendor + ")");
1330 }
1331 }
1332
1333 private void addMainClass(Manifest m, String mainApp) {
1334 Attributes global = m.getMainAttributes();
1335
1336 // overrides any existing Main-Class attribute
1337 global.put(Attributes.Name.MAIN_CLASS, mainApp);
1338 }
1339
1340 private void addMultiRelease(Manifest m) {
1341 Attributes global = m.getMainAttributes();
1342 global.put(Attributes.Name.MULTI_RELEASE, "true");
1343 }
1344
1345 private boolean isAmbiguousMainClass(Manifest m) {
1346 if (ename != null) {
1347 Attributes global = m.getMainAttributes();
1348 if ((global.get(Attributes.Name.MAIN_CLASS) != null)) {
1349 usageError(getMsg("error.bad.eflag"));
1350 return true;
1351 }
1352 }
1353 return false;
1354 }
1355
1356 /**
1357 * Adds a new file entry to the ZIP output stream.
1358 */
1359 void addFile(ZipOutputStream zos, Entry entry) throws IOException {
1360 // skip the generation of directory entries for META-INF/versions/*/
1361 if (entry.basename.isEmpty()) return;
1362
1363 File file = entry.file;
1364 String name = entry.entryname;
1365 boolean isDir = entry.isDir;
1366
1367 if (name.equals("") || name.equals(".") || name.equals(zname)) {
1368 return;
1369 } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST_NAME))
1805 * Prints entry information.
1806 */
1807 void printEntry(ZipEntry e) throws IOException {
1808 if (vflag) {
1809 StringBuilder sb = new StringBuilder();
1810 String s = Long.toString(e.getSize());
1811 for (int i = 6 - s.length(); i > 0; --i) {
1812 sb.append(' ');
1813 }
1814 sb.append(s).append(' ').append(new Date(e.getTime()).toString());
1815 sb.append(' ').append(e.getName());
1816 output(sb.toString());
1817 } else {
1818 output(e.getName());
1819 }
1820 }
1821
1822 /**
1823 * Prints usage message.
1824 */
1825 void usageError(String s) {
1826 err.println(s);
1827 Info.USAGE_TRYHELP.print(err);
1828 }
1829
1830 /**
1831 * A fatal exception has been caught. No recovery possible
1832 */
1833 void fatalError(Exception e) {
1834 e.printStackTrace();
1835 }
1836
1837 /**
1838 * A fatal condition has been detected; message is "s".
1839 * No recovery possible
1840 */
1841 void fatalError(String s) {
1842 error(program + ": " + s);
1843 }
1844
1845 /**
1846 * Print an output message; like verbose output and the like
1847 */
|