xref: /freebsd/contrib/atf/atf-sh/atf-sh.cpp (revision dece5b05f577fc4c6b89af7ad48dfbdf7cc4945c)
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 #include <utility>
35 #include <vector>
36 
37 #include "atf-c++/detail/application.hpp"
38 #include "atf-c++/detail/env.hpp"
39 #include "atf-c++/detail/fs.hpp"
40 #include "atf-c++/detail/sanity.hpp"
41 
42 // ------------------------------------------------------------------------
43 // Auxiliary functions.
44 // ------------------------------------------------------------------------
45 
46 namespace {
47 
48 static
49 std::string
50 fix_plain_name(const char *filename)
51 {
52     const atf::fs::path filepath(filename);
53     if (filepath.branch_path().str() == ".")
54         return std::string("./") + filename;
55     else
56         return std::string(filename);
57 }
58 
59 static
60 std::string
61 construct_script(const char* filename)
62 {
63     const std::string libexecdir = atf::env::get(
64         "ATF_LIBEXECDIR", ATF_LIBEXECDIR);
65     const std::string pkgdatadir = atf::env::get(
66         "ATF_PKGDATADIR", ATF_PKGDATADIR);
67     const std::string shell = atf::env::get("ATF_SHELL", ATF_SHELL);
68 
69     std::string command;
70 
71     command += ("Atf_Check='" + libexecdir + "/atf-check' ; " +
72                 "Atf_Shell='" + shell + "' ; " +
73                 ". " + pkgdatadir + "/libatf-sh.subr ; " +
74                 ". " + fix_plain_name(filename) + " ; " +
75                 "main \"${@}\"");
76     return command;
77 }
78 
79 static
80 std::vector<std::string>
81 construct_argv(const std::string& shell, const int interpreter_argc,
82                const char* const* interpreter_argv)
83 {
84     PRE(interpreter_argc >= 1);
85     PRE(interpreter_argv[0] != NULL);
86 
87     std::string script = construct_script(interpreter_argv[0]);
88 
89     std::vector<std::string> argv;
90 
91     argv.push_back(shell);
92     argv.push_back(std::string("-c"));
93     argv.push_back(std::move(script));
94     argv.push_back(std::string(interpreter_argv[0]));
95 
96     for (int i = 1; i < interpreter_argc; i++)
97         argv.push_back(interpreter_argv[i]);
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 
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
133 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
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
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     std::vector<std::string> argvv = construct_argv(m_shell.str(), m_argc,
171         m_argv);
172     std::vector<const char *> argv;
173 
174     for (std::size_t i = 0; i < argvv.size(); i++)
175         argv.push_back(argvv[i].c_str());
176     argv.push_back(NULL);
177 
178     const int ret = execv(m_shell.c_str(),
179         const_cast<char* const *>(argv.data()));
180     INV(ret == -1);
181 
182     std::cerr << "Failed to execute " << m_shell.str() << ": "
183               << std::strerror(errno) << "\n";
184     return EXIT_FAILURE;
185 }
186 
187 int
188 main(int argc, char* const* argv)
189 {
190     return atf_sh().run(argc, argv);
191 }
192