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("&");
681 break;
682 case '<' :
683 ret.append("<");
684 break;
685 case '\'' :
686 ret.append("'");
687 break;
688 case '"' :
689 ret.append(""");
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/&/&/g", s);
709 s = re.substitute("s/</</g", s);
710 s = re.substitute("s/&/&/g", s);
711 s = re.substitute("s/</</g", s);
712 s = re.substitute("s/'/\\'/g", s);
713 s = re.substitute("s/"/\\\"/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