fexc.c 7.81 KB
Newer Older
1
2
3
4
5
/*
 * Copyright (C) 2012  Alejandro Mery <amery@geeks.cl>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
Alejandro Mery's avatar
Alejandro Mery committed
6
 * the Free Software Foundation, either version 2 of the License, or
7
8
9
10
11
12
13
14
15
16
17
18
19
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "fexc.h"

20
#include <errno.h>
21
22
23
#include <libgen.h>
#include <stdlib.h>
#include <string.h>
24
25
26
#ifndef NO_MMAP
  #include <sys/mman.h>
#endif
27
#include <sys/stat.h>
28
#include <unistd.h>
29
30
#include <fcntl.h>

31
32
#define pr_info(...)	pr_error("fexc: " __VA_ARGS__)
#define pr_err(...)	pr_error("E: fexc: " __VA_ARGS__)
33
34
35
36

enum script_format {
	FEX_SCRIPT_FORMAT,
	BIN_SCRIPT_FORMAT,
Alejandro Mery's avatar
Alejandro Mery committed
37
	UBOOT_HEADER_FORMAT,
38
};
39

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
 */
static inline char *read_all(int fd, const char *filename, size_t *size)
{
	size_t buf_size = 4096, count = 0;
	char *p, *buf = malloc(buf_size);
	if (!buf) {
		pr_err("%s: %s\n", "malloc", strerror(errno));
		return NULL;
	}
	p = buf;
	while (1) {
		ssize_t rc = read(fd, p, buf_size-count);
		if (rc == 0)
			break;
		else if (rc > 0) {
			count += rc;
			p += rc;

			if (count == buf_size) {
				char *new;
				buf_size *= 2;
				new = realloc(buf, buf_size);
				if (!new) {
					pr_err("%s: %s\n", "realloc",
					       strerror(errno));
					free(buf);
					return NULL;
				} else if (new != buf) {
					buf = new;
					p = buf + count;
				}
			}
73
		} else if (errno != EAGAIN && errno != EINTR) {
74
75
76
77
78
79
80
81
82
83
84
			pr_err("%s: %s: %s\n", filename,
			       "read", strerror(errno));
			free(buf);
			return NULL;
		}
	}

	*size = count;
	return buf;
}

85
86
87
88
89
90
91
92
93
94
/*
 */
static inline int script_parse(enum script_format format,
			       const char *filename,
			       struct script *script)
{
	int ret = 0;
	switch (format) {
	case FEX_SCRIPT_FORMAT: {
		FILE *in = stdin;
95
96
97
98
		if (!filename)
			filename = "<stdin>";
		else if ((in = fopen(filename, "r")) == NULL) {
			pr_err("%s: %s\n", filename, strerror(errno));
99
100
			break;
		}
101
		ret = script_parse_fex(in, filename, script);
102
103
104
		fclose(in);
		}; break;
	case BIN_SCRIPT_FORMAT: {
105
106
107
		int in = 0; /* stdin */
		struct stat sb;
		void *bin = NULL;
108
109
		size_t bin_size;
		int allocated = 1;
110
111
112
113
114
115

		if (!filename)
			filename = "<stdin>";
		else if ((in = open(filename, O_RDONLY)) < 0) {
			pr_err("%s: %s\n", filename, strerror(errno));
			break;
116
		}
117
118
119
120
121
#ifdef _WIN32
		/* need to set "binary" mode for input, otherwise reading might fail
		 * prematurely on <EOF> chars */
		_setmode(in, _O_BINARY);
#endif
122
123

		if (fstat(in, &sb) == -1) {
124
125
126
			pr_err("%s: %s: %s\n", filename,
			       "fstat", strerror(errno));
			goto bin_close;
127
#ifndef NO_MMAP
128
129
130
131
132
133
134
135
		} else if (S_ISREG(sb.st_mode)) {
			/* regular file, mmap it */
			bin = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, in, 0);
			if (bin == MAP_FAILED) {
				pr_err("%s: %s: %s\n", filename,
				       "mmap", strerror(errno));
				goto bin_close;
			}
136
137
			bin_size = sb.st_size;
			allocated = 0;
138
#endif
139
		} else {
140
141
142
143
144
			/* something else... just read it all! */
			bin = read_all(in, filename, &bin_size);
			if (bin == NULL)
				goto bin_close;
			allocated = 1;
145
146
		}

147
148
149
		ret = script_decompile_bin(bin, bin_size, filename, script);
		if (allocated)
			free(bin);
150
#ifndef NO_MMAP
151
		else if (munmap(bin, bin_size) == -1) {
152
153
154
			pr_err("%s: %s: %s\n", filename,
			       "munmap", strerror(errno));
		}
155
#endif
156
157
bin_close:
		close(in);
158
		}; break;
Alejandro Mery's avatar
Alejandro Mery committed
159
160
	case UBOOT_HEADER_FORMAT: /* not valid input */
		;
161
162
163
164
165
166
167
168
	}
	return ret;
}
static inline int script_generate(enum script_format format,
				  const char *filename,
				  struct script *script)
{
	int ret = 0;
Alejandro Mery's avatar
Alejandro Mery committed
169
170
171
172
173
174
	static int (*text_gen[3]) (FILE *, const char *, struct script *) = {
		[FEX_SCRIPT_FORMAT] = script_generate_fex,
		[UBOOT_HEADER_FORMAT] = script_generate_uboot,
	};

	if (text_gen[format]) {
175
176
177
178
179
180
		FILE *out = stdout;

		if (!filename)
			filename = "<stdout>";
		else if ((out = fopen(filename, "w")) == NULL) {
			pr_err("%s: %s\n", filename, strerror(errno));
Alejandro Mery's avatar
Alejandro Mery committed
181
			goto done;
182
183
		}

Alejandro Mery's avatar
Alejandro Mery committed
184
		ret = text_gen[format](out, filename, script);
185
		fclose(out);
Alejandro Mery's avatar
Alejandro Mery committed
186
	} else {
187
188
189
190
191
192
193
		int out = 1; /* stdout */
		size_t sections, entries, bin_size;
		void *bin;

		if (!filename)
			filename = "<stdout>";
		else if ((out = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
194
			pr_err("%s: %s\n", filename, strerror(errno));
Alejandro Mery's avatar
Alejandro Mery committed
195
			goto done;
196
		}
197
198
199
200
201
#ifdef _WIN32
		/* need to set "binary" mode, otherwise writing might spoil the output
		 * with implicit <CR> --> <CR><LF> conversions */
		_setmode(out, _O_BINARY);
#endif
202
203
204
205

		bin_size = script_bin_size(script, &sections, &entries);
		bin = calloc(1, bin_size);
		if (!bin)
206
			pr_err("%s: %s\n", "malloc", strerror(errno));
207
		else if (script_generate_bin(bin, bin_size, script, sections, entries)) {
208
			char *p = bin;
209
			while(bin_size) {
210
				ssize_t wc = write(out, p, bin_size);
211
212

				if (wc>0) {
213
					p += wc;
214
215
					bin_size -= wc;
				} else if (wc < 0 && errno != EINTR) {
216
217
					pr_err("%s: %s: %s\n", filename,
					       "write", strerror(errno));
218
219
220
					break;
				}
			}
221
			ret = (bin_size == 0);
222
		}
223
224
		free(bin);
		close(out);
225
	}
Alejandro Mery's avatar
Alejandro Mery committed
226
done:
227
228
229
	return ret;
}

230
231
232
233
/*
 */
static inline void app_usage(const char *arg0, int mode)
{
234
	fputs("sunxi-fexc " VERSION "\n\n", stderr);
235
236
	pr_error("Usage: %s [-vq]%s[<input> [<output>]]\n", arg0,
		 mode ? " " : " [-I <infmt>] [-O <outfmt>] ");
237
238
239

	if (mode == 0)
		fputs("\ninfmt:  fex, bin  (default:fex)"
Alejandro Mery's avatar
Alejandro Mery committed
240
		      "\noutfmt: fex, bin, uboot  (default:bin)\n",
241
242
243
244
		      stderr);
}

static inline int app_choose_mode(char *arg0)
245
{
246
247
248
249
250
251
252
253
254
255
256
257
258
	const char *name = basename(arg0);
	if (strcmp(name, "fex2bin") == 0)
		return 1;
	else if (strcmp(name, "bin2fex") == 0)
		return 2;
	else
		return 0;
}

/*
 */
int main(int argc, char *argv[])
{
Alejandro Mery's avatar
Alejandro Mery committed
259
	static const char *formats[] = { "fex", "bin", "uboot", NULL };
260
261
262
	enum script_format infmt=FEX_SCRIPT_FORMAT;
	enum script_format outfmt=BIN_SCRIPT_FORMAT;
	const char *filename[] = { NULL /*stdin*/, NULL /*stdout*/};
Alejandro Mery's avatar
Alejandro Mery committed
263
	struct script *script;
264
265
266

	int app_mode = app_choose_mode(argv[0]);

267
268
	const char *opt_string = "I:O:vq?";
	if (app_mode != 0) opt_string += 4; /* disallow -I and -O */
Alejandro Mery's avatar
Alejandro Mery committed
269
	int opt, ret = 1;
270
271
	int verbose = 0;

Alejandro Mery's avatar
Alejandro Mery committed
272
273
274
275
	if (app_mode == 2) { /* bin2fex */
		infmt = BIN_SCRIPT_FORMAT;
		outfmt = FEX_SCRIPT_FORMAT;
	}
276

277
278
279
280
281
282
283
284
	while ((opt = getopt(argc, argv, opt_string)) != -1) {
		switch (opt) {
		case 'I':
			infmt=0;
			for (const char **f = formats; *f; f++, infmt++) {
				if (strcmp(*f, optarg) == 0)
					break;
			}
Alejandro Mery's avatar
Alejandro Mery committed
285
286
287
288
289
			switch (infmt) {
			case FEX_SCRIPT_FORMAT:
			case BIN_SCRIPT_FORMAT:
				break;
			default:
290
291
				pr_error("%s: invalid format -- \"%s\"\n",
					 argv[0], optarg);
292
293
294
295
296
297
298
299
300
301
				goto show_usage;
			}
			break;
		case 'O':
			outfmt=0;
			for (const char **f = formats; *f; f++, outfmt++) {
				if (strcmp(*f, optarg) == 0)
					break;
			}
			if (!formats[outfmt]) {
302
303
				pr_error("%s: invalid format -- \"%s\"\n",
					 argv[0], optarg);
304
305
306
307
308
309
				goto show_usage;
			}
			break;
		case 'v':
			verbose++;
			break;
310
311
312
		case 'q':
			verbose--;
			break;
313
314
315
		default:
show_usage:
			app_usage(argv[0], app_mode);
Alejandro Mery's avatar
Alejandro Mery committed
316
			goto done;
317
318
		}
	}
319

320
321
322
323
324
325
326
327
328
329
330
331
	switch (argc - optind) {
	case 2:
		filename[1] = argv[optind+1]; /* out */
	case 1:
		if (strcmp(argv[optind], "-") != 0)
			filename[0] = argv[optind]; /* in */
	case 0:
		break;
	default:
		goto show_usage;
	}

332
	if (verbose>0)
333
334
335
		pr_error("%s: from %s:%s to %s:%s\n", argv[0],
			 formats[infmt], filename[0]?filename[0]:"<stdin>",
			 formats[outfmt], filename[1]?filename[1]:"<stdout>");
336

Alejandro Mery's avatar
Alejandro Mery committed
337
338
339
340
341
342
343
344
345
346
	if ((script = script_new()) == NULL) {
		perror("malloc");
		goto done;
	} else if (script_parse(infmt, filename[0], script) &&
		   script_generate(outfmt, filename[1], script)) {
		ret = 0;
	}
	script_delete(script);
done:
	return ret;
347
}