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_path_new_child * @parent: a parent path * @basename: basename of a direct child of @parent directory in glib * filename encoding. (can be non-UTF-8). * * 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_child (FmPath *parent, const char *basename) { if (G_LIKELY (basename && *basename)) { int baselen = strlen (basename); return fm_path_new_child_len (parent, basename, baselen); } return G_LIKELY (parent) ? fm_path_ref (parent) : NULL; }
/** * 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; }
FmPath* fm_path_new(const char* path) { const char* sep; /* FIXME: need to canonicalize paths */ if( path[0] == '/' ) /* if this is a absolute native path */ { if (path[1]) return fm_path_new_relative(root, path + 1); else /* special case: handle root dir */ return fm_path_ref( root ); } else if ( path[0] == '~' && (path[1] == '\0' || path[1]=='/') ) /* home dir */ { ++path; return *path ? fm_path_new_relative(home, path) : fm_path_ref(home); } else /* then this should be a URL */ { FmPath* parent, *ret; char* colon = strchr(path, ':'); char* hier_part; char* rest; int root_len; if( !colon ) /* this shouldn't happen */ return NULL; /* invalid path FIXME: should we treat it as relative path? */ /* FIXME: convert file:/// to local native path */ hier_part = colon+1; if( hier_part[0] == '/' ) { if(hier_part[1] == '/') /* this is a scheme:// form URI */ rest = hier_part + 2; else /* a malformed URI */ rest = hier_part + 1; if(*rest == '/') /* :/// means there is no authoraty part */ ++rest; else /* we are now at autority part, something like <username>@domain/ */ { while( *rest && *rest != '/' ) ++rest; if(*rest == '/') ++rest; } if( strncmp(path, "trash:", 6) == 0 ) /* in trash:// */ { if(*rest) return fm_path_new_relative(trash_root, rest); else return fm_path_ref(trash_root); } /* other URIs */ } else /* this URI doesn't have //, like mailto: */ { /* FIXME: is this useful to file managers? */ rest = colon + 1; } root_len = (rest - path); parent = fm_path_new_child_len(NULL, path, root_len); if(*rest) { ret = fm_path_new_relative(parent, rest); fm_path_unref(parent); } else ret = parent; return ret; } return fm_path_new_relative(NULL, path); }
FmPath* fm_path_new_relative(FmPath* parent, const char* relative_path) { FmPath* path; const char* sep; gsize name_len; /* FIXME: need to canonicalize paths */ if(parent == root) { if( 0 == strncmp(relative_path, home_dir + 1, home_len - 1) ) /* in home dir */ { if( relative_path[home_len - 1] == '\0' ) /* this is the home dir */ { if(G_LIKELY(home)) return fm_path_ref(home); else goto _resolve_relative_path; } if( 0 == strncmp(relative_path, desktop_dir + home_len + 1, desktop_len - home_len -1) ) /* in desktop dir */ { if(relative_path[desktop_len - 1] == '\0') /* this is the desktop dir */ return fm_path_ref(desktop); return fm_path_new_relative(desktop, relative_path + desktop_len + 1); } } } _resolve_relative_path: sep = strchr(relative_path, '/'); if(sep) { char* end = sep; while(*end && *end == '/') /* prevent tailing slash or duplicated slashes. */ ++end; name_len = (sep - relative_path); if(relative_path[0] == '.' && (name_len == 1 || (name_len == 2 && relative_path[1] == '.')) ) /* . or .. */ { if(name_len == 1) /* . => current dir */ { relative_path = end; if(*end == '\0') return fm_path_ref(parent); /* . is the last component */ else { relative_path = end; /* skip this component */ goto _resolve_relative_path; } } else /* .. jump to parent dir => */ { if(parent->parent) parent = fm_path_ref(parent->parent); } } else parent = fm_path_new_child_len(parent, relative_path, name_len); if(*end != '\0') { relative_path = end; path = fm_path_new_relative(parent, relative_path); fm_path_unref(parent); } else /* this is tailing slash */ path = parent; } else /* this is the last component in the path */ { name_len = strlen(relative_path); if(relative_path[0] == '.' && (name_len == 1 || (name_len == 2 && relative_path[1] == '.')) ) /* . or .. */ { if(name_len == 1) /* . => current dir */ path = fm_path_ref(parent); /* . is the last component */ else /* .. jump to parent dir => */ { if(parent->parent) path = fm_path_ref(parent->parent); else path = fm_path_ref(parent); } } else path = fm_path_new_child_len(parent, relative_path, name_len); } return path; }
FmPath* fm_path_new_child(FmPath* parent, const char* basename) { int baselen = strlen(basename); return fm_path_new_child_len(parent, basename, baselen); }