/* Программа, совмещающая команды 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
Назад | Содержание | Вперед