source: trunk/libtransmission/platform.c @ 14382

Last change on this file since 14382 was 14382, checked in by mikedld, 6 years ago

Fix compilation on Windows

This should not affect non-Win32 platforms in any way.
As for Win32 (both MinGW and MSVC), this should hopefully allow for
unpatched compilation. Correct functioning is not yet guaranteed though.

  • Property svn:keywords set to Date Rev Author Id
File size: 13.6 KB
Line 
1/*
2 * This file Copyright (C) 2009-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: platform.c 14382 2014-12-13 15:22:39Z mikedld $
8 */
9
10#define _XOPEN_SOURCE 600  /* needed for recursive locks. */
11#ifndef __USE_UNIX98
12 #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
13#endif
14
15#include <assert.h>
16#include <stdlib.h>
17#include <string.h>
18
19#ifdef __HAIKU__
20 #include <limits.h> /* PATH_MAX */
21#endif
22
23#ifdef _WIN32
24 #include <process.h> /* _beginthreadex (), _endthreadex () */
25 #include <windows.h>
26 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
27#else
28#include <unistd.h> /* getuid() */
29 #ifdef BUILD_MAC_CLIENT
30  #include <CoreFoundation/CoreFoundation.h>
31 #endif
32 #ifdef __HAIKU__
33  #include <FindDirectory.h>
34 #endif
35 #include <pthread.h>
36#endif
37
38#include "transmission.h"
39#include "file.h"
40#include "list.h"
41#include "log.h"
42#include "platform.h"
43#include "session.h"
44#include "utils.h"
45
46/***
47****  THREADS
48***/
49
50#ifdef _WIN32
51typedef DWORD tr_thread_id;
52#else
53typedef pthread_t tr_thread_id;
54#endif
55
56static tr_thread_id
57tr_getCurrentThread (void)
58{
59#ifdef _WIN32
60  return GetCurrentThreadId ();
61#else
62  return pthread_self ();
63#endif
64}
65
66static bool
67tr_areThreadsEqual (tr_thread_id a, tr_thread_id b)
68{
69#ifdef _WIN32
70  return a == b;
71#else
72  return pthread_equal (a, b) != 0;
73#endif
74}
75
76/** @brief portability wrapper around OS-dependent threads */
77struct tr_thread
78{
79  void          (* func)(void *);
80  void           * arg;
81  tr_thread_id     thread;
82#ifdef _WIN32
83  HANDLE           thread_handle;
84#endif
85};
86
87bool
88tr_amInThread (const tr_thread * t)
89{
90  return tr_areThreadsEqual (tr_getCurrentThread (), t->thread);
91}
92
93#ifdef _WIN32
94 #define ThreadFuncReturnType unsigned WINAPI
95#else
96 #define ThreadFuncReturnType void
97#endif
98
99static ThreadFuncReturnType
100ThreadFunc (void * _t)
101{
102  tr_thread * t = _t;
103
104  t->func (t->arg);
105
106  tr_free (t);
107#ifdef _WIN32
108  _endthreadex (0);
109  return 0;
110#endif
111}
112
113tr_thread *
114tr_threadNew (void (*func)(void *), void * arg)
115{
116  tr_thread * t = tr_new0 (tr_thread, 1);
117
118  t->func = func;
119  t->arg  = arg;
120
121#ifdef _WIN32
122  {
123    unsigned int id;
124    t->thread_handle = (HANDLE) _beginthreadex (NULL, 0, &ThreadFunc, t, 0, &id);
125    t->thread = (DWORD) id;
126  }
127#else
128  pthread_create (&t->thread, NULL, (void* (*)(void*))ThreadFunc, t);
129  pthread_detach (t->thread);
130#endif
131
132  return t;
133}
134
135/***
136****  LOCKS
137***/
138
139/** @brief portability wrapper around OS-dependent thread mutexes */
140struct tr_lock
141{
142  int                 depth;
143#ifdef _WIN32
144  CRITICAL_SECTION    lock;
145  DWORD               lockThread;
146#else
147  pthread_mutex_t     lock;
148  pthread_t           lockThread;
149#endif
150};
151
152tr_lock*
153tr_lockNew (void)
154{
155  tr_lock * l = tr_new0 (tr_lock, 1);
156
157#ifdef _WIN32
158  InitializeCriticalSection (&l->lock); /* supports recursion */
159#else
160  pthread_mutexattr_t attr;
161  pthread_mutexattr_init (&attr);
162  pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
163  pthread_mutex_init (&l->lock, &attr);
164#endif
165
166  return l;
167}
168
169void
170tr_lockFree (tr_lock * l)
171{
172#ifdef _WIN32
173    DeleteCriticalSection (&l->lock);
174#else
175    pthread_mutex_destroy (&l->lock);
176#endif
177    tr_free (l);
178}
179
180void
181tr_lockLock (tr_lock * l)
182{
183#ifdef _WIN32
184  EnterCriticalSection (&l->lock);
185#else
186  pthread_mutex_lock (&l->lock);
187#endif
188
189  assert (l->depth >= 0);
190  assert (!l->depth || tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
191  l->lockThread = tr_getCurrentThread ();
192  ++l->depth;
193}
194
195int
196tr_lockHave (const tr_lock * l)
197{
198  return (l->depth > 0) &&
199         (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
200}
201
202void
203tr_lockUnlock (tr_lock * l)
204{
205  assert (l->depth > 0);
206  assert (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
207
208  --l->depth;
209  assert (l->depth >= 0);
210#ifdef _WIN32
211  LeaveCriticalSection (&l->lock);
212#else
213  pthread_mutex_unlock (&l->lock);
214#endif
215}
216
217/***
218****  PATHS
219***/
220
221#ifndef _WIN32
222 #include <pwd.h>
223#endif
224
225#ifdef _WIN32
226
227static char *
228win32_get_special_folder (int folder_id)
229{
230  wchar_t path[MAX_PATH]; /* SHGetFolderPath () requires MAX_PATH */
231  *path = L'\0';
232  SHGetFolderPathW (NULL, folder_id, NULL, 0, path);
233  return tr_win32_native_to_utf8 (path, -1);
234}
235
236#endif
237
238static const char *
239getHomeDir (void)
240{
241  static char * home = NULL;
242
243  if (!home)
244    {
245      home = tr_env_get_string ("HOME", NULL);
246
247      if (!home)
248        {
249#ifdef _WIN32
250          home = win32_get_special_folder (CSIDL_PERSONAL);
251#else
252          struct passwd * pw = getpwuid (getuid ());
253          if (pw)
254            home = tr_strdup (pw->pw_dir);
255          endpwent ();
256#endif
257        }
258
259      if (!home)
260        home = tr_strdup ("");
261    }
262
263  return home;
264}
265
266#if defined (__APPLE__) || defined (_WIN32)
267 #define RESUME_SUBDIR  "Resume"
268 #define TORRENT_SUBDIR "Torrents"
269#else
270 #define RESUME_SUBDIR  "resume"
271 #define TORRENT_SUBDIR "torrents"
272#endif
273
274void
275tr_setConfigDir (tr_session * session, const char * configDir)
276{
277  char * path;
278
279  session->configDir = tr_strdup (configDir);
280
281  path = tr_buildPath (configDir, RESUME_SUBDIR, NULL);
282  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
283  session->resumeDir = path;
284
285  path = tr_buildPath (configDir, TORRENT_SUBDIR, NULL);
286  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
287  session->torrentDir = path;
288}
289
290const char *
291tr_sessionGetConfigDir (const tr_session * session)
292{
293  return session->configDir;
294}
295
296const char *
297tr_getTorrentDir (const tr_session * session)
298{
299  return session->torrentDir;
300}
301
302const char *
303tr_getResumeDir (const tr_session * session)
304{
305  return session->resumeDir;
306}
307
308const char*
309tr_getDefaultConfigDir (const char * appname)
310{
311  static char * s = NULL;
312
313  if (!appname || !*appname)
314    appname = "Transmission";
315
316  if (!s)
317    {
318      s = tr_env_get_string ("TRANSMISSION_HOME", NULL);
319
320      if (s == NULL)
321        {
322#ifdef __APPLE__
323          s = tr_buildPath (getHomeDir (), "Library", "Application Support", appname, NULL);
324#elif defined (_WIN32)
325          char * appdata = win32_get_special_folder (CSIDL_APPDATA);
326          s = tr_buildPath (appdata, appname, NULL);
327          tr_free (appdata);
328#elif defined (__HAIKU__)
329          char buf[PATH_MAX];
330          find_directory (B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof (buf));
331          s = tr_buildPath (buf, appname, NULL);
332#else
333          if ((s = tr_env_get_string ("XDG_CONFIG_HOME", NULL)))
334            {
335              s = tr_buildPath (s, appname, NULL);
336              tr_free (s);
337            }
338          else
339            {
340              s = tr_buildPath (getHomeDir (), ".config", appname, NULL);
341            }
342#endif
343        }
344    }
345
346  return s;
347}
348
349const char*
350tr_getDefaultDownloadDir (void)
351{
352  static char * user_dir = NULL;
353
354  if (user_dir == NULL)
355    {
356      char * config_home;
357      char * config_file;
358      char * content;
359      size_t content_len;
360
361      /* figure out where to look for user-dirs.dirs */
362      config_home = tr_env_get_string ("XDG_CONFIG_HOME", NULL);
363      if (config_home && *config_home)
364        config_file = tr_buildPath (config_home, "user-dirs.dirs", NULL);
365      else
366        config_file = tr_buildPath (getHomeDir (), ".config", "user-dirs.dirs", NULL);
367      tr_free (config_home);
368
369      /* read in user-dirs.dirs and look for the download dir entry */
370      content = (char *) tr_loadFile (config_file, &content_len);
371      if (content && content_len>0)
372        {
373          const char * key = "XDG_DOWNLOAD_DIR=\"";
374          char * line = strstr (content, key);
375          if (line != NULL)
376            {
377              char * value = line + strlen (key);
378              char * end = strchr (value, '"');
379
380              if (end)
381                {
382                  *end = '\0';
383
384                  if (!memcmp (value, "$HOME/", 6))
385                    user_dir = tr_buildPath (getHomeDir (), value+6, NULL);
386                  else if (!strcmp (value, "$HOME"))
387                    user_dir = tr_strdup (getHomeDir ());
388                  else
389                    user_dir = tr_strdup (value);
390                }
391            }
392        }
393
394      if (user_dir == NULL)
395#ifdef __HAIKU__
396        user_dir = tr_buildPath (getHomeDir (), "Desktop", NULL);
397#else
398        user_dir = tr_buildPath (getHomeDir (), "Downloads", NULL);
399#endif
400
401      tr_free (content);
402      tr_free (config_file);
403    }
404
405  return user_dir;
406}
407
408/***
409****
410***/
411
412static bool
413isWebClientDir (const char * path)
414{
415  char * tmp = tr_buildPath (path, "index.html", NULL);
416  const bool ret = tr_sys_path_exists (tmp, NULL);
417  tr_logAddInfo (_("Searching for web interface file \"%s\""), tmp);
418  tr_free (tmp);
419
420  return ret;
421}
422
423const char *
424tr_getWebClientDir (const tr_session * session UNUSED)
425{
426  static char * s = NULL;
427
428  if (!s)
429    {
430      s = tr_env_get_string ("CLUTCH_HOME", NULL);
431      if (s == NULL)
432        s = tr_env_get_string ("TRANSMISSION_WEB_HOME", NULL);
433      if (s == NULL)
434        {
435
436#ifdef BUILD_MAC_CLIENT /* on Mac, look in the Application Support folder first, then in the app bundle. */
437
438          /* Look in the Application Support folder */
439          s = tr_buildPath (tr_sessionGetConfigDir (session), "web", NULL);
440
441          if (!isWebClientDir (s))
442            {
443              tr_free (s);
444
445              CFURLRef appURL = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
446              CFStringRef appRef = CFURLCopyFileSystemPath (appURL,
447                                                            kCFURLPOSIXPathStyle);
448              const CFIndex appStringLength = CFStringGetMaximumSizeOfFileSystemRepresentation (appRef);
449
450              char * appString = tr_malloc (appStringLength);
451              const bool success = CFStringGetFileSystemRepresentation (appRef, appString, appStringLength);
452              assert (success);
453
454              CFRelease (appURL);
455              CFRelease (appRef);
456
457              /* Fallback to the app bundle */
458              s = tr_buildPath (appString, "Contents", "Resources", "web", NULL);
459              if (!isWebClientDir (s))
460                {
461                  tr_free (s);
462                  s = NULL;
463                }
464
465              tr_free (appString);
466            }
467
468#elif defined (_WIN32)
469
470          /* Generally, Web interface should be stored in a Web subdir of
471           * calling executable dir. */
472
473          if (s == NULL) /* check personal AppData/Transmission/Web */
474            {
475              char * dir = win32_get_special_folder (CSIDL_COMMON_APPDATA);
476              s = tr_buildPath (dir, "Transmission", "Web", NULL);
477              tr_free (dir);
478              if (!isWebClientDir (s))
479                {
480                  tr_free (s);
481                  s = NULL;
482                }
483            }
484
485          if (s == NULL) /* check personal AppData */
486            {
487              char * dir = win32_get_special_folder (CSIDL_APPDATA);
488              s = tr_buildPath (dir, "Transmission", "Web", NULL);
489              tr_free (dir);
490              if (!isWebClientDir (s))
491                {
492                  tr_free (s);
493                  s = NULL;
494                }
495            }
496
497            if (s == NULL) /* check calling module place */
498              {
499                wchar_t wide_module_path[MAX_PATH];
500                char * module_path;
501                char * dir;
502                GetModuleFileNameW (NULL, wide_module_path,
503                                    sizeof (wide_module_path) / sizeof (*wide_module_path));
504                module_path = tr_win32_native_to_utf8 (wide_module_path, -1);
505                dir = tr_sys_path_dirname (module_path, NULL);
506                tr_free (module_path);
507                s = tr_buildPath (dir, "Web", NULL);
508                tr_free (dir);
509                if (!isWebClientDir (s))
510                  {
511                    tr_free (s);
512                    s = NULL;
513                  }
514            }
515
516#else /* everyone else, follow the XDG spec */
517
518          tr_list *candidates = NULL, *l;
519          char * tmp;
520
521          /* XDG_DATA_HOME should be the first in the list of candidates */
522          tmp = tr_env_get_string ("XDG_DATA_HOME", NULL);
523          if (tmp && *tmp)
524            {
525              tr_list_append (&candidates, tmp);
526            }
527          else
528            {
529              char * dhome = tr_buildPath (getHomeDir (), ".local", "share", NULL);
530              tr_list_append (&candidates, dhome);
531              tr_free (tmp);
532            }
533
534          /* XDG_DATA_DIRS are the backup directories */
535          {
536            const char * pkg = PACKAGE_DATA_DIR;
537            char * xdg = tr_env_get_string ("XDG_DATA_DIRS", NULL);
538            const char * fallback = "/usr/local/share:/usr/share";
539            char * buf = tr_strdup_printf ("%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback);
540            tr_free (xdg);
541            tmp = buf;
542            while (tmp && *tmp)
543              {
544                const char * end = strchr (tmp, ':');
545                if (end)
546                  {
547                    if ((end - tmp) > 1)
548                      tr_list_append (&candidates, tr_strndup (tmp, end - tmp));
549                    tmp = (char *) end + 1;
550                  }
551                else if (tmp && *tmp)
552                  {
553                    tr_list_append (&candidates, tr_strdup (tmp));
554                    break;
555                  }
556              }
557            tr_free (buf);
558          }
559
560          /* walk through the candidates & look for a match */
561          for (l=candidates; l; l=l->next)
562            {
563              char * path = tr_buildPath (l->data, "transmission", "web", NULL);
564              const int found = isWebClientDir (path);
565              if (found)
566                {
567                  s = path;
568                  break;
569                }
570              tr_free (path);
571            }
572
573          tr_list_free (&candidates, tr_free);
574
575#endif
576
577        }
578    }
579
580  return s;
581}
Note: See TracBrowser for help on using the repository browser.