Bug 399500 - "support XDG user dirs in the directory service" [p=chpe@gnome.org (Christian Persch) r=caillon sr=bsmedberg a1.9=damons]
authorreed@reedloden.com
Wed, 12 Mar 2008 15:35:56 -0700
changeset 12974 9e9f0cabbc376442bc2d27eab24c208ef19f7f25
parent 12973 780c2a3a5c6cbe4f68fd334e6ae74014d85266c1
child 12975 2c7e003aad42b033d96736ea984fa1cf8880bb66
push idunknown
push userunknown
push dateunknown
reviewerscaillon, bsmedberg
bugs399500
milestone1.9b5pre
Bug 399500 - "support XDG user dirs in the directory service" [p=chpe@gnome.org (Christian Persch) r=caillon sr=bsmedberg a1.9=damons]
xpcom/io/SpecialSystemDirectory.cpp
xpcom/io/SpecialSystemDirectory.h
xpcom/io/nsDirectoryService.cpp
xpcom/io/nsDirectoryService.h
xpcom/io/nsDirectoryServiceDefs.h
--- a/xpcom/io/SpecialSystemDirectory.cpp
+++ b/xpcom/io/SpecialSystemDirectory.cpp
@@ -233,16 +233,232 @@ GetUnixHomeDir(nsILocalFile** aFile)
                                      PR_TRUE, 
                                      aFile);
     }
 #else
     return NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")), 
                                  PR_TRUE, aFile);
 #endif
 }
+
+/*
+  The following licence applies to the xdg_user_dir_lookup function:
+ 
+  Copyright (c) 2007 Red Hat, inc
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions: 
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software. 
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
+
+static char *
+xdg_user_dir_lookup (const char *type)
+{
+  FILE *file;
+  char *home_dir, *config_home, *config_file;
+  char buffer[512];
+  char *user_dir;
+  char *p, *d;
+  int len;
+  int relative;
+  
+  home_dir = getenv ("HOME");
+
+  if (home_dir == NULL)
+    goto error;
+
+  config_home = getenv ("XDG_CONFIG_HOME");
+  if (config_home == NULL || config_home[0] == 0)
+    {
+      config_file = (char*) malloc (strlen (home_dir) + strlen ("/.config/user-dirs.dirs") + 1);
+      if (config_file == NULL)
+        goto error;
+
+      strcpy (config_file, home_dir);
+      strcat (config_file, "/.config/user-dirs.dirs");
+    }
+  else
+    {
+      config_file = (char*) malloc (strlen (config_home) + strlen ("/user-dirs.dirs") + 1);
+      if (config_file == NULL)
+        goto error;
+
+      strcpy (config_file, config_home);
+      strcat (config_file, "/user-dirs.dirs");
+    }
+
+  file = fopen (config_file, "r");
+  free (config_file);
+  if (file == NULL)
+    goto error;
+
+  user_dir = NULL;
+  while (fgets (buffer, sizeof (buffer), file))
+    {
+      /* Remove newline at end */
+      len = strlen (buffer);
+      if (len > 0 && buffer[len-1] == '\n')
+	buffer[len-1] = 0;
+      
+      p = buffer;
+      while (*p == ' ' || *p == '\t')
+	p++;
+      
+      if (strncmp (p, "XDG_", 4) != 0)
+	continue;
+      p += 4;
+      if (strncmp (p, type, strlen (type)) != 0)
+	continue;
+      p += strlen (type);
+      if (strncmp (p, "_DIR", 4) != 0)
+	continue;
+      p += 4;
+
+      while (*p == ' ' || *p == '\t')
+	p++;
+
+      if (*p != '=')
+	continue;
+      p++;
+      
+      while (*p == ' ' || *p == '\t')
+	p++;
+
+      if (*p != '"')
+	continue;
+      p++;
+      
+      relative = 0;
+      if (strncmp (p, "$HOME/", 6) == 0)
+	{
+	  p += 6;
+	  relative = 1;
+	}
+      else if (*p != '/')
+	continue;
+      
+      if (relative)
+	{
+	  user_dir = (char*) malloc (strlen (home_dir) + 1 + strlen (p) + 1);
+          if (user_dir == NULL)
+            goto error2;
+
+	  strcpy (user_dir, home_dir);
+	  strcat (user_dir, "/");
+	}
+      else
+	{
+	  user_dir = (char*) malloc (strlen (p) + 1);
+          if (user_dir == NULL)
+            goto error2;
+
+	  *user_dir = 0;
+	}
+      
+      d = user_dir + strlen (user_dir);
+      while (*p && *p != '"')
+	{
+	  if ((*p == '\\') && (*(p+1) != 0))
+	    p++;
+	  *d++ = *p++;
+	}
+      *d = 0;
+    }
+error2:
+  fclose (file);
+
+  if (user_dir)
+    return user_dir;
+
+ error:
+  return NULL;
+}
+
+static const char xdg_user_dirs[] =
+    "DESKTOP\0"
+    "DOCUMENTS\0"
+    "DOWNLOAD\0"
+    "MUSIC\0"
+    "PICTURES\0"
+    "PUBLICSHARE\0"
+    "TEMPLATES\0"
+    "VIDEOS";
+
+static const PRUint8 xdg_user_dir_offsets[] = {
+    0,
+    8,
+    18,
+    27,
+    33,
+    42,
+    54,
+    64
+};
+
+static nsresult
+GetUnixXDGUserDirectory(SystemDirectories aSystemDirectory,
+                        nsILocalFile** aFile)
+{
+    char *dir = xdg_user_dir_lookup
+                    (xdg_user_dirs + xdg_user_dir_offsets[aSystemDirectory -
+                                                          Unix_XDG_Desktop]);
+
+    nsresult rv;
+    nsCOMPtr<nsILocalFile> file;
+    if (dir) {
+        rv = NS_NewNativeLocalFile(nsDependentCString(dir), PR_TRUE,
+                                   getter_AddRefs(file));
+        free(dir);
+    } else if (Unix_XDG_Desktop == aSystemDirectory) {
+        // for the XDG desktop dir, fall back to HOME/Desktop
+        // (for historical compatibility)
+        rv = GetUnixHomeDir(getter_AddRefs(file));
+        if (NS_FAILED(rv))
+            return rv;
+
+        rv = file->AppendNative(NS_LITERAL_CSTRING("Desktop"));
+    } else {
+      // no fallback for the other XDG dirs
+      rv = NS_ERROR_FAILURE;
+    }
+
+    if (NS_FAILED(rv))
+        return rv;
+
+    PRBool exists;
+    rv = file->Exists(&exists);
+    if (NS_FAILED(rv))
+        return rv;
+    if (!exists) {
+        rv = file->Create(nsIFile::DIRECTORY_TYPE, 0755);
+        if (NS_FAILED(rv))
+            return rv;
+    }
+
+    *aFile = nsnull;
+    file.swap(*aFile);
+
+    return NS_OK;
+}
 #endif
 
 nsresult
 GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
                           nsILocalFile** aFile)
 {
 #if defined(XP_WIN)
     WCHAR path[MAX_PATH];
@@ -570,35 +786,25 @@ GetSpecialSystemDirectory(SystemDirector
         case Unix_LibDirectory:
             return NS_NewNativeLocalFile(nsDependentCString("/usr/local/lib/netscape/"), 
                                          PR_TRUE, 
                                          aFile);
 
         case Unix_HomeDirectory:
             return GetUnixHomeDir(aFile);
 
-        case Unix_DesktopDirectory:
-        {
-            nsCOMPtr<nsILocalFile> home;
-            nsresult rv = GetUnixHomeDir(getter_AddRefs(home));
-            if (NS_FAILED(rv))
-                return rv;
-            rv = home->AppendNative(NS_LITERAL_CSTRING("Desktop"));
-            if (NS_FAILED(rv))
-                return rv;
-            PRBool exists;
-            rv = home->Exists(&exists);
-            if (NS_FAILED(rv))
-                return rv;
-            if (!exists)
-                return GetUnixHomeDir(aFile);
-              
-            NS_ADDREF(*aFile = home);
-            return NS_OK;
-        }
+        case Unix_XDG_Desktop:
+        case Unix_XDG_Documents:
+        case Unix_XDG_Download:
+        case Unix_XDG_Music:
+        case Unix_XDG_Pictures:
+        case Unix_XDG_PublicShare:
+        case Unix_XDG_Templates:
+        case Unix_XDG_Videos:
+            return GetUnixXDGUserDirectory(aSystemSystemDirectory, aFile);
 #endif
 
 #ifdef XP_BEOS
         case BeOS_SettingsDirectory:
         {
             return GetBeOSFolder(B_USER_SETTINGS_DIRECTORY,0, aFile);
         }
 
--- a/xpcom/io/SpecialSystemDirectory.h
+++ b/xpcom/io/SpecialSystemDirectory.h
@@ -109,18 +109,25 @@ enum SystemDirectories {
   Win_Cookies               =   227, 
   Win_LocalAppdata          =   228,
   Win_ProgramFiles          =   229,
   Win_Downloads             =   230,
   
   Unix_LocalDirectory       =   301,   
   Unix_LibDirectory         =   302,   
   Unix_HomeDirectory        =   303,
-  Unix_DesktopDirectory     =   304,
-  
+  Unix_XDG_Desktop          =   304,
+  Unix_XDG_Documents        =   305,
+  Unix_XDG_Download         =   306,
+  Unix_XDG_Music            =   307,
+  Unix_XDG_Pictures         =   308,
+  Unix_XDG_PublicShare      =   309,
+  Unix_XDG_Templates        =   310,
+  Unix_XDG_Videos           =   311,
+
   BeOS_SettingsDirectory    =   401,   
   BeOS_HomeDirectory        =   402,   
   BeOS_DesktopDirectory     =   403,   
   BeOS_SystemDirectory      =   404,
   
   OS2_SystemDirectory       =   501,   
   OS2_OS2Directory          =   502,   
   OS2_DesktopDirectory      =   503,   
--- a/xpcom/io/nsDirectoryService.cpp
+++ b/xpcom/io/nsDirectoryService.cpp
@@ -353,16 +353,25 @@ nsIAtom*  nsDirectoryService::sCommon_De
 nsIAtom*  nsDirectoryService::sAppdata = nsnull;
 nsIAtom*  nsDirectoryService::sLocalAppdata = nsnull;
 nsIAtom*  nsDirectoryService::sPrinthood = nsnull;
 nsIAtom*  nsDirectoryService::sWinCookiesDirectory = nsnull;
 nsIAtom*  nsDirectoryService::sDefaultDownloadDirectory = nsnull;
 #elif defined (XP_UNIX)
 nsIAtom*  nsDirectoryService::sLocalDirectory = nsnull;
 nsIAtom*  nsDirectoryService::sLibDirectory = nsnull;
+nsIAtom*  nsDirectoryService::sDefaultDownloadDirectory = nsnull;
+nsIAtom*  nsDirectoryService::sXDGDesktop = nsnull;
+nsIAtom*  nsDirectoryService::sXDGDocuments = nsnull;
+nsIAtom*  nsDirectoryService::sXDGDownload = nsnull;
+nsIAtom*  nsDirectoryService::sXDGMusic = nsnull;
+nsIAtom*  nsDirectoryService::sXDGPictures = nsnull;
+nsIAtom*  nsDirectoryService::sXDGPublicShare = nsnull;
+nsIAtom*  nsDirectoryService::sXDGTemplates = nsnull;
+nsIAtom*  nsDirectoryService::sXDGVideos = nsnull;
 #elif defined (XP_OS2)
 nsIAtom*  nsDirectoryService::sSystemDirectory = nsnull;
 nsIAtom*  nsDirectoryService::sOS2Directory = nsnull;
 #elif defined (XP_BEOS)
 nsIAtom*  nsDirectoryService::sSettingsDirectory = nsnull;
 nsIAtom*  nsDirectoryService::sSystemDirectory = nsnull;
 #endif
 
@@ -460,16 +469,25 @@ static const nsStaticAtom directory_atom
     { NS_WIN_APPDATA_DIR,          &nsDirectoryService::sAppdata },
     { NS_WIN_LOCAL_APPDATA_DIR,    &nsDirectoryService::sLocalAppdata },
     { NS_WIN_PRINTHOOD,            &nsDirectoryService::sPrinthood },
     { NS_WIN_COOKIES_DIR,          &nsDirectoryService::sWinCookiesDirectory },
     { NS_WIN_DEFAULT_DOWNLOAD_DIR, &nsDirectoryService::sDefaultDownloadDirectory },
 #elif defined (XP_UNIX)
     { NS_UNIX_LOCAL_DIR,           &nsDirectoryService::sLocalDirectory },
     { NS_UNIX_LIB_DIR,             &nsDirectoryService::sLibDirectory },
+    { NS_UNIX_DEFAULT_DOWNLOAD_DIR, &nsDirectoryService::sDefaultDownloadDirectory },
+    { NS_UNIX_XDG_DESKTOP_DIR,     &nsDirectoryService::sXDGDesktop },
+    { NS_UNIX_XDG_DOCUMENTS_DIR,   &nsDirectoryService::sXDGDocuments },
+    { NS_UNIX_XDG_DOWNLOAD_DIR,    &nsDirectoryService::sXDGDownload },
+    { NS_UNIX_XDG_MUSIC_DIR,       &nsDirectoryService::sXDGMusic },
+    { NS_UNIX_XDG_PICTURES_DIR,    &nsDirectoryService::sXDGPictures },
+    { NS_UNIX_XDG_PUBLIC_SHARE_DIR, &nsDirectoryService::sXDGPublicShare },
+    { NS_UNIX_XDG_TEMPLATES_DIR,   &nsDirectoryService::sXDGTemplates },
+    { NS_UNIX_XDG_VIDEOS_DIR,      &nsDirectoryService::sXDGVideos },
 #elif defined (XP_OS2)
     { NS_OS_SYSTEM_DIR,            &nsDirectoryService::sSystemDirectory },
     { NS_OS2_DIR,                  &nsDirectoryService::sOS2Directory },
 #elif defined (XP_BEOS)
     { NS_OS_SYSTEM_DIR,            &nsDirectoryService::sSystemDirectory },
     { NS_BEOS_SETTINGS_DIR,        &nsDirectoryService::sSettingsDirectory },
 #endif
 };    
@@ -1145,19 +1163,57 @@ nsDirectoryService::GetFile(const char *
     else if (inAtom == nsDirectoryService::sLibDirectory)
     {
         rv = GetSpecialSystemDirectory(Unix_LibDirectory, getter_AddRefs(localFile));
     }
     else if (inAtom == nsDirectoryService::sOS_HomeDirectory)
     {
         rv = GetSpecialSystemDirectory(Unix_HomeDirectory, getter_AddRefs(localFile)); 
     }
-    else if (inAtom == nsDirectoryService::sOS_DesktopDirectory)
+    else if (inAtom == nsDirectoryService::sXDGDesktop ||
+             inAtom == nsDirectoryService::sOS_DesktopDirectory)
+    {
+        rv = GetSpecialSystemDirectory(Unix_XDG_Desktop, getter_AddRefs(localFile));
+        *persistent = PR_FALSE;
+    }
+    else if (inAtom == nsDirectoryService::sXDGDocuments)
+    {
+        rv = GetSpecialSystemDirectory(Unix_XDG_Documents, getter_AddRefs(localFile));
+        *persistent = PR_FALSE;
+    }
+    else if (inAtom == nsDirectoryService::sXDGDownload ||
+             inAtom == nsDirectoryService::sDefaultDownloadDirectory)
+    {
+        rv = GetSpecialSystemDirectory(Unix_XDG_Download, getter_AddRefs(localFile));
+        *persistent = PR_FALSE;
+    }
+    else if (inAtom == nsDirectoryService::sXDGMusic)
     {
-        rv = GetSpecialSystemDirectory(Unix_DesktopDirectory, getter_AddRefs(localFile)); 
+        rv = GetSpecialSystemDirectory(Unix_XDG_Music, getter_AddRefs(localFile));
+        *persistent = PR_FALSE;
+    }
+    else if (inAtom == nsDirectoryService::sXDGPictures)
+    {
+        rv = GetSpecialSystemDirectory(Unix_XDG_Pictures, getter_AddRefs(localFile));
+        *persistent = PR_FALSE;
+    }
+    else if (inAtom == nsDirectoryService::sXDGPublicShare)
+    {
+        rv = GetSpecialSystemDirectory(Unix_XDG_PublicShare, getter_AddRefs(localFile));
+        *persistent = PR_FALSE;
+    }
+    else if (inAtom == nsDirectoryService::sXDGTemplates)
+    {
+        rv = GetSpecialSystemDirectory(Unix_XDG_Templates, getter_AddRefs(localFile));
+        *persistent = PR_FALSE;
+    }
+    else if (inAtom == nsDirectoryService::sXDGVideos)
+    {
+        rv = GetSpecialSystemDirectory(Unix_XDG_Videos, getter_AddRefs(localFile));
+        *persistent = PR_FALSE;
     }
 #elif defined (XP_OS2)
     else if (inAtom == nsDirectoryService::sSystemDirectory)
     {
         rv = GetSpecialSystemDirectory(OS2_SystemDirectory, getter_AddRefs(localFile)); 
     }
     else if (inAtom == nsDirectoryService::sOS2Directory)
     {
--- a/xpcom/io/nsDirectoryService.h
+++ b/xpcom/io/nsDirectoryService.h
@@ -155,16 +155,25 @@ public:
     static nsIAtom *sAppdata;
     static nsIAtom *sLocalAppdata;
     static nsIAtom *sPrinthood;
     static nsIAtom *sWinCookiesDirectory;
     static nsIAtom *sDefaultDownloadDirectory;
 #elif defined (XP_UNIX)
     static nsIAtom *sLocalDirectory;
     static nsIAtom *sLibDirectory;
+    static nsIAtom *sXDGDesktop;
+    static nsIAtom *sXDGDocuments;
+    static nsIAtom *sXDGDownload;
+    static nsIAtom *sXDGMusic;
+    static nsIAtom *sXDGPictures;
+    static nsIAtom *sXDGPublicShare;
+    static nsIAtom *sXDGTemplates;
+    static nsIAtom *sXDGVideos;
+    static nsIAtom *sDefaultDownloadDirectory;
 #elif defined (XP_OS2)
     static nsIAtom *sSystemDirectory;
     static nsIAtom *sOS2Directory;
 #elif defined (XP_BEOS)
     static nsIAtom *sSettingsDirectory;
     static nsIAtom *sSystemDirectory;
 #endif
 
--- a/xpcom/io/nsDirectoryServiceDefs.h
+++ b/xpcom/io/nsDirectoryServiceDefs.h
@@ -181,16 +181,24 @@
     #define NS_WIN_LOCAL_APPDATA_DIR            "LocalAppData"
     #define NS_WIN_PRINTHOOD                    "PrntHd"
     #define NS_WIN_COOKIES_DIR                  "CookD"
     #define NS_WIN_DEFAULT_DOWNLOAD_DIR         "DfltDwnld"
 #elif defined (XP_UNIX)
     #define NS_UNIX_LOCAL_DIR                   "Locl"
     #define NS_UNIX_LIB_DIR                     "LibD"
     #define NS_UNIX_HOME_DIR                    NS_OS_HOME_DIR
+    #define NS_UNIX_XDG_DESKTOP_DIR             "XDGDesk"
+    #define NS_UNIX_XDG_DOCUMENTS_DIR           "XDGDocs"
+    #define NS_UNIX_XDG_DOWNLOAD_DIR            "XDGDwnld"
+    #define NS_UNIX_XDG_MUSIC_DIR               "XDGMusic"
+    #define NS_UNIX_XDG_PICTURES_DIR            "XDGPict"
+    #define NS_UNIX_XDG_PUBLIC_SHARE_DIR        "XDGPubSh"
+    #define NS_UNIX_XDG_TEMPLATES_DIR           "XDGTempl"
+    #define NS_UNIX_XDG_VIDEOS_DIR              "XDGVids"
 #elif defined (XP_OS2)
     #define NS_OS2_DIR                          "OS2Dir"
     #define NS_OS2_HOME_DIR                     NS_OS_HOME_DIR
     #define NS_OS2_DESKTOP_DIR                  NS_OS_DESKTOP_DIR
 #elif defined (XP_BEOS)
     #define NS_BEOS_SETTINGS_DIR                "Setngs"
     #define NS_BEOS_HOME_DIR                    NS_OS_HOME_DIR
     #define NS_BEOS_DESKTOP_DIR                 NS_OS_DESKTOP_DIR