domingo, 2 de dezembro de 2007

Lendo diretórios em C/C++

Outro dia me perguntaram como listar o conteúdo de um diretório em C.

Não é difícil. Já fiz isso algumas vezes, mas é claro que de cabeça não vou ficar guardando isso: pra que existem os comandos man e apropos?

Os cabeçalhos necessários para leitura de diretório são:
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>


Precisamos também das bibliotecas string.h – ou string em C++ –, time.h e stdio.h – ou iostream em C++.

A função para abertura de um diretório é opendir(), recebe como parâmetro uma string representando o nome do diretório e retorna um ponteiro para descritor de diretório, DIR.

Para abrir um diretório:
DIR *dir = opendir(dirname);


A função readdir() reitera sobre o diretório, retornando um ponteiro para estrutura do tipo dirent ou NULL caso não haja mais entradas.

Então uma forma de reiterar sobre um diretório é:
struct dirent *ent;
while (ent = readdir(dir)) {
    ...
}


Ao final da reiteração, é preciso fechar o diretório:
closedir(dir);


A estrutura dirent é declarada assim:
struct dirent {
    long     d_ino;                 /* número de inodes */
    off_t    d_off;                 /* offset para este dirent */
    unsigned short d_reclen;        /* tamanho de d_name */
    char     d_name[NAME_MAX+1];    /* nome do arquivo */
};


Se quiser mais informações sobre cada entrada, crie uma estrutura stat para a entrada. Para tanto use a função stat():
struct stat buf;
char aux[256];

strcpy(aux, dirname);
#ifdef WIN32
strcat(aux, "\\");
#else
strcat(aux, "/");
#endif
strcat(aux, ent->d_name);

stat(aux, &buf);


A estrutura stat é definida assim:
struct stat {
    dev_t     st_dev;       /* ID do dispositivo */
    ino_t     st_ino;       /* número do inode */
    mode_t    st_mode;      /* modo de proteção */
    nlink_t   st_nlink;     /* número de hard links */
    uid_t     st_uid;       /* ID do usuário dono */
    gid_t     st_gid;       /* ID do grupo dono */
    dev_t     st_rdev;      /* ID do dispositivo se for arquivo especial */
    off_t     st_size;      /* tamanho total em bytes */
    blksize_t st_blksize;   /* tamanho do bloco */
    blkcnt_t  st_blocks;    /* número de blocos alocados */
    time_t    st_atime;     /* hora do último acesso */
    time_t    st_mtime;     /* hora da última modificação */
    time_t    st_ctime;     /* hora da última troca de estado */
};


Um campo interessante é st_mode. O modo de acesso pode ser obtido como:
char *acc = (char *) malloc(sizeof(char) * 6);
sprintf(acc, "0%o", buf.st_mode % 4096);


E é possível saber se a entrada se trata de um arquivo ou diretório de forma similar:
int isDir = buf.st_mode & S_IFDIR;


É possível usar as funções da biblioteca time.h para facilitar a leitura dos campos st_atime, st_mtime e st_ctime.

Por exemplo, para exibir a última modificação de um arquivo:
printf("Última modificação: %s", ctime( &(buf.st_mtime) ));


Espero que este artigo seja útil. =)

[]'s
Cacilhas La Batalema
blog comments powered by Disqus