Пример 12



/* Программа, совмещающая команды mv и cp. Иллюстрация работы с файлами.

 * Пример того, как программа может выбирать род работы

 * по своему названию.

 * Компиляция:

 *              cc cpmv.c -o copy ; ln copy move

 * По мотивам книги М.Дансмура и Г.Дейвиса.

 */

#include <stdio.h>              /* буферизованный ввод/вывод */

#include <sys/types.h>          /* системные типы данных */

#include <sys/stat.h>           /* struct stat           */

#include <fcntl.h>              /* O_RDONLY              */

#include <errno.h>              /* системные коды ошибок */

/* #define strrchr rindex           /* для версии ДЕМОС (BSD)    */

extern char *strrchr(char *, char); /* из библиотеки libc.a      */

extern int   errno;

char    MV[] = "move"; char CP[] = "copy";

#define OK      1       /* success - успех   */

#define FAILED  0       /* failure - неудача */

#define YES     OK

#define NO      0

/* Выделить базовое имя файла:

 *     ../wawa/xxx  -->   xxx

 *     zzz          -->   zzz

 *     /            -->   /

 */

char *basename( char *name ){

	char *s      = strrchr( name , '/' );

	return (s    == NULL) ? name : /* нет слэшей       */

	       (s[1] == '\0') ? name : /* корневой каталог */

				s + 1;

}

#define ECANTSTAT (-1)  /* файл не существует */

struct ftype {

       unsigned type;  /* тип файла */

       dev_t    dev;   /* код устройства, содержащего файл */

       ino_t    ino;   /* индексный узел файла на этом устройстве */

};

/* Получение типа файла */

struct ftype filetype( char *name /* имя файла */   )

{

	struct stat st; struct ftype f;

	if( stat( name, &st ) < 0 ){

		 f.type = ECANTSTAT; f.dev = f.ino = 0;

	} else { f.type = st.st_mode & S_IFMT;

		 f.dev  = st.st_dev; f.ino = st.st_ino;

	}

	return f;

}

/* Удаляет файлы, кроме устройств */

int unlinkd( char *name, unsigned type )

{

	if( type == S_IFBLK || type == S_IFCHR || type == S_IFDIR)

		return 0;

	return unlink( name );

}

/* Функция нижнего уровня: копирование информации большими порциями */

int copyfile( int from, int to )

	/* from - дескриптор откуда */

	/* to   - дескриптор куда   */

{

	char buffer[ BUFSIZ ];

	int n; /* число прочитанных байт */

	while(( n = read( from, buffer, BUFSIZ )) > 0 )

	/* read возвращает число прочитанных байт,

	 * 0 в конце файла

	 */

		    if( write( to,  buffer, n ) != n ){

			printf( "Write error.\n" );

			return FAILED;

		    }

	return OK;

}

/* Копирование файла */

int docopy(char *src, char *dst, unsigned typefrom, unsigned typeto)

{       int retc; int fdin, fdout;

	printf( "copy %s --> %s\n", src, dst );

	if((fdin = open( src, O_RDONLY )) < 0 ){

		printf( "Сan't read %s\n", src );

		return FAILED;

	}

	if((fdout = creat( dst, 0644 )) < 0 ){  /* rw-r--r-- */

		printf( "Can't create %s\n", dst );

		return FAILED;

	}

	retc = copyfile( fdin, fdout );

	close( fdin ); close( fdout );

	return retc;

}

/* Переименование файла. Вернуть OK, если удачно, FAILED - неудачно */

int mlink(char *src, char *dst, unsigned typefrom, unsigned typeto)

{

	switch( typefrom ){

	case S_IFDIR:           /* переименование каталога */

		printf( "rename directory %s --> %s\n", src, dst );

		if( access( dst, 0 ) == 0 ){

		/* 0 - проверить существование файла */

			printf( "%s exists already\n", dst );

			/* файл уже существует */

			return FAILED;

		}

		if( link( src, dst ) < 0 ){

		    printf( "Can't link to directory %s\n", dst );

		    perror( "link" );

		 /* Возможно, что для выполнения link() для каталогов,

		  * программа должна обладать правами суперпользователя.

		  */

		    return FAILED;

		}

		unlink( src );

		return OK;

	default:   /* dst - не существует или обычный файл */

		printf( "move %s --> %s\n", src, dst );

		unlinkd( dst, typeto );

		/* зачищаем место, т.к. link()

		 * отказывается выполняться, если

		 * файл dst уже существует (errno==EEXIST).

		 */

		if( link( src, dst ) < 0 ) return FAILED;

		unlinkd( src, typefrom );  /* удаляем старый файл */

		return OK;

	}

}

/* Если не получилось связать файл при помощи link() - следует

 * скопировать файл в указанное место, а затем уничтожить старый файл.

 */

int mcopy(char *src, char *dst, unsigned typefrom, unsigned typeto)

{

	if( typefrom == S_IFDIR )

		return FAILED;

	/* каталог не копируем, поскольку непосредственная запись

	 * в каталог (как целевой файл) разрешена только ядру ОС.

	 */

	return docopy( src, dst, typefrom, typeto );

}

/* Переименование файла */

int domove(char *src, char *dst, unsigned typefrom, unsigned typeto)

{

	switch( typefrom ){

	default:

	   if( ! mlink( src, dst, typefrom, typeto)){

		 if( ! mcopy( src, dst, typefrom, typeto)){

		       printf( "Can't move %s\n", src );

		       return FAILED;

		 } else unlinkd( src, typefrom ); /* стереть старый */

	   }

	   break;

	case S_IFDIR: /* каталог переименовываем в каталог */

	   if( ! strcmp( ".", basename(src))){

		 printf( "impossible to move directory \".\"\n" );

		 return FAILED;

	   }

	   if( ! mlink( src, dst, typefrom, typeto )){

		 if( errno == EXDEV )

		     printf( "No cross device directory links\n" );

		 return FAILED;

	   }

	   break;

	case ECANTSTAT:

	   printf( "%s does not exist\n", src );

	   return FAILED;

	}

	return OK;    /* okay */

}

int docpmv( char *src,   /* файл-источник   */

	    char *dst,   /* файл-получатель */

	    struct ftype typeto, /* тип файла-получателя              */

	    int cp,      /* 0 - переименование, 1 - копирование       */

	    int *confirm /* запрашивать подтверждение на перезапись ? */

){

	struct ftype typefrom;  /* тип источника       */

	char namebuf[BUFSIZ];   /* новое имя получателя (если надо)   */

	typefrom = filetype(src);

	if(typefrom.type == ECANTSTAT){ /* не существует */

	   printf("%s does not exist.\n", src);

	   return FAILED;

	}

	if( typefrom.type != S_IFDIR && typeto.type == S_IFDIR ){

		/* файл в каталоге dst */

		sprintf(namebuf, "%s/%s", dst, basename(src));

		typeto = filetype(dst = namebuf);

	}

	if(typefrom.dev == typeto.dev && typefrom.ino == typeto.ino){

	/* Нельзя копировать файл сам в себя */

	   printf("%s and %s are identical.\n", src, dst);

	   return OK;  /* так как файл уже есть - считаем это удачей */

	}

	/* если получатель уже существует, то

	 * запросить подтверждение на перезапись */

	if(*confirm && typeto.type == S_IFREG){

	   char answer[40];

	   printf("%s already exists. Overwrite (y/n/all) ? ", dst);

	   fflush(stdout);

	   switch( *gets(answer)){

	   case 'n': default:  return OK; /* ничего не делать */

	   case 'y':           break;

	   case 'a': *confirm = NO; /* дальше - без запросов */

		     break;

	   }

	}

	return cp ? docopy(src, dst, typefrom.type, typeto.type) :

		    domove(src, dst, typefrom.type, typeto.type) ;

}

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

	char *cmd; int cp, i, err, confirm = YES;

	struct ftype typeto;  /* тип файла-получателя */

	if( argc < 3 ) {

		printf( "Usage: %s source... destination\n", argv[0] );

		exit(1);

		/* ненулевой код возврата сигнализирует об ошибке */

	}

	/* выделяем базовое имя программы. */

	cmd = basename( argv[0] );

	if     ( !strcmp( cmd, CP )) cp = 1;

	else if( !strcmp( cmd, MV )) cp = 0;

	else{

		printf( "%s - wrong program name.\n", cmd );

		exit(2);

	}

	typeto = filetype( argv[argc-1] );

	if(cp && typeto.type != S_IFDIR && typeto.type != S_IFBLK

	      && typeto.type != S_IFCHR && argc > 3){

		printf("Group of files can be copied "

		       "to the directory or device only.\n"); exit(3);

	}

	if(!cp && typeto.type != S_IFDIR && argc > 3){

		printf("Group of files can be moved "

		       "to the directory only.\n");           exit(4);

	}

	for(err=0, i=1; i < argc-1; i++)

		err += ! docpmv(argv[i], argv[argc-1], typeto,

				cp, &confirm);

	exit(err);  /* 0, если не было ошибок */

}

[Назад][Содержание][Вперед]

Используются технологии uCoz