001/*
002 * Copyright 2007-2018 The jdeb developers.
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 */
016package org.vafer.jdeb.mapping;
017
018import java.io.BufferedReader;
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.InputStreamReader;
022import java.util.HashMap;
023import java.util.Map;
024import java.util.regex.Matcher;
025import java.util.regex.Pattern;
026
027import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
028
029/**
030 * Reads permissions and ownerships from a "ls -laR > mapping.txt" dump and
031 * maps entries accordingly.
032 */
033public final class LsMapper implements Mapper {
034
035    private final Map<String, TarArchiveEntry> mapping;
036
037
038    public final static class ParseError extends Exception {
039
040        private static final long serialVersionUID = 1L;
041
042        public ParseError( String message ) {
043            super(message);
044        }
045    }
046
047
048    public LsMapper( final InputStream pInput ) throws IOException, ParseError {
049        mapping = parse(pInput);
050    }
051
052    /*
053./trunk/target/test-classes/org/vafer/dependency:
054total 176
055drwxr-xr-x   23 tcurdt  tcurdt   782 Jun 25 03:48 .
056drwxr-xr-x    3 tcurdt  tcurdt   102 Jun 25 03:48 ..
057-rw-r--r--    1 tcurdt  tcurdt  2934 Jun 25 03:48 DependenciesTestCase.class
058-rw-r--r--    1 tcurdt  tcurdt   786 Jun 25 03:48 JarCombiningTestCase$1.class
059-rw-r--r--    1 tcurdt  tcurdt  2176 Jun 25 03:48 WarTestCase.class
060drwxr-xr-x    4 tcurdt  tcurdt   136 Jun 25 03:48 classes
061
062./trunk/target/test-classes/org/vafer/dependency/classes:
063     */
064
065    final private Pattern basePattern = Pattern.compile("^\\./(.*):$");
066    final private Pattern totalPattern = Pattern.compile("^total ([0-9]+)$");
067    final private Pattern dirPattern = Pattern.compile("^d([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+[\\.]{1,2}$");
068    final private Pattern filePattern = Pattern.compile("^([d-])([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+(.*)$");
069    final private Pattern newlinePattern = Pattern.compile("$");
070
071    private String readBase( final BufferedReader reader ) throws IOException, ParseError {
072        final String line = reader.readLine();
073        if (line == null) {
074            return null;
075        }
076        final Matcher matcher = basePattern.matcher(line);
077        if (!matcher.matches()) {
078            throw new ParseError("expected base line but got \"" + line + "\"");
079        }
080        return matcher.group(1);
081    }
082
083    private String readTotal( final BufferedReader reader ) throws IOException, ParseError {
084        final String line = reader.readLine();
085        final Matcher matcher = totalPattern.matcher(line);
086        if (!matcher.matches()) {
087            throw new ParseError("expected total line but got \"" + line + "\"");
088        }
089        return matcher.group(1);
090    }
091
092    private TarArchiveEntry readDir( final BufferedReader reader, final String base ) throws IOException, ParseError {
093        final String current = reader.readLine();
094        final Matcher currentMatcher = dirPattern.matcher(current);
095        if (!currentMatcher.matches()) {
096            throw new ParseError("expected dirline but got \"" + current + "\"");
097        }
098
099        final String parent = reader.readLine();
100        final Matcher parentMatcher = dirPattern.matcher(parent);
101        if (!parentMatcher.matches()) {
102            throw new ParseError("expected dirline but got \"" + parent + "\"");
103        }
104
105        final TarArchiveEntry entry = new TarArchiveEntry(base, true);
106
107        entry.setMode(convertModeFromString(currentMatcher.group(1)));
108        entry.setUserName(currentMatcher.group(3));
109        entry.setGroupName(currentMatcher.group(4));
110
111        return entry;
112    }
113
114
115    private int convertModeFromString( final String mode ) {
116
117        final char[] m = mode.toCharArray();
118        /*
119           -rwxrwxrwx
120
121           4000    set-user-ID-on-execution bit
122           2000    set-user-ID-on-execution bit
123           1000    sticky bit
124           0400    allow read by owner.
125           0200    allow write by owner.
126           0100    execute / search
127           0040    allow read by group members.
128           0020    allow write by group members.
129           0010    execute / search
130           0004    allow read by others.
131           0002    allow write by others.
132           0001    execute / search
133         */
134        // TODO: simplified - needs fixing
135        int sum = 0;
136        int bit = 1;
137        for (int i = m.length - 1; i >= 0; i--) {
138            if (m[i] != '-') {
139                sum += bit;
140            }
141            bit += bit;
142        }
143        return sum;
144    }
145
146    private TarArchiveEntry readFile( final BufferedReader reader, final String base ) throws IOException, ParseError {
147
148        while (true) {
149            final String line = reader.readLine();
150
151            if (line == null) {
152                return null;
153            }
154
155            final Matcher currentMatcher = filePattern.matcher(line);
156            if (!currentMatcher.matches()) {
157                final Matcher newlineMatcher = newlinePattern.matcher(line);
158                if (newlineMatcher.matches()) {
159                    return null;
160                }
161                throw new ParseError("expected file line but got \"" + line + "\"");
162            }
163
164            final String type = currentMatcher.group(1);
165            if (type.startsWith("-")) {
166                final TarArchiveEntry entry = new TarArchiveEntry(base + "/" + currentMatcher.group(8), true);
167
168                entry.setMode(convertModeFromString(currentMatcher.group(2)));
169                entry.setUserName(currentMatcher.group(4));
170                entry.setGroupName(currentMatcher.group(5));
171
172                return entry;
173            }
174        }
175
176    }
177
178    private Map<String, TarArchiveEntry> parse( final InputStream pInput ) throws IOException, ParseError {
179        final Map<String, TarArchiveEntry> mapping = new HashMap<String, TarArchiveEntry>();
180
181        final BufferedReader reader = new BufferedReader(new InputStreamReader(pInput));
182
183        boolean first = true;
184        while (true) {
185
186            final String base;
187            if (first) {
188                base = "";
189                first = false;
190            } else {
191                base = readBase(reader);
192                if (base == null) {
193                    break;
194                }
195            }
196
197            readTotal(reader);
198            final TarArchiveEntry dir = readDir(reader, base);
199            mapping.put(dir.getName(), dir);
200
201            while (true) {
202                final TarArchiveEntry file = readFile(reader, base);
203
204                if (file == null) {
205                    break;
206                }
207
208                mapping.put(file.getName(), file);
209            }
210        }
211
212        return mapping;
213    }
214
215    public TarArchiveEntry map( final TarArchiveEntry pEntry ) {
216        final TarArchiveEntry entry = mapping.get(pEntry.getName());
217        if (entry != null) {
218            return entry;
219        }
220        return pEntry;
221    }
222
223}