printf.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /* printf.c - Format and Print the data.
  2. *
  3. * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com>
  4. * Copyright 2014 Kyungwan Han <asura321@gmail.com>
  5. *
  6. * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
  7. *
  8. * todo: *m$ ala printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec);
  9. USE_PRINTF(NEWTOY(printf, "<1?^", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_MAYFORK))
  10. config PRINTF
  11. bool "printf"
  12. default y
  13. help
  14. usage: printf FORMAT [ARGUMENT...]
  15. Format and print ARGUMENT(s) according to FORMAT, using C printf syntax
  16. (% escapes for cdeEfgGiosuxX, \ escapes for abefnrtv0 or \OCTAL or \xHEX).
  17. */
  18. #define FOR_printf
  19. #include "toys.h"
  20. // Detect matching character (return true/false) and advance pointer if match.
  21. static int eat(char **s, char c)
  22. {
  23. int x = (**s == c);
  24. if (x) ++*s;
  25. return x;
  26. }
  27. // Parse escape sequences.
  28. static int handle_slash(char **esc_val, int posix)
  29. {
  30. char *ptr = *esc_val;
  31. int len, base = 0;
  32. unsigned result = 0, num;
  33. if (*ptr == 'c') xexit();
  34. // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits.
  35. if (eat(&ptr, 'x')) base = 16;
  36. else {
  37. if (posix && *ptr=='0') ptr++;
  38. if (*ptr >= '0' && *ptr <= '7') base = 8;
  39. }
  40. len = (char []){0,3,2}[base/8];
  41. // Not a hex or octal escape? (This catches trailing \)
  42. if (!len) {
  43. if (!(result = unescape(*ptr))) result = '\\';
  44. else ++*esc_val;
  45. return result;
  46. }
  47. while (len) {
  48. num = tolower(*ptr) - '0';
  49. if (num >= 'a'-'0') num += '0'-'a'+10;
  50. if (num >= base) {
  51. // "\xav" is "\xa"+"v", but "\xva" is an error.
  52. if (base == 16 && len == 2) error_exit("bad \\x");
  53. break;
  54. }
  55. result = (result*base)+num;
  56. ptr++;
  57. len--;
  58. }
  59. *esc_val = ptr;
  60. return result;
  61. }
  62. void printf_main(void)
  63. {
  64. char **arg = toys.optargs+1;
  65. // Repeat format until arguments consumed
  66. for (;;) {
  67. int seen = 0;
  68. char *f = *toys.optargs;
  69. // Loop through characters in format
  70. while (*f) {
  71. if (eat(&f, '\\')) putchar(handle_slash(&f, 0));
  72. else if (!eat(&f, '%') || *f == '%') putchar(*f++);
  73. // Handle %escape
  74. else {
  75. char c, *end = 0, *aa, *to = toybuf;
  76. int wp[] = {0,-1}, i = 0;
  77. // Parse width.precision between % and type indicator.
  78. *to++ = '%';
  79. while (strchr("-+# '0", *f) && (to-toybuf)<10) *to++ = *f++;
  80. for (;;) {
  81. if (eat(&f, '*')) {
  82. if (*arg) wp[i] = atolx(*arg++);
  83. } else while (*f >= '0' && *f <= '9') wp[i] = (wp[i]*10)+(*f++)-'0';
  84. if (i++ || !eat(&f, '.')) break;
  85. wp[1] = 0;
  86. }
  87. c = *f++;
  88. seen = sprintf(to, "*.*%c", c);;
  89. errno = 0;
  90. aa = *arg ? *arg++ : "";
  91. // Output %esc using parsed format string
  92. if (c == 'b') {
  93. while (*aa) putchar(eat(&aa, '\\') ? handle_slash(&aa, 1) : *aa++);
  94. continue;
  95. } else if (c == 'c') printf(toybuf, wp[0], wp[1], *aa);
  96. else if (c == 's') printf(toybuf, wp[0], wp[1], aa);
  97. else if (strchr("diouxX", c)) {
  98. long long ll;
  99. if (*aa == '\'' || *aa == '"') ll = aa[1];
  100. else ll = strtoll(aa, &end, 0);
  101. sprintf(to, "*.*ll%c", c);
  102. printf(toybuf, wp[0], wp[1], ll);
  103. } else if (strchr("feEgG", c)) {
  104. long double ld = strtold(aa, &end);
  105. sprintf(to, "*.*L%c", c);
  106. printf(toybuf, wp[0], wp[1], ld);
  107. } else error_exit("bad %%%c@%ld", c, (long)(f-*toys.optargs));
  108. if (end && (errno || *end)) perror_msg("bad %%%c %s", c, aa);
  109. }
  110. }
  111. // Posix says to keep looping through format until we consume all args.
  112. // This only works if the format actually consumed at least one arg.
  113. if (!seen || !*arg) break;
  114. }
  115. }