printf.c revision cc39d95a344240bec8f0c74a02c74d54620b735d
10d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma/* printf.c - Format and Print the data.
20d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma *
30d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com>
40d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma * Copyright 2014 Kyungwan Han <asura321@gmail.com>
50d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma *
60d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
70d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
80d8467b371d93bff03f59a8f11f01094f7618372Ashwini SharmaUSE_PRINTF(NEWTOY(printf, "<1", TOYFLAG_USR|TOYFLAG_BIN))
90d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
100d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharmaconfig PRINTF
1187fd25f20b86731dd01332ce828424e7047ef29dRob Landley  bool "printf"
1287fd25f20b86731dd01332ce828424e7047ef29dRob Landley  default n
1387fd25f20b86731dd01332ce828424e7047ef29dRob Landley  help
1487fd25f20b86731dd01332ce828424e7047ef29dRob Landley    usage: printf FORMAT [ARGUMENT...]
150d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
1687fd25f20b86731dd01332ce828424e7047ef29dRob Landley    Format and print ARGUMENT(s) according to FORMAT, using C printf syntax
17cc39d95a344240bec8f0c74a02c74d54620b735dRob Landley    (% escapes for cdeEfgGiosuxX, \ escapes for abefnrtv0 or \OCTAL or \xHEX).
180d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma*/
1987fd25f20b86731dd01332ce828424e7047ef29dRob Landley
200d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma#define FOR_printf
210d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma#include "toys.h"
220d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
230d8467b371d93bff03f59a8f11f01094f7618372Ashwini SharmaGLOBALS(
240d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  char *hv_w;
250d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  char *hv_p;
260d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  int encountered;
270d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma)
280d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
2977c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley// Detect matching character (return true/valse) and advance pointer if match.
3077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landleystatic int eat(char **s, char c)
310d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma{
3277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  int x = (**s == c);
3387fd25f20b86731dd01332ce828424e7047ef29dRob Landley
3477c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  if (x) ++*s;
3587fd25f20b86731dd01332ce828424e7047ef29dRob Landley
3677c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  return x;
370d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma}
380d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
390d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma// Add ll and L to Interger and floating point formats respectively.
400d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharmastatic char *get_format(char *f)
410d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma{
420d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  int len = strlen(f);
430d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  char last = f[--len], *post = "";
440d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
4587fd25f20b86731dd01332ce828424e7047ef29dRob Landley  f[len] = 0;
460d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  if (strchr("diouxX", last)) post = "ll";  // add ll to integer modifier.
470d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  else if (strchr("feEgG", last)) post = "L"; // add L to float modifier.
480d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  return xmprintf("%s%s%c", f, post, last);
490d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma}
500d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
510d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma// Print arguments with corresponding conversion and width and precision.
52cc39d95a344240bec8f0c74a02c74d54620b735dRob Landleystatic void print(char *fmt, int w, int p, char *arg)
530d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma{
54cc39d95a344240bec8f0c74a02c74d54620b735dRob Landley  char *ptr = fmt, *ep = 0, *format = 0;
5587fd25f20b86731dd01332ce828424e7047ef29dRob Landley
560d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  errno = 0;
5787fd25f20b86731dd01332ce828424e7047ef29dRob Landley  if (strchr("diouxX", *ptr)) {
5887fd25f20b86731dd01332ce828424e7047ef29dRob Landley    long long val = 0;
5987fd25f20b86731dd01332ce828424e7047ef29dRob Landley
6087fd25f20b86731dd01332ce828424e7047ef29dRob Landley    if (arg) {
6187fd25f20b86731dd01332ce828424e7047ef29dRob Landley      if (*arg == '\'' || *arg == '"') val = arg[1];
620d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma      else {
6387fd25f20b86731dd01332ce828424e7047ef29dRob Landley        val = strtoll(arg, &ep, 0);
6487fd25f20b86731dd01332ce828424e7047ef29dRob Landley        if (errno || (ep && (*ep || ep == arg))) {
6587fd25f20b86731dd01332ce828424e7047ef29dRob Landley          perror_msg("Invalid num %s", arg);
6687fd25f20b86731dd01332ce828424e7047ef29dRob Landley          val = 0;
670d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma        }
680d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma      }
6987fd25f20b86731dd01332ce828424e7047ef29dRob Landley    }
7087fd25f20b86731dd01332ce828424e7047ef29dRob Landley    format = get_format(fmt);
7187fd25f20b86731dd01332ce828424e7047ef29dRob Landley    TT.hv_w ? (TT.hv_p ? printf(format, w, p, val) : printf(format, w, val))
7287fd25f20b86731dd01332ce828424e7047ef29dRob Landley      : (TT.hv_p ? printf(format, p, val) : printf(format, val));
7387fd25f20b86731dd01332ce828424e7047ef29dRob Landley  } else if (strchr("gGeEf", *ptr)) {
7487fd25f20b86731dd01332ce828424e7047ef29dRob Landley    long double dval = 0;
7587fd25f20b86731dd01332ce828424e7047ef29dRob Landley
7687fd25f20b86731dd01332ce828424e7047ef29dRob Landley    if (arg) {
7787fd25f20b86731dd01332ce828424e7047ef29dRob Landley      dval = strtold(arg, &ep);
7887fd25f20b86731dd01332ce828424e7047ef29dRob Landley      if (errno || (ep && (*ep || ep == arg))) {
7987fd25f20b86731dd01332ce828424e7047ef29dRob Landley        perror_msg("Invalid num %s", arg);
8087fd25f20b86731dd01332ce828424e7047ef29dRob Landley        dval = 0;
810d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma      }
8287fd25f20b86731dd01332ce828424e7047ef29dRob Landley    }
8387fd25f20b86731dd01332ce828424e7047ef29dRob Landley    format = get_format(fmt);
8487fd25f20b86731dd01332ce828424e7047ef29dRob Landley    TT.hv_w ? (TT.hv_p ? printf(format, w, p, dval) : printf(format, w, dval))
8587fd25f20b86731dd01332ce828424e7047ef29dRob Landley      : (TT.hv_p ? printf(format, p, dval) :  printf(format, dval));
8687fd25f20b86731dd01332ce828424e7047ef29dRob Landley  } else if (*ptr == 's') {
8787fd25f20b86731dd01332ce828424e7047ef29dRob Landley    char *str = arg;
8887fd25f20b86731dd01332ce828424e7047ef29dRob Landley
8987fd25f20b86731dd01332ce828424e7047ef29dRob Landley    if (!str) str = "";
9087fd25f20b86731dd01332ce828424e7047ef29dRob Landley
9187fd25f20b86731dd01332ce828424e7047ef29dRob Landley    TT.hv_w ? (TT.hv_p ? printf(fmt,w,p,str): printf(fmt, w, str))
9287fd25f20b86731dd01332ce828424e7047ef29dRob Landley      : (TT.hv_p ? printf(fmt, p, str) : printf(fmt, str));
9387fd25f20b86731dd01332ce828424e7047ef29dRob Landley  } else if (*ptr == 'c') printf(fmt, arg ? *arg : 0);
9487fd25f20b86731dd01332ce828424e7047ef29dRob Landley
950d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  if (format) free(format);
960d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma}
970d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
9877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley// Parse escape sequences.
990d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharmastatic int handle_slash(char **esc_val)
1000d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma{
1010d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  char *ptr = *esc_val;
10277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  int len = 1, base = 0;
10377c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  unsigned result = 0;
10477c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
10577c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  if (*ptr == 'c') xexit();
10677c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
10777c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits.
10877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  if (eat(&ptr, 'x')) base = 16;
10977c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  else if (*ptr >= '0' && *ptr <= '8') base = 8;
11077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  len += (base-8)/8;
11177c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
11277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  // Not a hex or octal escape? (This catches trailing \)
11377c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  if (!len) {
11477c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    if (!(result = unescape(*ptr))) result = '\\';
11577c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    else ++*esc_val;
11677c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
11777c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    return result;
11877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  }
1190d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
12077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  while (len) {
12177c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    unsigned num = tolower(*ptr)-'0';
12277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
12377c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    if (num > 10) num += '0'-'a'+10;
1240d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma    if (num >= base) {
12577c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      // Don't parse invalid hex value ala "\xvd", print it verbatim
12677c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      if (base == 16 && len == 2) {
12777c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        ptr--;
12877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        result = '\\';
1290d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma      }
1300d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma      break;
1310d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma    }
13277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    result = (result*base)+num;
1330d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma    ptr++;
13477c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    len--;
1350d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  }
1360d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  *esc_val = ptr;
1370d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
13877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  return (char)result;
1390d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma}
1400d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
1410d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharmavoid printf_main(void)
1420d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma{
14377c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  char *format = *toys.optargs, **arg = toys.optargs+1, *f, *p;
14477c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
14577c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  for (f = format; *f; f++) {
14677c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    if (eat(&f, '\\')) putchar(handle_slash(&f));
147cc39d95a344240bec8f0c74a02c74d54620b735dRob Landley    else if (!eat(&f, '%') || *f == '%') putchar(*f);
14877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    else if (*f == 'b')
14977c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      for (p = *arg ? *(arg++) : ""; *p; p++)
15077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        putchar(eat(&p, '\\') ? handle_slash(&p) : *p);
15177c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    else {
15277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      char *start = f;
15377c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      int wp[2], i;
15477c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
15577c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      // todo: we currently ignore these?
15677c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      if (strchr("-+# ", *f)) f++;
15777c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      memset(wp, 0, 8);
15877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      for (i=0; i<2; i++) {
15977c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        if (eat(&f, '*')) {
16077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley          if (*arg) wp[i] = atolx(*(arg++));
16177c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        } else while (isdigit(*f)) f++;
16277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        if (!eat(&f, '.')) break;
16377c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      }
16477c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      if (!(p = strchr("diouxXfeEgGcs", *f)))
165cc39d95a344240bec8f0c74a02c74d54620b735dRob Landley        error_exit("bad format@%ld", f-format);
16677c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      else {
16777c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        int len = f-start;
16877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
16977c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        TT.hv_p = strstr(start, ".*");
17077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        TT.hv_w = strchr(start, '*');
17177c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        //pitfall: handle diff b/w * and .*
17277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        if ((TT.hv_w-1) == TT.hv_p) TT.hv_w = NULL;
17377c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        memcpy((p = xzalloc(len+1)), start, len);
174cc39d95a344240bec8f0c74a02c74d54620b735dRob Landley        print(p+len-1, wp[0], wp[1], *arg);
17577c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        if (*arg) arg++;
17677c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        free(p);
17777c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        p = NULL;
17877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      }
17977c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      TT.encountered = 1;
18077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    }
18177c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  }
1820d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma}
183