kulp / tenyr
1
#include "os_common.h"
2

3
#include "asm.h"
4
#include "asmif.h"
5
#include "common.h"
6
#include "parser.h"
7
#include "parser_global.h"
8
#include "lexer.h"
9
#include "param.h"
10

11
#include <stdlib.h>
12
#include <stdio.h>
13
#include <getopt.h>
14
#include <search.h>
15
#include <string.h>
16
#include <strings.h>
17
#include <stdbool.h>
18

19
static const char shortopts[] = "df:o:p:qsv" "hV";
20

21
static const struct option longopts[] = {
22
    { "disassemble" ,       no_argument, NULL, 'd' },
23
    { "format"      , required_argument, NULL, 'f' },
24
    { "output"      , required_argument, NULL, 'o' },
25
    { "param"       , required_argument, NULL, 'p' },
26
    { "quiet"       ,       no_argument, NULL, 'q' },
27
    { "verbose"     ,       no_argument, NULL, 'v' },
28

29
    { "help"        ,       no_argument, NULL, 'h' },
30
    { "version"     ,       no_argument, NULL, 'V' },
31

32
    { NULL, 0, NULL, 0 },
33
};
34

35 1
static const char *version(void)
36
{
37 1
    return "tas version " STR(BUILD_NAME) " built " __DATE__;
38
}
39

40 1
static int format_has_output(const struct format *f)
41
{
42 1
    return !!f->out;
43
}
44

45 1
static void usage(const char *me)
46
{
47 1
    char format_list[256] = { 0 };
48 1
    make_format_list(format_has_output, tenyr_asm_formats_count, tenyr_asm_formats,
49
            sizeof format_list, format_list, ", ");
50

51 1
    printf("Usage: %s [ OPTIONS ] file [ file ... ] \n"
52
           "Options:\n"
53
           "  -d, --disassemble     disassemble (default is to assemble)\n"
54
           "  -f, --format=F        select output format (%s)\n"
55
           "  -o, --output=X        write output to filename X\n"
56
           "  -p, --param=X=Y       set parameter X to value Y\n"
57
           "  -q, --quiet           disable disassembly output comments\n"
58
           "  -v, --verbose         disable simplified disassembly output\n"
59
           "  -h, --help            display this message\n"
60
           "  -V, --version         print the string `%s'\n"
61
           , me, format_list, version());
62
}
63

64 1
static int process_stream(struct param_state *params, const struct format *f,
65
    FILE *infile, FILE *outfile, int flags)
66
{
67 1
    int rc = 0;
68

69 1
    const struct stream in_ = stream_make_from_file(infile), *in = &in_;
70 1
    const struct stream out_ = stream_make_from_file(outfile), *out = &out_;
71

72 1
    int disassemble = flags & ASM_DISASSEMBLE;
73 1
    STREAM *stream = disassemble ? in : out;
74 1
    void *ud = NULL;
75

76 1
    if (f->init(stream, params, &ud))
77 1
        fatal(PRINT_ERRNO, "Error during initialisation for format '%s'", f->name);
78

79 1
    if (disassemble) {
80
        // This output might be consumed by a tool that needs a line at a time
81 1
        os_set_buffering(outfile, _IOLBF);
82 1
        rc = do_disassembly(in, out, f, ud, flags);
83
    } else {
84 1
        rc = do_assembly(in, out, f, ud);
85
    }
86

87 1
    if (!rc && f->emit)
88 1
        rc |= f->emit(stream, &ud);
89

90 1
    rc |= f->fini(stream, &ud);
91

92 1
    out->op.fflush(out);
93

94 1
    return rc;
95
}
96

97 1
static int process_file(struct param_state *params, int flags,
98
        const struct format *fmt, const char *infname, FILE *out)
99
{
100 1
    int rc = 0;
101 1
    FILE *in = NULL;
102

103 1
    if (!strcmp(infname, "-")) {
104 1
        in = stdin;
105
    } else {
106 1
        in = os_fopen(infname, "rb");
107 1
        if (!in)
108 1
            fatal(PRINT_ERRNO, "Failed to open input file `%s'", infname);
109
    }
110

111
    // Explicitly clear errors and EOF in case we run main() twice
112
    // (emscripten)
113 1
    clearerr(in);
114

115 1
    rc = process_stream(params, fmt, in, out, flags);
116 1
    fclose(in);
117

118 1
    return rc;
119
}
120

121 1
int main(int argc, char *argv[])
122
{
123 1
    int rc = 0;
124 1
    volatile int disassemble = 0;
125 1
    volatile int flags = 0;
126

127 1
    char * volatile outfname = NULL;
128 1
    FILE * volatile out = stdout;
129

130 1
    struct param_state * volatile params = NULL;
131 1
    param_init((struct param_state **)&params);
132

133 1
    if ((rc = setjmp(errbuf))) {
134 1
        if (rc == DISPLAY_USAGE)
135 1
            usage(argv[0]);
136
        // We may have created an output file already, but we do not try to
137
        // remove it, because doing so by filename would be a race condition.
138
        // The most important reason to remove the output file is to avoid
139
        // tricking a build system into thinking that a failed build created a
140
        // good output file; with GNU Make this can be avoided by using
141
        // .DELETE_ON_ERROR, and other build systems have similar features.
142 1
        rc = EXIT_FAILURE;
143 1
        goto cleanup;
144
    }
145

146 1
    const struct format *fmt = &tenyr_asm_formats[0];
147

148
    // Explicitly reset optind for cases where main() is called more than once
149
    // (emscripten)
150 1
    optind = 0;
151

152
    int ch;
153 1
    while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
154 1
        switch (ch) {
155 1
            case 'd': disassemble = 1; break;
156 1
            case 'f': if (find_format(optarg, &fmt)) { usage(argv[0]); exit(EXIT_FAILURE); } break;
157 1
            case 'o': outfname = optarg; break;
158 1
            case 'p': param_add(params, optarg); break;
159 1
            case 'q': flags |= ASM_QUIET; break;
160 1
            case 'v': flags |= ASM_VERBOSE; break;
161

162 1
            case 'V':
163 1
                puts(version());
164 1
                rc = EXIT_SUCCESS;
165 1
                goto cleanup;
166 1
            case 'h':
167 1
                usage(argv[0]);
168 1
                rc = EXIT_SUCCESS;
169 1
                goto cleanup;
170 1
            default:
171 1
                usage(argv[0]);
172 1
                rc = EXIT_FAILURE;
173 1
                goto cleanup;
174
        }
175
    }
176

177 1
    if (optind >= argc)
178 1
        fatal(DISPLAY_USAGE, "No input files specified on the command line");
179

180 1
    param_set(params, "assembling", &"1\0""0\0"[disassemble], 1, false, false);
181

182
    {
183 1
        int setting  = 0;
184 1
        int clearing = 0;
185

186
        // Return values from param_get_int are not significant in this case,
187
        // since we have correct defaults.
188 1
        param_get_int(params, "tas.flags.set"  , &setting );
189 1
        param_get_int(params, "tas.flags.clear", &clearing);
190

191 1
        flags = (flags | setting) & ~clearing;
192
    }
193

194 1
    os_preamble();
195

196
    // TODO don't open output until input has been validated
197 1
    if (outfname)
198 1
        out = os_fopen(outfname, "wb");
199 1
    if (!out)
200 1
        fatal(PRINT_ERRNO, "Failed to open output file `%s'", outfname ? outfname : "<stdout>");
201

202 1
    if (disassemble)
203 1
        flags |= ASM_DISASSEMBLE;
204

205 1
    for (int i = optind; i < argc; i++) {
206 1
        rc = process_file(params, flags, fmt, argv[i], out);
207
        // We may have created an output file already, but we do not try to
208
        // remove it, because doing so by filename would be a race condition.
209
        // The most important reason to remove the output file is to avoid
210
        // tricking a build system into thinking that a failed build created a
211
        // good output file; with GNU Make this can be avoided by using
212
        // .DELETE_ON_ERROR, and other build systems have similar features.
213
    }
214

215 1
    fclose(out);
216 1
    out = NULL;
217

218 1
cleanup:
219 1
    param_destroy(params);
220

221 1
    return rc;
222
}
223

224
/* vi: set ts=4 sw=4 et: */

Read our documentation on viewing source code .

Loading