Fix remote screen check in graphic equalizer, so that it can be used on logf-enabled...
[Rockbox.git] / firmware / common / dir.c
blob00e3ade37d4979862fe924fcb3201ddad324b6b6
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Björn Stenberg
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include <stdio.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <stdbool.h>
23 #include "fat.h"
24 #include "dir.h"
25 #include "debug.h"
26 #include "atoi.h"
27 #include "dircache.h"
29 #define MAX_OPEN_DIRS 8
31 static DIR opendirs[MAX_OPEN_DIRS];
33 #ifdef HAVE_MULTIVOLUME
35 /* how to name volumes, first char must be outside of legal file names,
36 a number gets appended to enumerate, if applicable */
37 #ifdef HAVE_MMC
38 static const char* vol_names = "<MMC%d>";
39 #define VOL_ENUM_POS 4 /* position of %d, to avoid runtime calculation */
40 #else
41 static const char* vol_names = "<HD%d>";
42 #define VOL_ENUM_POS 3
43 #endif
45 /* returns on which volume this is, and copies the reduced name
46 (sortof a preprocessor for volume-decorated pathnames) */
47 static int strip_volume(const char* name, char* namecopy)
49 int volume = 0;
50 const char *temp = name;
52 while (*temp == '/') /* skip all leading slashes */
53 ++temp;
55 if (*temp && !strncmp(temp, vol_names, VOL_ENUM_POS))
57 temp += VOL_ENUM_POS; /* behind special name */
58 volume = atoi(temp); /* number is following */
59 temp = strchr(temp, '/'); /* search for slash behind */
60 if (temp != NULL)
61 name = temp; /* use the part behind the volume */
62 else
63 name = "/"; /* else this must be the root dir */
66 strncpy(namecopy, name, MAX_PATH);
67 namecopy[MAX_PATH-1] = '\0';
69 return volume;
71 #endif /* #ifdef HAVE_MULTIVOLUME */
74 #ifdef HAVE_HOTSWAP
75 // release all dir handles on a given volume "by force", to avoid leaks
76 int release_dirs(int volume)
78 DIR* pdir = opendirs;
79 int dd;
80 int closed = 0;
81 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
83 if (pdir->fatdir.file.volume == volume)
85 pdir->busy = false; /* mark as available, no further action */
86 closed++;
89 return closed; /* return how many we did */
91 #endif /* #ifdef HAVE_HOTSWAP */
93 DIR* opendir(const char* name)
95 char namecopy[MAX_PATH];
96 char* part;
97 char* end;
98 struct fat_direntry entry;
99 int dd;
100 DIR* pdir = opendirs;
101 #ifdef HAVE_MULTIVOLUME
102 int volume;
103 #endif
105 if ( name[0] != '/' ) {
106 DEBUGF("Only absolute paths supported right now\n");
107 return NULL;
110 /* find a free dir descriptor */
111 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
112 if ( !pdir->busy )
113 break;
115 if ( dd == MAX_OPEN_DIRS ) {
116 DEBUGF("Too many dirs open\n");
117 errno = EMFILE;
118 return NULL;
121 pdir->busy = true;
123 #ifdef HAVE_MULTIVOLUME
124 /* try to extract a heading volume name, if present */
125 volume = strip_volume(name, namecopy);
126 pdir->volumecounter = 0;
127 #else
128 strncpy(namecopy,name,sizeof(namecopy)); /* just copy */
129 namecopy[sizeof(namecopy)-1] = '\0';
130 #endif
132 if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
133 DEBUGF("Failed opening root dir\n");
134 pdir->busy = false;
135 return NULL;
138 for ( part = strtok_r(namecopy, "/", &end); part;
139 part = strtok_r(NULL, "/", &end)) {
140 /* scan dir for name */
141 while (1) {
142 if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
143 (!entry.name[0])) {
144 pdir->busy = false;
145 return NULL;
147 if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
148 (!strcasecmp(part, entry.name)) ) {
149 pdir->parent_dir = pdir->fatdir;
150 if ( fat_opendir(IF_MV2(volume,)
151 &pdir->fatdir,
152 entry.firstcluster,
153 &pdir->parent_dir) < 0 ) {
154 DEBUGF("Failed opening dir '%s' (%ld)\n",
155 part, entry.firstcluster);
156 pdir->busy = false;
157 return NULL;
159 #ifdef HAVE_MULTIVOLUME
160 pdir->volumecounter = -1; /* n.a. to subdirs */
161 #endif
162 break;
167 return pdir;
170 int closedir(DIR* dir)
172 dir->busy=false;
173 return 0;
176 struct dirent* readdir(DIR* dir)
178 struct fat_direntry entry;
179 struct dirent* theent = &(dir->theent);
181 if (!dir->busy)
182 return NULL;
184 #ifdef HAVE_MULTIVOLUME
185 /* Volumes (secondary file systems) get inserted into the root directory
186 of the first volume, since we have no separate top level. */
187 if (dir->volumecounter >= 0 /* on a root dir */
188 && dir->volumecounter < NUM_VOLUMES /* in range */
189 && dir->fatdir.file.volume == 0) /* at volume 0 */
190 { /* fake special directories, which don't really exist, but
191 will get redirected upon opendir() */
192 while (++dir->volumecounter < NUM_VOLUMES)
194 if (fat_ismounted(dir->volumecounter))
196 memset(theent, 0, sizeof(*theent));
197 theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
198 snprintf(theent->d_name, sizeof(theent->d_name),
199 vol_names, dir->volumecounter);
200 return theent;
204 #endif
205 /* normal directory entry fetching follows here */
206 if (fat_getnext(&(dir->fatdir),&entry) < 0)
207 return NULL;
209 if ( !entry.name[0] )
210 return NULL;
212 strncpy(theent->d_name, entry.name, sizeof( theent->d_name ) );
213 theent->attribute = entry.attr;
214 theent->size = entry.filesize;
215 theent->startcluster = entry.firstcluster;
216 theent->wrtdate = entry.wrtdate;
217 theent->wrttime = entry.wrttime;
219 return theent;
222 int mkdir(const char *name, int mode)
224 DIR *dir;
225 char namecopy[MAX_PATH];
226 char* end;
227 char *basename;
228 char *parent;
229 struct dirent *entry;
230 struct fat_dir newdir;
231 int rc;
233 (void)mode;
235 if ( name[0] != '/' ) {
236 DEBUGF("mkdir: Only absolute paths supported right now\n");
237 return -1;
240 strncpy(namecopy,name,sizeof(namecopy));
241 namecopy[sizeof(namecopy)-1] = 0;
243 /* Split the base name and the path */
244 end = strrchr(namecopy, '/');
245 *end = 0;
246 basename = end+1;
248 if(namecopy == end) /* Root dir? */
249 parent = "/";
250 else
251 parent = namecopy;
253 DEBUGF("mkdir: parent: %s, name: %s\n", parent, basename);
255 dir = opendir(parent);
257 if(!dir) {
258 DEBUGF("mkdir: can't open parent dir\n");
259 return -2;
262 if(basename[0] == 0) {
263 DEBUGF("mkdir: Empty dir name\n");
264 errno = EINVAL;
265 return -3;
268 /* Now check if the name already exists */
269 while ((entry = readdir(dir))) {
270 if ( !strcasecmp(basename, entry->d_name) ) {
271 DEBUGF("mkdir error: file exists\n");
272 errno = EEXIST;
273 closedir(dir);
274 return - 4;
278 memset(&newdir, sizeof(struct fat_dir), 0);
280 rc = fat_create_dir(basename, &newdir, &(dir->fatdir));
281 #ifdef HAVE_DIRCACHE
282 if (rc >= 0)
283 dircache_mkdir(name);
284 #endif
286 closedir(dir);
288 return rc;
291 int rmdir(const char* name)
293 int rc;
294 DIR* dir;
295 struct dirent* entry;
297 dir = opendir(name);
298 if (!dir)
300 errno = ENOENT; /* open error */
301 return -1;
304 /* check if the directory is empty */
305 while ((entry = readdir(dir)))
307 if (strcmp(entry->d_name, ".") &&
308 strcmp(entry->d_name, ".."))
310 DEBUGF("rmdir error: not empty\n");
311 errno = ENOTEMPTY;
312 closedir(dir);
313 return -2;
317 rc = fat_remove(&(dir->fatdir.file));
318 if ( rc < 0 ) {
319 DEBUGF("Failed removing dir: %d\n", rc);
320 errno = EIO;
321 rc = rc * 10 - 3;
323 #ifdef HAVE_DIRCACHE
324 else
326 dircache_rmdir(name);
328 #endif
330 closedir(dir);
332 return rc;