diff options
-rw-r--r-- | toys/pending/printf.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/toys/pending/printf.c b/toys/pending/printf.c new file mode 100644 index 00000000..0970c8cd --- /dev/null +++ b/toys/pending/printf.c @@ -0,0 +1,269 @@ +/* printf.c - Format and Print the data. + * + * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com> + * Copyright 2014 Kyungwan Han <asura321@gmail.com> + * + * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html + +USE_PRINTF(NEWTOY(printf, "<1", TOYFLAG_USR|TOYFLAG_BIN)) + +config PRINTF + bool "printf" + default n + help + usage: printf Format [Arguments..] + + Format and print ARGUMENT(s) according to FORMAT. + Format is 'C' control output as 'C' printf. +*/ +#define FOR_printf +#include "toys.h" + +GLOBALS( + char *hv_w; + char *hv_p; + int encountered; +) + +// Calculate width and precision from format string +static int get_w_p() +{ + char *ptr, *str = *toys.optargs; + + errno = 0; + if (*str == '-') str++; + long value = strtol(str, &ptr, 10); + if (errno || (ptr && (*ptr != '\0' || ptr == str))) + perror_msg("Invalid num %s", *toys.optargs); + if (*--str == '-') return (int)(-1 * value); + return value; +} + +// Add ll and L to Interger and floating point formats respectively. +static char *get_format(char *f) +{ + int len = strlen(f); + char last = f[--len], *post = ""; + + f[len] = '\0'; + if (strchr("diouxX", last)) post = "ll"; // add ll to integer modifier. + else if (strchr("feEgG", last)) post = "L"; // add L to float modifier. + return xmprintf("%s%s%c", f, post, last); +} + +// Print arguments with corresponding conversion and width and precision. +static void print(char *fmt, int w, int p, int l) +{ + char *ptr = (fmt+l-1), *ep = NULL, *format = NULL; + long long val; + long double dval; + + errno = 0; + switch (*ptr) { + case 'd': /*FALL_THROUGH*/ + case 'i': /*FALL_THROUGH*/ + case 'o': /*FALL_THROUGH*/ + case 'u': /*FALL_THROUGH*/ + case 'x': /*FALL_THROUGH*/ + case 'X': + if (!*toys.optargs) val = 0; + else { + if (**toys.optargs == '\'' || **toys.optargs == '"') + val = *((*toys.optargs) + 1); + else { + val = strtoll(*toys.optargs, &ep, 0); + if (errno || (ep && (*ep != '\0' || ep == *toys.optargs))) { + perror_msg("Invalid num %s", *toys.optargs); + val = 0; + } + } + } + format = get_format(fmt); + TT.hv_w ? (TT.hv_p ? printf(format, w, p, val) : printf(format, w, val)) + : (TT.hv_p ? printf(format, p, val) : printf(format, val)); + break; + case 'g': /*FALL_THROUGH*/ + case 'e': /*FALL_THROUGH*/ + case 'E': /*FALL_THROUGH*/ + case 'G': /*FALL_THROUGH*/ + case 'f': + if (*toys.optargs) { + dval = strtold(*toys.optargs, &ep); + if (errno || (ep && (*ep != '\0' || ep == *toys.optargs))) { + perror_msg("Invalid num %s", *toys.optargs); + dval = 0; + } + } else dval = 0; + format = get_format(fmt); + TT.hv_w ? (TT.hv_p ? printf(format, w, p, dval):printf(format, w, dval)) + : (TT.hv_p ? printf(format, p, dval) : printf(format, dval)); + break; + case 's': + { + char *str = (*toys.optargs ? *toys.optargs : ""); + TT.hv_w ? (TT.hv_p ? printf(fmt,w,p,str): printf(fmt, w, str)) + : (TT.hv_p ? printf(fmt, p, str) : printf(fmt, str)); + } + break; + case 'c': + printf(fmt, (*toys.optargs ? **toys.optargs : '\0')); + break; + } + if (format) free(format); +} + +// Handle the escape sequences. +static int handle_slash(char **esc_val) +{ + char *ptr = *esc_val; + int esc_length = 0; + unsigned base = 0, num = 0, result = 0, count = 0; + + /* + * Hex escape sequence have only 1 or 2 digits, xHH. Oct escape sequence + * have 1,2 or 3 digits, xHHH. Leading "0" (\0HHH) we are ignoring. + */ + if (*ptr == 'x') { + ptr++; + esc_length++; + base = 16; + } else if (isdigit(*ptr)) base = 8; + + while (esc_length < 3 && base) { + num = tolower(*ptr) - '0'; + if (num > 10) num += ('0' - 'a' + 10); + if (num >= base) { + if (base == 16) { + esc_length--; + if (!esc_length) {// Invalid hex value eg. /xvd, print as it is /xvd + result = '\\'; + ptr--; + } + } + break; + } + esc_length++; + count = result = (count * base) + num; + ptr++; + } + if (base) { + ptr--; + *esc_val = ptr; + return (char)result; + } else { + switch (*ptr) { + case 'n': result = '\n'; break; + case 't': result = '\t'; break; + case 'e': result = (char)27; break; + case 'b': result = '\b'; break; + case 'a': result = '\a'; break; + case 'f': result = '\f'; break; + case 'v': result = '\v'; break; + case 'r': result = '\r'; break; + case '\\': result = '\\'; break; + default : + result = '\\'; + ptr--; // Let pointer pointing to / we will increment after returning. + break; + } + } + *esc_val = ptr; + return (char)result; +} + +// Handle "%b" option with '\' interpreted. +static void print_esc_str(char *str) +{ + for (; *str; str++) { + if (*str == '\\') { + str++; + xputc(handle_slash(&str)); //print corresponding char + } else xputc(*str); + } +} + +// Prase the format string and print. +static void parse_print(char *f) +{ + char *start, *p, format_specifiers[] = "diouxXfeEgGcs"; + int len = 0, width = 0, prec = 0; + + while (*f) { + switch (*f) { + case '%': + start = f++; + len++; + if (*f == '%') { + xputc('%'); + break; + } + if (*f == 'b') { + if (*toys.optargs) { + print_esc_str(*toys.optargs++); + TT.encountered = 1; + } else print_esc_str(""); + break; + } + if (strchr("-+# ", *f)) f++, len++; + if (*f == '*') { + f++, len++; + if (*toys.optargs) { + width = get_w_p(); + toys.optargs++; + } + } else { + while (isdigit(*f)) f++, len++; + } + if (*f == '.') { + f++, len++; + if (*f == '*') { + f++, len++; + if (*toys.optargs) { + prec = get_w_p(); + toys.optargs++; + } + } else { + while (isdigit(*f)) f++, len++; + } + } + if (!(p = strchr(format_specifiers, *f))) + perror_exit("Missing OR Invalid format specifier"); + else { + len++; + TT.hv_p = strstr(start, ".*"); + TT.hv_w = strchr(start, '*'); + //pitfall: handle diff b/w * and .* + if ((TT.hv_w-1) == TT.hv_p) TT.hv_w = NULL; + memcpy((p = xzalloc(len+1)), start, len); + print(p, width, prec, len); + if (*toys.optargs) toys.optargs++; + free(p); + p = NULL; + } + TT.encountered = 1; + break; + case '\\': + if (f[1]) { + if (*++f == 'c') exit(0); //Got '\c', so no further output + xputc(handle_slash(&f)); + } else xputc(*f); + break; + default: + xputc(*f); + break; + } + f++; + len = 0; + } +} + +void printf_main(void) +{ + char *format = *toys.optargs++; + + TT.encountered = 0; + parse_print(format); //printf acc. to format. + //Re-use FORMAT arg as necessary to convert all given ARGS. + while (*toys.optargs && TT.encountered) parse_print(format); + xflush(); +} |