_Isis_ 202 Denunciar post Postado Dezembro 24, 2008 Utilizando a função scandir podemos ler o conteúdo de um diretório passado como argumento, além de ordenar a lista que armazena esse conteúdo e filtrar os nomes de acordo com um padrão. Para utilizar a função é necessário incluir dirent.h. int scandir(const char *dir, struct dirent ***namelist, int(*filter)(const struct dirent *), int(*compar)(const struct dirent **, const struct dirent **)); Protótipo da função O primeiro argumento é o nome do diretório a ser lido. Namelist é a lista que armazenará o conteúdo do diretório. Os dois últimos argumentos são ponteiros para funções, tratados mais tarde. Por enquanto vamos ficar no básico: ler e listar o conteúdo do diretório. #include <dirent.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char *argv[]) { if (argc != 2) return 1; struct dirent **lista = (struct dirent **)malloc(sizeof(struct dirent *)); if (lista == NULL) return 2; int conteudo; if ((conteudo = scandir(argv[1], &lista, 0, NULL)) == -1) { fprintf(stderr,"%m\n"); return 2; } puts("Imprimindo as entradas..."); while(conteudo--) printf("%s\n", lista[conteudo]->d_name); free(lista); lista = NULL; return 0; } Se a função scandir retornar um inteiro maior que -1, esse é o número de entradas selecionadas. Caso algo errado ocorra, retorna -1. O %m imprime a mensagem associada ao valor da variável errno (extensão Glibc). Caso o argumento passado não seja um diretório, a mensagem "Not a directory" é exibida. Podemos ordenar a lista de entradas utilizando as funções alphasort ou versionsort, passando seu nome no último argumento da função. A função alphasort utiliza a strcoll (C99) e a versionsort, strverscmp. Qual a diferença? Considere o seguinte diretório: O comando ls também utiliza as duas funções (strcoll e strverscmp). Listando os arquivos com ls -l, temos que o arquivo jan10 vem após jan1. Se a listagem for feita utilizando ls -lv, a função utilizada é strverscmp. #include <dirent.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { if (argc != 2) return 1; struct dirent **lista = (struct dirent **)malloc(sizeof(struct dirent *)); if (lista == NULL) return 2; int conteudo; if ((conteudo = scandir(argv[1], &lista, 0, versionsort)) == -1) { fprintf(stderr,"%m\n"); return 2; } puts("Imprimindo as entradas..."); while(conteudo--) printf("%s\n", lista[conteudo]->d_name); free(lista); lista = NULL; return 0; } A linha para compilar o código é gcc scandir_3.c -D _GNU_SOURCE Repare que a listagem inclui ".." (diretório pai) e "." (diretório atual). Podemos retirar essas linhas utilizando um filtro, o terceiro argumento de scandir. Aqui, precisamos conhecer os campos da estrutura dirent. struct dirent { ino_t d_ino; off_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[256]; }; O campo que vamos usar é d_name, que contém o nome do arquivo. #include <dirent.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int filtro(const struct dirent *F) { if (!strcmp(F->d_name,"..") || !strcmp(F->d_name,".")) return 0; return 1; } int main(int argc, char *argv[]) { if (argc != 2) return 1; struct dirent **lista = (struct dirent **)malloc(sizeof(struct dirent *)); if (lista == NULL) return 2; int conteudo; if ((conteudo = scandir(argv[1], &lista, filtro, versionsort)) == -1) { fprintf(stderr,"%m\n"); return 2; } puts("Imprimindo as entradas..."); while(conteudo--) printf("%s\n", lista[conteudo]->d_name); free(lista); lista = NULL; return 0; } Utilizando o campo d_type filtramos a listagem por tipo de arquivo.Criando diretórios, links simbólicos, sockets e executando o filtro p/ não exibir diretórios, temos: int filtro(const struct dirent *F) { if (!strcmp(F->d_name,"..") || !strcmp(F->d_name,".") || F->d_type == DT_DIR ) return 0; return 1; } d_type só está definido para arquivos regulares, diretórios, FIFO (ou pipe), socket UNIX e dispositivos de bloco e caractere. Se tentarmos filtrar links simbólicos ou hardlinks utilizando DT_UNKNOWN, o código não funciona. Nesse caso temos que usar o lstat e isso é um trabalho a mais, pois temos que ler sobre as macros de verificação de tipo de arquivo na manpage da função. char * base; int filtro(const struct dirent *F) { struct stat info; char * absolute_path = (char*)malloc(strlen(base)+strlen(F->d_name)); if (absolute_path == NULL) return 0; memcpy(absolute_path, base, strlen(base)); memcpy(absolute_path+strlen(base), F->d_name,strlen(F->d_name)); absolute_path[strlen(base)+strlen(F->d_name)] = '\0'; int retorno = lstat(absolute_path, &info); free(absolute_path); absolute_path = NULL; if (!strcmp(F->d_name,"..") || !strcmp(F->d_name,".")) return 0; if (!retorno && S_ISLNK(info.st_mode) || retorno == -1) return 0; return 1; } A função lstat retorna 0 se não ocorrer nenhum erro. Liberamos a memória em seguida e o segundo if realiza a comparação do campo st_mode (que indica o tipo do arquivo) apenas se lstat tiver retornado 0. Então, se o arquivo não for "..", "." e nem um link simbólico, ele é incluido na listagem do diretório. Se o retorno de lstat for -1, a expressão do segundo if recebe 1 tornando-se verdadeira e não incluímos o nome que gerou o erro na lista. Para retirar os hardlinks o processo é semelhante. A diferença é que o campo st_mode é identificado como um arquivo regular, então temos que contar o número de hardlinks, que, no caso de um hardlink será sempre maior que 1 (um arquivo regular possui apenas 1 hardlink). int filtro(const struct dirent *F) { struct stat info; char * absolute_path = (char*)malloc(strlen(base)+strlen(F->d_name)); if (absolute_path == NULL) return 0; memcpy(absolute_path, base, strlen(base)); memcpy(absolute_path+strlen(base), F->d_name,strlen(F->d_name)); absolute_path[strlen(base)+strlen(F->d_name)] = '\0'; int retorno = lstat(absolute_path, &info); free(absolute_path); absolute_path = NULL; if (!strcmp(F->d_name,"..") || !strcmp(F->d_name,".")) return 0; if (!retorno && (S_ISLNK(info.st_mode) || info.st_nlink > 1) || retorno == -1) return 0; return 1; } #include <dirent.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <sys/types.h> char * base; int filtro(const struct dirent *F) { struct stat info; char * absolute_path = (char*)malloc(strlen(base)+strlen(F->d_name)); if (absolute_path == NULL) return 0; memcpy(absolute_path, base, strlen(base)); memcpy(absolute_path+strlen(base), F->d_name,strlen(F->d_name)); absolute_path[strlen(base)+strlen(F->d_name)] = '\0'; int retorno = lstat(absolute_path, &info); free(absolute_path); absolute_path = NULL; if (!strcmp(F->d_name,"..") || !strcmp(F->d_name,".")) return 0; if (!retorno && (S_ISLNK(info.st_mode) || info.st_nlink > 1) || retorno == -1) return 0; return 1; } int main(int argc, char *argv[]) { if (argc != 2) return 1; struct dirent **lista = (struct dirent **)malloc(sizeof(struct dirent *)); if (lista == NULL) return 2; int conteudo; base = argv[1]; if ((conteudo = scandir(argv[1], &lista, filtro, versionsort)) == -1) { fprintf(stderr,"%m\n"); return 2; } puts("Imprimindo as entradas..."); while(conteudo--) printf("%s\n", lista[conteudo]->d_name); free(lista); lista = NULL; return 0; } Compartilhar este post Link para o post Compartilhar em outros sites