Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

2381
LINES

< > BotCompany Repo | #750 // x25.java (JavaX)

JavaX source code [tags: use-pretranspiled] - run with: x30.jar

Libraryless. Click here for Pure Java version (4185L/33K/87K).

1  
!747
2  
!awt {
3  
!actionListener {
4  
5  
/**
6  
 JavaX runner version 25
7  
8  
 Changes to v24:
9  
 -Moved to JavaX :)
10  
 -Updating installHelloMessage
11  
 -Adding console input (System.in)
12  
13  
 */
14  
15  
m { p { throw new RuntimeException("placebo"); } }
16  
17  
class x25 implements Runnable {
18  
  static final String version = "JavaX 25";
19  
20  
  // If programs run longer than this, they might have their class files
21  
  // deleted.
22  
  static int tempFileRetentionTime = 24; // hours
23  
  
24  
  static boolean verbose = false, translate = false, list = false, virtualizeTranslators = true;
25  
  static String translateTo = null;
26  
  static boolean preferCached = false, noID = false, noPrefetch = false;
27  
  static boolean safeOnly = false, safeTranslate = false, javacOnly = false, logOn = true;
28  
  static boolean runMainInProcess = true, consoleOn = true, hasHelloMessage = false;
29  
  static List<String[]> mainTranslators = new ArrayList<String[]>();
30  
  private static Map<Long, String> memSnippetCache = new HashMap<Long, String>();
31  
  private static int processesStarted, compilations;
32  
33  
  // snippet ID -> md5
34  
  private static HashMap<Long, String> prefetched = new HashMap<Long, String>();
35  
  private static File virtCache;
36  
37  
  // doesn't work yet
38  
  private static Map<String, Class<?>> programCache = new HashMap<String, Class<?>>();
39  
  static boolean cacheTranslators = false;
40  
41  
  // this should work (caches transpiled translators)
42  
  private static HashMap<Long, Object[]> translationCache = new HashMap<Long, Object[]>();
43  
  static boolean cacheTranspiledTranslators = true;
44  
45  
  // which snippets are available pre-transpiled server-side?
46  
  private static Set<Long> hasTranspiledSet = new HashSet<Long>();
47  
  static boolean useServerTranspiled = true;
48  
49  
  static Object androidContext;
50  
  static boolean android = isAndroid();
51  
52  
  // Translators currently being translated (to detect recursions)
53  
  private static Set<Long> translating = new HashSet<Long>();
54  
55  
  static String lastOutput;
56  
  static String[] fullArgs;
57  
  private static Console console;
58  
59  
  public static void main(String[] args) {
60  
    try {
61  
      goMain(args);
62  
    } catch (Throwable e) {
63  
      e.printStackTrace();
64  
    }
65  
  }
66  
  
67  
  static void goMain(String[] args) throws Exception {
68  
    if (args.length != 0 && args[0].equals("-v")) verbose = true;
69  
    redirectSystemOutAndErr();
70  
71  
    if (consoleOn && console == null)
72  
      tryToOpenConsole(args);
73  
    
74  
    String autoReport = loadTextFile(new File(userHome(), ".javax/auto-report-to-chat").getPath(), "").trim();
75  
    //print("autoReport=" + autoReport);
76  
    if (!isChatServer(args) && autoReport.equals("1"))
77  
      autoReportToChat();
78  
79  
    if (!hasHelloMessage) {
80  
      hasHelloMessage = true;
81  
      installHelloMessage(args.length == 0 ? "JavaX Start-Up VM" : "JavaX VM (" + smartJoin(args) + ")");
82  
    }
83  
    
84  
    File ioBaseDir = new File("."), inputDir = null, outputDir = null;
85  
    String src = null;
86  
    List<String> programArgs = new ArrayList<String>();
87  
    fullArgs = args;
88  
89  
    for (int i = 0; i < args.length; i++) {
90  
      String arg = args[i];
91  
92  
      if (arg.equals("-version")) {
93  
        showVersion();
94  
        return;
95  
      }
96  
97  
      if (arg.equals("-sysprop")) {
98  
        showSystemProperties();
99  
        return;
100  
      }
101  
102  
      if (arg.equals("-v") || arg.equals("-verbose"))
103  
        verbose = true;
104  
      else if (arg.equals("-finderror"))
105  
        verbose = true;
106  
      else if (arg.equals("-offline") || arg.equalsIgnoreCase("-prefercached"))
107  
        preferCached = true;
108  
      else if (arg.equals("-novirt"))
109  
        virtualizeTranslators = false;
110  
      else if (arg.equals("-safeonly"))
111  
        safeOnly = true;
112  
      else if (arg.equals("-safetranslate"))
113  
        safeTranslate = true;
114  
      else if (arg.equals("-noid"))
115  
        noID = true;
116  
      else if (arg.equals("-nocachetranspiled"))
117  
        cacheTranspiledTranslators = false;
118  
      else if (arg.equals("-javac"))
119  
        javacOnly = true;
120  
      else if (arg.equals("-localtranspile"))
121  
        useServerTranspiled = false;
122  
      else if (arg.equals("translate") && src == null)
123  
        translate = true;
124  
      else if (arg.equals("list") && src == null) {
125  
        list = true;
126  
        virtualizeTranslators = false; // so they are silenced
127  
      } else if (arg.equals("run") && src == null) {
128  
        // it's the default command anyway
129  
      } else if (arg.startsWith("input="))
130  
        inputDir = new File(arg.substring(6));
131  
      else if (arg.startsWith("output="))
132  
        outputDir = new File(arg.substring(7));
133  
      else if (arg.equals("with"))
134  
        mainTranslators.add(new String[] {args[++i], null});
135  
      else if (translate && arg.equals("to"))
136  
        translateTo = args[++i];
137  
      else if (src == null) {
138  
        //System.out.println("src=" + arg);
139  
        src = arg;
140  
      } else
141  
        programArgs.add(arg);
142  
    }
143  
144  
    cleanCache();
145  
146  
    if (useServerTranspiled)
147  
      noPrefetch = true;
148  
149  
    if (src == null) src = ".";
150  
151  
    // Might actually want to write to 2 disk caches (global/per program).
152  
    if (virtualizeTranslators && !preferCached)
153  
      virtCache = TempDirMaker_make();
154  
155  
    if (inputDir != null) {
156  
      ioBaseDir = TempDirMaker_make();
157  
      System.out.println("Taking input from: " + inputDir.getAbsolutePath());
158  
      System.out.println("Output is in: " + new File(ioBaseDir, "output").getAbsolutePath());
159  
      copyInput(inputDir, new File(ioBaseDir, "input"));
160  
    }
161  
    
162  
    if (logOn)
163  
      logStart(args);
164  
165  
    javaxmain(src, ioBaseDir, translate, list, programArgs.toArray(new String[programArgs.size()]));
166  
167  
    if (outputDir != null) {
168  
      copyInput(new File(ioBaseDir, "output"), outputDir);
169  
      System.out.println("Output copied to: " + outputDir.getAbsolutePath());
170  
    }
171  
172  
    if (verbose) {
173  
      // print stats
174  
      System.out.println("Processes started: " + processesStarted + ", compilations: " + compilations);
175  
    }
176  
  }
177  
178  
  public static void javaxmain(String src, File ioDir, boolean translate, boolean list,
179  
                               String[] args) throws Exception {
180  
    String programID = isSnippetID(src) ? "" + parseSnippetID(src) : null;
181  
    
182  
    if (programID != null)
183  
      System.err.println("JavaX TRANSLATE " + programID + " " + smartJoin(args));
184  
    
185  
    List<File> libraries = new ArrayList<File>();
186  
    File X = transpileMain(src, libraries);
187  
    if (X == null) {
188  
      showVersion();
189  
      
190  
      if (fullArgs != null) {
191  
        String[] nargs;
192  
        if (fullArgs.length == 0)
193  
          nargs = new String[] {"1000825"}; // swing-start
194  
        else {
195  
          // forward to search
196  
          nargs = new String[fullArgs.length+1];
197  
          nargs[0] = "636";
198  
          // nargs[1] = "search-runnables";
199  
          System.arraycopy(fullArgs, 0, nargs, 1, fullArgs.length);
200  
        }
201  
        main(nargs); // Hopefully we get no infinite recursion :)
202  
        return;
203  
      }
204  
      
205  
      System.out.println("No main.java found, exiting");
206  
      return;
207  
    }
208  
209  
    // list or run
210  
211  
    if (translate) {
212  
      File to = X;
213  
      if (translateTo != null) {
214  
        StringBuilder buf = new StringBuilder();
215  
        for (File f : libraries) buf.append(f.getName()+"\n");
216  
        if (new File(translateTo).isDirectory()) {
217  
          to = new File(translateTo, "main.java");
218  
          saveTextFile(new File(translateTo, "libraries.txt").getPath(), buf.toString());
219  
        } else {
220  
          to = new File(translateTo);
221  
          saveTextFile(new File(translateTo + "_libraries").getPath(), buf.toString());
222  
        }
223  
      }
224  
      if (to != X)
225  
        copy(new File(X, "main.java"), to);
226  
      System.out.println("Program translated to: " + to.getAbsolutePath());
227  
    } else if (list)
228  
      System.out.println(loadTextFile(new File(X, "main.java").getPath(), null));
229  
    else {
230  
      if (programID != null)
231  
        System.err.println("JavaX RUN " + programID + " " + smartJoin(args));
232  
      System.err.println(); // Make empty line before actual program starts
233  
      
234  
      javax2(X, ioDir, false, runMainInProcess, libraries, args, null, programID);
235  
      
236  
      if (verbose) System.out.println("JavaX main done");
237  
      // cleanup reportToChat thread
238  
      if (reportToChat_q != null) {
239  
        if (customSystemOut != null)
240  
          Thread.sleep(1000); // delay to finish autoReportToChat. Yes it's hacky.
241  
        if (verbose) System.out.println("Closing reportToChat queue");
242  
        reportToChat_q.done();
243  
      }
244  
    }
245  
  }
246  
247  
  static File transpileMain(String src, List<File> libraries) throws Exception {
248  
    File srcDir;
249  
    boolean isTranspiled = false;
250  
    if (isSnippetID(src)) {
251  
      prefetch(src);
252  
      long id = parseSnippetID(src);
253  
      prefetched.remove(id); // hackfix to ensure transpiled main program is found.
254  
      srcDir = loadSnippetAsMainJava(src);
255  
      if (verbose)
256  
        System.err.println("hasTranspiledSet: " + hasTranspiledSet);
257  
      if (hasTranspiledSet.contains(id) && useServerTranspiled) {
258  
        //System.err.println("Trying pretranspiled main program: #" + id);
259  
        String transpiledSrc = getServerTranspiled("#" + id);
260  
        int i = transpiledSrc.indexOf('\n');
261  
        String libs = transpiledSrc.substring(0, Math.max(0, i));
262  
        transpiledSrc = transpiledSrc.substring(i+1);
263  
        if (!transpiledSrc.isEmpty()) {
264  
          srcDir = TempDirMaker_make();
265  
          saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
266  
          isTranspiled = true;
267  
          //translationCache.put(id, new Object[] {srcDir, libraries});
268  
269  
          Matcher m = Pattern.compile("\\d+").matcher(libs);
270  
          while (m.find()) {
271  
            String libid = m.group();
272  
            File libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(libid));
273  
            loadLibrary(libid, libraries, libraryFile);
274  
          }
275  
        }
276  
      }
277  
    } else {
278  
      srcDir = new File(src);
279  
280  
      // if the argument is a file, it is assumed to be main.java
281  
      if (srcDir.isFile()) {
282  
        srcDir = TempDirMaker_make();
283  
        copy(new File(src), new File(srcDir, "main.java"));
284  
      }
285  
286  
      if (!new File(srcDir, "main.java").exists())
287  
        return null;
288  
    }
289  
290  
    // translate
291  
292  
    File X = srcDir;
293  
294  
    if (!isTranspiled) {
295  
      X = topLevelTranslate(X, libraries);
296  
      System.err.println("Translated " + src);
297  
298  
      // save prefetch data
299  
      if (isSnippetID(src))
300  
        savePrefetchData(src);
301  
    }
302  
    return X;
303  
  }
304  
305  
  private static void prefetch(String mainSnippetID) throws IOException {
306  
    if (noPrefetch) return;
307  
308  
    long mainID = parseSnippetID(mainSnippetID);
309  
    String s = mainID + " " + loadTextFile(new File(userHome(), ".tinybrain/prefetch/" + mainID + ".txt").getPath(), "");
310  
    String[] ids = s.trim().split(" ");
311  
    if (ids.length > 1) {
312  
      String url = "http://tinybrain.de:8080/tb-int/prefetch.php?ids=" + URLEncoder.encode(s, "UTF-8");
313  
      String data = loadPage(new URL(url));
314  
      String[] split = data.split(" ");
315  
      if (split.length == ids.length)
316  
        for (int i = 0; i < ids.length; i++)
317  
          prefetched.put(parseSnippetID(ids[i]), split[i]);
318  
    }
319  
  }
320  
321  
  static String userHome() {
322  
    if (android)
323  
      return ((File) call(androidContext, "getFilesDir")).getAbsolutePath();
324  
    else
325  
      return System.getProperty("user.home");
326  
  }
327  
328  
  private static void savePrefetchData(String mainSnippetID) throws IOException {
329  
    List<String> ids = new ArrayList<String>();
330  
    long mainID = parseSnippetID(mainSnippetID);
331  
332  
    for (long id : memSnippetCache.keySet())
333  
      if (id != mainID)
334  
        ids.add(String.valueOf(id));
335  
336  
    saveTextFile(new File(userHome(),".tinybrain/prefetch/" + mainID + ".txt").getPath(), join(" ", ids));
337  
  }
338  
339  
  static File topLevelTranslate(File srcDir, List<File> libraries_out) throws Exception {
340  
    File X = srcDir;
341  
    X = applyTranslators(X, mainTranslators, libraries_out); // translators supplied on command line (unusual)
342  
343  
    // actual inner translation of the JavaX source
344  
    X = defaultTranslate(X, libraries_out);
345  
    return X;
346  
  }
347  
348  
  private static File defaultTranslate(File x, List<File> libraries_out) throws Exception {
349  
    x = luaPrintToJavaPrint(x);
350  
    x = repeatAutoTranslate(x, libraries_out);
351  
    return x;
352  
  }
353  
354  
  private static File repeatAutoTranslate(File x, List<File> libraries_out) throws Exception {
355  
    while (true) {
356  
      File y = autoTranslate(x, libraries_out);
357  
      if (y == x)
358  
        return x;
359  
      x = y;
360  
    }
361  
  }
362  
363  
  private static File autoTranslate(File x, List<File> libraries_out) throws Exception {
364  
    String main = loadTextFile(new File(x, "main.java").getPath(), null);
365  
    List<String> lines = toLines(main);
366  
    List<String[]> translators = findTranslators(lines);
367  
    if (translators.isEmpty())
368  
      return x;
369  
370  
    main = fromLines(lines);
371  
    File newDir = TempDirMaker_make();
372  
    saveTextFile(new File(newDir, "main.java").getPath(), main);
373  
    return applyTranslators(newDir, translators, libraries_out);
374  
  }
375  
376  
  private static List<String[]> findTranslators(List<String> lines) {
377  
    List<String[]> translators = new ArrayList<String[]>();
378  
    Pattern pattern = Pattern.compile("^!([0-9# \t]+)");
379  
    Pattern pArgs = Pattern.compile("^\\s*\\((.*)\\)");
380  
    for (ListIterator<String> iterator = lines.listIterator(); iterator.hasNext(); ) {
381  
      String line = iterator.next();
382  
      line = line.trim();
383  
      Matcher matcher = pattern.matcher(line);
384  
      if (matcher.find()) {
385  
        String[] t = matcher.group(1).split("[ \t]+");
386  
        String rest = line.substring(matcher.end());
387  
        String arg = null;
388  
        if (t.length == 1) {
389  
          Matcher mArgs = pArgs.matcher(rest);
390  
          if (mArgs.find())
391  
            arg = mArgs.group(1);
392  
        }
393  
        for (String transi : t)
394  
          translators.add(new String[]{transi, arg});
395  
        iterator.remove();
396  
      }
397  
    }
398  
    return translators;
399  
  }
400  
401  
  public static List<String> toLines(String s) {
402  
    List<String> lines = new ArrayList<String>();
403  
    int start = 0;
404  
    while (true) {
405  
      int i = toLines_nextLineBreak(s, start);
406  
      if (i < 0) {
407  
        if (s.length() > start) lines.add(s.substring(start));
408  
        break;
409  
      }
410  
411  
      lines.add(s.substring(start, i));
412  
      if (s.charAt(i) == '\r' && i+1 < s.length() && s.charAt(i+1) == '\n')
413  
        i += 2;
414  
      else
415  
        ++i;
416  
417  
      start = i;
418  
    }
419  
    return lines;
420  
  }
421  
422  
  private static int toLines_nextLineBreak(String s, int start) {
423  
    for (int i = start; i < s.length(); i++) {
424  
      char c = s.charAt(i);
425  
      if (c == '\r' || c == '\n')
426  
        return i;
427  
    }
428  
    return -1;
429  
  }
430  
431  
  public static String fromLines(List<String> lines) {
432  
    StringBuilder buf = new StringBuilder();
433  
    for (String line : lines) {
434  
      buf.append(line).append('\n');
435  
    }
436  
    return buf.toString();
437  
  }
438  
439  
  private static File applyTranslators(File x, List<String[]> translators, List<File> libraries_out) throws Exception {
440  
    for (String[] translator : translators)
441  
      x = applyTranslator(x, translator[0], translator[1], libraries_out);
442  
    return x;
443  
  }
444  
445  
  // also takes a library
446  
  private static File applyTranslator(File x, String translator, String arg, List<File> libraries_out) throws Exception {
447  
    if (verbose)
448  
      System.out.println("Using translator " + translator + " on sources in " + x.getPath());
449  
450  
    File newDir = runTranslatorOnInput(translator, null, arg, x, !verbose, libraries_out);
451  
452  
    if (!new File(newDir, "main.java").exists()) {
453  
      throw new Exception("Translator " + translator + " did not generate main.java");
454  
      // TODO: show translator output
455  
    }
456  
    if (verbose)
457  
      System.out.println("Translated with " + translator + " from " + x.getPath() + " to " + newDir.getPath());
458  
    x = newDir;
459  
    return x;
460  
  }
461  
462  
  private static File luaPrintToJavaPrint(File x) throws IOException {
463  
    File newDir = TempDirMaker_make();
464  
    String code = loadTextFile(new File(x, "main.java").getPath(), null);
465  
    code = luaPrintToJavaPrint(code);
466  
    if (verbose)
467  
      System.out.println(code);
468  
    saveTextFile(new File(newDir, "main.java").getPath(), code);
469  
    return newDir;
470  
  }
471  
472  
  public static String luaPrintToJavaPrint(String code) {
473  
    return ("\n" + code).replaceAll(
474  
      "(\n\\s*)print (\".*\")",
475  
      "$1System.out.println($2);").substring(1);
476  
  }
477  
478  
  public static File loadSnippetAsMainJava(String snippetID) throws IOException {
479  
    checkProgramSafety(snippetID);
480  
    File srcDir = TempDirMaker_make();
481  
    saveTextFile(new File(srcDir, "main.java").getPath(), loadSnippet(snippetID));
482  
    return srcDir;
483  
  }
484  
485  
  public static File loadSnippetAsMainJavaVerified(String snippetID, String hash) throws IOException {
486  
    checkProgramSafety(snippetID);
487  
    File srcDir = TempDirMaker_make();
488  
    saveTextFile(new File(srcDir, "main.java").getPath(), loadSnippetVerified(snippetID, hash));
489  
    return srcDir;
490  
  }
491  
492  
  @SuppressWarnings( "unchecked" )
493  
  /** returns output dir */
494  
  private static File runTranslatorOnInput(String snippetID, String hash, String arg, File input,
495  
                                           boolean silent,
496  
                                           List<File> libraries_out) throws Exception {
497  
    if (safeTranslate)
498  
      checkProgramSafetyImpl(snippetID);
499  
    long id = parseSnippetID(snippetID);
500  
501  
    // It's a library, not a translator.
502  
    File libraryFile = DiskSnippetCache_getLibrary(id);
503  
    if (verbose)
504  
      System.out.println("Library file for " + id + ": " + libraryFile);
505  
    if (libraryFile != null) {
506  
      loadLibrary(snippetID, libraries_out, libraryFile);
507  
      return input;
508  
    }
509  
510  
    String[] args = arg != null ? new String[]{arg} : new String[0];
511  
512  
    File srcDir = hash == null ? loadSnippetAsMainJava(snippetID)
513  
      : loadSnippetAsMainJavaVerified(snippetID, hash);
514  
    long mainJavaSize = new File(srcDir, "main.java").length();
515  
516  
    if (verbose)
517  
      System.out.println(snippetID + ": length = " + mainJavaSize);
518  
    if (mainJavaSize == 0) { // no text in snippet? assume it's a library
519  
      loadLibrary(snippetID, libraries_out, libraryFile);
520  
      return input;
521  
    }
522  
523  
    List<File> libraries = new ArrayList<File>();
524  
    Object[] cached = translationCache.get(id);
525  
    if (cached != null) {
526  
      //System.err.println("Taking translator " + snippetID + " from cache!");
527  
      srcDir = (File) cached[0];
528  
      libraries = (List<File>) cached[1];
529  
    } else if (hasTranspiledSet.contains(id) && useServerTranspiled) {
530  
      System.err.println("Trying pretranspiled translator: #" + snippetID);
531  
      String transpiledSrc = getServerTranspiled(snippetID);
532  
      transpiledSrc = transpiledSrc.substring(transpiledSrc.indexOf('\n')+1);
533  
      // TODO: check for libraries
534  
      if (!transpiledSrc.isEmpty()) {
535  
        srcDir = TempDirMaker_make();
536  
        saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
537  
        translationCache.put(id, cached = new Object[] {srcDir, libraries});
538  
      }
539  
    }
540  
541  
    File ioBaseDir = TempDirMaker_make();
542  
543  
    /*Class<?> mainClass = programCache.get("" + parseSnippetID(snippetID));
544  
    if (mainClass != null)
545  
      return runCached(ioBaseDir, input, args);*/
546  
    // Doesn't work yet because virtualized directories are hardcoded in translator...
547  
548  
    if (cached == null) {
549  
      System.err.println("Translating translator #" + id);
550  
      if (translating.contains(id))
551  
        throw new RuntimeException("Recursive translator reference chain including #" + id);
552  
      translating.add(id);
553  
      try {
554  
        srcDir = defaultTranslate(srcDir, libraries);
555  
      } finally {
556  
        translating.remove(id);
557  
      }
558  
      System.err.println("Translated translator #" + id);
559  
      translationCache.put(id, new Object[]{srcDir, libraries});
560  
    }
561  
562  
    boolean runInProcess = false;
563  
564  
    if (virtualizeTranslators) {
565  
      if (verbose) System.out.println("Virtualizing translator");
566  
567  
      // TODO: don't virtualize class _javax (as included in, say, #636)
568  
569  
      //srcDir = applyTranslator(srcDir, "#2000351"); // I/O-virtualize the translator
570  
      // that doesn't work because it recurses infinitely...
571  
572  
      // So we do it right here:
573  
      String s = loadTextFile(new File(srcDir, "main.java").getPath(), null);
574  
      s = s.replaceAll("new\\s+File\\(", "virtual.newFile(");
575  
      s = s.replaceAll("new\\s+FileInputStream\\(", "virtual.newFileInputStream(");
576  
      s = s.replaceAll("new\\s+FileOutputStream\\(", "virtual.newFileOutputStream(");
577  
      s += "\n\n" + loadSnippet("#2000355"); // load class virtual
578  
579  
      // change baseDir
580  
      s = s.replace("virtual_baseDir = \"\";",
581  
        "virtual_baseDir " + "= " + javaQuote(ioBaseDir.getAbsolutePath()) + ";"); // extra + is necessary for Dumb TinyBrain :)
582  
583  
      // forward snippet cache (virtualized one)
584  
      File dir = virtCache != null ? virtCache : DiskSnippetCache_dir;
585  
      s = s.replace("static File DiskSnippetCache_dir" + ";",
586  
        "static File DiskSnippetCache_dir " + "= new File(" + javaQuote(dir.getAbsolutePath()) + ");"); // extra + is necessary for Dumb TinyBrain :)
587  
      s = s.replace("static boolean preferCached = false;", "static boolean preferCached = true;");
588  
589  
      if (verbose) {
590  
        System.out.println("==BEGIN VIRTUALIZED TRANSLATOR==");
591  
        System.out.println(s);
592  
        System.out.println("==END VIRTUALIZED TRANSLATOR==");
593  
      }
594  
      srcDir = TempDirMaker_make();
595  
      saveTextFile(new File(srcDir, "main.java").getPath(), s);
596  
597  
      // TODO: silence translator also
598  
      runInProcess = true;
599  
    }
600  
601  
    return runJavaX(ioBaseDir, srcDir, input, silent, runInProcess, libraries,
602  
      args, cacheTranslators ? "" + id : null, "" + id);
603  
  }
604  
605  
  private static String getServerTranspiled(String snippetID) throws IOException {
606  
    long id = parseSnippetID(snippetID);
607  
    URL url = new URL("http://tinybrain.de:8080/tb-int/get-transpiled.php?raw=1&withlibs=1&id=" + id);
608  
    return loadPage(url);
609  
  }
610  
611  
  static void checkProgramSafety(String snippetID) throws IOException {
612  
    if (!safeOnly) return;
613  
    checkProgramSafetyImpl(snippetID);
614  
  }
615  
616  
  static void checkProgramSafetyImpl(String snippetID) throws IOException {
617  
    URL url = new URL("http://tinybrain.de:8080/tb-int/is-javax-safe.php?id=" + parseSnippetID(snippetID));
618  
    String text = loadPage(url);
619  
    if (!text.startsWith("{\"safe\":\"1\"}"))
620  
      throw new RuntimeException("Program not safe: #" + parseSnippetID(snippetID));
621  
  }
622  
623  
  static void loadLibrary(String snippetID, List<File> libraries_out, File libraryFile) throws IOException {
624  
    if (verbose)
625  
      System.out.println("Assuming " + snippetID + " is a library.");
626  
627  
    if (libraryFile == null) {
628  
      byte[] data = loadDataSnippetImpl(snippetID);
629  
      DiskSnippetCache_putLibrary(parseSnippetID(snippetID), data);
630  
      libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(snippetID));
631  
    }
632  
633  
    if (!libraries_out.contains(libraryFile))
634  
      libraries_out.add(libraryFile);
635  
  }
636  
637  
  private static byte[] loadDataSnippetImpl(String snippetID) throws IOException {
638  
    byte[] data;
639  
    try {
640  
      URL url = new URL("http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_"
641  
        + parseSnippetID(snippetID) + "&contentType=application/binary");
642  
      System.err.println("Loading library: " + url);
643  
      data = loadBinaryPage(url.openConnection());
644  
      if (verbose)
645  
        System.err.println("Bytes loaded: " + data.length);
646  
    } catch (FileNotFoundException e) {
647  
      throw new IOException("Binary snippet #" + snippetID + " not found or not public");
648  
    }
649  
    return data;
650  
  }
651  
652  
  /** returns output dir */
653  
  private static File runJavaX(File ioBaseDir, File originalSrcDir, File originalInput,
654  
                               boolean silent, boolean runInProcess,
655  
                               List<File> libraries, String[] args, String cacheAs,
656  
                               String programID) throws Exception {
657  
    File srcDir = new File(ioBaseDir, "src");
658  
    File inputDir = new File(ioBaseDir, "input");
659  
    File outputDir = new File(ioBaseDir, "output");
660  
    copyInput(originalSrcDir, srcDir);
661  
    copyInput(originalInput, inputDir);
662  
    javax2(srcDir, ioBaseDir, silent, runInProcess, libraries, args, cacheAs, programID);
663  
    return outputDir;
664  
  }
665  
666  
  private static void copyInput(File src, File dst) throws IOException {
667  
    copyDirectory(src, dst);
668  
  }
669  
670  
  public static boolean hasFile(File inputDir, String name) {
671  
    return new File(inputDir, name).exists();
672  
  }
673  
674  
  public static void copyDirectory(File src, File dst) throws IOException {
675  
    if (verbose) System.out.println("Copying " + src.getAbsolutePath() + " to " + dst.getAbsolutePath());
676  
    dst.mkdirs();
677  
    File[] files = src.listFiles();
678  
    if (files == null) return;
679  
    for (File file : files) {
680  
      File dst1 = new File(dst, file.getName());
681  
      if (file.isDirectory())
682  
        copyDirectory(file, dst1);
683  
      else {
684  
        if (verbose) System.out.println("Copying " + file.getAbsolutePath() + " to " + dst1.getAbsolutePath());
685  
        copy(file, dst1);
686  
      }
687  
    }
688  
  }
689  
690  
  /** Quickly copy a file without a progress bar or any other fancy GUI... :) */
691  
  public static void copy(File src, File dest) throws IOException {
692  
    FileInputStream inputStream = newFileInputStream(src);
693  
    FileOutputStream outputStream = newFileOutputStream(dest);
694  
    try {
695  
      copy(inputStream, outputStream);
696  
      inputStream.close();
697  
    } finally {
698  
      outputStream.close();
699  
    }
700  
  }
701  
702  
  static Object call(Object o, String method, Object... args) {
703  
    try {
704  
      Method m = call_findMethod(o, method, args, false);
705  
      m.setAccessible(true);
706  
      return m.invoke(o, args);
707  
    } catch (Exception e) {
708  
      throw new RuntimeException(e);
709  
    }
710  
  }
711  
712  
  static Object call(Class c, String method, Object... args) {
713  
    try {
714  
      Method m = call_findStaticMethod(c, method, args, false);
715  
      m.setAccessible(true);
716  
      return m.invoke(null, args);
717  
    } catch (Exception e) {
718  
      throw new RuntimeException(e);
719  
    }
720  
  }
721  
722  
  static Method call_findStaticMethod(Class c, String method, Object[] args, boolean debug) {
723  
    while (c != null) {
724  
      for (Method m : c.getDeclaredMethods()) {
725  
        if (debug)
726  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
727  
        if (!m.getName().equals(method)) {
728  
          if (debug) System.out.println("Method name mismatch: " + method);
729  
          continue;
730  
        }
731  
732  
        if ((m.getModifiers() & Modifier.STATIC) == 0 || !call_checkArgs(m, args, debug))
733  
          continue;
734  
735  
        return m;
736  
      }
737  
      c = c.getSuperclass();
738  
    }
739  
    throw new RuntimeException("Method '" + method + "' (static) with " + args.length + " parameter(s) not found in " + c.getName());
740  
  }
741  
742  
  static Method call_findMethod(Object o, String method, Object[] args, boolean debug) {
743  
    Class c = o.getClass();
744  
    while (c != null) {
745  
      for (Method m : c.getDeclaredMethods()) {
746  
        if (debug)
747  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
748  
        if (m.getName().equals(method) && call_checkArgs(m, args, debug))
749  
          return m;
750  
      }
751  
      c = c.getSuperclass();
752  
    }
753  
    throw new RuntimeException("Method '" + method + "' (non-static) with " + args.length + " parameter(s) not found in " + o.getClass().getName());
754  
  }
755  
756  
  private static boolean call_checkArgs(Method m, Object[] args, boolean debug) {
757  
    Class<?>[] types = m.getParameterTypes();
758  
    if (types.length != args.length) {
759  
      if (debug)
760  
        System.out.println("checkArgs: Bad parameter length: " + args.length + " vs " + types.length);
761  
      return false;
762  
    }
763  
    for (int i = 0; i < types.length; i++)
764  
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
765  
        if (debug)
766  
          System.out.println("checkArgs: Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
767  
        return false;
768  
      }
769  
    return true;
770  
  }
771  
772  
  private static FileInputStream newFileInputStream(File f) throws FileNotFoundException {
773  
    /*if (androidContext != null)
774  
      return (FileInputStream) call(androidContext,
775  
        "openFileInput", f.getPath());
776  
    else*/
777  
    return new // line break for Dumb TinyBrain :)
778  
    FileInputStream(f);
779  
  }
780  
781  
  private static FileOutputStream newFileOutputStream(File f) throws FileNotFoundException {
782  
    /*if (androidContext != null)
783  
      return (FileOutputStream) call(androidContext,
784  
        "openFileOutput", f.getPath(), 0);
785  
    else*/
786  
    return new // line break for Dumb TinyBrain :)
787  
    FileOutputStream(f);
788  
  }
789  
790  
  public static void copy(InputStream in, OutputStream out) throws IOException {
791  
    byte[] buf = new byte[65536];
792  
    while (true) {
793  
      int n = in.read(buf);
794  
      if (n <= 0) return;
795  
      out.write(buf, 0, n);
796  
    }
797  
  }
798  
799  
  /** writes safely (to temp file, then rename) */
800  
  public static void saveTextFile(String fileName, String contents) throws IOException {
801  
    File file = new File(fileName);
802  
    File parentFile = file.getParentFile();
803  
    if (parentFile != null)
804  
      parentFile.mkdirs();
805  
    String tempFileName = fileName + "_temp";
806  
    FileOutputStream fileOutputStream = newFileOutputStream(new File(tempFileName));
807  
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, charsetForTextFiles);
808  
    PrintWriter printWriter = new PrintWriter(outputStreamWriter);
809  
    printWriter.print(contents);
810  
    printWriter.close();
811  
    if (file.exists() && !file.delete())
812  
      throw new IOException("Can't delete " + fileName);
813  
814  
    if (!new File(tempFileName).renameTo(file))
815  
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
816  
  }
817  
818  
  /** writes safely (to temp file, then rename) */
819  
  public static void saveBinaryFile(String fileName, byte[] contents) throws IOException {
820  
    File file = new File(fileName);
821  
    File parentFile = file.getParentFile();
822  
    if (parentFile != null)
823  
      parentFile.mkdirs();
824  
    String tempFileName = fileName + "_temp";
825  
    FileOutputStream fileOutputStream = newFileOutputStream(new File(tempFileName));
826  
    fileOutputStream.write(contents);
827  
    fileOutputStream.close();
828  
    if (file.exists() && !file.delete())
829  
      throw new IOException("Can't delete " + fileName);
830  
831  
    if (!new File(tempFileName).renameTo(file))
832  
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
833  
  }
834  
835  
  public static String loadTextFile(String fileName, String defaultContents) throws IOException {
836  
    if (!new File(fileName).exists())
837  
      return defaultContents;
838  
839  
    FileInputStream fileInputStream = newFileInputStream(new File(fileName));
840  
    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, charsetForTextFiles);
841  
    return loadTextFile(inputStreamReader, (int) new File(fileName).length());
842  
  }
843  
844  
  public static String loadTextFile(Reader reader, int length) throws IOException {
845  
    try {
846  
      char[] chars = new char[length];
847  
      int n = reader.read(chars);
848  
      return new String(chars, 0, n);
849  
    } finally {
850  
      reader.close();
851  
    }
852  
  }
853  
854  
  static File DiskSnippetCache_dir;
855  
856  
  public static void initDiskSnippetCache(File dir) {
857  
    DiskSnippetCache_dir = dir;
858  
    dir.mkdirs();
859  
  }
860  
861  
  // Data files are immutable, use centralized cache
862  
  public static synchronized File DiskSnippetCache_getLibrary(long snippetID) throws IOException {
863  
    File file = new File(getGlobalCache(), "data_" + snippetID + ".jar");
864  
    if (verbose)
865  
      System.out.println("Checking data cache: " + file.getPath());
866  
    return file.exists() ? file : null;
867  
  }
868  
869  
  public static synchronized String DiskSnippetCache_get(long snippetID) throws IOException {
870  
    return loadTextFile(DiskSnippetCache_getFile(snippetID).getPath(), null);
871  
  }
872  
873  
  private static File DiskSnippetCache_getFile(long snippetID) {
874  
    return new File(DiskSnippetCache_dir, "" + snippetID);
875  
  }
876  
877  
  public static synchronized void DiskSnippetCache_put(long snippetID, String snippet) throws IOException {
878  
    saveTextFile(DiskSnippetCache_getFile(snippetID).getPath(), snippet);
879  
  }
880  
881  
  public static synchronized void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException {
882  
    saveBinaryFile(new File(getGlobalCache(), "data_" + snippetID).getPath() + ".jar", data);
883  
  }
884  
885  
  public static File DiskSnippetCache_getDir() {
886  
    return DiskSnippetCache_dir;
887  
  }
888  
889  
  public static void initSnippetCache() {
890  
    if (DiskSnippetCache_dir == null)
891  
      initDiskSnippetCache(getGlobalCache());
892  
  }
893  
894  
  private static File getGlobalCache() {
895  
    File file = new File(userHome(), ".tinybrain/snippet-cache");
896  
    file.mkdirs();
897  
    return file;
898  
  }
899  
900  
  public static String loadSnippetVerified(String snippetID, String hash) throws IOException {
901  
    String text = loadSnippet(snippetID);
902  
    String realHash = getHash(text.getBytes("UTF-8"));
903  
    if (!realHash.equals(hash)) {
904  
      String msg;
905  
      if (hash.isEmpty())
906  
        msg = "Here's your hash for " + snippetID + ", please put in your program: " + realHash;
907  
      else
908  
        msg = "Hash mismatch for " + snippetID + ": " + realHash + " (new) vs " + hash + " - has tinybrain.de been hacked??";
909  
      throw new RuntimeException(msg);
910  
    }
911  
    return text;
912  
  }
913  
914  
  public static String getHash(byte[] data) {
915  
    return bytesToHex(getFullFingerprint(data));
916  
  }
917  
918  
  public static byte[] getFullFingerprint(byte[] data) {
919  
    try {
920  
      return MessageDigest.getInstance("MD5").digest(data);
921  
    } catch (NoSuchAlgorithmException e) {
922  
      throw new RuntimeException(e);
923  
    }
924  
  }
925  
926  
  public static String bytesToHex(byte[] bytes) {
927  
    return bytesToHex(bytes, 0, bytes.length);
928  
  }
929  
930  
  public static String bytesToHex(byte[] bytes, int ofs, int len) {
931  
    StringBuilder stringBuilder = new StringBuilder(len*2);
932  
    for (int i = 0; i < len; i++) {
933  
      String s = "0" + Integer.toHexString(bytes[ofs+i]);
934  
      stringBuilder.append(s.substring(s.length()-2, s.length()));
935  
    }
936  
    return stringBuilder.toString();
937  
  }
938  
939  
  public static String loadSnippet(String snippetID) throws IOException {
940  
    return loadSnippet(parseSnippetID(snippetID));
941  
  }
942  
943  
  public static long parseSnippetID(String snippetID) {
944  
    return Long.parseLong(shortenSnippetID(snippetID));
945  
  }
946  
947  
  private static String shortenSnippetID(String snippetID) {
948  
    if (snippetID.startsWith("#"))
949  
      snippetID = snippetID.substring(1);
950  
    String httpBlaBla = "http://tinybrain.de/";
951  
    if (snippetID.startsWith(httpBlaBla))
952  
      snippetID = snippetID.substring(httpBlaBla.length());
953  
    return snippetID;
954  
  }
955  
956  
  public static boolean isSnippetID(String snippetID) {
957  
    snippetID = shortenSnippetID(snippetID);
958  
    return isInteger(snippetID) && Long.parseLong(snippetID) != 0;
959  
  }
960  
961  
  public static boolean isInteger(String s) {
962  
    return Pattern.matches("\\-?\\d+", s);
963  
  }
964  
965  
  public static String loadSnippet(long snippetID) throws IOException {
966  
    String text = memSnippetCache.get(snippetID);
967  
    if (text != null) {
968  
      if (verbose)
969  
        System.out.println("Getting " + snippetID + " from mem cache");
970  
      return text;
971  
    }
972  
973  
    initSnippetCache();
974  
    text = DiskSnippetCache_get(snippetID);
975  
    if (preferCached && text != null) {
976  
      if (verbose)
977  
        System.out.println("Getting " + snippetID + " from disk cache (preferCached)");
978  
      return text;
979  
    }
980  
981  
    String md5 = text != null ? md5(text) : "-";
982  
    if (text != null) {
983  
      String hash = prefetched.get(snippetID);
984  
      if (hash != null) {
985  
        if (md5.equals(hash)) {
986  
          memSnippetCache.put(snippetID, text);
987  
          if (verbose)
988  
            System.out.println("Getting " + snippetID + " from prefetched");
989  
          return text;
990  
        } else
991  
          prefetched.remove(snippetID); // (maybe this is not necessary)
992  
      }
993  
    }
994  
995  
    try {
996  
      /*URL url = new URL("http://tinybrain.de:8080/getraw.php?id=" + snippetID);
997  
      text = loadPage(url);*/
998  
      String theURL = "http://tinybrain.de:8080/getraw.php?id=" + snippetID + "&getmd5=1&utf8=1&usetranspiled=1";
999  
      if (text != null) {
1000  
        //System.err.println("MD5: " + md5);
1001  
        theURL += "&md5=" + md5;
1002  
      }
1003  
      URL url = new URL(theURL);
1004  
      String page = loadPage(url);
1005  
1006  
      // parse & drop transpilation flag available line
1007  
      int i = page.indexOf('\n');
1008  
      boolean hasTranspiled = page.substring(0, i).trim().equals("1");
1009  
      if (hasTranspiled)
1010  
        hasTranspiledSet.add(snippetID);
1011  
      else
1012  
        hasTranspiledSet.remove(snippetID);
1013  
      page = page.substring(i+1);
1014  
1015  
      if (page.startsWith("==*#*==")) {
1016  
        // same, keep text
1017  
        //System.err.println("Snippet unchanged, keeping.");
1018  
      } else {
1019  
        // drop md5 line
1020  
        i = page.indexOf('\n');
1021  
        String hash = page.substring(0, i).trim();
1022  
        text = page.substring(i+1);
1023  
1024  
        String myHash = md5(text);
1025  
        if (myHash.equals(hash)) {
1026  
          //System.err.println("Hash match: " + hash);
1027  
        } else
1028  
          System.err.println("Hash mismatch");
1029  
      }
1030  
    } catch (FileNotFoundException e) {
1031  
      e.printStackTrace();
1032  
      throw new IOException("Snippet #" + snippetID + " not found or not public");
1033  
    }
1034  
1035  
    memSnippetCache.put(snippetID, text);
1036  
1037  
    try {
1038  
      initSnippetCache();
1039  
      DiskSnippetCache_put(snippetID, text);
1040  
    } catch (IOException e) {
1041  
      System.err.println("Minor warning: Couldn't save snippet to cache ("  + DiskSnippetCache_getDir() + ")");
1042  
    }
1043  
1044  
    return text;
1045  
  }
1046  
1047  
  private static String md5(String text) {
1048  
    try {
1049  
      return bytesToHex(md5impl(text.getBytes("UTF-8"))); // maybe different than the way PHP does it...
1050  
    } catch (UnsupportedEncodingException e) {
1051  
      throw new RuntimeException(e);
1052  
    }
1053  
  }
1054  
1055  
  public static byte[] md5impl(byte[] data) {
1056  
    try {
1057  
      return MessageDigest.getInstance("MD5").digest(data);
1058  
    } catch (NoSuchAlgorithmException e) {
1059  
      throw new RuntimeException(e);
1060  
    }
1061  
  }
1062  
1063  
  private static String loadPage(URL url) throws IOException {
1064  
    System.err.println("Loading: " + url.toExternalForm());
1065  
    URLConnection con = url.openConnection();
1066  
    return loadPage(con, url);
1067  
  }
1068  
1069  
  public static String loadPage(URLConnection con, URL url) throws IOException {
1070  
    setHeaders(con);
1071  
    String contentType = con.getContentType();
1072  
    if (contentType == null)
1073  
      throw new IOException("Page could not be read: " + url);
1074  
    //Log.info("Content-Type: " + contentType);
1075  
    String charset = guessCharset(contentType);
1076  
    //System.err.println("Charset: " + charset);
1077  
    Reader r = new InputStreamReader(con.getInputStream(), charset);
1078  
    StringBuilder buf = new StringBuilder();
1079  
    while (true) {
1080  
      int ch = r.read();
1081  
      if (ch < 0)
1082  
        break;
1083  
      //Log.info("Chars read: " + buf.length());
1084  
      buf.append((char) ch);
1085  
    }
1086  
    return buf.toString();
1087  
  }
1088  
1089  
  public static byte[] loadBinaryPage(URLConnection con) throws IOException {
1090  
    setHeaders(con);
1091  
    return loadBinaryPage_noHeaders(con);
1092  
  }
1093  
1094  
  private static byte[] loadBinaryPage_noHeaders(URLConnection con) throws IOException {
1095  
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
1096  
    InputStream inputStream = con.getInputStream();
1097  
    while (true) {
1098  
      int ch = inputStream.read();
1099  
      if (ch < 0)
1100  
        break;
1101  
      buf.write(ch);
1102  
    }
1103  
    inputStream.close();
1104  
    return buf.toByteArray();
1105  
  }
1106  
1107  
  private static void setHeaders(URLConnection con) throws IOException {
1108  
    String computerID = getComputerID();
1109  
    if (computerID != null)
1110  
      con.setRequestProperty("X-ComputerID", computerID);
1111  
  }
1112  
1113  
  public static String guessCharset(String contentType) {
1114  
    Pattern p = Pattern.compile("text/html;\\s+charset=([^\\s]+)\\s*");
1115  
    Matcher m = p.matcher(contentType);
1116  
    /* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */
1117  
    return m.matches() ? m.group(1) : "ISO-8859-1";
1118  
  }
1119  
1120  
  /** runs a transpiled set of sources */
1121  
  public static void javax2(File srcDir, File ioBaseDir, boolean silent, boolean runInProcess,
1122  
                            List<File> libraries, String[] args, String cacheAs,
1123  
                            String programID) throws Exception {
1124  
    if (android)
1125  
      javax2android(srcDir, args, programID);
1126  
    else {
1127  
      File classesDir = TempDirMaker_make();
1128  
      String javacOutput = compileJava(srcDir, libraries, classesDir);
1129  
1130  
      // run
1131  
1132  
      if (verbose) System.out.println("Running program (" + srcDir.getAbsolutePath()
1133  
        + ") on io dir " + ioBaseDir.getAbsolutePath() + (runInProcess ? "[in-process]" : "") + "\n");
1134  
      runProgram(javacOutput, classesDir, ioBaseDir, silent, runInProcess, libraries, args, cacheAs, programID);
1135  
    }
1136  
  }
1137  
1138  
  static Class<?> loadx2android(File srcDir, String programID) throws Exception {
1139  
    // TODO: optimize if it's a loaded snippet anyway
1140  
    URL url = new URL("http://tinybrain.de:8080/dexcompile.php");
1141  
    URLConnection conn = url.openConnection();
1142  
    String postData = "src=" + URLEncoder.encode(loadTextFile(new File(srcDir, "main.java").getPath(), null), "UTF-8");
1143  
    byte[] dexData = doPostBinary(postData, conn);
1144  
    if (!isDex(dexData))
1145  
      throw new RuntimeException("Dex generation error: " + dexData.length + " bytes - " + new String(dexData, "UTF-8"));
1146  
    System.out.println("Dex loaded: " + dexData.length + "b");
1147  
1148  
    File dexDir = TempDirMaker_make();
1149  
    File dexFile = new File(dexDir, System.currentTimeMillis() + ".dex");
1150  
    File dexOutputDir = TempDirMaker_make();
1151  
1152  
    System.out.println("Saving dex to: " + dexDir.getAbsolutePath());
1153  
    try {
1154  
      saveBinaryFile(dexFile.getPath(), dexData);
1155  
    } catch (Throwable e) {
1156  
      System.out.println("Whoa!");
1157  
      throw new RuntimeException(e);
1158  
    }
1159  
1160  
    System.out.println("Getting parent class loader.");
1161  
    ClassLoader parentClassLoader =
1162  
      //ClassLoader.getSystemClassLoader(); // does not find support jar
1163  
      //getClass().getClassLoader(); // Let's try this...
1164  
      x25.class.getClassLoader().getParent(); // XXX !
1165  
1166  
    //System.out.println("Making DexClassLoader.");
1167  
    //DexClassLoader classLoader = new DexClassLoader(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null,
1168  
    //  parentClassLoader);
1169  
    Class dcl = Class.forName("dalvik.system.DexClassLoader");
1170  
    Object classLoader = dcl.getConstructors()[0].newInstance(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null,
1171  
      parentClassLoader);
1172  
1173  
    //System.out.println("Loading main class.");
1174  
    //Class<?> theClass = classLoader.loadClass(mainClassName);
1175  
    Class<?> theClass = (Class<?>) call(classLoader, "loadClass", "main");
1176  
1177  
    //System.out.println("Main class loaded.");
1178  
    try {
1179  
      set(theClass, "androidContext", androidContext);
1180  
    } catch (Throwable e) {}
1181  
1182  
    setVars(theClass, programID);
1183  
1184  
    return theClass;
1185  
  }
1186  
1187  
  static void javax2android(File srcDir, String[] args, String programID) throws Exception {
1188  
    Class<?> theClass = loadx2android(srcDir, programID);
1189  
1190  
    Method main = null;
1191  
    try {
1192  
      main = call_findStaticMethod(theClass, "main", new Object[]{androidContext}, false);
1193  
    } catch (RuntimeException e) {
1194  
    }
1195  
1196  
    //System.out.println("main method for " + androidContext + " of " + theClass + ": " + main);
1197  
1198  
    if (main != null) {
1199  
      // old style main program that returns a View
1200  
      System.out.println("Calling main (old-style)");
1201  
      Object view = main.invoke(null, androidContext);
1202  
      System.out.println("Calling setContentView with " + view);
1203  
      call(Class.forName("main"), "setContentViewInUIThread", view);
1204  
      //call(androidContext, "setContentView", view);
1205  
      System.out.println("Done.");
1206  
    } else {
1207  
      System.out.println("New-style main method running.\n\n====\n");
1208  
      runMainMethod(args, theClass);
1209  
    }
1210  
  }
1211  
1212  
  static byte[] DEX_FILE_MAGIC = { 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 };
1213  
1214  
  static boolean isDex(byte[] dexData) {
1215  
    if (dexData.length < DEX_FILE_MAGIC.length) return false;
1216  
    for (int i = 0; i < DEX_FILE_MAGIC.length; i++)
1217  
      if (dexData[i] != DEX_FILE_MAGIC[i])
1218  
        return false;
1219  
    return true;
1220  
  }
1221  
1222  
  static byte[] doPostBinary(String urlParameters, URLConnection conn) throws IOException {
1223  
    // connect and do POST
1224  
    setHeaders(conn);
1225  
    conn.setDoOutput(true);
1226  
1227  
    OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
1228  
    writer.write(urlParameters);
1229  
    writer.flush();
1230  
1231  
    byte[] contents = loadBinaryPage_noHeaders(conn);
1232  
    writer.close();
1233  
    return contents;
1234  
  }
1235  
1236  
  static String compileJava(File srcDir, List<File> libraries, File classesDir) throws IOException {
1237  
    ++compilations;
1238  
1239  
    // collect sources
1240  
1241  
    List<File> sources = new ArrayList<File>();
1242  
    if (verbose) System.out.println("Scanning for sources in " + srcDir.getPath());
1243  
    scanForSources(srcDir, sources, true);
1244  
    if (sources.isEmpty())
1245  
      throw new IOException("No sources found");
1246  
1247  
    // compile
1248  
1249  
    File optionsFile = File.createTempFile("javax", "");
1250  
    if (verbose) System.out.println("Compiling " + sources.size() + " source(s) to " + classesDir.getPath());
1251  
    if (verbose) System.out.println("Libraries: " + libraries);
1252  
    String options = "-d " + bashQuote(classesDir.getPath());
1253  
    writeOptions(sources, libraries, optionsFile, options);
1254  
    classesDir.mkdirs();
1255  
    return invokeJavaCompiler(optionsFile);
1256  
  }
1257  
1258  
  private static void runProgram(String javacOutput, File classesDir, File ioBaseDir,
1259  
                                 boolean silent, boolean runInProcess,
1260  
                                 List<File> libraries, String[] args, String cacheAs,
1261  
                                 String programID) throws Exception {
1262  
    // print javac output if compile failed and it hasn't been printed yet
1263  
    boolean didNotCompile = !didCompile(classesDir);
1264  
    if (verbose || didNotCompile)
1265  
      System.out.println(javacOutput);
1266  
    if (didNotCompile)
1267  
      return;
1268  
1269  
    if (runInProcess
1270  
      || (ioBaseDir.getAbsolutePath().equals(new File(".").getAbsolutePath()) && !silent)) {
1271  
      runProgramQuick(classesDir, libraries, args, cacheAs, programID);
1272  
      return;
1273  
    }
1274  
1275  
    boolean echoOK = false;
1276  
    // TODO: add libraries to class path
1277  
    String bashCmd = "(cd " + bashQuote(ioBaseDir.getAbsolutePath()) + " && (java -cp "
1278  
      + bashQuote(classesDir.getAbsolutePath()) + " main" + (echoOK ? "; echo ok" : "") + "))";
1279  
    if (verbose) System.out.println(bashCmd);
1280  
    String output = backtick(bashCmd);
1281  
    lastOutput = output;
1282  
    if (verbose || !silent)
1283  
      System.out.println(output);
1284  
  }
1285  
1286  
  static boolean didCompile(File classesDir) {
1287  
    return hasFile(classesDir, "main.class");
1288  
  }
1289  
1290  
  private static void runProgramQuick(File classesDir, List<File> libraries,
1291  
                                      String[] args, String cacheAs,
1292  
                                      String programID) throws Exception {
1293  
    // collect urls
1294  
    URL[] urls = new URL[libraries.size()+1];
1295  
    urls[0] = classesDir.toURI().toURL();
1296  
    for (int i = 0; i < libraries.size(); i++)
1297  
      urls[i+1] = libraries.get(i).toURI().toURL();
1298  
1299  
    // make class loader
1300  
    URLClassLoader classLoader = new URLClassLoader(urls);
1301  
1302  
    // load JavaX main class
1303  
    Class<?> mainClass = classLoader.loadClass("main");
1304  
1305  
    if (cacheAs != null)
1306  
      programCache.put(cacheAs, mainClass);
1307  
1308  
    setVars(mainClass, programID);
1309  
    runMainMethod(args, mainClass);
1310  
  }
1311  
1312  
  static void setVars(Class<?> theClass, String programID) {
1313  
    try {
1314  
      set(theClass, "programID", programID);
1315  
    } catch (Throwable e) {}
1316  
1317  
    try {
1318  
      set(theClass, "__javax", x25.class);
1319  
    } catch (Throwable e) {}
1320  
  }
1321  
1322  
1323  
  static void runMainMethod(Object args, Class<?> mainClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1324  
    Method main = mainClass.getMethod("main", String[].class);
1325  
    main.invoke(null, args);
1326  
  }
1327  
1328  
  private static String invokeJavaCompiler(File optionsFile) throws IOException {
1329  
    String output;
1330  
    if (hasEcj() && !javacOnly)
1331  
      output = invokeEcj(optionsFile);
1332  
    else
1333  
      output = invokeJavac(optionsFile);
1334  
    if (verbose) System.out.println(output);
1335  
    return output;
1336  
  }
1337  
1338  
  private static boolean hasEcj() {
1339  
    try {
1340  
      Class.forName("org.eclipse.jdt.internal.compiler.batch.Main");
1341  
      return true;
1342  
    } catch (ClassNotFoundException e) {
1343  
      return false;
1344  
    }
1345  
  }
1346  
1347  
  private static String invokeJavac(File optionsFile) throws IOException {
1348  
    String output;
1349  
    output = backtick("javac " + bashQuote("@" + optionsFile.getPath()));
1350  
    if (exitValue != 0) {
1351  
      System.out.println(output);
1352  
      throw new RuntimeException("javac returned errors.");
1353  
    }
1354  
    return output;
1355  
  }
1356  
1357  
  // throws ClassNotFoundException if ecj is not in classpath
1358  
  static String invokeEcj(File optionsFile) {
1359  
    try {
1360  
      StringWriter writer = new StringWriter();
1361  
      PrintWriter printWriter = new PrintWriter(writer);
1362  
1363  
      // add more eclipse options in the line below
1364  
1365  
      String[] args = {"@" + optionsFile.getPath(),
1366  
        "-source", "1.7",
1367  
        "-nowarn"
1368  
      };
1369  
1370  
      Class ecjClass = Class.forName("org.eclipse.jdt.internal.compiler.batch.Main");
1371  
      Object main = newInstance(ecjClass, printWriter, printWriter, false);
1372  
      call(main, "compile", new Object[]{args});
1373  
      int errors = (Integer) get(main, "globalErrorsCount");
1374  
1375  
      String output = writer.toString();
1376  
      if (errors != 0) {
1377  
        System.out.println(output);
1378  
        throw new RuntimeException("Java compiler returned errors.");
1379  
      }
1380  
      return output;
1381  
    } catch (Exception e) {
1382  
      throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
1383  
    }
1384  
  }
1385  
1386  
  static Object get(Object o, String field) {
1387  
    try {
1388  
      Field f = findField(o.getClass(), field);
1389  
      f.setAccessible(true);
1390  
      return f.get(o);
1391  
    } catch (Exception e) {
1392  
      throw new RuntimeException(e);
1393  
    }
1394  
  }
1395  
1396  
  static Object newInstance(Class c, Object... args) { try {
1397  
    Constructor m = findConstructor(c, args);
1398  
    m.setAccessible(true);
1399  
    return m.newInstance(args);
1400  
  } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
1401  
1402  
  static Constructor findConstructor(Class c, Object... args) {
1403  
    for (Constructor m : c.getDeclaredConstructors()) {
1404  
      if (!checkArgs(m.getParameterTypes(), args, verbose))
1405  
        continue;
1406  
      return m;
1407  
    }
1408  
    throw new RuntimeException("Constructor with " + args.length + " matching parameter(s) not found in " + c.getName());
1409  
  }
1410  
1411  
  static boolean checkArgs(Class[] types, Object[] args, boolean debug) {
1412  
    if (types.length != args.length) {
1413  
      if (debug)
1414  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
1415  
      return false;
1416  
    }
1417  
    for (int i = 0; i < types.length; i++)
1418  
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
1419  
        if (debug)
1420  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
1421  
        return false;
1422  
      }
1423  
    return true;
1424  
  }
1425  
1426  
  // extended to handle primitive types
1427  
  private static boolean isInstanceX(Class type, Object arg) {
1428  
    if (type == boolean.class) return arg instanceof Boolean;
1429  
    if (type == int.class) return arg instanceof Integer;
1430  
    if (type == long.class) return arg instanceof Long;
1431  
    if (type == float.class) return arg instanceof Float;
1432  
    if (type == short.class) return arg instanceof Short;
1433  
    if (type == char.class) return arg instanceof Character;
1434  
    if (type == byte.class) return arg instanceof Byte;
1435  
    return type.isInstance(arg);
1436  
  }
1437  
1438  
  private static void writeOptions(List<File> sources, List<File> libraries,
1439  
                                   File optionsFile, String moreOptions) throws IOException {
1440  
    FileWriter writer = new FileWriter(optionsFile);
1441  
    for (File source : sources)
1442  
      writer.write(bashQuote(source.getPath()) + " ");
1443  
    if (!libraries.isEmpty()) {
1444  
      List<String> cp = new ArrayList<String>();
1445  
      for (File lib : libraries)
1446  
        cp.add(lib.getAbsolutePath());
1447  
      writer.write("-cp " + bashQuote(join(File.pathSeparator, cp)) + " ");
1448  
    }
1449  
    writer.write(moreOptions);
1450  
    writer.close();
1451  
  }
1452  
1453  
  static void scanForSources(File source, List<File> sources, boolean topLevel) {
1454  
    if (source.isFile() && source.getName().endsWith(".java"))
1455  
      sources.add(source);
1456  
    else if (source.isDirectory() && !isSkippedDirectoryName(source.getName(), topLevel)) {
1457  
      File[] files = source.listFiles();
1458  
      for (File file : files)
1459  
        scanForSources(file, sources, false);
1460  
    }
1461  
  }
1462  
1463  
  private static boolean isSkippedDirectoryName(String name, boolean topLevel) {
1464  
    if (topLevel) return false; // input or output ok as highest directory (intentionally specified by user, not just found by a directory scan in which case we probably don't want it. it's more like heuristics actually.)
1465  
    return name.equalsIgnoreCase("input") || name.equalsIgnoreCase("output");
1466  
  }
1467  
1468  
  static int exitValue;
1469  
  public static String backtick(String cmd) throws IOException {
1470  
    ++processesStarted;
1471  
    File outFile = File.createTempFile("_backtick", "");
1472  
    File scriptFile = File.createTempFile("_backtick", isWindows() ? ".bat" : "");
1473  
1474  
    String command = cmd + " >" + bashQuote(outFile.getPath()) + " 2>&1";
1475  
    //Log.info("[Backtick] " + command);
1476  
    try {
1477  
      saveTextFile(scriptFile.getPath(), command);
1478  
      String[] command2;
1479  
      if (isWindows())
1480  
        command2 = new String[] { scriptFile.getPath() };
1481  
      else
1482  
        command2 = new String[] { "/bin/bash", scriptFile.getPath() };
1483  
      Process process = Runtime.getRuntime().exec(command2);
1484  
      try {
1485  
        process.waitFor();
1486  
      } catch (InterruptedException e) {
1487  
        throw new RuntimeException(e);
1488  
      }
1489  
      exitValue = process.exitValue();
1490  
      if (verbose)
1491  
        System.out.println("Process return code: " + exitValue);
1492  
      return loadTextFile(outFile.getPath(), "");
1493  
    } finally {
1494  
      scriptFile.delete();
1495  
    }
1496  
  }
1497  
1498  
  /** possibly improvable */
1499  
  public static String javaQuote(String text) {
1500  
    return bashQuote(text);
1501  
  }
1502  
1503  
  /** possibly improvable */
1504  
  public static String bashQuote(String text) {
1505  
    if (text == null) return null;
1506  
    return "\"" + text
1507  
      .replace("\\", "\\\\")
1508  
      .replace("\"", "\\\"")
1509  
      .replace("\n", "\\n")
1510  
      .replace("\r", "\\r") + "\"";
1511  
  }
1512  
1513  
  public final static String charsetForTextFiles = "UTF8";
1514  
1515  
  static long TempDirMaker_lastValue;
1516  
1517  
  public static File TempDirMaker_make() {
1518  
    File dir = new File(userHome(), ".javax/" + TempDirMaker_newValue());
1519  
    dir.mkdirs();
1520  
    return dir;
1521  
  }
1522  
1523  
  private static long TempDirMaker_newValue() {
1524  
    long value;
1525  
    do
1526  
      value = System.currentTimeMillis();
1527  
    while (value == TempDirMaker_lastValue);
1528  
    TempDirMaker_lastValue = value;
1529  
    return value;
1530  
  }
1531  
1532  
  public static String join(String glue, String[] strings) {
1533  
    return join(glue, Arrays.asList(strings));
1534  
  }
1535  
   
1536  
  public static String join(String glue, Iterable<String> strings) {
1537  
    StringBuilder buf = new StringBuilder();
1538  
    Iterator<String> i = strings.iterator();
1539  
    if (i.hasNext()) {
1540  
      buf.append(i.next());
1541  
      while (i.hasNext())
1542  
        buf.append(glue).append(i.next());
1543  
    }
1544  
    return buf.toString();
1545  
  }
1546  
1547  
  public static boolean isWindows() {
1548  
    return System.getProperty("os.name").contains("Windows");
1549  
  }
1550  
1551  
  public static String makeRandomID(int length) {
1552  
    Random random = new Random();
1553  
    char[] id = new char[length];
1554  
    for (int i = 0; i< id.length; i++)
1555  
      id[i] = (char) ((int) 'a' + random.nextInt(26));
1556  
    return new String(id);
1557  
  }
1558  
1559  
  static String computerID;
1560  
  public static String getComputerID() throws IOException {
1561  
    if (noID) return null;
1562  
    if (computerID == null) {
1563  
      File file = new File(userHome(), ".tinybrain/computer-id");
1564  
      computerID = loadTextFile(file.getPath(), null);
1565  
      if (computerID == null) {
1566  
        computerID = makeRandomID(12);
1567  
        saveTextFile(file.getPath(), computerID);
1568  
      }
1569  
      if (verbose)
1570  
        System.out.println("Local computer ID: " + computerID);
1571  
    }
1572  
    return computerID;
1573  
  }
1574  
1575  
  static int fileDeletions;
1576  
1577  
  static void cleanCache() {
1578  
    try {
1579  
      if (verbose)
1580  
        System.out.println("Cleaning cache");
1581  
      fileDeletions = 0;
1582  
      File javax = new File(userHome(), ".javax");
1583  
      long now = System.currentTimeMillis();
1584  
      File[] files = javax.listFiles();
1585  
      if (files != null) for (File dir : files) {
1586  
        if (dir.isDirectory() && Pattern.compile("\\d+").matcher(dir.getName()).matches()) {
1587  
          long time = Long.parseLong(dir.getName());
1588  
          long seconds = (now - time) / 1000;
1589  
          long minutes = seconds / 60;
1590  
          long hours = minutes / 60;
1591  
          if (hours >= tempFileRetentionTime) {
1592  
            //System.out.println("Can delete " + dir.getAbsolutePath() + ", age: " + hours + " h");
1593  
            removeDir(dir);
1594  
          }
1595  
        }
1596  
      }
1597  
      if (verbose && fileDeletions != 0)
1598  
        System.out.println("Cleaned cache. File deletions: " + fileDeletions);
1599  
    } catch (Throwable e) {
1600  
      e.printStackTrace();
1601  
    }
1602  
  }
1603  
1604  
  static void removeDir(File dir) {
1605  
    if (dir.getAbsolutePath().indexOf(".javax") < 0)  // security check!
1606  
      return;
1607  
    for (File f : dir.listFiles()) {
1608  
      if (f.isDirectory())
1609  
        removeDir(f);
1610  
      else {
1611  
        if (verbose)
1612  
          System.out.println("Deleting " + f.getAbsolutePath());
1613  
        f.delete();
1614  
        ++fileDeletions;
1615  
      }
1616  
    }
1617  
    dir.delete();
1618  
  }
1619  
1620  
  static void showSystemProperties() {
1621  
    System.out.println("System properties:\n");
1622  
    for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
1623  
      System.out.println("  " + entry.getKey() + " = " + entry.getValue());
1624  
    }
1625  
    System.out.println();
1626  
  }
1627  
1628  
  static void showVersion() {
1629  
    //showSystemProperties();
1630  
    boolean eclipseFound = hasEcj();
1631  
    //String platform = System.getProperty("java.vendor") + " " + System.getProperty("java.runtime.name") + " " + System.getProperty("java.version");
1632  
    String platform = System.getProperty("java.vm.name") + " " + System.getProperty("java.version");
1633  
    String os = System.getProperty("os.name"), arch = System.getProperty("os.arch");
1634  
    System.out.println("This is " + version + ".");
1635  
    System.out.println("[Details: " +
1636  
      (eclipseFound ? "Eclipse compiler (good)" : "javac (not so good)")
1637  
      + ", " + platform + ", " + arch + ", " + os + "]");
1638  
  }
1639  
1640  
  static boolean isAndroid() {
1641  
    return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0;
1642  
  }
1643  
1644  
  static void set(Class c, String field, Object value) {
1645  
    try {
1646  
      Field f = findStaticField(c, field);
1647  
      f.setAccessible(true);
1648  
      f.set(null, value);
1649  
    } catch (Exception e) {
1650  
      throw new RuntimeException(e);
1651  
    }
1652  
  }
1653  
1654  
  static Field findStaticField(Class<?> c, String field) {
1655  
    for (Field f : c.getDeclaredFields())
1656  
      if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
1657  
        return f;
1658  
    throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
1659  
  }
1660  
1661  
  static Field findField(Class<?> c, String field) {
1662  
    for (Field f : c.getDeclaredFields())
1663  
      if (f.getName().equals(field))
1664  
        return f;
1665  
    throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
1666  
  }
1667  
  
1668  
  static String smartJoin(String[] args) {
1669  
    String[] a2 = new String[args.length];
1670  
    for (int i = 0; i < args.length; i++) {
1671  
      a2[i] = Pattern.compile("\\w+").matcher(args[i]).matches() ? args[i] : quote(args[i]);
1672  
    }
1673  
    return join(" ", a2);
1674  
  }
1675  
  
1676  
  static void logStart(String[] args) throws IOException {
1677  
    String line = smartJoin(args);
1678  
    appendToLog(new File(userHome(), ".javax/log.txt").getPath(), line);
1679  
  }
1680  
  
1681  
  static String quote(String s) {
1682  
    if (s == null) return "null";
1683  
    return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", "\\r").replace("\n", "\\n") + "\"";
1684  
  }
1685  
  
1686  
  static void appendToLog(String path, String line) throws IOException {
1687  
    appendToFile(path, "\n" + line + "\n");
1688  
  }
1689  
  
1690  
  static void appendToFile(String path, String s) throws IOException {
1691  
    new File(path).getParentFile().mkdirs();
1692  
    Writer writer = new BufferedWriter(new OutputStreamWriter(
1693  
      new FileOutputStream(path, true), "UTF-8"));
1694  
    writer.write(s);
1695  
    writer.close();
1696  
  }
1697  
1698  
  !include #1000943
1699  
1700  
  static PrintStream oldOut, oldErr;
1701  
  static Thread reader, reader2;
1702  
  static boolean quit; // always false now
1703  
  static PipedInputStream pin=new PipedInputStream();
1704  
  static PipedInputStream pin2=new PipedInputStream();
1705  
  static PipedInputStream pin3=new PipedInputStream();
1706  
1707  
  static class Console extends WindowAdapter implements WindowListener, ActionListener {
1708  
    JFrame frame;
1709  
    JTextArea textArea;
1710  
    JTextField tfInput;
1711  
    StringBuffer buf = new StringBuffer();
1712  
    JButton buttonclear, buttonkill, buttonrestart, buttonduplicate, buttonstacktrace;
1713  
    String[] args;
1714  
1715  
    final DelayedUpdate du = new DelayedUpdate(new Runnable() {
1716  
      public void run() { try {
1717  
1718  
        textArea.append(buf.substring(textArea.getText().length()));
1719  
1720  
      } catch (Exception __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}});
1721  
1722  
    public Console(final String[] args) ctex {
1723  
      this.args = args;
1724  
      // create all components and add them
1725  
      frame=new JFrame(args.length == 0 ? "JavaX Starter Output" : "JavaX Output - " + join(" ", args));
1726  
1727  
		/*Dimension screenSize=Toolkit.getDefaultToolkit().getScreenSize();
1728  
		Dimension frameSize=new Dimension((int)(screenSize.width/2),(int)(screenSize.height/2));
1729  
		int x=(int)(frameSize.width/2);
1730  
		int y=(int)(frameSize.height/2);
1731  
		frame.setBounds(x,y,frameSize.width,frameSize.height);*/
1732  
1733  
      // put in right-bottom corner
1734  
      Rectangle r = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
1735  
      int w = 550, h = 200;
1736  
      frame.setBounds(r.x+r.width-w, r.y+r.height-h, w, h);
1737  
1738  
      textArea=new JTextArea();
1739  
      textArea.setEditable(false);
1740  
      buttonclear = new JButton("clear");
1741  
      buttonkill = new JButton("kill");
1742  
      buttonrestart = new JButton("restart");
1743  
      buttonduplicate = new JButton("duplicate");
1744  
      buttonstacktrace = new JButton("status");
1745  
      buttonstacktrace.setToolTipText("Show threads & stack traces.");
1746  
1747  
      JPanel buttons = new JPanel(new GridLayout(1, 5));
1748  
      buttons.add(buttonclear);
1749  
      buttons.add(buttonkill);
1750  
      buttons.add(buttonrestart);
1751  
      buttons.add(buttonduplicate);
1752  
      buttons.add(buttonstacktrace);
1753  
1754  
      final PipedOutputStream pout3=new PipedOutputStream(pin3);
1755  
      tfInput = new JTextField();
1756  
      tfInput.addActionListener(actionListener {
1757  
        S line = tfInput.getText();
1758  
        try {
1759  
          pout3.write((line + "\n").getBytes("UTF-8"));
1760  
          pout3.flush();
1761  
        } catch (Exception e) {}
1762  
        tfInput.setText("");
1763  
      });
1764  
      
1765  
      JPanel panel = new JPanel(new BorderLayout());
1766  
      panel.add(new JScrollPane(textArea), BorderLayout.CENTER);
1767  
      panel.add(tfInput, BorderLayout.SOUTH);
1768  
      
1769  
      frame.addWindowListener(new WindowAdapter() {
1770  
        public void windowActivated(WindowEvent e) {
1771  
          tfInput.requestFocus();
1772  
        }
1773  
      });
1774  
      
1775  
      frame.getContentPane().setLayout(new BorderLayout());
1776  
      frame.getContentPane().add(panel, BorderLayout.CENTER);
1777  
      frame.getContentPane().add(buttons, BorderLayout.SOUTH);
1778  
      frame.setVisible(true);
1779  
      
1780  
      //frame.addWindowListener(this); // disabled for now
1781  
      buttonclear.addActionListener(this);
1782  
      buttonkill.addActionListener(this);
1783  
      buttonrestart.addActionListener(this);
1784  
      buttonduplicate.addActionListener(this);
1785  
      buttonstacktrace.addActionListener(this);
1786  
1787  
      quit=false; // signals the Threads that they should exit
1788  
      
1789  
      if (args.length != 0) {
1790  
        print("Starting title updater");
1791  
        new Thread("Console Title Updater :)") {
1792  
          public void run() {
1793  
            if (args.length != 0) {
1794  
              print("Getting title for " + args[0]);
1795  
              String title = getSnippetTitle(args[0]);
1796  
              print("Title: " + title);
1797  
              if (title != null && title.length() != 0)
1798  
                frame.setTitle(title + " [Output]");
1799  
            }
1800  
          }
1801  
        }.start();
1802  
      }
1803  
      
1804  
      System.setIn(pin3);
1805  
      
1806  
    }
1807  
1808  
    public synchronized void windowClosed(WindowEvent evt)
1809  
    {
1810  
      console = null;
1811  
      /*quit=true;
1812  
      this.notifyAll(); // stop all threads
1813  
      try { reader.join(1000);pin.close();   } catch (Exception e){}
1814  
      try { reader2.join(1000);pin2.close(); } catch (Exception e){}
1815  
      System.exit(0);*/
1816  
    }
1817  
1818  
    public synchronized void windowClosing(WindowEvent evt)
1819  
    {
1820  
      frame.setVisible(false); // default behaviour of JFrame
1821  
      frame.dispose();
1822  
    }
1823  
1824  
    public synchronized void actionPerformed(ActionEvent evt) {
1825  
      if (evt.getSource() == buttonkill) {
1826  
        print("Console: Kill button pressed!");
1827  
        // TODO: give threads time to finish, e.g. reportToChat?
1828  
        System.exit(0);
1829  
      } else if (evt.getSource() == buttonrestart) {
1830  
        print("Console: Restart button pressed.");
1831  
        nohupJavax(smartJoin(args));
1832  
        System.exit(0);
1833  
      } else if (evt.getSource() == buttonduplicate) {
1834  
        print("Console: Duplicate button pressed.");
1835  
        nohupJavax(smartJoin(args));
1836  
      } else if (evt.getSource() == buttonstacktrace) {
1837  
        listUserThreadsWithStackTraces();
1838  
      } else {
1839  
        textArea.setText("");
1840  
        buf = new StringBuffer();
1841  
      }
1842  
    }
1843  
1844  
    public void appendText(String s, boolean outNotErr) {
1845  
      if (verbose) oldOut.println("Console appendText " + outNotErr + " " + quote(s));
1846  
      buf.append(s);
1847  
      du.trigger();
1848  
    }
1849  
  } // Console
1850  
1851  
	static void tryToOpenConsole(String[] args) {
1852  
	  try {
1853  
	    console = new Console(args);
1854  
	  } catch (HeadlessException e) {
1855  
	    // ok, we're headless.
1856  
	  } catch (Throwable e) {
1857  
	    // some other error in console - continue without it
1858  
	    e.printStackTrace();
1859  
	  }
1860  
	}
1861  
1862  
  //// END CONSOLE STUFF
1863  
1864  
  static long now_virtualTime;
1865  
  static long now() {
1866  
    return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis();
1867  
  }
1868  
1869  
  static void print(Object o) {
1870  
    System.out.println(o);
1871  
  }
1872  
1873  
  public synchronized void run()
1874  
  {
1875  
    try
1876  
    {
1877  
      while (Thread.currentThread()==reader)
1878  
      {
1879  
        try { this.wait(100);}catch(InterruptedException ie) {}
1880  
        if (pin.available()!=0)
1881  
        {
1882  
          String input=readLine(pin);
1883  
          if (verbose) oldOut.println("reader: " + quote(input));
1884  
          appendText(input, true);
1885  
        }
1886  
        if (quit) return;
1887  
      }
1888  
1889  
      while (Thread.currentThread()==reader2)
1890  
      {
1891  
        try { this.wait(100);}catch(InterruptedException ie) {}
1892  
        if (pin2.available()!=0)
1893  
        {
1894  
          String input=readLine(pin2);
1895  
          if (verbose) oldOut.println("reader2: " + quote(input));
1896  
          appendText(input, false);
1897  
        }
1898  
        if (quit) return;
1899  
      }
1900  
    } catch (Exception e)
1901  
    {
1902  
      appendText("\nConsole reports an Internal error.", false);
1903  
      appendText("The error is: "+e, false);
1904  
    }
1905  
  }
1906  
1907  
  static void redirectSystemOutAndErr() {
1908  
    if (reader != null) return; // did this already
1909  
1910  
    x25 _this = new x25();
1911  
    
1912  
    if (verbose) System.out.println("Redirecting System.out");
1913  
    try
1914  
    {
1915  
      PipedOutputStream pout=new PipedOutputStream(pin);
1916  
      oldOut = System.out;
1917  
      TeeOutputStream tee = new TeeOutputStream(oldOut, pout);
1918  
      System.setOut(new PrintStream(tee,true));
1919  
    }
1920  
    catch (Exception io)
1921  
    {
1922  
      System.err.println("Couldn't redirect STDOUT - " + io.getMessage());
1923  
    }
1924  
1925  
    if (verbose) System.out.println("Redirecting System.err");
1926  
    try
1927  
    {
1928  
      PipedOutputStream pout2=new PipedOutputStream(pin2);
1929  
      oldErr = System.err;
1930  
      TeeOutputStream tee = new TeeOutputStream(oldErr, pout2);
1931  
      System.setErr(new PrintStream(tee,true));
1932  
    }
1933  
    catch (Exception io)
1934  
    {
1935  
      System.err.println("Couldn't redirect STDERR - " + io.getMessage());
1936  
    }
1937  
1938  
    if (verbose) System.out.println("Redirects done. Starting readers");
1939  
    
1940  
    // Starting two seperate threads to read from the PipedInputStreams
1941  
    //
1942  
    reader=new Thread(_this, "StdOut Piper");
1943  
    reader.setDaemon(true);
1944  
    reader.start();
1945  
    //
1946  
    reader2 = new Thread(_this, "StdErr Piper");
1947  
    reader2.setDaemon(true);
1948  
    reader2.start();
1949  
  }
1950  
1951  
  static Appendable customSystemOut;
1952  
1953  
  static void appendText(String s, boolean outNotErr) {
1954  
    // We do this with a TeeOutputStream now (safer).
1955  
    // (outNotErr ? oldOut : oldErr).print(s);
1956  
    
1957  
    if (console != null)
1958  
      console.appendText(s, outNotErr);
1959  
    if (customSystemOut != null)
1960  
      try {
1961  
        customSystemOut.append(s);
1962  
      } catch (IOException e) {
1963  
        e.printStackTrace();
1964  
      }
1965  
  }
1966  
1967  
  static String readLine(PipedInputStream in) throws IOException
1968  
  {
1969  
    String input="";
1970  
    do
1971  
    {
1972  
      int available=in.available();
1973  
      if (available==0) break;
1974  
      byte b[]=new byte[available];
1975  
      in.read(b);
1976  
      input=input+new String(b,0,b.length);
1977  
    }while( !input.endsWith("\n") &&  !input.endsWith("\r\n") && !quit);
1978  
    return input;
1979  
  }
1980  
1981  
  static void nohupJavax(String javaxargs) {
1982  
    try {
1983  
      File xfile = new File(userHome(), ".javax/x25.jar");
1984  
      if (!xfile.isFile()) {
1985  
        String url = "http://tinybrain.de/x25.jar";
1986  
        byte[] data = loadBinaryPage(new URL(url).openConnection());
1987  
        if (data.length < 1000000)
1988  
          throw new RuntimeException("Could not load " + url);
1989  
        saveBinaryFile(xfile.getPath(), data);
1990  
      }
1991  
      String jarPath = xfile.getPath();
1992  
      nohup("java -jar " + (isWindows() ? winQuote(jarPath) : bashQuote(jarPath)) + " " + javaxargs);
1993  
    } catch (Exception e) { throw new RuntimeException(e); }
1994  
  }
1995  
1996  
  /** possibly improvable */
1997  
  public static String winQuote(String text) {
1998  
    if (text == null) return null;
1999  
    return "\"" + text
2000  
      .replace("\\", "\\\\")
2001  
      .replace("\"", "\\\"")
2002  
      .replace("\n", "\\n")
2003  
      .replace("\r", "\\r") + "\"";
2004  
  }
2005  
2006  
  public static File nohup(String cmd) throws IOException {
2007  
    File outFile = File.createTempFile("nohup_" + nohup_sanitize(cmd), ".out");
2008  
    nohup(cmd, outFile, false);
2009  
    return outFile;
2010  
  }
2011  
2012  
  static String nohup_sanitize(String s) {
2013  
    return s.replaceAll("[^a-zA-Z0-9\\-_]", "");
2014  
  }
2015  
2016  
  /** outFile takes stdout and stderr. */
2017  
  public static void nohup(String cmd, File outFile, boolean append) throws IOException {
2018  
    String command = nohup_makeNohupCommand(cmd, outFile, append);
2019  
2020  
    File scriptFile = File.createTempFile("_realnohup", isWindows() ? ".bat" : "");
2021  
    System.out.println("[Nohup] " + command);
2022  
    try {
2023  
      //System.out.println("[RealNohup] Script file: " + scriptFile.getPath());
2024  
      saveTextFile(scriptFile.getPath(), command);
2025  
      String[] command2;
2026  
      if (isWindows())
2027  
        command2 = new String[] {"cmd", "/c", "start", "/b", scriptFile.getPath() };
2028  
      else
2029  
        command2 = new String[] {"/bin/bash", scriptFile.getPath() };
2030  
2031  
      Process process = Runtime.getRuntime().exec(command2);
2032  
      try {
2033  
        process.waitFor();
2034  
      } catch (InterruptedException e) {
2035  
        throw new RuntimeException(e);
2036  
      }
2037  
      int value = process.exitValue();
2038  
      //System.out.println("exit value: " + value);
2039  
    } finally {
2040  
      if (!isWindows())
2041  
        scriptFile.delete();
2042  
    }
2043  
  }
2044  
2045  
  public static String nohup_makeNohupCommand(String cmd, File outFile, boolean append) {
2046  
    mkdirsForFile(outFile);
2047  
2048  
    String command;
2049  
    if (isWindows())
2050  
      command = cmd + (append ? " >>" : " >") + winQuote(outFile.getPath()) + " 2>&1";
2051  
    else
2052  
      command = "nohup " + cmd + (append ? " >>" : " >") + bashQuote(outFile.getPath()) + " 2>&1 &";
2053  
    return command;
2054  
  }
2055  
2056  
  public static void mkdirsForFile(File file) {
2057  
    File dir = file.getParentFile();
2058  
    if (dir != null) // is null if file is in current dir
2059  
      dir.mkdirs();
2060  
  }
2061  
 
2062  
  static void autoReportToChat() {
2063  
    if (customSystemOut == null) {
2064  
      print("Auto-reporting to chat.");
2065  
      customSystemOut = new Appendable() {
2066  
        LineBuf buf = new LineBuf();
2067  
        
2068  
        // only using this one
2069  
        public Appendable append(CharSequence cs) {
2070  
          buf.append(cs.toString());
2071  
          while (true) {
2072  
            String s = buf.nextLine();
2073  
            if (s == null) break;
2074  
            reportToChat(s, true);
2075  
          }
2076  
          return this;
2077  
        }
2078  
    
2079  
        public Appendable append(char c) { return this; }
2080  
        public Appendable append(CharSequence s, int start, int end) { return this; }
2081  
      };
2082  
     }
2083  
  }
2084  
  
2085  
  static class Q {
2086  
    LinkedBlockingQueue<Runnable> q = new LinkedBlockingQueue<Runnable>();
2087  
    
2088  
    static class Done extends RuntimeException {}
2089  
    
2090  
    Q() {}
2091  
    
2092  
    Q(String name, boolean startThread) {
2093  
      if (startThread)
2094  
        new Thread(name) {
2095  
          public void run() {
2096  
            Q.this.run();
2097  
          }
2098  
        }.start();
2099  
    }
2100  
    
2101  
    Iterable<Runnable> master() {
2102  
      return new Iterable<Runnable>() {
2103  
        public Iterator<Runnable> iterator() {
2104  
          return new Iterator<Runnable>() {
2105  
            Runnable x;
2106  
            
2107  
            public boolean hasNext() { try {
2108  
   
2109  
              //debug("hasNext");
2110  
              while (x == null)
2111  
                x = q.poll(1, TimeUnit.DAYS);
2112  
              //debug("hasNext true");
2113  
              return true;
2114  
            
2115  
  } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2116  
            
2117  
            public Runnable next() {
2118  
              //debug("next");
2119  
              hasNext();
2120  
              Runnable _x = x;
2121  
              x = null;
2122  
              //debug("next " + structure(x));
2123  
              return _x;
2124  
            }
2125  
            
2126  
            public void remove() {
2127  
            }
2128  
          };
2129  
        }
2130  
      };
2131  
    }
2132  
    
2133  
    void add(Runnable r) {
2134  
      q.add(r);
2135  
    }
2136  
    
2137  
    void run() {
2138  
      for (Runnable r : master()) {
2139  
        try {
2140  
          r.run();
2141  
        } catch (Done e) {
2142  
          return; // break signal
2143  
        } catch (Throwable e) {
2144  
          e.printStackTrace();
2145  
        }
2146  
      }
2147  
    }
2148  
    
2149  
    void done() {
2150  
      add(new Runnable() {
2151  
        public void run() {
2152  
          throw new Done();
2153  
        }
2154  
      });
2155  
    }
2156  
  } // class Q
2157  
2158  
static void reportToChat(final String s, boolean silent) {
2159  
    if (s == null || s.length() == 0) return;
2160  
    if (!silent)
2161  
      print("reportToChat: " + quote(s));
2162  
    reportToChat_getChatThread().add(new Runnable() {
2163  
    public void run() { try {
2164  
        startChatServerIfNotUp();
2165  
        waitForChatServer();
2166  
        chatSend(s);
2167  
    } catch (Exception __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}});
2168  
   }
2169  
  
2170  
  static Q reportToChat_q;
2171  
  
2172  
  static Q reportToChat_getChatThread() {
2173  
    if (reportToChat_q == null)
2174  
      reportToChat_q = new Q("reportToChat", true);
2175  
    return reportToChat_q;
2176  
  }
2177  
  
2178  
  static void startChatServerIfNotUp() {
2179  
    if (portIsBound(9751)) {
2180  
      //print("Chat seems to be up.");
2181  
    } else {
2182  
      nohupJavax("1000867");
2183  
      print("Chat server should be coming up any minute now.");
2184  
    }
2185  
  }
2186  
  
2187  
  static void waitForChatServer() {
2188  
    if (!portIsBound(9751)) {
2189  
      //System.out.print("Waiting for chat server... ");
2190  
      do {
2191  
        sleep(1000);
2192  
      } while (!portIsBound(9751));
2193  
      //print("OK.");
2194  
    }
2195  
  }
2196  
2197  
  static boolean portIsBound(int port) {
2198  
    try {
2199  
      ServerSocket s = new ServerSocket(port);
2200  
      s.close();
2201  
      return false;
2202  
    } catch (IOException e) {
2203  
      return true;
2204  
    }
2205  
  }
2206  
  
2207  
  static class LineBuf {
2208  
    StringBuffer buf = new StringBuffer();
2209  
    
2210  
    void append(String s) {
2211  
      buf.append(s);
2212  
    }
2213  
    
2214  
    String nextLine() {
2215  
      int i = buf.indexOf("\n");
2216  
      if (i >= 0) {
2217  
        String s = buf.substring(0, i > 0 && buf.charAt(i-1) == '\r' ? i-1 : i);
2218  
        buf.delete(0, i+1);
2219  
        return s;
2220  
      }
2221  
      return null;
2222  
    }
2223  
  } // LineBuf
2224  
2225  
  static void sleep(long ms) {
2226  
    try {
2227  
      Thread.sleep(ms);
2228  
    } catch (Exception e) { throw new RuntimeException(e); }
2229  
  }
2230  
2231  
  static int chatSend_chatPort = 9751;
2232  
  
2233  
  static abstract class DialogIO {
2234  
    abstract boolean isStillConnected();
2235  
    abstract String readLineNoBlock();
2236  
    abstract boolean waitForLine();
2237  
    abstract void sendLine(String line);
2238  
    abstract void close();
2239  
  }
2240  
  
2241  
  static DialogIO chatSend_dialog;
2242  
  static String chatSend_id;
2243  
2244  
  static void chatSend(String line) { try {
2245  
   
2246  
    if (chatSend_dialog == null) {
2247  
      final Socket s = new Socket("localhost", chatSend_chatPort);    
2248  
    
2249  
      final Writer w = new OutputStreamWriter(s.getOutputStream(), "UTF-8");
2250  
      final BufferedReader in = new BufferedReader(
2251  
        new InputStreamReader(s.getInputStream(), "UTF-8"));
2252  
      chatSend_dialog = new DialogIO() {
2253  
        String line;
2254  
        boolean buff;
2255  
        
2256  
        boolean isStillConnected() {
2257  
          return !(buff || s.isClosed());
2258  
        }
2259  
        
2260  
        String readLineNoBlock() {
2261  
          String l = line;
2262  
          line = null;
2263  
          return l;
2264  
        }
2265  
        
2266  
        boolean waitForLine() { try {
2267  
   
2268  
          if (line != null) return true;
2269  
          print("Readline");
2270  
          line = in.readLine();
2271  
          print("Readline done: " + line);
2272  
          if (line == null) buff = true;
2273  
          return line != null;
2274  
        
2275  
  } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2276  
        
2277  
        void sendLine(String line) { try {
2278  
   
2279  
          w.write(line + "\n");
2280  
          w.flush();
2281  
        
2282  
  } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2283  
        
2284  
        void close() { try {
2285  
   
2286  
          s.close();
2287  
        
2288  
  } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2289  
      };
2290  
      chatSend_dialog.waitForLine();
2291  
      String l = chatSend_dialog.readLineNoBlock();
2292  
      if (l.startsWith("Your ID: "))
2293  
        chatSend_id = l.substring("Your ID: ".length());
2294  
    }
2295  
  
2296  
    chatSend_dialog.sendLine(line);
2297  
  
2298  
  } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2299  
2300  
  static class TeeOutputStream extends OutputStream {
2301  
    
2302  
    protected OutputStream out, branch;
2303  
2304  
    public TeeOutputStream( OutputStream out, OutputStream branch ) {
2305  
      this.out = out;
2306  
      this.branch = branch;
2307  
    }
2308  
2309  
    @Override
2310  
    public synchronized void write(byte[] b) throws IOException {
2311  
      write(b, 0, b.length);
2312  
    }
2313  
2314  
    @Override
2315  
    public synchronized void write(byte[] b, int off, int len) throws IOException {
2316  
      //if (verbose) oldOut.println("Tee write " + new String(b, "UTF-8"));
2317  
      out.write(b, off, len);
2318  
      this.branch.write(b, off, len);
2319  
    }
2320  
2321  
    @Override
2322  
    public synchronized void write(int b) throws IOException {
2323  
      write(new byte[] {(byte) b});
2324  
    }
2325  
2326  
    /**
2327  
     * Flushes both streams.
2328  
     * @throws IOException if an I/O error occurs
2329  
     */
2330  
    @Override
2331  
    public void flush() throws IOException {
2332  
      out.flush();
2333  
      this.branch.flush();
2334  
    }
2335  
2336  
    /**
2337  
     * Closes both streams.
2338  
     * @throws IOException if an I/O error occurs
2339  
     */
2340  
    @Override
2341  
    public void close() throws IOException {
2342  
      out.close();
2343  
      this.branch.close();
2344  
    }
2345  
  }
2346  
  
2347  
  static boolean isChatServer(String[] args) {
2348  
    for (int i = 0; i < args.length; i++)
2349  
      if (isSnippetID(args[i]))
2350  
        return parseSnippetID(args[i]) == 1000867;
2351  
    return false;
2352  
  }
2353  
  
2354  
  static String getSnippetTitle(String id) {
2355  
    try {
2356  
      return loadPage(new URL("http://tinybrain.de:8080/tb-int/getfield.php?id=" + parseSnippetID(id) + "&field=title"));
2357  
    } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }
2358  
  }
2359  
  
2360  
  static void listUserThreadsWithStackTraces() {
2361  
    print("");
2362  
    Map<Thread, StackTraceElement[]> threadMap = Thread.getAllStackTraces();
2363  
    int n = 0;
2364  
    for (Thread t : threadMap.keySet()) {
2365  
      ThreadGroup g = t.getThreadGroup();
2366  
      if (g != null && g.getName().equals("system")) continue;
2367  
      ++n;
2368  
      print(t);
2369  
      for (StackTraceElement e : threadMap.get(t)) {
2370  
        print("  " + e);
2371  
      }
2372  
      print("");
2373  
    }
2374  
    print(n + " user threads.");
2375  
  }
2376  
  
2377  
  static void killMyself() {
2378  
    print("Killing myself. (insert overall feeling here)");
2379  
    System.exit(0);
2380  
  }
2381  
}

Author comment

Now written in JavaX :)

download  show line numbers  debug dex  old transpilations   

Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #750
Snippet name: x25.java (JavaX)
Eternal ID of this version: #750/1
Text MD5: cf5aad5b0cd52db9af25066aedaa7b95
Transpilation MD5: 1013549f7364fbd49d68b07f7b6d8f84
Author: stefan
Category: javax
Type: JavaX source code
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2015-09-20 22:14:17
Source code size: 82832 bytes / 2381 lines
Pitched / IR pitched: No / Yes
Views / Downloads: 878 / 867
Referenced in: [show references]