1 //
2 //  Copyright (c) 2012 Artyom Beilis (Tonkikh)
3 //
4 //  Distributed under the Boost Software License, Version 1.0. (See
5 //  accompanying file LICENSE or copy at
6 //  http://www.boost.org/LICENSE_1_0.txt)
7 //
8 #ifdef _MSC_VER
9 #define _CRT_SECURE_NO_WARNINGS
10 #endif
11 
12 #include <boost/nowide/cstdlib.hpp>
13 
14 #include <boost/nowide/args.hpp>
15 #include <boost/nowide/utf/convert.hpp>
16 #include <boost/nowide/utf/utf.hpp>
17 #include <algorithm>
18 #include <cstdlib>
19 #include <cstring>
20 #include <iostream>
21 #include <string>
22 #include <vector>
23 
24 #include "test.hpp"
25 
is_ascii(const std::string & s)26 bool is_ascii(const std::string& s)
27 {
28     for(std::string::const_iterator it = s.begin(); it != s.end(); ++it)
29     {
30         if(static_cast<unsigned char>(*it) > 0x7F)
31             return false;
32     }
33     return true;
34 }
35 
replace_non_ascii(const std::string & s)36 std::string replace_non_ascii(const std::string& s)
37 {
38     std::string::const_iterator it = s.begin();
39     namespace utf = boost::nowide::utf;
40     using utf8 = utf::utf_traits<char>;
41     std::string result;
42     result.reserve(s.size());
43     while(it != s.end())
44     {
45         utf::code_point c = utf8::decode(it, s.end());
46         TEST(c != utf::illegal && c != utf::incomplete);
47         if(c > 0x7F)
48             c = '?'; // WinAPI seems to do this
49         result.push_back(static_cast<char>(c));
50     }
51     return result;
52 }
53 
compare_string_arrays(char ** main_val,char ** utf8_val,bool sort)54 void compare_string_arrays(char** main_val, char** utf8_val, bool sort)
55 {
56     std::vector<std::string> vec_main, vec_utf8;
57     for(; *main_val; ++main_val)
58         vec_main.push_back(std::string(*main_val));
59     for(; *utf8_val; ++utf8_val)
60         vec_utf8.push_back(std::string(*utf8_val));
61     // Same number of strings
62     TEST_EQ(vec_main.size(), vec_utf8.size());
63     if(sort)
64     {
65         // Order doesn't matter
66         std::sort(vec_main.begin(), vec_main.end());
67         std::sort(vec_utf8.begin(), vec_utf8.end());
68     }
69     for(size_t i = 0; i < vec_main.size(); ++i)
70     {
71         // Skip strings with non-ascii chars
72         if(is_ascii(vec_main[i]) && vec_main[i] != vec_utf8[i])
73             TEST_EQ(vec_main[i], replace_non_ascii(vec_utf8[i]));
74     }
75 }
76 
compare_getenv(char ** env)77 void compare_getenv(char** env)
78 {
79     // For all all variables in env check against getenv
80     for(char** e = env; *e != 0; e++)
81     {
82         const char* key_begin = *e;
83         const char* key_end = strchr(key_begin, '=');
84         TEST(key_end);
85         std::string key = std::string(key_begin, key_end);
86         const char* std_value = std::getenv(key.c_str());
87         const char* bnw_value = boost::nowide::getenv(key.c_str());
88         // If std_value is set, bnw value must be too and be equal, else bnw value must be unset too
89         if(std_value)
90         {
91             TEST(bnw_value);
92             // Compare only if ascii
93             if(is_ascii(std_value) && std::string(std_value) != std::string(bnw_value))
94                 TEST_EQ(std_value, replace_non_ascii(bnw_value));
95         } else
96             TEST(!bnw_value);
97     }
98 }
99 
100 const std::string example = "\xd7\xa9-\xd0\xbc-\xce\xbd";
101 
run_child(int argc,char ** argv,char ** env)102 void run_child(int argc, char** argv, char** env)
103 {
104     // Test arguments
105     TEST(argc == 2);
106     TEST_EQ(argv[1], example);
107     TEST(argv[2] == 0);
108 
109     // Test getenv
110     TEST(boost::nowide::getenv("BOOST_NOWIDE_TEST"));
111     TEST_EQ(boost::nowide::getenv("BOOST_NOWIDE_TEST"), example);
112     TEST(boost::nowide::getenv("BOOST_NOWIDE_TEST_NONE") == 0);
113     // Empty variables are unreliable on windows, hence skip. E.g. using "set FOO=" unsets FOO
114 #ifndef BOOST_WINDOWS
115     TEST(boost::nowide::getenv("BOOST_NOWIDE_EMPTY"));
116     TEST_EQ(boost::nowide::getenv("BOOST_NOWIDE_EMPTY"), std::string());
117 #endif // !_WIN32
118 
119     // This must be contained in env
120     std::string sample = "BOOST_NOWIDE_TEST=" + example;
121     bool found = false;
122     for(char** e = env; *e != 0; e++)
123     {
124         if(*e == sample)
125             found = true;
126     }
127     TEST(found);
128 
129     std::cout << "Subprocess ok" << std::endl;
130 }
131 
run_parent(const char * exe_path)132 void run_parent(const char* exe_path)
133 {
134 #if BOOST_NOWIDE_TEST_USE_NARROW
135     TEST(boost::nowide::setenv("BOOST_NOWIDE_TEST", example.c_str(), 1) == 0);
136     TEST(boost::nowide::setenv("BOOST_NOWIDE_TEST_NONE", example.c_str(), 1) == 0);
137     TEST(boost::nowide::unsetenv("BOOST_NOWIDE_TEST_NONE") == 0);
138     TEST(boost::nowide::setenv("BOOST_NOWIDE_EMPTY", "", 1) == 0);
139     TEST(boost::nowide::getenv("BOOST_NOWIDE_EMPTY"));
140     std::string command = "\"";
141     command += exe_path;
142     command += "\" ";
143     command += example;
144     TEST(boost::nowide::system(command.c_str()) == 0);
145     std::cout << "Parent ok" << std::endl;
146 #else
147     std::wstring envVar = L"BOOST_NOWIDE_TEST=" + boost::nowide::widen(example);
148     TEST(_wputenv(envVar.c_str()) == 0);
149     std::wstring wcommand = boost::nowide::widen(exe_path) + L" " + boost::nowide::widen(example);
150     TEST(_wsystem(wcommand.c_str()) == 0);
151     std::cout << "Wide Parent ok" << std::endl;
152 #endif
153 }
154 
test_main(int argc,char ** argv,char ** env)155 void test_main(int argc, char** argv, char** env)
156 {
157     const int old_argc = argc;
158     char** old_argv = argv;
159     char** old_env = env;
160     {
161         boost::nowide::args _(argc, argv, env);
162         TEST(argc == old_argc);
163         std::cout << "Checking arguments" << std::endl;
164         compare_string_arrays(old_argv, argv, false);
165         std::cout << "Checking env" << std::endl;
166         compare_string_arrays(old_env, env, true);
167         compare_getenv(env);
168     }
169     // When `args` is destructed the old values must be restored
170     TEST(argc == old_argc);
171     TEST(argv == old_argv);
172     TEST(env == old_env);
173 
174     boost::nowide::args a(argc, argv, env);
175     if(argc == 1)
176         run_parent(argv[0]);
177     else
178         run_child(argc, argv, env);
179 }
180