root/lib/vfs/path.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. path_magic
  2. _vfs_split_with_semi_skip_count
  3. vfs_canon
  4. vfs_path_url_split
  5. vfs_get_class_by_name
  6. vfs_path_is_str_path_deprecated
  7. vfs_path_from_str_deprecated_parser
  8. vfs_path_from_str_uri_parser
  9. vfs_path_tokens_add_class_info
  10. vfs_path_strip_home
  11. vfs_path_to_str_flags
  12. vfs_path_to_str_elements_count
  13. vfs_path_from_str_flags
  14. vfs_path_from_str
  15. vfs_path_new
  16. vfs_path_elements_count
  17. vfs_path_add_element
  18. vfs_path_get_by_index
  19. vfs_path_element_clone
  20. vfs_path_element_free
  21. vfs_path_clone
  22. vfs_path_free
  23. vfs_path_remove_element_by_index
  24. vfs_prefix_to_class
  25. vfs_get_encoding
  26. vfs_path_element_need_cleanup_converter
  27. vfs_path_change_encoding
  28. vfs_path_serialize
  29. vfs_path_deserialize
  30. vfs_path_build_filename
  31. vfs_path_append_new
  32. vfs_path_append_vpath_new
  33. vfs_path_tokens_count
  34. vfs_path_tokens_get
  35. vfs_path_vtokens_get
  36. vfs_path_build_url_params_str
  37. vfs_path_element_build_pretty_path_str
  38. vfs_path_equal
  39. vfs_path_equal_len
  40. vfs_path_len
  41. vfs_path_to_absolute

   1 /*
   2    Virtual File System path handlers
   3 
   4    Copyright (C) 2011-2024
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Slava Zanko <slavazanko@gmail.com>, 2011, 2013
   9    Andrew Borodin <aborodin@vmail.ru>, 2013-2022
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  25  */
  26 
  27 /**
  28  * \file
  29  * \brief Source: Virtual File System: path handlers
  30  * \author Slava Zanko
  31  * \date 2011
  32  */
  33 
  34 
  35 #include <config.h>
  36 
  37 #include <errno.h>
  38 
  39 #include "lib/global.h"
  40 #include "lib/strutil.h"
  41 #include "lib/util.h"           /* mc_build_filename() */
  42 #include "lib/serialize.h"
  43 
  44 #include "vfs.h"
  45 #include "utilvfs.h"
  46 #include "xdirentry.h"
  47 #include "path.h"
  48 
  49 extern GPtrArray *vfs__classes_list;
  50 
  51 /*** global variables ****************************************************************************/
  52 
  53 /*** file scope macro definitions ****************************************************************/
  54 
  55 /*** file scope type declarations ****************************************************************/
  56 
  57 /*** forward declarations (file scope functions) *************************************************/
  58 
  59 /*** file scope variables ************************************************************************/
  60 
  61 /* --------------------------------------------------------------------------------------------- */
  62 /*** file scope functions ************************************************************************/
  63 /* --------------------------------------------------------------------------------------------- */
  64 
  65 static gboolean
  66 path_magic (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
  67 {
  68     struct stat buf;
  69 
  70     return (stat (path, &buf) != 0);
  71 }
  72 
  73 /* --------------------------------------------------------------------------------------------- */
  74 
  75 /**
  76  * Splits path extracting vfs part.
  77  *
  78  * Splits path
  79  * \verbatim /p1#op/inpath \endverbatim
  80  * into
  81  * \verbatim inpath,op; \endverbatim
  82  * returns which vfs it is.
  83  * What is left in path is p1. You still want to g_free(path), you DON'T
  84  * want to free neither *inpath nor *op
  85  */
  86 
  87 static struct vfs_class *
  88 _vfs_split_with_semi_skip_count (char *path, const char **inpath, const char **op,
     /* [previous][next][first][last][top][bottom][index][help]  */
  89                                  size_t skip_count)
  90 {
  91     char *semi;
  92     char *slash;
  93     struct vfs_class *ret;
  94 
  95     if (path == NULL)
  96         vfs_die ("Cannot split NULL");
  97 
  98     semi = strrstr_skip_count (path, "#", skip_count);
  99 
 100     if ((semi == NULL) || (!path_magic (path)))
 101         return NULL;
 102 
 103     slash = strchr (semi, PATH_SEP);
 104     *semi = '\0';
 105 
 106     if (op != NULL)
 107         *op = NULL;
 108 
 109     if (inpath != NULL)
 110         *inpath = NULL;
 111 
 112     if (slash != NULL)
 113         *slash = '\0';
 114 
 115     ret = vfs_prefix_to_class (semi + 1);
 116     if (ret != NULL)
 117     {
 118         if (op != NULL)
 119             *op = semi + 1;
 120         if (inpath != NULL)
 121             *inpath = slash != NULL ? slash + 1 : NULL;
 122         return ret;
 123     }
 124 
 125     if (slash != NULL)
 126         *slash = PATH_SEP;
 127 
 128     *semi = '#';
 129     ret = _vfs_split_with_semi_skip_count (path, inpath, op, skip_count + 1);
 130     return ret;
 131 }
 132 
 133 /* --------------------------------------------------------------------------------------------- */
 134 /**
 135  * remove //, /./ and /../
 136  *
 137  * @return newly allocated string
 138  */
 139 
 140 static char *
 141 vfs_canon (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 142 {
 143     char *result;
 144 
 145     if (path == NULL)
 146         vfs_die ("Cannot canonicalize NULL");
 147 
 148     if (!IS_PATH_SEP (*path))
 149     {
 150         /* Relative to current directory */
 151 
 152         char *local;
 153 
 154 #ifdef HAVE_CHARSET
 155         if (g_str_has_prefix (path, VFS_ENCODING_PREFIX))
 156         {
 157             /*
 158                encoding prefix placed at start of string without the leading slash
 159                should be autofixed by adding the leading slash
 160              */
 161             local = mc_build_filename (PATH_SEP_STR, path, (char *) NULL);
 162         }
 163         else
 164 #endif
 165         {
 166             const char *curr_dir;
 167 
 168             curr_dir = vfs_get_current_dir ();
 169             local = mc_build_filename (curr_dir, path, (char *) NULL);
 170         }
 171         result = vfs_canon (local);
 172         g_free (local);
 173     }
 174     else
 175     {
 176         /* Absolute path */
 177 
 178         result = g_strdup (path);
 179         canonicalize_pathname (result);
 180     }
 181 
 182     return result;
 183 }
 184 
 185 /* --------------------------------------------------------------------------------------------- */
 186 /**  Extract the hostname and username from the path
 187  *
 188  * Format of the path is [user@]hostname:port/remote-dir, e.g.:
 189  *
 190  * ftp://sunsite.unc.edu/pub/linux
 191  * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
 192  * ftp://tsx-11.mit.edu:8192/
 193  * ftp://joe@foo.edu:11321/private
 194  * ftp://joe:password@foo.se
 195  *
 196  * @param path_element is an input string to be parsed
 197  * @param path is an input string to be parsed
 198  *
 199  * @return g_malloc()ed url info.
 200  *         If the user is empty, e.g. ftp://@roxanne/private, and URL_USE_ANONYMOUS
 201  *         is not set, then the current login name is supplied.
 202  *         Return value is a g_malloc()ed structure with the pathname relative to the
 203  *         host.
 204  */
 205 
 206 static void
 207 vfs_path_url_split (vfs_path_element_t *path_element, const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 208 {
 209     char *pcopy;
 210     char *colon, *at, *rest;
 211 
 212     path_element->port = 0;
 213 
 214     pcopy = g_strdup (path);
 215 
 216     /* search for any possible user */
 217     at = strrchr (pcopy, '@');
 218 
 219     /* We have a username */
 220     if (at == NULL)
 221         rest = pcopy;
 222     else
 223     {
 224         const char *pend;
 225         char *inner_colon;
 226 
 227         pend = strchr (at, '\0');
 228         *at = '\0';
 229 
 230         inner_colon = strchr (pcopy, ':');
 231         if (inner_colon != NULL)
 232         {
 233             *inner_colon = '\0';
 234             inner_colon++;
 235             path_element->password = g_strdup (inner_colon);
 236         }
 237 
 238         if (*pcopy != '\0')
 239             path_element->user = g_strdup (pcopy);
 240 
 241         if (pend == at + 1)
 242             rest = at;
 243         else
 244             rest = at + 1;
 245     }
 246 
 247     /* Check if the host comes with a port spec, if so, chop it */
 248     if (*rest != '[')
 249         colon = strchr (rest, ':');
 250     else
 251     {
 252         colon = strchr (++rest, ']');
 253         if (colon != NULL)
 254         {
 255             *colon = '\0';
 256             colon++;
 257             *colon = '\0';
 258             path_element->ipv6 = TRUE;
 259         }
 260     }
 261 
 262     if (colon != NULL)
 263     {
 264         *colon = '\0';
 265         /* cppcheck-suppress invalidscanf */
 266         if (sscanf (colon + 1, "%d", &path_element->port) == 1)
 267         {
 268             if (path_element->port <= 0 || path_element->port >= 65536)
 269                 path_element->port = 0;
 270         }
 271         else
 272             while (*(++colon) != '\0')
 273             {
 274                 switch (*colon)
 275                 {
 276                 case 'C':
 277                     path_element->port = 1;
 278                     break;
 279                 case 'r':
 280                     path_element->port = 2;
 281                     break;
 282                 default:
 283                     break;
 284                 }
 285             }
 286     }
 287     path_element->host = g_strdup (rest);
 288     g_free (pcopy);
 289 }
 290 
 291 /* --------------------------------------------------------------------------------------------- */
 292 /**
 293  * get VFS class for the given name
 294  *
 295  * @param class_name name of class
 296  *
 297  * @return pointer to class structure or NULL if class not found
 298  */
 299 
 300 static struct vfs_class *
 301 vfs_get_class_by_name (const char *class_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 302 {
 303     guint i;
 304 
 305     if (class_name == NULL)
 306         return NULL;
 307 
 308     for (i = 0; i < vfs__classes_list->len; i++)
 309     {
 310         struct vfs_class *vfs = VFS_CLASS (g_ptr_array_index (vfs__classes_list, i));
 311         if ((vfs->name != NULL) && (strcmp (vfs->name, class_name) == 0))
 312             return vfs;
 313     }
 314 
 315     return NULL;
 316 }
 317 
 318 /* --------------------------------------------------------------------------------------------- */
 319 /**
 320  * Check if path string contain URL-like elements
 321  *
 322  * @param path_str path
 323  *
 324  * @return TRUE if path is deprecated or FALSE otherwise
 325  */
 326 
 327 static gboolean
 328 vfs_path_is_str_path_deprecated (const char *path_str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 329 {
 330     return strstr (path_str, VFS_PATH_URL_DELIMITER) == NULL;
 331 }
 332 
 333 /* --------------------------------------------------------------------------------------------- */
 334 /** Split path string to path elements by deprecated algorithm.
 335  *
 336  * @param path_str VFS-path
 337  *
 338  * @return pointer to newly created vfs_path_t object with filled path elements array.
 339 */
 340 
 341 static vfs_path_t *
 342 vfs_path_from_str_deprecated_parser (char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 343 {
 344     vfs_path_t *vpath;
 345     vfs_path_element_t *element;
 346     struct vfs_class *class;
 347     const char *local, *op;
 348 
 349     vpath = vfs_path_new (FALSE);
 350 
 351     while ((class = _vfs_split_with_semi_skip_count (path, &local, &op, 0)) != NULL)
 352     {
 353         char *url_params;
 354         element = g_new0 (vfs_path_element_t, 1);
 355         element->class = class;
 356         if (local == NULL)
 357             local = "";
 358         element->path = vfs_translate_path_n (local);
 359 
 360 #ifdef HAVE_CHARSET
 361         element->encoding = vfs_get_encoding (local, -1);
 362         element->dir.converter =
 363             (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
 364 #endif
 365 
 366         url_params = strchr (op, ':');  /* skip VFS prefix */
 367         if (url_params != NULL)
 368         {
 369             *url_params = '\0';
 370             url_params++;
 371             vfs_path_url_split (element, url_params);
 372         }
 373 
 374         if (*op != '\0')
 375             element->vfs_prefix = g_strdup (op);
 376 
 377         g_array_prepend_val (vpath->path, element);
 378     }
 379     if (path[0] != '\0')
 380     {
 381         element = g_new0 (vfs_path_element_t, 1);
 382         element->class = g_ptr_array_index (vfs__classes_list, 0);
 383         element->path = vfs_translate_path_n (path);
 384 
 385 #ifdef HAVE_CHARSET
 386         element->encoding = vfs_get_encoding (path, -1);
 387         element->dir.converter =
 388             (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
 389 #endif
 390         g_array_prepend_val (vpath->path, element);
 391     }
 392 
 393     return vpath;
 394 }
 395 
 396 /* --------------------------------------------------------------------------------------------- */
 397 /** Split path string to path elements by URL algorithm.
 398  *
 399  * @param path_str VFS-path
 400  * @param flags    flags for converter
 401  *
 402  * @return pointer to newly created vfs_path_t object with filled path elements array.
 403 */
 404 
 405 static vfs_path_t *
 406 vfs_path_from_str_uri_parser (char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 407 {
 408     gboolean path_is_absolute;
 409     vfs_path_t *vpath;
 410     vfs_path_element_t *element;
 411     char *url_delimiter;
 412 
 413     if (path == NULL)
 414         return vfs_path_new (FALSE);
 415 
 416     path_is_absolute = IS_PATH_SEP (*path);
 417 #ifdef HAVE_CHARSET
 418     path_is_absolute = path_is_absolute || g_str_has_prefix (path, VFS_ENCODING_PREFIX);
 419 #endif
 420 
 421     vpath = vfs_path_new (!path_is_absolute);
 422 
 423     while ((url_delimiter = g_strrstr (path, VFS_PATH_URL_DELIMITER)) != NULL)
 424     {
 425         char *vfs_prefix_start;
 426         char *real_vfs_prefix_start = url_delimiter;
 427 
 428         while (real_vfs_prefix_start > path && !IS_PATH_SEP (*real_vfs_prefix_start))
 429             real_vfs_prefix_start--;
 430         vfs_prefix_start = real_vfs_prefix_start;
 431 
 432         if (IS_PATH_SEP (*vfs_prefix_start))
 433             vfs_prefix_start++;
 434 
 435         *url_delimiter = '\0';
 436 
 437         element = g_new0 (vfs_path_element_t, 1);
 438         element->class = vfs_prefix_to_class (vfs_prefix_start);
 439         element->vfs_prefix = g_strdup (vfs_prefix_start);
 440 
 441         url_delimiter += strlen (VFS_PATH_URL_DELIMITER);
 442 
 443         if (element->class != NULL && (element->class->flags & VFSF_REMOTE) != 0)
 444         {
 445             char *slash_pointer;
 446 
 447             slash_pointer = strchr (url_delimiter, PATH_SEP);
 448             if (slash_pointer == NULL)
 449                 element->path = g_strdup ("");
 450             else
 451             {
 452                 element->path = vfs_translate_path_n (slash_pointer + 1);
 453 #ifdef HAVE_CHARSET
 454                 element->encoding = vfs_get_encoding (slash_pointer, -1);
 455 #endif
 456                 *slash_pointer = '\0';
 457             }
 458             vfs_path_url_split (element, url_delimiter);
 459         }
 460         else
 461         {
 462             element->path = vfs_translate_path_n (url_delimiter);
 463 #ifdef HAVE_CHARSET
 464             element->encoding = vfs_get_encoding (url_delimiter, -1);
 465 #endif
 466         }
 467 #ifdef HAVE_CHARSET
 468         element->dir.converter =
 469             (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
 470 #endif
 471         g_array_prepend_val (vpath->path, element);
 472 
 473         if ((real_vfs_prefix_start > path && IS_PATH_SEP (*real_vfs_prefix_start)) ||
 474             (real_vfs_prefix_start == path && !IS_PATH_SEP (*real_vfs_prefix_start)))
 475             *real_vfs_prefix_start = '\0';
 476         else
 477             *(real_vfs_prefix_start + 1) = '\0';
 478     }
 479 
 480     if (path[0] != '\0')
 481     {
 482         element = g_new0 (vfs_path_element_t, 1);
 483         element->class = g_ptr_array_index (vfs__classes_list, 0);
 484         element->path = vfs_translate_path_n (path);
 485 #ifdef HAVE_CHARSET
 486         element->encoding = vfs_get_encoding (path, -1);
 487         element->dir.converter =
 488             (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
 489 #endif
 490         g_array_prepend_val (vpath->path, element);
 491     }
 492 
 493     return vpath;
 494 }
 495 
 496 /* --------------------------------------------------------------------------------------------- */
 497 /**
 498  * Add element's class info to result string (such as VFS name, host, encoding etc)
 499  * This function used as helper only in vfs_path_tokens_get() function
 500  *
 501  * @param element current path element
 502  * @param ret_tokens total tikens for return
 503  * @param element_tokens accumulated element-only tokens
 504  */
 505 
 506 static void
 507 vfs_path_tokens_add_class_info (const vfs_path_element_t *element, GString *ret_tokens,
     /* [previous][next][first][last][top][bottom][index][help]  */
 508                                 GString *element_tokens)
 509 {
 510     if (((element->class->flags & VFSF_LOCAL) == 0 || ret_tokens->len > 0)
 511         && element_tokens->len > 0)
 512     {
 513         GString *url_str;
 514 
 515         if (ret_tokens->len > 0 && !IS_PATH_SEP (ret_tokens->str[ret_tokens->len - 1]))
 516             g_string_append_c (ret_tokens, PATH_SEP);
 517 
 518         g_string_append (ret_tokens, element->vfs_prefix);
 519         g_string_append (ret_tokens, VFS_PATH_URL_DELIMITER);
 520 
 521         url_str = vfs_path_build_url_params_str (element, TRUE);
 522         if (url_str != NULL)
 523         {
 524             g_string_append_len (ret_tokens, url_str->str, url_str->len);
 525             g_string_append_c (ret_tokens, PATH_SEP);
 526             g_string_free (url_str, TRUE);
 527         }
 528     }
 529 
 530 #ifdef HAVE_CHARSET
 531     if (element->encoding != NULL)
 532     {
 533         if (ret_tokens->len > 0 && !IS_PATH_SEP (ret_tokens->str[ret_tokens->len - 1]))
 534             g_string_append (ret_tokens, PATH_SEP_STR);
 535         g_string_append (ret_tokens, VFS_ENCODING_PREFIX);
 536         g_string_append (ret_tokens, element->encoding);
 537         g_string_append (ret_tokens, PATH_SEP_STR);
 538     }
 539 #endif
 540 
 541     g_string_append (ret_tokens, element_tokens->str);
 542 }
 543 
 544 /* --------------------------------------------------------------------------------------------- */
 545 /**
 546  * Strip path to home dir.
 547  * @param dir pointer to string contains full path
 548  */
 549 
 550 static char *
 551 vfs_path_strip_home (const char *dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
 552 {
 553     const char *home_dir = mc_config_get_home_dir ();
 554 
 555     if (home_dir != NULL)
 556     {
 557         size_t len;
 558 
 559         len = strlen (home_dir);
 560 
 561         if (strncmp (dir, home_dir, len) == 0 && (IS_PATH_SEP (dir[len]) || dir[len] == '\0'))
 562             return g_strdup_printf ("~%s", dir + len);
 563     }
 564 
 565     return g_strdup (dir);
 566 }
 567 
 568 /* --------------------------------------------------------------------------------------------- */
 569 /*** public functions ****************************************************************************/
 570 /* --------------------------------------------------------------------------------------------- */
 571 
 572 #define vfs_append_from_path(appendfrom, is_relative) \
 573 { \
 574     if ((flags & VPF_STRIP_HOME) && element_index == 0 && \
 575         (element->class->flags & VFSF_LOCAL) != 0) \
 576     { \
 577         char *stripped_home_str; \
 578         stripped_home_str = vfs_path_strip_home (appendfrom); \
 579         g_string_append (buffer, stripped_home_str); \
 580         g_free (stripped_home_str); \
 581     } \
 582     else \
 583     { \
 584         if (!is_relative && !IS_PATH_SEP (*appendfrom) && *appendfrom != '\0' \
 585             && (buffer->len == 0 || !IS_PATH_SEP (buffer->str[buffer->len - 1]))) \
 586             g_string_append_c (buffer, PATH_SEP); \
 587         g_string_append (buffer, appendfrom); \
 588     } \
 589 }
 590 
 591 /**
 592  * Convert first elements_count elements from vfs_path_t to string representation with flags.
 593  *
 594  * @param vpath pointer to vfs_path_t object
 595  * @param elements_count count of first elements for convert
 596  * @param flags for converter
 597  *
 598  * @return pointer to newly created string.
 599  */
 600 
 601 char *
 602 vfs_path_to_str_flags (const vfs_path_t *vpath, int elements_count, vfs_path_flag_t flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 603 {
 604     int element_index;
 605     GString *buffer;
 606 #ifdef HAVE_CHARSET
 607     GString *recode_buffer = NULL;
 608 #endif
 609 
 610     if (vpath == NULL)
 611         return NULL;
 612 
 613     if (elements_count == 0 || elements_count > vfs_path_elements_count (vpath))
 614         elements_count = vfs_path_elements_count (vpath);
 615 
 616     if (elements_count < 0)
 617         elements_count = vfs_path_elements_count (vpath) + elements_count;
 618 
 619     buffer = g_string_new ("");
 620 
 621     for (element_index = 0; element_index < elements_count; element_index++)
 622     {
 623         const vfs_path_element_t *element;
 624         gboolean is_relative = vpath->relative && (element_index == 0);
 625 
 626         element = vfs_path_get_by_index (vpath, element_index);
 627         if (element->vfs_prefix != NULL)
 628         {
 629             GString *url_str;
 630 
 631             if (!is_relative && (buffer->len == 0 || !IS_PATH_SEP (buffer->str[buffer->len - 1])))
 632                 g_string_append_c (buffer, PATH_SEP);
 633 
 634             g_string_append (buffer, element->vfs_prefix);
 635             g_string_append (buffer, VFS_PATH_URL_DELIMITER);
 636 
 637             url_str = vfs_path_build_url_params_str (element, (flags & VPF_STRIP_PASSWORD) == 0);
 638             if (url_str != NULL)
 639             {
 640                 g_string_append_len (buffer, url_str->str, url_str->len);
 641                 g_string_append_c (buffer, PATH_SEP);
 642                 g_string_free (url_str, TRUE);
 643             }
 644         }
 645 
 646 #ifdef HAVE_CHARSET
 647         if ((flags & VPF_RECODE) == 0 && vfs_path_element_need_cleanup_converter (element))
 648         {
 649             if ((flags & VPF_HIDE_CHARSET) == 0)
 650             {
 651                 if ((!is_relative)
 652                     && (buffer->len == 0 || !IS_PATH_SEP (buffer->str[buffer->len - 1])))
 653                     g_string_append (buffer, PATH_SEP_STR);
 654                 g_string_append (buffer, VFS_ENCODING_PREFIX);
 655                 g_string_append (buffer, element->encoding);
 656             }
 657 
 658             if (recode_buffer == NULL)
 659                 recode_buffer = g_string_sized_new (32);
 660             else
 661                 g_string_set_size (recode_buffer, 0);
 662 
 663             str_vfs_convert_from (element->dir.converter, element->path, recode_buffer);
 664             vfs_append_from_path (recode_buffer->str, is_relative);
 665         }
 666         else
 667 #endif
 668         {
 669             vfs_append_from_path (element->path, is_relative);
 670         }
 671     }
 672 
 673 #ifdef HAVE_CHARSET
 674     if (recode_buffer != NULL)
 675         g_string_free (recode_buffer, TRUE);
 676 #endif
 677 
 678     return g_string_free (buffer, FALSE);
 679 }
 680 
 681 #undef vfs_append_from_path
 682 
 683 /* --------------------------------------------------------------------------------------------- */
 684 /**
 685  * Convert first elements_count elements from vfs_path_t to string representation.
 686  *
 687  * @param vpath pointer to vfs_path_t object
 688  * @param elements_count count of first elements for convert
 689  *
 690  * @return pointer to newly created string.
 691  */
 692 
 693 char *
 694 vfs_path_to_str_elements_count (const vfs_path_t *vpath, int elements_count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 695 {
 696     return vfs_path_to_str_flags (vpath, elements_count, VPF_NONE);
 697 }
 698 
 699 /* --------------------------------------------------------------------------------------------- */
 700 /**
 701  * Split path string to path elements with flags for change parce process.
 702  *
 703  * @param path_str VFS-path
 704  * @param flags flags for parser
 705  *
 706  * @return pointer to newly created vfs_path_t object with filled path elements array.
 707  */
 708 
 709 vfs_path_t *
 710 vfs_path_from_str_flags (const char *path_str, vfs_path_flag_t flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 711 {
 712     vfs_path_t *vpath;
 713     char *path;
 714 
 715     if (path_str == NULL)
 716         return NULL;
 717 
 718     if ((flags & VPF_NO_CANON) == 0)
 719         path = vfs_canon (path_str);
 720     else
 721         path = g_strdup (path_str);
 722 
 723     if (path == NULL)
 724         return NULL;
 725 
 726     if ((flags & VPF_USE_DEPRECATED_PARSER) != 0 && vfs_path_is_str_path_deprecated (path))
 727         vpath = vfs_path_from_str_deprecated_parser (path);
 728     else
 729         vpath = vfs_path_from_str_uri_parser (path);
 730 
 731     vpath->str = vfs_path_to_str_flags (vpath, 0, flags);
 732     g_free (path);
 733 
 734     return vpath;
 735 }
 736 
 737 /* --------------------------------------------------------------------------------------------- */
 738 /**
 739  * Split path string to path elements.
 740  *
 741  * @param path_str VFS-path
 742  *
 743  * @return pointer to newly created vfs_path_t object with filled path elements array.
 744  */
 745 
 746 vfs_path_t *
 747 vfs_path_from_str (const char *path_str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 748 {
 749     return vfs_path_from_str_flags (path_str, VPF_NONE);
 750 }
 751 
 752 /* --------------------------------------------------------------------------------------------- */
 753 /*
 754  * Create new vfs_path_t object.
 755  *
 756  * @return pointer to newly created vfs_path_t object.
 757  */
 758 
 759 vfs_path_t *
 760 vfs_path_new (gboolean relative)
     /* [previous][next][first][last][top][bottom][index][help]  */
 761 {
 762     vfs_path_t *vpath;
 763 
 764     vpath = g_new0 (vfs_path_t, 1);
 765     vpath->path = g_array_new (FALSE, TRUE, sizeof (vfs_path_element_t *));
 766     vpath->relative = relative;
 767 
 768     return vpath;
 769 }
 770 
 771 /* --------------------------------------------------------------------------------------------- */
 772 /*
 773  * Get count of path elements.
 774  *
 775  * @param vpath pointer to vfs_path_t object
 776  *
 777  * @return count of path elements.
 778  */
 779 
 780 int
 781 vfs_path_elements_count (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 782 {
 783     return (vpath != NULL && vpath->path != NULL) ? vpath->path->len : 0;
 784 }
 785 
 786 /* --------------------------------------------------------------------------------------------- */
 787 /**
 788  * Add vfs_path_element_t object to end of list in vfs_path_t object
 789  * @param vpath pointer to vfs_path_t object
 790  * @param path_element pointer to vfs_path_element_t object
 791  */
 792 
 793 void
 794 vfs_path_add_element (vfs_path_t *vpath, const vfs_path_element_t *path_element)
     /* [previous][next][first][last][top][bottom][index][help]  */
 795 {
 796     g_array_append_val (vpath->path, path_element);
 797     g_free (vpath->str);
 798     vpath->str = vfs_path_to_str_flags (vpath, 0, VPF_NONE);
 799 }
 800 
 801 /* --------------------------------------------------------------------------------------------- */
 802 /*
 803  * Get one path element by index.
 804  *
 805  * @param vpath pointer to vfs_path_t object.
 806  *              May be NULL. In this case NULL is returned and errno set to 0.
 807  * @param element_index element index. May have negative value (in this case count was started at
 808  *                      the end of list). If @element_index is out of range, NULL is returned and
 809  *                      errno set to EINVAL.
 810  *
 811  * @return path element
 812  */
 813 
 814 const vfs_path_element_t *
 815 vfs_path_get_by_index (const vfs_path_t *vpath, int element_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
 816 {
 817     int n;
 818 
 819     if (vpath == NULL)
 820     {
 821         errno = 0;
 822         return NULL;
 823     }
 824 
 825     n = vfs_path_elements_count (vpath);
 826 
 827     if (element_index < 0)
 828         element_index += n;
 829 
 830     if (element_index < 0 || element_index > n)
 831     {
 832         errno = EINVAL;
 833         return NULL;
 834     }
 835 
 836     return g_array_index (vpath->path, vfs_path_element_t *, element_index);
 837 }
 838 
 839 /* --------------------------------------------------------------------------------------------- */
 840 /*
 841  * Clone one path element
 842  *
 843  * @param element pointer to vfs_path_element_t object
 844  *
 845  * @return Newly allocated path element
 846  */
 847 
 848 vfs_path_element_t *
 849 vfs_path_element_clone (const vfs_path_element_t *element)
     /* [previous][next][first][last][top][bottom][index][help]  */
 850 {
 851     vfs_path_element_t *new_element = g_new (vfs_path_element_t, 1);
 852 
 853     new_element->user = g_strdup (element->user);
 854     new_element->password = g_strdup (element->password);
 855     new_element->host = g_strdup (element->host);
 856     new_element->ipv6 = element->ipv6;
 857     new_element->port = element->port;
 858     new_element->path = g_strdup (element->path);
 859     new_element->class = element->class;
 860     new_element->vfs_prefix = g_strdup (element->vfs_prefix);
 861 #ifdef HAVE_CHARSET
 862     new_element->encoding = g_strdup (element->encoding);
 863     if (vfs_path_element_need_cleanup_converter (element) && element->encoding != NULL)
 864         new_element->dir.converter = str_crt_conv_from (element->encoding);
 865     else
 866         new_element->dir.converter = element->dir.converter;
 867 #endif
 868     new_element->dir.info = element->dir.info;
 869 
 870     return new_element;
 871 }
 872 
 873 /* --------------------------------------------------------------------------------------------- */
 874 /*
 875  * Free one path element.
 876  *
 877  * @param element pointer to vfs_path_element_t object
 878  *
 879  */
 880 
 881 void
 882 vfs_path_element_free (vfs_path_element_t *element)
     /* [previous][next][first][last][top][bottom][index][help]  */
 883 {
 884     if (element == NULL)
 885         return;
 886 
 887     g_free (element->user);
 888     g_free (element->password);
 889     g_free (element->host);
 890     g_free (element->path);
 891     g_free (element->vfs_prefix);
 892 
 893 #ifdef HAVE_CHARSET
 894     g_free (element->encoding);
 895 
 896     if (vfs_path_element_need_cleanup_converter (element))
 897         str_close_conv (element->dir.converter);
 898 #endif
 899 
 900     g_free (element);
 901 }
 902 
 903 /* --------------------------------------------------------------------------------------------- */
 904 /*
 905  * Clone path
 906  *
 907  * @param vpath pointer to vfs_path_t object
 908  *
 909  * @return Newly allocated path object
 910  */
 911 
 912 vfs_path_t *
 913 vfs_path_clone (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 914 {
 915     vfs_path_t *new_vpath;
 916     int vpath_element_index;
 917 
 918     if (vpath == NULL)
 919         return NULL;
 920 
 921     new_vpath = vfs_path_new (vpath->relative);
 922 
 923     for (vpath_element_index = 0; vpath_element_index < vfs_path_elements_count (vpath);
 924          vpath_element_index++)
 925     {
 926         vfs_path_element_t *path_element;
 927 
 928         path_element = vfs_path_element_clone (vfs_path_get_by_index (vpath, vpath_element_index));
 929         g_array_append_val (new_vpath->path, path_element);
 930     }
 931     new_vpath->str = g_strdup (vpath->str);
 932 
 933     return new_vpath;
 934 }
 935 
 936 /* --------------------------------------------------------------------------------------------- */
 937 /*
 938  * Free vfs_path_t object.
 939  *
 940  * @param vpath pointer to vfs_path_t object
 941  * @param free_str if TRUE the string representation of vpath is freed as well
 942  *
 943  * @return the string representation of vpath (i.e. NULL if free_str is TRUE)
 944  */
 945 
 946 char *
 947 vfs_path_free (vfs_path_t *vpath, gboolean free_str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 948 {
 949     int vpath_element_index;
 950     char *ret;
 951 
 952     if (vpath == NULL)
 953         return NULL;
 954 
 955     for (vpath_element_index = 0; vpath_element_index < vfs_path_elements_count (vpath);
 956          vpath_element_index++)
 957     {
 958         vfs_path_element_t *path_element;
 959 
 960         path_element = (vfs_path_element_t *) vfs_path_get_by_index (vpath, vpath_element_index);
 961         vfs_path_element_free (path_element);
 962     }
 963 
 964     g_array_free (vpath->path, TRUE);
 965 
 966     if (!free_str)
 967         ret = vpath->str;
 968     else
 969     {
 970         g_free (vpath->str);
 971         ret = NULL;
 972     }
 973 
 974     g_free (vpath);
 975 
 976     return ret;
 977 }
 978 
 979 /* --------------------------------------------------------------------------------------------- */
 980 /*
 981  * Remove one path element by index
 982  *
 983  * @param vpath pointer to vfs_path_t object
 984  * @param element_index element index. May have negative value (in this case count was started at the end of list).
 985  *
 986  */
 987 
 988 void
 989 vfs_path_remove_element_by_index (vfs_path_t *vpath, int element_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
 990 {
 991     vfs_path_element_t *element;
 992 
 993     if ((vpath == NULL) || (vfs_path_elements_count (vpath) == 1))
 994         return;
 995 
 996     if (element_index < 0)
 997         element_index = vfs_path_elements_count (vpath) + element_index;
 998 
 999     element = (vfs_path_element_t *) vfs_path_get_by_index (vpath, element_index);
1000     vpath->path = g_array_remove_index (vpath->path, element_index);
1001     vfs_path_element_free (element);
1002     g_free (vpath->str);
1003     vpath->str = vfs_path_to_str_flags (vpath, 0, VPF_NONE);
1004 }
1005 
1006 /* --------------------------------------------------------------------------------------------- */
1007 /** Return VFS class for the given prefix */
1008 
1009 struct vfs_class *
1010 vfs_prefix_to_class (const char *prefix)
     /* [previous][next][first][last][top][bottom][index][help]  */
1011 {
1012     guint i;
1013 
1014     /* Avoid first class (localfs) that would accept any prefix */
1015     for (i = 1; i < vfs__classes_list->len; i++)
1016     {
1017         struct vfs_class *vfs;
1018 
1019         vfs = VFS_CLASS (g_ptr_array_index (vfs__classes_list, i));
1020         if (vfs->which != NULL)
1021         {
1022             if (vfs->which (vfs, prefix) == -1)
1023                 continue;
1024             return vfs;
1025         }
1026 
1027         if (vfs->prefix != NULL && strncmp (prefix, vfs->prefix, strlen (vfs->prefix)) == 0)
1028             return vfs;
1029     }
1030 
1031     return NULL;
1032 }
1033 
1034 /* --------------------------------------------------------------------------------------------- */
1035 
1036 #ifdef HAVE_CHARSET
1037 
1038 /** get encoding after last #enc: or NULL, if part does not contain #enc:
1039  *
1040  * @param path null-terminated string
1041  * @param len the maximum length of path, where #enc: should be searched
1042  *
1043  * @return newly allocated string.
1044  */
1045 
1046 char *
1047 vfs_get_encoding (const char *path, ssize_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
1048 {
1049     char *semi;
1050 
1051     /* try found #enc: */
1052     semi = g_strrstr_len (path, len, VFS_ENCODING_PREFIX);
1053     if (semi == NULL)
1054         return NULL;
1055 
1056     if (semi == path || IS_PATH_SEP (semi[-1]))
1057     {
1058         char *slash;
1059 
1060         semi += strlen (VFS_ENCODING_PREFIX);   /* skip "#enc:" */
1061         slash = strchr (semi, PATH_SEP);
1062         if (slash != NULL)
1063             return g_strndup (semi, slash - semi);
1064         return g_strdup (semi);
1065     }
1066 
1067     return vfs_get_encoding (path, semi - path);
1068 }
1069 
1070 /* --------------------------------------------------------------------------------------------- */
1071 /**
1072  * Check if need cleanup charset converter for vfs_path_element_t
1073  *
1074  * @param element part of path
1075  *
1076  * @return TRUE if need cleanup converter or FALSE otherwise
1077  */
1078 
1079 gboolean
1080 vfs_path_element_need_cleanup_converter (const vfs_path_element_t *element)
     /* [previous][next][first][last][top][bottom][index][help]  */
1081 {
1082     return (element->dir.converter != str_cnv_from_term && element->dir.converter != INVALID_CONV);
1083 }
1084 
1085 /* --------------------------------------------------------------------------------------------- */
1086 /**
1087  * Change encoding for last part (vfs_path_element_t) of vpath
1088  *
1089  * @param vpath pointer to path structure
1090  * encoding name of charset
1091  *
1092  * @return pointer to path structure (for use function in another functions)
1093  */
1094 vfs_path_t *
1095 vfs_path_change_encoding (vfs_path_t *vpath, const char *encoding)
     /* [previous][next][first][last][top][bottom][index][help]  */
1096 {
1097     vfs_path_element_t *path_element;
1098 
1099     path_element = (vfs_path_element_t *) vfs_path_get_by_index (vpath, -1);
1100     /* don't add current encoding */
1101     if ((path_element->encoding != NULL) && (strcmp (encoding, path_element->encoding) == 0))
1102         return vpath;
1103 
1104     g_free (path_element->encoding);
1105     path_element->encoding = g_strdup (encoding);
1106 
1107     if (vfs_path_element_need_cleanup_converter (path_element))
1108         str_close_conv (path_element->dir.converter);
1109 
1110     path_element->dir.converter = str_crt_conv_from (path_element->encoding);
1111 
1112     g_free (vpath->str);
1113     vpath->str = vfs_path_to_str_flags (vpath, 0, VPF_NONE);
1114     return vpath;
1115 }
1116 
1117 #endif
1118 
1119 /* --------------------------------------------------------------------------------------------- */
1120 
1121 /**
1122  * Serialize vfs_path_t object to string
1123  *
1124  * @param vpath data for serialization
1125  * @param error contain pointer to object for handle error code and message
1126  *
1127  * @return serialized vpath as newly allocated string
1128  */
1129 
1130 char *
1131 vfs_path_serialize (const vfs_path_t *vpath, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
1132 {
1133     mc_config_t *cpath;
1134     ssize_t element_index;
1135     char *ret_value;
1136 
1137     mc_return_val_if_error (mcerror, FALSE);
1138 
1139     if ((vpath == NULL) || (vfs_path_elements_count (vpath) == 0))
1140     {
1141         mc_propagate_error (mcerror, 0, "%s", "vpath object is empty");
1142         return NULL;
1143     }
1144 
1145     cpath = mc_config_init (NULL, FALSE);
1146 
1147     for (element_index = 0; element_index < vfs_path_elements_count (vpath); element_index++)
1148     {
1149         char groupname[BUF_TINY];
1150         const vfs_path_element_t *element;
1151 
1152         g_snprintf (groupname, sizeof (groupname), "path-element-%zd", element_index);
1153         element = vfs_path_get_by_index (vpath, element_index);
1154         /* convert one element to config group */
1155 
1156         mc_config_set_string_raw (cpath, groupname, "path", element->path);
1157         mc_config_set_string_raw (cpath, groupname, "class-name", element->class->name);
1158 #ifdef HAVE_CHARSET
1159         mc_config_set_string_raw (cpath, groupname, "encoding", element->encoding);
1160 #endif
1161         mc_config_set_string_raw (cpath, groupname, "vfs_prefix", element->vfs_prefix);
1162 
1163         mc_config_set_string_raw (cpath, groupname, "user", element->user);
1164         mc_config_set_string_raw (cpath, groupname, "password", element->password);
1165         mc_config_set_string_raw (cpath, groupname, "host", element->host);
1166         if (element->port != 0)
1167             mc_config_set_int (cpath, groupname, "port", element->port);
1168     }
1169 
1170     ret_value = mc_serialize_config (cpath, mcerror);
1171     mc_config_deinit (cpath);
1172     return ret_value;
1173 }
1174 
1175 /* --------------------------------------------------------------------------------------------- */
1176 /**
1177  * Deserialize string to vfs_path_t object
1178  *
1179  * @param data data for serialization
1180  * @param error contain pointer to object for handle error code and message
1181  *
1182  * @return newly allocated vfs_path_t object
1183  */
1184 
1185 vfs_path_t *
1186 vfs_path_deserialize (const char *data, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
1187 {
1188     mc_config_t *cpath;
1189     size_t element_index;
1190     vfs_path_t *vpath;
1191 
1192     mc_return_val_if_error (mcerror, FALSE);
1193 
1194     cpath = mc_deserialize_config (data, mcerror);
1195     if (cpath == NULL)
1196         return NULL;
1197 
1198     vpath = vfs_path_new (FALSE);
1199 
1200     for (element_index = 0;; element_index++)
1201     {
1202         struct vfs_class *eclass;
1203         vfs_path_element_t *element;
1204         char *cfg_value;
1205         char groupname[BUF_TINY];
1206 
1207         g_snprintf (groupname, sizeof (groupname), "path-element-%zu", element_index);
1208         if (!mc_config_has_group (cpath, groupname))
1209             break;
1210 
1211         cfg_value = mc_config_get_string_raw (cpath, groupname, "class-name", NULL);
1212         eclass = vfs_get_class_by_name (cfg_value);
1213         if (eclass == NULL)
1214         {
1215             vfs_path_free (vpath, TRUE);
1216             g_set_error (mcerror, MC_ERROR, 0, "Unable to find VFS class by name '%s'", cfg_value);
1217             g_free (cfg_value);
1218             mc_config_deinit (cpath);
1219             return NULL;
1220         }
1221         g_free (cfg_value);
1222 
1223         element = g_new0 (vfs_path_element_t, 1);
1224         element->class = eclass;
1225         element->path = mc_config_get_string_raw (cpath, groupname, "path", NULL);
1226 
1227 #ifdef HAVE_CHARSET
1228         element->encoding = mc_config_get_string_raw (cpath, groupname, "encoding", NULL);
1229         element->dir.converter =
1230             (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
1231 #endif
1232 
1233         element->vfs_prefix = mc_config_get_string_raw (cpath, groupname, "vfs_prefix", NULL);
1234 
1235         element->user = mc_config_get_string_raw (cpath, groupname, "user", NULL);
1236         element->password = mc_config_get_string_raw (cpath, groupname, "password", NULL);
1237         element->host = mc_config_get_string_raw (cpath, groupname, "host", NULL);
1238         element->port = mc_config_get_int (cpath, groupname, "port", 0);
1239 
1240         vpath->path = g_array_append_val (vpath->path, element);
1241     }
1242 
1243     mc_config_deinit (cpath);
1244     if (vfs_path_elements_count (vpath) == 0)
1245     {
1246         vfs_path_free (vpath, TRUE);
1247         g_set_error (mcerror, MC_ERROR, 0, "No any path elements found");
1248         return NULL;
1249     }
1250     vpath->str = vfs_path_to_str_flags (vpath, 0, VPF_NONE);
1251 
1252     return vpath;
1253 }
1254 
1255 /* --------------------------------------------------------------------------------------------- */
1256 /**
1257  * Build vfs_path_t object from arguments.
1258  *
1259  * @param first_element of path
1260  * @param ... path tokens, terminated by NULL
1261  *
1262  * @return newly allocated vfs_path_t object
1263  */
1264 
1265 vfs_path_t *
1266 vfs_path_build_filename (const char *first_element, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
1267 {
1268     va_list args;
1269     char *str_path;
1270     vfs_path_t *vpath;
1271 
1272     if (first_element == NULL)
1273         return NULL;
1274 
1275     va_start (args, first_element);
1276     str_path = mc_build_filenamev (first_element, args);
1277     va_end (args);
1278     vpath = vfs_path_from_str (str_path);
1279     g_free (str_path);
1280     return vpath;
1281 }
1282 
1283 /* --------------------------------------------------------------------------------------------- */
1284 /**
1285  * Append tokens to path object
1286  *
1287  * @param vpath path object
1288  * @param first_element of path
1289  * @param ... NULL-terminated strings
1290  *
1291  * @return newly allocated path object
1292  */
1293 
1294 vfs_path_t *
1295 vfs_path_append_new (const vfs_path_t *vpath, const char *first_element, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
1296 {
1297     va_list args;
1298     char *str_path;
1299     const char *result_str;
1300     vfs_path_t *ret_vpath;
1301 
1302     if (vpath == NULL || first_element == NULL)
1303         return NULL;
1304 
1305     va_start (args, first_element);
1306     str_path = mc_build_filenamev (first_element, args);
1307     va_end (args);
1308 
1309     result_str = vfs_path_as_str (vpath);
1310     ret_vpath = vfs_path_build_filename (result_str, str_path, (char *) NULL);
1311     g_free (str_path);
1312 
1313     return ret_vpath;
1314 
1315 }
1316 
1317 /* --------------------------------------------------------------------------------------------- */
1318 
1319 /**
1320  * Append vpath_t tokens to path object
1321  *
1322  * @param first_vpath vpath objects
1323  * @param ... NULL-terminated vpath objects
1324  *
1325  * @return newly allocated path object
1326  */
1327 
1328 vfs_path_t *
1329 vfs_path_append_vpath_new (const vfs_path_t *first_vpath, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
1330 {
1331     va_list args;
1332     vfs_path_t *ret_vpath;
1333     const vfs_path_t *current_vpath = first_vpath;
1334 
1335     if (first_vpath == NULL)
1336         return NULL;
1337 
1338     ret_vpath = vfs_path_new (FALSE);
1339 
1340     va_start (args, first_vpath);
1341     do
1342     {
1343         int vindex;
1344 
1345         for (vindex = 0; vindex < vfs_path_elements_count (current_vpath); vindex++)
1346         {
1347             vfs_path_element_t *path_element;
1348 
1349             path_element = vfs_path_element_clone (vfs_path_get_by_index (current_vpath, vindex));
1350             g_array_append_val (ret_vpath->path, path_element);
1351         }
1352         current_vpath = va_arg (args, const vfs_path_t *);
1353     }
1354     while (current_vpath != NULL);
1355     va_end (args);
1356 
1357     ret_vpath->str = vfs_path_to_str_flags (ret_vpath, 0, VPF_NONE);
1358 
1359     return ret_vpath;
1360 }
1361 
1362 /* --------------------------------------------------------------------------------------------- */
1363 
1364 /**
1365  * get tokens count in path.
1366  *
1367  * @param vpath path object
1368  *
1369  * @return count of tokens
1370  */
1371 
1372 size_t
1373 vfs_path_tokens_count (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1374 {
1375     size_t count_tokens = 0;
1376     int element_index;
1377 
1378     if (vpath == NULL)
1379         return 0;
1380 
1381     for (element_index = 0; element_index < vfs_path_elements_count (vpath); element_index++)
1382     {
1383         const vfs_path_element_t *element;
1384         const char *token, *prev_token;
1385 
1386         element = vfs_path_get_by_index (vpath, element_index);
1387 
1388         for (prev_token = element->path; (token = strchr (prev_token, PATH_SEP)) != NULL;
1389              prev_token = token + 1)
1390         {
1391             /* skip empty substring */
1392             if (token != prev_token)
1393                 count_tokens++;
1394         }
1395 
1396         if (*prev_token != '\0')
1397             count_tokens++;
1398     }
1399 
1400     return count_tokens;
1401 }
1402 
1403 /* --------------------------------------------------------------------------------------------- */
1404 
1405 /**
1406  * Get subpath by tokens
1407  *
1408  * @param vpath path object
1409  * @param start_position first token for got/ Started from 0.
1410  *        If negative, then position will be relative to end of path
1411  * @param length count of tokens
1412  *
1413  * @return newly allocated string with path tokens separated by slash
1414  */
1415 
1416 char *
1417 vfs_path_tokens_get (const vfs_path_t *vpath, ssize_t start_position, ssize_t length)
     /* [previous][next][first][last][top][bottom][index][help]  */
1418 {
1419     GString *ret_tokens, *element_tokens;
1420     int element_index;
1421     size_t tokens_count = vfs_path_tokens_count (vpath);
1422 
1423     if (vpath == NULL)
1424         return NULL;
1425 
1426     if (length == 0)
1427         length = tokens_count;
1428 
1429     if (length < 0)
1430         length = tokens_count + length;
1431 
1432     if (start_position < 0)
1433         start_position = (ssize_t) tokens_count + start_position;
1434 
1435     if (start_position < 0)
1436         return NULL;
1437 
1438     if (start_position >= (ssize_t) tokens_count)
1439         return NULL;
1440 
1441     if (start_position + (ssize_t) length > (ssize_t) tokens_count)
1442         length = tokens_count - start_position;
1443 
1444     ret_tokens = g_string_sized_new (32);
1445     element_tokens = g_string_sized_new (32);
1446 
1447     for (element_index = 0; element_index < vfs_path_elements_count (vpath); element_index++)
1448     {
1449         const vfs_path_element_t *element;
1450         char **path_tokens, **iterator;
1451 
1452         g_string_assign (element_tokens, "");
1453         element = vfs_path_get_by_index (vpath, element_index);
1454         path_tokens = g_strsplit (element->path, PATH_SEP_STR, -1);
1455 
1456         for (iterator = path_tokens; *iterator != NULL; iterator++)
1457         {
1458             if (**iterator != '\0')
1459             {
1460                 if (start_position == 0)
1461                 {
1462                     if (length == 0)
1463                     {
1464                         vfs_path_tokens_add_class_info (element, ret_tokens, element_tokens);
1465                         g_string_free (element_tokens, TRUE);
1466                         g_strfreev (path_tokens);
1467                         return g_string_free (ret_tokens, FALSE);
1468                     }
1469                     length--;
1470                     if (element_tokens->len != 0)
1471                         g_string_append_c (element_tokens, PATH_SEP);
1472                     g_string_append (element_tokens, *iterator);
1473                 }
1474                 else
1475                     start_position--;
1476             }
1477         }
1478         g_strfreev (path_tokens);
1479         vfs_path_tokens_add_class_info (element, ret_tokens, element_tokens);
1480     }
1481 
1482     g_string_free (element_tokens, TRUE);
1483     return g_string_free (ret_tokens, !(start_position == 0 && length == 0));
1484 }
1485 
1486 /* --------------------------------------------------------------------------------------------- */
1487 /**
1488  * Get subpath by tokens
1489  *
1490  * @param vpath path object
1491  * @param start_position first token for got/ Started from 0.
1492  *        If negative, then position will be relative to end of path
1493  * @param length count of tokens
1494  *
1495  * @return newly allocated path object with path tokens separated by slash
1496  */
1497 
1498 vfs_path_t *
1499 vfs_path_vtokens_get (const vfs_path_t *vpath, ssize_t start_position, ssize_t length)
     /* [previous][next][first][last][top][bottom][index][help]  */
1500 {
1501     char *str_tokens;
1502     vfs_path_t *ret_vpath = NULL;
1503 
1504     str_tokens = vfs_path_tokens_get (vpath, start_position, length);
1505     if (str_tokens != NULL)
1506     {
1507         ret_vpath = vfs_path_from_str_flags (str_tokens, VPF_NO_CANON);
1508         g_free (str_tokens);
1509     }
1510     return ret_vpath;
1511 }
1512 
1513 /* --------------------------------------------------------------------------------------------- */
1514 
1515 /**
1516  * Build URL parameters (such as user:pass @ host:port) from one path element object
1517  *
1518  * @param element path element
1519  * @param keep_password TRUE or FALSE
1520  *
1521  * @return newly allocated non-empty string or NULL
1522  */
1523 
1524 GString *
1525 vfs_path_build_url_params_str (const vfs_path_element_t *element, gboolean keep_password)
     /* [previous][next][first][last][top][bottom][index][help]  */
1526 {
1527     GString *buffer;
1528 
1529     if (element == NULL)
1530         return NULL;
1531 
1532     buffer = g_string_sized_new (64);
1533 
1534     if (element->user != NULL)
1535         g_string_append (buffer, element->user);
1536 
1537     if (element->password != NULL && keep_password)
1538     {
1539         g_string_append_c (buffer, ':');
1540         g_string_append (buffer, element->password);
1541     }
1542 
1543     if (element->host != NULL)
1544     {
1545         if ((element->user != NULL) || (element->password != NULL))
1546             g_string_append_c (buffer, '@');
1547         if (element->ipv6)
1548             g_string_append_c (buffer, '[');
1549         g_string_append (buffer, element->host);
1550         if (element->ipv6)
1551             g_string_append_c (buffer, ']');
1552 
1553         if (element->port != 0)
1554             g_string_append_printf (buffer, ":%d", element->port);
1555     }
1556 
1557     if (buffer->len != 0)
1558         return buffer;
1559 
1560     g_string_free (buffer, TRUE);
1561     return NULL;
1562 }
1563 
1564 /* --------------------------------------------------------------------------------------------- */
1565 /**
1566  * Build pretty string representation of one path_element_t object
1567  *
1568  * @param element path element
1569  *
1570  * @return newly allocated string
1571  */
1572 
1573 GString *
1574 vfs_path_element_build_pretty_path_str (const vfs_path_element_t *element)
     /* [previous][next][first][last][top][bottom][index][help]  */
1575 {
1576     GString *url_params, *pretty_path;
1577 
1578     pretty_path = g_string_new (element->class->prefix);
1579     g_string_append (pretty_path, VFS_PATH_URL_DELIMITER);
1580 
1581     url_params = vfs_path_build_url_params_str (element, FALSE);
1582     if (url_params != NULL)
1583     {
1584         g_string_append_len (pretty_path, url_params->str, url_params->len);
1585         g_string_free (url_params, TRUE);
1586     }
1587 
1588     if (!IS_PATH_SEP (*element->path))
1589         g_string_append_c (pretty_path, PATH_SEP);
1590 
1591     return g_string_append (pretty_path, element->path);
1592 }
1593 
1594 /* --------------------------------------------------------------------------------------------- */
1595 /**
1596  * Compare two path objects as strings
1597  *
1598  * @param vpath1 first path object
1599  * @param vpath2 second vpath object
1600  *
1601  * @return integer value like to strcmp.
1602  */
1603 
1604 gboolean
1605 vfs_path_equal (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1606 {
1607     const char *path1, *path2;
1608     gboolean ret_val;
1609 
1610     if (vpath1 == NULL || vpath2 == NULL)
1611         return FALSE;
1612 
1613     path1 = vfs_path_as_str (vpath1);
1614     path2 = vfs_path_as_str (vpath2);
1615 
1616     ret_val = strcmp (path1, path2) == 0;
1617 
1618     return ret_val;
1619 }
1620 
1621 /* --------------------------------------------------------------------------------------------- */
1622 /**
1623  * Compare two path objects as strings
1624  *
1625  * @param vpath1 first path object
1626  * @param vpath2 second vpath object
1627  * @param len number of first 'len' characters
1628  *
1629  * @return integer value like to strcmp.
1630  */
1631 
1632 gboolean
1633 vfs_path_equal_len (const vfs_path_t *vpath1, const vfs_path_t *vpath2, size_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
1634 {
1635     const char *path1, *path2;
1636     gboolean ret_val;
1637 
1638     if (vpath1 == NULL || vpath2 == NULL)
1639         return FALSE;
1640 
1641     path1 = vfs_path_as_str (vpath1);
1642     path2 = vfs_path_as_str (vpath2);
1643 
1644     ret_val = strncmp (path1, path2, len) == 0;
1645 
1646     return ret_val;
1647 }
1648 
1649 /* --------------------------------------------------------------------------------------------- */
1650 /**
1651  * Calculate path length in string representation
1652  *
1653  * @param vpath path object
1654  *
1655  * @return length of path
1656  */
1657 
1658 size_t
1659 vfs_path_len (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1660 {
1661     if (vpath == NULL)
1662         return 0;
1663 
1664     return strlen (vpath->str);
1665 }
1666 
1667 /* --------------------------------------------------------------------------------------------- */
1668 /**
1669  * Convert relative vpath object to absolute
1670  *
1671  * @param vpath path object
1672  *
1673  * @return absolute path object
1674  */
1675 
1676 vfs_path_t *
1677 vfs_path_to_absolute (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1678 {
1679     vfs_path_t *absolute_vpath;
1680     const char *path_str;
1681 
1682     if (!vpath->relative)
1683         return vfs_path_clone (vpath);
1684 
1685     path_str = vfs_path_as_str (vpath);
1686     absolute_vpath = vfs_path_from_str (path_str);
1687     return absolute_vpath;
1688 }
1689 
1690 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */