1130803Smarcel/* filesys.c -- filesystem specific functions.
2130803Smarcel   $Id: filesys.c,v 1.6 2004/07/30 17:17:40 karl Exp $
3130803Smarcel
4130803Smarcel   Copyright (C) 1993, 1997, 1998, 2000, 2002, 2003, 2004 Free Software
5130803Smarcel   Foundation, Inc.
6130803Smarcel
7130803Smarcel   This program is free software; you can redistribute it and/or modify
8130803Smarcel   it under the terms of the GNU General Public License as published by
9130803Smarcel   the Free Software Foundation; either version 2, or (at your option)
10130803Smarcel   any later version.
11130803Smarcel
12130803Smarcel   This program is distributed in the hope that it will be useful,
13130803Smarcel   but WITHOUT ANY WARRANTY; without even the implied warranty of
14130803Smarcel   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15130803Smarcel   GNU General Public License for more details.
16130803Smarcel
17130803Smarcel   You should have received a copy of the GNU General Public License
18130803Smarcel   along with this program; if not, write to the Free Software
19130803Smarcel   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20130803Smarcel
21130803Smarcel   Written by Brian Fox (bfox@ai.mit.edu). */
22130803Smarcel
23130803Smarcel#include "info.h"
24130803Smarcel
25130803Smarcel#include "tilde.h"
26130803Smarcel#include "filesys.h"
27130803Smarcel
28130803Smarcel/* Local to this file. */
29130803Smarcelstatic char *info_file_in_path (char *filename, char *path);
30130803Smarcelstatic char *lookup_info_filename (char *filename);
31130803Smarcelstatic char *info_absolute_file (char *fname);
32130803Smarcel
33130803Smarcelstatic void remember_info_filename (char *filename, char *expansion);
34130803Smarcelstatic void maybe_initialize_infopath (void);
35130803Smarcel
36130803Smarceltypedef struct
37130803Smarcel{
38130803Smarcel  char *suffix;
39130803Smarcel  char *decompressor;
40130803Smarcel} COMPRESSION_ALIST;
41130803Smarcel
42130803Smarcelstatic char *info_suffixes[] = {
43130803Smarcel  ".info",
44130803Smarcel  "-info",
45130803Smarcel  "/index",
46130803Smarcel  ".inf",       /* 8+3 file on filesystem which supports long file names */
47130803Smarcel#ifdef __MSDOS__
48130803Smarcel  /* 8+3 file names strike again...  */
49130803Smarcel  ".in",        /* for .inz, .igz etc. */
50130803Smarcel  ".i",
51130803Smarcel#endif
52130803Smarcel  "",
53130803Smarcel  NULL
54130803Smarcel};
55130803Smarcel
56130803Smarcelstatic COMPRESSION_ALIST compress_suffixes[] = {
57130803Smarcel  { ".gz", "gunzip" },
58130803Smarcel  { ".bz2", "bunzip2" },
59130803Smarcel  { ".z", "gunzip" },
60130803Smarcel  { ".Z", "uncompress" },
61130803Smarcel  { ".Y", "unyabba" },
62130803Smarcel#ifdef __MSDOS__
63130803Smarcel  { "gz", "gunzip" },
64130803Smarcel  { "z", "gunzip" },
65130803Smarcel#endif
66130803Smarcel  { (char *)NULL, (char *)NULL }
67130803Smarcel};
68130803Smarcel
69130803Smarcel/* The path on which we look for info files.  You can initialize this
70130803Smarcel   from the environment variable INFOPATH if there is one, or you can
71130803Smarcel   call info_add_path () to add paths to the beginning or end of it.
72130803Smarcel   You can call zap_infopath () to make the path go away. */
73130803Smarcelchar *infopath = (char *)NULL;
74130803Smarcelstatic int infopath_size = 0;
75130803Smarcel
76130803Smarcel/* Expand the filename in PARTIAL to make a real name for this operating
77130803Smarcel   system.  This looks in INFO_PATHS in order to find the correct file.
78130803Smarcel   If it can't find the file, it returns NULL. */
79130803Smarcelstatic char *local_temp_filename = (char *)NULL;
80130803Smarcelstatic int local_temp_filename_size = 0;
81130803Smarcel
82130803Smarcelchar *
83130803Smarcelinfo_find_fullpath (char *partial)
84130803Smarcel{
85130803Smarcel  int initial_character;
86130803Smarcel  char *temp;
87130803Smarcel
88130803Smarcel  filesys_error_number = 0;
89130803Smarcel
90130803Smarcel  maybe_initialize_infopath ();
91130803Smarcel
92130803Smarcel  if (partial && (initial_character = *partial))
93130803Smarcel    {
94130803Smarcel      char *expansion;
95130803Smarcel
96130803Smarcel      expansion = lookup_info_filename (partial);
97130803Smarcel
98130803Smarcel      if (expansion)
99130803Smarcel        return (expansion);
100130803Smarcel
101130803Smarcel      /* If we have the full path to this file, we still may have to add
102130803Smarcel         various extensions to it.  I guess we have to stat this file
103130803Smarcel         after all. */
104130803Smarcel      if (IS_ABSOLUTE (partial))
105130803Smarcel	temp = info_absolute_file (partial);
106130803Smarcel      else if (initial_character == '~')
107130803Smarcel        {
108130803Smarcel          expansion = tilde_expand_word (partial);
109130803Smarcel          if (IS_ABSOLUTE (expansion))
110130803Smarcel            {
111130803Smarcel              temp = info_absolute_file (expansion);
112130803Smarcel              free (expansion);
113130803Smarcel            }
114130803Smarcel          else
115130803Smarcel            temp = expansion;
116130803Smarcel        }
117130803Smarcel      else if (initial_character == '.' &&
118130803Smarcel               (IS_SLASH (partial[1]) ||
119130803Smarcel		(partial[1] == '.' && IS_SLASH (partial[2]))))
120130803Smarcel        {
121130803Smarcel          if (local_temp_filename_size < 1024)
122130803Smarcel            local_temp_filename = (char *)xrealloc
123130803Smarcel              (local_temp_filename, (local_temp_filename_size = 1024));
124130803Smarcel#if defined (HAVE_GETCWD)
125130803Smarcel          if (!getcwd (local_temp_filename, local_temp_filename_size))
126130803Smarcel#else /*  !HAVE_GETCWD */
127130803Smarcel          if (!getwd (local_temp_filename))
128130803Smarcel#endif /* !HAVE_GETCWD */
129130803Smarcel            {
130130803Smarcel              filesys_error_number = errno;
131130803Smarcel              return (partial);
132130803Smarcel            }
133130803Smarcel
134130803Smarcel          strcat (local_temp_filename, "/");
135130803Smarcel          strcat (local_temp_filename, partial);
136130803Smarcel	  temp = info_absolute_file (local_temp_filename); /* try extensions */
137130803Smarcel	  if (!temp)
138130803Smarcel	    partial = local_temp_filename;
139130803Smarcel        }
140130803Smarcel      else
141130803Smarcel        temp = info_file_in_path (partial, infopath);
142130803Smarcel
143130803Smarcel      if (temp)
144130803Smarcel        {
145130803Smarcel          remember_info_filename (partial, temp);
146130803Smarcel          if (strlen (temp) > (unsigned int) local_temp_filename_size)
147130803Smarcel            local_temp_filename = (char *) xrealloc
148130803Smarcel              (local_temp_filename,
149130803Smarcel               (local_temp_filename_size = (50 + strlen (temp))));
150130803Smarcel          strcpy (local_temp_filename, temp);
151130803Smarcel          free (temp);
152130803Smarcel          return (local_temp_filename);
153130803Smarcel        }
154130803Smarcel    }
155130803Smarcel  return (partial);
156130803Smarcel}
157130803Smarcel
158130803Smarcel/* Scan the list of directories in PATH looking for FILENAME.  If we find
159130803Smarcel   one that is a regular file, return it as a new string.  Otherwise, return
160130803Smarcel   a NULL pointer. */
161130803Smarcelstatic char *
162130803Smarcelinfo_file_in_path (char *filename, char *path)
163130803Smarcel{
164130803Smarcel  struct stat finfo;
165130803Smarcel  char *temp_dirname;
166130803Smarcel  int statable, dirname_index;
167130803Smarcel
168130803Smarcel  /* Reject ridiculous cases up front, to prevent infinite recursion
169130803Smarcel     later on.  E.g., someone might say "info '(.)foo'"...  */
170130803Smarcel  if (!*filename || STREQ (filename, ".") || STREQ (filename, ".."))
171130803Smarcel    return NULL;
172130803Smarcel
173130803Smarcel  dirname_index = 0;
174130803Smarcel
175130803Smarcel  while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
176130803Smarcel    {
177130803Smarcel      register int i, pre_suffix_length;
178130803Smarcel      char *temp;
179130803Smarcel
180130803Smarcel      /* Expand a leading tilde if one is present. */
181130803Smarcel      if (*temp_dirname == '~')
182130803Smarcel        {
183130803Smarcel          char *expanded_dirname;
184130803Smarcel
185130803Smarcel          expanded_dirname = tilde_expand_word (temp_dirname);
186130803Smarcel          free (temp_dirname);
187130803Smarcel          temp_dirname = expanded_dirname;
188130803Smarcel        }
189130803Smarcel
190130803Smarcel      temp = (char *)xmalloc (30 + strlen (temp_dirname) + strlen (filename));
191130803Smarcel      strcpy (temp, temp_dirname);
192130803Smarcel      if (!IS_SLASH (temp[(strlen (temp)) - 1]))
193130803Smarcel        strcat (temp, "/");
194130803Smarcel      strcat (temp, filename);
195130803Smarcel
196130803Smarcel      pre_suffix_length = strlen (temp);
197130803Smarcel
198130803Smarcel      free (temp_dirname);
199130803Smarcel
200      for (i = 0; info_suffixes[i]; i++)
201        {
202          strcpy (temp + pre_suffix_length, info_suffixes[i]);
203
204          statable = (stat (temp, &finfo) == 0);
205
206          /* If we have found a regular file, then use that.  Else, if we
207             have found a directory, look in that directory for this file. */
208          if (statable)
209            {
210              if (S_ISREG (finfo.st_mode))
211                {
212                  return (temp);
213                }
214              else if (S_ISDIR (finfo.st_mode))
215                {
216                  char *newpath, *filename_only, *newtemp;
217
218                  newpath = xstrdup (temp);
219                  filename_only = filename_non_directory (filename);
220                  newtemp = info_file_in_path (filename_only, newpath);
221
222                  free (newpath);
223                  if (newtemp)
224                    {
225                      free (temp);
226                      return (newtemp);
227                    }
228                }
229            }
230          else
231            {
232              /* Add various compression suffixes to the name to see if
233                 the file is present in compressed format. */
234              register int j, pre_compress_suffix_length;
235
236              pre_compress_suffix_length = strlen (temp);
237
238              for (j = 0; compress_suffixes[j].suffix; j++)
239                {
240                  strcpy (temp + pre_compress_suffix_length,
241                          compress_suffixes[j].suffix);
242
243                  statable = (stat (temp, &finfo) == 0);
244                  if (statable && (S_ISREG (finfo.st_mode)))
245                    return (temp);
246                }
247            }
248        }
249      free (temp);
250    }
251  return ((char *)NULL);
252}
253
254/* Assume FNAME is an absolute file name, and check whether it is
255   a regular file.  If it is, return it as a new string; otherwise
256   return a NULL pointer.  We do it by taking the file name apart
257   into its directory and basename parts, and calling info_file_in_path.*/
258static char *
259info_absolute_file (char *fname)
260{
261  char *containing_dir = xstrdup (fname);
262  char *base = filename_non_directory (containing_dir);
263
264  if (base > containing_dir)
265    base[-1] = '\0';
266
267  return info_file_in_path (filename_non_directory (fname), containing_dir);
268}
269
270
271/* Given a string containing units of information separated by the
272   PATH_SEP character, return the next one after IDX, or NULL if there
273   are no more.  Advance IDX to the character after the colon. */
274
275char *
276extract_colon_unit (char *string, int *idx)
277{
278  unsigned int i = (unsigned int) *idx;
279  unsigned int start = i;
280
281  if (!string || i >= strlen (string))
282    return NULL;
283
284  if (!string[i]) /* end of string */
285    return NULL;
286
287  /* Advance to next PATH_SEP.  */
288  while (string[i] && string[i] != PATH_SEP[0])
289    i++;
290
291  {
292    char *value = xmalloc ((i - start) + 1);
293    strncpy (value, &string[start], (i - start));
294    value[i - start] = 0;
295
296    i++; /* move past PATH_SEP */
297    *idx = i;
298    return value;
299  }
300}
301
302/* A structure which associates a filename with its expansion. */
303typedef struct
304{
305  char *filename;
306  char *expansion;
307} FILENAME_LIST;
308
309/* An array of remembered arguments and results. */
310static FILENAME_LIST **names_and_files = (FILENAME_LIST **)NULL;
311static int names_and_files_index = 0;
312static int names_and_files_slots = 0;
313
314/* Find the result for having already called info_find_fullpath () with
315   FILENAME. */
316static char *
317lookup_info_filename (char *filename)
318{
319  if (filename && names_and_files)
320    {
321      register int i;
322      for (i = 0; names_and_files[i]; i++)
323        {
324          if (FILENAME_CMP (names_and_files[i]->filename, filename) == 0)
325            return (names_and_files[i]->expansion);
326        }
327    }
328  return (char *)NULL;;
329}
330
331/* Add a filename and its expansion to our list. */
332static void
333remember_info_filename (char *filename, char *expansion)
334{
335  FILENAME_LIST *new;
336
337  if (names_and_files_index + 2 > names_and_files_slots)
338    {
339      int alloc_size;
340      names_and_files_slots += 10;
341
342      alloc_size = names_and_files_slots * sizeof (FILENAME_LIST *);
343
344      names_and_files =
345        (FILENAME_LIST **) xrealloc (names_and_files, alloc_size);
346    }
347
348  new = (FILENAME_LIST *)xmalloc (sizeof (FILENAME_LIST));
349  new->filename = xstrdup (filename);
350  new->expansion = expansion ? xstrdup (expansion) : (char *)NULL;
351
352  names_and_files[names_and_files_index++] = new;
353  names_and_files[names_and_files_index] = (FILENAME_LIST *)NULL;
354}
355
356static void
357maybe_initialize_infopath (void)
358{
359  if (!infopath_size)
360    {
361      infopath = (char *)
362        xmalloc (infopath_size = (1 + strlen (DEFAULT_INFOPATH)));
363
364      strcpy (infopath, DEFAULT_INFOPATH);
365    }
366}
367
368/* Add PATH to the list of paths found in INFOPATH.  2nd argument says
369   whether to put PATH at the front or end of INFOPATH. */
370void
371info_add_path (char *path, int where)
372{
373  int len;
374
375  if (!infopath)
376    {
377      infopath = (char *)xmalloc (infopath_size = 200 + strlen (path));
378      infopath[0] = '\0';
379    }
380
381  len = strlen (path) + strlen (infopath);
382
383  if (len + 2 >= infopath_size)
384    infopath = (char *)xrealloc (infopath, (infopath_size += (2 * len) + 2));
385
386  if (!*infopath)
387    strcpy (infopath, path);
388  else if (where == INFOPATH_APPEND)
389    {
390      strcat (infopath, PATH_SEP);
391      strcat (infopath, path);
392    }
393  else if (where == INFOPATH_PREPEND)
394    {
395      char *temp = xstrdup (infopath);
396      strcpy (infopath, path);
397      strcat (infopath, PATH_SEP);
398      strcat (infopath, temp);
399      free (temp);
400    }
401}
402
403/* Make INFOPATH have absolutely nothing in it. */
404void
405zap_infopath (void)
406{
407  if (infopath)
408    free (infopath);
409
410  infopath = (char *)NULL;
411  infopath_size = 0;
412}
413
414/* Given a chunk of text and its length, convert all CRLF pairs at every
415   end-of-line into a single Newline character.  Return the length of
416   produced text.
417
418   This is required because the rest of code is too entrenched in having
419   a single newline at each EOL; in particular, searching for various
420   Info headers and cookies can become extremely tricky if that assumption
421   breaks.
422
423   FIXME: this could also support Mac-style text files with a single CR
424   at the EOL, but what about random CR characters in non-Mac files?  Can
425   we afford converting them into newlines as well?  Maybe implement some
426   heuristics here, like in Emacs 20.
427
428   FIXME: is it a good idea to show the EOL type on the modeline?  */
429long
430convert_eols (char *text, long int textlen)
431{
432  register char *s = text;
433  register char *d = text;
434
435  while (textlen--)
436    {
437      if (*s == '\r' && textlen && s[1] == '\n')
438	{
439	  s++;
440	  textlen--;
441	}
442      *d++ = *s++;
443    }
444
445  return (long)(d - text);
446}
447
448/* Read the contents of PATHNAME, returning a buffer with the contents of
449   that file in it, and returning the size of that buffer in FILESIZE.
450   FINFO is a stat struct which has already been filled in by the caller.
451   If the file turns out to be compressed, set IS_COMPRESSED to non-zero.
452   If the file cannot be read, return a NULL pointer. */
453char *
454filesys_read_info_file (char *pathname, long int *filesize,
455    struct stat *finfo, int *is_compressed)
456{
457  long st_size;
458
459  *filesize = filesys_error_number = 0;
460
461  if (compressed_filename_p (pathname))
462    {
463      *is_compressed = 1;
464      return (filesys_read_compressed (pathname, filesize));
465    }
466  else
467    {
468      int descriptor;
469      char *contents;
470
471      *is_compressed = 0;
472      descriptor = open (pathname, O_RDONLY | O_BINARY, 0666);
473
474      /* If the file couldn't be opened, give up. */
475      if (descriptor < 0)
476        {
477          filesys_error_number = errno;
478          return ((char *)NULL);
479        }
480
481      /* Try to read the contents of this file. */
482      st_size = (long) finfo->st_size;
483      contents = (char *)xmalloc (1 + st_size);
484      if ((read (descriptor, contents, st_size)) != st_size)
485        {
486	  filesys_error_number = errno;
487	  close (descriptor);
488	  free (contents);
489	  return ((char *)NULL);
490        }
491
492      close (descriptor);
493
494      /* Convert any DOS-style CRLF EOLs into Unix-style NL.
495	 Seems like a good idea to have even on Unix, in case the Info
496	 files are coming from some Windows system across a network.  */
497      *filesize = convert_eols (contents, st_size);
498
499      /* EOL conversion can shrink the text quite a bit.  We don't
500	 want to waste storage.  */
501      if (*filesize < st_size)
502	contents = (char *)xrealloc (contents, 1 + *filesize);
503      contents[*filesize] = '\0';
504
505      return (contents);
506    }
507}
508
509/* Typically, pipe buffers are 4k. */
510#define BASIC_PIPE_BUFFER (4 * 1024)
511
512/* We use some large multiple of that. */
513#define FILESYS_PIPE_BUFFER_SIZE (16 * BASIC_PIPE_BUFFER)
514
515char *
516filesys_read_compressed (char *pathname, long int *filesize)
517{
518  FILE *stream;
519  char *command, *decompressor;
520  char *contents = (char *)NULL;
521
522  *filesize = filesys_error_number = 0;
523
524  decompressor = filesys_decompressor_for_file (pathname);
525
526  if (!decompressor)
527    return ((char *)NULL);
528
529  command = (char *)xmalloc (15 + strlen (pathname) + strlen (decompressor));
530  /* Explicit .exe suffix makes the diagnostics of `popen'
531     better on systems where COMMAND.COM is the stock shell.  */
532  sprintf (command, "%s%s < %s",
533	   decompressor, STRIP_DOT_EXE ? ".exe" : "", pathname);
534
535#if !defined (BUILDING_LIBRARY)
536  if (info_windows_initialized_p)
537    {
538      char *temp;
539
540      temp = (char *)xmalloc (5 + strlen (command));
541      sprintf (temp, "%s...", command);
542      message_in_echo_area ("%s", temp, NULL);
543      free (temp);
544    }
545#endif /* !BUILDING_LIBRARY */
546
547  stream = popen (command, FOPEN_RBIN);
548  free (command);
549
550  /* Read chunks from this file until there are none left to read. */
551  if (stream)
552    {
553      long offset, size;
554      char *chunk;
555
556      offset = size = 0;
557      chunk = (char *)xmalloc (FILESYS_PIPE_BUFFER_SIZE);
558
559      while (1)
560        {
561          int bytes_read;
562
563          bytes_read = fread (chunk, 1, FILESYS_PIPE_BUFFER_SIZE, stream);
564
565          if (bytes_read + offset >= size)
566            contents = (char *)xrealloc
567              (contents, size += (2 * FILESYS_PIPE_BUFFER_SIZE));
568
569          memcpy (contents + offset, chunk, bytes_read);
570          offset += bytes_read;
571          if (bytes_read != FILESYS_PIPE_BUFFER_SIZE)
572            break;
573        }
574
575      free (chunk);
576      if (pclose (stream) == -1)
577	{
578	  if (contents)
579	    free (contents);
580	  contents = (char *)NULL;
581	  filesys_error_number = errno;
582	}
583      else
584	{
585	  *filesize = convert_eols (contents, offset);
586	  contents = (char *)xrealloc (contents, 1 + *filesize);
587	  contents[*filesize] = '\0';
588	}
589    }
590  else
591    {
592      filesys_error_number = errno;
593    }
594
595#if !defined (BUILDING_LIBARARY)
596  if (info_windows_initialized_p)
597    unmessage_in_echo_area ();
598#endif /* !BUILDING_LIBRARY */
599  return (contents);
600}
601
602/* Return non-zero if FILENAME belongs to a compressed file. */
603int
604compressed_filename_p (char *filename)
605{
606  char *decompressor;
607
608  /* Find the final extension of this filename, and see if it matches one
609     of our known ones. */
610  decompressor = filesys_decompressor_for_file (filename);
611
612  if (decompressor)
613    return (1);
614  else
615    return (0);
616}
617
618/* Return the command string that would be used to decompress FILENAME. */
619char *
620filesys_decompressor_for_file (char *filename)
621{
622  register int i;
623  char *extension = (char *)NULL;
624
625  /* Find the final extension of FILENAME, and see if it appears in our
626     list of known compression extensions. */
627  for (i = strlen (filename) - 1; i > 0; i--)
628    if (filename[i] == '.')
629      {
630        extension = filename + i;
631        break;
632      }
633
634  if (!extension)
635    return ((char *)NULL);
636
637  for (i = 0; compress_suffixes[i].suffix; i++)
638    if (FILENAME_CMP (extension, compress_suffixes[i].suffix) == 0)
639      return (compress_suffixes[i].decompressor);
640
641#if defined (__MSDOS__)
642  /* If no other suffix matched, allow any extension which ends
643     with `z' to be decompressed by gunzip.  Due to limited 8+3 DOS
644     file namespace, we can expect many such cases, and supporting
645     every weird suffix thus produced would be a pain.  */
646  if (extension[strlen (extension) - 1] == 'z' ||
647      extension[strlen (extension) - 1] == 'Z')
648    return "gunzip";
649#endif
650
651  return ((char *)NULL);
652}
653
654/* The number of the most recent file system error. */
655int filesys_error_number = 0;
656
657/* A function which returns a pointer to a static buffer containing
658   an error message for FILENAME and ERROR_NUM. */
659static char *errmsg_buf = (char *)NULL;
660static int errmsg_buf_size = 0;
661
662char *
663filesys_error_string (char *filename, int error_num)
664{
665  int len;
666  char *result;
667
668  if (error_num == 0)
669    return ((char *)NULL);
670
671  result = strerror (error_num);
672
673  len = 4 + strlen (filename) + strlen (result);
674  if (len >= errmsg_buf_size)
675    errmsg_buf = (char *)xrealloc (errmsg_buf, (errmsg_buf_size = 2 + len));
676
677  sprintf (errmsg_buf, "%s: %s", filename, result);
678  return (errmsg_buf);
679}
680
681
682/* Check for "dir" with all the possible info and compression suffixes,
683   in combination.  */
684
685int
686is_dir_name (char *filename)
687{
688  unsigned i;
689
690  for (i = 0; info_suffixes[i]; i++)
691    {
692      unsigned c;
693      char trydir[50];
694      strcpy (trydir, "dir");
695      strcat (trydir, info_suffixes[i]);
696
697      if (strcasecmp (filename, trydir) == 0)
698        return 1;
699
700      for (c = 0; compress_suffixes[c].suffix; c++)
701        {
702          char dir_compressed[50]; /* can be short */
703          strcpy (dir_compressed, trydir);
704          strcat (dir_compressed, compress_suffixes[c].suffix);
705          if (strcasecmp (filename, dir_compressed) == 0)
706            return 1;
707        }
708    }
709
710  return 0;
711}
712