dccp_recv.c, stdout.h: change year in GPL header to 2007
[paraslash.git] / afs.c
1 /*
2  * Copyright (C) 2005-2007 Andre Noll <maan@systemlinux.org>
3  *
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  *
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  *
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
17  */
18
19
20 /** \file afs.c functions common to all audio file selectors */
21
22 #include "server.cmdline.h"
23 #include "server.h"
24 #include "vss.h"
25 #include <dirent.h> /* readdir() */
26 #include <sys/stat.h> /* stat */
27 #include <sys/types.h> /* mode_t */
28 #include "error.h"
29 #include "string.h"
30
31 /**
32  * traverse the given directory recursively
33  *
34  * @param dirname the directory to traverse
35  * @param f: the function to call for each entry.
36  *
37  * for each regular file whose filename ends in .yyy, where yyy is a supported
38  * audio format, the supplied function \a f is called.  The directory and
39  * filename component of the regular file are passed to \a f.
40  *
41  * \return On success, 1 is returned. Otherwise, this function returns a
42  * negative value which indicates the kind of the error.
43  */
44 int find_audio_files(const char *dirname, int (*f)(const char *, const char *))
45 {
46         DIR *dir = NULL;
47         struct dirent *entry;
48         /*
49          * Opening the current directory (".") and calling fchdir() to return
50          * is usually faster and more reliable than saving cwd in some buffer
51          * and calling chdir() afterwards (see man 3 getcwd).
52          */
53         int cwd_fd = open(".", O_RDONLY);
54         struct stat s;
55         int ret = -1;
56
57 //      PARA_DEBUG_LOG("dirname: %s\n", dirname);
58         if (cwd_fd < 0)
59                 return -E_GETCWD;
60         ret = -E_CHDIR;
61         if (chdir(dirname) < 0)
62                 goto out;
63         ret = -E_OPENDIR;
64         dir = opendir(".");
65         if (!dir)
66                 goto out;
67         /* scan cwd recursively */
68         while ((entry = readdir(dir))) {
69                 mode_t m;
70                 char *tmp;
71
72                 if (!strcmp(entry->d_name, "."))
73                         continue;
74                 if (!strcmp(entry->d_name, ".."))
75                         continue;
76                 ret = -E_LSTAT;
77                 if (lstat(entry->d_name, &s) == -1)
78                         continue;
79                 m = s.st_mode;
80                 if (!S_ISREG(m) && !S_ISDIR(m)) /* skip links, sockets, ... */
81                         continue;
82                 if (S_ISREG(m)) { /* regular file */
83                         if (guess_audio_format(entry->d_name) < 0)
84                                 continue;
85                         ret = f(dirname, entry->d_name);
86                         if (ret < 0)
87                                 goto out;
88                         continue;
89                 }
90                 /* directory */
91                 tmp = make_message("%s/%s", dirname, entry->d_name);
92                 ret = find_audio_files(tmp, f);
93                 free(tmp);
94                 if (ret < 0)
95                         goto out;
96         }
97         ret = 1;
98 out:
99         if (dir)
100                 closedir(dir);
101         if (fchdir(cwd_fd) < 0)
102                 ret = -E_CHDIR;
103         close(cwd_fd);
104         if (ret < 0)
105                 PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
106         return ret;
107 }