/* Canonicalize path, and return a new path. Do everything in situ. The new path differs from path in: Multiple `/'s are collapsed to a single `/'. Leading `./'s and trailing `/.'s are removed. Non-leading `../'s and trailing `..'s are handled by removing portions of the path. */ static gchar * mate_vfs_canonicalize_pathname (gchar *path) { int i, marker; if (path == NULL || strlen (path) == 0) { return ""; } /* Walk along path looking for things to compact. */ for (i = 0, marker = 0;;) { if (!path[i]) break; /* Check for `../', `./' or trailing `.' by itself. */ if (path[i] == '.') { /* Handle trailing `.' by itself. */ if (path[i + 1] == '\0') { if (i > 1 && path[i - 1] == MATE_VFS_URI_PATH_CHR) { /* strip the trailing /. */ path[i - 1] = '\0'; } else { /* convert path "/." to "/" */ path[i] = '\0'; } break; } /* Handle `./'. */ if (path[i + 1] == MATE_VFS_URI_PATH_CHR) { memmove (path + i, path + i + 2, strlen (path + i + 2) + 1); if (i == 0) { /* don't leave leading '/' for paths that started * as relative (.//foo) */ collapse_slash_runs (path, i); marker = 0; } continue; } /* Handle `../' or trailing `..' by itself. * Remove the previous xxx/ part */ if (path[i + 1] == '.' && (path[i + 2] == MATE_VFS_URI_PATH_CHR || path[i + 2] == '\0')) { /* ignore ../ at the beginning of a path */ if (i != 0) { marker = find_slash_before_offset (path, i - 1); /* Either advance past '/' or point to the first character */ marker ++; if (path [i + 2] == '\0' && marker > 1) { /* If we are looking at a /.. at the end of the uri and we * need to eat the last '/' too. */ marker--; } g_assert(marker < i); if (path[i + 2] == MATE_VFS_URI_PATH_CHR) { /* strip the entire ../ string */ i++; } memmove (path + marker, path + i + 2, strlen (path + i + 2) + 1); i = marker; } else { i = 2; if (path[i] == MATE_VFS_URI_PATH_CHR) { i++; } } collapse_slash_runs (path, i); continue; } } /* advance to the next '/' */ i = find_next_slash (path, i); /* If we didn't find any slashes, then there is nothing left to do. */ if (i < 0) { break; } marker = i++; collapse_slash_runs (path, i); } return path; }
/** * thunar_vfs_canonicalize_filename: * @filename : a local filename. * * Canonicalizes @filename and returns a new path. The new path * differs from @filename in: * * <simplelist> * <member>Multiple `/'s are collapsed to a single `/'.</member> * <member>Leading `./'s and trailing `/.'s are removed.</member> * <member>Non-leading `../'s and trailing `..'s are handled by removing portions of the path.</member> * </simplelist> * * The caller is responsible to free the returned string using * g_free() when no longer needed. * * Return value: the canonicalized path for @filename. **/ gchar* thunar_vfs_canonicalize_filename (const gchar *filename) { gchar *path; gint marker; gint i; g_return_val_if_fail (filename != NULL, NULL); /* take a copy of the filename to operate on */ path = g_strdup (filename); if (G_UNLIKELY (*path == '\0')) return path; /* Walk along path looking for things to compact. */ for (i = 0, marker = 0;;) { if (G_UNLIKELY (path[i] == '\0')) break; /* Check for `../', `./' or trailing `.' by itself. */ if (path[i] == '.') { /* Handle trailing `.' by itself. */ if (path[i + 1] == '\0') { if (i > 1 && path[i - 1] == G_DIR_SEPARATOR) { /* strip the trailing /. */ path[i - 1] = '\0'; } else { /* convert path "/." to "/" */ path[i] = '\0'; } break; } /* Handle `./'. */ if (path[i + 1] == G_DIR_SEPARATOR) { memmove (path + i, path + i + 2, strlen (path + i + 2) + 1); if (i == 0) { /* don't leave leading '/' for paths that started * as relative (.//foo) */ collapse_slash_runs (path, i); marker = 0; } continue; } /* Handle `../' or trailing `..' by itself. * Remove the previous xxx/ part */ if (path[i + 1] == '.' && (path[i + 2] == G_DIR_SEPARATOR || path[i + 2] == '\0')) { /* ignore ../ at the beginning of a path */ if (i != 0) { marker = find_slash_before_offset (path, i - 1); /* Either advance past '/' or point to the first character */ marker ++; if (path [i + 2] == '\0' && marker > 1) { /* If we are looking at a /.. at the end of the uri and we * need to eat the last '/' too. */ marker--; } /* strip the entire ../ string */ if (path[i + 2] == G_DIR_SEPARATOR) ++i; memmove (path + marker, path + i + 2, strlen (path + i + 2) + 1); i = marker; } else { i = 2; if (path[i] == G_DIR_SEPARATOR) i++; } collapse_slash_runs (path, i); continue; } } /* advance to the next '/' */ i = find_next_slash (path, i); /* If we didn't find any slashes, then there is nothing left to do. */ if (i < 0) break; marker = i++; collapse_slash_runs (path, i); } return path; }