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