Handle streams separately in tree_add_track()
[cmus.git] / format_print.c
blob3ca00049c0d28d0ec46f692699352491cdf4d3a2
1 #include "format_print.h"
2 #include "uchar.h"
3 #include "xmalloc.h"
4 #include "debug.h"
6 #include <string.h>
7 #include <ctype.h>
9 static int width;
10 static int align_left;
11 static int pad;
13 static int numlen(int num)
15 int digits;
17 if (num < 0)
18 return 1; /* '?' */
19 digits = 0;
20 do {
21 num /= 10;
22 digits++;
23 } while (num);
24 return digits;
27 static int stack_print(char *buf, char *stack, int stack_len)
29 int i = 0;
31 if (width) {
32 if (align_left) {
33 while (i < width && stack_len)
34 buf[i++] = stack[--stack_len];
35 while (i < width)
36 buf[i++] = pad;
37 } else {
38 int pad_len;
40 if (stack_len > width)
41 stack_len = width;
42 pad_len = width - stack_len;
43 while (i < pad_len)
44 buf[i++] = pad;
45 while (i < width)
46 buf[i++] = stack[--stack_len];
48 } else {
49 while (stack_len)
50 buf[i++] = stack[--stack_len];
52 return i;
55 static int print_num(char *buf, int num)
57 char stack[20];
58 int i, p;
60 if (num < 0) {
61 if (width == 0)
62 width = 1;
63 for (i = 0; i < width; i++)
64 buf[i] = '?';
65 return width;
67 p = 0;
68 do {
69 stack[p++] = num % 10 + '0';
70 num /= 10;
71 } while (num);
73 return stack_print(buf, stack, p);
76 /* print '{,-}{h:,}mm:ss' */
77 static int print_time(char *buf, int t)
79 int h, m, s;
80 char stack[32];
81 int neg = 0;
82 int p = 0;
84 if (t < 0) {
85 neg = 1;
86 t *= -1;
88 h = t / 3600;
89 t = t % 3600;
90 m = t / 60;
91 s = t % 60;
93 /* put all chars to stack in reverse order ;) */
94 stack[p++] = s % 10 + '0';
95 stack[p++] = s / 10 + '0';
96 stack[p++] = ':';
97 stack[p++] = m % 10 + '0';
98 stack[p++] = m / 10 + '0';
99 if (h) {
100 stack[p++] = ':';
101 do {
102 stack[p++] = h % 10 + '0';
103 h /= 10;
104 } while (h);
106 if (neg)
107 stack[p++] = '-';
109 return stack_print(buf, stack, p);
112 static void print_str(char *buf, int *idx, const char *str)
114 int d = *idx;
116 if (width) {
117 int ws_len;
118 int i = 0;
120 if (align_left) {
121 i = width;
122 d += u_copy_chars(buf + d, str, &i);
124 ws_len = width - i;
125 memset(buf + d, ' ', ws_len);
126 d += ws_len;
127 } else {
128 int s = 0;
130 ws_len = width - u_str_width(str);
132 if (ws_len > 0) {
133 memset(buf + d, ' ', ws_len);
134 d += ws_len;
135 i += ws_len;
138 if (ws_len < 0) {
139 int w, c = -ws_len;
140 uchar u;
142 while (c > 0) {
143 u_get_char(str, &s, &u);
144 w = u_char_width(u);
145 c -= w;
147 if (c < 0) {
148 /* gaah, skipped too much */
149 if (u_char_width(u) == 2) {
150 /* double-byte */
151 buf[d++] = ' ';
152 } else {
153 /* <xx> */
154 if (c == -3)
155 buf[d++] = hex_tab[(u >> 4) & 0xf];
156 if (c <= -2)
157 buf[d++] = hex_tab[u & 0xf];
158 buf[d++] = '>';
163 if (width - i > 0) {
164 int w = width - i;
166 d += u_copy_chars(buf + d, str + s, &w);
169 } else {
170 int s = 0;
171 uchar u;
173 while (1) {
174 u_get_char(str, &s, &u);
175 if (u == 0)
176 break;
177 u_set_char(buf, &d, u);
180 *idx = d;
183 static void print(char *str, const char *format, const struct format_option *fopts)
185 /* format and str indices */
186 int s = 0, d = 0;
188 while (format[s]) {
189 const struct format_option *fo;
190 uchar u;
192 u_get_char(format, &s, &u);
193 if (u != '%') {
194 u_set_char(str, &d, u);
195 continue;
197 u_get_char(format, &s, &u);
198 if (u == '%') {
199 u_set_char(str, &d, u);
200 continue;
203 if (u == '=') {
204 break;
207 align_left = 0;
208 if (u == '-') {
209 align_left = 1;
210 u_get_char(format, &s, &u);
212 pad = ' ';
213 if (u == '0') {
214 pad = '0';
215 u_get_char(format, &s, &u);
217 width = 0;
218 while (isdigit(u)) {
219 width *= 10;
220 width += u - '0';
221 u_get_char(format, &s, &u);
223 for (fo = fopts; fo->ch; fo++) {
224 if (fo->ch == u) {
225 int type = fo->type;
227 if (fo->empty) {
228 memset(str + d, ' ', width);
229 d += width;
230 } else if (type == FO_STR) {
231 print_str(str, &d, fo->fo_str);
232 } else if (type == FO_INT) {
233 d += print_num(str + d, fo->fo_int);
234 } else if (type == FO_TIME) {
235 d += print_time(str + d, fo->fo_time);
237 break;
241 str[d] = 0;
244 static char *l_str = NULL;
245 static char *r_str = NULL;
246 /* sizes in bytes. not counting the terminating 0! */
247 static int l_str_size = -1;
248 static int r_str_size = -1;
250 int format_print(char *str, int str_width, const char *format, const struct format_option *fopts)
252 /* lengths of left and right aligned texts */
253 int llen = 0;
254 int rlen = 0;
255 int *len = &llen;
256 int lsize, rsize;
257 int eq_pos = -1;
258 int s = 0;
260 while (format[s]) {
261 const struct format_option *fo;
262 int nlen;
263 uchar u;
265 u_get_char(format, &s, &u);
266 if (u != '%') {
267 (*len) += u_char_width(u);
268 continue;
270 u_get_char(format, &s, &u);
271 if (u == '%') {
272 (*len)++;
273 continue;
275 if (u == '=') {
276 /* right aligned text starts */
277 len = &rlen;
278 eq_pos = s - 1;
279 continue;
281 if (u == '-')
282 u_get_char(format, &s, &u);
283 nlen = 0;
284 while (isdigit(u)) {
285 /* minimum length of this field */
286 nlen *= 10;
287 nlen += u - '0';
288 u_get_char(format, &s, &u);
290 for (fo = fopts; ; fo++) {
291 BUG_ON(fo->ch == 0);
292 if (fo->ch == u) {
293 int type = fo->type;
294 int l = 0;
296 if (fo->empty) {
297 /* nothing */
298 } else if (type == FO_STR) {
299 l = u_str_width(fo->fo_str);
300 } else if (type == FO_INT) {
301 l = numlen(fo->fo_int);
302 } else if (type == FO_TIME) {
303 int t = fo->fo_time;
305 if (t < 0) {
306 t *= -1;
307 l++;
309 if (t >= 3600) {
310 l += numlen(t / 3600) + 6;
311 } else {
312 l += 5;
315 if (nlen) {
316 *len += nlen;
317 } else {
318 *len += l;
320 break;
325 /* max utf-8 char len is 4 */
326 lsize = llen * 4;
327 rsize = rlen * 4;
329 if (l_str_size < lsize) {
330 free(l_str);
331 l_str_size = lsize;
332 l_str = xnew(char, l_str_size + 1);
333 l_str[l_str_size] = 0;
335 if (r_str_size < rsize) {
336 free(r_str);
337 r_str_size = rsize;
338 r_str = xnew(char, r_str_size + 1);
339 r_str[r_str_size] = 0;
341 l_str[0] = 0;
342 r_str[0] = 0;
344 if (lsize > 0) {
345 print(l_str, format, fopts);
346 #if DEBUG > 1
348 int ul = u_str_width(l_str);
349 if (ul != llen)
350 d_print("L %d != %d: size=%d '%s'\n", ul, llen, lsize, l_str);
352 #endif
354 if (rsize > 0) {
355 print(r_str, format + eq_pos + 1, fopts);
356 #if DEBUG > 1
358 int ul = u_str_width(r_str);
359 if (ul != rlen)
360 d_print("R %d != %d: size=%d '%s'\n", ul, rlen, rsize, r_str);
362 #endif
365 /* NOTE: any invalid UTF-8 bytes have already been converted to <xx>
366 * (ASCII) where x is hex digit
369 if (llen + rlen <= str_width) {
370 /* both fit */
371 int ws_len = str_width - llen - rlen;
372 int pos = 0;
374 /* I would use strcpy if it returned anything useful */
375 while (l_str[pos]) {
376 str[pos] = l_str[pos];
377 pos++;
379 memset(str + pos, ' ', ws_len);
380 strcpy(str + pos + ws_len, r_str);
381 } else {
382 int l_space = str_width - rlen;
383 int pos = 0;
384 int idx = 0;
386 if (l_space > 0)
387 pos = u_copy_chars(str, l_str, &l_space);
388 if (l_space < 0) {
389 int w = -l_space;
391 idx = u_skip_chars(r_str, &w);
392 if (w != -l_space)
393 str[pos++] = ' ';
395 strcpy(str + pos, r_str + idx);
397 return 0;
400 /* FIXME: compare with struct format_option[] */
401 int format_valid(const char *format)
403 int s = 0;
405 while (format[s]) {
406 uchar u;
408 u_get_char(format, &s, &u);
409 if (u == '%') {
410 int pad_zero = 0;
412 u_get_char(format, &s, &u);
413 if (u == '%' || u == '=')
414 continue;
415 if (u == '-')
416 u_get_char(format, &s, &u);
417 if (u == '0') {
418 pad_zero = 1;
419 u_get_char(format, &s, &u);
421 while (isdigit(u))
422 u_get_char(format, &s, &u);
423 switch (u) {
424 case 'a':
425 case 'l':
426 case 't':
427 case 'd':
428 case 'g':
429 case 'c':
430 case 'f':
431 case 'F':
432 if (pad_zero)
433 return 0;
434 break;
435 case 'D':
436 case 'n':
437 case 'y':
438 break;
439 default:
440 return 0;
444 return 1;