Save This Page
Home » openjdk-7 » sun.tools » jar » [javadoc | source]
    1   /*
    2    * Copyright 1996-2007 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package sun.tools.jar;
   27   
   28   import java.io;
   29   import java.util;
   30   import java.util.zip;
   31   import java.util.jar;
   32   import java.util.jar.Manifest;
   33   import java.text.MessageFormat;
   34   import sun.misc.JarIndex;
   35   
   36   /**
   37    * 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
   39    * format, with optional meta-information stored in a MANIFEST entry.
   40    */
   41   public
   42   class Main {
   43       String program;
   44       PrintStream out, err;
   45       String fname, mname, ename;
   46       String zname = "";
   47       String[] files;
   48       String rootjar = null;
   49       Hashtable filesTable = new Hashtable();
   50       Vector paths = new Vector();
   51       Vector v;
   52       CRC32 crc32 = new CRC32();
   53       /*
   54        * cflag: create
   55        * uflag: update
   56        * xflag: xtract
   57        * tflag: table
   58        * vflag: verbose
   59        * flag0: no zip compression (store only)
   60        * Mflag: DO NOT generate a manifest file (just ZIP)
   61        * iflag: generate jar index
   62        */
   63       boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag;
   64   
   65       static final String MANIFEST = JarFile.MANIFEST_NAME;
   66       static final String MANIFEST_DIR = "META-INF/";
   67       static final String VERSION = "1.0";
   68       static final char SEPARATOR = File.separatorChar;
   69       static final String INDEX = JarIndex.INDEX_NAME;
   70   
   71       private static ResourceBundle rsrc;
   72   
   73       /**
   74        * If true, maintain compatibility with JDK releases prior to 6.0 by
   75        * timestamping extracted files with the time at which they are extracted.
   76        * Default is to use the time given in the archive.
   77        */
   78       private static final boolean useExtractionTime =
   79           Boolean.getBoolean("sun.tools.jar.useExtractionTime");
   80   
   81       /**
   82        * Initialize ResourceBundle
   83        */
   84       static {
   85           try {
   86               rsrc = ResourceBundle.getBundle("sun.tools.jar.resources.jar");
   87           } catch (MissingResourceException e) {
   88               throw new Error("Fatal: Resource for jar is missing");
   89           }
   90       }
   91   
   92       private String getMsg(String key) {
   93           try {
   94               return (rsrc.getString(key));
   95           } catch (MissingResourceException e) {
   96               throw new Error("Error in message file");
   97           }
   98       }
   99   
  100       private String formatMsg(String key, String arg) {
  101           String msg = getMsg(key);
  102           String[] args = new String[1];
  103           args[0] = arg;
  104           return MessageFormat.format(msg, (Object[]) args);
  105       }
  106   
  107       private String formatMsg2(String key, String arg, String arg1) {
  108           String msg = getMsg(key);
  109           String[] args = new String[2];
  110           args[0] = arg;
  111           args[1] = arg1;
  112           return MessageFormat.format(msg, (Object[]) args);
  113       }
  114   
  115       public Main(PrintStream out, PrintStream err, String program) {
  116           this.out = out;
  117           this.err = err;
  118           this.program = program;
  119       }
  120   
  121       private boolean ok;
  122   
  123       /*
  124        * Starts main program with the specified arguments.
  125        */
  126       public synchronized boolean run(String args[]) {
  127           ok = true;
  128           if (!parseArgs(args)) {
  129               return false;
  130           }
  131           try {
  132               if (cflag || uflag) {
  133                   if (fname != null) {
  134                       // The name of the zip file as it would appear as its own
  135                       // zip file entry. We use this to make sure that we don't
  136                       // add the zip file to itself.
  137                       zname = fname.replace(File.separatorChar, '/');
  138                       if (zname.startsWith("./")) {
  139                           zname = zname.substring(2);
  140                       }
  141                   }
  142               }
  143               if (cflag) {
  144                   Manifest manifest = null;
  145                   InputStream in = null;
  146   
  147                   if (!Mflag) {
  148                       if (mname != null) {
  149                           in = new FileInputStream(mname);
  150                           manifest = new Manifest(new BufferedInputStream(in));
  151                       } else {
  152                           manifest = new Manifest();
  153                       }
  154                       addVersion(manifest);
  155                       addCreatedBy(manifest);
  156                       if (isAmbigousMainClass(manifest)) {
  157                           if (in != null) {
  158                               in.close();
  159                           }
  160                           return false;
  161                       }
  162                       if (ename != null) {
  163                           addMainClass(manifest, ename);
  164                       }
  165                   }
  166                   OutputStream out;
  167                   if (fname != null) {
  168                       out = new FileOutputStream(fname);
  169                   } else {
  170                       out = new FileOutputStream(FileDescriptor.out);
  171                       if (vflag) {
  172                           // Disable verbose output so that it does not appear
  173                           // on stdout along with file data
  174                           // error("Warning: -v option ignored");
  175                           vflag = false;
  176                       }
  177                   }
  178                   create(new BufferedOutputStream(out), expand(files), manifest);
  179                   if (in != null) {
  180                       in.close();
  181                   }
  182                   out.close();
  183               } else if (uflag) {
  184                   File inputFile = null, tmpFile = null;
  185                   FileInputStream in;
  186                   FileOutputStream out;
  187                   if (fname != null) {
  188                       inputFile = new File(fname);
  189                       String path = inputFile.getParent();
  190                       tmpFile = File.createTempFile("tmp", null,
  191                                 new File((path == null) ? "." : path));
  192                       in = new FileInputStream(inputFile);
  193                       out = new FileOutputStream(tmpFile);
  194                   } else {
  195                       in = new FileInputStream(FileDescriptor.in);
  196                       out = new FileOutputStream(FileDescriptor.out);
  197                       vflag = false;
  198                   }
  199                   InputStream manifest = (!Mflag && (mname != null)) ?
  200                       (new FileInputStream(mname)) : null;
  201                   expand(files);
  202                   boolean updateOk = update(in, new BufferedOutputStream(out), manifest);
  203                   if (ok) {
  204                       ok = updateOk;
  205                   }
  206                   in.close();
  207                   out.close();
  208                   if (manifest != null) {
  209                       manifest.close();
  210                   }
  211                   if (fname != null) {
  212                       // on Win32, we need this delete
  213                       inputFile.delete();
  214                       if (!tmpFile.renameTo(inputFile)) {
  215                           tmpFile.delete();
  216                           throw new IOException(getMsg("error.write.file"));
  217                       }
  218                       tmpFile.delete();
  219                   }
  220               } else if (xflag || tflag) {
  221                   InputStream in;
  222                   if (fname != null) {
  223                       in = new FileInputStream(fname);
  224                   } else {
  225                       in = new FileInputStream(FileDescriptor.in);
  226                   }
  227                   if (xflag) {
  228                       extract(new BufferedInputStream(in), files);
  229                   } else {
  230                       list(new BufferedInputStream(in), files);
  231                   }
  232                   in.close();
  233               } else if (iflag) {
  234                   genIndex(rootjar, files);
  235               }
  236           } catch (IOException e) {
  237               fatalError(e);
  238               ok = false;
  239           } catch (Error ee) {
  240               ee.printStackTrace();
  241               ok = false;
  242           } catch (Throwable t) {
  243               t.printStackTrace();
  244               ok = false;
  245           }
  246           out.flush();
  247           err.flush();
  248           return ok;
  249       }
  250   
  251       /*
  252        * Parse command line arguments.
  253        */
  254       boolean parseArgs(String args[]) {
  255           /* Preprocess and expand @file arguments */
  256           try {
  257               args = CommandLine.parse(args);
  258           } catch (FileNotFoundException e) {
  259               fatalError(formatMsg("error.cant.open", e.getMessage()));
  260               return false;
  261           } catch (IOException e) {
  262               fatalError(e);
  263               return false;
  264           }
  265           /* parse flags */
  266           int count = 1;
  267           try {
  268               String flags = args[0];
  269               if (flags.startsWith("-")) {
  270                   flags = flags.substring(1);
  271               }
  272               for (int i = 0; i < flags.length(); i++) {
  273                   switch (flags.charAt(i)) {
  274                   case 'c':
  275                       if (xflag || tflag || uflag) {
  276                           usageError();
  277                           return false;
  278                       }
  279                       cflag = true;
  280                       break;
  281                   case 'u':
  282                       if (cflag || xflag || tflag) {
  283                           usageError();
  284                           return false;
  285                       }
  286                       uflag = true;
  287                       break;
  288                   case 'x':
  289                       if (cflag || uflag || tflag) {
  290                           usageError();
  291                           return false;
  292                       }
  293                       xflag = true;
  294                       break;
  295                   case 't':
  296                       if (cflag || uflag || xflag) {
  297                           usageError();
  298                           return false;
  299                       }
  300                       tflag = true;
  301                       break;
  302                   case 'M':
  303                       Mflag = true;
  304                       break;
  305                   case 'v':
  306                       vflag = true;
  307                       break;
  308                   case 'f':
  309                       fname = args[count++];
  310                       break;
  311                   case 'm':
  312                       mname = args[count++];
  313                       break;
  314                   case '0':
  315                       flag0 = true;
  316                       break;
  317                   case 'i':
  318                       // do not increase the counter, files will contain rootjar
  319                       rootjar = args[count++];
  320                       iflag = true;
  321                       break;
  322                   case 'e':
  323                        ename = args[count++];
  324                        break;
  325                   default:
  326                       error(formatMsg("error.illegal.option",
  327                                   String.valueOf(flags.charAt(i))));
  328                       usageError();
  329                       return false;
  330                   }
  331               }
  332           } catch (ArrayIndexOutOfBoundsException e) {
  333               usageError();
  334               return false;
  335           }
  336           if (!cflag && !tflag && !xflag && !uflag && !iflag) {
  337               error(getMsg("error.bad.option"));
  338               usageError();
  339               return false;
  340           }
  341           /* parse file arguments */
  342           int n = args.length - count;
  343           if (n > 0) {
  344               int k = 0;
  345               String[] nameBuf = new String[n];
  346               try {
  347                   for (int i = count; i < args.length; i++) {
  348                       if (args[i].equals("-C")) {
  349                           /* change the directory */
  350                           String dir = args[++i];
  351                           dir = (dir.endsWith(File.separator) ?
  352                                  dir : (dir + File.separator));
  353                           dir = dir.replace(File.separatorChar, '/');
  354                           while (dir.indexOf("//") > -1) {
  355                               dir = dir.replace("//", "/");
  356                           }
  357                           paths.addElement(dir.replace(File.separatorChar, '/'));
  358                           nameBuf[k++] = dir + args[++i];
  359                       } else {
  360                           nameBuf[k++] = args[i];
  361                       }
  362                   }
  363               } catch (ArrayIndexOutOfBoundsException e) {
  364                   usageError();
  365                   return false;
  366               }
  367               files = new String[k];
  368               System.arraycopy(nameBuf, 0, files, 0, k);
  369           } else if (cflag && (mname == null)) {
  370               error(getMsg("error.bad.cflag"));
  371               usageError();
  372               return false;
  373           } else if (uflag) {
  374               if ((mname != null) || (ename != null)) {
  375                   /* just want to update the manifest */
  376                   return true;
  377               } else {
  378                   error(getMsg("error.bad.uflag"));
  379                   usageError();
  380                   return false;
  381               }
  382           }
  383           return true;
  384       }
  385   
  386       /*
  387        * Expands list of files to process into full list of all files that
  388        * can be found by recursively descending directories.
  389        */
  390       String[] expand(String[] files) {
  391           v = new Vector();
  392           expand(null, files, v, filesTable);
  393           files = new String[v.size()];
  394           for (int i = 0; i < files.length; i++) {
  395               files[i] = ((File)v.elementAt(i)).getPath();
  396           }
  397           return files;
  398       }
  399   
  400       void expand(File dir, String[] files, Vector v, Hashtable t) {
  401           if (files == null) {
  402               return;
  403           }
  404           for (int i = 0; i < files.length; i++) {
  405               File f;
  406               if (dir == null) {
  407                   f = new File(files[i]);
  408               } else {
  409                   f = new File(dir, files[i]);
  410               }
  411               if (f.isFile()) {
  412                   if (!t.contains(f)) {
  413                       t.put(entryName(f.getPath()), f);
  414                       v.addElement(f);
  415                   }
  416               } else if (f.isDirectory()) {
  417                   String dirPath = f.getPath();
  418                   dirPath = (dirPath.endsWith(File.separator)) ? dirPath :
  419                       (dirPath + File.separator);
  420                   t.put(entryName(dirPath), f);
  421                   v.addElement(f);
  422                   expand(f, f.list(), v, t);
  423               } else {
  424                   error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
  425                   ok = false;
  426               }
  427           }
  428       }
  429   
  430       /*
  431        * Creates a new JAR file.
  432        */
  433       void create(OutputStream out, String[] files, Manifest manifest)
  434           throws IOException
  435       {
  436           ZipOutputStream zos = new JarOutputStream(out);
  437           if (flag0) {
  438               zos.setMethod(ZipOutputStream.STORED);
  439           }
  440           if (manifest != null) {
  441               if (vflag) {
  442                   output(getMsg("out.added.manifest"));
  443               }
  444               ZipEntry e = new ZipEntry(MANIFEST_DIR);
  445               e.setTime(System.currentTimeMillis());
  446               e.setSize(0);
  447               e.setCrc(0);
  448               zos.putNextEntry(e);
  449               e = new ZipEntry(MANIFEST);
  450               e.setTime(System.currentTimeMillis());
  451               if (flag0) {
  452                   crc32Manifest(e, manifest);
  453               }
  454               zos.putNextEntry(e);
  455               manifest.write(zos);
  456               zos.closeEntry();
  457           }
  458           for (int i = 0; i < files.length; i++) {
  459               addFile(zos, new File(files[i]));
  460           }
  461           zos.close();
  462       }
  463   
  464       /*
  465        * update an existing jar file.
  466        */
  467       boolean update(InputStream in, OutputStream out,
  468                   InputStream newManifest) throws IOException
  469       {
  470           Hashtable t = filesTable;
  471           Vector v = this.v;
  472           ZipInputStream zis = new ZipInputStream(in);
  473           ZipOutputStream zos = new JarOutputStream(out);
  474           ZipEntry e = null;
  475           boolean foundManifest = false;
  476           byte[] buf = new byte[1024];
  477           int n = 0;
  478           boolean updateOk = true;
  479   
  480           if (t.containsKey(INDEX)) {
  481               addIndex((JarIndex)t.get(INDEX), zos);
  482           }
  483   
  484           // put the old entries first, replace if necessary
  485           while ((e = zis.getNextEntry()) != null) {
  486               String name = e.getName();
  487   
  488               boolean isManifestEntry = name.toUpperCase(
  489                                               java.util.Locale.ENGLISH).
  490                                           equals(MANIFEST);
  491               if ((name.toUpperCase().equals(INDEX)
  492                       && t.containsKey(INDEX))
  493                       || (Mflag && isManifestEntry)) {
  494                   continue;
  495               } else if (isManifestEntry && ((newManifest != null) ||
  496                           (ename != null))) {
  497                   foundManifest = true;
  498                   if (newManifest != null) {
  499                       // Don't read from the newManifest InputStream, as we
  500                       // might need it below, and we can't re-read the same data
  501                       // twice.
  502                       FileInputStream fis = new FileInputStream(mname);
  503                       boolean ambigous = isAmbigousMainClass(new Manifest(fis));
  504                       fis.close();
  505                       if (ambigous) {
  506                           return false;
  507                       }
  508                   }
  509   
  510                   // Update the manifest.
  511                   Manifest old = new Manifest(zis);
  512                   if (newManifest != null) {
  513                       old.read(newManifest);
  514                   }
  515                   updateManifest(old, zos);
  516               } else {
  517                   if (!t.containsKey(name)) { // copy the old stuff
  518   
  519                       // do our own compression
  520                       ZipEntry e2 = new ZipEntry(name);
  521                       e2.setMethod(e.getMethod());
  522                       e2.setTime(e.getTime());
  523                       e2.setComment(e.getComment());
  524                       e2.setExtra(e.getExtra());
  525                       if (e.getMethod() == ZipEntry.STORED) {
  526                           e2.setSize(e.getSize());
  527                           e2.setCrc(e.getCrc());
  528                       }
  529                       zos.putNextEntry(e2);
  530                       while ((n = zis.read(buf, 0, buf.length)) != -1) {
  531                           zos.write(buf, 0, n);
  532                       }
  533                   } else { // replace with the new files
  534                       addFile(zos, (File)(t.get(name)));
  535                       t.remove(name);
  536                   }
  537               }
  538           }
  539           t.remove(INDEX);
  540   
  541           // add the remaining new files
  542           if (!t.isEmpty()) {
  543               for (int i = 0; i < v.size(); i++) {
  544                   File f = (File)v.elementAt(i);
  545                   if (t.containsValue(f)) {
  546                       addFile(zos, f);
  547                   }
  548               }
  549           }
  550           if (!foundManifest) {
  551               if (newManifest != null) {
  552                   Manifest m = new Manifest(newManifest);
  553                   updateOk = !isAmbigousMainClass(m);
  554                   if (updateOk) {
  555                       updateManifest(m, zos);
  556                   }
  557               } else if (ename != null) {
  558                   updateManifest(new Manifest(), zos);
  559               }
  560           }
  561           zis.close();
  562           zos.close();
  563           return updateOk;
  564       }
  565   
  566   
  567       private void addIndex(JarIndex index, ZipOutputStream zos)
  568           throws IOException
  569       {
  570           ZipEntry e = new ZipEntry(INDEX);
  571           e.setTime(System.currentTimeMillis());
  572           if (flag0) {
  573               e.setMethod(ZipEntry.STORED);
  574               File ifile = File.createTempFile("index", null, new File("."));
  575               BufferedOutputStream bos = new BufferedOutputStream
  576                   (new FileOutputStream(ifile));
  577               index.write(bos);
  578               crc32File(e, ifile);
  579               bos.close();
  580               ifile.delete();
  581           }
  582           zos.putNextEntry(e);
  583           index.write(zos);
  584           if (vflag) {
  585               // output(getMsg("out.update.manifest"));
  586           }
  587       }
  588   
  589       private void updateManifest(Manifest m, ZipOutputStream zos)
  590           throws IOException
  591       {
  592           addVersion(m);
  593           addCreatedBy(m);
  594           if (ename != null) {
  595               addMainClass(m, ename);
  596           }
  597           ZipEntry e = new ZipEntry(MANIFEST);
  598           e.setTime(System.currentTimeMillis());
  599           if (flag0) {
  600               e.setMethod(ZipEntry.STORED);
  601               crc32Manifest(e, m);
  602           }
  603           zos.putNextEntry(e);
  604           m.write(zos);
  605           if (vflag) {
  606               output(getMsg("out.update.manifest"));
  607           }
  608       }
  609   
  610   
  611       private String entryName(String name) {
  612           name = name.replace(File.separatorChar, '/');
  613           String matchPath = "";
  614           for (int i = 0; i < paths.size(); i++) {
  615               String path = (String)paths.elementAt(i);
  616               if (name.startsWith(path) && (path.length() > matchPath.length())) {
  617                   matchPath = path;
  618               }
  619           }
  620           name = name.substring(matchPath.length());
  621   
  622           if (name.startsWith("/")) {
  623               name = name.substring(1);
  624           } else if (name.startsWith("./")) {
  625               name = name.substring(2);
  626           }
  627           return name;
  628       }
  629   
  630       private void addVersion(Manifest m) {
  631           Attributes global = m.getMainAttributes();
  632           if (global.getValue(Attributes.Name.MANIFEST_VERSION) == null) {
  633               global.put(Attributes.Name.MANIFEST_VERSION, VERSION);
  634           }
  635       }
  636   
  637       private void addCreatedBy(Manifest m) {
  638           Attributes global = m.getMainAttributes();
  639           if (global.getValue(new Attributes.Name("Created-By")) == null) {
  640               String javaVendor = System.getProperty("java.vendor");
  641               String jdkVersion = System.getProperty("java.version");
  642               global.put(new Attributes.Name("Created-By"), jdkVersion + " (" +
  643                           javaVendor + ")");
  644           }
  645       }
  646   
  647       private void addMainClass(Manifest m, String mainApp) {
  648           Attributes global = m.getMainAttributes();
  649   
  650           // overrides any existing Main-Class attribute
  651           global.put(Attributes.Name.MAIN_CLASS, mainApp);
  652       }
  653   
  654       private boolean isAmbigousMainClass(Manifest m) {
  655           if (ename != null) {
  656               Attributes global = m.getMainAttributes();
  657               if ((global.get(Attributes.Name.MAIN_CLASS) != null)) {
  658                   error(getMsg("error.bad.eflag"));
  659                   usageError();
  660                   return true;
  661               }
  662           }
  663           return false;
  664       }
  665   
  666       /*
  667        * Adds a new file entry to the ZIP output stream.
  668        */
  669       void addFile(ZipOutputStream zos, File file) throws IOException {
  670           String name = file.getPath();
  671           boolean isDir = file.isDirectory();
  672   
  673           if (isDir) {
  674               name = name.endsWith(File.separator) ? name :
  675                   (name + File.separator);
  676           }
  677           name = entryName(name);
  678   
  679           if (name.equals("") || name.equals(".") || name.equals(zname)) {
  680               return;
  681           } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST))
  682                      && !Mflag) {
  683               if (vflag) {
  684                   output(formatMsg("out.ignore.entry", name));
  685               }
  686               return;
  687           }
  688   
  689           long size = isDir ? 0 : file.length();
  690   
  691           if (vflag) {
  692               out.print(formatMsg("out.adding", name));
  693           }
  694           ZipEntry e = new ZipEntry(name);
  695           e.setTime(file.lastModified());
  696           if (size == 0) {
  697               e.setMethod(ZipEntry.STORED);
  698               e.setSize(0);
  699               e.setCrc(0);
  700           } else if (flag0) {
  701               e.setSize(size);
  702               e.setMethod(ZipEntry.STORED);
  703               crc32File(e, file);
  704           }
  705           zos.putNextEntry(e);
  706           if (!isDir) {
  707               byte[] buf = new byte[1024];
  708               int len;
  709               InputStream is = new BufferedInputStream(new FileInputStream(file));
  710               while ((len = is.read(buf, 0, buf.length)) != -1) {
  711                   zos.write(buf, 0, len);
  712               }
  713               is.close();
  714           }
  715           zos.closeEntry();
  716           /* report how much compression occurred. */
  717           if (vflag) {
  718               size = e.getSize();
  719               long csize = e.getCompressedSize();
  720               out.print(formatMsg2("out.size", String.valueOf(size),
  721                           String.valueOf(csize)));
  722               if (e.getMethod() == ZipEntry.DEFLATED) {
  723                   long ratio = 0;
  724                   if (size != 0) {
  725                       ratio = ((size - csize) * 100) / size;
  726                   }
  727                   output(formatMsg("out.deflated", String.valueOf(ratio)));
  728               } else {
  729                   output(getMsg("out.stored"));
  730               }
  731           }
  732       }
  733   
  734       /*
  735        * compute the crc32 of a file.  This is necessary when the ZipOutputStream
  736        * is in STORED mode.
  737        */
  738       private void crc32Manifest(ZipEntry e, Manifest m) throws IOException {
  739           crc32.reset();
  740           CRC32OutputStream os = new CRC32OutputStream(crc32);
  741           m.write(os);
  742           e.setSize((long) os.n);
  743           e.setCrc(crc32.getValue());
  744       }
  745   
  746       /*
  747        * compute the crc32 of a file.  This is necessary when the ZipOutputStream
  748        * is in STORED mode.
  749        */
  750       private void crc32File(ZipEntry e, File f) throws IOException {
  751           InputStream is = new BufferedInputStream(new FileInputStream(f));
  752           byte[] buf = new byte[1024];
  753           crc32.reset();
  754           int r = 0;
  755           int nread = 0;
  756           long len = f.length();
  757           while ((r = is.read(buf)) != -1) {
  758               nread += r;
  759               crc32.update(buf, 0, r);
  760           }
  761           is.close();
  762           if (nread != (int) len) {
  763               throw new JarException(formatMsg(
  764                           "error.incorrect.length", f.getPath()));
  765           }
  766           e.setCrc(crc32.getValue());
  767       }
  768   
  769       /*
  770        * Extracts specified entries from JAR file.
  771        */
  772       void extract(InputStream in, String files[]) throws IOException {
  773           ZipInputStream zis = new ZipInputStream(in);
  774           ZipEntry e;
  775           // Set of all directory entries specified in archive.  Dissallows
  776           // null entries.  Disallows all entries if using pre-6.0 behavior.
  777           Set<ZipEntry> dirs = new HashSet<ZipEntry>() {
  778               public boolean add(ZipEntry e) {
  779                   return ((e == null || useExtractionTime) ? false : super.add(e));
  780               }};
  781   
  782           while ((e = zis.getNextEntry()) != null) {
  783               if (files == null) {
  784                   dirs.add(extractFile(zis, e));
  785   
  786               } else {
  787                   String name = e.getName();
  788                   for (int i = 0; i < files.length; i++) {
  789                       String file = files[i].replace(File.separatorChar, '/');
  790                       if (name.startsWith(file)) {
  791                           dirs.add(extractFile(zis, e));
  792                           break;
  793                       }
  794                   }
  795               }
  796           }
  797   
  798           // Update timestamps of directories specified in archive with their
  799           // timestamps as given in the archive.  We do this after extraction,
  800           // instead of during, because creating a file in a directory changes
  801           // that directory's timestamp.
  802           for (ZipEntry dirEntry : dirs) {
  803               long lastModified = dirEntry.getTime();
  804               if (lastModified != -1) {
  805                   File dir = new File(dirEntry.getName().replace('/', File.separatorChar));
  806                   dir.setLastModified(lastModified);
  807               }
  808           }
  809       }
  810   
  811       /*
  812        * Extracts next entry from JAR file, creating directories as needed.  If
  813        * the entry is for a directory which doesn't exist prior to this
  814        * invocation, returns that entry, otherwise returns null.
  815        */
  816       ZipEntry extractFile(ZipInputStream zis, ZipEntry e) throws IOException {
  817           ZipEntry rc = null;
  818           String name = e.getName();
  819           File f = new File(e.getName().replace('/', File.separatorChar));
  820           if (e.isDirectory()) {
  821               if (f.exists()) {
  822                   if (!f.isDirectory()) {
  823                       throw new IOException(formatMsg("error.create.dir",
  824                           f.getPath()));
  825                   }
  826               } else {
  827                   if (!f.mkdirs()) {
  828                       throw new IOException(formatMsg("error.create.dir",
  829                           f.getPath()));
  830                   } else {
  831                       rc = e;
  832                   }
  833               }
  834   
  835               if (vflag) {
  836                   output(formatMsg("out.create", name));
  837               }
  838           } else {
  839               if (f.getParent() != null) {
  840                   File d = new File(f.getParent());
  841                   if (!d.exists() && !d.mkdirs() || !d.isDirectory()) {
  842                       throw new IOException(formatMsg(
  843                           "error.create.dir", d.getPath()));
  844                   }
  845               }
  846               OutputStream os = new FileOutputStream(f);
  847               byte[] b = new byte[512];
  848               int len;
  849               while ((len = zis.read(b, 0, b.length)) != -1) {
  850                   os.write(b, 0, len);
  851               }
  852               zis.closeEntry();
  853               os.close();
  854               if (vflag) {
  855                   if (e.getMethod() == ZipEntry.DEFLATED) {
  856                       output(formatMsg("out.inflated", name));
  857                   } else {
  858                       output(formatMsg("out.extracted", name));
  859                   }
  860               }
  861           }
  862           if (!useExtractionTime) {
  863               long lastModified = e.getTime();
  864               if (lastModified != -1) {
  865                   f.setLastModified(lastModified);
  866               }
  867           }
  868           return rc;
  869       }
  870   
  871       /*
  872        * Lists contents of JAR file.
  873        */
  874       void list(InputStream in, String files[]) throws IOException {
  875           ZipInputStream zis = new ZipInputStream(in);
  876           ZipEntry e;
  877           while ((e = zis.getNextEntry()) != null) {
  878               String name = e.getName();
  879               /*
  880                * In the case of a compressed (deflated) entry, the entry size
  881                * is stored immediately following the entry data and cannot be
  882                * determined until the entry is fully read. Therefore, we close
  883                * the entry first before printing out its attributes.
  884                */
  885               zis.closeEntry();
  886               if (files == null) {
  887                   printEntry(e);
  888               } else {
  889                   for (int i = 0; i < files.length; i++) {
  890                       String file = files[i].replace(File.separatorChar, '/');
  891                       if (name.startsWith(file)) {
  892                           printEntry(e);
  893                           break;
  894                       }
  895                   }
  896               }
  897           }
  898       }
  899   
  900   
  901       /**
  902        * Output the class index table to the INDEX.LIST file of the
  903        * root jar file.
  904        */
  905       void dumpIndex(String rootjar, JarIndex index) throws IOException {
  906           filesTable.put(INDEX, index);
  907           File scratchFile = File.createTempFile("scratch", null, new File("."));
  908           File jarFile = new File(rootjar);
  909           boolean updateOk = update(new FileInputStream(jarFile),
  910                   new FileOutputStream(scratchFile), null);
  911           jarFile.delete();
  912           if (!scratchFile.renameTo(jarFile)) {
  913               scratchFile.delete();
  914               throw new IOException(getMsg("error.write.file"));
  915           }
  916           scratchFile.delete();
  917       }
  918   
  919       private Hashtable jarTable = new Hashtable();
  920       /*
  921        * Generate the transitive closure of the Class-Path attribute for
  922        * the specified jar file.
  923        */
  924       Vector getJarPath(String jar) throws IOException {
  925           Vector files = new Vector();
  926           files.add(jar);
  927           jarTable.put(jar, jar);
  928   
  929           // take out the current path
  930           String path = jar.substring(0, Math.max(0, jar.lastIndexOf('/') + 1));
  931   
  932           // class path attribute will give us jar file name with
  933           // '/' as separators, so we need to change them to the
  934           // appropriate one before we open the jar file.
  935           JarFile rf = new JarFile(jar.replace('/', File.separatorChar));
  936   
  937           if (rf != null) {
  938               Manifest man = rf.getManifest();
  939               if (man != null) {
  940                   Attributes attr = man.getMainAttributes();
  941                   if (attr != null) {
  942                       String value = attr.getValue(Attributes.Name.CLASS_PATH);
  943                       if (value != null) {
  944                           StringTokenizer st = new StringTokenizer(value);
  945                           while (st.hasMoreTokens()) {
  946                               String ajar = st.nextToken();
  947                               if (!ajar.endsWith("/")) {  // it is a jar file
  948                                   ajar = path.concat(ajar);
  949                                   /* check on cyclic dependency */
  950                                   if (jarTable.get(ajar) == null) {
  951                                       files.addAll(getJarPath(ajar));
  952                                   }
  953                               }
  954                           }
  955                       }
  956                   }
  957               }
  958           }
  959           rf.close();
  960           return files;
  961       }
  962   
  963       /**
  964        * Generate class index file for the specified root jar file.
  965        */
  966       void genIndex(String rootjar, String[] files) throws IOException {
  967           Vector jars = getJarPath(rootjar);
  968           int njars = jars.size();
  969           String[] jarfiles;
  970   
  971           if (njars == 1 && files != null) {
  972               // no class-path attribute defined in rootjar, will
  973               // use command line specified list of jars
  974               for (int i = 0; i < files.length; i++) {
  975                   jars.addAll(getJarPath(files[i]));
  976               }
  977               njars = jars.size();
  978           }
  979           jarfiles = (String[])jars.toArray(new String[njars]);
  980           JarIndex index = new JarIndex(jarfiles);
  981           dumpIndex(rootjar, index);
  982       }
  983   
  984   
  985       /*
  986        * Prints entry information.
  987        */
  988       void printEntry(ZipEntry e) throws IOException {
  989           if (vflag) {
  990               StringBuffer sb = new StringBuffer();
  991               String s = Long.toString(e.getSize());
  992               for (int i = 6 - s.length(); i > 0; --i) {
  993                   sb.append(' ');
  994               }
  995               sb.append(s).append(' ').append(new Date(e.getTime()).toString());
  996               sb.append(' ').append(e.getName());
  997               output(sb.toString());
  998           } else {
  999               output(e.getName());
 1000           }
 1001       }
 1002   
 1003       /*
 1004        * Print usage message and die.
 1005        */
 1006       void usageError() {
 1007           error(getMsg("usage"));
 1008       }
 1009   
 1010       /*
 1011        * A fatal exception has been caught.  No recovery possible
 1012        */
 1013       void fatalError(Exception e) {
 1014           e.printStackTrace();
 1015       }
 1016   
 1017       /*
 1018        * A fatal condition has been detected; message is "s".
 1019        * No recovery possible
 1020        */
 1021       void fatalError(String s) {
 1022           error(program + ": " + s);
 1023       }
 1024   
 1025       /**
 1026        * Print an output message; like verbose output and the like
 1027        */
 1028       protected void output(String s) {
 1029           out.println(s);
 1030       }
 1031   
 1032       /**
 1033        * Print an error mesage; like something is broken
 1034        */
 1035       protected void error(String s) {
 1036           err.println(s);
 1037       }
 1038   
 1039       /*
 1040        * Main routine to start program.
 1041        */
 1042       public static void main(String args[]) {
 1043           Main jartool = new Main(System.out, System.err, "jar");
 1044           System.exit(jartool.run(args) ? 0 : 1);
 1045       }
 1046   }
 1047   
 1048   /*
 1049    * an OutputStream that doesn't send its output anywhere, (but could).
 1050    * It's here to find the CRC32 of a manifest, necessary for STORED only
 1051    * mode in ZIP.
 1052    */
 1053   final class CRC32OutputStream extends java.io.OutputStream {
 1054       CRC32 crc;
 1055       int n = 0;
 1056       CRC32OutputStream(CRC32 crc) {
 1057           this.crc = crc;
 1058       }
 1059   
 1060       public void write(int r) throws IOException {
 1061           crc.update(r);
 1062           n++;
 1063       }
 1064   
 1065       public void write(byte[] b) throws IOException {
 1066           crc.update(b, 0, b.length);
 1067           n += b.length;
 1068       }
 1069   
 1070       public void write(byte[] b, int off, int len) throws IOException {
 1071           crc.update(b, off, len);
 1072           n += len - off;
 1073       }
 1074   }

Save This Page
Home » openjdk-7 » sun.tools » jar » [javadoc | source]