1/*	$NetBSD: util.c,v 1.1.1.3 2019/12/22 12:34:03 skrll Exp $	*/
2
3// SPDX-License-Identifier: GPL-2.0-or-later
4/*
5 * Copyright 2011 The Chromium Authors, All Rights Reserved.
6 * Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc.
7 *
8 * util_is_printable_string contributed by
9 *	Pantelis Antoniou <pantelis.antoniou AT gmail.com>
10 */
11
12#include <ctype.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <stdarg.h>
16#include <string.h>
17#include <assert.h>
18
19#include <errno.h>
20#include <fcntl.h>
21#include <unistd.h>
22
23#include "libfdt.h"
24#include "util.h"
25#include "version_gen.h"
26
27char *xstrdup(const char *s)
28{
29	int len = strlen(s) + 1;
30	char *d = xmalloc(len);
31
32	memcpy(d, s, len);
33
34	return d;
35}
36
37int xavsprintf_append(char **strp, const char *fmt, va_list ap)
38{
39	int n, size = 0;	/* start with 128 bytes */
40	char *p;
41	va_list ap_copy;
42
43	p = *strp;
44	if (p)
45		size = strlen(p);
46
47	va_copy(ap_copy, ap);
48	n = vsnprintf(NULL, 0, fmt, ap_copy) + 1;
49	va_end(ap_copy);
50
51	p = xrealloc(p, size + n);
52
53	n = vsnprintf(p + size, n, fmt, ap);
54
55	*strp = p;
56	return strlen(p);
57}
58
59int xasprintf_append(char **strp, const char *fmt, ...)
60{
61	int n;
62	va_list ap;
63
64	va_start(ap, fmt);
65	n = xavsprintf_append(strp, fmt, ap);
66	va_end(ap);
67
68	return n;
69}
70
71int xasprintf(char **strp, const char *fmt, ...)
72{
73	int n;
74	va_list ap;
75
76	*strp = NULL;
77
78	va_start(ap, fmt);
79	n = xavsprintf_append(strp, fmt, ap);
80	va_end(ap);
81
82	return n;
83}
84
85char *join_path(const char *path, const char *name)
86{
87	int lenp = strlen(path);
88	int lenn = strlen(name);
89	int len;
90	int needslash = 1;
91	char *str;
92
93	len = lenp + lenn + 2;
94	if ((lenp > 0) && (path[lenp-1] == '/')) {
95		needslash = 0;
96		len--;
97	}
98
99	str = xmalloc(len);
100	memcpy(str, path, lenp);
101	if (needslash) {
102		str[lenp] = '/';
103		lenp++;
104	}
105	memcpy(str+lenp, name, lenn+1);
106	return str;
107}
108
109bool util_is_printable_string(const void *data, int len)
110{
111	const char *s = data;
112	const char *ss, *se;
113
114	/* zero length is not */
115	if (len == 0)
116		return 0;
117
118	/* must terminate with zero */
119	if (s[len - 1] != '\0')
120		return 0;
121
122	se = s + len;
123
124	while (s < se) {
125		ss = s;
126		while (s < se && *s && isprint((unsigned char)*s))
127			s++;
128
129		/* not zero, or not done yet */
130		if (*s != '\0' || s == ss)
131			return 0;
132
133		s++;
134	}
135
136	return 1;
137}
138
139/*
140 * Parse a octal encoded character starting at index i in string s.  The
141 * resulting character will be returned and the index i will be updated to
142 * point at the character directly after the end of the encoding, this may be
143 * the '\0' terminator of the string.
144 */
145static char get_oct_char(const char *s, int *i)
146{
147	char x[4];
148	char *endx;
149	long val;
150
151	x[3] = '\0';
152	strncpy(x, s + *i, 3);
153
154	val = strtol(x, &endx, 8);
155
156	assert(endx > x);
157
158	(*i) += endx - x;
159	return val;
160}
161
162/*
163 * Parse a hexadecimal encoded character starting at index i in string s.  The
164 * resulting character will be returned and the index i will be updated to
165 * point at the character directly after the end of the encoding, this may be
166 * the '\0' terminator of the string.
167 */
168static char get_hex_char(const char *s, int *i)
169{
170	char x[3];
171	char *endx;
172	long val;
173
174	x[2] = '\0';
175	strncpy(x, s + *i, 2);
176
177	val = strtol(x, &endx, 16);
178	if (!(endx  > x))
179		die("\\x used with no following hex digits\n");
180
181	(*i) += endx - x;
182	return val;
183}
184
185char get_escape_char(const char *s, int *i)
186{
187	char	c = s[*i];
188	int	j = *i + 1;
189	char	val;
190
191	switch (c) {
192	case 'a':
193		val = '\a';
194		break;
195	case 'b':
196		val = '\b';
197		break;
198	case 't':
199		val = '\t';
200		break;
201	case 'n':
202		val = '\n';
203		break;
204	case 'v':
205		val = '\v';
206		break;
207	case 'f':
208		val = '\f';
209		break;
210	case 'r':
211		val = '\r';
212		break;
213	case '0':
214	case '1':
215	case '2':
216	case '3':
217	case '4':
218	case '5':
219	case '6':
220	case '7':
221		j--; /* need to re-read the first digit as
222		      * part of the octal value */
223		val = get_oct_char(s, &j);
224		break;
225	case 'x':
226		val = get_hex_char(s, &j);
227		break;
228	default:
229		val = c;
230	}
231
232	(*i) = j;
233	return val;
234}
235
236int utilfdt_read_err(const char *filename, char **buffp, size_t *len)
237{
238	int fd = 0;	/* assume stdin */
239	char *buf = NULL;
240	size_t bufsize = 1024, offset = 0;
241	int ret = 0;
242
243	*buffp = NULL;
244	if (strcmp(filename, "-") != 0) {
245		fd = open(filename, O_RDONLY);
246		if (fd < 0)
247			return errno;
248	}
249
250	/* Loop until we have read everything */
251	buf = xmalloc(bufsize);
252	do {
253		/* Expand the buffer to hold the next chunk */
254		if (offset == bufsize) {
255			bufsize *= 2;
256			buf = xrealloc(buf, bufsize);
257		}
258
259		ret = read(fd, &buf[offset], bufsize - offset);
260		if (ret < 0) {
261			ret = errno;
262			break;
263		}
264		offset += ret;
265	} while (ret != 0);
266
267	/* Clean up, including closing stdin; return errno on error */
268	close(fd);
269	if (ret)
270		free(buf);
271	else
272		*buffp = buf;
273	if (len)
274		*len = bufsize;
275	return ret;
276}
277
278char *utilfdt_read(const char *filename, size_t *len)
279{
280	char *buff;
281	int ret = utilfdt_read_err(filename, &buff, len);
282
283	if (ret) {
284		fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename,
285			strerror(ret));
286		return NULL;
287	}
288	/* Successful read */
289	return buff;
290}
291
292int utilfdt_write_err(const char *filename, const void *blob)
293{
294	int fd = 1;	/* assume stdout */
295	int totalsize;
296	int offset;
297	int ret = 0;
298	const char *ptr = blob;
299
300	if (strcmp(filename, "-") != 0) {
301		fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
302		if (fd < 0)
303			return errno;
304	}
305
306	totalsize = fdt_totalsize(blob);
307	offset = 0;
308
309	while (offset < totalsize) {
310		ret = write(fd, ptr + offset, totalsize - offset);
311		if (ret < 0) {
312			ret = -errno;
313			break;
314		}
315		offset += ret;
316	}
317	/* Close the file/stdin; return errno on error */
318	if (fd != 1)
319		close(fd);
320	return ret < 0 ? -ret : 0;
321}
322
323
324int utilfdt_write(const char *filename, const void *blob)
325{
326	int ret = utilfdt_write_err(filename, blob);
327
328	if (ret) {
329		fprintf(stderr, "Couldn't write blob to '%s': %s\n", filename,
330			strerror(ret));
331	}
332	return ret ? -1 : 0;
333}
334
335int utilfdt_decode_type(const char *fmt, int *type, int *size)
336{
337	int qualifier = 0;
338
339	if (!*fmt)
340		return -1;
341
342	/* get the conversion qualifier */
343	*size = -1;
344	if (strchr("hlLb", *fmt)) {
345		qualifier = *fmt++;
346		if (qualifier == *fmt) {
347			switch (*fmt++) {
348/* TODO:		case 'l': qualifier = 'L'; break;*/
349			case 'h':
350				qualifier = 'b';
351				break;
352			}
353		}
354	}
355
356	/* we should now have a type */
357	if ((*fmt == '\0') || !strchr("iuxs", *fmt))
358		return -1;
359
360	/* convert qualifier (bhL) to byte size */
361	if (*fmt != 's')
362		*size = qualifier == 'b' ? 1 :
363				qualifier == 'h' ? 2 :
364				qualifier == 'l' ? 4 : -1;
365	*type = *fmt++;
366
367	/* that should be it! */
368	if (*fmt)
369		return -1;
370	return 0;
371}
372
373void utilfdt_print_data(const char *data, int len)
374{
375	int i;
376	const char *s;
377
378	/* no data, don't print */
379	if (len == 0)
380		return;
381
382	if (util_is_printable_string(data, len)) {
383		printf(" = ");
384
385		s = data;
386		do {
387			printf("\"%s\"", s);
388			s += strlen(s) + 1;
389			if (s < data + len)
390				printf(", ");
391		} while (s < data + len);
392
393	} else if ((len % 4) == 0) {
394		const fdt32_t *cell = (const fdt32_t *)data;
395
396		printf(" = <");
397		for (i = 0, len /= 4; i < len; i++)
398			printf("0x%08x%s", fdt32_to_cpu(cell[i]),
399			       i < (len - 1) ? " " : "");
400		printf(">");
401	} else {
402		const unsigned char *p = (const unsigned char *)data;
403		printf(" = [");
404		for (i = 0; i < len; i++)
405			printf("%02x%s", *p++, i < len - 1 ? " " : "");
406		printf("]");
407	}
408}
409
410void NORETURN util_version(void)
411{
412	printf("Version: %s\n", DTC_VERSION);
413	exit(0);
414}
415
416void NORETURN util_usage(const char *errmsg, const char *synopsis,
417			 const char *short_opts,
418			 struct option const long_opts[],
419			 const char * const opts_help[])
420{
421	FILE *fp = errmsg ? stderr : stdout;
422	const char a_arg[] = "<arg>";
423	size_t a_arg_len = strlen(a_arg) + 1;
424	size_t i;
425	int optlen;
426
427	fprintf(fp,
428		"Usage: %s\n"
429		"\n"
430		"Options: -[%s]\n", synopsis, short_opts);
431
432	/* prescan the --long opt length to auto-align */
433	optlen = 0;
434	for (i = 0; long_opts[i].name; ++i) {
435		/* +1 is for space between --opt and help text */
436		int l = strlen(long_opts[i].name) + 1;
437		if (long_opts[i].has_arg == a_argument)
438			l += a_arg_len;
439		if (optlen < l)
440			optlen = l;
441	}
442
443	for (i = 0; long_opts[i].name; ++i) {
444		/* helps when adding new applets or options */
445		assert(opts_help[i] != NULL);
446
447		/* first output the short flag if it has one */
448		if (long_opts[i].val > '~')
449			fprintf(fp, "      ");
450		else
451			fprintf(fp, "  -%c, ", long_opts[i].val);
452
453		/* then the long flag */
454		if (long_opts[i].has_arg == no_argument)
455			fprintf(fp, "--%-*s", optlen, long_opts[i].name);
456		else
457			fprintf(fp, "--%s %s%*s", long_opts[i].name, a_arg,
458				(int)(optlen - strlen(long_opts[i].name) - a_arg_len), "");
459
460		/* finally the help text */
461		fprintf(fp, "%s\n", opts_help[i]);
462	}
463
464	if (errmsg) {
465		fprintf(fp, "\nError: %s\n", errmsg);
466		exit(EXIT_FAILURE);
467	} else
468		exit(EXIT_SUCCESS);
469}
470