"Das U-Boot" Source Tree
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cmd: setexpr: add format string handling

Add format string handling operator to the setexpr command.
It allows to use C or Bash like format string expressions to be
evaluated with the result being stored inside the environment variable
name.

setexpr <name> fmt <format> [value]...

The following example

setexpr foo fmt "%d, 0x%x" 0x100 ff

will result in $foo being set to "256, 0xff".

Signed-off-by: Roland Gaudig <roland.gaudig@weidmueller.com>
Reviewed-by: Simon Glass <sjg@chromium.org>

authored by

Roland Gaudig and committed by
Tom Rini
f4f8d8bb 6244cda4

+299 -177
+5
MAINTAINERS
··· 1050 1050 F: doc/arch/sandbox.rst 1051 1051 F: include/dt-bindings/*/sandbox*.h 1052 1052 1053 + SETEXPR 1054 + M: Roland Gaudig <roland.gaudig@weidmueller.com> 1055 + S: Maintained 1056 + F: cmd/printf.c 1057 + 1053 1058 SH 1054 1059 M: Marek Vasut <marek.vasut+renesas@gmail.com> 1055 1060 M: Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
+8
cmd/Kconfig
··· 1414 1414 Also supports loading the value at a memory location into a variable. 1415 1415 If CONFIG_REGEX is enabled, setexpr also supports a gsub function. 1416 1416 1417 + config CMD_SETEXPR_FMT 1418 + bool "setexpr_fmt" 1419 + default n 1420 + depends on CMD_SETEXPR 1421 + help 1422 + Evaluate format string expression and store result in an environment 1423 + variable. 1424 + 1417 1425 endmenu 1418 1426 1419 1427 menu "Android support commands"
+1
cmd/Makefile
··· 141 141 obj-$(CONFIG_CMD_SCSI) += scsi.o disk.o 142 142 obj-$(CONFIG_CMD_SHA1SUM) += sha1sum.o 143 143 obj-$(CONFIG_CMD_SETEXPR) += setexpr.o 144 + obj-$(CONFIG_CMD_SETEXPR_FMT) += printf.o 144 145 obj-$(CONFIG_CMD_SPI) += spi.o 145 146 obj-$(CONFIG_CMD_STRINGS) += strings.o 146 147 obj-$(CONFIG_CMD_SMC) += smccc.o
+244 -173
cmd/printf.c
··· 1 - /* vi: set sw=4 ts=4: */ 1 + // SPDX-License-Identifier: GPL-2.0+ 2 2 /* 3 - * printf - format and print data 3 + * Copyright (C) 2021 Weidmüller Interface GmbH & Co. KG 4 + * Roland Gaudig <roland.gaudig@weidmueller.com> 4 5 * 5 6 * Copyright 1999 Dave Cinege 6 7 * Portions copyright (C) 1990-1996 Free Software Foundation, Inc. 7 8 * 8 9 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 10 + */ 11 + /* 12 + * This file provides a shell printf like format string expansion as required 13 + * for the setexpr <name> fmt <format> <value> command. 14 + * This source file was mostly taken from the BusyBox project (www.busybox.net) 15 + * In contrast to the original sources the output is not written to stdout 16 + * anymore but into a char array, which can be used as input for the env_set() 17 + * function. 9 18 */ 10 19 /* Usage: printf format [argument...] 11 20 * ··· 59 68 //usage: "$ printf \"Val=%d\\n\" 5\n" 60 69 //usage: "Val=5\n" 61 70 62 - #include "libbb.h" 63 - 64 71 /* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it. 65 72 * They report it: 66 73 * bash: printf: XXX: invalid number ··· 77 84 * We try to be compatible. 78 85 */ 79 86 80 - typedef void FAST_FUNC (*converter)(const char *arg, void *result); 87 + #include <common.h> 88 + #include <ctype.h> 89 + #include <errno.h> 90 + #include <stddef.h> 91 + #include <stdio.h> 92 + #include <stdlib.h> 81 93 82 94 #define WANT_HEX_ESCAPES 0 95 + #define PRINT_CONVERSION_ERROR 1 96 + #define PRINT_TRUNCATED_ERROR 2 97 + #define PRINT_SIZE_ERROR 4 83 98 84 - /* Usual "this only works for ascii compatible encodings" disclaimer. */ 85 - #undef _tolower 86 - #define _tolower(X) ((X)|((char) 0x20)) 99 + struct print_inf { 100 + char *str; 101 + size_t size; 102 + size_t offset; 103 + unsigned int error; 104 + }; 105 + 106 + typedef void (*converter)(const char *arg, void *result); 107 + 108 + /** 109 + * printf_str() - print formatted into char array with length checks 110 + * 111 + * This function povides a printf like function for printing into a char array 112 + * with checking the boundaries. 113 + * Unlike snprintf, all checks are performed inside this function and status 114 + * reports are stored inside the print_inf struct. That way, this function can 115 + * be used almost as drop-in replacement without needing much code changes. 116 + * Unlike snprintf errors are not reported by return value, but inside the 117 + * error member of struct print_inf. The output stored inside the struct 118 + * print_inf str member shall only be used when the error member is 0. 119 + * 120 + * @inf: Info structure for print operation 121 + * @char: format string with optional arguments 122 + */ 123 + static void printf_str(struct print_inf *inf, char *format, ...) 124 + { 125 + va_list args; 126 + int i; 127 + 128 + if (!inf) 129 + return; 130 + 131 + /* Do not write anything if previous error is pending */ 132 + if (inf->error) 133 + return; 87 134 88 - char FAST_FUNC bb_process_escape_sequence(const char **ptr) 135 + /* Check if end of receiving buffer is already reached */ 136 + if (inf->offset >= inf->size) { 137 + inf->error |= PRINT_SIZE_ERROR; 138 + return; 139 + } 140 + 141 + size_t remaining = inf->size - inf->offset; 142 + 143 + va_start(args, format); 144 + i = vsnprintf(inf->str + inf->offset, remaining, format, args); 145 + va_end(args); 146 + 147 + if (i >= remaining) 148 + inf->error |= PRINT_TRUNCATED_ERROR; 149 + else if (i < 0) 150 + inf->error |= PRINT_CONVERSION_ERROR; 151 + else 152 + inf->offset += i; 153 + } 154 + 155 + /** 156 + * putchar_str() - Print single character into char array with length checks 157 + * 158 + * This function provices a putchar like function, which stores the output 159 + * into a char array with checking boundaries. 160 + * 161 + * @inf: Info structure for print operation 162 + * @char: Single character to be printed 163 + */ 164 + static void putchar_str(struct print_inf *inf, char c) 165 + { 166 + printf_str(inf, "%c", c); 167 + } 168 + 169 + static char process_escape_sequence(const char **ptr) 89 170 { 90 171 const char *q; 91 - unsigned num_digits; 92 - unsigned n; 93 - unsigned base; 172 + unsigned int num_digits; 173 + unsigned int n; 174 + unsigned int base; 94 175 95 - num_digits = n = 0; 176 + num_digits = 0; 177 + n = 0; 96 178 base = 8; 97 179 q = *ptr; 98 180 ··· 104 186 105 187 /* bash requires leading 0 in octal escapes: 106 188 * \02 works, \2 does not (prints \ and 2). 107 - * We treat \2 as a valid octal escape sequence. */ 189 + * We treat \2 as a valid octal escape sequence. 190 + */ 108 191 do { 109 - unsigned r; 110 - unsigned d = (unsigned char)(*q) - '0'; 192 + unsigned int r; 193 + unsigned int d = (unsigned char)(*q) - '0'; 111 194 #if WANT_HEX_ESCAPES 112 195 if (d >= 10) { 113 - d = (unsigned char)_tolower(*q) - 'a'; 196 + d = (unsigned char)tolower(*q) - 'a'; 114 197 //d += 10; 115 198 /* The above would map 'A'-'F' and 'a'-'f' to 10-15, 116 199 * however, some chars like '@' would map to 9 < base. ··· 125 208 --num_digits; 126 209 if (num_digits == 0) { 127 210 /* \x<bad_char>: return '\', 128 - * leave ptr pointing to x */ 211 + * leave ptr pointing to x 212 + */ 129 213 return '\\'; 130 214 } 131 215 } ··· 133 217 } 134 218 135 219 r = n * base + d; 136 - if (r > UCHAR_MAX) { 220 + if (r > 255) 137 221 break; 138 - } 139 222 140 223 n = r; 141 224 ++q; ··· 143 226 144 227 if (num_digits == 0) { 145 228 /* Not octal or hex escape sequence. 146 - * Is it one-letter one? */ 147 - 229 + * Is it one-letter one? 230 + */ 148 231 /* bash builtin "echo -e '\ec'" interprets \e as ESC, 149 232 * but coreutils "/bin/echo -e '\ec'" does not. 150 233 * Manpages tend to support coreutils way. 151 - * Update: coreutils added support for \e on 28 Oct 2009. */ 152 - static const char charmap[] ALIGN1 = { 234 + * Update: coreutils added support for \e on 28 Oct 2009. 235 + */ 236 + static const char charmap[] = { 153 237 'a', 'b', 'e', 'f', 'n', 'r', 't', 'v', '\\', '\0', 154 238 '\a', '\b', 27, '\f', '\n', '\r', '\t', '\v', '\\', '\\', 155 239 }; 240 + 156 241 const char *p = charmap; 242 + 157 243 do { 158 244 if (*p == *q) { 159 245 q++; ··· 163 249 /* p points to found escape char or NUL, 164 250 * advance it and find what it translates to. 165 251 * Note that \NUL and unrecognized sequence \z return '\' 166 - * and leave ptr pointing to NUL or z. */ 252 + * and leave ptr pointing to NUL or z. 253 + */ 167 254 n = p[sizeof(charmap) / 2]; 168 255 } 169 256 170 257 *ptr = q; 171 258 172 - return (char) n; 259 + return (char)n; 173 260 } 174 261 175 - char* FAST_FUNC skip_whitespace(const char *s) 262 + static char *skip_whitespace(const char *s) 176 263 { 177 264 /* In POSIX/C locale (the only locale we care about: do we REALLY want 178 265 * to allow Unicode whitespace in, say, .conf files? nuts!) ··· 183 270 while (*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9)) 184 271 s++; 185 272 186 - return (char *) s; 273 + return (char *)s; 187 274 } 188 275 189 276 /* Like strcpy but can copy overlapping strings. */ 190 - void FAST_FUNC overlapping_strcpy(char *dst, const char *src) 277 + static void overlapping_strcpy(char *dst, const char *src) 191 278 { 192 279 /* Cheap optimization for dst == src case - 193 280 * better to have it here than in many callers. ··· 202 289 203 290 static int multiconvert(const char *arg, void *result, converter convert) 204 291 { 205 - if (*arg == '"' || *arg == '\'') { 206 - arg = utoa((unsigned char)arg[1]); 207 - } 208 - errno = 0; 292 + if (*arg == '"' || *arg == '\'') 293 + sprintf((char *)arg + strlen(arg), "%u", (unsigned char)arg[1]); 294 + //errno = 0; 209 295 convert(arg, result); 210 - if (errno) { 211 - bb_error_msg("invalid number '%s'", arg); 212 - return 1; 213 - } 296 + /* Unlike their Posix counterparts, simple_strtoll and 297 + * simple_strtoull do not set errno 298 + * 299 + * if (errno) { 300 + * printf("error invalid number '%s'", arg); 301 + * return 1; 302 + * } 303 + */ 214 304 return 0; 215 305 } 216 306 217 - static void FAST_FUNC conv_strtoull(const char *arg, void *result) 307 + static void conv_strtoull(const char *arg, void *result) 218 308 { 219 - /* Allow leading '+' - bb_strtoull() by itself does not allow it, 220 - * and probably shouldn't (other callers might require purely numeric 221 - * inputs to be allowed. 222 - */ 223 - if (arg[0] == '+') 224 - arg++; 225 - *(unsigned long long*)result = bb_strtoull(arg, NULL, 0); 226 309 /* both coreutils 6.10 and bash 3.2: 227 310 * $ printf '%x\n' -2 228 311 * fffffffffffffffe 229 312 * Mimic that: 230 313 */ 231 - if (errno) { 232 - *(unsigned long long*)result = bb_strtoll(arg, NULL, 0); 314 + if (arg[0] == '-') { 315 + *(unsigned long long *)result = simple_strtoll(arg, NULL, 16); 316 + return; 233 317 } 234 - } 235 - static void FAST_FUNC conv_strtoll(const char *arg, void *result) 236 - { 318 + /* Allow leading '+' - simple_strtoull() by itself does not allow it, 319 + * and probably shouldn't (other callers might require purely numeric 320 + * inputs to be allowed. 321 + */ 237 322 if (arg[0] == '+') 238 323 arg++; 239 - *(long long*)result = bb_strtoll(arg, NULL, 0); 324 + *(unsigned long long *)result = simple_strtoull(arg, NULL, 16); 240 325 } 241 - static void FAST_FUNC conv_strtod(const char *arg, void *result) 326 + 327 + static void conv_strtoll(const char *arg, void *result) 242 328 { 243 - char *end; 244 - /* Well, this one allows leading whitespace... so what? */ 245 - /* What I like much less is that "-" accepted too! :( */ 246 - *(double*)result = strtod(arg, &end); 247 - if (end[0]) { 248 - errno = ERANGE; 249 - *(double*)result = 0; 250 - } 329 + if (arg[0] == '+') 330 + arg++; 331 + *(long long *)result = simple_strtoll(arg, NULL, 16); 251 332 } 252 333 253 334 /* Callers should check errno to detect errors */ 254 335 static unsigned long long my_xstrtoull(const char *arg) 255 336 { 256 337 unsigned long long result; 338 + 257 339 if (multiconvert(arg, &result, conv_strtoull)) 258 340 result = 0; 259 341 return result; 260 342 } 343 + 261 344 static long long my_xstrtoll(const char *arg) 262 345 { 263 346 long long result; 347 + 264 348 if (multiconvert(arg, &result, conv_strtoll)) 265 349 result = 0; 266 350 return result; 267 351 } 268 - static double my_xstrtod(const char *arg) 269 - { 270 - double result; 271 - multiconvert(arg, &result, conv_strtod); 272 - return result; 273 - } 274 352 275 353 /* Handles %b; return 1 if output is to be short-circuited by \c */ 276 - static int print_esc_string(const char *str) 354 + static int print_esc_string(struct print_inf *inf, const char *str) 277 355 { 278 356 char c; 357 + 279 358 while ((c = *str) != '\0') { 280 359 str++; 281 360 if (c == '\\') { ··· 285 364 /* 2nd char is 0..7: skip leading '0' */ 286 365 str++; 287 366 } 288 - } 289 - else if (*str == 'c') { 367 + } else if (*str == 'c') { 290 368 return 1; 291 369 } 292 370 { 293 371 /* optimization: don't force arg to be on-stack, 294 - * use another variable for that. */ 372 + * use another variable for that. 373 + */ 295 374 const char *z = str; 296 - c = bb_process_escape_sequence(&z); 375 + 376 + c = process_escape_sequence(&z); 297 377 str = z; 298 378 } 299 379 } 300 - putchar(c); 380 + putchar_str(inf, c); 301 381 } 302 382 303 383 return 0; 304 384 } 305 385 306 - static void print_direc(char *format, unsigned fmt_length, 307 - int field_width, int precision, 308 - const char *argument) 386 + static void print_direc(struct print_inf *inf, char *format, unsigned int fmt_length, 387 + int field_width, int precision, 388 + const char *argument) 309 389 { 310 390 long long llv; 311 - double dv; 312 391 char saved; 313 392 char *have_prec, *have_width; 314 393 ··· 325 404 326 405 switch (format[fmt_length - 1]) { 327 406 case 'c': 328 - printf(format, *argument); 407 + printf_str(inf, format, *argument); 329 408 break; 330 409 case 'd': 331 410 case 'i': ··· 333 412 print_long: 334 413 if (!have_width) { 335 414 if (!have_prec) 336 - printf(format, llv); 415 + printf_str(inf, format, llv); 337 416 else 338 - printf(format, precision, llv); 417 + printf_str(inf, format, precision, llv); 339 418 } else { 340 419 if (!have_prec) 341 - printf(format, field_width, llv); 420 + printf_str(inf, format, field_width, llv); 342 421 else 343 - printf(format, field_width, precision, llv); 422 + printf_str(inf, format, field_width, precision, llv); 344 423 } 345 424 break; 346 425 case 'o': ··· 357 436 goto print_long; 358 437 } else { 359 438 /* Hope compiler will optimize it out by moving call 360 - * instruction after the ifs... */ 439 + * instruction after the ifs... 440 + */ 361 441 if (!have_width) { 362 442 if (!have_prec) 363 - printf(format, argument, /*unused:*/ argument, argument); 443 + printf_str(inf, format, argument, 444 + /*unused:*/ argument, argument); 364 445 else 365 - printf(format, precision, argument, /*unused:*/ argument); 446 + printf_str(inf, format, precision, 447 + argument, /*unused:*/ argument); 366 448 } else { 367 449 if (!have_prec) 368 - printf(format, field_width, argument, /*unused:*/ argument); 450 + printf_str(inf, format, field_width, 451 + argument, /*unused:*/ argument); 369 452 else 370 - printf(format, field_width, precision, argument); 453 + printf_str(inf, format, field_width, 454 + precision, argument); 371 455 } 372 456 break; 373 457 } 374 - case 'f': 375 - case 'e': 376 - case 'E': 377 - case 'g': 378 - case 'G': 379 - dv = my_xstrtod(argument); 380 - if (!have_width) { 381 - if (!have_prec) 382 - printf(format, dv); 383 - else 384 - printf(format, precision, dv); 385 - } else { 386 - if (!have_prec) 387 - printf(format, field_width, dv); 388 - else 389 - printf(format, field_width, precision, dv); 390 - } 391 458 break; 392 459 } /* switch */ 393 460 ··· 397 464 /* Handle params for "%*.*f". Negative numbers are ok (compat). */ 398 465 static int get_width_prec(const char *str) 399 466 { 400 - int v = bb_strtoi(str, NULL, 10); 401 - if (errno) { 402 - bb_error_msg("invalid number '%s'", str); 403 - v = 0; 404 - } 405 - return v; 467 + long v = simple_strtol(str, NULL, 10); 468 + 469 + /* Unlike its Posix counterpart, simple_strtol does not set errno 470 + * 471 + * if (errno) { 472 + * printf("error invalid number '%s'", str); 473 + * v = 0; 474 + * } 475 + */ 476 + return (int)v; 406 477 } 407 478 408 479 /* Print the text in FORMAT, using ARGV for arguments to any '%' directives. 409 - Return advanced ARGV. */ 410 - static char **print_formatted(char *f, char **argv, int *conv_err) 480 + * Return advanced ARGV. 481 + */ 482 + static char **print_formatted(struct print_inf *inf, char *f, char **argv, int *conv_err) 411 483 { 412 - char *direc_start; /* Start of % directive. */ 413 - unsigned direc_length; /* Length of % directive. */ 414 - int field_width; /* Arg to first '*' */ 415 - int precision; /* Arg to second '*' */ 484 + char *direc_start; /* Start of % directive. */ 485 + unsigned int direc_length; /* Length of % directive. */ 486 + int field_width; /* Arg to first '*' */ 487 + int precision; /* Arg to second '*' */ 416 488 char **saved_argv = argv; 417 489 418 490 for (; *f; ++f) { ··· 420 492 case '%': 421 493 direc_start = f++; 422 494 direc_length = 1; 423 - field_width = precision = 0; 495 + field_width = 0; 496 + precision = 0; 424 497 if (*f == '%') { 425 - bb_putchar('%'); 498 + putchar_str(inf, '%'); 426 499 break; 427 500 } 428 501 if (*f == 'b') { 429 502 if (*argv) { 430 - if (print_esc_string(*argv)) 503 + if (print_esc_string(inf, *argv)) 431 504 return saved_argv; /* causes main() to exit */ 432 505 ++argv; 433 506 } ··· 467 540 /* Remove "lLhz" size modifiers, repeatedly. 468 541 * bash does not like "%lld", but coreutils 469 542 * happily takes even "%Llllhhzhhzd"! 470 - * We are permissive like coreutils */ 471 - while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { 543 + * We are permissive like coreutils 544 + */ 545 + while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') 472 546 overlapping_strcpy(f, f + 1); 473 - } 474 547 /* Add "ll" if integer modifier, then print */ 475 548 { 476 - static const char format_chars[] ALIGN1 = "diouxXfeEgGcs"; 549 + static const char format_chars[] = "diouxXcs"; 477 550 char *p = strchr(format_chars, *f); 478 551 /* needed - try "printf %" without it */ 479 - if (p == NULL || *f == '\0') { 480 - bb_error_msg("%s: invalid format", direc_start); 552 + if (!p || *f == '\0') { 553 + printf("`%s': invalid format\n", direc_start); 481 554 /* causes main() to exit with error */ 482 555 return saved_argv - 1; 483 556 } 484 557 ++direc_length; 485 558 if (p - format_chars <= 5) { 486 559 /* it is one of "diouxX" */ 487 - p = xmalloc(direc_length + 3); 560 + p = malloc(direc_length + 3); 561 + if (!p) { 562 + /* exit with error */ 563 + return saved_argv - 1; 564 + } 488 565 memcpy(p, direc_start, direc_length); 489 566 p[direc_length + 1] = p[direc_length - 1]; 490 567 p[direc_length - 1] = 'l'; ··· 496 573 p = NULL; 497 574 } 498 575 if (*argv) { 499 - print_direc(direc_start, direc_length, field_width, 500 - precision, *argv++); 576 + print_direc(inf, direc_start, direc_length, 577 + field_width, precision, *argv++); 501 578 } else { 502 - print_direc(direc_start, direc_length, field_width, 503 - precision, ""); 579 + print_direc(inf, direc_start, direc_length, 580 + field_width, precision, ""); 504 581 } 505 582 *conv_err |= errno; 506 583 free(p); 507 584 } 508 585 break; 509 586 case '\\': 510 - if (*++f == 'c') { 587 + if (*++f == 'c') 511 588 return saved_argv; /* causes main() to exit */ 512 - } 513 - bb_putchar(bb_process_escape_sequence((const char **)&f)); 589 + putchar_str(inf, process_escape_sequence((const char **)&f)); 514 590 f--; 515 591 break; 516 592 default: 517 - putchar(*f); 593 + putchar_str(inf, *f); 518 594 } 519 595 } 520 596 521 597 return argv; 522 598 } 523 599 524 - int printf_main(int argc UNUSED_PARAM, char **argv) 600 + /** 601 + * printf_setexpr() - Implements the setexpr <name> fmt <format> command 602 + * 603 + * This function implements the format string evaluation for the 604 + * setexpr <name> fmt <format> <value> command. 605 + * 606 + * @str: Output string of the evaluated expression 607 + * @size: Length of @str buffer 608 + * @argc: Number of arguments 609 + * @argv: Argument list 610 + * @return: 0 if OK, 1 on error 611 + */ 612 + int printf_setexpr(char *str, size_t size, int argc, char *const *argv) 525 613 { 526 614 int conv_err; 527 615 char *format; 528 616 char **argv2; 617 + struct print_inf inf = { 618 + .str = str, 619 + .size = size, 620 + .offset = 0, 621 + .error = 0, 622 + }; 529 623 530 - /* We must check that stdout is not closed. 531 - * The reason for this is highly non-obvious. 532 - * printf_main is used from shell. 533 - * Shell must correctly handle 'printf "%s" foo' 534 - * if stdout is closed. With stdio, output gets shoveled into 535 - * stdout buffer, and even fflush cannot clear it out. It seems that 536 - * even if libc receives EBADF on write attempts, it feels determined 537 - * to output data no matter what. So it will try later, 538 - * and possibly will clobber future output. Not good. */ 539 - // TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? 540 - if (fcntl(1, F_GETFL) == -1) 541 - return 1; /* match coreutils 6.10 (sans error msg to stderr) */ 542 - //if (dup2(1, 1) != 1) - old way 543 - // return 1; 624 + if (!str || !size) 625 + return 1; 544 626 545 - /* bash builtin errors out on "printf '-%s-\n' foo", 546 - * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo". 547 - * We will mimic coreutils. */ 548 - if (argv[1] && argv[1][0] == '-' && argv[1][1] == '-' && !argv[1][2]) 549 - argv++; 550 - if (!argv[1]) { 551 - if (ENABLE_ASH_PRINTF 552 - && applet_name[0] != 'p' 553 - ) { 554 - bb_simple_error_msg("usage: printf FORMAT [ARGUMENT...]"); 555 - return 2; /* bash compat */ 556 - } 557 - bb_show_usage(); 558 - } 627 + inf.str[0] = '\0'; 559 628 560 - format = argv[1]; 561 - argv2 = argv + 2; 629 + format = argv[0]; 630 + argv2 = (char **)argv + 1; 562 631 563 632 conv_err = 0; 564 - do { 565 - argv = argv2; 566 - argv2 = print_formatted(format, argv, &conv_err); 567 - } while (argv2 > argv && *argv2); 633 + argv = argv2; 634 + /* In case any print_str call raises an error inf.error will be 635 + * set after print_formatted returns. 636 + */ 637 + argv2 = print_formatted(&inf, format, (char **)argv, &conv_err); 568 638 569 639 /* coreutils compat (bash doesn't do this): 570 - if (*argv) 571 - fprintf(stderr, "excess args ignored"); 572 - */ 640 + *if (*argv) 641 + * fprintf(stderr, "excess args ignored"); 642 + */ 573 643 574 - return (argv2 < argv) /* if true, print_formatted errored out */ 575 - || conv_err; /* print_formatted saw invalid number */ 644 + return (argv2 < argv) || /* if true, print_formatted errored out */ 645 + conv_err || /* print_formatted saw invalid number */ 646 + inf.error; /* print_str reported error */ 576 647 }
+8
cmd/printf.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + 3 + #ifndef __PRINTF_H 4 + #define __PRINTF_H 5 + 6 + int printf_setexpr(char *str, size_t size, int argc, char *const *argv); 7 + 8 + #endif
+33 -4
cmd/setexpr.c
··· 11 11 #include <common.h> 12 12 #include <config.h> 13 13 #include <command.h> 14 + #include <ctype.h> 14 15 #include <env.h> 15 16 #include <log.h> 16 17 #include <malloc.h> 17 18 #include <mapmem.h> 18 19 #include <linux/sizes.h> 20 + #include "printf.h" 21 + 22 + #define MAX_STR_LEN 128 19 23 20 24 /** 21 25 * struct expr_arg: Holds an argument to an expression ··· 370 374 int w; 371 375 372 376 /* 373 - * We take 3, 5, or 6 arguments: 377 + * We take 3, 5, or 6 arguments, except fmt operation, which 378 + * takes 4 to 8 arguments (limited by _maxargs): 374 379 * 3 : setexpr name value 375 380 * 5 : setexpr name val1 op val2 376 381 * setexpr name [g]sub r s 377 382 * 6 : setexpr name [g]sub r s t 383 + * setexpr name fmt format [val1] [val2] [val3] [val4] 378 384 */ 379 385 380 - /* > 6 already tested by max command args */ 381 - if ((argc < 3) || (argc == 4)) 386 + if (argc < 3) 382 387 return CMD_RET_USAGE; 383 388 384 389 w = cmd_get_data_size(argv[0], 4); 385 390 386 391 if (get_arg(argv[2], w, &aval)) 387 392 return CMD_RET_FAILURE; 393 + 394 + /* format string assignment: "setexpr name fmt %d value" */ 395 + if (strcmp(argv[2], "fmt") == 0 && IS_ENABLED(CONFIG_CMD_SETEXPR_FMT)) { 396 + char str[MAX_STR_LEN]; 397 + int result; 398 + 399 + if (argc == 3) 400 + return CMD_RET_USAGE; 401 + 402 + result = printf_setexpr(str, sizeof(str), argc - 3, &argv[3]); 403 + if (result) 404 + return result; 405 + 406 + return env_set(argv[1], str); 407 + } 408 + 409 + if (argc == 4 || argc > 6) 410 + return CMD_RET_USAGE; 388 411 389 412 /* plain assignment: "setexpr name value" */ 390 413 if (argc == 3) { ··· 495 518 } 496 519 497 520 U_BOOT_CMD( 498 - setexpr, 6, 0, do_setexpr, 521 + setexpr, 8, 0, do_setexpr, 499 522 "set environment variable as the result of eval expression", 500 523 "[.b, .w, .l, .s] name [*]value1 <op> [*]value2\n" 501 524 " - set environment variable 'name' to the result of the evaluated\n" ··· 505 528 " memory addresses (*)\n" 506 529 "setexpr[.b, .w, .l] name [*]value\n" 507 530 " - load a value into a variable" 531 + #ifdef CONFIG_CMD_SETEXPR_FMT 532 + "\n" 533 + "setexpr name fmt <format> [value1] [value2] [value3] [value4]\n" 534 + " - set environment variable 'name' to the result of the bash like\n" 535 + " format string evaluation of value." 536 + #endif 508 537 #ifdef CONFIG_REGEX 509 538 "\n" 510 539 "setexpr name gsub r s [t]\n"