001    /*
002     * Copyright 2005 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.vafer.jdeb.mapping;
017    
018    import java.io.BufferedReader;
019    import java.io.IOException;
020    import java.io.InputStream;
021    import java.io.InputStreamReader;
022    import java.util.HashMap;
023    import java.util.Map;
024    import java.util.regex.Matcher;
025    import java.util.regex.Pattern;
026    
027    import org.apache.tools.tar.TarEntry;
028    
029    /**
030     * Reads permissions and ownerships from a "ls -laR > mapping.txt" dump and
031     * maps entries accordingly.
032     * 
033     * @author Torsten Curdt <tcurdt@vafer.org>
034     */
035    public final class LsMapper implements Mapper {
036    
037            private final Map mapping;
038    
039            
040            public final static class ParseError extends Exception {
041    
042                    private static final long serialVersionUID = 1L;
043    
044                    public ParseError() {
045                            super();
046                    }
047    
048                    public ParseError(String message, Throwable cause) {
049                            super(message, cause);
050                    }
051    
052                    public ParseError(String message) {
053                            super(message);
054                    }
055    
056                    public ParseError(Throwable cause) {
057                            super(cause);
058                    }
059                    
060            };
061            
062            public LsMapper( final InputStream pInput ) throws IOException, ParseError {
063                    mapping = parse(pInput);
064            }
065            
066            /*
067    ./trunk/target/test-classes/org/vafer/dependency:
068    total 176
069    drwxr-xr-x   23 tcurdt  tcurdt   782 Jun 25 03:48 .
070    drwxr-xr-x    3 tcurdt  tcurdt   102 Jun 25 03:48 ..
071    -rw-r--r--    1 tcurdt  tcurdt  2934 Jun 25 03:48 DependenciesTestCase.class
072    -rw-r--r--    1 tcurdt  tcurdt   786 Jun 25 03:48 JarCombiningTestCase$1.class
073    -rw-r--r--    1 tcurdt  tcurdt  2176 Jun 25 03:48 WarTestCase.class
074    drwxr-xr-x    4 tcurdt  tcurdt   136 Jun 25 03:48 classes
075    
076    ./trunk/target/test-classes/org/vafer/dependency/classes:
077             */
078            
079            final private Pattern basePattern = Pattern.compile("^\\./(.*):$");
080            final private Pattern totalPattern = Pattern.compile("^total ([0-9]+)$");
081            final private Pattern dirPattern = Pattern.compile("^d([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+[\\.]{1,2}$");
082            final private Pattern filePattern = Pattern.compile("^([d-])([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+(.*)$");
083            final private Pattern newlinePattern = Pattern.compile("$");
084    
085            private String readBase( final BufferedReader reader ) throws IOException, ParseError {
086                    final String line = reader.readLine();
087                    if (line == null) {
088                            return null;
089                    }
090                    final Matcher matcher = basePattern.matcher(line);
091                    if (!matcher.matches()) {
092                            throw new ParseError("expected base line but got \"" + line + "\"");
093                    }
094                    return matcher.group(1);
095            }
096    
097            private String readTotal( final BufferedReader reader ) throws IOException, ParseError {
098                    final String line = reader.readLine();
099                    final Matcher matcher = totalPattern.matcher(line);
100                    if (!matcher.matches()) {
101                            throw new ParseError("expected total line but got \"" + line + "\"");
102                    }
103                    return matcher.group(1);
104            }
105    
106            private TarEntry readDir( final BufferedReader reader, final String base ) throws IOException, ParseError {
107                    final String current = reader.readLine();
108                    final Matcher currentMatcher = dirPattern.matcher(current);
109                    if (!currentMatcher.matches()) {
110                            throw new ParseError("expected dirline but got \"" + current + "\"");
111                    }
112    
113                    final String parent = reader.readLine();
114                    final Matcher parentMatcher = dirPattern.matcher(parent);
115                    if (!parentMatcher.matches()) {
116                            throw new ParseError("expected dirline but got \"" + parent + "\"");
117                    }
118                    
119                    final TarEntry entry = new TarEntry(base);
120                    
121                    entry.setMode(convertModeFromString(currentMatcher.group(1)));
122                    entry.setUserName(currentMatcher.group(3));
123                    entry.setGroupName(currentMatcher.group(4));
124                    
125                    return entry;
126            }
127    
128            
129            private int convertModeFromString( final String mode ) {
130                    
131                    final char[] m = mode.toCharArray();
132                    /*
133                       -rwxrwxrwx
134    
135                       4000    set-user-ID-on-execution bit
136               2000    set-user-ID-on-execution bit
137               1000    sticky bit
138               0400    allow read by owner.
139               0200    allow write by owner.
140               0100    execute / search
141               0040    allow read by group members.
142               0020    allow write by group members.
143               0010    execute / search
144               0004    allow read by others.
145               0002    allow write by others.
146               0001    execute / search
147                     */
148                    // TODO: simplified - needs fixing
149                    int sum = 0;
150                    int bit = 1;
151                    for(int i=m.length-1; i>=0 ; i--) {
152                            if (m[i] != '-') {
153                                    sum += bit;
154                            }
155                            bit += bit;
156                    }
157                    return sum;
158            }
159            
160            private TarEntry readFile( final BufferedReader reader, final String base ) throws IOException, ParseError {
161                    
162                    while(true) {
163                            final String line = reader.readLine();
164                            
165                            if (line == null) {
166                                    return null;
167                            }
168                            
169                            final Matcher currentMatcher = filePattern.matcher(line);
170                            if (!currentMatcher.matches()) {
171                                    final Matcher newlineMatcher = newlinePattern.matcher(line);
172                                    if (newlineMatcher.matches()) {
173                                            return null;
174                                    }
175                                    throw new ParseError("expected file line but got \"" + line + "\"");
176                            }
177                            
178                            final String type = currentMatcher.group(1);
179                            if (type.startsWith("-")) {
180                                    final TarEntry entry = new TarEntry(base + "/" + currentMatcher.group(8));
181    
182                                    entry.setMode(convertModeFromString(currentMatcher.group(2)));
183                                    entry.setUserName(currentMatcher.group(4));
184                                    entry.setGroupName(currentMatcher.group(5));
185    
186                                    return entry;                           
187                            }                       
188                    }
189                    
190            }
191            
192            private Map parse( final InputStream pInput ) throws IOException, ParseError {
193                    final Map mapping = new HashMap();
194                    
195                    final BufferedReader reader = new BufferedReader(new InputStreamReader(pInput));
196                    
197                    boolean first = true;
198                    while(true) {
199    
200                            final String base;
201                            if (first) {
202                                    base = "";
203                                    first = false;
204                            } else {
205                                    base = readBase(reader);
206                                    if (base == null) {
207                                            break;
208                                    }
209                            }
210    
211                            readTotal(reader);
212                            final TarEntry dir = readDir(reader, base);
213                            mapping.put(dir.getName(), dir);
214    
215                            while(true) {
216                                    final TarEntry file = readFile(reader, base);
217    
218                                    if (file == null) {
219                                            break;
220                                    }
221                                    
222                                    mapping.put(file.getName(), file);
223                            }
224                    }
225                    
226                    return mapping;
227            }
228            
229            public TarEntry map( final TarEntry pEntry ) {
230                    
231                    final TarEntry entry = (TarEntry) mapping.get(pEntry.getName());
232                    
233                    if (entry != null) {
234    
235                            final TarEntry newEntry = new TarEntry(entry.getName());
236                            newEntry.setUserId(entry.getUserId());
237                            newEntry.setGroupId(entry.getGroupId());
238                            newEntry.setUserName(entry.getUserName());
239                            newEntry.setGroupName(entry.getGroupName());
240                            newEntry.setMode(entry.getMode());
241                            newEntry.setSize(entry.getSize());
242                            
243                            return newEntry;
244                    }
245                    
246                    return pEntry;
247            }
248    
249    }