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     char *ret_value;
1102 
1103     mc_return_val_if_error (mcerror, FALSE);
1104 
1105     if ((vpath == NULL) || (vfs_path_elements_count (vpath) == 0))
1106     {
1107         mc_propagate_error (mcerror, 0, "%s", "vpath object is empty");
1108         return NULL;
1109     }
1110 
1111     cpath = mc_config_init (NULL, FALSE);
1112 
1113     for (int element_index = 0; element_index < vfs_path_elements_count (vpath); element_index++)
1114     {
1115         char groupname[BUF_TINY];
1116         const vfs_path_element_t *element;
1117 
1118         g_snprintf (groupname, sizeof (groupname), "path-element-%d", element_index);
1119         element = vfs_path_get_by_index (vpath, element_index);
1120         // convert one element to config group
1121 
1122         mc_config_set_string_raw (cpath, groupname, "path", element->path);
1123         mc_config_set_string_raw (cpath, groupname, "class-name", element->class->name);
1124         mc_config_set_string_raw (cpath, groupname, "encoding", element->encoding);
1125         mc_config_set_string_raw (cpath, groupname, "vfs_prefix", element->vfs_prefix);
1126 
1127         mc_config_set_string_raw (cpath, groupname, "user", element->user);
1128         mc_config_set_string_raw (cpath, groupname, "password", element->password);
1129         mc_config_set_string_raw (cpath, groupname, "host", element->host);
1130         if (element->port != 0)
1131             mc_config_set_int (cpath, groupname, "port", element->port);
1132     }
1133 
1134     ret_value = mc_serialize_config (cpath, mcerror);
1135     mc_config_deinit (cpath);
1136     return ret_value;
1137 }
1138 
1139 /* --------------------------------------------------------------------------------------------- */
1140 /**
1141  * Deserialize string to vfs_path_t object
1142  *
1143  * @param data data for serialization
1144  * @param error contain pointer to object for handle error code and message
1145  *
1146  * @return newly allocated vfs_path_t object
1147  */
1148 
1149 vfs_path_t *
1150 vfs_path_deserialize (const char *data, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
1151 {
1152     mc_config_t *cpath;
1153     size_t element_index;
1154     vfs_path_t *vpath;
1155 
1156     mc_return_val_if_error (mcerror, FALSE);
1157 
1158     cpath = mc_deserialize_config (data, mcerror);
1159     if (cpath == NULL)
1160         return NULL;
1161 
1162     vpath = vfs_path_new (FALSE);
1163 
1164     for (element_index = 0;; element_index++)
1165     {
1166         struct vfs_class *eclass;
1167         vfs_path_element_t *element;
1168         char *cfg_value;
1169         char groupname[BUF_TINY];
1170 
1171         g_snprintf (groupname, sizeof (groupname), "path-element-%zu", element_index);
1172         if (!mc_config_has_group (cpath, groupname))
1173             break;
1174 
1175         cfg_value = mc_config_get_string_raw (cpath, groupname, "class-name", NULL);
1176         eclass = vfs_get_class_by_name (cfg_value);
1177         if (eclass == NULL)
1178         {
1179             vfs_path_free (vpath, TRUE);
1180             g_set_error (mcerror, MC_ERROR, 0, "Unable to find VFS class by name '%s'", cfg_value);
1181             g_free (cfg_value);
1182             mc_config_deinit (cpath);
1183             return NULL;
1184         }
1185         g_free (cfg_value);
1186 
1187         element = g_new0 (vfs_path_element_t, 1);
1188         element->class = eclass;
1189         element->path = mc_config_get_string_raw (cpath, groupname, "path", NULL);
1190 
1191         element->encoding = mc_config_get_string_raw (cpath, groupname, "encoding", NULL);
1192         element->dir.converter =
1193             (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
1194 
1195         element->vfs_prefix = mc_config_get_string_raw (cpath, groupname, "vfs_prefix", NULL);
1196 
1197         element->user = mc_config_get_string_raw (cpath, groupname, "user", NULL);
1198         element->password = mc_config_get_string_raw (cpath, groupname, "password", NULL);
1199         element->host = mc_config_get_string_raw (cpath, groupname, "host", NULL);
1200         element->port = mc_config_get_int (cpath, groupname, "port", 0);
1201 
1202         vpath->path = g_array_append_val (vpath->path, element);
1203     }
1204 
1205     mc_config_deinit (cpath);
1206     if (vfs_path_elements_count (vpath) == 0)
1207     {
1208         vfs_path_free (vpath, TRUE);
1209         g_set_error (mcerror, MC_ERROR, 0, "No any path elements found");
1210         return NULL;
1211     }
1212     vpath->str = vfs_path_to_str_flags (vpath, 0, VPF_NONE);
1213 
1214     return vpath;
1215 }
1216 
1217 /* --------------------------------------------------------------------------------------------- */
1218 /**
1219  * Build vfs_path_t object from arguments.
1220  *
1221  * @param first_element of path
1222  * @param ... path tokens, terminated by NULL
1223  *
1224  * @return newly allocated vfs_path_t object
1225  */
1226 
1227 vfs_path_t *
1228 vfs_path_build_filename (const char *first_element, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
1229 {
1230     va_list args;
1231     char *str_path;
1232     vfs_path_t *vpath;
1233 
1234     if (first_element == NULL)
1235         return NULL;
1236 
1237     va_start (args, first_element);
1238     str_path = mc_build_filenamev (first_element, args);
1239     va_end (args);
1240     vpath = vfs_path_from_str (str_path);
1241     g_free (str_path);
1242     return vpath;
1243 }
1244 
1245 /* --------------------------------------------------------------------------------------------- */
1246 /**
1247  * Append tokens to path object
1248  *
1249  * @param vpath path object
1250  * @param first_element of path
1251  * @param ... NULL-terminated strings
1252  *
1253  * @return newly allocated path object
1254  */
1255 
1256 vfs_path_t *
1257 vfs_path_append_new (const vfs_path_t *vpath, const char *first_element, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
1258 {
1259     va_list args;
1260     char *str_path;
1261     const char *result_str;
1262     vfs_path_t *ret_vpath;
1263 
1264     if (vpath == NULL || first_element == NULL)
1265         return NULL;
1266 
1267     va_start (args, first_element);
1268     str_path = mc_build_filenamev (first_element, args);
1269     va_end (args);
1270 
1271     result_str = vfs_path_as_str (vpath);
1272     ret_vpath = vfs_path_build_filename (result_str, str_path, (char *) NULL);
1273     g_free (str_path);
1274 
1275     return ret_vpath;
1276 }
1277 
1278 /* --------------------------------------------------------------------------------------------- */
1279 
1280 /**
1281  * Append vpath_t tokens to path object
1282  *
1283  * @param first_vpath vpath objects
1284  * @param ... NULL-terminated vpath objects
1285  *
1286  * @return newly allocated path object
1287  */
1288 
1289 vfs_path_t *
1290 vfs_path_append_vpath_new (const vfs_path_t *first_vpath, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
1291 {
1292     va_list args;
1293     vfs_path_t *ret_vpath;
1294     const vfs_path_t *current_vpath = first_vpath;
1295 
1296     if (first_vpath == NULL)
1297         return NULL;
1298 
1299     ret_vpath = vfs_path_new (FALSE);
1300 
1301     va_start (args, first_vpath);
1302     do
1303     {
1304         int vindex;
1305 
1306         for (vindex = 0; vindex < vfs_path_elements_count (current_vpath); vindex++)
1307         {
1308             vfs_path_element_t *path_element;
1309 
1310             path_element = vfs_path_element_clone (vfs_path_get_by_index (current_vpath, vindex));
1311             g_array_append_val (ret_vpath->path, path_element);
1312         }
1313         current_vpath = va_arg (args, const vfs_path_t *);
1314     }
1315     while (current_vpath != NULL);
1316     va_end (args);
1317 
1318     ret_vpath->str = vfs_path_to_str_flags (ret_vpath, 0, VPF_NONE);
1319 
1320     return ret_vpath;
1321 }
1322 
1323 /* --------------------------------------------------------------------------------------------- */
1324 
1325 /**
1326  * get tokens count in path.
1327  *
1328  * @param vpath path object
1329  *
1330  * @return count of tokens
1331  */
1332 
1333 size_t
1334 vfs_path_tokens_count (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1335 {
1336     size_t count_tokens = 0;
1337     int element_index;
1338 
1339     if (vpath == NULL)
1340         return 0;
1341 
1342     for (element_index = 0; element_index < vfs_path_elements_count (vpath); element_index++)
1343     {
1344         const vfs_path_element_t *element;
1345         const char *token, *prev_token;
1346 
1347         element = vfs_path_get_by_index (vpath, element_index);
1348 
1349         for (prev_token = element->path; (token = strchr (prev_token, PATH_SEP)) != NULL;
1350              prev_token = token + 1)
1351         {
1352             // skip empty substring
1353             if (token != prev_token)
1354                 count_tokens++;
1355         }
1356 
1357         if (*prev_token != '\0')
1358             count_tokens++;
1359     }
1360 
1361     return count_tokens;
1362 }
1363 
1364 /* --------------------------------------------------------------------------------------------- */
1365 
1366 /**
1367  * Get subpath by tokens
1368  *
1369  * @param vpath path object
1370  * @param start_position first token for got/ Started from 0.
1371  *        If negative, then position will be relative to end of path
1372  * @param length count of tokens
1373  *
1374  * @return newly allocated string with path tokens separated by slash
1375  */
1376 
1377 char *
1378 vfs_path_tokens_get (const vfs_path_t *vpath, ssize_t start_position, ssize_t length)
     /* [previous][next][first][last][top][bottom][index][help]  */
1379 {
1380     GString *ret_tokens, *element_tokens;
1381     int element_index;
1382     size_t tokens_count = vfs_path_tokens_count (vpath);
1383 
1384     if (vpath == NULL)
1385         return NULL;
1386 
1387     if (length == 0)
1388         length = tokens_count;
1389 
1390     if (length < 0)
1391         length = tokens_count + length;
1392 
1393     if (start_position < 0)
1394         start_position = (ssize_t) tokens_count + start_position;
1395 
1396     if (start_position < 0)
1397         return NULL;
1398 
1399     if (start_position >= (ssize_t) tokens_count)
1400         return NULL;
1401 
1402     if (start_position + (ssize_t) length > (ssize_t) tokens_count)
1403         length = tokens_count - start_position;
1404 
1405     ret_tokens = g_string_sized_new (32);
1406     element_tokens = g_string_sized_new (32);
1407 
1408     for (element_index = 0; element_index < vfs_path_elements_count (vpath); element_index++)
1409     {
1410         const vfs_path_element_t *element;
1411         char **path_tokens, **iterator;
1412 
1413         g_string_assign (element_tokens, "");
1414         element = vfs_path_get_by_index (vpath, element_index);
1415         path_tokens = g_strsplit (element->path, PATH_SEP_STR, -1);
1416 
1417         for (iterator = path_tokens; *iterator != NULL; iterator++)
1418         {
1419             if (**iterator != '\0')
1420             {
1421                 if (start_position == 0)
1422                 {
1423                     if (length == 0)
1424                     {
1425                         vfs_path_tokens_add_class_info (element, ret_tokens, element_tokens);
1426                         g_string_free (element_tokens, TRUE);
1427                         g_strfreev (path_tokens);
1428                         return g_string_free (ret_tokens, FALSE);
1429                     }
1430                     length--;
1431                     if (element_tokens->len != 0)
1432                         g_string_append_c (element_tokens, PATH_SEP);
1433                     g_string_append (element_tokens, *iterator);
1434                 }
1435                 else
1436                     start_position--;
1437             }
1438         }
1439         g_strfreev (path_tokens);
1440         vfs_path_tokens_add_class_info (element, ret_tokens, element_tokens);
1441     }
1442 
1443     g_string_free (element_tokens, TRUE);
1444     return g_string_free (ret_tokens, !(start_position == 0 && length == 0));
1445 }
1446 
1447 /* --------------------------------------------------------------------------------------------- */
1448 /**
1449  * Get subpath by tokens
1450  *
1451  * @param vpath path object
1452  * @param start_position first token for got/ Started from 0.
1453  *        If negative, then position will be relative to end of path
1454  * @param length count of tokens
1455  *
1456  * @return newly allocated path object with path tokens separated by slash
1457  */
1458 
1459 vfs_path_t *
1460 vfs_path_vtokens_get (const vfs_path_t *vpath, ssize_t start_position, ssize_t length)
     /* [previous][next][first][last][top][bottom][index][help]  */
1461 {
1462     char *str_tokens;
1463     vfs_path_t *ret_vpath = NULL;
1464 
1465     str_tokens = vfs_path_tokens_get (vpath, start_position, length);
1466     if (str_tokens != NULL)
1467     {
1468         ret_vpath = vfs_path_from_str_flags (str_tokens, VPF_NO_CANON);
1469         g_free (str_tokens);
1470     }
1471     return ret_vpath;
1472 }
1473 
1474 /* --------------------------------------------------------------------------------------------- */
1475 
1476 /**
1477  * Build URL parameters (such as user:pass @ host:port) from one path element object
1478  *
1479  * @param element path element
1480  * @param keep_password TRUE or FALSE
1481  *
1482  * @return newly allocated non-empty string or NULL
1483  */
1484 
1485 GString *
1486 vfs_path_build_url_params_str (const vfs_path_element_t *element, gboolean keep_password)
     /* [previous][next][first][last][top][bottom][index][help]  */
1487 {
1488     GString *buffer;
1489 
1490     if (element == NULL)
1491         return NULL;
1492 
1493     buffer = g_string_sized_new (64);
1494 
1495     if (element->user != NULL)
1496         g_string_append (buffer, element->user);
1497 
1498     if (element->password != NULL && keep_password)
1499     {
1500         g_string_append_c (buffer, ':');
1501         g_string_append (buffer, element->password);
1502     }
1503 
1504     if (element->host != NULL)
1505     {
1506         if ((element->user != NULL) || (element->password != NULL))
1507             g_string_append_c (buffer, '@');
1508         if (element->ipv6)
1509             g_string_append_c (buffer, '[');
1510         g_string_append (buffer, element->host);
1511         if (element->ipv6)
1512             g_string_append_c (buffer, ']');
1513 
1514         if (element->port != 0)
1515             g_string_append_printf (buffer, ":%d", element->port);
1516     }
1517 
1518     if (buffer->len != 0)
1519         return buffer;
1520 
1521     g_string_free (buffer, TRUE);
1522     return NULL;
1523 }
1524 
1525 /* --------------------------------------------------------------------------------------------- */
1526 /**
1527  * Build pretty string representation of one path_element_t object
1528  *
1529  * @param element path element
1530  *
1531  * @return newly allocated string
1532  */
1533 
1534 GString *
1535 vfs_path_element_build_pretty_path_str (const vfs_path_element_t *element)
     /* [previous][next][first][last][top][bottom][index][help]  */
1536 {
1537     GString *url_params, *pretty_path;
1538 
1539     pretty_path = g_string_new (element->class->prefix);
1540     g_string_append (pretty_path, VFS_PATH_URL_DELIMITER);
1541 
1542     url_params = vfs_path_build_url_params_str (element, FALSE);
1543     if (url_params != NULL)
1544     {
1545         g_string_append_len (pretty_path, url_params->str, url_params->len);
1546         g_string_free (url_params, TRUE);
1547     }
1548 
1549     if (!IS_PATH_SEP (*element->path))
1550         g_string_append_c (pretty_path, PATH_SEP);
1551 
1552     return g_string_append (pretty_path, element->path);
1553 }
1554 
1555 /* --------------------------------------------------------------------------------------------- */
1556 /**
1557  * Compare two path objects as strings
1558  *
1559  * @param vpath1 first path object
1560  * @param vpath2 second vpath object
1561  *
1562  * @return integer value like to strcmp.
1563  */
1564 
1565 gboolean
1566 vfs_path_equal (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1567 {
1568     const char *path1, *path2;
1569     gboolean ret_val;
1570 
1571     if (vpath1 == NULL || vpath2 == NULL)
1572         return FALSE;
1573 
1574     path1 = vfs_path_as_str (vpath1);
1575     path2 = vfs_path_as_str (vpath2);
1576 
1577     ret_val = strcmp (path1, path2) == 0;
1578 
1579     return ret_val;
1580 }
1581 
1582 /* --------------------------------------------------------------------------------------------- */
1583 /**
1584  * Compare two path objects as strings
1585  *
1586  * @param vpath1 first path object
1587  * @param vpath2 second vpath object
1588  * @param len number of first 'len' characters
1589  *
1590  * @return integer value like to strcmp.
1591  */
1592 
1593 gboolean
1594 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]  */
1595 {
1596     const char *path1, *path2;
1597     gboolean ret_val;
1598 
1599     if (vpath1 == NULL || vpath2 == NULL)
1600         return FALSE;
1601 
1602     path1 = vfs_path_as_str (vpath1);
1603     path2 = vfs_path_as_str (vpath2);
1604 
1605     ret_val = strncmp (path1, path2, len) == 0;
1606 
1607     return ret_val;
1608 }
1609 
1610 /* --------------------------------------------------------------------------------------------- */
1611 /**
1612  * Calculate path length in string representation
1613  *
1614  * @param vpath path object
1615  *
1616  * @return length of path
1617  */
1618 
1619 size_t
1620 vfs_path_len (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1621 {
1622     if (vpath == NULL)
1623         return 0;
1624 
1625     return strlen (vpath->str);
1626 }
1627 
1628 /* --------------------------------------------------------------------------------------------- */
1629 /**
1630  * Convert relative vpath object to absolute
1631  *
1632  * @param vpath path object
1633  *
1634  * @return absolute path object
1635  */
1636 
1637 vfs_path_t *
1638 vfs_path_to_absolute (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1639 {
1640     vfs_path_t *absolute_vpath;
1641     const char *path_str;
1642 
1643     if (!vpath->relative)
1644         return vfs_path_clone (vpath);
1645 
1646     path_str = vfs_path_as_str (vpath);
1647     absolute_vpath = vfs_path_from_str (path_str);
1648     return absolute_vpath;
1649 }
1650 
1651 /* --------------------------------------------------------------------------------------------- */

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