Ticket #2361: VFS URI reimplementation
[midnight-commander.git] / lib / vfs / path.c
blob9406ebf32122572c8ac37a4d24c26e9855b34f04
1 /* Virtual File System path handlers
2 Copyright (C) 2011 Free Software Foundation, Inc.
4 Written by:
5 Slava Zanko <slavazanko@gmail.com>, 2011
7 This file is part of the Midnight Commander.
9 The Midnight Commander is free software; you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
14 The Midnight Commander is distributed in the hope that it will be
15 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 MA 02110-1301, USA.
25 /**
26 * \file
27 * \brief Source: Virtual File System: path handlers
28 * \author Slava Zanko
29 * \date 2011
33 #include <config.h>
35 #include "lib/global.h"
36 #include "lib/strutil.h"
37 #include "lib/util.h" /* concat_dir_and_file */
38 #include "lib/serialize.h"
40 #include "vfs.h"
41 #include "utilvfs.h"
42 #include "xdirentry.h"
43 #include "path.h"
45 extern GPtrArray *vfs__classes_list;
47 /*** global variables ****************************************************************************/
49 /*** file scope macro definitions ****************************************************************/
51 /*** file scope type declarations ****************************************************************/
53 /*** file scope variables ************************************************************************/
55 /*** file scope functions ************************************************************************/
56 /* --------------------------------------------------------------------------------------------- */
58 static gboolean
59 path_magic (const char *path)
61 struct stat buf;
63 return (stat (path, &buf) != 0);
66 /* --------------------------------------------------------------------------------------------- */
68 /**
69 * Splits path extracting vfs part.
71 * Splits path
72 * \verbatim /p1#op/inpath \endverbatim
73 * into
74 * \verbatim inpath,op; \endverbatim
75 * returns which vfs it is.
76 * What is left in path is p1. You still want to g_free(path), you DON'T
77 * want to free neither *inpath nor *op
80 static struct vfs_class *
81 _vfs_split_with_semi_skip_count (char *path, const char **inpath, const char **op,
82 size_t skip_count)
84 char *semi;
85 char *slash;
86 struct vfs_class *ret;
88 if (path == NULL)
89 vfs_die ("Cannot split NULL");
91 semi = strrstr_skip_count (path, "#", skip_count);
93 if ((semi == NULL) || (!path_magic (path)))
94 return NULL;
96 slash = strchr (semi, PATH_SEP);
97 *semi = '\0';
99 if (op != NULL)
100 *op = NULL;
102 if (inpath != NULL)
103 *inpath = NULL;
105 if (slash != NULL)
106 *slash = '\0';
108 ret = vfs_prefix_to_class (semi + 1);
109 if (ret != NULL)
111 if (op != NULL)
112 *op = semi + 1;
113 if (inpath != NULL)
114 *inpath = slash != NULL ? slash + 1 : NULL;
115 return ret;
118 if (slash != NULL)
119 *slash = PATH_SEP;
121 *semi = '#';
122 ret = _vfs_split_with_semi_skip_count (path, inpath, op, skip_count + 1);
123 return ret;
126 /* --------------------------------------------------------------------------------------------- */
128 * remove //, /./ and /../
130 * @return newly allocated string
133 static char *
134 vfs_canon (const char *path)
136 if (!path)
137 vfs_die ("Cannot canonicalize NULL");
139 /* Relative to current directory */
140 if (*path != PATH_SEP)
142 char *local, *result, *curr_dir;
144 curr_dir = vfs_get_current_dir ();
145 local = concat_dir_and_file (curr_dir, path);
146 g_free (curr_dir);
148 result = vfs_canon (local);
149 g_free (local);
150 return result;
154 * So we have path of following form:
155 * /p1/p2#op/.././././p3#op/p4. Good luck.
158 char *result = g_strdup (path);
159 canonicalize_pathname (result);
160 return result;
164 /* --------------------------------------------------------------------------------------------- */
166 * Build URL parameters (such as user:pass@host:port) from one path element object
168 * @param element path element
170 * @return newly allocated string
173 static char *
174 vfs_path_build_url_params_str (vfs_path_element_t * element)
176 GString *buffer;
178 if (element == NULL)
179 return NULL;
181 buffer = g_string_new ("");
183 if (element->user != NULL)
184 g_string_append (buffer, element->user);
186 if (element->password != NULL)
188 g_string_append_c (buffer, ':');
189 g_string_append (buffer, element->password);
192 if (element->host != NULL)
194 if ((element->user != NULL) || (element->password != NULL))
195 g_string_append_c (buffer, '@');
196 g_string_append (buffer, element->host);
199 if ((element->port) != 0 && (element->host != NULL))
201 g_string_append_c (buffer, ':');
202 g_string_append_printf (buffer, "%d", element->port);
205 return g_string_free (buffer, FALSE);
208 /* --------------------------------------------------------------------------------------------- */
209 /** get encoding after last #enc: or NULL, if part does not contain #enc:
211 * @param path string
213 * @return newly allocated string.
216 static char *
217 vfs_get_encoding (const char *path)
219 char result[16];
220 char *work;
221 char *semi;
222 char *slash;
223 work = g_strdup (path);
225 /* try found #enc: */
226 semi = g_strrstr (work, VFS_ENCODING_PREFIX);
228 if (semi != NULL && (semi == work || *(semi - 1) == PATH_SEP))
230 semi += strlen (VFS_ENCODING_PREFIX); /* skip "#enc:" */
231 slash = strchr (semi, PATH_SEP);
232 if (slash != NULL)
233 slash[0] = '\0';
235 g_strlcpy (result, semi, sizeof (result));
236 g_free (work);
237 return g_strdup (result);
239 else
241 g_free (work);
242 return NULL;
246 /* --------------------------------------------------------------------------------------------- */
247 /** Extract the hostname and username from the path
249 * Format of the path is [user@]hostname:port/remote-dir, e.g.:
251 * ftp://sunsite.unc.edu/pub/linux
252 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
253 * ftp://tsx-11.mit.edu:8192/
254 * ftp://joe@foo.edu:11321/private
255 * ftp://joe:password@foo.se
257 * @param path_element is an input string to be parsed
258 * @param path is an input string to be parsed
260 * @return g_malloc()ed url info.
261 * If the user is empty, e.g. ftp://@roxanne/private, and URL_USE_ANONYMOUS
262 * is not set, then the current login name is supplied.
263 * Return value is a g_malloc()ed structure with the pathname relative to the
264 * host.
267 static void
268 vfs_path_url_split (vfs_path_element_t * path_element, const char *path)
270 char *pcopy;
271 const char *pend;
272 char *dir, *colon, *inner_colon, *at, *rest;
274 path_element->port = 0;
276 pcopy = g_strdup (path);
277 pend = pcopy + strlen (pcopy);
278 dir = pcopy;
280 /* search for any possible user */
281 at = strrchr (pcopy, '@');
283 /* We have a username */
284 if (at == NULL)
285 rest = pcopy;
286 else
288 *at = '\0';
289 inner_colon = strchr (pcopy, ':');
290 if (inner_colon != NULL)
292 *inner_colon = '\0';
293 inner_colon++;
294 path_element->password = g_strdup (inner_colon);
297 if (*pcopy != '\0')
298 path_element->user = g_strdup (pcopy);
300 if (pend == at + 1)
301 rest = at;
302 else
303 rest = at + 1;
306 /* Check if the host comes with a port spec, if so, chop it */
307 if (*rest != '[')
308 colon = strchr (rest, ':');
309 else
311 colon = strchr (++rest, ']');
312 if (colon != NULL)
314 colon[0] = '\0';
315 colon[1] = '\0';
316 colon++;
320 if (colon != NULL)
322 *colon = '\0';
323 if (sscanf (colon + 1, "%d", &path_element->port) == 1)
325 if (path_element->port <= 0 || path_element->port >= 65536)
326 path_element->port = 0;
328 else
329 while (*(++colon) != '\0')
331 switch (*colon)
333 case 'C':
334 path_element->port = 1;
335 break;
336 case 'r':
337 path_element->port = 2;
338 break;
342 path_element->host = g_strdup (rest);
343 g_free (pcopy);
346 /* --------------------------------------------------------------------------------------------- */
348 * get VFS class for the given name
350 * @param class_name name of class
352 * @return pointer to class structure or NULL if class not found
355 static struct vfs_class *
356 vfs_get_class_by_name (const char *class_name)
358 guint i;
360 if (class_name == NULL)
361 return NULL;
363 for (i = 0; i < vfs__classes_list->len; i++)
365 struct vfs_class *vfs = (struct vfs_class *) g_ptr_array_index (vfs__classes_list, i);
366 if ((vfs->name != NULL) && (strcmp (vfs->name, class_name) == 0))
367 return vfs;
370 return NULL;
373 /* --------------------------------------------------------------------------------------------- */
375 * Check if path string contain URL-like elements
377 * @param path_str path
379 * @return TRUE if path is deprecated or FALSE otherwise
382 static gboolean
383 vfs_path_is_str_path_deprecated (const char *path_str)
385 return strstr (path_str, VFS_PATH_URL_DELIMITER) == NULL;
388 /* --------------------------------------------------------------------------------------------- */
389 /** Split path string to path elements by deprecated algorithm.
391 * @param path_str VFS-path
393 * @return pointer to newly created vfs_path_t object with filled path elements array.
396 static vfs_path_t *
397 vfs_path_from_str_deprecated_parser (char *path)
399 vfs_path_t *vpath;
400 vfs_path_element_t *element;
401 struct vfs_class *class;
402 const char *local, *op;
404 vpath = vfs_path_new ();
406 while ((class = _vfs_split_with_semi_skip_count (path, &local, &op, 0)) != NULL)
408 char *url_params;
409 element = g_new0 (vfs_path_element_t, 1);
410 element->class = class;
411 if (local == NULL)
412 local = "";
413 element->path = vfs_translate_path_n (local);
415 element->encoding = vfs_get_encoding (local);
416 element->dir.converter = INVALID_CONV;
418 url_params = strchr (op, ':'); /* skip VFS prefix */
419 if (url_params != NULL)
421 *url_params = '\0';
422 url_params++;
423 vfs_path_url_split (element, url_params);
426 if (*op != '\0')
427 element->vfs_prefix = g_strdup (op);
429 vpath->path = g_list_prepend (vpath->path, element);
431 if (path[0] != '\0')
433 element = g_new0 (vfs_path_element_t, 1);
434 element->class = g_ptr_array_index (vfs__classes_list, 0);
435 element->path = vfs_translate_path_n (path);
437 element->encoding = vfs_get_encoding (path);
438 element->dir.converter = INVALID_CONV;
439 vpath->path = g_list_prepend (vpath->path, element);
442 return vpath;
445 /* --------------------------------------------------------------------------------------------- */
446 /** Split path string to path elements by URL algorithm.
448 * @param path_str VFS-path
450 * @return pointer to newly created vfs_path_t object with filled path elements array.
453 static vfs_path_t *
454 vfs_path_from_str_uri_parser (char *path)
456 vfs_path_t *vpath;
457 vfs_path_element_t *element;
459 char *url_delimiter;
461 vpath = vfs_path_new ();
463 while ((url_delimiter = g_strrstr (path, VFS_PATH_URL_DELIMITER)) != NULL)
465 char *vfs_prefix_start;
466 char *real_vfs_prefix_start = url_delimiter;
467 char *slash_pointer;
468 struct vfs_s_subclass *sub = NULL;
470 while (real_vfs_prefix_start > path && *(real_vfs_prefix_start) != PATH_SEP)
471 real_vfs_prefix_start--;
472 vfs_prefix_start = real_vfs_prefix_start;
474 if (*(vfs_prefix_start) == PATH_SEP)
475 vfs_prefix_start += 1;
477 *url_delimiter = '\0';
479 element = g_new0 (vfs_path_element_t, 1);
480 element->class = vfs_prefix_to_class (vfs_prefix_start);
481 element->vfs_prefix = g_strdup (vfs_prefix_start);
483 element->dir.converter = INVALID_CONV;
485 url_delimiter += strlen (VFS_PATH_URL_DELIMITER);
486 sub = VFSDATA (element);
487 if (sub != NULL && sub->flags & VFS_S_REMOTE)
489 slash_pointer = strchr (url_delimiter, PATH_SEP);
490 if (slash_pointer == NULL)
492 element->path = g_strdup ("");
494 else
496 element->path = vfs_translate_path_n (slash_pointer + 1);
497 element->encoding = vfs_get_encoding (slash_pointer);
498 *slash_pointer = '\0';
500 vfs_path_url_split (element, url_delimiter);
502 else
504 element->path = vfs_translate_path_n (url_delimiter);
505 element->encoding = vfs_get_encoding (url_delimiter);
507 vpath->path = g_list_prepend (vpath->path, element);
509 if (real_vfs_prefix_start > path && *(real_vfs_prefix_start) == PATH_SEP)
510 *real_vfs_prefix_start = '\0';
511 else
512 *(real_vfs_prefix_start + 1) = '\0';
515 if (path[0] != '\0')
517 element = g_new0 (vfs_path_element_t, 1);
518 element->class = g_ptr_array_index (vfs__classes_list, 0);
519 element->path = vfs_translate_path_n (path);
520 element->encoding = vfs_get_encoding (path);
521 element->dir.converter = INVALID_CONV;
522 vpath->path = g_list_prepend (vpath->path, element);
525 return vpath;
528 /* --------------------------------------------------------------------------------------------- */
529 /*** public functions ****************************************************************************/
530 /* --------------------------------------------------------------------------------------------- */
532 * Convert first elements_count elements from vfs_path_t to string representation.
534 * @param vpath pointer to vfs_path_t object
535 * @param elements_count count of first elements for convert
537 * @return pointer to newly created string.
540 char *
541 vfs_path_to_str_elements_count (const vfs_path_t * vpath, int elements_count)
543 int element_index;
544 GString *buffer;
546 if (vpath == NULL)
547 return NULL;
549 if (elements_count > vfs_path_elements_count (vpath))
550 elements_count = vfs_path_elements_count (vpath);
552 if (elements_count < 0)
553 elements_count = vfs_path_elements_count (vpath) + elements_count;
555 buffer = g_string_new ("");
557 for (element_index = 0; element_index < elements_count; element_index++)
559 vfs_path_element_t *element = vfs_path_get_by_index (vpath, element_index);
561 if (element->vfs_prefix != NULL)
563 char *url_str;
565 if (buffer->str[buffer->len - 1] != '/')
566 g_string_append_c (buffer, '/');
568 g_string_append (buffer, element->vfs_prefix);
569 g_string_append (buffer, VFS_PATH_URL_DELIMITER);
571 url_str = vfs_path_build_url_params_str (element);
572 if (*url_str != '\0')
573 g_string_append (buffer, url_str);
575 g_free (url_str);
578 if (element->encoding != NULL)
580 if (buffer->str[buffer->len - 1] != PATH_SEP)
581 g_string_append (buffer, PATH_SEP_STR);
582 g_string_append (buffer, VFS_ENCODING_PREFIX);
583 g_string_append (buffer, element->encoding);
585 if ((*element->path != PATH_SEP) && (*element->path != '\0')
586 && (buffer->str[buffer->len - 1] != PATH_SEP))
587 g_string_append_c (buffer, PATH_SEP);
589 g_string_append (buffer, element->path);
591 return g_string_free (buffer, FALSE);
594 /* --------------------------------------------------------------------------------------------- */
596 * Convert vfs_path_t to string representation.
598 * @param vpath pointer to vfs_path_t object
600 * @return pointer to newly created string.
602 char *
603 vfs_path_to_str (const vfs_path_t * vpath)
605 return vfs_path_to_str_elements_count (vpath, vfs_path_elements_count (vpath));
608 /* --------------------------------------------------------------------------------------------- */
610 * Split path string to path elements.
612 * @param path_str VFS-path
614 * @return pointer to newly created vfs_path_t object with filled path elements array.
617 vfs_path_t *
618 vfs_path_from_str (const char *path_str)
620 vfs_path_t *vpath;
621 char *path;
623 if (path_str == NULL)
624 return NULL;
626 path = vfs_canon (path_str);
627 if (path == NULL)
628 return NULL;
630 if (vfs_path_is_str_path_deprecated (path))
631 vpath = vfs_path_from_str_deprecated_parser (path);
632 else
633 vpath = vfs_path_from_str_uri_parser (path);
635 g_free (path);
637 return vpath;
640 /* --------------------------------------------------------------------------------------------- */
642 * Create new vfs_path_t object.
644 * @return pointer to newly created vfs_path_t object.
647 vfs_path_t *
648 vfs_path_new (void)
650 vfs_path_t *vpath;
651 vpath = g_new0 (vfs_path_t, 1);
652 return vpath;
655 /* --------------------------------------------------------------------------------------------- */
657 * Get count of path elements.
659 * @param vpath pointer to vfs_path_t object
661 * @return count of path elements.
665 vfs_path_elements_count (const vfs_path_t * vpath)
667 return (vpath != NULL && vpath->path != NULL) ? g_list_length (vpath->path) : 0;
670 /* --------------------------------------------------------------------------------------------- */
672 * Get one path element by index.
674 * @param vpath pointer to vfs_path_t object
675 * @param element_index element index. May have negative value (in this case count was started at the end of list).
677 * @return path element.
680 vfs_path_element_t *
681 vfs_path_get_by_index (const vfs_path_t * vpath, int element_index)
683 if (element_index < 0)
684 element_index += vfs_path_elements_count (vpath);
686 if (element_index < 0)
687 vfs_die ("vfs_path_get_by_index: incorrect index!");
689 return g_list_nth_data (vpath->path, element_index);
692 /* --------------------------------------------------------------------------------------------- */
694 * Clone one path element
696 * @param element pointer to vfs_path_element_t object
698 * @return Newly allocated path element
701 vfs_path_element_t *
702 vfs_path_element_clone (const vfs_path_element_t * element)
704 vfs_path_element_t *new_element = g_new0 (vfs_path_element_t, 1);
705 memcpy (new_element, element, sizeof (vfs_path_element_t));
707 new_element->user = g_strdup (element->user);
708 new_element->password = g_strdup (element->password);
709 new_element->host = g_strdup (element->host);
710 new_element->path = g_strdup (element->path);
711 new_element->encoding = g_strdup (element->encoding);
712 if (vfs_path_element_need_cleanup_converter (element) && new_element->encoding != NULL)
713 new_element->dir.converter = str_crt_conv_from (new_element->encoding);
714 new_element->vfs_prefix = g_strdup (element->vfs_prefix);
716 return new_element;
719 /* --------------------------------------------------------------------------------------------- */
721 * Free one path element.
723 * @param element pointer to vfs_path_element_t object
727 void
728 vfs_path_element_free (vfs_path_element_t * element)
730 if (element == NULL)
731 return;
733 g_free (element->user);
734 g_free (element->password);
735 g_free (element->host);
736 g_free (element->path);
737 g_free (element->encoding);
738 g_free (element->vfs_prefix);
740 if (vfs_path_element_need_cleanup_converter (element))
742 str_close_conv (element->dir.converter);
745 g_free (element);
748 /* --------------------------------------------------------------------------------------------- */
750 * Clone path
752 * @param vpath pointer to vfs_path_t object
754 * @return Newly allocated path object
757 vfs_path_t *
758 vfs_path_clone (const vfs_path_t * vpath)
760 vfs_path_t *new_vpath;
761 int vpath_element_index;
762 if (vpath == NULL)
763 return NULL;
765 new_vpath = vfs_path_new ();
766 for (vpath_element_index = 0; vpath_element_index < vfs_path_elements_count (vpath);
767 vpath_element_index++)
769 new_vpath->path =
770 g_list_append (new_vpath->path,
771 vfs_path_element_clone (vfs_path_get_by_index
772 (vpath, vpath_element_index)));
775 return new_vpath;
778 /* --------------------------------------------------------------------------------------------- */
780 * Free vfs_path_t object.
782 * @param vpath pointer to vfs_path_t object
786 void
787 vfs_path_free (vfs_path_t * path)
789 if (path == NULL)
790 return;
791 g_list_foreach (path->path, (GFunc) vfs_path_element_free, NULL);
792 g_list_free (path->path);
793 g_free (path);
796 /* --------------------------------------------------------------------------------------------- */
798 * Remove one path element by index
800 * @param vpath pointer to vfs_path_t object
801 * @param element_index element index. May have negative value (in this case count was started at the end of list).
805 void
806 vfs_path_remove_element_by_index (vfs_path_t * vpath, int element_index)
808 vfs_path_element_t *element;
810 if ((vpath == NULL) || (vfs_path_elements_count (vpath) == 1))
811 return;
813 if (element_index < 0)
814 element_index = vfs_path_elements_count (vpath) + element_index;
816 element = g_list_nth_data (vpath->path, element_index);
817 vpath->path = g_list_remove (vpath->path, element);
818 vfs_path_element_free (element);
821 /* --------------------------------------------------------------------------------------------- */
822 /** Return VFS class for the given prefix */
824 struct vfs_class *
825 vfs_prefix_to_class (const char *prefix)
827 guint i;
829 /* Avoid first class (localfs) that would accept any prefix */
830 for (i = 1; i < vfs__classes_list->len; i++)
832 struct vfs_class *vfs = (struct vfs_class *) g_ptr_array_index (vfs__classes_list, i);
833 if (vfs->which != NULL)
835 if (vfs->which (vfs, prefix) == -1)
836 continue;
837 return vfs;
840 if (vfs->prefix != NULL && strncmp (prefix, vfs->prefix, strlen (vfs->prefix)) == 0)
841 return vfs;
844 return NULL;
847 /* --------------------------------------------------------------------------------------------- */
849 * Check if need cleanup charset converter for vfs_path_element_t
851 * @param element part of path
853 * @return TRUE if need cleanup converter or FALSE otherwise
856 gboolean
857 vfs_path_element_need_cleanup_converter (const vfs_path_element_t * element)
859 return (element->dir.converter != str_cnv_from_term && element->dir.converter != INVALID_CONV);
862 /* --------------------------------------------------------------------------------------------- */
864 * Serialize vfs_path_t object to string
866 * @param vpath data for serialization
867 * @param error contain pointer to object for handle error code and message
869 * @return serialized vpath as newly allocated string
872 char *
873 vfs_path_serialize (const vfs_path_t * vpath, GError ** error)
875 mc_config_t *cpath = mc_config_init (NULL);
876 ssize_t element_index;
877 char *ret_value;
879 if ((vpath == NULL) || (vfs_path_elements_count (vpath) == 0))
881 g_set_error (error, MC_ERROR, -1, "vpath object is empty");
882 return NULL;
885 for (element_index = 0; element_index < vfs_path_elements_count (vpath); element_index++)
887 char *groupname = g_strdup_printf ("path-element-%zd", element_index);
888 vfs_path_element_t *element = vfs_path_get_by_index (vpath, element_index);
890 /* convert one element to config group */
892 mc_config_set_string_raw (cpath, groupname, "path", element->path);
893 mc_config_set_string_raw (cpath, groupname, "class-name", element->class->name);
894 mc_config_set_string_raw (cpath, groupname, "encoding", element->encoding);
896 mc_config_set_string_raw (cpath, groupname, "vfs_prefix", element->vfs_prefix);
898 mc_config_set_string_raw (cpath, groupname, "user", element->user);
899 mc_config_set_string_raw (cpath, groupname, "password", element->password);
900 mc_config_set_string_raw (cpath, groupname, "host", element->host);
901 if (element->port != 0)
902 mc_config_set_int (cpath, groupname, "port", element->port);
904 g_free (groupname);
907 ret_value = mc_serialize_config (cpath, error);
908 mc_config_deinit (cpath);
909 return ret_value;
912 /* --------------------------------------------------------------------------------------------- */
914 * Deserialize string to vfs_path_t object
916 * @param data data for serialization
917 * @param error contain pointer to object for handle error code and message
919 * @return newly allocated vfs_path_t object
922 vfs_path_t *
923 vfs_path_deserialize (const char *data, GError ** error)
925 mc_config_t *cpath = mc_deserialize_config (data, error);
926 size_t element_index = 0;
927 vfs_path_t *vpath;
929 if (cpath == NULL)
930 return NULL;
932 vpath = vfs_path_new ();
934 while (TRUE)
936 vfs_path_element_t *element;
937 char *cfg_value;
938 char *groupname;
940 groupname = g_strdup_printf ("path-element-%zd", element_index);
941 if (!mc_config_has_group (cpath, groupname))
943 g_free (groupname);
944 break;
947 element = g_new0 (vfs_path_element_t, 1);
948 element->dir.converter = INVALID_CONV;
950 cfg_value = mc_config_get_string_raw (cpath, groupname, "class-name", NULL);
951 element->class = vfs_get_class_by_name (cfg_value);
952 if (element->class == NULL)
954 g_free (element);
955 vfs_path_free (vpath);
956 g_set_error (error, MC_ERROR, -1, "Unable to find VFS class by name '%s'", cfg_value);
957 g_free (cfg_value);
958 mc_config_deinit (cpath);
959 return NULL;
961 g_free (cfg_value);
963 element->path = mc_config_get_string_raw (cpath, groupname, "path", NULL);
964 element->encoding = mc_config_get_string_raw (cpath, groupname, "encoding", NULL);
966 element->vfs_prefix = mc_config_get_string_raw (cpath, groupname, "vfs_prefix", NULL);
968 element->user = mc_config_get_string_raw (cpath, groupname, "user", NULL);
969 element->password = mc_config_get_string_raw (cpath, groupname, "password", NULL);
970 element->host = mc_config_get_string_raw (cpath, groupname, "host", NULL);
971 element->port = mc_config_get_int (cpath, groupname, "port", 0);
973 vpath->path = g_list_append (vpath->path, element);
975 g_free (groupname);
976 element_index++;
979 mc_config_deinit (cpath);
980 if (vfs_path_elements_count (vpath) == 0)
982 vfs_path_free (vpath);
983 g_set_error (error, MC_ERROR, -1, "No any path elements found");
984 return NULL;
987 return vpath;
990 /* --------------------------------------------------------------------------------------------- */