about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@google.com>2020-05-25T00·19+0100
committerVincent Ambo <tazjin@google.com>2020-05-25T00·19+0100
commit98299da0fda612b42ab933c47f18163cfef5fa71 (patch)
tree78fc0f1127b5f2336d3e370f342f72f38d5a5ee2
parentb371821db59d33851d521d66ba1fb126d388c00f (diff)
refactor(3p/nix/libutil): Replace string2Int & trim functions r/843
Replaces these functions with corresponding functions from Abseil,
namely absl::StripAsciiWhitespace and absl::SimpleAtoi.

In the course of doing this some minor things I encountered along the
way were also refactored.

This also changes the signatures of the various custom readFile
functions to use absl::string_view types.
-rw-r--r--third_party/nix/src/libexpr/attr-path.cc4
-rw-r--r--third_party/nix/src/libexpr/get-drvs.cc3
-rw-r--r--third_party/nix/src/libexpr/names.cc6
-rw-r--r--third_party/nix/src/libmain/shared.hh3
-rw-r--r--third_party/nix/src/libstore/binary-cache-store.cc13
-rw-r--r--third_party/nix/src/libstore/build.cc6
-rw-r--r--third_party/nix/src/libstore/download.cc16
-rw-r--r--third_party/nix/src/libstore/globals.cc3
-rw-r--r--third_party/nix/src/libstore/local-store.cc3
-rw-r--r--third_party/nix/src/libstore/machines.cc5
-rw-r--r--third_party/nix/src/libstore/nar-info.cc6
-rw-r--r--third_party/nix/src/libstore/profiles.cc29
-rw-r--r--third_party/nix/src/libstore/store-api.cc5
-rw-r--r--third_party/nix/src/libutil/args.hh4
-rw-r--r--third_party/nix/src/libutil/config.cc3
-rw-r--r--third_party/nix/src/libutil/meson.build2
-rw-r--r--third_party/nix/src/libutil/util.cc19
-rw-r--r--third_party/nix/src/libutil/util.hh19
-rw-r--r--third_party/nix/src/nix-env/nix-env.cc7
19 files changed, 84 insertions, 72 deletions
diff --git a/third_party/nix/src/libexpr/attr-path.cc b/third_party/nix/src/libexpr/attr-path.cc
index 628b58c1b1e3..5f14fca21493 100644
--- a/third_party/nix/src/libexpr/attr-path.cc
+++ b/third_party/nix/src/libexpr/attr-path.cc
@@ -1,5 +1,7 @@
 #include "attr-path.hh"
 
+#include <absl/strings/numbers.h>
+
 #include "eval-inline.hh"
 #include "util.hh"
 
@@ -50,7 +52,7 @@ Value* findAlongAttrPath(EvalState& state, const std::string& attrPath,
     /* Is i an index (integer) or a normal attribute name? */
     enum { apAttr, apIndex } apType = apAttr;
     unsigned int attrIndex;
-    if (string2Int(attr, attrIndex)) {
+    if (absl::SimpleAtoi(attr, &attrIndex)) {
       apType = apIndex;
     }
 
diff --git a/third_party/nix/src/libexpr/get-drvs.cc b/third_party/nix/src/libexpr/get-drvs.cc
index bc40ec3fef6c..968108e78c51 100644
--- a/third_party/nix/src/libexpr/get-drvs.cc
+++ b/third_party/nix/src/libexpr/get-drvs.cc
@@ -4,6 +4,7 @@
 #include <regex>
 #include <utility>
 
+#include <absl/strings/numbers.h>
 #include <glog/logging.h>
 
 #include "derivations.hh"
@@ -243,7 +244,7 @@ NixInt DrvInfo::queryMetaInt(const std::string& name, NixInt def) {
     /* Backwards compatibility with before we had support for
        integer meta fields. */
     NixInt n;
-    if (string2Int(v->string.s, n)) {
+    if (absl::SimpleAtoi(v->string.s, &n)) {
       return n;
     }
   }
diff --git a/third_party/nix/src/libexpr/names.cc b/third_party/nix/src/libexpr/names.cc
index 20c326776cb7..769f9e99db29 100644
--- a/third_party/nix/src/libexpr/names.cc
+++ b/third_party/nix/src/libexpr/names.cc
@@ -2,6 +2,8 @@
 
 #include <memory>
 
+#include <absl/strings/numbers.h>
+
 #include "util.hh"
 
 namespace nix {
@@ -68,8 +70,8 @@ std::string nextComponent(std::string::const_iterator& p,
 static bool componentsLT(const std::string& c1, const std::string& c2) {
   int n1;
   int n2;
-  bool c1Num = string2Int(c1, n1);
-  bool c2Num = string2Int(c2, n2);
+  bool c1Num = absl::SimpleAtoi(c1, &n1);
+  bool c2Num = absl::SimpleAtoi(c2, &n2);
 
   if (c1Num && c2Num) {
     return n1 < n2;
diff --git a/third_party/nix/src/libmain/shared.hh b/third_party/nix/src/libmain/shared.hh
index edd0a5159a9d..60d7918931d7 100644
--- a/third_party/nix/src/libmain/shared.hh
+++ b/third_party/nix/src/libmain/shared.hh
@@ -2,6 +2,7 @@
 
 #include <locale>
 
+#include <absl/strings/numbers.h>
 #include <signal.h>
 
 #include "args.hh"
@@ -78,7 +79,7 @@ N getIntArg(const std::string& opt, Strings::iterator& i,
     }
   }
   N n;
-  if (!string2Int(s, n))
+  if (!absl::SimpleAtoi(s, &n))
     throw UsageError(format("'%1%' requires an integer argument") % opt);
   return n * multiplier;
 }
diff --git a/third_party/nix/src/libstore/binary-cache-store.cc b/third_party/nix/src/libstore/binary-cache-store.cc
index 37d1c1a440a2..ea36078435ba 100644
--- a/third_party/nix/src/libstore/binary-cache-store.cc
+++ b/third_party/nix/src/libstore/binary-cache-store.cc
@@ -4,6 +4,9 @@
 #include <future>
 #include <memory>
 
+#include <absl/strings/ascii.h>
+#include <absl/strings/numbers.h>
+
 #include "archive.hh"
 #include "compression.hh"
 #include "derivations.hh"
@@ -21,7 +24,8 @@ namespace nix {
 
 BinaryCacheStore::BinaryCacheStore(const Params& params) : Store(params) {
   if (secretKeyFile != "") {
-    secretKey = std::make_unique<SecretKey>(readFile(secretKeyFile));
+    const std::string& secret_key_file = secretKeyFile;
+    secretKey = std::make_unique<SecretKey>(readFile(secret_key_file));
   }
 
   StringSink sink;
@@ -43,7 +47,8 @@ void BinaryCacheStore::init() {
         continue;
       }
       auto name = line.substr(0, colon);
-      auto value = trim(line.substr(colon + 1, std::string::npos));
+      auto value =
+          absl::StripAsciiWhitespace(line.substr(colon + 1, std::string::npos));
       if (name == "StoreDir") {
         if (value != storeDir) {
           throw Error(format("binary cache '%s' is for Nix stores with prefix "
@@ -53,7 +58,9 @@ void BinaryCacheStore::init() {
       } else if (name == "WantMassQuery") {
         wantMassQuery_ = value == "1";
       } else if (name == "Priority") {
-        string2Int(value, priority);
+        if (!absl::SimpleAtoi(value, &priority)) {
+          LOG(WARNING) << "Invalid 'Priority' value: " << value;
+        }
       }
     }
   }
diff --git a/third_party/nix/src/libstore/build.cc b/third_party/nix/src/libstore/build.cc
index 49204a72a8cd..c0fa0074d6ea 100644
--- a/third_party/nix/src/libstore/build.cc
+++ b/third_party/nix/src/libstore/build.cc
@@ -13,6 +13,7 @@
 #include <thread>
 
 #include <absl/strings/ascii.h>
+#include <absl/strings/numbers.h>
 #include <fcntl.h>
 #include <grp.h>
 #include <netdb.h>
@@ -2412,7 +2413,7 @@ void DerivationGoal::startBuilder() {
     userNamespaceSync.readSide = -1;
 
     pid_t tmp;
-    if (!string2Int<pid_t>(readLine(builderOut.readSide.get()), tmp)) {
+    if (!absl::SimpleAtoi(readLine(builderOut.readSide.get()), &tmp)) {
       abort();
     }
     pid = tmp;
@@ -2805,7 +2806,8 @@ void DerivationGoal::runChild() {
     std::string netrcData;
     try {
       if (drv->isBuiltin() && drv->builder == "builtin:fetchurl") {
-        netrcData = readFile(settings.netrcFile);
+        const std::string& netrc_file = settings.netrcFile;
+        netrcData = readFile(netrc_file);
       }
     } catch (SysError&) {
     }
diff --git a/third_party/nix/src/libstore/download.cc b/third_party/nix/src/libstore/download.cc
index a83599715c1e..bebe0d1bf848 100644
--- a/third_party/nix/src/libstore/download.cc
+++ b/third_party/nix/src/libstore/download.cc
@@ -1,6 +1,7 @@
 #include "download.hh"
 
 #include <absl/strings/ascii.h>
+#include <absl/strings/numbers.h>
 
 #include "archive.hh"
 #include "compression.hh"
@@ -174,7 +175,8 @@ struct CurlDownloader : public Downloader {
     size_t headerCallback(void* contents, size_t size, size_t nmemb) {
       size_t realSize = size * nmemb;
       std::string line((char*)contents, realSize);
-      DLOG(INFO) << "got header for '" << request.uri << "': " << trim(line);
+      DLOG(INFO) << "got header for '" << request.uri
+                 << "': " << absl::StripAsciiWhitespace(line);
       if (line.compare(0, 5, "HTTP/") == 0) {  // new response starts
         result.etag = "";
         auto ss = tokenizeString<std::vector<std::string>>(line, " ");
@@ -186,9 +188,10 @@ struct CurlDownloader : public Downloader {
       } else {
         auto i = line.find(':');
         if (i != std::string::npos) {
-          std::string name = toLower(trim(std::string(line, 0, i)));
+          std::string name = absl::AsciiStrToLower(
+              absl::StripAsciiWhitespace(std::string(line, 0, i)));
           if (name == "etag") {
-            result.etag = trim(std::string(line, i + 1));
+            result.etag = absl::StripAsciiWhitespace(std::string(line, i + 1));
             /* Hack to work around a GitHub bug: it sends
                ETags, but ignores If-None-Match. So if we get
                the expected ETag on a 200 response, then shut
@@ -200,9 +203,10 @@ struct CurlDownloader : public Downloader {
               return 0;
             }
           } else if (name == "content-encoding") {
-            encoding = trim(std::string(line, i + 1));
+            encoding = absl::StripAsciiWhitespace(std::string(line, i + 1));
           } else if (name == "accept-ranges" &&
-                     toLower(trim(std::string(line, i + 1))) == "bytes") {
+                     absl::AsciiStrToLower(absl::StripAsciiWhitespace(
+                         std::string(line, i + 1))) == "bytes") {
             acceptRanges = true;
           }
         }
@@ -893,7 +897,7 @@ CachedDownloadResult Downloader::downloadCached(
           tokenizeString<std::vector<std::string>>(readFile(dataFile), "\n");
       if (ss.size() >= 3 && ss[0] == url) {
         time_t lastChecked;
-        if (string2Int(ss[2], lastChecked) &&
+        if (absl::SimpleAtoi(ss[2], &lastChecked) &&
             (uint64_t)lastChecked + request.ttl >= (uint64_t)time(nullptr)) {
           skip = true;
           result.effectiveUri = request.uri;
diff --git a/third_party/nix/src/libstore/globals.cc b/third_party/nix/src/libstore/globals.cc
index 61293dffed50..7307f035275f 100644
--- a/third_party/nix/src/libstore/globals.cc
+++ b/third_party/nix/src/libstore/globals.cc
@@ -4,6 +4,7 @@
 #include <map>
 #include <thread>
 
+#include <absl/strings/numbers.h>
 #include <dlfcn.h>
 
 #include "archive.hh"
@@ -163,7 +164,7 @@ void BaseSetting<SandboxMode>::convertToArg(Args& args,
 void MaxBuildJobsSetting::set(const std::string& str) {
   if (str == "auto") {
     value = std::max(1U, std::thread::hardware_concurrency());
-  } else if (!string2Int(str, value)) {
+  } else if (!absl::SimpleAtoi(str, &value)) {
     throw UsageError(
         "configuration setting '%s' should be 'auto' or an integer", name);
   }
diff --git a/third_party/nix/src/libstore/local-store.cc b/third_party/nix/src/libstore/local-store.cc
index 84055740de2c..faea0fbce56a 100644
--- a/third_party/nix/src/libstore/local-store.cc
+++ b/third_party/nix/src/libstore/local-store.cc
@@ -7,6 +7,7 @@
 #include <ctime>
 #include <iostream>
 
+#include <absl/strings/numbers.h>
 #include <fcntl.h>
 #include <glog/logging.h>
 #include <grp.h>
@@ -295,7 +296,7 @@ int LocalStore::getSchema() {
   int curSchema = 0;
   if (pathExists(schemaPath)) {
     std::string s = readFile(schemaPath);
-    if (!string2Int(s, curSchema)) {
+    if (!absl::SimpleAtoi(s, &curSchema)) {
       throw Error(format("'%1%' is corrupt") % schemaPath);
     }
   }
diff --git a/third_party/nix/src/libstore/machines.cc b/third_party/nix/src/libstore/machines.cc
index 2f1a7289bd84..6472e037d14c 100644
--- a/third_party/nix/src/libstore/machines.cc
+++ b/third_party/nix/src/libstore/machines.cc
@@ -2,6 +2,8 @@
 
 #include <algorithm>
 
+#include <absl/strings/ascii.h>
+#include <absl/strings/string_view.h>
 #include <glog/logging.h>
 
 #include "globals.hh"
@@ -48,14 +50,13 @@ bool Machine::mandatoryMet(const std::set<std::string>& features) const {
 
 void parseMachines(const std::string& s, Machines& machines) {
   for (auto line : tokenizeString<std::vector<std::string>>(s, "\n;")) {
-    trim(line);
     line.erase(std::find(line.begin(), line.end(), '#'), line.end());
     if (line.empty()) {
       continue;
     }
 
     if (line[0] == '@') {
-      auto file = trim(std::string(line, 1));
+      auto file = absl::StripAsciiWhitespace(std::string(line, 1));
       try {
         parseMachines(readFile(file), machines);
       } catch (const SysError& e) {
diff --git a/third_party/nix/src/libstore/nar-info.cc b/third_party/nix/src/libstore/nar-info.cc
index 5a217d1b617d..9f4c53e19b59 100644
--- a/third_party/nix/src/libstore/nar-info.cc
+++ b/third_party/nix/src/libstore/nar-info.cc
@@ -1,5 +1,7 @@
 #include "nar-info.hh"
 
+#include <absl/strings/numbers.h>
+
 #include "globals.hh"
 
 namespace nix {
@@ -47,13 +49,13 @@ NarInfo::NarInfo(const Store& store, const std::string& s,
     } else if (name == "FileHash") {
       fileHash = parseHashField(value);
     } else if (name == "FileSize") {
-      if (!string2Int(value, fileSize)) {
+      if (!absl::SimpleAtoi(value, &fileSize)) {
         corrupt();
       }
     } else if (name == "NarHash") {
       narHash = parseHashField(value);
     } else if (name == "NarSize") {
-      if (!string2Int(value, narSize)) {
+      if (!absl::SimpleAtoi(value, &narSize)) {
         corrupt();
       }
     } else if (name == "References") {
diff --git a/third_party/nix/src/libstore/profiles.cc b/third_party/nix/src/libstore/profiles.cc
index 7c802d754935..3516954b50b7 100644
--- a/third_party/nix/src/libstore/profiles.cc
+++ b/third_party/nix/src/libstore/profiles.cc
@@ -3,6 +3,9 @@
 #include <cerrno>
 #include <cstdio>
 
+#include <absl/strings/numbers.h>
+#include <absl/strings/string_view.h>
+#include <absl/strings/strip.h>
 #include <glog/logging.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -17,22 +20,22 @@ static bool cmpGensByNumber(const Generation& a, const Generation& b) {
   return a.number < b.number;
 }
 
-/* Parse a generation name of the format
-   `<profilename>-<number>-link'. */
-static int parseName(const std::string& profileName, const std::string& name) {
-  if (std::string(name, 0, profileName.size() + 1) != profileName + "-") {
-    return -1;
-  }
-  std::string s = std::string(name, profileName.size() + 1);
-  std::string::size_type p = s.find("-link");
-  if (p == std::string::npos) {
+// Parse a generation out of the format
+// `<profilename>-<generation>-link'.
+static int parseName(absl::string_view profileName, absl::string_view name) {
+  // Consume the `<profilename>-' prefix and and `-link' suffix.
+  if (!(absl::ConsumePrefix(&name, profileName) &&
+        absl::ConsumePrefix(&name, "-") &&
+        absl::ConsumeSuffix(&name, "-link"))) {
     return -1;
   }
+
   int n;
-  if (string2Int(std::string(s, 0, p), n) && n >= 0) {
-    return n;
+  if (!absl::SimpleAtoi(name, &n) && n < 0) {
+    return -1;
   }
-  return -1;
+
+  return n;
 }
 
 Generations findGenerations(const Path& profile, int& curGen) {
@@ -218,7 +221,7 @@ void deleteGenerationsOlderThan(const Path& profile,
   std::string strDays = std::string(timeSpec, 0, timeSpec.size() - 1);
   int days;
 
-  if (!string2Int(strDays, days) || days < 1) {
+  if (!absl::SimpleAtoi(strDays, &days) || days < 1) {
     throw Error(format("invalid number of days specifier '%1%'") % timeSpec);
   }
 
diff --git a/third_party/nix/src/libstore/store-api.cc b/third_party/nix/src/libstore/store-api.cc
index e6891395cdc5..54214e6f4e94 100644
--- a/third_party/nix/src/libstore/store-api.cc
+++ b/third_party/nix/src/libstore/store-api.cc
@@ -3,6 +3,7 @@
 #include <future>
 #include <utility>
 
+#include <absl/strings/numbers.h>
 #include <glog/logging.h>
 
 #include "crypto.hh"
@@ -718,7 +719,7 @@ ValidPathInfo decodeValidPathInfo(std::istream& str, bool hashGiven) {
     getline(str, s);
     info.narHash = Hash(s, htSHA256);
     getline(str, s);
-    if (!string2Int(s, info.narSize)) {
+    if (!absl::SimpleAtoi(s, &info.narSize)) {
       throw Error("number expected");
     }
   }
@@ -726,7 +727,7 @@ ValidPathInfo decodeValidPathInfo(std::istream& str, bool hashGiven) {
   std::string s;
   int n;
   getline(str, s);
-  if (!string2Int(s, n)) {
+  if (!absl::SimpleAtoi(s, &n)) {
     throw Error("number expected");
   }
   while ((n--) != 0) {
diff --git a/third_party/nix/src/libutil/args.hh b/third_party/nix/src/libutil/args.hh
index 20a6379fecba..2c352a04368c 100644
--- a/third_party/nix/src/libutil/args.hh
+++ b/third_party/nix/src/libutil/args.hh
@@ -4,6 +4,8 @@
 #include <map>
 #include <memory>
 
+#include <absl/strings/numbers.h>
+
 #include "util.hh"
 
 namespace nix {
@@ -179,7 +181,7 @@ class Args {
         .arity(1)
         .handler([=](std::vector<std::string> ss) {
           I n;
-          if (!string2Int(ss[0], n))
+          if (!absl::SimpleAtoi(ss[0], &n))
             throw UsageError("flag '--%s' requires a integer argument",
                              longName);
           fun(n);
diff --git a/third_party/nix/src/libutil/config.cc b/third_party/nix/src/libutil/config.cc
index c7c952abf16f..bc81ce77ceb5 100644
--- a/third_party/nix/src/libutil/config.cc
+++ b/third_party/nix/src/libutil/config.cc
@@ -3,6 +3,7 @@
 
 #include <utility>
 
+#include <absl/strings/numbers.h>
 #include <glog/logging.h>
 
 #include "args.hh"
@@ -214,7 +215,7 @@ std::string BaseSetting<std::string>::to_string() {
 template <typename T>
 void BaseSetting<T>::set(const std::string& str) {
   static_assert(std::is_integral<T>::value, "Integer required.");
-  if (!string2Int(str, value)) {
+  if (!absl::SimpleAtoi(str, &value)) {
     throw UsageError("setting '%s' has invalid value '%s'", name, str);
   }
 }
diff --git a/third_party/nix/src/libutil/meson.build b/third_party/nix/src/libutil/meson.build
index a6494e6eac7c..50043a8fad57 100644
--- a/third_party/nix/src/libutil/meson.build
+++ b/third_party/nix/src/libutil/meson.build
@@ -47,7 +47,7 @@ libutil_dep_list = [
   openssl_dep,
   pthread_dep,
   libsodium_dep,
-]
+] + absl_deps
 
 libutil_link_list = []
 libutil_link_args = []
diff --git a/third_party/nix/src/libutil/util.cc b/third_party/nix/src/libutil/util.cc
index 696a8ff2365e..8dca97af98a0 100644
--- a/third_party/nix/src/libutil/util.cc
+++ b/third_party/nix/src/libutil/util.cc
@@ -12,6 +12,7 @@
 #include <thread>
 #include <utility>
 
+#include <absl/strings/string_view.h>
 #include <fcntl.h>
 #include <grp.h>
 #include <pwd.h>
@@ -306,16 +307,17 @@ std::string readFile(int fd) {
   return std::string((char*)buf.data(), st.st_size);
 }
 
-std::string readFile(const Path& path, bool drain) {
-  AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+std::string readFile(absl::string_view path, bool drain) {
+  AutoCloseFD fd = open(std::string(path).c_str(), O_RDONLY | O_CLOEXEC);
   if (!fd) {
     throw SysError(format("opening file '%1%'") % path);
   }
   return drain ? drainFD(fd.get()) : readFile(fd.get());
 }
 
-void readFile(const Path& path, Sink& sink) {
-  AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+void readFile(absl::string_view path, Sink& sink) {
+  // TODO(tazjin): use stdlib functions for this stuff
+  AutoCloseFD fd = open(std::string(path).c_str(), O_RDONLY | O_CLOEXEC);
   if (!fd) {
     throw SysError("opening file '%s'", path);
   }
@@ -1213,15 +1215,6 @@ std::string concatStringsSep(const std::string& sep, const StringSet& ss) {
   return s;
 }
 
-std::string trim(const std::string& s, const std::string& whitespace) {
-  auto i = s.find_first_not_of(whitespace);
-  if (i == std::string::npos) {
-    return "";
-  }
-  auto j = s.find_last_not_of(whitespace);
-  return std::string(s, i, j == std::string::npos ? j : j - i + 1);
-}
-
 std::string replaceStrings(const std::string& s, const std::string& from,
                            const std::string& to) {
   if (from.empty()) {
diff --git a/third_party/nix/src/libutil/util.hh b/third_party/nix/src/libutil/util.hh
index 7d10df50bf5b..0f3752dfde5a 100644
--- a/third_party/nix/src/libutil/util.hh
+++ b/third_party/nix/src/libutil/util.hh
@@ -8,6 +8,7 @@
 #include <optional>
 #include <sstream>
 
+#include <absl/strings/string_view.h>
 #include <dirent.h>
 #include <signal.h>
 #include <sys/stat.h>
@@ -97,8 +98,8 @@ unsigned char getFileType(const Path& path);
 
 /* Read the contents of a file into a string. */
 std::string readFile(int fd);
-std::string readFile(const Path& path, bool drain = false);
-void readFile(const Path& path, Sink& sink);
+std::string readFile(absl::string_view path, bool drain = false);
+void readFile(absl::string_view path, Sink& sink);
 
 /* Write a string to a file. */
 void writeFile(const Path& path, const std::string& s, mode_t mode = 0666);
@@ -325,10 +326,6 @@ C tokenizeString(const std::string& s,
 std::string concatStringsSep(const std::string& sep, const Strings& ss);
 std::string concatStringsSep(const std::string& sep, const StringSet& ss);
 
-/* Remove whitespace from the start and end of a string. */
-std::string trim(const std::string& s,
-                 const std::string& whitespace = " \n\r\t");
-
 /* Replace all occurrences of a string inside another string. */
 std::string replaceStrings(const std::string& s, const std::string& from,
                            const std::string& to);
@@ -339,16 +336,6 @@ std::string statusToString(int status);
 
 bool statusOk(int status);
 
-/* Parse a string into an integer. */
-template <class N>
-bool string2Int(const std::string& s, N& n) {
-  if (std::string(s, 0, 1) == "-" && !std::numeric_limits<N>::is_signed)
-    return false;
-  std::istringstream str(s);
-  str >> n;
-  return str && str.get() == EOF;
-}
-
 /* Parse a string into a float. */
 template <class N>
 bool string2Float(const std::string& s, N& n) {
diff --git a/third_party/nix/src/nix-env/nix-env.cc b/third_party/nix/src/nix-env/nix-env.cc
index e827f31e5df8..d17bde34f3cd 100644
--- a/third_party/nix/src/nix-env/nix-env.cc
+++ b/third_party/nix/src/nix-env/nix-env.cc
@@ -4,6 +4,7 @@
 #include <iostream>
 #include <sstream>
 
+#include <absl/strings/numbers.h>
 #include <glog/logging.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -1303,7 +1304,7 @@ static void opSwitchGeneration(Globals& globals, Strings opFlags,
   }
 
   int dstGen;
-  if (!string2Int(opArgs.front(), dstGen)) {
+  if (!absl::SimpleAtoi(opArgs.front(), &dstGen)) {
     throw UsageError(format("expected a generation number"));
   }
 
@@ -1369,7 +1370,7 @@ static void opDeleteGenerations(Globals& globals, Strings opFlags,
     }
     std::string str_max = std::string(opArgs.front(), 1, opArgs.front().size());
     int max;
-    if (!string2Int(str_max, max) || max == 0) {
+    if (!absl::SimpleAtoi(str_max, &max) || max == 0) {
       throw Error(format("invalid number of generations to keep ‘%1%’") %
                   opArgs.front());
     }
@@ -1378,7 +1379,7 @@ static void opDeleteGenerations(Globals& globals, Strings opFlags,
     std::set<unsigned int> gens;
     for (auto& i : opArgs) {
       unsigned int n;
-      if (!string2Int(i, n)) {
+      if (!absl::SimpleAtoi(i, &n)) {
         throw UsageError(format("invalid generation number '%1%'") % i);
       }
       gens.insert(n);