1 /********************************************************************
2  *                                                                  *
3  * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
4  * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
5  * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
6  * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
7  *                                                                  *
8  * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001                *
9  * by Stan Seibert <volsung@xiph.org> AND OTHER CONTRIBUTORS        *
10  * http://www.xiph.org/                                             *
11  *                                                                  *
12  ********************************************************************
13 
14  last mod: $Id$
15 
16  ********************************************************************/
17 
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <limits.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 #include "playlist.h"
32 #include "i18n.h"
33 
34 /* Work with *BSD differences */
35 #if !defined(NAME_MAX) && defined(MAXNAMLEN)
36 #define NAME_MAX MAXNAMLEN
37 #endif
38 
playlist_element_create(char * filename)39 playlist_element_t *playlist_element_create(char *filename)
40 {
41   playlist_element_t *element = (playlist_element_t *)
42                                    malloc(sizeof(playlist_t));
43 
44   if (element == NULL) {
45       fprintf(stderr,
46 	      _("ERROR: Out of memory in create_playlist_member().\n"));
47       exit(1);
48   }
49 
50   if (filename == NULL)
51     element->filename = NULL;
52   else {
53     element->filename = strdup(filename);
54 
55     if (element->filename == NULL) {
56 	fprintf(stderr,
57 		_("ERROR: Out of memory in create_playlist_member().\n"));
58 	exit(1);
59     }
60   }
61 
62   element->next = NULL;
63 
64   return element;
65 }
66 
67 
68 /* Only destroys the current node.  Does not affect linked nodes. */
playlist_element_destroy(playlist_element_t * element)69 void playlist_element_destroy(playlist_element_t *element)
70 {
71   free(element->filename);
72   free(element);
73 }
74 
75 
76 
playlist_create()77 playlist_t *playlist_create()
78 {
79   playlist_t *list = (playlist_t *) malloc(sizeof(playlist_t));
80 
81   if (list != NULL) {
82     list->head = playlist_element_create(NULL);
83     list->last = list->head;
84   }
85 
86   return list;
87 }
88 
89 
playlist_destroy(playlist_t * list)90 void playlist_destroy(playlist_t *list) {
91   playlist_element_t *next_element;
92 
93   while (list->head != NULL) {
94     next_element = list->head->next;
95 
96     playlist_element_destroy(list->head);
97 
98     list->head = next_element;
99   }
100 
101   free(list);
102 }
103 
104 
105 /* All of the playlist_append_* functions return
106    1 if append was successful
107    0 if failure (either directory could not be accessed or playlist on disk
108    could not be opened)
109 */
110 
111 
112 /* Add this filename to the playlist.  Filename will be strdup()'ed.  Note
113    that this function will never fail. */
playlist_append_file(playlist_t * list,char * filename)114 int playlist_append_file(playlist_t *list, char *filename)
115 {
116   list->last->next = playlist_element_create(filename);
117   list->last = list->last->next;
118 
119   return 1; /* No way to fail */
120 }
121 
122 /* Recursively adds files from the directory and subdirectories */
123 #if defined(HAVE_ALPHASORT) && defined(HAVE_SCANDIR)
playlist_append_directory(playlist_t * list,char * dirname)124 int playlist_append_directory(playlist_t *list, char *dirname)
125 {
126   int dir_len = strlen(dirname);
127   int num_entries = 0, i = 0;
128   struct dirent **entries;
129   struct stat stat_buf;
130   char nextfile[NAME_MAX + 1];
131 
132   num_entries = scandir(dirname, &entries, 0, alphasort);
133 
134   if (num_entries < 0) {
135     return 0;
136   }
137 
138   for (i=0; i<num_entries; i++) {
139     int sub_len = strlen(entries[i]->d_name);
140 
141     /* Make sure full pathname is within limits and we don't parse the
142        relative directory entries. */
143     if (dir_len + sub_len + 1 < NAME_MAX
144         && strcmp(entries[i]->d_name, ".") != 0
145         && strcmp(entries[i]->d_name, "..") != 0  ) {
146 
147       /* Build the new full pathname */
148       strcpy(nextfile, dirname);
149       strcat(nextfile, "/");
150       strcat(nextfile, entries[i]->d_name);
151 
152       if (stat(nextfile, &stat_buf) == 0) {
153 
154         /* Decide what type of entry this is */
155         if (S_ISDIR(stat_buf.st_mode)) {
156 
157         /* Recursively follow directories */
158           if ( playlist_append_directory(list, nextfile) == 0 ) {
159             fprintf(stderr,
160                   _("Warning: Could not read directory %s.\n"),
161                     nextfile);
162           }
163         } else {
164         /* Assume everything else is a file of some sort */
165           playlist_append_file(list, nextfile);
166         }
167       }
168     }
169 
170     free(entries[i]);
171   }
172 
173   free(entries);
174 
175   return 1;
176 }
177 #else
playlist_append_directory(playlist_t * list,char * dirname)178 int playlist_append_directory(playlist_t *list, char *dirname)
179 {
180   DIR *dir;
181   int dir_len = strlen(dirname);
182   struct dirent *entry;
183   struct stat stat_buf;
184   char nextfile[NAME_MAX + 1];
185 
186   dir = opendir(dirname);
187 
188   if (dir == NULL) {
189     return 0;
190   }
191 
192   entry = readdir(dir);
193   while (entry != NULL) {
194     int sub_len = strlen(entry->d_name);
195 
196     /* Make sure full pathname is within limits and we don't parse the
197        relative directory entries. */
198     if (dir_len + sub_len + 1 < NAME_MAX
199 	&& strcmp(entry->d_name, ".") != 0
200 	&& strcmp(entry->d_name, "..") != 0  ) {
201 
202       /* Build the new full pathname */
203       strcpy(nextfile, dirname);
204       strcat(nextfile, "/");
205       strcat(nextfile, entry->d_name);
206 
207       if (stat(nextfile, &stat_buf) == 0) {
208 
209 	/* Decide what type of entry this is */
210 	if (S_ISDIR(stat_buf.st_mode)) {
211 
212 	/* Recursively follow directories */
213 	  if ( playlist_append_directory(list, nextfile) == 0 ) {
214 	    fprintf(stderr,
215 		  _("Warning: Could not read directory %s.\n"),
216 		    nextfile);
217 	  }
218 	} else {
219 	/* Assume everything else is a file of some sort */
220 	  playlist_append_file(list, nextfile);
221 	}
222       }
223     }
224 
225     entry = readdir(dir);
226   }
227 
228   closedir(dir);
229 
230   return 1;
231 }
232 #endif
233 
234 
235 /* Opens a file containing filenames, one per line, and adds them to the
236    playlist */
playlist_append_from_file(playlist_t * list,char * playlist_filename)237 int playlist_append_from_file(playlist_t *list, char *playlist_filename)
238 {
239   FILE *fp;
240   char filename[NAME_MAX+1];
241   struct stat stat_buf;
242   int length;
243   int i;
244 
245   if (strcmp(playlist_filename, "-") == 0)
246     fp = stdin;
247   else
248     fp = fopen(playlist_filename, "r");
249 
250   if (fp == NULL)
251     return 0;
252 
253   while (!feof(fp)) {
254 
255     if ( fgets(filename, NAME_MAX+1 /* no, really! */, fp) == NULL )
256       continue;
257 
258     filename[NAME_MAX] = '\0'; /* Just to make sure */
259     length = strlen(filename);
260 
261     /* Skip blank lines */
262     for (i = 0; i < length && isspace(filename[i]); i++);
263     if (i == length)
264       continue;
265 
266     /* Crop off trailing newlines if present. Handle DOS (\r\n), Unix (\n)
267      * and MacOS<9 (\r) line endings. */
268     if (filename[length - 2] == '\r' && filename[length - 1] == '\n')
269       filename[length - 2] = '\0';
270     else if (filename[length - 1] == '\n' || filename[length - 1] == '\r')
271       filename[length - 1] = '\0';
272 
273     if (stat(filename, &stat_buf) == 0) {
274 
275       if (S_ISDIR(stat_buf.st_mode)) {
276 	if (playlist_append_directory(list, filename) == 0)
277 	  fprintf(stderr,
278 		  _("Warning from playlist %s: "
279 		    "Could not read directory %s.\n"), playlist_filename,
280 		  filename);
281       } else {
282 	playlist_append_file(list, filename);
283       }
284     } else /* If we can't stat it, it might be a non-disk source */
285       playlist_append_file(list, filename);
286 
287   }
288 
289   return 1;
290 }
291 
292 
293 /* Return the number of items in the playlist */
playlist_length(playlist_t * list)294 int playlist_length(playlist_t *list)
295 {
296   int length;
297   playlist_element_t *element;
298 
299   element = list->head;
300   length = 0; /* don't count head node */
301   while (element->next != NULL) {
302     length++;
303     element = element->next;
304   }
305 
306   return length;
307 }
308 
309 
310 /* Convert the playlist to an array of strings.  Strings are deep copied.
311    Size will be set to the number of elements in the array. */
playlist_to_array(playlist_t * list,int * size)312 char **playlist_to_array(playlist_t *list, int *size)
313 {
314   char **array;
315   int i;
316   playlist_element_t *element;
317 
318   *size = playlist_length(list);
319   array = calloc(*size, sizeof(char *));
320 
321   if (array == NULL) {
322     fprintf(stderr,
323 	    _("ERROR: Out of memory in playlist_to_array().\n"));
324     exit(1);
325   }
326 
327   for (i = 0, element = list->head->next;
328        i < *size;
329        i++, element = element->next) {
330 
331     array[i] = strdup(element->filename);
332 
333     if (array[i] == NULL) {
334       fprintf(stderr,
335 	      _("ERROR: Out of memory in playlist_to_array().\n"));
336       exit(1);
337     }
338   }
339 
340 
341   return array;
342 }
343 
344 
345 /* Deallocate array and all contained strings created by playlist_to_array. */
playlist_array_destroy(char ** array,int size)346 void playlist_array_destroy(char **array, int size)
347 {
348   int i;
349 
350   for (i = 0; i < size; i++)
351     free(array[i]);
352 
353   free(array);
354 }
355