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

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