cis-452-lab-11/programming-assignment.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, &lt);
strftime(timeBuf, MAX_TIMESTAMP_LEN * sizeof(char), "%b %d, %Y %X", &lt);
// 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;
}