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