DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/misc/cross.cpp
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 }