#include #include #include #include #include #include #include #include #include #include #include /************************************************************************** * 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; }