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

Last change on this file was 13440, checked in by ehuelsmann, 13 years ago

Rename writeToString() to printObject() since that's what it's being used for.
Additionally, create princToString() for use in error messages, making the

required replacement where appropriate.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 12.1 KB
Line 
1/*
2 * LogicalPathname.java
3 *
4 * Copyright (C) 2004-2005 Peter Graves
5 * $Id: LogicalPathname.java 13440 2011-08-05 21:25:10Z ehuelsmann $
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}
Note: See TracBrowser for help on using the repository browser.