Add cyan.theme
[cmus.git] / format_print.c
blob978c5c3a91246244c8f621c9ed1d6b27a31f682b
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include <format_print.h>
21 #include <uchar.h>
22 #include <utils.h>
23 #include <xmalloc.h>
24 #include <debug.h>
26 #include <string.h>
27 #include <ctype.h>
29 static int numlen(int num)
31 int digits;
33 if (num < 0)
34 return 1; /* '?' */
35 digits = 0;
36 do {
37 num /= 10;
38 digits++;
39 } while (num);
40 return digits;
43 static int stack_print(char *buf, char *stack, int stack_len, int width, int align_left, char pad)
45 int i = 0;
47 if (width) {
48 if (align_left) {
49 while (i < width && stack_len)
50 buf[i++] = stack[--stack_len];
51 while (i < width)
52 buf[i++] = pad;
53 } else {
54 int pad_len;
56 if (stack_len > width)
57 stack_len = width;
58 pad_len = width - stack_len;
59 while (i < pad_len)
60 buf[i++] = pad;
61 while (i < width)
62 buf[i++] = stack[--stack_len];
64 } else {
65 while (stack_len)
66 buf[i++] = stack[--stack_len];
68 return i;
71 static int print_num(char *buf, int num, int width, int align_left, char pad)
73 char stack[20];
74 int i, p;
76 if (num < 0) {
77 if (width == 0)
78 width = 1;
79 for (i = 0; i < width; i++)
80 buf[i] = '?';
81 return width;
83 p = 0;
84 do {
85 stack[p++] = num % 10 + '0';
86 num /= 10;
87 } while (num);
89 return stack_print(buf, stack, p, width, align_left, pad);
92 /* print '{,-}{h:,}mm:ss' */
93 static int print_time(char *buf, int t, int width, int align_left, char pad)
95 int h, m, s;
96 char stack[32];
97 int neg = 0;
98 int p = 0;
100 if (t < 0) {
101 neg = 1;
102 t *= -1;
104 h = t / 3600;
105 t = t % 3600;
106 m = t / 60;
107 s = t % 60;
109 /* put all chars to stack in reverse order ;) */
110 stack[p++] = s % 10 + '0';
111 stack[p++] = s / 10 + '0';
112 stack[p++] = ':';
113 stack[p++] = m % 10 + '0';
114 stack[p++] = m / 10 + '0';
115 if (h) {
116 stack[p++] = ':';
117 do {
118 stack[p++] = h % 10 + '0';
119 h /= 10;
120 } while (h);
122 if (neg)
123 stack[p++] = '-';
125 return stack_print(buf, stack, p, width, align_left, pad);
129 * buf:
130 * where to print
131 * idx:
132 * index to @buf
133 * str:
134 * what to print
135 * width:
136 * field width. 0 if not set
137 * aling_left:
138 * 1 => aling left. 0 => align right
140 static void print_str(char *buf, int *idx, const char *str, int width, int align_left)
142 int d = *idx;
144 if (width) {
145 int ws_len;
146 int i = 0;
148 if (align_left) {
149 i = width;
150 d += u_copy_chars(buf + d, str, &i);
152 ws_len = width - i;
153 memset(buf + d, ' ', ws_len);
154 d += ws_len;
155 } else {
156 int s = 0;
158 ws_len = width - u_str_width(str);
160 if (ws_len > 0) {
161 memset(buf + d, ' ', ws_len);
162 d += ws_len;
163 i += ws_len;
166 if (ws_len < 0) {
167 int w, c = -ws_len;
168 uchar u;
170 while (c > 0) {
171 u_get_char(str, &s, &u);
172 w = u_char_width(u);
173 c -= w;
175 if (c < 0) {
176 /* gaah, skipped too much */
177 if (u_char_width(u) == 2) {
178 /* double-byte */
179 buf[d++] = ' ';
180 } else {
181 /* <xx> */
182 if (c == -3)
183 buf[d++] = hex_tab[(u >> 4) & 0xf];
184 if (c <= -2)
185 buf[d++] = hex_tab[u & 0xf];
186 buf[d++] = '>';
191 if (width - i > 0) {
192 int w = width - i;
194 d += u_copy_chars(buf + d, str + s, &w);
197 } else {
198 int s = 0;
199 uchar u;
201 while (1) {
202 u_get_char(str, &s, &u);
203 if (u == 0)
204 break;
205 u_set_char(buf, &d, u);
208 *idx = d;
211 static void print(char *str, const char *format, const struct format_option *fopts)
213 /* format and str indices */
214 int s = 0, d = 0;
216 while (format[s]) {
217 uchar u;
218 int nlen, align_left, pad, j;
220 u_get_char(format, &s, &u);
221 if (u != '%') {
222 u_set_char(str, &d, u);
223 continue;
225 u_get_char(format, &s, &u);
226 if (u == '%') {
227 u_set_char(str, &d, u);
228 continue;
231 if (u == '=') {
232 break;
235 align_left = 0;
236 if (u == '-') {
237 align_left = 1;
238 u_get_char(format, &s, &u);
240 pad = ' ';
241 if (u == '0') {
242 pad = '0';
243 u_get_char(format, &s, &u);
245 nlen = 0;
246 while (isdigit(u)) {
247 nlen *= 10;
248 nlen += u - '0';
249 u_get_char(format, &s, &u);
251 for (j = 0; fopts[j].ch; j++) {
252 if (fopts[j].ch != u)
253 continue;
255 if (fopts[j].empty) {
256 memset(str + d, ' ', nlen);
257 d += nlen;
258 } else if (fopts[j].type == FO_STR) {
259 print_str(str, &d, fopts[j].fo_str, nlen, align_left);
260 } else if (fopts[j].type == FO_INT) {
261 d += print_num(str + d, fopts[j].fo_int, nlen, align_left, pad);
262 } else if (fopts[j].type == FO_TIME) {
263 d += print_time(str + d, fopts[j].fo_time, nlen, align_left, pad);
265 break;
268 str[d] = 0;
271 static char *l_str = NULL;
272 static char *r_str = NULL;
273 /* sizes in bytes. not counting the terminating 0! */
274 static int l_str_size = -1;
275 static int r_str_size = -1;
277 int format_print(char *str, int width, const char *format, const struct format_option *fopts)
279 /* lengths of left and right aligned texts */
280 int llen = 0;
281 int rlen = 0;
282 int *len = &llen;
283 int lsize, rsize;
284 int eq_pos = -1;
285 int s;
287 s = 0;
288 while (format[s]) {
289 uchar u;
290 int nlen, j;
292 u_get_char(format, &s, &u);
293 if (u != '%') {
294 (*len) += u_char_width(u);
295 continue;
297 u_get_char(format, &s, &u);
298 if (u == '%') {
299 (*len)++;
300 continue;
302 if (u == '=') {
303 /* right aligned text starts */
304 len = &rlen;
305 eq_pos = s - 1;
306 continue;
308 if (u == '-')
309 u_get_char(format, &s, &u);
310 nlen = 0;
311 while (isdigit(u)) {
312 /* minimum length of this field */
313 nlen *= 10;
314 nlen += u - '0';
315 u_get_char(format, &s, &u);
317 j = 0;
318 while (1) {
319 BUG_ON(fopts[j].ch == 0);
320 if (fopts[j].ch == u) {
321 int l = 0;
323 if (fopts[j].empty) {
324 /* nothing */
325 } else if (fopts[j].type == FO_STR) {
326 l = u_str_width(fopts[j].fo_str);
327 } else if (fopts[j].type == FO_INT) {
328 l = numlen(fopts[j].fo_int);
329 } else if (fopts[j].type == FO_TIME) {
330 int t = fopts[j].fo_time;
332 if (t < 0) {
333 t *= -1;
334 l++;
336 if (t >= 3600) {
337 l += numlen(t / 3600) + 6;
338 } else {
339 l += 5;
341 } else {
342 BUG("invalid format option\n");
344 if (nlen) {
345 *len += nlen;
346 } else {
347 *len += l;
349 break;
351 j++;
355 /* max utf-8 char len is 4 */
356 lsize = llen * 4;
357 rsize = rlen * 4;
359 if (l_str_size < lsize) {
360 free(l_str);
361 l_str_size = lsize;
362 l_str = xnew(char, l_str_size + 1);
363 l_str[l_str_size] = 0;
365 if (r_str_size < rsize) {
366 free(r_str);
367 r_str_size = rsize;
368 r_str = xnew(char, r_str_size + 1);
369 r_str[r_str_size] = 0;
371 l_str[0] = 0;
372 r_str[0] = 0;
374 if (lsize > 0) {
375 print(l_str, format, fopts);
376 #if DEBUG > 1
378 int ul = u_str_width(l_str);
379 if (ul != llen)
380 d_print("L %d != %d: size=%d '%s'\n", ul, llen, lsize, l_str);
382 #endif
384 if (rsize > 0) {
385 print(r_str, format + eq_pos + 1, fopts);
386 #if DEBUG > 1
388 int ul = u_str_width(r_str);
389 if (ul != rlen)
390 d_print("R %d != %d: size=%d '%s'\n", ul, rlen, rsize, r_str);
392 #endif
395 /* NOTE: any invalid UTF-8 bytes have already been converted to <xx>
396 * (ASCII) where x is hex digit
399 if (llen + rlen <= width) {
400 /* both fit */
401 int ws_len = width - llen - rlen;
402 int pos = 0;
404 /* I would use strcpy if it returned anything useful */
405 while (l_str[pos]) {
406 str[pos] = l_str[pos];
407 pos++;
409 memset(str + pos, ' ', ws_len);
410 strcpy(str + pos + ws_len, r_str);
411 } else {
412 int l_space = width - rlen;
413 int pos = 0;
414 int idx = 0;
416 if (l_space > 0)
417 pos = u_copy_chars(str, l_str, &l_space);
418 if (l_space < 0) {
419 int w = -l_space;
421 idx = u_skip_chars(r_str, &w);
422 if (w != -l_space)
423 str[pos++] = ' ';
425 strcpy(str + pos, r_str + idx);
427 return 0;
430 /* FIXME: compare with struct format_option[] */
431 int format_valid(const char *format)
433 int s;
435 s = 0;
436 while (format[s]) {
437 uchar u;
439 u_get_char(format, &s, &u);
440 if (u == '%') {
441 int pad_zero = 0;
443 u_get_char(format, &s, &u);
444 if (u == '%' || u == '=')
445 continue;
446 if (u == '-')
447 u_get_char(format, &s, &u);
448 if (u == '0') {
449 pad_zero = 1;
450 u_get_char(format, &s, &u);
452 while (isdigit(u))
453 u_get_char(format, &s, &u);
454 switch (u) {
455 case 'a':
456 case 'l':
457 case 't':
458 case 'd':
459 case 'f':
460 case 'F':
461 if (pad_zero)
462 return 0;
463 break;
464 case 'D':
465 case 'n':
466 case 'y':
467 case 'g':
468 break;
469 default:
470 return 0;
474 return 1;