1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef util_h__
8 #define util_h__
9 
10 #include <cassert>
11 #include <cstdlib>
12 #include <iomanip>
13 #include <iostream>
14 #include <sstream>
15 #include <sys/stat.h>
16 #include <vector>
17 #if defined(_WIN32)
18 #include <windows.h>
19 #include <codecvt>
20 #include <direct.h>
21 #else
22 #include <unistd.h>
23 #endif
24 
25 #include <nspr.h>
26 
hex_string_to_bytes(std::string s)27 static inline std::vector<uint8_t> hex_string_to_bytes(std::string s) {
28   std::vector<uint8_t> bytes;
29   for (size_t i = 0; i < s.length(); i += 2) {
30     bytes.push_back(std::stoul(s.substr(i, 2), nullptr, 16));
31   }
32   return bytes;
33 }
34 
35 // Given a prefix, attempts to create a unique directory that the user can do
36 // work in without impacting other tests. For example, if given the prefix
37 // "scratch", a directory like "scratch05c17b25" will be created in the current
38 // working directory (or the location specified by NSS_GTEST_WORKDIR, if
39 // defined).
40 // Upon destruction, the implementation will attempt to delete the directory.
41 // However, no attempt is made to first remove files in the directory - the
42 // user is responsible for this. If the directory is not empty, deleting it will
43 // fail.
44 // Statistically, it is technically possible to fail to create a unique
45 // directory name, but this is extremely unlikely given the expected workload of
46 // this implementation.
47 class ScopedUniqueDirectory {
48  public:
ScopedUniqueDirectory(const std::string & prefix)49   explicit ScopedUniqueDirectory(const std::string &prefix) {
50     std::string path;
51     const char *workingDirectory = PR_GetEnvSecure("NSS_GTEST_WORKDIR");
52     if (workingDirectory) {
53       path.assign(workingDirectory);
54     }
55     path.append(prefix);
56     for (int i = 0; i < RETRY_LIMIT; i++) {
57       std::string pathCopy(path);
58       // TryMakingDirectory will modify its input. If it fails, we want to throw
59       // away the modified result.
60       if (TryMakingDirectory(pathCopy)) {
61         mPath.assign(pathCopy);
62         break;
63       }
64     }
65     assert(mPath.length() > 0);
66 #if defined(_WIN32)
67     // sqldb always uses UTF-8 regardless of the current system locale.
68     DWORD len =
69         MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), nullptr, 0);
70     std::vector<wchar_t> buf(len, L'\0');
71     MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), buf.data(),
72                         buf.size());
73     std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
74     mUTF8Path = converter.to_bytes(std::wstring(buf.begin(), buf.end()));
75 #else
76     mUTF8Path = mPath;
77 #endif
78   }
79 
80   // NB: the directory must be empty upon destruction
~ScopedUniqueDirectory()81   ~ScopedUniqueDirectory() { assert(rmdir(mPath.c_str()) == 0); }
82 
GetPath()83   const std::string &GetPath() { return mPath; }
GetUTF8Path()84   const std::string &GetUTF8Path() { return mUTF8Path; }
85 
86  private:
87   static const int RETRY_LIMIT = 5;
88 
GenerateRandomName(std::string & prefix)89   static void GenerateRandomName(/*in/out*/ std::string &prefix) {
90     std::stringstream ss;
91     ss << prefix;
92     // RAND_MAX is at least 32767.
93     ss << std::setfill('0') << std::setw(4) << std::hex << rand() << rand();
94     // This will overwrite the value of prefix. This is a little inefficient,
95     // but at least it makes the code simple.
96     ss >> prefix;
97   }
98 
TryMakingDirectory(std::string & prefix)99   static bool TryMakingDirectory(/*in/out*/ std::string &prefix) {
100     GenerateRandomName(prefix);
101 #if defined(_WIN32)
102     return _mkdir(prefix.c_str()) == 0;
103 #else
104     return mkdir(prefix.c_str(), 0777) == 0;
105 #endif
106   }
107 
108   std::string mPath;
109   std::string mUTF8Path;
110 };
111 
112 #endif  // util_h__
113