1 /* -*- linux-c -*-
2 Copyright (C) 2007 Tom Szilagyi
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
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., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 $Id: utils.c 1261 2012-10-23 21:54:06Z assworth $
19 */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <glib.h>
29
30 #include "common.h"
31 #include "httpc.h"
32 #include "i18n.h"
33 #include "options.h"
34 #include "utils.h"
35
36
37 extern options_t options;
38
39
convf(char * s)40 float convf(char * s) {
41
42 float val, pow;
43 int i, sign;
44
45 for (i = 0; s[i] == ' ' || s[i] == '\n' || s[i] == '\t'; i++);
46 sign = 1;
47 if (s[i] == '+' || s[i] == '-')
48 sign = (s[i++] == '+') ? 1 : -1;
49 for (val = 0; s[i] >= '0' && s[i] <= '9'; i++)
50 val = 10 * val + s[i] - '0';
51 if ((s[i] == '.') || (s[i] == ','))
52 i++;
53 for (pow = 1; s[i] >= '0' && s[i] <= '9'; i++) {
54 val = 10 * val + s[i] - '0';
55 pow *= 10;
56 }
57 return(sign * val / pow);
58 }
59
60
61 int
is_all_wspace(char * str)62 is_all_wspace(char * str) {
63
64 int i;
65
66 if (str == NULL) {
67 return 1;
68 }
69
70 for (i = 0; str[i]; i++) {
71 if (str[i] != ' ' && str[i] != '\t') {
72 return 0;
73 }
74 }
75
76 return 1;
77 }
78
79
80 int
cut_trailing_whitespace(char * str)81 cut_trailing_whitespace(char * str) {
82
83 int i = strlen(str) - 1;
84
85 while (i >= 0) {
86 if ((str[i] == ' ') || (str[i] == '\t')) {
87 str[i] = '\0';
88 } else {
89 break;
90 }
91 --i;
92 }
93 return ((i >= 0) ? 1 : 0);
94 }
95
96
97 /* To print a string containing printf formatters, but
98 * *not* followed by arguments -- I just want the plain
99 * string, please...
100 */
101 void
escape_percents(char * in,char * out)102 escape_percents(char * in, char * out) {
103
104 int i;
105 int j = 0;
106 for (i = 0; in[i] != '\0'; i++) {
107 out[j] = in[i];
108 ++j;
109 if (in[i] == '%') {
110 out[j] = '%';
111 ++j;
112 }
113 }
114 out[j] = '\0';
115 }
116
117 int
contains(int * cbuf,int num,char c)118 contains(int * cbuf, int num, char c) {
119
120 int n;
121 for (n = 0; n < num; ++n) {
122 if (c == cbuf[n]) {
123 return n;
124 }
125 }
126
127 return -1;
128 }
129
130 /* returns
131 0: success
132 -1: error: expected '{' after '?'
133 -2: error: expected '}' after '{'
134 -3: error: unknown conversion type character after '%'
135 -4: error: unknown conversion type character after '?'
136 */
137 int
make_string_va(char * buf,char * format,...)138 make_string_va(char * buf, char * format, ...) {
139
140 va_list args;
141 char ** strbuf = NULL;
142 int * cbuf = NULL;
143 int ch;
144 int num = 0;
145 int i, j, n, conj, disj;
146
147 va_start(args, format);
148
149 while ((ch = va_arg(args, int)) != 0) {
150
151 char * p;
152
153 ++num;
154 cbuf = (int *)realloc(cbuf, num * sizeof(int));
155 cbuf[num-1] = ch;
156 strbuf = (char **)realloc(strbuf, num * sizeof(char *));
157 p = va_arg(args, char *);
158 strbuf[num-1] = (p != NULL) ? strdup(p) : NULL;
159 }
160
161 va_end(args);
162
163 cbuf = (int *)realloc(cbuf, (num + 3) * sizeof(int));
164 strbuf = (char **)realloc(strbuf, (num + 3) * sizeof(char *));
165 cbuf[num] = '%'; strbuf[num] = strdup("%"); ++num;
166 cbuf[num] = '?'; strbuf[num] = strdup("?"); ++num;
167 cbuf[num] = '}'; strbuf[num] = strdup("}"); ++num;
168
169 i = 0;
170 j = 0;
171 while (format[i]) {
172
173 switch (format[i]) {
174 case '%':
175 if ((n = contains(cbuf, num, format[i+1])) < 0) {
176 return -3;
177 } else {
178 if (strbuf[n]) {
179 buf[j] = '\0';
180 strcat(buf, strbuf[n]);
181 j = strlen(buf);
182 }
183 i += 2;
184 }
185 break;
186 case '?':
187 disj = 0;
188 conj = 1;
189 for (++i; format[i]; ++i) {
190 if (format[i] == '|' || format[i] == '{') {
191 disj = disj || conj;
192 conj = 1;
193 } else {
194 if ((n = contains(cbuf, num, format[i])) < 0) {
195 return -4;
196 } else {
197 conj = conj && (strbuf[n] != NULL);
198 }
199 }
200 if (format[i] == '{') {
201 break;
202 }
203 }
204 if (!format[i]) {
205 return -1;
206 }
207 ++i;
208 while (format[i] && format[i] != '}') {
209 if (format[i] == '%') {
210 if ((n = contains(cbuf, num, format[i+1])) < 0) {
211 return -3;
212 } else {
213 if (disj && strbuf[n]) {
214 buf[j] = '\0';
215 strcat(buf, strbuf[n]);
216 j = strlen(buf);
217 }
218 i += 2;
219 }
220 } else {
221 if (disj) {
222 buf[j++] = format[i];
223 }
224 ++i;
225 }
226 }
227 if (!format[i]) {
228 return -2;
229 }
230 ++i;
231 break;
232 default:
233 buf[j++] = format[i++];
234 break;
235 }
236 }
237
238 buf[j] = '\0';
239
240 for (n = 0; n < num; n++) {
241 if (strbuf[n] != NULL) {
242 free(strbuf[n]);
243 }
244 }
245
246 free(cbuf);
247 free(strbuf);
248
249 return 0;
250 }
251
252 void
make_title_string(char * dest,char * templ,char * artist,char * record,char * track)253 make_title_string(char * dest, char * templ,
254 char * artist, char * record, char * track) {
255
256 make_string_va(dest, templ, 'a', artist, 'r', record, 't', track, 0);
257 }
258
259 void
make_string_strerror(int ret,char * buf)260 make_string_strerror(int ret, char * buf) {
261
262 switch (ret) {
263 case -1:
264 strncpy(buf, _("Unexpected end of string after '?'."), MAXLEN-1);
265 break;
266 case -2:
267 strncpy(buf, _("Expected '}' after '{', but end of string found."), MAXLEN-1);
268 break;
269 case -3:
270 strncpy(buf, _("Unknown conversion type character found after '%%'."), MAXLEN-1);
271 break;
272 case -4:
273 strncpy(buf, _("Unknown conversion type character found after '?'."), MAXLEN-1);
274 break;
275 }
276 }
277
278 /* returns (hh:mm:ss) or (mm:ss) format time string from sample position */
279 void
sample2time(unsigned long SR,unsigned long long sample,char * str,int sign)280 sample2time(unsigned long SR, unsigned long long sample, char * str, int sign) {
281
282 int h;
283 char m, s;
284
285 if (!SR)
286 SR = 1;
287
288 h = (sample / SR) / 3600;
289 m = (sample / SR) / 60 - h * 60;
290 s = (sample / SR) - h * 3600 - m * 60;
291
292 if (h > 0) {
293 sprintf(str, (sign)?("-%d:%02d:%02d"):("%d:%02d:%02d"), h, m, s);
294 } else {
295 sprintf(str, (sign)?("-%02d:%02d"):("%02d:%02d"), m, s);
296 }
297 }
298
299
300 /* converts a length measured in seconds to the appropriate string */
301 void
time2time(float seconds,char * str)302 time2time(float seconds, char * str) {
303
304 int d, h;
305 char m, s;
306
307 d = seconds / 86400;
308 h = seconds / 3600;
309 m = seconds / 60 - h * 60;
310 s = seconds - h * 3600 - m * 60;
311 h = h - d * 24;
312
313 if (d > 0) {
314 if (d == 1 && h > 9) {
315 sprintf(str, "%d %s, %2d:%02d:%02d", d, _("day"), h, m, s);
316 } else if (d == 1 && h < 9) {
317 sprintf(str, "%d %s, %1d:%02d:%02d", d, _("day"), h, m, s);
318 } else if (d != 1 && h > 9) {
319 sprintf(str, "%d %s, %2d:%02d:%02d", d, _("days"), h, m, s);
320 } else {
321 sprintf(str, "%d %s, %1d:%02d:%02d", d, _("days"), h, m, s);
322 }
323 } else if (h > 0) {
324 if (h > 9) {
325 sprintf(str, "%02d:%02d:%02d", h, m, s);
326 } else {
327 sprintf(str, "%1d:%02d:%02d", h, m, s);
328 }
329 } else {
330 sprintf(str, "%02d:%02d", m, s);
331 }
332 }
333
334
335 void
time2time_na(float seconds,char * str)336 time2time_na(float seconds, char * str) {
337
338 if (seconds == 0.0) {
339 strcpy(str, "N/A");
340 } else {
341 time2time(seconds, str);
342 }
343 }
344
345
346 /* out should be defined as char[MAXLEN] */
347 void
normalize_filename(const char * in,char * out)348 normalize_filename(const char * in, char * out) {
349
350 if (httpc_is_url(in)) {
351 strncpy(out, in, MAXLEN-1);
352 return;
353 }
354
355 switch (in[0]) {
356 case '/':
357 strncpy(out, in, MAXLEN-1);
358 break;
359 case '~':
360 snprintf(out, MAXLEN-1, "%s%s", options.home, in + 1);
361 break;
362 default:
363 snprintf(out, MAXLEN-1, "%s/%s", options.cwd, in);
364 break;
365 }
366 }
367
368 void
free_strdup(char ** s,const char * str)369 free_strdup(char ** s, const char * str) {
370
371 if (*s != NULL) {
372 free(*s);
373 }
374
375 if (str != NULL) {
376 *s = strdup(str);
377 } else {
378 *s = NULL;
379 }
380 }
381
382 int
is_valid_year(int y)383 is_valid_year(int y) {
384
385 return y >= YEAR_MIN && y <= YEAR_MAX;
386 }
387
388 int
is_dir(char * path)389 is_dir(char * path) {
390
391 struct stat st_file;
392
393 if (stat(path, &st_file) == -1) {
394 return 0;
395 }
396
397 return S_ISDIR(st_file.st_mode);
398 }
399
400 #ifndef HAVE_STRNDUP
401 char *
strndup(char * str,size_t len)402 strndup(char * str, size_t len) {
403 char * dup = (char *)malloc(len + 1);
404 if (dup) {
405 strncpy(dup, str, len);
406 dup[len] = '\0';
407 }
408 return dup;
409 }
410 #endif /* HAVE_STRNDUP */
411
412 #ifndef HAVE_STRCASESTR
413 char
toupper(char c)414 toupper(char c) {
415
416 if (c >= 'a' && c <= 'z')
417 return (c-32);
418 return c;
419 }
420
421 char *
strcasestr(char * haystack,char * needle)422 strcasestr(char *haystack, char *needle) {
423
424 char *s1 = needle;
425 char *s2 = haystack;
426 char *sr = NULL;
427 int inside = 0;
428
429 while (*s2 != '\0') {
430 if (toupper(*s1) == toupper(*s2)) {
431 if (inside == 0) {
432 sr = s2;
433 }
434 inside = 1;
435 ++s1;
436 ++s2;
437 if (*s1 == '\0') {
438 break;
439 }
440 } else {
441 inside = 0;
442 s1 = needle;
443 ++s2;
444 }
445 }
446 if (inside == 1)
447 return sr;
448 return NULL;
449 }
450 #endif /* HAVE_STRCASESTR */
451
452
453 map_t *
map_new(char * str)454 map_new(char * str) {
455
456 map_t * map;
457
458 if ((map = (map_t *)malloc(sizeof(map_t))) == NULL) {
459 fprintf(stderr, "map_new(): malloc error\n");
460 return NULL;
461 }
462
463 strncpy(map->str, str, MAXLEN-1);
464 map->count = 1;
465 map->next = NULL;
466
467 return map;
468 }
469
470 void
map_put(map_t ** map,char * str)471 map_put(map_t ** map, char * str) {
472
473 map_t * pmap;
474 map_t * _pmap;
475
476 if (str == NULL || str[0] == '\0') {
477 return;
478 }
479
480 if (*map == NULL) {
481 *map = map_new(str);
482 } else {
483
484 for (_pmap = pmap = *map; pmap; _pmap = pmap, pmap = pmap->next) {
485
486 char * key1 = g_utf8_casefold(str, -1);
487 char * key2 = g_utf8_casefold(pmap->str, -1);
488
489 if (!g_utf8_collate(key1, key2)) {
490 pmap->count++;
491 g_free(key1);
492 g_free(key2);
493 return;
494 }
495
496 g_free(key1);
497 g_free(key2);
498 }
499
500 _pmap->next = map_new(str);
501 }
502 }
503
504 char *
map_get_max(map_t * map)505 map_get_max(map_t * map) {
506
507 map_t * pmap;
508 int max = 0;
509 char * str = NULL;
510
511 for (pmap = map; pmap; pmap = pmap->next) {
512 if (max <= pmap->count) {
513 str = pmap->str;
514 max = pmap->count;
515 }
516 }
517
518 return str;
519 }
520
521 void
map_free(map_t * map)522 map_free(map_t * map) {
523
524 map_t * pmap;
525
526 for (pmap = map; pmap; map = pmap) {
527 pmap = map->next;
528 free(map);
529 }
530 }
531
532
533 // vim: shiftwidth=8:tabstop=8:softtabstop=8:
534
535