🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

367 lines
9.7 KiB

  1. /*
  2. * Unix support routines for PhysicsFS.
  3. *
  4. * Please see the file LICENSE.txt in the source's root directory.
  5. *
  6. * This file written by Ryan C. Gordon.
  7. */
  8. #define __PHYSICSFS_INTERNAL__
  9. #include "physfs_platforms.h"
  10. #ifdef PHYSFS_PLATFORM_UNIX
  11. #include <ctype.h>
  12. #include <unistd.h>
  13. #include <stdlib.h>
  14. #include <sys/types.h>
  15. #include <pwd.h>
  16. #include <sys/stat.h>
  17. #include <sys/param.h>
  18. #include <dirent.h>
  19. #include <time.h>
  20. #include <errno.h>
  21. #include <limits.h>
  22. #if PHYSFS_NO_CDROM_SUPPORT
  23. #elif PHYSFS_PLATFORM_LINUX
  24. # define PHYSFS_HAVE_MNTENT_H 1
  25. #elif defined __CYGWIN__
  26. # define PHYSFS_HAVE_MNTENT_H 1
  27. #elif PHYSFS_PLATFORM_SOLARIS
  28. # define PHYSFS_HAVE_SYS_MNTTAB_H 1
  29. #elif PHYSFS_PLATFORM_BSD
  30. # define PHYSFS_HAVE_SYS_UCRED_H 1
  31. #else
  32. # warning No CD-ROM support included. Either define your platform here,
  33. # warning or define PHYSFS_NO_CDROM_SUPPORT=1 to confirm this is intentional.
  34. #endif
  35. #ifdef PHYSFS_HAVE_SYS_UCRED_H
  36. # ifdef PHYSFS_HAVE_MNTENT_H
  37. # undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
  38. # endif
  39. # include <sys/mount.h>
  40. # include <sys/ucred.h>
  41. #endif
  42. #ifdef PHYSFS_HAVE_MNTENT_H
  43. #include <mntent.h>
  44. #endif
  45. #ifdef PHYSFS_HAVE_SYS_MNTTAB_H
  46. #include <sys/mnttab.h>
  47. #endif
  48. #ifdef PHYSFS_PLATFORM_FREEBSD
  49. #include <sys/sysctl.h>
  50. #endif
  51. #include "physfs_internal.h"
  52. int __PHYSFS_platformInit(void)
  53. {
  54. return 1; /* always succeed. */
  55. } /* __PHYSFS_platformInit */
  56. void __PHYSFS_platformDeinit(void)
  57. {
  58. /* no-op */
  59. } /* __PHYSFS_platformDeinit */
  60. void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
  61. {
  62. #if (defined PHYSFS_NO_CDROM_SUPPORT)
  63. /* no-op. */
  64. #elif (defined PHYSFS_HAVE_SYS_UCRED_H)
  65. int i;
  66. struct statfs *mntbufp = NULL;
  67. int mounts = getmntinfo(&mntbufp, MNT_NOWAIT);
  68. for (i = 0; i < mounts; i++)
  69. {
  70. int add_it = 0;
  71. if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
  72. add_it = 1;
  73. else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
  74. add_it = 1;
  75. /* add other mount types here */
  76. if (add_it)
  77. cb(data, mntbufp[i].f_mntonname);
  78. } /* for */
  79. #elif (defined PHYSFS_HAVE_MNTENT_H)
  80. FILE *mounts = NULL;
  81. struct mntent *ent = NULL;
  82. mounts = setmntent("/etc/mtab", "r");
  83. BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
  84. while ( (ent = getmntent(mounts)) != NULL )
  85. {
  86. int add_it = 0;
  87. if (strcmp(ent->mnt_type, "iso9660") == 0)
  88. add_it = 1;
  89. else if (strcmp(ent->mnt_type, "udf") == 0)
  90. add_it = 1;
  91. /* !!! FIXME: these might pick up floppy drives, right? */
  92. else if (strcmp(ent->mnt_type, "auto") == 0)
  93. add_it = 1;
  94. else if (strcmp(ent->mnt_type, "supermount") == 0)
  95. add_it = 1;
  96. /* add other mount types here */
  97. if (add_it)
  98. cb(data, ent->mnt_dir);
  99. } /* while */
  100. endmntent(mounts);
  101. #elif (defined PHYSFS_HAVE_SYS_MNTTAB_H)
  102. FILE *mounts = fopen(MNTTAB, "r");
  103. struct mnttab ent;
  104. BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
  105. while (getmntent(mounts, &ent) == 0)
  106. {
  107. int add_it = 0;
  108. if (strcmp(ent.mnt_fstype, "hsfs") == 0)
  109. add_it = 1;
  110. /* add other mount types here */
  111. if (add_it)
  112. cb(data, ent.mnt_mountp);
  113. } /* while */
  114. fclose(mounts);
  115. #endif
  116. } /* __PHYSFS_platformDetectAvailableCDs */
  117. /*
  118. * See where program (bin) resides in the $PATH specified by (envr).
  119. * returns a copy of the first element in envr that contains it, or NULL
  120. * if it doesn't exist or there were other problems. PHYSFS_SetError() is
  121. * called if we have a problem.
  122. *
  123. * (envr) will be scribbled over, and you are expected to allocator.Free() the
  124. * return value when you're done with it.
  125. */
  126. static char *findBinaryInPath(const char *bin, char *envr)
  127. {
  128. size_t alloc_size = 0;
  129. char *exe = NULL;
  130. char *start = envr;
  131. char *ptr;
  132. assert(bin != NULL);
  133. assert(envr != NULL);
  134. do
  135. {
  136. size_t size;
  137. size_t binlen;
  138. ptr = strchr(start, ':'); /* find next $PATH separator. */
  139. if (ptr)
  140. *ptr = '\0';
  141. binlen = strlen(bin);
  142. size = strlen(start) + binlen + 2;
  143. if (size >= alloc_size)
  144. {
  145. char *x = (char *) allocator.Realloc(exe, size);
  146. if (!x)
  147. {
  148. if (exe != NULL)
  149. allocator.Free(exe);
  150. BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  151. } /* if */
  152. alloc_size = size;
  153. exe = x;
  154. } /* if */
  155. /* build full binary path... */
  156. strcpy(exe, start);
  157. if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
  158. strcat(exe, "/");
  159. strcat(exe, bin);
  160. if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */
  161. {
  162. exe[(size - binlen) - 1] = '\0'; /* chop off filename, leave '/' */
  163. return exe;
  164. } /* if */
  165. start = ptr + 1; /* start points to beginning of next element. */
  166. } while (ptr != NULL);
  167. if (exe != NULL)
  168. allocator.Free(exe);
  169. return NULL; /* doesn't exist in path. */
  170. } /* findBinaryInPath */
  171. static char *readSymLink(const char *path)
  172. {
  173. ssize_t len = 64;
  174. ssize_t rc = -1;
  175. char *retval = NULL;
  176. while (1)
  177. {
  178. char *ptr = (char *) allocator.Realloc(retval, (size_t) len);
  179. if (ptr == NULL)
  180. break; /* out of memory. */
  181. retval = ptr;
  182. rc = readlink(path, retval, len);
  183. if (rc == -1)
  184. break; /* not a symlink, i/o error, etc. */
  185. else if (rc < len)
  186. {
  187. retval[rc] = '\0'; /* readlink doesn't null-terminate. */
  188. return retval; /* we're good to go. */
  189. } /* else if */
  190. len *= 2; /* grow buffer, try again. */
  191. } /* while */
  192. if (retval != NULL)
  193. allocator.Free(retval);
  194. return NULL;
  195. } /* readSymLink */
  196. char *__PHYSFS_platformCalcBaseDir(const char *argv0)
  197. {
  198. char *retval = NULL;
  199. const char *envr = NULL;
  200. /* Try to avoid using argv0 unless forced to. Try system-specific stuff. */
  201. #if defined(PHYSFS_PLATFORM_FREEBSD)
  202. {
  203. char fullpath[PATH_MAX];
  204. size_t buflen = sizeof (fullpath);
  205. int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
  206. if (sysctl(mib, 4, fullpath, &buflen, NULL, 0) != -1)
  207. retval = __PHYSFS_strdup(fullpath);
  208. }
  209. #elif defined(PHYSFS_PLATFORM_SOLARIS)
  210. {
  211. const char *path = getexecname();
  212. if ((path != NULL) && (path[0] == '/')) /* must be absolute path... */
  213. retval = __PHYSFS_strdup(path);
  214. }
  215. #endif
  216. /* If there's a Linux-like /proc filesystem, you can get the full path to
  217. * the current process from a symlink in there.
  218. */
  219. if (!retval && (access("/proc", F_OK) == 0))
  220. {
  221. retval = readSymLink("/proc/self/exe");
  222. if (!retval) retval = readSymLink("/proc/curproc/file");
  223. if (!retval) retval = readSymLink("/proc/curproc/exe");
  224. if (retval == NULL)
  225. {
  226. /* older kernels don't have /proc/self ... try PID version... */
  227. const unsigned long long pid = (unsigned long long) getpid();
  228. char path[64];
  229. const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid);
  230. if ( (rc > 0) && (rc < sizeof(path)) )
  231. retval = readSymLink(path);
  232. } /* if */
  233. } /* if */
  234. if (retval != NULL) /* chop off filename. */
  235. {
  236. char *ptr = strrchr(retval, '/');
  237. if (ptr != NULL)
  238. *(ptr+1) = '\0';
  239. else /* shouldn't happen, but just in case... */
  240. {
  241. allocator.Free(retval);
  242. retval = NULL;
  243. } /* else */
  244. } /* if */
  245. /* No /proc/self/exe, etc, but we have an argv[0] we can parse? */
  246. if ((retval == NULL) && (argv0 != NULL))
  247. {
  248. /* fast path: default behaviour can handle this. */
  249. if (strchr(argv0, '/') != NULL)
  250. return NULL; /* higher level parses out real path from argv0. */
  251. /* If there's no dirsep on argv0, then look through $PATH for it. */
  252. envr = getenv("PATH");
  253. if (envr != NULL)
  254. {
  255. char *path = (char *) __PHYSFS_smallAlloc(strlen(envr) + 1);
  256. BAIL_IF(!path, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  257. strcpy(path, envr);
  258. retval = findBinaryInPath(argv0, path);
  259. __PHYSFS_smallFree(path);
  260. } /* if */
  261. } /* if */
  262. if (retval != NULL)
  263. {
  264. /* try to shrink buffer... */
  265. char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1);
  266. if (ptr != NULL)
  267. retval = ptr; /* oh well if it failed. */
  268. } /* if */
  269. return retval;
  270. } /* __PHYSFS_platformCalcBaseDir */
  271. char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
  272. {
  273. /*
  274. * We use XDG's base directory spec, even if you're not on Linux.
  275. * This isn't strictly correct, but the results are relatively sane
  276. * in any case.
  277. *
  278. * https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
  279. */
  280. const char *envr = getenv("XDG_DATA_HOME");
  281. const char *append = "/";
  282. char *retval = NULL;
  283. size_t len = 0;
  284. if (!envr)
  285. {
  286. /* You end up with "$HOME/.local/share/Game Name 2" */
  287. envr = __PHYSFS_getUserDir();
  288. BAIL_IF_ERRPASS(!envr, NULL); /* oh well. */
  289. append = ".local/share/";
  290. } /* if */
  291. len = strlen(envr) + strlen(append) + strlen(app) + 2;
  292. retval = (char *) allocator.Malloc(len);
  293. BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  294. snprintf(retval, len, "%s%s%s/", envr, append, app);
  295. return retval;
  296. } /* __PHYSFS_platformCalcPrefDir */
  297. #endif /* PHYSFS_PLATFORM_UNIX */
  298. /* end of physfs_platform_unix.c ... */