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

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