/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include "utf.h" #include "util.h" static void usage(void) { eprintf("usage: %s format [arg ...]\n", argv0); } int main(int argc, char *argv[]) { Rune *rarg; size_t i, j, argi, lastargi, formatlen, arglen; long long num; double dou; int cooldown = 0, width, precision; char *format, *tmp, *arg, *fmt; argv0 = argv[0]; if (argc < 2) usage(); format = argv[1]; if ((tmp = strstr(format, "\\c"))) { *tmp = 0; cooldown = 1; } formatlen = unescape(format); if (formatlen == 0) return 0; lastargi = 0; for (i = 0, argi = 2; !cooldown || i < formatlen; i++, i = cooldown ? i : (i % formatlen)) { if (i == 0) { if (lastargi == argi) break; lastargi = argi; } if (format[i] != '%') { putchar(format[i]); continue; } /* field width */ width = -1; for (i++; strchr("#-+ 0", format[i]); i++); if (format[i] == '*') { if (argi < argc) width = estrtonum(argv[argi++], 0, INT_MAX); else cooldown = 1; i++; } else { j = i; for (; strchr("+-0123456789", format[i]); i++); if (j != i) { tmp = estrndup(format + j, i - j); width = estrtonum(tmp, 0, INT_MAX); free(tmp); } else { width = 0; } } /* field precision */ precision = -1; if (format[i] == '.') { if (format[++i] == '*') { if (argi < argc) precision = estrtonum(argv[argi++], 0, INT_MAX); else cooldown = 1; i++; } else { j = i; for (; strchr("+-0123456789", format[i]); i++); if (j != i) { tmp = estrndup(format + j, i - j); precision = estrtonum(tmp, 0, INT_MAX); free(tmp); } else { precision = 0; } } } if (format[i] != '%') { if (argi < argc) arg = argv[argi++]; else { arg = ""; cooldown = 1; } } else putchar('%'); switch (format[i]) { case 'b': if ((tmp = strstr(arg, "\\c"))) { *tmp = 0; unescape(arg); fputs(arg, stdout); return 0; } unescape(arg); fputs(arg, stdout); break; case 'c': unescape(arg); rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg)); utftorunestr(arg, rarg); efputrune(rarg, stdout, ""); free(rarg); break; case 's': printf("%*.*s", width, precision, arg); break; case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': arglen = strlen(arg); for (j = 0; j < arglen && isspace(arg[j]); j++); if (arg[j] == '\'' || arg[j] == '\"') { arg += j + 1; unescape(arg); rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg)); utftorunestr(arg, rarg); num = rarg[0]; } else num = (strlen(arg) > 0) ? estrtonum(arg, LLONG_MIN, LLONG_MAX) : 0; fmt = estrdup("%*ll#"); fmt[4] = format[i]; printf(fmt, width, num); free(fmt); break; case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': fmt = estrdup("%*.*#"); fmt[4] = format[i]; dou = (strlen(arg) > 0) ? estrtod(arg) : 0; printf(fmt, width, precision, dou); free(fmt); break; default: eprintf("Invalid format specifier '%c'.\n", format[i]); } if (argi >= argc) cooldown = 1; } return fshut(stdout, ""); }