249 lines
7.2 KiB
C
249 lines
7.2 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <dirent.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <errno.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
#include <inttypes.h>
|
||
|
#include <time.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
/**************************************************************************
|
||
|
* Authors: Gary Fleming, Amber McCloughan
|
||
|
* Class: CIS452 - Operating Systems Concepts
|
||
|
* Professor: Dr. Greg Wolffe
|
||
|
* Date: 4/18/2019
|
||
|
*
|
||
|
* Program that mimics basic ls functionality. Depending on the flags
|
||
|
* passed into the program, we display information about a directory's
|
||
|
* files. We try and ensure that the output is human readable, so a
|
||
|
* user can easily summarize the contents of a directory.
|
||
|
*
|
||
|
* Arguments
|
||
|
* -------------------------------------------------------------------------
|
||
|
* -i : Displays the inode number along with basic output.
|
||
|
* -n : Displays user and group ID for each file, along with
|
||
|
* some other basic information.
|
||
|
* [directory] : Input a directory name as an argument to display
|
||
|
* information about a directory other than the one
|
||
|
* you're currently in.
|
||
|
***************************************************************************/
|
||
|
|
||
|
// Max size of a timestamp.
|
||
|
#define MAX_TIMESTAMP_LEN 255
|
||
|
|
||
|
// Max size of a character buffer.
|
||
|
#define MAX_BUF_LEN 1024
|
||
|
|
||
|
/**
|
||
|
* Main function of the program. Takes in
|
||
|
* three possible arguments: -i, -n, and
|
||
|
* the name of a directory.
|
||
|
*
|
||
|
* @param argc, number of arguments.
|
||
|
* @param argv, the actual arguments.
|
||
|
*/
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
// Integer used in for loop.
|
||
|
int i;
|
||
|
|
||
|
// Flag that is set to 1 if -n is used.
|
||
|
int nFlag = 0;
|
||
|
|
||
|
// Flag that is set to 1 if -i is used.
|
||
|
int iFlag = 0;
|
||
|
|
||
|
// For use with symbolic links.
|
||
|
char *linkName;
|
||
|
|
||
|
// Buffer for formatting time information.
|
||
|
char *timeBuf = malloc(MAX_TIMESTAMP_LEN * sizeof(char));
|
||
|
|
||
|
// Buffer for holding the directory we're concerned with.
|
||
|
char *direct = malloc(MAX_BUF_LEN * sizeof(char));
|
||
|
|
||
|
// File name buffer.
|
||
|
char *fileName = malloc(MAX_BUF_LEN * sizeof(char));
|
||
|
|
||
|
// For the fully-qualified path.
|
||
|
char *fulQual;
|
||
|
|
||
|
// Used in getting the fully-qualified path.
|
||
|
char actualPath[PATH_MAX + 1];
|
||
|
|
||
|
// Copying initial value into directory.
|
||
|
strcpy(direct, ".");
|
||
|
|
||
|
// Reading the program arguments.
|
||
|
for (i = 1; i < argc; i++)
|
||
|
{
|
||
|
// If we found the -i flag.
|
||
|
if (!strcmp(argv[i], "-i"))
|
||
|
{
|
||
|
iFlag = 1;
|
||
|
}
|
||
|
// If we found the -n flag.
|
||
|
else if (!strcmp(argv[i], "-n"))
|
||
|
{
|
||
|
nFlag = 1;
|
||
|
}
|
||
|
// If we found a combination of flags.
|
||
|
else if (!strcmp(argv[i], "-in") || !strcmp(argv[i], "-ni"))
|
||
|
{
|
||
|
nFlag = 1;
|
||
|
iFlag = 1;
|
||
|
}
|
||
|
// Anything else must be the directory.
|
||
|
else
|
||
|
{
|
||
|
strcpy(direct, argv[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Buffer for file statistics.
|
||
|
struct stat statBuf;
|
||
|
|
||
|
// Directory pointer.
|
||
|
DIR *dirPtr;
|
||
|
|
||
|
// Directory stream pointer.
|
||
|
struct dirent *entryPtr;
|
||
|
|
||
|
// Opening directory stream.
|
||
|
dirPtr = opendir(direct);
|
||
|
|
||
|
// If the directory could not be read (not found or improperly formatted).
|
||
|
if (dirPtr == NULL)
|
||
|
{
|
||
|
// Error message.
|
||
|
printf("An error occured while attempting to open directory.\n");
|
||
|
|
||
|
// Freeing resources.
|
||
|
free(timeBuf);
|
||
|
free(direct);
|
||
|
free(fileName);
|
||
|
|
||
|
// Goodbye.
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// Iterating over the directory's files.
|
||
|
while ((entryPtr = readdir(dirPtr)))
|
||
|
{
|
||
|
// Creating the full relative path.
|
||
|
strcpy(fileName, direct);
|
||
|
strcat(fileName, "/");
|
||
|
strcat(fileName, entryPtr->d_name);
|
||
|
|
||
|
/*
|
||
|
* Getting the fully-qualified path name. I learned how to do
|
||
|
* this using the following link:
|
||
|
* https://stackoverflow.com/questions/229012/
|
||
|
*/
|
||
|
fulQual = realpath(fileName, actualPath);
|
||
|
|
||
|
// Checking if the file is a soft link.
|
||
|
if (entryPtr->d_type == DT_LNK)
|
||
|
{
|
||
|
// Getting link statistics.
|
||
|
if (lstat(fileName, &statBuf) < 0)
|
||
|
{
|
||
|
free(timeBuf);
|
||
|
free(direct);
|
||
|
free(fileName);
|
||
|
perror("huh? there is ");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
// Allocating buffer for where the link leads.
|
||
|
linkName = malloc(statBuf.st_size + 1);
|
||
|
|
||
|
// Getting where the link leads.
|
||
|
if (readlink(fileName, linkName, statBuf.st_size + 1) < 0)
|
||
|
{
|
||
|
free(timeBuf);
|
||
|
free(direct);
|
||
|
free(fileName);
|
||
|
free(linkName);
|
||
|
perror("huh? there is ");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
// Adding a null-terminating character.
|
||
|
linkName[statBuf.st_size] = '\0';
|
||
|
|
||
|
// Creating a special link name for -n.
|
||
|
strcpy(fileName, entryPtr->d_name);
|
||
|
strcat(fileName, " -> ");
|
||
|
strcat(fileName, linkName);
|
||
|
|
||
|
free(linkName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Name for non-link files.
|
||
|
strcpy(fileName, entryPtr->d_name);
|
||
|
|
||
|
// Grabbing stats for non-link files.
|
||
|
if (stat(fulQual, &statBuf) < 0)
|
||
|
{
|
||
|
free(timeBuf);
|
||
|
free(direct);
|
||
|
free(fileName);
|
||
|
perror("huh? there is ");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Don't display the current directory or previous directory files.
|
||
|
if (!strcmp(entryPtr->d_name, ".") || !strcmp(entryPtr->d_name, ".."))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* We figured out how to format time using the article below.
|
||
|
* We have to convert the time_t type into a tm, which takes
|
||
|
* a few steps. Then, we utilize the strftime() function to
|
||
|
* format the time into a user-readable string.
|
||
|
* https://stackoverflow.com/questions/13542345
|
||
|
*/
|
||
|
time_t t = statBuf.st_mtime;
|
||
|
struct tm lt;
|
||
|
localtime_r(&t, <);
|
||
|
strftime(timeBuf, MAX_TIMESTAMP_LEN * sizeof(char), "%b %d, %Y %X", <);
|
||
|
|
||
|
// Changing name if we aren't using -n.
|
||
|
if (nFlag == 0)
|
||
|
{
|
||
|
strcpy(fileName, entryPtr->d_name);
|
||
|
}
|
||
|
|
||
|
// For no flags.
|
||
|
if (iFlag == 0 && nFlag == 0)
|
||
|
printf("%s\n", fileName);
|
||
|
// For only the -i flag.
|
||
|
else if (iFlag == 1 && nFlag == 0)
|
||
|
printf("%lu %s\n", entryPtr->d_ino, fileName);
|
||
|
// For only the -n flag.
|
||
|
else if (iFlag == 0 && nFlag == 1)
|
||
|
printf("%o | %lu | %d | %d | %ld | %s | %s\n", statBuf.st_mode, statBuf.st_nlink, statBuf.st_uid, statBuf.st_gid, statBuf.st_size, timeBuf, fileName);
|
||
|
// For both flags.
|
||
|
else if (iFlag == 1 && nFlag == 1)
|
||
|
printf("%lu | %o | %lu | %d | %d | %ld | %s | %s\n", entryPtr->d_ino, statBuf.st_mode, statBuf.st_nlink, statBuf.st_uid, statBuf.st_gid, statBuf.st_size, timeBuf, fileName);
|
||
|
}
|
||
|
|
||
|
// Closing stream.
|
||
|
closedir(dirPtr);
|
||
|
|
||
|
// Freeing resources.
|
||
|
free(timeBuf);
|
||
|
free(direct);
|
||
|
free(fileName);
|
||
|
|
||
|
// Goodbye.
|
||
|
return 0;
|
||
|
}
|