1 // Copyright (c) 2010 The NetBSD Foundation, Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions
6 // are met:
7 // 1. Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // 2. Redistributions in binary form must reproduce the above copyright
10 // notice, this list of conditions and the following disclaimer in the
11 // documentation and/or other materials provided with the distribution.
12 //
13 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26 extern "C" {
27 #include <unistd.h>
28 }
29
30 #include <cerrno>
31 #include <cstdlib>
32 #include <cstring>
33 #include <iostream>
34
35 #include "atf-c++/detail/application.hpp"
36 #include "atf-c++/detail/env.hpp"
37 #include "atf-c++/detail/fs.hpp"
38 #include "atf-c++/detail/sanity.hpp"
39
40 // ------------------------------------------------------------------------
41 // Auxiliary functions.
42 // ------------------------------------------------------------------------
43
44 namespace {
45
46 static
47 std::string
fix_plain_name(const char * filename)48 fix_plain_name(const char *filename)
49 {
50 const atf::fs::path filepath(filename);
51 if (filepath.branch_path().str() == ".")
52 return std::string("./") + filename;
53 else
54 return std::string(filename);
55 }
56
57 static
58 std::string*
construct_script(const char * filename)59 construct_script(const char* filename)
60 {
61 const std::string libexecdir = atf::env::get(
62 "ATF_LIBEXECDIR", ATF_LIBEXECDIR);
63 const std::string pkgdatadir = atf::env::get(
64 "ATF_PKGDATADIR", ATF_PKGDATADIR);
65 const std::string shell = atf::env::get("ATF_SHELL", ATF_SHELL);
66
67 std::string* command = new std::string();
68 command->reserve(512);
69 (*command) += ("Atf_Check='" + libexecdir + "/atf-check' ; " +
70 "Atf_Shell='" + shell + "' ; " +
71 ". " + pkgdatadir + "/libatf-sh.subr ; " +
72 ". " + fix_plain_name(filename) + " ; " +
73 "main \"${@}\"");
74 return command;
75 }
76
77 static
78 const char**
construct_argv(const std::string & shell,const int interpreter_argc,const char * const * interpreter_argv)79 construct_argv(const std::string& shell, const int interpreter_argc,
80 const char* const* interpreter_argv)
81 {
82 PRE(interpreter_argc >= 1);
83 PRE(interpreter_argv[0] != NULL);
84
85 const std::string* script = construct_script(interpreter_argv[0]);
86
87 const int count = 4 + (interpreter_argc - 1) + 1;
88 const char** argv = new const char*[count];
89 argv[0] = shell.c_str();
90 argv[1] = "-c";
91 argv[2] = script->c_str();
92 argv[3] = interpreter_argv[0];
93
94 for (int i = 1; i < interpreter_argc; i++)
95 argv[4 + i - 1] = interpreter_argv[i];
96
97 argv[count - 1] = NULL;
98
99 return argv;
100 }
101
102 } // anonymous namespace
103
104 // ------------------------------------------------------------------------
105 // The "atf_sh" class.
106 // ------------------------------------------------------------------------
107
108 class atf_sh : public atf::application::app {
109 static const char* m_description;
110
111 atf::fs::path m_shell;
112
113 options_set specific_options(void) const;
114 void process_option(int, const char*);
115
116 public:
117 atf_sh(void);
118
119 int main(void);
120 };
121
122 const char* atf_sh::m_description =
123 "atf-sh is a shell interpreter that extends the functionality of the "
124 "system sh(1) with the atf-sh library.";
125
atf_sh(void)126 atf_sh::atf_sh(void) :
127 app(m_description, "atf-sh(1)"),
128 m_shell(atf::fs::path(atf::env::get("ATF_SHELL", ATF_SHELL)))
129 {
130 }
131
132 atf_sh::options_set
specific_options(void) const133 atf_sh::specific_options(void)
134 const
135 {
136 using atf::application::option;
137 options_set opts;
138
139 INV(m_shell == atf::fs::path(atf::env::get("ATF_SHELL", ATF_SHELL)));
140 opts.insert(option('s', "shell", "Path to the shell interpreter to use; "
141 "default: " + m_shell.str()));
142
143 return opts;
144 }
145
146 void
process_option(int ch,const char * arg)147 atf_sh::process_option(int ch, const char* arg)
148 {
149 switch (ch) {
150 case 's':
151 m_shell = atf::fs::path(arg);
152 break;
153
154 default:
155 UNREACHABLE;
156 }
157 }
158
159 int
main(void)160 atf_sh::main(void)
161 {
162 if (m_argc < 1)
163 throw atf::application::usage_error("No test program provided");
164
165 const atf::fs::path script(m_argv[0]);
166 if (!atf::fs::exists(script))
167 throw std::runtime_error("The test program '" + script.str() + "' "
168 "does not exist");
169
170 const char** argv = construct_argv(m_shell.str(), m_argc, m_argv);
171 // Don't bother keeping track of the memory allocated by construct_argv:
172 // we are going to exec or die immediately.
173
174 const int ret = execv(m_shell.c_str(), const_cast< char** >(argv));
175 INV(ret == -1);
176 std::cerr << "Failed to execute " << m_shell.str() << ": "
177 << std::strerror(errno) << "\n";
178 return EXIT_FAILURE;
179 }
180
181 int
main(int argc,char * const * argv)182 main(int argc, char* const* argv)
183 {
184 return atf_sh().run(argc, argv);
185 }
186