printf.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
printf.c (4127B)
---
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, f, argi, lastargi, formatlen, blen, nflags;
23 long long num;
24 double dou;
25 int cooldown = 0, width, precision, ret = 0;
26 char *format, *tmp, *arg, *fmt;
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
48 if (format[i] != '%') {
49 putchar(format[i]);
50 continue;
51 }
52
53 /* flag */
54 f = ++i;
55 nflags = strspn(&format[f], "#-+ 0");
56 i += nflags;
57
58 if (nflags > INT_MAX)
59 eprintf("Too many flags in format\n");
60
61 /* field width */
62 width = -1;
63 if (format[i] == '*') {
64 if (argi < argc)
65 width = estrtonum(argv[argi++], 0, INT_MAX);
66 else
67 cooldown = 1;
68 i++;
69 } else {
70 j = i;
71 i += strspn(&format[i], "+-0123456789");
72 if (j != i) {
73 tmp = estrndup(format + j, i - j);
74 width = estrtonum(tmp, 0, INT_MAX);
75 free(tmp);
76 } else {
77 width = 0;
78 }
79 }
80
81 /* field precision */
82 precision = -1;
83 if (format[i] == '.') {
84 if (format[++i] == '*') {
85 if (argi < argc)
86 precision = estrtonum(argv[argi++], 0, INT_MAX);
87 else
88 cooldown = 1;
89 i++;
90 } else {
91 j = i;
92 i += strspn(&format[i], "+-0123456789");
93 if (j != i) {
94 tmp = estrndup(format + j, i - j);
95 precision = estrtonum(tmp, 0, INT_MAX);
96 free(tmp);
97 } else {
98 precision = 0;
99 }
100 }
101 }
102
103 if (format[i] != '%') {
104 if (argi < argc)
105 arg = argv[argi++];
106 else {
107 arg = "";
108 cooldown = 1;
109 }
110 } else {
111 putchar('%');
112 continue;
113 }
114
115 switch (format[i]) {
116 case 'b':
117 if ((tmp = strstr(arg, "\\c"))) {
118 *tmp = 0;
119 blen = unescape(arg);
120 fwrite(arg, sizeof(*arg), blen, stdout);
121 return 0;
122 }
123 blen = unescape(arg);
124 fwrite(arg, sizeof(*arg), blen, stdout);
125 break;
126 case 'c':
127 unescape(arg);
128 rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg));
129 utftorunestr(arg, rarg);
130 efputrune(rarg, stdout, "<stdout>");
131 free(rarg);
132 break;
133 case 's':
134 fmt = emalloc(sizeof("%*.*s") + nflags);
135 sprintf(fmt, "%%%.*s*.*s", (int)nflags, &format[f]);
136 printf(fmt, width, precision, arg);
137 free(fmt);
138 break;
139 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
140 for (j = 0; isspace(arg[j]); j++);
141 if (arg[j] == '\'' || arg[j] == '\"') {
142 arg += j + 1;
143 unescape(arg);
144 rarg = ereallocarray(NULL, utflen(arg) + 1, sizeof(*rarg));
145 utftorunestr(arg, rarg);
146 num = rarg[0];
147 } else if (arg[0]) {
148 errno = 0;
149 if (format[i] == 'd' || format[i] == 'i')
150 num = strtol(arg, &tmp, 0);
151 else
152 num = strtoul(arg, &tmp, 0);
153
154 if (tmp == arg || *tmp != '\0') {
155 ret = 1;
156 weprintf("%%%c %s: conversion error\n",
157 format[i], arg);
158 }
159 if (errno == ERANGE) {
160 ret = 1;
161 weprintf("%%%c %s: out of range\n",
162 format[i], arg);
163 }
164 } else {
165 num = 0;
166 }
167 fmt = emalloc(sizeof("%*.*ll#") + nflags);
168 sprintf(fmt, "%%%.*s*.*ll%c", (int)nflags, &format[f], format[i]);
169 printf(fmt, width, precision, num);
170 free(fmt);
171 break;
172 case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
173 fmt = emalloc(sizeof("%*.*#") + nflags);
174 sprintf(fmt, "%%%.*s*.*%c", (int)nflags, &format[f], format[i]);
175 dou = (strlen(arg) > 0) ? estrtod(arg) : 0;
176 printf(fmt, width, precision, dou);
177 free(fmt);
178 break;
179 case '\0':
180 eprintf("Missing format specifier.\n");
181 default:
182 eprintf("Invalid format specifier '%c'.\n", format[i]);
183 }
184 if (argi >= argc)
185 cooldown = 1;
186 }
187
188 return fshut(stdout, "<stdout>") | ret;
189 }