 /*
  * Khoros: $Id: listdir.c,v 1.2 1991/12/18 09:54:57 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: listdir.c,v 1.2 1991/12/18 09:54:57 dkhoros Exp $";
#endif

 /*
  * $Log: listdir.c,v $
 * Revision 1.2  1991/12/18  09:54:57  dkhoros
 * HellPatch3
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * 
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *----------------------------------------------------------------------
 */

#include "unmcopyright.h"        /* Copyright 1990 by UNM */
#include "vgparm.h"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>     file name: listdir.c                              <<<<
   >>>>                                                       <<<<
   >>>>   description: File Utility                           <<<<
   >>>>                                                       <<<<
   >>>>      routines: vlistdir()                             <<<<
   >>>>                                                       <<<<
   >>>> modifications:                                        <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


#define MaxEntries 10

/********************************************************
*
*  MODULE NAME: vlistdir
*
*     PURPOSE: This module is used to create a list of files
*	       according to a user supplied basename and a
*	       initial global directory.  The list mode is used
*	       to indicate what we are going to list in the
*	       directory.  The possible defines are listed in
*	       KHOROS_HOME/include/vdefines.h.  The following
*	       is the current list mode:
*
*			XV_PATH		- prepend the path to each file
*			XV_FILE		- list plain text files
*			XV_DIR		- list directories
*			XV_DOT		- list dot files
*			XV_LINK		- list symbolic files
*			XV_SOCK		- list socket files
*
*			XV_READ		- file is readable by caller
*			XV_WRITE	- file is writable by caller
*			XV_EXEC		- file is executable by caller
*
*		The selections are or'ed together in order to choose
*		which attribute is desired.  ie) (XV_FILE | XV_DIR)
*		will list only files and directories that match
*		the basename.
*
*       INPUT:  basename    - base name of the files to be listed.
*		global_dir  - the global directory is used as a prefix
*			      to the basename
*		list_mode   - a flag indicating what we are to list
*			      (ie. files, directories, dot files, symbolic,
*				   links, etc).
*		format	    - whether the files should be prepended with
*			      type of file. (ie. "@" for sym links, "/" for
*			      directories).
*		num_entries - the number of entries in the list that
*			      are returned.
*
*      OUTPUT:  returns a list of strings to filenames that match
*		the basename
*
* CALLED FROM:
*
* WRITTEN BY:  Mark Young & Charlie Gage
*
********************************************************/


char **vlistdir(basename, global_dir, list_mode, format, num_entries)

char *basename, *global_dir;
int  list_mode, format,
     *num_entries;
{
	DIR	*dir;
	struct  stat buf;
	vdirect *file;
	int	mode, check_perm, size, base_len, num;
	char    *dirpath, *fullpath, entry[LENGTH], *vdirname(), **list;


	*num_entries = 0;
	if (global_dir == NULL && basename == NULL)
	{
	   (void) fprintf(stderr,"\nvlistdir:\n");
	   (void) fprintf(stderr,"   Error!  NULL input basename and directory \
 encountered.\n");
	   return(NULL);
	}

	/*
	 *  Expand the path to get the full path to the directory.
	 */
	if (VStrlen(basename) == 0)
	   fullpath = vfullpath(global_dir, NULL, NULL);
	else
	   fullpath = vfullpath(basename, global_dir, NULL);

	/*
	 *  Get the directory path to the file
	 */
	mode = stat(fullpath, &buf);
	if (mode == -1 || ((buf.st_mode & S_IFMT) != S_IFDIR))
	{
	   if ((dirpath = vdirname(fullpath)) == NULL)
	   {
	      free(fullpath);
	      return(NULL);
	   }
	   basename = vbasename(fullpath);
	}
	else
	{
	   basename = NULL;
	   dirpath  = VStrcpy(fullpath);
	}

	/*
	 *  Open the directory
	 */
	if ((dir = opendir(dirpath)) == NULL)
	{
	   free(fullpath); free(dirpath);
	   return(NULL);
	}
	base_len = VStrlen(basename);

	/*
	 *  Malloc the array of filenames.
	 */
	num  = 0;
	size = MaxEntries;
	if ((list = (char **) malloc(size * sizeof(char *))) == NULL)
	{
	   (void) fprintf(stderr,"\nvlistdir:\n");
	   (void) fprintf(stderr,"   Error!  Unable to allocate enough memory \
for directory list.\n");
	   free(fullpath); free(dirpath); closedir(dir);
	   return(NULL);
	}

	/*
	 *  Read the entries and see if the file compares to that of the
	 *  basename we are looking for.  If the basename is NULL then 
	 *  we add all regular files.
	 */
	check_perm = 0;
	if (list_mode & XV_READ)  check_perm |= R_OK;
	if (list_mode & XV_WRITE) check_perm |= W_OK;
	if (list_mode & XV_EXEC)  check_perm |= X_OK;
	while ((file = readdir(dir)) != NULL)
	{
	   if (basename != NULL)
	   {
	      if (strncmp(file->d_name, basename, base_len) != 0)
		 continue;
	   }
	   else if ((list_mode & XV_DOT) == 0 && file->d_name[0] == '.')
	   {
	      if (((list_mode & XV_DIR) && strcmp(file->d_name,"..") != 0) ||
		  ((list_mode & XV_DIR) == 0))
	         continue;
	   }

	   /*
	    *  Check to see if the user wants to look at file permissions.
	    *  If so then we check the access of the "check_perm" mode(s).
	    */
	   (void) sprintf(entry, "%s/%s", dirpath, file->d_name);
	   if (check_perm)
	   {
	      if (access(entry, check_perm) == -1)
	         continue;
	   }

	   /*
	    *  Need to get infomation (status) about the file.  If the user
	    *  wants to know about symbolic links then we'll call lstat()
	    *  otherwise we will call stat().
	    */
#ifndef SYSV
	   if (list_mode & XV_LINK)
	   {
	      if (lstat(entry, &buf) == -1)  /*  get stats for file */
	         continue;
	   }
	   else
#endif
	   {
	      if (stat(entry, &buf) == -1)  /*  get stats for file */
	         continue;
	   }

	   if ((list_mode & XV_PATH) == 0)
	      (void) sprintf(entry, "%s", file->d_name);

	   /*
	    *  Check the kind of file we have.  Make sure it part of the
	    *  list of files to be be listed.
	    */
	   mode = buf.st_mode & S_IFMT;
	   if ((mode == S_IFLNK) && (list_mode & XV_LINK))
	   {
	      if (format) strcat(entry,"@");
	   }
	   else if ((mode == S_IFDIR) && (list_mode & XV_DIR))
	   {
	      if (format) strcat(entry,"/");
	   }
	   else if ((mode == S_IFSOCK) && (list_mode & XV_SOCK))
	   {
	      if (format) strcat(entry,"=");
	   }
	   else if ((mode == S_IFREG) && (list_mode & XV_FILE) == 0)
	   {
	      continue;
	   }


	   /*
	    *  Make sure that we have enough room to add the filename to
	    *  the directory listing array.
	    */
	   if (num == size)
	   {
	      size += MaxEntries;
	      list = (char **) realloc(list, size * sizeof(char *));
	      if (list == NULL)
	      {
	         (void) fprintf(stderr,"\nvlistdir:\n");
	         (void) fprintf(stderr,"  Error!  Unable to allocate enough \
memory for directory list.\n");
		 closedir(dir);
	         return(NULL);
	       }
	   }
	   list[num++] = VStrcpy(entry);
	}

	/*
	 *  Cleanup time.  Free the fullpath and dirpath, and the basename if
	 *  currently allocated.  We need to make sure that the list of file-
	 *  names is greater than 0.  If not then free the list and set it to
	 *  NULL, otherwise we need to sort the entries into descending order.
	 */
	free(fullpath); free(dirpath);
	if (basename != NULL) free(basename);
	if (num == 0)
	{
	   free(list);
	   list = NULL;
	} 
	else
	{
	   list = vsortlist(list, num, TRUE);
	}
	*num_entries = num;
	closedir(dir);
	return(list);
}
