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