Standart C Programlama Dili


C.7.3. Bir Örnek—Disket Saklama

Uyarı
Kitabın bu kısmı eski bilgiler içermektedir. Ancak, okuyucunun yararına olabileceği düşünülerek aynen korunmuştur.

Bu kısımda, bir disketi olduğu gibi diske kopyalayıp geri alabilen veya disketle disk dosyasını karşılaştırabilen bir programı inceleyeceğiz. Program bir disketi blok blok okuyup diskteki tek bir dosya içine kopyalamakta veya bu işin tam tersini yapmaktadır.

Bu amaçla diskette istediğimiz bir sektörü okuyup yazabilen bir yönteme gereksinimimiz vardır. MS-DOS kesintilerinden 0x25 ve 0x26 bu işi yaparlar, ancak bir sorunları vardır: Olası bir hata durumunu bayrak yazmacının elde bitinde, hata kodunu ise AX yazmacında döndürürler. Bu arada, bayrak yazmacının eski durumunu da yığıtta saklarlar. Bu durumda, bu kesintilerden dönüldüğü zaman yığıtta fazla bir sözcük bulunmaktadır. Programa devam etmeden önce bunun geri alınması gerekir. Bu yüzden, bu kesintileri dos.h başlık dosyasında tanımlanmış _int86 fonksiyonunu kullanarak çağıramayız; birleştirici kodu yazmamız gerekir. Aşağıdaki programda mdisk fonksiyonu bu işi yapar.

/*  Disk üzerinde dosya şeklinde disket saklama. Uyarlama 2.00.
 *  © Fedon Kadifeli 1989-1993.
 *  Derleyici: Microsoft C. Uyarlama 8.00.
 */

#include  <stdio.h>
#include  <stdlib.h>
#include  <conio.h>               /* MS-CL */

#define M_OKU   (0x25)            /* mutlak disk okuma kesinti kodu */
#define M_YAZ   (0x26)            /* mutlak disk yazma kesinti kodu */
#define SEKTOR  ((size_t)512)     /* sektör boyu                    */
#define IOS     ((size_t)48)      /* bellek tampon boyu (sektör)    */
#define BBS     (1024/SEKTOR)     /* 1K’lik bloklar                 */
#define MAKS_DB (66)              /* en uzun dosya ismi boyu        */

typedef unsigned char  BAYT;      /* 8 bit  */

BAYT  tam1 [IOS*SEKTOR], tam2 [IOS*SEKTOR];

/* karşılaştırma işlemi */
int esit (register BAYT * a, register BAYT * b,  register size_t u)
{
  while (u--)
    if (*a++ != *b++)
      return 0;                   /* uyuşmuyor */
  return 1;                       /* uyuşuyor  */
}  /* esit */

/* mutlak disk girdi ve çıktı */
size_t mdisk (BAYT kesno, BAYT surno, size_t seksay, size_t sekno,
  BAYT * aktadr)
{
  size_t dondeg;
  __asm {
       push    di                 ; yazmaçları
       push    si                 ;  sakla
       push    bp
       mov     ah,kesno           ; kesinti numarası (M_OKU, M_YAZ)
       mov     al,surno           ; disket sürücü numarası
       sub     al,'A'             ; 'A' -> 0, 'B' ->  1
       mov     cx,seksay          ; sektör sayısı
       mov     dx,sekno           ; sektör numarası
       mov     bx,aktadr          ; aktarma adresi
       cmp     ah,M_OKU
       jne     else               ; eğer M_YAZ ise
       int     M_OKU
       jmp     SHORT devam
else:  int     M_YAZ
devam: pushf
       pop     ax                 ; bayrakları al
       and     ax,1               ; hata biti (kesinti dönüş değeri)
       popf                       ; bayrakları geri al
       pop     bp                 ; yazmaçları
       pop     si                 ;  geri al
       pop     di
       mov     dondeg,ax
  }
  return !dondeg;                 /* 0 hata; 1 tamam */
}  /* mdisk */

/* menü seçeneklerini sor */
int mensor (void)
{
  int            sec;
  static char *  scnklr [] = {
    "\n",
    " 1. Sakla\tDisket --> Disk",
    " 2. Yukle\tDisket <-- Disk",
    " 3. Karsilastir\tDisket <-> Disk" };

  printf("\n\n\t%s\n\t%s\n\t%s\n\nSecenek:",
    scnklr[1], scnklr[2], scnklr[3]);
  if ((sec = _getch()) == '1' || sec == 'S' || sec == 's')
    sec = 1;
  else if (sec == '2' || sec == 'Y' || sec == 'y')
    sec = 2;
  else if (sec == '3' || sec == 'K' || sec == 'k')
    sec = 3;
  else
    sec = 0;
  puts(scnklr[sec]);
  return sec;
}  /* mensor */

/* kullanılacak disket sürücüsünü sor */
int sursor (void)
{
  int  sur;

  printf("Disket surucusu (A/B): ");
  if ((sur = toupper(_getch())) != 'A' && sur != 'B')
    sur = 0;
  printf(sur ? "%c:\n" : "\n", sur);
  return sur;
}  /* sursor */

/* kullanılacak disk dosyasının adını sor */
char * dossor (void)
{
  static char  da [MAKS_DB+3] = { MAKS_DB+1 };

  printf("Disk dosya adi: ");
  _cgets(da);
  putchar('\n');
  return da[2] ? da+2 : NULL;
}  /* dossor */

int main (void)
{
  int     secenek, surucu;
  char *  da;
  FILE *  dg;
  int     b;
  size_t  i, d1, d2;

  printf("Disk uzerinde dosya seklinde disket saklama."
    " Uyarlama 2.00.\n(C) Fedon Kadifeli 1989-1993.\n");
  while ((secenek = mensor()) != 0)
    while ((surucu = sursor()) != 0)
      while ((da = dossor()) != NULL) {
        i = 0;
        d1 = 1;
        b = 0;
        /* 1 nolu seçenek : Disket --> Disk */
        if (secenek == 1) {
          if ((dg = fopen(da,"rb")) != NULL) {
            fclose(dg);
            printf("'%s' dosyasi zaten var!\n", da);
            continue;
          }
          if ((dg = fopen(da,"wb")) == NULL) {
            printf("'%s' dosyasi acilamiyor!\n", da);
            continue;
          }
          while (mdisk(M_OKU, (BAYT)surucu, IOS, i, tam1) &&
            (d1=fwrite((void *)tam1, SEKTOR, IOS, dg)==IOS) != 0)
              printf("\r%d", (i+=IOS)/BBS);
          printf(d1 ? (i?" adet blok '%s' dosyasina aktarildi.\n"
                        :".  Disket okunamadi!\a\n")
                    : ".  '%s' dosyasinda yazma hatasi!\a\n", da);
        } else {
        /* 2 veya 3 nolu seçenek */
          if ((dg = fopen(da,"rb")) == NULL) {
            printf("'%s' dosyasi acilamiyor!\n", da);
            continue;
          }
          /* 2 nolu seçenek : Disket <-- Disk */
          if (secenek == 2) {
            while (fread((void *)tam1, SEKTOR, IOS, dg) == IOS &&
              (d1=mdisk(M_YAZ, (BAYT)surucu, IOS, i, tam1)) != 0)
                printf("\r%d", (i+=IOS)/BBS);
            printf(d1 ? " adet blok diskete aktarildi.\n"
                      : ".  Diskette yazma hatasi!\a\n");
          } else {
          /* 3 nolu seçenek : Disket <-> Disk */
            while ((d1=mdisk(M_OKU, (BAYT)surucu, IOS, i, tam1)) &
              (d2=fread((void *)tam2, SEKTOR, IOS, dg)==IOS) &&
              (b =esit(tam1, tam2, IOS*SEKTOR)) != 0)
                printf("\r%d", (i+=IOS)/BBS);
            printf(
              !b ? ".  Karsilastirma hatasi!\a\n" :
              d1 ? ".  Dosya disketten kisa!\a\n" :
              d2 ? ".  Disket dosyadan kisa!\a\n" :
                   " adet blok uyusuyor.\n");
          }
        }  /* if */
        fclose(dg);
      }  /* while */
  printf("\nIslem tamamlandi\n");
  return 0;
}  /* main */

esit fonksiyonu (Satır 22-28) iki tampon bölgeyi karşılaştırır, eşitse 1 döndürür, aksi takdirde 0 döndürür.

mdisk fonksiyonu (Satır 31-60) surnoda verilen sürücüdeki—bu programda, surno 'A' veya 'B' değerlerini alabilmektedir—sekno başlangıç sektör (kesim) numarasından seksay sektör sayısı kadar sektör üzerinde işlem yapar. kesno M_OKU’ya eşitse disketten bu sektörleri okuyup aktadr tampon bölgesine aktarır; kesno M_YAZ’a eşitse aktadr tampon bölgesindeki baytları disketteki ilgili sektörlere yazar. Bu fonksiyon, yukarıda anlatıldığı gibi ilgili kesintiyi çağırdıktan sonra, yığıtta saklanmış bulunan bayrakları bayrak yazmacına geri alır (Satır 53).

Menü seçeneklerini kullanıcıya mensor fonksiyonu (Satır 63-84) gösterir ve istenilen seçeneği kullanıcıdan alır. Bir numaralı seçenek, programın disketi sektör sektör okuyup, adı kullanıcıdan alınan, bir disk dosyası içine kopyalamasını sağlar. İki numaralı seçenek bu işin tersini yapar; daha önce, bir numaralı seçenek kullanılarak, disk dosyası içine aktarılmış olan bir disket görüntüsünün sektör sektör okunup formatlanmış bir diskete aktarılmasını sağlar. Üç nolu seçenek diskteki dosya ile disketi karşılaştırır; bu seçenek, bir veya iki nolu seçenekler kullanılarak yapılan bir kopyalama işleminden sonra, işlemin doğruluğunu sınamak için kullanılabilir.

Kullanılacak disket sürücüsü (A: veya B:) sursor fonksiyonu (Satır 87-96) aracılığıyla kullanıcıdan alınır. Kullanılacak disk dosyasının adı ise dossor fonksiyonu (Satır 99-107) tarafından alınır. Burada, dosya adını okumak için standart olmayan _cgets fonksiyonu kullanılmıştır. _cgets fonksiyonu kontrollü bir şekilde klavyeden bir satır okumak için kullanılabilir.

Ana fonksiyon içiçe üç döngüden oluşur; bunlar, sırasıyla, kullanıcıdan menü seçeneğini, kullanılacak sürücüyü ve dosya adını alırlar. Her bir menü seçeneği için yapılan işlem temelde aynıdır: Bir döngü içinde disk dosyası ve/veya disketten bilgi okunup gerekli işlem yapılır. (Karşılaştırma seçeneği kısmında, satır 157’nin sonunda neden && değil de & kullanılmıştır?)

Not: Bu program kullanılırken dikkat edilmesi gereken bir nokta disket kapasitesidir. İşlenen bilgi miktarı kilobayt cinsinden ekranda görüntülenir. Örneğin 1.44 megabaytlık bir disket ile işlem yapıldığında ekrandaki sayının son durumu 1440 olmalıdır. Programda kullanılan kesintinin gerçek disket kapasitesini anlaması olası değildir; MS-DOS ortamında iken en son yapılan disket işlemindeki disket kapasitesi kullanılır. Bu yüzden, herhangi bir sorun çıkmaması için, program kullanılmadan önce, kullanılacak disket tipinden bir disket sürücüye takılıp, örneğin DIR A: veya CHKDSK A: şeklinde bir MS-DOS komutu çalıştırılmalıdır. Bundan sonra, program yürütülmesi esnasında, kullanılan disketlerin kapasitesi değişmediği sürece, herhangi bir sorun çıkmayacaktır. Dikkat edilmesi gereken bir diğer nokta da, kullanılan disketlerin formatlı olması ve bozuk sektörlerin bulunmamasıdır.