aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'toys/pending/printf.c')
-rw-r--r--toys/pending/printf.c269
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();
+}