DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #include "dosbox.h" 00021 #include "cross.h" 00022 #include "support.h" 00023 #include <string> 00024 #include <limits.h> 00025 #include <stdlib.h> 00026 00027 #if defined(MACOSX) 00028 std::string MacOSXEXEPath; 00029 std::string MacOSXResPath; 00030 #endif 00031 00032 #ifdef WIN32 00033 #ifndef _WIN32_IE 00034 #define _WIN32_IE 0x0400 00035 #endif 00036 #include <shlobj.h> 00037 #endif 00038 00039 #if defined HAVE_SYS_TYPES_H && defined HAVE_PWD_H 00040 #include <sys/types.h> 00041 #include <pwd.h> 00042 #endif 00043 00044 #if defined __MINGW32__ 00045 #define _mkdir(x) mkdir(x) 00046 #endif 00047 00048 #if defined(WIN32) && !defined(HX_DOS) 00049 static void W32_ConfDir(std::string& in,bool create) { 00050 int c = create?1:0; 00051 char result[MAX_PATH] = { 0 }; 00052 BOOL r = SHGetSpecialFolderPath(NULL,result,CSIDL_LOCAL_APPDATA,c); 00053 if(!r || result[0] == 0) r = SHGetSpecialFolderPath(NULL,result,CSIDL_APPDATA,c); 00054 if(!r || result[0] == 0) { 00055 char const * windir = getenv("windir"); 00056 if(!windir) windir = "c:\\windows"; 00057 safe_strncpy(result,windir,MAX_PATH); 00058 char const* appdata = "\\Application Data"; 00059 size_t len = strlen(result); 00060 if(len + strlen(appdata) < MAX_PATH) strcat(result,appdata); 00061 if(create) _mkdir(result); 00062 } 00063 in = result; 00064 } 00065 #endif 00066 00067 void Cross::GetPlatformResDir(std::string& in) { 00068 #if defined(MACOSX) 00069 in = MacOSXResPath; 00070 #elif defined(RISCOS) 00071 in = "/<DosBox-X$Dir>/resources"; 00072 #elif defined(RESDIR) 00073 in = RESDIR; 00074 #endif 00075 if (!in.empty()) 00076 in += CROSS_FILESPLIT; 00077 } 00078 00079 void Cross::GetPlatformConfigDir(std::string& in) { 00080 #if defined(WIN32) && !defined(HX_DOS) 00081 W32_ConfDir(in,false); 00082 in += "\\DOSBox-X"; 00083 #elif defined(MACOSX) 00084 in = "~/Library/Preferences"; 00085 ResolveHomedir(in); 00086 #elif defined(HAIKU) 00087 in = "~/config/settings/dosbox-x"; 00088 ResolveHomedir(in); 00089 #elif defined(RISCOS) 00090 in = "/<Choices$Write>/DosBox-X"; 00091 #elif !defined(HX_DOS) 00092 in = "~/.config/dosbox-x"; 00093 ResolveHomedir(in); 00094 #endif 00095 in += CROSS_FILESPLIT; 00096 } 00097 00098 void Cross::GetPlatformConfigName(std::string& in) { 00099 #ifdef WIN32 00100 #define DEFAULT_CONFIG_FILE "dosbox-x-" VERSION ".conf" 00101 #elif defined(MACOSX) 00102 #define DEFAULT_CONFIG_FILE "DOSBox-X " VERSION " Preferences" 00103 #else /*linux freebsd*/ 00104 #define DEFAULT_CONFIG_FILE "dosbox-x-" VERSION ".conf" 00105 #endif 00106 in = DEFAULT_CONFIG_FILE; 00107 } 00108 00109 void Cross::CreatePlatformConfigDir(std::string& in) { 00110 #if defined(WIN32) && !defined(HX_DOS) 00111 W32_ConfDir(in,true); 00112 in += "\\DOSBox-X"; 00113 _mkdir(in.c_str()); 00114 #elif defined(MACOSX) 00115 in = "~/Library/Preferences"; 00116 ResolveHomedir(in); 00117 //Don't create it. Assume it exists 00118 #elif defined(HAIKU) 00119 in = "~/config/settings/dosbox-x"; 00120 ResolveHomedir(in); 00121 mkdir(in.c_str(),0700); 00122 #elif defined(RISCOS) 00123 in = "/<Choices$Write>/DosBox-X"; 00124 mkdir(in.c_str(),0700); 00125 #elif !defined(HX_DOS) 00126 in = "~/.config/dosbox-x"; 00127 ResolveHomedir(in); 00128 mkdir(in.c_str(),0700); 00129 #endif 00130 in += CROSS_FILESPLIT; 00131 } 00132 00133 void Cross::ResolveHomedir(std::string & temp_line) { 00134 if(!temp_line.size() || temp_line[0] != '~') return; //No ~ 00135 00136 if(temp_line.size() == 1 || temp_line[1] == CROSS_FILESPLIT) { //The ~ and ~/ variant 00137 char * home = getenv("HOME"); 00138 if(home) temp_line.replace(0,1,std::string(home)); 00139 #if defined HAVE_SYS_TYPES_H && defined HAVE_PWD_H 00140 } else { // The ~username variant 00141 std::string::size_type namelen = temp_line.find(CROSS_FILESPLIT); 00142 if(namelen == std::string::npos) namelen = temp_line.size(); 00143 std::string username = temp_line.substr(1,namelen - 1); 00144 struct passwd* pass = getpwnam(username.c_str()); 00145 if(pass) temp_line.replace(0,namelen,pass->pw_dir); //namelen -1 +1(for the ~) 00146 #endif // USERNAME lookup code 00147 } 00148 } 00149 00150 void Cross::CreateDir(std::string const& in) { 00151 #ifdef WIN32 00152 _mkdir(in.c_str()); 00153 #else 00154 mkdir(in.c_str(),0700); 00155 #endif 00156 } 00157 00158 bool Cross::IsPathAbsolute(std::string const& in) { 00159 // Absolute paths 00160 #if defined (WIN32) || defined(OS2) 00161 // drive letter 00162 if (in.size() > 2 && in[1] == ':' ) return true; 00163 // UNC path 00164 else if (in.size() > 2 && in[0]=='\\' && in[1]=='\\') return true; 00165 #else 00166 if (in.size() > 1 && in[0] == '/' ) return true; 00167 #endif 00168 return false; 00169 } 00170 00171 #if defined (WIN32) 00172 typedef wchar_t host_cnv_char_t; 00173 extern char *CodePageHostToGuest(const host_cnv_char_t *s); 00174 00175 /* does the filename fit the 8.3 format? */ 00176 static bool is_filename_8by3w(const wchar_t* fname) { 00177 if (CodePageHostToGuest(fname)==NULL) return false; 00178 int i; 00179 00180 /* Is the first part 8 chars or less? */ 00181 i=0; 00182 while (*fname != 0 && *fname != L'.') { 00183 if (*fname<=32||*fname==127||*fname==L'"'||*fname==L'+'||*fname==L'='||*fname==L','||*fname==L';'||*fname==L':'||*fname==L'<'||*fname==L'>'||*fname==L'|'||*fname==L'?'||*fname==L'*') return false; 00184 if (IS_PC98_ARCH && (*fname & 0xFF00u) != 0u && (*fname & 0xFCu) != 0x08u) i++; 00185 fname++; i++; 00186 } 00187 if (i > 8) return false; 00188 00189 if (*fname == L'.') fname++; 00190 00191 /* Is the second part 3 chars or less? A second '.' also makes it a LFN */ 00192 i=0; 00193 while (*fname != 0 && *fname != L'.') { 00194 if (*fname<=32||*fname==127||*fname==L'"'||*fname==L'+'||*fname==L'='||*fname==L','||*fname==L';'||*fname==L':'||*fname==L'<'||*fname==L'>'||*fname==L'|'||*fname==L'?'||*fname==L'*') return false; 00195 if (IS_PC98_ARCH && (*fname & 0xFF00u) != 0u && (*fname & 0xFCu) != 0x08u) i++; 00196 fname++; i++; 00197 } 00198 if (i > 3) return false; 00199 00200 /* if there is anything beyond this point, it's an LFN */ 00201 if (*fname != 0) return false; 00202 00203 return true; 00204 } 00205 00206 dir_information* open_directoryw(const wchar_t* dirname) { 00207 if (dirname == NULL) return NULL; 00208 00209 size_t len = wcslen(dirname); 00210 if (len == 0) return NULL; 00211 00212 static dir_information dir; 00213 00214 wcsncpy(dir.wbase_path(),dirname,MAX_PATH); 00215 00216 if (dirname[len-1] == '\\') wcscat(dir.wbase_path(),L"*.*"); 00217 else wcscat(dir.wbase_path(),L"\\*.*"); 00218 00219 dir.wide = true; 00220 dir.handle = INVALID_HANDLE_VALUE; 00221 00222 return (_waccess(dirname,0) ? NULL : &dir); 00223 } 00224 00225 dir_information* open_directory(const char* dirname) { 00226 if (dirname == NULL) return NULL; 00227 00228 size_t len = strlen(dirname); 00229 if (len == 0) return NULL; 00230 00231 static dir_information dir; 00232 00233 safe_strncpy(dir.base_path,dirname,MAX_PATH); 00234 00235 if (dirname[len-1] == '\\') strcat(dir.base_path,"*.*"); 00236 else strcat(dir.base_path,"\\*.*"); 00237 00238 dir.wide = false; 00239 dir.handle = INVALID_HANDLE_VALUE; 00240 00241 return (_access(dirname,0) ? NULL : &dir); 00242 } 00243 00244 bool read_directory_firstw(dir_information* dirp, wchar_t* entry_name, wchar_t* entry_sname, bool& is_directory) { 00245 if (!dirp->wide) return false; 00246 00247 // TODO: offer a config.h option to opt out of Windows widechar functions 00248 dirp->handle = FindFirstFileW(dirp->wbase_path(), &dirp->search_data.w); 00249 if (INVALID_HANDLE_VALUE == dirp->handle) { 00250 return false; 00251 } 00252 00253 // TODO: offer a config.h option to opt out of Windows widechar functions 00254 wcsncpy(entry_name,dirp->search_data.w.cFileName,(MAX_PATH<CROSS_LEN)?MAX_PATH:CROSS_LEN); 00255 if (dirp->search_data.w.cAlternateFileName[0] != 0 && is_filename_8by3w(dirp->search_data.w.cFileName)) 00256 wcsncpy(entry_sname,dirp->search_data.w.cFileName,13); 00257 else 00258 wcsncpy(entry_sname,dirp->search_data.w.cAlternateFileName,13); 00259 00260 if (dirp->search_data.w.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) is_directory = true; 00261 else is_directory = false; 00262 00263 return true; 00264 } 00265 00266 bool read_directory_nextw(dir_information* dirp, wchar_t* entry_name, wchar_t* entry_sname, bool& is_directory) { 00267 if (!dirp->wide) return false; 00268 00269 // TODO: offer a config.h option to opt out of Windows widechar functions 00270 int result = FindNextFileW(dirp->handle, &dirp->search_data.w); 00271 if (result==0) return false; 00272 00273 // TODO: offer a config.h option to opt out of Windows widechar functions 00274 wcsncpy(entry_name,dirp->search_data.w.cFileName,(MAX_PATH<CROSS_LEN)?MAX_PATH:CROSS_LEN); 00275 if (dirp->search_data.w.cAlternateFileName[0] != 0 && is_filename_8by3w(dirp->search_data.w.cFileName)) 00276 wcsncpy(entry_sname,dirp->search_data.w.cFileName,13); 00277 else 00278 wcsncpy(entry_sname,dirp->search_data.w.cAlternateFileName,13); 00279 00280 if (dirp->search_data.w.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) is_directory = true; 00281 else is_directory = false; 00282 00283 return true; 00284 } 00285 00286 bool read_directory_first(dir_information* dirp, char* entry_name, char* entry_sname, bool& is_directory) { 00287 if (!dirp) return false; 00288 if (dirp->wide) return false; 00289 00290 // TODO: offer a config.h option to opt out of Windows widechar functions 00291 dirp->handle = FindFirstFileA(dirp->base_path, &dirp->search_data.a); 00292 if (INVALID_HANDLE_VALUE == dirp->handle) { 00293 return false; 00294 } 00295 00296 // TODO: offer a config.h option to opt out of Windows widechar functions 00297 safe_strncpy(entry_name,dirp->search_data.a.cFileName,(MAX_PATH<CROSS_LEN)?MAX_PATH:CROSS_LEN); 00298 safe_strncpy(entry_sname,dirp->search_data.a.cAlternateFileName,13); 00299 00300 if (dirp->search_data.a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) is_directory = true; 00301 else is_directory = false; 00302 00303 return true; 00304 } 00305 00306 bool read_directory_next(dir_information* dirp, char* entry_name, char* entry_sname, bool& is_directory) { 00307 if (!dirp) return false; 00308 if (dirp->wide) return false; 00309 00310 // TODO: offer a config.h option to opt out of Windows widechar functions 00311 int result = FindNextFileA(dirp->handle, &dirp->search_data.a); 00312 if (result==0) return false; 00313 00314 // TODO: offer a config.h option to opt out of Windows widechar functions 00315 safe_strncpy(entry_name,dirp->search_data.a.cFileName,(MAX_PATH<CROSS_LEN)?MAX_PATH:CROSS_LEN); 00316 safe_strncpy(entry_sname,dirp->search_data.a.cAlternateFileName,13); 00317 00318 if (dirp->search_data.a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) is_directory = true; 00319 else is_directory = false; 00320 00321 return true; 00322 } 00323 00324 void close_directory(dir_information* dirp) { 00325 if (dirp && dirp->handle != INVALID_HANDLE_VALUE) { 00326 FindClose(dirp->handle); 00327 dirp->handle = INVALID_HANDLE_VALUE; 00328 } 00329 } 00330 00331 #else 00332 00333 dir_information* open_directory(const char* dirname) { 00334 static dir_information dir; 00335 dir.dir=opendir(dirname); 00336 safe_strncpy(dir.base_path,dirname,CROSS_LEN); 00337 return dir.dir?&dir:NULL; 00338 } 00339 00340 bool read_directory_first(dir_information* dirp, char* entry_name, char* entry_sname, bool& is_directory) { 00341 if (!dirp) return false; 00342 struct dirent* dentry = readdir(dirp->dir); 00343 if (dentry==NULL) { 00344 return false; 00345 } 00346 00347 // safe_strncpy(entry_name,dentry->d_name,(FILENAME_MAX<MAX_PATH)?FILENAME_MAX:MAX_PATH); // [include stdio.h], maybe pathconf() 00348 safe_strncpy(entry_name,dentry->d_name,CROSS_LEN); 00349 entry_sname[0]=0; 00350 00351 #ifdef DIRENT_HAS_D_TYPE 00352 if(dentry->d_type == DT_DIR) { 00353 is_directory = true; 00354 return true; 00355 } else if(dentry->d_type == DT_REG) { 00356 is_directory = false; 00357 return true; 00358 } 00359 #endif 00360 00361 // probably use d_type here instead of a full stat() 00362 static char buffer[2*CROSS_LEN] = { 0 }; 00363 buffer[0] = 0; 00364 strcpy(buffer,dirp->base_path); 00365 strcat(buffer,entry_name); 00366 struct stat status; 00367 if (stat(buffer,&status)==0) is_directory = (S_ISDIR(status.st_mode)>0); 00368 else is_directory = false; 00369 00370 return true; 00371 } 00372 00373 bool read_directory_next(dir_information* dirp, char* entry_name, char* entry_sname, bool& is_directory) { 00374 if (!dirp) return false; 00375 struct dirent* dentry = readdir(dirp->dir); 00376 if (dentry==NULL) { 00377 return false; 00378 } 00379 00380 // safe_strncpy(entry_name,dentry->d_name,(FILENAME_MAX<MAX_PATH)?FILENAME_MAX:MAX_PATH); // [include stdio.h], maybe pathconf() 00381 safe_strncpy(entry_name,dentry->d_name,CROSS_LEN); 00382 entry_sname[0]=0; 00383 00384 00385 #ifdef DIRENT_HAS_D_TYPE 00386 if(dentry->d_type == DT_DIR) { 00387 is_directory = true; 00388 return true; 00389 } else if(dentry->d_type == DT_REG) { 00390 is_directory = false; 00391 return true; 00392 } 00393 #endif 00394 00395 // probably use d_type here instead of a full stat() 00396 static char buffer[2*CROSS_LEN] = { 0 }; 00397 buffer[0] = 0; 00398 strcpy(buffer,dirp->base_path); 00399 strcat(buffer,entry_name); 00400 struct stat status; 00401 00402 if (stat(buffer,&status)==0) is_directory = (S_ISDIR(status.st_mode)>0); 00403 else is_directory = false; 00404 00405 return true; 00406 } 00407 00408 void close_directory(dir_information* dirp) { 00409 if (dirp) closedir(dirp->dir); 00410 } 00411 00412 #endif 00413 00414 FILE *fopen_wrap(const char *path, const char *mode) { 00415 #if !defined(WIN32) && !defined(OS2) && !defined(MACOSX) && defined(HAVE_REALPATH) 00416 char work[CROSS_LEN] = {0}; 00417 strncpy(work,path,CROSS_LEN-1); 00418 char* last = strrchr(work,'/'); 00419 00420 if (last) { 00421 if (last != work) { 00422 *last = 0; 00423 //If this compare fails, then we are dealing with files in / 00424 //Which is outside the scope, but test anyway. 00425 //However as realpath only works for exising files. The testing is 00426 //in that case not done against new files. 00427 } 00428 char* check = realpath(work,NULL); 00429 if (check) { 00430 if ( ( strlen(check) == 5 && strcmp(check,"/proc") == 0) || strncmp(check,"/proc/",6) == 0) { 00431 // LOG_MSG("lst hit %s blocking!",path); 00432 free(check); 00433 return NULL; 00434 } 00435 free(check); 00436 } 00437 } 00438 00439 #if 0 00440 //Lightweight version, but then existing files can still be read, which is not ideal 00441 if (strpbrk(mode,"aw+") != NULL) { 00442 LOG_MSG("pbrk ok"); 00443 char* check = realpath(path,NULL); 00444 //Will be null if file doesn't exist.... ENOENT 00445 //TODO What about unlink /proc/self/mem and then create it ? 00446 //Should be safe for what we want.. 00447 if (check) { 00448 if (strncmp(check,"/proc/",6) == 0) { 00449 free(check); 00450 return NULL; 00451 } 00452 free(check); 00453 } 00454 } 00455 */ 00456 #endif //0 00457 #endif 00458 00459 return fopen(path,mode); 00460 }