001    /*
002     * Copyright 2010 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    }