Merge "Fix const issues in preparation for libcxx rebase."
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
index ca621fa..04c299c 100644
--- a/base/include/android-base/errors.h
+++ b/base/include/android-base/errors.h
@@ -27,8 +27,8 @@
 // special handling to get the error string. Refer to Microsoft documentation
 // to determine which error code to check for each function.
 
-#ifndef BASE_ERRORS_H
-#define BASE_ERRORS_H
+#ifndef ANDROID_BASE_ERRORS_H
+#define ANDROID_BASE_ERRORS_H
 
 #include <string>
 
@@ -43,4 +43,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_ERRORS_H
+#endif  // ANDROID_BASE_ERRORS_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 5342d98..aa18ea7 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_FILE_H
-#define BASE_FILE_H
+#ifndef ANDROID_BASE_FILE_H
+#define ANDROID_BASE_FILE_H
 
 #include <sys/stat.h>
 #include <string>
@@ -46,4 +46,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_FILE_H
+#endif // ANDROID_BASE_FILE_H
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 41fe4b3..b86c232 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef BASE_LOGGING_H
-#define BASE_LOGGING_H
+
+#ifndef ANDROID_BASE_LOGGING_H
+#define ANDROID_BASE_LOGGING_H
 
 // NOTE: For Windows, you must include logging.h after windows.h to allow the
 // following code to suppress the evil ERROR macro:
@@ -346,4 +347,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_LOGGING_H
+#endif  // ANDROID_BASE_LOGGING_H
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index b1ce7c6..913a9a0 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef UTILS_MACROS_H
-#define UTILS_MACROS_H
+#ifndef ANDROID_BASE_MACROS_H
+#define ANDROID_BASE_MACROS_H
 
 #include <stddef.h>  // for size_t
 #include <unistd.h>  // for TEMP_FAILURE_RETRY
@@ -185,4 +185,4 @@
   } while (0)
 #endif
 
-#endif  // UTILS_MACROS_H
+#endif  // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
index 882582f..3a2f8fa 100644
--- a/base/include/android-base/memory.h
+++ b/base/include/android-base/memory.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_MEMORY_H
-#define BASE_MEMORY_H
+#ifndef ANDROID_BASE_MEMORY_H
+#define ANDROID_BASE_MEMORY_H
 
 namespace android {
 namespace base {
@@ -44,4 +44,4 @@
 } // namespace base
 } // namespace android
 
-#endif // BASE_MEMORY_H
+#endif  // ANDROID_BASE_MEMORY_H
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 0543795..ed75e2d 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_PARSEINT_H
-#define BASE_PARSEINT_H
+#ifndef ANDROID_BASE_PARSEINT_H
+#define ANDROID_BASE_PARSEINT_H
 
 #include <errno.h>
 #include <stdlib.h>
@@ -70,4 +70,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_PARSEINT_H
+#endif  // ANDROID_BASE_PARSEINT_H
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
index 2de5ac9..b4ac025 100644
--- a/base/include/android-base/parsenetaddress.h
+++ b/base/include/android-base/parsenetaddress.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_PARSENETADDRESS_H
-#define BASE_PARSENETADDRESS_H
+#ifndef ANDROID_BASE_PARSENETADDRESS_H
+#define ANDROID_BASE_PARSENETADDRESS_H
 
 #include <string>
 
@@ -35,4 +35,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_PARSENETADDRESS_H
+#endif  // ANDROID_BASE_PARSENETADDRESS_H
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
index d68af87..cf666ab 100644
--- a/base/include/android-base/stringprintf.h
+++ b/base/include/android-base/stringprintf.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_STRINGPRINTF_H
-#define BASE_STRINGPRINTF_H
+#ifndef ANDROID_BASE_STRINGPRINTF_H
+#define ANDROID_BASE_STRINGPRINTF_H
 
 #include <stdarg.h>
 #include <string>
@@ -53,4 +53,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_STRINGPRINTF_H
+#endif  // ANDROID_BASE_STRINGPRINTF_H
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 20da144..69781cd 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_STRINGS_H
-#define BASE_STRINGS_H
+#ifndef ANDROID_BASE_STRINGS_H
+#define ANDROID_BASE_STRINGS_H
 
 #include <sstream>
 #include <string>
@@ -65,4 +65,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_STRINGS_H
+#endif  // ANDROID_BASE_STRINGS_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 3f6872c..4ea3c8e 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef TEST_UTILS_H
-#define TEST_UTILS_H
+#ifndef ANDROID_BASE_TEST_UTILS_H
+#define ANDROID_BASE_TEST_UTILS_H
 
 #include <string>
 
@@ -48,4 +48,4 @@
   DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
 };
 
-#endif // TEST_UTILS_H
+#endif  // ANDROID_BASE_TEST_UTILS_H
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 90979df..2422102 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef UTILS_THREAD_ANNOTATIONS_H
-#define UTILS_THREAD_ANNOTATIONS_H
+#ifndef ANDROID_BASE_THREAD_ANNOTATIONS_H
+#define ANDROID_BASE_THREAD_ANNOTATIONS_H
 
 #if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
 #define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))
@@ -80,4 +80,4 @@
 #define NO_THREAD_SAFETY_ANALYSIS \
       THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
 
-#endif  // UTILS_THREAD_ANNOTATIONS_H
+#endif  // ANDROID_BASE_THREAD_ANNOTATIONS_H
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index d3b27ca..dfaac9d 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -21,18 +21,18 @@
 
 #include <android-base/macros.h>
 
-/* Container for a file descriptor that automatically closes the descriptor as
- * it goes out of scope.
- *
- *      unique_fd ufd(open("/some/path", "r"));
- *
- *      if (ufd.get() < 0) // invalid descriptor
- *          return error;
- *
- *      // Do something useful
- *
- *      return 0; // descriptor is closed here
- */
+// Container for a file descriptor that automatically closes the descriptor as
+// it goes out of scope.
+//
+//      unique_fd ufd(open("/some/path", "r"));
+//      if (ufd.get() == -1) return error;
+//
+//      // Do something useful, possibly including 'return'.
+//
+//      return 0; // Descriptor is closed for you.
+//
+// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
+// you find this class if you're searching for one of those names.
 namespace android {
 namespace base {
 
@@ -44,14 +44,18 @@
   ~unique_fd() { clear(); }
 
   unique_fd(unique_fd&& other) : value_(other.release()) {}
-  unique_fd& operator = (unique_fd&& s) {
+  unique_fd& operator=(unique_fd&& s) {
     reset(s.release());
     return *this;
   }
 
   void reset(int new_value) {
-    if (value_ >= 0)
+    if (value_ != -1) {
+      // Even if close(2) fails with EINTR, the fd will have been closed.
+      // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone else's fd.
+      // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
       close(value_);
+    }
     value_ = new_value;
   }
 
@@ -61,7 +65,7 @@
 
   int get() const { return value_; }
 
-  int release() {
+  int release() __attribute__((warn_unused_result)) {
     int ret = value_;
     value_ = -1;
     return ret;
@@ -76,4 +80,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif // ANDROID_BASE_UNIQUE_FD_H
+#endif  // ANDROID_BASE_UNIQUE_FD_H
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index 3b0ed0a..2d5a6f6 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_UTF8_H
-#define BASE_UTF8_H
+#ifndef ANDROID_BASE_UTF8_H
+#define ANDROID_BASE_UTF8_H
 
 #ifdef _WIN32
 #include <string>
@@ -84,4 +84,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_UTF8_H
+#endif  // ANDROID_BASE_UTF8_H
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 5d1fae9..ef4f68e 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -55,8 +55,11 @@
     return false;
   }
 
-  int32_t value = std::stoi(content);
-  bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+  // Ignore existing bootstat records (which do not contain file content).
+  if (!content.empty()) {
+    int32_t value = std::stoi(content);
+    bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+  }
 
   return true;
 }
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index 4d5deee..a2b8318 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -55,6 +55,7 @@
   FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
   FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);
   FRIEND_TEST(BootEventRecordStoreTest, GetBootEvent);
+  FRIEND_TEST(BootEventRecordStoreTest, GetBootEventNoFileContent);
 
   // Sets the filesystem path of the record store.
   void SetStorePath(const std::string& path);
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 343f9d0..b7dd9ba 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -17,12 +17,16 @@
 #include "boot_event_record_store.h"
 
 #include <dirent.h>
+#include <fcntl.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <unistd.h>
 #include <cstdint>
 #include <cstdlib>
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/test_utils.h>
+#include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
 #include "uptime_parser.h"
@@ -31,6 +35,36 @@
 
 namespace {
 
+// Creates a fake boot event record file at |record_path| containing the boot
+// record |value|. This method is necessary as truncating a
+// BootEventRecordStore-created file would modify the mtime, which would alter
+// the value of the record.
+bool CreateEmptyBootEventRecord(const std::string& record_path, int32_t value) {
+  android::base::unique_fd record_fd(creat(record_path.c_str(), S_IRUSR | S_IWUSR));
+  if (record_fd.get() == -1) {
+    return false;
+  }
+
+  // Writing the value as content in the record file is a debug measure to
+  // ensure the validity of the file mtime value, i.e., to check that the record
+  // file mtime values are not changed once set.
+  // TODO(jhawkins): Remove this block.
+  if (!android::base::WriteStringToFd(std::to_string(value), record_fd.get())) {
+    return false;
+  }
+
+  // Set the |mtime| of the file to store the value of the boot event while
+  // preserving the |atime|.
+  struct timeval atime = {/* tv_sec */ 0, /* tv_usec */ 0};
+  struct timeval mtime = {/* tv_sec */ value, /* tv_usec */ 0};
+  const struct timeval times[] = {atime, mtime};
+  if (utimes(record_path.c_str(), times) != 0) {
+    return false;
+  }
+
+  return true;
+}
+
 // Returns true if the time difference between |a| and |b| is no larger
 // than 10 seconds.  This allow for a relatively large fuzz when comparing
 // two timestamps taken back-to-back.
@@ -178,4 +212,19 @@
 
   // Null |record|.
   EXPECT_DEATH(store.GetBootEvent("carboniferous", nullptr), std::string());
-}
\ No newline at end of file
+}
+
+// Tests that the BootEventRecordStore is capable of handling an older record
+// protocol which does not contain file contents.
+TEST_F(BootEventRecordStoreTest, GetBootEventNoFileContent) {
+  BootEventRecordStore store;
+  store.SetStorePath(GetStorePathForTesting());
+
+  EXPECT_TRUE(CreateEmptyBootEventRecord(store.GetBootEventPath("devonian"), 2718));
+
+  BootEventRecordStore::BootEventRecord record;
+  bool result = store.GetBootEvent("devonian", &record);
+  EXPECT_EQ(true, result);
+  EXPECT_EQ("devonian", record.first);
+  EXPECT_EQ(2718, record.second);
+}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7e60a72..fa8f19a 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -355,8 +355,8 @@
             "                                           formatting.\n"
             "  -s <specific device>                     Specify a device. For USB, provide either\n"
             "                                           a serial number or path to device port.\n"
-            "                                           For ethernet, provide an address in the"
-            "                                           form <protocol>:<hostname>[:port] where"
+            "                                           For ethernet, provide an address in the\n"
+            "                                           form <protocol>:<hostname>[:port] where\n"
             "                                           <protocol> is either tcp or udp.\n"
             "  -p <product>                             Specify product name.\n"
             "  -c <cmdline>                             Override kernel commandline.\n"
diff --git a/init/init.cpp b/init/init.cpp
index 69f0595..9b7d108 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,7 +18,6 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <fstream>
 #include <libgen.h>
 #include <paths.h>
 #include <signal.h>
@@ -290,100 +289,6 @@
     return result;
 }
 
-static void security_failure() {
-    ERROR("Security failure; rebooting into recovery mode...\n");
-    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
-    while (true) { pause(); }  // never reached
-}
-
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
-    std::string path;
-    if (compat) {
-        path = MMAP_RND_COMPAT_PATH;
-    } else {
-        path = MMAP_RND_PATH;
-    }
-    std::ifstream inf(path, std::fstream::in);
-    if (!inf) {
-        return false;
-    }
-    while (start >= min) {
-        // try to write out new value
-        std::string str_val = std::to_string(start);
-        std::ofstream of(path, std::fstream::out);
-        if (!of) {
-            return false;
-        }
-        of << str_val << std::endl;
-        of.close();
-
-        // check to make sure it was recorded
-        inf.seekg(0);
-        std::string str_rec;
-        inf >> str_rec;
-        if (str_val.compare(str_rec) == 0) {
-            break;
-        }
-        start--;
-    }
-    inf.close();
-    return (start >= min);
-}
-
-/*
- * Set /proc/sys/vm/mmap_rnd_bits and potentially
- * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
- * Returns -1 if unable to set these to an acceptable value.  Apply
- * upstream patch-sets https://lkml.org/lkml/2015/12/21/337 and
- * https://lkml.org/lkml/2016/2/4/831 to enable this.
- */
-static int set_mmap_rnd_bits_action(const std::vector<std::string>& args)
-{
-    int ret = -1;
-
-    /* values are arch-dependent */
-#if defined(__aarch64__)
-    /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
-    if (set_mmap_rnd_bits_min(33, 24, false)
-            && set_mmap_rnd_bits_min(16, 16, true)) {
-        ret = 0;
-    }
-#elif defined(__x86_64__)
-    /* x86_64 supports 28 - 32 bits */
-    if (set_mmap_rnd_bits_min(32, 32, false)
-            && set_mmap_rnd_bits_min(16, 16, true)) {
-        ret = 0;
-    }
-#elif defined(__arm__) || defined(__i386__)
-    /* check to see if we're running on 64-bit kernel */
-    bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
-    /* supported 32-bit architecture must have 16 bits set */
-    if (set_mmap_rnd_bits_min(16, 16, h64)) {
-        ret = 0;
-    }
-#elif defined(__mips__) || defined(__mips64__)
-    // TODO: add mips support b/27788820
-    ret = 0;
-#else
-    ERROR("Unknown architecture\n");
-#endif
-
-#ifdef __BRILLO__
-    // TODO: b/27794137
-    ret = 0;
-#endif
-    if (ret == -1) {
-        ERROR("Unable to set adequate mmap entropy value!\n");
-        security_failure();
-    }
-    return ret;
-}
-
 static int keychord_init_action(const std::vector<std::string>& args)
 {
     keychord_init();
@@ -540,6 +445,12 @@
     return 0;
 }
 
+static void security_failure() {
+    ERROR("Security failure; rebooting into recovery mode...\n");
+    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+    while (true) { pause(); }  // never reached
+}
+
 static void selinux_initialize(bool in_kernel_domain) {
     Timer t;
 
@@ -689,7 +600,6 @@
     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
     // ... so that we can start queuing up actions that require stuff from /dev.
     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
-    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
     am.QueueBuiltinAction(keychord_init_action, "keychord_init");
     am.QueueBuiltinAction(console_init_action, "console_init");
 
diff --git a/liblog/logprint.c b/liblog/logprint.c
index d7de864..9b60d4a 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -512,8 +512,18 @@
     }
 
     if (msgStart == -1) {
-        fprintf(stderr, "+++ LOG: malformed log message\n");
-        return -1;
+        /* +++ LOG: malformed log message, DYB */
+        for (i = 1; i < buf->len; i++) {
+            /* odd characters in tag? */
+            if ((msg[i] <= ' ') || (msg[i] == ':') || (msg[i] >= 0x7f)) {
+                msg[i] = '\0';
+                msgStart = i + 1;
+                break;
+            }
+        }
+        if (msgStart == -1) {
+            msgStart = buf->len - 1; /* All tag, no message, print truncates */
+        }
     }
     if (msgEnd == -1) {
         /* incoming message not null-terminated; force it */
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 941b9b9..456f8b3 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1065,8 +1065,10 @@
             fflush(stderr);
             int printLogLine =
                     android_log_printLogLine(logformat, fileno(stderr), &entry);
+            // Legacy tag truncation
             EXPECT_LE(128, printLogLine);
-            EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD, printLogLine);
+            // Measured maximum if we try to print part of the tag as message
+            EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD * 13 / 8, printLogLine);
         }
         android_log_format_free(logformat);
     }
diff --git a/logcat/Android.mk b/logcat/Android.mk
index b828a9f..1dacbe1 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -5,7 +5,7 @@
 
 LOCAL_SRC_FILES:= logcat.cpp event.logtags
 
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils libpcrecpp
 
 LOCAL_MODULE := logcat
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index c148d89..cc3333c 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -37,6 +37,8 @@
 #include <log/logprint.h>
 #include <system/thread_defs.h>
 
+#include <pcrecpp.h>
+
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
 static AndroidLogFormat * g_logformat;
@@ -76,6 +78,10 @@
 static size_t g_outByteCount = 0;
 static int g_printBinary = 0;
 static int g_devCount = 0;                              // >1 means multiple
+static pcrecpp::RE* g_regex;
+// 0 means "infinite"
+static size_t g_maxCount = 0;
+static size_t g_printCount = 0;
 
 __noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
 
@@ -143,6 +149,21 @@
     TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
 }
 
+static bool regexOk(const AndroidLogEntry& entry, log_id_t id)
+{
+    if (! g_regex) {
+        return true;
+    }
+
+    if (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) {
+        return false;
+    }
+
+    std::string messageString(entry.message, entry.messageLen);
+
+    return g_regex->PartialMatch(messageString);
+}
+
 static void processBuffer(log_device_t* dev, struct log_msg *buf)
 {
     int bytesWritten = 0;
@@ -171,9 +192,12 @@
         goto error;
     }
 
-    if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
+    if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority) &&
+        regexOk(entry, buf->id())) {
         bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
 
+        g_printCount++;
+
         if (bytesWritten < 0) {
             logcat_panic(false, "output error");
         }
@@ -271,6 +295,10 @@
                     "  -c              clear (flush) the entire log and exit\n"
                     "  --clear\n"
                     "  -d              dump the log and then exit (don't block)\n"
+                    "  -e <expr>       only print lines where the log message matches <expr>\n"
+                    "  --regex <expr>  where <expr> is a regular expression\n"
+                    "  -m <count>      quit after printing <count> lines. This is meant to be\n"
+                    "  --max-count=<count> paired with --regex, but will work on its own.\n"
                     "  -t <count>      print only the most recent <count> lines (implies -d)\n"
                     "  -t '<time>'     print most recent lines since specified time (implies -d)\n"
                     "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
@@ -521,6 +549,7 @@
     size_t tail_lines = 0;
     log_time tail_time(log_time::EPOCH);
     size_t pid = 0;
+    bool got_t = false;
 
     signal(SIGPIPE, exit);
 
@@ -547,7 +576,9 @@
           { "format",        required_argument, NULL,   'v' },
           { "last",          no_argument,       NULL,   'L' },
           { pid_str,         required_argument, NULL,   0 },
+          { "max-count",     required_argument, NULL,   'm' },
           { "prune",         optional_argument, NULL,   'p' },
+          { "regex",         required_argument, NULL,   'e' },
           { "rotate_count",  required_argument, NULL,   'n' },
           { "rotate_kbytes", required_argument, NULL,   'r' },
           { "statistics",    no_argument,       NULL,   'S' },
@@ -556,7 +587,7 @@
           { NULL,            0,                 NULL,   0 }
         };
 
-        ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:",
+        ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
                           long_options, &option_index);
 
         if (ret < 0) {
@@ -613,6 +644,7 @@
             break;
 
             case 't':
+                got_t = true;
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
                 /* FALLTHRU */
             case 'T':
@@ -644,6 +676,19 @@
                 printDividers = true;
             break;
 
+            case 'e':
+                g_regex = new pcrecpp::RE(optarg);
+            break;
+
+            case 'm': {
+                char *end = NULL;
+                if (!getSizeTArg(optarg, &g_maxCount)) {
+                    logcat_panic(false, "-%c \"%s\" isn't an "
+                                 "integer greater than zero\n", ret, optarg);
+                }
+            }
+            break;
+
             case 'g':
                 if (!optarg) {
                     getLogSize = 1;
@@ -920,6 +965,10 @@
         }
     }
 
+    if (g_maxCount && got_t) {
+        logcat_panic(true, "Cannot use -m (--max-count) and -t together");
+    }
+
     if (!devices) {
         dev = devices = new log_device_t("main", false);
         g_devCount = 1;
@@ -1135,7 +1184,8 @@
 
     dev = NULL;
     log_device_t unexpected("unexpected", false);
-    while (1) {
+
+    while (!g_maxCount || g_printCount < g_maxCount) {
         struct log_msg log_msg;
         log_device_t* d;
         int ret = android_logger_list_read(logger_list, &log_msg);
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 8459bd3..c6eb7f8 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -953,3 +953,67 @@
     free(list);
     list = NULL;
 }
+
+TEST(logcat, regex) {
+    FILE *fp;
+    int count = 0;
+
+    char buffer[5120];
+
+    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid());
+
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_ab"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_b"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaab"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaa"));
+
+    // Let the logs settle
+    sleep(1);
+
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+            continue;
+        }
+
+        EXPECT_TRUE(strstr(buffer, "logcat_test_") != NULL);
+
+        count++;
+    }
+
+    pclose(fp);
+
+    ASSERT_EQ(2, count);
+}
+
+TEST(logcat, maxcount) {
+    FILE *fp;
+    int count = 0;
+
+    char buffer[5120];
+
+    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid());
+
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+
+    // Let the logs settle
+    sleep(1);
+
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+            continue;
+        }
+
+        count++;
+    }
+
+    pclose(fp);
+
+    ASSERT_EQ(3, count);
+}