vfs.c 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863
  1. /* Virtual File System switch code
  2. Copyright (C) 1995 The Free Software Foundation
  3. Written by: 1995 Miguel de Icaza
  4. 1995 Jakub Jelinek
  5. 1998 Pavel Machek
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU Library General Public License
  8. as published by the Free Software Foundation; either version 2 of
  9. the License, or (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU Library General Public License for more details.
  14. You should have received a copy of the GNU Library General Public
  15. License along with this program; if not, write to the Free Software
  16. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  17. /* Warning: funtions like extfs_lstat() have right to destroy any
  18. * strings you pass to them. This is acutally ok as you g_strdup what
  19. * you are passing to them, anyway; still, beware. */
  20. /* Namespace: exports *many* functions with vfs_ prefix; exports
  21. parse_ls_lga and friends which do not have that prefix. */
  22. #include <config.h>
  23. #ifndef NO_SYSLOG_H
  24. # include <syslog.h>
  25. #endif
  26. #include <stdio.h>
  27. #include <stdlib.h> /* For atol() */
  28. #include <stdarg.h>
  29. #include <string.h>
  30. #include <errno.h>
  31. #include <sys/types.h>
  32. #include <signal.h>
  33. #include <ctype.h> /* is_digit() */
  34. #include "utilvfs.h"
  35. #include "../src/panel.h" /* get_current_panel() */
  36. #include "../src/layout.h" /* get_current_type() */
  37. #include "../src/wtools.h" /* input_dialog() */
  38. #include "vfs.h"
  39. #include "extfs.h" /* FIXME: we should not know anything about our modules */
  40. #ifdef USE_NETCODE
  41. # include "tcputil.h"
  42. #endif
  43. int vfs_timeout = 60; /* VFS timeout in seconds */
  44. /* They keep track of the current directory */
  45. static struct vfs_class *current_vfs;
  46. static char *current_dir;
  47. /*
  48. * FIXME: this is broken. It depends on mc not crossing border on month!
  49. */
  50. static int current_mday;
  51. static int current_mon;
  52. static int current_year;
  53. /* FIXME: Open files managed by the vfs layer, should be dynamical */
  54. #define MAX_VFS_FILES 100
  55. static struct {
  56. void *fs_info;
  57. struct vfs_class *operations;
  58. } vfs_file_table [MAX_VFS_FILES];
  59. static struct vfs_class *localfs_class;
  60. static int
  61. get_bucket (void)
  62. {
  63. int i;
  64. /* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
  65. for (i = 3; i < MAX_VFS_FILES; i++){
  66. if (!vfs_file_table [i].fs_info)
  67. return i;
  68. }
  69. vfs_die ("No more virtual file handles");
  70. return 0;
  71. }
  72. static struct vfs_class *vfs_list;
  73. int
  74. vfs_register_class (struct vfs_class *vfs)
  75. {
  76. if (vfs->init) /* vfs has own initialization function */
  77. if (!(*vfs->init)(vfs)) /* but it failed */
  78. return 0;
  79. vfs->next = vfs_list;
  80. vfs_list = vfs;
  81. return 1;
  82. }
  83. /* Return VFS class for the given prefix */
  84. static struct vfs_class *
  85. vfs_prefix_to_class (char *prefix)
  86. {
  87. struct vfs_class *vfs;
  88. for (vfs = vfs_list; vfs; vfs = vfs->next) {
  89. if (vfs->which) {
  90. if ((*vfs->which) (vfs, prefix) == -1)
  91. continue;
  92. return vfs;
  93. }
  94. if (vfs->prefix
  95. && !strncmp (prefix, vfs->prefix, strlen (vfs->prefix)))
  96. return vfs;
  97. }
  98. return NULL;
  99. }
  100. /* Strip known vfs suffixes from a filename (possible improvement: strip
  101. suffix from last path component).
  102. Returns a malloced string which has to be freed. */
  103. char *
  104. vfs_strip_suffix_from_filename (const char *filename)
  105. {
  106. struct vfs_class *vfs;
  107. char *semi;
  108. char *p;
  109. if (!filename)
  110. vfs_die ("vfs_strip_suffix_from_path got NULL: impossible");
  111. p = g_strdup (filename);
  112. if (!(semi = strrchr (p, '#')))
  113. return p;
  114. /* Avoid last class (localfs) that would accept any prefix */
  115. for (vfs = vfs_list; vfs->next; vfs = vfs->next) {
  116. if (vfs->which) {
  117. if ((*vfs->which) (vfs, semi + 1) == -1)
  118. continue;
  119. *semi = '\0'; /* Found valid suffix */
  120. return p;
  121. }
  122. if (vfs->prefix
  123. && !strncmp (semi + 1, vfs->prefix, strlen (vfs->prefix))) {
  124. *semi = '\0'; /* Found valid suffix */
  125. return p;
  126. }
  127. }
  128. return p;
  129. }
  130. static int
  131. path_magic (const char *path)
  132. {
  133. struct stat buf;
  134. if (!stat(path, &buf))
  135. return 0;
  136. return 1;
  137. }
  138. /*
  139. * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
  140. * What is left in path is p1. You still want to g_free(path), you DON'T
  141. * want to free neither *inpath nor *op
  142. */
  143. struct vfs_class *
  144. vfs_split (const char *path, char **inpath, char **op)
  145. {
  146. char *semi;
  147. char *slash;
  148. struct vfs_class *ret;
  149. if (!path)
  150. vfs_die("Cannot split NULL");
  151. semi = strrchr (path, '#');
  152. if (!semi || !path_magic(path))
  153. return NULL;
  154. slash = strchr (semi, PATH_SEP);
  155. *semi = 0;
  156. if (op)
  157. *op = NULL;
  158. if (inpath)
  159. *inpath = NULL;
  160. if (slash)
  161. *slash = 0;
  162. if ((ret = vfs_prefix_to_class (semi+1))){
  163. if (op)
  164. *op = semi + 1;
  165. if (inpath)
  166. *inpath = slash ? slash + 1 : NULL;
  167. return ret;
  168. }
  169. if (slash)
  170. *slash = PATH_SEP;
  171. ret = vfs_split (path, inpath, op);
  172. *semi = '#';
  173. return ret;
  174. }
  175. static struct vfs_class *
  176. _vfs_get_class (const char *path)
  177. {
  178. char *semi;
  179. char *slash;
  180. struct vfs_class *ret;
  181. g_return_val_if_fail(path, NULL);
  182. semi = strrchr (path, '#');
  183. if (!semi || !path_magic (path))
  184. return NULL;
  185. slash = strchr (semi, PATH_SEP);
  186. *semi = 0;
  187. if (slash)
  188. *slash = 0;
  189. ret = vfs_prefix_to_class (semi+1);
  190. if (slash)
  191. *slash = PATH_SEP;
  192. if (!ret)
  193. ret = _vfs_get_class (path);
  194. *semi = '#';
  195. return ret;
  196. }
  197. struct vfs_class *
  198. vfs_get_class (const char *path)
  199. {
  200. struct vfs_class *vfs;
  201. vfs = _vfs_get_class(path);
  202. if (!vfs)
  203. vfs = localfs_class;
  204. return vfs;
  205. }
  206. static struct vfs_stamping *stamps;
  207. /*
  208. * Returns the number of seconds remaining to the vfs timeout
  209. *
  210. * FIXME: currently this is set to 10 seconds. We should compute this.
  211. */
  212. int
  213. vfs_timeouts ()
  214. {
  215. return stamps ? 10 : 0;
  216. }
  217. static void
  218. vfs_addstamp (struct vfs_class *v, vfsid id, struct vfs_stamping *parent)
  219. {
  220. if (!(v->flags & VFSF_LOCAL) && id != (vfsid)-1){
  221. struct vfs_stamping *stamp;
  222. struct vfs_stamping *last_stamp = NULL;
  223. for (stamp = stamps; stamp != NULL; stamp = stamp->next) {
  224. if (stamp->v == v && stamp->id == id){
  225. gettimeofday(&(stamp->time), NULL);
  226. return;
  227. }
  228. last_stamp = stamp;
  229. }
  230. stamp = g_new (struct vfs_stamping, 1);
  231. stamp->v = v;
  232. stamp->id = id;
  233. if (parent){
  234. struct vfs_stamping *st = stamp;
  235. while (parent){
  236. st->parent = g_new (struct vfs_stamping, 1);
  237. *st->parent = *parent;
  238. parent = parent->parent;
  239. st = st->parent;
  240. }
  241. st->parent = 0;
  242. }
  243. else
  244. stamp->parent = 0;
  245. gettimeofday (&(stamp->time), NULL);
  246. stamp->next = 0;
  247. if (stamps) {
  248. /* Add to the end */
  249. last_stamp->next = stamp;
  250. } else {
  251. /* Add first element */
  252. stamps = stamp;
  253. }
  254. }
  255. }
  256. void
  257. vfs_stamp (struct vfs_class *v, vfsid id)
  258. {
  259. struct vfs_stamping *stamp;
  260. for (stamp = stamps; stamp != NULL; stamp = stamp->next)
  261. if (stamp->v == v && stamp->id == id){
  262. gettimeofday (&(stamp->time), NULL);
  263. if (stamp->parent != NULL)
  264. vfs_stamp (stamp->parent->v, stamp->parent->id);
  265. return;
  266. }
  267. }
  268. static void
  269. vfs_rm_parents (struct vfs_stamping *stamp)
  270. {
  271. struct vfs_stamping *parent;
  272. while (stamp) {
  273. parent = stamp->parent;
  274. g_free (stamp);
  275. stamp = parent;
  276. }
  277. }
  278. void
  279. vfs_rmstamp (struct vfs_class *v, vfsid id, int removeparents)
  280. {
  281. struct vfs_stamping *stamp, *st1;
  282. for (stamp = stamps, st1 = NULL; stamp != NULL; st1 = stamp, stamp = stamp->next)
  283. if (stamp->v == v && stamp->id == id){
  284. if (stamp->parent != NULL){
  285. if (removeparents)
  286. vfs_rmstamp (stamp->parent->v, stamp->parent->id, 1);
  287. vfs_rm_parents (stamp->parent);
  288. }
  289. if (st1 == NULL){
  290. stamps = stamp->next;
  291. } else {
  292. st1->next = stamp->next;
  293. }
  294. g_free (stamp);
  295. return;
  296. }
  297. }
  298. static int
  299. ferrno (struct vfs_class *vfs)
  300. {
  301. return vfs->ferrno ? (*vfs->ferrno)(vfs) : E_UNKNOWN;
  302. /* Hope that error message is obscure enough ;-) */
  303. }
  304. int
  305. mc_open (const char *filename, int flags, ...)
  306. {
  307. int handle;
  308. int mode;
  309. void *info;
  310. va_list ap;
  311. char *file = vfs_canon (filename);
  312. struct vfs_class *vfs = vfs_get_class (file);
  313. /* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
  314. va_start (ap, flags);
  315. mode = va_arg (ap, int);
  316. va_end (ap);
  317. if (!vfs->open) {
  318. g_free (file);
  319. errno = -EOPNOTSUPP;
  320. return -1;
  321. }
  322. info = (*vfs->open) (vfs, file, flags, mode); /* open must be supported */
  323. g_free (file);
  324. if (!info){
  325. errno = ferrno (vfs);
  326. return -1;
  327. }
  328. handle = get_bucket ();
  329. vfs_file_table [handle].fs_info = info;
  330. vfs_file_table [handle].operations = vfs;
  331. return handle;
  332. }
  333. #define vfs_op(handle) vfs_file_table [handle].operations
  334. #define vfs_info(handle) vfs_file_table [handle].fs_info
  335. #define vfs_free_bucket(handle) vfs_info(handle) = 0;
  336. #define MC_OP(name, inarg, callarg, pre, post) \
  337. int mc_##name inarg \
  338. { \
  339. struct vfs_class *vfs; \
  340. int result; \
  341. \
  342. pre \
  343. result = vfs->name ? (*vfs->name)callarg : -1; \
  344. post \
  345. if (result == -1) \
  346. errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
  347. return result; \
  348. }
  349. #define MC_NAMEOP(name, inarg, callarg) \
  350. MC_OP (name, inarg, callarg, path = vfs_canon (path); vfs = vfs_get_class (path);, g_free (path); )
  351. #define MC_HANDLEOP(name, inarg, callarg) \
  352. MC_OP (name, inarg, callarg, if (handle == -1) return -1; vfs = vfs_op (handle);, ;)
  353. MC_HANDLEOP(read, (int handle, char *buffer, int count), (vfs_info (handle), buffer, count) )
  354. int
  355. mc_ctl (int handle, int ctlop, void *arg)
  356. {
  357. struct vfs_class *vfs = vfs_op (handle);
  358. return vfs->ctl ? (*vfs->ctl)(vfs_info (handle), ctlop, arg) : 0;
  359. }
  360. int
  361. mc_setctl (char *path, int ctlop, void *arg)
  362. {
  363. struct vfs_class *vfs;
  364. int result;
  365. if (!path)
  366. vfs_die("You don't want to pass NULL to mc_setctl.");
  367. path = vfs_canon (path);
  368. vfs = vfs_get_class (path);
  369. result = vfs->setctl ? (*vfs->setctl)(vfs, path, ctlop, arg) : 0;
  370. g_free (path);
  371. return result;
  372. }
  373. int
  374. mc_close (int handle)
  375. {
  376. struct vfs_class *vfs;
  377. int result;
  378. if (handle == -1 || !vfs_info (handle))
  379. return -1;
  380. vfs = vfs_op (handle);
  381. if (handle < 3)
  382. return close (handle);
  383. if (!vfs->close)
  384. vfs_die ("VFS must support close.\n");
  385. result = (*vfs->close)(vfs_info (handle));
  386. vfs_free_bucket (handle);
  387. if (result == -1)
  388. errno = ferrno (vfs);
  389. return result;
  390. }
  391. DIR *
  392. mc_opendir (char *dirname)
  393. {
  394. int handle, *handlep;
  395. void *info;
  396. struct vfs_class *vfs;
  397. dirname = vfs_canon (dirname);
  398. vfs = vfs_get_class (dirname);
  399. info = vfs->opendir ? (*vfs->opendir)(vfs, dirname) : NULL;
  400. g_free (dirname);
  401. if (!info){
  402. errno = vfs->opendir ? ferrno (vfs) : E_NOTSUPP;
  403. return NULL;
  404. }
  405. handle = get_bucket ();
  406. vfs_file_table [handle].fs_info = info;
  407. vfs_file_table [handle].operations = vfs;
  408. handlep = g_new (int, 1);
  409. *handlep = handle;
  410. return (DIR *) handlep;
  411. }
  412. #define MC_DIROP(name, type, onerr ) \
  413. type mc_##name (DIR *dirp) \
  414. { \
  415. int handle; \
  416. struct vfs_class *vfs; \
  417. type result; \
  418. \
  419. if (!dirp){ \
  420. errno = EFAULT; \
  421. return onerr; \
  422. } \
  423. handle = *(int *) dirp; \
  424. vfs = vfs_op (handle); \
  425. result = vfs->name ? (*vfs->name) (vfs_info (handle)) : onerr; \
  426. if (result == onerr) \
  427. errno = vfs->name ? ferrno(vfs) : E_NOTSUPP; \
  428. return result; \
  429. }
  430. MC_DIROP (readdir, struct dirent *, NULL)
  431. int
  432. mc_closedir (DIR *dirp)
  433. {
  434. int handle = *(int *) dirp;
  435. struct vfs_class *vfs = vfs_op (handle);
  436. int result;
  437. result = vfs->closedir ? (*vfs->closedir)(vfs_info (handle)) : -1;
  438. vfs_free_bucket (handle);
  439. g_free (dirp);
  440. return result;
  441. }
  442. int mc_stat (const char *filename, struct stat *buf) {
  443. struct vfs_class *vfs;
  444. int result;
  445. char *path;
  446. path = vfs_canon (filename); vfs = vfs_get_class (path);
  447. result = vfs->stat ? (*vfs->stat) (vfs, path, buf) : -1;
  448. g_free (path);
  449. if (result == -1)
  450. errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
  451. return result;
  452. }
  453. int mc_lstat (const char *filename, struct stat *buf) {
  454. struct vfs_class *vfs;
  455. int result;
  456. char *path;
  457. path = vfs_canon (filename); vfs = vfs_get_class (path);
  458. result = vfs->lstat ? (*vfs->lstat) (vfs, path, buf) : -1;
  459. g_free (path);
  460. if (result == -1)
  461. errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
  462. return result;
  463. }
  464. int mc_fstat (int handle, struct stat *buf) {
  465. struct vfs_class *vfs;
  466. int result;
  467. if (handle == -1)
  468. return -1;
  469. vfs = vfs_op (handle);
  470. result = vfs->fstat ? (*vfs->fstat) (vfs_info (handle), buf) : -1;
  471. if (result == -1)
  472. errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
  473. return result;
  474. }
  475. /*
  476. * Return current directory. If it's local, reread the current directory
  477. * from the OS. You must g_strdup whatever this function returns.
  478. */
  479. static const char *
  480. _vfs_get_cwd (void)
  481. {
  482. char *p;
  483. struct stat my_stat, my_stat2;
  484. if (!_vfs_get_class (current_dir)) {
  485. p = g_get_current_dir ();
  486. if (!p) /* One of the directories in the path is not readable */
  487. return current_dir;
  488. /* Otherwise check if it is O.K. to use the current_dir */
  489. if (!cd_symlinks || mc_stat (p, &my_stat)
  490. || mc_stat (current_dir, &my_stat2)
  491. || my_stat.st_ino != my_stat2.st_ino
  492. || my_stat.st_dev != my_stat2.st_dev) {
  493. g_free (current_dir);
  494. current_dir = p;
  495. return p;
  496. } /* Otherwise we return current_dir below */
  497. g_free (p);
  498. }
  499. return current_dir;
  500. }
  501. static void
  502. vfs_setup_wd (void)
  503. {
  504. current_dir = g_strdup (PATH_SEP_STR);
  505. _vfs_get_cwd ();
  506. if (strlen (current_dir) > MC_MAXPATHLEN - 2)
  507. vfs_die ("Current dir too long.\n");
  508. current_vfs = vfs_get_class (current_dir);
  509. }
  510. /*
  511. * Return current directory. If it's local, reread the current directory
  512. * from the OS. Put directory to the provided buffer.
  513. */
  514. char *
  515. mc_get_current_wd (char *buffer, int size)
  516. {
  517. const char *cwd = _vfs_get_cwd ();
  518. g_strlcpy (buffer, cwd, size - 1);
  519. buffer[size - 1] = 0;
  520. return buffer;
  521. }
  522. /*
  523. * Return current directory without any OS calls.
  524. */
  525. char *
  526. vfs_get_current_dir (void)
  527. {
  528. return current_dir;
  529. }
  530. MC_NAMEOP (chmod, (char *path, int mode), (vfs, path, mode))
  531. MC_NAMEOP (chown, (char *path, int owner, int group), (vfs, path, owner, group))
  532. MC_NAMEOP (utime, (char *path, struct utimbuf *times), (vfs, path, times))
  533. MC_NAMEOP (readlink, (char *path, char *buf, int bufsiz), (vfs, path, buf, bufsiz))
  534. MC_NAMEOP (unlink, (char *path), (vfs, path))
  535. MC_NAMEOP (symlink, (char *name1, char *path), (vfs, name1, path))
  536. #define MC_RENAMEOP(name) \
  537. int mc_##name (const char *fname1, const char *fname2) \
  538. { \
  539. struct vfs_class *vfs; \
  540. int result; \
  541. \
  542. char *name2, *name1 = vfs_canon (fname1); \
  543. vfs = vfs_get_class (name1); \
  544. name2 = vfs_canon (fname2); \
  545. if (vfs != vfs_get_class (name2)){ \
  546. errno = EXDEV; \
  547. g_free (name1); \
  548. g_free (name2); \
  549. return -1; \
  550. } \
  551. \
  552. result = vfs->name ? (*vfs->name)(vfs, name1, name2) : -1; \
  553. g_free (name1); \
  554. g_free (name2); \
  555. if (result == -1) \
  556. errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
  557. return result; \
  558. }
  559. MC_RENAMEOP (link)
  560. MC_RENAMEOP (rename)
  561. MC_HANDLEOP (write, (int handle, char *buf, int nbyte), (vfs_info (handle), buf, nbyte))
  562. off_t mc_lseek (int fd, off_t offset, int whence)
  563. {
  564. struct vfs_class *vfs;
  565. int result;
  566. if (fd == -1)
  567. return -1;
  568. vfs = vfs_op (fd);
  569. result = vfs->lseek ? (*vfs->lseek)(vfs_info (fd), offset, whence) : -1;
  570. if (result == -1)
  571. errno = vfs->lseek ? ferrno (vfs) : E_NOTSUPP;
  572. return result;
  573. }
  574. /*
  575. * remove //, /./ and /../, local should point to big enough buffer
  576. */
  577. #define ISSLASH(a) (!a || (a == '/'))
  578. char *
  579. vfs_canon (const char *path)
  580. {
  581. if (!path)
  582. vfs_die("Cannot canonicalize NULL");
  583. /* Relative to current directory */
  584. if (*path != PATH_SEP){
  585. char *local, *result;
  586. local = concat_dir_and_file (current_dir, path);
  587. result = vfs_canon (local);
  588. g_free (local);
  589. return result;
  590. }
  591. /*
  592. * So we have path of following form:
  593. * /p1/p2#op/.././././p3#op/p4. Good luck.
  594. */
  595. {
  596. char *result = g_strdup (path);
  597. canonicalize_pathname (result);
  598. return result;
  599. }
  600. }
  601. static vfsid
  602. vfs_ncs_getid (struct vfs_class *nvfs, const char *dir, struct vfs_stamping **par)
  603. {
  604. vfsid nvfsid;
  605. char *dir1;
  606. dir1 = concat_dir_and_file (dir, "");
  607. nvfsid = (*nvfs->getid) (nvfs, dir1, par);
  608. g_free (dir1);
  609. return nvfsid;
  610. }
  611. static int
  612. is_parent (struct vfs_class * nvfs, vfsid nvfsid, struct vfs_stamping *parent)
  613. {
  614. struct vfs_stamping *stamp;
  615. for (stamp = parent; stamp; stamp = stamp->parent)
  616. if (stamp->v == nvfs && stamp->id == nvfsid)
  617. break;
  618. return (stamp ? 1 : 0);
  619. }
  620. static void
  621. _vfs_add_noncurrent_stamps (struct vfs_class *oldvfs, vfsid oldvfsid,
  622. struct vfs_stamping *parent)
  623. {
  624. struct vfs_class *nvfs, *n2vfs, *n3vfs;
  625. vfsid nvfsid, n2vfsid, n3vfsid;
  626. struct vfs_stamping *par, *stamp;
  627. int f;
  628. /* FIXME: As soon as we convert to multiple panels, this stuff
  629. has to change. It works like this: We do not time out the
  630. vfs's which are current in any panel and on the other
  631. side we add the old directory with all its parents which
  632. are not in any panel (if we find such one, we stop adding
  633. parents to the time-outing structure. */
  634. /* There are three directories we have to take care of: current_dir,
  635. current_panel->cwd and other_panel->cwd. Athough most of the time either
  636. current_dir and current_panel->cwd or current_dir and other_panel->cwd are the
  637. same, it's possible that all three are different -- Norbert */
  638. if (!current_panel)
  639. return;
  640. nvfs = vfs_get_class (current_dir);
  641. nvfsid = vfs_ncs_getid (nvfs, current_dir, &par);
  642. vfs_rmstamp (nvfs, nvfsid, 1);
  643. f = is_parent (oldvfs, oldvfsid, par);
  644. vfs_rm_parents (par);
  645. if ((nvfs == oldvfs && nvfsid == oldvfsid) || oldvfsid == (vfsid *) - 1
  646. || f) {
  647. return;
  648. }
  649. if (get_current_type () == view_listing) {
  650. n2vfs = vfs_get_class (current_panel->cwd);
  651. n2vfsid = vfs_ncs_getid (n2vfs, current_panel->cwd, &par);
  652. f = is_parent (oldvfs, oldvfsid, par);
  653. vfs_rm_parents (par);
  654. if ((n2vfs == oldvfs && n2vfsid == oldvfsid) || f)
  655. return;
  656. } else {
  657. n2vfs = (struct vfs_class *) -1;
  658. n2vfsid = (vfsid) - 1;
  659. }
  660. if (get_other_type () == view_listing) {
  661. n3vfs = vfs_get_class (other_panel->cwd);
  662. n3vfsid = vfs_ncs_getid (n3vfs, other_panel->cwd, &par);
  663. f = is_parent (oldvfs, oldvfsid, par);
  664. vfs_rm_parents (par);
  665. if ((n3vfs == oldvfs && n3vfsid == oldvfsid) || f)
  666. return;
  667. } else {
  668. n3vfs = (struct vfs_class *) -1;
  669. n3vfsid = (vfsid) - 1;
  670. }
  671. if ((*oldvfs->nothingisopen) (oldvfsid)) {
  672. #if 0 /* need setctl for this */
  673. if (oldvfs == &vfs_extfs_ops
  674. && ((extfs_archive *) oldvfsid)->name == 0) {
  675. /* Free the resources immediatly when we leave a mtools fs
  676. ('cd a:') instead of waiting for the vfs-timeout */
  677. (oldvfs->free) (oldvfsid);
  678. } else
  679. #endif
  680. vfs_addstamp (oldvfs, oldvfsid, parent);
  681. for (stamp = parent; stamp != NULL; stamp = stamp->parent) {
  682. if ((stamp->v == nvfs && stamp->id == nvfsid)
  683. || (stamp->v == n2vfs && stamp->id == n2vfsid)
  684. || (stamp->v == n3vfs && stamp->id == n3vfsid)
  685. || stamp->id == (vfsid) - 1
  686. || !(*stamp->v->nothingisopen) (stamp->id))
  687. break;
  688. #if 0
  689. if (stamp->v == &vfs_extfs_ops
  690. && ((extfs_archive *) stamp->id)->name == 0) {
  691. (stamp->v->free) (stamp->id);
  692. vfs_rmstamp (stamp->v, stamp->id, 0);
  693. } else
  694. #endif
  695. vfs_addstamp (stamp->v, stamp->id, stamp->parent);
  696. }
  697. }
  698. }
  699. void
  700. vfs_add_noncurrent_stamps (struct vfs_class *oldvfs, vfsid oldvfsid,
  701. struct vfs_stamping *parent)
  702. {
  703. _vfs_add_noncurrent_stamps (oldvfs, oldvfsid, parent);
  704. vfs_rm_parents (parent);
  705. }
  706. static void
  707. vfs_stamp_path (char *path)
  708. {
  709. struct vfs_class *vfs;
  710. vfsid id;
  711. struct vfs_stamping *par, *stamp;
  712. vfs = vfs_get_class (path);
  713. id = vfs_ncs_getid (vfs, path, &par);
  714. vfs_addstamp (vfs, id, par);
  715. for (stamp = par; stamp != NULL; stamp = stamp->parent)
  716. vfs_addstamp (stamp->v, stamp->id, stamp->parent);
  717. vfs_rm_parents (par);
  718. }
  719. void
  720. vfs_add_current_stamps (void)
  721. {
  722. vfs_stamp_path (current_dir);
  723. if (current_panel) {
  724. if (get_current_type () == view_listing)
  725. vfs_stamp_path (current_panel->cwd);
  726. }
  727. if (other_panel) {
  728. if (get_other_type () == view_listing)
  729. vfs_stamp_path (other_panel->cwd);
  730. }
  731. }
  732. /*
  733. * VFS chdir.
  734. * Return 0 on success, -1 on failure.
  735. */
  736. int
  737. mc_chdir (char *path)
  738. {
  739. char *new_dir, *new_dir_copy;
  740. struct vfs_class *old_vfs, *new_vfs;
  741. vfsid old_vfsid;
  742. struct vfs_stamping *parent;
  743. int result;
  744. new_dir = vfs_canon (path);
  745. new_vfs = vfs_get_class (new_dir);
  746. if (!new_vfs->chdir)
  747. return -1;
  748. /* new_vfs->chdir can write to the second argument, use a copy */
  749. new_dir_copy = g_strdup (new_dir);
  750. result = (*new_vfs->chdir) (new_vfs, new_dir_copy);
  751. g_free (new_dir_copy);
  752. if (result == -1) {
  753. errno = ferrno (new_vfs);
  754. g_free (new_dir);
  755. return -1;
  756. }
  757. old_vfsid = vfs_ncs_getid (current_vfs, current_dir, &parent);
  758. old_vfs = current_vfs;
  759. /* Actually change directory */
  760. g_free (current_dir);
  761. current_dir = new_dir;
  762. current_vfs = new_vfs;
  763. /* This function uses the new current_dir implicitly */
  764. vfs_add_noncurrent_stamps (old_vfs, old_vfsid, parent);
  765. /* Sometimes we assume no trailing slash on cwd */
  766. if (*current_dir) {
  767. char *p;
  768. p = strchr (current_dir, 0) - 1;
  769. if (*p == PATH_SEP && p > current_dir)
  770. *p = 0;
  771. }
  772. return 0;
  773. }
  774. /* Return 1 is the current VFS class is local */
  775. int
  776. vfs_current_is_local (void)
  777. {
  778. return (current_vfs->flags & VFSF_LOCAL) != 0;
  779. }
  780. /* Return flags of the VFS class of the given filename */
  781. int
  782. vfs_file_class_flags (const char *filename)
  783. {
  784. struct vfs_class *vfs;
  785. char *fname;
  786. fname = vfs_canon (filename);
  787. vfs = vfs_get_class (fname);
  788. g_free (fname);
  789. return vfs->flags;
  790. }
  791. MC_NAMEOP (mkdir, (char *path, mode_t mode), (vfs, path, mode))
  792. MC_NAMEOP (rmdir, (char *path), (vfs, path))
  793. MC_NAMEOP (mknod, (char *path, int mode, int dev), (vfs, path, mode, dev))
  794. #ifdef HAVE_MMAP
  795. static struct mc_mmapping {
  796. caddr_t addr;
  797. void *vfs_info;
  798. struct vfs_class *vfs;
  799. struct mc_mmapping *next;
  800. } *mc_mmaparray = NULL;
  801. caddr_t
  802. mc_mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
  803. {
  804. struct vfs_class *vfs;
  805. caddr_t result;
  806. struct mc_mmapping *mcm;
  807. if (fd == -1)
  808. return (caddr_t) -1;
  809. vfs = vfs_op (fd);
  810. result = vfs->mmap ? (*vfs->mmap)(vfs, addr, len, prot, flags, vfs_info (fd), offset) : (caddr_t)-1;
  811. if (result == (caddr_t)-1){
  812. errno = ferrno (vfs);
  813. return (caddr_t)-1;
  814. }
  815. mcm =g_new (struct mc_mmapping, 1);
  816. mcm->addr = result;
  817. mcm->vfs_info = vfs_info (fd);
  818. mcm->vfs = vfs;
  819. mcm->next = mc_mmaparray;
  820. mc_mmaparray = mcm;
  821. return result;
  822. }
  823. int
  824. mc_munmap (caddr_t addr, size_t len)
  825. {
  826. struct mc_mmapping *mcm, *mcm2 = NULL;
  827. for (mcm = mc_mmaparray; mcm != NULL; mcm2 = mcm, mcm = mcm->next){
  828. if (mcm->addr == addr){
  829. if (mcm2 == NULL)
  830. mc_mmaparray = mcm->next;
  831. else
  832. mcm2->next = mcm->next;
  833. if (mcm->vfs->munmap)
  834. (*mcm->vfs->munmap)(mcm->vfs, addr, len, mcm->vfs_info);
  835. g_free (mcm);
  836. return 0;
  837. }
  838. }
  839. return -1;
  840. }
  841. #endif
  842. static char *
  843. mc_def_getlocalcopy (struct vfs_class *vfs, const char *filename)
  844. {
  845. char *tmp, *suffix;
  846. const char *basename;
  847. int fdin, fdout, i;
  848. char buffer[8192];
  849. struct stat mystat;
  850. fdin = mc_open (filename, O_RDONLY | O_LINEAR);
  851. if (fdin == -1)
  852. return NULL;
  853. /* retain original filename as a suffix for a temporary filename */
  854. basename = strrchr (filename, PATH_SEP);
  855. if (!basename)
  856. basename = filename;
  857. else
  858. basename++;
  859. suffix = g_strconcat ("-", basename, NULL);
  860. if ((fdout = mc_mkstemps (&tmp, "vfs", suffix)) == -1) {
  861. /* fallback for the case if the filename is too long */
  862. fdout = mc_mkstemps (&tmp, "vfs", NULL);
  863. }
  864. g_free (suffix);
  865. if (fdout == -1)
  866. goto fail;
  867. while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0) {
  868. if (write (fdout, buffer, i) != i)
  869. goto fail;
  870. }
  871. if (i == -1)
  872. goto fail;
  873. i = mc_close (fdin);
  874. fdin = -1;
  875. if (i == -1)
  876. goto fail;
  877. if (close (fdout) == -1)
  878. goto fail;
  879. if (mc_stat (filename, &mystat) != -1) {
  880. chmod (tmp, mystat.st_mode);
  881. }
  882. return tmp;
  883. fail:
  884. if (fdout)
  885. close (fdout);
  886. if (fdin)
  887. mc_close (fdin);
  888. g_free (tmp);
  889. return NULL;
  890. }
  891. char *
  892. mc_getlocalcopy (const char *pathname)
  893. {
  894. char *result;
  895. char *path = vfs_canon (pathname);
  896. struct vfs_class *vfs = vfs_get_class (path);
  897. result = vfs->getlocalcopy ? (*vfs->getlocalcopy)(vfs, path) :
  898. mc_def_getlocalcopy (vfs, path);
  899. g_free (path);
  900. if (!result)
  901. errno = ferrno (vfs);
  902. return result;
  903. }
  904. static int
  905. mc_def_ungetlocalcopy (struct vfs_class *vfs, const char *filename,
  906. const char *local, int has_changed)
  907. {
  908. int fdin = -1, fdout = -1, i;
  909. if (has_changed) {
  910. char buffer[8192];
  911. if (!vfs->write)
  912. goto failed;
  913. fdin = open (local, O_RDONLY);
  914. if (fdin == -1)
  915. goto failed;
  916. fdout = mc_open (filename, O_WRONLY | O_TRUNC);
  917. if (fdout == -1)
  918. goto failed;
  919. while ((i = read (fdin, buffer, sizeof (buffer))) > 0) {
  920. if (mc_write (fdout, buffer, i) != i)
  921. goto failed;
  922. }
  923. if (i == -1)
  924. goto failed;
  925. if (close (fdin) == -1) {
  926. fdin = -1;
  927. goto failed;
  928. }
  929. fdin = -1;
  930. if (mc_close (fdout) == -1) {
  931. fdout = -1;
  932. goto failed;
  933. }
  934. }
  935. unlink (local);
  936. return 0;
  937. failed:
  938. message (1, _("Changes to file lost"), filename);
  939. if (fdout != -1)
  940. mc_close (fdout);
  941. if (fdin != -1)
  942. close (fdin);
  943. unlink (local);
  944. return -1;
  945. }
  946. int
  947. mc_ungetlocalcopy (const char *pathname, char *local, int has_changed)
  948. {
  949. int return_value = 0;
  950. char *path = vfs_canon (pathname);
  951. struct vfs_class *vfs = vfs_get_class (path);
  952. return_value = vfs->ungetlocalcopy ?
  953. (*vfs->ungetlocalcopy)(vfs, path, local, has_changed) :
  954. mc_def_ungetlocalcopy (vfs, path, local, has_changed);
  955. g_free (path);
  956. g_free (local);
  957. return return_value;
  958. }
  959. /*
  960. * Hmm, as timeout is minute or so, do we need to care about usecs?
  961. */
  962. static inline int
  963. timeoutcmp (struct timeval *t1, struct timeval *t2)
  964. {
  965. return ((t1->tv_sec < t2->tv_sec)
  966. || ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec <= t2->tv_usec)));
  967. }
  968. /* This is called from timeout handler with now = 0, or can be called
  969. with now = 1 to force freeing all filesystems that are not in use */
  970. void
  971. vfs_expire (int now)
  972. {
  973. static int locked = 0;
  974. struct timeval time;
  975. struct vfs_stamping *stamp, *st;
  976. /* Avoid recursive invocation, e.g. when one of the free functions
  977. calls message */
  978. if (locked)
  979. return;
  980. locked = 1;
  981. gettimeofday (&time, NULL);
  982. time.tv_sec -= vfs_timeout;
  983. for (stamp = stamps; stamp != NULL;){
  984. if (now || (timeoutcmp (&stamp->time, &time))){
  985. st = stamp->next;
  986. (*stamp->v->free) (stamp->id);
  987. vfs_rmstamp (stamp->v, stamp->id, 0);
  988. stamp = st;
  989. } else
  990. stamp = stamp->next;
  991. }
  992. locked = 0;
  993. }
  994. void
  995. vfs_timeout_handler (void)
  996. {
  997. vfs_expire (0);
  998. }
  999. void
  1000. vfs_init (void)
  1001. {
  1002. time_t current_time;
  1003. struct tm *t;
  1004. memset (vfs_file_table, 0, sizeof (vfs_file_table));
  1005. current_time = time (NULL);
  1006. t = localtime (&current_time);
  1007. current_mday = t->tm_mday;
  1008. current_mon = t->tm_mon;
  1009. current_year = t->tm_year;
  1010. /* localfs needs to be the first one */
  1011. init_localfs();
  1012. /* fallback value for vfs_get_class() */
  1013. localfs_class = vfs_list;
  1014. init_extfs ();
  1015. init_sfs ();
  1016. init_tarfs ();
  1017. init_cpiofs ();
  1018. #ifdef USE_EXT2FSLIB
  1019. init_undelfs ();
  1020. #endif /* USE_EXT2FSLIB */
  1021. #ifdef USE_NETCODE
  1022. tcp_init();
  1023. init_ftpfs ();
  1024. init_fish ();
  1025. #ifdef WITH_SMBFS
  1026. init_smbfs ();
  1027. #endif /* WITH_SMBFS */
  1028. #ifdef WITH_MCFS
  1029. init_mcfs ();
  1030. #endif /* WITH_SMBFS */
  1031. #endif /* USE_NETCODE */
  1032. vfs_setup_wd ();
  1033. }
  1034. void
  1035. vfs_shut (void)
  1036. {
  1037. struct vfs_stamping *stamp, *st;
  1038. struct vfs_class *vfs;
  1039. for (stamp = stamps, stamps = 0; stamp != NULL;){
  1040. (*stamp->v->free)(stamp->id);
  1041. st = stamp->next;
  1042. g_free (stamp);
  1043. stamp = st;
  1044. }
  1045. if (stamps)
  1046. vfs_rmstamp (stamps->v, stamps->id, 1);
  1047. if (current_dir)
  1048. g_free (current_dir);
  1049. for (vfs=vfs_list; vfs; vfs=vfs->next)
  1050. if (vfs->done)
  1051. (*vfs->done) (vfs);
  1052. }
  1053. /*
  1054. * These ones grab information from the VFS
  1055. * and handles them to an upper layer
  1056. */
  1057. void
  1058. vfs_fill_names (void (*func)(char *))
  1059. {
  1060. struct vfs_class *vfs;
  1061. for (vfs=vfs_list; vfs; vfs=vfs->next)
  1062. if (vfs->fill_names)
  1063. (*vfs->fill_names) (vfs, func);
  1064. }
  1065. /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
  1066. #define MAXCOLS 30
  1067. static char *columns [MAXCOLS]; /* Points to the string in column n */
  1068. static int column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
  1069. int
  1070. vfs_split_text (char *p)
  1071. {
  1072. char *original = p;
  1073. int numcols;
  1074. memset (columns, 0, sizeof (columns));
  1075. for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
  1076. while (*p == ' ' || *p == '\r' || *p == '\n'){
  1077. *p = 0;
  1078. p++;
  1079. }
  1080. columns [numcols] = p;
  1081. column_ptr [numcols] = p - original;
  1082. while (*p && *p != ' ' && *p != '\r' && *p != '\n')
  1083. p++;
  1084. }
  1085. return numcols;
  1086. }
  1087. static int
  1088. is_num (int idx)
  1089. {
  1090. char *column = columns[idx];
  1091. if (!column || column[0] < '0' || column[0] > '9')
  1092. return 0;
  1093. return 1;
  1094. }
  1095. /* Return 1 for MM-DD-YY and MM-DD-YYYY */
  1096. static int
  1097. is_dos_date (const char *str)
  1098. {
  1099. int len;
  1100. if (!str)
  1101. return 0;
  1102. len = strlen (str);
  1103. if (len != 8 && len != 10)
  1104. return 0;
  1105. if (str[2] != str[5])
  1106. return 0;
  1107. if (!strchr ("\\-/", (int) str[2]))
  1108. return 0;
  1109. return 1;
  1110. }
  1111. static int
  1112. is_week (const char *str, struct tm *tim)
  1113. {
  1114. static const char *week = "SunMonTueWedThuFriSat";
  1115. char *pos;
  1116. if (!str)
  1117. return 0;
  1118. if ((pos = strstr (week, str)) != NULL) {
  1119. if (tim != NULL)
  1120. tim->tm_wday = (pos - week) / 3;
  1121. return 1;
  1122. }
  1123. return 0;
  1124. }
  1125. static int
  1126. is_month (const char *str, struct tm *tim)
  1127. {
  1128. static const char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
  1129. char *pos;
  1130. if (!str)
  1131. return 0;
  1132. if ((pos = strstr (month, str)) != NULL) {
  1133. if (tim != NULL)
  1134. tim->tm_mon = (pos - month) / 3;
  1135. return 1;
  1136. }
  1137. return 0;
  1138. }
  1139. /*
  1140. * Check for possible locale's abbreviated month name (Jan..Dec).
  1141. * Any 3 bytes long string without digit and control characters.
  1142. * isalpha() is locale specific, so it cannot be used if current
  1143. * locale is "C" and ftp server use Cyrillic.
  1144. * TODO: Punctuation characters also cannot be part of month name.
  1145. * NB: It is assumed there are no whitespaces in month.
  1146. */
  1147. static int
  1148. is_localized_month (const unsigned char *month)
  1149. {
  1150. int i = 0;
  1151. while ((i < 3) && *month && !isdigit (*month) && !iscntrl (*month)) {
  1152. i++;
  1153. month++;
  1154. }
  1155. return ((i == 3) && (*month == 0));
  1156. }
  1157. static int
  1158. is_time (const char *str, struct tm *tim)
  1159. {
  1160. char *p, *p2;
  1161. if (!str)
  1162. return 0;
  1163. if ((p = strchr (str, ':')) && (p2 = strrchr (str, ':'))) {
  1164. if (p != p2) {
  1165. if (sscanf
  1166. (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min,
  1167. &tim->tm_sec) != 3)
  1168. return 0;
  1169. } else {
  1170. if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
  1171. return 0;
  1172. }
  1173. } else
  1174. return 0;
  1175. return 1;
  1176. }
  1177. static int is_year (char *str, struct tm *tim)
  1178. {
  1179. long year;
  1180. if (!str)
  1181. return 0;
  1182. if (strchr (str, ':'))
  1183. return 0;
  1184. if (strlen (str) != 4)
  1185. return 0;
  1186. if (sscanf (str, "%ld", &year) != 1)
  1187. return 0;
  1188. if (year < 1900 || year > 3000)
  1189. return 0;
  1190. tim->tm_year = (int) (year - 1900);
  1191. return 1;
  1192. }
  1193. /*
  1194. * FIXME: this is broken. Consider following entry:
  1195. * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
  1196. * where "2904 1234" is filename. Well, this code decodes it as year :-(.
  1197. */
  1198. int
  1199. vfs_parse_filetype (char c)
  1200. {
  1201. switch (c) {
  1202. case 'd': return S_IFDIR;
  1203. case 'b': return S_IFBLK;
  1204. case 'c': return S_IFCHR;
  1205. case 'l': return S_IFLNK;
  1206. case 's': /* Socket */
  1207. #ifdef S_IFSOCK
  1208. return S_IFSOCK;
  1209. #else
  1210. /* If not supported, we fall through to IFIFO */
  1211. return S_IFIFO;
  1212. #endif
  1213. case 'D': /* Solaris door */
  1214. #ifdef S_IFDOOR
  1215. return S_IFDOOR;
  1216. #else
  1217. return S_IFIFO;
  1218. #endif
  1219. case 'p': return S_IFIFO;
  1220. case 'm': case 'n': /* Don't know what these are :-) */
  1221. case '-': case '?': return S_IFREG;
  1222. default: return -1;
  1223. }
  1224. }
  1225. int vfs_parse_filemode (const char *p)
  1226. { /* converts rw-rw-rw- into 0666 */
  1227. int res = 0;
  1228. switch (*(p++)){
  1229. case 'r': res |= 0400; break;
  1230. case '-': break;
  1231. default: return -1;
  1232. }
  1233. switch (*(p++)){
  1234. case 'w': res |= 0200; break;
  1235. case '-': break;
  1236. default: return -1;
  1237. }
  1238. switch (*(p++)){
  1239. case 'x': res |= 0100; break;
  1240. case 's': res |= 0100 | S_ISUID; break;
  1241. case 'S': res |= S_ISUID; break;
  1242. case '-': break;
  1243. default: return -1;
  1244. }
  1245. switch (*(p++)){
  1246. case 'r': res |= 0040; break;
  1247. case '-': break;
  1248. default: return -1;
  1249. }
  1250. switch (*(p++)){
  1251. case 'w': res |= 0020; break;
  1252. case '-': break;
  1253. default: return -1;
  1254. }
  1255. switch (*(p++)){
  1256. case 'x': res |= 0010; break;
  1257. case 's': res |= 0010 | S_ISGID; break;
  1258. case 'l': /* Solaris produces these */
  1259. case 'S': res |= S_ISGID; break;
  1260. case '-': break;
  1261. default: return -1;
  1262. }
  1263. switch (*(p++)){
  1264. case 'r': res |= 0004; break;
  1265. case '-': break;
  1266. default: return -1;
  1267. }
  1268. switch (*(p++)){
  1269. case 'w': res |= 0002; break;
  1270. case '-': break;
  1271. default: return -1;
  1272. }
  1273. switch (*(p++)){
  1274. case 'x': res |= 0001; break;
  1275. case 't': res |= 0001 | S_ISVTX; break;
  1276. case 'T': res |= S_ISVTX; break;
  1277. case '-': break;
  1278. default: return -1;
  1279. }
  1280. return res;
  1281. }
  1282. /* This function parses from idx in the columns[] array */
  1283. int
  1284. vfs_parse_filedate (int idx, time_t *t)
  1285. {
  1286. char *p;
  1287. struct tm tim;
  1288. int d[3];
  1289. int got_year = 0;
  1290. int l10n = 0; /* Locale's abbreviated month name */
  1291. /* Let's setup default time values */
  1292. tim.tm_year = current_year;
  1293. tim.tm_mon = current_mon;
  1294. tim.tm_mday = current_mday;
  1295. tim.tm_hour = 0;
  1296. tim.tm_min = 0;
  1297. tim.tm_sec = 0;
  1298. tim.tm_isdst = -1; /* Let mktime() try to guess correct dst offset */
  1299. p = columns[idx++];
  1300. /* We eat weekday name in case of extfs */
  1301. if (is_week (p, &tim))
  1302. p = columns[idx++];
  1303. /* Month name */
  1304. if (is_month (p, &tim)) {
  1305. /* And we expect, it followed by day number */
  1306. if (is_num (idx))
  1307. tim.tm_mday = (int) atol (columns[idx++]);
  1308. else
  1309. return 0; /* No day */
  1310. } else {
  1311. /* We usually expect:
  1312. Mon DD hh:mm
  1313. Mon DD YYYY
  1314. But in case of extfs we allow these date formats:
  1315. Mon DD YYYY hh:mm
  1316. Mon DD hh:mm YYYY
  1317. Wek Mon DD hh:mm:ss YYYY
  1318. MM-DD-YY hh:mm
  1319. where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
  1320. YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
  1321. /* Special case with MM-DD-YY or MM-DD-YYYY */
  1322. if (is_dos_date (p)) {
  1323. p[2] = p[5] = '-';
  1324. if (sscanf (p, "%2d-%2d-%d", &d[0], &d[1], &d[2]) == 3) {
  1325. /* Months are zero based */
  1326. if (d[0] > 0)
  1327. d[0]--;
  1328. if (d[2] > 1900) {
  1329. d[2] -= 1900;
  1330. } else {
  1331. /* Y2K madness */
  1332. if (d[2] < 70)
  1333. d[2] += 100;
  1334. }
  1335. tim.tm_mon = d[0];
  1336. tim.tm_mday = d[1];
  1337. tim.tm_year = d[2];
  1338. got_year = 1;
  1339. } else
  1340. return 0; /* sscanf failed */
  1341. } else {
  1342. /* Locale's abbreviated month name followed by day number */
  1343. if (is_localized_month (p) && (is_num (idx++)))
  1344. l10n = 1;
  1345. else
  1346. return 0; /* unsupported format */
  1347. }
  1348. }
  1349. /* Here we expect to find time and/or year */
  1350. if (is_num (idx)) {
  1351. if (is_time (columns[idx], &tim)
  1352. || (got_year = is_year (columns[idx], &tim))) {
  1353. idx++;
  1354. /* This is a special case for ctime() or Mon DD YYYY hh:mm */
  1355. if (is_num (idx) && (columns[idx + 1][0])) {
  1356. if (got_year) {
  1357. if (is_time (columns[idx], &tim))
  1358. idx++; /* time also */
  1359. } else {
  1360. if ((got_year = is_year (columns[idx], &tim)))
  1361. idx++; /* year also */
  1362. }
  1363. }
  1364. } /* only time or date */
  1365. } else
  1366. return 0; /* Nor time or date */
  1367. /*
  1368. * If the date is less than 6 months in the past, it is shown without year
  1369. * other dates in the past or future are shown with year but without time
  1370. * This does not check for years before 1900 ... I don't know, how
  1371. * to represent them at all
  1372. */
  1373. if (!got_year && current_mon < 6 && current_mon < tim.tm_mon
  1374. && tim.tm_mon - current_mon >= 6)
  1375. tim.tm_year--;
  1376. if (l10n || (*t = mktime (&tim)) < 0)
  1377. *t = 0;
  1378. return idx;
  1379. }
  1380. int
  1381. vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linkname)
  1382. {
  1383. int idx, idx2, num_cols;
  1384. int i;
  1385. char *p_copy = NULL;
  1386. char *t = NULL;
  1387. const char *line = p;
  1388. if (strncmp (p, "total", 5) == 0)
  1389. return 0;
  1390. if ((i = vfs_parse_filetype(*(p++))) == -1)
  1391. goto error;
  1392. s->st_mode = i;
  1393. if (*p == ' ') /* Notwell 4 */
  1394. p++;
  1395. if (*p == '['){
  1396. if (strlen (p) <= 8 || p [8] != ']')
  1397. goto error;
  1398. /* Should parse here the Notwell permissions :) */
  1399. if (S_ISDIR (s->st_mode))
  1400. s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
  1401. else
  1402. s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
  1403. p += 9;
  1404. } else {
  1405. if ((i = vfs_parse_filemode(p)) == -1)
  1406. goto error;
  1407. s->st_mode |= i;
  1408. p += 9;
  1409. /* This is for an extra ACL attribute (HP-UX) */
  1410. if (*p == '+')
  1411. p++;
  1412. }
  1413. p_copy = g_strdup(p);
  1414. num_cols = vfs_split_text (p_copy);
  1415. s->st_nlink = atol (columns [0]);
  1416. if (s->st_nlink <= 0)
  1417. goto error;
  1418. if (!is_num (1))
  1419. s->st_uid = vfs_finduid (columns [1]);
  1420. else
  1421. s->st_uid = (uid_t) atol (columns [1]);
  1422. /* Mhm, the ls -lg did not produce a group field */
  1423. for (idx = 3; idx <= 5; idx++)
  1424. if (is_month (columns[idx], NULL) || is_week (columns[idx], NULL)
  1425. || is_dos_date (columns[idx]) || is_localized_month (columns[idx]))
  1426. break;
  1427. if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
  1428. goto error;
  1429. /* We don't have gid */
  1430. if (idx == 3 || (idx == 4 && (S_ISCHR(s->st_mode) || S_ISBLK (s->st_mode))))
  1431. idx2 = 2;
  1432. else {
  1433. /* We have gid field */
  1434. if (is_num (2))
  1435. s->st_gid = (gid_t) atol (columns [2]);
  1436. else
  1437. s->st_gid = vfs_findgid (columns [2]);
  1438. idx2 = 3;
  1439. }
  1440. /* This is device */
  1441. if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)){
  1442. int maj, min;
  1443. if (!is_num (idx2) || sscanf(columns [idx2], " %d,", &maj) != 1)
  1444. goto error;
  1445. if (!is_num (++idx2) || sscanf(columns [idx2], " %d", &min) != 1)
  1446. goto error;
  1447. #ifdef HAVE_ST_RDEV
  1448. s->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
  1449. #endif
  1450. s->st_size = 0;
  1451. } else {
  1452. /* Common file size */
  1453. if (!is_num (idx2))
  1454. goto error;
  1455. s->st_size = (size_t) atol (columns [idx2]);
  1456. #ifdef HAVE_ST_RDEV
  1457. s->st_rdev = 0;
  1458. #endif
  1459. }
  1460. idx = vfs_parse_filedate (idx, &s->st_mtime);
  1461. if (!idx)
  1462. goto error;
  1463. /* Use resulting time value */
  1464. s->st_atime = s->st_ctime = s->st_mtime;
  1465. /* s->st_dev and s->st_ino must be initialized by vfs_s_new_inode () */
  1466. #ifdef HAVE_ST_BLKSIZE
  1467. s->st_blksize = 512;
  1468. #endif
  1469. #ifdef HAVE_ST_BLOCKS
  1470. s->st_blocks = (s->st_size + 511) / 512;
  1471. #endif
  1472. for (i = idx + 1, idx2 = 0; i < num_cols; i++ )
  1473. if (strcmp (columns [i], "->") == 0){
  1474. idx2 = i;
  1475. break;
  1476. }
  1477. if (((S_ISLNK (s->st_mode) ||
  1478. (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
  1479. && idx2){
  1480. if (filename){
  1481. *filename = g_strndup (p + column_ptr [idx], column_ptr [idx2] - column_ptr [idx] - 1);
  1482. }
  1483. if (linkname){
  1484. t = g_strdup (p + column_ptr [idx2+1]);
  1485. *linkname = t;
  1486. }
  1487. } else {
  1488. /* Extract the filename from the string copy, not from the columns
  1489. * this way we have a chance of entering hidden directories like ". ."
  1490. */
  1491. if (filename){
  1492. /*
  1493. * filename = g_strdup (columns [idx++]);
  1494. */
  1495. t = g_strdup (p + column_ptr [idx]);
  1496. *filename = t;
  1497. }
  1498. if (linkname)
  1499. *linkname = NULL;
  1500. }
  1501. if (t) {
  1502. int p = strlen (t);
  1503. if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
  1504. t [p] = 0;
  1505. if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
  1506. t [p] = 0;
  1507. }
  1508. g_free (p_copy);
  1509. return 1;
  1510. error:
  1511. {
  1512. static int errorcount = 0;
  1513. if (++errorcount < 5) {
  1514. message (1, _("Cannot parse:"), (p_copy && *p_copy) ? p_copy : line);
  1515. } else if (errorcount == 5)
  1516. message (1, _("Error"), _("More parsing errors will be ignored."));
  1517. }
  1518. g_free (p_copy);
  1519. return 0;
  1520. }
  1521. void
  1522. vfs_die (const char *m)
  1523. {
  1524. message (1, _("Internal error:"), m);
  1525. exit (1);
  1526. }
  1527. void
  1528. vfs_print_stats (const char *fs_name, const char *action, const char *file_name, off_t have, off_t need)
  1529. {
  1530. static char *i18n_percent_transf_format = NULL, *i18n_transf_format = NULL;
  1531. if (i18n_percent_transf_format == NULL) {
  1532. i18n_percent_transf_format = _("%s: %s: %s %3d%% (%lu bytes transferred)");
  1533. i18n_transf_format = _("%s: %s: %s %lu bytes transferred");
  1534. }
  1535. if (need)
  1536. print_vfs_message (i18n_percent_transf_format, fs_name, action,
  1537. file_name, (int)((double)have*100/need), (unsigned long) have);
  1538. else
  1539. print_vfs_message (i18n_transf_format,
  1540. fs_name, action, file_name, (unsigned long) have);
  1541. }
  1542. char *
  1543. vfs_get_password (char *msg)
  1544. {
  1545. return (char *) input_dialog (msg, _("Password:"), INPUT_PASSWORD);
  1546. }
  1547. /*
  1548. * Returns vfs path corresponding to given url. If passed string is
  1549. * not recognized as url, g_strdup(url) is returned.
  1550. */
  1551. char *
  1552. vfs_translate_url (const char *url)
  1553. {
  1554. if (strncmp (url, "ftp://", 6) == 0)
  1555. return g_strconcat ("/#ftp:", url + 6, NULL);
  1556. else if (strncmp (url, "a:", 2) == 0)
  1557. return g_strdup ("/#a");
  1558. else
  1559. return g_strdup (url);
  1560. }
  1561. void
  1562. vfs_release_path (const char *dir)
  1563. {
  1564. struct vfs_class *oldvfs;
  1565. vfsid oldvfsid;
  1566. struct vfs_stamping *parent;
  1567. oldvfs = vfs_get_class (dir);
  1568. oldvfsid = vfs_ncs_getid (oldvfs, dir, &parent);
  1569. vfs_add_noncurrent_stamps (oldvfs, oldvfsid, parent);
  1570. }