|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* | 
|  | * Copyright 2011 The Chromium Authors, All Rights Reserved. | 
|  | * Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc. | 
|  | * | 
|  | * util_is_printable_string contributed by | 
|  | *	Pantelis Antoniou <pantelis.antoniou AT gmail.com> | 
|  | */ | 
|  |  | 
|  | #include <ctype.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdarg.h> | 
|  | #include <string.h> | 
|  | #include <assert.h> | 
|  | #include <inttypes.h> | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include "libfdt.h" | 
|  | #include "util.h" | 
|  | #include "version_gen.h" | 
|  |  | 
|  | char *xstrdup(const char *s) | 
|  | { | 
|  | int len = strlen(s) + 1; | 
|  | char *d = xmalloc(len); | 
|  |  | 
|  | memcpy(d, s, len); | 
|  |  | 
|  | return d; | 
|  | } | 
|  |  | 
|  | char *xstrndup(const char *s, size_t n) | 
|  | { | 
|  | size_t len = strnlen(s, n) + 1; | 
|  | char *d = xmalloc(len); | 
|  |  | 
|  | memcpy(d, s, len - 1); | 
|  | d[len - 1] = '\0'; | 
|  |  | 
|  | return d; | 
|  | } | 
|  |  | 
|  | int xavsprintf_append(char **strp, const char *fmt, va_list ap) | 
|  | { | 
|  | int n, size = 0;	/* start with 128 bytes */ | 
|  | char *p; | 
|  | va_list ap_copy; | 
|  |  | 
|  | p = *strp; | 
|  | if (p) | 
|  | size = strlen(p); | 
|  |  | 
|  | va_copy(ap_copy, ap); | 
|  | n = vsnprintf(NULL, 0, fmt, ap_copy) + 1; | 
|  | va_end(ap_copy); | 
|  |  | 
|  | p = xrealloc(p, size + n); | 
|  |  | 
|  | n = vsnprintf(p + size, n, fmt, ap); | 
|  |  | 
|  | *strp = p; | 
|  | return strlen(p); | 
|  | } | 
|  |  | 
|  | int xasprintf_append(char **strp, const char *fmt, ...) | 
|  | { | 
|  | int n; | 
|  | va_list ap; | 
|  |  | 
|  | va_start(ap, fmt); | 
|  | n = xavsprintf_append(strp, fmt, ap); | 
|  | va_end(ap); | 
|  |  | 
|  | return n; | 
|  | } | 
|  |  | 
|  | int xasprintf(char **strp, const char *fmt, ...) | 
|  | { | 
|  | int n; | 
|  | va_list ap; | 
|  |  | 
|  | *strp = NULL; | 
|  |  | 
|  | va_start(ap, fmt); | 
|  | n = xavsprintf_append(strp, fmt, ap); | 
|  | va_end(ap); | 
|  |  | 
|  | return n; | 
|  | } | 
|  |  | 
|  | char *join_path(const char *path, const char *name) | 
|  | { | 
|  | int lenp = strlen(path); | 
|  | int lenn = strlen(name); | 
|  | int len; | 
|  | int needslash = 1; | 
|  | char *str; | 
|  |  | 
|  | len = lenp + lenn + 2; | 
|  | if ((lenp > 0) && (path[lenp-1] == '/')) { | 
|  | needslash = 0; | 
|  | len--; | 
|  | } | 
|  |  | 
|  | str = xmalloc(len); | 
|  | memcpy(str, path, lenp); | 
|  | if (needslash) { | 
|  | str[lenp] = '/'; | 
|  | lenp++; | 
|  | } | 
|  | memcpy(str+lenp, name, lenn+1); | 
|  | return str; | 
|  | } | 
|  |  | 
|  | bool util_is_printable_string(const void *data, int len) | 
|  | { | 
|  | const char *s = data; | 
|  | const char *ss, *se; | 
|  |  | 
|  | /* zero length is not */ | 
|  | if (len == 0) | 
|  | return 0; | 
|  |  | 
|  | /* must terminate with zero */ | 
|  | if (s[len - 1] != '\0') | 
|  | return 0; | 
|  |  | 
|  | se = s + len; | 
|  |  | 
|  | while (s < se) { | 
|  | ss = s; | 
|  | while (s < se && *s && isprint((unsigned char)*s)) | 
|  | s++; | 
|  |  | 
|  | /* not zero, or not done yet */ | 
|  | if (*s != '\0' || s == ss) | 
|  | return 0; | 
|  |  | 
|  | s++; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Parse a octal encoded character starting at index i in string s.  The | 
|  | * resulting character will be returned and the index i will be updated to | 
|  | * point at the character directly after the end of the encoding, this may be | 
|  | * the '\0' terminator of the string. | 
|  | */ | 
|  | static char get_oct_char(const char *s, int *i) | 
|  | { | 
|  | char x[4]; | 
|  | char *endx; | 
|  | long val; | 
|  |  | 
|  | x[3] = '\0'; | 
|  | strncpy(x, s + *i, 3); | 
|  |  | 
|  | val = strtol(x, &endx, 8); | 
|  |  | 
|  | assert(endx > x); | 
|  |  | 
|  | (*i) += endx - x; | 
|  | return val; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Parse a hexadecimal encoded character starting at index i in string s.  The | 
|  | * resulting character will be returned and the index i will be updated to | 
|  | * point at the character directly after the end of the encoding, this may be | 
|  | * the '\0' terminator of the string. | 
|  | */ | 
|  | static char get_hex_char(const char *s, int *i) | 
|  | { | 
|  | char x[3]; | 
|  | char *endx; | 
|  | long val; | 
|  |  | 
|  | x[2] = '\0'; | 
|  | strncpy(x, s + *i, 2); | 
|  |  | 
|  | val = strtol(x, &endx, 16); | 
|  | if (!(endx  > x)) | 
|  | die("\\x used with no following hex digits\n"); | 
|  |  | 
|  | (*i) += endx - x; | 
|  | return val; | 
|  | } | 
|  |  | 
|  | char get_escape_char(const char *s, int *i) | 
|  | { | 
|  | char	c = s[*i]; | 
|  | int	j = *i + 1; | 
|  | char	val; | 
|  |  | 
|  | switch (c) { | 
|  | case 'a': | 
|  | val = '\a'; | 
|  | break; | 
|  | case 'b': | 
|  | val = '\b'; | 
|  | break; | 
|  | case 't': | 
|  | val = '\t'; | 
|  | break; | 
|  | case 'n': | 
|  | val = '\n'; | 
|  | break; | 
|  | case 'v': | 
|  | val = '\v'; | 
|  | break; | 
|  | case 'f': | 
|  | val = '\f'; | 
|  | break; | 
|  | case 'r': | 
|  | val = '\r'; | 
|  | break; | 
|  | case '0': | 
|  | case '1': | 
|  | case '2': | 
|  | case '3': | 
|  | case '4': | 
|  | case '5': | 
|  | case '6': | 
|  | case '7': | 
|  | j--; /* need to re-read the first digit as | 
|  | * part of the octal value */ | 
|  | val = get_oct_char(s, &j); | 
|  | break; | 
|  | case 'x': | 
|  | val = get_hex_char(s, &j); | 
|  | break; | 
|  | default: | 
|  | val = c; | 
|  | } | 
|  |  | 
|  | (*i) = j; | 
|  | return val; | 
|  | } | 
|  |  | 
|  | int utilfdt_read_err(const char *filename, char **buffp, size_t *len) | 
|  | { | 
|  | int fd = 0;	/* assume stdin */ | 
|  | char *buf = NULL; | 
|  | size_t bufsize = 1024, offset = 0; | 
|  | int ret = 0; | 
|  |  | 
|  | *buffp = NULL; | 
|  | if (strcmp(filename, "-") != 0) { | 
|  | fd = open(filename, O_RDONLY); | 
|  | if (fd < 0) | 
|  | return errno; | 
|  | } | 
|  |  | 
|  | /* Loop until we have read everything */ | 
|  | buf = xmalloc(bufsize); | 
|  | do { | 
|  | /* Expand the buffer to hold the next chunk */ | 
|  | if (offset == bufsize) { | 
|  | bufsize *= 2; | 
|  | buf = xrealloc(buf, bufsize); | 
|  | } | 
|  |  | 
|  | ret = read(fd, &buf[offset], bufsize - offset); | 
|  | if (ret < 0) { | 
|  | ret = errno; | 
|  | break; | 
|  | } | 
|  | offset += ret; | 
|  | } while (ret != 0); | 
|  |  | 
|  | /* Clean up, including closing stdin; return errno on error */ | 
|  | close(fd); | 
|  | if (ret) | 
|  | free(buf); | 
|  | else | 
|  | *buffp = buf; | 
|  | if (len) | 
|  | *len = bufsize; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | char *utilfdt_read(const char *filename, size_t *len) | 
|  | { | 
|  | char *buff; | 
|  | int ret = utilfdt_read_err(filename, &buff, len); | 
|  |  | 
|  | if (ret) { | 
|  | fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename, | 
|  | strerror(ret)); | 
|  | return NULL; | 
|  | } | 
|  | /* Successful read */ | 
|  | return buff; | 
|  | } | 
|  |  | 
|  | int utilfdt_write_err(const char *filename, const void *blob) | 
|  | { | 
|  | int fd = 1;	/* assume stdout */ | 
|  | int totalsize; | 
|  | int offset; | 
|  | int ret = 0; | 
|  | const char *ptr = blob; | 
|  |  | 
|  | if (strcmp(filename, "-") != 0) { | 
|  | fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); | 
|  | if (fd < 0) | 
|  | return errno; | 
|  | } | 
|  |  | 
|  | totalsize = fdt_totalsize(blob); | 
|  | offset = 0; | 
|  |  | 
|  | while (offset < totalsize) { | 
|  | ret = write(fd, ptr + offset, totalsize - offset); | 
|  | if (ret < 0) { | 
|  | ret = -errno; | 
|  | break; | 
|  | } | 
|  | offset += ret; | 
|  | } | 
|  | /* Close the file/stdin; return errno on error */ | 
|  | if (fd != 1) | 
|  | close(fd); | 
|  | return ret < 0 ? -ret : 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | int utilfdt_write(const char *filename, const void *blob) | 
|  | { | 
|  | int ret = utilfdt_write_err(filename, blob); | 
|  |  | 
|  | if (ret) { | 
|  | fprintf(stderr, "Couldn't write blob to '%s': %s\n", filename, | 
|  | strerror(ret)); | 
|  | } | 
|  | return ret ? -1 : 0; | 
|  | } | 
|  |  | 
|  | int utilfdt_decode_type(const char *fmt, int *type, int *size) | 
|  | { | 
|  | int qualifier = 0; | 
|  |  | 
|  | if (!*fmt) | 
|  | return -1; | 
|  |  | 
|  | /* get the conversion qualifier */ | 
|  | *size = -1; | 
|  | if (strchr("hlLb", *fmt)) { | 
|  | qualifier = *fmt++; | 
|  | if (qualifier == *fmt) { | 
|  | switch (*fmt++) { | 
|  | /* TODO:		case 'l': qualifier = 'L'; break;*/ | 
|  | case 'h': | 
|  | qualifier = 'b'; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* we should now have a type */ | 
|  | if ((*fmt == '\0') || !strchr("iuxsr", *fmt)) | 
|  | return -1; | 
|  |  | 
|  | /* convert qualifier (bhL) to byte size */ | 
|  | if (*fmt != 's' && *fmt != 'r') | 
|  | *size = qualifier == 'b' ? 1 : | 
|  | qualifier == 'h' ? 2 : | 
|  | qualifier == 'l' ? 4 : -1; | 
|  | *type = *fmt++; | 
|  |  | 
|  | /* that should be it! */ | 
|  | if (*fmt) | 
|  | return -1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void utilfdt_print_data(const char *data, int len) | 
|  | { | 
|  | int i; | 
|  | const char *s; | 
|  |  | 
|  | /* no data, don't print */ | 
|  | if (len == 0) | 
|  | return; | 
|  |  | 
|  | if (util_is_printable_string(data, len)) { | 
|  | printf(" = "); | 
|  |  | 
|  | s = data; | 
|  | do { | 
|  | printf("\"%s\"", s); | 
|  | s += strlen(s) + 1; | 
|  | if (s < data + len) | 
|  | printf(", "); | 
|  | } while (s < data + len); | 
|  |  | 
|  | } else if ((len % 4) == 0) { | 
|  | const fdt32_t *cell = (const fdt32_t *)data; | 
|  |  | 
|  | printf(" = <"); | 
|  | for (i = 0, len /= 4; i < len; i++) | 
|  | printf("0x%08" PRIx32 "%s", fdt32_to_cpu(cell[i]), | 
|  | i < (len - 1) ? " " : ""); | 
|  | printf(">"); | 
|  | } else { | 
|  | const unsigned char *p = (const unsigned char *)data; | 
|  | printf(" = ["); | 
|  | for (i = 0; i < len; i++) | 
|  | printf("%02x%s", *p++, i < len - 1 ? " " : ""); | 
|  | printf("]"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void NORETURN util_version(void) | 
|  | { | 
|  | printf("Version: %s\n", DTC_VERSION); | 
|  | exit(0); | 
|  | } | 
|  |  | 
|  | void NORETURN util_usage(const char *errmsg, const char *synopsis, | 
|  | const char *short_opts, | 
|  | struct option const long_opts[], | 
|  | const char * const opts_help[]) | 
|  | { | 
|  | FILE *fp = errmsg ? stderr : stdout; | 
|  | const char a_arg[] = "<arg>"; | 
|  | size_t a_arg_len = strlen(a_arg) + 1; | 
|  | size_t i; | 
|  | int optlen; | 
|  |  | 
|  | fprintf(fp, | 
|  | "Usage: %s\n" | 
|  | "\n" | 
|  | "Options: -[%s]\n", synopsis, short_opts); | 
|  |  | 
|  | /* prescan the --long opt length to auto-align */ | 
|  | optlen = 0; | 
|  | for (i = 0; long_opts[i].name; ++i) { | 
|  | /* +1 is for space between --opt and help text */ | 
|  | int l = strlen(long_opts[i].name) + 1; | 
|  | if (long_opts[i].has_arg == a_argument) | 
|  | l += a_arg_len; | 
|  | if (optlen < l) | 
|  | optlen = l; | 
|  | } | 
|  |  | 
|  | for (i = 0; long_opts[i].name; ++i) { | 
|  | /* helps when adding new applets or options */ | 
|  | assert(opts_help[i] != NULL); | 
|  |  | 
|  | /* first output the short flag if it has one */ | 
|  | if (long_opts[i].val > '~') | 
|  | fprintf(fp, "      "); | 
|  | else | 
|  | fprintf(fp, "  -%c, ", long_opts[i].val); | 
|  |  | 
|  | /* then the long flag */ | 
|  | if (long_opts[i].has_arg == no_argument) | 
|  | fprintf(fp, "--%-*s", optlen, long_opts[i].name); | 
|  | else | 
|  | fprintf(fp, "--%s %s%*s", long_opts[i].name, a_arg, | 
|  | (int)(optlen - strlen(long_opts[i].name) - a_arg_len), ""); | 
|  |  | 
|  | /* finally the help text */ | 
|  | fprintf(fp, "%s\n", opts_help[i]); | 
|  | } | 
|  |  | 
|  | if (errmsg) { | 
|  | fprintf(fp, "\nError: %s\n", errmsg); | 
|  | exit(EXIT_FAILURE); | 
|  | } else | 
|  | exit(EXIT_SUCCESS); | 
|  | } |