#include <stdio.h>
#include <dirent.h>
#include <limits.h>	/* PATH_MAX def */

/* Uncomment the directory file separator your OS uses */
/*#define SEP "\"*/
#define SEP "/"

/* prototypes */
void fatal(const char *msg);
void print_help();
int is_excluded(const char *dirent_name);
void print_subtree(const char *root_dirname, const char *leader, int depth, int is_last_dir);

char *excluded_dirname_list[16] = {	/* list of directories not to be printed */
	".",
	"..",
	0
};

int main(int argc, char *argv[]) {

	if(argc != 2)
		print_subtree(".", "", 0, 1);
	else
		print_subtree(argv[1], "", 0, 1);
	return 0;
}


void print_subtree(const char *root_dirname, const char *leader, int depth, int is_last_dir) {
	DIR *curdir;	/* pointer to current directory */
	struct dirent *dir_entry;	/* entry in current dir */
	struct dirent *next_dir_entry;	/* entry after current dir */
	char str[PATH_MAX];	/* gen. purpose string */
	char path[PATH_MAX];	/* */
	char *cp, *cp2;
	const char *cp3;

	curdir = opendir(root_dirname);	/* open the subtree root directory */
	if(!curdir) {
		strcpy(str, "Unable to open directory ");
		strcat(str, root_dirname);
		strcat(str, ".");
		fatal(str);
	};
	fputs(leader, stdout);	/* print line leader string */
	if(depth) {
		fputs("  +---", stdout);
		cp3 = root_dirname + strlen(root_dirname);
		while(cp3 != root_dirname && strncmp(cp3, SEP, 1))
			cp3--;
		cp3++;
	} else
		cp3 = root_dirname;
	fputs(cp3, stdout); /* print our directory name */
	fputc('\n', stdout);
	rewinddir(curdir);	/* go back to beginning */
	next_dir_entry = readdir(curdir);	/* prime the pipeline */
	do {
		next_dir_entry = readdir(curdir);
	} while(!(!next_dir_entry ||
		(next_dir_entry && next_dir_entry->d_type == DT_DIR
		&& !is_excluded(next_dir_entry->d_name))));
	strncpy(str, leader, PATH_MAX);	/* copy the leader now */
	strncpy(path, root_dirname, PATH_MAX);
	strncat(path, SEP, PATH_MAX);	/* copy separator */
	cp = path + strlen(path);
	cp2 = str + strlen(str);
	while(next_dir_entry) {
		dir_entry = next_dir_entry;
		do {
			next_dir_entry = readdir(curdir);
		} while(!(!next_dir_entry || (next_dir_entry
			&& next_dir_entry->d_type == DT_DIR
			&& !is_excluded(next_dir_entry->d_name))));
		/* is this entry a directory name? */
		if(dir_entry->d_type == DT_DIR) {
			/* exclude certain directory names */
			if(!is_excluded(dir_entry->d_name)) {
				/* this one's okay to print */
				if(depth)
					if(!is_last_dir)
						strncpy(cp2, "  |   ", PATH_MAX - (cp2-str));
					else
						strncpy(cp2, "      ", PATH_MAX - (cp2-str));
					strncpy(cp, dir_entry->d_name, PATH_MAX - (cp-path));
				print_subtree(path, str, depth+1, !next_dir_entry);
			}
		}
	}
	closedir(curdir);
}

void fatal(const char *msg) {
	fputs(msg, stderr);
	fputc('\n', stderr);
	perror(NULL);
	exit(1);
}

int is_excluded(const char *dirent_name) {
	int i=0;

	while(excluded_dirname_list[i]) {
		if(strcmp(excluded_dirname_list[i++], dirent_name) == 0)
			return 1;	/* this directory's a naughty one */
	}
	return 0;
}
