PDA

View Full Version : file size program




jamesapp
Jun 14, 2008, 12:56 PM
working on a program from the last chapter of k+r book.
here are the error messages i am getting when i tried to compile the program:


james-collinss-macbook-pro:chap8 jamescollins$ gcc fsize1.c -o fsize1.out
fsize1.c:21: error: conflicting types for ‘stat’
/usr/include/sys/stat.h:430: error: previous declaration of ‘stat’ was here
fsize1.c: In function ‘opendir’:
fsize1.c:77: warning: incompatible implicit declaration of built-in function ‘malloc’
In file included from /usr/include/sys/dir.h:72,
from fsize1.c:92:
/usr/include/dirent.h: At top level:
/usr/include/dirent.h:82: error: conflicting types for ‘DIR’
dirent.h:12: error: previous declaration of ‘DIR’ was here
/usr/include/dirent.h:106: error: conflicting types for ‘closedir’
fsize1.c:85: error: previous definition of ‘closedir’ was here
/usr/include/dirent.h:110: error: conflicting types for ‘opendir’
fsize1.c:69: error: previous definition of ‘opendir’ was here
/usr/include/dirent.h:114: error: conflicting types for ‘readdir’
dirent.h:15: error: previous declaration of ‘readdir’ was here
fsize1.c:96: error: conflicting types for ‘readdir’
dirent.h:15: error: previous declaration of ‘readdir’ was here
fsize1.c: In function ‘readdir’:
fsize1.c:100: error: ‘struct <anonymous>’ has no member named ‘fd’
fsize1.c:105: error: ‘DIRSIZ’ undeclared (first use in this function)
fsize1.c:105: error: (Each undeclared identifier is reported only once
fsize1.c:105: error: for each function it appears in.)


here is my source file which i called fsize1.c:

#include <string.h>
#include "syscalls.h"
#include <sys/file.h> /* flags for read and write */
#include <sys/types.h> /* typedefs */
#include <sys/stat.h> /* structure returned by stat */
#include "dirent.h"

void fsize(char *);

/* print file sizes */
main (int argc, char **argv)
{
if (argc == 1) /* default: current directory */
fsize(".");
else
while (--argc > 0)
fsize(*++argv);
return 0;
}

int stat(char *, struct stat *);
void dirwalk(char *, void (*fcn)(char *));

/* fsize: print size of file "name" */
void fsize(char *name)
{
struct stat stbuf;

if (stat(name, &stbuf) == -1) {
fprintf(stderr, "fsize: can't access %s\n", name);
return;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
dirwalk(name, fsize);
printf("%8ld %s", stbuf.st_size, name);
}

#define MAX_PATH 1024
/* dirwalk: apply fcn to all files in dir */
void dirwalk(char *dir, void (*fcn)(char *))
{
char name[MAX_PATH];
Dirent *dp;
DIR *dfd;

if ((dfd = opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: can't open %s\n", dir);
return;
}
while ((dp = readdir(dfd)) != NULL) {
if (strcmp(dp->name, ".") == 0
|| strcmp(dp->name, "..") == 0)
continue; /* skip self and parent */
if (strlen(dir)+strlen(dp->name)+2 > sizeof(name))
fprintf(stderr, "dirwalk: name %s/%s too long\n",
dir, dp->name);
else {
sprintf(name, "%s/%s", dir, dp->name);
(*fcn)(name);
}
}
closedir(dfd);
}

int fstat(int fd, struct stat *);

/* opendir: open a directory for readdir calls */
DIR *opendir(char *dirname)
{
int fd;
struct stat stbuf;
DIR *dp;

if ((fd = open(dirname, O_RDONLY, 0)) == -1
|| fstat(fd, &stbuf) == -1
|| (stbuf.st_mode & S_IFMT) != S_IFDIR
|| (dp = (DIR *) malloc(sizeof(DIR))) == NULL)
return NULL;
dp->fd = fd;
return dp;
}

/* closedir: close directory opened by opendir */
void closedir(DIR *dp)
{
if (dp) {
close(dp->fd);
free(dp);
}
}

#include <sys/dir.h> /* local directory structures */

/* readdir: read directory entries in sequence */
Dirent *readdir(DIR *dp)
{
struct direct dirbuf; /* local directory structure */
static Dirent d; /* return: portable structure */

while (read(dp->fd, (char *) &dirbuf, sizeof(dirbuf))
== sizeof(dirbuf)) {
if (dirbuf.d_ino == 0) /* slot not in use */
continue;
d.ino = dirbuf.d_ino;
strncpy(d.name, dirbuf.d_name, DIRSIZ);
d.name[DIRSIZ] = '\0'; /* ensure termination */
return &d;
}
return NULL;
}


one thing i will mention, is that i compiled a version of the program in which i took out most if not all of the functions that were causing most of the error messages. For example the error message that dealt with the function from the book called readdir i took out, my logic is that it is contained in one of the include files and is causing the error message:


dirent.h:15: error: previous declaration of ‘readdir’ was here




i will include my updated source file which i called fsize.c


#include <string.h>
#include "syscalls.h"
#include <sys/file.h> /* flags for read and write */
#include <sys/types.h> /* typedefs */
#include <sys/stat.h> /* structure returned by stat */
#include "dirent.h"

void fsize(char *);

/* print file sizes */
main (int argc, char **argv)
{
if (argc == 1) /* default: current directory */
fsize(".");
else
while (--argc > 0)
fsize(*++argv);
return 0;
}



/* fsize: print size of file "name" */
void fsize(char *name)
{
struct stat stbuf;

if (stat(name, &stbuf) == -1) {
fprintf(stderr, "fsize: can't access %s\n", name);
return;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
dirwalk(name, fsize);
printf("%8ld %s", stbuf.st_size, name);
}

#define MAX_PATH 1024
/* dirwalk: apply fcn to all files in dir */
void dirwalk(char *dir, void (*fcn)(char *))
{
char name[MAX_PATH];
Dirent *dp;
DIR *dfd;

if ((dfd = opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: can't open %s\n", dir);
return;
}
while ((dp = readdir(dfd)) != NULL) {
if (strcmp(dp->name, ".") == 0
|| strcmp(dp->name, "..") == 0)
continue; /* skip self and parent */
if (strlen(dir)+strlen(dp->name)+2 > sizeof(name))
fprintf(stderr, "dirwalk: name %s/%s too long\n",
dir, dp->name);
else {
sprintf(name, "%s/%s", dir, dp->name);
(*fcn)(name);
}
}
closedir(dfd);
}

int fstat(int fd, struct stat *);

/* opendir: open a directory for readdir calls */
DIR *opendir(char *dirname)
{
int fd;
struct stat stbuf;
DIR *dp;

if ((fd = open(dirname, O_RDONLY, 0)) == -1
|| fstat(fd, &stbuf) == -1
|| (stbuf.st_mode & S_IFMT) != S_IFDIR
|| (dp = (DIR *) malloc(sizeof(DIR))) == NULL)
return NULL;
dp->fd = fd;
return dp;
}

/* closedir: close directory opened by opendir */
void closedir(DIR *dp)
{
if (dp) {
close(dp->fd);
free(dp);
}
}



i got the program to compile but when i went to run it i got the following from terminal:


james-collinss-macbook-pro:chap8 jamescollins$ ./fsize.out
374 (null)james-collinss-macbook-pro:chap8 jamescollins$


when no dir is given as an argument it is supposed to use the current directory.
i don't know maybe this is how the program is supposed to work. count the number of bytes in a directory? any help?
why does terminal say (null)? why doesn't computer go to newline after (null)?
does 374 sound reasonable for a directory with like ten ordinary c files?
i ran the program a couple of more times.
when i just type ./fsize.out without any arguments i got the following:


james-collinss-macbook-pro:chap8 jamescollins$ ./fsize.out
442 (null)james-collinss-macbook-pro:chap8 jamescollins$


and then when i wrote ./fsize.out cpcee.c
i got the following;


/fsize.out cpcee.c
878 (null)james-collinss-macbook-pro:chap8 jamescollins$


i did a get info and the number 878 matched the get info's
i tried other tests like writing a couple of files and they matched the get info's i looked at. One question i have is when i used no arguments i got


james-collinss-macbook-pro:chap8 jamescollins$ ./fsize.out
442 (null)james-collinss-macbook-pro:chap8 jamescollins$


any help? do you think this program is behaving how the book intended?



lee1210
Jun 14, 2008, 01:33 PM
It's not there yet, but this is what I have so far:


#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <sys/file.h> /* flags for read and write */
#include <sys/types.h> /* typedefs */
#include <sys/stat.h> /* structure returned by stat */

void fsize(char *);
void dirwalk(char *,void (*)(char *));
DIR *my_opendir(char *);
void my_closedir(DIR *);

/* print file sizes */
main (int argc, char **argv)
{
if (argc == 1) /* default: current directory */
fsize(".");
else
while (--argc > 0)
fsize(*++argv);
return 0;
}



/* fsize: print size of file "name" */
void fsize(char *name)
{
struct stat stbuf;
int intsize = 0;
long long int lintsize = 0;

if (stat(name, &stbuf) == -1) {
fprintf(stderr, "fsize: can't access %s\n", name);
return;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
dirwalk(name, fsize);
if(sizeof(stbuf.st_size) == sizeof(intsize)) {
printf("%8d\t%s\n",stbuf.st_size,name);
} else if(sizeof(stbuf.st_size) == sizeof(lintsize)) {
printf("%8lld\t%s\n",stbuf.st_size,name);
} else {
fprintf(stderr,"off_t is not the same bit width of an int or long long int. It is %d bytes wide.\n",sizeof(stbuf.st_size));
}
}

#define MAX_PATH 1024
/* dirwalk: apply fcn to all files in dir */
void dirwalk(char *dir, void (*fcn)(char *))
{
char name[MAX_PATH];
struct dirent *dp;
DIR *dfd;

if ((dfd = my_opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: can't open %s\n", dir);
return;
}
while ((dp = readdir(dfd)) != NULL) {
if (strcmp(dp->d_name, ".") == 0
|| strcmp(dp->d_name, "..") == 0)
continue; /* skip self and parent */
if (strlen(dir)+strlen(dp->d_name)+2 > MAX_PATH)
fprintf(stderr, "dirwalk: name %s/%s too long\n",
dir, dp->d_name);
else {
snprintf(name,MAX_PATH, "%s/%s", dir, dp->d_name);
(*fcn)(name);
}
}
my_closedir(dfd);
}

int fstat(int fd, struct stat *);

/* my_opendir: open a directory for readdir calls */
DIR *my_opendir(char *dirname)
{
int fd;
struct stat stbuf;
DIR *dp;

if ((fd = open(dirname, O_RDONLY, 0)) == -1
|| fstat(fd, &stbuf) == -1
|| (stbuf.st_mode & S_IFMT) != S_IFDIR
|| (dp = (DIR *) malloc((size_t)sizeof(DIR))) == NULL)
return NULL;
dp->dd_fd = fd;
return dp;
}

/* my_closedir: close directory opened by my_opendir */
void my_closedir(DIR *dp)
{
if (dp) {
close(dp->dd_fd);
free(dp);
}
}


If i run it:
./fsize fsize.c
2561 fsize.c

I'll try to remember the changes. Some of them are going to be because I don't have the includes you used with the book, so I included standard system things. Other than that, you were redefining opendir and closedir, which are system functions. I changed their names so they didn't collide. Your directory code (dirent was in quotes, so that may be "book included") was not using the field names I was getting in the DIR and dirent structs, so I changed those. The size of st_size on my system is 8 bytes. I added code to check the size where it's being run and either print as a 4 byte int or an 8 byte long long int. You were printing it as 4 byte, and your system probably has off_t defined as 8 bytes. When the printf was moving the st_size off of the stack, it was only taking 4 bytes. If this were on a different endian system, your result would have been different. In this case, it happened that the next 4 bytes of the 8 byte off_t type were all null. These were shifted off and used as a char *. Your libc was nice enough to print (null) instead of crashing irreparably. The name pointer was still happily sitting on the stack waiting to be used, and never was.

I'll look at the directory traversal code next, it doesn't seem to be working on my system.

-Lee

lee1210
Jun 14, 2008, 01:54 PM
Got it going for directory size, going through files and recursively through subdirectories. I got rid of your opendir command because the system commands (readdir, etc.) I was using were not happy with that. Readdir was returning null no matter what. You're getting the size of the inodes used to store the directory. I calculated that into the total size as well. There's a chance that you're going to go over your max stack size with this code as fsize/dirwalk end up being called recursively as you go down the directory tree. This may not be a big deal, your parameters are only 4 and 12 bytes on the stack respectively, and they don't have a lot of locals, but it could happen.


#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <sys/file.h> /* flags for read and write */
#include <sys/types.h> /* typedefs */
#include <sys/stat.h> /* structure returned by stat */

long long int fsize(char *);
long long int dirwalk(char *,long long int(*)(char *));
DIR *my_opendir(char *);
void my_closedir(DIR *);

/* print file sizes */
main (int argc, char **argv)
{
if (argc == 1) /* default: current directory */
fsize(".");
else
while (--argc > 0)
fsize(*++argv);
return 0;
}



/* fsize: print size of file "name" */
long long int fsize(char *name)
{
struct stat stbuf;
int intsize = 0;
long long int lintsize = 0;

if (stat(name, &stbuf) == -1) {
fprintf(stderr, "fsize: can't access %s\n", name);
return;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
lintsize = dirwalk(name, fsize);
}
lintsize = lintsize + stbuf.st_size;
printf("%8lld\t%s\n",lintsize,name);
return lintsize;
}

#define MAX_PATH 1024
/* dirwalk: apply fcn to all files in dir */
long long int dirwalk(char *dir, long long int(*fcn)(char *))
{
char name[MAX_PATH];
struct dirent *dp;
DIR *dfd;
long long int totalsize = 0;
/*
if ((dfd = my_opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: can't open %s\n", dir);
return totalsize;
}
*/
if((dfd = opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: can't open %s\n", dir);
return totalsize;
}
while ((dp = readdir(dfd)) != NULL) {
if (strcmp(dp->d_name, ".") == 0
|| strcmp(dp->d_name, "..") == 0)
continue; /* skip self and parent */
if (strlen(dir)+strlen(dp->d_name)+2 > MAX_PATH) {
fprintf(stderr, "dirwalk: name %s/%s too long\n",
dir, dp->d_name);
} else {
snprintf(name,MAX_PATH, "%s/%s", dir, dp->d_name);
totalsize += (*fcn)(name);
}
}
my_closedir(dfd);
return totalsize;
}

int fstat(int fd, struct stat *);

/* my_opendir: open a directory for readdir calls */
DIR *my_opendir(char *dirname)
{
int fd;
struct stat stbuf;
DIR *dp;

if ((fd = open(dirname, O_RDONLY, 0)) == -1
|| fstat(fd, &stbuf) == -1
|| (stbuf.st_mode & S_IFMT) != S_IFDIR
|| (dp = (DIR *) malloc((size_t)sizeof(DIR))) == NULL)
return NULL;
dp->dd_fd = fd;
return dp;
}

/* my_closedir: close directory opened by my_opendir */
void my_closedir(DIR *dp)
{
if (dp) {
close(dp->dd_fd);
free(dp);
}
}


I didn't check an overflow condition on totalsize in dirwalk, so this is also a potential failure. I don't think you could have a harddrive that's big enough to store 63 bits worth of bytes for the next few years, but I'm pointing it out for the purpose of rigor, not practicality.

-Lee

P.S. Didn't notice this before, but *++argv makes me cringe. Maybe the book says that's good times, fun, etc. but it seems pretty questionable to me. Looping from 1 to argc-1 would take 2 or so more lines of code. This is, I suppose, a matter of personal preference. I'm just pointing it out because it made me a little queasy to see, as any pointer arithmatic does.

jamesapp
Jun 18, 2008, 11:44 AM
james-collinss-macbook-pro:chap8 jamescollins$ gcc fsize2.c -o fsize2.out
fsize2.c: In function ‘my_opendir’:
fsize2.c:94: error: ‘struct <anonymous>’ has no member named ‘dd_fd’
fsize2.c: In function ‘my_closedir’:
fsize2.c:102: error: ‘struct <anonymous>’ has no member named ‘dd_fd’

got the following error messages when i tried to compile your version of the program i had posted. I called your version fsize2.c

does this have anything to do with dirent.h, i posted your version of fsize as it was didn't change anything, i wonder about the program dirent.h that was an included file which i got from the book. I should have posted it in my original post here it is:

#define NAME_MAX 14 /* longest filename component */
/* system dependent */

typedef struct { /* portable directory entry */
long ino; /* inode number */
char name[NAME_MAX+1]; /* name + '\0' terminator */
} Dirent;

typedef struct { /* minimal DIR: no buffering, etc. */
int fd; /* file descriptor for directory */
Dirent d; /* the directory entry */
} DIR;

DIR *opendir(char *dirname);
Dirent *readdir(DIR *dfd);
void closedir(DIR *dfd);

like i could change closedir(DIR *dfd); to my_closedir(DIR *dfd); and then include dirent.h, maybe the error messages have nothing to do with dirent.h, but not sure?

lee1210
Jun 19, 2008, 01:07 PM
There's definitely an issue with your use of book defined structures to call system functions that expect something different. In this case you've implemented opendir and closedir, but are using the system version of readdir, which wants something different than what you're passing in. I'm not sure how the book is expecting this to work. I would look at the manpages for these functions, and the types they're expecting. if you want to do some of that work yourself, you'll need to use the same structs as the system, or make yours (defined in your version of dirent.h) be exactly equivalent.

It seems like you should use all system calls, or do everything yourself. I'm not sure you can do it yourself in a portable way, but that may not matter.

-Lee