1/* vi: set sw=4 ts=4: */ 2/* printf - format and print data 3 4 Copyright 1999 Dave Cinege 5 Portions copyright (C) 1990-1996 Free Software Foundation, Inc. 6 7 Licensed under GPL v2 or later, see file LICENSE in this tarball for details. 8*/ 9 10/* Usage: printf format [argument...] 11 12 A front end to the printf function that lets it be used from the shell. 13 14 Backslash escapes: 15 16 \" = double quote 17 \\ = backslash 18 \a = alert (bell) 19 \b = backspace 20 \c = produce no further output 21 \f = form feed 22 \n = new line 23 \r = carriage return 24 \t = horizontal tab 25 \v = vertical tab 26 \0ooo = octal number (ooo is 0 to 3 digits) 27 \xhhh = hexadecimal number (hhh is 1 to 3 digits) 28 29 Additional directive: 30 31 %b = print an argument string, interpreting backslash escapes 32 33 The 'format' argument is re-used as many times as necessary 34 to convert all of the given arguments. 35 36 David MacKenzie <djm@gnu.ai.mit.edu> */ 37 38 39// 19990508 Busy Boxed! Dave Cinege 40 41#include "libbb.h" 42 43typedef void (*converter)(const char *arg, void *result); 44 45static void multiconvert(const char *arg, void *result, converter convert) 46{ 47 char s[sizeof(int)*3 + 2]; 48 49 if (*arg == '"' || *arg == '\'') { 50 sprintf(s, "%d", (unsigned char)arg[1]); 51 arg = s; 52 } 53 convert(arg, result); 54 /* if there was conversion error, print unconverted string */ 55 if (errno) 56 fputs(arg, stderr); 57} 58 59static void conv_strtoul(const char *arg, void *result) 60{ 61 *(unsigned long*)result = bb_strtoul(arg, NULL, 0); 62} 63static void conv_strtol(const char *arg, void *result) 64{ 65 *(long*)result = bb_strtol(arg, NULL, 0); 66} 67static void conv_strtod(const char *arg, void *result) 68{ 69 char *end; 70 /* Well, this one allows leading whitespace... so what */ 71 /* What I like much less is that "-" is accepted too! :( */ 72 *(double*)result = strtod(arg, &end); 73 if (end[0]) errno = ERANGE; 74} 75 76static unsigned long my_xstrtoul(const char *arg) 77{ 78 unsigned long result; 79 multiconvert(arg, &result, conv_strtoul); 80 return result; 81} 82 83static long my_xstrtol(const char *arg) 84{ 85 long result; 86 multiconvert(arg, &result, conv_strtol); 87 return result; 88} 89 90static double my_xstrtod(const char *arg) 91{ 92 double result; 93 multiconvert(arg, &result, conv_strtod); 94 return result; 95} 96 97static 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 110static void print_direc(char *start, size_t length, int field_width, int precision, 111 const char *argument) 112{ 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]) { 120 case 'd': 121 case 'i': 122 if (field_width < 0) { 123 if (precision < 0) 124 printf(p, my_xstrtol(argument)); 125 else 126 printf(p, precision, my_xstrtol(argument)); 127 } else { 128 if (precision < 0) 129 printf(p, field_width, my_xstrtol(argument)); 130 else 131 printf(p, field_width, precision, my_xstrtol(argument)); 132 } 133 break; 134 case 'o': 135 case 'u': 136 case 'x': 137 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)); 143 } else { 144 if (precision < 0) 145 printf(p, field_width, my_xstrtoul(argument)); 146 else 147 printf(p, field_width, precision, my_xstrtoul(argument)); 148 } 149 break; 150 case 'f': 151 case 'e': 152 case 'E': 153 case 'g': 154 case 'G': 155 if (field_width < 0) { 156 if (precision < 0) 157 printf(p, my_xstrtod(argument)); 158 else 159 printf(p, precision, my_xstrtod(argument)); 160 } else { 161 if (precision < 0) 162 printf(p, field_width, my_xstrtod(argument)); 163 else 164 printf(p, field_width, precision, my_xstrtod(argument)); 165 } 166 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 192static int print_formatted(char *format, int argc, char **argv) 193{ 194 int save_argc = argc; /* Preserve original value. */ 195 char *f; /* Pointer into 'format'. */ 196 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) { 202 switch (*f) { 203 case '%': 204 direc_start = f++; 205 direc_length = 1; 206 field_width = precision = -1; 207 if (*f == '%') { 208 putchar('%'); 209 break; 210 } 211 if (*f == 'b') { 212 if (argc > 0) { 213 print_esc_string(*argv); 214 ++argv; 215 --argc; 216 } 217 break; 218 } 219 if (strchr("-+ #", *f)) { 220 ++f; 221 ++direc_length; 222 } 223 if (*f == '*') { 224 ++f; 225 ++direc_length; 226 if (argc > 0) { 227 field_width = my_xstrtoul(*argv); 228 ++argv; 229 --argc; 230 } else 231 field_width = 0; 232 } else { 233 while (isdigit(*f)) { 234 ++f; 235 ++direc_length; 236 } 237 } 238 if (*f == '.') { 239 ++f; 240 ++direc_length; 241 if (*f == '*') { 242 ++f; 243 ++direc_length; 244 if (argc > 0) { 245 precision = my_xstrtoul(*argv); 246 ++argv; 247 --argc; 248 } else 249 precision = 0; 250 } else 251 while (isdigit(*f)) { 252 ++f; 253 ++direc_length; 254 } 255 } 256 if (*f == 'l' || *f == 'L' || *f == 'h') { 257 ++f; 258 ++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, ""); 273 break; 274 case '\\': 275 if (*++f == 'c') 276 exit(0); 277 putchar(bb_process_escape_sequence((const char **)&f)); 278 f--; 279 break; 280 default: 281 putchar(*f); 282 } 283 } 284 285 return save_argc - argc; 286} 287 288int printf_main(int argc, char **argv); 289int printf_main(int argc, char **argv) 290{ 291 char *format; 292 int args_used; 293 294 if (argc <= 1 || argv[1][0] == '-') { 295 bb_show_usage(); 296 } 297 298 format = argv[1]; 299 argc -= 2; 300 argv += 2; 301 302 do { 303 args_used = print_formatted(format, argc, argv); 304 argc -= args_used; 305 argv += args_used; 306 } while (args_used > 0 && argc > 0); 307 308/* if (argc > 0) 309 fprintf(stderr, "excess args ignored"); 310*/ 311 312 return EXIT_SUCCESS; 313} 314