filesys.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /* filesys.c -- filesystem specific functions.
  2. $Id$
  3. Copyright 1993, 1997, 1998, 2000, 2002, 2003, 2004, 2007, 2008, 2009, 2011,
  4. 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (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 General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. Originally written by Brian Fox. */
  16. #include "info.h"
  17. #include "tilde.h"
  18. #include "filesys.h"
  19. #include "tag.h"
  20. #include "session.h"
  21. /* Local to this file. */
  22. static char *info_file_in_path (char *filename, struct stat *finfo);
  23. char *info_add_extension (char *dirname, char *fname,
  24. struct stat *finfo);
  25. static char *filesys_read_compressed (char *pathname, size_t *filesize);
  26. /* Return the command string that would be used to decompress FILENAME. */
  27. static char *filesys_decompressor_for_file (char *filename);
  28. static int compressed_filename_p (char *filename);
  29. typedef struct
  30. {
  31. char *suffix;
  32. char *decompressor;
  33. } COMPRESSION_ALIST;
  34. static char *info_suffixes[] = {
  35. ".info",
  36. "-info",
  37. "/index",
  38. ".inf", /* 8+3 file on filesystem which supports long file names */
  39. #ifdef __MSDOS__
  40. /* 8+3 file names strike again... */
  41. ".in", /* for .inz, .igz etc. */
  42. ".i",
  43. #endif
  44. "",
  45. NULL
  46. };
  47. static COMPRESSION_ALIST compress_suffixes[] = {
  48. #if STRIP_DOT_EXE
  49. { ".gz", "gunzip" },
  50. { ".lz", "lunzip" },
  51. #else
  52. { ".gz", "gzip -d" },
  53. { ".lz", "lzip -d" },
  54. #endif
  55. { ".xz", "unxz" },
  56. { ".bz2", "bunzip2" },
  57. { ".z", "gunzip" },
  58. { ".lzma", "unlzma" },
  59. { ".Z", "uncompress" },
  60. { ".Y", "unyabba" },
  61. #ifdef __MSDOS__
  62. { "gz", "gunzip" },
  63. { "z", "gunzip" },
  64. #endif
  65. { NULL, NULL }
  66. };
  67. /* Look for the filename PARTIAL in INFOPATH in order to find the correct file.
  68. Return file name and set *FINFO with information about file. If it
  69. can't find the file, it returns NULL, and sets filesys_error_number.
  70. Return value should be freed by caller. */
  71. char *
  72. info_find_fullpath (char *partial, struct stat *finfo)
  73. {
  74. char *fullpath = 0;
  75. struct stat dummy;
  76. debug(1, (_("looking for file \"%s\""), partial));
  77. if (!finfo)
  78. finfo = &dummy;
  79. filesys_error_number = 0;
  80. if (!partial || !*partial)
  81. return 0;
  82. /* IS_SLASH and IS_ABSOLUTE defined in ../system.h. */
  83. /* If path is absolute already, see if it needs an extension. */
  84. if (IS_ABSOLUTE (partial)
  85. || partial[0] == '.' && IS_SLASH(partial[1]))
  86. {
  87. fullpath = info_add_extension (0, partial, finfo);
  88. }
  89. /* Tilde expansion. FIXME: Not needed, because done by shell. */
  90. else if (partial[0] == '~')
  91. {
  92. partial = tilde_expand_word (partial);
  93. fullpath = info_add_extension (0, partial, finfo);
  94. }
  95. /* If just a simple name element, look for it in the path. */
  96. else
  97. fullpath = info_file_in_path (partial, finfo);
  98. if (!fullpath)
  99. filesys_error_number = ENOENT;
  100. return fullpath;
  101. }
  102. /* Scan the directories in search path looking for FILENAME. If we find
  103. one that is a regular file, return it as a new string. Otherwise, return
  104. a NULL pointer. Set *FINFO with information about file. */
  105. char *
  106. info_file_find_next_in_path (char *filename, int *path_index, struct stat *finfo)
  107. {
  108. struct stat dummy;
  109. /* Used for output of stat in case the caller doesn't care about
  110. its value. */
  111. if (!finfo)
  112. finfo = &dummy;
  113. /* Reject ridiculous cases up front, to prevent infinite recursion
  114. later on. E.g., someone might say "info '(.)foo'"... */
  115. if (!*filename || STREQ (filename, ".") || STREQ (filename, ".."))
  116. return NULL;
  117. while (1)
  118. {
  119. char *dirname, *with_extension = 0;
  120. dirname = infopath_next (path_index);
  121. if (!dirname)
  122. break;
  123. debug(1, (_("looking for file %s in %s"), filename, dirname));
  124. /* Expand a leading tilde if one is present. */
  125. if (*dirname == '~')
  126. {
  127. char *expanded_dirname = tilde_expand_word (dirname);
  128. free (dirname);
  129. dirname = expanded_dirname;
  130. }
  131. with_extension = info_add_extension (dirname, filename, finfo);
  132. if (with_extension)
  133. {
  134. if (!IS_ABSOLUTE (with_extension))
  135. {
  136. /* Prefix "./" to it. */
  137. char *s;
  138. asprintf (&s, "%s%s", "./", with_extension);
  139. free (with_extension);
  140. return s;
  141. }
  142. else
  143. return with_extension;
  144. }
  145. }
  146. return NULL;
  147. }
  148. /* Return full path of first Info file known as FILENAME in
  149. search path. If relative to current directory, precede it with './'. */
  150. static char *
  151. info_file_in_path (char *filename, struct stat *finfo)
  152. {
  153. int i = 0;
  154. return info_file_find_next_in_path (filename, &i, finfo);
  155. }
  156. /* Look for a file called FILENAME in a directory called DIRNAME, adding file
  157. extensions if necessary. FILENAME can be an absolute path or a path
  158. relative to the current directory, in which case DIRNAME should be
  159. null. Return it as a new string; otherwise return a NULL pointer. */
  160. char *
  161. info_add_extension (char *dirname, char *filename, struct stat *finfo)
  162. {
  163. char *try_filename;
  164. register int i, pre_suffix_length = 0;
  165. struct stat dummy;
  166. if (!finfo)
  167. finfo = &dummy;
  168. if (dirname)
  169. pre_suffix_length += strlen (dirname);
  170. pre_suffix_length += strlen (filename);
  171. /* Add enough space for any file extensions at end. */
  172. try_filename = xmalloc (pre_suffix_length + 30);
  173. try_filename[0] = '\0';
  174. if (dirname)
  175. {
  176. strcpy (try_filename, dirname);
  177. if (!IS_SLASH (try_filename[(strlen (try_filename)) - 1]))
  178. {
  179. strcat (try_filename, "/");
  180. pre_suffix_length++;
  181. }
  182. }
  183. strcat (try_filename, filename);
  184. for (i = 0; info_suffixes[i]; i++)
  185. {
  186. int statable;
  187. strcpy (try_filename + pre_suffix_length, info_suffixes[i]);
  188. statable = (stat (try_filename, finfo) == 0);
  189. /* If we have found a regular file, then use that. Else, if we
  190. have found a directory, look in that directory for this file. */
  191. if (statable)
  192. {
  193. if (S_ISREG (finfo->st_mode))
  194. {
  195. debug(1, (_("found file %s"), try_filename));
  196. return try_filename;
  197. }
  198. else if (S_ISDIR (finfo->st_mode))
  199. {
  200. char *newpath, *new_filename;
  201. newpath = xstrdup (try_filename);
  202. new_filename = info_add_extension (newpath, filename, finfo);
  203. free (newpath);
  204. if (new_filename)
  205. {
  206. free (try_filename);
  207. debug(1, (_("found file %s"), new_filename));
  208. return new_filename;
  209. }
  210. }
  211. }
  212. else
  213. {
  214. /* Add various compression suffixes to the name to see if
  215. the file is present in compressed format. */
  216. register int j, pre_compress_suffix_length;
  217. pre_compress_suffix_length = strlen (try_filename);
  218. for (j = 0; compress_suffixes[j].suffix; j++)
  219. {
  220. strcpy (try_filename + pre_compress_suffix_length,
  221. compress_suffixes[j].suffix);
  222. statable = (stat (try_filename, finfo) == 0);
  223. if (statable && (S_ISREG (finfo->st_mode)))
  224. {
  225. debug(1, (_("found file %s"), try_filename));
  226. return try_filename;
  227. }
  228. }
  229. }
  230. }
  231. /* Nothing was found. */
  232. free (try_filename);
  233. return 0;
  234. }
  235. /* Read the contents of PATHNAME, returning a buffer with the contents of
  236. that file in it, and returning the size of that buffer in FILESIZE.
  237. If the file turns out to be compressed, set IS_COMPRESSED to non-zero.
  238. If the file cannot be read, set filesys_error_number and return a NULL
  239. pointer. Set *FINFO with information about file. */
  240. char *
  241. filesys_read_info_file (char *pathname, size_t *filesize,
  242. struct stat *finfo, int *is_compressed)
  243. {
  244. size_t fsize;
  245. char *contents;
  246. fsize = filesys_error_number = 0;
  247. stat (pathname, finfo);
  248. fsize = (long) finfo->st_size;
  249. if (compressed_filename_p (pathname))
  250. {
  251. *is_compressed = 1;
  252. contents = filesys_read_compressed (pathname, &fsize);
  253. }
  254. else
  255. {
  256. int descriptor;
  257. *is_compressed = 0;
  258. descriptor = open (pathname, O_RDONLY | O_BINARY, 0666);
  259. /* If the file couldn't be opened, give up. */
  260. if (descriptor < 0)
  261. {
  262. filesys_error_number = errno;
  263. return NULL;
  264. }
  265. /* Try to read the contents of this file. */
  266. contents = xmalloc (1 + fsize);
  267. if ((read (descriptor, contents, fsize)) != fsize)
  268. {
  269. filesys_error_number = errno;
  270. close (descriptor);
  271. free (contents);
  272. return NULL;
  273. }
  274. contents[fsize] = 0;
  275. close (descriptor);
  276. }
  277. *filesize = fsize;
  278. return contents;
  279. }
  280. /* Typically, pipe buffers are 4k. */
  281. #define BASIC_PIPE_BUFFER (4 * 1024)
  282. /* We use some large multiple of that. */
  283. #define FILESYS_PIPE_BUFFER_SIZE (16 * BASIC_PIPE_BUFFER)
  284. static char *
  285. filesys_read_compressed (char *pathname, size_t *filesize)
  286. {
  287. FILE *stream;
  288. char *command, *decompressor;
  289. char *contents = NULL;
  290. *filesize = filesys_error_number = 0;
  291. decompressor = filesys_decompressor_for_file (pathname);
  292. if (!decompressor)
  293. return NULL;
  294. command = xmalloc (15 + strlen (pathname) + strlen (decompressor));
  295. /* Explicit .exe suffix makes the diagnostics of `popen'
  296. better on systems where COMMAND.COM is the stock shell. */
  297. sprintf (command, "%s%s < %s",
  298. decompressor, STRIP_DOT_EXE ? ".exe" : "", pathname);
  299. if (info_windows_initialized_p)
  300. {
  301. char *temp;
  302. temp = xmalloc (5 + strlen (command));
  303. sprintf (temp, "%s...", command);
  304. message_in_echo_area ("%s", temp);
  305. free (temp);
  306. }
  307. stream = popen (command, FOPEN_RBIN);
  308. free (command);
  309. /* Read chunks from this file until there are none left to read. */
  310. if (stream)
  311. {
  312. size_t offset, size;
  313. char *chunk;
  314. offset = size = 0;
  315. chunk = xmalloc (FILESYS_PIPE_BUFFER_SIZE);
  316. while (1)
  317. {
  318. size_t bytes_read;
  319. bytes_read = fread (chunk, 1, FILESYS_PIPE_BUFFER_SIZE, stream);
  320. if (bytes_read + offset >= size)
  321. contents = xrealloc
  322. (contents, size += (2 * FILESYS_PIPE_BUFFER_SIZE));
  323. memcpy (contents + offset, chunk, bytes_read);
  324. offset += bytes_read;
  325. if (bytes_read != FILESYS_PIPE_BUFFER_SIZE)
  326. break;
  327. }
  328. free (chunk);
  329. if (pclose (stream) == -1)
  330. {
  331. if (contents)
  332. free (contents);
  333. contents = NULL;
  334. filesys_error_number = errno;
  335. }
  336. else
  337. {
  338. contents = xrealloc (contents, 1 + offset);
  339. contents[offset] = '\0';
  340. *filesize = offset;
  341. }
  342. }
  343. else
  344. {
  345. filesys_error_number = errno;
  346. }
  347. if (info_windows_initialized_p)
  348. unmessage_in_echo_area ();
  349. return contents;
  350. }
  351. /* Return non-zero if FILENAME belongs to a compressed file. */
  352. static int
  353. compressed_filename_p (char *filename)
  354. {
  355. char *decompressor;
  356. /* Find the final extension of this filename, and see if it matches one
  357. of our known ones. */
  358. decompressor = filesys_decompressor_for_file (filename);
  359. if (decompressor)
  360. return 1;
  361. else
  362. return 0;
  363. }
  364. /* Return the command string that would be used to decompress FILENAME. */
  365. static char *
  366. filesys_decompressor_for_file (char *filename)
  367. {
  368. register int i;
  369. char *extension = NULL;
  370. /* Find the final extension of FILENAME, and see if it appears in our
  371. list of known compression extensions. */
  372. for (i = strlen (filename) - 1; i > 0; i--)
  373. if (filename[i] == '.')
  374. {
  375. extension = filename + i;
  376. break;
  377. }
  378. if (!extension)
  379. return NULL;
  380. for (i = 0; compress_suffixes[i].suffix; i++)
  381. if (FILENAME_CMP (extension, compress_suffixes[i].suffix) == 0)
  382. return compress_suffixes[i].decompressor;
  383. #if defined (__MSDOS__)
  384. /* If no other suffix matched, allow any extension which ends
  385. with `z' to be decompressed by gunzip. Due to limited 8+3 DOS
  386. file namespace, we can expect many such cases, and supporting
  387. every weird suffix thus produced would be a pain. */
  388. if (extension[strlen (extension) - 1] == 'z' ||
  389. extension[strlen (extension) - 1] == 'Z')
  390. return "gunzip";
  391. #endif
  392. return NULL;
  393. }
  394. /* The number of the most recent file system error. */
  395. int filesys_error_number = 0;
  396. /* A function which returns a pointer to a static buffer containing
  397. an error message for FILENAME and ERROR_NUM. */
  398. static char *errmsg_buf = NULL;
  399. static int errmsg_buf_size = 0;
  400. /* Return string for ERROR_NUM when opening file. Return value should not
  401. be freed by caller. */
  402. char *
  403. filesys_error_string (char *filename, int error_num)
  404. {
  405. int len;
  406. char *result;
  407. if (error_num == 0)
  408. return NULL;
  409. result = strerror (error_num);
  410. len = 4 + strlen (filename) + strlen (result);
  411. if (len >= errmsg_buf_size)
  412. errmsg_buf = xrealloc (errmsg_buf, (errmsg_buf_size = 2 + len));
  413. sprintf (errmsg_buf, "%s: %s", filename, result);
  414. return errmsg_buf;
  415. }
  416. /* Check for "dir" with all the possible info and compression suffixes,
  417. in combination. */
  418. int
  419. is_dir_name (char *filename)
  420. {
  421. unsigned i;
  422. for (i = 0; info_suffixes[i]; i++)
  423. {
  424. unsigned c;
  425. char trydir[50];
  426. strcpy (trydir, "dir");
  427. strcat (trydir, info_suffixes[i]);
  428. if (mbscasecmp (filename, trydir) == 0)
  429. return 1;
  430. for (c = 0; compress_suffixes[c].suffix; c++)
  431. {
  432. char dir_compressed[50]; /* can be short */
  433. strcpy (dir_compressed, trydir);
  434. strcat (dir_compressed, compress_suffixes[c].suffix);
  435. if (mbscasecmp (filename, dir_compressed) == 0)
  436. return 1;
  437. }
  438. }
  439. return 0;
  440. }