printf.c (3919B)
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 printf("%*.*s", width, precision, arg); 131 break; 132 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': 133 for (j = 0; isspace(arg[j]); j++); 134 if (arg[j] == '\'' || arg[j] == '\"') { 135 arg += j + 1; 136 unescape(arg); 137 rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg)); 138 utftorunestr(arg, rarg); 139 num = rarg[0]; 140 } else if (arg[0]) { 141 errno = 0; 142 if (format[i] == 'd' || format[i] == 'i') 143 num = strtol(arg, &tmp, 0); 144 else 145 num = strtoul(arg, &tmp, 0); 146 147 if (tmp == arg || *tmp != '\0') { 148 ret = 1; 149 weprintf("%%%c %s: conversion error\n", 150 format[i], arg); 151 } 152 if (errno == ERANGE) { 153 ret = 1; 154 weprintf("%%%c %s: out of range\n", 155 format[i], arg); 156 } 157 } else { 158 num = 0; 159 } 160 fmt = estrdup(flag ? "%#*.*ll#" : "%*.*ll#"); 161 if (flag) 162 fmt[1] = flag; 163 fmt[flag ? 7 : 6] = format[i]; 164 printf(fmt, width, precision, num); 165 free(fmt); 166 break; 167 case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': 168 fmt = estrdup(flag ? "%#*.*#" : "%*.*#"); 169 if (flag) 170 fmt[1] = flag; 171 fmt[flag ? 5 : 4] = format[i]; 172 dou = (strlen(arg) > 0) ? estrtod(arg) : 0; 173 printf(fmt, width, precision, dou); 174 free(fmt); 175 break; 176 default: 177 eprintf("Invalid format specifier '%c'.\n", format[i]); 178 } 179 if (argi >= argc) 180 cooldown = 1; 181 } 182 183 return fshut(stdout, "<stdout>") | ret; 184 }