/* Программа, совмещающая команды 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, если не было ошибок */
}
© Copyright А. Богатырев, 1992-95
Си в UNIX
Назад | Содержание | Вперед