Merge "Add 64-bit slm optimized strlcpy and srlcat."
diff --git a/libc/Android.mk b/libc/Android.mk
index 893738a..345a1f3 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -43,7 +43,6 @@
     bionic/ether_aton.c \
     bionic/ether_ntoa.c \
     bionic/fts.c \
-    bionic/gethostname.c \
     bionic/getpriority.c \
     bionic/if_indextoname.c \
     bionic/if_nametoindex.c \
@@ -120,6 +119,7 @@
     bionic/getauxval.cpp \
     bionic/getcwd.cpp \
     bionic/getentropy_linux.c \
+    bionic/gethostname.cpp \
     bionic/getpgrp.cpp \
     bionic/getpid.cpp \
     bionic/gettid.cpp \
diff --git a/libc/bionic/dirent.cpp b/libc/bionic/dirent.cpp
index 5e1c7a5..6d87097 100644
--- a/libc/bionic/dirent.cpp
+++ b/libc/bionic/dirent.cpp
@@ -43,6 +43,7 @@
   int fd_;
   size_t available_bytes_;
   dirent* next_;
+  long current_pos_;
   pthread_mutex_t mutex_;
   dirent buff_[15];
 };
@@ -55,6 +56,7 @@
   d->fd_ = fd;
   d->available_bytes_ = 0;
   d->next_ = NULL;
+  d->current_pos_ = 0L;
   pthread_mutex_init(&d->mutex_, NULL);
   return d;
 }
@@ -100,6 +102,9 @@
   dirent* entry = d->next_;
   d->next_ = reinterpret_cast<dirent*>(reinterpret_cast<char*>(entry) + entry->d_reclen);
   d->available_bytes_ -= entry->d_reclen;
+  // The directory entry offset uses 0, 1, 2 instead of real file offset,
+  // so the value range of long type is enough.
+  d->current_pos_ = static_cast<long>(entry->d_off);
   return entry;
 }
 
@@ -146,6 +151,20 @@
   ScopedPthreadMutexLocker locker(&d->mutex_);
   lseek(d->fd_, 0, SEEK_SET);
   d->available_bytes_ = 0;
+  d->current_pos_ = 0L;
+}
+
+void seekdir(DIR* d, long offset) {
+  ScopedPthreadMutexLocker locker(&d->mutex_);
+  off_t ret = lseek(d->fd_, offset, SEEK_SET);
+  if (ret != -1L) {
+    d->available_bytes_ = 0;
+    d->current_pos_ = ret;
+  }
+}
+
+long telldir(DIR* d) {
+  return d->current_pos_;
 }
 
 int alphasort(const dirent** a, const dirent** b) {
diff --git a/libc/bionic/gethostname.c b/libc/bionic/gethostname.cpp
similarity index 78%
rename from libc/bionic/gethostname.c
rename to libc/bionic/gethostname.cpp
index 5d3d7d9..962fea1 100644
--- a/libc/bionic/gethostname.c
+++ b/libc/bionic/gethostname.cpp
@@ -25,27 +25,24 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #include <errno.h>
-#include <unistd.h>
 #include <string.h>
 #include <sys/utsname.h>
+#include <unistd.h>
 
-int gethostname(char*  buff, size_t  buflen)
-{
-    struct utsname  name;
-    int             result = 0;
+int gethostname(char* buf, size_t n) {
+  struct utsname name;
+  if (uname(&name) == -1) {
+    return -1;
+  }
 
-    result = uname(&name);
-    if (result != -1)
-    {
-        int  namelen = strlen(name.nodename);
+  size_t name_length = static_cast<size_t>(strlen(name.nodename) + 1);
+  if (name_length > n) {
+    errno = ENAMETOOLONG;
+    return -1;
+  }
 
-        if ((int)buflen < namelen+1) {
-            errno = EINVAL;
-            result = -1;
-        } else {
-            memcpy( buff, name.nodename, namelen+1 );
-        }
-    }
-    return result;
+  memcpy(buf, name.nodename, name_length);
+  return 0;
 }
diff --git a/libc/include/dirent.h b/libc/include/dirent.h
index 4ca3a7d..63716a4 100644
--- a/libc/include/dirent.h
+++ b/libc/include/dirent.h
@@ -76,6 +76,8 @@
 extern int readdir64_r(DIR*, struct dirent64*, struct dirent64**);
 extern int closedir(DIR*);
 extern void rewinddir(DIR*);
+extern void seekdir(DIR*, long);
+extern long telldir(DIR*);
 extern int dirfd(DIR*);
 extern int alphasort(const struct dirent**, const struct dirent**);
 extern int alphasort64(const struct dirent64**, const struct dirent64**);
diff --git a/tests/dirent_test.cpp b/tests/dirent_test.cpp
index 6aadb37..214dd78 100644
--- a/tests/dirent_test.cpp
+++ b/tests/dirent_test.cpp
@@ -231,3 +231,50 @@
     ASSERT_EQ(pass1[i], pass2[i]);
   }
 }
+
+TEST(dirent, seekdir_telldir) {
+  DIR* d = opendir("/proc/self");
+  ASSERT_TRUE(d != NULL);
+  std::vector<long> offset_list;
+  std::vector<std::string> name_list;
+  dirent* e = NULL;
+
+  offset_list.push_back(telldir(d));
+  ASSERT_EQ(0L, offset_list.back());
+
+  while ((e = readdir(d)) != NULL) {
+    name_list.push_back(e->d_name);
+    offset_list.push_back(telldir(d));
+    // Make sure telldir() point to the next entry.
+    ASSERT_EQ(e->d_off, offset_list.back());
+  }
+
+  long end_offset = telldir(d);
+  // telldir() should not pass the end of the file.
+  ASSERT_EQ(offset_list.back(), end_offset);
+  offset_list.pop_back();
+
+  for (size_t i = 0; i < offset_list.size(); ++i) {
+    seekdir(d, offset_list[i]);
+    ASSERT_EQ(offset_list[i], telldir(d));
+    e = readdir(d);
+    ASSERT_TRUE(e != NULL);
+    ASSERT_STREQ(name_list[i].c_str(), e->d_name);
+  }
+  for (int i = static_cast<int>(offset_list.size()) - 1; i >= 0; --i) {
+    seekdir(d, offset_list[i]);
+    ASSERT_EQ(offset_list[i], telldir(d));
+    e = readdir(d);
+    ASSERT_TRUE(e != NULL);
+    ASSERT_STREQ(name_list[i].c_str(), e->d_name);
+  }
+
+  // Seek to the end, read NULL.
+  seekdir(d, end_offset);
+  ASSERT_EQ(end_offset, telldir(d));
+  errno = 0;
+  ASSERT_EQ(NULL, readdir(d));
+  ASSERT_EQ(0, errno);
+
+  ASSERT_EQ(0, closedir(d));
+}
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 549792e..6be372c 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -77,7 +77,7 @@
   int rc = dprintf(tf.fd, "hello\n");
   ASSERT_EQ(rc, 6);
 
-  lseek(tf.fd, SEEK_SET, 0);
+  lseek(tf.fd, 0, SEEK_SET);
   FILE* tfile = fdopen(tf.fd, "r");
   ASSERT_TRUE(tfile != NULL);
 
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 38c8efa..19d4017 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -25,6 +25,7 @@
 #include <stdint.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
+#include <sys/utsname.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
@@ -478,3 +479,26 @@
   // all we can do for sethostname(2).
   ASSERT_EQ(-1, sethostname("", -1));
 }
+
+TEST(unistd, gethostname) {
+  char hostname[HOST_NAME_MAX + 1];
+  memset(hostname, 0, sizeof(hostname));
+
+  // Can we get the hostname with a big buffer?
+  ASSERT_EQ(0, gethostname(hostname, HOST_NAME_MAX));
+
+  // Can we get the hostname with a right-sized buffer?
+  errno = 0;
+  ASSERT_EQ(0, gethostname(hostname, strlen(hostname) + 1));
+
+  // Does uname(2) agree?
+  utsname buf;
+  ASSERT_EQ(0, uname(&buf));
+  ASSERT_EQ(0, strncmp(hostname, buf.nodename, SYS_NMLN));
+  ASSERT_GT(strlen(hostname), 0U);
+
+  // Do we correctly detect truncation?
+  errno = 0;
+  ASSERT_EQ(-1, gethostname(hostname, strlen(hostname)));
+  ASSERT_EQ(ENAMETOOLONG, errno);
+}