Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / servers / exchange / lib / e2k-path.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* e-path.c
3  *
4  * Copyright (C) 2001 Ximian, Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include <config.h>
22
23 #include <sys/types.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27
28 #include <glib.h>
29 #include <glib/gstdio.h>
30
31 #include "e2k-path.h"
32
33 #define SUBFOLDER_DIR_NAME     "subfolders"
34 #define SUBFOLDER_DIR_NAME_LEN 10
35
36 /**
37  * e_path_to_physical:
38  * @prefix: a prefix to prepend to the path, or %NULL
39  * @path: the virtual path to convert to a filesystem path.
40  *
41  * This converts the "virtual" path @path into an expanded form that
42  * allows a given name to refer to both a file and a directory. The
43  * expanded path will have a "subfolders" directory inserted between
44  * each path component. If the path ends with "/", the returned
45  * physical path will end with "/subfolders"
46  *
47  * If @prefix is non-%NULL, it will be prepended to the returned path.
48  *
49  * Return value: the expanded path
50  **/
51 char *
52 e_path_to_physical (const char *prefix, const char *vpath)
53 {
54         const char *p, *newp;
55         char *dp;
56         char *ppath;
57         int ppath_len;
58         int prefix_len;
59
60         while (*vpath == '/')
61                 vpath++;
62         if (!prefix)
63                 prefix = "";
64
65         /* Calculate the length of the real path. */
66         ppath_len = strlen (vpath);
67         ppath_len++;    /* For the ending zero.  */
68
69         prefix_len = strlen (prefix);
70         ppath_len += prefix_len;
71         ppath_len++;    /* For the separating slash.  */
72
73         /* Take account of the fact that we need to translate every
74          * separator into `subfolders/'.
75          */
76         p = vpath;
77         while (1) {
78                 newp = strchr (p, '/');
79                 if (newp == NULL)
80                         break;
81
82                 ppath_len += SUBFOLDER_DIR_NAME_LEN;
83                 ppath_len++; /* For the separating slash.  */
84
85                 /* Skip consecutive slashes.  */
86                 while (*newp == '/')
87                         newp++;
88
89                 p = newp;
90         };
91
92         ppath = g_malloc (ppath_len);
93         dp = ppath;
94
95         memcpy (dp, prefix, prefix_len);
96         dp += prefix_len;
97         *(dp++) = '/';
98
99         /* Copy the mangled path.  */
100         p = vpath;
101         while (1) {
102                 newp = strchr (p, '/');
103                 if (newp == NULL) {
104                         strcpy (dp, p);
105                         break;
106                 }
107
108                 memcpy (dp, p, newp - p + 1); /* `+ 1' to copy the slash too.  */
109                 dp += newp - p + 1;
110
111                 memcpy (dp, SUBFOLDER_DIR_NAME, SUBFOLDER_DIR_NAME_LEN);
112                 dp += SUBFOLDER_DIR_NAME_LEN;
113
114                 *(dp++) = '/';
115
116                 /* Skip consecutive slashes.  */
117                 while (*newp == '/')
118                         newp++;
119
120                 p = newp;
121         }
122
123         return ppath;
124 }
125
126
127 static gboolean
128 find_folders_recursive (const char *physical_path, const char *path,
129                         EPathFindFoldersCallback callback, gpointer data)
130 {
131         GDir *dir;
132         char *subfolder_directory_path;
133         gboolean ok;
134
135         if (*path) {
136                 if (!callback (physical_path, path, data))
137                         return FALSE;
138
139                 subfolder_directory_path = g_strdup_printf ("%s/%s", physical_path, SUBFOLDER_DIR_NAME);
140         } else {
141                 /* On the top level, we have no folders and,
142                  * consequently, no subfolder directory.
143                  */
144
145                 subfolder_directory_path = g_strdup (physical_path);
146         }
147
148         /* Now scan the subfolders and load them. */
149         dir = g_dir_open (subfolder_directory_path, 0, NULL);
150         if (dir == NULL) {
151                 g_free (subfolder_directory_path);
152                 return TRUE;
153         }
154
155         ok = TRUE;
156         while (ok) {
157                 struct stat file_stat;
158                 const char *dirent;
159                 char *file_path;
160                 char *new_path;
161
162                 dirent = g_dir_read_name (dir);
163                 if (dirent == NULL)
164                         break;
165
166                 file_path = g_strdup_printf ("%s/%s", subfolder_directory_path,
167                                              dirent);
168
169                 if (g_stat (file_path, &file_stat) < 0 ||
170                     ! S_ISDIR (file_stat.st_mode)) {
171                         g_free (file_path);
172                         continue;
173                 }
174
175                 new_path = g_strdup_printf ("%s/%s", path, dirent);
176
177                 ok = find_folders_recursive (file_path, new_path, callback, data);
178
179                 g_free (file_path);
180                 g_free (new_path);
181         }
182
183         g_dir_close (dir);
184         g_free (subfolder_directory_path);
185
186         return ok;
187 }
188
189 /**
190  * e_path_find_folders:
191  * @prefix: directory to start from
192  * @callback: Callback to invoke on each folder
193  * @data: Data for @callback
194  *
195  * Walks the folder tree starting at @prefix and calls @callback
196  * on each folder.
197  *
198  * Return value: %TRUE on success, %FALSE if an error occurs at any point
199  **/
200 gboolean
201 e_path_find_folders (const char *prefix,
202                      EPathFindFoldersCallback callback,
203                      gpointer data)
204 {
205         return find_folders_recursive (prefix, "", callback, data);
206 }
207
208
209 /**
210  * e_path_rmdir:
211  * @prefix: a prefix to prepend to the path, or %NULL
212  * @path: the virtual path to convert to a filesystem path.
213  *
214  * This removes the directory pointed to by @prefix and @path
215  * and attempts to remove its parent "subfolders" directory too
216  * if it's empty.
217  *
218  * Return value: -1 (with errno set) if it failed to rmdir the
219  * specified directory. 0 otherwise, whether or not it removed
220  * the parent directory.
221  **/
222 int
223 e_path_rmdir (const char *prefix, const char *vpath)
224 {
225         char *physical_path, *p;
226
227         /* Remove the directory itself */
228         physical_path = e_path_to_physical (prefix, vpath);
229         if (g_rmdir (physical_path) == -1) {
230                 g_free (physical_path);
231                 return -1;
232         }
233
234         /* Attempt to remove its parent "subfolders" directory,
235          * ignoring errors since it might not be empty.
236          */
237
238         p = strrchr (physical_path, '/');
239         if (p[1] == '\0') {
240                 g_free (physical_path);
241                 return 0;
242         }
243         *p = '\0';
244         p = strrchr (physical_path, '/');
245         if (!p || strcmp (p + 1, SUBFOLDER_DIR_NAME) != 0) {
246                 g_free (physical_path);
247                 return 0;
248         }
249
250         g_rmdir (physical_path);
251         g_free (physical_path);
252         return 0;
253 }