12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others.
22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
3bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert/**
4bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert*******************************************************************************
5bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert* Copyright (C) 2004-2012, International Business Machines Corporation and    *
6bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert* others. All Rights Reserved.                                                *
7bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert*******************************************************************************
8bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert*/
9bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
10bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertpackage com.ibm.icu.dev.tool.docs;
11bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
12bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.io.BufferedReader;
13bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.io.File;
14bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.io.FileInputStream;
15bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.io.FileOutputStream;
16bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.io.FilenameFilter;
17bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.io.IOException;
18bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.io.InputStream;
19bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.io.InputStreamReader;
20bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.io.PrintStream;
21bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.util.ArrayList;
22bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.util.HashMap;
23bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.util.Iterator;
24bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.util.Map;
25bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.util.TreeMap;
26bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
27bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
28bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert/**
29bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * A simple facility for adding C-like preprocessing to .java files.
30bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * This only understands a subset of the C preprocessing syntax.
31bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * Its used to manage files that with only small differences can be
32bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * compiled for different JVMs.  This changes files in place,
33bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * commenting out lines based on the current flag settings.
34bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert */
35bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertpublic class CodeMangler {
36bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private File indir;        // root of input
37bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private File outdir;       // root of output
38bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private String suffix;     // suffix to process, default '.jpp'
39bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private boolean recurse;   // true if recurse on directories
40bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private boolean force;     // true if force reprocess of files
41bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private boolean clean;     // true if output is to be cleaned
42bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private boolean timestamp; // true if we read/write timestamp
43bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private boolean nonames;   // true if no names in header
44bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private HashMap map;       // defines
45bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private ArrayList names;   // files/directories to process
46bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private String header;     // sorted list of defines passed in
47bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
48bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private boolean verbose; // true if we emit debug output
49bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
50bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private static final String IGNORE_PREFIX = "//##";
51bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private static final String HEADER_PREFIX = "//##header";
52bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
53bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    public static void main(String[] args) {
54bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        new CodeMangler(args).run();
55bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
56bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
57bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private static final String usage = "Usage:\n" +
58bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "    CodeMangler [flags] file... dir... @argfile... \n" +
59bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "-in[dir] path          - root directory of input files, otherwise use current directory\n" +
60bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "-out[dir] path         - root directory of output files, otherwise use input directory\n" +
61bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "-s[uffix] string       - suffix of inputfiles to process, otherwise use '.java' (directories only)\n" +
62bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "-c[lean]               - remove all control flags from code on output (does not proceed if overwriting)\n" +
63bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "-r[ecurse]             - if present, recursively process subdirectories\n" +
64bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "-f[orce]               - force reprocessing of files even if timestamp and headers match\n" +
65bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "-t[imestamp]           - expect/write timestamp in header\n" +
66bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "-dNAME[=VALUE]         - define NAME with optional value VALUE\n" +
67bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "  (or -d NAME[=VALUE])\n" +
68bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "-n                     - do not put NAME/VALUE in header\n" +
69bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "-help                  - print this usage message and exit.\n" +
70bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "\n" +
71bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "For file arguments, output '.java' files using the same path/name under the output directory.\n" +
72bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "For directory arguments, process all files with the defined suffix in the directory.\n" +
73bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "  (if recursing, do the same for all files recursively under each directory)\n" +
74bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "For @argfile arguments, read the specified text file (strip the '@'), and process each line of that file as \n" +
75bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "an argument.\n" +
76bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "\n" +
77bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "Directives are one of the following:\n" +
78bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "  #ifdef, #ifndef, #else, #endif, #if, #elif, #define, #undef\n" +
79bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "These may optionally be preceeded by whitespace or //.\n" +
80bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "#if, #elif args are of the form 'key == value' or 'key != value'.\n" +
81bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "Only exact character match key with value is performed.\n" +
82bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        "#define args are 'key [==] value', the '==' is optional.\n";
83bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
84bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    CodeMangler(String[] args) {
85bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        map = new HashMap();
86bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        names = new ArrayList();
87bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        suffix = ".java";
88bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        clean = false;
89bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        timestamp = false;
90bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
91bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        String inname = null;
92bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        String outname = null;
93bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        boolean processArgs = true;
94bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        String arg = null;
95bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        try {
96bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            for (int i = 0; i < args.length; ++i) {
97bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                arg = args[i];
98bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if ("--".equals(arg)) {
99bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    processArgs = false;
100bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                } else if (processArgs && arg.charAt(0) == '-') {
101bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (arg.startsWith("-in")) {
102bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        inname = args[++i];
103bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (arg.startsWith("-out")) {
104bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        outname = args[++i];
105bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (arg.startsWith("-d")) {
106bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        String id = arg.substring(2);
107bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (id.length() == 0) {
108bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            id = args[++i];
109bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
110bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        String val = "";
111bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        int ix = id.indexOf('=');
112bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (ix >= 0) {
113bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            val = id.substring(ix+1);
114bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            id = id.substring(0,ix);
115bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
116bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        map.put(id, val);
117bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (arg.startsWith("-s")) {
118bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        suffix = args[++i];
119bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (arg.startsWith("-r")) {
120bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        recurse = true;
121bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (arg.startsWith("-f")) {
122bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        force = true;
123bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (arg.startsWith("-c")) {
124bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        clean = true;
125bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (arg.startsWith("-t")) {
126bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        timestamp = true;
127bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (arg.startsWith("-h")) {
128bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        System.out.print(usage);
129bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        break; // stop before processing arguments, so we will do nothing
130bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (arg.startsWith("-v")) {
131bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        verbose = true;
132bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (arg.startsWith("-n")) {
133bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        nonames = true;
134bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else {
135bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        System.err.println("Error: unrecognized argument '" + arg + "'");
136bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        System.err.println(usage);
137bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        throw new IllegalArgumentException(arg);
138bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
139bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                } else {
140bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (arg.charAt(0) == '@') {
141bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        File argfile = new File(arg.substring(1));
142bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (argfile.exists() && !argfile.isDirectory()) {
143bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            BufferedReader br = null;
144bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            try {
145bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                br = new BufferedReader(new InputStreamReader(new FileInputStream(argfile)));
146bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                ArrayList list = new ArrayList();
147bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                for (int x = 0; x < args.length; ++x) {
148bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    list.add(args[x]);
149bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                }
150bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                String line;
151bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                while (null != (line = br.readLine())) {
152bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    line = line.trim();
153bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    if (line.length() > 0 && line.charAt(0) != '#') {
154bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                        if (verbose) System.out.println("adding argument: " + line);
155bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                        list.add(line);
156bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    }
157bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                }
158bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                args = (String[])list.toArray(new String[list.size()]);
159bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            }
160bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            catch (IOException e) {
161bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                System.err.println("error reading arg file: " + e);
162bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            }
163bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            finally {
164bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                if (br != null) {
165bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    try {
166bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                        br.close();
167bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    } catch (Exception e){
168bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                        // ignore
169bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    }
170bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                }
171bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            }
172bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
173bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else {
174bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        names.add(arg);
175bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
176bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
177bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
178bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        } catch (IndexOutOfBoundsException e) {
179bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            String msg = "Error: argument '" + arg + "' missing value";
180bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            System.err.println(msg);
181bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            System.err.println(usage);
182bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            throw new IllegalArgumentException(msg);
183bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
184bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
185bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        String username = System.getProperty("user.dir");
186bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (inname == null) {
187bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            inname = username;
188bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        } else if (!(inname.startsWith("\\") || inname.startsWith("/"))) {
189bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            inname = username + File.separator + inname;
190bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
191bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        indir = new File(inname);
192bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        try {
193bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            indir = indir.getCanonicalFile();
194bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
195bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        catch (IOException e) {
196bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            // continue, but most likely we'll fail later
197bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
198bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (!indir.exists()) {
199bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            throw new IllegalArgumentException("Input directory '" + indir.getAbsolutePath() + "' does not exist.");
200bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        } else if (!indir.isDirectory()) {
201bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            throw new IllegalArgumentException("Input path '" + indir.getAbsolutePath() + "' is not a directory.");
202bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
203bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (verbose) System.out.println("indir: " + indir.getAbsolutePath());
204bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
205bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (outname == null) {
206bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            outname = inname;
207bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        } else if (!(outname.startsWith("\\") || outname.startsWith("/"))) {
208bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            outname = username + File.separator + outname;
209bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
210bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        outdir = new File(outname);
211bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        try {
212bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            outdir = outdir.getCanonicalFile();
213bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
214bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        catch (IOException e) {
215bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            // continue, but most likely we'll fail later
216bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
217bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (!outdir.exists()) {
218bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            throw new IllegalArgumentException("Output directory '" + outdir.getAbsolutePath() + "' does not exist.");
219bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        } else if (!outdir.isDirectory()) {
220bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            throw new IllegalArgumentException("Output path '" + outdir.getAbsolutePath() + "' is not a directory.");
221bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
222bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (verbose) System.out.println("outdir: " + outdir.getAbsolutePath());
223bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
224bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (clean && suffix.equals(".java")) {
225bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            try {
226bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (outdir.getCanonicalPath().equals(indir.getCanonicalPath())) {
227bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    throw new IllegalArgumentException("Cannot use 'clean' to overwrite .java files in same directory tree");
228bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
229bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
230bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            catch (IOException e) {
231bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                System.err.println("possible overwrite, error: " + e.getMessage());
232bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                throw new IllegalArgumentException("Cannot use 'clean' to overrwrite .java files");
233bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
234bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
235bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
236bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (names.isEmpty()) {
237bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            names.add(".");
238bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
239bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
240bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        TreeMap sort = new TreeMap(String.CASE_INSENSITIVE_ORDER);
241bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        sort.putAll(map);
242bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        Iterator iter = sort.entrySet().iterator();
243bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        StringBuffer buf = new StringBuffer();
244bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (!nonames) {
245bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            while (iter.hasNext()) {
246bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                Map.Entry e = (Map.Entry)iter.next();
247bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (buf.length() > 0) {
248bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    buf.append(", ");
249bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
250bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                buf.append(e.getKey());
251bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                String v = (String)e.getValue();
252bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (v != null && v.length() > 0) {
253bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    buf.append('=');
254bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    buf.append(v);
255bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
256bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
257bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
258bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        header = buf.toString();
259bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
260bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
261bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    public int run() {
262bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return process("", (String[])names.toArray(new String[names.size()]));
263bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
264bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
265bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    public int process(String path, String[] filenames) {
266bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (verbose) System.out.println("path: '" + path + "'");
267bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int count = 0;
268bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        for (int i = 0; i < filenames.length; ++i) {
269bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (verbose) System.out.println("name " + i + " of " + filenames.length + ": '" + filenames[i] + "'");
270bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            String name = path + filenames[i];
271bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            File fin = new File(indir, name);
272bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            try {
273bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                fin = fin.getCanonicalFile();
274bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
275bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            catch (IOException e) {
276bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
277bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (!fin.exists()) {
278bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                System.err.println("File " + fin.getAbsolutePath() + " does not exist.");
279bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                continue;
280bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
281bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (fin.isFile()) {
282bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (verbose) System.out.println("processing file: '" + fin.getAbsolutePath() + "'");
283bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                String oname;
284bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                int ix = name.lastIndexOf(".");
285bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (ix != -1) {
286bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    oname = name.substring(0, ix);
287bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                } else {
288bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    oname = name;
289bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
290bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                oname += ".java";
291bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                File fout = new File(outdir, oname);
292bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (processFile(fin, fout)) {
293bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    ++count;
294bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
295bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            } else if (fin.isDirectory()) {
296bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (verbose) System.out.println("recursing on directory '" + fin.getAbsolutePath() + "'");
297bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                String npath = ".".equals(name) ? path : path + fin.getName() + File.separator;
298bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                count += process(npath, fin.list(filter)); // recursive call
299bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
300bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
301bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return count;
302bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
303bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
304bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
305bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private final FilenameFilter filter = new FilenameFilter() {
306bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            public boolean accept(File dir, String name) {
307bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                File f = new File(dir, name);
308bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return (f.isFile() && name.endsWith(suffix)) || (f.isDirectory() && recurse);
309bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
310bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        };
311bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
312bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    public boolean processFile(File infile, File outfile) {
313bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        File backup = null;
314bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
315bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        class State {
316bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            int lc;
317bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            String line;
318bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            boolean emit = true;
319bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            boolean tripped;
320bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            private State next;
321bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
322bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            public String toString() {
323bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return "line " + lc
324bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    + ": '" + line
325bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    + "' (emit: " + emit
326bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    + " tripped: " + tripped
327bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    + ")";
328bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
329bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
330bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            void trip(boolean trip) {
331bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (!tripped & trip) {
332bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    tripped = true;
333bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    emit = next != null ? next.emit : true;
334bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                } else {
335bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    emit = false;
336bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
337bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
338bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
339bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            State push(int lc, String line, boolean trip) {
340bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                this.lc = lc;
341bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                this.line = line;
342bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                State ret = new State();
343bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                ret.next = this;
344bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                ret.emit = this.emit & trip;
345bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                ret.tripped = trip;
346bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return ret;
347bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
348bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
349bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            State pop() {
350bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return next;
351bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
352bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
353bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
354bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        HashMap oldMap = null;
355bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
356bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        long outModTime = 0;
357bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
358bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        try {
359bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            PrintStream outstream = null;
360bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            InputStream instream = new FileInputStream(infile);
361bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
362bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            BufferedReader reader = new BufferedReader(new InputStreamReader(instream));
363bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            int lc = 0;
364bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            State state = new State();
365bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            String line;
366bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            while ((line = reader.readLine()) != null) {
367bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (lc == 0) { // check and write header for output file if needed
368bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    boolean hasHeader = line.startsWith(HEADER_PREFIX);
369bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (hasHeader && !force) {
370bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        long expectLastModified = ((infile.lastModified() + 999)/1000)*1000;
371bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        String headerline = HEADER_PREFIX;
372bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (header.length() > 0) {
373bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            headerline += " ";
374bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            headerline += header;
375bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
376bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (timestamp) {
377bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            headerline += " ";
378bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            headerline += String.valueOf(expectLastModified);
379bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
380bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (line.equals(headerline)) {
381bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            if (verbose) System.out.println("no changes necessary to " + infile.getCanonicalPath());
382bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            reader.close();
383bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            return false; // nothing to do
384bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
385bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (verbose) {
386bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            System.out.println("  old header:  " + line);
387bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            System.out.println("  != expected: " + headerline);
388bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
389bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
390bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
391bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    // create output file directory structure
392bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    String outpname = outfile.getParent();
393bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (outpname != null) {
394bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        File outp = new File(outpname);
395bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (!(outp.exists() || outp.mkdirs())) {
396bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            System.err.println("could not create directory: '" + outpname + "'");
397bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            reader.close();
398bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            return false;
399bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
400bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
401bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
402bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    // if we're overwriting, use a temporary file
403bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (suffix.equals(".java")) {
404bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        backup = outfile;
405bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        try {
406bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            outfile = File.createTempFile(outfile.getName(), null, outfile.getParentFile());
407bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
408bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        catch (IOException ex) {
409bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            System.err.println(ex.getMessage());
410bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            reader.close();
411bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            return false;
412bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
413bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
414bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
415bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    outModTime = ((outfile.lastModified()+999)/1000)*1000; // round up
416bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    outstream = new PrintStream(new FileOutputStream(outfile));
417bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    String headerline = HEADER_PREFIX;
418bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (header.length() > 0) {
419bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        headerline += " ";
420bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        headerline += header;
421bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
422bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (timestamp) {
423bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        headerline += " ";
424bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        headerline += String.valueOf(outModTime);
425bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
426bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    outstream.println(headerline);
427bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (verbose) System.out.println("header: " + headerline);
428bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
429bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    // discard the old header if we had one, otherwise match this line like any other
430bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (hasHeader) {
431bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        ++lc; // mark as having read a line so we never reexecute this block
432bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        continue;
433bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
434bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
435bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
436bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                String[] res = new String[3];
437bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (patMatch(line, res)) {
438bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    String lead = res[0];
439bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    String key = res[1];
440bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    String val = res[2];
441bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
442bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (verbose) System.out.println("directive: " + line
443bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                                    + " key: '" + key
444bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                                    + "' val: '" + val
445bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                                    + "' " + state);
446bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (key.equals("ifdef")) {
447bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        state = state.push(lc, line, map.get(val) != null);
448bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (key.equals("ifndef")) {
449bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        state = state.push(lc, line, map.get(val) == null);
450bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (key.equals("else")) {
451bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        state.trip(true);
452bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (key.equals("endif")) {
453bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        state = state.pop();
454bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (key.equals("undef")) {
455bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (state.emit) {
456bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            if (oldMap == null) {
457bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                oldMap = (HashMap)map.clone();
458bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            }
459bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            map.remove(val);
460bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
461bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else if (key.equals("define")) {
462bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (pat2Match(val, res)) {
463bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            String key2 = res[0];
464bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            String val2 = res[2];
465bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
466bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            if (verbose) System.out.println("val2: '" + val2
467bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                                            + "' key2: '" + key2
468bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                                            + "'");
469bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            if (state.emit) {
470bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                if (oldMap == null) {
471bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    oldMap = (HashMap)map.clone();
472bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                }
473bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                map.put(key2, val2);
474bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            }
475bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
476bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else { // #if, #elif
477bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        // only top level OR (||) operator is supported for now
478bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        int count = 1;
479bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        int index = 0;
480bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        while ((index = val.indexOf("||", index)) > 0) {
481bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            count++;
482bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            index++;
483bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
484bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        String[] expressions = new String[count];
485bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (count == 1) {
486bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            expressions[0] = val;
487bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        } else {
488bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            int start = 0;
489bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            index = 0;
490bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            count = 0;
491bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            while (true) {
492bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                index = val.indexOf("||", start);
493bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                if (index > 0) {
494bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    expressions[count++] = val.substring(start, index);
495bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    start = index + 2;
496bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                } else {
497bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    expressions[count++] = val.substring(start);
498bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    break;
499bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                }
500bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            }
501bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
502bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        boolean eval = false;
503bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        for (count = 0; count < expressions.length && !eval; count++) {
504bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            if (pat2Match(expressions[count], res)) {
505bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                String key2 = res[0];
506bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                String val2 = res[2];
507bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
508bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                if (key2.equals("defined")) {
509bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    // defined command
510bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    if (verbose) System.out.println(
511bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                            "index: '" + count
512bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                            + "' val2: '" + val2
513bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                            + "' key2: '" + key2
514bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                            + "'");
515bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    eval = map.containsKey(val2);
516bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                } else {
517bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    boolean neq = false;
518bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    if (res[1].equals("!=")) {
519bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                        neq = true;
520bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    } else if (!res[1].equals("==")) {
521bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                        System.err.println("Invalid expression: '" + val);
522bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    }
523bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    if (verbose) System.out.println(
524bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                            "index: '" + count
525bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                            + "' val2: '" + val2
526bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                            + "' neq: '" + neq
527bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                            + "' key2: '" + key2
528bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                            + "'");
529bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                    eval = (val2.equals(map.get(key2)) != neq);
530bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                }
531bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            }
532bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
533bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (key.equals("if")) {
534bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            state = state.push(lc, line, eval);
535bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        } else if (key.equals("elif")) {
536bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            state.trip(eval);
537bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
538bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
539bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (!clean) {
540bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        lc++;
541bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        if (!lead.equals("//")) {
542bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            outstream.print("//");
543bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                            line = line.substring(lead.length());
544bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        }
545bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        outstream.println(line);
546bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
547bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    continue;
548bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
549bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
550bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                lc++;
551bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                String found = pat3Match(line);
552bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                boolean hasIgnore = found != null;
553bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (state.emit == hasIgnore) {
554bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (state.emit) {
555bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        line = line.substring(found.length());
556bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    } else {
557bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        line = IGNORE_PREFIX + line;
558bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
559bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                } else if (hasIgnore && !found.equals(IGNORE_PREFIX)) {
560bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    line = IGNORE_PREFIX + line.substring(found.length());
561bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
562bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (!clean || state.emit) {
563bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    outstream.println(line);
564bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
565bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
566bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
567bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            state = state.pop();
568bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (state != null) {
569bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                System.err.println("Error: unclosed directive(s):");
570bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                do {
571bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    System.err.println(state);
572bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                } while ((state = state.pop()) != null);
573bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                System.err.println(" in file: " + outfile.getCanonicalPath());
574bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (oldMap != null) {
575bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    map = oldMap;
576bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
577bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                reader.close();
578bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                outstream.close();
579bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return false;
580bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
581bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
582bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            outstream.close();
583bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            instream.close();
584bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
585bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (backup != null) {
586bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (backup.exists()) {
587bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    backup.delete();
588bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
589bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                outfile.renameTo(backup);
590bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
591bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
592bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (timestamp) {
593bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                outfile.setLastModified(outModTime); // synch with timestamp
594bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
595bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
596bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (oldMap != null) {
597bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                map = oldMap;
598bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
599bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
600bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        catch (IOException e) {
601bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            System.err.println(e);
602bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return false;
603bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
604bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return true;
605bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
606bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
607bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
608bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /**
609bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * Perform same operation as matching on pat.  on exit
610bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * leadKeyValue contains the three strings lead, key, and value.
611bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * 'lead' is the portion before the #ifdef directive.  'key' is
612bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * the directive.  'value' is the portion after the directive.  if
613bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * there is a match, return true, else return false.
614bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     */
615bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    static boolean patMatch(String line, String[] leadKeyValue) {
616bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (line.length() == 0) {
617bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return false;
618bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
619bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (!line.endsWith("\n")) {
620bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            line = line + '\n';
621bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
622bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int mark = 0;
623bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int state = 0;
624bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        loop: for (int i = 0; i < line.length(); ++i) {
625bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            char c = line.charAt(i);
626bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            switch (state) {
627bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 0: // at start of line, haven't seen anything but whitespace yet
628bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == ' ' || c == '\t' || c == '\r') continue;
629bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '/') { state = 1; continue; }
630bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '#') { state = 4; continue; }
631bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return false;
632bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 1: // have seen a single slash after start of line
633bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '/') { state = 2; continue; }
634bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return false;
635bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 2: // have seen two or more slashes
636bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '/') continue;
637bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == ' ' || c == '\t' || c == '\r') { state = 3; continue; }
638bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '#') { state = 4; continue; }
639bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return false;
640bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 3: // have seen a space after two or more slashes
641bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == ' ' || c == '\t' || c == '\r') continue;
642bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '#') { state = 4; continue; }
643bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return false;
644bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 4: // have seen a '#'
645bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                leadKeyValue[0] = line.substring(mark, i-1);
646bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { mark = i; state = 5; continue; }
647bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return false;
648bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 5: // an ascii char followed the '#'
649bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) continue;
650bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == ' ' || c == '\t' || c == '\n') {
651bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    String key = line.substring(mark, i).toLowerCase();
652bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (key.equals("ifdef") ||
653bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        key.equals("ifndef") ||
654bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        key.equals("else") ||
655bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        key.equals("endif") ||
656bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        key.equals("undef") ||
657bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        key.equals("define") ||
658bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        key.equals("if") ||
659bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        key.equals("elif")) {
660bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        leadKeyValue[1] = key;
661bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        mark = i;
662bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        state = 6;
663bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        break loop;
664bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
665bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
666bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return false;
667bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            default:
668bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                throw new IllegalStateException();
669bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
670bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
671bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (state == 6) {
672bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            leadKeyValue[2] = line.substring(mark, line.length()).trim();
673bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return true;
674bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
675bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return false; // never reached, does the compiler know this?
676bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
677bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
678bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /**
679bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * Perform same operation as matching on pat2.  on exit
680bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * keyRelValue contains the three strings key, rel, and value.
681bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * 'key' is the portion before the relation (or final word).  'rel' is
682bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * the relation, if present, either == or !=.  'value' is the final
683bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * word.  if there is a match, return true, else return false.
684bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     */
685bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    static boolean pat2Match(String line, String[] keyRelVal) {
686bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
687bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (line.length() == 0) {
688bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return false;
689bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
690bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        keyRelVal[0] = keyRelVal[1] = keyRelVal[2] = "";
691bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int mark = 0;
692bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int state = 0;
693bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        String command = null;
694bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        loop: for (int i = 0; i < line.length(); ++i) {
695bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            char c = line.charAt(i);
696bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            switch (state) {
697bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 0: // saw beginning or space, no rel yet
698bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == ' ' || c == '\t' || c == '\n') {
699bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    continue;
700bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
701bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if ((c == '!' || c == '=')) {
702bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    return false;
703bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
704bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                state = 1;
705bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                continue;
706bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 1: // saw start of a word
707bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == ' ' || c == '\t') {
708bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    state = 2;
709bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
710bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                else if (c == '(') {
711bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    command = line.substring(0, i).trim();
712bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    if (!command.equals("defined")) {
713bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        return false;
714bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    }
715bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    keyRelVal[0] = command;
716bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    state = 2;
717bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
718bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                else if (c == '!' || c == '=') {
719bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    state = 3;
720bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
721bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                continue;
722bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 2: // saw end of word, and space
723bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == ' ' || c == '\t') {
724bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    continue;
725bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
726bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                else if (command == null && c == '(') {
727bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    continue;
728bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
729bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                else if (c == '!' || c == '=') {
730bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    state = 3;
731bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    continue;
732bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
733bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                keyRelVal[0] = line.substring(0, i-1).trim();
734bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                mark = i;
735bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                state = 4;
736bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                break loop;
737bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 3: // saw end of word, and '!' or '='
738bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '=') {
739bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    keyRelVal[0] = line.substring(0, i-1).trim();
740bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    keyRelVal[1] = line.substring(i-1, i+1);
741bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    mark = i+1;
742bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    state = 4;
743bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    break loop;
744bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
745bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                return false;
746bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            default:
747bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                break;
748bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
749bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
750bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        switch (state) {
751bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        case 0:
752bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return false; // found nothing
753bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        case 1:
754bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        case 2:
755bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            keyRelVal[0] = line.trim(); break; // found only a word
756bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        case 3:
757bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return false; // found a word and '!' or '=" then end of line, incomplete
758bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        case 4:
759bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            keyRelVal[2] = line.substring(mark).trim(); // found a word, possible rel, and who knows what
760bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (command != null) {
761bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                int len = keyRelVal[2].length();
762bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (keyRelVal[2].charAt(len - 1) != ')') {
763bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    // closing parenthesis is missing
764bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    return false;
765bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
766bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                keyRelVal[2] = keyRelVal[2].substring(0, len - 1).trim();
767bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
768bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            break;
769bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        default:
770bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            throw new IllegalStateException();
771bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
772bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return true;
773bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
774bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
775bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    static String pat3Match(String line) {
776bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int state = 0;
777bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        loop: for (int i = 0; i < line.length(); ++i) {
778bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            char c = line.charAt(i);
779bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            switch(state) {
780bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 0: if (c == ' ' || c == '\t') continue;
781bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '/') { state = 1; continue; }
782bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                break loop;
783bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 1:
784bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '/') { state = 2; continue; }
785bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                break loop;
786bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 2:
787bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '#') { state = 3; continue; }
788bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                break loop;
789bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            case 3:
790bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (c == '#') return line.substring(0, i+1);
791bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                break loop;
792bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            default:
793bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                break loop;
794bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
795bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
796bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return null;
797bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
798bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert}
799