1273929Sjmmv/* 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
24273929Sjmmv * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  */
25240116Smarcel
26240116Smarcel#if defined(HAVE_CONFIG_H)
27273929Sjmmv#include "config.h"
28240116Smarcel#endif
29240116Smarcel
30240116Smarcel#include <ctype.h>
31240116Smarcel#include <stdarg.h>
32240116Smarcel#include <stdio.h>
33240116Smarcel#include <stdlib.h>
34240116Smarcel#include <string.h>
35240116Smarcel#include <unistd.h>
36240116Smarcel
37273929Sjmmv#include "atf-c/detail/dynstr.h"
38273929Sjmmv#include "atf-c/detail/env.h"
39273929Sjmmv#include "atf-c/detail/fs.h"
40273929Sjmmv#include "atf-c/detail/map.h"
41273929Sjmmv#include "atf-c/detail/sanity.h"
42240116Smarcel#include "atf-c/error.h"
43240116Smarcel#include "atf-c/tc.h"
44240116Smarcel#include "atf-c/tp.h"
45240116Smarcel#include "atf-c/utils.h"
46240116Smarcel
47240116Smarcel#if defined(HAVE_GNU_GETOPT)
48240116Smarcel#   define GETOPT_POSIX "+"
49240116Smarcel#else
50240116Smarcel#   define GETOPT_POSIX ""
51240116Smarcel#endif
52240116Smarcel
53240116Smarcelstatic const char *progname = NULL;
54240116Smarcel
55240116Smarcel/* This prototype is provided by macros.h during instantiation of the test
56240116Smarcel * program, so it can be kept private.  Don't know if that's the best idea
57240116Smarcel * though. */
58240116Smarcelint atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *));
59240116Smarcel
60240116Smarcelenum tc_part {
61240116Smarcel    BODY,
62240116Smarcel    CLEANUP,
63240116Smarcel};
64240116Smarcel
65240116Smarcel/* ---------------------------------------------------------------------
66240116Smarcel * The "usage" and "user" error types.
67240116Smarcel * --------------------------------------------------------------------- */
68240116Smarcel
69240116Smarcel#define FREE_FORM_ERROR(name) \
70240116Smarcel    struct name ## _error_data { \
71240116Smarcel        char m_what[2048]; \
72240116Smarcel    }; \
73240116Smarcel    \
74240116Smarcel    static \
75240116Smarcel    void \
76240116Smarcel    name ## _format(const atf_error_t err, char *buf, size_t buflen) \
77240116Smarcel    { \
78240116Smarcel        const struct name ## _error_data *data; \
79240116Smarcel        \
80240116Smarcel        PRE(atf_error_is(err, #name)); \
81240116Smarcel        \
82240116Smarcel        data = atf_error_data(err); \
83240116Smarcel        snprintf(buf, buflen, "%s", data->m_what); \
84240116Smarcel    } \
85240116Smarcel    \
86240116Smarcel    static \
87240116Smarcel    atf_error_t \
88240116Smarcel    name ## _error(const char *fmt, ...) \
89240116Smarcel    { \
90240116Smarcel        atf_error_t err; \
91240116Smarcel        struct name ## _error_data data; \
92240116Smarcel        va_list ap; \
93240116Smarcel        \
94240116Smarcel        va_start(ap, fmt); \
95240116Smarcel        vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \
96240116Smarcel        va_end(ap); \
97240116Smarcel        \
98240116Smarcel        err = atf_error_new(#name, &data, sizeof(data), name ## _format); \
99240116Smarcel        \
100240116Smarcel        return err; \
101240116Smarcel    }
102240116Smarcel
103240116SmarcelFREE_FORM_ERROR(usage);
104240116SmarcelFREE_FORM_ERROR(user);
105240116Smarcel
106240116Smarcel/* ---------------------------------------------------------------------
107240116Smarcel * Printing functions.
108240116Smarcel * --------------------------------------------------------------------- */
109240116Smarcel
110240116Smarcelstatic
111240116Smarcelvoid
112240116Smarcelprint_error(const atf_error_t err)
113240116Smarcel{
114240116Smarcel    char buf[4096];
115240116Smarcel
116240116Smarcel    PRE(atf_is_error(err));
117240116Smarcel
118240116Smarcel    atf_error_format(err, buf, sizeof(buf));
119240116Smarcel    fprintf(stderr, "%s: ERROR: %s\n", progname, buf);
120240116Smarcel
121240116Smarcel    if (atf_error_is(err, "usage"))
122240116Smarcel        fprintf(stderr, "%s: See atf-test-program(1) for usage details.\n",
123240116Smarcel                progname);
124240116Smarcel}
125240116Smarcel
126240116Smarcelstatic
127240116Smarcelvoid
128240116Smarcelprint_warning(const char *message)
129240116Smarcel{
130240116Smarcel    fprintf(stderr, "%s: WARNING: %s\n", progname, message);
131240116Smarcel}
132240116Smarcel
133240116Smarcel/* ---------------------------------------------------------------------
134240116Smarcel * Options handling.
135240116Smarcel * --------------------------------------------------------------------- */
136240116Smarcel
137240116Smarcelstruct params {
138240116Smarcel    bool m_do_list;
139240116Smarcel    atf_fs_path_t m_srcdir;
140240116Smarcel    char *m_tcname;
141240116Smarcel    enum tc_part m_tcpart;
142240116Smarcel    atf_fs_path_t m_resfile;
143240116Smarcel    atf_map_t m_config;
144240116Smarcel};
145240116Smarcel
146240116Smarcelstatic
147240116Smarcelatf_error_t
148240116Smarcelargv0_to_dir(const char *argv0, atf_fs_path_t *dir)
149240116Smarcel{
150240116Smarcel    atf_error_t err;
151240116Smarcel    atf_fs_path_t temp;
152240116Smarcel
153240116Smarcel    err = atf_fs_path_init_fmt(&temp, "%s", argv0);
154240116Smarcel    if (atf_is_error(err))
155240116Smarcel        goto out;
156240116Smarcel
157240116Smarcel    err = atf_fs_path_branch_path(&temp, dir);
158240116Smarcel
159240116Smarcel    atf_fs_path_fini(&temp);
160240116Smarcelout:
161240116Smarcel    return err;
162240116Smarcel}
163240116Smarcel
164240116Smarcelstatic
165240116Smarcelatf_error_t
166240116Smarcelparams_init(struct params *p, const char *argv0)
167240116Smarcel{
168240116Smarcel    atf_error_t err;
169240116Smarcel
170240116Smarcel    p->m_do_list = false;
171240116Smarcel    p->m_tcname = NULL;
172240116Smarcel    p->m_tcpart = BODY;
173240116Smarcel
174240116Smarcel    err = argv0_to_dir(argv0, &p->m_srcdir);
175240116Smarcel    if (atf_is_error(err))
176240116Smarcel        return err;
177240116Smarcel
178240116Smarcel    err = atf_fs_path_init_fmt(&p->m_resfile, "/dev/stdout");
179240116Smarcel    if (atf_is_error(err)) {
180240116Smarcel        atf_fs_path_fini(&p->m_srcdir);
181240116Smarcel        return err;
182240116Smarcel    }
183240116Smarcel
184240116Smarcel    err = atf_map_init(&p->m_config);
185240116Smarcel    if (atf_is_error(err)) {
186240116Smarcel        atf_fs_path_fini(&p->m_resfile);
187240116Smarcel        atf_fs_path_fini(&p->m_srcdir);
188240116Smarcel        return err;
189240116Smarcel    }
190240116Smarcel
191240116Smarcel    return err;
192240116Smarcel}
193240116Smarcel
194240116Smarcelstatic
195240116Smarcelvoid
196240116Smarcelparams_fini(struct params *p)
197240116Smarcel{
198240116Smarcel    atf_map_fini(&p->m_config);
199240116Smarcel    atf_fs_path_fini(&p->m_resfile);
200240116Smarcel    atf_fs_path_fini(&p->m_srcdir);
201240116Smarcel    if (p->m_tcname != NULL)
202240116Smarcel        free(p->m_tcname);
203240116Smarcel}
204240116Smarcel
205240116Smarcelstatic
206240116Smarcelatf_error_t
207240116Smarcelparse_vflag(char *arg, atf_map_t *config)
208240116Smarcel{
209240116Smarcel    atf_error_t err;
210240116Smarcel    char *split;
211240116Smarcel
212240116Smarcel    split = strchr(arg, '=');
213240116Smarcel    if (split == NULL) {
214240116Smarcel        err = usage_error("-v requires an argument of the form var=value");
215240116Smarcel        goto out;
216240116Smarcel    }
217240116Smarcel
218240116Smarcel    *split = '\0';
219240116Smarcel    split++;
220240116Smarcel
221240116Smarcel    err = atf_map_insert(config, arg, split, false);
222240116Smarcel
223240116Smarcelout:
224240116Smarcel    return err;
225240116Smarcel}
226240116Smarcel
227240116Smarcelstatic
228240116Smarcelatf_error_t
229240116Smarcelreplace_path_param(atf_fs_path_t *param, const char *value)
230240116Smarcel{
231240116Smarcel    atf_error_t err;
232240116Smarcel    atf_fs_path_t temp;
233240116Smarcel
234240116Smarcel    err = atf_fs_path_init_fmt(&temp, "%s", value);
235240116Smarcel    if (!atf_is_error(err)) {
236240116Smarcel        atf_fs_path_fini(param);
237240116Smarcel        *param = temp;
238240116Smarcel    }
239240116Smarcel
240240116Smarcel    return err;
241240116Smarcel}
242240116Smarcel
243240116Smarcel/* ---------------------------------------------------------------------
244240116Smarcel * Test case listing.
245240116Smarcel * --------------------------------------------------------------------- */
246240116Smarcel
247240116Smarcelstatic
248240116Smarcelvoid
249240116Smarcellist_tcs(const atf_tp_t *tp)
250240116Smarcel{
251240116Smarcel    const atf_tc_t *const *tcs;
252240116Smarcel    const atf_tc_t *const *tcsptr;
253240116Smarcel
254240116Smarcel    printf("Content-Type: application/X-atf-tp; version=\"1\"\n\n");
255240116Smarcel
256240116Smarcel    tcs = atf_tp_get_tcs(tp);
257240116Smarcel    INV(tcs != NULL);  /* Should be checked. */
258240116Smarcel    for (tcsptr = tcs; *tcsptr != NULL; tcsptr++) {
259240116Smarcel        const atf_tc_t *tc = *tcsptr;
260240116Smarcel        char **vars = atf_tc_get_md_vars(tc);
261240116Smarcel        char **ptr;
262240116Smarcel
263240116Smarcel        INV(vars != NULL);  /* Should be checked. */
264240116Smarcel
265240116Smarcel        if (tcsptr != tcs)  /* Not first. */
266240116Smarcel            printf("\n");
267240116Smarcel
268240116Smarcel        for (ptr = vars; *ptr != NULL; ptr += 2) {
269240116Smarcel            if (strcmp(*ptr, "ident") == 0) {
270240116Smarcel                printf("ident: %s\n", *(ptr + 1));
271240116Smarcel                break;
272240116Smarcel            }
273240116Smarcel        }
274240116Smarcel
275240116Smarcel        for (ptr = vars; *ptr != NULL; ptr += 2) {
276240116Smarcel            if (strcmp(*ptr, "ident") != 0) {
277240116Smarcel                printf("%s: %s\n", *ptr, *(ptr + 1));
278240116Smarcel            }
279240116Smarcel        }
280240116Smarcel
281240116Smarcel        atf_utils_free_charpp(vars);
282240116Smarcel    }
283240116Smarcel}
284240116Smarcel
285240116Smarcel/* ---------------------------------------------------------------------
286240116Smarcel * Main.
287240116Smarcel * --------------------------------------------------------------------- */
288240116Smarcel
289240116Smarcelstatic
290240116Smarcelatf_error_t
291240116Smarcelhandle_tcarg(const char *tcarg, char **tcname, enum tc_part *tcpart)
292240116Smarcel{
293240116Smarcel    atf_error_t err;
294240116Smarcel
295240116Smarcel    err = atf_no_error();
296240116Smarcel
297240116Smarcel    *tcname = strdup(tcarg);
298240116Smarcel    if (*tcname == NULL) {
299240116Smarcel        err = atf_no_memory_error();
300240116Smarcel        goto out;
301240116Smarcel    }
302240116Smarcel
303240116Smarcel    char *delim = strchr(*tcname, ':');
304240116Smarcel    if (delim != NULL) {
305240116Smarcel        *delim = '\0';
306240116Smarcel
307240116Smarcel        delim++;
308240116Smarcel        if (strcmp(delim, "body") == 0) {
309240116Smarcel            *tcpart = BODY;
310240116Smarcel        } else if (strcmp(delim, "cleanup") == 0) {
311240116Smarcel            *tcpart = CLEANUP;
312240116Smarcel        } else {
313240116Smarcel            err = usage_error("Invalid test case part `%s'", delim);
314240116Smarcel            goto out;
315240116Smarcel        }
316240116Smarcel    }
317240116Smarcel
318240116Smarcelout:
319240116Smarcel    return err;
320240116Smarcel}
321240116Smarcel
322240116Smarcelstatic
323240116Smarcelatf_error_t
324240116Smarcelprocess_params(int argc, char **argv, struct params *p)
325240116Smarcel{
326240116Smarcel    atf_error_t err;
327240116Smarcel    int ch;
328240116Smarcel    int old_opterr;
329240116Smarcel
330240116Smarcel    err = params_init(p, argv[0]);
331240116Smarcel    if (atf_is_error(err))
332240116Smarcel        goto out;
333240116Smarcel
334240116Smarcel    old_opterr = opterr;
335240116Smarcel    opterr = 0;
336240116Smarcel    while (!atf_is_error(err) &&
337240116Smarcel           (ch = getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) {
338240116Smarcel        switch (ch) {
339240116Smarcel        case 'l':
340240116Smarcel            p->m_do_list = true;
341240116Smarcel            break;
342240116Smarcel
343240116Smarcel        case 'r':
344240116Smarcel            err = replace_path_param(&p->m_resfile, optarg);
345240116Smarcel            break;
346240116Smarcel
347240116Smarcel        case 's':
348240116Smarcel            err = replace_path_param(&p->m_srcdir, optarg);
349240116Smarcel            break;
350240116Smarcel
351240116Smarcel        case 'v':
352240116Smarcel            err = parse_vflag(optarg, &p->m_config);
353240116Smarcel            break;
354240116Smarcel
355240116Smarcel        case ':':
356240116Smarcel            err = usage_error("Option -%c requires an argument.", optopt);
357240116Smarcel            break;
358240116Smarcel
359240116Smarcel        case '?':
360240116Smarcel        default:
361240116Smarcel            err = usage_error("Unknown option -%c.", optopt);
362240116Smarcel        }
363240116Smarcel    }
364240116Smarcel    argc -= optind;
365240116Smarcel    argv += optind;
366240116Smarcel
367240116Smarcel    /* Clear getopt state just in case the test wants to use it. */
368240116Smarcel    opterr = old_opterr;
369240116Smarcel    optind = 1;
370240116Smarcel#if defined(HAVE_OPTRESET)
371240116Smarcel    optreset = 1;
372240116Smarcel#endif
373240116Smarcel
374240116Smarcel    if (!atf_is_error(err)) {
375240116Smarcel        if (p->m_do_list) {
376240116Smarcel            if (argc > 0)
377240116Smarcel                err = usage_error("Cannot provide test case names with -l");
378240116Smarcel        } else {
379240116Smarcel            if (argc == 0)
380240116Smarcel                err = usage_error("Must provide a test case name");
381240116Smarcel            else if (argc == 1)
382240116Smarcel                err = handle_tcarg(argv[0], &p->m_tcname, &p->m_tcpart);
383240116Smarcel            else if (argc > 1) {
384240116Smarcel                err = usage_error("Cannot provide more than one test case "
385240116Smarcel                                  "name");
386240116Smarcel            }
387240116Smarcel        }
388240116Smarcel    }
389240116Smarcel
390240116Smarcel    if (atf_is_error(err))
391240116Smarcel        params_fini(p);
392240116Smarcel
393240116Smarcelout:
394240116Smarcel    return err;
395240116Smarcel}
396240116Smarcel
397240116Smarcelstatic
398240116Smarcelatf_error_t
399240116Smarcelsrcdir_strip_libtool(atf_fs_path_t *srcdir)
400240116Smarcel{
401240116Smarcel    atf_error_t err;
402240116Smarcel    atf_fs_path_t parent;
403240116Smarcel
404240116Smarcel    err = atf_fs_path_branch_path(srcdir, &parent);
405240116Smarcel    if (atf_is_error(err))
406240116Smarcel        goto out;
407240116Smarcel
408240116Smarcel    atf_fs_path_fini(srcdir);
409240116Smarcel    *srcdir = parent;
410240116Smarcel
411240116Smarcel    INV(!atf_is_error(err));
412240116Smarcelout:
413240116Smarcel    return err;
414240116Smarcel}
415240116Smarcel
416240116Smarcelstatic
417240116Smarcelatf_error_t
418240116Smarcelhandle_srcdir(struct params *p)
419240116Smarcel{
420240116Smarcel    atf_error_t err;
421240116Smarcel    atf_dynstr_t leafname;
422240116Smarcel    atf_fs_path_t exe, srcdir;
423240116Smarcel    bool b;
424240116Smarcel
425240116Smarcel    err = atf_fs_path_copy(&srcdir, &p->m_srcdir);
426240116Smarcel    if (atf_is_error(err))
427240116Smarcel        goto out;
428240116Smarcel
429240116Smarcel    if (!atf_fs_path_is_absolute(&srcdir)) {
430240116Smarcel        atf_fs_path_t srcdirabs;
431240116Smarcel
432240116Smarcel        err = atf_fs_path_to_absolute(&srcdir, &srcdirabs);
433240116Smarcel        if (atf_is_error(err))
434240116Smarcel            goto out_srcdir;
435240116Smarcel
436240116Smarcel        atf_fs_path_fini(&srcdir);
437240116Smarcel        srcdir = srcdirabs;
438240116Smarcel    }
439240116Smarcel
440240116Smarcel    err = atf_fs_path_leaf_name(&srcdir, &leafname);
441240116Smarcel    if (atf_is_error(err))
442240116Smarcel        goto out_srcdir;
443240116Smarcel    else {
444240116Smarcel        const bool libs = atf_equal_dynstr_cstring(&leafname, ".libs");
445240116Smarcel        atf_dynstr_fini(&leafname);
446240116Smarcel
447240116Smarcel        if (libs) {
448240116Smarcel            err = srcdir_strip_libtool(&srcdir);
449240116Smarcel            if (atf_is_error(err))
450240116Smarcel                goto out;
451240116Smarcel        }
452240116Smarcel    }
453240116Smarcel
454240116Smarcel    err = atf_fs_path_copy(&exe, &srcdir);
455240116Smarcel    if (atf_is_error(err))
456240116Smarcel        goto out_srcdir;
457240116Smarcel
458240116Smarcel    err = atf_fs_path_append_fmt(&exe, "%s", progname);
459240116Smarcel    if (atf_is_error(err))
460240116Smarcel        goto out_exe;
461240116Smarcel
462240116Smarcel    err = atf_fs_exists(&exe, &b);
463240116Smarcel    if (!atf_is_error(err)) {
464240116Smarcel        if (b) {
465240116Smarcel            err = atf_map_insert(&p->m_config, "srcdir",
466240116Smarcel                                 strdup(atf_fs_path_cstring(&srcdir)), true);
467240116Smarcel        } else {
468240116Smarcel            err = user_error("Cannot find the test program in the source "
469240116Smarcel                             "directory `%s'", atf_fs_path_cstring(&srcdir));
470240116Smarcel        }
471240116Smarcel    }
472240116Smarcel
473240116Smarcelout_exe:
474240116Smarcel    atf_fs_path_fini(&exe);
475240116Smarcelout_srcdir:
476240116Smarcel    atf_fs_path_fini(&srcdir);
477240116Smarcelout:
478240116Smarcel    return err;
479240116Smarcel}
480240116Smarcel
481240116Smarcelstatic
482240116Smarcelatf_error_t
483240116Smarcelrun_tc(const atf_tp_t *tp, struct params *p, int *exitcode)
484240116Smarcel{
485240116Smarcel    atf_error_t err;
486240116Smarcel
487240116Smarcel    err = atf_no_error();
488240116Smarcel
489240116Smarcel    if (!atf_tp_has_tc(tp, p->m_tcname)) {
490240116Smarcel        err = usage_error("Unknown test case `%s'", p->m_tcname);
491240116Smarcel        goto out;
492240116Smarcel    }
493240116Smarcel
494240116Smarcel    if (!atf_env_has("__RUNNING_INSIDE_ATF_RUN") || strcmp(atf_env_get(
495240116Smarcel        "__RUNNING_INSIDE_ATF_RUN"), "internal-yes-value") != 0)
496240116Smarcel    {
497273929Sjmmv        print_warning("Running test cases outside of kyua(1) is unsupported");
498240116Smarcel        print_warning("No isolation nor timeout control is being applied; you "
499240116Smarcel                      "may get unexpected failures; see atf-test-case(4)");
500240116Smarcel    }
501240116Smarcel
502240116Smarcel    switch (p->m_tcpart) {
503240116Smarcel    case BODY:
504240116Smarcel        err = atf_tp_run(tp, p->m_tcname, atf_fs_path_cstring(&p->m_resfile));
505240116Smarcel        if (atf_is_error(err)) {
506240116Smarcel            /* TODO: Handle error */
507240116Smarcel            *exitcode = EXIT_FAILURE;
508240116Smarcel            atf_error_free(err);
509240116Smarcel        } else {
510240116Smarcel            *exitcode = EXIT_SUCCESS;
511240116Smarcel        }
512240116Smarcel
513240116Smarcel        break;
514240116Smarcel
515240116Smarcel    case CLEANUP:
516240116Smarcel        err = atf_tp_cleanup(tp, p->m_tcname);
517240116Smarcel        if (atf_is_error(err)) {
518240116Smarcel            /* TODO: Handle error */
519240116Smarcel            *exitcode = EXIT_FAILURE;
520240116Smarcel            atf_error_free(err);
521240116Smarcel        } else {
522240116Smarcel            *exitcode = EXIT_SUCCESS;
523240116Smarcel        }
524240116Smarcel
525240116Smarcel        break;
526240116Smarcel
527240116Smarcel    default:
528240116Smarcel        UNREACHABLE;
529240116Smarcel    }
530240116Smarcel
531240116Smarcel    INV(!atf_is_error(err));
532240116Smarcelout:
533240116Smarcel    return err;
534240116Smarcel}
535240116Smarcel
536240116Smarcelstatic
537240116Smarcelatf_error_t
538240116Smarcelcontrolled_main(int argc, char **argv,
539240116Smarcel                atf_error_t (*add_tcs_hook)(atf_tp_t *),
540240116Smarcel                int *exitcode)
541240116Smarcel{
542240116Smarcel    atf_error_t err;
543240116Smarcel    struct params p;
544240116Smarcel    atf_tp_t tp;
545240116Smarcel    char **raw_config;
546240116Smarcel
547240116Smarcel    err = process_params(argc, argv, &p);
548240116Smarcel    if (atf_is_error(err))
549240116Smarcel        goto out;
550240116Smarcel
551240116Smarcel    err = handle_srcdir(&p);
552240116Smarcel    if (atf_is_error(err))
553240116Smarcel        goto out_p;
554240116Smarcel
555240116Smarcel    raw_config = atf_map_to_charpp(&p.m_config);
556240116Smarcel    if (raw_config == NULL) {
557240116Smarcel        err = atf_no_memory_error();
558240116Smarcel        goto out_p;
559240116Smarcel    }
560240116Smarcel    err = atf_tp_init(&tp, (const char* const*)raw_config);
561240116Smarcel    atf_utils_free_charpp(raw_config);
562240116Smarcel    if (atf_is_error(err))
563240116Smarcel        goto out_p;
564240116Smarcel
565240116Smarcel    err = add_tcs_hook(&tp);
566240116Smarcel    if (atf_is_error(err))
567240116Smarcel        goto out_tp;
568240116Smarcel
569240116Smarcel    if (p.m_do_list) {
570240116Smarcel        list_tcs(&tp);
571240116Smarcel        INV(!atf_is_error(err));
572240116Smarcel        *exitcode = EXIT_SUCCESS;
573240116Smarcel    } else {
574240116Smarcel        err = run_tc(&tp, &p, exitcode);
575240116Smarcel    }
576240116Smarcel
577240116Smarcelout_tp:
578240116Smarcel    atf_tp_fini(&tp);
579240116Smarcelout_p:
580240116Smarcel    params_fini(&p);
581240116Smarcelout:
582240116Smarcel    return err;
583240116Smarcel}
584240116Smarcel
585240116Smarcelint
586240116Smarcelatf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *))
587240116Smarcel{
588240116Smarcel    atf_error_t err;
589240116Smarcel    int exitcode;
590240116Smarcel
591240116Smarcel    progname = strrchr(argv[0], '/');
592240116Smarcel    if (progname == NULL)
593240116Smarcel        progname = argv[0];
594240116Smarcel    else
595240116Smarcel        progname++;
596240116Smarcel
597240116Smarcel    /* Libtool workaround: if running from within the source tree (binaries
598240116Smarcel     * that are not installed yet), skip the "lt-" prefix added to files in
599240116Smarcel     * the ".libs" directory to show the real (not temporary) name. */
600240116Smarcel    if (strncmp(progname, "lt-", 3) == 0)
601240116Smarcel        progname += 3;
602240116Smarcel
603240116Smarcel    exitcode = EXIT_FAILURE; /* Silence GCC warning. */
604240116Smarcel    err = controlled_main(argc, argv, add_tcs_hook, &exitcode);
605240116Smarcel    if (atf_is_error(err)) {
606240116Smarcel        print_error(err);
607240116Smarcel        atf_error_free(err);
608240116Smarcel        exitcode = EXIT_FAILURE;
609240116Smarcel    }
610240116Smarcel
611240116Smarcel    return exitcode;
612240116Smarcel}
613