Merge "Fix broken links in crypto documentation."
diff --git a/ojluni/src/main/java/sun/nio/ch/PollSelectorImpl.java b/ojluni/src/main/java/sun/nio/ch/PollSelectorImpl.java
index 17c9f03..1911c35 100644
--- a/ojluni/src/main/java/sun/nio/ch/PollSelectorImpl.java
+++ b/ojluni/src/main/java/sun/nio/ch/PollSelectorImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -57,9 +57,23 @@
         long pipeFds = IOUtil.makePipe(false);
         fd0 = (int) (pipeFds >>> 32);
         fd1 = (int) pipeFds;
-        pollWrapper = new PollArrayWrapper(INIT_CAP);
-        pollWrapper.initInterrupt(fd0, fd1);
-        channelArray = new SelectionKeyImpl[INIT_CAP];
+        try {
+            pollWrapper = new PollArrayWrapper(INIT_CAP);
+            pollWrapper.initInterrupt(fd0, fd1);
+            channelArray = new SelectionKeyImpl[INIT_CAP];
+        } catch (Throwable t) {
+            try {
+                FileDispatcherImpl.closeIntFD(fd0);
+            } catch (IOException ioe0) {
+                t.addSuppressed(ioe0);
+            }
+            try {
+                FileDispatcherImpl.closeIntFD(fd1);
+            } catch (IOException ioe1) {
+                t.addSuppressed(ioe1);
+            }
+            throw t;
+        }
     }
 
     protected int doSelect(long timeout)
diff --git a/ojluni/src/main/java/sun/nio/fs/UnixFileStore.java b/ojluni/src/main/java/sun/nio/fs/UnixFileStore.java
index 98fc9ab..0532e4d 100644
--- a/ojluni/src/main/java/sun/nio/fs/UnixFileStore.java
+++ b/ojluni/src/main/java/sun/nio/fs/UnixFileStore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -186,7 +186,8 @@
             return false;
         UnixFileStore other = (UnixFileStore)ob;
         return (this.dev == other.dev) &&
-               Arrays.equals(this.entry.dir(), other.entry.dir());
+               Arrays.equals(this.entry.dir(), other.entry.dir()) &&
+               this.entry.name().equals(other.entry.name());
     }
 
     @Override
diff --git a/tools/upstream/src/main/java/libcore/CompareUpstreams.java b/tools/upstream/src/main/java/libcore/CompareUpstreams.java
index fa6bd4f..348ec62 100644
--- a/tools/upstream/src/main/java/libcore/CompareUpstreams.java
+++ b/tools/upstream/src/main/java/libcore/CompareUpstreams.java
@@ -16,7 +16,8 @@
 
 package libcore;
 
-import java.io.*;
+import java.io.IOException;
+import java.io.PrintStream;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -119,45 +120,6 @@
         return escapeTsv(String.join("\n", result));
     }
 
-    /**
-     * Computes the edit distance of two lists, i.e. the smallest number of list items to delete,
-     * insert or replace that would transform the content of one list into the other.
-     */
-    private <T> int editDistance(List<T> a, List<T> b) {
-        int numB = b.size();
-        int[] prevCost = new int[numB + 1];
-        for (int i = 0; i <= numB; i++) {
-            prevCost[i] = i;
-        }
-        int[] curCost = new int[numB + 1];
-        for (int endA = 1; endA <= a.size(); endA++) {
-            // For each valid index i, prevCost[i] is the edit distance between
-            // a.subList(0, endA-1) and b.sublist(0, i).
-            // We now calculate curCost[end_b] as the edit distance between
-            // a.subList(0, endA) and b.subList(0, endB)
-            curCost[0] = endA;
-            for (int endB = 1; endB <= numB; endB++) {
-                boolean endsMatch = a.get(endA - 1).equals(b.get(endB - 1));
-                curCost[endB] = min(
-                        curCost[endB - 1] + 1, // append item from b
-                        prevCost[endB] + 1, // append item from a
-                        prevCost[endB - 1] + (endsMatch ? 0 : 1)); // match or replace item
-            }
-            int[] tmp = curCost;
-            curCost = prevCost;
-            prevCost = tmp;
-        }
-        return prevCost[numB];
-    }
-
-    private static int min(int a, int b, int c) {
-        if (a < b) {
-            return a < c ? a : c;
-        } else {
-            return b < c ? b : c;
-        }
-    }
-
     private static String escapeTsv(String value) {
         if (value.contains("\t")) {
             throw new IllegalArgumentException(value); // tsv doesn't support escaping tabs
@@ -207,7 +169,7 @@
                     comparison = "missing";
                 } else {
                     List<String> linesA = Util.readLines(upstreamFile);
-                    int distance = editDistance(linesA, linesB);
+                    int distance = Util.editDistance(linesA, linesB);
                     if (distance == 0) {
                         comparison = "identical";
                     } else {
@@ -246,7 +208,7 @@
     }
 
     public void run() throws IOException {
-        List<Path> relPaths = standardRepositories.ojluni().loadRelPathsFromMakefile();
+        List<Path> relPaths = standardRepositories.ojluni().loadRelPathsFromBlueprint();
         run(System.out, relPaths);
     }
 
diff --git a/tools/upstream/src/main/java/libcore/CopyUpstreamFiles.java b/tools/upstream/src/main/java/libcore/CopyUpstreamFiles.java
index 007914f..6bd758e 100644
--- a/tools/upstream/src/main/java/libcore/CopyUpstreamFiles.java
+++ b/tools/upstream/src/main/java/libcore/CopyUpstreamFiles.java
@@ -20,7 +20,6 @@
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
@@ -36,7 +35,7 @@
     }
 
     public void run() throws IOException {
-        List<Path> relPaths = standardRepositories.ojluni().loadRelPathsFromMakefile();
+        List<Path> relPaths = standardRepositories.ojluni().loadRelPathsFromBlueprint();
         if (outputDir.toFile().exists()) {
             throw new IOException(outputDir + " already exists");
         } else {
diff --git a/tools/upstream/src/main/java/libcore/Lines.java b/tools/upstream/src/main/java/libcore/Lines.java
new file mode 100644
index 0000000..c60fd0b
--- /dev/null
+++ b/tools/upstream/src/main/java/libcore/Lines.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.RandomAccess;
+
+/**
+ * A List of lines (eg. from a text file or a command's output).
+ */
+public class Lines extends AbstractList<String> implements RandomAccess {
+    public static Lines EMPTY = new Lines(Collections.emptyList());
+
+    private final List<String> delegate;
+    private volatile int hashCode = 0;
+
+    public Lines(Collection<String> collection) {
+        this.delegate = new ArrayList<>(collection);
+    }
+
+    @Override
+    public int hashCode() {
+        if (hashCode == 0) {
+            hashCode = super.hashCode();
+        }
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o.hashCode() != this.hashCode()) {
+            return false;
+        }
+        return super.equals(o);
+    }
+
+    @Override
+    public Iterator<String> iterator() {
+        return Collections.unmodifiableList(delegate).iterator();
+    }
+
+    @Override
+    public String get(int index) {
+        return delegate.get(index);
+    }
+
+    @Override
+    public int size() {
+        return delegate.size();
+    }
+}
diff --git a/tools/upstream/src/main/java/libcore/Repository.java b/tools/upstream/src/main/java/libcore/Repository.java
index 9a4eee6..ed92a24 100644
--- a/tools/upstream/src/main/java/libcore/Repository.java
+++ b/tools/upstream/src/main/java/libcore/Repository.java
@@ -23,8 +23,11 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -33,12 +36,22 @@
  */
 abstract class Repository {
 
+    /**
+     * Maps from a file's (current) relPath to the corresponding OpenJDK relPath from
+     * which it has been, and still remains, renamed.
+     */
+    static final Map<Path, Path> OPENJDK_REL_PATH = Collections.singletonMap(
+        // renamed in libcore commit 583eb0e4738456f0547014a4857a14456be267ee
+        Paths.get("native/linux_close.cpp"), Paths.get("native/linux_close.c"));
+
     protected final Path rootPath;
     protected final String name;
+    protected final List<String> sourceDirs;
 
-    protected Repository(Path rootPath, String name) {
+    protected Repository(Path rootPath, String name, List<String> sourceDirs) {
         this.rootPath = Objects.requireNonNull(rootPath);
         this.name = Objects.requireNonNull(name);
+        this.sourceDirs = Objects.requireNonNull(sourceDirs);
         if (!rootPath.toFile().isDirectory()) {
             throw new IllegalArgumentException("Missing or not a directory: " + rootPath);
         }
@@ -55,7 +68,21 @@
         return p == null ? null : rootPath.resolve(p).toAbsolutePath();
     }
 
-    public abstract Path pathFromRepository(Path relPath);
+    public Path pathFromRepository(Path relPath) {
+        // Search across all sourceDirs for the indicated file.
+        for (String sourceDir : sourceDirs) {
+            Path repositoryRelativePath = Paths.get(sourceDir).resolve(relPath);
+            File file = rootPath.resolve(repositoryRelativePath).toFile();
+            if (file.exists()) {
+                return repositoryRelativePath;
+            }
+        }
+        return null;
+    }
+
+    public final Path rootPath() {
+        return rootPath;
+    }
 
     /**
      * @return A human readable name to identify this repository, suitable for use as a
@@ -76,18 +103,22 @@
      */
     public static Repository openJdk9(Path upstreamRoot, String upstreamName) {
         List<String> sourceDirs = Arrays.asList(
-                "jdk/src/java.base/share/classes",
-                "jdk/src/java.logging/share/classes",
-                "jdk/src/java.prefs/share/classes",
-                "jdk/src/java.sql/share/classes",
-                "jdk/src/java.desktop/share/classes",
-                "jdk/src/java.base/solaris/classes",
-                "jdk/src/java.base/unix/classes",
-                "jdk/src/java.prefs/unix/classes",
-                "jdk/src/jdk.unsupported/share/classes",
-                "jdk/src/jdk.net/share/classes",
-                "jdk/src/java.base/linux/classes",
-                "build/linux-x86_64-normal-server-release/support/gensrc/java.base"
+            "jdk/src/java.base/share/classes",
+            "jdk/src/java.logging/share/classes",
+            "jdk/src/java.prefs/share/classes",
+            "jdk/src/java.sql/share/classes",
+            "jdk/src/java.desktop/share/classes",
+            "jdk/src/java.base/solaris/classes",
+            "jdk/src/java.base/unix/classes",
+            "jdk/src/java.prefs/unix/classes",
+            "jdk/src/jdk.unsupported/share/classes",
+            "jdk/src/jdk.net/share/classes",
+            "jdk/src/java.base/linux/classes",
+            "build/linux-x86_64-normal-server-release/support/gensrc/java.base",
+
+            // Native (.c) files
+            "jdk/src/java.base/unix/native/libjava",
+            "jdk/src/java.base/share/native/libjava"
         );
         return new OpenJdkRepository(upstreamRoot, upstreamName, sourceDirs);
     }
@@ -97,11 +128,27 @@
      * subdirectory {@code upstreamName} under the directory {@code upstreamRoot}.
      */
     public static Repository openJdkLegacy(Path upstreamRoot, String upstreamName) {
-        List<String> sourceDirs = Arrays.asList(
-                "jdk/src/share/classes",
-                "jdk/src/solaris/classes",
-                "build/linux-x86_64-normal-server-release/jdk/gensrc"
-                );
+        List<String> sourceDirs = new ArrayList<>();
+        sourceDirs.addAll(Arrays.asList(
+            "jdk/src/share/classes",
+            "jdk/src/solaris/classes",
+            "build/linux-x86_64-normal-server-release/jdk/gensrc"
+        ));
+
+        // In legacy OpenJDK versions, the source files are organized into a subfolder
+        // hierarchy based on package name, whereas in Android and OpenJDK 9+ they're in
+        // a flat folder. We work around this by just searching through all of the
+        // applicable folders (from which we have sources) in legacy OpenJDK versions.
+        List<String> nativeSourceDirs = new ArrayList<>();
+        List<String> pkgPaths = Arrays.asList("", "java/io", "java/lang", "java/net", "java/nio",
+            "java/util", "java/util/zip", "sun/nio/ch", "sun/nio/fs");
+        for (String pkgPath : pkgPaths) {
+            nativeSourceDirs.add("jdk/src/solaris/native/" + pkgPath);
+            nativeSourceDirs.add("jdk/src/share/native/" + pkgPath);
+            nativeSourceDirs.add("jdk/src/solaris/native/common/" + pkgPath);
+            nativeSourceDirs.add("jdk/src/share/native/common/" + pkgPath);
+        }
+        sourceDirs.addAll(nativeSourceDirs);
 
         return new OpenJdkRepository(upstreamRoot, upstreamName, sourceDirs);
     }
@@ -120,7 +167,6 @@
     }
 
     static class OjluniRepository extends Repository {
-
         /**
          * The repository of ojluni java files belonging to the Android sources under
          * {@code buildTop}.
@@ -129,29 +175,42 @@
          *        {@quote ANDROID_BUILD_TOP} environment variable.
          */
         public OjluniRepository(Path buildTop) {
-            super(buildTop.resolve("libcore"), "ojluni");
+            super(buildTop.resolve("libcore"), "ojluni",
+                /* sourceDirs */ Arrays.asList("ojluni/src/main/java", "ojluni/src/main/native"));
         }
 
 
         @Override
         public Path pathFromRepository(Path relPath) {
-            return Paths.get("ojluni/src/main/java").resolve(relPath);
+            // Enforce that the file exists in ojluni
+            return Objects.requireNonNull(super.pathFromRepository(relPath));
         }
 
         /**
-         * Returns the list of relative paths to .java files parsed from openjdk_java_files.mk
+         * Returns the list of relative paths to files parsed from blueprint files.
          */
-        public List<Path> loadRelPathsFromMakefile() throws IOException {
+        public List<Path> loadRelPathsFromBlueprint() throws IOException {
             List<Path> result = new ArrayList<>();
-            Path makefile = rootPath.resolve("openjdk_java_files.bp");
-            Pattern pattern = Pattern.compile("\"ojluni/src/main/java/(.+\\.java)\"");
-            for (String line : Util.readLines(makefile)) {
+            result.addAll(loadRelPathsFromBlueprint(
+                "openjdk_java_files.bp", "\"ojluni/src/main/java/(.+\\.java)\""));
+            result.addAll(loadRelPathsFromBlueprint(
+                "ojluni/src/main/native/Android.bp", "\\s+\"(.+\\.(?:c|cpp))\","));
+            return result;
+        }
+
+        private List<Path> loadRelPathsFromBlueprint(
+            String blueprintPathString, String patternString) throws IOException {
+            Path blueprintPath = rootPath.resolve(blueprintPathString);
+            Pattern pattern = Pattern.compile(patternString);
+            List<Path> result = new ArrayList<>();
+            for (String line : Util.readLines(blueprintPath)) {
                 Matcher matcher = pattern.matcher(line);
                 while (matcher.find()) {
-                    Path path = new File(matcher.group(1)).toPath();
-                    result.add(path);
+                    Path relPath = Paths.get(matcher.group(1));
+                    result.add(relPath);
                 }
             }
+            Collections.sort(result);
             return result;
         }
 
@@ -162,23 +221,17 @@
     }
 
     static class OpenJdkRepository extends Repository {
-        private final List<String> sourceDirs;
 
         public OpenJdkRepository(Path upstreamRoot, String name, List<String> sourceDirs) {
-            super(upstreamRoot.resolve(name), name);
-            this.sourceDirs = Objects.requireNonNull(sourceDirs);
+            super(upstreamRoot.resolve(name), name, sourceDirs);
         }
 
         @Override
         public Path pathFromRepository(Path relPath) {
-            for (String sourceDir : sourceDirs) {
-                Path repositoryRelativePath = Paths.get(sourceDir).resolve(relPath);
-                Path file = rootPath.resolve(repositoryRelativePath);
-                if (file.toFile().exists()) {
-                    return repositoryRelativePath;
-                }
+            if (OPENJDK_REL_PATH.containsKey(relPath)) {
+                relPath = OPENJDK_REL_PATH.get(relPath);
             }
-            return null;
+            return super.pathFromRepository(relPath);
         }
 
         @Override
@@ -187,5 +240,4 @@
         }
     }
 
-
 }
diff --git a/tools/upstream/src/main/java/libcore/StandardRepositories.java b/tools/upstream/src/main/java/libcore/StandardRepositories.java
index 82b039c..a79e905 100644
--- a/tools/upstream/src/main/java/libcore/StandardRepositories.java
+++ b/tools/upstream/src/main/java/libcore/StandardRepositories.java
@@ -91,17 +91,22 @@
                     "SplittableRandom"
             )));
 
-    public Repository currentUpstream(Path relPath) {
-        boolean isJsr166 = relPath.startsWith("java/util/concurrent/");
+    public boolean isJsr166(Path relPath) {
+        boolean result = relPath.startsWith("java/util/concurrent/");
         String ju = "java/util/";
         String suffix = ".java";
-        if (!isJsr166 && relPath.startsWith(ju)) {
+        if (!result && relPath.startsWith(ju)) {
             String name = relPath.toString().substring(ju.length());
             if (name.endsWith(suffix)) {
                 name = name.substring(0, name.length() - suffix.length());
-                isJsr166 = juFilesFromJsr166.contains(name);
+                result = juFilesFromJsr166.contains(name);
             }
         }
+        return result;
+    }
+
+    public Repository currentUpstream(Path relPath) {
+        boolean isJsr166 = isJsr166(relPath);
         if (isJsr166) {
             return jsr166Upstream;
         } else if (relPath.startsWith("java/sql/") || relPath.startsWith("javax/sql/")) {
diff --git a/tools/upstream/src/main/java/libcore/Util.java b/tools/upstream/src/main/java/libcore/Util.java
index d213080..c50e990 100644
--- a/tools/upstream/src/main/java/libcore/Util.java
+++ b/tools/upstream/src/main/java/libcore/Util.java
@@ -17,25 +17,90 @@
 package libcore;
 
 import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.FileReader;
+import java.io.FileWriter;
 import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 
+/**
+ * Utilities for dealing with text file contents.
+ */
 class Util {
     private Util() {
     }
 
-    public static List<String> readLines(Path path) throws IOException {
+    public static Lines readLines(Reader reader) throws IOException {
         List<String> result = new ArrayList<>();
-        try (BufferedReader reader = new BufferedReader(new FileReader(path.toFile()))) {
-            String line;
-            while ((line = reader.readLine()) != null) {
-                result.add(line);
+        BufferedReader br = (reader instanceof BufferedReader)
+                ? (BufferedReader) reader : new BufferedReader(reader);
+        String line;
+        while ((line = br.readLine()) != null) {
+            result.add(line);
+        }
+        return new Lines(result);
+    }
+
+    public static Lines readLines(Path path) throws IOException {
+        try (Reader reader = new FileReader(path.toFile())) {
+            return readLines(reader);
+        }
+    }
+
+    public static void writeLines(Path path, Lines lines) throws IOException {
+        try (PrintStream printStream = new PrintStream(path.toFile())) {
+            for (String line : lines) {
+                printStream.println(line);
             }
         }
-        return result;
+    }
+
+    /**
+     * Computes the edit distance of two lists, i.e. the smallest number of list items to delete,
+     * insert or replace that would transform the content of one list into the other.
+     */
+    public static <T> int editDistance(List<T> a, List<T> b) {
+        if (a.equals(b)) {
+            return 0;
+        }
+        int numB = b.size();
+        int[] prevCost = new int[numB + 1];
+        for (int i = 0; i <= numB; i++) {
+            prevCost[i] = i;
+        }
+        int[] curCost = new int[numB + 1];
+        for (int endA = 1; endA <= a.size(); endA++) {
+            // For each valid index i, prevCost[i] is the edit distance between
+            // a.subList(0, endA-1) and b.sublist(0, i).
+            // We now calculate curCost[end_b] as the edit distance between
+            // a.subList(0, endA) and b.subList(0, endB)
+            curCost[0] = endA;
+            for (int endB = 1; endB <= numB; endB++) {
+                boolean endsMatch = a.get(endA - 1).equals(b.get(endB - 1));
+                curCost[endB] = min(
+                        curCost[endB - 1] + 1, // append item from b
+                        prevCost[endB] + 1, // append item from a
+                        prevCost[endB - 1] + (endsMatch ? 0 : 1)); // match or replace item
+            }
+            int[] tmp = curCost;
+            curCost = prevCost;
+            prevCost = tmp;
+        }
+        return prevCost[numB];
+    }
+
+    private static int min(int a, int b, int c) {
+        if (a < b) {
+            return a < c ? a : c;
+        } else {
+            return b < c ? b : c;
+        }
     }
 
 }
diff --git a/tools/upstream/upstream-diff b/tools/upstream/upstream-diff
index 359ffee..b689b33 100755
--- a/tools/upstream/upstream-diff
+++ b/tools/upstream/upstream-diff
@@ -52,6 +52,7 @@
 import argparse
 import os
 import os.path
+import re
 import subprocess
 import sys
 
@@ -61,13 +62,17 @@
     # Root of repository snapshots. See go/libcore-o-verify for how you'd want to set this.
     ojluni_upstreams = os.environ['OJLUNI_UPSTREAMS']
     for rel_path in rel_paths:
-        if not rel_path.endswith('.java'):
-            # Might be a fully qualified class name
+        # Paths end with a dot and lowercase file extension (.c, .java, ...) but
+        # fully qualified class names do not.
+        if ('/' not in rel_path) and (not re.match('.+\\.[a-z]{1,4}$', rel_path)):
+            # Assume a fully qualified class name
             rel_path = rel_path.replace('.', '/') + '.java'
         paths = []
         for repository in repositories:
-            if repository == "ojluni":
-                paths.append('%s/libcore/ojluni/src/main/java/%s' % (android_build_top, rel_path))
+            if repository == 'ojluni':
+                file_group = 'java/' if rel_path.endswith('.java') else 'native/'
+                paths.append('%s/libcore/ojluni/src/main/%s/%s'
+                             % (android_build_top, file_group, rel_path))
             else:
                 paths.append('%s/%s/%s' % (ojluni_upstreams, repository, rel_path))
         subprocess.call([diff] + paths)
@@ -88,8 +93,8 @@
     parser.add_argument('-d', '--diff', default='meld',
                         help='Application to use for diffing.')
     parser.add_argument('rel_path', nargs="+",
-                        help='File to compare: either a relative path below '
-                             'libcore/ojluni/src/main/java, or a fully qualified class name.')
+                        help='File to compare: either a relative path below libcore/ojluni/'
+                             'src/main/{java,native}, or a fully qualified class name.')
     args = parser.parse_args()
     repositories = args.repositories.split(',')
     if (len(repositories) < 2):