思路:
如下图,目录文件按指定格式打包到一个文件。
#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>
#include<string.h>
#include<sys/stat.h>
#include<unistd.h>
//filename: ./test/file
struct file_info{//用于保存文件名和对应的inode,用于判断是否为硬链接文件
char filename[100];
ino_t inode;
}filesave[1024];
void tarfile(const char* filename,FILE* fpOut){//打包文件
struct stat stat_buf;
stat(filename,&stat_buf);
filesave[1024];
int i;
static n=0;//记录写入文件的真实数量,不包括硬链接文件
for(i=0;i<=n;i++){//判断文件是否已写入
if(filesave[i].inode==stat_buf.st_ino){//判断是硬链接文件
fprintf(fpOut,"h\n%s\n%s\n",filename,filesave[i].filename);//写入标记h->newname->oldname
return;
}
}
fprintf(fpOut,"f\n%s\n%d\n",filename,(int)stat_buf.st_size);
FILE *fpIn=fopen(filename,"r");
char buf[4096];
while(1){
int ret=fread(buf,1,sizeof(buf),fpIn);
if(ret<=0){
break;
}
fwrite(buf,ret,1,fpOut);
}
strcpy(filesave[n].filename,filename);//将新打包的文件写入结构体记录
filesave[n].inode=stat_buf.st_ino;
n++;
fclose(fpIn);
}
//dirname: ./test
int tardir(const char* dirname,FILE* fpOut){//打包目录
char filepath[1024];
fprintf(fpOut,"d\n");//d目录标记
fprintf(fpOut,"%s\n",dirname);//打包的根目录
DIR* dir=opendir(dirname);//打开文件目录项
struct dirent* entry=readdir(dir);
while(entry){
//./test/file
sprintf(filepath,"%s/%s",dirname,entry->d_name);//拼凑每个目录项的路径
if(entry->d_type==DT_REG){//判断是否为文件
tarfile(filepath,fpOut);//打包文件
}
else if(entry->d_type==DT_DIR){//判断是否为目录,若是就继续递归
if((strcmp(entry->d_name,".")==0)||
(strcmp(entry->d_name,"..")==0)){//. ..忽略
entry=readdir(dir);
continue;
}
tardir(filepath,fpOut);
}
entry=readdir(dir);
}
closedir(dir);
}
int tar(const char* dirname,const char *outfile){
FILE* fpOut=fopen(outfile,"w");
fprintf(fpOut,"xgltar\n");//标记打包文件类型
fprintf(fpOut,"1.0\n");//版本
int ret=tardir(dirname,fpOut);//打包目录
fclose(fpOut);
return ret;
}
int untarfile(FILE *fin){
char buf[1024];
if(fgets(buf,sizeof(buf),fin)==NULL){
return -1;
}
printf("now utar type=%s",buf);
if(strcmp(buf,"d\n")==0){//目录标记
fgets(buf,sizeof(buf),fin);
buf[strlen(buf)-1]=0;
mkdir(buf,0777);
printf("mkdir %s\n",buf);
}
else if(strcmp(buf,"f\n")==0){//文件标记
fgets(buf,sizeof(buf),fin);
buf[strlen(buf)-1]=0;
FILE *out=fopen(buf,"w");
printf("create file %s\n",buf);
fgets(buf,sizeof(buf),fin);
int len=atol(buf);
printf("filelen %s\n",buf);
while(len>0){
int readlen=len<sizeof(buf)?len:sizeof(buf);
int ret=fread(buf,1,readlen,fin);
fwrite(buf,1,ret,out);
len-=ret;
}
fclose(out);
}
else if(strcmp(buf,"h\n")==0){//硬链接文件标记
fgets(buf,sizeof(buf),fin);//读取链接文件名
buf[strlen(buf)-1]=0;
char oldbuf[1024];//被链接的文件名
fgets(oldbuf,sizeof(oldbuf),fin);
oldbuf[strlen(oldbuf)-1]=0;
link(oldbuf,buf);
}
return 0;
}
//解包
int untar(const char* tarfile){
char buf[1024];
FILE *fin=fopen(tarfile,"r");
fgets(buf,sizeof(buf),fin);
if(strcmp(buf,"xgltar\n")!=0){//判断是否为打包文件类型
printf("unknown file format\n");
return -1;
}
fgets(buf,sizeof(buf),fin);
if(strcmp(buf,"1.0\n")==0){//判断版本是否正确
while(1){
int ret=untarfile(fin);//解包
if(ret!=0){
break;
}
}
}else{
printf("unknown version\n");
return -1;
}
return 0;
}
//./tar -c test tartest
//./tar -u tartest
int main(int argc,char *argv[])
{
if(argc==1){
printf("usage:\t./tar -c file1 file2 or ./tar -u file1\n");
return -1;
}
const char *option=argv[1];
if(strcmp(option,"-c")==0){
const char* dirname=argv[2];
const char* outfile=argv[3];
return tar(dirname,outfile); //打包
}else if(strcmp(option,"-u")==0){
const char* tarfile=argv[2];
return untar(tarfile); //解包
}
printf("option error\n");
return -1;
}