Manual pages: mcmcdiffmceditmcview

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

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