printf.c (4006B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <ctype.h> 3 #include <errno.h> 4 #include <limits.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 9 #include "utf.h" 10 #include "util.h" 11 12 static void 13 usage(void) 14 { 15 eprintf("usage: %s format [arg ...]\n", argv0); 16 } 17 18 int 19 main(int argc, char *argv[]) 20 { 21 Rune *rarg; 22 size_t i, j, argi, lastargi, formatlen, blen; 23 long long num; 24 double dou; 25 int cooldown = 0, width, precision, ret = 0; 26 char *format, *tmp, *arg, *fmt, flag; 27 28 argv0 = argv[0]; 29 if (argc < 2) 30 usage(); 31 32 format = argv[1]; 33 if ((tmp = strstr(format, "\\c"))) { 34 *tmp = 0; 35 cooldown = 1; 36 } 37 formatlen = unescape(format); 38 if (formatlen == 0) 39 return 0; 40 lastargi = 0; 41 for (i = 0, argi = 2; !cooldown || i < formatlen; i++, i = cooldown ? i : (i % formatlen)) { 42 if (i == 0) { 43 if (lastargi == argi) 44 break; 45 lastargi = argi; 46 } 47 if (format[i] != '%') { 48 putchar(format[i]); 49 continue; 50 } 51 52 /* flag */ 53 for (flag = '\0', i++; strchr("#-+ 0", format[i]); i++) { 54 flag = format[i]; 55 } 56 57 /* field width */ 58 width = -1; 59 if (format[i] == '*') { 60 if (argi < argc) 61 width = estrtonum(argv[argi++], 0, INT_MAX); 62 else 63 cooldown = 1; 64 i++; 65 } else { 66 j = i; 67 for (; strchr("+-0123456789", format[i]); i++); 68 if (j != i) { 69 tmp = estrndup(format + j, i - j); 70 width = estrtonum(tmp, 0, INT_MAX); 71 free(tmp); 72 } else { 73 width = 0; 74 } 75 } 76 77 /* field precision */ 78 precision = -1; 79 if (format[i] == '.') { 80 if (format[++i] == '*') { 81 if (argi < argc) 82 precision = estrtonum(argv[argi++], 0, INT_MAX); 83 else 84 cooldown = 1; 85 i++; 86 } else { 87 j = i; 88 for (; strchr("+-0123456789", format[i]); i++); 89 if (j != i) { 90 tmp = estrndup(format + j, i - j); 91 precision = estrtonum(tmp, 0, INT_MAX); 92 free(tmp); 93 } else { 94 precision = 0; 95 } 96 } 97 } 98 99 if (format[i] != '%') { 100 if (argi < argc) 101 arg = argv[argi++]; 102 else { 103 arg = ""; 104 cooldown = 1; 105 } 106 } else { 107 putchar('%'); 108 continue; 109 } 110 111 switch (format[i]) { 112 case 'b': 113 if ((tmp = strstr(arg, "\\c"))) { 114 *tmp = 0; 115 blen = unescape(arg); 116 fwrite(arg, sizeof(*arg), blen, stdout); 117 return 0; 118 } 119 blen = unescape(arg); 120 fwrite(arg, sizeof(*arg), blen, stdout); 121 break; 122 case 'c': 123 unescape(arg); 124 rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg)); 125 utftorunestr(arg, rarg); 126 efputrune(rarg, stdout, "<stdout>"); 127 free(rarg); 128 break; 129 case 's': 130 fmt = estrdup(flag ? "%#*.*s" : "%*.*s"); 131 if (flag) 132 fmt[1] = flag; 133 printf(fmt, width, precision, arg); 134 free(fmt); 135 break; 136 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': 137 for (j = 0; isspace(arg[j]); j++); 138 if (arg[j] == '\'' || arg[j] == '\"') { 139 arg += j + 1; 140 unescape(arg); 141 rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg)); 142 utftorunestr(arg, rarg); 143 num = rarg[0]; 144 } else if (arg[0]) { 145 errno = 0; 146 if (format[i] == 'd' || format[i] == 'i') 147 num = strtol(arg, &tmp, 0); 148 else 149 num = strtoul(arg, &tmp, 0); 150 151 if (tmp == arg || *tmp != '\0') { 152 ret = 1; 153 weprintf("%%%c %s: conversion error\n", 154 format[i], arg); 155 } 156 if (errno == ERANGE) { 157 ret = 1; 158 weprintf("%%%c %s: out of range\n", 159 format[i], arg); 160 } 161 } else { 162 num = 0; 163 } 164 fmt = estrdup(flag ? "%#*.*ll#" : "%*.*ll#"); 165 if (flag) 166 fmt[1] = flag; 167 fmt[flag ? 7 : 6] = format[i]; 168 printf(fmt, width, precision, num); 169 free(fmt); 170 break; 171 case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': 172 fmt = estrdup(flag ? "%#*.*#" : "%*.*#"); 173 if (flag) 174 fmt[1] = flag; 175 fmt[flag ? 5 : 4] = format[i]; 176 dou = (strlen(arg) > 0) ? estrtod(arg) : 0; 177 printf(fmt, width, precision, dou); 178 free(fmt); 179 break; 180 default: 181 eprintf("Invalid format specifier '%c'.\n", format[i]); 182 } 183 if (argi >= argc) 184 cooldown = 1; 185 } 186 187 return fshut(stdout, "<stdout>") | ret; 188 }