/** * Create a valid path name from a possibly invalid name by erasing invalid * path parts at the end of the string. If string doesn't contain any path * component, it will be pointed to the root directory. Empty string will * be left as-is to prevent overwriting past allocated area. */ void File_MakeValidPathName(char *pPathName) { struct stat dirstat; char *pLastSlash; do { /* Check for a valid path */ if (stat(pPathName, &dirstat) == 0 && S_ISDIR(dirstat.st_mode)) { break; } pLastSlash = strrchr(pPathName, PATHSEP); if (pLastSlash) { /* Erase the (probably invalid) part after the last slash */ *pLastSlash = 0; } else { if (pPathName[0]) { /* point to root */ pPathName[0] = PATHSEP; pPathName[1] = 0; } return; } } while (pLastSlash); /* Make sure that path name ends with a slash */ File_AddSlashToEndFileName(pPathName); }
DlgFileSelect::DlgFileSelect(SGOBJ *dlg, char *new_path_and_name, bool bAllowNew) : Dialog(dlg), path_and_name(new_path_and_name) , reloaddir(true), refreshentries(true), selection(-1), ypos(0), eol(true) , pressed_ok(false), redraw(true) { gui_file_list = NULL; fsdlg[SGFSDLG_FILENAME].type = bAllowNew ? SGEDITFIELD : SGTEXT; /* Prepare the path and filename variables */ File_splitpath(path_and_name, file_path, file_fname, NULL); if (strlen(file_path) == 0) { assert(getcwd(file_path, sizeof(file_path)) != NULL); File_AddSlashToEndFileName(file_path); } File_ShrinkName(dlgpath, file_path, sizeof(dlgpath)-1); File_ShrinkName(dlgfname, file_fname, sizeof(dlgfname)-1); }
/** * Show and process a file selection dialog. * Returns path/name user selected or NULL if user canceled * input: zip_path = pointer's pointer to buffer to contain file path * within a selected zip file, or NULL if browsing zip files is disallowed. * bAllowNew: true if the user is allowed to insert new file names. */ char* SDLGui_FileSelect(const char *path_and_name, char **zip_path, bool bAllowNew) { struct dirent **files = NULL; char *pStringMem; char *retpath = NULL; const char *home; char *path, *fname; /* The actual file and path names */ bool reloaddir = true; /* Do we have to reload the directory file list? */ int retbut; bool bOldMouseVisibility; int selection; /* The selection index */ char *zipfilename; /* Filename in zip file */ char *zipdir; bool browsingzip = false; /* Are we browsing an archive? */ zip_dir *zipfiles = NULL; SDL_Event sdlEvent; int yScrolbar_size; /* Size of the vertical scrollbar */ ypos = 0; scrollbar_Ypos = 0.0; refreshentries = true; entries = 0; /* Allocate memory for the file and path name strings: */ pStringMem = malloc(4 * FILENAME_MAX); path = pStringMem; fname = pStringMem + FILENAME_MAX; zipdir = pStringMem + 2 * FILENAME_MAX; zipfilename = pStringMem + 3 * FILENAME_MAX; zipfilename[0] = 0; fname[0] = 0; path[0] = 0; /* Save mouse state and enable cursor */ bOldMouseVisibility = SDL_ShowCursor(SDL_QUERY); SDL_ShowCursor(SDL_ENABLE); SDLGui_CenterDlg(fsdlg); if (bAllowNew) { fsdlg[SGFSDLG_FILENAME].type = SGEDITFIELD; fsdlg[SGFSDLG_FILENAME].flags |= SG_EXIT; } else { fsdlg[SGFSDLG_FILENAME].type = SGTEXT; fsdlg[SGFSDLG_FILENAME].flags &= ~SG_EXIT; } /* Prepare the path and filename variables */ if (path_and_name && path_and_name[0]) { strncpy(path, path_and_name, FILENAME_MAX); path[FILENAME_MAX-1] = '\0'; } if (!File_DirExists(path)) { File_SplitPath(path, path, fname, NULL); if (!(File_DirExists(path) || getcwd(path, FILENAME_MAX))) { perror("SDLGui_FileSelect: non-existing path and CWD failed"); goto clean_exit; } } File_MakeAbsoluteName(path); File_MakeValidPathName(path); File_ShrinkName(dlgpath, path, DLGPATH_SIZE); File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE); do { if (reloaddir) { files = files_free(files); if (browsingzip) { files = ZIP_GetFilesDir(zipfiles, zipdir, &entries); if(!files) { fprintf(stderr, "SDLGui_FileSelect: ZIP_GetFilesDir error!\n"); goto clean_exit; } } else { /* Load directory entries: */ entries = scandir(path, &files, 0, alphasort); } /* Remove hidden files from the list if necessary: */ if (!(fsdlg[SGFSDLG_SHOWHIDDEN].state & SG_SELECTED)) { DlgFileSelect_RemoveHiddenFiles(files); } if (entries < 0) { fprintf(stderr, "SDLGui_FileSelect: Path not found.\n"); goto clean_exit; } /* reload always implies refresh */ reloaddir = false; refreshentries = true; }/* reloaddir */ /* Refresh scrollbar size */ if (entries <= SGFS_NUMENTRIES) yScrolbar_size = (SGFS_NUMENTRIES-2) * sdlgui_fontheight; else yScrolbar_size = (int)((SGFS_NUMENTRIES-2) / ((float)entries/(float)SGFS_NUMENTRIES) * sdlgui_fontheight); fsdlg[SGFSDLG_SCROLLBAR].w = yScrolbar_size; /* Refresh scrolbar pos */ fsdlg[SGFSDLG_SCROLLBAR].h = (int) (scrollbar_Ypos * sdlgui_fontheight); ypos = (int) (scrollbar_Ypos * ((float)entries/(float)(SGFS_NUMENTRIES-2)) + 0.5); /* Update the file name strings in the dialog? */ if (refreshentries) { if (!DlgFileSelect_RefreshEntries(files, path, browsingzip)) { goto clean_exit; } refreshentries = false; } /* Show dialog: */ retbut = SDLGui_DoDialog(fsdlg, &sdlEvent); /* Has the user clicked on a file or folder? */ if (retbut>=SGFSDLG_ENTRYFIRST && retbut<=SGFSDLG_ENTRYLAST && retbut-SGFSDLG_ENTRYFIRST+ypos<entries) { char *tempstr; tempstr = malloc(FILENAME_MAX); if (!tempstr) { perror("Error while allocating temporary memory in SDLGui_FileSelect()"); goto clean_exit; } if (browsingzip == true) { if (!strcat_maxlen(tempstr, FILENAME_MAX, zipdir, files[retbut-SGFSDLG_ENTRYFIRST+ypos]->d_name)) { fprintf(stderr, "SDLGui_FileSelect: Path name too long!\n"); goto clean_exit; } /* directory? */ if (File_DoesFileNameEndWithSlash(tempstr)) { /* handle the ../ directory */ if (strcmp(files[retbut-SGFSDLG_ENTRYFIRST+ypos]->d_name, "../") == 0) { /* close the zip file */ if (strcmp(tempstr, "../") == 0) { /* free zip file entries */ ZIP_FreeZipDir(zipfiles); zipfiles = NULL; /* Copy the path name to the dialog */ File_ShrinkName(dlgpath, path, DLGPATH_SIZE); browsingzip = false; } else { /* remove "../" and previous dir from path */ File_PathShorten(tempstr, 2); correct_zip_root(tempstr); strcpy(zipdir, tempstr); File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE); } } else /* not the "../" directory */ { strcpy(zipdir, tempstr); File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE); } reloaddir = true; /* Copy the path name to the dialog */ zipfilename[0] = '\0'; dlgfname[0] = 0; ypos = 0; scrollbar_Ypos = 0.0; } else { /* not dir, select a file in the zip */ selection = retbut-SGFSDLG_ENTRYFIRST+ypos; strcpy(zipfilename, files[selection]->d_name); File_ShrinkName(dlgfname, zipfilename, DLGFNAME_SIZE); } } else /* not browsingzip */ { if (!strcat_maxlen(tempstr, FILENAME_MAX, path, files[retbut-SGFSDLG_ENTRYFIRST+ypos]->d_name)) { fprintf(stderr, "SDLGui_FileSelect: Path name too long!\n"); goto clean_exit; } if (File_DirExists(tempstr)) { File_HandleDotDirs(tempstr); File_AddSlashToEndFileName(tempstr); /* Copy the path name to the dialog */ File_ShrinkName(dlgpath, tempstr, DLGPATH_SIZE); strcpy(path, tempstr); reloaddir = true; dlgfname[0] = 0; ypos = 0; scrollbar_Ypos = 0.0; } else if (ZIP_FileNameIsZIP(tempstr) && zip_path != NULL) { /* open a zip file */ zipfiles = ZIP_GetFiles(tempstr); if (zipfiles != NULL && browsingzip == false) { selection = retbut-SGFSDLG_ENTRYFIRST+ypos; strcpy(fname, files[selection]->d_name); File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE); browsingzip = true; zipdir[0] = '\0'; /* zip root */ File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE); reloaddir = true; ypos = 0; scrollbar_Ypos = 0.0; } } else { /* Select a file */ selection = retbut-SGFSDLG_ENTRYFIRST+ypos; strcpy(fname, files[selection]->d_name); File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE); } } /* not browsingzip */ free(tempstr); } else /* Has the user clicked on another button? */ { switch(retbut) { case SGFSDLG_UPDIR: /* Change path to parent directory */ if (browsingzip) { /* close the zip file? */ if (!zipdir[0]) { /* free zip file entries */ ZIP_FreeZipDir(zipfiles); browsingzip = false; zipfiles = NULL; File_ShrinkName(dlgpath, path, DLGPATH_SIZE); } else { /* remove last dir from zipdir path */ File_PathShorten(zipdir, 1); correct_zip_root(zipdir); File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE); zipfilename[0] = '\0'; } } /* not a zip file: */ else { File_PathShorten(path, 1); File_ShrinkName(dlgpath, path, DLGPATH_SIZE); } reloaddir = true; break; case SGFSDLG_HOMEDIR: /* Change to home directory */ home = Paths_GetUserHome(); if (home == NULL || !*home) break; if (browsingzip) { /* free zip file entries */ ZIP_FreeZipDir(zipfiles); zipfiles = NULL; browsingzip = false; } strcpy(path, home); File_AddSlashToEndFileName(path); File_ShrinkName(dlgpath, path, DLGPATH_SIZE); reloaddir = true; break; case SGFSDLG_ROOTDIR: /* Change to root directory */ if (browsingzip) { /* free zip file entries */ ZIP_FreeZipDir(zipfiles); zipfiles = NULL; browsingzip = false; } path[0] = PATHSEP; path[1] = '\0'; strcpy(dlgpath, path); reloaddir = true; break; case SGFSDLG_UP: /* Scroll up */ DlgFileSelect_ScrollUp(); SDL_Delay(10); break; case SGFSDLG_DOWN: /* Scroll down */ DlgFileSelect_ScrollDown(); SDL_Delay(10); break; case SGFSDLG_SCROLLBAR: /* Scrollbar selected */ DlgFileSelect_ManageScrollbar(); SDL_Delay(10); break; case SGFSDLG_FILENAME: /* User entered new filename */ strcpy(fname, dlgfname); break; case SGFSDLG_SHOWHIDDEN: /* Show/hide hidden files */ reloaddir = true; ypos = 0; scrollbar_Ypos = 0.0; break; case SDLGUI_UNKNOWNEVENT: DlgFileSelect_HandleSdlEvents(&sdlEvent); break; } /* switch */ if (reloaddir) { /* Remove old selection */ fname[0] = 0; dlgfname[0] = 0; ypos = 0; scrollbar_Ypos = 0.0; } } /* other button code */ } /* do */ while (retbut!=SGFSDLG_OKAY && retbut!=SGFSDLG_CANCEL && retbut!=SDLGUI_QUIT && retbut != SDLGUI_ERROR && !bQuitProgram); files_free(files); if (browsingzip) { /* free zip file entries */ ZIP_FreeZipDir(zipfiles); zipfiles = NULL; } if (retbut == SGFSDLG_OKAY) { if (zip_path) *zip_path = zip_get_path(zipdir, zipfilename, browsingzip); retpath = File_MakePath(path, fname, NULL); } else retpath = NULL; clean_exit: SDL_ShowCursor(bOldMouseVisibility); free(pStringMem); return retpath; }
int DlgFileSelect::processDialog(void) { int retval = Dialog::GUI_CONTINUE; switch (return_obj) { case SGFSDLG_UPDIR: /* Change path to parent directory */ if (strlen(file_path) > 2) { File_CleanFileName(file_path); char *ptr = strrchr(file_path, '/'); if (ptr) *(ptr + 1) = 0; File_AddSlashToEndFileName(file_path); reloaddir = true; /* Copy the path name to the dialog */ File_ShrinkName(dlgpath, file_path, sizeof(dlgpath)-1); /* Remove old selection */ selection = -1; file_fname[0] = 0; dlgfname[0] = 0; ypos = 0; } break; case SGFSDLG_ROOTDIR: /* Change to root directory */ strcpy(file_path, "/"); reloaddir = true; strcpy(dlgpath, file_path); /* Remove old selection */ selection = -1; file_fname[0] = 0; dlgfname[0] = 0; ypos = 0; break; case SGFSDLG_UP: /* Scroll up */ if (ypos > 0) { --ypos; refreshentries = true; } break; case SGFSDLG_DOWN: /* Scroll down */ if (eol == false) { ++ypos; refreshentries = true; } break; case SGFSDLG_OKAY: pressed_ok = true; confirm(); // passthrough case SGFSDLG_CANCEL: retval = Dialog::GUI_CLOSE; break; default: break; } /* Has the user clicked on a file or folder? */ if ((return_obj >= SGFSDLG_FIRSTENTRY) && (return_obj <= SGFSDLG_LASTENTRY)) { char tempstr[MAX_FILENAME_LENGTH]; struct stat filestat; struct listentry *temp = gui_file_list; int i; strcpy(tempstr, file_path); for (i = 0; i < ((return_obj - SGFSDLG_FIRSTENTRY) + ypos); i++) temp = temp->next; strcat(tempstr, temp->filename); if (stat(tempstr, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { /* Set the new directory */ strcpy(file_path, tempstr); if (strlen(file_path) >= 3) { if ((file_path[strlen(file_path) - 2] == '/') && (file_path[strlen(file_path) - 1] == '.')) { /* Strip a single dot at the end of the path name */ file_path[strlen(file_path) - 2] = 0; } if ((file_path[strlen(file_path) - 3] == '/') && (file_path[strlen(file_path) - 2] == '.') && (file_path[strlen(file_path) - 1] == '.')) { /* Handle the ".." folder */ char *ptr; if (strlen(file_path) == 3) { file_path[1] = 0; } else { file_path[strlen(file_path) - 3] = 0; ptr = strrchr(file_path, '/'); if (ptr) *(ptr + 1) = 0; } } } File_AddSlashToEndFileName(file_path); reloaddir = true; /* Copy the path name to the dialog */ File_ShrinkName(dlgpath, file_path, sizeof(dlgpath)-1); selection = -1; /* Remove old selection */ // gui_file_fname[0] = 0; dlgfname[0] = 0; ypos = 0; } else { /* Select a file */ selection = return_obj - SGFSDLG_FIRSTENTRY + ypos; strcpy(file_fname, temp->filename); File_ShrinkName(dlgfname, file_fname, sizeof(dlgfname)-1); } } if (reloaddir || refreshentries) { refreshEntries(); } return retval; }
/** * Create a clean absolute file name from a (possibly) relative file name. * I.e. filter out all occurancies of "./" and "../". * pFileName needs to point to a buffer of at least FILENAME_MAX bytes. */ void File_MakeAbsoluteName(char *pFileName) { char *pTempName; int inpos, outpos; #if defined (__AMIGAOS4__) /* This function does not work on Amiga OS */ return; #endif inpos = 0; pTempName = malloc(FILENAME_MAX); if (!pTempName) { perror("File_MakeAbsoluteName - malloc"); return; } /* Is it already an absolute name? */ if (File_IsRootFileName(pFileName)) { outpos = 0; } else { if (!getcwd(pTempName, FILENAME_MAX)) { perror("File_MakeAbsoluteName - getcwd"); free(pTempName); return; } File_AddSlashToEndFileName(pTempName); outpos = strlen(pTempName); } /* Now filter out the relative paths "./" and "../" */ while (pFileName[inpos] != 0 && outpos < FILENAME_MAX) { if (pFileName[inpos] == '.' && pFileName[inpos+1] == PATHSEP) { /* Ignore "./" */ inpos += 2; } else if (pFileName[inpos] == '.' && pFileName[inpos+1] == 0) { inpos += 1; /* Ignore "." at the end of the path string */ if (outpos > 1) pTempName[outpos - 1] = 0; /* Remove the last slash, too */ } else if (pFileName[inpos] == '.' && pFileName[inpos+1] == '.' && (pFileName[inpos+2] == PATHSEP || pFileName[inpos+2] == 0)) { /* Handle "../" */ char *pSlashPos; inpos += 2; pTempName[outpos - 1] = 0; pSlashPos = strrchr(pTempName, PATHSEP); if (pSlashPos) { *(pSlashPos + 1) = 0; outpos = strlen(pTempName); } else { pTempName[0] = PATHSEP; outpos = 1; } /* Were we already at the end of the string or is there more to come? */ if (pFileName[inpos] == PATHSEP) { /* There was a slash after the '..', so skip slash and * simply proceed with next part */ inpos += 1; } else { /* We were at the end of the string, so let's remove the slash * from the new string, too */ if (outpos > 1) pTempName[outpos - 1] = 0; } } else { /* Copy until next slash or end of input string */ while (pFileName[inpos] != 0 && outpos < FILENAME_MAX) { pTempName[outpos++] = pFileName[inpos++]; if (pFileName[inpos - 1] == PATHSEP) break; } } } pTempName[outpos] = 0; strcpy(pFileName, pTempName); /* Copy back */ free(pTempName); }