qt: playlist: use item title if available
[vlc.git] / src / text / filesystem.c
1 /*****************************************************************************
2  * filesystem.c: Common file system helpers
3  *****************************************************************************
4  * Copyright (C) 2005-2006 VLC authors and VideoLAN
5  * Copyright © 2005-2008 Rémi Denis-Courmont
6  *
7  * Authors: Rémi Denis-Courmont
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_fs.h>
33 #include <vlc_sort.h>
34
35 #include <assert.h>
36
37 #include <stdio.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42
43 /**
44  * Opens a FILE pointer.
45  * @param filename file path, using UTF-8 encoding
46  * @param mode fopen file open mode
47  * @return NULL on error, an open FILE pointer on success.
48  */
49 FILE *vlc_fopen (const char *filename, const char *mode)
50 {
51     int rwflags = 0, oflags = 0;
52
53     for (const char *ptr = mode; *ptr; ptr++)
54     {
55         switch (*ptr)
56         {
57             case 'r':
58                 rwflags = O_RDONLY;
59                 break;
60
61             case 'a':
62                 rwflags = O_WRONLY;
63                 oflags |= O_CREAT | O_APPEND;
64                 break;
65
66             case 'w':
67                 rwflags = O_WRONLY;
68                 oflags |= O_CREAT | O_TRUNC;
69                 break;
70
71             case 'x':
72                 oflags |= O_EXCL;
73                 break;
74
75             case '+':
76                 rwflags = O_RDWR;
77                 break;
78
79 #ifdef O_BINARY
80             case 'b':
81                 oflags = (oflags & ~O_TEXT) | O_BINARY;
82                 break;
83
84             case 't':
85                 oflags = (oflags & ~O_BINARY) | O_TEXT;
86                 break;
87 #endif
88         }
89     }
90
91     int fd = vlc_open (filename, rwflags | oflags, 0666);
92     if (fd == -1)
93         return NULL;
94
95     FILE *stream = fdopen (fd, mode);
96     if (stream == NULL)
97         vlc_close (fd);
98
99     return stream;
100 }
101
102
103 static int dummy_select( const char *str )
104 {
105     (void)str;
106     return 1;
107 }
108
109 static int compar_void(const void *a, const void *b, void *data)
110 {
111     const char *sa = a, *sb = b;
112     int (*cmp)(const char **, const char **) = data;
113
114     return cmp(&sa, &sb);
115 }
116
117 /**
118  * Does the same as vlc_scandir(), but takes an open directory pointer
119  * instead of a directory path.
120  */
121 int vlc_loaddir( DIR *dir, char ***namelist,
122                   int (*select)( const char * ),
123                   int (*compar)( const char **, const char ** ) )
124 {
125     assert (dir);
126
127     if (select == NULL)
128         select = dummy_select;
129
130     char **tab = NULL;
131     unsigned num = 0;
132
133     rewinddir (dir);
134
135     for (unsigned size = 0;;)
136     {
137         errno = 0;
138         const char *entry = vlc_readdir (dir);
139         if (entry == NULL)
140         {
141             if (errno)
142                 goto error;
143             break;
144         }
145
146         if (!select (entry))
147             continue;
148
149         if (num >= size)
150         {
151             size = size ? (2 * size) : 16;
152             char **newtab = realloc (tab, sizeof (*tab) * (size));
153
154             if (unlikely(newtab == NULL))
155                 goto error;
156             tab = newtab;
157         }
158
159         tab[num] = strdup(entry);
160         if (likely(tab[num] != NULL))
161             num++;
162     }
163
164     if (compar != NULL && num > 0)
165         vlc_qsort(tab, num, sizeof (*tab), compar_void, compar);
166     *namelist = tab;
167     return num;
168
169 error:
170     for (unsigned i = 0; i < num; i++)
171         free (tab[i]);
172     free (tab);
173     return -1;
174 }
175
176 /**
177  * Selects file entries from a directory, as GNU C scandir().
178  *
179  * @param dirname UTF-8 diretory path
180  * @param pointer [OUT] pointer set, on successful completion, to the address
181  * of a table of UTF-8 filenames. All filenames must be freed with free().
182  * The table itself must be freed with free() as well.
183  *
184  * @return How many file names were selected (possibly 0),
185  * or -1 in case of error.
186  */
187 int vlc_scandir( const char *dirname, char ***namelist,
188                   int (*select)( const char * ),
189                   int (*compar)( const char **, const char ** ) )
190 {
191     DIR *dir = vlc_opendir (dirname);
192     int val = -1;
193
194     if (dir != NULL)
195     {
196         val = vlc_loaddir (dir, namelist, select, compar);
197         closedir (dir);
198     }
199     return val;
200 }
201
202 # include <vlc_rand.h>
203
204 VLC_WEAK int vlc_mkstemp(char *template)
205 {
206     static const char bytes[] =
207         "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqstruvwxyz_-";
208     static const size_t nbytes = ARRAY_SIZE(bytes) - 1;
209     char *pattern;
210
211     static_assert(((ARRAY_SIZE(bytes) - 1) & (ARRAY_SIZE(bytes) - 2)) == 0,
212                   "statistical bias");
213
214     /* Check template validity */
215     assert(template != NULL);
216
217     const size_t len = strlen(template);
218     if (len < 6
219      || strcmp(pattern = template + len - 6, "XXXXXX")) {
220         errno = EINVAL;
221         return -1;
222     }
223
224     /* */
225     for( int i = 0; i < 256; i++ )
226     {
227         /* Create a pseudo random file name */
228         uint8_t pi_rand[6];
229
230         vlc_rand_bytes( pi_rand, sizeof(pi_rand) );
231         for( int j = 0; j < 6; j++ )
232             pattern[j] = bytes[pi_rand[j] % nbytes];
233
234         /* */
235         int fd = vlc_open( template, O_CREAT | O_EXCL | O_RDWR, 0600 );
236         if( fd >= 0 )
237             return fd;
238         if( errno != EEXIST )
239             return -1;
240     }
241
242     errno = EEXIST;
243     return -1;
244 }