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