home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / umich / tex / tex31 / texsrc.lzh / COMMON.LZH / EXTRA.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-01-13  |  16.8 KB  |  759 lines

  1. /* External procedures for these programs.  */
  2.  
  3. /* This includes <stdio.h> and "site.h".  */
  4. #include "extra.h"
  5.  
  6. extern unsigned char xord[], xchr[];
  7.  
  8. /* These help to deal with Pascal vs. C strings.  */
  9. void make_c_string (), make_pascal_string ();
  10. void end_with_null (), end_with_space ();
  11.  
  12.  
  13.  
  14. /* Round R to the nearest whole number.  */
  15.  
  16. integer
  17. zround (r)
  18.   double r;
  19. {
  20.   return r >= 0.0 ? (r + 0.5) : (r - 0.5);
  21. }
  22.  
  23.  
  24.  
  25. /* File operations.  */
  26.  
  27. /* Open a file; don't return if any error occurs.  NAME is a Pascal
  28.    string, but is changed to a C string and not changed back.  */ 
  29.  
  30. FILE *
  31. checked_fopen (name, mode)
  32.   char *name;
  33.   char *mode;
  34. {
  35.   FILE *result;
  36.   char *cp;
  37.  
  38.   make_c_string (&name);
  39.   
  40.   result = fopen (name, mode);
  41.   if (result != NULL)
  42.     return result;
  43.     
  44.   perror (name);
  45.   exit (1);
  46.   /*NOTREACHED*/
  47. }
  48.  
  49.  
  50. /* Return true if we're at the end of FILE, else false.  This implements
  51.    Pascal's `eof' builtin.  */
  52.  
  53. boolean
  54. test_eof (file)
  55.   FILE *file;
  56. {
  57.   register int c;
  58.  
  59.   /* Maybe we're already at the end?  */
  60.   if (feof (file))
  61.     return true;
  62.  
  63.   if ((c = getc (file)) == EOF)
  64.     return true;
  65.  
  66.   /* Whoops, we weren't at the end.  Back up.  */
  67.   (void) ungetc (c, file);
  68.  
  69.   return false;
  70. }
  71.  
  72.  
  73. /* Return true on end-of-line in FILE or at the end of FILE, else false.  */
  74.  
  75. boolean
  76. eoln (file)
  77.   FILE *file;
  78. {
  79.   register int c;
  80.  
  81.   if (feof (file))
  82.     return true;
  83.   
  84.   c = getc (file);
  85.   
  86.   if (c != EOF)
  87.     (void) ungetc (c, file);
  88.     
  89.   return c == '\n' || c == EOF;
  90. }
  91.  
  92.  
  93. /* Print real number R in the Pascal format N:M on the file F.  */
  94.  
  95. void
  96. fprintreal (f, r, n, m)
  97.   FILE *f;
  98.   double r;
  99.   int n, m;
  100. {
  101.   char fmt[50];  /* Surely enough, since N and M won't be more than 25
  102.                     digits each!  */
  103.  
  104.   (void) sprintf (fmt, "%%%d.%dlf", n, m);
  105.   (void) fprintf (f, fmt, r);
  106. }
  107.  
  108.  
  109. /* Print S, a Pascal string, on the file F.  It starts at index 1 and is
  110.    terminated by a space.  */
  111.  
  112. static void
  113. fprint_pascal_string (s, f)
  114.   char *s;
  115.   FILE *f;
  116. {
  117.   s++;
  118.   while (*s != ' ') putc (*s++, f);
  119. }
  120.  
  121. /* Print S, a Pascal string, on stdout.  */
  122.  
  123. void
  124. printpascalstring (s)
  125.   char *s;
  126. {
  127.   fprint_pascal_string (s, stdout);
  128. }
  129.  
  130. /* Ditto, for stderr.  */
  131.  
  132. void
  133. errprintpascalstring (s)
  134.   char *s;
  135. {
  136.   fprint_pascal_string (s, stderr);
  137. }
  138.  
  139. /* Read an integer from the file F, reading past the subsequent end of
  140.    line.  */
  141.  
  142. integer
  143. inputint (f)
  144.   FILE *f;
  145. {
  146.   char buffer[50]; /* Long enough for anything reasonable.  */
  147.  
  148.   return
  149.     fgets (buffer, sizeof (buffer), f)
  150.     ? atoi (buffer)
  151.     : 0;
  152. }
  153.  
  154. /* Read three integers from stdin.  */
  155.  
  156. void
  157. zinput3ints (a,b,c)
  158.   integer *a, *b, *c;
  159. {
  160.   while (scanf ("%ld %ld %ld\n", a, b, c) != 3)
  161.     (void) fprintf (stderr, "Please enter three integers.\n");
  162. }
  163.  
  164.  
  165.  
  166. /* String operations.  */
  167.  
  168. /* Change the suffix of BASE (a Pascal string) to be SUFFIX (another
  169.    Pascal string).  We have to change them to C strings to do the work,
  170.    then convert them back to Pascal strings.  */
  171.  
  172. void
  173. makesuffix (base, suffix)
  174.   char *base;
  175.   char *suffix;
  176. {
  177.   char *last_dot, *last_slash;
  178.   make_c_string (&base);
  179.   
  180.   last_dot = rindex (base, '.');
  181. #ifdef atarist
  182.   (last_slash = rindex (base, '/')) || (last_slash = rindex (base, '\\'));
  183. #else
  184.   last_slash = rindex (base, '/');
  185. #endif
  186.  
  187.   if (last_dot == NULL || last_dot < last_slash) /* Add the suffix?  */
  188.     {
  189.       make_c_string (&suffix);
  190.       strcat (base, suffix);
  191.       make_pascal_string (&suffix);
  192.     }
  193.     
  194.   make_pascal_string (&base);
  195. }
  196.    
  197.    
  198. /* Deal with C and Pascal strings.  */
  199.  
  200. /* Change the Pascal string P_STRING into a C string; i.e., make it
  201.    start after the leading character Pascal puts in, and terminate it
  202.    with a null.  We also have to convert from the internal character set
  203.    (ASCII) to the external character set, if we're running on a
  204.    non-ASCII system.  */
  205.  
  206. void
  207. make_c_string (p_string)
  208.   char **p_string;
  209. {
  210.   (*p_string)++;
  211.   end_with_null (*p_string);
  212. }
  213.  
  214.  
  215. /* Replace the first space we come to with a null.  */
  216.  
  217. void
  218. end_with_null (s)
  219.   char *s;
  220. {
  221.   for ( ; *s != ' '; s++)
  222. #ifdef NONASCII
  223.     *s = xchr[*s]
  224. #endif
  225.     ;
  226.  
  227.   *s = 0;
  228. }
  229.  
  230.  
  231. /* Change the C string C_STRING into a Pascal string; i.e., make it
  232.    start one character before it does now (so C_STRING had better have
  233.    been a Pascal string originally), and terminate with a space.  We
  234.    also convert back from the external character set to the internal
  235.    character set (ASCII), if we're running on a non-ASCII system.  */
  236.  
  237. void
  238. make_pascal_string (c_string)
  239.   char **c_string;
  240. {
  241.   end_with_space (*c_string);
  242.   (*c_string)--;
  243. }
  244.  
  245.  
  246. /* Replace the first null we come to with a space.  */
  247.  
  248. void
  249. end_with_space (s)
  250.   char *s;
  251. {
  252.   for ( ; *s != 0; s++)
  253. #ifdef NONASCII
  254.     *s = xchr[*s]
  255. #endif
  256.     ;
  257.   
  258.   *s = ' ';
  259. }
  260.  
  261.  
  262. static char *
  263. concat (s1, s2)
  264.   char *s1, *s2;
  265. {
  266.   char *r = xmalloc (strlen (s1) + strlen (s2) + 1);
  267.   strcpy (r, s1);
  268.   strcat (r, s2);
  269.   return r;
  270. }
  271.  
  272.  
  273. static char *
  274. string_copy (char *s)
  275. {
  276.   char *new_string = xmalloc (strlen (s) + 1);
  277.  
  278.   strcpy (new_string, s);
  279.  
  280.   return new_string;
  281. }
  282.  
  283.  
  284.  
  285. /* Memory operations: variants of malloc(3) and realloc(3) that just
  286.    give up the ghost when they fail.  */
  287.  
  288. extern char *malloc (), *realloc ();
  289.  
  290. char *
  291. xmalloc (size)
  292.   unsigned size;
  293. {
  294.   char *mem = malloc (size);
  295.   
  296.   if (mem == NULL)
  297.     {
  298.       fprintf (stderr, "! Cannot allocate %u bytes.\n", size);
  299.       exit (10);
  300.     }
  301.   
  302.   return mem;
  303. }
  304.  
  305.  
  306. char *
  307. xrealloc (ptr, size)
  308.   char *ptr;
  309.   unsigned size;
  310. {
  311.   char *mem = realloc (ptr, size);
  312.   
  313.   if (mem == NULL)
  314.     {
  315.       fprintf (stderr, "! Cannot reallocate %u bytes at %x.\n", size, ptr);
  316.       exit (10);
  317.     }
  318.     
  319.   return mem;
  320. }
  321.  
  322.  
  323.  
  324. /* Path searching.  */
  325.  
  326. #define NUMBER_OF_PATHS 11
  327.  
  328. static char *path[NUMBER_OF_PATHS];
  329. static char *env_var_names[NUMBER_OF_PATHS]
  330.   = { "BIBINPUTS",
  331.       "GFFONTS",
  332.       "MFBASES", "MFINPUTS", "MFPOOL",
  333.       "PKFONTS",
  334.       "TEXFORMATS", "TEXINPUTS", "TEXPOOL", "TEXFONTS", 
  335.       "VFFONTS"
  336.     };
  337.  
  338. #define READ_ACCESS 4       /* Used in access(2) call.  */
  339.  
  340. /* What separates elements of the path variables.  */
  341. #if defined(MS_DOS) || defined(atarist)
  342. #define PATH_DELIMITER ';'
  343. #define PATH_DELIMITER_STRING ";"
  344. #else
  345. #define PATH_DELIMITER ':'
  346. #define PATH_DELIMITER_STRING ":"
  347. #endif
  348.  
  349. /* We will need some system include files to deal with directories, even
  350.    when SEARCH_SUBDIRECTORIES is undefined.  (We also make sure we don't
  351.    open a directory as an input file.  */
  352. #include <sys/types.h>
  353. #include <sys/stat.h>
  354.  
  355. /* And if we're actually going to search subdirectorys, we need still
  356.    more include files.  */
  357. #ifdef SEARCH_SUBDIRECTORIES
  358. #ifndef BSD
  359. #include <dirent.h>
  360. typedef struct dirent *directory_entry_type;
  361. #else /* BSD */
  362. #include <sys/dir.h>
  363. typedef struct direct *directory_entry_type;
  364. #endif /* BSD */
  365.  
  366. /* Declare the routine to get the current working directory.  */
  367.  
  368. #ifdef HAVE_GETWD
  369. extern char *getwd ();
  370. #define getcwd(b, len)  ((b) ? getwd (b) : getwd (xmalloc (len)))
  371. #else
  372. #ifdef ANSI
  373. extern char *getcwd (char *, int);
  374. #else
  375. extern char *getcwd ();
  376. #endif /* not ANSI */
  377. #endif /* not HAVE_GETWD */
  378.  
  379. extern int errno;
  380. #endif /* SEARCH_SUBDIRECTORIES */
  381.  
  382.  
  383. /* Subroutines.  */
  384. static void next_component ();
  385. extern int is_dir ();
  386.  
  387. /* Says whether NAME is ok to open for reading.  */
  388. #define READABLE_FILE(name) access (name, READ_ACCESS) == 0 && !is_dir (name)
  389.  
  390.  
  391. /* Replace a leading or trailing `:' in DIR_LIST with DEFAULT_VALUE. 
  392.    The result is always dynamically allocated.  */
  393.  
  394. static char *
  395. expand_colon (dir_list, default_value)
  396.   char *dir_list;
  397.   char *default_value;
  398. {
  399.   char *expansion = xmalloc (strlen (dir_list) + 1);
  400.   strcpy (expansion, dir_list);
  401.  
  402.   if (*expansion == PATH_DELIMITER)
  403.     {
  404.       char *temp = expansion;
  405.       expansion = concat (default_value, expansion);
  406.       free (temp);
  407.     }
  408.   if (*(expansion + strlen (expansion) - 1) == PATH_DELIMITER)
  409.     {
  410.       char *temp = expansion;
  411.       expansion = concat (expansion, default_value);
  412.       free (temp);
  413.     }
  414.   
  415.   return expansion;
  416. }
  417.  
  418.  
  419. /* This routine initializes `path[PATH_INDEX]'.  If the environment
  420.    variable `env_var_names[PATH_INDEX]' is not set, we simply use 
  421.    DEFAULT_VALUE.  Otherwise, we use the value of the environment
  422.    variable, and then replace a leading or trailing colon with
  423.    DEFAULT_VALUE.  The result is always dynamically allocated.
  424.  
  425.    For example, if DEFAULT_VALUE is `foo', and the environment variable
  426.    value is `:bar:baz:', the final result will be `foo:bar:baz:foo'.
  427.    (Of course, it is pointless to have more than one extra `:' in
  428.    practice.)  */
  429.    
  430. static void
  431. do_path (path_index, default_value)
  432.   unsigned path_index;
  433.   char *default_value;
  434. {
  435.   char *temp = getenv (env_var_names[path_index]);
  436.  
  437.   path[path_index] = temp == NULL
  438.                      ? string_copy (default_value)
  439.                      : expand_colon (temp, default_value); 
  440. }
  441.  
  442.  
  443. #ifdef SEARCH_SUBDIRECTORIES
  444. static char *
  445. concat3 (s1, s2, s3)
  446.   char *s1, *s2, *s3;
  447. {
  448.   char *r = xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
  449.   strcpy (r, s1);
  450.   strcat (r, s2);
  451.   strcat (r, s3);
  452.   return r;
  453. }
  454.  
  455.  
  456. /* DIR_LIST is the default list of directories (colon-separated) to
  457.    search.  If the environment variable
  458.    "`env_var_names[PATH_INDEX]'_SUBDIR" exists, we use that instead.  We
  459.    want to add all the subdirectories directly below each of the
  460.    directories in the path.
  461.      
  462.    We return the list of directories found.
  463.    
  464.    By doing this all at the beginning of the program, we make the
  465.    programs that look for only one file somewhat less efficient but this
  466.    is more efficient when we look up many files using the same path.  */
  467.  
  468. static char *
  469. do_subdir_path (path_index, dir_list)
  470.   unsigned path_index;
  471.   char *dir_list;
  472. {
  473.   char *cwd;
  474.   unsigned len;
  475.   char *result = xmalloc (1);
  476.   char *env_var = concat (env_var_names[path_index], "_SUBDIR");
  477.   char *temp = getenv (env_var);
  478.  
  479.   free (env_var);
  480.   
  481.   if (temp == NULL)
  482.     {
  483.       temp = dir_list;
  484.       /* Make a copy in writable memory.  */
  485.       dir_list = xmalloc (strlen (temp) + 1);
  486.       strcpy (dir_list, temp);
  487.     }
  488.   else
  489.     dir_list = expand_colon (temp, dir_list);
  490.  
  491.   *result = 0;
  492.  
  493.   /* Unfortunately, we can't look in the environment for the current
  494.      directory, because if we are running under a program (let's say
  495.      Emacs), the PWD variable might have been set by Emacs' parent
  496.      to the current directory at the time Emacs was invoked.  This
  497.      is not necessarily the same directory the user expects to be
  498.      in.  So, we must always call getcwd(3) or getwd(3), even though
  499.      they are slow and prone to hang in networked installations.  */
  500.   cwd = getcwd (NULL, FILENAMESIZE + 2);
  501.   if (cwd == NULL)
  502.     {
  503.       perror ("getcwd");
  504.       exit (errno);
  505.     }
  506.  
  507.   do
  508.     {
  509.       DIR *dir;
  510.       directory_entry_type e;
  511.       char dirname[FILENAMESIZE];
  512.  
  513.       next_component (dirname, &dir_list);
  514.  
  515.       /* All the `::'s should be gone by now, but we may as well make
  516.          sure `chdir' doesn't crash.  */
  517.       if (*dirname == 0) continue;
  518.  
  519.       /* By changing directories, we save a bunch of string
  520.          concatenations (and make the pathnames the kernel looks up
  521.          shorter).  */
  522.       if (chdir (dirname) != 0) continue;
  523.  
  524.       dir = opendir (".");
  525.       if (dir == NULL) continue;
  526.  
  527.       while ((e = readdir (dir)) != NULL)
  528.         {
  529.           if (is_dir (e->d_name) && strcmp (e->d_name, ".") != 0
  530.               && strcmp (e->d_name, "..") != 0)
  531.             {
  532.               char *found = concat3 (dirname, "/", e->d_name);
  533.  
  534.               result = xrealloc (result, strlen (result) + strlen (found) + 2);
  535.  
  536.               len = strlen (result);
  537.               if (len > 0)
  538.                 {
  539.                   result[len] = PATH_DELIMITER;
  540.                   result[len + 1] = 0;
  541.                 }
  542.               strcat (result, found);
  543.               free (found);
  544.             }
  545.         }
  546.       closedir (dir);
  547.  
  548.       /* Change back to the current directory, in case the path
  549.          contains relative directory names.  */
  550.       if (chdir (cwd) != 0)
  551.         {
  552.           perror (cwd);
  553.           exit (errno);
  554.         }
  555.     }
  556.   while (*dir_list != 0);
  557.   
  558.   len = strlen (path[path_index]);
  559.   path[path_index] = xrealloc (path[path_index], len + strlen (result) + 2);
  560.   *(path[path_index] + len) = PATH_DELIMITER;
  561.   *(path[path_index] + len + 1) = 0;
  562.   strcat (path[path_index], result);
  563.   
  564.   return result;
  565. }
  566. #endif /* SEARCH_SUBDIRECTORIES */
  567.  
  568.  
  569. /* This sets up the paths, by either copying from an environment variable
  570.    or using the default path, which is defined as a preprocessor symbol
  571.    (with the same name as the environment variable) in `site.h'.  The
  572.    parameter PATH_BITS is a logical or of the paths we need to set.  */
  573.  
  574. extern void
  575. setpaths (path_bits)
  576.   int path_bits;
  577. {
  578. #if defined(SEARCH_SUBDIRECTORIES) && defined(TEXFONTS_SUBDIR)
  579.   char *font_subdirs;
  580. #endif
  581.  
  582.   /* We must assign to the TFM file path before doing any of the other
  583.      font paths, since it is used as a default.  */
  584.   if (path_bits
  585.       & (TFMFILEPATHBIT | GFFILEPATHBIT | PKFILEPATHBIT | VFFILEPATHBIT))
  586.     {
  587.       do_path (TFMFILEPATH, TEXFONTS);
  588. #if defined(SEARCH_SUBDIRECTORIES) && defined(TEXFONTS_SUBDIR)
  589.       font_subdirs = do_subdir_path (TFMFILEPATH, TEXFONTS_SUBDIR);
  590. #endif
  591.     }
  592.  
  593.   if (path_bits & BIBINPUTPATHBIT)
  594.     do_path (BIBINPUTPATH, BIBINPUTS);
  595.  
  596.   if (path_bits & GFFILEPATHBIT)
  597.     {
  598.       do_path (GFFILEPATH, path[TFMFILEPATH]);
  599. #if defined(SEARCH_SUBDIRECTORIES) && defined(TEXFONTS_SUBDIR)
  600.       path[GFFILEPATH] = concat3 (path[GFFILEPATH],
  601.                   PATH_DELIMITER_STRING,
  602.                   font_subdirs);
  603. #endif
  604.     }
  605.  
  606.   if (path_bits & MFBASEPATHBIT)
  607.     do_path (MFBASEPATH, MFBASES);
  608.  
  609.   if (path_bits & MFINPUTPATHBIT)
  610.     {
  611.       do_path (MFINPUTPATH, MFINPUTS);
  612. #if defined(SEARCH_SUBDIRECTORIES) && defined(MFINPUTS_SUBDIR)
  613.       (void) do_subdir_path (MFINPUTPATH, MFINPUTS_SUBDIR);
  614. #endif
  615.     }
  616.  
  617.   if (path_bits & MFPOOLPATHBIT)
  618.     do_path (MFPOOLPATH, MFPOOL);
  619.  
  620.   if (path_bits & PKFILEPATHBIT)
  621.     {
  622.       do_path (PKFILEPATH, path[TFMFILEPATH]);
  623. #if defined(SEARCH_SUBDIRECTORIES) && defined(TEXFONTS_SUBDIR)
  624.       path[PKFILEPATH] = concat3 (path[PKFILEPATH],
  625.                   PATH_DELIMITER_STRING,
  626.                   font_subdirs);
  627. #endif
  628.     }
  629.  
  630.   if (path_bits & TEXFORMATPATHBIT)
  631.     do_path (TEXFORMATPATH, TEXFORMATS);
  632.  
  633.   if (path_bits & TEXINPUTPATHBIT)
  634.     {
  635.       do_path (TEXINPUTPATH, TEXINPUTS);
  636. #if defined(SEARCH_SUBDIRECTORIES) && defined(TEXINPUTS_SUBDIR)
  637.       (void) do_subdir_path (TEXINPUTPATH, TEXINPUTS_SUBDIR);
  638. #endif
  639.     }
  640.  
  641.   if (path_bits & TEXPOOLPATHBIT)
  642.     do_path (TEXPOOLPATH, TEXPOOL);
  643.  
  644.   /* Some sites want to have a system default for VFFONTS, and others
  645.      don't.  */
  646.   if (path_bits & VFFILEPATHBIT)
  647.     {
  648. #ifdef VFFONTS
  649.       do_path (VFFILEPATH, VFFONTS);
  650. #else
  651.       do_path (VFFILEPATH, path[TFMFILEPATH]);
  652. #endif
  653. #if defined(SEARCH_SUBDIRECTORIES) && defined(TEXFONTS_SUBDIR)
  654.       path[VFFILEPATH] = concat3 (path[VFFILEPATH],
  655.                   PATH_DELIMITER_STRING,
  656.                   font_subdirs);
  657. #endif
  658.     }
  659. }
  660.  
  661.  
  662. /* Look for NAME, a Pascal string, in a colon-separated list of
  663.    directories.  The path to use is given (indirectly) by PATH_INDEX.
  664.    If the search is successful, leave the full pathname in NAME (which
  665.    therefore must have enough room for such a pathname), padded with
  666.    blanks.  Otherwise, or if NAME is an absolute or relative pathname,
  667.    just leave it alone.  */
  668.  
  669. boolean
  670. testreadaccess (name, path_index)
  671.   char *name;
  672.   int path_index;
  673. {
  674.   char potential[FILENAMESIZE];
  675.   int ok = 0;
  676.   char *the_path = path[path_index];
  677.   char *saved_path = the_path;
  678.   char *ptr;
  679.   
  680.   make_c_string (&name);
  681.  
  682.   ptr = name;
  683.   while (ptr = index(ptr, '\\'))
  684.     *ptr = '/';
  685.       
  686.   if (*name == '/'
  687.       || (*name == '.' && *(name + 1) == '/' )
  688.       || (*name == '.' && *(name + 1) == '.' && *(name + 2) == '/' )
  689.       || (((*name >= 'a' && *name <= 'z')
  690.        || (*name >= 'A' && *name <= 'Z')) && *(name + 1) == ':'))
  691.     ok = READABLE_FILE (name); 
  692.   else
  693.     {
  694.       do
  695.     {
  696.       next_component (potential, &the_path);
  697.  
  698.           if (*potential != 0)
  699.             {
  700.               strcat (potential, "/");
  701.           strcat (potential, name);
  702.           ok = READABLE_FILE (potential);
  703.             }
  704.     }
  705.       while (!ok && *the_path != 0);
  706.  
  707.       /* If we found it, leave the answer in NAME.  */
  708.       if (ok)
  709.         strcpy (name, potential);
  710.     }
  711.   
  712.   ptr = name;
  713.   while (ptr = index(ptr, '\\'))
  714.     *ptr = '/';
  715.       
  716.   make_pascal_string (&name);
  717.   
  718.   return ok;
  719. }
  720.  
  721.  
  722. /* Return, in NAME, the next component of PATH, i.e., the characters up
  723.    to the next PATH_DELIMITER.  */
  724.    
  725. static void
  726. next_component (name, path)
  727.   char name[];
  728.   char **path;
  729. {
  730.   unsigned count = 0;
  731.   
  732.   while (**path != 0 && **path != PATH_DELIMITER)
  733.     {
  734.       name[count++] = **path;
  735.       (*path)++; /* Move further along, even between calls.  */
  736.     }
  737.   
  738.   name[count] = 0;
  739.   if (**path == PATH_DELIMITER)
  740.     (*path)++; /* Move past the delimiter.  */
  741. }
  742.  
  743.  
  744. #if !defined(S_ISDIR)
  745. #define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
  746. #endif
  747.  
  748. /* Return true if FN is a directory or a symlink to a directory,
  749.    false if not. */
  750.  
  751. int
  752. is_dir (fn)
  753.   char *fn;
  754. {
  755.   struct stat stats;
  756.  
  757.   return stat (fn, &stats) == 0 && S_ISDIR (stats.st_mode);
  758. }
  759.