1240116Smarcel// Copyright (c) 2008 The NetBSD Foundation, Inc.
2240116Smarcel// All rights reserved.
3240116Smarcel//
4240116Smarcel// Redistribution and use in source and binary forms, with or without
5240116Smarcel// modification, are permitted provided that the following conditions
6240116Smarcel// are met:
7240116Smarcel// 1. Redistributions of source code must retain the above copyright
8240116Smarcel//    notice, this list of conditions and the following disclaimer.
9240116Smarcel// 2. Redistributions in binary form must reproduce the above copyright
10240116Smarcel//    notice, this list of conditions and the following disclaimer in the
11240116Smarcel//    documentation and/or other materials provided with the distribution.
12240116Smarcel//
13240116Smarcel// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14240116Smarcel// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15240116Smarcel// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16240116Smarcel// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17240116Smarcel// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18240116Smarcel// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19240116Smarcel// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20240116Smarcel// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21240116Smarcel// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22240116Smarcel// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23240116Smarcel// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24240116Smarcel// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25240116Smarcel
26240116Smarcelextern "C" {
27240116Smarcel#include <sys/types.h>
28240116Smarcel#include <sys/wait.h>
29240116Smarcel
30240116Smarcel#include <limits.h>
31240116Smarcel#include <signal.h>
32240116Smarcel#include <unistd.h>
33240116Smarcel}
34240116Smarcel
35240116Smarcel#include <cerrno>
36240116Smarcel#include <cstdlib>
37240116Smarcel#include <cstring>
38240116Smarcel#include <fstream>
39240116Smarcel#include <ios>
40240116Smarcel#include <iostream>
41240116Smarcel#include <iterator>
42240116Smarcel#include <list>
43240116Smarcel#include <memory>
44240116Smarcel#include <utility>
45240116Smarcel
46240116Smarcel#include "atf-c++/check.hpp"
47240116Smarcel#include "atf-c++/detail/application.hpp"
48258289Sjmmv#include "atf-c++/detail/auto_array.hpp"
49273929Sjmmv#include "atf-c++/detail/env.hpp"
50240116Smarcel#include "atf-c++/detail/exceptions.hpp"
51240116Smarcel#include "atf-c++/detail/fs.hpp"
52240116Smarcel#include "atf-c++/detail/process.hpp"
53240116Smarcel#include "atf-c++/detail/sanity.hpp"
54240116Smarcel#include "atf-c++/detail/text.hpp"
55240116Smarcel
56240116Smarcel// ------------------------------------------------------------------------
57240116Smarcel// Auxiliary functions.
58240116Smarcel// ------------------------------------------------------------------------
59240116Smarcel
60240116Smarcelnamespace {
61240116Smarcel
62240116Smarcelenum status_check_t {
63240116Smarcel    sc_exit,
64240116Smarcel    sc_ignore,
65240116Smarcel    sc_signal,
66240116Smarcel};
67240116Smarcel
68240116Smarcelstruct status_check {
69240116Smarcel    status_check_t type;
70240116Smarcel    bool negated;
71240116Smarcel    int value;
72240116Smarcel
73240116Smarcel    status_check(const status_check_t& p_type, const bool p_negated,
74240116Smarcel                 const int p_value) :
75240116Smarcel        type(p_type),
76240116Smarcel        negated(p_negated),
77240116Smarcel        value(p_value)
78240116Smarcel    {
79240116Smarcel    }
80240116Smarcel};
81240116Smarcel
82240116Smarcelenum output_check_t {
83240116Smarcel    oc_ignore,
84240116Smarcel    oc_inline,
85240116Smarcel    oc_file,
86240116Smarcel    oc_empty,
87240116Smarcel    oc_match,
88240116Smarcel    oc_save
89240116Smarcel};
90240116Smarcel
91240116Smarcelstruct output_check {
92240116Smarcel    output_check_t type;
93240116Smarcel    bool negated;
94240116Smarcel    std::string value;
95240116Smarcel
96240116Smarcel    output_check(const output_check_t& p_type, const bool p_negated,
97240116Smarcel                 const std::string& p_value) :
98240116Smarcel        type(p_type),
99240116Smarcel        negated(p_negated),
100240116Smarcel        value(p_value)
101240116Smarcel    {
102240116Smarcel    }
103240116Smarcel};
104240116Smarcel
105240116Smarcelclass temp_file : public std::ostream {
106240116Smarcel    std::auto_ptr< atf::fs::path > m_path;
107240116Smarcel    int m_fd;
108240116Smarcel
109240116Smarcelpublic:
110273929Sjmmv    temp_file(const char* pattern) :
111240116Smarcel        std::ostream(NULL),
112240116Smarcel        m_fd(-1)
113240116Smarcel    {
114273929Sjmmv        const atf::fs::path file = atf::fs::path(
115273929Sjmmv            atf::env::get("TMPDIR", "/tmp")) / pattern;
116240116Smarcel
117273929Sjmmv        atf::auto_array< char > buf(new char[file.str().length() + 1]);
118273929Sjmmv        std::strcpy(buf.get(), file.c_str());
119273929Sjmmv
120240116Smarcel        m_fd = ::mkstemp(buf.get());
121240116Smarcel        if (m_fd == -1)
122240116Smarcel            throw atf::system_error("atf_check::temp_file::temp_file(" +
123273929Sjmmv                                    file.str() + ")", "mkstemp(3) failed",
124240116Smarcel                                    errno);
125240116Smarcel
126240116Smarcel        m_path.reset(new atf::fs::path(buf.get()));
127240116Smarcel    }
128240116Smarcel
129240116Smarcel    ~temp_file(void)
130240116Smarcel    {
131240116Smarcel        close();
132240116Smarcel        try {
133240116Smarcel            remove(*m_path);
134240116Smarcel        } catch (const atf::system_error&) {
135240116Smarcel            // Ignore deletion errors.
136240116Smarcel        }
137240116Smarcel    }
138240116Smarcel
139240116Smarcel    const atf::fs::path&
140240116Smarcel    get_path(void) const
141240116Smarcel    {
142240116Smarcel        return *m_path;
143240116Smarcel    }
144240116Smarcel
145240116Smarcel    void
146240116Smarcel    write(const std::string& text)
147240116Smarcel    {
148240116Smarcel        if (::write(m_fd, text.c_str(), text.size()) == -1)
149240116Smarcel            throw atf::system_error("atf_check", "write(2) failed", errno);
150240116Smarcel    }
151240116Smarcel
152240116Smarcel    void
153240116Smarcel    close(void)
154240116Smarcel    {
155240116Smarcel        if (m_fd != -1) {
156240116Smarcel            flush();
157240116Smarcel            ::close(m_fd);
158240116Smarcel            m_fd = -1;
159240116Smarcel        }
160240116Smarcel    }
161240116Smarcel};
162240116Smarcel
163240116Smarcel} // anonymous namespace
164240116Smarcel
165240116Smarcelstatic int
166240116Smarcelparse_exit_code(const std::string& str)
167240116Smarcel{
168240116Smarcel    try {
169240116Smarcel        const int value = atf::text::to_type< int >(str);
170240116Smarcel        if (value < 0 || value > 255)
171240116Smarcel            throw std::runtime_error("Unused reason");
172240116Smarcel        return value;
173240116Smarcel    } catch (const std::runtime_error&) {
174240116Smarcel        throw atf::application::usage_error("Invalid exit code for -s option; "
175240116Smarcel            "must be an integer in range 0-255");
176240116Smarcel    }
177240116Smarcel}
178240116Smarcel
179240116Smarcelstatic struct name_number {
180240116Smarcel    const char *name;
181240116Smarcel    int signo;
182240116Smarcel} signal_names_to_numbers[] = {
183240116Smarcel    { "hup", SIGHUP },
184240116Smarcel    { "int", SIGINT },
185240116Smarcel    { "quit", SIGQUIT },
186240116Smarcel    { "trap", SIGTRAP },
187240116Smarcel    { "abrt", SIGABRT },
188240116Smarcel    { "kill", SIGKILL },
189240116Smarcel    { "segv", SIGSEGV },
190240116Smarcel    { "pipe", SIGPIPE },
191240116Smarcel    { "alrm", SIGALRM },
192240116Smarcel    { "term", SIGTERM },
193240116Smarcel    { "usr1", SIGUSR1 },
194240116Smarcel    { "usr2", SIGUSR2 },
195240116Smarcel    { NULL, INT_MIN },
196240116Smarcel};
197240116Smarcel
198240116Smarcelstatic int
199240116Smarcelsignal_name_to_number(const std::string& str)
200240116Smarcel{
201240116Smarcel    struct name_number* iter = signal_names_to_numbers;
202240116Smarcel    int signo = INT_MIN;
203240116Smarcel    while (signo == INT_MIN && iter->name != NULL) {
204240116Smarcel        if (str == iter->name || str == std::string("sig") + iter->name)
205240116Smarcel            signo = iter->signo;
206240116Smarcel        else
207240116Smarcel            iter++;
208240116Smarcel    }
209240116Smarcel    return signo;
210240116Smarcel}
211240116Smarcel
212240116Smarcelstatic int
213240116Smarcelparse_signal(const std::string& str)
214240116Smarcel{
215240116Smarcel    const int signo = signal_name_to_number(str);
216240116Smarcel    if (signo == INT_MIN) {
217240116Smarcel        try {
218240116Smarcel            return atf::text::to_type< int >(str);
219240116Smarcel        } catch (std::runtime_error) {
220240116Smarcel            throw atf::application::usage_error("Invalid signal name or number "
221240116Smarcel                "in -s option");
222240116Smarcel        }
223240116Smarcel    }
224240116Smarcel    INV(signo != INT_MIN);
225240116Smarcel    return signo;
226240116Smarcel}
227240116Smarcel
228240116Smarcelstatic status_check
229240116Smarcelparse_status_check_arg(const std::string& arg)
230240116Smarcel{
231240116Smarcel    const std::string::size_type delimiter = arg.find(':');
232240116Smarcel    bool negated = (arg.compare(0, 4, "not-") == 0);
233240116Smarcel    const std::string action_str = arg.substr(0, delimiter);
234240116Smarcel    const std::string action = negated ? action_str.substr(4) : action_str;
235240116Smarcel    const std::string value_str = (
236240116Smarcel        delimiter == std::string::npos ? "" : arg.substr(delimiter + 1));
237240116Smarcel    int value;
238240116Smarcel
239240116Smarcel    status_check_t type;
240240116Smarcel    if (action == "eq") {
241240116Smarcel        // Deprecated; use exit instead.  TODO: Remove after 0.10.
242240116Smarcel        type = sc_exit;
243240116Smarcel        if (negated)
244240116Smarcel            throw atf::application::usage_error("Cannot negate eq checker");
245240116Smarcel        negated = false;
246240116Smarcel        value = parse_exit_code(value_str);
247240116Smarcel    } else if (action == "exit") {
248240116Smarcel        type = sc_exit;
249240116Smarcel        if (value_str.empty())
250240116Smarcel            value = INT_MIN;
251240116Smarcel        else
252240116Smarcel            value = parse_exit_code(value_str);
253240116Smarcel    } else if (action == "ignore") {
254240116Smarcel        if (negated)
255240116Smarcel            throw atf::application::usage_error("Cannot negate ignore checker");
256240116Smarcel        type = sc_ignore;
257240116Smarcel        value = INT_MIN;
258240116Smarcel    } else if (action == "ne") {
259240116Smarcel        // Deprecated; use not-exit instead.  TODO: Remove after 0.10.
260240116Smarcel        type = sc_exit;
261240116Smarcel        if (negated)
262240116Smarcel            throw atf::application::usage_error("Cannot negate ne checker");
263240116Smarcel        negated = true;
264240116Smarcel        value = parse_exit_code(value_str);
265240116Smarcel    } else if (action == "signal") {
266240116Smarcel        type = sc_signal;
267240116Smarcel        if (value_str.empty())
268240116Smarcel            value = INT_MIN;
269240116Smarcel        else
270240116Smarcel            value = parse_signal(value_str);
271240116Smarcel    } else
272240116Smarcel        throw atf::application::usage_error("Invalid status checker");
273240116Smarcel
274240116Smarcel    return status_check(type, negated, value);
275240116Smarcel}
276240116Smarcel
277240116Smarcelstatic
278240116Smarceloutput_check
279240116Smarcelparse_output_check_arg(const std::string& arg)
280240116Smarcel{
281240116Smarcel    const std::string::size_type delimiter = arg.find(':');
282240116Smarcel    const bool negated = (arg.compare(0, 4, "not-") == 0);
283240116Smarcel    const std::string action_str = arg.substr(0, delimiter);
284240116Smarcel    const std::string action = negated ? action_str.substr(4) : action_str;
285240116Smarcel
286240116Smarcel    output_check_t type;
287240116Smarcel    if (action == "empty")
288240116Smarcel        type = oc_empty;
289240116Smarcel    else if (action == "file")
290240116Smarcel        type = oc_file;
291240116Smarcel    else if (action == "ignore") {
292240116Smarcel        if (negated)
293240116Smarcel            throw atf::application::usage_error("Cannot negate ignore checker");
294240116Smarcel        type = oc_ignore;
295240116Smarcel    } else if (action == "inline")
296240116Smarcel        type = oc_inline;
297240116Smarcel    else if (action == "match")
298240116Smarcel        type = oc_match;
299240116Smarcel    else if (action == "save") {
300240116Smarcel        if (negated)
301240116Smarcel            throw atf::application::usage_error("Cannot negate save checker");
302240116Smarcel        type = oc_save;
303240116Smarcel    } else
304240116Smarcel        throw atf::application::usage_error("Invalid output checker");
305240116Smarcel
306240116Smarcel    return output_check(type, negated, arg.substr(delimiter + 1));
307240116Smarcel}
308240116Smarcel
309240116Smarcelstatic
310240116Smarcelstd::string
311240116Smarcelflatten_argv(char* const* argv)
312240116Smarcel{
313240116Smarcel    std::string cmdline;
314240116Smarcel
315240116Smarcel    char* const* arg = &argv[0];
316240116Smarcel    while (*arg != NULL) {
317240116Smarcel        if (arg != &argv[0])
318240116Smarcel            cmdline += ' ';
319240116Smarcel
320240116Smarcel        cmdline += *arg;
321240116Smarcel
322240116Smarcel        arg++;
323240116Smarcel    }
324240116Smarcel
325240116Smarcel    return cmdline;
326240116Smarcel}
327240116Smarcel
328240116Smarcelstatic
329240116Smarcelstd::auto_ptr< atf::check::check_result >
330240116Smarcelexecute(const char* const* argv)
331240116Smarcel{
332258289Sjmmv    // TODO: This should go to stderr... but fixing it now may be hard as test
333258289Sjmmv    // cases out there might be relying on stderr being silent.
334240116Smarcel    std::cout << "Executing command [ ";
335240116Smarcel    for (int i = 0; argv[i] != NULL; ++i)
336240116Smarcel        std::cout << argv[i] << " ";
337240116Smarcel    std::cout << "]\n";
338258289Sjmmv    std::cout.flush();
339240116Smarcel
340240116Smarcel    atf::process::argv_array argva(argv);
341240116Smarcel    return atf::check::exec(argva);
342240116Smarcel}
343240116Smarcel
344240116Smarcelstatic
345240116Smarcelstd::auto_ptr< atf::check::check_result >
346240116Smarcelexecute_with_shell(char* const* argv)
347240116Smarcel{
348240116Smarcel    const std::string cmd = flatten_argv(argv);
349240116Smarcel
350240116Smarcel    const char* sh_argv[4];
351273929Sjmmv    sh_argv[0] = atf::env::get("ATF_SHELL", ATF_SHELL).c_str();
352240116Smarcel    sh_argv[1] = "-c";
353240116Smarcel    sh_argv[2] = cmd.c_str();
354240116Smarcel    sh_argv[3] = NULL;
355240116Smarcel    return execute(sh_argv);
356240116Smarcel}
357240116Smarcel
358240116Smarcelstatic
359240116Smarcelvoid
360240116Smarcelcat_file(const atf::fs::path& path)
361240116Smarcel{
362240116Smarcel    std::ifstream stream(path.c_str());
363240116Smarcel    if (!stream)
364240116Smarcel        throw std::runtime_error("Failed to open " + path.str());
365240116Smarcel
366240116Smarcel    stream >> std::noskipws;
367240116Smarcel    std::istream_iterator< char > begin(stream), end;
368240116Smarcel    std::ostream_iterator< char > out(std::cerr);
369240116Smarcel    std::copy(begin, end, out);
370240116Smarcel
371240116Smarcel    stream.close();
372240116Smarcel}
373240116Smarcel
374240116Smarcelstatic
375240116Smarcelbool
376240116Smarcelgrep_file(const atf::fs::path& path, const std::string& regexp)
377240116Smarcel{
378240116Smarcel    std::ifstream stream(path.c_str());
379240116Smarcel    if (!stream)
380240116Smarcel        throw std::runtime_error("Failed to open " + path.str());
381240116Smarcel
382240116Smarcel    bool found = false;
383240116Smarcel
384240116Smarcel    std::string line;
385240116Smarcel    while (!found && !std::getline(stream, line).fail()) {
386240116Smarcel        if (atf::text::match(line, regexp))
387240116Smarcel            found = true;
388240116Smarcel    }
389240116Smarcel
390240116Smarcel    stream.close();
391240116Smarcel
392240116Smarcel    return found;
393240116Smarcel}
394240116Smarcel
395240116Smarcelstatic
396240116Smarcelbool
397240116Smarcelfile_empty(const atf::fs::path& p)
398240116Smarcel{
399240116Smarcel    atf::fs::file_info f(p);
400240116Smarcel
401240116Smarcel    return (f.get_size() == 0);
402240116Smarcel}
403240116Smarcel
404240116Smarcelstatic bool
405240116Smarcelcompare_files(const atf::fs::path& p1, const atf::fs::path& p2)
406240116Smarcel{
407240116Smarcel    bool equal = false;
408240116Smarcel
409240116Smarcel    std::ifstream f1(p1.c_str());
410240116Smarcel    if (!f1)
411240116Smarcel        throw std::runtime_error("Failed to open " + p1.str());
412240116Smarcel
413240116Smarcel    std::ifstream f2(p2.c_str());
414240116Smarcel    if (!f2)
415240116Smarcel        throw std::runtime_error("Failed to open " + p1.str());
416240116Smarcel
417240116Smarcel    for (;;) {
418240116Smarcel        char buf1[512], buf2[512];
419240116Smarcel
420240116Smarcel        f1.read(buf1, sizeof(buf1));
421240116Smarcel        if (f1.bad())
422240116Smarcel            throw std::runtime_error("Failed to read from " + p1.str());
423240116Smarcel
424240116Smarcel        f2.read(buf2, sizeof(buf2));
425240116Smarcel        if (f2.bad())
426240116Smarcel            throw std::runtime_error("Failed to read from " + p1.str());
427240116Smarcel
428240116Smarcel        if ((f1.gcount() == 0) && (f2.gcount() == 0)) {
429240116Smarcel            equal = true;
430240116Smarcel            break;
431240116Smarcel        }
432240116Smarcel
433240116Smarcel        if ((f1.gcount() != f2.gcount()) ||
434240116Smarcel            (std::memcmp(buf1, buf2, f1.gcount()) != 0)) {
435240116Smarcel            break;
436240116Smarcel        }
437240116Smarcel    }
438240116Smarcel
439240116Smarcel    return equal;
440240116Smarcel}
441240116Smarcel
442240116Smarcelstatic
443240116Smarcelvoid
444240116Smarcelprint_diff(const atf::fs::path& p1, const atf::fs::path& p2)
445240116Smarcel{
446240116Smarcel    const atf::process::status s =
447240116Smarcel        atf::process::exec(atf::fs::path("diff"),
448240116Smarcel                           atf::process::argv_array("diff", "-u", p1.c_str(),
449240116Smarcel                                                    p2.c_str(), NULL),
450240116Smarcel                           atf::process::stream_connect(STDOUT_FILENO,
451240116Smarcel                                                        STDERR_FILENO),
452240116Smarcel                           atf::process::stream_inherit());
453240116Smarcel
454240116Smarcel    if (!s.exited())
455240116Smarcel        std::cerr << "Failed to run diff(3)\n";
456240116Smarcel
457240116Smarcel    if (s.exitstatus() != 1)
458240116Smarcel        std::cerr << "Error while running diff(3)\n";
459240116Smarcel}
460240116Smarcel
461240116Smarcelstatic
462240116Smarcelstd::string
463240116Smarceldecode(const std::string& s)
464240116Smarcel{
465240116Smarcel    size_t i;
466240116Smarcel    std::string res;
467240116Smarcel
468240116Smarcel    res.reserve(s.length());
469240116Smarcel
470240116Smarcel    i = 0;
471240116Smarcel    while (i < s.length()) {
472240116Smarcel        char c = s[i++];
473240116Smarcel
474240116Smarcel        if (c == '\\') {
475240116Smarcel            switch (s[i++]) {
476240116Smarcel            case 'a': c = '\a'; break;
477240116Smarcel            case 'b': c = '\b'; break;
478240116Smarcel            case 'c': break;
479240116Smarcel            case 'e': c = 033; break;
480240116Smarcel            case 'f': c = '\f'; break;
481240116Smarcel            case 'n': c = '\n'; break;
482240116Smarcel            case 'r': c = '\r'; break;
483240116Smarcel            case 't': c = '\t'; break;
484240116Smarcel            case 'v': c = '\v'; break;
485240116Smarcel            case '\\': break;
486240116Smarcel            case '0':
487240116Smarcel                {
488240116Smarcel                    int count = 3;
489240116Smarcel                    c = 0;
490240116Smarcel                    while (--count >= 0 && (unsigned)(s[i] - '0') < 8)
491240116Smarcel                        c = (c << 3) + (s[i++] - '0');
492240116Smarcel                    break;
493240116Smarcel                }
494240116Smarcel            default:
495240116Smarcel                --i;
496240116Smarcel                break;
497240116Smarcel            }
498240116Smarcel        }
499240116Smarcel
500240116Smarcel        res.push_back(c);
501240116Smarcel    }
502240116Smarcel
503240116Smarcel    return res;
504240116Smarcel}
505240116Smarcel
506240116Smarcelstatic
507240116Smarcelbool
508240116Smarcelrun_status_check(const status_check& sc, const atf::check::check_result& cr)
509240116Smarcel{
510240116Smarcel    bool result;
511240116Smarcel
512240116Smarcel    if (sc.type == sc_exit) {
513240116Smarcel        if (cr.exited() && sc.value != INT_MIN) {
514240116Smarcel            const int status = cr.exitcode();
515240116Smarcel
516240116Smarcel            if (!sc.negated && sc.value != status) {
517240116Smarcel                std::cerr << "Fail: incorrect exit status: "
518240116Smarcel                          << status << ", expected: "
519240116Smarcel                          << sc.value << "\n";
520240116Smarcel                result = false;
521240116Smarcel            } else if (sc.negated && sc.value == status) {
522240116Smarcel                std::cerr << "Fail: incorrect exit status: "
523240116Smarcel                          << status << ", expected: "
524240116Smarcel                          << "anything else\n";
525240116Smarcel                result = false;
526240116Smarcel            } else
527240116Smarcel                result = true;
528240116Smarcel        } else if (cr.exited() && sc.value == INT_MIN) {
529240116Smarcel            result = true;
530240116Smarcel        } else {
531240116Smarcel            std::cerr << "Fail: program did not exit cleanly\n";
532240116Smarcel            result = false;
533240116Smarcel        }
534240116Smarcel    } else if (sc.type == sc_ignore) {
535240116Smarcel        result = true;
536240116Smarcel    } else if (sc.type == sc_signal) {
537240116Smarcel        if (cr.signaled() && sc.value != INT_MIN) {
538240116Smarcel            const int status = cr.termsig();
539240116Smarcel
540240116Smarcel            if (!sc.negated && sc.value != status) {
541240116Smarcel                std::cerr << "Fail: incorrect signal received: "
542240116Smarcel                          << status << ", expected: " << sc.value << "\n";
543240116Smarcel                result = false;
544240116Smarcel            } else if (sc.negated && sc.value == status) {
545240116Smarcel                std::cerr << "Fail: incorrect signal received: "
546240116Smarcel                          << status << ", expected: "
547240116Smarcel                          << "anything else\n";
548240116Smarcel                result = false;
549240116Smarcel            } else
550240116Smarcel                result = true;
551240116Smarcel        } else if (cr.signaled() && sc.value == INT_MIN) {
552240116Smarcel            result = true;
553240116Smarcel        } else {
554240116Smarcel            std::cerr << "Fail: program did not receive a signal\n";
555240116Smarcel            result = false;
556240116Smarcel        }
557240116Smarcel    } else {
558240116Smarcel        UNREACHABLE;
559240116Smarcel        result = false;
560240116Smarcel    }
561240116Smarcel
562240116Smarcel    if (result == false) {
563240116Smarcel        std::cerr << "stdout:\n";
564240116Smarcel        cat_file(atf::fs::path(cr.stdout_path()));
565240116Smarcel        std::cerr << "\n";
566240116Smarcel
567240116Smarcel        std::cerr << "stderr:\n";
568240116Smarcel        cat_file(atf::fs::path(cr.stderr_path()));
569240116Smarcel        std::cerr << "\n";
570240116Smarcel    }
571240116Smarcel
572240116Smarcel    return result;
573240116Smarcel}
574240116Smarcel
575240116Smarcelstatic
576240116Smarcelbool
577240116Smarcelrun_status_checks(const std::vector< status_check >& checks,
578240116Smarcel                  const atf::check::check_result& result)
579240116Smarcel{
580240116Smarcel    bool ok = false;
581240116Smarcel
582240116Smarcel    for (std::vector< status_check >::const_iterator iter = checks.begin();
583240116Smarcel         !ok && iter != checks.end(); iter++) {
584240116Smarcel         ok |= run_status_check(*iter, result);
585240116Smarcel    }
586240116Smarcel
587240116Smarcel    return ok;
588240116Smarcel}
589240116Smarcel
590240116Smarcelstatic
591240116Smarcelbool
592240116Smarcelrun_output_check(const output_check oc, const atf::fs::path& path,
593240116Smarcel                 const std::string& stdxxx)
594240116Smarcel{
595240116Smarcel    bool result;
596240116Smarcel
597240116Smarcel    if (oc.type == oc_empty) {
598240116Smarcel        const bool is_empty = file_empty(path);
599240116Smarcel        if (!oc.negated && !is_empty) {
600240116Smarcel            std::cerr << "Fail: " << stdxxx << " not empty\n";
601240116Smarcel            print_diff(atf::fs::path("/dev/null"), path);
602240116Smarcel            result = false;
603240116Smarcel        } else if (oc.negated && is_empty) {
604240116Smarcel            std::cerr << "Fail: " << stdxxx << " is empty\n";
605240116Smarcel            result = false;
606240116Smarcel        } else
607240116Smarcel            result = true;
608240116Smarcel    } else if (oc.type == oc_file) {
609240116Smarcel        const bool equals = compare_files(path, atf::fs::path(oc.value));
610240116Smarcel        if (!oc.negated && !equals) {
611240116Smarcel            std::cerr << "Fail: " << stdxxx << " does not match golden "
612240116Smarcel                "output\n";
613240116Smarcel            print_diff(atf::fs::path(oc.value), path);
614240116Smarcel            result = false;
615240116Smarcel        } else if (oc.negated && equals) {
616240116Smarcel            std::cerr << "Fail: " << stdxxx << " matches golden output\n";
617240116Smarcel            cat_file(atf::fs::path(oc.value));
618240116Smarcel            result = false;
619240116Smarcel        } else
620240116Smarcel            result = true;
621240116Smarcel    } else if (oc.type == oc_ignore) {
622240116Smarcel        result = true;
623240116Smarcel    } else if (oc.type == oc_inline) {
624273929Sjmmv        temp_file temp("atf-check.XXXXXX");
625240116Smarcel        temp.write(decode(oc.value));
626240116Smarcel        temp.close();
627240116Smarcel
628240116Smarcel        const bool equals = compare_files(path, temp.get_path());
629240116Smarcel        if (!oc.negated && !equals) {
630240116Smarcel            std::cerr << "Fail: " << stdxxx << " does not match expected "
631240116Smarcel                "value\n";
632240116Smarcel            print_diff(temp.get_path(), path);
633240116Smarcel            result = false;
634240116Smarcel        } else if (oc.negated && equals) {
635240116Smarcel            std::cerr << "Fail: " << stdxxx << " matches expected value\n";
636240116Smarcel            cat_file(temp.get_path());
637240116Smarcel            result = false;
638240116Smarcel        } else
639240116Smarcel            result = true;
640240116Smarcel    } else if (oc.type == oc_match) {
641240116Smarcel        const bool matches = grep_file(path, oc.value);
642240116Smarcel        if (!oc.negated && !matches) {
643240116Smarcel            std::cerr << "Fail: regexp " + oc.value + " not in " << stdxxx
644240116Smarcel                      << "\n";
645240116Smarcel            cat_file(path);
646240116Smarcel            result = false;
647240116Smarcel        } else if (oc.negated && matches) {
648240116Smarcel            std::cerr << "Fail: regexp " + oc.value + " is in " << stdxxx
649240116Smarcel                      << "\n";
650240116Smarcel            cat_file(path);
651240116Smarcel            result = false;
652240116Smarcel        } else
653240116Smarcel            result = true;
654240116Smarcel    } else if (oc.type == oc_save) {
655240116Smarcel        INV(!oc.negated);
656240116Smarcel        std::ifstream ifs(path.c_str(), std::fstream::binary);
657240116Smarcel        ifs >> std::noskipws;
658240116Smarcel        std::istream_iterator< char > begin(ifs), end;
659240116Smarcel
660240116Smarcel        std::ofstream ofs(oc.value.c_str(), std::fstream::binary
661240116Smarcel                                     | std::fstream::trunc);
662240116Smarcel        std::ostream_iterator <char> obegin(ofs);
663240116Smarcel
664240116Smarcel        std::copy(begin, end, obegin);
665240116Smarcel        result = true;
666240116Smarcel    } else {
667240116Smarcel        UNREACHABLE;
668240116Smarcel        result = false;
669240116Smarcel    }
670240116Smarcel
671240116Smarcel    return result;
672240116Smarcel}
673240116Smarcel
674240116Smarcelstatic
675240116Smarcelbool
676240116Smarcelrun_output_checks(const std::vector< output_check >& checks,
677240116Smarcel                  const atf::fs::path& path, const std::string& stdxxx)
678240116Smarcel{
679240116Smarcel    bool ok = true;
680240116Smarcel
681240116Smarcel    for (std::vector< output_check >::const_iterator iter = checks.begin();
682240116Smarcel         iter != checks.end(); iter++) {
683240116Smarcel         ok &= run_output_check(*iter, path, stdxxx);
684240116Smarcel    }
685240116Smarcel
686240116Smarcel    return ok;
687240116Smarcel}
688240116Smarcel
689240116Smarcel// ------------------------------------------------------------------------
690240116Smarcel// The "atf_check" application.
691240116Smarcel// ------------------------------------------------------------------------
692240116Smarcel
693240116Smarcelnamespace {
694240116Smarcel
695240116Smarcelclass atf_check : public atf::application::app {
696240116Smarcel    bool m_xflag;
697240116Smarcel
698240116Smarcel    std::vector< status_check > m_status_checks;
699240116Smarcel    std::vector< output_check > m_stdout_checks;
700240116Smarcel    std::vector< output_check > m_stderr_checks;
701240116Smarcel
702240116Smarcel    static const char* m_description;
703240116Smarcel
704240116Smarcel    bool run_output_checks(const atf::check::check_result&,
705240116Smarcel                           const std::string&) const;
706240116Smarcel
707240116Smarcel    std::string specific_args(void) const;
708240116Smarcel    options_set specific_options(void) const;
709240116Smarcel    void process_option(int, const char*);
710240116Smarcel    void process_option_s(const std::string&);
711240116Smarcel
712240116Smarcelpublic:
713240116Smarcel    atf_check(void);
714240116Smarcel    int main(void);
715240116Smarcel};
716240116Smarcel
717240116Smarcel} // anonymous namespace
718240116Smarcel
719240116Smarcelconst char* atf_check::m_description =
720240116Smarcel    "atf-check executes given command and analyzes its results.";
721240116Smarcel
722240116Smarcelatf_check::atf_check(void) :
723261897Sjmmv    app(m_description, "atf-check(1)"),
724240116Smarcel    m_xflag(false)
725240116Smarcel{
726240116Smarcel}
727240116Smarcel
728240116Smarcelbool
729240116Smarcelatf_check::run_output_checks(const atf::check::check_result& r,
730240116Smarcel                             const std::string& stdxxx)
731240116Smarcel    const
732240116Smarcel{
733240116Smarcel    if (stdxxx == "stdout") {
734240116Smarcel        return ::run_output_checks(m_stdout_checks,
735240116Smarcel            atf::fs::path(r.stdout_path()), "stdout");
736240116Smarcel    } else if (stdxxx == "stderr") {
737240116Smarcel        return ::run_output_checks(m_stderr_checks,
738240116Smarcel            atf::fs::path(r.stderr_path()), "stderr");
739240116Smarcel    } else {
740240116Smarcel        UNREACHABLE;
741240116Smarcel        return false;
742240116Smarcel    }
743240116Smarcel}
744240116Smarcel
745240116Smarcelstd::string
746240116Smarcelatf_check::specific_args(void)
747240116Smarcel    const
748240116Smarcel{
749240116Smarcel    return "<command>";
750240116Smarcel}
751240116Smarcel
752240116Smarcelatf_check::options_set
753240116Smarcelatf_check::specific_options(void)
754240116Smarcel    const
755240116Smarcel{
756240116Smarcel    using atf::application::option;
757240116Smarcel    options_set opts;
758240116Smarcel
759240116Smarcel    opts.insert(option('s', "qual:value", "Handle status. Qualifier "
760240116Smarcel                "must be one of: ignore exit:<num> signal:<name|num>"));
761240116Smarcel    opts.insert(option('o', "action:arg", "Handle stdout. Action must be "
762240116Smarcel                "one of: empty ignore file:<path> inline:<val> match:regexp "
763240116Smarcel                "save:<path>"));
764240116Smarcel    opts.insert(option('e', "action:arg", "Handle stderr. Action must be "
765240116Smarcel                "one of: empty ignore file:<path> inline:<val> match:regexp "
766240116Smarcel                "save:<path>"));
767240116Smarcel    opts.insert(option('x', "", "Execute command as a shell command"));
768240116Smarcel
769240116Smarcel    return opts;
770240116Smarcel}
771240116Smarcel
772240116Smarcelvoid
773240116Smarcelatf_check::process_option(int ch, const char* arg)
774240116Smarcel{
775240116Smarcel    switch (ch) {
776240116Smarcel    case 's':
777240116Smarcel        m_status_checks.push_back(parse_status_check_arg(arg));
778240116Smarcel        break;
779240116Smarcel
780240116Smarcel    case 'o':
781240116Smarcel        m_stdout_checks.push_back(parse_output_check_arg(arg));
782240116Smarcel        break;
783240116Smarcel
784240116Smarcel    case 'e':
785240116Smarcel        m_stderr_checks.push_back(parse_output_check_arg(arg));
786240116Smarcel        break;
787240116Smarcel
788240116Smarcel    case 'x':
789240116Smarcel        m_xflag = true;
790240116Smarcel        break;
791240116Smarcel
792240116Smarcel    default:
793240116Smarcel        UNREACHABLE;
794240116Smarcel    }
795240116Smarcel}
796240116Smarcel
797240116Smarcelint
798240116Smarcelatf_check::main(void)
799240116Smarcel{
800240116Smarcel    if (m_argc < 1)
801240116Smarcel        throw atf::application::usage_error("No command specified");
802240116Smarcel
803240116Smarcel    int status = EXIT_FAILURE;
804240116Smarcel
805240116Smarcel    std::auto_ptr< atf::check::check_result > r =
806240116Smarcel        m_xflag ? execute_with_shell(m_argv) : execute(m_argv);
807240116Smarcel
808240116Smarcel    if (m_status_checks.empty())
809240116Smarcel        m_status_checks.push_back(status_check(sc_exit, false, EXIT_SUCCESS));
810240116Smarcel    else if (m_status_checks.size() > 1) {
811240116Smarcel        // TODO: Remove this restriction.
812240116Smarcel        throw atf::application::usage_error("Cannot specify -s more than once");
813240116Smarcel    }
814240116Smarcel
815240116Smarcel    if (m_stdout_checks.empty())
816240116Smarcel        m_stdout_checks.push_back(output_check(oc_empty, false, ""));
817240116Smarcel    if (m_stderr_checks.empty())
818240116Smarcel        m_stderr_checks.push_back(output_check(oc_empty, false, ""));
819240116Smarcel
820240116Smarcel    if ((run_status_checks(m_status_checks, *r) == false) ||
821240116Smarcel        (run_output_checks(*r, "stderr") == false) ||
822240116Smarcel        (run_output_checks(*r, "stdout") == false))
823240116Smarcel        status = EXIT_FAILURE;
824240116Smarcel    else
825240116Smarcel        status = EXIT_SUCCESS;
826240116Smarcel
827240116Smarcel    return status;
828240116Smarcel}
829240116Smarcel
830240116Smarcelint
831240116Smarcelmain(int argc, char* const* argv)
832240116Smarcel{
833240116Smarcel    return atf_check().run(argc, argv);
834240116Smarcel}
835