Пример 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, если не было ошибок */
}

© Copyright А. Богатырев, 1992-95 www.abyss-group.narod.ru
Си в UNIX

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

Hosted by uCoz