1 /* Copyright (C) Heikki Orsila 2003-2005
2 email: heikki.orsila@iki.fi
3 License: LGPL and GPL
4 */
5
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <time.h>
12 #include <string.h>
13 #include <fcntl.h>
14 #include <errno.h>
15 #include <stdint.h>
16 #include <assert.h>
17
18 #include "unixwalkdir.h"
19 #include "uadeconfig.h"
20 #include "unixatomic.h"
21 #include "playlist.h"
22 #include "uade123.h"
23 #include "ossupport.h"
24 #include "unixsupport.h"
25
26 static int random_fd = -1;
27 #ifdef UADE_CONFIG_HAVE_URANDOM
28 static int using_urandom = 1;
29 #else
30 static int using_urandom = 0;
31 #endif
32
33
random_init(void)34 static void random_init(void)
35 {
36 if (using_urandom == 0)
37 srandom(time(NULL));
38 }
39
40
get_random(int max)41 static int get_random(int max)
42 {
43 int ret;
44 uint8_t buf[4];
45 if (using_urandom) {
46 if (random_fd == -1) {
47 random_fd = open("/dev/urandom", O_RDONLY);
48 if (random_fd < 0) {
49 fprintf(stderr, "not using urandom anymore: %s\n", strerror(errno));
50 using_urandom = 0;
51 goto nourandom;
52 }
53 }
54 ret = atomic_read(random_fd, buf, sizeof(buf));
55 if (ret < 0) {
56 fprintf(stderr, "error on reading urandom: %s\n", strerror(errno));
57 using_urandom = 0;
58 goto nourandom;
59 } else if (ret == 0) {
60 fprintf(stderr, "unexpected eof on urandom\n");
61 using_urandom = 0;
62 goto nourandom;
63 }
64 return ((double) max * ((* (uint32_t * ) buf) & 0x3fffffff)) / 0x40000000;
65 }
66 nourandom:
67 return ((double) max * random()) / (RAND_MAX + 1.0);
68 }
69
70
playlist_init(struct playlist * pl)71 int playlist_init(struct playlist *pl)
72 {
73 random_init();
74 pl->pos = 0;
75 pl->randomize = 0;
76 pl->repeat = 0;
77 pl->valid = chrarray_init(&pl->list) ? 1 : 0;
78 return pl->valid;
79 }
80
81
playlist_iterator(struct playlist_iterator * pli,struct playlist * pl)82 void playlist_iterator(struct playlist_iterator *pli, struct playlist *pl)
83 {
84 assert(pl != NULL);
85
86 pli->index = 0;
87 pli->pl = pl;
88 }
89
90
playlist_iterator_get(struct playlist_iterator * pli)91 char *playlist_iterator_get(struct playlist_iterator *pli)
92 {
93 char *s;
94 struct chrarray *arr;
95 struct chrentry *e;
96
97 arr = &pli->pl->list;
98
99 if (pli->index >= arr->n_entries)
100 return NULL;
101
102 e = &arr->entries[pli->index];
103 s = &arr->data[e->off];
104
105 pli->index++;
106
107 return s;
108 }
109
110
111 /* enable == 0: disable random play
112 enable == 1: enable random play
113 enable == -1: toggle random play state between enabled and disabled
114
115 Returns new state value.
116 */
playlist_random(struct playlist * pl,int enable)117 int playlist_random(struct playlist *pl, int enable)
118 {
119 if (enable < 0) {
120 pl->randomize = pl->randomize ? 0 : 1;
121 } else {
122 pl->randomize = enable ? 1 : 0;
123 }
124
125 return pl->randomize;
126 }
127
128
129 /* Shuffle the whole deck at once */
playlist_randomize(struct playlist * pl)130 void playlist_randomize(struct playlist *pl)
131 {
132 size_t i;
133 size_t n;
134 struct chrentry t;
135 struct chrarray *l = &pl->list;
136 size_t ri;
137
138 assert(l->n_entries >= 0);
139
140 n = l->n_entries;
141
142 for (i = 0; i < n; i++) {
143 ri = i + get_random(n - i);
144 if (ri != i) {
145 /* swap i and ri */
146 t = l->entries[i];
147 l->entries[i] = l->entries[ri];
148 l->entries[ri] = t;
149 }
150 }
151 }
152
153
playlist_repeat(struct playlist * pl)154 void playlist_repeat(struct playlist *pl)
155 {
156 pl->repeat = 1;
157 }
158
159
playlist_empty(struct playlist * pl)160 int playlist_empty(struct playlist *pl)
161 {
162 if (!pl->valid) {
163 fprintf(stderr, "playlist invalid\n");
164 return 1;
165 }
166 if (pl->list.n_entries == 0)
167 return 1;
168 if (pl->repeat)
169 return 0;
170 return pl->pos == pl->list.n_entries;
171 }
172
173
recursive_func(const char * file,enum uade_wtype wtype,void * pl)174 static void *recursive_func(const char *file, enum uade_wtype wtype, void *pl)
175 {
176 if (wtype == UADE_WALK_REGULAR_FILE) {
177 if (!playlist_add(pl, file, 0, 0))
178 fprintf(stderr, "error enqueuing %s\n", file);
179 }
180 return NULL;
181 }
182
183
playlist_add(struct playlist * pl,const char * name,int recursive,int cygwin)184 int playlist_add(struct playlist *pl, const char *name, int recursive,
185 int cygwin)
186 {
187 int ret = 0;
188 struct stat st;
189 int allocated = 0;
190 char *path;
191
192 if (!pl->valid)
193 goto out;
194
195 path = (char *) name;
196 if (cygwin) {
197 path = windows_to_cygwin_path(name);
198 allocated = 1;
199 }
200
201 if (stat(path, &st))
202 goto out;
203
204 if (S_ISREG(st.st_mode)) {
205 /* fprintf(stderr, "enqueuing regular: %s\n", path); */
206 ret = chrarray_add(&pl->list, path, strlen(path) + 1);
207
208 } else if (S_ISDIR(st.st_mode)) {
209 /* add directories to playlist only if 'recursive' is non-zero */
210 if (recursive) {
211
212 /* strip directory path of ending '/' characters */
213 char *strippedpath = strdup(path);
214 size_t len = strlen(path);
215
216 if (strippedpath == NULL)
217 die("Not enough memory for directory path.\n");
218
219 while (len > 0) {
220 len--;
221 if (strippedpath[len] != '/')
222 break;
223 strippedpath[len] = 0;
224 }
225
226 /* walk directory hierarchy */
227 uade_walk_directories(strippedpath, recursive_func, pl);
228
229 /* free stripped path */
230 free(strippedpath);
231
232 } else {
233 debug(1, "Not adding directory %s. Use -r to add recursively.\n", path);
234 }
235 ret = 1;
236 }
237
238 out:
239 if (allocated)
240 free(path);
241
242 return ret;
243 }
244
245
pl_get_random(char ** s,int * len,struct playlist * pl)246 static int pl_get_random(char **s, int *len, struct playlist *pl)
247 {
248 int i;
249 struct chrentry t;
250
251 pl->pos++;
252 if (pl->pos >= pl->list.n_entries) {
253 if (!pl->repeat)
254 return 0;
255 pl->pos = 0;
256 }
257
258 i = pl->pos + get_random(pl->list.n_entries - pl->pos);
259
260 t = pl->list.entries[i];
261
262 if (i != pl->pos) {
263 pl->list.entries[i] = pl->list.entries[pl->pos];
264 pl->list.entries[pl->pos] = t;
265 }
266
267 *s = &pl->list.data[t.off];
268 *len = t.len;
269 return 1;
270 }
271
272
pl_get_cur(char ** s,int * len,struct playlist * pl)273 static void pl_get_cur(char **s, int *len, struct playlist *pl)
274 {
275 struct chrentry *t;
276 t = &pl->list.entries[pl->pos];
277 *s = &pl->list.data[t->off];
278 *len = t->len;
279 }
280
281
pl_get_next(char ** s,int * len,struct playlist * pl)282 static int pl_get_next(char **s, int *len, struct playlist *pl)
283 {
284 pl->pos++;
285 if (pl->pos >= pl->list.n_entries) {
286 if (!pl->repeat)
287 return 0;
288 pl->pos = 0;
289 }
290
291 pl_get_cur(s, len, pl);
292 return 1;
293 }
294
295
pl_get_prev(char ** s,int * len,struct playlist * pl)296 static void pl_get_prev(char **s, int *len, struct playlist *pl)
297 {
298 if (pl->pos == 0) {
299 if (pl->repeat)
300 pl->pos = pl->list.n_entries - 1;
301 } else {
302 pl->pos--;
303 }
304
305 pl_get_cur(s, len, pl);
306 }
307
308
playlist_get(char * name,size_t maxlen,struct playlist * pl,int dir)309 int playlist_get(char *name, size_t maxlen, struct playlist *pl, int dir)
310 {
311 int len;
312 char *s;
313
314 if (!pl->valid)
315 return 0;
316
317 if (pl->list.n_entries == 0)
318 return 0;
319
320 if (!maxlen) {
321 fprintf(stderr, "uade123: playlist_get(): given maxlen = 0\n");
322 return 0;
323 }
324
325 if (dir == UADE_PLAY_NEXT) {
326 if (pl->randomize) {
327 if (pl_get_random(&s, &len, pl) == 0)
328 return 0;
329 } else {
330 if (pl_get_next(&s, &len, pl) == 0)
331 return 0;
332 }
333 } else if (dir == UADE_PLAY_PREVIOUS) {
334 pl_get_prev(&s, &len, pl);
335 } else if (dir == UADE_PLAY_CURRENT) {
336 pl_get_cur(&s, &len, pl);
337 } else {
338 die("invalid playlist direction: %d\n", dir);
339 }
340
341 if (len > maxlen) {
342 fprintf(stderr, "uade: playlist_get(): too long a string: %s\n", s);
343 return 0;
344 }
345
346 memcpy(name, s, len);
347 return 1;
348 }
349