1 /*
   2  * Copyright (c) 2005, 2013, 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 /**
  27  * jrunscript JavaScript built-in functions and objects.
  28  */
  29 
  30 /**
  31  * Creates an object that delegates all method calls on
  32  * it to the 'invoke' method on the given delegate object.<br>
  33  *
  34  * Example:
  35  * <pre>
  36  * <code>
  37  *     var x  = { invoke: function(name, args) { //code...}
  38  *     var y = new JSInvoker(x);
  39  *     y.func(3, 3); // calls x.invoke('func', args); where args is array of arguments
  40  * </code>
  41  * </pre>
  42  * @param obj object to be wrapped by JSInvoker
  43  * @constructor
  44  */
  45 function JSInvoker(obj) {
  46     return new JSAdapter({
  47         __get__ : function(name) {
  48             return function() {
  49                 return obj.invoke(name, arguments);
  50             }
  51         }
  52     });
  53 }
  54 
  55 /**
  56  * This variable represents OS environment. Environment
  57  * variables can be accessed as fields of this object. For
  58  * example, env.PATH will return PATH value configured.
  59  */
  60 var env = new JSAdapter({
  61     __get__ : function (name) {
  62         return java.lang.System.getenv(name);
  63     },
  64     __has__ : function (name) {
  65         return java.lang.System.getenv().containsKey(name);
  66     },
  67     __getIds__ : function() {
  68         return java.lang.System.getenv().keySet().toArray();
  69     },
  70     __delete__ : function(name) {
  71         println("can't delete env item");
  72     },
  73     __put__ : function (name, value) {
  74         println("can't change env item");
  75     },
  76     toString: function() {
  77         return java.lang.System.getenv().toString();
  78     }
  79 });
  80 
  81 /**
  82  * Creates a convenient script object to deal with java.util.Map instances.
  83  * The result script object's field names are keys of the Map. For example,
  84  * scriptObj.keyName can be used to access value associated with given key.<br>
  85  * Example:
  86  * <pre>
  87  * <code>
  88  *     var x = java.lang.SystemProperties();
  89  *     var y = jmap(x);
  90  *     println(y['java.class.path']); // prints java.class.path System property
  91  *     delete y['java.class.path']; // remove java.class.path System property
  92  * </code>
  93  * </pre>
  94  *
  95  * @param map java.util.Map instance that will be wrapped
  96  * @constructor
  97  */
  98 function jmap(map) {
  99     return new JSAdapter({
 100         __get__ : function(name) {
 101             if (map.containsKey(name)) {
 102                 return map.get(name);
 103             } else {
 104                 return undefined;
 105             }
 106         },
 107         __has__ :  function(name) {
 108             return map.containsKey(name);
 109         },
 110 
 111         __delete__ : function (name) {
 112             return map.remove(name);
 113         },
 114         __put__ : function(name, value) {
 115             map.put(name, value);
 116         },
 117         __getIds__ : function() {
 118             return map.keySet().toArray();
 119         },
 120         toString: function() {
 121             return map.toString();
 122         }
 123     });
 124 }
 125 
 126 /**
 127  * Creates a convenient script object to deal with java.util.List instances.
 128  * The result script object behaves like an array. For example,
 129  * scriptObj[index] syntax can be used to access values in the List instance.
 130  * 'length' field gives size of the List. <br>
 131  *
 132  * Example:
 133  * <pre>
 134  * <code>
 135  *    var x = new java.util.ArrayList(4);
 136  *    x.add('Java');
 137  *    x.add('JavaScript');
 138  *    x.add('SQL');
 139  *    x.add('XML');
 140  *
 141  *    var y = jlist(x);
 142  *    println(y[2]); // prints third element of list
 143  *    println(y.length); // prints size of the list
 144  *
 145  * @param map java.util.List instance that will be wrapped
 146  * @constructor
 147  */
 148 function jlist(list) {
 149     function isValid(index) {
 150         return typeof(index) == 'number' &&
 151             index > -1 && index < list.size();
 152     }
 153     return new JSAdapter({
 154         __get__ :  function(name) {
 155             if (isValid(name)) {
 156                 return list.get(name);
 157             } else if (name == 'length') {
 158                 return list.size();
 159             } else {
 160                 return undefined;
 161             }
 162         },
 163         __has__ : function (name) {
 164             return isValid(name) || name == 'length';
 165         },
 166         __delete__ : function(name) {
 167             if (isValid(name)) {
 168                 list.remove(name);
 169             }
 170         },
 171         __put__ : function(name, value) {
 172             if (isValid(name)) {
 173                 list.set(name, value);
 174             }
 175         },
 176         __getIds__: function() {
 177             var res = new Array(list.size());
 178             for (var i = 0; i < res.length; i++) {
 179                 res[i] = i;
 180             }
 181             return res;
 182         },
 183         toString: function() {
 184             return list.toString();
 185         }
 186     });
 187 }
 188 
 189 /**
 190  * This is java.lang.System properties wrapped by JSAdapter.
 191  * For eg. to access java.class.path property, you can use
 192  * the syntax sysProps["java.class.path"]
 193  */
 194 var sysProps = new JSAdapter({
 195     __get__ : function (name) {
 196         return java.lang.System.getProperty(name);
 197     },
 198     __has__ : function (name) {
 199         return java.lang.System.getProperty(name) != null;
 200     },
 201     __getIds__ : function() {
 202         return java.lang.System.getProperties().keySet().toArray();
 203     },
 204     __delete__ : function(name) {
 205         java.lang.System.clearProperty(name);
 206         return true;
 207     },
 208     __put__ : function (name, value) {
 209         java.lang.System.setProperty(name, value);
 210     },
 211     toString: function() {
 212         return "<system properties>";
 213     }
 214 });
 215 
 216 // stdout, stderr & stdin
 217 var out = java.lang.System.out;
 218 var err = java.lang.System.err;
 219 // can't use 'in' because it is a JavaScript keyword :-(
 220 var inp = java.lang.System["in"];
 221 
 222 var BufferedInputStream = java.io.BufferedInputStream;
 223 var BufferedOutputStream = java.io.BufferedOutputStream;
 224 var BufferedReader = java.io.BufferedReader;
 225 var DataInputStream = java.io.DataInputStream;
 226 var File = java.io.File;
 227 var FileInputStream = java.io.FileInputStream;
 228 var FileOutputStream = java.io.FileOutputStream;
 229 var InputStream = java.io.InputStream;
 230 var InputStreamReader = java.io.InputStreamReader;
 231 var OutputStream = java.io.OutputStream;
 232 var Reader = java.io.Reader;
 233 var URL = java.net.URL;
 234 
 235 /**
 236  * Generic any object to input stream mapper
 237  * @param str input file name, URL or InputStream
 238  * @return InputStream object
 239  * @private
 240  */
 241 function inStream(str) {
 242     if (typeof(str) == "string") {
 243         // '-' means standard input
 244         if (str == '-') {
 245             return java.lang.System["in"];
 246         }
 247         // try file first
 248         var file = null;
 249         try {
 250             file = pathToFile(str);
 251         } catch (e) {
 252         }
 253         if (file && file.exists()) {
 254             return new FileInputStream(file);
 255         } else {
 256             try {
 257                 // treat the string as URL
 258                 return new URL(str).openStream();
 259             } catch (e) {
 260                 throw 'file or URL ' + str + ' not found';
 261             }
 262         }
 263     } else {
 264         if (str instanceof InputStream) {
 265             return str;
 266         } else if (str instanceof URL) {
 267             return str.openStream();
 268         } else if (str instanceof File) {
 269             return new FileInputStream(str);
 270         }
 271     }
 272     // everything failed, just give input stream
 273     return java.lang.System["in"];
 274 }
 275 
 276 /**
 277  * Generic any object to output stream mapper
 278  *
 279  * @param out output file name or stream
 280  * @return OutputStream object
 281  * @private
 282  */
 283 function outStream(out) {
 284     if (typeof(out) == "string") {
 285         if (out == '>') {
 286             return java.lang.System.out;
 287         } else {
 288             // treat it as file
 289             return new FileOutputStream(pathToFile(out));
 290         }
 291     } else {
 292         if (out instanceof OutputStream) {
 293             return out;
 294         } else if (out instanceof File) {
 295             return new FileOutputStream(out);
 296         }
 297     }
 298 
 299     // everything failed, just return System.out
 300     return java.lang.System.out;
 301 }
 302 
 303 /**
 304  * stream close takes care not to close stdin, out & err.
 305  * @private
 306  */
 307 function streamClose(stream) {
 308     if (stream) {
 309         if (stream != java.lang.System["in"] &&
 310             stream != java.lang.System.out &&
 311             stream != java.lang.System.err) {
 312             try {
 313                 stream.close();
 314             } catch (e) {
 315                 println(e);
 316             }
 317         }
 318     }
 319 }
 320 
 321 /**
 322  * Loads and evaluates JavaScript code from a stream or file or URL<br>
 323  *
 324  * Examples:
 325  * <pre>
 326  * <code>
 327  *    load('test.js'); // load script file 'test.js'
 328  *    load('http://java.sun.com/foo.js'); // load from a URL
 329  * </code>
 330  * </pre>
 331  *
 332  * @param str input from which script is loaded and evaluated
 333  */
 334 if (typeof(load) == 'undefined') {
 335     this.load = function(str) {
 336         var stream = inStream(str);
 337         var bstream = new BufferedInputStream(stream);
 338         var reader = new BufferedReader(new InputStreamReader(bstream));
 339         var oldFilename = engine.get(engine.FILENAME);
 340         engine.put(engine.FILENAME, str);
 341         try {
 342             engine.eval(reader);
 343         } finally {
 344             engine.put(engine.FILENAME, oldFilename);
 345             streamClose(stream);
 346         }
 347     }
 348 }
 349 
 350 // file system utilities
 351 
 352 /**
 353  * Creates a Java byte[] of given length
 354  * @param len size of the array to create
 355  * @private
 356  */
 357 function javaByteArray(len) {
 358     return java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, len);
 359 }
 360 
 361 var curDir = new File('.');
 362 
 363 /**
 364  * Print present working directory
 365  */
 366 function pwd() {
 367     println(curDir.getAbsolutePath());
 368 }
 369 
 370 /**
 371  * Changes present working directory to given directory
 372  * @param target directory to change to. optional, defaults to user's HOME
 373  */
 374 function cd(target) {
 375     if (target == undefined) {
 376         target = sysProps["user.home"];
 377     }
 378     if (!(target instanceof File)) {
 379         target = pathToFile(target);
 380     }
 381     if (target.exists() && target.isDirectory()) {
 382         curDir = target;
 383     } else {
 384         println(target + " is not a directory");
 385     }
 386 }
 387 
 388 /**
 389  * Converts path to java.io.File taking care of shell present working dir
 390  *
 391  * @param pathname file path to be converted
 392  * @private
 393  */
 394 function pathToFile(pathname) {
 395     var tmp = pathname;
 396     if (!(tmp instanceof File)) {
 397         tmp = new File(tmp);
 398     }
 399     if (!tmp.isAbsolute()) {
 400         return new File(curDir, pathname);
 401     } else {
 402         return tmp;
 403     }
 404 }
 405 
 406 /**
 407  * Copies a file or URL or stream to another file or stream
 408  *
 409  * @param from input file or URL or stream
 410  * @param to output stream or file
 411  */
 412 function cp(from, to) {
 413     if (from == to) {
 414         println("file " + from + " cannot be copied onto itself!");
 415         return;
 416     }
 417     var inp = inStream(from);
 418     var out = outStream(to);
 419     var binp = new BufferedInputStream(inp);
 420     var bout = new BufferedOutputStream(out);
 421     var buff = javaByteArray(1024);
 422     var len;
 423     while ((len = binp.read(buff)) > 0 )
 424         bout.write(buff, 0, len);
 425 
 426     bout.flush();
 427     streamClose(inp);
 428     streamClose(out);
 429 }
 430 
 431 /**
 432  * Shows the content of a file or URL or any InputStream<br>
 433  * Examples:
 434  * <pre>
 435  * <code>
 436  *    cat('test.txt'); // show test.txt file contents
 437  *    cat('http://java.net'); // show the contents from the URL http://java.net
 438  * </code>
 439  * </pre>
 440  * @param obj input to show
 441  * @param pattern optional. show only the lines matching the pattern
 442  */
 443 function cat(obj, pattern) {
 444     if (obj instanceof File && obj.isDirectory()) {
 445         ls(obj);
 446         return;
 447     }
 448 
 449     var inp = null;
 450     if (!(obj instanceof Reader)) {
 451         inp = inStream(obj);
 452         obj = new BufferedReader(new InputStreamReader(inp));
 453     }
 454     var line;
 455     if (pattern) {
 456         var count = 1;
 457         while ((line=obj.readLine()) != null) {
 458             if (line.match(pattern)) {
 459                 println(count + "\t: " + line);
 460             }
 461             count++;
 462         }
 463     } else {
 464         while ((line=obj.readLine()) != null) {
 465             println(line);
 466         }
 467     }
 468 }
 469 
 470 /**
 471  * Returns directory part of a filename
 472  *
 473  * @param pathname input path name
 474  * @return directory part of the given file name
 475  */
 476 function dirname(pathname) {
 477     var dirName = ".";
 478     // Normalize '/' to local file separator before work.
 479     var i = pathname.replace('/', File.separatorChar ).lastIndexOf(
 480         File.separator );
 481     if ( i != -1 )
 482         dirName = pathname.substring(0, i);
 483     return dirName;
 484 }
 485 
 486 /**
 487  * Creates a new dir of given name
 488  *
 489  * @param dir name of the new directory
 490  */
 491 function mkdir(dir) {
 492     dir = pathToFile(dir);
 493     println(dir.mkdir()? "created" : "can not create dir");
 494 }
 495 
 496 /**
 497  * Creates the directory named by given pathname, including
 498  * any necessary but nonexistent parent directories.
 499  *
 500  * @param dir input path name
 501  */
 502 function mkdirs(dir) {
 503     dir = pathToFile(dir);
 504     println(dir.mkdirs()? "created" : "can not create dirs");
 505 }
 506 
 507 /**
 508  * Removes a given file
 509  *
 510  * @param pathname name of the file
 511  */
 512 function rm(pathname) {
 513     var file = pathToFile(pathname);
 514     if (!file.exists()) {
 515         println("file not found: " + pathname);
 516         return false;
 517     }
 518     // note that delete is a keyword in JavaScript!
 519     println(file["delete"]()? "deleted" : "can not delete");
 520 }
 521 
 522 /**
 523  * Removes a given directory
 524  *
 525  * @param pathname name of the directory
 526  */
 527 function rmdir(pathname) {
 528     rm(pathname);
 529 }
 530 
 531 /**
 532  * Synonym for 'rm'
 533  */
 534 function del(pathname) {
 535     rm(pathname);
 536 }
 537 
 538 /**
 539  * Moves a file to another
 540  *
 541  * @param from original name of the file
 542  * @param to new name for the file
 543  */
 544 function mv(from, to) {
 545     println(pathToFile(from).renameTo(pathToFile(to))?
 546         "moved" : "can not move");
 547 }
 548 
 549 /**
 550  * Synonym for 'mv'.
 551  */
 552 function ren(from, to) {
 553     mv(from, to);
 554 }
 555 
 556 var months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 557         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
 558 
 559 /**
 560  * Helper function called by ls
 561  * @private
 562  */
 563 function printFile(f) {
 564     var sb = new java.lang.StringBuffer();
 565     sb.append(f.isDirectory()? "d" : "-");
 566     sb.append(f.canRead() ? "r": "-" );
 567     sb.append(f.canWrite() ? "w": "-" );
 568     sb.append(" ");
 569 
 570     var d = new java.util.Date(f.lastModified());
 571     var c = new java.util.GregorianCalendar();
 572     c.setTime(d);
 573     var day    = c.get(java.util.Calendar.DAY_OF_MONTH);
 574     sb.append(months[c.get(java.util.Calendar.MONTH)]
 575          + " " + day );
 576     if (day < 10) {
 577         sb.append(" ");
 578     }
 579 
 580     // to get fixed length 'length' field
 581     var fieldlen = 8;
 582     var len = new java.lang.StringBuffer();
 583     for(var j=0; j<fieldlen; j++)
 584         len.append(" ");
 585     len.insert(0, java.lang.Long.toString(f.length()));
 586     len.setLength(fieldlen);
 587     // move the spaces to the front
 588     var si = len.toString().indexOf(" ");
 589     if ( si != -1 ) {
 590         var pad = len.toString().substring(si);
 591         len.setLength(si);
 592         len.insert(0, pad);
 593     }
 594     sb.append(len.toString());
 595     sb.append(" ");
 596     sb.append(f.getName());
 597     if (f.isDirectory()) {
 598         sb.append('/');
 599     }
 600     println(sb.toString());
 601 }
 602 
 603 /**
 604  * Lists the files in a directory
 605  *
 606  * @param dir directory from which to list the files. optional, default to pwd
 607  * @param filter pattern to filter the files listed. optional, default is '.'.
 608  */
 609 function ls(dir, filter) {
 610     if (dir) {
 611         dir = pathToFile(dir);
 612     } else {
 613         dir = curDir;
 614     }
 615     if (dir.isDirectory()) {
 616         var files = dir.listFiles();
 617         for (var i in files) {
 618             var f = files[i];
 619             if (filter) {
 620                 if(!f.getName().match(filter)) {
 621                     continue;
 622                 }
 623             }
 624             printFile(f);
 625         }
 626     } else {
 627         printFile(dir);
 628     }
 629 }
 630 
 631 /**
 632  * Synonym for 'ls'.
 633  */
 634 function dir(d, filter) {
 635     ls(d, filter);
 636 }
 637 
 638 /**
 639  * Unix-like grep, but accepts JavaScript regex patterns
 640  *
 641  * @param pattern to search in files
 642  * @param files one or more files
 643  */
 644 function grep(pattern, files /*, one or more files */) {
 645     if (arguments.length < 2) return;
 646     for (var i = 1; i < arguments.length; i++) {
 647         println(arguments[i] + ":");
 648         cat(arguments[i], pattern);
 649     }
 650 }
 651 
 652 /**
 653  * Find in files. Calls arbitrary callback function
 654  * for each matching file.<br>
 655  *
 656  * Examples:
 657  * <pre>
 658  * <code>
 659  *    find('.')
 660  *    find('.', '.*\.class', rm);  // remove all .class files
 661  *    find('.', '.*\.java');       // print fullpath of each .java file
 662  *    find('.', '.*\.java', cat);  // print all .java files
 663  * </code>
 664  * </pre>
 665  *
 666  * @param dir directory to search files
 667  * @param pattern to search in the files
 668  * @param callback function to call for matching files
 669  */
 670 function find(dir, pattern, callback) {
 671     dir = pathToFile(dir);
 672     if (!callback) callback = print;
 673     var files = dir.listFiles();
 674     for (var f in files) {
 675         var file = files[f];
 676         if (file.isDirectory()) {
 677             find(file, pattern, callback);
 678         } else {
 679             if (pattern) {
 680                 if (file.getName().match(pattern)) {
 681                     callback(file);
 682                 }
 683             } else {
 684                 callback(file);
 685             }
 686         }
 687     }
 688 }
 689 
 690 // process utilities
 691 
 692 /**
 693  * Exec's a child process, waits for completion &amp; returns exit code
 694  *
 695  * @param cmd command to execute in child process
 696  */
 697 function exec(cmd) {
 698     var process = java.lang.Runtime.getRuntime().exec(cmd);
 699     var inp = new DataInputStream(process.getInputStream());
 700     var line = null;
 701     while ((line = inp.readLine()) != null) {
 702         println(line);
 703     }
 704     process.waitFor();
 705     $exit = process.exitValue();
 706 }
 707 
 708 if (typeof(exit) == 'undefined') {
 709     /**
 710      * Exit the shell program.
 711      *
 712      * @param exitCode integer code returned to OS shell.
 713      * optional, defaults to 0
 714      */
 715     this.exit = function (code) {
 716         if (code) {
 717             java.lang.System.exit(code + 0);
 718         } else {
 719             java.lang.System.exit(0);
 720         }
 721     }
 722 }
 723 
 724 if (typeof(quit) == 'undefined') {
 725     /**
 726      * synonym for exit
 727      */
 728     this.quit = function (code) {
 729         exit(code);
 730     }
 731 }
 732 
 733 // XML utilities
 734 
 735 /**
 736  * Converts input to DOM Document object
 737  *
 738  * @param inp file or reader. optional, without this param,
 739  * this function returns a new DOM Document.
 740  * @return returns a DOM Document object
 741  */
 742 function XMLDocument(inp) {
 743     var factory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
 744     var builder = factory.newDocumentBuilder();
 745     if (inp) {
 746         if (typeof(inp) == "string") {
 747             return builder.parse(pathToFile(inp));
 748         } else {
 749             return builder.parse(inp);
 750         }
 751     } else {
 752         return builder.newDocument();
 753     }
 754 }
 755 
 756 /**
 757  * Converts arbitrary stream, file, URL to XMLSource
 758  *
 759  * @param inp input stream or file or URL
 760  * @return XMLSource object
 761  */
 762 function XMLSource(inp) {
 763     if (inp instanceof javax.xml.transform.Source) {
 764         return inp;
 765     } else if (inp instanceof Packages.org.w3c.dom.Document) {
 766         return new javax.xml.transform.dom.DOMSource(inp);
 767     } else {
 768         inp = new BufferedInputStream(inStream(inp));
 769         return new javax.xml.transform.stream.StreamSource(inp);
 770     }
 771 }
 772 
 773 /**
 774  * Converts arbitrary stream, file to XMLResult
 775  *
 776  * @param inp output stream or file
 777  * @return XMLResult object
 778  */
 779 function XMLResult(out) {
 780     if (out instanceof javax.xml.transform.Result) {
 781         return out;
 782     } else if (out instanceof Packages.org.w3c.dom.Document) {
 783         return new javax.xml.transform.dom.DOMResult(out);
 784     } else {
 785         out = new BufferedOutputStream(outStream(out));
 786         return new javax.xml.transform.stream.StreamResult(out);
 787     }
 788 }
 789 
 790 /**
 791  * Perform XSLT transform
 792  *
 793  * @param inp Input XML to transform (URL, File or InputStream)
 794  * @param style XSL Stylesheet to be used (URL, File or InputStream). optional.
 795  * @param out Output XML (File or OutputStream
 796  */
 797 function XSLTransform(inp, style, out) {
 798     switch (arguments.length) {
 799     case 2:
 800         inp = arguments[0];
 801         out = arguments[1];
 802         break;
 803     case 3:
 804         inp = arguments[0];
 805         style = arguments[1];
 806         out = arguments[2];
 807         break;
 808     default:
 809         println("XSL transform requires 2 or 3 arguments");
 810         return;
 811     }
 812 
 813     var factory = javax.xml.transform.TransformerFactory.newInstance();
 814     var transformer;
 815     if (style) {
 816         transformer = factory.newTransformer(XMLSource(style));
 817     } else {
 818         transformer = factory.newTransformer();
 819     }
 820     var source = XMLSource(inp);
 821     var result = XMLResult(out);
 822     transformer.transform(source, result);
 823     if (source.getInputStream) {
 824         streamClose(source.getInputStream());
 825     }
 826     if (result.getOutputStream) {
 827         streamClose(result.getOutputStream());
 828     }
 829 }
 830 
 831 // miscellaneous utilities
 832 
 833 /**
 834  * Prints which command is selected from PATH
 835  *
 836  * @param cmd name of the command searched from PATH
 837  */
 838 function which(cmd) {
 839     var st = new java.util.StringTokenizer(env.PATH, File.pathSeparator);
 840     while (st.hasMoreTokens()) {
 841         var file = new File(st.nextToken(), cmd);
 842         if (file.exists()) {
 843             println(file.getAbsolutePath());
 844             return;
 845         }
 846     }
 847 }
 848 
 849 /**
 850  * Prints IP addresses of given domain name
 851  *
 852  * @param name domain name
 853  */
 854 function ip(name) {
 855     var addrs = InetAddress.getAllByName(name);
 856     for (var i in addrs) {
 857         println(addrs[i]);
 858     }
 859 }
 860 
 861 /**
 862  * Prints current date in current locale
 863  */
 864 function date() {
 865     println(new Date().toLocaleString());
 866 }
 867 
 868 /**
 869  * Echoes the given string arguments
 870  */
 871 function echo(x) {
 872     for (var i = 0; i < arguments.length; i++) {
 873         println(arguments[i]);
 874     }
 875 }
 876 
 877 if (typeof(printf) == 'undefined') {
 878     /**
 879      * This is C-like printf 
 880      *
 881      * @param format string to format the rest of the print items
 882      * @param args variadic argument list
 883      */
 884     this.printf = function (format, args/*, more args*/) {  
 885         var array = java.lang.reflect.Array.newInstance(java.lang.Object, 
 886                     arguments.length - 1);
 887         for (var i = 0; i < array.length; i++) {
 888             array[i] = arguments[i+1];
 889         }
 890         java.lang.System.out.printf(format, array);
 891     }
 892 }
 893 
 894 /**
 895  * Reads one or more lines from stdin after printing prompt
 896  *
 897  * @param prompt optional, default is '>'
 898  * @param multiline to tell whether to read single line or multiple lines
 899  */
 900 function read(prompt, multiline) {
 901     if (!prompt) {
 902         prompt = '>';
 903     }
 904     var inp = java.lang.System["in"];
 905     var reader = new BufferedReader(new InputStreamReader(inp));
 906     if (multiline) {
 907         var line = '';
 908         while (true) {
 909             java.lang.System.err.print(prompt);
 910             java.lang.System.err.flush();
 911             var tmp = reader.readLine();
 912             if (tmp == '' || tmp == null) break;
 913             line += tmp + '\n';
 914         }
 915         return line;
 916     } else {
 917         java.lang.System.err.print(prompt);
 918         java.lang.System.err.flush();
 919         return reader.readLine();
 920     }
 921 }
 922 
 923 if (typeof(println) == 'undefined') {
 924     // just synonym to print
 925     this.println = print;
 926 }
 927