utilvfs.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134
  1. /* Utilities for VFS modules.
  2. Copyright (C) 1988, 1992, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
  3. 2005, 2006, 2007 Free Software Foundation, Inc.
  4. Copyright (C) 1995, 1996 Miguel de Icaza
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Library General Public License
  7. as published by the Free Software Foundation; either version 2 of
  8. the License, or (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU Library General Public License for more details.
  13. You should have received a copy of the GNU Library General Public
  14. License along with this program; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
  16. /**
  17. * \file
  18. * \brief Source: Utilities for VFS modules
  19. * \author Miguel de Icaza
  20. * \date 1995, 1996
  21. */
  22. #include <config.h>
  23. #include <ctype.h>
  24. #include <sys/types.h>
  25. #include <pwd.h>
  26. #include <grp.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <unistd.h>
  30. #include "lib/global.h"
  31. #include "lib/unixcompat.h"
  32. #include "lib/util.h" /* mc_mkstemps() */
  33. #include "lib/widget.h" /* message() */
  34. #include "vfs.h"
  35. #include "utilvfs.h"
  36. /*** global variables ****************************************************************************/
  37. /*** file scope macro definitions ****************************************************************/
  38. #ifndef TUNMLEN
  39. #define TUNMLEN 256
  40. #endif
  41. #ifndef TGNMLEN
  42. #define TGNMLEN 256
  43. #endif
  44. #define myuid ( my_uid < 0? (my_uid = getuid()): my_uid )
  45. #define mygid ( my_gid < 0? (my_gid = getgid()): my_gid )
  46. /* Parsing code is used by ftpfs, fish and extfs */
  47. #define MAXCOLS 30
  48. #define MC_HISTORY_VFS_PASSWORD "mc.vfs.password"
  49. /*** file scope type declarations ****************************************************************/
  50. /*** file scope variables ************************************************************************/
  51. static char *columns[MAXCOLS]; /* Points to the string in column n */
  52. static int column_ptr[MAXCOLS]; /* Index from 0 to the starting positions of the columns */
  53. /*** file scope functions ************************************************************************/
  54. /* --------------------------------------------------------------------------------------------- */
  55. static int
  56. is_num (int idx)
  57. {
  58. char *column = columns[idx];
  59. if (!column || column[0] < '0' || column[0] > '9')
  60. return 0;
  61. return 1;
  62. }
  63. /* --------------------------------------------------------------------------------------------- */
  64. /* Return 1 for MM-DD-YY and MM-DD-YYYY */
  65. static int
  66. is_dos_date (const char *str)
  67. {
  68. int len;
  69. if (!str)
  70. return 0;
  71. len = strlen (str);
  72. if (len != 8 && len != 10)
  73. return 0;
  74. if (str[2] != str[5])
  75. return 0;
  76. if (!strchr ("\\-/", (int) str[2]))
  77. return 0;
  78. return 1;
  79. }
  80. /* --------------------------------------------------------------------------------------------- */
  81. static int
  82. is_week (const char *str, struct tm *tim)
  83. {
  84. static const char *week = "SunMonTueWedThuFriSat";
  85. const char *pos;
  86. if (!str)
  87. return 0;
  88. pos = strstr (week, str);
  89. if (pos != NULL)
  90. {
  91. if (tim != NULL)
  92. tim->tm_wday = (pos - week) / 3;
  93. return 1;
  94. }
  95. return 0;
  96. }
  97. /* --------------------------------------------------------------------------------------------- */
  98. static int
  99. is_month (const char *str, struct tm *tim)
  100. {
  101. static const char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
  102. const char *pos;
  103. if (!str)
  104. return 0;
  105. pos = strstr (month, str);
  106. if (pos != NULL)
  107. {
  108. if (tim != NULL)
  109. tim->tm_mon = (pos - month) / 3;
  110. return 1;
  111. }
  112. return 0;
  113. }
  114. /* --------------------------------------------------------------------------------------------- */
  115. /**
  116. * Check for possible locale's abbreviated month name (Jan..Dec).
  117. * Any 3 bytes long string without digit, control and punctuation characters.
  118. * isalpha() is locale specific, so it cannot be used if current
  119. * locale is "C" and ftp server use Cyrillic.
  120. * NB: It is assumed there are no whitespaces in month.
  121. */
  122. static int
  123. is_localized_month (const char *month)
  124. {
  125. int i = 0;
  126. if (!month)
  127. return 0;
  128. while ((i < 3) && *month && !isdigit ((unsigned char) *month)
  129. && !iscntrl ((unsigned char) *month) && !ispunct ((unsigned char) *month))
  130. {
  131. i++;
  132. month++;
  133. }
  134. return ((i == 3) && (*month == 0));
  135. }
  136. /* --------------------------------------------------------------------------------------------- */
  137. static int
  138. is_time (const char *str, struct tm *tim)
  139. {
  140. const char *p, *p2;
  141. if (str == NULL)
  142. return 0;
  143. p = strchr (str, ':');
  144. p2 = strrchr (str, ':');
  145. if (p != NULL && p2 != NULL)
  146. {
  147. if (p != p2)
  148. {
  149. if (sscanf (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min, &tim->tm_sec) != 3)
  150. return 0;
  151. }
  152. else
  153. {
  154. if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
  155. return 0;
  156. }
  157. }
  158. else
  159. return 0;
  160. return 1;
  161. }
  162. /* --------------------------------------------------------------------------------------------- */
  163. static int
  164. is_year (char *str, struct tm *tim)
  165. {
  166. long year;
  167. if (!str)
  168. return 0;
  169. if (strchr (str, ':'))
  170. return 0;
  171. if (strlen (str) != 4)
  172. return 0;
  173. if (sscanf (str, "%ld", &year) != 1)
  174. return 0;
  175. if (year < 1900 || year > 3000)
  176. return 0;
  177. tim->tm_year = (int) (year - 1900);
  178. return 1;
  179. }
  180. /* --------------------------------------------------------------------------------------------- */
  181. /*** public functions ****************************************************************************/
  182. /* --------------------------------------------------------------------------------------------- */
  183. /** Get current username
  184. *
  185. * @return g_malloc()ed string with the name of the currently logged in
  186. * user ("anonymous" if uid is not registered in the system)
  187. */
  188. char *
  189. vfs_get_local_username (void)
  190. {
  191. struct passwd *p_i;
  192. p_i = getpwuid (geteuid ());
  193. return (p_i && p_i->pw_name) ? g_strdup (p_i->pw_name) : g_strdup ("anonymous"); /* Unknown UID, strange */
  194. }
  195. /* --------------------------------------------------------------------------------------------- */
  196. /**
  197. * Look up a user or group name from a uid/gid, maintaining a cache.
  198. * FIXME, for now it's a one-entry cache.
  199. * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
  200. * This file should be modified for non-unix systems to do something
  201. * reasonable.
  202. */
  203. /* --------------------------------------------------------------------------------------------- */
  204. int
  205. vfs_finduid (const char *uname)
  206. {
  207. static int saveuid = -993;
  208. static char saveuname[TUNMLEN];
  209. static int my_uid = -993;
  210. struct passwd *pw;
  211. if (uname[0] != saveuname[0] /* Quick test w/o proc call */
  212. || 0 != strncmp (uname, saveuname, TUNMLEN))
  213. {
  214. g_strlcpy (saveuname, uname, TUNMLEN);
  215. pw = getpwnam (uname);
  216. if (pw)
  217. {
  218. saveuid = pw->pw_uid;
  219. }
  220. else
  221. {
  222. saveuid = myuid;
  223. }
  224. }
  225. return saveuid;
  226. }
  227. /* --------------------------------------------------------------------------------------------- */
  228. int
  229. vfs_findgid (const char *gname)
  230. {
  231. static int savegid = -993;
  232. static char savegname[TGNMLEN];
  233. static int my_gid = -993;
  234. struct group *gr;
  235. if (gname[0] != savegname[0] /* Quick test w/o proc call */
  236. || 0 != strncmp (gname, savegname, TUNMLEN))
  237. {
  238. g_strlcpy (savegname, gname, TUNMLEN);
  239. gr = getgrnam (gname);
  240. if (gr)
  241. {
  242. savegid = gr->gr_gid;
  243. }
  244. else
  245. {
  246. savegid = mygid;
  247. }
  248. }
  249. return savegid;
  250. }
  251. /* --------------------------------------------------------------------------------------------- */
  252. /**
  253. * Create a temporary file with a name resembling the original.
  254. * This is needed e.g. for local copies requested by extfs.
  255. * Some extfs scripts may look at the extension.
  256. * We also protect stupid scripts agains dangerous names.
  257. */
  258. int
  259. vfs_mkstemps (char **pname, const char *prefix, const char *param_basename)
  260. {
  261. const char *p;
  262. char *suffix, *q;
  263. int shift;
  264. int fd;
  265. /* Strip directories */
  266. p = strrchr (param_basename, PATH_SEP);
  267. if (!p)
  268. p = param_basename;
  269. else
  270. p++;
  271. /* Protection against very long names */
  272. shift = strlen (p) - (MC_MAXPATHLEN - 16);
  273. if (shift > 0)
  274. p += shift;
  275. suffix = g_malloc (MC_MAXPATHLEN);
  276. /* Protection against unusual characters */
  277. q = suffix;
  278. while (*p && (*p != '#'))
  279. {
  280. if (strchr (".-_@", *p) || isalnum ((unsigned char) *p))
  281. *q++ = *p;
  282. p++;
  283. }
  284. *q = 0;
  285. fd = mc_mkstemps (pname, prefix, suffix);
  286. g_free (suffix);
  287. return fd;
  288. }
  289. /* --------------------------------------------------------------------------------------------- */
  290. /** Extract the hostname and username from the path
  291. *
  292. * Format of the path is [user@]hostname:port/remote-dir, e.g.:
  293. *
  294. * ftp://sunsite.unc.edu/pub/linux
  295. * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
  296. * ftp://tsx-11.mit.edu:8192/
  297. * ftp://joe@foo.edu:11321/private
  298. * ftp://joe:password@foo.se
  299. *
  300. * @param path is an input string to be parsed
  301. * @param default_port is an input default port
  302. * @param flags are parsing modifier flags (@see vfs_url_flags_t)
  303. *
  304. * @return g_malloc()ed url info.
  305. * If the user is empty, e.g. ftp://@roxanne/private, and URL_USE_ANONYMOUS
  306. * is not set, then the current login name is supplied.
  307. * Return value is a g_malloc()ed structure with the pathname relative to the
  308. * host.
  309. */
  310. vfs_path_element_t *
  311. vfs_url_split (const char *path, int default_port, vfs_url_flags_t flags)
  312. {
  313. vfs_path_element_t *path_element;
  314. char *pcopy;
  315. const char *pend;
  316. char *dir, *colon, *inner_colon, *at, *rest;
  317. path_element = g_new0 (vfs_path_element_t, 1);
  318. path_element->port = default_port;
  319. pcopy = g_strdup (path);
  320. pend = pcopy + strlen (pcopy);
  321. dir = pcopy;
  322. if ((flags & URL_NOSLASH) == 0)
  323. {
  324. /* locate path component */
  325. while (*dir != PATH_SEP && *dir != '\0')
  326. dir++;
  327. if (*dir == '\0')
  328. path_element->path = g_strdup (PATH_SEP_STR);
  329. else
  330. {
  331. path_element->path = g_strdup (dir);
  332. *dir = '\0';
  333. }
  334. }
  335. /* search for any possible user */
  336. at = strrchr (pcopy, '@');
  337. /* We have a username */
  338. if (at == NULL)
  339. rest = pcopy;
  340. else
  341. {
  342. *at = '\0';
  343. inner_colon = strchr (pcopy, ':');
  344. if (inner_colon != NULL)
  345. {
  346. *inner_colon = '\0';
  347. inner_colon++;
  348. path_element->password = g_strdup (inner_colon);
  349. }
  350. if (*pcopy != '\0')
  351. path_element->user = g_strdup (pcopy);
  352. if (pend == at + 1)
  353. rest = at;
  354. else
  355. rest = at + 1;
  356. }
  357. if ((flags & URL_USE_ANONYMOUS) == 0)
  358. path_element->user = vfs_get_local_username ();
  359. /* Check if the host comes with a port spec, if so, chop it */
  360. if (*rest != '[')
  361. colon = strchr (rest, ':');
  362. else
  363. {
  364. colon = strchr (++rest, ']');
  365. if (colon != NULL)
  366. {
  367. colon[0] = '\0';
  368. colon[1] = '\0';
  369. colon++;
  370. }
  371. else
  372. {
  373. vfs_path_element_free (path_element);
  374. return NULL;
  375. }
  376. }
  377. if (colon != NULL)
  378. {
  379. *colon = '\0';
  380. if (sscanf (colon + 1, "%d", &path_element->port) == 1)
  381. {
  382. if (path_element->port <= 0 || path_element->port >= 65536)
  383. path_element->port = default_port;
  384. }
  385. else
  386. while (*(++colon) != '\0')
  387. {
  388. switch (*colon)
  389. {
  390. case 'C':
  391. path_element->port = 1;
  392. break;
  393. case 'r':
  394. path_element->port = 2;
  395. break;
  396. }
  397. }
  398. }
  399. path_element->host = g_strdup (rest);
  400. return path_element;
  401. }
  402. /* --------------------------------------------------------------------------------------------- */
  403. int
  404. vfs_split_text (char *p)
  405. {
  406. char *original = p;
  407. int numcols;
  408. memset (columns, 0, sizeof (columns));
  409. for (numcols = 0; *p && numcols < MAXCOLS; numcols++)
  410. {
  411. while (*p == ' ' || *p == '\r' || *p == '\n')
  412. {
  413. *p = 0;
  414. p++;
  415. }
  416. columns[numcols] = p;
  417. column_ptr[numcols] = p - original;
  418. while (*p && *p != ' ' && *p != '\r' && *p != '\n')
  419. p++;
  420. }
  421. return numcols;
  422. }
  423. /* --------------------------------------------------------------------------------------------- */
  424. gboolean
  425. vfs_parse_filetype (const char *s, size_t * ret_skipped, mode_t * ret_type)
  426. {
  427. mode_t type;
  428. switch (*s)
  429. {
  430. case 'd':
  431. type = S_IFDIR;
  432. break;
  433. case 'b':
  434. type = S_IFBLK;
  435. break;
  436. case 'c':
  437. type = S_IFCHR;
  438. break;
  439. case 'l':
  440. type = S_IFLNK;
  441. break;
  442. #ifdef S_IFSOCK
  443. case 's':
  444. type = S_IFSOCK;
  445. break;
  446. #else
  447. case 's':
  448. type = S_IFIFO;
  449. break;
  450. #endif
  451. #ifdef S_IFDOOR /* Solaris door */
  452. case 'D':
  453. type = S_IFDOOR;
  454. break;
  455. #else
  456. case 'D':
  457. type = S_IFIFO;
  458. break;
  459. #endif
  460. case 'p':
  461. type = S_IFIFO;
  462. break;
  463. #ifdef S_IFNAM /* Special named files */
  464. case 'n':
  465. type = S_IFNAM;
  466. break;
  467. #else
  468. case 'n':
  469. type = S_IFREG;
  470. break;
  471. #endif
  472. case 'm': /* Don't know what these are :-) */
  473. case '-':
  474. case '?':
  475. type = S_IFREG;
  476. break;
  477. default:
  478. return FALSE;
  479. }
  480. *ret_type = type;
  481. *ret_skipped = 1;
  482. return TRUE;
  483. }
  484. /* --------------------------------------------------------------------------------------------- */
  485. gboolean
  486. vfs_parse_fileperms (const char *s, size_t * ret_skipped, mode_t * ret_perms)
  487. {
  488. const char *p;
  489. mode_t perms;
  490. p = s;
  491. perms = 0;
  492. switch (*p++)
  493. {
  494. case '-':
  495. break;
  496. case 'r':
  497. perms |= S_IRUSR;
  498. break;
  499. default:
  500. return FALSE;
  501. }
  502. switch (*p++)
  503. {
  504. case '-':
  505. break;
  506. case 'w':
  507. perms |= S_IWUSR;
  508. break;
  509. default:
  510. return FALSE;
  511. }
  512. switch (*p++)
  513. {
  514. case '-':
  515. break;
  516. case 'S':
  517. perms |= S_ISUID;
  518. break;
  519. case 's':
  520. perms |= S_IXUSR | S_ISUID;
  521. break;
  522. case 'x':
  523. perms |= S_IXUSR;
  524. break;
  525. default:
  526. return FALSE;
  527. }
  528. switch (*p++)
  529. {
  530. case '-':
  531. break;
  532. case 'r':
  533. perms |= S_IRGRP;
  534. break;
  535. default:
  536. return FALSE;
  537. }
  538. switch (*p++)
  539. {
  540. case '-':
  541. break;
  542. case 'w':
  543. perms |= S_IWGRP;
  544. break;
  545. default:
  546. return FALSE;
  547. }
  548. switch (*p++)
  549. {
  550. case '-':
  551. break;
  552. case 'S':
  553. perms |= S_ISGID;
  554. break;
  555. case 'l':
  556. perms |= S_ISGID;
  557. break; /* found on Solaris */
  558. case 's':
  559. perms |= S_IXGRP | S_ISGID;
  560. break;
  561. case 'x':
  562. perms |= S_IXGRP;
  563. break;
  564. default:
  565. return FALSE;
  566. }
  567. switch (*p++)
  568. {
  569. case '-':
  570. break;
  571. case 'r':
  572. perms |= S_IROTH;
  573. break;
  574. default:
  575. return FALSE;
  576. }
  577. switch (*p++)
  578. {
  579. case '-':
  580. break;
  581. case 'w':
  582. perms |= S_IWOTH;
  583. break;
  584. default:
  585. return FALSE;
  586. }
  587. switch (*p++)
  588. {
  589. case '-':
  590. break;
  591. case 'T':
  592. perms |= S_ISVTX;
  593. break;
  594. case 't':
  595. perms |= S_IXOTH | S_ISVTX;
  596. break;
  597. case 'x':
  598. perms |= S_IXOTH;
  599. break;
  600. default:
  601. return FALSE;
  602. }
  603. if (*p == '+')
  604. { /* ACLs on Solaris, HP-UX and others */
  605. p++;
  606. }
  607. *ret_skipped = p - s;
  608. *ret_perms = perms;
  609. return TRUE;
  610. }
  611. /* --------------------------------------------------------------------------------------------- */
  612. gboolean
  613. vfs_parse_filemode (const char *s, size_t * ret_skipped, mode_t * ret_mode)
  614. {
  615. const char *p;
  616. mode_t type, perms;
  617. size_t skipped;
  618. p = s;
  619. if (!vfs_parse_filetype (p, &skipped, &type))
  620. return FALSE;
  621. p += skipped;
  622. if (!vfs_parse_fileperms (p, &skipped, &perms))
  623. return FALSE;
  624. p += skipped;
  625. *ret_skipped = p - s;
  626. *ret_mode = type | perms;
  627. return TRUE;
  628. }
  629. /* --------------------------------------------------------------------------------------------- */
  630. gboolean
  631. vfs_parse_raw_filemode (const char *s, size_t * ret_skipped, mode_t * ret_mode)
  632. {
  633. const char *p;
  634. mode_t remote_type = 0, local_type, perms = 0;
  635. p = s;
  636. /* isoctal */
  637. while (*p >= '0' && *p <= '7')
  638. {
  639. perms *= 010;
  640. perms += (*p - '0');
  641. ++p;
  642. }
  643. if (*p++ != ' ')
  644. return FALSE;
  645. while (*p >= '0' && *p <= '7')
  646. {
  647. remote_type *= 010;
  648. remote_type += (*p - '0');
  649. ++p;
  650. }
  651. if (*p++ != ' ')
  652. return FALSE;
  653. /* generated with:
  654. $ perl -e 'use Fcntl ":mode";
  655. my @modes = (S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFREG);
  656. foreach $t (@modes) { printf ("%o\n", $t); };'
  657. TODO: S_IFDOOR, S_IFIFO, S_IFSOCK (if supported by os)
  658. (see vfs_parse_filetype)
  659. */
  660. switch (remote_type)
  661. {
  662. case 020000:
  663. local_type = S_IFCHR;
  664. break;
  665. case 040000:
  666. local_type = S_IFDIR;
  667. break;
  668. case 060000:
  669. local_type = S_IFBLK;
  670. break;
  671. case 0120000:
  672. local_type = S_IFLNK;
  673. break;
  674. case 0100000:
  675. default: /* don't know what is it */
  676. local_type = S_IFREG;
  677. break;
  678. }
  679. *ret_skipped = p - s;
  680. *ret_mode = local_type | perms;
  681. return TRUE;
  682. }
  683. /* --------------------------------------------------------------------------------------------- */
  684. /** This function parses from idx in the columns[] array */
  685. int
  686. vfs_parse_filedate (int idx, time_t * t)
  687. {
  688. char *p;
  689. struct tm tim;
  690. int d[3];
  691. int got_year = 0;
  692. int l10n = 0; /* Locale's abbreviated month name */
  693. time_t current_time;
  694. struct tm *local_time;
  695. /* Let's setup default time values */
  696. current_time = time (NULL);
  697. local_time = localtime (&current_time);
  698. tim.tm_mday = local_time->tm_mday;
  699. tim.tm_mon = local_time->tm_mon;
  700. tim.tm_year = local_time->tm_year;
  701. tim.tm_hour = 0;
  702. tim.tm_min = 0;
  703. tim.tm_sec = 0;
  704. tim.tm_isdst = -1; /* Let mktime() try to guess correct dst offset */
  705. p = columns[idx++];
  706. /* We eat weekday name in case of extfs */
  707. if (is_week (p, &tim))
  708. p = columns[idx++];
  709. /* Month name */
  710. if (is_month (p, &tim))
  711. {
  712. /* And we expect, it followed by day number */
  713. if (is_num (idx))
  714. tim.tm_mday = (int) atol (columns[idx++]);
  715. else
  716. return 0; /* No day */
  717. }
  718. else
  719. {
  720. /* We expect:
  721. 3 fields max or we'll see oddities with certain file names.
  722. So both year and time is not allowed.
  723. Mon DD hh:mm[:ss]
  724. Mon DD YYYY
  725. But in case of extfs we allow these date formats:
  726. MM-DD-YY hh:mm[:ss]
  727. where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
  728. YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
  729. /* Special case with MM-DD-YY or MM-DD-YYYY */
  730. if (is_dos_date (p))
  731. {
  732. p[2] = p[5] = '-';
  733. if (sscanf (p, "%2d-%2d-%d", &d[0], &d[1], &d[2]) == 3)
  734. {
  735. /* Months are zero based */
  736. if (d[0] > 0)
  737. d[0]--;
  738. if (d[2] > 1900)
  739. {
  740. d[2] -= 1900;
  741. }
  742. else
  743. {
  744. /* Y2K madness */
  745. if (d[2] < 70)
  746. d[2] += 100;
  747. }
  748. tim.tm_mon = d[0];
  749. tim.tm_mday = d[1];
  750. tim.tm_year = d[2];
  751. got_year = 1;
  752. }
  753. else
  754. return 0; /* sscanf failed */
  755. }
  756. else
  757. {
  758. /* Locale's abbreviated month name followed by day number */
  759. if (is_localized_month (p) && (is_num (idx++)))
  760. l10n = 1;
  761. else
  762. return 0; /* unsupported format */
  763. }
  764. }
  765. /* Here we expect to find time or year */
  766. if (is_num (idx) && (is_time (columns[idx], &tim) || (got_year = is_year (columns[idx], &tim))))
  767. idx++;
  768. else
  769. return 0; /* Neither time nor date */
  770. /*
  771. * If the date is less than 6 months in the past, it is shown without year
  772. * other dates in the past or future are shown with year but without time
  773. * This does not check for years before 1900 ... I don't know, how
  774. * to represent them at all
  775. */
  776. if (!got_year && local_time->tm_mon < 6
  777. && local_time->tm_mon < tim.tm_mon && tim.tm_mon - local_time->tm_mon >= 6)
  778. tim.tm_year--;
  779. *t = mktime (&tim);
  780. if (l10n || (*t < 0))
  781. *t = 0;
  782. return idx;
  783. }
  784. /* --------------------------------------------------------------------------------------------- */
  785. int
  786. vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linkname)
  787. {
  788. int idx, idx2, num_cols;
  789. int i;
  790. char *p_copy = NULL;
  791. char *t = NULL;
  792. const char *line = p;
  793. size_t skipped;
  794. if (strncmp (p, "total", 5) == 0)
  795. return 0;
  796. if (!vfs_parse_filetype (p, &skipped, &s->st_mode))
  797. goto error;
  798. p += skipped;
  799. if (*p == ' ') /* Notwell 4 */
  800. p++;
  801. if (*p == '[')
  802. {
  803. if (strlen (p) <= 8 || p[8] != ']')
  804. goto error;
  805. /* Should parse here the Notwell permissions :) */
  806. if (S_ISDIR (s->st_mode))
  807. s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
  808. else
  809. s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
  810. p += 9;
  811. }
  812. else
  813. {
  814. size_t lc_skipped;
  815. mode_t perms;
  816. if (!vfs_parse_fileperms (p, &lc_skipped, &perms))
  817. goto error;
  818. p += lc_skipped;
  819. s->st_mode |= perms;
  820. }
  821. p_copy = g_strdup (p);
  822. num_cols = vfs_split_text (p_copy);
  823. s->st_nlink = atol (columns[0]);
  824. if (s->st_nlink <= 0)
  825. goto error;
  826. if (!is_num (1))
  827. s->st_uid = vfs_finduid (columns[1]);
  828. else
  829. s->st_uid = (uid_t) atol (columns[1]);
  830. /* Mhm, the ls -lg did not produce a group field */
  831. for (idx = 3; idx <= 5; idx++)
  832. if (is_month (columns[idx], NULL) || is_week (columns[idx], NULL)
  833. || is_dos_date (columns[idx]) || is_localized_month (columns[idx]))
  834. break;
  835. if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
  836. goto error;
  837. /* We don't have gid */
  838. if (idx == 3 || (idx == 4 && (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode))))
  839. idx2 = 2;
  840. else
  841. {
  842. /* We have gid field */
  843. if (is_num (2))
  844. s->st_gid = (gid_t) atol (columns[2]);
  845. else
  846. s->st_gid = vfs_findgid (columns[2]);
  847. idx2 = 3;
  848. }
  849. /* This is device */
  850. if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode))
  851. {
  852. int maj, min;
  853. /* Corner case: there is no whitespace(s) between maj & min */
  854. if (!is_num (idx2) && idx2 == 2)
  855. {
  856. if (!is_num (++idx2) || sscanf (columns[idx2], " %d,%d", &maj, &min) != 2)
  857. goto error;
  858. }
  859. else
  860. {
  861. if (!is_num (idx2) || sscanf (columns[idx2], " %d,", &maj) != 1)
  862. goto error;
  863. if (!is_num (++idx2) || sscanf (columns[idx2], " %d", &min) != 1)
  864. goto error;
  865. }
  866. #ifdef HAVE_STRUCT_STAT_ST_RDEV
  867. s->st_rdev = makedev (maj, min);
  868. #endif
  869. s->st_size = 0;
  870. }
  871. else
  872. {
  873. /* Common file size */
  874. if (!is_num (idx2))
  875. goto error;
  876. #ifdef HAVE_ATOLL
  877. s->st_size = (off_t) atoll (columns[idx2]);
  878. #else
  879. s->st_size = (off_t) atof (columns[idx2]);
  880. #endif
  881. #ifdef HAVE_STRUCT_STAT_ST_RDEV
  882. s->st_rdev = 0;
  883. #endif
  884. }
  885. idx = vfs_parse_filedate (idx, &s->st_mtime);
  886. if (!idx)
  887. goto error;
  888. /* Use resulting time value */
  889. s->st_atime = s->st_ctime = s->st_mtime;
  890. /* s->st_dev and s->st_ino must be initialized by vfs_s_new_inode () */
  891. #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
  892. s->st_blksize = 512;
  893. #endif
  894. #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
  895. s->st_blocks = (s->st_size + 511) / 512;
  896. #endif
  897. for (i = idx + 1, idx2 = 0; i < num_cols; i++)
  898. if (strcmp (columns[i], "->") == 0)
  899. {
  900. idx2 = i;
  901. break;
  902. }
  903. if (((S_ISLNK (s->st_mode) || (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
  904. && idx2)
  905. {
  906. if (filename)
  907. {
  908. *filename = g_strndup (p + column_ptr[idx], column_ptr[idx2] - column_ptr[idx] - 1);
  909. }
  910. if (linkname)
  911. {
  912. t = g_strdup (p + column_ptr[idx2 + 1]);
  913. *linkname = t;
  914. }
  915. }
  916. else
  917. {
  918. /* Extract the filename from the string copy, not from the columns
  919. * this way we have a chance of entering hidden directories like ". ."
  920. */
  921. if (filename)
  922. {
  923. /*
  924. * filename = g_strdup (columns [idx++]);
  925. */
  926. t = g_strdup (p + column_ptr[idx]);
  927. *filename = t;
  928. }
  929. if (linkname)
  930. *linkname = NULL;
  931. }
  932. if (t)
  933. {
  934. int p2 = strlen (t);
  935. if ((--p2 > 0) && (t[p2] == '\r' || t[p2] == '\n'))
  936. t[p2] = 0;
  937. if ((--p2 > 0) && (t[p2] == '\r' || t[p2] == '\n'))
  938. t[p2] = 0;
  939. }
  940. g_free (p_copy);
  941. return 1;
  942. error:
  943. {
  944. static int errorcount = 0;
  945. if (++errorcount < 5)
  946. {
  947. message (D_ERROR, _("Cannot parse:"), "%s", (p_copy && *p_copy) ? p_copy : line);
  948. }
  949. else if (errorcount == 5)
  950. message (D_ERROR, MSG_ERROR, _("More parsing errors will be ignored."));
  951. }
  952. g_free (p_copy);
  953. return 0;
  954. }
  955. /* --------------------------------------------------------------------------------------------- */
  956. void
  957. vfs_die (const char *m)
  958. {
  959. message (D_ERROR, _("Internal error:"), "%s", m);
  960. exit (EXIT_FAILURE);
  961. }
  962. /* --------------------------------------------------------------------------------------------- */
  963. char *
  964. vfs_get_password (const char *msg)
  965. {
  966. return input_dialog (msg, _("Password:"), MC_HISTORY_VFS_PASSWORD, INPUT_PASSWORD);
  967. }
  968. /* --------------------------------------------------------------------------------------------- */