Population

Directory Viewer

A.First Edition
This is third assignment of comp229
B.The problem

6. [50%] System Calls

In this assignment you are going to call many system functions from a C program to look at

file information. In addition to the directory system calls in the program of Assignment 1,

you will use the ctime and gmtime system calls from the standard library. Look at the

online man pages for these system calls for more information.

In this assignment, you are going to write a C program, say mydv.c, which lets you view

the files in a directory according to the specified options. Specifically, your program

(mydv.c) should behave as follows:

?Display the prompt ?/FONT>mydv: ¡± (without the quotes) on the screen and wait for user

input (command).

?If the user enters exit, your program should exit.

?If the user enters an empty line, mydv.c should simply display the prompt again (on

a new line) and wait for user input.

?If the user enters one of the following commands, mydv.c should do the specified

function, and then it should display the prompt ¡°mydv: ¡± (again, without the quotes)

on a new line and wait for user input.

cd directory

Change the current working directory to directory by calling the function

ChangeDir as described latter.

dl [options]

List the files in the current working directory according to the specified

options (as in Assignment 2) by calling one or more of the functions

GetAllFiles, SelectRange, SelectMonth, SelectDate,

SelectBigger, SelectSmaller, RemoveHidden, and PrintList as

necessary according to the specified options.

?If the user enters any other command, mydv.c should create a new process and

execute the indicated command in this new process. When this new process exits,

mydv.c should display its prompt on a new line and wait for user input.

Specifically, when called as

dl option1 option2 option3 option4 option5

your mydv.c program should do the following (notice that any of these options may be

omitted).

?The very first call to dl after a directory change invokes function GetAllFiles to

create a list, say fileinfo, of information (name, size, date of last modification) of all

the files in the current working directory, and all successive calls to dl (until the next

cd) use this fileinfo list to extract and print the files matching the specified options.

?Create the list of file pointers, filelist containing the indices to fileinfo for the

files of interest (initially all the files in fileinfo), in the current working directory. If

there are no files in filelist, then print the following message and exit:

Directory dirname does not contain any file.

?Call functions SelectRange, SelectBefore, SelectAfter, SelectBigger,

SelectSmaller, and RemoveHidden, one by one to remove from filelist those files

which are not of interest depending upon the specified options. If any of these functions

returns 0, then print the following message and exit:

There are no files in dirname satisfying the specifications: option1 option2 option3

option4 option5.

?Call the function PrintList to print the information about each of these files in

filelist (one per line) in the proper sorted order after the following header and exit.

Directory dirname contains the following files satisfying the specifications: option1

option2 option3 option4 option5.

The output from your mydv.c program should show for each file the following three fields

(the fields are separated by three blanks) in a single line: (i) the name of the file (30

characters, left justified), (ii) the size of the file (8 digits, right justified), and (iii) the date of

last modification of the file in mmm dd, yyyy; hh:mm:ss format. Notice that the file date

and time is stored internally as the number of seconds that have elapsed since January 1,

1970. You need to use functions ctime and gmtime from the standard library to convert

between the internal time and the calendar time. Look at the online man page for ctime

for more information.

Following are the specifications of the necessary functions.

?int ChangeDir (char *directory):

Change the current working directory to directory if it exists by calling the chdir

system function (page 546) and return 1. If the specified directory does not exist,

print the following message

Directory dirname does not exist.

and return ¨C1.

?int GetAllFiles ():

This function sets up a table fileinfo of 256 entries of containing the information

about all the files (including hidden files but not subdirectories) in the current

working directory and returns the number of entries in fileinfo. You should

extract and modify the code from your debugged program of Assignment 1 to do this.

?int SelectRange (char *begin, char *end):

This function is called with two character strings ¡ª the beginning and end range of

file names. It removes from filelist those files which are not in the specified

range and returns the number of files whose names are within the specified range.

?int SelectDate (int date):

This function is called with a date. It removes from filelist those files which were

not modified on the specified date and returns the number of remaining files which

were modified on the specified date.

?int SelectMonth (char *month):

This function is called with a month. It removes from filelist those files which

were not modified during the specified month and returns the number of remaining

files which were modified during the specified month.

?int SelectBigger (int size):

This function is called with an integer size. It removes from filelist those files

whose size is smaller than specified size and returns the number of remaining files

whose size is equal to or greater than the specified size.

?int SelectSmaller (int size):

This function is called with an integer size. It removes from filelist those files

whose size is greater than the specified size and returns the number of remaining

files whose size is equal to or less than the specified size.

?int RemoveHidden ():

This function removes all the hidden files from filelist and returns the number of

remaining files.

?void PrintList (char *sortby):

This function is called with a parameter specifying the required sort order (name,

size, time). It prints the information about the files in filelist according to the

specified sort order.

Your output should include a listing of all your functions and main program and the output

for all test cases (at least for each of the specifications you used in Assignment 2) showing

that your mydv.c program works as expected (as described above).

You will also have to demo your program to the marker.

¡¡
C.The idea of program
D.The major functions
C.Further improvement
////////////////////////////////////////////////////////////////////////////
//program: Assignment 3 of comp229
//author: Qingzhe Huang
//ID: 5037735
//date: Oct. 24, 2003
//purpose of program: to use system call to implement "dv" directory viewer
///////////////////////////////////////////////////////////////////////////// 
/////////////////////////////////////////////////////////////////////////////
//features of program:
//1. I used quite a few of string arrays, enum types even function arrays.
//2. I used too much global variables as flag of states which is ugly.
//limits of program:
//1. The arguments for user-defined including "dl" and "cd" is between 2-11.
//	 Any number of commands arguments will be regarded as system commands.
//2. All 5 options---"-all", "-modified","-smallerthan","-biggerthan","-range"---
//	 can appear in any order and may or may not appear. 
//3. Sorting order is defined by last option: "-modified" by date, "-biggerthan"
//	 and "-smallerthan" by size, the rest is by name.
//4. Subdirectories won't be shown, including "." and  "..".
//5. Hidden files would be removed whenever "-all" is not present in commands.
//6. Whenever "cd" is used successfully, all states start again.
//7. When no files in list, it will not do selections by commands any more.
//8. Once "cd" is recognized, parameters after second one are ignored.
////////////////////////////////////////////////////////////////////////////////


#include <sys/types.h>
#include <sys/stat.h> 
#include <stdio.h>
#include <stdlib.h> 
#include <strings.h> 
#include <unistd.h> 
#include <dirent.h> 
#include <time.h>
//#include <fcntl.h>

extern int errno;

//a record to store file info
struct FileInfo
{
	char name[31];
	long size;
	time_t  mdate;
} fileInfo[256];

//the fileInfo counter
int infoCount =0;

//fileList stores the file index interested by user
int fileList[256]={0};
//this is its counter
int listCount =0;



enum SortBy
{ByName, ByDate, BySize};

//parameter for "PrintList()" to indicate what field to be sorted by
char* sortedBy[3] = {"name", "date", "size"};
//the actual enum SortBy variable for index of "sortedBy[3]"
int sortIndex = 0;

//the parameter for "SelectRange()"
char beginStr[2];
char endStr[2];

//flag for "GetAllFiles()", 1 means re-read all files which is 
//default, until set to 0 by "cd" parameter
int reRead=1;

//a simple way to change string to index, is it faster than MIA's little
//switch function "MonToStr"?
char* months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", 
	"Sep", "Oct", "Nov", "Dec"};

//the command prompt for our "directory viewer"
char* prompt = "mydv: ";

//this is the directory name, cause I need to display several times
//in different functions, I have to make it global.
char buffer[256];

//these are all functions defined in assignment
int SelectMonth(char* month);
int ChangeDir(char* dir);
int GetAllFiles();
int RemoveHidden();
int SelectRange(char* begin, char* end);
int SelectDate(int date);
int SelectBigger(int size);
int SelectSmaller(int size);
void PrintList(char* sortby);

//these are my own defined utility functions
//this check the command of "dl" and "cd" then send all rest 
//arguments to "checkOptions()"
int checkCmd(char* cmd);
//this will collect user input lines until "exit"
int input(char* str);
//check all options and call each "selection" functions by options accordingly
int checkOptions(char** options, int count);
//a utility function does the printing job
int printInfo();


//three callback function for "qsort"
int compName(const void*, const void*);
int compDate(const void*, const void*);
int compSize(const void*, const void*);

//a function pointer array so as to be used in "qsort"
int (*compFuns[3])(const void*, const void*) = {compName, compDate, compSize};

int main()
{
	char str[256];
	write(0, prompt, strlen(prompt));
	//input will only return 0 unless "exit" met
	while (input(str))
	{
		checkCmd(str);	
		write(0, prompt, strlen(prompt));
	}
		
	return 0;
}


int ChangeDir(char* dir)
{
	int temp =0;
	if ((temp=chdir(dir))==-1)
	{
		printf("Directory %s does not exist.\n", dir);
		return -1;
	}
	else
	{
		reRead =1;//need re-Getallfiles
		return 1;

	}
}

int input(char* str)
{
	char* ptr = str;
	while (1)
	{
		read(0, ptr, 1);
		if (*ptr==10)
		{
			break;
		}
		ptr++;
	}
	*ptr = '\0';
	return strcmp(str, "exit");
}

int GetAllFiles()
{
	char fileName[256];
	DIR *dp=NULL;
	struct dirent *d;
	struct stat s;
	
	//maybe MIA's way is simple---dir = ".";
	if (!(dp = opendir(getcwd(buffer, 256))))
	{
		printf("%s\n", strerror(errno));
		return -1;
	}
	//strcat(buffer, buffer[strlen(buffer)]=='/'?"":"/");
	infoCount=listCount=0;
	while (d=readdir(dp))
	{
		if (stat(d->d_name, &s)==-1)
		{
			printf("cannot access file %s\n", d->d_name);
			continue;
		}
		else
		{
			//we shall not select directories
			if (S_ISDIR(s.st_mode))
			{
				continue;
			}
		}
		
		if (d->d_ino!=0)
		{
			//strcpy(fileInfo[infoCount].name, buffer);
			//printf("%s\n", d->d_name);
			if (stat(d->d_name, &s)!=-1)
			{
				strcpy(fileInfo[infoCount].name, d->d_name);
				fileInfo[infoCount].size = s.st_size;
				fileInfo[infoCount].mdate = s.st_mtime;
				infoCount++;
				fileList[listCount]=listCount; //means there is an index to...
				listCount++;				
			}
				
		}
	}
	return infoCount;			
}


int checkCmd(char* cmd)
{
	char* ptr = cmd;
	//is there any limit for command line parameters? Yes, I said it is 20
	//who would say no????:)
	char* options[20];
	int count = 0, i;
	pid_t pid=0;
	//if user enters nothing, it is not NULL!!!
	if (!strcmp(cmd, ""))
	{
		return 0;
	}
	//\n should also be delimiter
	while (options[count]=strtok(ptr, " \n"))
	{
		ptr = NULL;
		count++;
	}
	//then we can pass options as char**
	options[count] = NULL; //just make sure it is null at end
	//the maximum and minimum of parameters
	if (count<=11&&count>=2)
	{
		if (!strcmp(options[0], "cd"))
		{
			//I won't check all other parameters
			return ChangeDir(options[1]);;
		}
		if (!strcmp(options[0], "dl"))
		{
			if (reRead)
			{
				GetAllFiles();
				//make sure all files will be read only once with 
				//multiple "dl"
				reRead =0;
			}
			//passing char** 
			return checkOptions(&options[1], count-1);
		
		}
	}
	
	//fork another process to execute whatever user input
	if ((pid = fork())==0)
	{
		execvp(options[0], options);
		printf("%s\n", strerror(errno));
		exit(-1);
	}
	//waiting utill user returns...
	wait(NULL);
	return 1;

}

int printInfo()
{
	int i=0;
	char str[23];
	struct tm* t;
	for (i=0; i<listCount; i++)
	{
		t = gmtime(&fileInfo[fileList[i]].mdate);
		sprintf(str, "%s %2d, %5d; %2d:%2d:%2d", months[t->tm_mon], t->tm_mday, t->tm_year, 
			t->tm_hour, t->tm_min, t->tm_sec);
		
		printf("%-30s   %8d   %s\n", fileInfo[fileList[i]].name, fileInfo[fileList[i]].size, str);
	}
}


int checkOptions(char** options, int count)
{
	int i=0;
	int result=0;
	int removeHidden=1;
	char string[256];
	//make sure there is any file
	while (i<count&&listCount)
	{
		if (!strcmp(options[i], "-range"))
		{
			if (i+1<count)
			{
				//since it is only a character
				beginStr[0] = options[i+1][0];
				beginStr[1] = '\0';
				endStr[0] = options[i+1][2];
				endStr[1] = '\0';

				listCount=SelectRange(beginStr, endStr);
				i+=2;	
				continue;
			}	
			else
			{
				printf("%s\n", "missing parameter for -range options");
				return -1;
				
			}
		}
		if (!strcmp(options[i], "-modified"))
		{
			if (i+1< count)
			{
				//this is for month
				listCount=SelectMonth(options[i+1]);
				i+=2;
			}
			else
			{
				printf("%s\n", "missing parameter for -modified options");
			    return -1;
			}

			if (i<count)
			{
				if (strcmp(options[i], "-all")&&strcmp(options[i], "-range")
					&&strcmp(options[i], "-biggerthan")&&strcmp(options[i],"-smallerthan"))
				{
					listCount=SelectDate(atoi(options[i]));
					i++;
				}
			}
			continue;
		}	
			
		if (!strcmp(options[i], "-biggerthan"))
		{
			if (i+1<count)
			{
				listCount = SelectBigger(atoi(options[i+1]));
				i+=2;
				continue;
			}
			else
			{
				printf("%s\n", "missing parameter for -biggerthan options");
				return -1;
			}
		}

		if (!strcmp(options[i], "-smallerthan"))
		{
			if (i+1<count)
			{
				listCount=SelectSmaller(atoi(options[i+1]));
				i+=2;
				continue;
			}
			else
			{
				printf("%s\n", "missing parameter for -smallerthan options");
				return -1;
			}					
		}

		if (!strcmp(options[i], "-all"))
		{
			removeHidden=0;
			i++;
			sortIndex = ByName;
			continue;
		}
		if (i==0)
		{
			printf("%s\n", "invalide parameter!");
			return -1;
		}
	}
	if (removeHidden)
	{
		listCount = RemoveHidden();
		
	}
	//if there is no more files,
	if (!listCount)
	{
		//I cannot understand why printf doesn't work properly
		//it won't display until the function returns, so I have to 
		//use system call "write" which is very fast
		strcpy(string, "Directory ");
		strcat(string, buffer);
		strcat(string, " does not contain any files.\n");
		write(0, string, strlen(string));
//		printf("Directory %s does not contain any files.", buffer);
	}
	else
	{
		PrintList(sortedBy[sortIndex]);
		return result;
	}
}

int SelectMonth(char* month)
{
	int i, temp=0;
	int imonth=0;//the month
	//find out the index of month
	for (i=0; i<12; i++)
	{
		if (!strcmp(month, months[i]))
		{
			imonth=i;
			break;
		}
	}

	for (i=0; i<listCount; i++)
	{
		if (gmtime(&fileInfo[fileList[i]].mdate)->tm_mon==imonth)
		{
			fileList[temp]=fileList[i];
			temp++;
		}
		
	}
	listCount = temp;
	sortIndex = ByDate;
	return listCount;
}		
		
int RemoveHidden()
{
	int i, temp=0;
	for (i=0; i<listCount; i++)
	{	
		if (fileInfo[fileList[i]].name[0]!='.')
		{
			fileList[temp]=fileList[i];
			temp++;
		}
		
	}
	listCount=temp;
	return listCount;
}		

int SelectBigger(int size)
{
	int i, temp=0;
	for (i=0; i<listCount; i++)
	{	
		if (fileInfo[fileList[i]].size>=size)
		{
			fileList[temp]=fileList[i];
			temp++;
		}		
	}
	listCount =temp;
	sortIndex = BySize;
	return listCount;
}

int SelectSmaller(int size)
{
	int i, temp=0;
	for (i=0; i<listCount; i++)
	{	
		if (fileInfo[fileList[i]].size<=size)
		{
			fileList[temp]=fileList[i];
			temp++;
		}		
	}
	listCount =temp;
	sortIndex = BySize;
	return listCount;
}
	
int SelectRange(char* begin, char* end)
{
	int i, temp=0;
	char* ptr;
	for (i=0; i<listCount; i++)
	{	
		if (fileInfo[fileList[i]].name[0]=='.')
		{
			ptr = fileInfo[fileList[i]].name + 1;
		}
		else
		{
			ptr = fileInfo[fileList[i]].name;
		}
		if (strcmp(ptr, begin)>=0
			&&strcmp(ptr, end)<=0)
		{
			fileList[temp]=fileList[i];
			temp++;
		}		
	}
	listCount =temp;
	sortIndex = ByName;
	return listCount;
}

//this is mad! I already defined an enum type global variable to 
//indicate what should by sorted, however, I was forced to pass a 
//string instead of an enum index which I need to use a loop to find out!!!
void PrintList(char* sortby)
{
	int i=0;
	int (*comp)(const void*, const void*);//comp function pointer
	for (i=0; i<3; i++)
	{
		if (!strcmp(sortby, sortedBy[i]))
		{
			comp = compFuns[i];
			break;
		}
	}
	
	qsort(fileList, listCount, sizeof(int), comp);
	printInfo();
}

//the help for UNIX is so BAAAAAAAAAAD that they give a wrong example
//which won't work under gcc which doesn't allow modify const pointer!!!!
int compName(const void*first, const void*second)
{
	int* pi = first, *pj = second;
	int i = *pi;
	int j = *pj;
	return strcmp(fileInfo[i].name, fileInfo[j].name);

}

int compDate(const void*first, const void*second)
{
	int *pi = first, *pj=second;
	long i=fileInfo[*pi].mdate;
	long j=fileInfo[*pj].mdate;

	if ( i< j)
	{
		return -1;
	}
	else
	{
		if (i>j)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
}

int compSize(const void*first, const void*second)
{
	int *pi = first, *pj=second;
	
	int i=fileInfo[*pi].size;
	int j=fileInfo[*pj].size;
	
	if ( i< j)
	{
		return -1;
	}
	else
	{
		if (i>j)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
}


int SelectDate(int date)
{
	int i, temp=0;
	for (i=0; i<listCount; i++)
	{
		if (gmtime(&fileInfo[fileList[i]].mdate)->tm_mday==date)
		{
			fileList[temp]=fileList[i];
			temp++;
		}
		
	}
	listCount = temp;
	sortIndex = ByDate;
	return listCount;
}





			


                                 back.gif (341 bytes)       up.gif (335 bytes)         next.gif (337 bytes)