What is wrong with this example from K&R?
I am trying the listing directories example from K&R section 8.6, but when testing the code it, says "can't access" for every single file in the directory. It is because it's designed for Unix? Below is what I think the code should look like when pieced together, what am I missing? ADD:How do I fix this code to make it work?
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "dirent.h"
#include <sys/dir.h>
#define NAME_MAX 14
typedef struct {
long ino;
char name[NAME_MAX+1];
} Dirent;
void fsize(char *);
/* print file sizes */
main(int argc, char **argv)
{
if (argc == 1)
fsize(".");
else
while (--argc > 0)
fsize(*++argv);
return 0;
}
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, "fsiz开发者_如何学Goe: can't access %s\n", name);
return;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
dirwalk(name, fsize);
printf("%81d %s\n", stbuf.st_size, name);
}
#define MAX_PATH 1024
/* dirwalk: apply fn 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;
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);
}
Where to start....
- NAME_MAX and PATH_MAX actually exist... you shouldn't define these yourself.
- Dirent actually exists (struct dirent from dirent.h), so use the existing
struct dirent
. - You aren't using braces consistently (always use braces), making your code unreadable.
- You should be including <dirent.h>, not "dirent.h" (unless you have a specific reason to do so).
- You need to forward-declare your functions before you use them.
Also, in the future, it would be helpful to know:
- Your compiler.
- Your operating system.
- The errors emitted by your compiler.
As the above can make it much easier to resolve your issues. More importantly, though, you mentioned that you are learning "K&R C". Note that "K&R C" predates the standardization of the C programming language by the International Standards Organization (ISO). Since K&R C, there have been several iterations of ISO C: ISO C 1989 (a.k.a. C89), ISO C 1990 (a.k.a. C90), and ISO C 1999 (a.k.a. C99). I strongly suggest you teach yourself from a more up-to-date book.
Also, if you are targetting UNIX, I strongly recommend you get comfortable with the UNIX Manual Pages. The UNIX Manual Pages chrome extension makes it really easy to access the official IEEE Std. 1003.1 POSIX manual pages. I strongly suggest you look at those to understand what standard functions are available, what is expected of the inputs, what is guaranteed of the outputs, and how they may fail.
The structure Dirent
defined in your example probably doesn't match struct dirent
for your system (it doesn't on mine).
On a Linux system the following will fix the problem:
/* dirwalk: apply fn to all files in dir */
void dirwalk(char *dir, void (*fcn)(char *))
{
char name[MAX_PATH];
struct 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->d_name, ".") == 0
|| strcmp(dp->d_name, "..") == 0)
continue;
if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name))
fprintf(stderr, "dirwalk: name %s/%s too long\n",
dir, dp->d_name);
else {
sprintf(name, "%s/%s", dir, dp->d_name);
(*fcn)(name);
}
}
closedir(dfd);
}
The readdir
function is system-specific, so you should consult the documentation for yours. On a Linux system you can type man 2 readdir
in a terminal to see the online manual for the readdir
function.
Your specific problem is your homebrew Dirent
. readdir
actually returns a struct dirent *
and its layout does not necessarily match your Dirent
, in particular there are probably more members before the d_name
. So, when you look at your dp->name
to get the name you probably end up trying to read some non-string as a string and that gives you nonsense; everything falls apart once you get nonsense.
So fix up everything that Michael Aaron Safyan mentioned, turn up your compiler's warning flags (-Wall -Werror
is a good starting place for gcc), and your program should start working.
The problem is obvious when you look at the compiler warnings. At least on my system GCC gives:
x.c: In function ‘dirwalk’:
x.c:58:16: warning: assignment from incompatible pointer type
This is the while ((dp = readdir(dfd)) != NULL) {
line. As Michael has pointed out, struct dirent
already exists and this is what is returned by readdir
. You are using a custom, incompatible structure, this causes you to get garbage instead of file/dir names.
The quickest fix to make it work is to get rid of Dirent
and use struct dirent
instead.
精彩评论