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
7d0dead30a53c0353fd1c31aa5183fdcc2b30491bRob Landley *
8d0dead30a53c0353fd1c31aa5183fdcc2b30491bRob Landley * todo: *m$ ala printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec);
90d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
100d8467b371d93bff03f59a8f11f01094f7618372Ashwini SharmaUSE_PRINTF(NEWTOY(printf, "<1", TOYFLAG_USR|TOYFLAG_BIN))
110d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
120d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharmaconfig PRINTF
1387fd25f20b86731dd01332ce828424e7047ef29dRob Landley  bool "printf"
14d3d633ff13187c34c3a04ce20b9ffcfb85c2c383Rob Landley  default y
1587fd25f20b86731dd01332ce828424e7047ef29dRob Landley  help
1687fd25f20b86731dd01332ce828424e7047ef29dRob Landley    usage: printf FORMAT [ARGUMENT...]
170d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
1887fd25f20b86731dd01332ce828424e7047ef29dRob Landley    Format and print ARGUMENT(s) according to FORMAT, using C printf syntax
19cc39d95a344240bec8f0c74a02c74d54620b735dRob Landley    (% escapes for cdeEfgGiosuxX, \ escapes for abefnrtv0 or \OCTAL or \xHEX).
200d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma*/
2187fd25f20b86731dd01332ce828424e7047ef29dRob Landley
220d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma#define FOR_printf
230d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma#include "toys.h"
240d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
259d1d0ad1236d3c2d85e6c244d148f5ae8f589631Elliott Hughes// Detect matching character (return true/false) and advance pointer if match.
2677c8d1a7d006e177bd0283b48530a3397b687b6cRob Landleystatic int eat(char **s, char c)
270d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma{
2877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  int x = (**s == c);
2987fd25f20b86731dd01332ce828424e7047ef29dRob Landley
3077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  if (x) ++*s;
3187fd25f20b86731dd01332ce828424e7047ef29dRob Landley
3277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  return x;
330d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma}
340d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
3577c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley// Parse escape sequences.
360d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharmastatic int handle_slash(char **esc_val)
370d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma{
380d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  char *ptr = *esc_val;
3970cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley  int len, base = 0;
4070cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley  unsigned result = 0, num;
4177c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
4277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  if (*ptr == 'c') xexit();
4377c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
4477c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits.
4577c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  if (eat(&ptr, 'x')) base = 16;
4677c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  else if (*ptr >= '0' && *ptr <= '8') base = 8;
4770cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley  len = (char []){0,3,2}[base/8];
4877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
4977c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  // Not a hex or octal escape? (This catches trailing \)
5077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  if (!len) {
5177c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    if (!(result = unescape(*ptr))) result = '\\';
5277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    else ++*esc_val;
5377c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley
5477c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    return result;
5577c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  }
560d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
5777c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  while (len) {
5870cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley    num = tolower(*ptr) - '0';
5970cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley    if (num >= 'a'-'0') num += '0'-'a'+10;
600d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma    if (num >= base) {
6177c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      // Don't parse invalid hex value ala "\xvd", print it verbatim
6277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      if (base == 16 && len == 2) {
6377c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        ptr--;
6477c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley        result = '\\';
650d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma      }
660d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma      break;
670d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma    }
6877c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    result = (result*base)+num;
690d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma    ptr++;
7077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    len--;
710d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  }
720d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma  *esc_val = ptr;
730d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
7470cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley  return result;
750d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma}
760d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma
770d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharmavoid printf_main(void)
780d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma{
7970cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley  char **arg = toys.optargs+1;
8070cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley
8170cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley  // Repeat format until arguments consumed
8270cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley  for (;;) {
8370cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley    int seen = 0;
8470cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley    char *f = *toys.optargs;
8570cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley
8670cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley    // Loop through characters in format
8770cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley    while (*f) {
8870cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley      if (eat(&f, '\\')) putchar(handle_slash(&f));
8970cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley      else if (!eat(&f, '%') || *f == '%') putchar(*f++);
9070cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley
9170cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley      // Handle %escape
9277c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley      else {
93caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley        char c, *end = 0, *aa, *to = toybuf;
945d431d1e1f9172587775c00a07b767f9b544cb91Rob Landley        int wp[] = {0,-1}, i = 0;
9570cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley
9670cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley        // Parse width.precision between % and type indicator.
97caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley        *to++ = '%';
98d0dead30a53c0353fd1c31aa5183fdcc2b30491bRob Landley        while (strchr("-+# '0", *f) && (to-toybuf)<10) *to++ = *f++;
995d431d1e1f9172587775c00a07b767f9b544cb91Rob Landley        for (;;) {
10070cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley          if (eat(&f, '*')) {
10170cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley            if (*arg) wp[i] = atolx(*arg++);
1025d431d1e1f9172587775c00a07b767f9b544cb91Rob Landley          } else while (*f >= '0' && *f <= '9') wp[i] = (wp[i]*10)+(*f++)-'0';
1035d431d1e1f9172587775c00a07b767f9b544cb91Rob Landley          if (i++ || !eat(&f, '.')) break;
1045d431d1e1f9172587775c00a07b767f9b544cb91Rob Landley          wp[1] = 0;
10570cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley        }
10670cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley        c = *f++;
107caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley        seen = sprintf(to, "*.*%c", c);;
108caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley        errno = 0;
10970cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley        aa = *arg ? *arg++ : "";
11070cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley
111caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley        // Output %esc using parsed format string
11270cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley        if (c == 'b') {
11370cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley          while (*aa) putchar(eat(&aa, '\\') ? handle_slash(&aa) : *aa++);
11470cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley
11570cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley          continue;
116caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley        } else if (c == 'c') printf(toybuf, wp[0], wp[1], *aa);
117caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley        else if (c == 's') printf(toybuf, wp[0], wp[1], aa);
11870cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley        else if (strchr("diouxX", c)) {
119caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley          long ll;
12070cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley
121caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley          if (*aa == '\'' || *aa == '"') ll = aa[1];
122caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley          else ll = strtoll(aa, &end, 0);
12370cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley
124d0dead30a53c0353fd1c31aa5183fdcc2b30491bRob Landley          sprintf(to, "*.*ll%c", c);
125caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley          printf(toybuf, wp[0], wp[1], ll);
126caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley        } else if (strchr("feEgG", c)) {
127caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley          long double ld = strtold(aa, &end);
128caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley
129caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley          sprintf(to, "*.*L%c", c);
130caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley          printf(toybuf, wp[0], wp[1], ld);
13138e5485c6a8ece99aa8aac2f318f14764857ee24Rob Landley        } else error_exit("bad %%%c@%ld", c, (long)(f-*toys.optargs));
132caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley
133caa6b014ba15dea377e5f65fcac57afaa932fa64Rob Landley        if (end && (errno || *end)) perror_msg("bad %%%c %s", c, aa);
13470cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley      }
13577c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley    }
13670cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley
13770cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley    // Posix says to keep looping through format until we consume all args.
13870cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley    // This only works if the format actually consumed at least one arg.
13970cbfe8eda34df26cc91c51cd77098612e33fd80Rob Landley    if (!seen || !*arg) break;
14077c8d1a7d006e177bd0283b48530a3397b687b6cRob Landley  }
1410d8467b371d93bff03f59a8f11f01094f7618372Ashwini Sharma}
142