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

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