source: branches/streams/abcl/src/org/armedbear/lisp/LogicalPathname.java

Last change on this file was 14793, checked in by Mark Evenson, 9 years ago

Fix FILE-WRITE-DATE for logical pathnames

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 12.2 KB
Line 
1/*
2 * LogicalPathname.java
3 *
4 * Copyright (C) 2004-2005 Peter Graves
5 * $Id: LogicalPathname.java 14793 2015-08-31 20:55:22Z 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.util.HashMap;
39import java.util.StringTokenizer;
40
41public final class LogicalPathname extends Pathname
42{
43    private static final String LOGICAL_PATHNAME_CHARS =
44        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-;*.";
45
46    private static final HashMap map = new HashMap();
47
48    protected LogicalPathname()
49    {
50    }
51
52    protected LogicalPathname(Pathname p) {
53        super(p);
54    }
55
56    public LogicalPathname(String host, String rest)
57    {
58        final int limit = rest.length();
59        for (int i = 0; i < limit; i++) {
60            char c = rest.charAt(i);
61            if (LOGICAL_PATHNAME_CHARS.indexOf(c) < 0) {
62                error(new ParseError("The character #\\" + c + " is not valid in a logical pathname."));
63                return;
64            }
65        }
66
67        this.host = new SimpleString(host);
68
69        // "The device component of a logical pathname is always :UNSPECIFIC;
70        // no other component of a logical pathname can be :UNSPECIFIC."
71        device = Keyword.UNSPECIFIC;
72
73        int semi = rest.lastIndexOf(';');
74        if (semi >= 0) {
75            // Directory.
76            String d = rest.substring(0, semi + 1);
77            directory = parseDirectory(d);
78            rest = rest.substring(semi + 1);
79        } else {
80            // "If a relative-directory-marker precedes the directories, the
81            // directory component parsed is as relative; otherwise, the
82            // directory component is parsed as absolute."
83            directory = new Cons(Keyword.ABSOLUTE);
84        }
85
86        int dot = rest.indexOf('.');
87        if (dot >= 0) {
88            String n = rest.substring(0, dot);
89            if (n.equals("*"))
90                name = Keyword.WILD;
91            else
92                name = new SimpleString(n.toUpperCase());
93            rest = rest.substring(dot + 1);
94            dot = rest.indexOf('.');
95            if (dot >= 0) {
96                String t = rest.substring(0, dot);
97                if (t.equals("*"))
98                    type = Keyword.WILD;
99                else
100                    type = new SimpleString(t.toUpperCase());
101                // What's left is the version.
102                String v = rest.substring(dot + 1);
103                if (v.equals("*"))
104                    version = Keyword.WILD;
105                else if (v.equals("NEWEST") || v.equals("newest"))
106                    version = Keyword.NEWEST;
107                else
108                    version = PACKAGE_CL.intern("PARSE-INTEGER").execute(new SimpleString(v));
109            } else {
110                String t = rest;
111                if (t.equals("*"))
112                    type = Keyword.WILD;
113                else
114                    type = new SimpleString(t.toUpperCase());
115            }
116        } else {
117            String n = rest;
118            if (n.equals("*"))
119                name = Keyword.WILD;
120            else if (n.length() > 0)
121                name = new SimpleString(n.toUpperCase());
122        }
123    }
124
125    private static final String LOGICAL_PATHNAME_COMPONENT_CHARS =
126        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-";
127
128    public static final SimpleString canonicalizeStringComponent(AbstractString s)
129
130    {
131        final int limit = s.length();
132        for (int i = 0; i < limit; i++) {
133            char c = s.charAt(i);
134            if (LOGICAL_PATHNAME_COMPONENT_CHARS.indexOf(c) < 0) {
135                error(new ParseError("Invalid character #\\" + c +
136                                      " in logical pathname component \"" + s +
137                                      '"'));
138                // Not reached.
139                return null;
140            }
141        }
142        return new SimpleString(s.getStringValue().toUpperCase());
143    }
144
145    public static Pathname translateLogicalPathname(LogicalPathname pathname)
146
147    {
148        return (Pathname) Symbol.TRANSLATE_LOGICAL_PATHNAME.execute(pathname);
149    }
150
151    private static final LispObject parseDirectory(String s)
152
153    {
154        LispObject result;
155        if (s.charAt(0) == ';') {
156            result = new Cons(Keyword.RELATIVE);
157            s = s.substring(1);
158        } else
159            result = new Cons(Keyword.ABSOLUTE);
160        StringTokenizer st = new StringTokenizer(s, ";");
161        while (st.hasMoreTokens()) {
162            String token = st.nextToken();
163            LispObject obj;
164            if (token.equals("*"))
165                obj = Keyword.WILD;
166            else if (token.equals("**"))
167                obj = Keyword.WILD_INFERIORS;
168            else if (token.equals("..")) {
169                if (result.car() instanceof AbstractString) {
170                    result = result.cdr();
171                    continue;
172                }
173                obj= Keyword.UP;
174            } else
175                obj = new SimpleString(token.toUpperCase());
176            result = new Cons(obj, result);
177        }
178        return result.nreverse();
179    }
180
181    @Override
182    public LispObject typeOf()
183    {
184        return Symbol.LOGICAL_PATHNAME;
185    }
186
187    @Override
188    public LispObject classOf()
189    {
190        return BuiltInClass.LOGICAL_PATHNAME;
191    }
192
193    @Override
194    public LispObject typep(LispObject type)
195    {
196        if (type == Symbol.LOGICAL_PATHNAME)
197            return T;
198        if (type == BuiltInClass.LOGICAL_PATHNAME)
199            return T;
200        return super.typep(type);
201    }
202
203    @Override
204    protected String getDirectoryNamestring()
205    {
206        StringBuilder sb = new StringBuilder();
207        // "If a pathname is converted to a namestring, the symbols NIL and
208        // :UNSPECIFIC cause the field to be treated as if it were empty. That
209        // is, both NIL and :UNSPECIFIC cause the component not to appear in
210        // the namestring." 19.2.2.2.3.1
211        if (directory != NIL) {
212            LispObject temp = directory;
213            LispObject part = temp.car();
214            if (part == Keyword.ABSOLUTE) {
215            } else if (part == Keyword.RELATIVE)
216                sb.append(';');
217            else
218                error(new FileError("Unsupported directory component " + part.princToString() + ".",
219                                     this));
220            temp = temp.cdr();
221            while (temp != NIL) {
222                part = temp.car();
223                if (part instanceof AbstractString)
224                    sb.append(part.getStringValue());
225                else if (part == Keyword.WILD)
226                    sb.append('*');
227                else if (part == Keyword.WILD_INFERIORS)
228                    sb.append("**");
229                else if (part == Keyword.UP)
230                    sb.append("..");
231                else
232                    error(new FileError("Unsupported directory component " + part.princToString() + ".",
233                                         this));
234                sb.append(';');
235                temp = temp.cdr();
236            }
237        }
238        return sb.toString();
239    }
240
241    @Override
242    public String printObject()
243    {
244        final LispThread thread = LispThread.currentThread();
245        boolean printReadably = (Symbol.PRINT_READABLY.symbolValue(thread) != NIL);
246        boolean printEscape = (Symbol.PRINT_ESCAPE.symbolValue(thread) != NIL);
247        StringBuilder sb = new StringBuilder();
248        if (printReadably || printEscape)
249            sb.append("#P\"");
250        sb.append(host.getStringValue());
251        sb.append(':');
252        if (directory != NIL)
253            sb.append(getDirectoryNamestring());
254        if (name != NIL) {
255            if (name == Keyword.WILD)
256                sb.append('*');
257            else
258                sb.append(name.getStringValue());
259        }
260        if (type != NIL) {
261            sb.append('.');
262            if (type == Keyword.WILD)
263                sb.append('*');
264            else
265                sb.append(type.getStringValue());
266        }
267        if (version.integerp()) {
268            sb.append('.');
269            int base = Fixnum.getValue(Symbol.PRINT_BASE.symbolValue(thread));
270            if (version instanceof Fixnum)
271                sb.append(Integer.toString(((Fixnum)version).value, base).toUpperCase());
272            else if (version instanceof Bignum)
273                sb.append(((Bignum)version).value.toString(base).toUpperCase());
274        } else if (version == Keyword.WILD) {
275            sb.append(".*");
276        } else if (version == Keyword.NEWEST) {
277            sb.append(".NEWEST");
278        }
279        if (printReadably || printEscape)
280            sb.append('"');
281        return sb.toString();
282    }
283
284    // ### canonicalize-logical-host host => canonical-host
285    private static final Primitive CANONICALIZE_LOGICAL_HOST = new canonicalize_logical_host();
286    private static class canonicalize_logical_host extends Primitive {
287        canonicalize_logical_host() {
288            super("canonicalize-logical-host", PACKAGE_SYS, true, "host");
289        }
290        @Override
291        public LispObject execute(LispObject arg)
292        {
293            AbstractString s = checkString(arg);
294            if (s.length() == 0) {
295                // "The null string, "", is not a valid value for any
296                // component of a logical pathname." 19.3.2.2
297                return error(new LispError("Invalid logical host name: \"" +
298                                           s.getStringValue() + '"'));
299            }
300            return canonicalizeStringComponent(s);
301        }
302    }
303
304    // ### %make-logical-pathname namestring => logical-pathname
305    private static final Primitive _MAKE_LOGICAL_PATHNAME = new _make_logical_pathname();
306    private static class _make_logical_pathname extends Primitive {
307        _make_logical_pathname() {
308            super("%make-logical-pathname", PACKAGE_SYS, true, "namestring");
309        }
310        @Override
311        public LispObject execute(LispObject arg)
312
313        {
314            // Check for a logical pathname host.
315            String s = arg.getStringValue();
316            String h = getHostString(s);
317            if (h != null) {
318                if (h.length() == 0) {
319                    // "The null string, "", is not a valid value for any
320                    // component of a logical pathname." 19.3.2.2
321                    return error(new LispError("Invalid logical host name: \"" +
322                                                h + '"'));
323                }
324                if (Pathname.LOGICAL_PATHNAME_TRANSLATIONS.get(new SimpleString(h)) != null) {
325                    // A defined logical pathname host.
326                    return new LogicalPathname(h, s.substring(s.indexOf(':') + 1));
327                }
328            }
329            return error(new TypeError("Logical namestring does not specify a host: \"" + s + '"'));
330        }
331    }
332
333    public long getLastModified() {
334        Pathname p = translateLogicalPathname(this);
335        return p.getLastModified();
336    }
337}
Note: See TracBrowser for help on using the repository browser.