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

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