Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/coreutils/printf.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/coreutils/printf.c
r1765 r2725 5 5 Portions copyright (C) 1990-1996 Free Software Foundation, Inc. 6 6 7 Licensed under GPL v2 or later, see file LICENSE in this tarball for details.7 Licensed under GPLv2 or later, see file LICENSE in this source tree. 8 8 */ 9 9 … … 34 34 to convert all of the given arguments. 35 35 36 David MacKenzie <djm@gnu.ai.mit.edu> */37 36 David MacKenzie <djm@gnu.ai.mit.edu> 37 */ 38 38 39 39 // 19990508 Busy Boxed! Dave Cinege … … 41 41 #include "libbb.h" 42 42 43 typedef void (*converter)(const char *arg, void *result); 44 45 static void multiconvert(const char *arg, void *result, converter convert) 46 { 47 char s[sizeof(int)*3 + 2]; 48 43 /* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it. 44 * They report it: 45 * bash: printf: XXX: invalid number 46 * printf: XXX: expected a numeric value 47 * bash: printf: 123XXX: invalid number 48 * printf: 123XXX: value not completely converted 49 * but then they use 0 (or partially converted numeric prefix) as a value 50 * and continue. They exit with 1 in this case. 51 * Both accept insane field width/precision (e.g. %9999999999.9999999999d). 52 * Both print error message and assume 0 if %*.*f width/precision is "bad" 53 * (but negative numbers are not "bad"). 54 * Both accept negative numbers for %u specifier. 55 * 56 * We try to be compatible. 57 */ 58 59 typedef void FAST_FUNC (*converter)(const char *arg, void *result); 60 61 static int multiconvert(const char *arg, void *result, converter convert) 62 { 49 63 if (*arg == '"' || *arg == '\'') { 50 sprintf(s, "%d",(unsigned char)arg[1]);51 arg = s;52 }64 arg = utoa((unsigned char)arg[1]); 65 } 66 errno = 0; 53 67 convert(arg, result); 54 /* if there was conversion error, print unconverted string */ 55 if (errno) 56 fputs(arg, stderr); 57 } 58 59 static void conv_strtoul(const char *arg, void *result) 60 { 61 *(unsigned long*)result = bb_strtoul(arg, NULL, 0); 62 } 63 static void conv_strtol(const char *arg, void *result) 64 { 65 *(long*)result = bb_strtol(arg, NULL, 0); 66 } 67 static void conv_strtod(const char *arg, void *result) 68 if (errno) { 69 bb_error_msg("invalid number '%s'", arg); 70 return 1; 71 } 72 return 0; 73 } 74 75 static void FAST_FUNC conv_strtoull(const char *arg, void *result) 76 { 77 *(unsigned long long*)result = bb_strtoull(arg, NULL, 0); 78 /* both coreutils 6.10 and bash 3.2: 79 * $ printf '%x\n' -2 80 * fffffffffffffffe 81 * Mimic that: 82 */ 83 if (errno) { 84 *(unsigned long long*)result = bb_strtoll(arg, NULL, 0); 85 } 86 } 87 static void FAST_FUNC conv_strtoll(const char *arg, void *result) 88 { 89 *(long long*)result = bb_strtoll(arg, NULL, 0); 90 } 91 static void FAST_FUNC conv_strtod(const char *arg, void *result) 68 92 { 69 93 char *end; 70 /* Well, this one allows leading whitespace... so what */71 /* What I like much less is that "-" isaccepted too! :( */94 /* Well, this one allows leading whitespace... so what? */ 95 /* What I like much less is that "-" accepted too! :( */ 72 96 *(double*)result = strtod(arg, &end); 73 if (end[0]) errno = ERANGE; 74 } 75 76 static unsigned long my_xstrtoul(const char *arg) 77 { 78 unsigned long result; 79 multiconvert(arg, &result, conv_strtoul); 97 if (end[0]) { 98 errno = ERANGE; 99 *(double*)result = 0; 100 } 101 } 102 103 /* Callers should check errno to detect errors */ 104 static unsigned long long my_xstrtoull(const char *arg) 105 { 106 unsigned long long result; 107 if (multiconvert(arg, &result, conv_strtoull)) 108 result = 0; 80 109 return result; 81 110 } 82 83 static long my_xstrtol(const char *arg) 84 { 85 long result;86 multiconvert(arg, &result, conv_strtol);111 static long long my_xstrtoll(const char *arg) 112 { 113 long long result; 114 if (multiconvert(arg, &result, conv_strtoll)) 115 result = 0; 87 116 return result; 88 117 } 89 90 118 static double my_xstrtod(const char *arg) 91 119 { … … 95 123 } 96 124 97 static void print_esc_string(char *str) 98 { 99 for (; *str; str++) { 100 if (*str == '\\') { 101 str++; 102 putchar(bb_process_escape_sequence((const char **)&str)); 103 } else { 104 putchar(*str); 105 } 106 107 } 108 } 109 110 static void print_direc(char *start, size_t length, int field_width, int precision, 125 static void print_esc_string(const char *str) 126 { 127 char c; 128 while ((c = *str) != '\0') { 129 str++; 130 if (c == '\\') 131 c = bb_process_escape_sequence(&str); 132 putchar(c); 133 } 134 } 135 136 static void print_direc(char *format, unsigned fmt_length, 137 int field_width, int precision, 111 138 const char *argument) 112 139 { 113 char *p; /* Null-terminated copy of % directive. */ 114 115 p = xmalloc((unsigned) (length + 1)); 116 strncpy(p, start, length); 117 p[length] = 0; 118 119 switch (p[length - 1]) { 140 long long llv; 141 double dv; 142 char saved; 143 char *have_prec, *have_width; 144 145 saved = format[fmt_length]; 146 format[fmt_length] = '\0'; 147 148 have_prec = strstr(format, ".*"); 149 have_width = strchr(format, '*'); 150 if (have_width - 1 == have_prec) 151 have_width = NULL; 152 153 errno = 0; 154 155 switch (format[fmt_length - 1]) { 156 case 'c': 157 printf(format, *argument); 158 break; 120 159 case 'd': 121 160 case 'i': 122 if (field_width < 0) { 123 if (precision < 0) 124 printf(p, my_xstrtol(argument)); 161 llv = my_xstrtoll(argument); 162 print_long: 163 if (!have_width) { 164 if (!have_prec) 165 printf(format, llv); 125 166 else 126 printf( p, precision, my_xstrtol(argument));167 printf(format, precision, llv); 127 168 } else { 128 if ( precision < 0)129 printf( p, field_width, my_xstrtol(argument));169 if (!have_prec) 170 printf(format, field_width, llv); 130 171 else 131 printf( p, field_width, precision, my_xstrtol(argument));172 printf(format, field_width, precision, llv); 132 173 } 133 174 break; … … 136 177 case 'x': 137 178 case 'X': 138 if (field_width < 0) { 139 if (precision < 0) 140 printf(p, my_xstrtoul(argument)); 141 else 142 printf(p, precision, my_xstrtoul(argument)); 179 llv = my_xstrtoull(argument); 180 /* cheat: unsigned long and long have same width, so... */ 181 goto print_long; 182 case 's': 183 /* Are char* and long long the same? */ 184 if (sizeof(argument) == sizeof(llv)) { 185 llv = (long long)(ptrdiff_t)argument; 186 goto print_long; 143 187 } else { 144 if (precision < 0) 145 printf(p, field_width, my_xstrtoul(argument)); 146 else 147 printf(p, field_width, precision, my_xstrtoul(argument)); 188 /* Hope compiler will optimize it out by moving call 189 * instruction after the ifs... */ 190 if (!have_width) { 191 if (!have_prec) 192 printf(format, argument, /*unused:*/ argument, argument); 193 else 194 printf(format, precision, argument, /*unused:*/ argument); 195 } else { 196 if (!have_prec) 197 printf(format, field_width, argument, /*unused:*/ argument); 198 else 199 printf(format, field_width, precision, argument); 200 } 201 break; 148 202 } 149 break;150 203 case 'f': 151 204 case 'e': … … 153 206 case 'g': 154 207 case 'G': 155 if (field_width < 0) { 156 if (precision < 0) 157 printf(p, my_xstrtod(argument)); 208 dv = my_xstrtod(argument); 209 if (!have_width) { 210 if (!have_prec) 211 printf(format, dv); 158 212 else 159 printf( p, precision, my_xstrtod(argument));213 printf(format, precision, dv); 160 214 } else { 161 if ( precision < 0)162 printf( p, field_width, my_xstrtod(argument));215 if (!have_prec) 216 printf(format, field_width, dv); 163 217 else 164 printf( p, field_width, precision, my_xstrtod(argument));218 printf(format, field_width, precision, dv); 165 219 } 166 220 break; 167 case 'c': 168 printf(p, *argument); 169 break; 170 case 's': 171 if (field_width < 0) { 172 if (precision < 0) 173 printf(p, argument); 174 else 175 printf(p, precision, argument); 176 } else { 177 if (precision < 0) 178 printf(p, field_width, argument); 179 else 180 printf(p, field_width, precision, argument); 181 } 182 break; 183 } 184 185 free(p); 186 } 187 188 /* Print the text in FORMAT, using ARGV (with ARGC elements) for 189 arguments to any '%' directives. 190 Return the number of elements of ARGV used. */ 191 192 static int print_formatted(char *format, int argc, char **argv) 193 { 194 int save_argc = argc; /* Preserve original value. */ 195 char *f; /* Pointer into 'format'. */ 221 } /* switch */ 222 223 format[fmt_length] = saved; 224 } 225 226 /* Handle params for "%*.*f". Negative numbers are ok (compat). */ 227 static int get_width_prec(const char *str) 228 { 229 int v = bb_strtoi(str, NULL, 10); 230 if (errno) { 231 bb_error_msg("invalid number '%s'", str); 232 v = 0; 233 } 234 return v; 235 } 236 237 /* Print the text in FORMAT, using ARGV for arguments to any '%' directives. 238 Return advanced ARGV. */ 239 static char **print_formatted(char *f, char **argv, int *conv_err) 240 { 196 241 char *direc_start; /* Start of % directive. */ 197 size_t direc_length; /* Length of % directive. */ 198 int field_width; /* Arg to first '*', or -1 if none. */ 199 int precision; /* Arg to second '*', or -1 if none. */ 200 201 for (f = format; *f; ++f) { 242 unsigned direc_length; /* Length of % directive. */ 243 int field_width; /* Arg to first '*' */ 244 int precision; /* Arg to second '*' */ 245 char **saved_argv = argv; 246 247 for (; *f; ++f) { 202 248 switch (*f) { 203 249 case '%': 204 250 direc_start = f++; 205 251 direc_length = 1; 206 field_width = precision = -1;252 field_width = precision = 0; 207 253 if (*f == '%') { 208 putchar('%');254 bb_putchar('%'); 209 255 break; 210 256 } 211 257 if (*f == 'b') { 212 if ( argc > 0) {258 if (*argv) { 213 259 print_esc_string(*argv); 214 260 ++argv; 215 --argc;216 261 } 217 262 break; … … 224 269 ++f; 225 270 ++direc_length; 226 if (argc > 0) { 227 field_width = my_xstrtoul(*argv); 228 ++argv; 229 --argc; 230 } else 231 field_width = 0; 271 if (*argv) 272 field_width = get_width_prec(*argv++); 232 273 } else { 233 274 while (isdigit(*f)) { … … 242 283 ++f; 243 284 ++direc_length; 244 if (argc > 0) { 245 precision = my_xstrtoul(*argv); 246 ++argv; 247 --argc; 248 } else 249 precision = 0; 250 } else 285 if (*argv) 286 precision = get_width_prec(*argv++); 287 } else { 251 288 while (isdigit(*f)) { 252 289 ++f; 253 290 ++direc_length; 254 291 } 255 } 256 if (*f == 'l' || *f == 'L' || *f == 'h') { 257 ++f; 292 } 293 } 294 295 /* Remove "lLhz" size modifiers, repeatedly. 296 * bash does not like "%lld", but coreutils 297 * happily takes even "%Llllhhzhhzd"! 298 * We are permissive like coreutils */ 299 while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { 300 overlapping_strcpy(f, f + 1); 301 } 302 /* Add "ll" if integer modifier, then print */ 303 { 304 static const char format_chars[] ALIGN1 = "diouxXfeEgGcs"; 305 char *p = strchr(format_chars, *f); 306 /* needed - try "printf %" without it */ 307 if (p == NULL) { 308 bb_error_msg("%s: invalid format", direc_start); 309 /* causes main() to exit with error */ 310 return saved_argv - 1; 311 } 258 312 ++direc_length; 259 } 260 /* 261 if (!strchr ("diouxXfeEgGcs", *f)) 262 fprintf(stderr, "%%%c: invalid directive", *f); 263 */ 264 ++direc_length; 265 if (argc > 0) { 266 print_direc(direc_start, direc_length, field_width, 267 precision, *argv); 268 ++argv; 269 --argc; 270 } else 271 print_direc(direc_start, direc_length, field_width, 272 precision, ""); 313 if (p - format_chars <= 5) { 314 /* it is one of "diouxX" */ 315 p = xmalloc(direc_length + 3); 316 memcpy(p, direc_start, direc_length); 317 p[direc_length + 1] = p[direc_length - 1]; 318 p[direc_length - 1] = 'l'; 319 p[direc_length] = 'l'; 320 //bb_error_msg("<%s>", p); 321 direc_length += 2; 322 direc_start = p; 323 } else { 324 p = NULL; 325 } 326 if (*argv) { 327 print_direc(direc_start, direc_length, field_width, 328 precision, *argv++); 329 } else { 330 print_direc(direc_start, direc_length, field_width, 331 precision, ""); 332 } 333 *conv_err |= errno; 334 free(p); 335 } 273 336 break; 274 337 case '\\': 275 if (*++f == 'c') 276 exit(0); 277 putchar(bb_process_escape_sequence((const char **)&f)); 338 if (*++f == 'c') { 339 return saved_argv; /* causes main() to exit */ 340 } 341 bb_putchar(bb_process_escape_sequence((const char **)&f)); 278 342 f--; 279 343 break; … … 283 347 } 284 348 285 return save_argc - argc;286 } 287 288 int printf_main(int argc , char **argv);289 int printf_main(int argc, char **argv) 290 { 349 return argv; 350 } 351 352 int printf_main(int argc UNUSED_PARAM, char **argv) 353 { 354 int conv_err; 291 355 char *format; 292 int args_used; 293 294 if (argc <= 1 || argv[1][0] == '-') { 356 char **argv2; 357 358 /* We must check that stdout is not closed. 359 * The reason for this is highly non-obvious. 360 * printf_main is used from shell. 361 * Shell must correctly handle 'printf "%s" foo' 362 * if stdout is closed. With stdio, output gets shoveled into 363 * stdout buffer, and even fflush cannot clear it out. It seems that 364 * even if libc receives EBADF on write attempts, it feels determined 365 * to output data no matter what. So it will try later, 366 * and possibly will clobber future output. Not good. */ 367 // TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? 368 if (fcntl(1, F_GETFL) == -1) 369 return 1; /* match coreutils 6.10 (sans error msg to stderr) */ 370 //if (dup2(1, 1) != 1) - old way 371 // return 1; 372 373 /* bash builtin errors out on "printf '-%s-\n' foo", 374 * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo". 375 * We will mimic coreutils. */ 376 if (argv[1] && argv[1][0] == '-' && argv[1][1] == '-' && !argv[1][2]) 377 argv++; 378 if (!argv[1]) { 379 if (ENABLE_ASH_BUILTIN_PRINTF 380 && applet_name[0] != 'p' 381 ) { 382 bb_error_msg("usage: printf FORMAT [ARGUMENT...]"); 383 return 2; /* bash compat */ 384 } 295 385 bb_show_usage(); 296 386 } 297 387 298 388 format = argv[1]; 299 arg c -=2;300 argv += 2; 301 389 argv2 = argv + 2; 390 391 conv_err = 0; 302 392 do { 303 arg s_used = print_formatted(format, argc, argv);304 arg c -= args_used;305 argv += args_used;306 } while (args_used > 0 && argc > 0); 307 308 /* if (argc > 0)393 argv = argv2; 394 argv2 = print_formatted(format, argv, &conv_err); 395 } while (argv2 > argv && *argv2); 396 397 /* coreutils compat (bash doesn't do this): 398 if (*argv) 309 399 fprintf(stderr, "excess args ignored"); 310 */ 311 312 return EXIT_SUCCESS; 313 } 400 */ 401 402 return (argv2 < argv) /* if true, print_formatted errored out */ 403 || conv_err; /* print_formatted saw invalid number */ 404 }
Note:
See TracChangeset
for help on using the changeset viewer.