1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard;
22
23import java.io.*;
24import java.util.*;
25
26/**
27 * This class checks and prints out information about the GPL.
28 *
29 * @author Eric Lafortune
30 */
31public class GPL
32{
33    /**
34     * Prints out a note about the GPL if ProGuard is linked against unknown
35     * code.
36     */
37    public static void check()
38    {
39        ByteArrayOutputStream out = new ByteArrayOutputStream();
40        new Exception().printStackTrace(new PrintStream(out));
41        LineNumberReader reader = new LineNumberReader(
42                                  new InputStreamReader(
43                                  new ByteArrayInputStream(out.toByteArray())));
44
45        Set unknownPackageNames = unknownPackageNames(reader);
46
47        if (unknownPackageNames.size() > 0)
48        {
49            String uniquePackageNames = uniquePackageNames(unknownPackageNames);
50
51            System.out.println("ProGuard is released under the GNU General Public License. You therefore");
52            System.out.println("must ensure that programs that link to it ("+uniquePackageNames+"...)");
53            System.out.println("carry the GNU General Public License as well. Alternatively, you can");
54            System.out.println("apply for an exception with the author of ProGuard.");
55        }
56    }
57
58
59    /**
60     * Returns a set of package names from the given stack trace.
61     */
62    private static Set unknownPackageNames(LineNumberReader reader)
63    {
64        Set packageNames = new HashSet();
65
66        try
67        {
68            while (true)
69            {
70                String line = reader.readLine();
71                if (line == null)
72                {
73                    break;
74                }
75
76                line = line.trim();
77                if (line.startsWith("at "))
78                {
79                    line = line.substring(2).trim();
80                    line = trimSuffix(line, '(');
81                    line = trimSuffix(line, '.');
82                    line = trimSuffix(line, '.');
83
84                    if (line.length() > 0 && !isKnown(line))
85                    {
86                        packageNames.add(line);
87                    }
88                }
89            }
90        }
91        catch (IOException ex)
92        {
93            // We'll just stop looking for more names.
94        }
95
96        return packageNames;
97    }
98
99
100    /**
101     * Returns a comma-separated list of package names from the set, excluding
102     * any subpackages of packages in the set.
103     */
104    private static String uniquePackageNames(Set packageNames)
105    {
106        StringBuffer buffer = new StringBuffer();
107
108        Iterator iterator = packageNames.iterator();
109        while (iterator.hasNext())
110        {
111            String packageName = (String)iterator.next();
112            if (!containsPrefix(packageNames, packageName))
113            {
114                buffer.append(packageName).append(", ");
115            }
116        }
117
118        return buffer.toString();
119    }
120
121
122    /**
123     * Returns a given string without the suffix, as defined by the given
124     * separator.
125     */
126    private static String trimSuffix(String string, char separator)
127    {
128        int index = string.lastIndexOf(separator);
129        return index < 0 ? "" : string.substring(0, index);
130    }
131
132
133    /**
134     * Returns whether the given set contains a prefix of the given name.
135     */
136    private static boolean containsPrefix(Set set, String name)
137    {
138        int index = 0;
139
140        while (!set.contains(name.substring(0, index)))
141        {
142            index = name.indexOf('.', index + 1);
143            if (index < 0)
144            {
145                return false;
146            }
147        }
148
149        return true;
150    }
151
152
153    /**
154     * Returns whether the given package name has been granted an exception
155     * against the GPL linking clause, by the copyright holder of ProGuard.
156     * This method is not legally binding, but of course the actual license is.
157     * Please contact the copyright holder if you would like an exception for
158     * your code as well.
159     */
160    private static boolean isKnown(String packageName)
161    {
162        return packageName.startsWith("java")                   ||
163               packageName.startsWith("sun.reflect")            ||
164               packageName.startsWith("proguard")               ||
165               packageName.startsWith("org.apache.tools.ant")   ||
166               packageName.startsWith("org.apache.tools.maven") ||
167               packageName.startsWith("org.gradle")             ||
168               packageName.startsWith("org.codehaus.groovy")    ||
169               packageName.startsWith("org.eclipse")            ||
170               packageName.startsWith("org.netbeans")           ||
171               packageName.startsWith("com.android")            ||
172               packageName.startsWith("com.sun.kvem")           ||
173               packageName.startsWith("com.intel")              ||
174               packageName.startsWith("net.certiv.proguarddt")  ||
175               packageName.startsWith("groovy")                 ||
176               packageName.startsWith("scala")                  ||
177               packageName.startsWith("sbt")                    ||
178               packageName.startsWith("xsbt")                   ||
179               packageName.startsWith("eclipseme")              ||
180               packageName.startsWith("com.neomades")           ||
181               packageName.startsWith("jg.j2me")                ||
182               packageName.startsWith("jg.common")              ||
183               packageName.startsWith("jg.buildengine");
184    }
185
186
187    public static void main(String[] args)
188    {
189        LineNumberReader reader = new LineNumberReader(
190                                  new InputStreamReader(System.in));
191
192        Set unknownPackageNames = unknownPackageNames(reader);
193
194        if (unknownPackageNames.size() > 0)
195        {
196            String uniquePackageNames = uniquePackageNames(unknownPackageNames);
197
198            System.out.println(uniquePackageNames);
199        }
200    }
201}
202