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

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