Options.java revision 2981:d1e5707cd4eb
11592Srgrimes/*
21592Srgrimes * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
31592Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
41592Srgrimes *
51592Srgrimes * This code is free software; you can redistribute it and/or modify it
61592Srgrimes * under the terms of the GNU General Public License version 2 only, as
71592Srgrimes * published by the Free Software Foundation.  Oracle designates this
81592Srgrimes * particular file as subject to the "Classpath" exception as provided
91592Srgrimes * by Oracle in the LICENSE file that accompanied this code.
101592Srgrimes *
111592Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT
121592Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131592Srgrimes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
141592Srgrimes * version 2 for more details (a copy is included in the LICENSE file that
151592Srgrimes * accompanied this code).
161592Srgrimes *
171592Srgrimes * You should have received a copy of the GNU General Public License version
181592Srgrimes * 2 along with this work; if not, write to the Free Software Foundation,
191592Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
201592Srgrimes *
211592Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
221592Srgrimes * or visit www.oracle.com if you need additional information or have any
231592Srgrimes * questions.
241592Srgrimes */
251592Srgrimes
261592Srgrimespackage com.sun.tools.sjavac.options;
271592Srgrimes
281592Srgrimesimport java.nio.file.Path;
291592Srgrimesimport java.util.ArrayList;
301592Srgrimesimport java.util.Arrays;
311592Srgrimesimport java.util.Collection;
321592Srgrimesimport java.util.HashMap;
331592Srgrimesimport java.util.List;
341592Srgrimesimport java.util.Map;
351592Srgrimesimport java.util.Set;
361592Srgrimesimport java.util.HashSet;
371592Srgrimes
381592Srgrimesimport com.sun.tools.sjavac.Transformer;
391592Srgrimesimport com.sun.tools.sjavac.Util;
401592Srgrimes
411592Srgrimes/**
421592Srgrimes * Instances of this class represent values for sjavac command line options.
431592Srgrimes *
4431329Scharnier *  <p><b>This is NOT part of any supported API.
451592Srgrimes *  If you write code that depends on this, you do so at your own risk.
4631329Scharnier *  This code and its internal interfaces are subject to change or
4731329Scharnier *  deletion without notice.</b>
4850476Speter */
491592Srgrimespublic class Options {
501592Srgrimes
511592Srgrimes    // Output directories
521592Srgrimes    private Path destDir, genSrcDir, headerDir, stateDir;
531592Srgrimes
541592Srgrimes    // Input directories
551592Srgrimes    private List<SourceLocation> sources = new ArrayList<>();
561592Srgrimes    private List<SourceLocation> sourceSearchPaths = new ArrayList<>();
571592Srgrimes    private List<SourceLocation> classSearchPaths = new ArrayList<>();
581592Srgrimes    private List<SourceLocation> moduleSearchPaths = new ArrayList<>();
591592Srgrimes
601592Srgrimes    private String logLevel = "info";
6192090Smaxim
6292272Smaxim    private Set<String> permitted_artifacts = new HashSet<>();
6392090Smaxim    private boolean permitUnidentifiedArtifacts = false;
6456668Sshin    private boolean permitSourcesInDefaultPackage = false;
651592Srgrimes
661592Srgrimes    private Path sourceReferenceList;
671592Srgrimes    private int numCores = 4;
681592Srgrimes    private String implicitPolicy = "none";
691592Srgrimes    private List<String> javacArgs = new ArrayList<>();
701592Srgrimes
711592Srgrimes    private Map<String, Transformer> trRules = new HashMap<>();
721592Srgrimes
731592Srgrimes    private boolean startServer = false;
741592Srgrimes
75109380Syar    // Server configuration string
761592Srgrimes    private String serverConf;
7756668Sshin
78110037Syar    /** Get the policy for implicit classes */
791592Srgrimes    public String getImplicitPolicy() {
801592Srgrimes        return implicitPolicy;
811592Srgrimes    }
82110036Syar
8317435Spst    /** Get the path for generated sources (or null if no such path is set) */
841592Srgrimes    public Path getGenSrcDir() {
851592Srgrimes        return genSrcDir;
861592Srgrimes    }
8776096Smarkm
881592Srgrimes    /** Get the path for the destination directory */
891592Srgrimes    public Path getDestDir() {
901592Srgrimes        return destDir;
9127650Sdavidn    }
921592Srgrimes
931592Srgrimes    /** Get the path for the header directory (or null if no such path is set) */
941592Srgrimes    public Path getHeaderDir() {
9570102Sphk        return headerDir;
9670102Sphk    }
9782460Snik
9882796Ssheldonh    /** Get the path for the state directory, defaults to destDir. */
99100684Syar    public Path getStateDir() {
1001592Srgrimes        return stateDir != null ? stateDir : destDir;
1011592Srgrimes    }
1021592Srgrimes
1031592Srgrimes    /** Get all source locations for files to be compiled */
1041592Srgrimes    public List<SourceLocation> getSources() {
1051592Srgrimes        return sources;
10689935Syar    }
1071592Srgrimes
10888935Sdwmalone    /**
1091592Srgrimes     * Get all paths to search for classes in .java format. (Java-files in
11056668Sshin     * found here should not be compiled.
11156668Sshin     */
1121592Srgrimes    public List<SourceLocation> getSourceSearchPaths() {
1131592Srgrimes        return sourceSearchPaths;
1141592Srgrimes    }
11592272Smaxim
11692272Smaxim    /** Get all paths to search for classes in. */
11792272Smaxim    public List<SourceLocation> getClassSearchPath() {
11892272Smaxim        return classSearchPaths;
1191592Srgrimes    }
1201592Srgrimes
1211592Srgrimes    /** Get all paths to search for modules in. */
1221592Srgrimes    public List<SourceLocation> getModuleSearchPaths() {
1231592Srgrimes        return moduleSearchPaths;
1241592Srgrimes    }
12556668Sshin
1261592Srgrimes    /** Get the log level. */
1271592Srgrimes    public String getLogLevel() {
1281592Srgrimes        return logLevel;
1291592Srgrimes    }
1301592Srgrimes
1311592Srgrimes    /** Returns true iff the artifact is permitted in the output dir. */
1321592Srgrimes    public boolean isUnidentifiedArtifactPermitted(String f) {
1331592Srgrimes        return permitted_artifacts.contains(f);
1341592Srgrimes    }
1351592Srgrimes
13656668Sshin    /** Returns true iff artifacts in the output directories should be kept,
1371592Srgrimes     * even if they would not be generated in a clean build. */
13875535Sphk    public boolean areUnidentifiedArtifactsPermitted() {
1391592Srgrimes        return permitUnidentifiedArtifacts;
140102565Syar    }
1411592Srgrimes
1421592Srgrimes    /** Returns true iff sources in the default package should be permitted. */
14392272Smaxim    public boolean isDefaultPackagePermitted() {
1441592Srgrimes        return permitSourcesInDefaultPackage;
14592272Smaxim    }
14692272Smaxim
14792272Smaxim    /** Get the path to the list of reference sources (or null if none is set) */
14875567Speter    public Path getSourceReferenceList() {
149102565Syar        return sourceReferenceList;
1501592Srgrimes    }
1511592Srgrimes
1521592Srgrimes    /** Get the number of cores to be used by sjavac */
1531592Srgrimes    public int getNumCores() {
1541592Srgrimes        return numCores;
1551592Srgrimes    }
1561592Srgrimes
1571592Srgrimes    /** Returns all arguments relevant to javac but irrelevant to sjavac. */
1581592Srgrimes    public List<String> getJavacArgs() {
15988935Sdwmalone        return javacArgs;
16088935Sdwmalone    }
1611592Srgrimes
1621592Srgrimes    /**
1631592Srgrimes     * Get a map which maps suffixes to transformers (for example
1641592Srgrimes     * ".java" {@literal ->} CompileJavaPackages)
1651592Srgrimes     */
1661592Srgrimes    public Map<String, Transformer> getTranslationRules() {
1671592Srgrimes        return trRules;
1681592Srgrimes    }
1691592Srgrimes
1701592Srgrimes    /** Return true iff a new server should be started */
1711592Srgrimes    public boolean startServerFlag() {
1721592Srgrimes        return startServer;
1731592Srgrimes    }
1741592Srgrimes
1751592Srgrimes    /** Return the server configuration string. */
1761592Srgrimes    public String getServerConf() {
1771592Srgrimes        return serverConf;
17875556Sgreen    }
17975556Sgreen
18075556Sgreen    /**
18175556Sgreen     * Parses the given argument array and returns a corresponding Options
18217433Spst     * instance.
1831592Srgrimes     */
18456668Sshin    public static Options parseArgs(String... args) {
18556668Sshin        Options options = new Options();
18656668Sshin        options.new ArgDecoderOptionHelper().traverse(args);
18756668Sshin        return options;
18856668Sshin    }
18956668Sshin
19056668Sshin    /** Returns true iff a .java file is among the javac arguments */
19156668Sshin    public boolean isJavaFilesAmongJavacArgs() {
19256668Sshin        for (String javacArg : javacArgs)
19356668Sshin            if (javacArg.endsWith(".java"))
19456668Sshin                return true;
19556668Sshin        return false;
19656668Sshin    }
19756668Sshin
19856668Sshin    /**
19956668Sshin     * Returns a string representation of the options that affect the result of
20056668Sshin     * the compilation. (Used for saving the state of the options used in a
20156668Sshin     * previous compile.)
20256668Sshin     */
203132925Syar    public String getStateArgsString() {
20456668Sshin
20556668Sshin        // Local utility class for collecting the arguments
20656668Sshin        class StateArgs {
20756668Sshin
20856668Sshin            private List<String> args = new ArrayList<>();
20956668Sshin
21056668Sshin            void addArg(Option opt) {
21156668Sshin                args.add(opt.arg);
21256668Sshin            }
21356668Sshin
21456668Sshin            void addArg(Option opt, Object val) {
21556668Sshin                addArg(opt);
21656668Sshin                args.add(val.toString());
21756668Sshin            }
21856668Sshin
21956668Sshin            void addSourceLocations(Option opt, List<SourceLocation> locs) {
22056668Sshin                for (SourceLocation sl : locs) {
22156668Sshin                    for (String pkg : sl.includes) addArg(Option.I, pkg);
22256668Sshin                    for (String pkg : sl.excludes) addArg(Option.X, pkg);
22356668Sshin                    for (String f : sl.excludedFiles) addArg(Option.XF, f);
22456668Sshin                    for (String f : sl.includedFiles) addArg(Option.IF, f);
225132925Syar                    addArg(opt, sl.getPath());
22656668Sshin                }
22756668Sshin            }
22856668Sshin
22956668Sshin            String getResult() {
23056668Sshin                String result = "";
23156668Sshin                for (String s : args)
23256668Sshin                    result += s + " ";
23356668Sshin                return result.trim();
23456668Sshin            }
23556668Sshin
23656668Sshin            public void addAll(Collection<String> toAdd) {
23756668Sshin                args.addAll(toAdd);
23856668Sshin            }
23956668Sshin        }
24056668Sshin
24156668Sshin        StateArgs args = new StateArgs();
24256668Sshin
24356668Sshin        // Directories
24456668Sshin        if (genSrcDir != null)
24556668Sshin            args.addArg(Option.S, genSrcDir.normalize());
24676096Smarkm
24756668Sshin        if (headerDir != null)
24856668Sshin            args.addArg(Option.H, headerDir.normalize());
24976096Smarkm
25056668Sshin        if (destDir != null)
25156668Sshin            args.addArg(Option.D, destDir.normalize());
25256668Sshin
25356668Sshin        if (stateDir != null)
25456668Sshin            args.addArg(Option.STATE_DIR, stateDir.normalize());
25556668Sshin
25656668Sshin        // Source roots
25756668Sshin        args.addSourceLocations(Option.SRC, sources);
25856668Sshin        args.addSourceLocations(Option.SOURCEPATH, sourceSearchPaths);
25956668Sshin        args.addSourceLocations(Option.CLASSPATH,  classSearchPaths);
26056668Sshin        args.addSourceLocations(Option.MODULEPATH, moduleSearchPaths);
26156668Sshin
26256668Sshin        // Boolean options
26356668Sshin        if (permitSourcesInDefaultPackage)
26417433Spst            args.addArg(Option.PERMIT_SOURCES_WITHOUT_PACKAGE);
26556668Sshin
26617433Spst        for (String f : permitted_artifacts) {
26756668Sshin            args.addArg(Option.PERMIT_ARTIFACT, f);
26856668Sshin        }
26976096Smarkm
27056668Sshin        if (permitUnidentifiedArtifacts)
27156668Sshin            args.addArg(Option.PERMIT_UNIDENTIFIED_ARTIFACTS);
2721592Srgrimes
27356668Sshin        // Translation rules
27456668Sshin        for (Map.Entry<String, Transformer> tr : trRules.entrySet()) {
27556668Sshin            String val = tr.getKey() + "=" + tr.getValue().getClass().getName();
27656668Sshin            args.addArg(Option.TR, val);
27756668Sshin        }
27856668Sshin
27956668Sshin        // Javac args
28056668Sshin        args.addAll(javacArgs);
28156668Sshin
28256668Sshin        return args.getResult();
28356668Sshin    }
28456668Sshin
28556668Sshin
28656668Sshin    /** Extract the arguments to be passed on to javac. */
28756668Sshin    public String[] prepJavacArgs() {
28856668Sshin        List<String> args = new ArrayList<>();
28956668Sshin
29056668Sshin        // Output directories
29156668Sshin        args.add("-d");
29256668Sshin        args.add(destDir.toString());
29356668Sshin
29456668Sshin        if (getGenSrcDir() != null) {
29556668Sshin            args.add("-s");
29656668Sshin            args.add(genSrcDir.toString());
29756668Sshin        }
29856668Sshin
29956668Sshin        if (headerDir != null) {
30056668Sshin            args.add("-h");
30156668Sshin            args.add(headerDir.toString());
30256668Sshin        }
30356668Sshin
30456668Sshin        // Prep sourcepath
30556668Sshin        List<SourceLocation> sourcepath = new ArrayList<>();
30656668Sshin        sourcepath.addAll(sources);
30756668Sshin        sourcepath.addAll(sourceSearchPaths);
30856668Sshin        if (sourcepath.size() > 0) {
30956668Sshin            args.add("-sourcepath");
31056668Sshin            args.add(concatenateSourceLocations(sourcepath));
31156668Sshin        }
31256668Sshin
31356668Sshin        // Prep classpath
31456668Sshin        if (classSearchPaths.size() > 0) {
31556668Sshin            args.add("-classpath");
31656668Sshin            args.add(concatenateSourceLocations(classSearchPaths));
31756668Sshin        }
31856668Sshin
31956668Sshin        // Enable dependency generation
32056668Sshin        args.add("-XDcompletionDeps=source,class");
32156668Sshin
32256668Sshin        // This can't be anything but 'none'. Enforced by sjavac main method.
32356668Sshin        args.add("-implicit:" + implicitPolicy);
32456668Sshin
32588935Sdwmalone        // If this option is not used, Object for instance is erroneously
32688935Sdwmalone        // picked up from PLATFORM_CLASS_PATH instead of CLASS_PATH.
3271592Srgrimes        //
32817433Spst        // Discussing this further led to the decision of letting bootclasspath
3291592Srgrimes        // be a dummy (empty) directory when building the JDK.
33056668Sshin        //args.add("-XXuserPathsFirst");
33156668Sshin
33256668Sshin        // Append javac-options (i.e. pass through options not recognized by
33317433Spst        // sjavac to javac.)
3341592Srgrimes        args.addAll(javacArgs);
33556668Sshin
33656668Sshin        return args.toArray(new String[args.size()]);
33756668Sshin    }
33856668Sshin
33956668Sshin    // Helper method to join a list of source locations separated by
34056668Sshin    // File.pathSeparator
34156668Sshin    private static String concatenateSourceLocations(List<SourceLocation> locs) {
34270102Sphk        String s = "";
34356668Sshin        for (SourceLocation loc : locs)
34456668Sshin            s += (s.isEmpty() ? "" : java.io.File.pathSeparator) + loc.getPath();
34556668Sshin        return s;
34692272Smaxim    }
34756668Sshin
34856668Sshin    // OptionHelper that records the traversed options in this Options instance.
34956668Sshin    private class ArgDecoderOptionHelper extends OptionHelper {
35056668Sshin
35156668Sshin        List<String> includes, excludes, includeFiles, excludeFiles;
35256668Sshin        {
35356668Sshin            resetFilters();
35456668Sshin        }
35556668Sshin
35656668Sshin        boolean headerProvided = false;
35756668Sshin        boolean genSrcProvided = false;
35856668Sshin        boolean stateProvided = false;
35956668Sshin
36056668Sshin        @Override
36156668Sshin        public void reportError(String msg) {
36270102Sphk            throw new IllegalArgumentException(msg);
36356668Sshin        }
36456668Sshin
36556668Sshin        @Override
36656668Sshin        public void sourceRoots(List<Path> paths) {
36756668Sshin            sources.addAll(createSourceLocations(paths));
36856668Sshin        }
36956668Sshin
37070102Sphk        @Override
37156668Sshin        public void exclude(String exclPattern) {
37256668Sshin            exclPattern = Util.normalizeDriveLetter(exclPattern);
37356668Sshin            excludes.add(exclPattern);
37456668Sshin        }
37571278Sjedgar
3761592Srgrimes        @Override
37771278Sjedgar        public void include(String inclPattern) {
37871278Sjedgar            inclPattern = Util.normalizeDriveLetter(inclPattern);
3791592Srgrimes            includes.add(inclPattern);
38071278Sjedgar        }
38171278Sjedgar
38271278Sjedgar        @Override
38371278Sjedgar        public void excludeFile(String exclFilePattern) {
38471278Sjedgar            exclFilePattern = Util.normalizeDriveLetter(exclFilePattern);
38571278Sjedgar            excludeFiles.add(exclFilePattern);
38671278Sjedgar        }
38771278Sjedgar
3881592Srgrimes        @Override
38971278Sjedgar        public void includeFile(String inclFilePattern) {
39071278Sjedgar            inclFilePattern = Util.normalizeDriveLetter(inclFilePattern);
39171278Sjedgar            includeFiles.add(inclFilePattern);
3921592Srgrimes        }
39371278Sjedgar
39471278Sjedgar        @Override
39571278Sjedgar        public void addTransformer(String suffix, Transformer tr) {
39671278Sjedgar            if (trRules.containsKey(suffix)) {
3971592Srgrimes                reportError("More than one transformer specified for " +
39871278Sjedgar                            "suffix " + suffix + ".");
399103949Smike                return;
40071278Sjedgar            }
40171278Sjedgar            trRules.put(suffix, tr);
40271278Sjedgar        }
40371278Sjedgar
40471278Sjedgar        @Override
40571278Sjedgar        public void sourcepath(List<Path> paths) {
406103949Smike            sourceSearchPaths.addAll(createSourceLocations(paths));
407103949Smike        }
408103949Smike
40971278Sjedgar        @Override
4101592Srgrimes        public void modulepath(List<Path> paths) {
4111592Srgrimes            moduleSearchPaths.addAll(createSourceLocations(paths));
41271278Sjedgar        }
4131592Srgrimes
41471278Sjedgar        @Override
41571278Sjedgar        public void classpath(List<Path> paths) {
4161592Srgrimes            classSearchPaths.addAll(createSourceLocations(paths));
41771278Sjedgar        }
41871278Sjedgar
41971278Sjedgar        @Override
4201592Srgrimes        public void numCores(int n) {
42171278Sjedgar            numCores = n;
42271278Sjedgar        }
42371278Sjedgar
4241592Srgrimes        @Override
4251592Srgrimes        public void logLevel(String level) {
42671278Sjedgar            logLevel = level;
4271592Srgrimes        }
42871278Sjedgar
42971278Sjedgar        @Override
4301592Srgrimes        public void compareFoundSources(Path referenceList) {
43171278Sjedgar            sourceReferenceList = referenceList;
43271278Sjedgar        }
43371278Sjedgar
43471278Sjedgar        @Override
43571278Sjedgar        public void permitArtifact(String f) {
43671278Sjedgar            permitted_artifacts.add(f);
43771278Sjedgar        }
4381592Srgrimes
4391592Srgrimes        @Override
44071278Sjedgar        public void permitUnidentifiedArtifacts() {
4411592Srgrimes            permitUnidentifiedArtifacts = true;
44271278Sjedgar        }
44371278Sjedgar
44471278Sjedgar        @Override
4451592Srgrimes        public void permitDefaultPackage() {
44671278Sjedgar            permitSourcesInDefaultPackage = true;
4471592Srgrimes        }
44871278Sjedgar
44971278Sjedgar        @Override
45071278Sjedgar        public void serverConf(String conf) {
4511592Srgrimes            if (serverConf != null)
4521592Srgrimes                reportError("Can not specify more than one server configuration.");
4531592Srgrimes            else
45482796Ssheldonh                serverConf = conf;
45582460Snik        }
45682460Snik
4571592Srgrimes        @Override
45882460Snik        public void implicit(String policy) {
4591592Srgrimes            implicitPolicy = policy;
4601592Srgrimes        }
4611592Srgrimes
46270102Sphk        @Override
4631592Srgrimes        public void startServerConf(String conf) {
4641592Srgrimes            if (serverConf != null)
4651592Srgrimes                reportError("Can not specify more than one server configuration.");
4661592Srgrimes            else {
4671592Srgrimes                startServer = true;
4681592Srgrimes                serverConf = conf;
46970102Sphk            }
4701592Srgrimes        }
4711592Srgrimes
4721592Srgrimes        @Override
4731592Srgrimes        public void javacArg(String... arg) {
4741592Srgrimes            javacArgs.addAll(Arrays.asList(arg));
4751592Srgrimes        }
4761592Srgrimes
4771592Srgrimes        @Override
4781592Srgrimes        public void destDir(Path dir) {
4791592Srgrimes            if (destDir != null) {
4801592Srgrimes                reportError("Destination directory already specified.");
481101395Syar                return;
4821592Srgrimes            }
483101395Syar            destDir = dir.toAbsolutePath();
4841592Srgrimes        }
485101395Syar
4861592Srgrimes        @Override
4871592Srgrimes        public void generatedSourcesDir(Path dir) {
4881592Srgrimes            if (genSrcProvided) {
4891592Srgrimes                reportError("Directory for generated sources already specified.");
490109380Syar                return;
4911592Srgrimes            }
49275567Speter            genSrcProvided = true;
4931592Srgrimes            genSrcDir = dir.toAbsolutePath();
494101395Syar        }
495109380Syar
496101395Syar        @Override
4971592Srgrimes        public void headerDir(Path dir) {
4981592Srgrimes            if (headerProvided) {
4991592Srgrimes                reportError("Header directory already specified.");
5001592Srgrimes                return;
5011592Srgrimes            }
5021592Srgrimes            headerProvided = true;
5031592Srgrimes            headerDir = dir.toAbsolutePath();
5041592Srgrimes        }
50571278Sjedgar
5061592Srgrimes        @Override
50771278Sjedgar        public void stateDir(Path dir) {
50871278Sjedgar            if (stateProvided) {
50971278Sjedgar                reportError("State directory already specified.");
5101592Srgrimes                return;
51170102Sphk            }
5121592Srgrimes            stateProvided = true;
5131592Srgrimes            stateDir = dir.toAbsolutePath();
5141592Srgrimes        }
5151592Srgrimes
5161592Srgrimes        private List<SourceLocation> createSourceLocations(List<Path> paths) {
5171592Srgrimes            List<SourceLocation> result = new ArrayList<>();
51870102Sphk            for (Path path : paths) {
5191592Srgrimes                result.add(new SourceLocation(
520101379Syar                        path,
52117433Spst                        includes,
52217433Spst                        excludes,
52317433Spst                        includeFiles,
52417433Spst                        excludeFiles));
52517433Spst            }
52617433Spst            resetFilters();
52717433Spst            return result;
5281592Srgrimes        }
529101379Syar
530101379Syar        private void resetFilters() {
5311592Srgrimes            includes = new ArrayList<>();
53271278Sjedgar            excludes = new ArrayList<>();
5331592Srgrimes            includeFiles = new ArrayList<>();
53471278Sjedgar            excludeFiles = new ArrayList<>();
53571278Sjedgar        }
5361592Srgrimes    }
5371592Srgrimes
5381592Srgrimes}
53969234Sdanny