> Dizin İçerisindeki Dosyaların (Dizin Girişlerinin) Elde Edilmesi: Dizinlerin içerisinde dosyalar ve dizinler olabilmektedir. İşletim sistemleri dünyasında dizin içerisindeki öğelere genellikle "dizin girişi (directory entry)" denilmektedir. Bir dizin içerisinde dosyaların kendisi bulunmaz. Yalnızca onların isimleri ve bazı önemli bilgileri bulundurulur. Daha önceden de belirttiğimiz gibi dizinler aslında "içerisinde dizin girişlerinin bulunduğu" dosyalar gibi organize edilmiştir. Dizinlerin içerisindeki girişlerin elde edilmesi dizinlerle ilgili önemli işlemlerden biridir. Aşağıda iki Windows ve UNIX/Linux sistemlerinde kullanılan fonksiyonların karşılaştırma tablosu verilmiştir: Windows - UNIX/Linux FindFirstFile - opendir FindNextFile - readdir FindClose - closedir Şimdi de bunları incelemeye başlayalım. >> Windows Sistemlerinde: Windows sistemlerinde bir dizin'in içerisindeki dizin girişlerini elde etmek için "FindFirstFile", "FindNextFile", "FindClose" API fonksiyonları kullanılmaktadır. Bu fonksiyonların biraz daha gelişmiş olan Ext'li biçimleri de vardır. Bu fonksiyonlardan, >>> "FindFirstFile" : Fonksiyonun prototipi aşağıdaki gibidir: HANDLE FindFirstFile( LPCSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData ); FindFirstFile fonksiyonunun birinci parametresi yol ifadesini almaktadır. Bu fonksiyonla biz tek bir girişin bilgilerini alabileceğimiz gibi * ve ? gibi joker karakterlerini kullanarak birden fazla girişin bilgilerini de alabiliriz. Tabii eğer FindFirstFile ile birden fazla girişin bilgileri alınıyorsa bu fonksiyon onların yalnızca ilkinin bilgisini vermektedir. FindFirstFile girişe ilişkin bilgileri WIN32_FIND_DATA isimli bir yapı nesnesinin içerisine yerleştirmektedir. Fonksiyon başarı durumunda sonraki dizin girişlerini elde etmek için gereken HANDLE değerine başarısızlık durumunda INVALID_HANDLE_VALUE değerine geri dönmektedir. Başarısızlığın nedeni dizinde belirtilen kalıba uygun dosyanın bulunamaması ise GetLastError ERROR_FILE_NOT_FOUND değerine geri dönmektedir. >>> "FindNextFile" : Fonksiyonun prototipi aşağıdaki gibidir: BOOL FindNextFile( HANDLE hFindFile, LPWIN32_FIND_DATA lpFindFileData ); Eğer dizin içerisindeki birden fazla girişin bilgileri elde edilecekse bunların ilki FindFirstFile fonsiyonu tarafından diğerleri ise FindNextFile fonksiyonu tarafından elde edilmelidir. FindNextFile fonksiyonu bir kez değil döngü içerisinde çağrılmalıdır. Bu fonksiyon her çağrılışta yeni bir girişin bilgilerini elde etmektedir. FindNextFile fonksiyonunun birinci parametresi FindFirstFile fonksiyonundan elde edilen HANDLE değeridir. İkinci parametre yine bulunan dizin girişinin bilgilerinin yerleştirileceği WIN32_FIND_DATA türünden nesnenin adresini almaktadır. Fonksiyonun geri dönüş değeri işlemin başarısını belirtmektedir. FindNextFile fonksiyonu iki nedenden dolayı başarısız olmaktadır. Birincisi artık tüm dizin girişlerinin elde edilmiş olmasıdır. İkincisi ise bir IO hatasının oluşmuş olmasıdır. Programcı döngüden çıkınca bir IO hatası olup olmadığını tespit etmek ister. Eğer GetLastError ERROR_NO_MORE_FILES değerine geri dönüyorsa bu durum gayet normal olarak fonksiyonun tüm girişleri elde ettiğinden dolayı başarısız olduğu anlamına gelmektedir. >>> "FindClose" : Fonksiyonun prototipi aşağıdaki gibidir: BOOL FindClose( HANDLE hFindFile ); İşlemler bitince elde edilen HANDLE alanının serbest bhırakılması için FindClose fonksiyonun çağrılması gerekir. Bu fonksiyon da işlemin başarısına geri dönmektedir. Tabii bu fonksiyonunun başarısının kontrl edilmesine gerek yoktur. Bu fonksiyonlara argüman olarak geçilen WIN32_FIND_DATA yapısı ise şöyledir: typedef struct _WIN32_FIND_DATA { DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD dwReserved0; DWORD dwReserved1; CHAR cFileName[MAX_PATH]; CHAR cAlternateFileName[14]; DWORD dwFileType; // Obsolete. Do not use. DWORD dwCreatorType; // Obsolete. Do not use WORD wFinderFlags; // Obsolete. Do not use } WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA; Bu yapının, -> Burada bulunan dizin girişinin ismi yapının cFileName elemanında bulunmaktadır. -> Bulunan dosyanın uzunluğu iki ayrı DWORD biçiminde yapının nFileSizeHigh ve nFileSizeLow elemanlarında bulunmaktadır. Dizin girişlerinin uzunlukları 0 biçiminde verilmektedir. -> Yapının dwFileAttributes elemanı dizin girişinin özelliklerini vermektedir. Bu eleman bit bit anlamlıdır. Bu elemanın her biri bir özelliğin olup olmadığını belirtmektedir. Dizin girişi özellikleri FILE_ATTRIBUTE_XXX biçiminde sembolik sabitlerle belirtilmiştir. Örneğin, bir dizin girişinin bir dizine ilişkin olup olmadığını anlamak için FILE_ATTRIBUTE_DIRECTORY değeriyle bu özellik elemanının "bit-AND" işlemine sokulması gerekir. -> Windows dosya sistemine de bağlı olarak dosyalar ve dizinler için üç zaman bilgisi tutmaktadır: Son yazma zamanı, son okuma zamanı ve ilk yaratma zamanı. WIN32_FIND_DATA yapısı içerisinde bu bilgiler FILETIME isimli bir yapı biçiminde tutulmaktadır. Bu yapı 32 bitlik iki parçadan oluşmaktadır. Tarih ve zaman bilgisi bu yapı içerisinde 01/01/1601'den geçen 100 nano saniyelerin sayısı biçiminde tutulmaktadır. Windows her zaman dizin girişlerindeki zamanları UTC (Universial Time Clock) olarak tutmaktadır. Eğer zamanları yerel saata dönüştüreceksek önce FileTimeToLocalFileTime fonksiyonuna sokmalıyız. FILETIME değerini parçalarına ayırmak için FileTimeToSystemTime fonksiyonu kullanılmaktadır. Aşağıda bir dizin girişinin komut satırındaki "dir" komutu tarzıyla yazdırılmasına bir örnek verilmiştir. * Örnek 1, #include #include #include void ExitSys(LPCSTR lpszMsg); int main(void) { HANDLE hFileFind; WIN32_FIND_DATA findData; unsigned long long ullSize; SYSTEMTIME sysTime; if ((hFileFind = FindFirstFile("*.*", &findData)) == INVALID_HANDLE_VALUE) ExitSys("FindFirstFile"); do { FileTimeToLocalFileTime(&findData.ftLastWriteTime, &findData.ftLastWriteTime); FileTimeToSystemTime(&findData.ftLastWriteTime, &sysTime); printf("%02d.%02d.%04d %02d:%02d ", sysTime.wDay, sysTime.wMonth, sysTime.wYear, sysTime.wHour, sysTime.wMinute); if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) printf("%-14s", ""); else { ullSize = (unsigned long long)findData.nFileSizeHigh << 32 | findData.nFileSizeLow; printf("%14llu", ullSize); } printf(" %s\n", findData.cFileName); } while (FindNextFile(hFileFind, &findData)); if (GetLastError() != ERROR_NO_MORE_FILES) ExitSys("FindNextFile"); FindClose(hFileFind); return 0; } void ExitSys(LPCSTR lpszMsg) { DWORD dwLastErr = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { fprintf(stderr, "%s: %s", lpszMsg, lpszErr); LocalFree(lpszErr); } exit(EXIT_FAILURE); } >> UNIX/Linux Sistemlerinde: UNIX/Linux sistemlerinde bir dizin'in içerisindeki dizin girişlerini elde etmek için "opendir", "readdir", "closedir" POSIX fonksiyonları kullanılmaktadır. Bu fonksiyonlardan, >>> "opendir" : Fonksiyonun prototipi aşağıdaki gibidir: #include DIR *opendir(const char *name); opendir fonksiyonu, bir dizini açmak için kullanılmaktadır. Prosesin dizini açabilmesi için dizine "r" hakkının bulunuyor olması gerekir. Fonksiyon başarı durumunda DIR isimli yapı türünden adrese, başarısızlık durumunda NULL adrese geri dönmektedir. DIR yapısı dökümante edilmemiştir. Ancak programcının bu yapının içeriğini bilmek zorunda değildir. Bu yapı bir handle olarak kullanılmaktadır. >>> "readdir" : Fonksiyonun prototipi aşağıdaki gibidir: #include struct dirent *readdir(DIR *dirp); Dizin girişleri bir döngü içerisinde readdir fonksiyonu çağrılarak elde edilmektedir. Her readdir çağrıldıında bir dizin girişi dirent isimli bir yapı adresi biçiminde bize verilmektedir. readdir fonksiyonun geri döndürdüğü adresteki yapı nesnesi statik düzeyde tahsis edilmiş durumdadır. readdir fonksiyonu dizin listesinin sonuna gelindiğinde ya da IO hatası oluştuğunda NULL adrese geri dönmektedir. Ancak eğer dizin listenin sonuna gelinmişse (bu normal durumdur) errno değişkeninin değeri değiştirilmemektedir. Eğer fonksiyon IO hatasından dolayı başarısız olmuşsa errno değişkeni uygun biçimde set edilmektedir. Burada programcı fonksiyonu çağırmadan önce errno değişkenine 0 atamalı döngüden çıkıldığında errno değişkeninin değerine bakmalıdır. readdir fonksiyonu her çağrıldığında dizin girişi bilgilerini dirent isimli bir yapı nesnesinin içerisine yerleştirir. Bu yapı nesnesinin adresiyle geri döner. Yani programcı dizin girişlerine ilişkin bilgileri bu yapıdan almaktadır. dirent yapısı şöyle bildirilmiştir: struct dirent { ino_t d_ino; /* Inode number */ char d_name[256]; /* Null-terminated filename */ }; Windows sistemlerinde dizin girişlerinde dosyaya ya da dizine ilişkin pek çok bilgi tutulmaktadır. Ancak UNIX/Linux sistemlerinde dizin girişlerinde yalnızca (kabaca) "dizin girinin ismi" ve "i-node numarası" tutulmaktadır. Şöyleki: dizin_girişi_ismi i-node_no dizin_girişi_ismi i-node_no dizin_girişi_ismi i-node_no dizin_girişi_ismi i-node_no ... Görüldüğü gibi UNIX/Linux sistemlerinde biz dizin girişlerindne yalnızca girişin ismini ve i-node numarasını elde etmekteyiz. Ayrıca UNIX/Linux sistemlerinde Windows sistemlerinde olduğu gibi "*" ve "?" gibi joker karakterlerini kullanamadığımıza dikkat ediniz. Bu sistemlerde biz dizindeki belli girişleri değil tüm girişleri elde etmekteyiz. Pekiyi bizler dizin girişlerindeki dosya isimlerini ve i-node numaralarını elde ettikten sonra o dosyaya ilişkin diğer bilgileri nasıl elde edeceğiz? İşte burada yapılması gereken readdir ile dizin girişinin ismi elde edildikten sonra ayrıca stat fonksiyonuyla dosyanın bilgilerinin elde edilmesidir. Yani tek başına readdir kullanmak yerine aynı zamanda stat fonksiyonunun da kullanılması gerekmektedir. Pekiyi readdir fonksiyonu ile okuduğumuz dizin girişlerindeki i-node numarası be anlama gelmektedir? >>>> "I-Node" Numarası: Anımsanacağı gibi dosya bilgileri stat fonksiyonları ile elde edilirken stat yapısınında st_ino elemanı da ilgili dosyanın i-node numarasını belirtiyordu. Bir disk i-node tabanlı bir dosya sistemi ile (örneğin ext-2, ext-3 gibi) formatlandığında diskte dört farklı bölüm oluşturulmaktadır: Boot Block Super Block I-Node Block Data Block Bu bloklardan, -> Boot Block 1024 byte uzunluğunda sistemin boot edilmesi için gereken bilgileri içermektedir. -> Formatlanmış diskin tüm parametrik bilgileri Super Block'ta bulunmaktadır. -> Dosyaların parçaları yani dosya bilgileri ise Data Block içerisinde tutulmaktadır. -> İşte I-Node block, i-node elemanlarından oluşmaktadır: I-Node Block ---------------- i-node elemanı i-node elemanı i-node elemanı ... i-node elemanı i-node elemanı ... Bir dosyaya ilişkin tüm bilgiler o dosyaya ilişkin i-node elemanından elde edilmektedir. Başka bir deyişle stat, lstat ve fstat fonksiyonları aslında dosya bilgilerini dosyaya ilişkin i-node elemanından elde ederler. İşte her I-Node Block'taki her i-node elemanının ilk i-node elemanın numarası 0 olacak biçimde bir sıra numarası vardır: I-Node Block ---------------- 0 i-node elemanı 1 i-node elemanı 2 i-node elemanı ... 1200 i-node elemanı 1201 i-node elemanı ... İşte bir dosyanın i-node numarası o dosyanın bilgilerinin i-node block'taki kaçıncı i-node elemanında bulunduğunu belirtmektedir. Ancak bir dosyanın i-node numarasından hareketle bilgilerinin elde edilmesi için kullanılabilecek bir POSIX fonksiyonu yoktur. Dosya bilgileri yol ifadelerinden ya da dosya betimleyicilerinden hareketle elde edilebilmektedir. Tabii işletim sistemi kendisi bu i-node numarasından hareketle dosya bilgilerine erişmektedir. Dosyaların i-node numaraları sistem genelinde tektir. >>> "closedir" : Fonksiyonun prototipi aşağıdaki gibidir: #include int closedir(DIR *dirp); Nihayet programcı işini bitirdikten sonra closedir fonksiyonu ile dizini kapatmalıdır. closedir başarı durumunda 0 değerine başarısızllık durumunda -1 değerine geri dönmektedir. Fonksiyonun başarısının kontrol edilmesine gerek yoktur. Şimdi de konuyla ilgili örnekleri inceleyelim: * Örnek 1, Aşağıdkai örnekte belli bir dizindeki dizin girişleri elde edilip stat fonksiyonuna sokulmuş ve ilgili girişe ilişkin dosya bilgileri elde edilmiştir. Bu örnekte bir noktaya dikkat ediniz. readdir fonksiyonu ile elde ettiğimiz giriş isminde bir yol ifadesi yoktur. Halbuki bizim stat fonksiyonu için uygun bir yol ifadesine gereksnimimiz vardır. Bu nedenle kodda sprintf fonksiyonu ile uygun yol ifadesi elde edilmiştir. #include #include #include #include #include #include void exit_sys(const char *msg); int main(int argc, char *argv[]) { DIR *dir; struct dirent *ent; struct stat finfo; char path[PATH_MAX]; if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } if ((dir = opendir(argv[1])) == NULL) exit_sys("opendir"); while (errno = 0, (ent = readdir(dir)) != NULL) { snprintf(path, PATH_MAX, "%s/%s", argv[1], ent->d_name); if (stat(path, &finfo) == -1) perror(path); printf("%-30s%jd\n", ent->d_name, (intmax_t)finfo.st_size); } if (errno != 0) exit_sys("readdir"); closedir(dir); return 0; } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 2.0, Aşağıdaki program sayesinde dizin içerisindeki dosyaları da "ls -l" formatında ekrana yazdırabiliriz. Ancak hizalamaya dikkat edilmemiştir. /* Ekran Çıktısı */ -rw-r--r--1 kaan study 99 Haz 11 2023 18:17 y.c -rwxr-xr-x1 kaan study 16136 Haz 17 2023 18:51 a.out -rw-r--r--1 kaan study 0 Tem 23 2023 19:56 y.txt drwxr-xr-x2 kaan study 4096 Ağu 13 2023 19:29 xxx -rw-r--r--1 kaan study 895 Ağu 26 2023 17:09 sample.c -rw-r--r--1 kaan study 2645 Ağu 26 2023 17:37 mample.c -rw-rw-rw-1 kaan study 0 Tem 23 2023 20:57 m.txt prw-r--r--1 kaan study 0 Ağu 12 2023 17:35 mypipe -rw-r--r--1 kaan study 0 Tem 23 2023 19:31 x.txt -rwxr-xr-x1 kaan study 16328 Haz 18 2023 19:17 mycalc -rwxr-xr-x1 kaan study 16472 Ağu 26 2023 17:09 sample -rw-r--r--1 kaan study 3570 Haz 18 2023 18:57 disp.c -rw-r--r--1 kaan study 1161 Haz 18 2023 19:16 mycalc.c -rw-r--r--1 kaan study 99 Haz 11 2023 18:17 x.c drwxrwxr-x8 kaan study 4096 Ağu 14 2023 22:15 .. -rw-r--r--1 kaan study 5000001 Tem 22 2023 20:07 test.txt -rwxr-xr-x1 kaan study 16816 Ağu 26 2023 17:37 mample -rw-rw-rw-1 kaan study 0 Tem 23 2023 20:45 z.txt drwxrwxr-x3 kaan study 4096 Ağu 26 2023 17:37 . /* Programın Kendisi */ #include #include #include #include #include #include #include #include #include #include void disp_ls_style(const struct stat *finfo, const char *name); void exit_sys(const char *msg); int main(int argc, char *argv[]) { DIR *dir; struct dirent *ent; char path[PATH_MAX]; struct stat finfo; if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } if (setlocale(LC_ALL, "tr_TR.utf-8") == NULL) { fprintf(stderr, "cannot set locale!..\n"); exit(EXIT_FAILURE); } if ((dir = opendir(argv[1])) == NULL) exit_sys("opendir"); while (errno = 0, (ent = readdir(dir)) != NULL) { snprintf(path, PATH_MAX, "%s/%s", argv[1], ent->d_name); if (stat(path, &finfo) == -1) perror(path); disp_ls_style(&finfo, ent->d_name); } if (errno != 0) exit_sys("readdir"); closedir(dir); } void disp_ls_style(const struct stat *finfo, const char *name) { mode_t modes[9] = {S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH}; char buf[32]; struct tm *ptm; struct passwd *pwd; struct group *grp; switch (finfo->st_mode & S_IFMT) { case S_IFBLK: putchar('b'); break; case S_IFCHR: putchar('c'); break; case S_IFIFO: putchar('p'); break; case S_IFREG: putchar('-'); break; case S_IFDIR: putchar('d'); break; case S_IFLNK: putchar('l'); break; case S_IFSOCK: putchar('s'); break; default: putchar('?'); break; } for (int i = 0; i < 9; ++i) putchar(finfo->st_mode & modes[i] ? "rwx"[i % 3] : '-'); printf("%ju ", (uintmax_t)finfo->st_nlink); if ((pwd = getpwuid(finfo->st_uid)) != NULL) printf("%s ", pwd->pw_name); else printf("%ju ", (uintmax_t)finfo->st_uid); if ((grp = getgrgid(finfo->st_gid)) != NULL) printf("%s ", grp->gr_name); else printf("%ju ", (uintmax_t)finfo->st_gid); printf("%jd ", (intmax_t)finfo->st_size); ptm = localtime(&finfo->st_mtim.tv_sec); strftime(buf, 32, "%b %2e %Y %H:%M ", ptm); printf("%s ", buf); printf("%s\n", name); } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 2.1, Yukarıdaki programın hizalamaya dikkat eden versiyonu. Ancak bu program direkt ekrana değil, önce statik ömürlü bir tampona yazmaktadır. #include #include #include #include #include #include #include #include #include #include #include void exit_sys(const char *msg); const char *get_ls(const char *path, int hlink_digit, int uname_digit, int gname_digit, int size_digit); int main(int argc, char *argv[]) { DIR *dir; struct dirent *dent; struct stat finfo; char path[PATH_MAX]; struct passwd *pass; struct group *gr; int len; int hlink_digit, uname_digit, gname_digit, size_digit; if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } if ((dir = opendir(argv[1])) == NULL) exit_sys("open"); hlink_digit = uname_digit = gname_digit = size_digit = 0; while (errno = 0, (dent = readdir(dir)) != NULL) { snprintf(path, PATH_MAX, "%s/%s", argv[1], dent->d_name); if (stat(path, &finfo) == -1) exit_sys("stat"); len = (int)log10(finfo.st_nlink) + 1; if (len > hlink_digit) hlink_digit = len; if ((pass = getpwuid(finfo.st_uid)) == NULL) exit_sys("getppuid"); len = (int)strlen(pass->pw_name); if (len > uname_digit) uname_digit = len; if ((gr = getgrgid(finfo.st_gid)) == NULL) exit_sys("getgrgid"); len = (int)strlen(gr->gr_name); if (len > gname_digit) gname_digit = len; len = (int)log10(finfo.st_size) + 1; if (len > size_digit) size_digit = len; } if (errno != 0) exit_sys("readdir"); rewinddir(dir); while (errno = 0, (dent = readdir(dir)) != NULL) { sprintf(path, "%s/%s", argv[1], dent->d_name); if (stat(path, &finfo) == -1) exit_sys("stat"); printf("%s\n", get_ls(path, hlink_digit, uname_digit, gname_digit, size_digit)); } if (errno != 0) exit_sys("readdir"); closedir(dir); return 0; } const char *get_ls(const char *path, int hlink_digit, int uname_digit, int gname_digit, int size_digit) { struct stat finfo; static char buf[4096]; static mode_t modes[] = { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH }; struct passwd *pass; struct group *gr; char *str; int index = 0; int i; if (stat(path, &finfo) == -1) return NULL; if (S_ISREG(finfo.st_mode)) buf[index] = '-'; else if (S_ISDIR(finfo.st_mode)) buf[index] = 'd'; else if (S_ISCHR(finfo.st_mode)) buf[index] = 'c'; else if (S_ISBLK(finfo.st_mode)) buf[index] = 'b'; else if (S_ISFIFO(finfo.st_mode)) buf[index] = 'p'; else if (S_ISLNK(finfo.st_mode)) buf[index] = 'l'; else if (S_ISSOCK(finfo.st_mode)) buf[index] = 's'; ++index; for (i = 0; i < 9; ++i) buf[index++] = (finfo.st_mode & modes[i]) ? "rwx"[i % 3] : '-'; buf[index] = '\0'; index += sprintf(buf + index, " %*llu", hlink_digit, (unsigned long long)finfo.st_nlink); if ((pass = getpwuid(finfo.st_uid)) == NULL) return NULL; index += sprintf(buf + index, " %-*s", uname_digit, pass->pw_name); if ((gr = getgrgid(finfo.st_gid)) == NULL) return NULL; index += sprintf(buf + index, " %-*s", gname_digit, gr->gr_name); index += sprintf(buf + index, " %*lld", size_digit, (long long)finfo.st_size); index += strftime(buf + index, 100, " %b %e %H:%M", localtime(&finfo.st_mtime)); str = strrchr(path, '/'); sprintf(buf + index, " %s", str ? str + 1 : path); return buf; } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); }