path.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038
  1. /*
  2. Virtual File System path handlers
  3. Copyright (C) 2011
  4. The Free Software Foundation, Inc.
  5. Written by:
  6. Slava Zanko <slavazanko@gmail.com>, 2011
  7. This file is part of the Midnight Commander.
  8. The Midnight Commander is free software: you can redistribute it
  9. and/or modify it under the terms of the GNU General Public License as
  10. published by the Free Software Foundation, either version 3 of the License,
  11. or (at your option) any later version.
  12. The Midnight Commander is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16. You should have received a copy of the GNU General Public License
  17. along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /**
  20. * \file
  21. * \brief Source: Virtual File System: path handlers
  22. * \author Slava Zanko
  23. * \date 2011
  24. */
  25. #include <config.h>
  26. #include "lib/global.h"
  27. #include "lib/strutil.h"
  28. #include "lib/util.h" /* concat_dir_and_file */
  29. #include "lib/serialize.h"
  30. #include "vfs.h"
  31. #include "utilvfs.h"
  32. #include "xdirentry.h"
  33. #include "path.h"
  34. extern GPtrArray *vfs__classes_list;
  35. /*** global variables ****************************************************************************/
  36. /*** file scope macro definitions ****************************************************************/
  37. /*** file scope type declarations ****************************************************************/
  38. /*** file scope variables ************************************************************************/
  39. /*** file scope functions ************************************************************************/
  40. /* --------------------------------------------------------------------------------------------- */
  41. static gboolean
  42. path_magic (const char *path)
  43. {
  44. struct stat buf;
  45. return (stat (path, &buf) != 0);
  46. }
  47. /* --------------------------------------------------------------------------------------------- */
  48. /**
  49. * Splits path extracting vfs part.
  50. *
  51. * Splits path
  52. * \verbatim /p1#op/inpath \endverbatim
  53. * into
  54. * \verbatim inpath,op; \endverbatim
  55. * returns which vfs it is.
  56. * What is left in path is p1. You still want to g_free(path), you DON'T
  57. * want to free neither *inpath nor *op
  58. */
  59. static struct vfs_class *
  60. _vfs_split_with_semi_skip_count (char *path, const char **inpath, const char **op,
  61. size_t skip_count)
  62. {
  63. char *semi;
  64. char *slash;
  65. struct vfs_class *ret;
  66. if (path == NULL)
  67. vfs_die ("Cannot split NULL");
  68. semi = strrstr_skip_count (path, "#", skip_count);
  69. if ((semi == NULL) || (!path_magic (path)))
  70. return NULL;
  71. slash = strchr (semi, PATH_SEP);
  72. *semi = '\0';
  73. if (op != NULL)
  74. *op = NULL;
  75. if (inpath != NULL)
  76. *inpath = NULL;
  77. if (slash != NULL)
  78. *slash = '\0';
  79. ret = vfs_prefix_to_class (semi + 1);
  80. if (ret != NULL)
  81. {
  82. if (op != NULL)
  83. *op = semi + 1;
  84. if (inpath != NULL)
  85. *inpath = slash != NULL ? slash + 1 : NULL;
  86. return ret;
  87. }
  88. if (slash != NULL)
  89. *slash = PATH_SEP;
  90. *semi = '#';
  91. ret = _vfs_split_with_semi_skip_count (path, inpath, op, skip_count + 1);
  92. return ret;
  93. }
  94. /* --------------------------------------------------------------------------------------------- */
  95. /**
  96. * remove //, /./ and /../
  97. *
  98. * @return newly allocated string
  99. */
  100. static char *
  101. vfs_canon (const char *path)
  102. {
  103. if (!path)
  104. vfs_die ("Cannot canonicalize NULL");
  105. /* Relative to current directory */
  106. if (*path != PATH_SEP)
  107. {
  108. char *local, *result, *curr_dir;
  109. curr_dir = vfs_get_current_dir ();
  110. local = concat_dir_and_file (curr_dir, path);
  111. g_free (curr_dir);
  112. result = vfs_canon (local);
  113. g_free (local);
  114. return result;
  115. }
  116. /*
  117. * So we have path of following form:
  118. * /p1/p2#op/.././././p3#op/p4. Good luck.
  119. */
  120. {
  121. char *result = g_strdup (path);
  122. canonicalize_pathname (result);
  123. return result;
  124. }
  125. }
  126. /* --------------------------------------------------------------------------------------------- */
  127. /**
  128. * Build URL parameters (such as user:pass@host:port) from one path element object
  129. *
  130. * @param element path element
  131. *
  132. * @return newly allocated string
  133. */
  134. static char *
  135. vfs_path_build_url_params_str (vfs_path_element_t * element)
  136. {
  137. GString *buffer;
  138. if (element == NULL)
  139. return NULL;
  140. buffer = g_string_new ("");
  141. if (element->user != NULL)
  142. g_string_append (buffer, element->user);
  143. if (element->password != NULL)
  144. {
  145. g_string_append_c (buffer, ':');
  146. g_string_append (buffer, element->password);
  147. }
  148. if (element->host != NULL)
  149. {
  150. if ((element->user != NULL) || (element->password != NULL))
  151. g_string_append_c (buffer, '@');
  152. if (element->ipv6)
  153. g_string_append_c (buffer, '[');
  154. g_string_append (buffer, element->host);
  155. if (element->ipv6)
  156. g_string_append_c (buffer, ']');
  157. }
  158. if ((element->port) != 0 && (element->host != NULL))
  159. {
  160. g_string_append_c (buffer, ':');
  161. g_string_append_printf (buffer, "%d", element->port);
  162. }
  163. return g_string_free (buffer, FALSE);
  164. }
  165. /* --------------------------------------------------------------------------------------------- */
  166. /** get encoding after last #enc: or NULL, if part does not contain #enc:
  167. *
  168. * @param path string
  169. *
  170. * @return newly allocated string.
  171. */
  172. static char *
  173. vfs_get_encoding (const char *path)
  174. {
  175. char result[16];
  176. char *work;
  177. char *semi;
  178. char *slash;
  179. work = g_strdup (path);
  180. /* try found #enc: */
  181. semi = g_strrstr (work, VFS_ENCODING_PREFIX);
  182. if (semi != NULL && (semi == work || *(semi - 1) == PATH_SEP))
  183. {
  184. semi += strlen (VFS_ENCODING_PREFIX); /* skip "#enc:" */
  185. slash = strchr (semi, PATH_SEP);
  186. if (slash != NULL)
  187. slash[0] = '\0';
  188. g_strlcpy (result, semi, sizeof (result));
  189. g_free (work);
  190. return g_strdup (result);
  191. }
  192. else
  193. {
  194. g_free (work);
  195. return NULL;
  196. }
  197. }
  198. /* --------------------------------------------------------------------------------------------- */
  199. /** Extract the hostname and username from the path
  200. *
  201. * Format of the path is [user@]hostname:port/remote-dir, e.g.:
  202. *
  203. * ftp://sunsite.unc.edu/pub/linux
  204. * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
  205. * ftp://tsx-11.mit.edu:8192/
  206. * ftp://joe@foo.edu:11321/private
  207. * ftp://joe:password@foo.se
  208. *
  209. * @param path_element is an input string to be parsed
  210. * @param path is an input string to be parsed
  211. *
  212. * @return g_malloc()ed url info.
  213. * If the user is empty, e.g. ftp://@roxanne/private, and URL_USE_ANONYMOUS
  214. * is not set, then the current login name is supplied.
  215. * Return value is a g_malloc()ed structure with the pathname relative to the
  216. * host.
  217. */
  218. static void
  219. vfs_path_url_split (vfs_path_element_t * path_element, const char *path)
  220. {
  221. char *pcopy;
  222. const char *pend;
  223. char *dir, *colon, *inner_colon, *at, *rest;
  224. path_element->port = 0;
  225. pcopy = g_strdup (path);
  226. pend = pcopy + strlen (pcopy);
  227. dir = pcopy;
  228. /* search for any possible user */
  229. at = strrchr (pcopy, '@');
  230. /* We have a username */
  231. if (at == NULL)
  232. rest = pcopy;
  233. else
  234. {
  235. *at = '\0';
  236. inner_colon = strchr (pcopy, ':');
  237. if (inner_colon != NULL)
  238. {
  239. *inner_colon = '\0';
  240. inner_colon++;
  241. path_element->password = g_strdup (inner_colon);
  242. }
  243. if (*pcopy != '\0')
  244. path_element->user = g_strdup (pcopy);
  245. if (pend == at + 1)
  246. rest = at;
  247. else
  248. rest = at + 1;
  249. }
  250. /* Check if the host comes with a port spec, if so, chop it */
  251. if (*rest != '[')
  252. colon = strchr (rest, ':');
  253. else
  254. {
  255. colon = strchr (++rest, ']');
  256. if (colon != NULL)
  257. {
  258. colon[0] = '\0';
  259. colon[1] = '\0';
  260. colon++;
  261. path_element->ipv6 = TRUE;
  262. }
  263. }
  264. if (colon != NULL)
  265. {
  266. *colon = '\0';
  267. if (sscanf (colon + 1, "%d", &path_element->port) == 1)
  268. {
  269. if (path_element->port <= 0 || path_element->port >= 65536)
  270. path_element->port = 0;
  271. }
  272. else
  273. while (*(++colon) != '\0')
  274. {
  275. switch (*colon)
  276. {
  277. case 'C':
  278. path_element->port = 1;
  279. break;
  280. case 'r':
  281. path_element->port = 2;
  282. break;
  283. }
  284. }
  285. }
  286. path_element->host = g_strdup (rest);
  287. g_free (pcopy);
  288. }
  289. /* --------------------------------------------------------------------------------------------- */
  290. /**
  291. * get VFS class for the given name
  292. *
  293. * @param class_name name of class
  294. *
  295. * @return pointer to class structure or NULL if class not found
  296. */
  297. static struct vfs_class *
  298. vfs_get_class_by_name (const char *class_name)
  299. {
  300. guint i;
  301. if (class_name == NULL)
  302. return NULL;
  303. for (i = 0; i < vfs__classes_list->len; i++)
  304. {
  305. struct vfs_class *vfs = (struct vfs_class *) g_ptr_array_index (vfs__classes_list, i);
  306. if ((vfs->name != NULL) && (strcmp (vfs->name, class_name) == 0))
  307. return vfs;
  308. }
  309. return NULL;
  310. }
  311. /* --------------------------------------------------------------------------------------------- */
  312. /**
  313. * Check if path string contain URL-like elements
  314. *
  315. * @param path_str path
  316. *
  317. * @return TRUE if path is deprecated or FALSE otherwise
  318. */
  319. static gboolean
  320. vfs_path_is_str_path_deprecated (const char *path_str)
  321. {
  322. return strstr (path_str, VFS_PATH_URL_DELIMITER) == NULL;
  323. }
  324. /* --------------------------------------------------------------------------------------------- */
  325. /** Split path string to path elements by deprecated algorithm.
  326. *
  327. * @param path_str VFS-path
  328. *
  329. * @return pointer to newly created vfs_path_t object with filled path elements array.
  330. */
  331. static vfs_path_t *
  332. vfs_path_from_str_deprecated_parser (char *path)
  333. {
  334. vfs_path_t *vpath;
  335. vfs_path_element_t *element;
  336. struct vfs_class *class;
  337. const char *local, *op;
  338. vpath = vfs_path_new ();
  339. while ((class = _vfs_split_with_semi_skip_count (path, &local, &op, 0)) != NULL)
  340. {
  341. char *url_params;
  342. element = g_new0 (vfs_path_element_t, 1);
  343. element->class = class;
  344. if (local == NULL)
  345. local = "";
  346. element->path = vfs_translate_path_n (local);
  347. element->encoding = vfs_get_encoding (local);
  348. element->dir.converter =
  349. (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
  350. url_params = strchr (op, ':'); /* skip VFS prefix */
  351. if (url_params != NULL)
  352. {
  353. *url_params = '\0';
  354. url_params++;
  355. vfs_path_url_split (element, url_params);
  356. }
  357. if (*op != '\0')
  358. element->vfs_prefix = g_strdup (op);
  359. vpath->path = g_list_prepend (vpath->path, element);
  360. }
  361. if (path[0] != '\0')
  362. {
  363. element = g_new0 (vfs_path_element_t, 1);
  364. element->class = g_ptr_array_index (vfs__classes_list, 0);
  365. element->path = vfs_translate_path_n (path);
  366. element->encoding = vfs_get_encoding (path);
  367. element->dir.converter =
  368. (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
  369. vpath->path = g_list_prepend (vpath->path, element);
  370. }
  371. return vpath;
  372. }
  373. /* --------------------------------------------------------------------------------------------- */
  374. /** Split path string to path elements by URL algorithm.
  375. *
  376. * @param path_str VFS-path
  377. *
  378. * @return pointer to newly created vfs_path_t object with filled path elements array.
  379. */
  380. static vfs_path_t *
  381. vfs_path_from_str_uri_parser (char *path)
  382. {
  383. vfs_path_t *vpath;
  384. vfs_path_element_t *element;
  385. char *url_delimiter;
  386. vpath = vfs_path_new ();
  387. while ((url_delimiter = g_strrstr (path, VFS_PATH_URL_DELIMITER)) != NULL)
  388. {
  389. char *vfs_prefix_start;
  390. char *real_vfs_prefix_start = url_delimiter;
  391. char *slash_pointer;
  392. struct vfs_s_subclass *sub = NULL;
  393. while (real_vfs_prefix_start > path && *(real_vfs_prefix_start) != PATH_SEP)
  394. real_vfs_prefix_start--;
  395. vfs_prefix_start = real_vfs_prefix_start;
  396. if (*(vfs_prefix_start) == PATH_SEP)
  397. vfs_prefix_start += 1;
  398. *url_delimiter = '\0';
  399. element = g_new0 (vfs_path_element_t, 1);
  400. element->class = vfs_prefix_to_class (vfs_prefix_start);
  401. element->vfs_prefix = g_strdup (vfs_prefix_start);
  402. url_delimiter += strlen (VFS_PATH_URL_DELIMITER);
  403. sub = VFSDATA (element);
  404. if (sub != NULL && sub->flags & VFS_S_REMOTE)
  405. {
  406. slash_pointer = strchr (url_delimiter, PATH_SEP);
  407. if (slash_pointer == NULL)
  408. {
  409. element->path = g_strdup ("");
  410. }
  411. else
  412. {
  413. element->path = vfs_translate_path_n (slash_pointer + 1);
  414. element->encoding = vfs_get_encoding (slash_pointer);
  415. *slash_pointer = '\0';
  416. }
  417. vfs_path_url_split (element, url_delimiter);
  418. }
  419. else
  420. {
  421. element->path = vfs_translate_path_n (url_delimiter);
  422. element->encoding = vfs_get_encoding (url_delimiter);
  423. }
  424. element->dir.converter =
  425. (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
  426. vpath->path = g_list_prepend (vpath->path, element);
  427. if (real_vfs_prefix_start > path && *(real_vfs_prefix_start) == PATH_SEP)
  428. *real_vfs_prefix_start = '\0';
  429. else
  430. *(real_vfs_prefix_start + 1) = '\0';
  431. }
  432. if (path[0] != '\0')
  433. {
  434. element = g_new0 (vfs_path_element_t, 1);
  435. element->class = g_ptr_array_index (vfs__classes_list, 0);
  436. element->path = vfs_translate_path_n (path);
  437. element->encoding = vfs_get_encoding (path);
  438. element->dir.converter =
  439. (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
  440. vpath->path = g_list_prepend (vpath->path, element);
  441. }
  442. return vpath;
  443. }
  444. /* --------------------------------------------------------------------------------------------- */
  445. /*** public functions ****************************************************************************/
  446. /* --------------------------------------------------------------------------------------------- */
  447. /**
  448. * Convert first elements_count elements from vfs_path_t to string representation.
  449. *
  450. * @param vpath pointer to vfs_path_t object
  451. * @param elements_count count of first elements for convert
  452. * @param flags flags for parser
  453. *
  454. * @return pointer to newly created string.
  455. */
  456. #define vfs_append_from_path(appendfrom) \
  457. { \
  458. if ((*appendfrom != PATH_SEP) && (*appendfrom != '\0') \
  459. && (buffer->str[buffer->len - 1] != PATH_SEP)) \
  460. g_string_append_c (buffer, PATH_SEP); \
  461. g_string_append (buffer, appendfrom); \
  462. }
  463. char *
  464. vfs_path_to_str_elements_count (const vfs_path_t * vpath, int elements_count)
  465. {
  466. int element_index;
  467. GString *buffer;
  468. GString *recode_buffer;
  469. if (vpath == NULL)
  470. return NULL;
  471. if (elements_count > vfs_path_elements_count (vpath))
  472. elements_count = vfs_path_elements_count (vpath);
  473. if (elements_count < 0)
  474. elements_count = vfs_path_elements_count (vpath) + elements_count;
  475. buffer = g_string_new ("");
  476. recode_buffer = g_string_new ("");
  477. for (element_index = 0; element_index < elements_count; element_index++)
  478. {
  479. vfs_path_element_t *element = vfs_path_get_by_index (vpath, element_index);
  480. if (element->vfs_prefix != NULL)
  481. {
  482. char *url_str;
  483. if (buffer->str[buffer->len - 1] != '/')
  484. g_string_append_c (buffer, '/');
  485. g_string_append (buffer, element->vfs_prefix);
  486. g_string_append (buffer, VFS_PATH_URL_DELIMITER);
  487. url_str = vfs_path_build_url_params_str (element);
  488. if (*url_str != '\0')
  489. g_string_append (buffer, url_str);
  490. g_free (url_str);
  491. }
  492. if (vfs_path_element_need_cleanup_converter (element))
  493. {
  494. if (buffer->str[buffer->len - 1] != PATH_SEP)
  495. g_string_append (buffer, PATH_SEP_STR);
  496. g_string_append (buffer, VFS_ENCODING_PREFIX);
  497. g_string_append (buffer, element->encoding);
  498. str_vfs_convert_from (element->dir.converter, element->path, recode_buffer);
  499. vfs_append_from_path (recode_buffer->str);
  500. g_string_set_size (recode_buffer, 0);
  501. }
  502. else
  503. {
  504. vfs_append_from_path (element->path);
  505. }
  506. }
  507. g_string_free (recode_buffer, TRUE);
  508. return g_string_free (buffer, FALSE);
  509. }
  510. #undef vfs_append_from_path
  511. /* --------------------------------------------------------------------------------------------- */
  512. /**
  513. * Convert vfs_path_t to string representation.
  514. *
  515. * @param vpath pointer to vfs_path_t object
  516. *
  517. * @return pointer to newly created string.
  518. */
  519. char *
  520. vfs_path_to_str (const vfs_path_t * vpath)
  521. {
  522. return vfs_path_to_str_elements_count (vpath, vfs_path_elements_count (vpath));
  523. }
  524. /* --------------------------------------------------------------------------------------------- */
  525. /**
  526. * Split path string to path elements with flags for change parce process.
  527. *
  528. * @param path_str VFS-path
  529. * @param flags flags for parser
  530. *
  531. * @return pointer to newly created vfs_path_t object with filled path elements array.
  532. */
  533. vfs_path_t *
  534. vfs_path_from_str_flags (const char *path_str, vfs_path_flag_t flags)
  535. {
  536. vfs_path_t *vpath;
  537. char *path;
  538. if (path_str == NULL)
  539. return NULL;
  540. if ((flags & VPF_NO_CANON) == 0)
  541. path = vfs_canon (path_str);
  542. else
  543. path = g_strdup (path_str);
  544. if (path == NULL)
  545. return NULL;
  546. if ((flags & VPF_USE_DEPRECATED_PARSER) != 0 && vfs_path_is_str_path_deprecated (path))
  547. vpath = vfs_path_from_str_deprecated_parser (path);
  548. else
  549. vpath = vfs_path_from_str_uri_parser (path);
  550. g_free (path);
  551. return vpath;
  552. }
  553. /* --------------------------------------------------------------------------------------------- */
  554. /**
  555. * Split path string to path elements.
  556. *
  557. * @param path_str VFS-path
  558. *
  559. * @return pointer to newly created vfs_path_t object with filled path elements array.
  560. */
  561. vfs_path_t *
  562. vfs_path_from_str (const char *path_str)
  563. {
  564. return vfs_path_from_str_flags (path_str, VPF_NONE);
  565. }
  566. /* --------------------------------------------------------------------------------------------- */
  567. /*
  568. * Create new vfs_path_t object.
  569. *
  570. * @return pointer to newly created vfs_path_t object.
  571. */
  572. vfs_path_t *
  573. vfs_path_new (void)
  574. {
  575. vfs_path_t *vpath;
  576. vpath = g_new0 (vfs_path_t, 1);
  577. return vpath;
  578. }
  579. /* --------------------------------------------------------------------------------------------- */
  580. /*
  581. * Get count of path elements.
  582. *
  583. * @param vpath pointer to vfs_path_t object
  584. *
  585. * @return count of path elements.
  586. */
  587. int
  588. vfs_path_elements_count (const vfs_path_t * vpath)
  589. {
  590. return (vpath != NULL && vpath->path != NULL) ? g_list_length (vpath->path) : 0;
  591. }
  592. /* --------------------------------------------------------------------------------------------- */
  593. /*
  594. * Get one path element by index.
  595. *
  596. * @param vpath pointer to vfs_path_t object
  597. * @param element_index element index. May have negative value (in this case count was started at the end of list).
  598. *
  599. * @return path element.
  600. */
  601. vfs_path_element_t *
  602. vfs_path_get_by_index (const vfs_path_t * vpath, int element_index)
  603. {
  604. if (element_index < 0)
  605. element_index += vfs_path_elements_count (vpath);
  606. if (element_index < 0)
  607. vfs_die ("vfs_path_get_by_index: incorrect index!");
  608. return g_list_nth_data (vpath->path, element_index);
  609. }
  610. /* --------------------------------------------------------------------------------------------- */
  611. /*
  612. * Clone one path element
  613. *
  614. * @param element pointer to vfs_path_element_t object
  615. *
  616. * @return Newly allocated path element
  617. */
  618. vfs_path_element_t *
  619. vfs_path_element_clone (const vfs_path_element_t * element)
  620. {
  621. vfs_path_element_t *new_element = g_new0 (vfs_path_element_t, 1);
  622. memcpy (new_element, element, sizeof (vfs_path_element_t));
  623. new_element->user = g_strdup (element->user);
  624. new_element->password = g_strdup (element->password);
  625. new_element->host = g_strdup (element->host);
  626. new_element->path = g_strdup (element->path);
  627. new_element->encoding = g_strdup (element->encoding);
  628. if (vfs_path_element_need_cleanup_converter (element) && new_element->encoding != NULL)
  629. new_element->dir.converter = str_crt_conv_from (new_element->encoding);
  630. new_element->vfs_prefix = g_strdup (element->vfs_prefix);
  631. return new_element;
  632. }
  633. /* --------------------------------------------------------------------------------------------- */
  634. /*
  635. * Free one path element.
  636. *
  637. * @param element pointer to vfs_path_element_t object
  638. *
  639. */
  640. void
  641. vfs_path_element_free (vfs_path_element_t * element)
  642. {
  643. if (element == NULL)
  644. return;
  645. g_free (element->user);
  646. g_free (element->password);
  647. g_free (element->host);
  648. g_free (element->path);
  649. g_free (element->encoding);
  650. g_free (element->vfs_prefix);
  651. if (vfs_path_element_need_cleanup_converter (element))
  652. {
  653. str_close_conv (element->dir.converter);
  654. }
  655. g_free (element);
  656. }
  657. /* --------------------------------------------------------------------------------------------- */
  658. /*
  659. * Clone path
  660. *
  661. * @param vpath pointer to vfs_path_t object
  662. *
  663. * @return Newly allocated path object
  664. */
  665. vfs_path_t *
  666. vfs_path_clone (const vfs_path_t * vpath)
  667. {
  668. vfs_path_t *new_vpath;
  669. int vpath_element_index;
  670. if (vpath == NULL)
  671. return NULL;
  672. new_vpath = vfs_path_new ();
  673. for (vpath_element_index = 0; vpath_element_index < vfs_path_elements_count (vpath);
  674. vpath_element_index++)
  675. {
  676. new_vpath->path =
  677. g_list_append (new_vpath->path,
  678. vfs_path_element_clone (vfs_path_get_by_index
  679. (vpath, vpath_element_index)));
  680. }
  681. return new_vpath;
  682. }
  683. /* --------------------------------------------------------------------------------------------- */
  684. /*
  685. * Free vfs_path_t object.
  686. *
  687. * @param vpath pointer to vfs_path_t object
  688. *
  689. */
  690. void
  691. vfs_path_free (vfs_path_t * path)
  692. {
  693. if (path == NULL)
  694. return;
  695. g_list_foreach (path->path, (GFunc) vfs_path_element_free, NULL);
  696. g_list_free (path->path);
  697. g_free (path);
  698. }
  699. /* --------------------------------------------------------------------------------------------- */
  700. /*
  701. * Remove one path element by index
  702. *
  703. * @param vpath pointer to vfs_path_t object
  704. * @param element_index element index. May have negative value (in this case count was started at the end of list).
  705. *
  706. */
  707. void
  708. vfs_path_remove_element_by_index (vfs_path_t * vpath, int element_index)
  709. {
  710. vfs_path_element_t *element;
  711. if ((vpath == NULL) || (vfs_path_elements_count (vpath) == 1))
  712. return;
  713. if (element_index < 0)
  714. element_index = vfs_path_elements_count (vpath) + element_index;
  715. element = g_list_nth_data (vpath->path, element_index);
  716. vpath->path = g_list_remove (vpath->path, element);
  717. vfs_path_element_free (element);
  718. }
  719. /* --------------------------------------------------------------------------------------------- */
  720. /** Return VFS class for the given prefix */
  721. struct vfs_class *
  722. vfs_prefix_to_class (const char *prefix)
  723. {
  724. guint i;
  725. /* Avoid first class (localfs) that would accept any prefix */
  726. for (i = 1; i < vfs__classes_list->len; i++)
  727. {
  728. struct vfs_class *vfs = (struct vfs_class *) g_ptr_array_index (vfs__classes_list, i);
  729. if (vfs->which != NULL)
  730. {
  731. if (vfs->which (vfs, prefix) == -1)
  732. continue;
  733. return vfs;
  734. }
  735. if (vfs->prefix != NULL && strncmp (prefix, vfs->prefix, strlen (vfs->prefix)) == 0)
  736. return vfs;
  737. }
  738. return NULL;
  739. }
  740. /* --------------------------------------------------------------------------------------------- */
  741. /**
  742. * Check if need cleanup charset converter for vfs_path_element_t
  743. *
  744. * @param element part of path
  745. *
  746. * @return TRUE if need cleanup converter or FALSE otherwise
  747. */
  748. gboolean
  749. vfs_path_element_need_cleanup_converter (const vfs_path_element_t * element)
  750. {
  751. return (element->dir.converter != str_cnv_from_term && element->dir.converter != INVALID_CONV);
  752. }
  753. /* --------------------------------------------------------------------------------------------- */
  754. /**
  755. * Serialize vfs_path_t object to string
  756. *
  757. * @param vpath data for serialization
  758. * @param error contain pointer to object for handle error code and message
  759. *
  760. * @return serialized vpath as newly allocated string
  761. */
  762. char *
  763. vfs_path_serialize (const vfs_path_t * vpath, GError ** error)
  764. {
  765. mc_config_t *cpath = mc_config_init (NULL);
  766. ssize_t element_index;
  767. char *ret_value;
  768. if ((vpath == NULL) || (vfs_path_elements_count (vpath) == 0))
  769. {
  770. g_set_error (error, MC_ERROR, -1, "vpath object is empty");
  771. return NULL;
  772. }
  773. for (element_index = 0; element_index < vfs_path_elements_count (vpath); element_index++)
  774. {
  775. char *groupname = g_strdup_printf ("path-element-%zd", element_index);
  776. vfs_path_element_t *element = vfs_path_get_by_index (vpath, element_index);
  777. /* convert one element to config group */
  778. mc_config_set_string_raw (cpath, groupname, "path", element->path);
  779. mc_config_set_string_raw (cpath, groupname, "class-name", element->class->name);
  780. mc_config_set_string_raw (cpath, groupname, "encoding", element->encoding);
  781. mc_config_set_string_raw (cpath, groupname, "vfs_prefix", element->vfs_prefix);
  782. mc_config_set_string_raw (cpath, groupname, "user", element->user);
  783. mc_config_set_string_raw (cpath, groupname, "password", element->password);
  784. mc_config_set_string_raw (cpath, groupname, "host", element->host);
  785. if (element->port != 0)
  786. mc_config_set_int (cpath, groupname, "port", element->port);
  787. g_free (groupname);
  788. }
  789. ret_value = mc_serialize_config (cpath, error);
  790. mc_config_deinit (cpath);
  791. return ret_value;
  792. }
  793. /* --------------------------------------------------------------------------------------------- */
  794. /**
  795. * Deserialize string to vfs_path_t object
  796. *
  797. * @param data data for serialization
  798. * @param error contain pointer to object for handle error code and message
  799. *
  800. * @return newly allocated vfs_path_t object
  801. */
  802. vfs_path_t *
  803. vfs_path_deserialize (const char *data, GError ** error)
  804. {
  805. mc_config_t *cpath = mc_deserialize_config (data, error);
  806. size_t element_index = 0;
  807. vfs_path_t *vpath;
  808. if (cpath == NULL)
  809. return NULL;
  810. vpath = vfs_path_new ();
  811. while (TRUE)
  812. {
  813. vfs_path_element_t *element;
  814. char *cfg_value;
  815. char *groupname;
  816. groupname = g_strdup_printf ("path-element-%zd", element_index);
  817. if (!mc_config_has_group (cpath, groupname))
  818. {
  819. g_free (groupname);
  820. break;
  821. }
  822. element = g_new0 (vfs_path_element_t, 1);
  823. cfg_value = mc_config_get_string_raw (cpath, groupname, "class-name", NULL);
  824. element->class = vfs_get_class_by_name (cfg_value);
  825. if (element->class == NULL)
  826. {
  827. g_free (element);
  828. vfs_path_free (vpath);
  829. g_set_error (error, MC_ERROR, -1, "Unable to find VFS class by name '%s'", cfg_value);
  830. g_free (cfg_value);
  831. mc_config_deinit (cpath);
  832. return NULL;
  833. }
  834. g_free (cfg_value);
  835. element->path = mc_config_get_string_raw (cpath, groupname, "path", NULL);
  836. element->encoding = mc_config_get_string_raw (cpath, groupname, "encoding", NULL);
  837. element->dir.converter =
  838. (element->encoding != NULL) ? str_crt_conv_from (element->encoding) : INVALID_CONV;
  839. element->vfs_prefix = mc_config_get_string_raw (cpath, groupname, "vfs_prefix", NULL);
  840. element->user = mc_config_get_string_raw (cpath, groupname, "user", NULL);
  841. element->password = mc_config_get_string_raw (cpath, groupname, "password", NULL);
  842. element->host = mc_config_get_string_raw (cpath, groupname, "host", NULL);
  843. element->port = mc_config_get_int (cpath, groupname, "port", 0);
  844. vpath->path = g_list_append (vpath->path, element);
  845. g_free (groupname);
  846. element_index++;
  847. }
  848. mc_config_deinit (cpath);
  849. if (vfs_path_elements_count (vpath) == 0)
  850. {
  851. vfs_path_free (vpath);
  852. g_set_error (error, MC_ERROR, -1, "No any path elements found");
  853. return NULL;
  854. }
  855. return vpath;
  856. }
  857. /* --------------------------------------------------------------------------------------------- */