

/*********************************************************************/
/* Author: Suzanne Miller Dorney                                     */
/* File: lab6.c                                                      */
/* Class:  CPSC-202 lab section 1                                    */
/* Project:  Lab 6                                                   */
/* Purpose:  This program will implement a modified version of the   */
/*           UNIX ls command.                                        */
/*********************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>

/* global declaration of structure */
typedef struct{
  struct stat fileStat;
  char        fileName[50];
}fileStructure;

#define MAX_FILES 100

/* function prototypes */
char typeOfFile( mode_t );
char *permOfFile( mode_t );
void outputStatInfo( char *, struct stat * );
void outputLSStyle( char *, char *, struct stat * );
int  addFile( fileStructure list[], char *fileName,
              struct stat *st, int *cnt, int sizeFlag );

/* main function */
int main( int argc, char *argv[] )
{
  struct stat    st;             /* status structure              */
  fileStructure  fileList[MAX_FILES];  
                                 /* list of files in a directory  */
  DIR            *dp;            /* directory pointer             */
  struct dirent  *dirp;          /* directory structure pointer   */
  char           fullName[4096]; /* full path name for each file  */
  int            fileCnt;        /* number of files in the dir.   */
  int            totalBlocks;    /* total number of blocks        */
  int            i;              /* loop control variable         */
  char           *directoryName; /* name of the directory to list */
  int            orderOnSize;    /* order on file size flag       */ 

  /* Process the command line arguments                          */
  if ( argc == 1 )
  {
    directoryName = strdup( getenv( "PWD" ) );
    orderOnSize = 0;
  }
  else 
    if ( argc == 2 )
    {
      if ( strcmp( argv[1], "-size" ) == 0 )
      {
        directoryName = strdup( getenv( "PWD" ) );
        orderOnSize = 1;
      }
      else
      {
        directoryName = strdup( argv[1] );
        orderOnSize = 0;
      }
    }
    else 
    {
      if ( strcmp( argv[1], "-size" ) == 0 )
      {
        directoryName = strdup( argv[2] );
        orderOnSize = 1;
      }
      else
      {
        if ( strcmp( argv[2], "-size" ) == 0 )
        {
          directoryName = strdup( argv[1] );
          orderOnSize = 1;
        }
        else
          printf( "Invalid argument list to function.  Program terminated\n" );
      }
    }

  /* open the directory */
  if ( ( dp = opendir( directoryName ) ) == NULL )
  {
    printf( "Cannot open directory %s\n", directoryName );
    exit( 1 );
  }

  /* initialize variables */ 
  fileCnt = 0;
  totalBlocks = 0;

  /* for each file listed in the directory */ 
  while ( ( dirp = readdir( dp ) ) != NULL )
  {
    fullName[0] = '\0';   
    strcpy( fullName, directoryName );
    strcat( fullName, "/" );
    strcat( fullName, dirp->d_name );

    if ( lstat( fullName, &st ) < 0 )
    {
      perror( fullName );
      putchar( '\n' );
      continue; 
    }

    if ( addFile( fileList, dirp->d_name, &st, &fileCnt, orderOnSize ) )
      totalBlocks += st.st_blocks;
  }

  printf( "total %d\n", totalBlocks );
  for ( i=0; i<fileCnt; i++ )
  { 
    /* output the information */
    outputLSStyle( directoryName, fileList[i].fileName, &fileList[i].fileStat );
    putchar( '\n' );
  }
  return 0;
}

/****************************************************************/
/* function to add fileStructures ordered on the fileName field */
/****************************************************************/
int  addFile( fileStructure list[], char *fileName, struct stat *st, 
               int *cnt, int sizeFlag )
{
  int testValue;
  int i = *cnt - 1; 

  if ( *cnt == 0 )
  {
    list[0].fileStat = *st;
    strcpy( list[0].fileName, fileName );
    *cnt = 1;
    return 1;
  }

  /* make sure there is sufficient room in array */
  if ( *cnt == MAX_FILES-1 )
  {
    printf( "Too many files in directory" );
    return 0;
  }

  if ( sizeFlag )
    testValue = st->st_size < list[i].fileStat.st_size;
  else
    testValue = strcmp( fileName, list[i].fileName ) < 0;

  while ( i>=0 && testValue )
  {
    strcpy( list[i+1].fileName, list[i].fileName );
    list[i+1].fileStat = list[i].fileStat;
    i--;
    if ( sizeFlag )
      testValue = st->st_size < list[i].fileStat.st_size;
    else
      testValue = strcmp( fileName, list[i].fileName ) < 0;
  }

  strcpy( list[i+1].fileName, fileName );
  list[i+1].fileStat = *st;

  (*cnt)++;

  return 1;
}

/*****************************************************************/
/* outputLSStype - print out the contents of the stat structure  */
/*                 in the format of the ls command.              */
/*****************************************************************/
void outputLSStyle( char *directoryName, char *filename, struct stat *st )
{
  char *tempTime;               /* string to hold the current time     */
  char buf[100];                /* temp buffer                         */
  char login[16], group[16];    /* strings to hold user name and group */
  struct group *gr;             /* group entry matching group-id       */
  struct passwd *pw;            /* password entry matching user-id     */
  int    i;                     /* loop control variable               */

  /* print out the first two columns of information */
  printf( "%c", typeOfFile( st->st_mode ) );
  printf( "%s", permOfFile( st->st_mode ) );
  printf( "  %ld", st->st_nlink );

  /* print out the owner's login name or number if it cannot be found */
  if ( ( pw = getpwuid( st->st_uid ) ) != NULL )
    strcpy( login, pw->pw_name );
  else
    sprintf( login, "%ld", st->st_uid );
  printf( "  %s", login );

  /* print out the owner's group name or number if it cannot be found */
  if ( ( gr = getgrgid( st->st_gid ) ) != NULL )
    strcpy( group, gr->gr_name );
  else
    sprintf( group, "%ld", st->st_gid );
  printf( "  %s", group );

  /* print file size */
  printf( " %8ld", st->st_size );

  /* print out the modified time of the file */
  tempTime = ctime( &st->st_mtime );

  tempTime[strlen(tempTime)-1] = '\0'; 
  for ( i=0; i<strlen( tempTime ); i++ )
    tempTime[i] = tempTime[i+3];
  tempTime[i-9] = '\0';
  printf( " %s", tempTime );

  /* print out the file name */
  printf( " %s", filename );

  /* print out the / for a directory, * for an executable,  */ 
  /*   the path for a symbolic link                         */
  if ( typeOfFile( st->st_mode ) == 'd' )
    printf( "/" );
  else if ( typeOfFile( st->st_mode ) == 'l' )
  {
    tempTime = directoryName;
    strcat( tempTime, "/" );
    strcat( tempTime, filename );

    if ( readlink( tempTime,   buf, 100 ) < 0 )
      perror( "link" );
    else
      printf( " -> %s", buf );
  }
  if ( typeOfFile( st->st_mode ) != 'd' &&
       strstr( permOfFile( st->st_mode ), "x" ) )
    printf( "*" ); 
}
 
/*********************************************************/
/* typeOfFile - return a string indicating the tile type */
/*********************************************************/
char typeOfFile ( mode_t mode )
{
  switch( mode & S_IFMT )
  {
    case S_IFREG:
      return '-';
    case S_IFDIR:
      return 'd';
    case S_IFCHR:
      return '-';
    case S_IFBLK:
      return '-';
    case S_IFLNK:
      return 'l';
    case S_IFIFO:
      return '-';
    case S_IFSOCK:
      return '-';
  }

  return '?';
}

/********************************************************************/
/* permOfFIle - return the file permissions in an "ls"-like string. */
/********************************************************************/
char * permOfFile( mode_t mode )
{
  int i;                   /* loop control variable          */
  char *p;                 /* pointer to step through string */
  static char perms[10];   /* permission string              */

  p = perms;
  strcpy( perms, "---------" );

  /*
   * The permission bits are three sets of three bits:
   * user read/write/exec, group read/write/exec, other
   * read/write/exec.  We deal with each set of three
   * bits in one pass through the loop.
   */

  for ( i=0; i<3; i++ )
  {
    if ( mode & ( S_IREAD >> i*3 ) )
      *p = 'r';
    p++;

    if ( mode & ( S_IWRITE >> i*3 ) )
      *p = 'w';
    p++;

    if ( mode & ( S_IEXEC >> i*3 ) )
      *p = 'x';
    p++;
  }

  /*
   * Put special codes in for set-user-id, set-group-id,
   * and the sticky bit.
   */

  if ( ( mode & S_ISUID ) != 0 )
    perms[2] = 's';

  if ( ( mode & S_ISGID ) != 0 )
    perms[5] = 's';

  if ( ( mode & S_ISVTX ) != 0 )
    perms[8] = 't';

  return perms;
}
