The open source OpenXR runtime
at main 286 lines 6.3 kB view raw
1// Copyright 2019-2025, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Very simple file opening functions. 6 * @author Jakob Bornecrantz <jakob@collabora.com> 7 * @author Pete Black <pblack@collabora.com> 8 * @ingroup aux_util 9 */ 10 11#include "xrt/xrt_config_os.h" 12#include "xrt/xrt_windows.h" 13#include "util/u_file.h" 14 15#include <errno.h> 16#include <stdbool.h> 17#include <stdio.h> 18#include <stdlib.h> 19#include <string.h> 20 21#if defined(XRT_OS_WINDOWS) && !defined(XRT_ENV_MINGW) 22#define PATH_MAX 4096 23#endif 24 25#ifdef XRT_OS_LINUX 26#include <sys/stat.h> 27#include <linux/limits.h> 28 29static int 30mkpath(const char *path) 31{ 32 char tmp[PATH_MAX]; 33 char *p = NULL; 34 size_t len; 35 36 snprintf(tmp, sizeof(tmp), "%s", path); 37 len = strlen(tmp) - 1; 38 if (tmp[len] == '/') { 39 tmp[len] = 0; 40 } 41 42 for (p = tmp + 1; *p; p++) { 43 if (*p == '/') { 44 *p = 0; 45 if (mkdir(tmp, S_IRWXU) < 0 && errno != EEXIST) { 46 return -1; 47 } 48 *p = '/'; 49 } 50 } 51 52 if (mkdir(tmp, S_IRWXU) < 0 && errno != EEXIST) { 53 return -1; 54 } 55 56 return 0; 57} 58 59static bool 60is_dir(const char *path) 61{ 62 struct stat st = {0}; 63 if (!stat(path, &st)) { 64 return S_ISDIR(st.st_mode); 65 } else { 66 return false; 67 } 68} 69 70int 71u_file_get_config_dir(char *out_path, size_t out_path_size) 72{ 73 const char *xdg_home = getenv("XDG_CONFIG_HOME"); 74 const char *home = getenv("HOME"); 75 if (xdg_home != NULL) { 76 return snprintf(out_path, out_path_size, "%s/monado", xdg_home); 77 } 78 if (home != NULL) { 79 return snprintf(out_path, out_path_size, "%s/.config/monado", home); 80 } 81 return -1; 82} 83 84int 85u_file_get_path_in_config_dir(const char *suffix, char *out_path, size_t out_path_size) 86{ 87 char tmp[PATH_MAX]; 88 int i = u_file_get_config_dir(tmp, sizeof(tmp)); 89 if (i <= 0) { 90 return i; 91 } 92 93 return snprintf(out_path, out_path_size, "%s/%s", tmp, suffix); 94} 95 96FILE * 97u_file_open_file_in_config_dir(const char *filename, const char *mode) 98{ 99 char tmp[PATH_MAX]; 100 int i = u_file_get_config_dir(tmp, sizeof(tmp)); 101 if (i <= 0) { 102 return NULL; 103 } 104 105 char file_str[PATH_MAX + 15]; 106 i = snprintf(file_str, sizeof(file_str), "%s/%s", tmp, filename); 107 if (i <= 0) { 108 return NULL; 109 } 110 111 FILE *file = fopen(file_str, mode); 112 if (file != NULL) { 113 return file; 114 } 115 116 // Try creating the path. 117 mkpath(tmp); 118 119 // Do not report error. 120 return fopen(file_str, mode); 121} 122 123FILE * 124u_file_open_file_in_config_dir_subpath(const char *subpath, const char *filename, const char *mode) 125{ 126 char tmp[PATH_MAX]; 127 int i = u_file_get_config_dir(tmp, sizeof(tmp)); 128 if (i < 0 || i >= (int)sizeof(tmp)) { 129 return NULL; 130 } 131 132 char fullpath[PATH_MAX]; 133 i = snprintf(fullpath, sizeof(fullpath), "%s/%s", tmp, subpath); 134 if (i < 0 || i >= (int)sizeof(fullpath)) { 135 return NULL; 136 } 137 138 char file_str[PATH_MAX + 15]; 139 i = snprintf(file_str, sizeof(file_str), "%s/%s", fullpath, filename); 140 if (i < 0 || i >= (int)sizeof(file_str)) { 141 return NULL; 142 } 143 144 FILE *file = fopen(file_str, mode); 145 if (file != NULL) { 146 return file; 147 } 148 149 // Try creating the path. 150 mkpath(fullpath); 151 152 // Do not report error. 153 return fopen(file_str, mode); 154} 155 156int 157u_file_get_hand_tracking_models_dir(char *out_path, size_t out_path_size) 158{ 159 const char *suffix = "/monado/hand-tracking-models"; 160 const char *xdg_data_home = getenv("XDG_DATA_HOME"); 161 const char *home = getenv("HOME"); 162 int ret = 0; 163 164 if (xdg_data_home != NULL) { 165 ret = snprintf(out_path, out_path_size, "%s%s", xdg_data_home, suffix); 166 if (ret > 0 && is_dir(out_path)) { 167 return ret; 168 } 169 } 170 171 if (home != NULL) { 172 ret = snprintf(out_path, out_path_size, "%s/.local/share%s", home, suffix); 173 if (ret > 0 && is_dir(out_path)) { 174 return ret; 175 } 176 } 177 178 ret = snprintf(out_path, out_path_size, "/usr/local/share%s", suffix); 179 if (ret > 0 && is_dir(out_path)) { 180 return ret; 181 } 182 183 ret = snprintf(out_path, out_path_size, "/usr/share%s", suffix); 184 if (ret > 0 && is_dir(out_path)) { 185 return ret; 186 } 187 188 if (out_path_size > 0) { 189 out_path[0] = '\0'; 190 } 191 192 return ret; 193} 194 195#endif /* XRT_OS_LINUX */ 196 197int 198u_file_get_runtime_dir(char *out_path, size_t out_path_size) 199{ 200 const char *xdg_rt = getenv("XDG_RUNTIME_DIR"); 201 if (xdg_rt != NULL) { 202 return snprintf(out_path, out_path_size, "%s", xdg_rt); 203 } 204 205 const char *xdg_cache = getenv("XDG_CACHE_HOME"); 206 if (xdg_cache != NULL) { 207 return snprintf(out_path, out_path_size, "%s", xdg_cache); 208 } 209 210#ifdef XRT_OS_WINDOWS 211#ifndef UNICODE // If Unicode support is disabled, use ANSI functions directly into out_path 212#ifdef GetTempPath2 // GetTempPath2 is only available on Windows 11 >= 22000, fallback to GetTempPath for older versions 213 return (int)GetTempPath2A(out_path_size, out_path); 214#else 215 return (int)GetTempPathA(out_path_size, out_path); 216#endif 217#else 218 WCHAR temp[MAX_PATH] = {0}; 219#ifdef GetTempPath2 // GetTempPath2 is only available on Windows 11 >= 22000, fallback to GetTempPath for older versions 220 GetTempPath2W(sizeof(temp), temp); 221#else // GetTempPath2 222 GetTempPathW(sizeof(temp), temp); 223#endif 224 return wcstombs(out_path, temp, out_path_size); 225#endif // UNICODE 226#else 227 const char *cache = "~/.cache"; 228 return snprintf(out_path, out_path_size, "%s", cache); 229#endif 230} 231 232int 233u_file_get_path_in_runtime_dir(const char *suffix, char *out_path, size_t out_path_size) 234{ 235 char tmp[PATH_MAX]; 236 int i = u_file_get_runtime_dir(tmp, sizeof(tmp)); 237 if (i <= 0) { 238 return i; 239 } 240 241 return snprintf(out_path, out_path_size, "%s/%s", tmp, suffix); 242} 243 244char * 245u_file_read_content(FILE *file, size_t *out_file_size) 246{ 247 // Go to the end of the file. 248 fseek(file, 0L, SEEK_END); 249 size_t file_size = ftell(file); 250 251 // Return back to the start of the file. 252 fseek(file, 0L, SEEK_SET); 253 254 char *buffer = (char *)calloc(file_size + 1, sizeof(char)); 255 if (buffer == NULL) { 256 return NULL; 257 } 258 259 // Do the actual reading. 260 size_t ret = fread(buffer, sizeof(char), file_size, file); 261 if (ret != file_size) { 262 free(buffer); 263 return NULL; 264 } 265 266 if (out_file_size) 267 *out_file_size = file_size; 268 269 return buffer; 270} 271 272char * 273u_file_read_content_from_path(const char *path, size_t *out_file_size) 274{ 275 FILE *file = fopen(path, "rb"); 276 if (file == NULL) { 277 return NULL; 278 } 279 char *file_content = u_file_read_content(file, out_file_size); 280 int ret = fclose(file); 281 // We don't care about the return value since we're just reading 282 (void)ret; 283 284 // Either valid non-null or null 285 return file_content; 286}