void custom_canonicalize_pathname (char *path, CANON_PATH_FLAGS flags) { char *p, *s; char *lpath = path; /* path without leading UNC part */ const size_t url_delim_len = strlen (VFS_PATH_URL_DELIMITER); /* Detect and preserve UNC paths: //server/... */ if ((flags & CANON_PATH_GUARDUNC) != 0 && IS_PATH_SEP (path[0]) && IS_PATH_SEP (path[1])) { p = path + 2; while (p[0] != '\0' && !IS_PATH_SEP (p[0])) p++; if (IS_PATH_SEP (p[0]) && p > path + 2) lpath = p; } if (!lpath[0] || !lpath[1]) return; if (flags & CANON_PATH_JOINSLASHES) { /* Collapse multiple slashes */ p = lpath; while (*p) { if (IS_PATH_SEP (p[0]) && IS_PATH_SEP (p[1]) && (p == lpath || *(p - 1) != ':')) { s = p + 1; while (IS_PATH_SEP (*(++s))) ; str_move (p + 1, s); } p++; } } if (flags & CANON_PATH_JOINSLASHES) { /* Collapse "/./" -> "/" */ p = lpath; while (*p) { if (IS_PATH_SEP (p[0]) && p[1] == '.' && IS_PATH_SEP (p[2])) str_move (p, p + 2); else p++; } } if (flags & CANON_PATH_REMSLASHDOTS) { size_t len; /* Remove trailing slashes */ p = lpath + strlen (lpath) - 1; while (p > lpath && IS_PATH_SEP (*p)) { if (p >= lpath + url_delim_len - 1 && strncmp (p - url_delim_len + 1, VFS_PATH_URL_DELIMITER, url_delim_len) == 0) break; *p-- = 0; } /* Remove leading "./" */ if (lpath[0] == '.' && IS_PATH_SEP (lpath[1])) { if (lpath[2] == 0) { lpath[1] = 0; return; } else { str_move (lpath, lpath + 2); } } /* Remove trailing "/" or "/." */ len = strlen (lpath); if (len < 2) return; if (IS_PATH_SEP (lpath[len - 1]) && (len < url_delim_len || strncmp (lpath + len - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) != 0)) { lpath[len - 1] = '\0'; } else { if (lpath[len - 1] == '.' && IS_PATH_SEP (lpath[len - 2])) { if (len == 2) { lpath[1] = '\0'; return; } else { lpath[len - 2] = '\0'; } } } } if (flags & CANON_PATH_REMDOUBLEDOTS) { #ifdef HAVE_CHARSET const size_t enc_prefix_len = strlen (VFS_ENCODING_PREFIX); #endif /* HAVE_CHARSET */ /* Collapse "/.." with the previous part of path */ p = lpath; while (p[0] && p[1] && p[2]) { if (!IS_PATH_SEP (p[0]) || p[1] != '.' || p[2] != '.' || (!IS_PATH_SEP (p[3]) && p[3] != '\0')) { p++; continue; } /* search for the previous token */ s = p - 1; if (s >= lpath + url_delim_len - 2 && strncmp (s - url_delim_len + 2, VFS_PATH_URL_DELIMITER, url_delim_len) == 0) { s -= (url_delim_len - 2); while (s >= lpath && !IS_PATH_SEP (*s--)) ; } while (s >= lpath) { if (s - url_delim_len > lpath && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0) { char *vfs_prefix = s - url_delim_len; struct vfs_class *vclass; while (vfs_prefix > lpath && !IS_PATH_SEP (*--vfs_prefix)) ; if (IS_PATH_SEP (*vfs_prefix)) vfs_prefix++; *(s - url_delim_len) = '\0'; vclass = vfs_prefix_to_class (vfs_prefix); *(s - url_delim_len) = *VFS_PATH_URL_DELIMITER; if (vclass != NULL) { struct vfs_s_subclass *sub = (struct vfs_s_subclass *) vclass->data; if (sub != NULL && sub->flags & VFS_S_REMOTE) { s = vfs_prefix; continue; } } } if (IS_PATH_SEP (*s)) break; s--; } s++; /* If the previous token is "..", we cannot collapse it */ if (s[0] == '.' && s[1] == '.' && s + 2 == p) { p += 3; continue; } if (p[3] != 0) { if (s == lpath && IS_PATH_SEP (*s)) { /* "/../foo" -> "/foo" */ str_move (s + 1, p + 4); } else { /* "token/../foo" -> "foo" */ #ifdef HAVE_CHARSET if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0) && (is_supported_encoding (s + enc_prefix_len))) /* special case: remove encoding */ str_move (s, p + 1); else #endif /* HAVE_CHARSET */ str_move (s, p + 4); } p = (s > lpath) ? s - 1 : s; continue; } /* trailing ".." */ if (s == lpath) { /* "token/.." -> "." */ if (!IS_PATH_SEP (lpath[0])) lpath[0] = '.'; lpath[1] = '\0'; } else { /* "foo/token/.." -> "foo" */ if (s == lpath + 1) s[0] = '\0'; #ifdef HAVE_CHARSET else if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0) && (is_supported_encoding (s + enc_prefix_len))) { /* special case: remove encoding */ s[0] = '.'; s[1] = '.'; s[2] = '\0'; /* search for the previous token */ /* IS_PATH_SEP (s[-1]) */ p = s - 1; while (p >= lpath && !IS_PATH_SEP (*p)) p--; if (p >= lpath) continue; } #endif /* HAVE_CHARSET */ else { if (s >= lpath + url_delim_len && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0) *s = '\0'; else s[-1] = '\0'; } } break; } } }
static estr_t _vfs_translate_path (const char *path, int size, GIConv defcnv, GString * buffer) { estr_t state = ESTR_SUCCESS; #ifdef HAVE_CHARSET const char *semi; const char *slash; if (size == 0) return ESTR_SUCCESS; size = (size > 0) ? size : (signed int) strlen (path); /* try found /#enc: */ semi = g_strrstr_len (path, size, VFS_ENCODING_PREFIX); if (semi != NULL && (semi == path || *(semi - 1) == PATH_SEP)) { char encoding[16]; GIConv coder = INVALID_CONV; int ms; /* first must be translated part before #enc: */ ms = semi - path; state = _vfs_translate_path (path, ms, defcnv, buffer); if (state != ESTR_SUCCESS) return state; /* now can be translated part after #enc: */ semi += strlen (VFS_ENCODING_PREFIX); /* skip "#enc:" */ slash = strchr (semi, PATH_SEP); /* ignore slashes after size; */ if (slash - path >= size) slash = NULL; ms = (slash != NULL) ? slash - semi : (int) strlen (semi); ms = min ((unsigned int) ms, sizeof (encoding) - 1); /* limit encoding size (ms) to path size (size) */ if (semi + ms > path + size) ms = path + size - semi; memcpy (encoding, semi, ms); encoding[ms] = '\0'; if (is_supported_encoding (encoding)) coder = str_crt_conv_to (encoding); if (coder != INVALID_CONV) { if (slash != NULL) state = str_vfs_convert_to (coder, slash + 1, path + size - slash - 1, buffer); str_close_conv (coder); return state; } errno = EINVAL; state = ESTR_FAILURE; } else { /* path can be translated whole at once */ state = str_vfs_convert_to (defcnv, path, size, buffer); } #else (void) size; (void) defcnv; g_string_assign (buffer, path); #endif /* HAVE_CHARSET */ return state; }