开发者

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....

  1. NAME_MAX and PATH_MAX actually exist... you shouldn't define these yourself.
  2. Dirent actually exists (struct dirent from dirent.h), so use the existing struct dirent.
  3. You aren't using braces consistently (always use braces), making your code unreadable.
  4. You should be including <dirent.h>, not "dirent.h" (unless you have a specific reason to do so).
  5. You need to forward-declare your functions before you use them.

Also, in the future, it would be helpful to know:

  1. Your compiler.
  2. Your operating system.
  3. 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜