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-2025
   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 <https://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 #include <config.h>
  35 
  36 #include <errno.h>
  37 
  38 #include "lib/global.h"
  39 #include "lib/strutil.h"
  40 #include "lib/util.h"  // mc_build_filename()
  41 #include "lib/serialize.h"
  42 
  43 #include "vfs.h"
  44 #include "utilvfs.h"
  45 #include "xdirentry.h"
  46 #include "path.h"
  47 
  48 extern GPtrArray *vfs__classes_list;
  49 
  50 /*** global variables ****************************************************************************/
  51 
  52 /*** file scope macro definitions ****************************************************************/
  53 
  54 /*** file scope type declarations ****************************************************************/
  55 
  56 /*** forward declarations (file scope functions) *************************************************/
  57 
  58 /*** file scope variables ************************************************************************/
  59 
  60 /* --------------------------------------------------------------------------------------------- */
  61 /*** file scope functions ************************************************************************/
  62 /* --------------------------------------------------------------------------------------------- */
  63 
  64 static gboolean
  65 path_magic (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
  66 {
  67     struct stat buf;
  68 
  69     return (stat (path, &buf) != 0);
  70 }
  71 
  72 /* --------------------------------------------------------------------------------------------- */
  73 
  74 /**
  75  * Splits path extracting vfs part.
  76  *
  77  * Splits path
  78  * \verbatim /p1#op/inpath \endverbatim
  79  * into
  80  * \verbatim inpath,op; \endverbatim
  81  * returns which vfs it is.
  82  * What is left in path is p1. You still want to g_free(path), you DON'T
  83  * want to free neither *inpath nor *op
  84  */
  85 
  86 static struct vfs_class *
  87 _vfs_split_with_semi_skip_count (char *path, const char **inpath, const char **op,
     /* [previous][next][first][last][top][bottom][index][help]  */
  88                                  size_t skip_count)
  89 {
  90     char *semi;
  91     char *slash;
  92     struct vfs_class *ret;
  93 
  94     if (path == NULL)
  95         vfs_die ("Cannot split NULL");
  96 
  97     semi = strrstr_skip_count (path, "#", skip_count);
  98 
  99     if ((semi == NULL) || (!path_magic (path)))
 100         return NULL;
 101 
 102     slash = strchr (semi, PATH_SEP);
 103     *semi = '\0';
 104 
 105     if (op != NULL)
 106         *op = NULL;
 107 
 108     if (inpath != NULL)
 109         *inpath = NULL;
 110 
 111     if (slash != NULL)
 112         *slash = '\0';
 113 
 114     ret = vfs_prefix_to_class (semi + 1);
 115     if (ret != NULL)
 116     {
 117         if (op != NULL)
 118             *op = semi + 1;
 119         if (inpath != NULL)
 120             *inpath = slash != NULL ? slash + 1 : NULL;
 121         return ret;
 122     }
 123 
 124     if (slash != NULL)
 125         *slash = PATH_SEP;
 126 
 127     *semi = '#';
 128     ret = _vfs_split_with_semi_skip_count (path, inpath, op, skip_count + 1);
 129     return ret;
 130 }
 131 
 132 /* --------------------------------------------------------------------------------------------- */
 133 /**
 134  * remove //, /./ and /../
 135  *
 136  * @return newly allocated string
 137  */
 138 
 139 static char *
 140 vfs_canon (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 141 {
 142     char *result;
 143 
 144     if (path == NULL)
 145         vfs_die ("Cannot canonicalize NULL");
 146 
 147     if (!IS_PATH_SEP (*path))
 148     {
 149         // Relative to current directory
 150 
 151         char *local;
 152 
 153 #ifdef HAVE_CHARSET
 154         if (g_str_has_prefix (path, VFS_ENCODING_PREFIX))
 155         {
 156             /*
 157                encoding prefix placed at start of string without the leading slash
 158                should be autofixed by adding the leading slash
 159              */
 160             local = mc_build_filename (PATH_SEP_STR, path, (char *) NULL);
 161         }
 162         else
 163 #endif
 164         {
 165             const char *curr_dir;
 166 
 167             curr_dir = vfs_get_current_dir ();
 168             local = mc_build_filename (curr_dir, path, (char *) NULL);
 169         }
 170         result = vfs_canon (local);
 171         g_free (local);
 172     }
 173     else
 174     {
 175         // Absolute path
 176 
 177         result = g_strdup (path);
 178         canonicalize_pathname (result);
 179     }
 180 
 181     return result;
 182 }
 183 
 184 /* --------------------------------------------------------------------------------------------- */
 185 /**  Extract the hostname and username from the path
 186  *
 187  * Format of the path is [user@]hostname:port/remote-dir, e.g.:
 188  *
 189  * ftp://sunsite.unc.edu/pub/linux
 190  * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
 191  * ftp://tsx-11.mit.edu:8192/
 192  * ftp://joe@foo.edu:11321/private
 193  * ftp://joe:password@foo.se
 194  *
 195  * @param path_element is an input string to be parsed
 196  * @param path is an input string to be parsed
 197  *
 198  * @return g_malloc()ed url info.
 199  *         If the user is empty, e.g. ftp://@roxanne/private, and URL_USE_ANONYMOUS
 200  *         is not set, then the current login name is supplied.
 201  *         Return value is a g_malloc()ed structure with the pathname relative to the
 202  *         host.
 203  */
 204 
 205 static void
 206 vfs_path_url_split (vfs_path_element_t *path_element, const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 207 {
 208     char *pcopy;
 209     char *colon, *at, *rest;
 210 
 211     path_element->port = 0;
 212 
 213     pcopy = g_strdup (path);
 214 
 215     // search for any possible user
 216     at = strrchr (pcopy, '@');
 217 
 218     // We have a username
 219     if (at == NULL)
 220         rest = pcopy;
 221     else
 222     {
 223         const char *pend;
 224         char *inner_colon;
 225 
 226         pend = strchr (at, '\0');
 227         *at = '\0';
 228 
 229         inner_colon = strchr (pcopy, ':');
 230         if (inner_colon != NULL)
 231         {
 232             *inner_colon = '\0';
 233             inner_colon++;
 234             path_element->password = g_strdup (inner_colon);
 235         }
 236 
 237         if (*pcopy != '\0')
 238             path_element->user = g_strdup (pcopy);
 239 
 240         if (pend == at + 1)
 241             rest = at;
 242         else
 243             rest = at + 1;
 244     }
 245 
 246     // Check if the host comes with a port spec, if so, chop it
 247     if (*rest != '[')
 248         colon = strchr (rest, ':');
 249     else
 250     {
 251         colon = strchr (++rest, ']');
 252         if (colon != NULL)
 253         {
 254             *colon = '\0';
 255             colon++;
 256             *colon = '\0';
 257             path_element->ipv6 = TRUE;
 258         }
 259     }
 260 
 261     if (colon != NULL)
 262     {
 263         *colon = '\0';
 264         // cppcheck-suppress invalidscanf
 265         if (sscanf (colon + 1, "%d", &path_element->port) == 1)
 266         {
 267             if (path_element->port <= 0 || path_element->port >= 65536)
 268                 path_element->port = 0;
 269         }
 270         else
 271             while (*(++colon) != '\0')
 272             {
 273                 switch (*colon)
 274                 {
 275                 case 'C':
 276                     path_element->port = 1;
 277                     break;
 278                 case 'r':
 279                     path_element->port = 2;
 280                     break;
 281                 default:
 282                     break;
 283                 }
 284             }
 285     }
 286     path_element->host = g_strdup (rest);
 287     g_free (pcopy);
 288 }
 289 
 290 /* --------------------------------------------------------------------------------------------- */
 291 /**
 292  * get VFS class for the given name
 293  *
 294  * @param class_name name of class
 295  *
 296  * @return pointer to class structure or NULL if class not found
 297  */
 298 
 299 static struct vfs_class *
 300 vfs_get_class_by_name (const char *class_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 301 {
 302     guint i;
 303 
 304     if (class_name == NULL)
 305         return NULL;
 306 
 307     for (i = 0; i < vfs__classes_list->len; i++)
 308     {
 309         struct vfs_class *vfs = VFS_CLASS (g_ptr_array_index (vfs__classes_list, i));
 310         if ((vfs->name != NULL) && (strcmp (vfs->name, class_name) == 0))
 311             return vfs;
 312     }
 313 
 314     return NULL;
 315 }
 316 
 317 /* --------------------------------------------------------------------------------------------- */
 318 /**
 319  * Check if path string contain URL-like elements
 320  *
 321  * @param path_str path
 322  *
 323  * @return TRUE if path is deprecated or FALSE otherwise
 324  */
 325 
 326 static gboolean
 327 vfs_path_is_str_path_deprecated (const char *path_str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 328 {
 329     return strstr (path_str, VFS_PATH_URL_DELIMITER) == NULL;
 330 }
 331 
 332 /* --------------------------------------------------------------------------------------------- */
 333 /** Split path string to path elements by deprecated algorithm.
 334  *
 335  * @param path_str VFS-path
 336  *
 337  * @return pointer to newly created vfs_path_t object with filled path elements array.
 338  */
 339 
 340 static vfs_path_t *
 341 vfs_path_from_str_deprecated_parser (char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 342 {
 343     vfs_path_t *vpath;
 344     vfs_path_element_t *element;
 345     struct vfs_class *class;
 346     const char *local, *op;
 347 
 348     vpath = vfs_path_new (FALSE);
 349 
 350     while ((class = _vfs_split_with_semi_skip_count (path, &local, &op, 0)) != NULL)
 351     {
 352         char *url_params;
 353         element = g_new0 (vfs_path_element_t, 1);
 354         element->class = class;
 355         if (local == NULL)
 356             local = "";
 357         element->path = vfs_translate_path_n (local);
 358 
 359 #ifdef HAVE_CHARSET
 360         element->encoding = vfs_get_encoding (local, -1);
 361         element->dir.converter =
 362             (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
 363 #endif
 364 
 365         url_params = strchr (op, ':');  // skip VFS prefix
 366         if (url_params != NULL)
 367         {
 368             *url_params = '\0';
 369             url_params++;
 370             vfs_path_url_split (element, url_params);
 371         }
 372 
 373         if (*op != '\0')
 374             element->vfs_prefix = g_strdup (op);
 375 
 376         g_array_prepend_val (vpath->path, element);
 377     }
 378     if (path[0] != '\0')
 379     {
 380         element = g_new0 (vfs_path_element_t, 1);
 381         element->class = g_ptr_array_index (vfs__classes_list, 0);
 382         element->path = vfs_translate_path_n (path);
 383 
 384 #ifdef HAVE_CHARSET
 385         element->encoding = vfs_get_encoding (path, -1);
 386         element->dir.converter =
 387             (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
 388 #endif
 389         g_array_prepend_val (vpath->path, element);
 390     }
 391 
 392     return vpath;
 393 }
 394 
 395 /* --------------------------------------------------------------------------------------------- */
 396 /** Split path string to path elements by URL algorithm.
 397  *
 398  * @param path_str VFS-path
 399  * @param flags    flags for converter
 400  *
 401  * @return pointer to newly created vfs_path_t object with filled path elements array.
 402  */
 403 
 404 static vfs_path_t *
 405 vfs_path_from_str_uri_parser (char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 406 {
 407     gboolean path_is_absolute;
 408     vfs_path_t *vpath;
 409     vfs_path_element_t *element;
 410     char *url_delimiter;
 411 
 412     if (path == NULL)
 413         return vfs_path_new (FALSE);
 414 
 415     path_is_absolute = IS_PATH_SEP (*path);
 416 #ifdef HAVE_CHARSET
 417     path_is_absolute = path_is_absolute || g_str_has_prefix (path, VFS_ENCODING_PREFIX);
 418 #endif
 419 
 420     vpath = vfs_path_new (!path_is_absolute);
 421 
 422     while ((url_delimiter = g_strrstr (path, VFS_PATH_URL_DELIMITER)) != NULL)
 423     {
 424         char *vfs_prefix_start;
 425         char *real_vfs_prefix_start = url_delimiter;
 426 
 427         while (real_vfs_prefix_start > path && !IS_PATH_SEP (*real_vfs_prefix_start))
 428             real_vfs_prefix_start--;
 429         vfs_prefix_start = real_vfs_prefix_start;
 430 
 431         if (IS_PATH_SEP (*vfs_prefix_start))
 432             vfs_prefix_start++;
 433 
 434         *url_delimiter = '\0';
 435 
 436         element = g_new0 (vfs_path_element_t, 1);
 437         element->class = vfs_prefix_to_class (vfs_prefix_start);
 438         element->vfs_prefix = g_strdup (vfs_prefix_start);
 439 
 440         url_delimiter += strlen (VFS_PATH_URL_DELIMITER);
 441 
 442         if (element->class != NULL && (element->class->flags & VFSF_REMOTE) != 0)
 443         {
 444             char *slash_pointer;
 445 
 446             slash_pointer = strchr (url_delimiter, PATH_SEP);
 447             if (slash_pointer == NULL)
 448                 element->path = g_strdup ("");
 449             else
 450             {
 451                 element->path = vfs_translate_path_n (slash_pointer + 1);
 452 #ifdef HAVE_CHARSET
 453                 element->encoding = vfs_get_encoding (slash_pointer, -1);
 454 #endif
 455                 *slash_pointer = '\0';
 456             }
 457             vfs_path_url_split (element, url_delimiter);
 458         }
 459         else
 460         {
 461             element->path = vfs_translate_path_n (url_delimiter);
 462 #ifdef HAVE_CHARSET
 463             element->encoding = vfs_get_encoding (url_delimiter, -1);
 464 #endif
 465         }
 466 #ifdef HAVE_CHARSET
 467         element->dir.converter =
 468             (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
 469 #endif
 470         g_array_prepend_val (vpath->path, element);
 471 
 472         if ((real_vfs_prefix_start > path && IS_PATH_SEP (*real_vfs_prefix_start))
 473             || (real_vfs_prefix_start == path && !IS_PATH_SEP (*real_vfs_prefix_start)))
 474             *real_vfs_prefix_start = '\0';
 475         else
 476             *(real_vfs_prefix_start + 1) = '\0';
 477     }
 478 
 479     if (path[0] != '\0')
 480     {
 481         element = g_new0 (vfs_path_element_t, 1);
 482         element->class = g_ptr_array_index (vfs__classes_list, 0);
 483         element->path = vfs_translate_path_n (path);
 484 #ifdef HAVE_CHARSET
 485         element->encoding = vfs_get_encoding (path, -1);
 486         element->dir.converter =
 487             (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
 488 #endif
 489         g_array_prepend_val (vpath->path, element);
 490     }
 491 
 492     return vpath;
 493 }
 494 
 495 /* --------------------------------------------------------------------------------------------- */
 496 /**
 497  * Add element's class info to result string (such as VFS name, host, encoding etc)
 498  * This function used as helper only in vfs_path_tokens_get() function
 499  *
 500  * @param element current path element
 501  * @param ret_tokens total tikens for return
 502  * @param element_tokens accumulated element-only tokens
 503  */
 504 
 505 static void
 506 vfs_path_tokens_add_class_info (const vfs_path_element_t *element, GString *ret_tokens,
     /* [previous][next][first][last][top][bottom][index][help]  */
 507                                 GString *element_tokens)
 508 {
 509     if (((element->class->flags & VFSF_LOCAL) == 0 || ret_tokens->len > 0)
 510         && element_tokens->len > 0)
 511     {
 512         GString *url_str;
 513 
 514         if (ret_tokens->len > 0 && !IS_PATH_SEP (ret_tokens->str[ret_tokens->len - 1]))
 515             g_string_append_c (ret_tokens, PATH_SEP);
 516 
 517         g_string_append (ret_tokens, element->vfs_prefix);
 518         g_string_append (ret_tokens, VFS_PATH_URL_DELIMITER);
 519 
 520         url_str = vfs_path_build_url_params_str (element, TRUE);
 521         if (url_str != NULL)
 522         {
 523             g_string_append_len (ret_tokens, url_str->str, url_str->len);
 524             g_string_append_c (ret_tokens, PATH_SEP);
 525             g_string_free (url_str, TRUE);
 526         }
 527     }
 528 
 529 #ifdef HAVE_CHARSET
 530     if (element->encoding != NULL)
 531     {
 532         if (ret_tokens->len > 0 && !IS_PATH_SEP (ret_tokens->str[ret_tokens->len - 1]))
 533             g_string_append (ret_tokens, PATH_SEP_STR);
 534         g_string_append (ret_tokens, VFS_ENCODING_PREFIX);
 535         g_string_append (ret_tokens, element->encoding);
 536         g_string_append (ret_tokens, PATH_SEP_STR);
 537     }
 538 #endif
 539 
 540     g_string_append (ret_tokens, element_tokens->str);
 541 }
 542 
 543 /* --------------------------------------------------------------------------------------------- */
 544 /**
 545  * Strip path to home dir.
 546  * @param dir pointer to string contains full path
 547  */
 548 
 549 static char *
 550 vfs_path_strip_home (const char *dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
 551 {
 552     const char *home_dir = mc_config_get_home_dir ();
 553 
 554     if (home_dir != NULL)
 555     {
 556         size_t len;
 557 
 558         len = strlen (home_dir);
 559 
 560         if (strncmp (dir, home_dir, len) == 0 && (IS_PATH_SEP (dir[len]) || dir[len] == '\0'))
 561             return g_strdup_printf ("~%s", dir + len);
 562     }
 563 
 564     return g_strdup (dir);
 565 }
 566 
 567 /* --------------------------------------------------------------------------------------------- */
 568 /*** public functions ****************************************************************************/
 569 /* --------------------------------------------------------------------------------------------- */
 570 
 571 #define vfs_append_from_path(appendfrom, is_relative)                                              \
 572     {                                                                                              \
 573         if ((flags & VPF_STRIP_HOME) && element_index == 0                                         \
 574             && (element->class->flags & VFSF_LOCAL) != 0)                                          \
 575         {                                                                                          \
 576             char *stripped_home_str;                                                               \
 577             stripped_home_str = vfs_path_strip_home (appendfrom);                                  \
 578             g_string_append (buffer, stripped_home_str);                                           \
 579             g_free (stripped_home_str);                                                            \
 580         }                                                                                          \
 581         else                                                                                       \
 582         {                                                                                          \
 583             if (!is_relative && !IS_PATH_SEP (*appendfrom) && *appendfrom != '\0'                  \
 584                 && (buffer->len == 0 || !IS_PATH_SEP (buffer->str[buffer->len - 1])))              \
 585                 g_string_append_c (buffer, PATH_SEP);                                              \
 586             g_string_append (buffer, appendfrom);                                                  \
 587         }                                                                                          \
 588     }
 589 
 590 /**
 591  * Convert first elements_count elements from vfs_path_t to string representation with flags.
 592  *
 593  * @param vpath pointer to vfs_path_t object
 594  * @param elements_count count of first elements for convert
 595  * @param flags for converter
 596  *
 597  * @return pointer to newly created string.
 598  */
 599 
 600 char *
 601 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]  */
 602 {
 603     int element_index;
 604     GString *buffer;
 605 #ifdef HAVE_CHARSET
 606     GString *recode_buffer = NULL;
 607 #endif
 608 
 609     if (vpath == NULL)
 610         return NULL;
 611 
 612     if (elements_count == 0 || elements_count > vfs_path_elements_count (vpath))
 613         elements_count = vfs_path_elements_count (vpath);
 614 
 615     if (elements_count < 0)
 616         elements_count = vfs_path_elements_count (vpath) + elements_count;
 617 
 618     buffer = g_string_new ("");
 619 
 620     for (element_index = 0; element_index < elements_count; element_index++)
 621     {
 622         const vfs_path_element_t *element;
 623         gboolean is_relative = vpath->relative && (element_index == 0);
 624 
 625         element = vfs_path_get_by_index (vpath, element_index);
 626         if (element->vfs_prefix != NULL)
 627         {
 628             GString *url_str;
 629 
 630             if (!is_relative && (buffer->len == 0 || !IS_PATH_SEP (buffer->str[buffer->len - 1])))
 631                 g_string_append_c (buffer, PATH_SEP);
 632 
 633             g_string_append (buffer, element->vfs_prefix);
 634             g_string_append (buffer, VFS_PATH_URL_DELIMITER);
 635 
 636             url_str = vfs_path_build_url_params_str (element, (flags & VPF_STRIP_PASSWORD) == 0);
 637             if (url_str != NULL)
 638             {
 639                 g_string_append_len (buffer, url_str->str, url_str->len);
 640                 g_string_append_c (buffer, PATH_SEP);
 641                 g_string_free (url_str, TRUE);
 642             }
 643         }
 644 
 645 #ifdef HAVE_CHARSET
 646         if ((flags & VPF_RECODE) == 0 && vfs_path_element_need_cleanup_converter (element))
 647         {
 648             if ((flags & VPF_HIDE_CHARSET) == 0)
 649             {
 650                 if ((!is_relative)
 651                     && (buffer->len == 0 || !IS_PATH_SEP (buffer->str[buffer->len - 1])))
 652                     g_string_append (buffer, PATH_SEP_STR);
 653                 g_string_append (buffer, VFS_ENCODING_PREFIX);
 654                 g_string_append (buffer, element->encoding);
 655             }
 656 
 657             if (recode_buffer == NULL)
 658                 recode_buffer = g_string_sized_new (32);
 659             else
 660                 g_string_set_size (recode_buffer, 0);
 661 
 662             str_vfs_convert_from (element->dir.converter, element->path, recode_buffer);
 663             vfs_append_from_path (recode_buffer->str, is_relative);
 664         }
 665         else
 666 #endif
 667         {
 668             vfs_append_from_path (element->path, is_relative);
 669         }
 670     }
 671 
 672 #ifdef HAVE_CHARSET
 673     if (recode_buffer != NULL)
 674         g_string_free (recode_buffer, TRUE);
 675 #endif
 676 
 677     return g_string_free (buffer, FALSE);
 678 }
 679 
 680 #undef vfs_append_from_path
 681 
 682 /* --------------------------------------------------------------------------------------------- */
 683 /**
 684  * Convert first elements_count elements from vfs_path_t to string representation.
 685  *
 686  * @param vpath pointer to vfs_path_t object
 687  * @param elements_count count of first elements for convert
 688  *
 689  * @return pointer to newly created string.
 690  */
 691 
 692 char *
 693 vfs_path_to_str_elements_count (const vfs_path_t *vpath, int elements_count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 694 {
 695     return vfs_path_to_str_flags (vpath, elements_count, VPF_NONE);
 696 }
 697 
 698 /* --------------------------------------------------------------------------------------------- */
 699 /**
 700  * Split path string to path elements with flags for change parce process.
 701  *
 702  * @param path_str VFS-path
 703  * @param flags flags for parser
 704  *
 705  * @return pointer to newly created vfs_path_t object with filled path elements array.
 706  */
 707 
 708 vfs_path_t *
 709 vfs_path_from_str_flags (const char *path_str, vfs_path_flag_t flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 710 {
 711     vfs_path_t *vpath;
 712     char *path;
 713 
 714     if (path_str == NULL)
 715         return NULL;
 716 
 717     if ((flags & VPF_NO_CANON) == 0)
 718         path = vfs_canon (path_str);
 719     else
 720         path = g_strdup (path_str);
 721 
 722     if (path == NULL)
 723         return NULL;
 724 
 725     if ((flags & VPF_USE_DEPRECATED_PARSER) != 0 && vfs_path_is_str_path_deprecated (path))
 726         vpath = vfs_path_from_str_deprecated_parser (path);
 727     else
 728         vpath = vfs_path_from_str_uri_parser (path);
 729 
 730     vpath->str = vfs_path_to_str_flags (vpath, 0, flags);
 731     g_free (path);
 732 
 733     return vpath;
 734 }
 735 
 736 /* --------------------------------------------------------------------------------------------- */
 737 /**
 738  * Split path string to path elements.
 739  *
 740  * @param path_str VFS-path
 741  *
 742  * @return pointer to newly created vfs_path_t object with filled path elements array.
 743  */
 744 
 745 vfs_path_t *
 746 vfs_path_from_str (const char *path_str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 747 {
 748     return vfs_path_from_str_flags (path_str, VPF_NONE);
 749 }
 750 
 751 /* --------------------------------------------------------------------------------------------- */
 752 /*
 753  * Create new vfs_path_t object.
 754  *
 755  * @return pointer to newly created vfs_path_t object.
 756  */
 757 
 758 vfs_path_t *
 759 vfs_path_new (gboolean relative)
     /* [previous][next][first][last][top][bottom][index][help]  */
 760 {
 761     vfs_path_t *vpath;
 762 
 763     vpath = g_new0 (vfs_path_t, 1);
 764     vpath->path = g_array_new (FALSE, TRUE, sizeof (vfs_path_element_t *));
 765     vpath->relative = relative;
 766 
 767     return vpath;
 768 }
 769 
 770 /* --------------------------------------------------------------------------------------------- */
 771 /*
 772  * Get count of path elements.
 773  *
 774  * @param vpath pointer to vfs_path_t object
 775  *
 776  * @return count of path elements.
 777  */
 778 
 779 int
 780 vfs_path_elements_count (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 781 {
 782     return (vpath != NULL && vpath->path != NULL) ? vpath->path->len : 0;
 783 }
 784 
 785 /* --------------------------------------------------------------------------------------------- */
 786 /**
 787  * Add vfs_path_element_t object to end of list in vfs_path_t object
 788  * @param vpath pointer to vfs_path_t object
 789  * @param path_element pointer to vfs_path_element_t object
 790  */
 791 
 792 void
 793 vfs_path_add_element (vfs_path_t *vpath, const vfs_path_element_t *path_element)
     /* [previous][next][first][last][top][bottom][index][help]  */
 794 {
 795     g_array_append_val (vpath->path, path_element);
 796     g_free (vpath->str);
 797     vpath->str = vfs_path_to_str_flags (vpath, 0, VPF_NONE);
 798 }
 799 
 800 /* --------------------------------------------------------------------------------------------- */
 801 /*
 802  * Get one path element by index.
 803  *
 804  * @param vpath pointer to vfs_path_t object.
 805  *              May be NULL. In this case NULL is returned and errno set to 0.
 806  * @param element_index element index. May have negative value (in this case count was started at
 807  *                      the end of list). If @element_index is out of range, NULL is returned and
 808  *                      errno set to EINVAL.
 809  *
 810  * @return path element
 811  */
 812 
 813 const vfs_path_element_t *
 814 vfs_path_get_by_index (const vfs_path_t *vpath, int element_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
 815 {
 816     int n;
 817 
 818     if (vpath == NULL)
 819     {
 820         errno = 0;
 821         return NULL;
 822     }
 823 
 824     n = vfs_path_elements_count (vpath);
 825 
 826     if (element_index < 0)
 827         element_index += n;
 828 
 829     if (element_index < 0 || element_index > n)
 830     {
 831         errno = EINVAL;
 832         return NULL;
 833     }
 834 
 835     return g_array_index (vpath->path, vfs_path_element_t *, element_index);
 836 }
 837 
 838 /* --------------------------------------------------------------------------------------------- */
 839 /*
 840  * Clone one path element
 841  *
 842  * @param element pointer to vfs_path_element_t object
 843  *
 844  * @return Newly allocated path element
 845  */
 846 
 847 vfs_path_element_t *
 848 vfs_path_element_clone (const vfs_path_element_t *element)
     /* [previous][next][first][last][top][bottom][index][help]  */
 849 {
 850     vfs_path_element_t *new_element = g_new (vfs_path_element_t, 1);
 851 
 852     new_element->user = g_strdup (element->user);
 853     new_element->password = g_strdup (element->password);
 854     new_element->host = g_strdup (element->host);
 855     new_element->ipv6 = element->ipv6;
 856     new_element->port = element->port;
 857     new_element->path = g_strdup (element->path);
 858     new_element->class = element->class;
 859     new_element->vfs_prefix = g_strdup (element->vfs_prefix);
 860 #ifdef HAVE_CHARSET
 861     new_element->encoding = g_strdup (element->encoding);
 862     if (vfs_path_element_need_cleanup_converter (element) && element->encoding != NULL)
 863         new_element->dir.converter = str_crt_conv_from (element->encoding);
 864     else
 865         new_element->dir.converter = element->dir.converter;
 866 #endif
 867     new_element->dir.info = element->dir.info;
 868 
 869     return new_element;
 870 }
 871 
 872 /* --------------------------------------------------------------------------------------------- */
 873 /*
 874  * Free one path element.
 875  *
 876  * @param element pointer to vfs_path_element_t object
 877  *
 878  */
 879 
 880 void
 881 vfs_path_element_free (vfs_path_element_t *element)
     /* [previous][next][first][last][top][bottom][index][help]  */
 882 {
 883     if (element == NULL)
 884         return;
 885 
 886     g_free (element->user);
 887     g_free (element->password);
 888     g_free (element->host);
 889     g_free (element->path);
 890     g_free (element->vfs_prefix);
 891 
 892 #ifdef HAVE_CHARSET
 893     g_free (element->encoding);
 894 
 895     if (vfs_path_element_need_cleanup_converter (element))
 896         str_close_conv (element->dir.converter);
 897 #endif
 898 
 899     g_free (element);
 900 }
 901 
 902 /* --------------------------------------------------------------------------------------------- */
 903 /*
 904  * Clone path
 905  *
 906  * @param vpath pointer to vfs_path_t object
 907  *
 908  * @return Newly allocated path object
 909  */
 910 
 911 vfs_path_t *
 912 vfs_path_clone (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 913 {
 914     vfs_path_t *new_vpath;
 915     int vpath_element_index;
 916 
 917     if (vpath == NULL)
 918         return NULL;
 919 
 920     new_vpath = vfs_path_new (vpath->relative);
 921 
 922     for (vpath_element_index = 0; vpath_element_index < vfs_path_elements_count (vpath);
 923          vpath_element_index++)
 924     {
 925         vfs_path_element_t *path_element;
 926 
 927         path_element = vfs_path_element_clone (vfs_path_get_by_index (vpath, vpath_element_index));
 928         g_array_append_val (new_vpath->path, path_element);
 929     }
 930     new_vpath->str = g_strdup (vpath->str);
 931 
 932     return new_vpath;
 933 }
 934 
 935 /* --------------------------------------------------------------------------------------------- */
 936 /*
 937  * Free vfs_path_t object.
 938  *
 939  * @param vpath pointer to vfs_path_t object
 940  * @param free_str if TRUE the string representation of vpath is freed as well
 941  *
 942  * @return the string representation of vpath (i.e. NULL if free_str is TRUE)
 943  */
 944 
 945 char *
 946 vfs_path_free (vfs_path_t *vpath, gboolean free_str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 947 {
 948     int vpath_element_index;
 949     char *ret;
 950 
 951     if (vpath == NULL)
 952         return NULL;
 953 
 954     for (vpath_element_index = 0; vpath_element_index < vfs_path_elements_count (vpath);
 955          vpath_element_index++)
 956     {
 957         vfs_path_element_t *path_element;
 958 
 959         path_element = (vfs_path_element_t *) vfs_path_get_by_index (vpath, vpath_element_index);
 960         vfs_path_element_free (path_element);
 961     }
 962 
 963     g_array_free (vpath->path, TRUE);
 964 
 965     if (!free_str)
 966         ret = vpath->str;
 967     else
 968     {
 969         g_free (vpath->str);
 970         ret = NULL;
 971     }
 972 
 973     g_free (vpath);
 974 
 975     return ret;
 976 }
 977 
 978 /* --------------------------------------------------------------------------------------------- */
 979 /*
 980  * Remove one path element by index
 981  *
 982  * @param vpath pointer to vfs_path_t object
 983  * @param element_index element index. May have negative value (in this case count was started at
 984  * 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  * Append vpath_t tokens to path object
1320  *
1321  * @param first_vpath vpath objects
1322  * @param ... NULL-terminated vpath objects
1323  *
1324  * @return newly allocated path object
1325  */
1326 
1327 vfs_path_t *
1328 vfs_path_append_vpath_new (const vfs_path_t *first_vpath, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
1329 {
1330     va_list args;
1331     vfs_path_t *ret_vpath;
1332     const vfs_path_t *current_vpath = first_vpath;
1333 
1334     if (first_vpath == NULL)
1335         return NULL;
1336 
1337     ret_vpath = vfs_path_new (FALSE);
1338 
1339     va_start (args, first_vpath);
1340     do
1341     {
1342         int vindex;
1343 
1344         for (vindex = 0; vindex < vfs_path_elements_count (current_vpath); vindex++)
1345         {
1346             vfs_path_element_t *path_element;
1347 
1348             path_element = vfs_path_element_clone (vfs_path_get_by_index (current_vpath, vindex));
1349             g_array_append_val (ret_vpath->path, path_element);
1350         }
1351         current_vpath = va_arg (args, const vfs_path_t *);
1352     }
1353     while (current_vpath != NULL);
1354     va_end (args);
1355 
1356     ret_vpath->str = vfs_path_to_str_flags (ret_vpath, 0, VPF_NONE);
1357 
1358     return ret_vpath;
1359 }
1360 
1361 /* --------------------------------------------------------------------------------------------- */
1362 
1363 /**
1364  * get tokens count in path.
1365  *
1366  * @param vpath path object
1367  *
1368  * @return count of tokens
1369  */
1370 
1371 size_t
1372 vfs_path_tokens_count (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1373 {
1374     size_t count_tokens = 0;
1375     int element_index;
1376 
1377     if (vpath == NULL)
1378         return 0;
1379 
1380     for (element_index = 0; element_index < vfs_path_elements_count (vpath); element_index++)
1381     {
1382         const vfs_path_element_t *element;
1383         const char *token, *prev_token;
1384 
1385         element = vfs_path_get_by_index (vpath, element_index);
1386 
1387         for (prev_token = element->path; (token = strchr (prev_token, PATH_SEP)) != NULL;
1388              prev_token = token + 1)
1389         {
1390             // skip empty substring
1391             if (token != prev_token)
1392                 count_tokens++;
1393         }
1394 
1395         if (*prev_token != '\0')
1396             count_tokens++;
1397     }
1398 
1399     return count_tokens;
1400 }
1401 
1402 /* --------------------------------------------------------------------------------------------- */
1403 
1404 /**
1405  * Get subpath by tokens
1406  *
1407  * @param vpath path object
1408  * @param start_position first token for got/ Started from 0.
1409  *        If negative, then position will be relative to end of path
1410  * @param length count of tokens
1411  *
1412  * @return newly allocated string with path tokens separated by slash
1413  */
1414 
1415 char *
1416 vfs_path_tokens_get (const vfs_path_t *vpath, ssize_t start_position, ssize_t length)
     /* [previous][next][first][last][top][bottom][index][help]  */
1417 {
1418     GString *ret_tokens, *element_tokens;
1419     int element_index;
1420     size_t tokens_count = vfs_path_tokens_count (vpath);
1421 
1422     if (vpath == NULL)
1423         return NULL;
1424 
1425     if (length == 0)
1426         length = tokens_count;
1427 
1428     if (length < 0)
1429         length = tokens_count + length;
1430 
1431     if (start_position < 0)
1432         start_position = (ssize_t) tokens_count + start_position;
1433 
1434     if (start_position < 0)
1435         return NULL;
1436 
1437     if (start_position >= (ssize_t) tokens_count)
1438         return NULL;
1439 
1440     if (start_position + (ssize_t) length > (ssize_t) tokens_count)
1441         length = tokens_count - start_position;
1442 
1443     ret_tokens = g_string_sized_new (32);
1444     element_tokens = g_string_sized_new (32);
1445 
1446     for (element_index = 0; element_index < vfs_path_elements_count (vpath); element_index++)
1447     {
1448         const vfs_path_element_t *element;
1449         char **path_tokens, **iterator;
1450 
1451         g_string_assign (element_tokens, "");
1452         element = vfs_path_get_by_index (vpath, element_index);
1453         path_tokens = g_strsplit (element->path, PATH_SEP_STR, -1);
1454 
1455         for (iterator = path_tokens; *iterator != NULL; iterator++)
1456         {
1457             if (**iterator != '\0')
1458             {
1459                 if (start_position == 0)
1460                 {
1461                     if (length == 0)
1462                     {
1463                         vfs_path_tokens_add_class_info (element, ret_tokens, element_tokens);
1464                         g_string_free (element_tokens, TRUE);
1465                         g_strfreev (path_tokens);
1466                         return g_string_free (ret_tokens, FALSE);
1467                     }
1468                     length--;
1469                     if (element_tokens->len != 0)
1470                         g_string_append_c (element_tokens, PATH_SEP);
1471                     g_string_append (element_tokens, *iterator);
1472                 }
1473                 else
1474                     start_position--;
1475             }
1476         }
1477         g_strfreev (path_tokens);
1478         vfs_path_tokens_add_class_info (element, ret_tokens, element_tokens);
1479     }
1480 
1481     g_string_free (element_tokens, TRUE);
1482     return g_string_free (ret_tokens, !(start_position == 0 && length == 0));
1483 }
1484 
1485 /* --------------------------------------------------------------------------------------------- */
1486 /**
1487  * Get subpath by tokens
1488  *
1489  * @param vpath path object
1490  * @param start_position first token for got/ Started from 0.
1491  *        If negative, then position will be relative to end of path
1492  * @param length count of tokens
1493  *
1494  * @return newly allocated path object with path tokens separated by slash
1495  */
1496 
1497 vfs_path_t *
1498 vfs_path_vtokens_get (const vfs_path_t *vpath, ssize_t start_position, ssize_t length)
     /* [previous][next][first][last][top][bottom][index][help]  */
1499 {
1500     char *str_tokens;
1501     vfs_path_t *ret_vpath = NULL;
1502 
1503     str_tokens = vfs_path_tokens_get (vpath, start_position, length);
1504     if (str_tokens != NULL)
1505     {
1506         ret_vpath = vfs_path_from_str_flags (str_tokens, VPF_NO_CANON);
1507         g_free (str_tokens);
1508     }
1509     return ret_vpath;
1510 }
1511 
1512 /* --------------------------------------------------------------------------------------------- */
1513 
1514 /**
1515  * Build URL parameters (such as user:pass @ host:port) from one path element object
1516  *
1517  * @param element path element
1518  * @param keep_password TRUE or FALSE
1519  *
1520  * @return newly allocated non-empty string or NULL
1521  */
1522 
1523 GString *
1524 vfs_path_build_url_params_str (const vfs_path_element_t *element, gboolean keep_password)
     /* [previous][next][first][last][top][bottom][index][help]  */
1525 {
1526     GString *buffer;
1527 
1528     if (element == NULL)
1529         return NULL;
1530 
1531     buffer = g_string_sized_new (64);
1532 
1533     if (element->user != NULL)
1534         g_string_append (buffer, element->user);
1535 
1536     if (element->password != NULL && keep_password)
1537     {
1538         g_string_append_c (buffer, ':');
1539         g_string_append (buffer, element->password);
1540     }
1541 
1542     if (element->host != NULL)
1543     {
1544         if ((element->user != NULL) || (element->password != NULL))
1545             g_string_append_c (buffer, '@');
1546         if (element->ipv6)
1547             g_string_append_c (buffer, '[');
1548         g_string_append (buffer, element->host);
1549         if (element->ipv6)
1550             g_string_append_c (buffer, ']');
1551 
1552         if (element->port != 0)
1553             g_string_append_printf (buffer, ":%d", element->port);
1554     }
1555 
1556     if (buffer->len != 0)
1557         return buffer;
1558 
1559     g_string_free (buffer, TRUE);
1560     return NULL;
1561 }
1562 
1563 /* --------------------------------------------------------------------------------------------- */
1564 /**
1565  * Build pretty string representation of one path_element_t object
1566  *
1567  * @param element path element
1568  *
1569  * @return newly allocated string
1570  */
1571 
1572 GString *
1573 vfs_path_element_build_pretty_path_str (const vfs_path_element_t *element)
     /* [previous][next][first][last][top][bottom][index][help]  */
1574 {
1575     GString *url_params, *pretty_path;
1576 
1577     pretty_path = g_string_new (element->class->prefix);
1578     g_string_append (pretty_path, VFS_PATH_URL_DELIMITER);
1579 
1580     url_params = vfs_path_build_url_params_str (element, FALSE);
1581     if (url_params != NULL)
1582     {
1583         g_string_append_len (pretty_path, url_params->str, url_params->len);
1584         g_string_free (url_params, TRUE);
1585     }
1586 
1587     if (!IS_PATH_SEP (*element->path))
1588         g_string_append_c (pretty_path, PATH_SEP);
1589 
1590     return g_string_append (pretty_path, element->path);
1591 }
1592 
1593 /* --------------------------------------------------------------------------------------------- */
1594 /**
1595  * Compare two path objects as strings
1596  *
1597  * @param vpath1 first path object
1598  * @param vpath2 second vpath object
1599  *
1600  * @return integer value like to strcmp.
1601  */
1602 
1603 gboolean
1604 vfs_path_equal (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1605 {
1606     const char *path1, *path2;
1607     gboolean ret_val;
1608 
1609     if (vpath1 == NULL || vpath2 == NULL)
1610         return FALSE;
1611 
1612     path1 = vfs_path_as_str (vpath1);
1613     path2 = vfs_path_as_str (vpath2);
1614 
1615     ret_val = strcmp (path1, path2) == 0;
1616 
1617     return ret_val;
1618 }
1619 
1620 /* --------------------------------------------------------------------------------------------- */
1621 /**
1622  * Compare two path objects as strings
1623  *
1624  * @param vpath1 first path object
1625  * @param vpath2 second vpath object
1626  * @param len number of first 'len' characters
1627  *
1628  * @return integer value like to strcmp.
1629  */
1630 
1631 gboolean
1632 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]  */
1633 {
1634     const char *path1, *path2;
1635     gboolean ret_val;
1636 
1637     if (vpath1 == NULL || vpath2 == NULL)
1638         return FALSE;
1639 
1640     path1 = vfs_path_as_str (vpath1);
1641     path2 = vfs_path_as_str (vpath2);
1642 
1643     ret_val = strncmp (path1, path2, len) == 0;
1644 
1645     return ret_val;
1646 }
1647 
1648 /* --------------------------------------------------------------------------------------------- */
1649 /**
1650  * Calculate path length in string representation
1651  *
1652  * @param vpath path object
1653  *
1654  * @return length of path
1655  */
1656 
1657 size_t
1658 vfs_path_len (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1659 {
1660     if (vpath == NULL)
1661         return 0;
1662 
1663     return strlen (vpath->str);
1664 }
1665 
1666 /* --------------------------------------------------------------------------------------------- */
1667 /**
1668  * Convert relative vpath object to absolute
1669  *
1670  * @param vpath path object
1671  *
1672  * @return absolute path object
1673  */
1674 
1675 vfs_path_t *
1676 vfs_path_to_absolute (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1677 {
1678     vfs_path_t *absolute_vpath;
1679     const char *path_str;
1680 
1681     if (!vpath->relative)
1682         return vfs_path_clone (vpath);
1683 
1684     path_str = vfs_path_as_str (vpath);
1685     absolute_vpath = vfs_path_from_str (path_str);
1686     return absolute_vpath;
1687 }
1688 
1689 /* --------------------------------------------------------------------------------------------- */

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