Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/port80/util/msg.java


1   /*
2     $id$
3   
4     TODO:
5     . Remove synchronized for functions that are classified as synchronized just
6       because of using Perl5Util re.
7    */
8   
9   //
10  // Copyright(c) 2002, Chris Leung
11  //
12  
13  package com.port80.util;
14  
15  import gnu.getopt.Getopt;
16  import gnu.getopt.LongOpt;
17  import java.io.File;
18  import java.io.FileFilter;
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.Method;
21  import java.sql.SQLException;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Hashtable;
27  import java.util.Iterator;
28  import java.util.LinkedList;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.SortedSet;
33  import java.util.TreeSet;
34  import java.util.Vector;
35  
36  import org.apache.oro.text.perl.Perl5Util;
37  
38  import com.port80.util.struct.FloatList;
39  import com.port80.util.struct.IntValueHashMap;
40  
41  /** Debug and utility functions.  Class cannot be instantiated. All
42   *  functions are static.
43   *
44   * @author  chrisl
45   * @version
46   */
47  public final class msg {
48  
49    ////////////////////////////////////////////////////////////////////////
50    // Data
51  
52    private static final String NAME = "msg";
53    private static boolean DEBUG_CALLFUNC = false;
54  
55    private static final int NONE = 0;
56    private static final int REQ = 1;
57    private static final int OPT = 2;
58    private static final int REQARRAY = 3;
59    private static final int OPTARRAY = 4;
60  
61    /** NOTE: Perl5Util methods (such as split, match ... etc) are public synchronized.
62      So no need to synchronize methods because of access to re.
63    */
64    private static Perl5Util re = new Perl5Util();
65  
66    private static Map hexTable = new Hashtable();
67    private static Map uriEscapeChars = new Hashtable();
68    private static Map filepathEscapeChars = new Hashtable();
69    private static Map standardErrors = new Hashtable();
70    private static String pwd = System.getProperty("user.dir");
71  
72    static {
73      hexTable.put("0", new Integer(0));
74      hexTable.put("1", new Integer(1));
75      hexTable.put("2", new Integer(2));
76      hexTable.put("3", new Integer(3));
77      hexTable.put("4", new Integer(4));
78      hexTable.put("5", new Integer(5));
79      hexTable.put("6", new Integer(6));
80      hexTable.put("7", new Integer(7));
81      hexTable.put("8", new Integer(8));
82      hexTable.put("9", new Integer(9));
83      hexTable.put("a", new Integer(10));
84      hexTable.put("b", new Integer(11));
85      hexTable.put("c", new Integer(12));
86      hexTable.put("d", new Integer(13));
87      hexTable.put("e", new Integer(14));
88      hexTable.put("f", new Integer(15));
89      hexTable.put("A", new Integer(10));
90      hexTable.put("B", new Integer(11));
91      hexTable.put("C", new Integer(12));
92      hexTable.put("D", new Integer(13));
93      hexTable.put("E", new Integer(14));
94      hexTable.put("F", new Integer(15));
95  
96      // ";", "/", "?", ":", "@", "&", "=", "+", "$", ",",   # reserved
97      uriEscapeChars.put(";", "%3b");
98      uriEscapeChars.put("/", "%2f");
99      uriEscapeChars.put("?", "%3f");
100     uriEscapeChars.put(":", "%3a");
101     uriEscapeChars.put("\"", "%22");
102     uriEscapeChars.put("@", "%40");
103     uriEscapeChars.put("&", "%26");
104     uriEscapeChars.put("=", "%3d");
105     uriEscapeChars.put("+", "%2b");
106     uriEscapeChars.put("$", "%24");
107     uriEscapeChars.put(",", "%2c");
108     uriEscapeChars.put("%", "%25");
109 
110     uriEscapeChars.put("%3b", ";");
111     uriEscapeChars.put("%2f", "/");
112     uriEscapeChars.put("%3f", "?");
113     uriEscapeChars.put("%3a", ":");
114     uriEscapeChars.put("%22", "\"");
115     uriEscapeChars.put("%40", "@");
116     uriEscapeChars.put("%26", "&");
117     uriEscapeChars.put("%3d", "=");
118     uriEscapeChars.put("%2b", "+");
119     uriEscapeChars.put("%24", "$");
120     uriEscapeChars.put("%2c", ",");
121     uriEscapeChars.put("%25", "%");
122 
123     filepathEscapeChars.put("~", "^7e");
124     filepathEscapeChars.put("!", "^21");
125     filepathEscapeChars.put("$", "^24");
126     filepathEscapeChars.put("&", "^26");
127     filepathEscapeChars.put("*", "^2a");
128     filepathEscapeChars.put("(", "^28");
129     filepathEscapeChars.put(")", "^29");
130     filepathEscapeChars.put("[", "^5b");
131     filepathEscapeChars.put("]", "^5d");
132     filepathEscapeChars.put("|", "^7c");
133     filepathEscapeChars.put("?", "^3f");
134     filepathEscapeChars.put(";", "^3b");
135     filepathEscapeChars.put("<", "^3c");
136     filepathEscapeChars.put(">", "^3e");
137     filepathEscapeChars.put("\"", "^22");
138     filepathEscapeChars.put("'", "^27");
139 
140     filepathEscapeChars.put("^7e", "~");
141     filepathEscapeChars.put("^21", "!");
142     filepathEscapeChars.put("^24", "$");
143     filepathEscapeChars.put("^26", "&");
144     filepathEscapeChars.put("^2a", "*");
145     filepathEscapeChars.put("^28", "(");
146     filepathEscapeChars.put("^29", ")");
147     filepathEscapeChars.put("^5b", "[");
148     filepathEscapeChars.put("^5d", "]");
149     filepathEscapeChars.put("^7c", "|");
150     filepathEscapeChars.put("^3f", "?");
151     filepathEscapeChars.put("^3b", ";");
152     filepathEscapeChars.put("^3c", "<");
153     filepathEscapeChars.put("^3e", ">");
154     filepathEscapeChars.put("^22", "\"");
155     filepathEscapeChars.put("^27", "'");
156 
157     standardErrors.put("usage", new String[] { "100", "" });
158     standardErrors.put("openr", new String[] { "101", "Open file for read" });
159     standardErrors.put("openw", new String[] { "102", "Open file for write" });
160     standardErrors.put("read", new String[] { "103", "Reading file" });
161     standardErrors.put("write", new String[] { "104", "Writing file" });
162 
163   }
164 
165   ////////////////////////////////////////////////////////////////////////
166   // Methods.
167 
168   /** Prevent instantiation. 
169    */
170   private msg() {}
171 
172   //////////////////// Message output functions.
173 
174   public static boolean asserts(boolean ok) {
175     if (!ok)
176       printStack("ASSERTS:");
177     return ok;
178   }
179 
180   public static void debug(String s) {
181     if (Debug.on())
182       msg.println("# DEBUG: " + s);
183   }
184 
185   public static void debugs(String s) {
186     if (Debug.on())
187       msg.printStack("# DEBUG: " + s);
188   }
189 
190   public static void trace(String s) {
191     if (Debug.trace())
192       msg.println("# TRACE: " + s);
193   }
194 
195   public static void test(String s) {
196     if (Debug.test())
197       System.err.println("# TEST: " + s);
198   }
199 
200   public static void err(String s) {
201     printStack("\n# ERROR: " + s);
202   }
203 
204   public static void err(Exception e) {
205     e.printStackTrace();
206   }
207 
208   public static void warn(String s) {
209     System.err.println("\n# WARN: " + s);
210   }
211 
212   public static void warns(String s) {
213     printStack("\n# WARN: " + s);
214   }
215 
216   public static void err(String s, Exception e) {
217     System.err.println("\n# ERROR: " + s);
218     e.printStackTrace();
219   }
220   public static void err(String s, SQLException e) {
221     System.err.println(
222       "\n# ERROR: "
223         + s
224         + "\nSQLState:     "
225         + e.getSQLState()
226         + "\nVendorError:  "
227         + e.getErrorCode());
228     e.printStackTrace();
229   }
230 
231   public static void usage(String m) {
232     System.err.println(m);
233     System.exit(100);
234   }
235 
236   public static void fatal(String s) {
237     printStack(s);
238     System.exit(101);
239   }
240 
241   public static void errExit(String s) {
242     fatal(s);
243   }
244 
245   // Message I/O functions ///////////////////////////////////////////////
246 
247   public static void out(String s) {
248     System.out.print(s);
249   }
250 
251   public static void outln(String s) {
252     System.out.println(s);
253   }
254 
255   public static void print(String s) {
256     System.err.print(s);
257     System.err.flush();
258   }
259 
260   public static void println(String s) {
261     System.err.println(s);
262   }
263 
264   public static void println(String s, int[] a) {
265     System.err.print(s+sprint.array("", "%7d ",a));
266   }
267 
268   public static void printStack(String s) {
269     if (s != null)
270       msg.println(s);
271     Throwable thrower = new Throwable();
272     thrower.printStackTrace(System.err);
273   }
274 
275   public static void printStat(long stime, int n) {
276 
277     java.util.Date date = new java.util.Date();
278     stime = (date.getTime() - stime) / 1000;
279     msg.println("# Time used: = " + stime + ", " + (n / (stime + 0.0001)) + " record/sec");
280   }
281 
282   /** Exit with standard error messages.
283    */
284   public static void exit(String id, String m) {
285     if (id.equals("usage")) {
286       System.err.println(m);
287       System.exit(100);
288     }
289     String[] a = (String[]) standardErrors.get(id);
290     if (a == null) {
291       System.err.println("# ERROR: " + id + ": " + m);
292       System.exit(1);
293     }
294     int ecode = Integer.parseInt(a[0]);
295     String emsg = a[1];
296     System.err.println("# ERROR: " + ecode + ": " + emsg + ": " + m);
297     System.exit(ecode);
298   }
299 
300   ////////////////////////////////////////////////////////////////////////
301 
302   public static synchronized String sprintObject(Object object) {
303     return sprintObject(object, "", "    ", null);
304   }
305 
306   public static synchronized String sprintObject(Object object, String indent, String tab, String mesg) {
307 
308     if (object == null)
309       return (indent + "null,\n");
310 
311     StringBuffer ret = new StringBuffer();
312     if (mesg != null)
313       ret.append(mesg);
314     if (object instanceof Map) {
315       Map map = (Map) object;
316       ret.append(indent + "{\n");
317       SortedSet keys = new TreeSet(map.keySet());
318       for (Iterator it = keys.iterator(); it.hasNext();) {
319         Object key = it.next();
320         Object value = map.get(key);
321         ret.append(indent + tab + key.toString() + "=> ");
322         // Make it more readable by putting these at same line as the key.
323         if (value instanceof String || value instanceof Character)
324           ret.append(indent + tab + "'" + value.toString() + "',\n");
325         else if (
326           value instanceof Integer
327             || value instanceof Long
328             || value instanceof Float
329             || value instanceof Double
330             || value instanceof Boolean
331             || value instanceof Byte
332             || value instanceof Short)
333           ret.append(indent + tab + value.toString() + ",\n");
334         else
335           ret.append(
336           // "#"+value.getClass().getName()+
337           "\n" + sprintObject(value, indent + tab, tab, null));
338       }
339       ret.append(indent + "},\n");
340     } else if (object instanceof Collection) {
341       List list = (List) object;
342       ret.append(indent + "[ #" + object.getClass().getName() + "\n");
343       for (Iterator it = list.iterator(); it.hasNext();) {
344         Object value = it.next();
345         ret.append(sprintObject(value, indent + tab, tab, null));
346       }
347       ret.append(indent + "],\n");
348     } else if (object instanceof String || object instanceof Character) {
349       // Put "'" around string and characters.
350       ret.append(indent + "'" + object.toString() + "',\n");
351     } else if (
352       object instanceof char[]
353         || object instanceof int[]
354         || object instanceof long[]
355         || object instanceof float[]
356         || object instanceof double[]
357         || object instanceof String[]
358         || object instanceof boolean[]
359         || object instanceof byte[]
360         || object instanceof short[]) {
361       ret.append(sprintArray(object, indent, tab, null));
362     } else {
363       ret.append(indent + object.toString() + ",\n");
364     }
365     return ret.toString();
366   }
367 
368   public static synchronized String sprintArray(Object object) {
369     return sprintArray(object, "", "    ", null);
370   }
371 
372   public static synchronized String sprintArray(Object object, String indent, String tab, String mesg) {
373 
374     StringBuffer ret = new StringBuffer();
375     String s = indent + tab;
376 
377     ret.append(indent + "[ #" + object.getClass().getName());
378     if (object instanceof int[]) {
379       int[] a = (int[]) object;
380       int i = 0;
381       for (; i < a.length; i++) {
382         if (i % 4 == 0)
383           ret.append("\n" + s);
384         ret.append(sprint.f("%15d,").a(a[i]).end());
385       }
386       if (i % 4 != 0)
387         ret.append("\n");
388     } else if (object instanceof long[]) {
389       long[] a = (long[]) object;
390       int i = 0;
391       for (; i < a.length; i++) {
392         if (i % 4 == 0)
393           ret.append("\n" + s);
394         ret.append(sprint.f("%15ld,").a(a[i]).end());
395       }
396       if (i % 4 != 0)
397         ret.append("\n");
398     } else if (object instanceof float[]) {
399       float[] a = (float[]) object;
400       int i = 0;
401       for (; i < a.length; i++) {
402         if (i % 4 == 0)
403           ret.append("\n" + s);
404         ret.append(sprint.f("%15s,").a("" + a[i]).end());
405       }
406       if (i % 4 != 0)
407         ret.append("\n");
408     } else if (object instanceof double[]) {
409       double[] a = (double[]) object;
410       int i = 0;
411       for (; i < a.length; i++) {
412         if (i % 4 == 0)
413           ret.append("\n" + s);
414         ret.append(sprint.f("%15s,").a("" + a[i]).end());
415       }
416       if (i % 4 != 0)
417         ret.append("\n");
418     } else if (object instanceof boolean[]) {
419       boolean[] a = (boolean[]) object;
420       int i = 0;
421       for (; i < a.length; i++) {
422         if (i % 4 == 0)
423           ret.append("\n" + s);
424         ret.append(sprint.f("%15s,").a("" + a[i]).end());
425       }
426       if (i % 4 != 0)
427         ret.append("\n");
428     } else if (object instanceof byte[]) {
429       byte[] a = (byte[]) object;
430       int i = 0;
431       for (; i < a.length; i++) {
432         if (i % 4 == 0)
433           ret.append("\n" + s);
434         ret.append(sprint.f("%15s,").a("" + a[i]).end());
435       }
436       if (i % 4 != 0)
437         ret.append("\n");
438     } else if (object instanceof short[]) {
439       short[] a = (short[]) object;
440       int i = 0;
441       for (; i < a.length; i++) {
442         if (i % 4 == 0)
443           ret.append("\n" + s);
444         ret.append(sprint.f("%15s,").a("" + a[i]).end());
445       }
446       if (i % 4 != 0)
447         ret.append("\n");
448     } else if (object instanceof char[]) {
449       char[] a = (char[]) object;
450       int i = 0;
451       for (; i < a.length; i++) {
452         if (i % 16 == 0)
453           ret.append("\n" + s);
454         ret.append(sprint.f("%4s,").a("" + a[i]).end());
455       }
456       if (i % 16 != 0)
457         ret.append("\n");
458     } else {
459       warn("msg.sprintArray(): Invalid object type: " + object.getClass().getName());
460       ret.append(indent + tab + object.toString() + ",\n");
461     }
462     ret.append(indent + "],\n");
463     return ret.toString();
464   }
465 
466   //////////////////// String functions
467 
468   public static synchronized boolean match(String pattern, String s) {
469     return re.match(pattern, s);
470   }
471 
472   public static synchronized String subst(String pattern, String s) {
473     return re.substitute(pattern, s);
474   }
475 
476   public static synchronized List split(String pattern, String s) {
477     List ret = new ArrayList();
478     re.split(ret, pattern, s);
479     return ret;
480   }
481 
482   public static synchronized String[] groups(String pattern, String s) {
483     if (!re.match(pattern, s))
484       return null;
485     // re.groups() and re.group() includes the 0th group which is
486     // the entire matched string.
487     int groups = re.groups() - 1;
488     String[] ret = new String[groups];
489     for (int i = 0; i < groups; ++i) {
490       ret[i] = re.group(i + 1);
491     }
492     return ret;
493   }
494 
495   /** Unescape escape sequences inside a string. 
496    *  eg. "a\\\tbc\\\n\\
497    *       cde"
498    *  becomes:
499    *      "a\tbc\ncde"
500    *
501    * Java EscapeSequence:
502    *
503    *  \b  \u0008: backspace BS 
504    *  \t      \u0009: horizontal tab HT 
505    *  \n      \u000a: linefeed LF 
506    *  \f      \u000c: form feed FF 
507    *  \r      \u000d: carriage return CR 
508    *  \"      \u0022: double quote " 
509    *  \'      \u0027: single quote ' 
510    *  \\      \u005c: backslash \ 
511    */
512   public static String unescString(String s) {
513     StringBuffer ret = new StringBuffer();
514     char c;
515     int start = 0;
516     for (int i = 0, max = s.length(); i < max;) {
517       c = s.charAt(i++);
518       if (c == '\\') {
519         switch (c = s.charAt(i++)) {
520 
521           case 'n' :
522             c = '\n';
523             break;
524           case 'r' :
525             c = '\r';
526             break;
527           case 't' :
528             c = '\t';
529             break;
530           case '\\' :
531             c = '\\';
532             break;
533           case '\'' :
534             c = '\'';
535             break;
536           case '\"' :
537             c = '\"';
538             break;
539           case 'b' :
540             c = '\b';
541             break;
542           case 'f' :
543             c = '\f';
544             break;
545             //case 'v': c='\012'; break; //FIXME: ascii code for VT
546             //case 'a': c='\007'; break;
547             //case '?': c='?'; break;
548           case '0' : //\001
549             try {
550               start = i - 1;
551               while (Character.digit(s.charAt(i), 8) >= 0 && i < max)
552                 ++i;
553               c = (char) Integer.parseInt(s.substring(start, i), 8);
554             } catch (Exception e) {
555               msg.err(
556                 "msg.unescString(): invalid octal: "
557                   + s.substring(start));
558             }
559             break;
560           case 'x' : //\xabcd
561             try {
562               start = i++;
563               while (i < max && Character.digit(s.charAt(i), 16) >= 0)
564                 ++i;
565               c = (char) Integer.parseInt(s.substring(start, i), 16);
566             } catch (Exception e) {
567               msg.err(
568                 "msg.unescString(): invalid hex: "
569                   + s.substring(start));
570             }
571             break;
572           case '\n' :
573             continue;
574           default :
575             msg.err("Unknown escaped char: \\" + c);
576             ret.append("\\" + c);
577         }
578       }
579       ret.append(c);
580     }
581     return ret.toString();
582   }
583 
584   public static String escString(String s) {
585     StringBuffer ret = new StringBuffer();
586     String cc;
587     char c;
588     int n;
589     for (int i = 0, max = s.length(); i < max;) {
590       c = s.charAt(i++);
591       switch (c) {
592         case '\n' :
593           cc = "\\n";
594           break;
595         case '\r' :
596           cc = "\\r";
597           break;
598         case '\t' :
599           cc = "\\t";
600           break;
601         case '\'' :
602           cc = "\\\'";
603           break;
604         case '\"' :
605           cc = "\\\"";
606           break;
607         case '\\' :
608           cc = "\\\\";
609           break;
610         case '\b' :
611           cc = "\\b";
612           break;
613         case '\f' :
614           cc = "\\f";
615           break;
616           //case '\012': cc="\\v"; break;
617           //case '\007': cc="\\a"; break;
618           //case '?': cc="\\?"; break;
619         default :
620           n = (int) c;
621           if (n < 0x20 || n == 0x7f) {
622             //msg.println(sprint.f("n=%02x").a(n).end());
623             cc = "\\0" + Integer.toOctalString(n);
624           } else
625             cc = "" + c;
626       }
627       ret.append(cc);
628     }
629     return ret.toString();
630   }
631 
632   public static String quote(String s) {
633     s = msg.subst("s/\"/\\\"/g", s);
634     return "\"" + s + "\"";
635   }
636 
637   public static String join(String sep, List a) {
638     if (a == null)
639       return "";
640     StringBuffer buf = new StringBuffer();
641     for (int i = 0; i < a.size(); i++) {
642       if (i != 0)
643         buf.append(sep);
644       buf.append((String) a.get(i));
645     }
646     return buf.toString();
647   }
648 
649   public static String removeQuote(String s) {
650     int last = s.length() - 1;
651     if (s.charAt(0) == '"' && s.charAt(last) == '"' || s.charAt(0) == '\'' && s.charAt(last) == '\'') {
652       s = s.substring(1, last);
653     }
654     //s = s.trim();
655     return s;
656   }
657 
658   /** Count number of occurance of a char in a string.
659    */
660   public static int count(String s, char c) {
661     int ret = 0;
662     for (int i = 0; i < s.length(); i++) {
663       if (s.charAt(i) == c)
664         ret++;
665     }
666     return ret;
667   }
668 
669   /** Escape XML reserved characters.
670    */
671 
672   public static String xmlEscape(String s) {
673     if (s == null)
674       return "";
675     StringBuffer ret = new StringBuffer();
676     for (int i = 0; i < s.length(); i++) {
677       char c = s.charAt(i);
678       switch (c) {
679         case '&' :
680           ret.append("&amp;");
681           break;
682         case '<' :
683           ret.append("&lt;");
684           break;
685         case '\'' :
686           ret.append("&apos;");
687           break;
688         case '"' :
689           ret.append("&quot;");
690           break;
691           // FIXME: no 16 bit chars. for now.
692         default :
693           if (((int) c) > 0x7f) {
694             ret.append("?");
695           } else {
696             ret.append(c);
697           }
698       }
699     }
700     return ret.toString();
701   }
702 
703   /** Decode XML escaped characters back to ASCII.
704    */
705   public static synchronized String xmlUnescape(String s) {
706     if (s == null)
707       return "";
708     s = re.substitute("s/&amp;/&/g", s);
709     s = re.substitute("s/&lt;/</g", s);
710     s = re.substitute("s/&#38;/&/g", s);
711     s = re.substitute("s/&#60;/</g", s);
712     s = re.substitute("s/&#39;/\\'/g", s);
713     s = re.substitute("s/&#34;/\\\"/g", s);
714     return s;
715   }
716 
717   /** Esacpe reserved URL characters.
718     NOTE: % is escaped, so existing %xx triplet are NOT preserved.
719    */
720   public static String escapeURL(String s) {
721     StringBuffer ret = new StringBuffer();
722     for (int n = 0; n < s.length(); n++) {
723       String i = s.substring(n, n + 1);
724       String o = (String) uriEscapeChars.get(i);
725       ret.append((o == null) ? i : o);
726     }
727     return ret.toString();
728   }
729 
730   public static String escapeURI(String s) {
731     return escapeURL(s);
732   }
733 
734   /** Unesacpe only the URI reserved characters %xx triplet.
735     NOTE: %% is unescape to %.
736    */
737   public static String unescapeURL(String s) {
738     StringBuffer ret = new StringBuffer();
739     for (int n = 0; n < s.length(); n++) {
740       char c = s.charAt(n);
741       if (c != '%')
742         ret.append(c);
743       else if (s.substring(n + 1, n + 2).equals("%")) {
744         ret.append("%");
745         n++;
746       } else {
747         String i = s.substring(n, n + 3);
748         String o = (String) uriEscapeChars.get(i);
749         if (o == null)
750           ret.append("%");
751         else {
752           ret.append(o);
753           n += 2;
754         }
755       }
756     }
757     return ret.toString();
758   }
759 
760   public static String unescapeURI(String s) {
761     return unescapeURL(s);
762   }
763 
764   /** Unescape all %xx triplets.
765    */
766   public static String unescapePercents(String s) {
767     StringBuffer ret = new StringBuffer();
768     for (int n = 0; n < s.length(); n++) {
769       char c = s.charAt(n);
770       if (c != '%')
771         ret.append(c);
772       else {
773         String i = s.substring(n + 1, n + 3);
774         int o = hexToInt(i, -1);
775         if (o == -1)
776           ret.append("%");
777         else {
778           ret.append((char) o);
779           n += 2;
780         }
781       }
782     }
783     return ret.toString();
784   }
785 
786   /** Convert hex string to int.
787    */
788   public static int hexToInt(String s, int def) {
789     int ret = 0;
790     if (s.startsWith("0x"))
791       s = s.substring(2);
792     if (s.startsWith("0X"))
793       s = s.substring(2);
794     if (s.length() > 8) {
795       if (Debug.on())
796         msg.warn("msg.hexToInt(): hex too large: " + s);
797       return def;
798     }
799     for (int n = 0; n < s.length(); n++) {
800       String i = s.substring(n, n + 1);
801       Integer o = (Integer) hexTable.get(i);
802       if (o == null) {
803         if (Debug.on())
804           msg.warn("msg.hexToInt(): Invalid digit: " + s + ": " + i);
805         return def;
806       }
807       ret = (ret << 4) | o.intValue();
808     }
809     return ret;
810   }
811 
812   // Filename/file processing methods ////////////////////////////////////
813   //
814 
815   /** Esacpe characters that are sensitive as a unix filename.
816     NOTE: % is not escaped so that existing %xx triplet are preserved.
817    */
818   public static String escapeFilepath(String s) {
819     StringBuffer ret = new StringBuffer();
820     for (int n = 0; n < s.length(); n++) {
821       String i = s.substring(n, n + 1);
822       String o = (String) filepathEscapeChars.get(i);
823       ret.append((o == null) ? i : o);
824     }
825     return ret.toString();
826   }
827 
828   /** Unesacpe sensitive unix filename characters.
829     NOTE that we do not unescape all ^xx triplet. Use unescapePercents() for that purpose.
830    */
831   public static String unescapeFilepath(String s) {
832     StringBuffer ret = new StringBuffer();
833     for (int n = 0; n < s.length(); n++) {
834       char c = s.charAt(n);
835       if (c != '^')
836         ret.append(c);
837       else {
838         String i = s.substring(n, n + 3);
839         String o = (String) filepathEscapeChars.get(i);
840         if (o == null)
841           ret.append("^");
842         else {
843           ret.append(o);
844           n += 2;
845         }
846       }
847     }
848     return ret.toString();
849   }
850 
851   /** Fixup file path to full path and remove redundant chars. */
852   public static String normalizeFilePath(String path, String pwd) {
853     if (pwd != null && !path.startsWith("/"))
854       path = pwd + "/" + path;
855     // Remove duplicated / and /./
856     path = msg.subst("s:(/(?=/)|/\\.(?=/))::g", path);
857     // Remove /../
858     while (msg.match("/\\/\\.\\.\\//", path)) {
859       path = msg.subst("s:[^/]+/\\.\\./::", path);
860     }
861     // Remove trailing /
862     path = msg.subst("s/\\/$//", path);
863     return path;
864   }
865 
866   /** Fixup file path to full path and remove redundant chars. */
867   public static String normalizeFilePath(String path) {
868     return normalizeFilePath(path, pwd);
869   }
870 
871   /** Get dir,name,base,ext parts of a file path.
872    */
873   public static String[] basename(String path) {
874     String dir = "";
875     String name = path;
876     String base = "";
877     String ext = "";
878     int index = path.lastIndexOf('/');
879     if (index != -1) {
880       dir = path.substring(0, index);
881       name = (index + 1 < path.length()) ? path.substring(index + 1) : "";
882     }
883     if (name.length() > 0) {
884       base = name;
885       index = name.lastIndexOf('.');
886       if (index != -1) {
887         base = name.substring(0, index);
888         ext = (index + 1 < name.length()) ? name.substring(index + 1) : "";
889       }
890     }
891     return new String[] { dir, name, base, ext };
892   }
893 
894   /** Return dir part of a file path. eg.
895    *    dir1/dir2/file.java return dir1/dir2
896    */
897   public static String dirName(String path) {
898     int index = path.lastIndexOf('/');
899     if (index >= 0)
900       return path.substring(0, index);
901     return null;
902   }
903 
904   /** Return name part of a file path. eg.
905    *    dir1/dir2/file.java return file.java
906    */
907   public static String fileName(String path) {
908     int index = path.lastIndexOf('/');
909     if (index >= 0) {
910       path = path.substring(index + 1);
911       if (path.length() == 0)
912         return null;
913     }
914     return path;
915   }
916 
917   /** Return base name part of a file path. eg. 
918    *    dir1/dir2/file.java return file. 
919    */
920   public static String baseName(String path) {
921     path = fileName(path);
922     int index = path.lastIndexOf('.');
923     if (index < 0)
924       return path;
925     return path.substring(0, index);
926   }
927 
928   /** Remove filename extension from a filepath if there is one. eg.
929    *    dir1/dir2/file.java return dir1/dir2/file
930    */
931   public static String removeExt(String path) {
932     int sindex = path.lastIndexOf('/');
933     int index = path.lastIndexOf('.');
934     if (index < 0)
935       return path;
936     if (sindex < 0)
937       return path.substring(0, index);
938     if (index <= sindex)
939       return path;
940     return path.substring(0, index);
941   }
942 
943   /** Return a list of File (with absolute path) under specified
944    *  filepath.
945    */
946   public static List findFiles(File dir, FileFilter filter, List ret) {
947     if (ret == null)
948       ret = new ArrayList();
949     if (!dir.isDirectory()) {
950       if (filter.accept(dir))
951         ret.add(dir);
952       return ret;
953     }
954     // Make sure path is absolute.
955     dir = dir.getAbsoluteFile();
956     File[] files = dir.listFiles();
957     File f;
958     for (int i = 0; i < files.length; ++i) {
959       f = files[i];
960       if (files[i].isDirectory())
961         findFiles(f, filter, ret);
962       else if (filter.accept(f))
963         ret.add(f);
964     }
965     return ret;
966   }
967 
968   public static List findFiles(File dir, final String pattern, List ret) {
969     FileFilter filter = new FileFilter() {
970       public boolean accept(File f) {
971         return msg.match(pattern, f.getPath());
972       }
973     };
974     return findFiles(dir, filter, ret);
975   }
976 
977   public static List findFiles(String path, String pattern, List ret) {
978     return findFiles(new File(path), pattern, ret);
979   }
980 
981   /** Create a new File at the given path, create directories if neccessary.*/
982   public static File createFile(String path) {
983     String dirname = dirName(path);
984     if (dirname != null) {
985       File dir = new File(dirname);
986       if (!dir.exists()) {
987         if (!dir.mkdirs())
988           println("# createFile(): mkdirs failed: path=" + path);
989       }
990     }
991     return new File(path);
992   }
993 
994   ////////////////////////////////////////////////////////////////////////
995 
996   /** Return a list of absolute filepath under specified filepath.
997    */
998   public static String[] findFiles(String path, String pattern) {
999     List a = findFiles(new File(path), pattern, null);
1000    String[] ret = new String[a.size()];
1001    File f;
1002    for (int i = 0; i < ret.length; ++i) {
1003      f = (File) a.get(i);
1004      ret[i] = f.getAbsolutePath();
1005    }
1006    return ret;
1007  }
1008
1009  ////////////////////////////////////////////////////////////////////////
1010
1011  /** Convert List of String to String[].
1012   */
1013  public static String[] toStringArray(List a) {
1014    String[] ret = new String[a.size()];
1015    for (int i = 0; i < ret.length; ++i) {
1016      ret[i] = (String) a.get(i);
1017    }
1018    return ret;
1019  }
1020
1021  // Methods from App.java ///////////////////////////////////////////////
1022  //
1023  // . Utility methods for command line processing and method factory.
1024  //
1025
1026  /** Get short/long options.
1027   *
1028   *  Options are specified in a space separated string.  The first
1029   *  field is the short options.  The second and following specify
1030   *  the long options. 
1031   *
1032   *  . Long options format: <long>[=<short>]+[:|@]
1033   *    . <long> is the long name, <short> is the short alias name.
1034   *      If short alias is specified, only the long option is
1035   *      returned and the short option should not be specified in the
1036   *      short option spec.
1037   *    . Option end with ':' have an required String argument.
1038   *    . Option end with '@' have an required List of String as argument.
1039   *    . Option end with '::' have an optional String argument.
1040   *    . Option end with '@@' have an optional List of String as argument.
1041   *
1042   *  . To skip the short option spec. start the spec string with ' ' or '-'.
1043   *
1044   *  eg 'ab:cI@v:: vv output: help=h debug=d@@ include=I@' specifies options:
1045   *    a b: c d@@ h v:: vv output: help debug@@ include@
1046   *
1047   *  @return    Index of args for next non-option argument.
1048   *  @param ret    Map to return the options.
1049   *  @param name  Application name.
1050   *  @param args  Command line argument.
1051   *  @param spec  Command line option spec. for getopt.
1052   *
1053   *  BUG:
1054   *  . Control characters eg. \t\r\n in option arguments are lost,
1055   *    got 'trn' instead. Maybe this is an security feature :)
1056   */
1057  public static int getOpt(Map ret, String name, String[] args, String spec) {
1058
1059    if (ret == null)
1060      ret = new HashMap();
1061
1062    Map alias = new HashMap();
1063    IntValueHashMap typeTable = new IntValueHashMap();
1064    int type; // option type.
1065    String stype; // short option type (':','::') since gun.getOpt() only accept ':' and '::'.
1066    //
1067    // Parse short options.
1068    //
1069    List specs = msg.split("/\\s+/", spec);
1070    String shortspec = (String) specs.get(0);
1071    if (shortspec.startsWith("-"))
1072      shortspec = shortspec.substring(1);
1073    if (!shortspec.startsWith(":"))
1074      shortspec = ":" + shortspec;
1075    char c;
1076    char t;
1077    char[] shortarray = shortspec.toCharArray();
1078    int len = shortarray.length;
1079    for (int i = 1; i < len;) {
1080      c = shortarray[i++];
1081      //assert(c!=':' && c!='@',"msg.getOpt(): c!=':' && c!='@'");
1082      type = NONE;
1083      if (i < len) {
1084        t = shortarray[i];
1085        if (t == ':') {
1086          i++;
1087          if (i < len && shortarray[i] == ':') {
1088            type = OPT;
1089            i++;
1090          } else
1091            type = REQ;
1092        } else if (t == '@') {
1093          shortarray[i] = ':';
1094          i++;
1095          if (i < len && shortarray[i] == '@') {
1096            shortarray[i] = ':';
1097            type = OPTARRAY;
1098            i++;
1099          } else
1100            type = REQARRAY;
1101        }
1102      }
1103      typeTable.put("" + c, type);
1104    }
1105    shortspec = String.copyValueOf(shortarray);
1106    //
1107    // Parse long options.
1108    //
1109    List longspecs = new LinkedList();
1110    for (int i = 1; i < specs.size(); ++i) {
1111      String lopt = (String) specs.get(i);
1112      if (lopt.length() == 0)
1113        continue;
1114      int index = 0;
1115      type = NONE;
1116      stype = "";
1117      int itype = LongOpt.NO_ARGUMENT;
1118      if (lopt.endsWith("::")) {
1119        index = 2;
1120        type = OPT;
1121        stype = "::";
1122        itype = LongOpt.OPTIONAL_ARGUMENT;
1123      } else if (lopt.endsWith(":")) {
1124        index = 1;
1125        type = REQ;
1126        stype = ":";
1127        itype = LongOpt.REQUIRED_ARGUMENT;
1128      } else if (lopt.endsWith("@@")) {
1129        index = 2;
1130        type = OPTARRAY;
1131        stype = "::";
1132        itype = LongOpt.OPTIONAL_ARGUMENT;
1133      } else if (lopt.endsWith("@")) {
1134        index = 1;
1135        type = REQARRAY;
1136        stype = ":";
1137        itype = LongOpt.REQUIRED_ARGUMENT;
1138      }
1139      if (type != NONE)
1140        lopt = lopt.substring(0, lopt.length() - index);
1141      index = lopt.indexOf('=');
1142      if (index >= 0) {
1143        // With short alias.
1144        if (!(index != 0 && index < (lopt.length() - 1)))
1145          msg.err("msg.getOpt(): index!=0 && index<lopt.length-1");
1146        String sopt = lopt.substring(index + 1);
1147        lopt = lopt.substring(0, index);
1148        alias.put(sopt, lopt);
1149        shortspec += sopt + stype;
1150        typeTable.put(lopt, type);
1151        longspecs.add(new LongOpt(lopt, itype, null, 0));
1152      } else {
1153        // Long option only.
1154        typeTable.put(lopt, type);
1155        longspecs.add(new LongOpt(lopt, itype, null, 0));
1156      }
1157    }
1158    Getopt opt;
1159    if (longspecs.size() > 0) {
1160      LongOpt[] longspec = new LongOpt[longspecs.size()];
1161      for (int i = 0; i < longspecs.size(); ++i)
1162        longspec[i] = (LongOpt) longspecs.get(i);
1163      opt = new Getopt(name, args, shortspec, longspec, true);
1164    } else
1165      opt = new Getopt(name, args, shortspec);
1166
1167    // Set options according to command line arguments.
1168    int optc;
1169    while ((optc = opt.getopt()) >= 0) {
1170      String key;
1171      List a;
1172      if (optc == 0) {
1173        // Long options.
1174        int n = opt.getLongind();
1175        key = ((LongOpt) longspecs.get(n)).getName();
1176      } else {
1177        key = (String) alias.get("" + (char) optc);
1178        if (key == null)
1179          key = "" + (char) optc;
1180      }
1181      switch (typeTable.get(key)) {
1182        case NONE :
1183          ret.put(key, "1");
1184          break;
1185        case OPT :
1186        case REQ :
1187          ret.put(key, opt.getOptarg());
1188          break;
1189        case OPTARRAY :
1190        case REQARRAY :
1191          a = (List) ret.get(key);
1192          if (a == null)
1193            ret.put(key, a = new ArrayList());
1194          a.add(opt.getOptarg());
1195          break;
1196        default :
1197          msg.err(
1198            "msg.getOpt(): Invalid type: "
1199              + typeTable.get(key)
1200              + ", key="
1201              + key
1202              + ", option="
1203              + opt.getOptopt());
1204          break;
1205      }
1206    }
1207    return opt.getOptind();
1208  }
1209
1210  /** Return the stripped args[] instead of optind.
1211   */
1212  public static String[] getArgs(Map ret, String name, String[] args, String spec) {
1213
1214    int optind = msg.getOpt(ret, name, args, spec);
1215
1216    ///// Remove options from the args string.
1217    //
1218    String[] newArgs = new String[args.length - optind];
1219    for (int i = optind; i < args.length; i++) {
1220      newArgs[i - optind] = args[i];
1221    }
1222    return newArgs;
1223  }
1224
1225  /** A convinient function to process standard option (-h,-D) for
1226   *  program that do not have extra options. Typically used by
1227   *  class that use the main routine as a test stub.
1228   */
1229  public static String[] standardOptions(String name, String[] args) {
1230    return standardOptions(name, args, null);
1231
1232  }
1233
1234  public static String[] standardOptions(String name, String[] args, String usage) {
1235
1236    Map opt = new HashMap();
1237    String[] ret = getArgs(opt, name, args, "D:X:h");
1238
1239    ///// Help
1240    // 
1241    if (opt.get("h") != null) {
1242</