10f66f451Sopenharmony_ci/* printf.c - Format and Print the data. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com> 40f66f451Sopenharmony_ci * Copyright 2014 Kyungwan Han <asura321@gmail.com> 50f66f451Sopenharmony_ci * 60f66f451Sopenharmony_ci * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html 70f66f451Sopenharmony_ci * 80f66f451Sopenharmony_ci * todo: *m$ ala printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec); 90f66f451Sopenharmony_ci 100f66f451Sopenharmony_ciUSE_PRINTF(NEWTOY(printf, "<1?^", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_MAYFORK)) 110f66f451Sopenharmony_ci 120f66f451Sopenharmony_ciconfig PRINTF 130f66f451Sopenharmony_ci bool "printf" 140f66f451Sopenharmony_ci default y 150f66f451Sopenharmony_ci help 160f66f451Sopenharmony_ci usage: printf FORMAT [ARGUMENT...] 170f66f451Sopenharmony_ci 180f66f451Sopenharmony_ci Format and print ARGUMENT(s) according to FORMAT, using C printf syntax 190f66f451Sopenharmony_ci (% escapes for cdeEfgGiosuxX, \ escapes for abefnrtv0 or \OCTAL or \xHEX). 200f66f451Sopenharmony_ci*/ 210f66f451Sopenharmony_ci 220f66f451Sopenharmony_ci#define FOR_printf 230f66f451Sopenharmony_ci#include "toys.h" 240f66f451Sopenharmony_ci 250f66f451Sopenharmony_ci// Detect matching character (return true/false) and advance pointer if match. 260f66f451Sopenharmony_cistatic int eat(char **s, char c) 270f66f451Sopenharmony_ci{ 280f66f451Sopenharmony_ci int x = (**s == c); 290f66f451Sopenharmony_ci 300f66f451Sopenharmony_ci if (x) ++*s; 310f66f451Sopenharmony_ci 320f66f451Sopenharmony_ci return x; 330f66f451Sopenharmony_ci} 340f66f451Sopenharmony_ci 350f66f451Sopenharmony_ci// Parse escape sequences. 360f66f451Sopenharmony_cistatic int handle_slash(char **esc_val, int posix) 370f66f451Sopenharmony_ci{ 380f66f451Sopenharmony_ci char *ptr = *esc_val; 390f66f451Sopenharmony_ci int len, base = 0; 400f66f451Sopenharmony_ci unsigned result = 0, num; 410f66f451Sopenharmony_ci 420f66f451Sopenharmony_ci if (*ptr == 'c') xexit(); 430f66f451Sopenharmony_ci 440f66f451Sopenharmony_ci // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits. 450f66f451Sopenharmony_ci if (eat(&ptr, 'x')) base = 16; 460f66f451Sopenharmony_ci else { 470f66f451Sopenharmony_ci if (posix && *ptr=='0') ptr++; 480f66f451Sopenharmony_ci if (*ptr >= '0' && *ptr <= '7') base = 8; 490f66f451Sopenharmony_ci } 500f66f451Sopenharmony_ci len = (char []){0,3,2}[base/8]; 510f66f451Sopenharmony_ci 520f66f451Sopenharmony_ci // Not a hex or octal escape? (This catches trailing \) 530f66f451Sopenharmony_ci if (!len) { 540f66f451Sopenharmony_ci if (!(result = unescape(*ptr))) result = '\\'; 550f66f451Sopenharmony_ci else ++*esc_val; 560f66f451Sopenharmony_ci 570f66f451Sopenharmony_ci return result; 580f66f451Sopenharmony_ci } 590f66f451Sopenharmony_ci 600f66f451Sopenharmony_ci while (len) { 610f66f451Sopenharmony_ci num = tolower(*ptr) - '0'; 620f66f451Sopenharmony_ci if (num >= 'a'-'0') num += '0'-'a'+10; 630f66f451Sopenharmony_ci if (num >= base) { 640f66f451Sopenharmony_ci // "\xav" is "\xa"+"v", but "\xva" is an error. 650f66f451Sopenharmony_ci if (base == 16 && len == 2) error_exit("bad \\x"); 660f66f451Sopenharmony_ci break; 670f66f451Sopenharmony_ci } 680f66f451Sopenharmony_ci result = (result*base)+num; 690f66f451Sopenharmony_ci ptr++; 700f66f451Sopenharmony_ci len--; 710f66f451Sopenharmony_ci } 720f66f451Sopenharmony_ci *esc_val = ptr; 730f66f451Sopenharmony_ci 740f66f451Sopenharmony_ci return result; 750f66f451Sopenharmony_ci} 760f66f451Sopenharmony_ci 770f66f451Sopenharmony_civoid printf_main(void) 780f66f451Sopenharmony_ci{ 790f66f451Sopenharmony_ci char **arg = toys.optargs+1; 800f66f451Sopenharmony_ci 810f66f451Sopenharmony_ci // Repeat format until arguments consumed 820f66f451Sopenharmony_ci for (;;) { 830f66f451Sopenharmony_ci int seen = 0; 840f66f451Sopenharmony_ci char *f = *toys.optargs; 850f66f451Sopenharmony_ci 860f66f451Sopenharmony_ci // Loop through characters in format 870f66f451Sopenharmony_ci while (*f) { 880f66f451Sopenharmony_ci if (eat(&f, '\\')) putchar(handle_slash(&f, 0)); 890f66f451Sopenharmony_ci else if (!eat(&f, '%') || *f == '%') putchar(*f++); 900f66f451Sopenharmony_ci 910f66f451Sopenharmony_ci // Handle %escape 920f66f451Sopenharmony_ci else { 930f66f451Sopenharmony_ci char c, *end = 0, *aa, *to = toybuf; 940f66f451Sopenharmony_ci int wp[] = {0,-1}, i = 0; 950f66f451Sopenharmony_ci 960f66f451Sopenharmony_ci // Parse width.precision between % and type indicator. 970f66f451Sopenharmony_ci *to++ = '%'; 980f66f451Sopenharmony_ci while (strchr("-+# '0", *f) && (to-toybuf)<10) *to++ = *f++; 990f66f451Sopenharmony_ci for (;;) { 1000f66f451Sopenharmony_ci if (eat(&f, '*')) { 1010f66f451Sopenharmony_ci if (*arg) wp[i] = atolx(*arg++); 1020f66f451Sopenharmony_ci } else while (*f >= '0' && *f <= '9') wp[i] = (wp[i]*10)+(*f++)-'0'; 1030f66f451Sopenharmony_ci if (i++ || !eat(&f, '.')) break; 1040f66f451Sopenharmony_ci wp[1] = 0; 1050f66f451Sopenharmony_ci } 1060f66f451Sopenharmony_ci c = *f++; 1070f66f451Sopenharmony_ci seen = sprintf(to, "*.*%c", c);; 1080f66f451Sopenharmony_ci errno = 0; 1090f66f451Sopenharmony_ci aa = *arg ? *arg++ : ""; 1100f66f451Sopenharmony_ci 1110f66f451Sopenharmony_ci // Output %esc using parsed format string 1120f66f451Sopenharmony_ci if (c == 'b') { 1130f66f451Sopenharmony_ci while (*aa) putchar(eat(&aa, '\\') ? handle_slash(&aa, 1) : *aa++); 1140f66f451Sopenharmony_ci 1150f66f451Sopenharmony_ci continue; 1160f66f451Sopenharmony_ci } else if (c == 'c') printf(toybuf, wp[0], wp[1], *aa); 1170f66f451Sopenharmony_ci else if (c == 's') printf(toybuf, wp[0], wp[1], aa); 1180f66f451Sopenharmony_ci else if (strchr("diouxX", c)) { 1190f66f451Sopenharmony_ci long long ll; 1200f66f451Sopenharmony_ci 1210f66f451Sopenharmony_ci if (*aa == '\'' || *aa == '"') ll = aa[1]; 1220f66f451Sopenharmony_ci else ll = strtoll(aa, &end, 0); 1230f66f451Sopenharmony_ci 1240f66f451Sopenharmony_ci sprintf(to, "*.*ll%c", c); 1250f66f451Sopenharmony_ci printf(toybuf, wp[0], wp[1], ll); 1260f66f451Sopenharmony_ci } else if (strchr("feEgG", c)) { 1270f66f451Sopenharmony_ci long double ld = strtold(aa, &end); 1280f66f451Sopenharmony_ci 1290f66f451Sopenharmony_ci sprintf(to, "*.*L%c", c); 1300f66f451Sopenharmony_ci printf(toybuf, wp[0], wp[1], ld); 1310f66f451Sopenharmony_ci } else error_exit("bad %%%c@%ld", c, (long)(f-*toys.optargs)); 1320f66f451Sopenharmony_ci 1330f66f451Sopenharmony_ci if (end && (errno || *end)) perror_msg("bad %%%c %s", c, aa); 1340f66f451Sopenharmony_ci } 1350f66f451Sopenharmony_ci } 1360f66f451Sopenharmony_ci 1370f66f451Sopenharmony_ci // Posix says to keep looping through format until we consume all args. 1380f66f451Sopenharmony_ci // This only works if the format actually consumed at least one arg. 1390f66f451Sopenharmony_ci if (!seen || !*arg) break; 1400f66f451Sopenharmony_ci } 1410f66f451Sopenharmony_ci} 142