util.c 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732
  1. /* Various utilities
  2. Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
  3. 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
  4. Written 1994, 1995, 1996 by:
  5. Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
  6. Jakub Jelinek, Mauricio Plaza.
  7. The file_date routine is mostly from GNU's fileutils package,
  8. written by Richard Stallman and David MacKenzie.
  9. This program is free software; you can redistribute it and/or modify
  10. it under the terms of the GNU General Public License as published by
  11. the Free Software Foundation; either version 2 of the License, or
  12. (at your option) any later version.
  13. This program 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, write to the Free Software
  19. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
  20. /** \file
  21. * \brief Source: various utilities
  22. */
  23. #include <config.h>
  24. #include <ctype.h>
  25. #include <limits.h>
  26. #include <stdarg.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <fcntl.h>
  31. #include <sys/time.h>
  32. #include <sys/types.h>
  33. #include <sys/stat.h>
  34. #include <unistd.h>
  35. #include "lib/global.h"
  36. #include "lib/tty/win.h" /* xterm_flag */
  37. #include "lib/search.h"
  38. #include "lib/mcconfig.h"
  39. #include "lib/timefmt.h"
  40. #include "lib/fileloc.h"
  41. #include "lib/vfs/mc-vfs/vfs.h"
  42. #include "lib/strutil.h"
  43. #include "src/filegui.h"
  44. #include "src/file.h" /* copy_file_file() */
  45. #ifndef HAVE_CHARSET
  46. #include "src/main.h" /* eight_bit_clean */
  47. #endif
  48. int easy_patterns = 1;
  49. /*
  50. * If true, SI units (1000 based) will be used for
  51. * larger units (kilobyte, megabyte, ...).
  52. * If false binary units (1024 based) will be used.
  53. */
  54. int kilobyte_si = 0;
  55. char *user_recent_timeformat = NULL; /* time format string for recent dates */
  56. char *user_old_timeformat = NULL; /* time format string for older dates */
  57. extern void
  58. str_replace (char *s, char from, char to)
  59. {
  60. for (; *s != '\0'; s++)
  61. {
  62. if (*s == from)
  63. *s = to;
  64. }
  65. }
  66. static inline int
  67. is_7bit_printable (unsigned char c)
  68. {
  69. return (c > 31 && c < 127);
  70. }
  71. static inline int
  72. is_iso_printable (unsigned char c)
  73. {
  74. return ((c > 31 && c < 127) || c >= 160);
  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 (xterm_flag)
  81. return is_iso_printable (c);
  82. return (c > 31 && c != 127 && c != 155);
  83. }
  84. int
  85. is_printable (int c)
  86. {
  87. c &= 0xff;
  88. #ifdef HAVE_CHARSET
  89. /* "Display bits" is ignored, since the user controls the output
  90. by setting the output codepage */
  91. return is_8bit_printable (c);
  92. #else
  93. if (!eight_bit_clean)
  94. return is_7bit_printable (c);
  95. if (full_eight_bits)
  96. {
  97. return is_8bit_printable (c);
  98. }
  99. else
  100. return is_iso_printable (c);
  101. #endif /* !HAVE_CHARSET */
  102. }
  103. /* Calculates the message dimensions (lines and columns) */
  104. void
  105. msglen (const char *text, int *lines, int *columns)
  106. {
  107. int nlines = 1; /* even the empty string takes one line */
  108. int ncolumns = 0;
  109. int colindex = 0;
  110. for (; *text != '\0'; text++)
  111. {
  112. if (*text == '\n')
  113. {
  114. nlines++;
  115. colindex = 0;
  116. }
  117. else
  118. {
  119. colindex++;
  120. if (colindex > ncolumns)
  121. ncolumns = colindex;
  122. }
  123. }
  124. *lines = nlines;
  125. *columns = ncolumns;
  126. }
  127. /*
  128. * Copy from s to d, and trim the beginning if necessary, and prepend
  129. * "..." in this case. The destination string can have at most len
  130. * bytes, not counting trailing 0.
  131. */
  132. char *
  133. trim (const char *s, char *d, int len)
  134. {
  135. int source_len;
  136. /* Sanity check */
  137. len = max (len, 0);
  138. source_len = strlen (s);
  139. if (source_len > len)
  140. {
  141. /* Cannot fit the whole line */
  142. if (len <= 3)
  143. {
  144. /* We only have room for the dots */
  145. memset (d, '.', len);
  146. d[len] = 0;
  147. return d;
  148. }
  149. else
  150. {
  151. /* Begin with ... and add the rest of the source string */
  152. memset (d, '.', 3);
  153. strcpy (d + 3, s + 3 + source_len - len);
  154. }
  155. }
  156. else
  157. /* We can copy the whole line */
  158. strcpy (d, s);
  159. return d;
  160. }
  161. /*
  162. * Quote the filename for the purpose of inserting it into the command
  163. * line. If quote_percent is 1, replace "%" with "%%" - the percent is
  164. * processed by the mc command line.
  165. */
  166. char *
  167. name_quote (const char *s, int quote_percent)
  168. {
  169. char *ret, *d;
  170. d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
  171. if (*s == '-')
  172. {
  173. *d++ = '.';
  174. *d++ = '/';
  175. }
  176. for (; *s; s++, d++)
  177. {
  178. switch (*s)
  179. {
  180. case '%':
  181. if (quote_percent)
  182. *d++ = '%';
  183. break;
  184. case '\'':
  185. case '\\':
  186. case '\r':
  187. case '\n':
  188. case '\t':
  189. case '"':
  190. case ';':
  191. case ' ':
  192. case '?':
  193. case '|':
  194. case '[':
  195. case ']':
  196. case '{':
  197. case '}':
  198. case '<':
  199. case '>':
  200. case '`':
  201. case '!':
  202. case '$':
  203. case '&':
  204. case '*':
  205. case '(':
  206. case ')':
  207. *d++ = '\\';
  208. break;
  209. case '~':
  210. case '#':
  211. if (d == ret)
  212. *d++ = '\\';
  213. break;
  214. }
  215. *d = *s;
  216. }
  217. *d = '\0';
  218. return ret;
  219. }
  220. char *
  221. fake_name_quote (const char *s, int quote_percent)
  222. {
  223. (void) quote_percent;
  224. return g_strdup (s);
  225. }
  226. /*
  227. * Remove the middle part of the string to fit given length.
  228. * Use "~" to show where the string was truncated.
  229. * Return static buffer, no need to free() it.
  230. */
  231. const char *
  232. name_trunc (const char *txt, size_t trunc_len)
  233. {
  234. return str_trunc (txt, trunc_len);
  235. }
  236. /*
  237. * path_trunc() is the same as name_trunc() above but
  238. * it deletes possible password from path for security
  239. * reasons.
  240. */
  241. const char *
  242. path_trunc (const char *path, size_t trunc_len)
  243. {
  244. char *secure_path = strip_password (g_strdup (path), 1);
  245. const char *ret = str_trunc (secure_path, trunc_len);
  246. g_free (secure_path);
  247. return ret;
  248. }
  249. const char *
  250. size_trunc (double size)
  251. {
  252. static char x[BUF_TINY];
  253. long int divisor = 1;
  254. const char *xtra = "";
  255. if (size > 999999999L)
  256. {
  257. divisor = kilobyte_si ? 1000 : 1024;
  258. xtra = kilobyte_si ? "k" : "K";
  259. if (size / divisor > 999999999L)
  260. {
  261. divisor = kilobyte_si ? (1000 * 1000) : (1024 * 1024);
  262. xtra = kilobyte_si ? "m" : "M";
  263. }
  264. }
  265. g_snprintf (x, sizeof (x), "%.0f%s", (size / divisor), xtra);
  266. return x;
  267. }
  268. const char *
  269. size_trunc_sep (double size)
  270. {
  271. static char x[60];
  272. int count;
  273. const char *p, *y;
  274. char *d;
  275. p = y = size_trunc (size);
  276. p += strlen (p) - 1;
  277. d = x + sizeof (x) - 1;
  278. *d-- = 0;
  279. while (p >= y && isalpha ((unsigned char) *p))
  280. *d-- = *p--;
  281. for (count = 0; p >= y; count++)
  282. {
  283. if (count == 3)
  284. {
  285. *d-- = ',';
  286. count = 0;
  287. }
  288. *d-- = *p--;
  289. }
  290. d++;
  291. if (*d == ',')
  292. d++;
  293. return d;
  294. }
  295. /*
  296. * Print file SIZE to BUFFER, but don't exceed LEN characters,
  297. * not including trailing 0. BUFFER should be at least LEN+1 long.
  298. * This function is called for every file on panels, so avoid
  299. * floating point by any means.
  300. *
  301. * Units: size units (filesystem sizes are 1K blocks)
  302. * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
  303. */
  304. void
  305. size_trunc_len (char *buffer, unsigned int len, off_t size, int units)
  306. {
  307. /* Avoid taking power for every file. */
  308. static const off_t power10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
  309. 1000000000
  310. };
  311. static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
  312. static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
  313. int j = 0;
  314. int size_remain;
  315. if (len == 0)
  316. len = 9;
  317. /*
  318. * recalculate from 1024 base to 1000 base if units>0
  319. * We can't just multiply by 1024 - that might cause overflow
  320. * if off_t type is too small
  321. */
  322. if (units && kilobyte_si)
  323. {
  324. for (j = 0; j < units; j++)
  325. {
  326. size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
  327. size = size / 125; /* 128/125 = 1024/1000 */
  328. size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
  329. size += size_remain; /* Re-add remainder lost by division/multiplication */
  330. }
  331. }
  332. for (j = units; suffix[j] != NULL; j++)
  333. {
  334. if (size == 0)
  335. {
  336. if (j == units)
  337. {
  338. /* Empty files will print "0" even with minimal width. */
  339. g_snprintf (buffer, len + 1, "0");
  340. break;
  341. }
  342. /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
  343. g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
  344. (j > 1) ? (kilobyte_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
  345. break;
  346. }
  347. if (size < power10[len - (j > 0)])
  348. {
  349. g_snprintf (buffer, len + 1, "%lu%s", (unsigned long) size,
  350. kilobyte_si ? suffix_lc[j] : suffix[j]);
  351. break;
  352. }
  353. /* Powers of 1000 or 1024, with rounding. */
  354. if (kilobyte_si)
  355. {
  356. size = (size + 500) / 1000;
  357. }
  358. else
  359. {
  360. size = (size + 512) >> 10;
  361. }
  362. }
  363. }
  364. int
  365. is_exe (mode_t mode)
  366. {
  367. if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
  368. return 1;
  369. return 0;
  370. }
  371. #define ismode(n,m) ((n & m) == m)
  372. const char *
  373. string_perm (mode_t mode_bits)
  374. {
  375. static char mode[11];
  376. strcpy (mode, "----------");
  377. if (S_ISDIR (mode_bits))
  378. mode[0] = 'd';
  379. if (S_ISCHR (mode_bits))
  380. mode[0] = 'c';
  381. if (S_ISBLK (mode_bits))
  382. mode[0] = 'b';
  383. if (S_ISLNK (mode_bits))
  384. mode[0] = 'l';
  385. if (S_ISFIFO (mode_bits))
  386. mode[0] = 'p';
  387. if (S_ISNAM (mode_bits))
  388. mode[0] = 'n';
  389. if (S_ISSOCK (mode_bits))
  390. mode[0] = 's';
  391. if (S_ISDOOR (mode_bits))
  392. mode[0] = 'D';
  393. if (ismode (mode_bits, S_IXOTH))
  394. mode[9] = 'x';
  395. if (ismode (mode_bits, S_IWOTH))
  396. mode[8] = 'w';
  397. if (ismode (mode_bits, S_IROTH))
  398. mode[7] = 'r';
  399. if (ismode (mode_bits, S_IXGRP))
  400. mode[6] = 'x';
  401. if (ismode (mode_bits, S_IWGRP))
  402. mode[5] = 'w';
  403. if (ismode (mode_bits, S_IRGRP))
  404. mode[4] = 'r';
  405. if (ismode (mode_bits, S_IXUSR))
  406. mode[3] = 'x';
  407. if (ismode (mode_bits, S_IWUSR))
  408. mode[2] = 'w';
  409. if (ismode (mode_bits, S_IRUSR))
  410. mode[1] = 'r';
  411. #ifdef S_ISUID
  412. if (ismode (mode_bits, S_ISUID))
  413. mode[3] = (mode[3] == 'x') ? 's' : 'S';
  414. #endif /* S_ISUID */
  415. #ifdef S_ISGID
  416. if (ismode (mode_bits, S_ISGID))
  417. mode[6] = (mode[6] == 'x') ? 's' : 'S';
  418. #endif /* S_ISGID */
  419. #ifdef S_ISVTX
  420. if (ismode (mode_bits, S_ISVTX))
  421. mode[9] = (mode[9] == 'x') ? 't' : 'T';
  422. #endif /* S_ISVTX */
  423. return mode;
  424. }
  425. /* p: string which might contain an url with a password (this parameter is
  426. modified in place).
  427. has_prefix = 0: The first parameter is an url without a prefix
  428. (user[:pass]@]machine[:port][remote-dir). Delete
  429. the password.
  430. has_prefix = 1: Search p for known url prefixes. If found delete
  431. the password from the url.
  432. Caveat: only the first url is found
  433. */
  434. char *
  435. strip_password (char *p, int has_prefix)
  436. {
  437. static const struct
  438. {
  439. const char *name;
  440. size_t len;
  441. } prefixes[] =
  442. {
  443. {
  444. "/#ftp:", 6},
  445. {
  446. "ftp://", 6},
  447. {
  448. "/#mc:", 5},
  449. {
  450. "mc://", 5},
  451. {
  452. "/#smb:", 6},
  453. {
  454. "smb://", 6},
  455. {
  456. "/#sh:", 5},
  457. {
  458. "sh://", 5},
  459. {
  460. "ssh://", 6}
  461. };
  462. char *at, *inner_colon, *dir;
  463. size_t i;
  464. char *result = p;
  465. for (i = 0; i < sizeof (prefixes) / sizeof (prefixes[0]); i++)
  466. {
  467. char *q;
  468. if (has_prefix)
  469. {
  470. if ((q = strstr (p, prefixes[i].name)) == 0)
  471. continue;
  472. else
  473. p = q + prefixes[i].len;
  474. }
  475. if ((dir = strchr (p, PATH_SEP)) != NULL)
  476. *dir = '\0';
  477. /* search for any possible user */
  478. at = strrchr (p, '@');
  479. if (dir)
  480. *dir = PATH_SEP;
  481. /* We have a username */
  482. if (at)
  483. {
  484. inner_colon = memchr (p, ':', at - p);
  485. if (inner_colon)
  486. memmove (inner_colon, at, strlen (at) + 1);
  487. }
  488. break;
  489. }
  490. return (result);
  491. }
  492. const char *
  493. strip_home_and_password (const char *dir)
  494. {
  495. size_t len;
  496. static char newdir[MC_MAXPATHLEN];
  497. if (home_dir && !strncmp (dir, home_dir, len = strlen (home_dir)) &&
  498. (dir[len] == PATH_SEP || dir[len] == '\0'))
  499. {
  500. newdir[0] = '~';
  501. g_strlcpy (&newdir[1], &dir[len], sizeof (newdir) - 1);
  502. return newdir;
  503. }
  504. /* We do not strip homes in /#ftp tree, I do not like ~'s there
  505. (see ftpfs.c why) */
  506. g_strlcpy (newdir, dir, sizeof (newdir));
  507. strip_password (newdir, 1);
  508. return newdir;
  509. }
  510. const char *
  511. extension (const char *filename)
  512. {
  513. const char *d = strrchr (filename, '.');
  514. return (d != NULL) ? d + 1 : "";
  515. }
  516. int
  517. exist_file (const char *name)
  518. {
  519. return access (name, R_OK) == 0;
  520. }
  521. int
  522. check_for_default (const char *default_file, const char *file)
  523. {
  524. if (!exist_file (file))
  525. {
  526. FileOpContext *ctx;
  527. FileOpTotalContext *tctx;
  528. if (!exist_file (default_file))
  529. return -1;
  530. ctx = file_op_context_new (OP_COPY);
  531. tctx = file_op_total_context_new ();
  532. file_op_context_create_ui (ctx, 0, FALSE);
  533. copy_file_file (tctx, ctx, default_file, file);
  534. file_op_total_context_destroy (tctx);
  535. file_op_context_destroy (ctx);
  536. }
  537. return 0;
  538. }
  539. char *
  540. load_file (const char *filename)
  541. {
  542. FILE *data_file;
  543. struct stat s;
  544. char *data;
  545. long read_size;
  546. if ((data_file = fopen (filename, "r")) == NULL)
  547. {
  548. return 0;
  549. }
  550. if (fstat (fileno (data_file), &s) != 0)
  551. {
  552. fclose (data_file);
  553. return 0;
  554. }
  555. data = g_malloc (s.st_size + 1);
  556. read_size = fread (data, 1, s.st_size, data_file);
  557. data[read_size] = 0;
  558. fclose (data_file);
  559. if (read_size > 0)
  560. return data;
  561. else
  562. {
  563. g_free (data);
  564. return 0;
  565. }
  566. }
  567. char *
  568. load_mc_home_file (const char *_mc_home, const char *_mc_home_alt, const char *filename,
  569. char **allocated_filename)
  570. {
  571. char *hintfile_base, *hintfile;
  572. char *lang;
  573. char *data;
  574. hintfile_base = concat_dir_and_file (_mc_home, filename);
  575. lang = guess_message_value ();
  576. hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
  577. data = load_file (hintfile);
  578. if (!data)
  579. {
  580. g_free (hintfile);
  581. g_free (hintfile_base);
  582. hintfile_base = concat_dir_and_file (_mc_home_alt, filename);
  583. hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
  584. data = load_file (hintfile);
  585. if (!data)
  586. {
  587. /* Fall back to the two-letter language code */
  588. if (lang[0] && lang[1])
  589. lang[2] = 0;
  590. hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
  591. data = load_file (hintfile);
  592. if (!data)
  593. {
  594. g_free (hintfile);
  595. hintfile = hintfile_base;
  596. data = load_file (hintfile_base);
  597. }
  598. }
  599. }
  600. g_free (lang);
  601. if (hintfile != hintfile_base)
  602. g_free (hintfile_base);
  603. if (allocated_filename)
  604. *allocated_filename = hintfile;
  605. else
  606. g_free (hintfile);
  607. return data;
  608. }
  609. /* Check strftime() results. Some systems (i.e. Solaris) have different
  610. short-month-name sizes for different locales */
  611. size_t
  612. i18n_checktimelength (void)
  613. {
  614. size_t length;
  615. time_t testtime = time (NULL);
  616. struct tm *lt = localtime (&testtime);
  617. if (lt == NULL)
  618. {
  619. /* huh, localtime() doesnt seem to work ... falling back to "(invalid)" */
  620. length = str_term_width1 (_(INVALID_TIME_TEXT));
  621. }
  622. else
  623. {
  624. char buf[MB_LEN_MAX * MAX_I18NTIMELENGTH + 1];
  625. size_t a, b;
  626. strftime (buf, sizeof (buf) - 1, user_recent_timeformat, lt);
  627. a = str_term_width1 (buf);
  628. strftime (buf, sizeof (buf) - 1, user_old_timeformat, lt);
  629. b = str_term_width1 (buf);
  630. length = max (a, b);
  631. length = max ((size_t) str_term_width1 (_(INVALID_TIME_TEXT)), length);
  632. }
  633. /* Don't handle big differences. Use standard value (email bug, please) */
  634. if (length > MAX_I18NTIMELENGTH || length < MIN_I18NTIMELENGTH)
  635. length = STD_I18NTIMELENGTH;
  636. return length;
  637. }
  638. const char *
  639. file_date (time_t when)
  640. {
  641. static char timebuf[MB_LEN_MAX * MAX_I18NTIMELENGTH + 1];
  642. time_t current_time = time ((time_t) 0);
  643. const char *fmt;
  644. if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
  645. || current_time < when - 60L * 60L) /* In the future. */
  646. /* The file is fairly old or in the future.
  647. POSIX says the cutoff is 6 months old;
  648. approximate this by 6*30 days.
  649. Allow a 1 hour slop factor for what is considered "the future",
  650. to allow for NFS server/client clock disagreement.
  651. Show the year instead of the time of day. */
  652. fmt = user_old_timeformat;
  653. else
  654. fmt = user_recent_timeformat;
  655. FMT_LOCALTIME (timebuf, sizeof (timebuf), fmt, when);
  656. return timebuf;
  657. }
  658. const char *
  659. extract_line (const char *s, const char *top)
  660. {
  661. static char tmp_line[BUF_MEDIUM];
  662. char *t = tmp_line;
  663. while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
  664. *t++ = *s++;
  665. *t = 0;
  666. return tmp_line;
  667. }
  668. /* The basename routine */
  669. const char *
  670. x_basename (const char *s)
  671. {
  672. const char *where;
  673. return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
  674. }
  675. const char *
  676. unix_error_string (int error_num)
  677. {
  678. static char buffer[BUF_LARGE];
  679. gchar *strerror_currentlocale;
  680. strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
  681. g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
  682. g_free (strerror_currentlocale);
  683. return buffer;
  684. }
  685. const char *
  686. skip_separators (const char *s)
  687. {
  688. const char *su = s;
  689. for (; *su; str_cnext_char (&su))
  690. if (*su != ' ' && *su != '\t' && *su != ',')
  691. break;
  692. return su;
  693. }
  694. const char *
  695. skip_numbers (const char *s)
  696. {
  697. const char *su = s;
  698. for (; *su; str_cnext_char (&su))
  699. if (!str_isdigit (su))
  700. break;
  701. return su;
  702. }
  703. /* Remove all control sequences from the argument string. We define
  704. * "control sequence", in a sort of pidgin BNF, as follows:
  705. *
  706. * control-seq = Esc non-'['
  707. * | Esc '[' (0 or more digits or ';' or '?') (any other char)
  708. *
  709. * This scheme works for all the terminals described in my termcap /
  710. * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
  711. * terminals. If I hear from a single person who uses such a terminal
  712. * with MC, I'll be glad to add support for it. (Dugan)
  713. * Non-printable characters are also removed.
  714. */
  715. char *
  716. strip_ctrl_codes (char *s)
  717. {
  718. char *w; /* Current position where the stripped data is written */
  719. char *r; /* Current position where the original data is read */
  720. char *n;
  721. if (!s)
  722. return 0;
  723. for (w = s, r = s; *r;)
  724. {
  725. if (*r == ESC_CHAR)
  726. {
  727. /* Skip the control sequence's arguments */ ;
  728. /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
  729. if (*(++r) == '[' || *r == '(')
  730. {
  731. /* strchr() matches trailing binary 0 */
  732. while (*(++r) && strchr ("0123456789;?", *r));
  733. }
  734. else if (*r == ']')
  735. {
  736. /*
  737. * Skip xterm's OSC (Operating System Command)
  738. * http://www.xfree86.org/current/ctlseqs.html
  739. * OSC P s ; P t ST
  740. * OSC P s ; P t BEL
  741. */
  742. char *new_r = r;
  743. for (; *new_r; ++new_r)
  744. {
  745. switch (*new_r)
  746. {
  747. /* BEL */
  748. case '\a':
  749. r = new_r;
  750. goto osc_out;
  751. case ESC_CHAR:
  752. /* ST */
  753. if (*(new_r + 1) == '\\')
  754. {
  755. r = new_r + 1;
  756. goto osc_out;
  757. }
  758. }
  759. }
  760. osc_out:;
  761. }
  762. /*
  763. * Now we are at the last character of the sequence.
  764. * Skip it unless it's binary 0.
  765. */
  766. if (*r)
  767. r++;
  768. continue;
  769. }
  770. n = str_get_next_char (r);
  771. if (str_isprint (r))
  772. {
  773. memmove (w, r, n - r);
  774. w += n - r;
  775. }
  776. r = n;
  777. }
  778. *w = 0;
  779. return s;
  780. }
  781. #ifndef ENABLE_VFS
  782. char *
  783. get_current_wd (char *buffer, int size)
  784. {
  785. char *p;
  786. int len;
  787. p = g_get_current_dir ();
  788. len = strlen (p) + 1;
  789. if (len > size)
  790. {
  791. g_free (p);
  792. return NULL;
  793. }
  794. memcpy (buffer, p, len);
  795. g_free (p);
  796. return buffer;
  797. }
  798. #endif /* !ENABLE_VFS */
  799. enum compression_type
  800. get_compression_type (int fd, const char *name)
  801. {
  802. unsigned char magic[16];
  803. size_t str_len;
  804. /* Read the magic signature */
  805. if (mc_read (fd, (char *) magic, 4) != 4)
  806. return COMPRESSION_NONE;
  807. /* GZIP_MAGIC and OLD_GZIP_MAGIC */
  808. if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
  809. {
  810. return COMPRESSION_GZIP;
  811. }
  812. /* PKZIP_MAGIC */
  813. if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
  814. {
  815. /* Read compression type */
  816. mc_lseek (fd, 8, SEEK_SET);
  817. if (mc_read (fd, (char *) magic, 2) != 2)
  818. return COMPRESSION_NONE;
  819. /* Gzip can handle only deflated (8) or stored (0) files */
  820. if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
  821. return COMPRESSION_NONE;
  822. /* Compatible with gzip */
  823. return COMPRESSION_GZIP;
  824. }
  825. /* PACK_MAGIC and LZH_MAGIC and compress magic */
  826. if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
  827. {
  828. /* Compatible with gzip */
  829. return COMPRESSION_GZIP;
  830. }
  831. /* BZIP and BZIP2 files */
  832. if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
  833. {
  834. switch (magic[2])
  835. {
  836. case '0':
  837. return COMPRESSION_BZIP;
  838. case 'h':
  839. return COMPRESSION_BZIP2;
  840. }
  841. }
  842. /* Support for LZMA (only utils format with magic in header).
  843. * This is the default format of LZMA utils 4.32.1 and later. */
  844. if (mc_read (fd, (char *) magic + 4, 2) != 2)
  845. return COMPRESSION_NONE;
  846. /* LZMA utils format */
  847. if (magic[0] == 0xFF
  848. && magic[1] == 'L'
  849. && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
  850. return COMPRESSION_LZMA;
  851. /* XZ compression magic */
  852. if (magic[0] == 0xFD
  853. && magic[1] == 0x37
  854. && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
  855. return COMPRESSION_XZ;
  856. str_len = strlen (name);
  857. /* HACK: we must belive to extention of LZMA file :) ... */
  858. if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
  859. (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
  860. return COMPRESSION_LZMA;
  861. return COMPRESSION_NONE;
  862. }
  863. const char *
  864. decompress_extension (int type)
  865. {
  866. switch (type)
  867. {
  868. case COMPRESSION_GZIP:
  869. return "#ugz";
  870. case COMPRESSION_BZIP:
  871. return "#ubz";
  872. case COMPRESSION_BZIP2:
  873. return "#ubz2";
  874. case COMPRESSION_LZMA:
  875. return "#ulzma";
  876. case COMPRESSION_XZ:
  877. return "#uxz";
  878. }
  879. /* Should never reach this place */
  880. fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
  881. return 0;
  882. }
  883. /* Hooks */
  884. void
  885. add_hook (Hook ** hook_list, void (*hook_fn) (void *), void *data)
  886. {
  887. Hook *new_hook = g_new (Hook, 1);
  888. new_hook->hook_fn = hook_fn;
  889. new_hook->next = *hook_list;
  890. new_hook->hook_data = data;
  891. *hook_list = new_hook;
  892. }
  893. void
  894. execute_hooks (Hook * hook_list)
  895. {
  896. Hook *new_hook = 0;
  897. Hook *p;
  898. /* We copy the hook list first so tahat we let the hook
  899. * function call delete_hook
  900. */
  901. while (hook_list)
  902. {
  903. add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
  904. hook_list = hook_list->next;
  905. }
  906. p = new_hook;
  907. while (new_hook)
  908. {
  909. (*new_hook->hook_fn) (new_hook->hook_data);
  910. new_hook = new_hook->next;
  911. }
  912. for (hook_list = p; hook_list;)
  913. {
  914. p = hook_list;
  915. hook_list = hook_list->next;
  916. g_free (p);
  917. }
  918. }
  919. void
  920. delete_hook (Hook ** hook_list, void (*hook_fn) (void *))
  921. {
  922. Hook *current, *new_list, *next;
  923. new_list = 0;
  924. for (current = *hook_list; current; current = next)
  925. {
  926. next = current->next;
  927. if (current->hook_fn == hook_fn)
  928. g_free (current);
  929. else
  930. add_hook (&new_list, current->hook_fn, current->hook_data);
  931. }
  932. *hook_list = new_list;
  933. }
  934. int
  935. hook_present (Hook * hook_list, void (*hook_fn) (void *))
  936. {
  937. Hook *p;
  938. for (p = hook_list; p; p = p->next)
  939. if (p->hook_fn == hook_fn)
  940. return 1;
  941. return 0;
  942. }
  943. void
  944. wipe_password (char *passwd)
  945. {
  946. char *p = passwd;
  947. if (!p)
  948. return;
  949. for (; *p; p++)
  950. *p = 0;
  951. g_free (passwd);
  952. }
  953. /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
  954. /* Returns a newly allocated string */
  955. char *
  956. convert_controls (const char *p)
  957. {
  958. char *valcopy = g_strdup (p);
  959. char *q;
  960. /* Parse the escape special character */
  961. for (q = valcopy; *p;)
  962. {
  963. if (*p == '\\')
  964. {
  965. p++;
  966. if ((*p == 'e') || (*p == 'E'))
  967. {
  968. p++;
  969. *q++ = ESC_CHAR;
  970. }
  971. }
  972. else
  973. {
  974. if (*p == '^')
  975. {
  976. p++;
  977. if (*p == '^')
  978. *q++ = *p++;
  979. else
  980. {
  981. char c = (*p | 0x20);
  982. if (c >= 'a' && c <= 'z')
  983. {
  984. *q++ = c - 'a' + 1;
  985. p++;
  986. }
  987. else if (*p)
  988. p++;
  989. }
  990. }
  991. else
  992. *q++ = *p++;
  993. }
  994. }
  995. *q = 0;
  996. return valcopy;
  997. }
  998. static char *
  999. resolve_symlinks (const char *path)
  1000. {
  1001. char *buf, *buf2, *q, *r, c;
  1002. int len;
  1003. struct stat mybuf;
  1004. const char *p;
  1005. if (*path != PATH_SEP)
  1006. return NULL;
  1007. r = buf = g_malloc (MC_MAXPATHLEN);
  1008. buf2 = g_malloc (MC_MAXPATHLEN);
  1009. *r++ = PATH_SEP;
  1010. *r = 0;
  1011. p = path;
  1012. for (;;)
  1013. {
  1014. q = strchr (p + 1, PATH_SEP);
  1015. if (!q)
  1016. {
  1017. q = strchr (p + 1, 0);
  1018. if (q == p + 1)
  1019. break;
  1020. }
  1021. c = *q;
  1022. *q = 0;
  1023. if (mc_lstat (path, &mybuf) < 0)
  1024. {
  1025. g_free (buf);
  1026. g_free (buf2);
  1027. *q = c;
  1028. return NULL;
  1029. }
  1030. if (!S_ISLNK (mybuf.st_mode))
  1031. strcpy (r, p + 1);
  1032. else
  1033. {
  1034. len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
  1035. if (len < 0)
  1036. {
  1037. g_free (buf);
  1038. g_free (buf2);
  1039. *q = c;
  1040. return NULL;
  1041. }
  1042. buf2[len] = 0;
  1043. if (*buf2 == PATH_SEP)
  1044. strcpy (buf, buf2);
  1045. else
  1046. strcpy (r, buf2);
  1047. }
  1048. canonicalize_pathname (buf);
  1049. r = strchr (buf, 0);
  1050. if (!*r || *(r - 1) != PATH_SEP)
  1051. {
  1052. *r++ = PATH_SEP;
  1053. *r = 0;
  1054. }
  1055. *q = c;
  1056. p = q;
  1057. if (!c)
  1058. break;
  1059. }
  1060. if (!*buf)
  1061. strcpy (buf, PATH_SEP_STR);
  1062. else if (*(r - 1) == PATH_SEP && r != buf + 1)
  1063. *(r - 1) = 0;
  1064. g_free (buf2);
  1065. return buf;
  1066. }
  1067. static gboolean
  1068. mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
  1069. {
  1070. FILE *backup_fd;
  1071. char *contents;
  1072. gsize length;
  1073. if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
  1074. return FALSE;
  1075. backup_fd = fopen (to_file_name, "w");
  1076. if (backup_fd == NULL)
  1077. {
  1078. g_free (contents);
  1079. return FALSE;
  1080. }
  1081. fwrite ((const void *) contents, length, 1, backup_fd);
  1082. fflush (backup_fd);
  1083. fclose (backup_fd);
  1084. g_free (contents);
  1085. return TRUE;
  1086. }
  1087. /* Finds out a relative path from first to second, i.e. goes as many ..
  1088. * as needed up in first and then goes down using second */
  1089. char *
  1090. diff_two_paths (const char *first, const char *second)
  1091. {
  1092. char *p, *q, *r, *s, *buf = NULL;
  1093. int i, j, prevlen = -1, currlen;
  1094. char *my_first = NULL, *my_second = NULL;
  1095. my_first = resolve_symlinks (first);
  1096. if (my_first == NULL)
  1097. return NULL;
  1098. my_second = resolve_symlinks (second);
  1099. if (my_second == NULL)
  1100. {
  1101. g_free (my_first);
  1102. return NULL;
  1103. }
  1104. for (j = 0; j < 2; j++)
  1105. {
  1106. p = my_first;
  1107. q = my_second;
  1108. for (;;)
  1109. {
  1110. r = strchr (p, PATH_SEP);
  1111. s = strchr (q, PATH_SEP);
  1112. if (!r || !s)
  1113. break;
  1114. *r = 0;
  1115. *s = 0;
  1116. if (strcmp (p, q))
  1117. {
  1118. *r = PATH_SEP;
  1119. *s = PATH_SEP;
  1120. break;
  1121. }
  1122. else
  1123. {
  1124. *r = PATH_SEP;
  1125. *s = PATH_SEP;
  1126. }
  1127. p = r + 1;
  1128. q = s + 1;
  1129. }
  1130. p--;
  1131. for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
  1132. currlen = (i + 1) * 3 + strlen (q) + 1;
  1133. if (j)
  1134. {
  1135. if (currlen < prevlen)
  1136. g_free (buf);
  1137. else
  1138. {
  1139. g_free (my_first);
  1140. g_free (my_second);
  1141. return buf;
  1142. }
  1143. }
  1144. p = buf = g_malloc (currlen);
  1145. prevlen = currlen;
  1146. for (; i >= 0; i--, p += 3)
  1147. strcpy (p, "../");
  1148. strcpy (p, q);
  1149. }
  1150. g_free (my_first);
  1151. g_free (my_second);
  1152. return buf;
  1153. }
  1154. /* If filename is NULL, then we just append PATH_SEP to the dir */
  1155. char *
  1156. concat_dir_and_file (const char *dir, const char *file)
  1157. {
  1158. int i = strlen (dir);
  1159. if (dir[i - 1] == PATH_SEP)
  1160. return g_strconcat (dir, file, (char *) NULL);
  1161. else
  1162. return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
  1163. }
  1164. /* Append text to GList, remove all entries with the same text */
  1165. GList *
  1166. list_append_unique (GList * list, char *text)
  1167. {
  1168. GList *lc_link;
  1169. /*
  1170. * Go to the last position and traverse the list backwards
  1171. * starting from the second last entry to make sure that we
  1172. * are not removing the current link.
  1173. */
  1174. list = g_list_append (list, text);
  1175. list = g_list_last (list);
  1176. lc_link = g_list_previous (list);
  1177. while (lc_link != NULL)
  1178. {
  1179. GList *newlink;
  1180. newlink = g_list_previous (lc_link);
  1181. if (strcmp ((char *) lc_link->data, text) == 0)
  1182. {
  1183. GList *tmp;
  1184. g_free (lc_link->data);
  1185. tmp = g_list_remove_link (list, lc_link);
  1186. g_list_free_1 (lc_link);
  1187. }
  1188. lc_link = newlink;
  1189. }
  1190. return list;
  1191. }
  1192. /* Following code heavily borrows from libiberty, mkstemps.c */
  1193. /* Number of attempts to create a temporary file */
  1194. #ifndef TMP_MAX
  1195. #define TMP_MAX 16384
  1196. #endif /* !TMP_MAX */
  1197. /*
  1198. * Arguments:
  1199. * pname (output) - pointer to the name of the temp file (needs g_free).
  1200. * NULL if the function fails.
  1201. * prefix - part of the filename before the random part.
  1202. * Prepend $TMPDIR or /tmp if there are no path separators.
  1203. * suffix - if not NULL, part of the filename after the random part.
  1204. *
  1205. * Result:
  1206. * handle of the open file or -1 if couldn't open any.
  1207. */
  1208. int
  1209. mc_mkstemps (char **pname, const char *prefix, const char *suffix)
  1210. {
  1211. static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  1212. static unsigned long value;
  1213. struct timeval tv;
  1214. char *tmpbase;
  1215. char *tmpname;
  1216. char *XXXXXX;
  1217. int count;
  1218. if (strchr (prefix, PATH_SEP) == NULL)
  1219. {
  1220. /* Add prefix first to find the position of XXXXXX */
  1221. tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
  1222. }
  1223. else
  1224. {
  1225. tmpbase = g_strdup (prefix);
  1226. }
  1227. tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
  1228. *pname = tmpname;
  1229. XXXXXX = &tmpname[strlen (tmpbase)];
  1230. g_free (tmpbase);
  1231. /* Get some more or less random data. */
  1232. gettimeofday (&tv, NULL);
  1233. value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
  1234. for (count = 0; count < TMP_MAX; ++count)
  1235. {
  1236. unsigned long v = value;
  1237. int fd;
  1238. /* Fill in the random bits. */
  1239. XXXXXX[0] = letters[v % 62];
  1240. v /= 62;
  1241. XXXXXX[1] = letters[v % 62];
  1242. v /= 62;
  1243. XXXXXX[2] = letters[v % 62];
  1244. v /= 62;
  1245. XXXXXX[3] = letters[v % 62];
  1246. v /= 62;
  1247. XXXXXX[4] = letters[v % 62];
  1248. v /= 62;
  1249. XXXXXX[5] = letters[v % 62];
  1250. fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, S_IRUSR | S_IWUSR);
  1251. if (fd >= 0)
  1252. {
  1253. /* Successfully created. */
  1254. return fd;
  1255. }
  1256. /* This is a random value. It is only necessary that the next
  1257. TMP_MAX values generated by adding 7777 to VALUE are different
  1258. with (module 2^32). */
  1259. value += 7777;
  1260. }
  1261. /* Unsuccessful. Free the filename. */
  1262. g_free (tmpname);
  1263. *pname = NULL;
  1264. return -1;
  1265. }
  1266. /*
  1267. * Read and restore position for the given filename.
  1268. * If there is no stored data, return line 1 and col 0.
  1269. */
  1270. void
  1271. load_file_position (const char *filename, long *line, long *column, off_t * offset)
  1272. {
  1273. char *fn;
  1274. FILE *f;
  1275. char buf[MC_MAXPATHLEN + 20];
  1276. int len;
  1277. /* defaults */
  1278. *line = 1;
  1279. *column = 0;
  1280. *offset = 0;
  1281. /* open file with positions */
  1282. fn = g_build_filename (home_dir, MC_USERCONF_DIR, MC_FILEPOS_FILE, NULL);
  1283. f = fopen (fn, "r");
  1284. g_free (fn);
  1285. if (!f)
  1286. return;
  1287. len = strlen (filename);
  1288. while (fgets (buf, sizeof (buf), f))
  1289. {
  1290. const char *p;
  1291. gchar **pos_tokens;
  1292. /* check if the filename matches the beginning of string */
  1293. if (strncmp (buf, filename, len) != 0)
  1294. continue;
  1295. /* followed by single space */
  1296. if (buf[len] != ' ')
  1297. continue;
  1298. /* and string without spaces */
  1299. p = &buf[len + 1];
  1300. if (strchr (p, ' '))
  1301. continue;
  1302. pos_tokens = g_strsplit_set (p, ";", 3);
  1303. if (pos_tokens[0] != NULL)
  1304. {
  1305. *line = strtol (pos_tokens[0], NULL, 10);
  1306. if (pos_tokens[1] != NULL)
  1307. {
  1308. *column = strtol (pos_tokens[1], NULL, 10);
  1309. if (pos_tokens[2] != NULL)
  1310. *offset = strtoll (pos_tokens[2], NULL, 10);
  1311. else
  1312. *offset = 0;
  1313. }
  1314. else
  1315. {
  1316. *column = 0;
  1317. *offset = 0;
  1318. }
  1319. }
  1320. else
  1321. {
  1322. *line = 1;
  1323. *column = 0;
  1324. *offset = 0;
  1325. }
  1326. g_strfreev (pos_tokens);
  1327. }
  1328. fclose (f);
  1329. }
  1330. /* Save position for the given file */
  1331. #define TMP_SUFFIX ".tmp"
  1332. void
  1333. save_file_position (const char *filename, long line, long column, off_t offset)
  1334. {
  1335. static int filepos_max_saved_entries = 0;
  1336. char *fn, *tmp_fn;
  1337. FILE *f, *tmp_f;
  1338. char buf[MC_MAXPATHLEN + 20];
  1339. int i = 1;
  1340. gsize len;
  1341. if (filepos_max_saved_entries == 0)
  1342. filepos_max_saved_entries =
  1343. mc_config_get_int (mc_main_config, CONFIG_APP_SECTION, "filepos_max_saved_entries",
  1344. 1024);
  1345. fn = g_build_filename (home_dir, MC_USERCONF_DIR, MC_FILEPOS_FILE, NULL);
  1346. if (fn == NULL)
  1347. goto early_error;
  1348. len = strlen (filename);
  1349. mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
  1350. /* open file */
  1351. f = fopen (fn, "w");
  1352. if (f == NULL)
  1353. goto open_target_error;
  1354. tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
  1355. tmp_f = fopen (tmp_fn, "r");
  1356. if (tmp_f == NULL)
  1357. goto open_source_error;
  1358. /* put the new record */
  1359. if (line != 1 || column != 0)
  1360. {
  1361. if (fprintf (f, "%s %ld;%ld;%llu\n", filename, line, column, (unsigned long long) offset) <
  1362. 0)
  1363. goto write_position_error;
  1364. }
  1365. while (fgets (buf, sizeof (buf), tmp_f))
  1366. {
  1367. if (buf[len] == ' ' && strncmp (buf, filename, len) == 0 && !strchr (&buf[len + 1], ' '))
  1368. continue;
  1369. fprintf (f, "%s", buf);
  1370. if (++i > filepos_max_saved_entries)
  1371. break;
  1372. }
  1373. fclose (tmp_f);
  1374. g_free (tmp_fn);
  1375. fclose (f);
  1376. mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
  1377. g_free (fn);
  1378. return;
  1379. write_position_error:
  1380. fclose (tmp_f);
  1381. open_source_error:
  1382. g_free (tmp_fn);
  1383. fclose (f);
  1384. mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
  1385. open_target_error:
  1386. g_free (fn);
  1387. early_error:
  1388. return;
  1389. }
  1390. #undef TMP_SUFFIX
  1391. extern const char *
  1392. cstrcasestr (const char *haystack, const char *needle)
  1393. {
  1394. char *nee = str_create_search_needle (needle, 0);
  1395. const char *result = str_search_first (haystack, nee, 0);
  1396. str_release_search_needle (nee, 0);
  1397. return result;
  1398. }
  1399. const char *
  1400. cstrstr (const char *haystack, const char *needle)
  1401. {
  1402. return strstr (haystack, needle);
  1403. }
  1404. extern char *
  1405. str_unconst (const char *s)
  1406. {
  1407. return (char *) s;
  1408. }
  1409. #define ASCII_A (0x40 + 1)
  1410. #define ASCII_Z (0x40 + 26)
  1411. #define ASCII_a (0x60 + 1)
  1412. #define ASCII_z (0x60 + 26)
  1413. extern int
  1414. ascii_alpha_to_cntrl (int ch)
  1415. {
  1416. if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
  1417. {
  1418. ch &= 0x1f;
  1419. }
  1420. return ch;
  1421. }
  1422. const char *
  1423. Q_ (const char *s)
  1424. {
  1425. const char *result, *sep;
  1426. result = _(s);
  1427. sep = strchr (result, '|');
  1428. return (sep != NULL) ? sep + 1 : result;
  1429. }
  1430. gboolean
  1431. mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
  1432. {
  1433. struct stat stat_buf;
  1434. char *backup_path;
  1435. gboolean ret;
  1436. if (!exist_file (file_name))
  1437. return FALSE;
  1438. backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
  1439. if (backup_path == NULL)
  1440. return FALSE;
  1441. ret = mc_util_write_backup_content (file_name, backup_path);
  1442. if (ret)
  1443. {
  1444. /* Backup file will have same ownership with main file. */
  1445. if (stat (file_name, &stat_buf) == 0)
  1446. chmod (backup_path, stat_buf.st_mode);
  1447. else
  1448. chmod (backup_path, S_IRUSR | S_IWUSR);
  1449. }
  1450. g_free (backup_path);
  1451. return ret;
  1452. }
  1453. gboolean
  1454. mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
  1455. {
  1456. gboolean ret;
  1457. char *backup_path;
  1458. backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
  1459. if (backup_path == NULL)
  1460. return FALSE;
  1461. ret = mc_util_write_backup_content (backup_path, file_name);
  1462. g_free (backup_path);
  1463. return ret;
  1464. }
  1465. gboolean
  1466. mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
  1467. {
  1468. char *backup_path;
  1469. backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
  1470. if (backup_path == NULL)
  1471. return FALSE;
  1472. if (exist_file (backup_path))
  1473. mc_unlink (backup_path);
  1474. g_free (backup_path);
  1475. return TRUE;
  1476. }
  1477. /* partly taken from dcigettext.c, returns "" for default locale */
  1478. /* value should be freed by calling function g_free() */
  1479. char *
  1480. guess_message_value (void)
  1481. {
  1482. static const char *const var[] = {
  1483. /* Setting of LC_ALL overwrites all other. */
  1484. /* Do not use LANGUAGE for check user locale and drowing hints */
  1485. "LC_ALL",
  1486. /* Next comes the name of the desired category. */
  1487. "LC_MESSAGES",
  1488. /* Last possibility is the LANG environment variable. */
  1489. "LANG",
  1490. /* NULL exit loops */
  1491. NULL
  1492. };
  1493. unsigned i = 0;
  1494. const char *locale = NULL;
  1495. while (var[i] != NULL)
  1496. {
  1497. locale = getenv (var[i]);
  1498. if (locale != NULL && locale[0] != '\0')
  1499. break;
  1500. i++;
  1501. }
  1502. if (locale == NULL)
  1503. locale = "";
  1504. return g_strdup (locale);
  1505. }