1/*
2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27package javax.activation;
28
29import java.util.*;
30import java.io.*;
31import java.net.*;
32import java.security.AccessController;
33import java.security.PrivilegedAction;
34import com.sun.activation.registries.MailcapFile;
35import com.sun.activation.registries.LogSupport;
36
37/**
38 * MailcapCommandMap extends the CommandMap
39 * abstract class. It implements a CommandMap whose configuration
40 * is based on mailcap files
41 * (<A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>).
42 * The MailcapCommandMap can be configured both programmatically
43 * and via configuration files.
44 * <p>
45 * <b>Mailcap file search order:</b><p>
46 * The MailcapCommandMap looks in various places in the user's
47 * system for mailcap file entries. When requests are made
48 * to search for commands in the MailcapCommandMap, it searches
49 * mailcap files in the following order:
50 * <ol>
51 * <li> Programatically added entries to the MailcapCommandMap instance.
52 * <li> The file {@code .mailcap} in the user's home directory.
53 * <li> The file {@code mailcap} in the Java runtime.
54 * <li> The file or resources named {@code META-INF/mailcap}.
55 * <li> The file or resource named {@code META-INF/mailcap.default}
56 * (usually found only in the {@code activation.jar} file).
57 * </ol>
58 * <p>
59 * (The current implementation looks for the {@code mailcap} file
60 * in the Java runtime in the directory <i>java.home</i>{@code /conf}
61 * if it exists, and otherwise in the directory
62 * <i>java.home</i>{@code /lib}, where <i>java.home</i> is the value
63 * of the "java.home" System property.  Note that the "conf" directory was
64 * introduced in JDK 9.)
65 * <p>
66 * <b>Mailcap file format:</b><p>
67 *
68 * Mailcap files must conform to the mailcap
69 * file specification (RFC 1524, <i>A User Agent Configuration Mechanism
70 * For Multimedia Mail Format Information</i>).
71 * The file format consists of entries corresponding to
72 * particular MIME types. In general, the specification
73 * specifies <i>applications</i> for clients to use when they
74 * themselves cannot operate on the specified MIME type. The
75 * MailcapCommandMap extends this specification by using a parameter mechanism
76 * in mailcap files that allows JavaBeans(tm) components to be specified as
77 * corresponding to particular commands for a MIME type.<p>
78 *
79 * When a mailcap file is
80 * parsed, the MailcapCommandMap recognizes certain parameter signatures,
81 * specifically those parameter names that begin with {@code x-java-}.
82 * The MailcapCommandMap uses this signature to find
83 * command entries for inclusion into its registries.
84 * Parameter names with the form {@code x-java-<name>}
85 * are read by the MailcapCommandMap as identifying a command
86 * with the name <i>name</i>. When the <i>name</i> is {@code
87 * content-handler} the MailcapCommandMap recognizes the class
88 * signified by this parameter as a <i>DataContentHandler</i>.
89 * All other commands are handled generically regardless of command
90 * name. The command implementation is specified by a fully qualified
91 * class name of a JavaBean(tm) component. For example; a command for viewing
92 * some data can be specified as: {@code x-java-view=com.foo.ViewBean}.<p>
93 *
94 * When the command name is {@code fallback-entry}, the value of
95 * the command may be {@code true} or {@code false}.  An
96 * entry for a MIME type that includes a parameter of
97 * {@code x-java-fallback-entry=true} defines fallback commands
98 * for that MIME type that will only be used if no non-fallback entry
99 * can be found.  For example, an entry of the form {@code text/*; ;
100 * x-java-fallback-entry=true; x-java-view=com.sun.TextViewer}
101 * specifies a view command to be used for any text MIME type.  This
102 * view command would only be used if a non-fallback view command for
103 * the MIME type could not be found.<p>
104 *
105 * MailcapCommandMap aware mailcap files have the
106 * following general form:
107 * <pre>{@code
108 * # Comments begin with a '#' and continue to the end of the line.
109 * <mime type>; ; <parameter list>
110 * # Where a parameter list consists of one or more parameters,
111 * # where parameters look like: x-java-view=com.sun.TextViewer
112 * # and a parameter list looks like:
113 * text/plain; ; x-java-view=com.sun.TextViewer; x-java-edit=com.sun.TextEdit
114 * # Note that mailcap entries that do not contain 'x-java' parameters
115 * # and comply to RFC 1524 are simply ignored:
116 * image/gif; /usr/dt/bin/sdtimage %s
117 * }</pre>
118 *
119 * @author Bart Calder
120 * @author Bill Shannon
121 *
122 * @since 1.6
123 */
124
125public class MailcapCommandMap extends CommandMap {
126    /*
127     * We manage a collection of databases, searched in order.
128     */
129    private MailcapFile[] DB;
130    private static final int PROG = 0;  // programmatically added entries
131
132    private static final String confDir;
133
134    static {
135        String dir = null;
136        try {
137            dir = (String)AccessController.doPrivileged(
138                new PrivilegedAction() {
139                    public Object run() {
140                        String home = System.getProperty("java.home");
141                        String newdir = home + File.separator + "conf";
142                        File conf = new File(newdir);
143                        if (conf.exists())
144                            return newdir + File.separator;
145                        else
146                            return home + File.separator + "lib" + File.separator;
147                    }
148                });
149        } catch (Exception ex) {
150            // ignore any exceptions
151        }
152        confDir = dir;
153    }
154
155    /**
156     * The default Constructor.
157     */
158    public MailcapCommandMap() {
159        super();
160        List dbv = new ArrayList(5);    // usually 5 or less databases
161        MailcapFile mf = null;
162        dbv.add(null);          // place holder for PROG entry
163
164        LogSupport.log("MailcapCommandMap: load HOME");
165        try {
166            String user_home = System.getProperty("user.home");
167
168            if (user_home != null) {
169                String path = user_home + File.separator + ".mailcap";
170                mf = loadFile(path);
171                if (mf != null)
172                    dbv.add(mf);
173            }
174        } catch (SecurityException ex) {}
175
176        LogSupport.log("MailcapCommandMap: load SYS");
177        try {
178            // check system's home
179            if (confDir != null) {
180                mf = loadFile(confDir + "mailcap");
181                if (mf != null)
182                    dbv.add(mf);
183            }
184        } catch (SecurityException ex) {}
185
186        LogSupport.log("MailcapCommandMap: load JAR");
187        // load from the app's jar file
188        loadAllResources(dbv, "META-INF/mailcap");
189
190        LogSupport.log("MailcapCommandMap: load DEF");
191        mf = loadResource("/META-INF/mailcap.default");
192
193        if (mf != null)
194            dbv.add(mf);
195
196        DB = new MailcapFile[dbv.size()];
197        DB = (MailcapFile[])dbv.toArray(DB);
198    }
199
200    /**
201     * Load from the named resource.
202     */
203    private MailcapFile loadResource(String name) {
204        InputStream clis = null;
205        try {
206            clis = SecuritySupport.getResourceAsStream(this.getClass(), name);
207            if (clis != null) {
208                MailcapFile mf = new MailcapFile(clis);
209                if (LogSupport.isLoggable())
210                    LogSupport.log("MailcapCommandMap: successfully loaded " +
211                        "mailcap file: " + name);
212                return mf;
213            } else {
214                if (LogSupport.isLoggable())
215                    LogSupport.log("MailcapCommandMap: not loading " +
216                        "mailcap file: " + name);
217            }
218        } catch (IOException e) {
219            if (LogSupport.isLoggable())
220                LogSupport.log("MailcapCommandMap: can't load " + name, e);
221        } catch (SecurityException sex) {
222            if (LogSupport.isLoggable())
223                LogSupport.log("MailcapCommandMap: can't load " + name, sex);
224        } finally {
225            try {
226                if (clis != null)
227                    clis.close();
228            } catch (IOException ex) { }        // ignore it
229        }
230        return null;
231    }
232
233    /**
234     * Load all of the named resource.
235     */
236    private void loadAllResources(List v, String name) {
237        boolean anyLoaded = false;
238        try {
239            URL[] urls;
240            ClassLoader cld = null;
241            // First try the "application's" class loader.
242            cld = SecuritySupport.getContextClassLoader();
243            if (cld == null)
244                cld = this.getClass().getClassLoader();
245            if (cld != null)
246                urls = SecuritySupport.getResources(cld, name);
247            else
248                urls = SecuritySupport.getSystemResources(name);
249            if (urls != null) {
250                if (LogSupport.isLoggable())
251                    LogSupport.log("MailcapCommandMap: getResources");
252                for (int i = 0; i < urls.length; i++) {
253                    URL url = urls[i];
254                    InputStream clis = null;
255                    if (LogSupport.isLoggable())
256                        LogSupport.log("MailcapCommandMap: URL " + url);
257                    try {
258                        clis = SecuritySupport.openStream(url);
259                        if (clis != null) {
260                            v.add(new MailcapFile(clis));
261                            anyLoaded = true;
262                            if (LogSupport.isLoggable())
263                                LogSupport.log("MailcapCommandMap: " +
264                                    "successfully loaded " +
265                                    "mailcap file from URL: " +
266                                    url);
267                        } else {
268                            if (LogSupport.isLoggable())
269                                LogSupport.log("MailcapCommandMap: " +
270                                    "not loading mailcap " +
271                                    "file from URL: " + url);
272                        }
273                    } catch (IOException ioex) {
274                        if (LogSupport.isLoggable())
275                            LogSupport.log("MailcapCommandMap: can't load " +
276                                                url, ioex);
277                    } catch (SecurityException sex) {
278                        if (LogSupport.isLoggable())
279                            LogSupport.log("MailcapCommandMap: can't load " +
280                                                url, sex);
281                    } finally {
282                        try {
283                            if (clis != null)
284                                clis.close();
285                        } catch (IOException cex) { }
286                    }
287                }
288            }
289        } catch (Exception ex) {
290            if (LogSupport.isLoggable())
291                LogSupport.log("MailcapCommandMap: can't load " + name, ex);
292        }
293
294        // if failed to load anything, fall back to old technique, just in case
295        if (!anyLoaded) {
296            if (LogSupport.isLoggable())
297                LogSupport.log("MailcapCommandMap: !anyLoaded");
298            MailcapFile mf = loadResource("/" + name);
299            if (mf != null)
300                v.add(mf);
301        }
302    }
303
304    /**
305     * Load from the named file.
306     */
307    private MailcapFile loadFile(String name) {
308        MailcapFile mtf = null;
309
310        try {
311            mtf = new MailcapFile(name);
312        } catch (IOException e) {
313            //  e.printStackTrace();
314        }
315        return mtf;
316    }
317
318    /**
319     * Constructor that allows the caller to specify the path
320     * of a <i>mailcap</i> file.
321     *
322     * @param fileName The name of the <i>mailcap</i> file to open
323     * @exception       IOException     if the file can't be accessed
324     */
325    public MailcapCommandMap(String fileName) throws IOException {
326        this();
327
328        if (LogSupport.isLoggable())
329            LogSupport.log("MailcapCommandMap: load PROG from " + fileName);
330        if (DB[PROG] == null) {
331            DB[PROG] = new MailcapFile(fileName);
332        }
333    }
334
335
336    /**
337     * Constructor that allows the caller to specify an <i>InputStream</i>
338     * containing a mailcap file.
339     *
340     * @param is        InputStream of the <i>mailcap</i> file to open
341     */
342    public MailcapCommandMap(InputStream is) {
343        this();
344
345        LogSupport.log("MailcapCommandMap: load PROG");
346        if (DB[PROG] == null) {
347            try {
348                DB[PROG] = new MailcapFile(is);
349            } catch (IOException ex) {
350                // XXX - should throw it
351            }
352        }
353    }
354
355    /**
356     * Get the preferred command list for a MIME Type. The MailcapCommandMap
357     * searches the mailcap files as described above under
358     * <i>Mailcap file search order</i>.<p>
359     *
360     * The result of the search is a proper subset of available
361     * commands in all mailcap files known to this instance of
362     * MailcapCommandMap.  The first entry for a particular command
363     * is considered the preferred command.
364     *
365     * @param mimeType  the MIME type
366     * @return the CommandInfo objects representing the preferred commands.
367     */
368    public synchronized CommandInfo[] getPreferredCommands(String mimeType) {
369        List cmdList = new ArrayList();
370        if (mimeType != null)
371            mimeType = mimeType.toLowerCase(Locale.ENGLISH);
372
373        for (int i = 0; i < DB.length; i++) {
374            if (DB[i] == null)
375                continue;
376            Map cmdMap = DB[i].getMailcapList(mimeType);
377            if (cmdMap != null)
378                appendPrefCmdsToList(cmdMap, cmdList);
379        }
380
381        // now add the fallback commands
382        for (int i = 0; i < DB.length; i++) {
383            if (DB[i] == null)
384                continue;
385            Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
386            if (cmdMap != null)
387                appendPrefCmdsToList(cmdMap, cmdList);
388        }
389
390        CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()];
391        cmdInfos = (CommandInfo[])cmdList.toArray(cmdInfos);
392
393        return cmdInfos;
394    }
395
396    /**
397     * Put the commands that are in the hash table, into the list.
398     */
399    private void appendPrefCmdsToList(Map cmdHash, List cmdList) {
400        Iterator verb_enum = cmdHash.keySet().iterator();
401
402        while (verb_enum.hasNext()) {
403            String verb = (String)verb_enum.next();
404            if (!checkForVerb(cmdList, verb)) {
405                List cmdList2 = (List)cmdHash.get(verb); // get the list
406                String className = (String)cmdList2.get(0);
407                cmdList.add(new CommandInfo(verb, className));
408            }
409        }
410    }
411
412    /**
413     * Check the cmdList to see if this command exists, return
414     * true if the verb is there.
415     */
416    private boolean checkForVerb(List cmdList, String verb) {
417        Iterator ee = cmdList.iterator();
418        while (ee.hasNext()) {
419            String enum_verb =
420                (String)((CommandInfo)ee.next()).getCommandName();
421            if (enum_verb.equals(verb))
422                return true;
423        }
424        return false;
425    }
426
427    /**
428     * Get all the available commands in all mailcap files known to
429     * this instance of MailcapCommandMap for this MIME type.
430     *
431     * @param mimeType  the MIME type
432     * @return the CommandInfo objects representing all the commands.
433     */
434    public synchronized CommandInfo[] getAllCommands(String mimeType) {
435        List cmdList = new ArrayList();
436        if (mimeType != null)
437            mimeType = mimeType.toLowerCase(Locale.ENGLISH);
438
439        for (int i = 0; i < DB.length; i++) {
440            if (DB[i] == null)
441                continue;
442            Map cmdMap = DB[i].getMailcapList(mimeType);
443            if (cmdMap != null)
444                appendCmdsToList(cmdMap, cmdList);
445        }
446
447        // now add the fallback commands
448        for (int i = 0; i < DB.length; i++) {
449            if (DB[i] == null)
450                continue;
451            Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
452            if (cmdMap != null)
453                appendCmdsToList(cmdMap, cmdList);
454        }
455
456        CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()];
457        cmdInfos = (CommandInfo[])cmdList.toArray(cmdInfos);
458
459        return cmdInfos;
460    }
461
462    /**
463     * Put the commands that are in the hash table, into the list.
464     */
465    private void appendCmdsToList(Map typeHash, List cmdList) {
466        Iterator verb_enum = typeHash.keySet().iterator();
467
468        while (verb_enum.hasNext()) {
469            String verb = (String)verb_enum.next();
470            List cmdList2 = (List)typeHash.get(verb);
471            Iterator cmd_enum = ((List)cmdList2).iterator();
472
473            while (cmd_enum.hasNext()) {
474                String cmd = (String)cmd_enum.next();
475                cmdList.add(new CommandInfo(verb, cmd));
476                // cmdList.add(0, new CommandInfo(verb, cmd));
477            }
478        }
479    }
480
481    /**
482     * Get the command corresponding to {@code cmdName} for the MIME type.
483     *
484     * @param mimeType  the MIME type
485     * @param cmdName   the command name
486     * @return the CommandInfo object corresponding to the command.
487     */
488    public synchronized CommandInfo getCommand(String mimeType,
489                                                        String cmdName) {
490        if (mimeType != null)
491            mimeType = mimeType.toLowerCase(Locale.ENGLISH);
492
493        for (int i = 0; i < DB.length; i++) {
494            if (DB[i] == null)
495                continue;
496            Map cmdMap = DB[i].getMailcapList(mimeType);
497            if (cmdMap != null) {
498                // get the cmd list for the cmd
499                List v = (List)cmdMap.get(cmdName);
500                if (v != null) {
501                    String cmdClassName = (String)v.get(0);
502
503                    if (cmdClassName != null)
504                        return new CommandInfo(cmdName, cmdClassName);
505                }
506            }
507        }
508
509        // now try the fallback list
510        for (int i = 0; i < DB.length; i++) {
511            if (DB[i] == null)
512                continue;
513            Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
514            if (cmdMap != null) {
515                // get the cmd list for the cmd
516                List v = (List)cmdMap.get(cmdName);
517                if (v != null) {
518                    String cmdClassName = (String)v.get(0);
519
520                    if (cmdClassName != null)
521                        return new CommandInfo(cmdName, cmdClassName);
522                }
523            }
524        }
525        return null;
526    }
527
528    /**
529     * Add entries to the registry.  Programmatically
530     * added entries are searched before other entries.<p>
531     *
532     * The string that is passed in should be in mailcap
533     * format.
534     *
535     * @param mail_cap a correctly formatted mailcap string
536     */
537    public synchronized void addMailcap(String mail_cap) {
538        // check to see if one exists
539        LogSupport.log("MailcapCommandMap: add to PROG");
540        if (DB[PROG] == null)
541            DB[PROG] = new MailcapFile();
542
543        DB[PROG].appendToMailcap(mail_cap);
544    }
545
546    /**
547     * Return the DataContentHandler for the specified MIME type.
548     *
549     * @param mimeType  the MIME type
550     * @return          the DataContentHandler
551     */
552    public synchronized DataContentHandler createDataContentHandler(
553                                                        String mimeType) {
554        if (LogSupport.isLoggable())
555            LogSupport.log(
556                "MailcapCommandMap: createDataContentHandler for " + mimeType);
557        if (mimeType != null)
558            mimeType = mimeType.toLowerCase(Locale.ENGLISH);
559
560        for (int i = 0; i < DB.length; i++) {
561            if (DB[i] == null)
562                continue;
563            if (LogSupport.isLoggable())
564                LogSupport.log("  search DB #" + i);
565            Map cmdMap = DB[i].getMailcapList(mimeType);
566            if (cmdMap != null) {
567                List v = (List)cmdMap.get("content-handler");
568                if (v != null) {
569                    String name = (String)v.get(0);
570                    DataContentHandler dch = getDataContentHandler(name);
571                    if (dch != null)
572                        return dch;
573                }
574            }
575        }
576
577        // now try the fallback entries
578        for (int i = 0; i < DB.length; i++) {
579            if (DB[i] == null)
580                continue;
581            if (LogSupport.isLoggable())
582                LogSupport.log("  search fallback DB #" + i);
583            Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
584            if (cmdMap != null) {
585                List v = (List)cmdMap.get("content-handler");
586                if (v != null) {
587                    String name = (String)v.get(0);
588                    DataContentHandler dch = getDataContentHandler(name);
589                    if (dch != null)
590                        return dch;
591                }
592            }
593        }
594        return null;
595    }
596
597    private DataContentHandler getDataContentHandler(String name) {
598        if (LogSupport.isLoggable())
599            LogSupport.log("    got content-handler");
600        if (LogSupport.isLoggable())
601            LogSupport.log("      class " + name);
602        try {
603            ClassLoader cld = null;
604            // First try the "application's" class loader.
605            cld = SecuritySupport.getContextClassLoader();
606            if (cld == null)
607                cld = this.getClass().getClassLoader();
608            Class cl = null;
609            try {
610                cl = cld.loadClass(name);
611            } catch (Exception ex) {
612                // if anything goes wrong, do it the old way
613                cl = Class.forName(name);
614            }
615            return (DataContentHandler) cl.newInstance();
616        } catch (IllegalAccessException e) {
617            if (LogSupport.isLoggable())
618                LogSupport.log("Can't load DCH " + name, e);
619        } catch (ClassNotFoundException e) {
620            if (LogSupport.isLoggable())
621                LogSupport.log("Can't load DCH " + name, e);
622        } catch (InstantiationException e) {
623            if (LogSupport.isLoggable())
624                LogSupport.log("Can't load DCH " + name, e);
625        }
626        return null;
627    }
628
629    /**
630     * Get all the MIME types known to this command map.
631     *
632     * @return          array of MIME types as strings
633     * @since   1.6, JAF 1.1
634     */
635    public synchronized String[] getMimeTypes() {
636        List mtList = new ArrayList();
637
638        for (int i = 0; i < DB.length; i++) {
639            if (DB[i] == null)
640                continue;
641            String[] ts = DB[i].getMimeTypes();
642            if (ts != null) {
643                for (int j = 0; j < ts.length; j++) {
644                    // eliminate duplicates
645                    if (!mtList.contains(ts[j]))
646                        mtList.add(ts[j]);
647                }
648            }
649        }
650
651        String[] mts = new String[mtList.size()];
652        mts = (String[])mtList.toArray(mts);
653
654        return mts;
655    }
656
657    /**
658     * Get the native commands for the given MIME type.
659     * Returns an array of strings where each string is
660     * an entire mailcap file entry.  The application
661     * will need to parse the entry to extract the actual
662     * command as well as any attributes it needs. See
663     * <A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>
664     * for details of the mailcap entry syntax.  Only mailcap
665     * entries that specify a view command for the specified
666     * MIME type are returned.
667     *
668     * @param   mimeType        the MIME type
669     * @return          array of native command entries
670     * @since   1.6, JAF 1.1
671     */
672    public synchronized String[] getNativeCommands(String mimeType) {
673        List cmdList = new ArrayList();
674        if (mimeType != null)
675            mimeType = mimeType.toLowerCase(Locale.ENGLISH);
676
677        for (int i = 0; i < DB.length; i++) {
678            if (DB[i] == null)
679                continue;
680            String[] cmds = DB[i].getNativeCommands(mimeType);
681            if (cmds != null) {
682                for (int j = 0; j < cmds.length; j++) {
683                    // eliminate duplicates
684                    if (!cmdList.contains(cmds[j]))
685                        cmdList.add(cmds[j]);
686                }
687            }
688        }
689
690        String[] cmds = new String[cmdList.size()];
691        cmds = (String[])cmdList.toArray(cmds);
692
693        return cmds;
694    }
695
696    /**
697     * for debugging...
698     *
699    public static void main(String[] argv) throws Exception {
700        MailcapCommandMap map = new MailcapCommandMap();
701        CommandInfo[] cmdInfo;
702
703        cmdInfo = map.getPreferredCommands(argv[0]);
704        System.out.println("Preferred Commands:");
705        for (int i = 0; i < cmdInfo.length; i++)
706            System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
707                                            cmdInfo[i].getCommandClass() + "]");
708        cmdInfo = map.getAllCommands(argv[0]);
709        System.out.println();
710        System.out.println("All Commands:");
711        for (int i = 0; i < cmdInfo.length; i++)
712            System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
713                                            cmdInfo[i].getCommandClass() + "]");
714        DataContentHandler dch = map.createDataContentHandler(argv[0]);
715        if (dch != null)
716            System.out.println("DataContentHandler " +
717                                                dch.getClass().toString());
718        System.exit(0);
719    }
720    */
721}
722