/** ls1.c
 **   Program wyswietla zawartosc katalogu biezacego
 **   lub katalogu wskazanego argumentem
 **/
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <unistd.h>

int longListing = 0;
void do_ls(char []);


int main(int argc, char *argv[])
{
  if (argc > 1 && (strcmp(argv[1], "-l") == 0))
  {
    longListing = 1;
    ++argv;
    --argc;
  }
  if ( argc == 1 )
    do_ls( "." );
  else
    while ( --argc ){
      printf("%s:\n", *++argv );
      do_ls( *argv );
    }
  return 0;
}

char mode_to_type(int mode)
{
	if ( S_ISDIR(mode) ) return 'd';
	if ( S_ISCHR(mode) ) return 'c';
	if ( S_ISBLK(mode) ) return 'b';
	if ( S_ISREG(mode) ) return 'f';
	if ( S_ISFIFO(mode) ) return 'i';
	if ( S_ISLNK(mode) ) return 'l';
	if ( S_ISSOCK(mode) ) return 's';
	return '?';
}

void mode_to_letters( int mode, char str[] )
{
	strcpy( str, "---------" );
	if ( mode & S_IRUSR ) str[0] = 'r';
	if ( mode & S_IWUSR ) str[1] = 'w';
	if ( mode & S_IXUSR ) str[2] = 'x';
	if ( mode & S_IRGRP ) str[3] = 'r';
	if ( mode & S_IWGRP ) str[4] = 'w';
	if ( mode & S_IXGRP ) str[5] = 'x';
	if ( mode & S_IROTH ) str[6] = 'r';
	if ( mode & S_IWOTH ) str[7] = 'w';
	if ( mode & S_IXOTH ) str[8] = 'x';
	if ( mode & S_ISUID ) str[2] = 's';
	if ( mode & S_ISGID ) str[5] = 's';
	if ( mode & S_ISVTX ) str[8] = 't';
}

char * zinterpretujRozmiar(off_t size)
{
	double rozm = size;
	unsigned int rozmBuf = 20;
	char * buf = malloc(sizeof(char *) * rozmBuf);
	char * sufiksy[] = {"bajtow", "kilobajtow", "megabajtow", "gigabajtow", "terabajtow" };
	int i;
	
	bzero(buf, rozmBuf);
	for(i = 0; rozm > 1024 && i < 5; ++i)
		rozm = rozm / 1024;
	snprintf(buf, rozmBuf, "%.2f %s", rozm, sufiksy[i]);
	return buf;
}

void show_stat_info(char *fname, struct stat *buf)
{
	char buf2[10];
	char * buf3;
	char * c;;
	mode_to_letters(buf->st_mode, buf2);
	printf("%c", mode_to_type(buf->st_mode));
	printf("%s", buf2);
/*	printf("Links: %d\n", (int) buf->st_nlink); */
	printf("\t%s:", getpwuid(buf->st_uid)->pw_name);
	printf("%s", getgrgid(buf->st_gid)->gr_name);
	printf("\t%s", zinterpretujRozmiar(buf->st_size));
/*	printf("Accestime: %s", ctime(&buf->st_atime)); */
	buf3 = ctime(&buf->st_mtime);
	c = strrchr(buf3, '\n');
	*c = '\0';
	printf("\t%s", buf3);
/*	printf("Createtime: %s", ctime(&buf->st_ctime)); */
	printf("\t%s\n", fname);
}

void fileDetails(char * sciezka)
{
    struct stat info;
    stat(sciezka, &info);
    show_stat_info(sciezka, &info);
}

static int cmpDirs(const void *p1, const void *p2)
{
  return strcmp((*(struct dirent **)p1)->d_name, (*(struct dirent **)p2)->d_name);
}


void do_ls( char dirname[] )
/*
 *  wyswietl pliki w katalogu dirname
 */
{
  DIR *dir_ptr;    /* katalog */
  struct dirent *direntp;    /* wpis w katalogu */
  unsigned int dirSize = 0;
  struct dirent ** tabl;
  unsigned int tablIndex = 0;
  char buf[1024];
  char buf2[1024];
  char bufPath[PATH_MAX+1];
  if ( ( dir_ptr = opendir( dirname ) ) == NULL )
    fprintf(stderr,"ls1: cannot open %s\n", dirname);
  else
  {
    while ( ( direntp = readdir( dir_ptr ) ) != NULL )
    {
      if (*direntp->d_name != '.') ++dirSize;
    }
    rewinddir(dir_ptr);
    tabl = malloc(sizeof(struct dirent *) * dirSize);
    bzero(tabl, sizeof(struct dirent *) * dirSize);
    while ( ( direntp = readdir( dir_ptr ) ) != NULL )
    {
      if (*direntp->d_name != '.')
      {
        tabl[tablIndex++] = direntp;
      }
    }
    qsort(tabl, dirSize, sizeof(struct dirent *), cmpDirs);
    for (tablIndex = 0; tablIndex < dirSize; ++tablIndex)
    {
      if (longListing == 1)
      {
        getcwd(buf, sizeof(buf));
	snprintf(buf2, sizeof(buf2), "%s/%s/%s", buf, dirname, tabl[tablIndex]->d_name);
        realpath(buf2, bufPath);
        fileDetails(bufPath);
      } else {
        printf("%s\n", tabl[tablIndex]->d_name);
      }
    }
    closedir(dir_ptr);
  }
}
