jdk/src/share/classes/sun/tools/jar/Main.java
changeset 3218 855a6257b729
parent 3056 0864d307b376
child 3288 db82a42da273
equal deleted inserted replaced
3217:07b65d4b6227 3218:855a6257b729
    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.
    56     Set<File> entries = new LinkedHashSet<File>();
    60     Set<File> entries = new LinkedHashSet<File>();
    57 
    61 
    58     // Directories specified by "-C" operation.
    62     // Directories specified by "-C" operation.
    59     Set<String> paths = new HashSet<String>();
    63     Set<String> paths = new HashSet<String>();
    60 
    64 
    61     CRC32 crc32 = new CRC32();
       
    62     /*
    65     /*
    63      * cflag: create
    66      * cflag: create
    64      * uflag: update
    67      * uflag: update
    65      * xflag: xtract
    68      * xflag: xtract
    66      * tflag: table
    69      * tflag: table
    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
   124         this.out = out;
   125         this.out = out;
   125         this.err = err;
   126         this.err = err;
   126         this.program = program;
   127         this.program = program;
   127     }
   128     }
   128 
   129 
       
   130     /**
       
   131      * Creates a new empty temporary file in the same directory as the
       
   132      * specified file.  A variant of File.createTempFile.
       
   133      */
       
   134     private static File createTempFileInSameDirectoryAs(File file)
       
   135         throws IOException {
       
   136         File dir = file.getParentFile();
       
   137         if (dir == null)
       
   138             dir = new File(".");
       
   139         return File.createTempFile("jartmp", null, dir);
       
   140     }
       
   141 
   129     private boolean ok;
   142     private boolean ok;
   130 
   143 
   131     /*
   144     /**
   132      * Starts main program with the specified arguments.
   145      * Starts main program with the specified arguments.
   133      */
   146      */
   134     public synchronized boolean run(String args[]) {
   147     public synchronized boolean run(String args[]) {
   135         ok = true;
   148         ok = true;
   136         if (!parseArgs(args)) {
   149         if (!parseArgs(args)) {
   159                     } else {
   172                     } else {
   160                         manifest = new Manifest();
   173                         manifest = new Manifest();
   161                     }
   174                     }
   162                     addVersion(manifest);
   175                     addVersion(manifest);
   163                     addCreatedBy(manifest);
   176                     addCreatedBy(manifest);
   164                     if (isAmbigousMainClass(manifest)) {
   177                     if (isAmbiguousMainClass(manifest)) {
   165                         if (in != null) {
   178                         if (in != null) {
   166                             in.close();
   179                             in.close();
   167                         }
   180                         }
   168                         return false;
   181                         return false;
   169                     }
   182                     }
   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();
   268         out.flush();
   280         out.flush();
   269         err.flush();
   281         err.flush();
   270         return ok;
   282         return ok;
   271     }
   283     }
   272 
   284 
   273     /*
   285     /**
   274      * Parse command line arguments.
   286      * Parses command line arguments.
   275      */
   287      */
   276     boolean parseArgs(String args[]) {
   288     boolean parseArgs(String args[]) {
   277         /* Preprocess and expand @file arguments */
   289         /* Preprocess and expand @file arguments */
   278         try {
   290         try {
   279             args = CommandLine.parse(args);
   291             args = CommandLine.parse(args);
   403             }
   415             }
   404         }
   416         }
   405         return true;
   417         return true;
   406     }
   418     }
   407 
   419 
   408     /*
   420     /**
   409      * Expands list of files to process into full list of all files that
   421      * Expands list of files to process into full list of all files that
   410      * can be found by recursively descending directories.
   422      * can be found by recursively descending directories.
   411      */
   423      */
   412     void expand(File dir, String[] files, boolean isUpdate) {
   424     void expand(File dir, String[] files, boolean isUpdate) {
   413         if (files == null) {
   425         if (files == null) {
   440                 ok = false;
   452                 ok = false;
   441             }
   453             }
   442         }
   454         }
   443     }
   455     }
   444 
   456 
   445     /*
   457     /**
   446      * Creates a new JAR file.
   458      * Creates a new JAR file.
   447      */
   459      */
   448     void create(OutputStream out, Manifest manifest)
   460     void create(OutputStream out, Manifest manifest)
   449         throws IOException
   461         throws IOException
   450     {
   462     {
   459             ZipEntry e = new ZipEntry(MANIFEST_DIR);
   471             ZipEntry e = new ZipEntry(MANIFEST_DIR);
   460             e.setTime(System.currentTimeMillis());
   472             e.setTime(System.currentTimeMillis());
   461             e.setSize(0);
   473             e.setSize(0);
   462             e.setCrc(0);
   474             e.setCrc(0);
   463             zos.putNextEntry(e);
   475             zos.putNextEntry(e);
   464             e = new ZipEntry(MANIFEST);
   476             e = new ZipEntry(MANIFEST_NAME);
   465             e.setTime(System.currentTimeMillis());
   477             e.setTime(System.currentTimeMillis());
   466             if (flag0) {
   478             if (flag0) {
   467                 crc32Manifest(e, manifest);
   479                 crc32Manifest(e, manifest);
   468             }
   480             }
   469             zos.putNextEntry(e);
   481             zos.putNextEntry(e);
   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);
   556             addFile(zos, f);
   587             addFile(zos, f);
   557         }
   588         }
   558         if (!foundManifest) {
   589         if (!foundManifest) {
   559             if (newManifest != null) {
   590             if (newManifest != null) {
   560                 Manifest m = new Manifest(newManifest);
   591                 Manifest m = new Manifest(newManifest);
   561                 updateOk = !isAmbigousMainClass(m);
   592                 updateOk = !isAmbiguousMainClass(m);
   562                 if (updateOk) {
   593                 if (updateOk) {
   563                     updateManifest(m, zos);
   594                     updateManifest(m, zos);
   564                 }
   595                 }
   565             } else if (ename != null) {
   596             } else if (ename != null) {
   566                 updateManifest(new Manifest(), zos);
   597                 updateManifest(new Manifest(), zos);
   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) {
   618 
   641 
   619     private String entryName(String name) {
   642     private String entryName(String name) {
   620         name = name.replace(File.separatorChar, '/');
   643         name = name.replace(File.separatorChar, '/');
   621         String matchPath = "";
   644         String matchPath = "";
   622         for (String path : paths) {
   645         for (String path : paths) {
   623             if (name.startsWith(path) && (path.length() > matchPath.length())) {
   646             if (name.startsWith(path)
       
   647                 && (path.length() > matchPath.length())) {
   624                 matchPath = path;
   648                 matchPath = path;
   625             }
   649             }
   626         }
   650         }
   627         name = name.substring(matchPath.length());
   651         name = name.substring(matchPath.length());
   628 
   652 
   656 
   680 
   657         // overrides any existing Main-Class attribute
   681         // overrides any existing Main-Class attribute
   658         global.put(Attributes.Name.MAIN_CLASS, mainApp);
   682         global.put(Attributes.Name.MAIN_CLASS, mainApp);
   659     }
   683     }
   660 
   684 
   661     private boolean isAmbigousMainClass(Manifest m) {
   685     private boolean isAmbiguousMainClass(Manifest m) {
   662         if (ename != null) {
   686         if (ename != null) {
   663             Attributes global = m.getMainAttributes();
   687             Attributes global = m.getMainAttributes();
   664             if ((global.get(Attributes.Name.MAIN_CLASS) != null)) {
   688             if ((global.get(Attributes.Name.MAIN_CLASS) != null)) {
   665                 error(getMsg("error.bad.eflag"));
   689                 error(getMsg("error.bad.eflag"));
   666                 usageError();
   690                 usageError();
   668             }
   692             }
   669         }
   693         }
   670         return false;
   694         return false;
   671     }
   695     }
   672 
   696 
   673     /*
   697     /**
   674      * Adds a new file entry to the ZIP output stream.
   698      * Adds a new file entry to the ZIP output stream.
   675      */
   699      */
   676     void addFile(ZipOutputStream zos, File file) throws IOException {
   700     void addFile(ZipOutputStream zos, File file) throws IOException {
   677         String name = file.getPath();
   701         String name = file.getPath();
   678         boolean isDir = file.isDirectory();
   702         boolean isDir = file.isDirectory();
   682         }
   706         }
   683         name = entryName(name);
   707         name = entryName(name);
   684 
   708 
   685         if (name.equals("") || name.equals(".") || name.equals(zname)) {
   709         if (name.equals("") || name.equals(".") || name.equals(zname)) {
   686             return;
   710             return;
   687         } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST))
   711         } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST_NAME))
   688                    && !Mflag) {
   712                    && !Mflag) {
   689             if (vflag) {
   713             if (vflag) {
   690                 output(formatMsg("out.ignore.entry", name));
   714                 output(formatMsg("out.ignore.entry", name));
   691             }
   715             }
   692             return;
   716             return;
   702         if (size == 0) {
   726         if (size == 0) {
   703             e.setMethod(ZipEntry.STORED);
   727             e.setMethod(ZipEntry.STORED);
   704             e.setSize(0);
   728             e.setSize(0);
   705             e.setCrc(0);
   729             e.setCrc(0);
   706         } else if (flag0) {
   730         } else if (flag0) {
   707             e.setSize(size);
       
   708             e.setMethod(ZipEntry.STORED);
       
   709             crc32File(e, file);
   731             crc32File(e, file);
   710         }
   732         }
   711         zos.putNextEntry(e);
   733         zos.putNextEntry(e);
   712         if (!isDir) {
   734         if (!isDir) {
   713             byte[] buf = new byte[8192];
   735             copy(file, zos);
   714             int len;
       
   715             InputStream is = new BufferedInputStream(new FileInputStream(file));
       
   716             while ((len = is.read(buf, 0, buf.length)) != -1) {
       
   717                 zos.write(buf, 0, len);
       
   718             }
       
   719             is.close();
       
   720         }
   736         }
   721         zos.closeEntry();
   737         zos.closeEntry();
   722         /* report how much compression occurred. */
   738         /* report how much compression occurred. */
   723         if (vflag) {
   739         if (vflag) {
   724             size = e.getSize();
   740             size = e.getSize();
   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             }};
   795                 f.setLastModified(lastModified);
   856                 f.setLastModified(lastModified);
   796             }
   857             }
   797         }
   858         }
   798     }
   859     }
   799 
   860 
   800     /*
   861     /**
   801      * Extracts specified entries from JAR file.
   862      * Extracts specified entries from JAR file.
   802      */
   863      */
   803     void extract(InputStream in, String files[]) throws IOException {
   864     void extract(InputStream in, String files[]) throws IOException {
   804         ZipInputStream zis = new ZipInputStream(in);
   865         ZipInputStream zis = new ZipInputStream(in);
   805         ZipEntry e;
   866         ZipEntry e;
   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();
   851         }
   912         }
   852         zf.close();
   913         zf.close();
   853         updateLastModifiedTime(dirs);
   914         updateLastModifiedTime(dirs);
   854     }
   915     }
   855 
   916 
   856     /*
   917     /**
   857      * Extracts next entry from JAR file, creating directories as needed.  If
   918      * Extracts next entry from JAR file, creating directories as needed.  If
   858      * the entry is for a directory which doesn't exist prior to this
   919      * the entry is for a directory which doesn't exist prior to this
   859      * invocation, returns that entry, otherwise returns null.
   920      * invocation, returns that entry, otherwise returns null.
   860      */
   921      */
   861     ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException {
   922     ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException {
   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 {
   917             }
   972             }
   918         }
   973         }
   919         return rc;
   974         return rc;
   920     }
   975     }
   921 
   976 
   922     /*
   977     /**
   923      * Lists contents of JAR file.
   978      * Lists contents of JAR file.
   924      */
   979      */
   925     void list(InputStream in, String files[]) throws IOException {
   980     void list(InputStream in, String files[]) throws IOException {
   926         ZipInputStream zis = new ZipInputStream(in);
   981         ZipInputStream zis = new ZipInputStream(in);
   927         ZipEntry e;
   982         ZipEntry e;
   935             zis.closeEntry();
   990             zis.closeEntry();
   936             printEntry(e, files);
   991             printEntry(e, files);
   937         }
   992         }
   938     }
   993     }
   939 
   994 
   940     /*
   995     /**
   941      * Lists contents of JAR file, via ZipFile.
   996      * Lists contents of JAR file, via ZipFile.
   942      */
   997      */
   943     void list(String fname, String files[]) throws IOException {
   998     void list(String fname, String files[]) throws IOException {
   944         ZipFile zf = new ZipFile(fname);
   999         ZipFile zf = new ZipFile(fname);
   945         Enumeration<? extends ZipEntry> zes = zf.entries();
  1000         Enumeration<? extends ZipEntry> zes = zf.entries();
   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
   996                         while (st.hasMoreTokens()) {
  1057                         while (st.hasMoreTokens()) {
   997                             String ajar = st.nextToken();
  1058                             String ajar = st.nextToken();
   998                             if (!ajar.endsWith("/")) {  // it is a jar file
  1059                             if (!ajar.endsWith("/")) {  // it is a jar file
   999                                 ajar = path.concat(ajar);
  1060                                 ajar = path.concat(ajar);
  1000                                 /* check on cyclic dependency */
  1061                                 /* check on cyclic dependency */
  1001                                 if (jarTable.get(ajar) == null) {
  1062                                 if (! jarPaths.contains(ajar)) {
  1002                                     files.addAll(getJarPath(ajar));
  1063                                     files.addAll(getJarPath(ajar));
  1003                                 }
  1064                                 }
  1004                             }
  1065                             }
  1005                         }
  1066                         }
  1006                     }
  1067                     }
  1010         rf.close();
  1071         rf.close();
  1011         return files;
  1072         return files;
  1012     }
  1073     }
  1013 
  1074 
  1014     /**
  1075     /**
  1015      * Generate class index file for the specified root jar file.
  1076      * Generates class index file for the specified root jar file.
  1016      */
  1077      */
  1017     void genIndex(String rootjar, String[] files) throws IOException {
  1078     void genIndex(String rootjar, String[] files) throws IOException {
  1018         Vector jars = getJarPath(rootjar);
  1079         List<String> jars = getJarPath(rootjar);
  1019         int njars = jars.size();
  1080         int njars = jars.size();
  1020         String[] jarfiles;
  1081         String[] jarfiles;
  1021 
  1082 
  1022         if (njars == 1 && files != null) {
  1083         if (njars == 1 && files != null) {
  1023             // no class-path attribute defined in rootjar, will
  1084             // no class-path attribute defined in rootjar, will
  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);
  1047                 }
  1108                 }
  1048             }
  1109             }
  1049         }
  1110         }
  1050     }
  1111     }
  1051 
  1112 
  1052     /*
  1113     /**
  1053      * Prints entry information.
  1114      * Prints entry information.
  1054      */
  1115      */
  1055     void printEntry(ZipEntry e) throws IOException {
  1116     void printEntry(ZipEntry e) throws IOException {
  1056         if (vflag) {
  1117         if (vflag) {
  1057             StringBuilder sb = new StringBuilder();
  1118             StringBuilder sb = new StringBuilder();
  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 }