util.c 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550
  1. /*
  2. Various utilities
  3. Copyright (C) 1994-2024
  4. Free Software Foundation, Inc.
  5. Written by:
  6. Miguel de Icaza, 1994, 1995, 1996
  7. Janne Kukonlehto, 1994, 1995, 1996
  8. Dugan Porter, 1994, 1995, 1996
  9. Jakub Jelinek, 1994, 1995, 1996
  10. Mauricio Plaza, 1994, 1995, 1996
  11. Slava Zanko <slavazanko@gmail.com>, 2013
  12. This file is part of the Midnight Commander.
  13. The Midnight Commander is free software: you can redistribute it
  14. and/or modify it under the terms of the GNU General Public License as
  15. published by the Free Software Foundation, either version 3 of the License,
  16. or (at your option) any later version.
  17. The Midnight Commander is distributed in the hope that it will be useful,
  18. but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. GNU General Public License for more details.
  21. You should have received a copy of the GNU General Public License
  22. along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. /** \file lib/util.c
  25. * \brief Source: various utilities
  26. */
  27. #include <config.h>
  28. #include <ctype.h>
  29. #include <stddef.h> /* ptrdiff_t */
  30. #include <limits.h>
  31. #include <stdarg.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <sys/types.h>
  36. #include <sys/stat.h>
  37. #include <unistd.h>
  38. #include "lib/global.h"
  39. #include "lib/mcconfig.h"
  40. #include "lib/fileloc.h"
  41. #include "lib/vfs/vfs.h"
  42. #include "lib/strutil.h"
  43. #include "lib/util.h"
  44. /*** global variables ****************************************************************************/
  45. /*** file scope macro definitions ****************************************************************/
  46. #define ismode(n,m) ((n & m) == m)
  47. /* Number of attempts to create a temporary file */
  48. #ifndef TMP_MAX
  49. #define TMP_MAX 16384
  50. #endif /* !TMP_MAX */
  51. #define TMP_SUFFIX ".tmp"
  52. #define ASCII_A (0x40 + 1)
  53. #define ASCII_Z (0x40 + 26)
  54. #define ASCII_a (0x60 + 1)
  55. #define ASCII_z (0x60 + 26)
  56. /*** file scope type declarations ****************************************************************/
  57. /*** forward declarations (file scope functions) *************************************************/
  58. /*** file scope variables ************************************************************************/
  59. /* --------------------------------------------------------------------------------------------- */
  60. /*** file scope functions ************************************************************************/
  61. /* --------------------------------------------------------------------------------------------- */
  62. #ifndef HAVE_CHARSET
  63. static inline int
  64. is_7bit_printable (unsigned char c)
  65. {
  66. return (c > 31 && c < 127);
  67. }
  68. #endif
  69. /* --------------------------------------------------------------------------------------------- */
  70. static inline int
  71. is_iso_printable (unsigned char c)
  72. {
  73. return ((c > 31 && c < 127) || c >= 160);
  74. }
  75. /* --------------------------------------------------------------------------------------------- */
  76. static inline int
  77. is_8bit_printable (unsigned char c)
  78. {
  79. /* "Full 8 bits output" doesn't work on xterm */
  80. if (mc_global.tty.xterm_flag)
  81. return is_iso_printable (c);
  82. return (c > 31 && c != 127 && c != 155);
  83. }
  84. /* --------------------------------------------------------------------------------------------- */
  85. static char *
  86. resolve_symlinks (const vfs_path_t *vpath)
  87. {
  88. char *p, *p2;
  89. char *buf, *buf2, *q, *r, c;
  90. struct stat mybuf;
  91. if (vpath->relative)
  92. return NULL;
  93. p = p2 = g_strdup (vfs_path_as_str (vpath));
  94. r = buf = g_malloc (MC_MAXPATHLEN);
  95. buf2 = g_malloc (MC_MAXPATHLEN);
  96. *r++ = PATH_SEP;
  97. *r = '\0';
  98. do
  99. {
  100. q = strchr (p + 1, PATH_SEP);
  101. if (q == NULL)
  102. {
  103. q = strchr (p + 1, '\0');
  104. if (q == p + 1)
  105. break;
  106. }
  107. c = *q;
  108. *q = '\0';
  109. if (mc_lstat (vpath, &mybuf) < 0)
  110. {
  111. MC_PTR_FREE (buf);
  112. goto ret;
  113. }
  114. if (!S_ISLNK (mybuf.st_mode))
  115. strcpy (r, p + 1);
  116. else
  117. {
  118. int len;
  119. len = mc_readlink (vpath, buf2, MC_MAXPATHLEN - 1);
  120. if (len < 0)
  121. {
  122. MC_PTR_FREE (buf);
  123. goto ret;
  124. }
  125. buf2[len] = '\0';
  126. if (IS_PATH_SEP (*buf2))
  127. strcpy (buf, buf2);
  128. else
  129. strcpy (r, buf2);
  130. }
  131. canonicalize_pathname (buf);
  132. r = strchr (buf, '\0');
  133. if (*r == '\0' || !IS_PATH_SEP (r[-1]))
  134. /* FIXME: this condition is always true because r points to the EOL */
  135. {
  136. *r++ = PATH_SEP;
  137. *r = '\0';
  138. }
  139. *q = c;
  140. p = q;
  141. }
  142. while (c != '\0');
  143. if (*buf == '\0')
  144. strcpy (buf, PATH_SEP_STR);
  145. else if (IS_PATH_SEP (r[-1]) && r != buf + 1)
  146. r[-1] = '\0';
  147. ret:
  148. g_free (buf2);
  149. g_free (p2);
  150. return buf;
  151. }
  152. /* --------------------------------------------------------------------------------------------- */
  153. static gboolean
  154. mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
  155. {
  156. FILE *backup_fd;
  157. char *contents;
  158. gsize length;
  159. gboolean ret1 = TRUE;
  160. if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
  161. return FALSE;
  162. backup_fd = fopen (to_file_name, "w");
  163. if (backup_fd == NULL)
  164. {
  165. g_free (contents);
  166. return FALSE;
  167. }
  168. if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
  169. ret1 = FALSE;
  170. {
  171. int ret2;
  172. /* cppcheck-suppress redundantAssignment */
  173. ret2 = fflush (backup_fd);
  174. /* cppcheck-suppress redundantAssignment */
  175. ret2 = fclose (backup_fd);
  176. (void) ret2;
  177. }
  178. g_free (contents);
  179. return ret1;
  180. }
  181. /* --------------------------------------------------------------------------------------------- */
  182. /*** public functions ****************************************************************************/
  183. /* --------------------------------------------------------------------------------------------- */
  184. int
  185. is_printable (int c)
  186. {
  187. c &= 0xff;
  188. #ifdef HAVE_CHARSET
  189. /* "Display bits" is ignored, since the user controls the output
  190. by setting the output codepage */
  191. return is_8bit_printable (c);
  192. #else
  193. if (!mc_global.eight_bit_clean)
  194. return is_7bit_printable (c);
  195. if (mc_global.full_eight_bits)
  196. return is_8bit_printable (c);
  197. return is_iso_printable (c);
  198. #endif /* !HAVE_CHARSET */
  199. }
  200. /* --------------------------------------------------------------------------------------------- */
  201. /**
  202. * Quote the filename for the purpose of inserting it into the command
  203. * line. If quote_percent is TRUE, replace "%" with "%%" - the percent is
  204. * processed by the mc command line.
  205. */
  206. char *
  207. name_quote (const char *s, gboolean quote_percent)
  208. {
  209. GString *ret;
  210. if (s == NULL || *s == '\0')
  211. return NULL;
  212. ret = g_string_sized_new (64);
  213. if (*s == '-')
  214. g_string_append (ret, "." PATH_SEP_STR);
  215. for (; *s != '\0'; s++)
  216. {
  217. switch (*s)
  218. {
  219. case '%':
  220. if (quote_percent)
  221. g_string_append_c (ret, '%');
  222. break;
  223. case '\'':
  224. case '\\':
  225. case '\r':
  226. case '\n':
  227. case '\t':
  228. case '"':
  229. case ';':
  230. case ' ':
  231. case '?':
  232. case '|':
  233. case '[':
  234. case ']':
  235. case '{':
  236. case '}':
  237. case '<':
  238. case '>':
  239. case '`':
  240. case '!':
  241. case '$':
  242. case '&':
  243. case '*':
  244. case '(':
  245. case ')':
  246. g_string_append_c (ret, '\\');
  247. break;
  248. case '~':
  249. case '#':
  250. if (ret->len == 0)
  251. g_string_append_c (ret, '\\');
  252. break;
  253. default:
  254. break;
  255. }
  256. g_string_append_c (ret, *s);
  257. }
  258. return g_string_free (ret, ret->len == 0);
  259. }
  260. /* --------------------------------------------------------------------------------------------- */
  261. char *
  262. fake_name_quote (const char *s, gboolean quote_percent)
  263. {
  264. (void) quote_percent;
  265. return (s == NULL || *s == '\0' ? NULL : g_strdup (s));
  266. }
  267. /* --------------------------------------------------------------------------------------------- */
  268. /**
  269. * path_trunc() is the same as str_trunc() but
  270. * it deletes possible password from path for security
  271. * reasons.
  272. */
  273. const char *
  274. path_trunc (const char *path, size_t trunc_len)
  275. {
  276. vfs_path_t *vpath;
  277. const char *ret;
  278. vpath = vfs_path_from_str_flags (path, VPF_STRIP_PASSWORD);
  279. ret = str_trunc (vfs_path_as_str (vpath), trunc_len);
  280. vfs_path_free (vpath, TRUE);
  281. return ret;
  282. }
  283. /* --------------------------------------------------------------------------------------------- */
  284. const char *
  285. size_trunc (uintmax_t size, gboolean use_si)
  286. {
  287. static char x[BUF_TINY];
  288. uintmax_t divisor = 1;
  289. const char *xtra = _("B");
  290. if (size > 999999999UL)
  291. {
  292. divisor = use_si ? 1000 : 1024;
  293. xtra = use_si ? _("kB") : _("KiB");
  294. if (size / divisor > 999999999UL)
  295. {
  296. divisor = use_si ? (1000 * 1000) : (1024 * 1024);
  297. xtra = use_si ? _("MB") : _("MiB");
  298. if (size / divisor > 999999999UL)
  299. {
  300. divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
  301. xtra = use_si ? _("GB") : _("GiB");
  302. }
  303. }
  304. }
  305. g_snprintf (x, sizeof (x), "%.0f %s", 1.0 * size / divisor, xtra);
  306. return x;
  307. }
  308. /* --------------------------------------------------------------------------------------------- */
  309. const char *
  310. size_trunc_sep (uintmax_t size, gboolean use_si)
  311. {
  312. static char x[60];
  313. int count;
  314. const char *p, *y;
  315. char *d;
  316. p = y = size_trunc (size, use_si);
  317. p += strlen (p) - 1;
  318. d = x + sizeof (x) - 1;
  319. *d-- = '\0';
  320. /* @size format is "size unit", i.e. "[digits][space][letters]".
  321. Copy all characters after digits. */
  322. while (p >= y && !g_ascii_isdigit (*p))
  323. *d-- = *p--;
  324. for (count = 0; p >= y; count++)
  325. {
  326. if (count == 3)
  327. {
  328. *d-- = ',';
  329. count = 0;
  330. }
  331. *d-- = *p--;
  332. }
  333. d++;
  334. if (*d == ',')
  335. d++;
  336. return d;
  337. }
  338. /* --------------------------------------------------------------------------------------------- */
  339. /**
  340. * Print file SIZE to BUFFER, but don't exceed LEN characters,
  341. * not including trailing 0. BUFFER should be at least LEN+1 long.
  342. * This function is called for every file on panels, so avoid
  343. * floating point by any means.
  344. *
  345. * Units: size units (filesystem sizes are 1K blocks)
  346. * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
  347. */
  348. void
  349. size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
  350. {
  351. /* Avoid taking power for every file. */
  352. /* *INDENT-OFF* */
  353. static const uintmax_t power10[] = {
  354. /* we hope that size of uintmax_t is 4 bytes at least */
  355. 1ULL,
  356. 10ULL,
  357. 100ULL,
  358. 1000ULL,
  359. 10000ULL,
  360. 100000ULL,
  361. 1000000ULL,
  362. 10000000ULL,
  363. 100000000ULL,
  364. 1000000000ULL
  365. /* maximum value of uintmax_t (in case of 4 bytes) is
  366. 4294967295
  367. */
  368. #if SIZEOF_UINTMAX_T == 8
  369. ,
  370. 10000000000ULL,
  371. 100000000000ULL,
  372. 1000000000000ULL,
  373. 10000000000000ULL,
  374. 100000000000000ULL,
  375. 1000000000000000ULL,
  376. 10000000000000000ULL,
  377. 100000000000000000ULL,
  378. 1000000000000000000ULL,
  379. 10000000000000000000ULL
  380. /* maximum value of uintmax_t (in case of 8 bytes) is
  381. 18447644073710439615
  382. */
  383. #endif
  384. };
  385. static const char *const suffix[] =
  386. { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q", NULL };
  387. static const char *const suffix_lc[] =
  388. { "", "k", "m", "g", "t", "p", "e", "z", "y", "r", "q", NULL };
  389. /* *INDENT-ON* */
  390. const char *const *sfx = use_si ? suffix_lc : suffix;
  391. int j = 0;
  392. if (len == 0)
  393. len = 9;
  394. #if SIZEOF_UINTMAX_T == 8
  395. /* 20 decimal digits are required to represent 8 bytes */
  396. else if (len > 19)
  397. len = 19;
  398. #else
  399. /* 10 decimal digits are required to represent 4 bytes */
  400. else if (len > 9)
  401. len = 9;
  402. #endif
  403. /*
  404. * recalculate from 1024 base to 1000 base if units>0
  405. * We can't just multiply by 1024 - that might cause overflow
  406. * if uintmax_t type is too small
  407. */
  408. if (use_si)
  409. for (j = 0; j < units; j++)
  410. {
  411. uintmax_t size_remain;
  412. size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
  413. size /= 125; /* 128/125 = 1024/1000 */
  414. size *= 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
  415. size += size_remain; /* Re-add remainder lost by division/multiplication */
  416. }
  417. for (j = units; sfx[j] != NULL; j++)
  418. {
  419. if (size == 0)
  420. {
  421. if (j == units)
  422. {
  423. /* Empty files will print "0" even with minimal width. */
  424. g_snprintf (buffer, len + 1, "%s", "0");
  425. }
  426. else
  427. {
  428. /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
  429. g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s", (j > 1) ? sfx[j - 1] : "B");
  430. }
  431. break;
  432. }
  433. if (size < power10[len - (j > 0 ? 1 : 0)])
  434. {
  435. g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, sfx[j]);
  436. break;
  437. }
  438. /* Powers of 1000 or 1024, with rounding. */
  439. if (use_si)
  440. size = (size + 500) / 1000;
  441. else
  442. size = (size + 512) >> 10;
  443. }
  444. }
  445. /* --------------------------------------------------------------------------------------------- */
  446. const char *
  447. string_perm (mode_t mode_bits)
  448. {
  449. static char mode[11];
  450. strcpy (mode, "----------");
  451. if (S_ISDIR (mode_bits))
  452. mode[0] = 'd';
  453. if (S_ISCHR (mode_bits))
  454. mode[0] = 'c';
  455. if (S_ISBLK (mode_bits))
  456. mode[0] = 'b';
  457. if (S_ISLNK (mode_bits))
  458. mode[0] = 'l';
  459. if (S_ISFIFO (mode_bits))
  460. mode[0] = 'p';
  461. if (S_ISNAM (mode_bits))
  462. mode[0] = 'n';
  463. if (S_ISSOCK (mode_bits))
  464. mode[0] = 's';
  465. if (S_ISDOOR (mode_bits))
  466. mode[0] = 'D';
  467. if (ismode (mode_bits, S_IXOTH))
  468. mode[9] = 'x';
  469. if (ismode (mode_bits, S_IWOTH))
  470. mode[8] = 'w';
  471. if (ismode (mode_bits, S_IROTH))
  472. mode[7] = 'r';
  473. if (ismode (mode_bits, S_IXGRP))
  474. mode[6] = 'x';
  475. if (ismode (mode_bits, S_IWGRP))
  476. mode[5] = 'w';
  477. if (ismode (mode_bits, S_IRGRP))
  478. mode[4] = 'r';
  479. if (ismode (mode_bits, S_IXUSR))
  480. mode[3] = 'x';
  481. if (ismode (mode_bits, S_IWUSR))
  482. mode[2] = 'w';
  483. if (ismode (mode_bits, S_IRUSR))
  484. mode[1] = 'r';
  485. #ifdef S_ISUID
  486. if (ismode (mode_bits, S_ISUID))
  487. mode[3] = (mode[3] == 'x') ? 's' : 'S';
  488. #endif /* S_ISUID */
  489. #ifdef S_ISGID
  490. if (ismode (mode_bits, S_ISGID))
  491. mode[6] = (mode[6] == 'x') ? 's' : 'S';
  492. #endif /* S_ISGID */
  493. #ifdef S_ISVTX
  494. if (ismode (mode_bits, S_ISVTX))
  495. mode[9] = (mode[9] == 'x') ? 't' : 'T';
  496. #endif /* S_ISVTX */
  497. return mode;
  498. }
  499. /* --------------------------------------------------------------------------------------------- */
  500. const char *
  501. extension (const char *filename)
  502. {
  503. const char *d;
  504. d = strrchr (filename, '.');
  505. return d != NULL ? d + 1 : "";
  506. }
  507. /* --------------------------------------------------------------------------------------------- */
  508. char *
  509. load_mc_home_file (const char *from, const char *filename, char **allocated_filename,
  510. size_t *length)
  511. {
  512. char *hintfile_base, *hintfile;
  513. char *lang;
  514. char *data;
  515. hintfile_base = g_build_filename (from, filename, (char *) NULL);
  516. lang = guess_message_value ();
  517. hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
  518. if (!g_file_get_contents (hintfile, &data, length, NULL))
  519. {
  520. /* Fall back to the two-letter language code */
  521. if (lang[0] != '\0' && lang[1] != '\0')
  522. lang[2] = '\0';
  523. g_free (hintfile);
  524. hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
  525. if (!g_file_get_contents (hintfile, &data, length, NULL))
  526. {
  527. g_free (hintfile);
  528. hintfile = hintfile_base;
  529. g_file_get_contents (hintfile_base, &data, length, NULL);
  530. }
  531. }
  532. g_free (lang);
  533. if (hintfile != hintfile_base)
  534. g_free (hintfile_base);
  535. if (allocated_filename != NULL)
  536. *allocated_filename = hintfile;
  537. else
  538. g_free (hintfile);
  539. return data;
  540. }
  541. /* --------------------------------------------------------------------------------------------- */
  542. const char *
  543. extract_line (const char *s, const char *top, size_t *len)
  544. {
  545. static char tmp_line[BUF_MEDIUM];
  546. char *t = tmp_line;
  547. while (*s != '\0' && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
  548. *t++ = *s++;
  549. *t = '\0';
  550. if (len != NULL)
  551. *len = (size_t) (t - tmp_line);
  552. return tmp_line;
  553. }
  554. /* --------------------------------------------------------------------------------------------- */
  555. /**
  556. * The basename routine
  557. */
  558. const char *
  559. x_basename (const char *s)
  560. {
  561. const char *url_delim, *path_sep;
  562. url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
  563. path_sep = strrchr (s, PATH_SEP);
  564. if (path_sep == NULL)
  565. return s;
  566. if (url_delim == NULL
  567. || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
  568. || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
  569. {
  570. /* avoid trailing PATH_SEP, if present */
  571. if (!IS_PATH_SEP (s[strlen (s) - 1]))
  572. return path_sep + 1;
  573. while (--path_sep > s && !IS_PATH_SEP (*path_sep))
  574. ;
  575. return (path_sep != s) ? path_sep + 1 : s;
  576. }
  577. while (--url_delim > s && !IS_PATH_SEP (*url_delim))
  578. ;
  579. while (--url_delim > s && !IS_PATH_SEP (*url_delim))
  580. ;
  581. return url_delim == s ? s : url_delim + 1;
  582. }
  583. /* --------------------------------------------------------------------------------------------- */
  584. const char *
  585. unix_error_string (int error_num)
  586. {
  587. static char buffer[BUF_LARGE];
  588. gchar *strerror_currentlocale;
  589. strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
  590. g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
  591. g_free (strerror_currentlocale);
  592. return buffer;
  593. }
  594. /* --------------------------------------------------------------------------------------------- */
  595. const char *
  596. skip_separators (const char *s)
  597. {
  598. const char *su = s;
  599. for (; *su != '\0'; str_cnext_char (&su))
  600. if (!whitespace (*su) && *su != ',')
  601. break;
  602. return su;
  603. }
  604. /* --------------------------------------------------------------------------------------------- */
  605. const char *
  606. skip_numbers (const char *s)
  607. {
  608. const char *su = s;
  609. for (; *su != '\0'; str_cnext_char (&su))
  610. if (!str_isdigit (su))
  611. break;
  612. return su;
  613. }
  614. /* --------------------------------------------------------------------------------------------- */
  615. /**
  616. * Remove all control sequences from the argument string. We define
  617. * "control sequence", in a sort of pidgin BNF, as follows:
  618. *
  619. * control-seq = Esc non-'['
  620. * | Esc '[' (0 or more digits or ';' or ':' or '?') (any other char)
  621. *
  622. * The 256-color and true-color escape sequences should allow either ';' or ':' inside as separator,
  623. * actually, ':' is the more correct according to ECMA-48.
  624. * Some terminal emulators (e.g. xterm, gnome-terminal) support this.
  625. *
  626. * Non-printable characters are also removed.
  627. */
  628. char *
  629. strip_ctrl_codes (char *s)
  630. {
  631. char *w; /* Current position where the stripped data is written */
  632. char *r; /* Current position where the original data is read */
  633. if (s == NULL)
  634. return NULL;
  635. for (w = s, r = s; *r != '\0';)
  636. {
  637. if (*r == ESC_CHAR)
  638. {
  639. /* Skip the control sequence's arguments */ ;
  640. /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
  641. if (*(++r) == '[' || *r == '(')
  642. {
  643. /* strchr() matches trailing binary 0 */
  644. while (*(++r) != '\0' && strchr ("0123456789;:?", *r) != NULL)
  645. ;
  646. }
  647. else if (*r == ']')
  648. {
  649. /*
  650. * Skip xterm's OSC (Operating System Command)
  651. * http://www.xfree86.org/current/ctlseqs.html
  652. * OSC P s ; P t ST
  653. * OSC P s ; P t BEL
  654. */
  655. char *new_r;
  656. for (new_r = r; *new_r != '\0'; new_r++)
  657. {
  658. switch (*new_r)
  659. {
  660. /* BEL */
  661. case '\a':
  662. r = new_r;
  663. goto osc_out;
  664. case ESC_CHAR:
  665. /* ST */
  666. if (new_r[1] == '\\')
  667. {
  668. r = new_r + 1;
  669. goto osc_out;
  670. }
  671. break;
  672. default:
  673. break;
  674. }
  675. }
  676. osc_out:
  677. ;
  678. }
  679. /*
  680. * Now we are at the last character of the sequence.
  681. * Skip it unless it's binary 0.
  682. */
  683. if (*r != '\0')
  684. r++;
  685. }
  686. else
  687. {
  688. char *n;
  689. n = str_get_next_char (r);
  690. if (str_isprint (r))
  691. {
  692. memmove (w, r, n - r);
  693. w += n - r;
  694. }
  695. r = n;
  696. }
  697. }
  698. *w = '\0';
  699. return s;
  700. }
  701. /* --------------------------------------------------------------------------------------------- */
  702. enum compression_type
  703. get_compression_type (int fd, const char *name)
  704. {
  705. unsigned char magic[16];
  706. size_t str_len;
  707. /* Read the magic signature */
  708. if (mc_read (fd, (char *) magic, 4) != 4)
  709. return COMPRESSION_NONE;
  710. /* GZIP_MAGIC and OLD_GZIP_MAGIC */
  711. if (magic[0] == 0x1F && (magic[1] == 0x8B || magic[1] == 0x9E))
  712. return COMPRESSION_GZIP;
  713. /* PKZIP_MAGIC */
  714. if (magic[0] == 'P' && magic[1] == 'K' && magic[2] == 0x03 && magic[3] == 0x04)
  715. {
  716. /* Read compression type */
  717. mc_lseek (fd, 8, SEEK_SET);
  718. if (mc_read (fd, (char *) magic, 2) != 2)
  719. return COMPRESSION_NONE;
  720. if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
  721. return COMPRESSION_NONE;
  722. return COMPRESSION_ZIP;
  723. }
  724. /* PACK_MAGIC and LZH_MAGIC and compress magic */
  725. if (magic[0] == 0x1F && (magic[1] == 0x1E || magic[1] == 0xA0 || magic[1] == 0x9D))
  726. /* Compatible with gzip */
  727. return COMPRESSION_GZIP;
  728. /* BZIP and BZIP2 files */
  729. if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
  730. switch (magic[2])
  731. {
  732. case '0':
  733. return COMPRESSION_BZIP;
  734. case 'h':
  735. return COMPRESSION_BZIP2;
  736. default:
  737. break;
  738. }
  739. /* LZ4 format - v1.5.0 - 0x184D2204 (little endian) */
  740. if (magic[0] == 0x04 && magic[1] == 0x22 && magic[2] == 0x4d && magic[3] == 0x18)
  741. return COMPRESSION_LZ4;
  742. if (mc_read (fd, (char *) magic + 4, 2) != 2)
  743. return COMPRESSION_NONE;
  744. /* LZIP files */
  745. if (magic[0] == 'L'
  746. && magic[1] == 'Z'
  747. && magic[2] == 'I' && magic[3] == 'P' && (magic[4] == 0x00 || magic[4] == 0x01))
  748. return COMPRESSION_LZIP;
  749. /* Support for LZMA (only utils format with magic in header).
  750. * This is the default format of LZMA utils 4.32.1 and later. */
  751. if (magic[0] == 0xFF
  752. && magic[1] == 'L'
  753. && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
  754. return COMPRESSION_LZMA;
  755. /* LZO format - \x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a lzop compressed data */
  756. if (magic[0] == 0x89 && magic[1] == 0x4c &&
  757. magic[2] == 0x5a && magic[3] == 0x4f && magic[4] == 0x00 && magic[5] == 0x0d)
  758. return COMPRESSION_LZO;
  759. /* XZ compression magic */
  760. if (magic[0] == 0xFD
  761. && magic[1] == 0x37
  762. && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
  763. return COMPRESSION_XZ;
  764. if (magic[0] == 0x28 && magic[1] == 0xB5 && magic[2] == 0x2F && magic[3] == 0xFD)
  765. return COMPRESSION_ZSTD;
  766. str_len = strlen (name);
  767. /* HACK: we must believe to extension of LZMA file :) ... */
  768. if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
  769. (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
  770. return COMPRESSION_LZMA;
  771. return COMPRESSION_NONE;
  772. }
  773. /* --------------------------------------------------------------------------------------------- */
  774. const char *
  775. decompress_extension (int type)
  776. {
  777. switch (type)
  778. {
  779. case COMPRESSION_ZIP:
  780. return "/uz" VFS_PATH_URL_DELIMITER;
  781. case COMPRESSION_GZIP:
  782. return "/ugz" VFS_PATH_URL_DELIMITER;
  783. case COMPRESSION_BZIP:
  784. return "/ubz" VFS_PATH_URL_DELIMITER;
  785. case COMPRESSION_BZIP2:
  786. return "/ubz2" VFS_PATH_URL_DELIMITER;
  787. case COMPRESSION_LZIP:
  788. return "/ulz" VFS_PATH_URL_DELIMITER;
  789. case COMPRESSION_LZ4:
  790. return "/ulz4" VFS_PATH_URL_DELIMITER;
  791. case COMPRESSION_LZMA:
  792. return "/ulzma" VFS_PATH_URL_DELIMITER;
  793. case COMPRESSION_LZO:
  794. return "/ulzo" VFS_PATH_URL_DELIMITER;
  795. case COMPRESSION_XZ:
  796. return "/uxz" VFS_PATH_URL_DELIMITER;
  797. case COMPRESSION_ZSTD:
  798. return "/uzst" VFS_PATH_URL_DELIMITER;
  799. default:
  800. break;
  801. }
  802. /* Should never reach this place */
  803. fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
  804. return 0;
  805. }
  806. /* --------------------------------------------------------------------------------------------- */
  807. void
  808. wipe_password (char *passwd)
  809. {
  810. if (passwd != NULL)
  811. {
  812. char *p;
  813. for (p = passwd; *p != '\0'; p++)
  814. *p = '\0';
  815. g_free (passwd);
  816. }
  817. }
  818. /* --------------------------------------------------------------------------------------------- */
  819. /**
  820. * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
  821. *
  822. * @param p pointer to string
  823. *
  824. * @return newly allocated string
  825. */
  826. char *
  827. convert_controls (const char *p)
  828. {
  829. char *valcopy;
  830. char *q;
  831. valcopy = g_strdup (p);
  832. /* Parse the escape special character */
  833. for (q = valcopy; *p != '\0';)
  834. switch (*p)
  835. {
  836. case '\\':
  837. p++;
  838. if (*p == 'e' || *p == 'E')
  839. {
  840. p++;
  841. *q++ = ESC_CHAR;
  842. }
  843. break;
  844. case '^':
  845. p++;
  846. if (*p == '^')
  847. *q++ = *p++;
  848. else
  849. {
  850. char c;
  851. c = *p | 0x20;
  852. if (c >= 'a' && c <= 'z')
  853. {
  854. *q++ = c - 'a' + 1;
  855. p++;
  856. }
  857. else if (*p != '\0')
  858. p++;
  859. }
  860. break;
  861. default:
  862. *q++ = *p++;
  863. }
  864. *q = '\0';
  865. return valcopy;
  866. }
  867. /* --------------------------------------------------------------------------------------------- */
  868. /**
  869. * Finds out a relative path from first to second, i.e. goes as many ..
  870. * as needed up in first and then goes down using second
  871. */
  872. char *
  873. diff_two_paths (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
  874. {
  875. int j, prevlen = -1, currlen;
  876. char *my_first = NULL, *my_second = NULL;
  877. char *buf = NULL;
  878. my_first = resolve_symlinks (vpath1);
  879. if (my_first == NULL)
  880. goto ret;
  881. my_second = resolve_symlinks (vpath2);
  882. if (my_second == NULL)
  883. goto ret;
  884. for (j = 0; j < 2; j++)
  885. {
  886. char *p, *q;
  887. int i;
  888. p = my_first;
  889. q = my_second;
  890. while (TRUE)
  891. {
  892. char *r, *s;
  893. ptrdiff_t len;
  894. r = strchr (p, PATH_SEP);
  895. if (r == NULL)
  896. break;
  897. s = strchr (q, PATH_SEP);
  898. if (s == NULL)
  899. break;
  900. len = r - p;
  901. if (len != (s - q) || strncmp (p, q, (size_t) len) != 0)
  902. break;
  903. p = r + 1;
  904. q = s + 1;
  905. }
  906. p--;
  907. for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++)
  908. ;
  909. currlen = (i + 1) * 3 + strlen (q) + 1;
  910. if (j != 0)
  911. {
  912. if (currlen < prevlen)
  913. g_free (buf);
  914. else
  915. goto ret;
  916. }
  917. p = buf = g_malloc (currlen);
  918. prevlen = currlen;
  919. for (; i >= 0; i--, p += 3)
  920. strcpy (p, "../");
  921. strcpy (p, q);
  922. }
  923. ret:
  924. g_free (my_first);
  925. g_free (my_second);
  926. return buf;
  927. }
  928. /* --------------------------------------------------------------------------------------------- */
  929. /**
  930. * Append text to GList, remove all entries with the same text
  931. */
  932. GList *
  933. list_append_unique (GList *list, char *text)
  934. {
  935. GList *lc_link;
  936. /*
  937. * Go to the last position and traverse the list backwards
  938. * starting from the second last entry to make sure that we
  939. * are not removing the current link.
  940. */
  941. list = g_list_append (list, text);
  942. list = g_list_last (list);
  943. lc_link = g_list_previous (list);
  944. while (lc_link != NULL)
  945. {
  946. GList *newlink;
  947. newlink = g_list_previous (lc_link);
  948. if (strcmp ((char *) lc_link->data, text) == 0)
  949. {
  950. GList *tmp;
  951. g_free (lc_link->data);
  952. tmp = g_list_remove_link (list, lc_link);
  953. (void) tmp;
  954. g_list_free_1 (lc_link);
  955. }
  956. lc_link = newlink;
  957. }
  958. return list;
  959. }
  960. /* --------------------------------------------------------------------------------------------- */
  961. /**
  962. * Read and restore position for the given filename.
  963. * If there is no stored data, return line 1 and col 0.
  964. */
  965. void
  966. load_file_position (const vfs_path_t *filename_vpath, long *line, long *column, off_t *offset,
  967. GArray **bookmarks)
  968. {
  969. char *fn;
  970. FILE *f;
  971. char buf[MC_MAXPATHLEN + 100];
  972. const size_t len = vfs_path_len (filename_vpath);
  973. /* defaults */
  974. *line = 1;
  975. *column = 0;
  976. *offset = 0;
  977. /* open file with positions */
  978. fn = mc_config_get_full_path (MC_FILEPOS_FILE);
  979. f = fopen (fn, "r");
  980. g_free (fn);
  981. if (f == NULL)
  982. return;
  983. /* prepare array for serialized bookmarks */
  984. if (bookmarks != NULL)
  985. *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
  986. while (fgets (buf, sizeof (buf), f) != NULL)
  987. {
  988. const char *p;
  989. gchar **pos_tokens;
  990. /* check if the filename matches the beginning of string */
  991. if (strncmp (buf, vfs_path_as_str (filename_vpath), len) != 0)
  992. continue;
  993. /* followed by single space */
  994. if (buf[len] != ' ')
  995. continue;
  996. /* and string without spaces */
  997. p = &buf[len + 1];
  998. if (strchr (p, ' ') != NULL)
  999. continue;
  1000. pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
  1001. if (pos_tokens[0] == NULL)
  1002. {
  1003. *line = 1;
  1004. *column = 0;
  1005. *offset = 0;
  1006. }
  1007. else
  1008. {
  1009. *line = strtol (pos_tokens[0], NULL, 10);
  1010. if (pos_tokens[1] == NULL)
  1011. {
  1012. *column = 0;
  1013. *offset = 0;
  1014. }
  1015. else
  1016. {
  1017. *column = strtol (pos_tokens[1], NULL, 10);
  1018. if (pos_tokens[2] == NULL)
  1019. *offset = 0;
  1020. else if (bookmarks != NULL)
  1021. {
  1022. size_t i;
  1023. *offset = (off_t) g_ascii_strtoll (pos_tokens[2], NULL, 10);
  1024. for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
  1025. {
  1026. size_t val;
  1027. val = strtoul (pos_tokens[3 + i], NULL, 10);
  1028. g_array_append_val (*bookmarks, val);
  1029. }
  1030. }
  1031. }
  1032. }
  1033. g_strfreev (pos_tokens);
  1034. }
  1035. fclose (f);
  1036. }
  1037. /* --------------------------------------------------------------------------------------------- */
  1038. /**
  1039. * Save position for the given file
  1040. */
  1041. void
  1042. save_file_position (const vfs_path_t *filename_vpath, long line, long column, off_t offset,
  1043. GArray *bookmarks)
  1044. {
  1045. static size_t filepos_max_saved_entries = 0;
  1046. char *fn, *tmp_fn;
  1047. FILE *f, *tmp_f;
  1048. char buf[MC_MAXPATHLEN + 100];
  1049. size_t i;
  1050. const size_t len = vfs_path_len (filename_vpath);
  1051. gboolean src_error = FALSE;
  1052. if (filepos_max_saved_entries == 0)
  1053. filepos_max_saved_entries = mc_config_get_int (mc_global.main_config, CONFIG_APP_SECTION,
  1054. "filepos_max_saved_entries", 1024);
  1055. fn = mc_config_get_full_path (MC_FILEPOS_FILE);
  1056. if (fn == NULL)
  1057. goto early_error;
  1058. mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
  1059. /* open file */
  1060. f = fopen (fn, "w");
  1061. if (f == NULL)
  1062. goto open_target_error;
  1063. tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
  1064. tmp_f = fopen (tmp_fn, "r");
  1065. if (tmp_f == NULL)
  1066. {
  1067. src_error = TRUE;
  1068. goto open_source_error;
  1069. }
  1070. /* put the new record */
  1071. if (line != 1 || column != 0 || bookmarks != NULL)
  1072. {
  1073. if (fprintf
  1074. (f, "%s %ld;%ld;%" PRIuMAX, vfs_path_as_str (filename_vpath), line, column,
  1075. (uintmax_t) offset) < 0)
  1076. goto write_position_error;
  1077. if (bookmarks != NULL)
  1078. for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
  1079. if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
  1080. goto write_position_error;
  1081. if (fprintf (f, "\n") < 0)
  1082. goto write_position_error;
  1083. }
  1084. i = 1;
  1085. while (fgets (buf, sizeof (buf), tmp_f) != NULL)
  1086. {
  1087. if (buf[len] == ' ' && strncmp (buf, vfs_path_as_str (filename_vpath), len) == 0
  1088. && strchr (&buf[len + 1], ' ') == NULL)
  1089. continue;
  1090. fprintf (f, "%s", buf);
  1091. if (++i > filepos_max_saved_entries)
  1092. break;
  1093. }
  1094. write_position_error:
  1095. fclose (tmp_f);
  1096. open_source_error:
  1097. g_free (tmp_fn);
  1098. fclose (f);
  1099. if (src_error)
  1100. mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
  1101. else
  1102. mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
  1103. open_target_error:
  1104. g_free (fn);
  1105. early_error:
  1106. if (bookmarks != NULL)
  1107. g_array_free (bookmarks, TRUE);
  1108. }
  1109. /* --------------------------------------------------------------------------------------------- */
  1110. extern int
  1111. ascii_alpha_to_cntrl (int ch)
  1112. {
  1113. if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
  1114. ch &= 0x1f;
  1115. return ch;
  1116. }
  1117. /* --------------------------------------------------------------------------------------------- */
  1118. const char *
  1119. Q_ (const char *s)
  1120. {
  1121. const char *result, *sep;
  1122. result = _(s);
  1123. sep = strchr (result, '|');
  1124. return sep != NULL ? sep + 1 : result;
  1125. }
  1126. /* --------------------------------------------------------------------------------------------- */
  1127. gboolean
  1128. mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
  1129. {
  1130. struct stat stat_buf;
  1131. char *backup_path;
  1132. gboolean ret;
  1133. if (!exist_file (file_name))
  1134. return FALSE;
  1135. backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
  1136. if (backup_path == NULL)
  1137. return FALSE;
  1138. ret = mc_util_write_backup_content (file_name, backup_path);
  1139. if (ret)
  1140. {
  1141. /* Backup file will have same ownership with main file. */
  1142. if (stat (file_name, &stat_buf) == 0)
  1143. chmod (backup_path, stat_buf.st_mode);
  1144. else
  1145. chmod (backup_path, S_IRUSR | S_IWUSR);
  1146. }
  1147. g_free (backup_path);
  1148. return ret;
  1149. }
  1150. /* --------------------------------------------------------------------------------------------- */
  1151. gboolean
  1152. mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
  1153. {
  1154. gboolean ret;
  1155. char *backup_path;
  1156. backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
  1157. if (backup_path == NULL)
  1158. return FALSE;
  1159. ret = mc_util_write_backup_content (backup_path, file_name);
  1160. g_free (backup_path);
  1161. return ret;
  1162. }
  1163. /* --------------------------------------------------------------------------------------------- */
  1164. gboolean
  1165. mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
  1166. {
  1167. char *backup_path;
  1168. backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
  1169. if (backup_path == NULL)
  1170. return FALSE;
  1171. if (exist_file (backup_path))
  1172. {
  1173. vfs_path_t *vpath;
  1174. vpath = vfs_path_from_str (backup_path);
  1175. mc_unlink (vpath);
  1176. vfs_path_free (vpath, TRUE);
  1177. }
  1178. g_free (backup_path);
  1179. return TRUE;
  1180. }
  1181. /* --------------------------------------------------------------------------------------------- */
  1182. /**
  1183. * partly taken from dcigettext.c, returns "" for default locale
  1184. * value should be freed by calling function g_free()
  1185. */
  1186. char *
  1187. guess_message_value (void)
  1188. {
  1189. static const char *const var[] = {
  1190. /* Setting of LC_ALL overwrites all other. */
  1191. /* Do not use LANGUAGE for check user locale and drowing hints */
  1192. "LC_ALL",
  1193. /* Next comes the name of the desired category. */
  1194. "LC_MESSAGES",
  1195. /* Last possibility is the LANG environment variable. */
  1196. "LANG",
  1197. /* NULL exit loops */
  1198. NULL
  1199. };
  1200. size_t i;
  1201. const char *locale = NULL;
  1202. for (i = 0; var[i] != NULL; i++)
  1203. {
  1204. locale = getenv (var[i]);
  1205. if (locale != NULL && locale[0] != '\0')
  1206. break;
  1207. }
  1208. if (locale == NULL)
  1209. locale = "";
  1210. return g_strdup (locale);
  1211. }
  1212. /* --------------------------------------------------------------------------------------------- */
  1213. /**
  1214. * The "profile root" is the tree under which all of MC's user data &
  1215. * settings are stored.
  1216. *
  1217. * It defaults to the user's home dir. The user may override this default
  1218. * with the environment variable $MC_PROFILE_ROOT.
  1219. */
  1220. const char *
  1221. mc_get_profile_root (void)
  1222. {
  1223. static const char *profile_root = NULL;
  1224. if (profile_root == NULL)
  1225. {
  1226. profile_root = g_getenv ("MC_PROFILE_ROOT");
  1227. if (profile_root == NULL || *profile_root == '\0')
  1228. profile_root = mc_config_get_home_dir ();
  1229. }
  1230. return profile_root;
  1231. }
  1232. /* --------------------------------------------------------------------------------------------- */
  1233. /**
  1234. * Propagate error in simple way.
  1235. *
  1236. * @param dest error return location
  1237. * @param code error code
  1238. * @param format printf()-style format for error message
  1239. * @param ... parameters for message format
  1240. */
  1241. void
  1242. mc_propagate_error (GError **dest, int code, const char *format, ...)
  1243. {
  1244. if (dest != NULL && *dest == NULL)
  1245. {
  1246. GError *tmp_error;
  1247. va_list args;
  1248. va_start (args, format);
  1249. tmp_error = g_error_new_valist (MC_ERROR, code, format, args);
  1250. va_end (args);
  1251. g_propagate_error (dest, tmp_error);
  1252. }
  1253. }
  1254. /* --------------------------------------------------------------------------------------------- */
  1255. /**
  1256. * Replace existing error in simple way.
  1257. *
  1258. * @param dest error return location
  1259. * @param code error code
  1260. * @param format printf()-style format for error message
  1261. * @param ... parameters for message format
  1262. */
  1263. void
  1264. mc_replace_error (GError **dest, int code, const char *format, ...)
  1265. {
  1266. if (dest != NULL)
  1267. {
  1268. GError *tmp_error;
  1269. va_list args;
  1270. va_start (args, format);
  1271. tmp_error = g_error_new_valist (MC_ERROR, code, format, args);
  1272. va_end (args);
  1273. g_error_free (*dest);
  1274. *dest = NULL;
  1275. g_propagate_error (dest, tmp_error);
  1276. }
  1277. }
  1278. /* --------------------------------------------------------------------------------------------- */
  1279. /**
  1280. * Returns if the given duration has elapsed since the given timestamp,
  1281. * and if it has then updates the timestamp.
  1282. *
  1283. * @param timestamp the last timestamp in microseconds, updated if the given time elapsed
  1284. * @param delay amount of time in microseconds
  1285. * @return TRUE if clock skew detected, FALSE otherwise
  1286. */
  1287. gboolean
  1288. mc_time_elapsed (gint64 *timestamp, gint64 delay)
  1289. {
  1290. gint64 now;
  1291. now = g_get_monotonic_time ();
  1292. if (now >= *timestamp && now < *timestamp + delay)
  1293. return FALSE;
  1294. *timestamp = now;
  1295. return TRUE;
  1296. }
  1297. /* --------------------------------------------------------------------------------------------- */