Population

Directory Viewer---dynamic linking and static linking

A.Third Edition
This is third edition of fourth assignment of comp229. And there is little change except that in order to
demonstrate that dynamic library is not really necessary, I moved all global variable declaration from dynamic library
to static library. Therefore, main and static library will not have to run on dependence to dynamic library. In other 
word, we can run executable file even without existence of dynamic library! (In fact it is not completely true. 
See here.)
I also corrected a small bug: When I type in option "-range a-d" and all files with starting letter "d" are not 
selected. Reason is simple: You see the range parameter with "a-d", however, if you use "strcmp" to compare 
string "d" with any file name starting with letter "d", string "d" is always smaller. So, I change the 
if (strcmp(ptr, begin)>=0 &&strcmp(ptr, end)<=0)
to 
if (ptr[0]>=begin[0]&&ptr[0]<=end[0])
B.The problem

6. [50%] Static and Dynamic Program Libraries

The attached sheets give basic information about static and dynamic program libraries.

For more information browse the course web page.

In this assignment, you are going to develop a static and a dynamic program libraries and

use them with your mydv program of Assignment 3. Specifically do the following.

?Modify (modularize) your mydv program of Assignment 3 so that it contains a main

program which calls the various functions your have written (as described in

Assignment 3).

?Select all the functions that must be executed in every execution of your main program

and put them in a single source file, say mydv_slib.c. Make the necessary

modifications to this source file so that these functions can be compiled independent of

the main program. Build a static library (mydv_slib.a) of these functions.

?Select all the functions that will be executed as required in an execution of your main

program and put them in another source file, say mydv_dlib.c. Make the necessary

modifications to this source file so that these functions can be compiled independent of

the main program. Build a dynamic library (mydv_dlib.so) of these functions.

?Put the main program in a separate source file, say mydv_main.c. Make the necessary

changes so that the main program can call the functions from the static and dynamic

libraries you have developed.

?Compile you main program and link it with the necessary program libraries

(mydv_slib.a and mydv_dlib.so) to get the executable program mydv. Test your

executable program (mydv) for all the test cases you used in Assignment 3.

Your submission should include the source files for the libraries and the main program and

all the output from test cases. You should also discuss the following.

(a) Do you really need the static library in this program? If so, explain why; if not, explain

the major advantage of using the static library in this program.

(b) Repeat (a) for the dynamic library.

 
C.The idea of program
 
1. In order to prove that program can run without existence of dynamic library, we need to move all global declaration out of dynamic library. Thus executable file will run without need to find external reference declared in dynamic library. Or in other words all global variable or external references static library and main file are using must not appear in dynamic library except those functions such as "selectrange", "getAllFiles",etc so that as long as these functions are not called, the program can run correctly. For example, at prompt "mydv: " I input "ls -Al" etc. Of course if I type in "dl -all", the program should crash.
2. The compilation and linking is almost same as before.
3. Try to cheat compiler by using a fake dynamic library because the Unix system is so fussy that it will try to check the file type. So, empty file named as "fakeDynamic.lib"  won't work. Try to copy a dynamic library from /bin and renamed it. Or simply compile a empty code source file into a "PIC" "shared" library by:
 "gcc -fPIC -shared fake.c -o mydynamic.lib"
4.  Then set environment by:
 "setenv LD_LIBRARY_PATH ."              (Pay attention to the last ".")
5. Move the original dynamic library to other directory or simply rename it. Then rename your "fake" dynamic file as your original dynamic library.
6. Run your program, it runs as long as you don't invoke those dynamic functions located in dynamic library.
7. This proves:
    a) Dynamic library is indeed called when needed.
    b) During compile time linker indeed check all external reference located in dynamic library.
    c) O.S. check for all dynamic library in all paths to make sure there is such a file with same name.
 
 
 
D.The major functions
C.Further improvement
 
//this is main program: main.c
#include <stdio.h>

extern char* prompt;
//these are my own defined utility functions 
//this check the command of "dl" and "cd" then send all rest 
//arguments to "checkOptions()" 
extern int checkCmd(char* cmd);
 
//get user input string from keyboard and store in the parameter
extern int input(char* str);


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;
}

//this is static library program: staticLib.c
////////////////////////////////////////////////////////////////////////////
//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
///////////////////////////////////////////////////////////////////////////// 


#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>


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

int sortIndex = 0;
extern int errno;
char* sortedBy[3] = {"name", "date", "size"};

enum SortBy
{ByName, ByDate, BySize};

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

//the fileInfo counter
int infoCount =0;

//fileList stores the file index interested by user
int fileList[1024]={0};
//this is its counter
int listCount =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 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();


//get user input string from keyboard and store in the parameter
int input(char* str);


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 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
			if (ChangeDir(options[1])==1)
			{
				reRead =1;//need re-Getallfiles
				return 1;
			}
			else
			{
				return -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 %02d, %4d; %02d:%02d:%02d", months[t->tm_mon], t->tm_mday, 
			t->tm_year + 1900, 	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;
		}
		i++;
	}
	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;
	}
}







		
//this is dynamic library program: dynamicLib.c
 
#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>

struct FileInfo
{
	char name[31];
	long size;
	time_t  mdate;
};

extern int fileList[1024];
extern int errno;
extern struct FileInfo fileInfo[1024];
extern int infoCount;
extern int listCount;
extern char* months[12];
extern 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);

enum SortBy
{ByName, ByDate, BySize};

//parameter for "PrintList()" to indicate what field to be sorted by

//the actual enum SortBy variable for index of "sortedBy[3]"
extern int sortIndex;
extern char* sortedBy[3];


//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 ChangeDir(char* dir)
{
	int temp =0;
	if ((temp=chdir(dir))==-1)
	{
		printf("Directory %s does not exist.\n", dir);
		return -1;
	}
	else
	{		
		return 1;

	}
}


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 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)
		if (ptr[0]>=begin[0]&&ptr[0]<=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();
}


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;
}



//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;
	int i = *((int*)first);
	int j = *((int*)second);
	return strcmp(fileInfo[i].name, fileInfo[j].name);

}


//this is the bug place
int compDate(const void*first, const void*second)
{
	int tempi = *((int*)first);  //is it strange?
	int tempj = *((int*)second);//is it strange?
	long i=fileInfo[tempi].mdate;
	long j=fileInfo[tempj].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 = *((int*)first);
	int pj = *((int*)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;
		}
	}
}


	


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