Chinaunix首页 | 论坛 | 博客
  • 博客访问: 57397
  • 博文数量: 22
  • 博客积分: 1546
  • 博客等级: 上尉
  • 技术积分: 230
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-20 20:33
文章分类
文章存档

2010年(22)

分类: C/C++

2010-07-26 11:31:45

昨天我们主要从主函数入手,对命令行参数进行合法性检测,并引导主程序进入相应的子函数。今天我们要实现一个最基本的复制功能,将一个源文件复制到指定路径。之所以说路径,是因为目的文件可能是一个存在的文件,也可能是一个不存在的文件或者是一个目录(不存在的目录会出错)。在我们详细分析代码前,先看看我做的这个my_cp的运行结果吧。
1.成功将一个已存在源文件复制到另一个指定文件名的文件中。

gues@huangwei-desktop:~/code/shell_command$ ls
cptest  dd  dd1  ed  ls  ls1  my_cp  my_cp1  my_cp.c  my_ls.c  my_shell.c  newls.c  tdir  test  tfile.c
gues@huangwei-desktop:~/code/shell_command$ ./my_cp tfile.c ttfile.c
gues@huangwei-desktop:~/code/shell_command$ ls -l
总用量 124
-rw-r--r-- 1 gues gues  7378 2010-06-22 23:58 my_ls.c
-rw-r--r-- 1 gues gues  6271 2010-07-17 14:29 my_shell.c
-rw-r--r-- 1 gues gues  7378 2010-07-25 17:20 newls.c
drwxr-xr-x 2 gues gues  4096 2010-07-25 18:03 tdir
drwxr-xr-x 3 gues gues  4096 2010-07-25 18:03 test
-rw-r--r-- 1 gues gues  6271 2010-07-25 16:35 tfile.c
-rw-r--r-- 1 gues gues  6271 2010-07-26 10:14 ttfile.c

2.将已存在的源文件拷贝到一个不存在的目录下,会提示错误信息。

gues@huangwei-desktop:~/code/shell_command$ ./my_cp tfile.c ~/nothisdirectory/
my_cp:can't create the file:"/home/gues/nothisdirectory/":it is a directory.

3.将不存在的源文件拷贝到一个目录或文件中,提示相应错误。这里的目标文件或指定目录是否存在不确定。因为只有一个源文件时,cp命令总先检查源文件是否存在。

gues@huangwei-desktop:~/code/shell_command$ ./my_cp nothisfile ~/nothisdirectory
my_cp: can't get file status of "nothisfile" : no this file or directory.

4.成功将源文件拷贝到已存在的指定目录,由于指定路径没有文件名,因此目标文件名与源文件名相同。

gues@huangwei-desktop:~/code/shell_command$ ./my_cp tfile.c ~/
gues@huangwei-desktop:~/code/shell_command$ ls ~/
code     Documents  EIOffice               EIOffice_Personal_Lin.tar.gz  Pictures   tfile.c  Yozo_Office
cptest   Downloads  EIOfficelog.txt        examples.desktop              Public     tmp
Desktop  edsionte   EIOffice_Personal_Lin  Music

5.之所以首先演示这些结果是因为我们在编写cp_single函数的时候都要考虑到这些情况,加之路径相对灵活可能少一个/就会产生不结果。比如下面的结果:

gues@huangwei-desktop:~/code/shell_command$ ./my_cp tfile.c ~/nothisdirectory
gues@huangwei-desktop:~/code/shell_command$ ls ~/
code     Documents  EIOffice               EIOffice_Personal_Lin.tar.gz  nothisdirectory  Templates  Videos
cptest   Downloads  EIOfficelog.txt        examples.desktop              Pictures         tfile.c    Yozo_Office
Desktop  edsionte   EIOffice_Personal_Lin  Music

拷贝成功。这里我们输入的参数仅仅与2中输入的参数少一个/,为什么结果就大不相同?因为2中目标文件是一个不存在的目录(~/nothisdirectory/),而上面的命令是将已存在文件拷贝到已存在目录(~/)下,并且指定文件名为nothisdirectory。
好了,我们下面来分析代码。进入cp_single函数,我们将传递过来的路径拷贝到局部变量src_path和dest_path当中。因为cp_single函数可能在程序的一次运行中被调用多次,如果修改了传递过来的路径(指针)那么会导致下面的调用不正确。如果传递过来的源文件只是一个文件名,那么我们自动为其加上当前路径,这可以方便下面提取文件名。

void cp_single(char *temp_src_path,char* temp_dest_path)
{
	struct stat buf;
	int len;
	char ch[10],filename[PATH_MAX+1],dest_dir[PATH_MAX+1];
	int fdrd,fdwt,i,j,k;
	char src_path[PATH_MAX+1],dest_path[PATH_MAX+1];

	strcpy(src_path,temp_src_path);
	strcpy(dest_path,temp_dest_path);
	for(k=0;k<\strlen(src_path);k++)
	{
		if(src_path[k]=='/')
		break;
	}
	char temp_path[PATH_MAX+1]="./";
	if(k==strlen(src_path))
	{
		strcat(temp_path,src_path);
	        strcpy(src_path,temp_path);
	}

        //the following code be omited
}

接着,从源文件路径中提取文件名。即提取最后一个/符号后面的字符串。

	//extract the file name from src_path
	for(i=strlen(src_path)-1;i>\0;i--)
	{
		if(src_path[i]=='/')
			break;
	}
	j=k=0;
	for(j=i;j<\strlen(src_path);j++)
	{
		filename[k++]=src_path[j];
	}
	filename[k]='\0';

如果目标文件路径存在,并且不含文件名,那么这时候就用到了我们上面提取的源文件名,用strcat连接即可。当然在连接之前还要检查目标文件夹是否包含/,如果包含则删除,否则会连接成这样:existeddir//filename。当不存在此目标路径,我们要检测这个路径末尾这是一个不存在的目录(上述举例2)还是一个已存在目录下不存在的文件(举例5)。我们先找到目标路径中出现的最后一个/,然后检测这个/之前的路径是否存在。比如对于路径:~/existdirectory/nothisdirectory/nothisfile。我们需要检测的是~/existdirectory/nothisdirectory/是否存在,若不存在那就显示出错信息。如果存在,那么按照完整路径:~/existdirectory/nothisdirectory/nothisfile打开文件即可。实现代码如下:

	//check the if dest path has exsited
	if(stat(dest_path,&buf)==0)
	{
		//the dest_path exsited
		if(S_ISDIR(buf.st_mode))
		{
			if(dest_path[strlen(dest_path)-1]=='/')
				dest_path[strlen(dest_path)-1]='\0';
			strcat(dest_path,filename);
		}
	}
	else
	{
		//the dest_path didn't exsit
		for(i=strlen(dest_path)-1;i>=0;i--)
		{
			if(dest_path[i]=='/')
				break;
		}
		if(i>=0)
		{
			strncpy(dest_dir,dest_path,i+1);
		        if(stat(dest_dir,&buf)==-1)
	            	 {
		         	printf("my_cp:accessing:\"%s\" :it is't a directory.\n",dest_path);
			        exit(1);
               		}
		}

	}

下面是cp命令和本程序运行结果的比较。

gues@huangwei-desktop:~/code/shell_command$ ./my_cp tfile.c ~/nothisdirectory/nothisfile
my_cp:accessing:"/home/gues/nothisdirectory/nothisfile" :it is't a directory.
gues@huangwei-desktop:~/code/shell_command$ cp tfile.c ~/nothisdirectory/nothisfile
cp: 正在访问"/home/gues/nothisdirectory/nothisfile": 不是目录

完成上述功能,便进行真正的拷贝了。我们不仅要拷贝源文件的内容,还要拷贝相关文件属性,比如存取权限,用户ID,用户组ID等。下面的代码便是实现上述功能。如果你完成了my_ls,下面的代码并不困难理解,在此不在赘述。

	//fistly the content which was read from srouce file will be write to dest file
	if((fdrd=open(src_path,O_RDONLY))==-1)
	{
		perror("open");
		exit(1);
	}
	if(lseek(fdrd,0,SEEK_END)==-1)
	{
		perror("lseek");
		exit(1);
	}
	if((len=lseek(fdrd,0,SEEK_CUR))==-1)
	{
		perror("lseek");
		exit(1);
	}
	if(lseek(fdrd,0,SEEK_SET)==-1)
	{
		perror("lseek");
		exit(1);
	}
	//open the dest file
	if((fdwt=open(dest_path,O_CREAT|O_TRUNC|O_RDWR,S_IRWXU))==-1)
	{
		perror("open");
		exit(1);
	}
	close(fdwt);
	if((fdwt=open(dest_path,O_WRONLY|O_APPEND))==-1)
	{
		perror("open");
		exit(1);
	}

	while(len-->\0)
	{
		//write all characters to dest file
		if(read(fdrd,ch,1)!=1)
		{
			perror("read");
			exit(1);
		}
		if(write(fdwt,ch,1)!=1)
		{
			perror("write");
			exit(1);
		}

	}

	//get src file's attributes
	if(fstat(fdrd,&buf)==-1)
	{
		perror("fstat");
		exit(1);
	}
	//set the dset file's access right
	if(fchmod(fdwt,buf.st_mode)==-1)
	{
		perror("fchmod");
		exit(1);
	}
	//set file's user id and group id
	if(fchown(fdwt,buf.st_uid,buf.st_gid)==-1)
	{
		perror("fchown");
		exit(1);
	}
	close(fdwt);
	close(fdrd);

现在基本上完成了最基本的拷贝功能。如果上述代码有问题,欢迎留言指正。

阅读(895) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~