/** * fill_pathname_parent_dir_name: * @out_dir : output directory * @in_dir : input directory * @size : size of output directory * * Copies only the parent directory name of @in_dir into @out_dir. * The two buffers must not overlap. Removes trailing '/'. **/ void fill_pathname_parent_dir_name(char *out_dir, const char *in_dir, size_t size) { char *temp = strdup(in_dir); char *last = find_last_slash(temp); *last = '\0'; in_dir = find_last_slash(temp) + 1; strlcpy(out_dir, in_dir, size); free(temp); }
/** * path_get_archive_delim: * @path : path * * Find delimiter of an archive file. Only the first '#' * after a compression extension is considered. * * Returns: pointer to the delimiter in the path if it contains * a path inside a compressed file, otherwise NULL. */ const char *path_get_archive_delim(const char *path) { #ifdef HAVE_COMPRESSION const char *last = find_last_slash(path); const char *delim = NULL; #ifdef HAVE_ZLIB if (last) { delim = strcasestr(last, ".zip#"); if (!delim) delim = strcasestr(last, ".apk#"); } if (delim) return delim + 4; #endif #ifdef HAVE_7ZIP if (last) delim = strcasestr(last, ".7z#"); if (delim) return delim + 3; #endif #endif return NULL; }
/** * fill_short_pathname_representation: * @out_rep : output representation * @in_path : input path * @size : size of output representation * * Generates a short representation of path. It should only * be used for displaying the result; the output representation is not * binding in any meaningful way (for a normal path, this is the same as basename) * In case of more complex URLs, this should cut everything except for * the main image file. * * E.g.: "/path/to/game.img" -> game.img * "/path/to/myarchive.7z#folder/to/game.img" -> game.img */ void fill_short_pathname_representation(char* out_rep, const char *in_path, size_t size) { char path_short[PATH_MAX_LENGTH]; #ifdef HAVE_COMPRESSION char *last_slash = NULL; #endif path_short[0] = '\0'; fill_pathname(path_short, path_basename(in_path), "", sizeof(path_short)); #ifdef HAVE_COMPRESSION last_slash = find_last_slash(path_short); if (last_slash != NULL) { /* We handle paths like: * /path/to/file.7z#mygame.img * short_name: mygame.img: * * We check whether something is actually * after the hash to avoid going over the buffer. */ retro_assert(strlen(last_slash) > 1); strlcpy(out_rep, last_slash + 1, size); } else #endif strlcpy(out_rep, path_short, size); }
void fill_pathname_base(char *out, const char *in_path, size_t size) { const char *ptr = find_last_slash(in_path); if (ptr) ptr++; else ptr = in_path; /* In case of compression, we also have to consider paths like * /path/to/archive.7z#mygame.img * and * /path/to/archive.7z#folder/mygame.img * basename would be mygame.img in both cases */ #ifdef HAVE_COMPRESSION const char *ptr_bak = ptr; ptr = strchr(ptr_bak,'#'); if (ptr) ptr++; else ptr = ptr_bak; #endif rarch_assert(strlcpy(out, ptr, size) < size); }
/** * fill_short_pathname_representation: * @out_rep : output representation * @in_path : input path * @size : size of output representation * * Generates a short representation of path. It should only * be used for displaying the result; the output representation is not * binding in any meaningful way (for a normal path, this is the same as basename) * In case of more complex URLs, this should cut everything except for * the main image file. * * E.g.: "/path/to/game.img" -> game.img * "/path/to/myarchive.7z#folder/to/game.img" -> game.img */ void fill_short_pathname_representation_wrapper(char* out_rep, const char *in_path, size_t size) { #ifdef HAVE_COMPRESSION char *path_short = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); char *last_slash = NULL; path_short[0] = '\0'; fill_pathname(path_short, path_basename(in_path), "", PATH_MAX_LENGTH * sizeof(char) ); last_slash = find_last_slash(path_short); if (last_slash != NULL) { /* We handle paths like: * /path/to/file.7z#mygame.img * short_name: mygame.img: * * We check whether something is actually * after the hash to avoid going over the buffer. */ retro_assert(strlen(last_slash) > 1); strlcpy(out_rep, last_slash + 1, size); free(path_short); return; } free(path_short); #endif fill_short_pathname_representation(out_rep, in_path, size); }
string path_dirname(const string& path) { size_t index = find_last_slash(path); if(index != string::npos) { #ifndef _WIN32 if(index == 0 && path.size() > 1) { return string(1, DIR_SEP); } #endif return path.substr(0, index); } return ""; }
/** * path_basedir: * @path : path * * Extracts base directory by mutating path. * Keeps trailing '/'. **/ void path_basedir(char *path) { char *last = NULL; if (strlen(path) < 2) return; last = find_last_slash(path); if (last) last[1] = '\0'; else snprintf(path, 3, ".%s", path_default_slash()); }
/* Assumes path is a directory. Appends a slash * if not already there. */ void fill_pathname_slash(char *path, size_t size) { size_t path_len = strlen(path); const char *last_slash = find_last_slash(path); // Try to preserve slash type. if (last_slash && (last_slash != (path + path_len - 1))) { char join_str[2] = {*last_slash}; rarch_assert(strlcat(path, join_str, size) < size); } else if (!last_slash) rarch_assert(strlcat(path, path_default_slash(), size) < size); }
/** * fill_pathname_parent_dir_name: * @out_dir : output directory * @in_dir : input directory * @size : size of output directory * * Copies only the parent directory name of @in_dir into @out_dir. * The two buffers must not overlap. Removes trailing '/'. * Returns true on success, false if a slash was not found in the path. **/ bool fill_pathname_parent_dir_name(char *out_dir, const char *in_dir, size_t size) { char *temp = strdup(in_dir); char *last = find_last_slash(temp); bool ret = false; *last = '\0'; in_dir = find_last_slash(temp); if (in_dir && in_dir + 1) { strlcpy(out_dir, in_dir + 1, size); ret = true; } else ret = false; free(temp); return ret; }
/** * fill_pathname_slash: * @path : path * @size : size of path * * Assumes path is a directory. Appends a slash * if not already there. **/ void fill_pathname_slash(char *path, size_t size) { size_t path_len = strlen(path); const char *last_slash = find_last_slash(path); /* Try to preserve slash type. */ if (last_slash && (last_slash != (path + path_len - 1))) { char join_str[2]; strlcpy(join_str, last_slash, sizeof(join_str)); retro_assert(strlcat(path, join_str, size) < size); } else if (!last_slash) retro_assert(strlcat(path, path_default_slash(), size) < size); }
/** * path_basename: * @path : path * * Get basename from @path. * * Returns: basename from path. **/ const char *path_basename(const char *path) { /* We cut either at the first compression-related hash * or the last slash; whichever comes last */ const char *last = find_last_slash(path); const char *delim = path_get_archive_delim(path); if (delim) return delim + 1; if (last) return last + 1; return path; }
const char *path_basename(const char *path) { const char *last = find_last_slash(path); /* We cut either at the last hash or the last slash; whichever comes last */ #ifdef HAVE_COMPRESSION const char *last_hash = strchr(path,'#'); if (last_hash > last) { return last_hash + 1; } #endif if (last) return last + 1; return path; }
void path_basedir(char *path) { char *last = NULL; if (strlen(path) < 2) return; #ifdef HAVE_COMPRESSION /* We want to find the directory with the zipfile in basedir. */ last = strchr(path,'#'); if (last) *last = '\0'; #endif last = find_last_slash(path); if (last) last[1] = '\0'; else snprintf(path, 3, ".%s", path_default_slash()); }
/** * path_basedir: * @path : path * * Extracts base directory by mutating path. * Keeps trailing '/'. **/ void path_basedir_wrapper(char *path) { char *last = NULL; if (strlen(path) < 2) return; #ifdef HAVE_COMPRESSION /* We want to find the directory with the archive in basedir. */ last = (char*)path_get_archive_delim(path); if (last) *last = '\0'; #endif last = find_last_slash(path); if (last) last[1] = '\0'; else snprintf(path, 3, ".%s", path_default_slash()); }
string path_filename(const string& path) { size_t index = find_last_slash(path); if(index != string::npos) { /* Corner cases to match boost behavior. */ #ifndef _WIN32 if(index == 0 && path.size() == 1) { return path; } #endif if(index == path.size() - 1) { #ifdef _WIN32 if(index == 2) { return string(1, DIR_SEP); } #endif return "."; } return path.substr(index + 1, path.size() - index - 1); } return path; }
/** * path_get_archive_delim: * @path : path * * Find delimiter of an archive file. Only the first '#' * after a compression extension is considered. * * Returns: pointer to the delimiter in the path if it contains * a path inside a compressed file, otherwise NULL. */ const char *path_get_archive_delim(const char *path) { const char *last = find_last_slash(path); const char *delim = NULL; if (last) { delim = strcasestr(last, ".zip#"); if (!delim) delim = strcasestr(last, ".apk#"); } if (delim) return delim + 4; if (last) delim = strcasestr(last, ".7z#"); if (delim) return delim + 3; return NULL; }
/* Updates path for specified thumbnail identifier (right, left). * Must be called after: * - menu_thumbnail_set_system() * - menu_thumbnail_set_content*() * ...and before: * - menu_thumbnail_get_path() * Returns true if generated path is valid */ bool menu_thumbnail_update_path(menu_thumbnail_path_data_t *path_data, enum menu_thumbnail_id thumbnail_id) { settings_t *settings = config_get_ptr(); const char *type = menu_thumbnail_get_type(thumbnail_id); const char *system_name = NULL; char *thumbnail_path = NULL; char content_dir[PATH_MAX_LENGTH]; if (!path_data) return false; /* Determine which path we are updating... */ switch (thumbnail_id) { case MENU_THUMBNAIL_RIGHT: thumbnail_path = path_data->right_path; break; case MENU_THUMBNAIL_LEFT: thumbnail_path = path_data->left_path; break; default: return false; } thumbnail_path[0] = '\0'; /* Sundry error checking */ if (!settings) return false; if (string_is_empty(settings->paths.directory_thumbnails)) return false; if (!menu_thumbnail_is_enabled(thumbnail_id)) return false; /* Generate new path */ /* > Check path_data for empty strings */ if (string_is_empty(path_data->content_path) || string_is_empty(path_data->content_img) || (string_is_empty(path_data->system) && string_is_empty(path_data->content_db_name))) return false; /* > Get current system */ if (string_is_empty(path_data->content_db_name)) { /* If this is a content history or favorites playlist * then the current 'path_data->system' string is * meaningless. In this case, we fall back to the * content directory name */ if (string_is_equal(path_data->system, "history") || string_is_equal(path_data->system, "favorites")) { char tmp_buf[PATH_MAX_LENGTH] = {0}; const char *last_slash = find_last_slash(path_data->content_path); content_dir[0] = '\0'; system_name = content_dir; if (last_slash) { size_t path_length = last_slash + 1 - path_data->content_path; if ((path_length > 1) && (path_length < PATH_MAX_LENGTH)) { strlcpy(tmp_buf, path_data->content_path, path_length * sizeof(char)); strlcpy(content_dir, path_basename(tmp_buf), sizeof(content_dir)); } } if (string_is_empty(system_name)) return false; } else system_name = path_data->system; } else system_name = path_data->content_db_name; /* > Special case: thumbnail for imageviewer content * is the image file itself */ if (string_is_equal(system_name, "images_history") || string_is_equal(path_data->content_core_name, "imageviewer")) { /* imageviewer content is identical for left and right thumbnails */ if (path_is_media_type(path_data->content_path) == RARCH_CONTENT_IMAGE) strlcpy(thumbnail_path, path_data->content_path, PATH_MAX_LENGTH * sizeof(char)); } else { char tmp_buf[PATH_MAX_LENGTH]; tmp_buf[0] = '\0'; /* > Normal content: assemble path */ /* >> Base + system name */ fill_pathname_join(thumbnail_path, settings->paths.directory_thumbnails, system_name, PATH_MAX_LENGTH * sizeof(char)); /* >> Add type */ fill_pathname_join(tmp_buf, thumbnail_path, type, sizeof(tmp_buf)); /* >> Add content image */ thumbnail_path[0] = '\0'; fill_pathname_join(thumbnail_path, tmp_buf, path_data->content_img, PATH_MAX_LENGTH * sizeof(char)); } /* Final error check - is cached path empty? */ if (string_is_empty(thumbnail_path)) return false; return true; }
static void task_database_handler(retro_task_t *task) { const char *name = NULL; database_info_handle_t *dbinfo = NULL; database_state_handle_t *dbstate = NULL; db_handle_t *db = NULL; if (!task) goto task_finished; db = (db_handle_t*)task->state; if (!db) goto task_finished; if (!db->scan_started) { db->scan_started = true; if (!string_is_empty(db->fullpath)) { if (db->is_directory) db->handle = database_info_dir_init(db->fullpath, DATABASE_TYPE_ITERATE, task, db->show_hidden_files); else db->handle = database_info_file_init(db->fullpath, DATABASE_TYPE_ITERATE, task); } task_free_title(task); if (db->handle) db->handle->status = DATABASE_STATUS_ITERATE_BEGIN; } dbinfo = db->handle; dbstate = &db->state; if (!dbinfo || task_get_cancelled(task)) goto task_finished; switch (dbinfo->status) { case DATABASE_STATUS_ITERATE_BEGIN: if (dbstate && !dbstate->list) { if (!string_is_empty(db->content_database_path)) dbstate->list = dir_list_new( db->content_database_path, "rdb", false, db->show_hidden_files, false, false); /* If the scan path matches a database path exactly then * save time by only processing that database. */ if (dbstate->list && db->is_directory) { size_t i; char *dirname = NULL; if (!string_is_empty(db->fullpath)) dirname = find_last_slash(db->fullpath) + 1; if (!string_is_empty(dirname)) { for (i = 0; i < dbstate->list->size; i++) { const char *data = dbstate->list->elems[i].data; char *dbname = NULL; bool strmatch = false; char *dbpath = strdup(data); path_remove_extension(dbpath); dbname = find_last_slash(dbpath) + 1; strmatch = strcasecmp(dbname, dirname) == 0; free(dbpath); if (strmatch) { struct string_list *single_list = string_list_new(); string_list_append(single_list, data, dbstate->list->elems[i].attr); dir_list_free(dbstate->list); dbstate->list = single_list; break; } } } } } dbinfo->status = DATABASE_STATUS_ITERATE_START; break; case DATABASE_STATUS_ITERATE_START: name = database_info_get_current_element_name(dbinfo); task_database_cleanup_state(dbstate); dbstate->list_index = 0; dbstate->entry_index = 0; task_database_iterate_start(dbinfo, name); break; case DATABASE_STATUS_ITERATE: if (task_database_iterate(db, dbstate, dbinfo) == 0) { dbinfo->status = DATABASE_STATUS_ITERATE_NEXT; dbinfo->type = DATABASE_TYPE_ITERATE; } break; case DATABASE_STATUS_ITERATE_NEXT: if (task_database_iterate_next(dbinfo) == 0) { dbinfo->status = DATABASE_STATUS_ITERATE_START; dbinfo->type = DATABASE_TYPE_ITERATE; } else { const char *msg = NULL; if (db->is_directory) msg = msg_hash_to_str(MSG_SCANNING_OF_DIRECTORY_FINISHED); else msg = msg_hash_to_str(MSG_SCANNING_OF_FILE_FINISHED); #ifdef RARCH_INTERNAL runloop_msg_queue_push(msg, 0, 180, true); #else fprintf(stderr, "msg: %s\n", msg); #endif ui_companion_driver_notify_refresh(); goto task_finished; } break; default: case DATABASE_STATUS_FREE: case DATABASE_STATUS_NONE: goto task_finished; } return; task_finished: if (task) task_set_finished(task, true); if (dbstate) { if (dbstate->list) dir_list_free(dbstate->list); } if (db) { if (!string_is_empty(db->playlist_directory)) free(db->playlist_directory); if (!string_is_empty(db->content_database_path)) free(db->content_database_path); if (!string_is_empty(db->fullpath)) free(db->fullpath); if (db->state.buf) free(db->state.buf); if (db->handle) database_info_free(db->handle); free(db); } if (dbinfo) free(dbinfo); }
/* Initialise runtime log, loading current parameters * if log file exists. Returned object must be free()'d. * Returns NULL if content_path and/or core_path are invalid */ runtime_log_t *runtime_log_init(const char *content_path, const char *core_path, bool log_per_core) { settings_t *settings = config_get_ptr(); core_info_list_t *core_info = NULL; runtime_log_t *runtime_log = NULL; const char *core_path_basename = path_basename(core_path); char content_name[PATH_MAX_LENGTH]; char core_name[PATH_MAX_LENGTH]; char log_file_dir[PATH_MAX_LENGTH]; char log_file_path[PATH_MAX_LENGTH]; char tmp_buf[PATH_MAX_LENGTH]; unsigned i; content_name[0] = '\0'; core_name[0] = '\0'; log_file_dir[0] = '\0'; log_file_path[0] = '\0'; tmp_buf[0] = '\0'; /* Error checking */ if (!settings) return NULL; if (string_is_empty(settings->paths.directory_playlist)) { RARCH_ERR("Playlist directory is undefined - cannot save runtime logs.\n"); return NULL; } if (string_is_empty(content_path) || string_is_empty(core_path_basename)) return NULL; if (string_is_equal(core_path, "builtin") || string_is_equal(core_path, file_path_str(FILE_PATH_DETECT))) return NULL; /* Get core name * Note: An annoyance - this is required even when * we are performing aggregate (not per core) logging, * since content name is sometimes dependent upon core * (e.g. see TyrQuake below) */ core_info_get_list(&core_info); if (!core_info) return NULL; for (i = 0; i < core_info->count; i++) { if (string_is_equal(path_basename(core_info->list[i].path), core_path_basename)) { strlcpy(core_name, core_info->list[i].core_name, sizeof(core_name)); break; } } if (string_is_empty(core_name)) return NULL; /* Get runtime log directory */ fill_pathname_join( tmp_buf, settings->paths.directory_playlist, "logs", sizeof(tmp_buf)); if (log_per_core) { fill_pathname_join( log_file_dir, tmp_buf, core_name, sizeof(log_file_dir)); } else { strlcpy(log_file_dir, tmp_buf, sizeof(log_file_dir)); } if (string_is_empty(log_file_dir)) return NULL; /* Create directory, if required */ if (!path_is_directory(log_file_dir)) { path_mkdir(log_file_dir); if(!path_is_directory(log_file_dir)) { RARCH_ERR("Failed to create directory for runtime log: %s.\n", log_file_dir); return NULL; } } /* Get content name * Note: TyrQuake requires a specific hack, since all * content has the same name... */ if (string_is_equal(core_name, "TyrQuake")) { const char *last_slash = find_last_slash(content_path); if (last_slash) { size_t path_length = last_slash + 1 - content_path; if (path_length < PATH_MAX_LENGTH) { memset(tmp_buf, 0, sizeof(tmp_buf)); strlcpy(tmp_buf, content_path, path_length * sizeof(char)); strlcpy(content_name, path_basename(tmp_buf), sizeof(content_name)); } } } else { /* path_remove_extension() requires a char * (not const) * so have to use a temporary buffer... */ tmp_buf[0] = '\0'; strlcpy(tmp_buf, path_basename(content_path), sizeof(tmp_buf)); strlcpy(content_name, path_remove_extension(tmp_buf), sizeof(content_name)); } if (string_is_empty(content_name)) return NULL; /* Build final log file path */ fill_pathname_join(log_file_path, log_file_dir, content_name, sizeof(log_file_path)); strlcat(log_file_path, file_path_str(FILE_PATH_RUNTIME_EXTENSION), sizeof(log_file_path)); if (string_is_empty(log_file_path)) return NULL; /* Phew... If we get this far then all is well. * > Create 'runtime_log' object */ runtime_log = (runtime_log_t*)calloc(1, sizeof(*runtime_log)); if (!runtime_log) return NULL; /* > Populate default values */ runtime_log->runtime.hours = 0; runtime_log->runtime.minutes = 0; runtime_log->runtime.seconds = 0; runtime_log->last_played.year = 0; runtime_log->last_played.month = 0; runtime_log->last_played.day = 0; runtime_log->last_played.hour = 0; runtime_log->last_played.minute = 0; runtime_log->last_played.second = 0; strlcpy(runtime_log->path, log_file_path, sizeof(runtime_log->path)); /* Load existing log file, if it exists */ runtime_log_read_file(runtime_log); return runtime_log; }