ENUNCIADO
Esta es una práctica que no hay que entregar.
Esta práctica vale doble en cuanto al mejor que la entregue.
El programa que hay que realizar no admite ningún argumento
por la línea de órdenes de la shell.
El programa listará el
nombre de todos los
ficheros del directorio actual que no posean más de un enlace
duro. Hacer una comprobación no paranoica de errores.
COMENTARIOS
La práctica con 0.50 es la de Tiosoni y Peco ;) :
/* Sección de compatibilidad con Linux y HPUX */
#ifndef _HPUX_SOURCE
# ifndef __USE_BSD
# define __USE_BSD
# endif
#endif
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#if defined _HPUX_SOURCE
struct direct
{
unsigned long d_fileno; /* inodo aprox. */
unsigned short d_reclen; /* longitud de la estructura */
unsigned short d_namlen; /* longitud del nombre */
char d_name[MAXNAMLEN + 1]; /* nombre del fichero */
};
int getdirentries(int fildes, struct direct *buf, size_t nbytes,
off_t *basep);
# define DIR direct
# define CAST (struct direct *)
#else
# define DIR dirent
# define CAST
#endif
int main(int argc, char **argv)
{
char mens_error[80], *buffer_getd;
int filedes, val_ret, parte_actual;
long tam;
struct DIR *sdpt, *sdpt_origen;
struct stat buffer_stat;
off_t posic;
if (argc > 1)
{
fprintf(stderr, "\nError de sintaxis.");
fprintf(stderr, "\nUso: %s\n", argv[0]);
return 1;
}
/* No hace falta complicarse la vida: "." es el directorio actual */
if ((filedes = open(".", O_RDONLY)) == -1)
{
/* La macro __LINE__ la define el propio compilador como la línea actual. */
/* No aporta mucho al programa. */
sprintf(mens_error, "%s(line[%d]): open", argv[0], __LINE__-2);
perror(mens_error);
return 2;
}
/* Aprovechamos el descriptor de fichro abierto */
if ((val_ret = fstat(filedes, &buffer_stat)) == -1)
{
sprintf(mens_error, "%s(line[%d]): fstat", argv[0], __LINE__-2);
perror(mens_error);
return 3;
}
/* Puede dar problemas de portabilidad elegir un búfer más grande. */
tam = 5 * buffer_stat.st_blksize;
if ((buffer_getd = (char *)calloc(tam, sizeof(char))) == NULL)
{
sprintf(mens_error, "%s(line[%d]): calloc", argv[0], __LINE__-2);
perror(mens_error);
return 4;
}
/* Este es uno de los pocos casos en que recomiendo una asignación */
/* y una comparación en la misma línea. Es muy fácil olvidar los */
/* paréntesis debido a que == tiene más precedencia que = y el */
/* poner las cosas así no aporta nada. En este caso, sin embargo, */
/* te ahorras tener que escribir dos veces la llamada a getdiren... */
while ((val_ret = getdirentries(filedes, CAST buffer_getd,
(size_t)tam, &posic)) != 0)
{
if (val_ret == -1)
{
sprintf(mens_error, "%s(line[%d]): getdirentries", argv[0],
__LINE__-11);
perror(mens_error);
return 5;
}
/* En C, el operador de asignación (=) también devuelve un valor. */
/* Eso permite hacer dobles, triples... asignaciones como la siguiente. */
sdpt_origen = sdpt = (struct DIR *)buffer_getd;
parte_actual = val_ret;
/* La variable sdpt_origen no aporta nada más que confusión al código. */
while (((char *)sdpt - (char *)sdpt_origen) < parte_actual)
{
if ((val_ret = stat(sdpt->d_name, &buffer_stat)) == -1)
{
sprintf(mens_error, "%s(line[%d]): stat", argv[0],
__LINE__-2);
perror(mens_error);
/* De siempre hacer lo mismo, no nos paramos a pensar: */
/* ¿Es necesario parar todo el programa porque un sólo */
/* fichero falle en su stat? */
return 7;
}
/* La macro S_ISDIR devuelve verdadero si tenemos */
/* un directorio. Aquí no vale para nada. Todos */
/* los dirs tienen al menos 2 enlaces: ellos */
/* mismos y el ".." dentro de ellos. */
if (!(S_ISDIR(buffer_stat.st_mode)) && (buffer_stat.st_nlink < 2))
printf("%s\n", sdpt->d_name);
sdpt = (struct DIR *)((char *)sdpt + (sdpt->d_reclen));
}
}
close(filedes);
free(buffer_getd);
return 0;
}
MENCIONES ESPECIALES
- De IorI (0.1 ptos.): para recorrer el búfer que nos ha
pasado una llamada de
getdirentries
nada mejor
que la potencia del bucle for
de C.
for (reg=(struct direct *)buffer;
regnum;
regnum-=reg->d_reclen,
reg=(struct direct *)((unsigned char*)reg+reg->d_reclen)
)
{
stat(reg->d_name,&info);
if(info.st_nlink<2) printf("%d %s\n",reg->d_fileno,reg->d_name);
}
En la segunda claúsula del for
, un escueto
regnum
, que así nos ahorramos poner !=0
.
En la tercera claúsula del for
dos instrucciones
de C separadas por el operador ",". Lástima que estos alardes
programísticos no se acompañen con la
comprobación de errores del stat
.
© 2000 Guillermo González Talaván.