source: branches/1.1.x/src/org/armedbear/lisp/zip.java

Last change on this file was 14176, checked in by Mark Evenson, 12 years ago

Refactor PATHNAME implementation details to tighten existing semantics.

None of this should change the behavior of CL:PATHNAME, but it
prepares for that in subsequent patches to address problems in merging
when the defaults points to a JAR-PATHNAME.

Fix COMPILE-FILE to work with source located in jar archive.

Moved Utilities.getFile() to instance method of Pathname which makes
more logical sense. Moved Utilities.getPathnameDirectory() to static
instance classes. These functions no longer merge their argument with
*DEFAULT-PATHNAME-DEFAULTS*, as this should be done explictly at a
higher level in the Lisp calling into Java abstraction.

RENAME-FILE no longer on namestrings, but instead use the result of
TRUENAME invocation, as namestrings will not always roundtrip
exactly back to PATHNAMES.

POPULATE-ZIP-FASL no longer forms its argumentes by merging paths,
instead using MAKE-PATHNAME with controlled defaults.

SYSTEM:NEXT-CLASSFILE-NAME and SYSTEM:COMPUTE-CLASSFILE-NAME changed
to NEXT-CLASSFILE and COMPUTE-CLASSFILE returning PATHNAME objects
rather than namestrings.

Compiler now dumps pathname in alternate form that preserves DEVICE
:UNSPECIFIC.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.3 KB
Line 
1/*
2 * zip.java
3 *
4 * Copyright (C) 2005 Peter Graves
5 * $Id: zip.java 14176 2012-10-11 11:33:19Z mevenson $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38import java.io.File;
39import java.io.FileNotFoundException;
40import java.io.FileInputStream;
41import java.io.FileOutputStream;
42import java.io.IOException;
43import java.util.HashSet;
44import java.util.Set;
45import java.util.zip.ZipEntry;
46import java.util.zip.ZipOutputStream;
47
48@DocString(name="zip",
49           args="pathname pathnames &optional topdir",
50           doc="Creates a zip archive at PATHNAME whose entries enumerated via the list of PATHNAMES.\n"
51           + "If the optional TOPDIR argument is specified, the archive will "
52           + "preserve the hierarchy of PATHNAMES relative to TOPDIR.  Without "
53           + "TOPDIR, there will be no sub-directories in the archive, i.e. it will "
54           + "be flat.")
55public final class zip extends Primitive
56{
57    private zip()
58    {
59        super("zip", PACKAGE_SYS, true);
60    }
61   
62
63    @Override
64    public LispObject execute(LispObject first, LispObject second)
65    {
66        Pathname zipfilePathname = coerceToPathname(first);
67        if (second instanceof org.armedbear.lisp.protocol.Hashtable) {
68            return execute(zipfilePathname, (org.armedbear.lisp.protocol.Hashtable)second);
69        }
70        byte[] buffer = new byte[4096];
71        try {
72            String zipfileNamestring = zipfilePathname.getNamestring();
73            if (zipfileNamestring == null)
74                return error(new SimpleError("Pathname has no namestring: " +
75                                              zipfilePathname.princToString()));
76            ZipOutputStream out =
77                new ZipOutputStream(new FileOutputStream(zipfileNamestring));
78            LispObject list = second;
79            while (list != NIL) {
80                Pathname pathname = coerceToPathname(list.car());
81                String namestring = pathname.getNamestring();
82                if (namestring == null) {
83                    // Clean up before signalling error.
84                    out.close();
85                    File zipfile = new File(zipfileNamestring);
86                    zipfile.delete();
87                    return error(new SimpleError("Pathname has no namestring: "
88                                                 + pathname.princToString()));
89                }
90                File file = new File(namestring);
91                makeEntry(out, file);
92                list = list.cdr();
93            }
94            out.close();
95        }
96        catch (IOException e) {
97            return error(new LispError(e.getMessage()));
98        }
99        return zipfilePathname;
100    }
101
102   
103
104    @Override
105    public LispObject execute(LispObject first, LispObject second, LispObject third)
106    {
107        Pathname zipfilePathname = coerceToPathname(first);
108        try {
109            String zipfileNamestring = zipfilePathname.getNamestring();
110            if (zipfileNamestring == null)
111                return error(new SimpleError("Pathname has no namestring: " +
112                                              zipfilePathname.princToString()));
113            ZipOutputStream out =
114                new ZipOutputStream(new FileOutputStream(zipfileNamestring));
115            Pathname root = (Pathname) Pathname.truename(coerceToPathname(third));
116            String rootPath = root.getDirectoryNamestring();
117            int rootPathLength = rootPath.length();
118            Set<String> directories = new HashSet<String>();
119            LispObject list = second;
120            while (list != NIL) {
121                Pathname pathname = (Pathname) Pathname.truename(coerceToPathname(list.car()));
122                String namestring = pathname.getNamestring();
123                if (namestring == null) {
124                    // Clean up before signalling error.
125                    out.close();
126                    File zipfile = new File(zipfileNamestring);
127                    zipfile.delete();
128                    return error(new SimpleError("Pathname has no namestring: " +
129                                                  pathname.princToString()));
130                }
131                String directory = "";
132                String dir = pathname.getDirectoryNamestring();
133                if (dir.length() > rootPathLength) {
134                  String d = dir.substring(rootPathLength);
135                  int i = 0;
136                  int j;
137                  while ((j = d.indexOf(Pathname.separator, i)) != -1) {
138                    i = j + 1;
139                    directory = d.substring(0, j) + Pathname.separator;
140                    if (!directories.contains(directory)) {
141                      directories.add(directory);
142                      ZipEntry entry = new ZipEntry(directory);
143                      out.putNextEntry(entry);
144                      out.closeEntry();
145                    }
146                  }
147                }
148                File file = new File(namestring);
149                if (file.isDirectory()) {
150                    list = list.cdr();
151                    continue;
152                }
153                makeEntry(out, file, directory + file.getName());
154                list = list.cdr();
155            }
156            out.close();
157        }
158        catch (IOException e) {
159            return error(new LispError(e.getMessage()));
160        }
161        return zipfilePathname;
162    }
163
164    static class Directories extends HashSet<String> {
165        private Directories() {
166            super();
167        }
168       
169        ZipOutputStream out;
170        public Directories(ZipOutputStream out) {
171            this.out = out;
172        }
173           
174        public void ensure(String path) 
175            throws IOException
176        {
177            int i = 0;
178            int j;
179            while ((j = path.indexOf(Pathname.separator, i)) != -1) {
180                i = j + 1;
181                final String directory = path.substring(0, j) + Pathname.separator;
182                if (!contains(directory)) {
183                    add(directory);
184                    ZipEntry entry = new ZipEntry(directory);
185                    out.putNextEntry(entry);
186                    out.closeEntry();
187                }
188            }
189        }
190    }
191
192    public LispObject execute(final Pathname zipfilePathname, final org.armedbear.lisp.protocol.Hashtable table) {
193        LispObject entriesObject = (LispObject)table.getEntries();
194        if (!(entriesObject instanceof Cons)) {
195            return NIL;
196        }
197        Cons entries = (Cons)entriesObject;
198
199        String zipfileNamestring = zipfilePathname.getNamestring();
200        if (zipfileNamestring == null)
201            return error(new SimpleError("Pathname has no namestring: " +
202                                         zipfilePathname.princToString()));
203        ZipOutputStream out = null;
204        try {
205            out = new ZipOutputStream(new FileOutputStream(zipfileNamestring));
206        } catch (FileNotFoundException e) {
207            return error(new FileError("Failed to create file for writing zip archive", zipfilePathname));
208        }
209        Directories directories = new Directories(out);
210
211
212        for (LispObject head = entries; head != NIL; head = head.cdr()) {
213            final LispObject key = head.car().car();
214            final LispObject value = head.car().cdr();
215
216            final Pathname source = Lisp.coerceToPathname(key);
217            final Pathname destination = Lisp.coerceToPathname(value);
218            final File file = source.getFile();
219            try {
220                String jarEntry = destination.getNamestring();
221                if (jarEntry.startsWith("/")) {
222                    jarEntry = jarEntry.substring(1);
223                }
224                directories.ensure(jarEntry);
225                makeEntry(out, file, jarEntry);
226            } catch (FileNotFoundException e) {
227                return error(new FileError("Failed to read file for incoporation in zip archive.", source));
228            } catch (IOException e) {
229                return error(new FileError("Failed to add file to zip archive.", source));
230            }
231        } 
232        try {
233            out.close();
234        } catch (IOException ex) {
235            return error(new FileError("Failed to close zip archive.", zipfilePathname));
236        }
237        return zipfilePathname;
238    }
239
240    private static final Primitive zip = new zip();
241
242    private void makeEntry(ZipOutputStream zip, File file) 
243        throws FileNotFoundException, IOException
244    {
245        makeEntry(zip, file, file.getName());
246    }
247
248    private void makeEntry(ZipOutputStream zip, File file, String name) 
249        throws FileNotFoundException, IOException
250    {
251        byte[] buffer = new byte[4096];
252        long lastModified = file.lastModified();
253        FileInputStream in = new FileInputStream(file);
254        ZipEntry entry = new ZipEntry(name);
255        if (lastModified > 0) {
256            entry.setTime(lastModified);
257        }
258        zip.putNextEntry(entry);
259        int n;
260        while ((n = in.read(buffer)) > 0)
261            zip.write(buffer, 0, n);
262        zip.closeEntry();
263        in.close();
264    }
265       
266}
Note: See TracBrowser for help on using the repository browser.