/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* e-path.c * * Copyright (C) 2001 Ximian, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include "e-path.h" #define SUBFOLDER_DIR_NAME "subfolders" #define SUBFOLDER_DIR_NAME_LEN 10 /** * e_path_to_physical: * @prefix: a prefix to prepend to the path, or %NULL * @path: the virtual path to convert to a filesystem path. * * This converts the "virtual" path @path into an expanded form that * allows a given name to refer to both a file and a directory. The * expanded path will have a "subfolders" directory inserted between * each path component. If the path ends with "/", the returned * physical path will end with "/subfolders" * * If @prefix is non-%NULL, it will be prepended to the returned path. * * Return value: the expanded path **/ char * e_path_to_physical (const char *prefix, const char *vpath) { const char *p, *newp; char *dp; char *ppath; int ppath_len; int prefix_len; while (*vpath == '/') vpath++; if (!prefix) prefix = ""; /* Calculate the length of the real path. */ ppath_len = strlen (vpath); ppath_len++; /* For the ending zero. */ prefix_len = strlen (prefix); ppath_len += prefix_len; ppath_len++; /* For the separating slash. */ /* Take account of the fact that we need to translate every * separator into `subfolders/'. */ p = vpath; while (1) { newp = strchr (p, '/'); if (newp == NULL) break; ppath_len += SUBFOLDER_DIR_NAME_LEN; ppath_len++; /* For the separating slash. */ /* Skip consecutive slashes. */ while (*newp == '/') newp++; p = newp; }; ppath = g_malloc (ppath_len); dp = ppath; memcpy (dp, prefix, prefix_len); dp += prefix_len; *(dp++) = '/'; /* Copy the mangled path. */ p = vpath; while (1) { newp = strchr (p, '/'); if (newp == NULL) { strcpy (dp, p); break; } memcpy (dp, p, newp - p + 1); /* `+ 1' to copy the slash too. */ dp += newp - p + 1; memcpy (dp, SUBFOLDER_DIR_NAME, SUBFOLDER_DIR_NAME_LEN); dp += SUBFOLDER_DIR_NAME_LEN; *(dp++) = '/'; /* Skip consecutive slashes. */ while (*newp == '/') newp++; p = newp; } return ppath; } static gboolean find_folders_recursive (const char *physical_path, const char *path, EPathFindFoldersCallback callback, gpointer data) { DIR *dir; char *subfolder_directory_path; gboolean ok; if (*path) { if (!callback (physical_path, path, data)) return FALSE; subfolder_directory_path = g_strdup_printf ("%s/%s", physical_path, SUBFOLDER_DIR_NAME); } else { /* On the top level, we have no folders and, * consequently, no subfolder directory. */ subfolder_directory_path = g_strdup (physical_path); } /* Now scan the subfolders and load them. */ dir = opendir (subfolder_directory_path); if (dir == NULL) { g_free (subfolder_directory_path); return TRUE; } ok = TRUE; while (ok) { struct stat file_stat; struct dirent *dirent; char *file_path; char *new_path; dirent = readdir (dir); if (dirent == NULL) break; if (strcmp (dirent->d_name, ".") == 0 || strcmp (dirent->d_name, "..") == 0) continue; file_path = g_strdup_printf ("%s/%s", subfolder_directory_path, dirent->d_name); if (stat (file_path, &file_stat) < 0 || ! S_ISDIR (file_stat.st_mode)) { g_free (file_path); continue; } new_path = g_strdup_printf ("%s/%s", path, dirent->d_name); ok = find_folders_recursive (file_path, new_path, callback, data); g_free (file_path); g_free (new_path); } closedir (dir); g_free (subfolder_directory_path); return ok; } /** * e_path_find_folders: * @prefix: directory to start from * @callback: Callback to invoke on each folder * @data: Data for @callback * * Walks the folder tree starting at @prefix and calls @callback * on each folder. * * Return value: %TRUE on success, %FALSE if an error occurs at any point **/ gboolean e_path_find_folders (const char *prefix, EPathFindFoldersCallback callback, gpointer data) { return find_folders_recursive (prefix, "", callback, data); } /** * e_path_rmdir: * @prefix: a prefix to prepend to the path, or %NULL * @path: the virtual path to convert to a filesystem path. * * This removes the directory pointed to by @prefix and @path * and attempts to remove its parent "subfolders" directory too * if it's empty. * * Return value: -1 (with errno set) if it failed to rmdir the * specified directory. 0 otherwise, whether or not it removed * the parent directory. **/ int e_path_rmdir (const char *prefix, const char *vpath) { char *physical_path, *p; /* Remove the directory itself */ physical_path = e_path_to_physical (prefix, vpath); if (rmdir (physical_path) == -1) { g_free (physical_path); return -1; } /* Attempt to remove its parent "subfolders" directory, * ignoring errors since it might not be empty. */ p = strrchr (physical_path, '/'); if (p[1] == '\0') { g_free (physical_path); return 0; } *p = '\0'; p = strrchr (physical_path, '/'); if (!p || strcmp (p + 1, SUBFOLDER_DIR_NAME) != 0) { g_free (physical_path); return 0; } rmdir (physical_path); g_free (physical_path); return 0; }