path.c 47 KB

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