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     var 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 }