about summary refs log tree commit diff
path: root/src/libutil
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/hash.cc99
-rw-r--r--src/libutil/monitor-fd.hh1
-rw-r--r--src/libutil/util.cc90
-rw-r--r--src/libutil/util.hh7
4 files changed, 117 insertions, 80 deletions
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 965f3ed47701..a83ba0a81817 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -84,7 +84,7 @@ string printHash(const Hash & hash)
     return string(buf, hash.hashSize * 2);
 }
 
-    
+
 Hash parseHash(HashType ht, const string & s)
 {
     Hash hash(ht);
@@ -92,7 +92,7 @@ Hash parseHash(HashType ht, const string & s)
         throw Error(format("invalid hash ‘%1%’") % s);
     for (unsigned int i = 0; i < hash.hashSize; i++) {
         string s2(s, i * 2, 2);
-        if (!isxdigit(s2[0]) || !isxdigit(s2[1])) 
+        if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
             throw Error(format("invalid hash ‘%1%’") % s);
         std::istringstream str(s2);
         int n;
@@ -103,24 +103,6 @@ Hash parseHash(HashType ht, const string & s)
 }
 
 
-static unsigned char divMod(unsigned char * bytes, unsigned char y)
-{
-    unsigned int borrow = 0;
-
-    int pos = Hash::maxHashSize - 1;
-    while (pos >= 0 && !bytes[pos]) --pos;
-
-    for ( ; pos >= 0; --pos) {
-        unsigned int s = bytes[pos] + (borrow << 8);
-        unsigned int d = s / y;
-        borrow = s % y;
-        bytes[pos] = d;
-    }
-
-    return borrow;
-}
-
-
 unsigned int hashLength32(const Hash & hash)
 {
     return (hash.hashSize * 8 - 1) / 5 + 1;
@@ -136,19 +118,19 @@ string printHash32(const Hash & hash)
     Hash hash2(hash);
     unsigned int len = hashLength32(hash);
 
-    const char * chars = base32Chars.data();
-    
-    string s(len, '0');
-
-    int pos = len - 1;
-    while (pos >= 0) {
-        unsigned char digit = divMod(hash2.hash, 32);
-        s[pos--] = chars[digit];
+    string s;
+    s.reserve(len);
+
+    for (int n = len - 1; n >= 0; n--) {
+        unsigned int b = n * 5;
+        unsigned int i = b / 8;
+        unsigned int j = b % 8;
+        unsigned char c =
+            (hash.hash[i] >> j)
+            | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
+        s.push_back(base32Chars[c & 0x1f]);
     }
 
-    for (unsigned int i = 0; i < hash2.maxHashSize; ++i)
-        assert(hash2.hash[i] == 0);
-
     return s;
 }
 
@@ -159,51 +141,24 @@ string printHash16or32(const Hash & hash)
 }
 
 
-static bool mul(unsigned char * bytes, unsigned char y, int maxSize)
-{
-    unsigned char carry = 0;
-
-    for (int pos = 0; pos < maxSize; ++pos) {
-        unsigned int m = bytes[pos] * y + carry;
-        bytes[pos] = m & 0xff;
-        carry = m >> 8;
-    }
-
-    return carry;
-}
-
-
-static bool add(unsigned char * bytes, unsigned char y, int maxSize)
-{
-    unsigned char carry = y;
-
-    for (int pos = 0; pos < maxSize; ++pos) {
-        unsigned int m = bytes[pos] + carry;
-        bytes[pos] = m & 0xff;
-        carry = m >> 8;
-        if (carry == 0) break;
-    }
-
-    return carry;
-}
-
-
 Hash parseHash32(HashType ht, const string & s)
 {
     Hash hash(ht);
+    unsigned int len = hashLength32(ht);
+    assert(s.size() == len);
 
-    const char * chars = base32Chars.data();
-
-    for (unsigned int i = 0; i < s.length(); ++i) {
-        char c = s[i];
+    for (unsigned int n = 0; n < len; ++n) {
+        char c = s[len - n - 1];
         unsigned char digit;
         for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
-            if (chars[digit] == c) break;
+            if (base32Chars[digit] == c) break;
         if (digit >= 32)
             throw Error(format("invalid base-32 hash ‘%1%’") % s);
-        if (mul(hash.hash, 32, hash.hashSize) ||
-            add(hash.hash, digit, hash.hashSize))
-            throw Error(format("base-32 hash ‘%1%’ is too large") % s);
+        unsigned int b = n * 5;
+        unsigned int i = b / 8;
+        unsigned int j = b % 8;
+        hash.hash[i] |= digit << j;
+        if (i < hash.hashSize - 1) hash.hash[i + 1] |= digit >> (8 - j);
     }
 
     return hash;
@@ -299,7 +254,7 @@ Hash hashFile(HashType ht, const Path & path)
         if (n == -1) throw SysError(format("reading file ‘%1%’") % path);
         update(ht, ctx, buf, n);
     }
-    
+
     finish(ht, ctx, hash.hash);
     return hash;
 }
@@ -311,7 +266,7 @@ HashSink::HashSink(HashType ht) : ht(ht)
     bytes = 0;
     start(ht, *ctx);
 }
-    
+
 HashSink::~HashSink()
 {
     bufPos = 0;
@@ -369,7 +324,7 @@ HashType parseHashType(const string & s)
     else return htUnknown;
 }
 
- 
+
 string printHashType(HashType ht)
 {
     if (ht == htMD5) return "md5";
@@ -378,5 +333,5 @@ string printHashType(HashType ht)
     else throw Error("cannot print unknown hash type");
 }
 
- 
+
 }
diff --git a/src/libutil/monitor-fd.hh b/src/libutil/monitor-fd.hh
index 72d23fb6934c..6f01ccd91a43 100644
--- a/src/libutil/monitor-fd.hh
+++ b/src/libutil/monitor-fd.hh
@@ -3,6 +3,7 @@
 #include <thread>
 #include <atomic>
 
+#include <cstdlib>
 #include <poll.h>
 #include <sys/types.h>
 #include <unistd.h>
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index dcdb438e03b2..be0a9bf317d1 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -193,8 +193,12 @@ Path readLink(const Path & path)
     if (!S_ISLNK(st.st_mode))
         throw Error(format("‘%1%’ is not a symlink") % path);
     char buf[st.st_size];
-    if (readlink(path.c_str(), buf, st.st_size) != st.st_size)
+    ssize_t rlsize = readlink(path.c_str(), buf, st.st_size);
+    if (rlsize == -1)
         throw SysError(format("reading symbolic link ‘%1%’") % path);
+    else if (rlsize > st.st_size)
+        throw Error(format("symbolic link ‘%1%’ size overflow %2% > %3%")
+            % path % rlsize % st.st_size);
     return string(buf, st.st_size);
 }
 
@@ -921,18 +925,24 @@ std::vector<const char *> stringsToCharPtrs(const Strings & ss)
 }
 
 
-string runProgram(Path program, bool searchPath, const Strings & args)
+string runProgram(Path program, bool searchPath, const Strings & args,
+    const string & input)
 {
     checkInterrupt();
 
     /* Create a pipe. */
-    Pipe pipe;
-    pipe.create();
+    Pipe stdout, stdin;
+    stdout.create();
+    if (!input.empty()) stdin.create();
 
     /* Fork. */
     Pid pid = startProcess([&]() {
-        if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
+        if (dup2(stdout.writeSide, STDOUT_FILENO) == -1)
             throw SysError("dupping stdout");
+        if (!input.empty()) {
+            if (dup2(stdin.readSide, STDIN_FILENO) == -1)
+                throw SysError("dupping stdin");
+        }
 
         Strings args_(args);
         args_.push_front(program);
@@ -946,9 +956,16 @@ string runProgram(Path program, bool searchPath, const Strings & args)
         throw SysError(format("executing ‘%1%’") % program);
     });
 
-    pipe.writeSide.close();
+    stdout.writeSide.close();
+
+    /* FIXME: This can deadlock if the input is too long. */
+    if (!input.empty()) {
+        stdin.readSide.close();
+        writeFull(stdin.writeSide, input);
+        stdin.writeSide.close();
+    }
 
-    string result = drainFD(pipe.readSide);
+    string result = drainFD(stdout.readSide);
 
     /* Wait for the child to finish. */
     int status = pid.wait(true);
@@ -1191,4 +1208,63 @@ string filterANSIEscapes(const string & s, bool nixOnly)
 }
 
 
+static char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+
+string base64Encode(const string & s)
+{
+    string res;
+    int data = 0, nbits = 0;
+
+    for (char c : s) {
+        data = data << 8 | (unsigned char) c;
+        nbits += 8;
+        while (nbits >= 6) {
+            nbits -= 6;
+            res.push_back(base64Chars[data >> nbits & 0x3f]);
+        }
+    }
+
+    if (nbits) res.push_back(base64Chars[data << (6 - nbits) & 0x3f]);
+    while (res.size() % 4) res.push_back('=');
+
+    return res;
+}
+
+
+string base64Decode(const string & s)
+{
+    bool init = false;
+    char decode[256];
+    if (!init) {
+        // FIXME: not thread-safe.
+        memset(decode, -1, sizeof(decode));
+        for (int i = 0; i < 64; i++)
+            decode[(int) base64Chars[i]] = i;
+        init = true;
+    }
+
+    string res;
+    unsigned int d = 0, bits = 0;
+
+    for (char c : s) {
+        if (c == '=') break;
+        if (c == '\n') continue;
+
+        char digit = decode[(unsigned char) c];
+        if (digit == -1)
+            throw Error("invalid character in Base64 string");
+
+        bits += 6;
+        d = d << 6 | digit;
+        if (bits >= 8) {
+            res.push_back(d >> (bits - 8) & 0xff);
+            bits -= 8;
+        }
+    }
+
+    return res;
+}
+
+
 }
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 186ee71f45d0..20330fb7699e 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -285,7 +285,7 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = P
 /* Run a program and return its stdout in a string (i.e., like the
    shell backtick operator). */
 string runProgram(Path program, bool searchPath = false,
-    const Strings & args = Strings());
+    const Strings & args = Strings(), const string & input = "");
 
 MakeError(ExecError, Error)
 
@@ -398,4 +398,9 @@ void ignoreException();
 string filterANSIEscapes(const string & s, bool nixOnly = false);
 
 
+/* Base64 encoding/decoding. */
+string base64Encode(const string & s);
+string base64Decode(const string & s);
+
+
 }