4.4   Видалення і перейменування файлів. Створення резервних копій файлів

Видалення файлів виконують функції remove() та unlink(). Фактично remove() являє собою макро, описане в заголовному файлі <stdio.h> так:

#define remove( path )  unlink( path )

Наведемо опис функції unlink.

#include <stdio.h>
int unlink( const char *filename )

Видаляє файл, на ASCIIZ-рядок специфікації якого вказує filename. У випадку успіху функція повертає 0. У протилежному випадку повертається -1 і код помилки записується в зовнішню змінну errno. Якщо функція не може відшукати файл, заданий параметром filename, errno встановлюється в значення ENOENT. Якщо файл, що видаляється має атрибут “тільки для читання”, errno встановлюється в значення EACCES.

Перейменування файлу здійснює функція rename. Необхідність у цій операції часто виникає при виконанні “резервних” копій файлів що редагуються (наприклад, зберігання “старої” копії файлу з новим розширенням імені .ВАК). Загальновживаною є така практика. Існуюча (“стара”) копія файлу перейменовується у файл із розширенням імені .ВАК. Потім нова копія файлу записується на диск під своїм ім’ям.

#include <dir.h>
int rename( const char *oldname, const char *newname)

Змінює ім’я файлу або директорія з заданого рядком oldname на задане рядком newname. У випадку успіху функція повертає 0. У противному випадку повертається -1 і в зовнішню змінну errno записується код помилки. Якщо файл або директорій, ім’я якого задає рядок oldname, існує, або файл з ім’ям newname не може бути створений, або oldname директорій, a newname задає інший директорій, зовнішня змінна errno буде дорівнювати EACCES. Якщо файл з ім’ям oldname не існує, зовнішня змінна errno дорівнює ENOENT. Функція працює в межах тільки одного накопичувача. Спроба задати різні букви накопичувача в oldname і newname призводить до повернення помилки ENOTSAM. При перейменуванні файлів вони попередньо закриваються. Тому щоб уникнути непорозумінь варто перейменовувати попередньо закриті файли.

Наведемо приклад програми, що виконує переміщення файлів з одного директорія в інший, тобто подібній команді rename COMMAND.COM, але працюючої і для різних накопичувачів. “Переміщення” на різні накопичувачі пов’язано з виконанням фізичної копії файлу на новий диск і знищенням файлу-джерела. Для копіювання використовується виклик повторної копії COMMAND.COM. Імена файлів задаються в командному рядку при запуску програми.

/* L4_4.C */
#include <stdio.h>
#include <ermo.h>
#include <string.h>
#include <process.h>
#include <io.h>
#define BLANK             “ ”
int main( int argc, char **argv) {
  char string[81]=”copy “;
  if( argc < 3 ) {
    printf( “\а Використання програми \п”\
               “%s <старе_ім’я>  <новое_ім’я> \п”, argv[0] );
    return(1);
  }
  if( rename( argv[1], argv[2] ) ) {
    if( errno == ENOTSAM ) { /* задані різні накопичувачі */
       /* Формування командного рядка copy argv[1] argv[2]
           і виконання її через оболонку */
       strcat( string, argv[1] );
       strcat( string, BLANK );
       strcat( string, argv[2] );
       if( system( string ) ) {
           printf( “\а Помилка запуску COMMAND.COM \n”);
           return(2);
       }
       if( unlink( argv[1] ) ) {
           printf( “\a Не можу видалити %s\n”, argv[1] );
           retiirn( 3 );
       }
    }
    else {
      printf( “\a Команда не виконана “ );
      return( 4 );
    }
  }
return( 0 );
}

Наведемо приклад використання функції rename() при створенні резервних копій файлів. Програма приймає рядки з клавіатури і записує їх у файл на диску, зберігаючи попередній вміст. Ім’я файлу задається в командному рядку при запуску програми на виконання. Якщо файл не існує, він буде створений. Програма працює так. Спочатку в зарезервований масив - source зчитується весь файл. Потім на вільне місце, що залишилося, приймаються рядки з клавіатури. Їхній прийом завершується при введенні порожнього рядка. Після цього вихідний файл перейменовується у файл із розширенням імені .ВАК, а обновлений буфер записується у вихідний файл.

/*L4_5.C*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <dir.h>
#include <io.h>
#include <fcntl.h>
#include <stat.h>
#define BLANK             “\n”
#define SIZE  1024
int main( int argc, char **argv ) {
  char bak_name[128], drive[3], dir[64], name[9], ext[5];
  char source[SIZE], *src_ptr;
  int fptr, ret;
  if( argc < 2) { /* чи задані параметри в командному рядку? */
    printf( “\a Використання програми %s <ім’я_файлe> \п”,
                argv[0] );
    return( 1 );
  }
  /* Запис у пам’ять усього вмісту файлу argv[1] */
  if( ( fptr=open(argv[1], O_RDWR|O_APPEND|O_TEXT|O_CREAT,
                          S_IWRITE))==-1 ) {
    реrror( “\а Помилка відкриття файлу”);
    return( 2 );
  }
  if( ( ret=read( fptr, source, SIZE))==-1) {
    perror(“\a Помилка читання файлу”);
    return(3);
  }
  close(fptr); /* перейменування потребує закриття файлів */
  src_ptr=source+ret; /* покажчик на вільний байт */
  if(ret==SIZE) { /* чи є ще місце у буфері */
    puts( “\a Не вистачає оперативної пам’яті“ );
    return( 4 );
  }
  puts("Вводьте рядки- Кінець вводу - ENTER");
  /* Нескінчений цикл прийому рядків з клавіатури. */
  while( strcmp( fgets( src_ptr, 128, stdin), BLANK)) {
    src_ptr += strlen( src_ptr );
    if((src_ptr - source) > SIZE ) break;
  }
  /* Формування імені .ВАК - файлу. */
  fnsplit(argv[1], drive, dir; name, ext);
  fnmerge(bak_name, drive, dir, name, “.ВАК”);
  /* Перейменування файлу argv[1] у файл із розширенням .ВАК.
  Попередньо знищується .ВАК-файл, якщо він є. */
  unlink( bak_name );
  rename( argv[1], bak_name );
  /* Створення нового файла з ім’ям argv[1] */
  if((fptr=open(argv[1], O_WRONLY|O_TRUNC|O_TEXT|0_CREAT,
                         S_IWRITE )) == -1 ) {
    printf(“\aПомилка відкриття файлу %s”, argv[1]);
    perror(“”);
    return( 5 );
  }
  /* Запис у файл з ім’ям argv[1] */
  if( write( fptr, source, (unsigned)(src_ptr-source))==-1) {
    perror( “\aПомилка запису у файл”);
    return( 5 );
  }
  close( fptr );
  return( 0 );
}

При виконанні перейменування іноді потрібно отримати унікальне ім’я файлу, що не співпадає з жодним з імен файлів у даному директорії. Виконати таку операцію допомагають функції бібліотеки Turbo С mktemp() або tmpnam().

#include <dir.h>
char *mktemp(char *template)

Використовуючи ASCIIZ-рядок шаблону, на початок якого вказує template, утворює ім’я файлу, що не співпадає з жодним з імен у поточному директорії. Сформоване ім’я записується в рядок template. Рядок template має вид baseXXXXXX, де base - набір, що містить не більш шести будь-яких символів, які обов’язково включаються в ім’я що генерується; символи “X” позначають позиції, що будуть замінені символами унікального імені, причому третій із них - це завжди символ “.”. Функція mktemp починає генерацію імен із комбінації АА.ААА, Вона повертає покажчик на сформований рядок (тобто покажчик template). У випадку помилки повертається NULL.

#include <dir.h>
char *tmpnam( char *sptr )

Генерує ім’я файлу, яке не співпадає з жодним з імен файлів у поточному робочому директорії. Отримане ім’я записується в буфері на початок якого вказує sptr. Розмір буферу повинний бути 13 байт, тому що там поміщається і символ “.”, що відокремлює ім’я файлу від розширення імені. Якщо sptr дорівнює NULL, отримане ім’я зберігається в динамічно розподіленій області пам’яті і перевизначається кожним таким викликом до tmpnam(). Функція повертає покажчик на початок отриманого рядка імені файлу; у випадку помилки повертається NULL.