void fm_path_init() { const char* sep, *name; FmPath* tmp, *parent; /* path object of root dir */ root = fm_path_new_child(NULL, "/"); home_dir = g_get_home_dir(); home_len = strlen(home_dir); /* build path object for home dir */ name = home_dir + 1; /* skip leading / */ parent = root; while( sep = strchr(name, '/') ) { int len = (sep - name); /* ref counting is not a problem here since this path component * will exist till the termination of the program. So mem leak is ok. */ tmp = fm_path_new_child_len(parent, name, len); name = sep + 1; parent = tmp; } home = fm_path_new_child(parent, name); desktop_dir = g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP); desktop_len = strlen(desktop_dir); /* build path object for desktop dir */ name = desktop_dir + home_len + 1; /* skip home dir part / */ parent = home; while( sep = strchr(name, '/') ) { int len = (sep - name); /* ref counting is not a problem here since this path component * will exist till the termination of the program. So mem leak is ok. */ tmp = fm_path_new_child_len(parent, name, len); name = sep + 1; parent = tmp; } desktop = fm_path_new_child(parent, name); /* build path object for trash can */ /* FIXME: currently there are problems with URIs. using trash:/ here will cause problems. */ trash_root = fm_path_new_child(NULL, "trash:///"); trash_root->flags |= (FM_PATH_IS_TRASH|FM_PATH_IS_VIRTUAL|FM_PATH_IS_LOCAL); apps_root = fm_path_new_child(NULL, "applications:///"); apps_root->flags |= (FM_PATH_IS_VIRTUAL); }
/** * fm_folder_config_open * @path: path to get config * * Searches for settings in the cache that are specific to @path. Locks * the cache. Returned descriptor can be used for access to settings. * * Returns: (transfer full): new configuration descriptor. * * Since: 1.2.0 */ FmFolderConfig *fm_folder_config_open(FmPath *path) { FmFolderConfig *fc = g_slice_new(FmFolderConfig); FmPath *sub_path; fc->changed = FALSE; /* clear .directory file first */ sub_path = fm_path_new_child(path, ".directory"); fc->filepath = fm_path_to_str(sub_path); fm_path_unref(sub_path); if (g_file_test(fc->filepath, G_FILE_TEST_EXISTS)) { fc->kf = g_key_file_new(); if (g_key_file_load_from_file(fc->kf, fc->filepath, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL) && g_key_file_has_group(fc->kf, "File Manager")) { fc->group = "File Manager"; return fc; } g_key_file_free(fc->kf); } g_free(fc->filepath); fc->filepath = NULL; fc->group = fm_path_to_str(path); G_LOCK(cache); fc->kf = fc_cache; return fc; }
/** * fm_path_new_relative * @parent: a parent path * @rel: a path relative to @parent in glib filename encoding. (can be * non-UTF-8). However this should not be a escaped ASCII string used in * URI. If you're building a relative path for a URI, and the relative * path is escaped, you have to unescape it first. * * For example, if @parent is "http://wiki.lxde.org/" and @rel is * "zh/%E9%A6%96%E9%A0%81", you have to unescape the relative path * prior to passing it to fm_path_new_relative (). * * If @parent is NULL, this works the same as fm_path_new_for_str (@rel) * * Returns: a newly created FmPath for the path. You have to call * fm_path_unref () when it's no longer needed. */ FmPath *fm_path_new_relative (FmPath *parent, const char *rel) { FmPath *path; if (G_UNLIKELY (!rel || !*rel)) // relative path is empty return parent ? fm_path_ref (parent) : fm_path_ref (root_path); // return parent if (G_LIKELY (parent)) { char *sep; // remove leading slashes while (*rel == '/') ++rel; if (!*rel) path = fm_path_ref (parent); else { #if 0 // FIXME_pcm: Let's optimize this later. Make things working first is more important. // use some pre-defined paths when possible if (G_UNLIKELY (parent == root_path)) { if (strcmp (home_dir + 1, rel) == 0) return fm_path_ref (home_path); if (strcmp (desktop_dir + 1, rel) == 0) return fm_path_ref (desktop_dir); } #endif sep = strchr (rel, '/'); if (sep) { FmPath *new_parent = fm_path_new_child_len (parent, rel, sep - rel); path = fm_path_new_relative (new_parent, sep + 1); fm_path_unref (new_parent); } else { path = fm_path_new_child (parent, rel); } } } else // this is actaully a full path path = fm_path_new_for_str (rel); return path; }
static void test_path_child() { FmPath* parent = fm_path_get_home(); FmPath* path; g_print("\n"); path = fm_path_new_child(parent, "child"); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "child"); fm_path_unref(path); path = fm_path_new_child(parent, "child/"); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "child"); fm_path_unref(path); path = fm_path_new_child(parent, "child///"); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "child"); fm_path_unref(path); path = fm_path_new_child(parent, ".."); g_printf("path->name = %s\n", path->name); g_assert(path == parent->parent); fm_path_unref(path); path = fm_path_new_child(parent, "../"); g_printf("path->name = %s\n", path->name); g_assert(path == parent->parent); fm_path_unref(path); path = fm_path_new_child(parent, "/"); g_assert(path == parent); fm_path_unref(path); parent = fm_path_get_root(); path = fm_path_new_child(parent, ".."); g_assert(path == parent); fm_path_unref(path); path = fm_path_new_child(parent, "../"); g_assert(path == parent); fm_path_unref(path); parent = NULL; path = fm_path_new_child(parent, "/"); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "/"); fm_path_unref(path); path = fm_path_new_child(parent, "//"); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "/"); fm_path_unref(path); path = fm_path_new_child(parent, "///"); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "/"); fm_path_unref(path); /* FIXME: how to handle this case? path = fm_path_new_child(parent, "/test"); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "/test/"); fm_path_unref(path); */ path = fm_path_new_child(parent, "trash:"); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "trash:///"); fm_path_unref(path); path = fm_path_new_child(parent, "trash:/"); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "trash:///"); fm_path_unref(path); path = fm_path_new_child(parent, "trash:////"); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "trash:///"); fm_path_unref(path); path = fm_path_new_child(parent, ".."); g_printf("path->name = %s\n", path->name); g_assert_cmpstr(path->name, ==, "/"); fm_path_unref(path); path = fm_path_new_child(parent, "../"); g_assert_cmpstr(path->name, ==, "/"); fm_path_unref(path); /* path = fm_path_new_child(parent, ".."); g_assert(path == NULL); path = fm_path_new_child(parent, "."); g_assert(path == NULL); */ }
static gboolean fm_dir_list_job_run_gio(FmDirListJob* job) { GFileEnumerator *enu; GFileInfo *inf; FmFileInfo* fi; GError *err = NULL; FmJob* fmjob = FM_JOB(job); GFile* gf; const char* query; gf = fm_path_to_gfile(job->dir_path); _retry: inf = g_file_query_info(gf, gfile_info_query_attribs, 0, fm_job_get_cancellable(fmjob), &err); if(!inf ) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); if( act == FM_JOB_RETRY ) { err = NULL; goto _retry; } else { g_object_unref(gf); return FALSE; } } if( g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) { char *path_str = fm_path_to_str(job->dir_path); err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The specified directory '%s' is not valid"), path_str); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_free(path_str); g_error_free(err); g_object_unref(gf); g_object_unref(inf); return FALSE; } /* check if FS is R/O and set attr. into inf */ _fm_file_info_job_update_fs_readonly(gf, inf, NULL, NULL); job->dir_fi = fm_file_info_new_from_g_file_data(gf, inf, job->dir_path); g_object_unref(inf); if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) { query = G_FILE_ATTRIBUTE_STANDARD_TYPE","G_FILE_ATTRIBUTE_STANDARD_NAME"," G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN","G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP"," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK","G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL"," G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME"," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","G_FILE_ATTRIBUTE_STANDARD_ICON"," G_FILE_ATTRIBUTE_STANDARD_SIZE","G_FILE_ATTRIBUTE_STANDARD_TARGET_URI"," "unix::*,time::*,access::*,id::filesystem"; } else query = gfile_info_query_attribs; enu = g_file_enumerate_children (gf, query, 0, fm_job_get_cancellable(fmjob), &err); g_object_unref(gf); if(enu) { while( ! fm_job_is_cancelled(fmjob) ) { inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err); if(inf) { FmPath *dir, *sub; GFile *child; if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) { /* FIXME: handle symlinks */ if(g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) { g_object_unref(inf); continue; } } /* virtual folders may return children not within them */ dir = fm_path_new_for_gfile(g_file_enumerator_get_container(enu)); if (fm_path_equal(job->dir_path, dir)) sub = fm_path_new_child(job->dir_path, g_file_info_get_name(inf)); else sub = fm_path_new_child(dir, g_file_info_get_name(inf)); child = g_file_get_child(g_file_enumerator_get_container(enu), g_file_info_get_name(inf)); if (g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY) /* for dir: check if its FS is R/O and set attr. into inf */ _fm_file_info_job_update_fs_readonly(child, inf, NULL, NULL); fi = fm_file_info_new_from_g_file_data(child, inf, sub); fm_path_unref(sub); fm_path_unref(dir); g_object_unref(child); fm_dir_list_job_add_found_file(job, fi); fm_file_info_unref(fi); } else { if(err) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); /* FM_JOB_RETRY is not supported. */ if(act == FM_JOB_ABORT) fm_job_cancel(fmjob); } /* otherwise it's EOL */ break; } g_object_unref(inf); } g_file_enumerator_close(enu, NULL, &err); g_object_unref(enu); } else { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); return FALSE; } return TRUE; }
static gboolean fm_dir_list_job_run_posix(FmDirListJob* job) { FmJob* fmjob = FM_JOB(job); FmFileInfo* fi; GError *err = NULL; char* path_str; GDir* dir; path_str = fm_path_to_str(job->dir_path); fi = _new_info_for_native_file(job, job->dir_path, path_str, NULL); if(fi) { if(! fm_file_info_is_dir(fi)) { err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The specified directory '%s' is not valid"), path_str); fm_file_info_unref(fi); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); g_free(path_str); return FALSE; } job->dir_fi = fi; } else { err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The specified directory '%s' is not valid"), path_str); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); g_free(path_str); return FALSE; } dir = g_dir_open(path_str, 0, &err); if( dir ) { const char* name; GString* fpath = g_string_sized_new(4096); int dir_len = strlen(path_str); g_string_append_len(fpath, path_str, dir_len); if(fpath->str[dir_len-1] != '/') { g_string_append_c(fpath, '/'); ++dir_len; } while( ! fm_job_is_cancelled(fmjob) && (name = g_dir_read_name(dir)) ) { FmPath* new_path; g_string_truncate(fpath, dir_len); g_string_append(fpath, name); if(job->flags & FM_DIR_LIST_JOB_DIR_ONLY) /* if we only want directories */ { struct stat st; /* FIXME: this results in an additional stat() call, which is inefficient */ if(stat(fpath->str, &st) == -1 || !S_ISDIR(st.st_mode)) continue; } new_path = fm_path_new_child(job->dir_path, name); _retry: fi = _new_info_for_native_file(job, new_path, fpath->str, &err); if (fi == NULL) /* we got a damaged file */ { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); GFile *gf; GFileInfo *inf; gchar *disp_basename; g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry; /* bug #3615271: Damaged mountpoint isn't shown let make a simple file info then */ inf = g_file_info_new(); gf = fm_path_to_gfile(new_path); g_file_info_set_file_type(inf, G_FILE_TYPE_UNKNOWN); g_file_info_set_name(inf, name); disp_basename = g_filename_display_basename(fpath->str); g_file_info_set_display_name(inf, disp_basename); g_free(disp_basename); g_file_info_set_content_type(inf, "inode/x-corrupted"); fi = fm_file_info_new_from_g_file_data(gf, inf, new_path); g_object_unref(inf); g_object_unref(gf); } fm_dir_list_job_add_found_file(job, fi); fm_file_info_unref(fi); fm_path_unref(new_path); } g_string_free(fpath, TRUE); g_dir_close(dir); } else { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); } g_free(path_str); return TRUE; }
gboolean on_idle(FmFolder* folder) { GSList* l; FmFileInfoJob* job = NULL; FmPath* path; folder->idle_handler = 0; if(folder->files_to_update || folder->files_to_add) job = (FmFileInfoJob*)fm_file_info_job_new(NULL, 0); if(folder->files_to_update) { GSList* prev = NULL; for(l=folder->files_to_update;l;) { /* if a file is already in files_to_add, remove it. */ if(g_slist_find_custom(folder->files_to_add, l->data, (GCompareFunc)strcmp)) { GSList* tmp = l; l=l->next; if(G_LIKELY(prev)) prev->next = l; g_free(tmp->data); g_slist_free_1(tmp); if(G_UNLIKELY(tmp == folder->files_to_update)) folder->files_to_update = l; continue; } path = fm_path_new_child(folder->dir_path, (char*)l->data); fm_file_info_job_add(job, path); fm_path_unref(path); g_free(l->data); prev = l; l=l->next; } g_slist_free(folder->files_to_update); folder->files_to_update = NULL; } if(folder->files_to_add) { for(l=folder->files_to_add;l;l=l->next) { path = fm_path_new_child(folder->dir_path, (char*)l->data); fm_file_info_job_add(job, path); fm_path_unref(path); g_free(l->data); } g_slist_free(folder->files_to_add); folder->files_to_add = NULL; } if(job) { g_signal_connect(job, "finished", on_file_info_finished, folder); folder->pending_jobs = g_slist_prepend(folder->pending_jobs, job); fm_job_run_async(FM_JOB(job)); } if(folder->files_to_del) { GSList* ll; for(ll=folder->files_to_del;ll;ll=ll->next) { GList* l= (GList*)ll->data; ll->data = l->data; fm_list_delete_link_nounref(folder->files , l); } g_signal_emit(folder, signals[FILES_REMOVED], 0, folder->files_to_del); g_slist_foreach(folder->files_to_del, (GFunc)fm_file_info_unref, NULL); g_slist_free(folder->files_to_del); folder->files_to_del = NULL; } return FALSE; }
void on_create_new(GtkAction* action, FmMainWin* win) { FmFolderView* fv = FM_FOLDER_VIEW(win->folder_view); const char* name = gtk_action_get_name(action); GError* err = NULL; FmPath* cwd = fm_folder_view_get_cwd(fv); FmPath* dest; char* basename; _retry: basename = fm_get_user_input(GTK_WINDOW(win), _("Create New..."), _("Enter a name for the newly created file:"), _("New")); if(!basename) return; dest = fm_path_new_child(cwd, basename); g_free(basename); if( strcmp(name, "NewFolder") == 0 ) { GFile* gf = fm_path_to_gfile(dest); if(!g_file_make_directory(gf, NULL, &err)) { if(err->domain = G_IO_ERROR && err->code == G_IO_ERROR_EXISTS) { fm_path_unref(dest); g_error_free(err); g_object_unref(gf); err = NULL; goto _retry; } fm_show_error(GTK_WINDOW(win), err->message); g_error_free(err); } if(!err) /* select the newly created file */ { /*FIXME: this doesn't work since the newly created file will * only be shown after file-created event was fired on its * folder's monitor and after FmFolder handles it in idle * handler. So, we cannot select it since it's not yet in * the folder model now. */ /* fm_folder_view_select_file_path(fv, dest); */ } g_object_unref(gf); } else if( strcmp(name, "NewBlank") == 0 ) { GFile* gf = fm_path_to_gfile(dest); GFileOutputStream* f = g_file_create(gf, G_FILE_CREATE_NONE, NULL, &err); if(f) { g_output_stream_close(G_OUTPUT_STREAM(f), NULL, NULL); g_object_unref(f); } else { if(err->domain = G_IO_ERROR && err->code == G_IO_ERROR_EXISTS) { fm_path_unref(dest); g_error_free(err); g_object_unref(gf); err = NULL; goto _retry; } fm_show_error(GTK_WINDOW(win), err->message); g_error_free(err); } if(!err) /* select the newly created file */ { /*FIXME: this doesn't work since the newly created file will * only be shown after file-created event was fired on its * folder's monitor and after FmFolder handles it in idle * handler. So, we cannot select it since it's not yet in * the folder model now. */ /* fm_folder_view_select_file_path(fv, dest); */ } g_object_unref(gf); } else /* templates */ { } fm_path_unref(dest); }
gboolean fm_dnd_dest_drag_drop(FmDndDest* dd, GdkDragContext *drag_context, GdkAtom target, guint time) { gboolean ret = FALSE; GtkWidget* dest_widget = dd->widget; int i; for(i = 0; i < G_N_ELEMENTS(fm_default_dnd_dest_targets); ++i) { if(gdk_atom_intern_static_string(fm_default_dnd_dest_targets[i].target) == target) { ret = TRUE; break; } } if(ret) /* we support this kind of target */ { if(i == FM_DND_DEST_TARGET_XDS) /* if this is XDS */ { guchar *data = NULL; gint len = 0; GdkAtom text_atom = gdk_atom_intern_static_string("text/plain"); /* get filename from the source window */ if(gdk_property_get(drag_context->source_window, xds_target_atom, text_atom, 0, 1024, FALSE, NULL, NULL, &len, &data) && data) { FmFileInfo* dest = fm_dnd_dest_get_dest_file(dd); if( dest && fm_file_info_is_dir(dest) ) { FmPath* path = fm_path_new_child(dest->path, data); char* uri = fm_path_to_uri(path); /* setup the property */ gdk_property_change(GDK_DRAWABLE(drag_context->source_window), xds_target_atom, text_atom, 8, GDK_PROP_MODE_REPLACE, (const guchar *)uri, strlen(uri) + 1); fm_path_unref(path); g_free(uri); } } else { fm_show_error(gtk_widget_get_toplevel(dest_widget), _("XDirectSave failed.")); gdk_property_change(GDK_DRAWABLE(drag_context->source_window), xds_target_atom, text_atom, 8, GDK_PROP_MODE_REPLACE, (const guchar *)"", 0); } g_free(data); gtk_drag_get_data(dest_widget, drag_context, target, time); /* we should call gtk_drag_finish later in data-received callback. */ return TRUE; } /* see if the drag files are cached */ if(dd->src_files) { /* emit files-dropped signal */ g_signal_emit(dd, signals[FILES_DROPPED], 0, drag_context->action, dd->info_type, dd->src_files, &ret); } else /* we don't have the data */ { if(dd->waiting_data) /* if we're still waiting for the data */ { /* FIXME: how to handle this? */ ret = FALSE; } else ret = FALSE; } gtk_drag_finish(drag_context, ret, FALSE, time); } return ret; }
gboolean deep_count_posix(FmDeepCountJob* job, FmPath* fm_path) { FmJob* fmjob = (FmJob*)job; char* path = fm_path_to_str(fm_path); struct stat st; int ret; _retry_stat: if( G_UNLIKELY(job->flags & FM_DC_JOB_FOLLOW_LINKS) ) ret = stat(path, &st); else ret = lstat(path, &st); if( ret == 0 ) { ++job->count; job->total_size += (goffset)st.st_size; job->total_block_size += (st.st_blocks * st.st_blksize); /* NOTE: if job->dest_dev is 0, that means our destination * folder is not on native UNIX filesystem. Hence it's not * on the same device. Our st.st_dev will always be non-zero * since our file is on a native UNIX filesystem. */ /* only descends into files on the same filesystem */ if( job->flags & FM_DC_JOB_SAME_FS ) { if( st.st_dev != job->dest_dev ) return TRUE; } /* only descends into files on the different filesystem */ else if( job->flags & FM_DC_JOB_PREPARE_MOVE ) { if( st.st_dev == job->dest_dev ) return TRUE; } } else { GError* err = g_error_new(G_IO_ERROR, g_io_error_from_errno(errno), "%s", g_strerror(errno)); FmJobErrorAction act = fm_job_emit_error(FM_JOB(job), err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_stat; return FALSE; } if(fm_job_is_cancelled(fmjob)) return FALSE; if( S_ISDIR(st.st_mode) ) /* if it's a dir */ { GDir* dir_ent = g_dir_open(path, 0, NULL); if(dir_ent) { const char* basename; while( !fm_job_is_cancelled(fmjob) && (basename = g_dir_read_name(dir_ent)) ) { FmPath* sub = fm_path_new_child(fm_path, basename); if(!fm_job_is_cancelled(fmjob)) { if(deep_count_posix(job, sub)) { /* for moving across different devices, an additional 'delete' * for source file is needed. so let's +1 for the delete.*/ if(job->flags & FM_DC_JOB_PREPARE_MOVE) { ++job->total_size; ++job->total_block_size; ++job->count; } } } fm_path_unref(sub); } g_dir_close(dir_ent); } } g_free(path); return TRUE; }