changeset 1256:2fe64bc16a61

An implementation of __printf__ is attached.
author Ashwini Sharma <ak.ashwini1981@gmail.com>
date Sun, 13 Apr 2014 15:58:20 -0500
parents 69b34a10f1e7
children eb19245b94b6
files toys/pending/printf.c
diffstat 1 files changed, 269 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/pending/printf.c	Sun Apr 13 15:58:20 2014 -0500
@@ -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();
+}