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