well, I got a working product. Doesn't format as pretty as I want, but I was more concerned with finishing the major requirements.
Code style still needs some work, several examples of frustration can be seen (repeated code in a place or two)
Two files are included - my main.c and the helper file (mostly just text printing)
Code
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <libgen.h>
#include "queue.h"
#include "helpers.h"
/*
$Author: eep (har har) $
$Date: 2014/02/10 03:52:47 $
$Log: bt.c,v $
Revision 1.8 2014/02/10 03:52:47
Fixed a large bug with the queue and how the loops interacted.
Revision 1.7 2014/02/10 00:14:46
Moved all functions to a helpers file, included in the make utility.
Added suffix rules to make utility.
Revision 1.6 2014/02/08 23:07:01
Now correctly displays depth levels. Added -t option. Fixed syntax on error printing.
Removed some unused variables, patched possible memory leak.
Revision 1.5 2014/02/06 21:09:11
Refactored some parts and modularized the option printing into a function
Revision 1.4 2014/02/06 19:27:47
Added comments, modified some more formatting.
Revision 1.3 2014/02/05 20:26:18
Added more options, tried to fix some issues with indenting
Revision 1.2 2014/02/04 23:31:49
Added basic traversal system.
*/
int main(int argc, char *argv[]) {
/* DATA DECLARATIONS */
int options[10] = { 0 };
int I_OPT = 4;
int opt = 0;
char *path;
char *name;
char *initialPath;
struct queue *q = NULL;
struct stat stats;
struct passwd *passwd;
struct group *group;
struct dirent *dent;
DIR *dir;
long pathLen, tempLen;
int x;
int init = 0;
int currentDepth = 1;
int nodes = 0;
/* END DATA DECLARATIONS */
while ((opt = getopt (argc, argv, "hLdgipstuI:l")) != -1) {
switch (opt) {
case 'h':
helpResponse();
return 0;
break;
case 'L':
options[0] = 1;
break;
case 'd':
options[1] = 1;
break;
case 'g':
options[2] = 1;
break;
case 'i':
options[3] = 1;
break;
case 'p':
options[4] = 1;
break;
case 's':
options[5] = 1;
break;
case 't':
options[6] = 1;
break;
case 'u':
options[7] = 1;
break;
case 'I':
options[8] = 1;
I_OPT = atoi(optarg);
break;
case 'l':
options[9] = 1;
break;
}
}
/* This checks for user path, else sets cwd as path */
if (argc > optind) {
path = argv[argc-1];
} else {
path = getcwd(NULL, 64);
}
/* Enqueue the initial path (from user arg or cwd) */
enqueue(&q, path, currentDepth);
nodes++;
/* Begin outer loop - loops until queue is empty..*/
while (!isEmpty(q)) {
if ((dir = opendir(front(q))) == NULL) {
perror("bt: Directory Opening Error");
printf("Path in question: %s\n", front(q));
return 1; //exit program to prevent infinite loops
} else {
/* Get path name from top queue node, dequeue top node, begin directory reading while-loop */
pathLen = strlen(front(q));
name = front(q);
currentDepth = getDepth(q);
dequeue(&q);
nodes--;
while ((dent = readdir(dir)) != NULL) {
/* Get rid of '.' and '..' as possible results */
if ((strcmp(dent->d_name, "..") != 0) && (strcmp(dent->d_name, ".") != 0)) {
/* Correctly print out the initial filename(starting path base name) */
if (!init) {
stat(path, &stats);
initialPath = basename(path);
printf("%-25s", initialPath);
printOptions(stats, options, passwd, group);
init = 1;
}
/* Get length of path name and file name to malloc correct size of new string */
tempLen = strlen(dent->d_name);
path = (char *)malloc(tempLen + pathLen + 2);
strcpy(path, name);
strcat(path, "/");
strcat(path, dent->d_name);
/* Stat the current file, if there is an issue we report it */
if (options[0]) {
if(lstat(path, &stats) < 0) {
perror("\n\t\t\t\t\t\tbt: File Stat Error");
printf("\t\t\t\t\t\tProblematic path: %s\n\n", path);
} else {
/* Is the file a directory? If so, add it to the queue! */
if (S_ISDIR(stats.st_mode)) {
enqueue(&q, path, currentDepth+1);
nodes++;
}
/* Indents sub directories */
for (x = 0; x < I_OPT * currentDepth; x++) {
printf(" ");
}
/* Print information on files based on what options are flagged */
printf("%-25s", dent->d_name); //Poor attempt at formatting text
printOptions(stats, options, passwd, group);
}
} else {
if (stat(path, &stats) < 0) {
perror("\n\t\t\t\t\t\tbt: File Stat Error");
printf("\t\t\t\t\t\tProblematic path: %s\n\n", path);
} else {
/* Is the file a directory? If so, add it to the queue! */
if (S_ISDIR(stats.st_mode)) {
enqueue(&q, path, currentDepth+1);
nodes++;
}
/* Indents sub directories */
for (x = 0; x < I_OPT * currentDepth; x++) {
printf(" ");
}
/* Print information on files based on what options are flagged */
printf("%-25s", dent->d_name); //Poor attempt at formatting text
printOptions(stats, options, passwd, group);
}
}
}
} //inner loop
closedir(dir);
//free(path);
} // if-else
} //outer loop
free(path);
return 0;
}
/* HELPERS.C */
void helpResponse() {
printf("Correct usage: bt [-h] [-L -d -g -i -p -s -t -u -In -l] [dirName]\n");
printf("dirName is optional, by default bt uses the current working directory\n");
printf("Options: -h lists help, -L follows symbolic links, -d shows last modification...\n");
printf("... -g prints GID, -i prints # of links to file, -p prints perms, -s prints size of file in bytes...\n");
printf("... -t prints info on file type, -u prints UID, -I [n] sets depth indendation by n spaces...\n");
printf("... -l runs bt as though the flags -t -p -i -u -g -s are set.\n");
};
void printOptions(struct stat stats, int *options, struct passwd *passwd, struct group *group) {
long fileSize = 0;
char *timeFetch;
printf("\t\t\t");
if (options[6]) {
printFileType(stats);
}
/* This code uses a bit-wise AND with the value of st_mode + pre-defined values of various permissions to output correct perms */
if (options[4] || options[9]) {
printf((S_ISDIR(stats.st_mode)) ? "d" : "-");
printf((stats.st_mode & S_IRUSR) ? "r" : "-");
printf((stats.st_mode & S_IWUSR) ? "w" : "-");
printf((stats.st_mode & S_IXUSR) ? "x" : "-");
printf((stats.st_mode & S_IRGRP) ? "r" : "-");
printf((stats.st_mode & S_IWGRP) ? "w" : "-");
printf((stats.st_mode & S_IXGRP) ? "x" : "-");
printf((stats.st_mode & S_IROTH) ? "r" : "-");
printf((stats.st_mode & S_IWOTH) ? "w" : "-");
printf((stats.st_mode & S_IXOTH) ? "x" : "-");
}
if (options[3] || options[9]) {
printf("\t%ld\t", stats.st_nlink);
}
if (options[7] || options[9]) {
passwd = getpwuid(stats.st_uid);
printf( "%s", passwd->pw_name);
}
if (options[2] || options[9]) {
group = getgrgid(stats.st_gid);
printf("\t%s", group->gr_name);
if (options[5] || options[9]) {
fileSize = stats.st_size;
if (fileSize < 1024) {
printf("\t%ld", fileSize);
} else if (fileSize >= 1024 && fileSize < 1048576) {
printf("\t%ldK", fileSize / 1024 );
} else if (fileSize >= 1048576 && fileSize < 1073741824) {
printf("\t%ldM", fileSize / 1048576);
} else {
printf("\t%ldG", fileSize / 1073741824);
}
}
/* Print last date modified - ctime returns string with a newline so don't double print newlines */
if (options[1] || options[9]) {
timeFetch = ctime(&stats.st_mtime);
printf("\t%s", timeFetch);
} else {
printf("\n");
}
};
void printFileType(struct stat stats) {
switch (stats.st_mode & S_IFMT) {
case S_IFBLK:
printf("block");
break;
case S_IFCHR:
printf("character device");
break;
case S_IFDIR:
printf("directory");
break;
case S_IFIFO:
printf("pipe");
break;
case S_IFLNK:
printf("symbolic link");
break;
case S_IFREG:
printf("regular file");
break;
case S_IFSOCK:
printf("socket");
break;
default:
break;
}
printf("\t");
};